From: isuckatcs <65320245+isuckatcs@users.noreply.github.com> Date: Fri, 10 Feb 2023 17:07:31 +0000 (+0100) Subject: [analyzer] Remove the loop from the exploded graph caused by missing information... X-Git-Tag: upstream/17.0.6~15870 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=d65379c8d4768ddae143672f5d4827acafe22553;p=platform%2Fupstream%2Fllvm.git [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 --- diff --git a/clang/include/clang/Analysis/ProgramPoint.h b/clang/include/clang/Analysis/ProgramPoint.h index 6dba0582c8dd..b9339570e1ae 100644 --- a/clang/include/clang/Analysis/ProgramPoint.h +++ b/clang/include/clang/Analysis/ProgramPoint.h @@ -95,35 +95,33 @@ private: 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 @@ public: } 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 @@ public: 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 @@ private: } }; +// 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 @@ private: 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 @@ private: 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 @@ private: 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; diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index 710bc8c33849..ffd6af528b8f 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -154,6 +154,7 @@ private: 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 @@ private: 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 @@ public: 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 @@ public: 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 @@ class SimpleFunctionCall : public AnyFunctionCall { 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 @@ class BlockCall : public CallEvent { 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 @@ public: 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 @@ class CXXMemberCall : public CXXInstanceCall { 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 @@ class CXXMemberOperatorCall : public CXXInstanceCall { 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 @@ protected: /// \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 @@ public: 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 @@ protected: /// 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 @@ class CXXInheritedConstructorCall : public AnyCXXConstructorCall { 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 @@ class CXXAllocatorCall : public AnyFunctionCall { 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 @@ class CXXDeallocatorCall : public AnyFunctionCall { 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 @@ class ObjCMethodCall : public CallEvent { 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 @@ class CallEventManager { } 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 @@ public: /// 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); } }; diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index d73661545535..8c3d9120788d 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -234,6 +234,11 @@ public: 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); diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index f05cd9227b65..67e8afbb432a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -3446,7 +3446,8 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N, 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 diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index e11e509f159d..379163e12787 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -234,8 +234,8 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, 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 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, } } - 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. diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index 8516e3643425..195940e5e643 100644 --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -287,6 +287,7 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount, 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 @@ ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit, 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 @@ void ObjCMethodCall::getInitialStackFrameContents( 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 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx, 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 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx, 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 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx, 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; } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 977c2b7f51fd..bd5781a81bb5 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1313,7 +1313,8 @@ void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE, 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 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, 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 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, 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 @@ void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, 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 @@ void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, 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 @@ void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, 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 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, 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 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, 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 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, NodeBuilder Bldr(Pred, Dst, *currBldrCtx); PostImplicitCall PP(D.getDestructorDecl(getContext()), D.getBindTemporaryExpr()->getBeginLoc(), - Pred->getLocationContext()); + Pred->getLocationContext(), getCFGElementRef()); Bldr.generateNode(PP, State, Pred); return; } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 6eb37287b136..e87639713991 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -357,7 +357,8 @@ SVal ExprEngine::computeObjectUnderConstruction( }; 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 @@ SVal ExprEngine::computeObjectUnderConstruction( } 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 @@ void ExprEngine::handleConstructor(const Expr *E, 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 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, // 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 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, } 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 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, "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 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, 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 @@ void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, 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); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 54528475cb31..ac8b5003df19 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -610,8 +610,8 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, // 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 @@ void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, 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 diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index 25c36e9aea24..8072531ef6fd 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -148,8 +148,8 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, 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, diff --git a/clang/test/Analysis/PR60412.cpp b/clang/test/Analysis/PR60412.cpp new file mode 100644 index 000000000000..da56c68cafc5 --- /dev/null +++ b/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 +} diff --git a/clang/test/Analysis/analysis-after-multiple-dtors.cpp b/clang/test/Analysis/analysis-after-multiple-dtors.cpp new file mode 100644 index 000000000000..a8f6d38fbd1b --- /dev/null +++ b/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; +} diff --git a/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp b/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp index 14fb03545265..e8b237b891ca 100644 --- a/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp +++ b/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp @@ -89,11 +89,14 @@ class CallDescriptionConsumer : public ExprEngineConsumer { 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."); }();