Index: clang/include/clang/Analysis/ProgramPoint.h =================================================================== --- clang/include/clang/Analysis/ProgramPoint.h +++ clang/include/clang/Analysis/ProgramPoint.h @@ -95,35 +95,33 @@ llvm::PointerIntPair Tag; + CFGBlock::ConstCFGElementRef ElemRef = {nullptr, 0}; + protected: ProgramPoint() = default; - ProgramPoint(const void *P, - Kind k, - const LocationContext *l, - const ProgramPointTag *tag = nullptr) - : Data1(P), - Data2(nullptr, (((unsigned) k) >> 0) & 0x3), - L(l, (((unsigned) k) >> 2) & 0x3), - Tag(tag, (((unsigned) k) >> 4) & 0x3) { - 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) - : Data1(P1), - Data2(P2, (((unsigned) k) >> 0) & 0x3), - L(l, (((unsigned) k) >> 2) & 0x3), - Tag(tag, (((unsigned) k) >> 4) & 0x3) {} + 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 @@ -190,17 +188,13 @@ } bool operator==(const ProgramPoint & RHS) const { - return Data1 == RHS.Data1 && - Data2 == RHS.Data2 && - L == RHS.L && - Tag == RHS.Tag; + 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; + return Data1 != RHS.Data1 || Data2 != RHS.Data2 || L != RHS.L || + Tag != RHS.Tag || ElemRef != RHS.ElemRef; } void Profile(llvm::FoldingSetNodeID& ID) const { @@ -209,6 +203,8 @@ 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; @@ -266,6 +262,7 @@ } }; +// 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, @@ -557,8 +554,9 @@ class ImplicitCallPoint : public ProgramPoint { public: ImplicitCallPoint(const Decl *D, SourceLocation Loc, Kind K, - const LocationContext *L, const ProgramPointTag *Tag) - : ProgramPoint(Loc.getPtrEncoding(), D, K, L, Tag) {} + const LocationContext *L, const ProgramPointTag *Tag, + CFGBlock::ConstCFGElementRef ElemRef) + : ProgramPoint(Loc.getPtrEncoding(), D, K, L, Tag, ElemRef) {} const Decl *getDecl() const { return static_cast(getData2()); } SourceLocation getLocation() const { @@ -581,8 +579,9 @@ 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) {} + : ImplicitCallPoint(D, Loc, PreImplicitCallKind, L, Tag, ElemRef) {} private: friend class ProgramPoint; @@ -598,8 +597,9 @@ 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) {} + : ImplicitCallPoint(D, Loc, PostImplicitCallKind, L, Tag, ElemRef) {} private: friend class ProgramPoint; Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -154,6 +154,7 @@ ProgramStateRef State; const LocationContext *LCtx; llvm::PointerUnion Origin; + CFGBlock::ConstCFGElementRef ElemRef = {nullptr, 0}; mutable std::optional Foreign; // Set by CTU analysis. protected: @@ -176,16 +177,19 @@ protected: friend class CallEventManager; - CallEvent(const Expr *E, ProgramStateRef state, const LocationContext *lctx) - : State(std::move(state)), LCtx(lctx), Origin(E) {} + CallEvent(const Expr *E, ProgramStateRef state, const LocationContext *lctx, + CFGBlock::ConstCFGElementRef ElemRef) + : State(std::move(state)), LCtx(lctx), Origin(E), ElemRef(ElemRef) {} - CallEvent(const Decl *D, ProgramStateRef state, const LocationContext *lctx) - : State(std::move(state)), LCtx(lctx), Origin(D) {} + CallEvent(const Decl *D, ProgramStateRef state, const LocationContext *lctx, + CFGBlock::ConstCFGElementRef ElemRef) + : State(std::move(state)), LCtx(lctx), Origin(D), ElemRef(ElemRef) {} // DO NOT MAKE PUBLIC CallEvent(const CallEvent &Original) : State(Original.State), LCtx(Original.LCtx), Origin(Original.Origin), - Data(Original.Data), Location(Original.Location) {} + ElemRef(Original.ElemRef), Data(Original.Data), + Location(Original.Location) {} /// Copies this CallEvent, with vtable intact, into a new block of memory. virtual void cloneTo(void *Dest) const = 0; @@ -232,6 +236,10 @@ return LCtx; } + const CFGBlock::ConstCFGElementRef &getCFGElementRef() const { + return ElemRef; + } + /// Returns the definition of the function or method that will be /// called. virtual RuntimeDefinition getRuntimeDefinition() const = 0; @@ -484,11 +492,13 @@ class AnyFunctionCall : public CallEvent { protected: AnyFunctionCall(const Expr *E, ProgramStateRef St, - const LocationContext *LCtx) - : CallEvent(E, St, LCtx) {} + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) + : CallEvent(E, St, LCtx, ElemRef) {} AnyFunctionCall(const Decl *D, ProgramStateRef St, - const LocationContext *LCtx) - : CallEvent(D, St, LCtx) {} + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) + : CallEvent(D, St, LCtx, ElemRef) {} AnyFunctionCall(const AnyFunctionCall &Other) = default; public: @@ -521,8 +531,9 @@ protected: SimpleFunctionCall(const CallExpr *CE, ProgramStateRef St, - const LocationContext *LCtx) - : AnyFunctionCall(CE, St, LCtx) {} + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) + : AnyFunctionCall(CE, St, LCtx, ElemRef) {} SimpleFunctionCall(const SimpleFunctionCall &Other) = default; void cloneTo(void *Dest) const override { @@ -557,9 +568,9 @@ friend class CallEventManager; protected: - BlockCall(const CallExpr *CE, ProgramStateRef St, - const LocationContext *LCtx) - : CallEvent(CE, St, LCtx) {} + BlockCall(const CallExpr *CE, ProgramStateRef St, const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) + : CallEvent(CE, St, LCtx, ElemRef) {} BlockCall(const BlockCall &Other) = default; void cloneTo(void *Dest) const override { new (Dest) BlockCall(*this); } @@ -661,11 +672,13 @@ class CXXInstanceCall : public AnyFunctionCall { protected: CXXInstanceCall(const CallExpr *CE, ProgramStateRef St, - const LocationContext *LCtx) - : AnyFunctionCall(CE, St, LCtx) {} + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) + : AnyFunctionCall(CE, St, LCtx, ElemRef) {} CXXInstanceCall(const FunctionDecl *D, ProgramStateRef St, - const LocationContext *LCtx) - : AnyFunctionCall(D, St, LCtx) {} + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) + : AnyFunctionCall(D, St, LCtx, ElemRef) {} CXXInstanceCall(const CXXInstanceCall &Other) = default; void getExtraInvalidatedValues(ValueList &Values, @@ -699,8 +712,9 @@ protected: CXXMemberCall(const CXXMemberCallExpr *CE, ProgramStateRef St, - const LocationContext *LCtx) - : CXXInstanceCall(CE, St, LCtx) {} + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) + : CXXInstanceCall(CE, St, LCtx, ElemRef) {} CXXMemberCall(const CXXMemberCall &Other) = default; void cloneTo(void *Dest) const override { new (Dest) CXXMemberCall(*this); } @@ -741,8 +755,9 @@ protected: CXXMemberOperatorCall(const CXXOperatorCallExpr *CE, ProgramStateRef St, - const LocationContext *LCtx) - : CXXInstanceCall(CE, St, LCtx) {} + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) + : CXXInstanceCall(CE, St, LCtx, ElemRef) {} CXXMemberOperatorCall(const CXXMemberOperatorCall &Other) = default; void cloneTo(void *Dest) const override { @@ -808,10 +823,17 @@ /// \param Target The object region to be destructed. /// \param St The path-sensitive state at this point in the program. /// \param LCtx The location context at this point in the program. + /// \param ElemRef The reference to this destructor in the CFG. + /// + /// FIXME: Eventually we want to drop \param Target and deduce it from + /// \param ElemRef. To do that we need to migrate the logic for target + /// region lookup from ExprEngine::ProcessImplicitDtor() and make it + /// independent from ExprEngine. CXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger, const MemRegion *Target, bool IsBaseDestructor, - ProgramStateRef St, const LocationContext *LCtx) - : CXXInstanceCall(DD, St, LCtx) { + ProgramStateRef St, const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) + : CXXInstanceCall(DD, St, LCtx, ElemRef) { Data = DtorDataTy(Target, IsBaseDestructor).getOpaqueValue(); Location = Trigger->getEndLoc(); } @@ -847,8 +869,9 @@ class AnyCXXConstructorCall : public AnyFunctionCall { protected: AnyCXXConstructorCall(const Expr *E, const MemRegion *Target, - ProgramStateRef St, const LocationContext *LCtx) - : AnyFunctionCall(E, St, LCtx) { + ProgramStateRef St, const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) + : AnyFunctionCall(E, St, LCtx, ElemRef) { assert(E && (isa(E) || isa(E))); // Target may be null when the region is unknown. Data = Target; @@ -884,9 +907,14 @@ /// a new symbolic region will be used. /// \param St The path-sensitive state at this point in the program. /// \param LCtx The location context at this point in the program. + /// \param ElemRef The reference to this constructor in the CFG. + /// + /// FIXME: Eventually we want to drop \param Target and deduce it from + /// \param ElemRef. CXXConstructorCall(const CXXConstructExpr *CE, const MemRegion *Target, - ProgramStateRef St, const LocationContext *LCtx) - : AnyCXXConstructorCall(CE, Target, St, LCtx) {} + ProgramStateRef St, const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) + : AnyCXXConstructorCall(CE, Target, St, LCtx, ElemRef) {} CXXConstructorCall(const CXXConstructorCall &Other) = default; @@ -941,8 +969,9 @@ protected: CXXInheritedConstructorCall(const CXXInheritedCtorInitExpr *CE, const MemRegion *Target, ProgramStateRef St, - const LocationContext *LCtx) - : AnyCXXConstructorCall(CE, Target, St, LCtx) {} + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) + : AnyCXXConstructorCall(CE, Target, St, LCtx, ElemRef) {} CXXInheritedConstructorCall(const CXXInheritedConstructorCall &Other) = default; @@ -1003,8 +1032,9 @@ protected: CXXAllocatorCall(const CXXNewExpr *E, ProgramStateRef St, - const LocationContext *LCtx) - : AnyFunctionCall(E, St, LCtx) {} + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) + : AnyFunctionCall(E, St, LCtx, ElemRef) {} CXXAllocatorCall(const CXXAllocatorCall &Other) = default; void cloneTo(void *Dest) const override { new (Dest) CXXAllocatorCall(*this); } @@ -1084,8 +1114,9 @@ protected: CXXDeallocatorCall(const CXXDeleteExpr *E, ProgramStateRef St, - const LocationContext *LCtx) - : AnyFunctionCall(E, St, LCtx) {} + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) + : AnyFunctionCall(E, St, LCtx, ElemRef) {} CXXDeallocatorCall(const CXXDeallocatorCall &Other) = default; void cloneTo(void *Dest) const override { @@ -1136,8 +1167,9 @@ protected: ObjCMethodCall(const ObjCMessageExpr *Msg, ProgramStateRef St, - const LocationContext *LCtx) - : CallEvent(Msg, St, LCtx) { + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) + : CallEvent(Msg, St, LCtx, ElemRef) { Data = nullptr; } @@ -1265,34 +1297,36 @@ } template - T *create(Arg A, ProgramStateRef St, const LocationContext *LCtx) { + T *create(Arg A, ProgramStateRef St, const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) { static_assert(sizeof(T) == sizeof(CallEventTemplateTy), "CallEvent subclasses are not all the same size"); - return new (allocate()) T(A, St, LCtx); + return new (allocate()) T(A, St, LCtx, ElemRef); } template - T *create(Arg1 A1, Arg2 A2, ProgramStateRef St, const LocationContext *LCtx) { + T *create(Arg1 A1, Arg2 A2, ProgramStateRef St, const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) { static_assert(sizeof(T) == sizeof(CallEventTemplateTy), "CallEvent subclasses are not all the same size"); - return new (allocate()) T(A1, A2, St, LCtx); + return new (allocate()) T(A1, A2, St, LCtx, ElemRef); } template T *create(Arg1 A1, Arg2 A2, Arg3 A3, ProgramStateRef St, - const LocationContext *LCtx) { + const LocationContext *LCtx, CFGBlock::ConstCFGElementRef ElemRef) { static_assert(sizeof(T) == sizeof(CallEventTemplateTy), "CallEvent subclasses are not all the same size"); - return new (allocate()) T(A1, A2, A3, St, LCtx); + return new (allocate()) T(A1, A2, A3, St, LCtx, ElemRef); } template T *create(Arg1 A1, Arg2 A2, Arg3 A3, Arg4 A4, ProgramStateRef St, - const LocationContext *LCtx) { + const LocationContext *LCtx, CFGBlock::ConstCFGElementRef ElemRef) { static_assert(sizeof(T) == sizeof(CallEventTemplateTy), "CallEvent subclasses are not all the same size"); - return new (allocate()) T(A1, A2, A3, A4, St, LCtx); + return new (allocate()) T(A1, A2, A3, A4, St, LCtx, ElemRef); } public: @@ -1304,50 +1338,57 @@ /// Gets a call event for a function call, Objective-C method call, /// a 'new', or a 'delete' call. - CallEventRef<> - getCall(const Stmt *S, ProgramStateRef State, - const LocationContext *LC); + CallEventRef<> getCall(const Stmt *S, ProgramStateRef State, + const LocationContext *LC, + CFGBlock::ConstCFGElementRef ElemRef); - CallEventRef<> - getSimpleCall(const CallExpr *E, ProgramStateRef State, - const LocationContext *LCtx); + CallEventRef<> getSimpleCall(const CallExpr *E, ProgramStateRef State, + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef); CallEventRef getObjCMethodCall(const ObjCMessageExpr *E, ProgramStateRef State, - const LocationContext *LCtx) { - return create(E, State, LCtx); + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) { + return create(E, State, LCtx, ElemRef); } CallEventRef getCXXConstructorCall(const CXXConstructExpr *E, const MemRegion *Target, - ProgramStateRef State, const LocationContext *LCtx) { - return create(E, Target, State, LCtx); + ProgramStateRef State, const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) { + return create(E, Target, State, LCtx, ElemRef); } CallEventRef getCXXInheritedConstructorCall(const CXXInheritedCtorInitExpr *E, const MemRegion *Target, ProgramStateRef State, - const LocationContext *LCtx) { - return create(E, Target, State, LCtx); + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) { + return create(E, Target, State, LCtx, ElemRef); } CallEventRef getCXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger, const MemRegion *Target, bool IsBase, - ProgramStateRef State, const LocationContext *LCtx) { - return create(DD, Trigger, Target, IsBase, State, LCtx); + ProgramStateRef State, const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) { + return create(DD, Trigger, Target, IsBase, State, LCtx, + ElemRef); } CallEventRef getCXXAllocatorCall(const CXXNewExpr *E, ProgramStateRef State, - const LocationContext *LCtx) { - return create(E, State, LCtx); + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) { + return create(E, State, LCtx, ElemRef); } CallEventRef getCXXDeallocatorCall(const CXXDeleteExpr *E, ProgramStateRef State, - const LocationContext *LCtx) { - return create(E, State, LCtx); + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) { + return create(E, State, LCtx, ElemRef); } }; Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -234,6 +234,11 @@ return (*G.roots_begin())->getLocation().getLocationContext(); } + CFGBlock::ConstCFGElementRef getCFGElementRef() const { + const CFGBlock *blockPtr = currBldrCtx ? currBldrCtx->getBlock() : nullptr; + return {blockPtr, currStmtIdx}; + } + void GenerateAutoTransition(ExplodedNode *N); void enqueueEndOfPath(ExplodedNodeSet &S); void GenerateCallExitNode(ExplodedNode *N); Index: clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -3446,7 +3446,8 @@ OS << OpCallE->getDirectCallee()->getDeclName(); } else if (const auto *CallE = dyn_cast(S)) { auto &CEMgr = BRC.getStateManager().getCallEventManager(); - CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC); + CallEventRef<> Call = + CEMgr.getSimpleCall(CallE, state, CurrentLC, {nullptr, 0}); if (const auto *D = dyn_cast_or_null(Call->getDecl())) OS << D->getDeclName(); else Index: clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -234,8 +234,8 @@ os << "Operator 'new'"; } else { assert(isa(S)); - CallEventRef Call = - Mgr.getObjCMethodCall(cast(S), CurrSt, LCtx); + CallEventRef Call = Mgr.getObjCMethodCall( + cast(S), CurrSt, LCtx, {nullptr, 0}); switch (Call->getMessageKind()) { case OCM_Message: @@ -250,7 +250,7 @@ } } - std::optional> CE = Mgr.getCall(S, CurrSt, LCtx); + std::optional> CE = Mgr.getCall(S, CurrSt, LCtx, {nullptr, 0}); auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE); // If index is not found, we assume that the symbol was returned. Index: clang/lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -287,6 +287,7 @@ ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit, const ProgramPointTag *Tag) const { + if (const Expr *E = getOriginExpr()) { if (IsPreVisit) return PreStmt(E, getLocationContext(), Tag); @@ -295,11 +296,13 @@ const Decl *D = getDecl(); assert(D && "Cannot get a program point without a statement or decl"); + assert(ElemRef.getParent() && + "Cannot get a program point without a CFGElementRef"); SourceLocation Loc = getSourceRange().getBegin(); if (IsPreVisit) - return PreImplicitCall(D, Loc, getLocationContext(), Tag); - return PostImplicitCall(D, Loc, getLocationContext(), Tag); + return PreImplicitCall(D, Loc, getLocationContext(), ElemRef, Tag); + return PostImplicitCall(D, Loc, getLocationContext(), ElemRef, Tag); } SVal CallEvent::getArgSVal(unsigned Index) const { @@ -1373,23 +1376,24 @@ CallEventRef<> CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State, - const LocationContext *LCtx) { + const LocationContext *LCtx, + CFGBlock::ConstCFGElementRef ElemRef) { if (const auto *MCE = dyn_cast(CE)) - return create(MCE, State, LCtx); + return create(MCE, State, LCtx, ElemRef); if (const auto *OpCE = dyn_cast(CE)) { const FunctionDecl *DirectCallee = OpCE->getDirectCallee(); if (const auto *MD = dyn_cast(DirectCallee)) if (MD->isInstance()) - return create(OpCE, State, LCtx); + return create(OpCE, State, LCtx, ElemRef); } else if (CE->getCallee()->getType()->isBlockPointerType()) { - return create(CE, State, LCtx); + return create(CE, State, LCtx, ElemRef); } // Otherwise, it's a normal function call, static member function call, or // something we can't reason about. - return create(CE, State, LCtx); + return create(CE, State, LCtx, ElemRef); } CallEventRef<> @@ -1397,12 +1401,14 @@ ProgramStateRef State) { const LocationContext *ParentCtx = CalleeCtx->getParent(); const LocationContext *CallerCtx = ParentCtx->getStackFrame(); + CFGBlock::ConstCFGElementRef ElemRef = {CalleeCtx->getCallSiteBlock(), + CalleeCtx->getIndex()}; assert(CallerCtx && "This should not be used for top-level stack frames"); const Stmt *CallSite = CalleeCtx->getCallSite(); if (CallSite) { - if (CallEventRef<> Out = getCall(CallSite, State, CallerCtx)) + if (CallEventRef<> Out = getCall(CallSite, State, CallerCtx, ElemRef)) return Out; SValBuilder &SVB = State->getStateManager().getSValBuilder(); @@ -1411,10 +1417,11 @@ SVal ThisVal = State->getSVal(ThisPtr); if (const auto *CE = dyn_cast(CallSite)) - return getCXXConstructorCall(CE, ThisVal.getAsRegion(), State, CallerCtx); + return getCXXConstructorCall(CE, ThisVal.getAsRegion(), State, CallerCtx, + ElemRef); else if (const auto *CIE = dyn_cast(CallSite)) return getCXXInheritedConstructorCall(CIE, ThisVal.getAsRegion(), State, - CallerCtx); + CallerCtx, ElemRef); else { // All other cases are handled by getCall. llvm_unreachable("This is not an inlineable statement"); @@ -1444,19 +1451,20 @@ return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(), E.getAs().has_value(), State, - CallerCtx); + CallerCtx, ElemRef); } CallEventRef<> CallEventManager::getCall(const Stmt *S, ProgramStateRef State, - const LocationContext *LC) { + const LocationContext *LC, + CFGBlock::ConstCFGElementRef ElemRef) { if (const auto *CE = dyn_cast(S)) { - return getSimpleCall(CE, State, LC); + return getSimpleCall(CE, State, LC, ElemRef); } else if (const auto *NE = dyn_cast(S)) { - return getCXXAllocatorCall(NE, State, LC); + return getCXXAllocatorCall(NE, State, LC, ElemRef); } else if (const auto *DE = dyn_cast(S)) { - return getCXXDeallocatorCall(DE, State, LC); + return getCXXDeallocatorCall(DE, State, LC, ElemRef); } else if (const auto *ME = dyn_cast(S)) { - return getObjCMethodCall(ME, State, LC); + return getObjCMethodCall(ME, State, LC, ElemRef); } else { return nullptr; } Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1313,7 +1313,8 @@ else { NodeBuilder Bldr(Pred, Dst, *currBldrCtx); const LocationContext *LCtx = Pred->getLocationContext(); - PostImplicitCall PP(NE->getOperatorNew(), NE->getBeginLoc(), LCtx); + PostImplicitCall PP(NE->getOperatorNew(), NE->getBeginLoc(), LCtx, + getCFGElementRef()); Bldr.generateNode(PP, Pred->getState(), Pred); } Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); @@ -1361,7 +1362,8 @@ static SimpleProgramPointTag PT( "ExprEngine", "Skipping automatic 0 length array destruction, " "which shouldn't be in the CFG."); - PostImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, &PT); + PostImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, + getCFGElementRef(), &PT); NodeBuilder Bldr(Pred, Dst, *currBldrCtx); Bldr.generateSink(PP, Pred->getState(), Pred); return; @@ -1378,7 +1380,8 @@ static SimpleProgramPointTag PT("ExprEngine", "Prepare for object destruction"); - PreImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, &PT); + PreImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, getCFGElementRef(), + &PT); Pred = Bldr.generateNode(PP, state, Pred); if (!Pred) @@ -1406,7 +1409,7 @@ const CXXRecordDecl *RD = BTy->getAsCXXRecordDecl(); const CXXDestructorDecl *Dtor = RD->getDestructor(); - PostImplicitCall PP(Dtor, DE->getBeginLoc(), LCtx); + PostImplicitCall PP(Dtor, DE->getBeginLoc(), LCtx, getCFGElementRef()); NodeBuilder Bldr(Pred, Dst, *currBldrCtx); Bldr.generateNode(PP, Pred->getState(), Pred); return; @@ -1439,7 +1442,8 @@ static SimpleProgramPointTag PT( "ExprEngine", "Skipping 0 length array delete destruction"); - PostImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, &PT); + PostImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, + getCFGElementRef(), &PT); NodeBuilder Bldr(Pred, Dst, *currBldrCtx); Bldr.generateNode(PP, Pred->getState(), Pred); return; @@ -1453,7 +1457,8 @@ NodeBuilder Bldr(Pred, Dst, getBuilderContext()); static SimpleProgramPointTag PT("ExprEngine", "Prepare for object destruction"); - PreImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, &PT); + PreImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, + getCFGElementRef(), &PT); Pred = Bldr.generateNode(PP, State, Pred); if (!Pred) @@ -1513,7 +1518,8 @@ static SimpleProgramPointTag PT( "ExprEngine", "Skipping member 0 length array destruction, which " "shouldn't be in the CFG."); - PostImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, &PT); + PostImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, + getCFGElementRef(), &PT); NodeBuilder Bldr(Pred, Dst, *currBldrCtx); Bldr.generateSink(PP, Pred->getState(), Pred); return; @@ -1529,7 +1535,8 @@ static SimpleProgramPointTag PT("ExprEngine", "Prepare for object destruction"); - PreImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, &PT); + PreImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, getCFGElementRef(), + &PT); Pred = Bldr.generateNode(PP, State, Pred); if (!Pred) @@ -1565,7 +1572,7 @@ NodeBuilder Bldr(Pred, Dst, *currBldrCtx); PostImplicitCall PP(D.getDestructorDecl(getContext()), D.getBindTemporaryExpr()->getBeginLoc(), - Pred->getLocationContext()); + Pred->getLocationContext(), getCFGElementRef()); Bldr.generateNode(PP, State, Pred); return; } Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -357,7 +357,8 @@ }; if (const auto *CE = dyn_cast(E)) { - CallEventRef<> Caller = CEMgr.getSimpleCall(CE, State, LCtx); + CallEventRef<> Caller = + CEMgr.getSimpleCall(CE, State, LCtx, getCFGElementRef()); if (std::optional V = getArgLoc(Caller)) return *V; else @@ -365,14 +366,15 @@ } else if (const auto *CCE = dyn_cast(E)) { // Don't bother figuring out the target region for the future // constructor because we won't need it. - CallEventRef<> Caller = - CEMgr.getCXXConstructorCall(CCE, /*Target=*/nullptr, State, LCtx); + CallEventRef<> Caller = CEMgr.getCXXConstructorCall( + CCE, /*Target=*/nullptr, State, LCtx, getCFGElementRef()); if (std::optional V = getArgLoc(Caller)) return *V; else break; } else if (const auto *ME = dyn_cast(E)) { - CallEventRef<> Caller = CEMgr.getObjCMethodCall(ME, State, LCtx); + CallEventRef<> Caller = + CEMgr.getObjCMethodCall(ME, State, LCtx, getCFGElementRef()); if (std::optional V = getArgLoc(Caller)) return *V; else @@ -726,9 +728,9 @@ CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<> Call = CIE ? (CallEventRef<>)CEMgr.getCXXInheritedConstructorCall( - CIE, TargetRegion, State, LCtx) + CIE, TargetRegion, State, LCtx, getCFGElementRef()) : (CallEventRef<>)CEMgr.getCXXConstructorCall( - CE, TargetRegion, State, LCtx); + CE, TargetRegion, State, LCtx, getCFGElementRef()); ExplodedNodeSet DstPreVisit; getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, E, *this); @@ -869,7 +871,8 @@ // it would interrupt the analysis instead. static SimpleProgramPointTag T("ExprEngine", "SkipInvalidDestructor"); // FIXME: PostImplicitCall with a null decl may crash elsewhere anyway. - PostImplicitCall PP(/*Decl=*/nullptr, S->getEndLoc(), LCtx, &T); + PostImplicitCall PP(/*Decl=*/nullptr, S->getEndLoc(), LCtx, + getCFGElementRef(), &T); NodeBuilder Bldr(Pred, Dst, *currBldrCtx); Bldr.generateNode(PP, Pred->getState(), Pred); return; @@ -894,8 +897,8 @@ } CallEventManager &CEMgr = getStateManager().getCallEventManager(); - CallEventRef Call = - CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx); + CallEventRef Call = CEMgr.getCXXDestructorCall( + DtorDecl, S, Dest, IsBaseDtor, State, LCtx, getCFGElementRef()); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), Call->getSourceRange().getBegin(), @@ -925,7 +928,7 @@ "Error evaluating New Allocator Call"); CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef Call = - CEMgr.getCXXAllocatorCall(CNE, State, LCtx); + CEMgr.getCXXAllocatorCall(CNE, State, LCtx, getCFGElementRef()); ExplodedNodeSet DstPreCall; getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, @@ -1023,7 +1026,7 @@ CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef Call = - CEMgr.getCXXAllocatorCall(CNE, State, LCtx); + CEMgr.getCXXAllocatorCall(CNE, State, LCtx, getCFGElementRef()); if (!AMgr.getAnalyzerOptions().MayInlineCXXAllocator) { // Invalidate placement args. @@ -1124,7 +1127,7 @@ CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef Call = CEMgr.getCXXDeallocatorCall( - CDE, Pred->getState(), Pred->getLocationContext()); + CDE, Pred->getState(), Pred->getLocationContext(), getCFGElementRef()); ExplodedNodeSet DstPreCall; getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, *Call, *this); Index: clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -610,8 +610,8 @@ // Get the call in its initial state. We use this as a template to perform // all the checks. CallEventManager &CEMgr = getStateManager().getCallEventManager(); - CallEventRef<> CallTemplate - = CEMgr.getSimpleCall(CE, Pred->getState(), Pred->getLocationContext()); + CallEventRef<> CallTemplate = CEMgr.getSimpleCall( + CE, Pred->getState(), Pred->getLocationContext(), getCFGElementRef()); // Evaluate the function call. We try each of the checkers // to see if the can evaluate the function call. @@ -837,7 +837,8 @@ State = bindReturnValue(Call, Pred->getLocationContext(), State); // And make the result node. - Bldr.generateNode(Call.getProgramPoint(), State, Pred); + static SimpleProgramPointTag PT("ExprEngine", "Conservative eval call"); + Bldr.generateNode(Call.getProgramPoint(false, &PT), State, Pred); } ExprEngine::CallInlinePolicy Index: clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -148,8 +148,8 @@ ExplodedNode *Pred, ExplodedNodeSet &Dst) { CallEventManager &CEMgr = getStateManager().getCallEventManager(); - CallEventRef Msg = - CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext()); + CallEventRef Msg = CEMgr.getObjCMethodCall( + ME, Pred->getState(), Pred->getLocationContext(), getCFGElementRef()); // There are three cases for the receiver: // (1) it is definitely nil, Index: clang/test/Analysis/PR60412.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/PR60412.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.deadcode.UnreachableCode -verify %s +// expected-no-diagnostics + +struct Test { + Test() {} + ~Test(); +}; + +int foo() { + struct a { + Test b, c; + } d; + return 1; +} + +int main() { + if (foo()) return 1; // <- this used to warn as unreachable +} Index: clang/test/Analysis/analysis-after-multiple-dtors.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/analysis-after-multiple-dtors.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s + +#include "Inputs/system-header-simulator-cxx.h" + +struct Test { + Test() {} + ~Test(); +}; + +int foo() { + struct a { + // The dtor invocation of 'b' and 'c' used to create + // a loop in the egraph and the analysis stopped after + // this point. + Test b, c; + } d; + return 1; +} + +int main() { + if (foo()) { + } + + int x; + int y = x; + // expected-warning@-1{{Assigned value is garbage or undefined}} + (void)y; +} Index: clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp =================================================================== --- clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp +++ clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp @@ -89,11 +89,14 @@ CallEventManager &CEMgr = Eng.getStateManager().getCallEventManager(); CallEventRef<> Call = [=, &CEMgr]() -> CallEventRef { + CFGBlock::ConstCFGElementRef ElemRef = {SFC->getCallSiteBlock(), + SFC->getIndex()}; if (std::is_base_of::value) - return CEMgr.getCall(E, State, SFC); + return CEMgr.getCall(E, State, SFC, ElemRef); if (std::is_same::value) return CEMgr.getCXXConstructorCall(cast(E), - /*Target=*/nullptr, State, SFC); + /*Target=*/nullptr, State, SFC, + ElemRef); llvm_unreachable("Only these expressions are supported for now."); }();