Files
clang-p2996/clang/include/clang/Analysis/ProgramPoint.h
isuckatcs d65379c8d4 [analyzer] Remove the loop from the exploded graph caused by missing information in program points
This patch adds CFGElementRef to ProgramPoints
and helps the analyzer to differentiate between
two otherwise identically looking ProgramPoints.

Fixes #60412

Differential Revision: https://reviews.llvm.org/D143328
2023-03-04 02:01:45 +01:00

780 lines
24 KiB
C++

//==- ProgramPoint.h - Program Points for Path-Sensitive Analysis --*- C++ -*-//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines the interface ProgramPoint, which identifies a
// distinct location in a function.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_ANALYSIS_PROGRAMPOINT_H
#define LLVM_CLANG_ANALYSIS_PROGRAMPOINT_H
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/DataTypes.h"
#include <cassert>
#include <optional>
#include <string>
#include <utility>
namespace clang {
class AnalysisDeclContext;
class LocationContext;
/// ProgramPoints can be "tagged" as representing points specific to a given
/// analysis entity. Tags are abstract annotations, with an associated
/// description and potentially other information.
class ProgramPointTag {
public:
ProgramPointTag(void *tagKind = nullptr) : TagKind(tagKind) {}
virtual ~ProgramPointTag();
virtual StringRef getTagDescription() const = 0;
/// Used to implement 'isKind' in subclasses.
const void *getTagKind() const { return TagKind; }
private:
const void *const TagKind;
};
class SimpleProgramPointTag : public ProgramPointTag {
std::string Desc;
public:
SimpleProgramPointTag(StringRef MsgProvider, StringRef Msg);
StringRef getTagDescription() const override;
};
class ProgramPoint {
public:
enum Kind { BlockEdgeKind,
BlockEntranceKind,
BlockExitKind,
PreStmtKind,
PreStmtPurgeDeadSymbolsKind,
PostStmtPurgeDeadSymbolsKind,
PostStmtKind,
PreLoadKind,
PostLoadKind,
PreStoreKind,
PostStoreKind,
PostConditionKind,
PostLValueKind,
PostAllocatorCallKind,
MinPostStmtKind = PostStmtKind,
MaxPostStmtKind = PostAllocatorCallKind,
PostInitializerKind,
CallEnterKind,
CallExitBeginKind,
CallExitEndKind,
FunctionExitKind,
PreImplicitCallKind,
PostImplicitCallKind,
MinImplicitCallKind = PreImplicitCallKind,
MaxImplicitCallKind = PostImplicitCallKind,
LoopExitKind,
EpsilonKind};
private:
const void *Data1;
llvm::PointerIntPair<const void *, 2, unsigned> Data2;
// The LocationContext could be NULL to allow ProgramPoint to be used in
// context insensitive analysis.
llvm::PointerIntPair<const LocationContext *, 2, unsigned> L;
llvm::PointerIntPair<const ProgramPointTag *, 2, unsigned> Tag;
CFGBlock::ConstCFGElementRef ElemRef = {nullptr, 0};
protected:
ProgramPoint() = default;
ProgramPoint(const void *P, Kind k, const LocationContext *l,
const ProgramPointTag *tag = nullptr,
CFGBlock::ConstCFGElementRef ElemRef = {nullptr, 0})
: Data1(P), Data2(nullptr, (((unsigned)k) >> 0) & 0x3),
L(l, (((unsigned)k) >> 2) & 0x3), Tag(tag, (((unsigned)k) >> 4) & 0x3),
ElemRef(ElemRef) {
assert(getKind() == k);
assert(getLocationContext() == l);
assert(getData1() == P);
}
ProgramPoint(const void *P1, const void *P2, Kind k, const LocationContext *l,
const ProgramPointTag *tag = nullptr,
CFGBlock::ConstCFGElementRef ElemRef = {nullptr, 0})
: Data1(P1), Data2(P2, (((unsigned)k) >> 0) & 0x3),
L(l, (((unsigned)k) >> 2) & 0x3), Tag(tag, (((unsigned)k) >> 4) & 0x3),
ElemRef(ElemRef) {}
protected:
const void *getData1() const { return Data1; }
const void *getData2() const { return Data2.getPointer(); }
void setData2(const void *d) { Data2.setPointer(d); }
CFGBlock::ConstCFGElementRef getElementRef() const { return ElemRef; }
public:
/// Create a new ProgramPoint object that is the same as the original
/// except for using the specified tag value.
ProgramPoint withTag(const ProgramPointTag *tag) const {
return ProgramPoint(getData1(), getData2(), getKind(),
getLocationContext(), tag);
}
/// Convert to the specified ProgramPoint type, asserting that this
/// ProgramPoint is of the desired type.
template<typename T>
T castAs() const {
assert(T::isKind(*this));
T t;
ProgramPoint& PP = t;
PP = *this;
return t;
}
/// Convert to the specified ProgramPoint type, returning std::nullopt if this
/// ProgramPoint is not of the desired type.
template <typename T> std::optional<T> getAs() const {
if (!T::isKind(*this))
return std::nullopt;
T t;
ProgramPoint& PP = t;
PP = *this;
return t;
}
Kind getKind() const {
unsigned x = Tag.getInt();
x <<= 2;
x |= L.getInt();
x <<= 2;
x |= Data2.getInt();
return (Kind) x;
}
/// Is this a program point corresponding to purge/removal of dead
/// symbols and bindings.
bool isPurgeKind() {
Kind K = getKind();
return (K == PostStmtPurgeDeadSymbolsKind ||
K == PreStmtPurgeDeadSymbolsKind);
}
const ProgramPointTag *getTag() const { return Tag.getPointer(); }
const LocationContext *getLocationContext() const {
return L.getPointer();
}
const StackFrameContext *getStackFrame() const {
return getLocationContext()->getStackFrame();
}
// For use with DenseMap. This hash is probably slow.
unsigned getHashValue() const {
llvm::FoldingSetNodeID ID;
Profile(ID);
return ID.ComputeHash();
}
bool operator==(const ProgramPoint & RHS) const {
return Data1 == RHS.Data1 && Data2 == RHS.Data2 && L == RHS.L &&
Tag == RHS.Tag && ElemRef == RHS.ElemRef;
}
bool operator!=(const ProgramPoint &RHS) const {
return Data1 != RHS.Data1 || Data2 != RHS.Data2 || L != RHS.L ||
Tag != RHS.Tag || ElemRef != RHS.ElemRef;
}
void Profile(llvm::FoldingSetNodeID& ID) const {
ID.AddInteger((unsigned) getKind());
ID.AddPointer(getData1());
ID.AddPointer(getData2());
ID.AddPointer(getLocationContext());
ID.AddPointer(getTag());
ID.AddPointer(ElemRef.getParent());
ID.AddInteger(ElemRef.getIndexInBlock());
}
void printJson(llvm::raw_ostream &Out, const char *NL = "\n") const;
LLVM_DUMP_METHOD void dump() const;
static ProgramPoint getProgramPoint(const Stmt *S, ProgramPoint::Kind K,
const LocationContext *LC,
const ProgramPointTag *tag);
};
class BlockEntrance : public ProgramPoint {
public:
BlockEntrance(const CFGBlock *B, const LocationContext *L,
const ProgramPointTag *tag = nullptr)
: ProgramPoint(B, BlockEntranceKind, L, tag) {
assert(B && "BlockEntrance requires non-null block");
}
const CFGBlock *getBlock() const {
return reinterpret_cast<const CFGBlock*>(getData1());
}
std::optional<CFGElement> getFirstElement() const {
const CFGBlock *B = getBlock();
return B->empty() ? std::optional<CFGElement>() : B->front();
}
private:
friend class ProgramPoint;
BlockEntrance() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == BlockEntranceKind;
}
};
class BlockExit : public ProgramPoint {
public:
BlockExit(const CFGBlock *B, const LocationContext *L)
: ProgramPoint(B, BlockExitKind, L) {}
const CFGBlock *getBlock() const {
return reinterpret_cast<const CFGBlock*>(getData1());
}
const Stmt *getTerminator() const {
return getBlock()->getTerminatorStmt();
}
private:
friend class ProgramPoint;
BlockExit() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == BlockExitKind;
}
};
// FIXME: Eventually we want to take a CFGElementRef as parameter here too.
class StmtPoint : public ProgramPoint {
public:
StmtPoint(const Stmt *S, const void *p2, Kind k, const LocationContext *L,
const ProgramPointTag *tag)
: ProgramPoint(S, p2, k, L, tag) {
assert(S);
}
const Stmt *getStmt() const { return (const Stmt*) getData1(); }
template <typename T>
const T* getStmtAs() const { return dyn_cast<T>(getStmt()); }
protected:
StmtPoint() = default;
private:
friend class ProgramPoint;
static bool isKind(const ProgramPoint &Location) {
unsigned k = Location.getKind();
return k >= PreStmtKind && k <= MaxPostStmtKind;
}
};
class PreStmt : public StmtPoint {
public:
PreStmt(const Stmt *S, const LocationContext *L, const ProgramPointTag *tag,
const Stmt *SubStmt = nullptr)
: StmtPoint(S, SubStmt, PreStmtKind, L, tag) {}
const Stmt *getSubStmt() const { return (const Stmt*) getData2(); }
private:
friend class ProgramPoint;
PreStmt() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == PreStmtKind;
}
};
class PostStmt : public StmtPoint {
protected:
PostStmt() = default;
PostStmt(const Stmt *S, const void *data, Kind k, const LocationContext *L,
const ProgramPointTag *tag = nullptr)
: StmtPoint(S, data, k, L, tag) {}
public:
explicit PostStmt(const Stmt *S, Kind k, const LocationContext *L,
const ProgramPointTag *tag = nullptr)
: StmtPoint(S, nullptr, k, L, tag) {}
explicit PostStmt(const Stmt *S, const LocationContext *L,
const ProgramPointTag *tag = nullptr)
: StmtPoint(S, nullptr, PostStmtKind, L, tag) {}
private:
friend class ProgramPoint;
static bool isKind(const ProgramPoint &Location) {
unsigned k = Location.getKind();
return k >= MinPostStmtKind && k <= MaxPostStmtKind;
}
};
class FunctionExitPoint : public ProgramPoint {
public:
explicit FunctionExitPoint(const ReturnStmt *S,
const LocationContext *LC,
const ProgramPointTag *tag = nullptr)
: ProgramPoint(S, FunctionExitKind, LC, tag) {}
const CFGBlock *getBlock() const {
return &getLocationContext()->getCFG()->getExit();
}
const ReturnStmt *getStmt() const {
return reinterpret_cast<const ReturnStmt *>(getData1());
}
private:
friend class ProgramPoint;
FunctionExitPoint() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == FunctionExitKind;
}
};
// PostCondition represents the post program point of a branch condition.
class PostCondition : public PostStmt {
public:
PostCondition(const Stmt *S, const LocationContext *L,
const ProgramPointTag *tag = nullptr)
: PostStmt(S, PostConditionKind, L, tag) {}
private:
friend class ProgramPoint;
PostCondition() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == PostConditionKind;
}
};
class LocationCheck : public StmtPoint {
protected:
LocationCheck() = default;
LocationCheck(const Stmt *S, const LocationContext *L,
ProgramPoint::Kind K, const ProgramPointTag *tag)
: StmtPoint(S, nullptr, K, L, tag) {}
private:
friend class ProgramPoint;
static bool isKind(const ProgramPoint &location) {
unsigned k = location.getKind();
return k == PreLoadKind || k == PreStoreKind;
}
};
class PreLoad : public LocationCheck {
public:
PreLoad(const Stmt *S, const LocationContext *L,
const ProgramPointTag *tag = nullptr)
: LocationCheck(S, L, PreLoadKind, tag) {}
private:
friend class ProgramPoint;
PreLoad() = default;
static bool isKind(const ProgramPoint &location) {
return location.getKind() == PreLoadKind;
}
};
class PreStore : public LocationCheck {
public:
PreStore(const Stmt *S, const LocationContext *L,
const ProgramPointTag *tag = nullptr)
: LocationCheck(S, L, PreStoreKind, tag) {}
private:
friend class ProgramPoint;
PreStore() = default;
static bool isKind(const ProgramPoint &location) {
return location.getKind() == PreStoreKind;
}
};
class PostLoad : public PostStmt {
public:
PostLoad(const Stmt *S, const LocationContext *L,
const ProgramPointTag *tag = nullptr)
: PostStmt(S, PostLoadKind, L, tag) {}
private:
friend class ProgramPoint;
PostLoad() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == PostLoadKind;
}
};
/// Represents a program point after a store evaluation.
class PostStore : public PostStmt {
public:
/// Construct the post store point.
/// \param Loc can be used to store the information about the location
/// used in the form it was uttered in the code.
PostStore(const Stmt *S, const LocationContext *L, const void *Loc,
const ProgramPointTag *tag = nullptr)
: PostStmt(S, PostStoreKind, L, tag) {
assert(getData2() == nullptr);
setData2(Loc);
}
/// Returns the information about the location used in the store,
/// how it was uttered in the code.
const void *getLocationValue() const {
return getData2();
}
private:
friend class ProgramPoint;
PostStore() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == PostStoreKind;
}
};
class PostLValue : public PostStmt {
public:
PostLValue(const Stmt *S, const LocationContext *L,
const ProgramPointTag *tag = nullptr)
: PostStmt(S, PostLValueKind, L, tag) {}
private:
friend class ProgramPoint;
PostLValue() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == PostLValueKind;
}
};
/// Represents a point after we ran remove dead bindings BEFORE
/// processing the given statement.
class PreStmtPurgeDeadSymbols : public StmtPoint {
public:
PreStmtPurgeDeadSymbols(const Stmt *S, const LocationContext *L,
const ProgramPointTag *tag = nullptr)
: StmtPoint(S, nullptr, PreStmtPurgeDeadSymbolsKind, L, tag) { }
private:
friend class ProgramPoint;
PreStmtPurgeDeadSymbols() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == PreStmtPurgeDeadSymbolsKind;
}
};
/// Represents a point after we ran remove dead bindings AFTER
/// processing the given statement.
class PostStmtPurgeDeadSymbols : public StmtPoint {
public:
PostStmtPurgeDeadSymbols(const Stmt *S, const LocationContext *L,
const ProgramPointTag *tag = nullptr)
: StmtPoint(S, nullptr, PostStmtPurgeDeadSymbolsKind, L, tag) { }
private:
friend class ProgramPoint;
PostStmtPurgeDeadSymbols() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == PostStmtPurgeDeadSymbolsKind;
}
};
class BlockEdge : public ProgramPoint {
public:
BlockEdge(const CFGBlock *B1, const CFGBlock *B2, const LocationContext *L)
: ProgramPoint(B1, B2, BlockEdgeKind, L) {
assert(B1 && "BlockEdge: source block must be non-null");
assert(B2 && "BlockEdge: destination block must be non-null");
}
const CFGBlock *getSrc() const {
return static_cast<const CFGBlock*>(getData1());
}
const CFGBlock *getDst() const {
return static_cast<const CFGBlock*>(getData2());
}
private:
friend class ProgramPoint;
BlockEdge() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == BlockEdgeKind;
}
};
class PostInitializer : public ProgramPoint {
public:
/// Construct a PostInitializer point that represents a location after
/// CXXCtorInitializer expression evaluation.
///
/// \param I The initializer.
/// \param Loc The location of the field being initialized.
PostInitializer(const CXXCtorInitializer *I,
const void *Loc,
const LocationContext *L)
: ProgramPoint(I, Loc, PostInitializerKind, L) {}
const CXXCtorInitializer *getInitializer() const {
return static_cast<const CXXCtorInitializer *>(getData1());
}
/// Returns the location of the field.
const void *getLocationValue() const {
return getData2();
}
private:
friend class ProgramPoint;
PostInitializer() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == PostInitializerKind;
}
};
/// Represents an implicit call event.
///
/// The nearest statement is provided for diagnostic purposes.
class ImplicitCallPoint : public ProgramPoint {
public:
ImplicitCallPoint(const Decl *D, SourceLocation Loc, Kind K,
const LocationContext *L, const ProgramPointTag *Tag,
CFGBlock::ConstCFGElementRef ElemRef)
: ProgramPoint(Loc.getPtrEncoding(), D, K, L, Tag, ElemRef) {}
const Decl *getDecl() const { return static_cast<const Decl *>(getData2()); }
SourceLocation getLocation() const {
return SourceLocation::getFromPtrEncoding(getData1());
}
protected:
ImplicitCallPoint() = default;
private:
friend class ProgramPoint;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() >= MinImplicitCallKind &&
Location.getKind() <= MaxImplicitCallKind;
}
};
/// Represents a program point just before an implicit call event.
///
/// Explicit calls will appear as PreStmt program points.
class PreImplicitCall : public ImplicitCallPoint {
public:
PreImplicitCall(const Decl *D, SourceLocation Loc, const LocationContext *L,
CFGBlock::ConstCFGElementRef ElemRef,
const ProgramPointTag *Tag = nullptr)
: ImplicitCallPoint(D, Loc, PreImplicitCallKind, L, Tag, ElemRef) {}
private:
friend class ProgramPoint;
PreImplicitCall() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == PreImplicitCallKind;
}
};
/// Represents a program point just after an implicit call event.
///
/// Explicit calls will appear as PostStmt program points.
class PostImplicitCall : public ImplicitCallPoint {
public:
PostImplicitCall(const Decl *D, SourceLocation Loc, const LocationContext *L,
CFGBlock::ConstCFGElementRef ElemRef,
const ProgramPointTag *Tag = nullptr)
: ImplicitCallPoint(D, Loc, PostImplicitCallKind, L, Tag, ElemRef) {}
private:
friend class ProgramPoint;
PostImplicitCall() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == PostImplicitCallKind;
}
};
class PostAllocatorCall : public StmtPoint {
public:
PostAllocatorCall(const Stmt *S, const LocationContext *L,
const ProgramPointTag *Tag = nullptr)
: StmtPoint(S, nullptr, PostAllocatorCallKind, L, Tag) {}
private:
friend class ProgramPoint;
PostAllocatorCall() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == PostAllocatorCallKind;
}
};
/// Represents a point when we begin processing an inlined call.
/// CallEnter uses the caller's location context.
class CallEnter : public ProgramPoint {
public:
CallEnter(const Stmt *stmt, const StackFrameContext *calleeCtx,
const LocationContext *callerCtx)
: ProgramPoint(stmt, calleeCtx, CallEnterKind, callerCtx, nullptr) {}
const Stmt *getCallExpr() const {
return static_cast<const Stmt *>(getData1());
}
const StackFrameContext *getCalleeContext() const {
return static_cast<const StackFrameContext *>(getData2());
}
/// Returns the entry block in the CFG for the entered function.
const CFGBlock *getEntry() const {
const StackFrameContext *CalleeCtx = getCalleeContext();
const CFG *CalleeCFG = CalleeCtx->getCFG();
return &(CalleeCFG->getEntry());
}
private:
friend class ProgramPoint;
CallEnter() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == CallEnterKind;
}
};
/// Represents a point when we start the call exit sequence (for inlined call).
///
/// The call exit is simulated with a sequence of nodes, which occur between
/// CallExitBegin and CallExitEnd. The following operations occur between the
/// two program points:
/// - CallExitBegin
/// - Bind the return value
/// - Run Remove dead bindings (to clean up the dead symbols from the callee).
/// - CallExitEnd
class CallExitBegin : public ProgramPoint {
public:
// CallExitBegin uses the callee's location context.
CallExitBegin(const StackFrameContext *L, const ReturnStmt *RS)
: ProgramPoint(RS, CallExitBeginKind, L, nullptr) { }
const ReturnStmt *getReturnStmt() const {
return static_cast<const ReturnStmt *>(getData1());
}
private:
friend class ProgramPoint;
CallExitBegin() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == CallExitBeginKind;
}
};
/// Represents a point when we finish the call exit sequence (for inlined call).
/// \sa CallExitBegin
class CallExitEnd : public ProgramPoint {
public:
// CallExitEnd uses the caller's location context.
CallExitEnd(const StackFrameContext *CalleeCtx,
const LocationContext *CallerCtx)
: ProgramPoint(CalleeCtx, CallExitEndKind, CallerCtx, nullptr) {}
const StackFrameContext *getCalleeContext() const {
return static_cast<const StackFrameContext *>(getData1());
}
private:
friend class ProgramPoint;
CallExitEnd() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == CallExitEndKind;
}
};
/// Represents a point when we exit a loop.
/// When this ProgramPoint is encountered we can be sure that the symbolic
/// execution of the corresponding LoopStmt is finished on the given path.
/// Note: It is possible to encounter a LoopExit element when we haven't even
/// encountered the loop itself. At the current state not all loop exits will
/// result in a LoopExit program point.
class LoopExit : public ProgramPoint {
public:
LoopExit(const Stmt *LoopStmt, const LocationContext *LC)
: ProgramPoint(LoopStmt, nullptr, LoopExitKind, LC) {}
const Stmt *getLoopStmt() const {
return static_cast<const Stmt *>(getData1());
}
private:
friend class ProgramPoint;
LoopExit() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == LoopExitKind;
}
};
/// This is a meta program point, which should be skipped by all the diagnostic
/// reasoning etc.
class EpsilonPoint : public ProgramPoint {
public:
EpsilonPoint(const LocationContext *L, const void *Data1,
const void *Data2 = nullptr,
const ProgramPointTag *tag = nullptr)
: ProgramPoint(Data1, Data2, EpsilonKind, L, tag) {}
const void *getData() const { return getData1(); }
private:
friend class ProgramPoint;
EpsilonPoint() = default;
static bool isKind(const ProgramPoint &Location) {
return Location.getKind() == EpsilonKind;
}
};
} // end namespace clang
namespace llvm { // Traits specialization for DenseMap
template <> struct DenseMapInfo<clang::ProgramPoint> {
static inline clang::ProgramPoint getEmptyKey() {
uintptr_t x =
reinterpret_cast<uintptr_t>(DenseMapInfo<void*>::getEmptyKey()) & ~0x7;
return clang::BlockEntrance(reinterpret_cast<clang::CFGBlock*>(x), nullptr);
}
static inline clang::ProgramPoint getTombstoneKey() {
uintptr_t x =
reinterpret_cast<uintptr_t>(DenseMapInfo<void*>::getTombstoneKey()) & ~0x7;
return clang::BlockEntrance(reinterpret_cast<clang::CFGBlock*>(x), nullptr);
}
static unsigned getHashValue(const clang::ProgramPoint &Loc) {
return Loc.getHashValue();
}
static bool isEqual(const clang::ProgramPoint &L,
const clang::ProgramPoint &R) {
return L == R;
}
};
} // end namespace llvm
#endif