Index: clang/include/clang/Analysis/ProgramPoint.h =================================================================== --- clang/include/clang/Analysis/ProgramPoint.h +++ clang/include/clang/Analysis/ProgramPoint.h @@ -16,6 +16,7 @@ #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/Optional.h" @@ -57,37 +58,42 @@ 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}; + 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, + PreDestructorCallKind, + PostDestructorCallKind, + MinImplicitCallKind = PreImplicitCallKind, + MaxImplicitCallKind = PostDestructorCallKind, + LoopExitKind, + EpsilonKind + }; private: const void *Data1; llvm::PointerIntPair Data2; + const void *Data3; // The LocationContext could be NULL to allow ProgramPoint to be used in // context insensitive analysis. @@ -97,33 +103,27 @@ protected: ProgramPoint() = default; - ProgramPoint(const void *P, - Kind k, - const LocationContext *l, + 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, + : Data1(P), Data2(nullptr, (((unsigned)k) >> 0) & 0x3), Data3(nullptr), + 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) {} + : Data1(P1), Data2(P2, (((unsigned)k) >> 0) & 0x3), Data3(nullptr), + L(l, (((unsigned)k) >> 2) & 0x3), Tag(tag, (((unsigned)k) >> 4) & 0x3) { + } protected: const void *getData1() const { return Data1; } const void *getData2() const { return Data2.getPointer(); } + const void *getData3() const { return Data3; } void setData2(const void *d) { Data2.setPointer(d); } + void setData3(const void *d) { Data3 = d; } public: /// Create a new ProgramPoint object that is the same as the original @@ -208,6 +208,7 @@ ID.AddInteger((unsigned) getKind()); ID.AddPointer(getData1()); ID.AddPointer(getData2()); + ID.AddPointer(getData3()); ID.AddPointer(getLocationContext()); ID.AddPointer(getTag()); } @@ -585,14 +586,38 @@ const ProgramPointTag *Tag = nullptr) : ImplicitCallPoint(D, Loc, PreImplicitCallKind, L, Tag) {} +protected: + PreImplicitCall(Kind K, const Decl *D, SourceLocation Loc, + const LocationContext *L, + const ProgramPointTag *Tag = nullptr) + : ImplicitCallPoint(D, Loc, K, L, Tag) {} + + PreImplicitCall() = default; + private: friend class ProgramPoint; - PreImplicitCall() = default; static bool isKind(const ProgramPoint &Location) { return Location.getKind() == PreImplicitCallKind; } }; +class PreDestructorCall : public PreImplicitCall { +public: + PreDestructorCall(const Decl *D, const ento::MemRegion *TargetRegion, + SourceLocation Loc, const LocationContext *L, + const ProgramPointTag *Tag = nullptr) + : PreImplicitCall(PreDestructorCallKind, D, Loc, L, Tag) { + setData3(TargetRegion); + } + +private: + friend class ProgramPoint; + PreDestructorCall() = default; + static bool isKind(const ProgramPoint &Location) { + return Location.getKind() == PreDestructorCallKind; + } +}; + /// Represents a program point just after an implicit call event. /// /// Explicit calls will appear as PostStmt program points. @@ -602,14 +627,38 @@ const ProgramPointTag *Tag = nullptr) : ImplicitCallPoint(D, Loc, PostImplicitCallKind, L, Tag) {} +protected: + PostImplicitCall(Kind K, const Decl *D, SourceLocation Loc, + const LocationContext *L, + const ProgramPointTag *Tag = nullptr) + : ImplicitCallPoint(D, Loc, K, L, Tag) {} + + PostImplicitCall() = default; + private: friend class ProgramPoint; - PostImplicitCall() = default; static bool isKind(const ProgramPoint &Location) { return Location.getKind() == PostImplicitCallKind; } }; +class PostDestructorCall : public PostImplicitCall { +public: + PostDestructorCall(const Decl *D, const ento::MemRegion *TargetRegion, + SourceLocation Loc, const LocationContext *L, + const ProgramPointTag *Tag = nullptr) + : PostImplicitCall(PostDestructorCallKind, D, Loc, L, Tag) { + setData3(TargetRegion); + } + +private: + friend class ProgramPoint; + PostDestructorCall() = default; + static bool isKind(const ProgramPoint &Location) { + return Location.getKind() == PostDestructorCallKind; + } +}; + class PostAllocatorCall : public StmtPoint { public: PostAllocatorCall(const Stmt *S, const LocationContext *L, Index: clang/lib/Analysis/PathDiagnostic.cpp =================================================================== --- clang/lib/Analysis/PathDiagnostic.cpp +++ clang/lib/Analysis/PathDiagnostic.cpp @@ -689,8 +689,12 @@ SMng); } else if (Optional PIC = P.getAs()) { return PathDiagnosticLocation(PIC->getLocation(), SMng); + } else if (Optional PDC = P.getAs()) { + return PathDiagnosticLocation(PDC->getLocation(), SMng); } else if (Optional PIE = P.getAs()) { return PathDiagnosticLocation(PIE->getLocation(), SMng); + } else if (Optional PDC = P.getAs()) { + return PathDiagnosticLocation(PDC->getLocation(), SMng); } else if (Optional CE = P.getAs()) { return getLocationForCaller(CE->getCalleeContext(), CE->getLocationContext(), Index: clang/lib/Analysis/ProgramPoint.cpp =================================================================== --- clang/lib/Analysis/ProgramPoint.cpp +++ clang/lib/Analysis/ProgramPoint.cpp @@ -107,6 +107,15 @@ break; } + case ProgramPoint::PreDestructorCallKind: { + ImplicitCallPoint PC = castAs(); + Out << "PreDestructorCall\", \"decl\": \"" + << PC.getDecl()->getAsFunction()->getQualifiedNameAsString() + << "\", \"location\": "; + printSourceLocationAsJson(Out, PC.getLocation(), SM); + break; + } + case ProgramPoint::PostImplicitCallKind: { ImplicitCallPoint PC = castAs(); Out << "PostCall\", \"decl\": \"" @@ -116,6 +125,15 @@ break; } + case ProgramPoint::PostDestructorCallKind: { + ImplicitCallPoint PC = castAs(); + Out << "PostDestructorCall\", \"decl\": \"" + << PC.getDecl()->getAsFunction()->getQualifiedNameAsString() + << "\", \"location\": "; + printSourceLocationAsJson(Out, PC.getLocation(), SM); + break; + } + case ProgramPoint::PostInitializerKind: { Out << "PostInitializer\", "; const CXXCtorInitializer *Init = castAs().getInitializer(); Index: clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -3431,28 +3431,31 @@ QualType ObjTy = TypedRegion->getValueType(); OS << "Inner buffer of '" << ObjTy << "' "; - if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) { - OS << "deallocated by call to destructor"; - StackHint = std::make_unique( - Sym, "Returning; inner buffer was deallocated"); + if (N->getLocation().getKind() == + ProgramPoint::PostImplicitCallKind || + N->getLocation().getKind() == + clang::ProgramPoint::PostDestructorCallKind) { + OS << "deallocated by call to destructor"; + StackHint = std::make_unique( + Sym, "Returning; inner buffer was deallocated"); } else { - OS << "reallocated by call to '"; - const Stmt *S = RSCurr->getStmt(); - if (const auto *MemCallE = dyn_cast(S)) { - OS << MemCallE->getMethodDecl()->getDeclName(); - } else if (const auto *OpCallE = dyn_cast(S)) { - OS << OpCallE->getDirectCallee()->getDeclName(); - } else if (const auto *CallE = dyn_cast(S)) { - auto &CEMgr = BRC.getStateManager().getCallEventManager(); - CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC); - if (const auto *D = dyn_cast_or_null(Call->getDecl())) - OS << D->getDeclName(); - else - OS << "unknown"; - } - OS << "'"; - StackHint = std::make_unique( - Sym, "Returning; inner buffer was reallocated"); + OS << "reallocated by call to '"; + const Stmt *S = RSCurr->getStmt(); + if (const auto *MemCallE = dyn_cast(S)) { + OS << MemCallE->getMethodDecl()->getDeclName(); + } else if (const auto *OpCallE = dyn_cast(S)) { + OS << OpCallE->getDirectCallee()->getDeclName(); + } else if (const auto *CallE = dyn_cast(S)) { + auto &CEMgr = BRC.getStateManager().getCallEventManager(); + CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC); + if (const auto *D = dyn_cast_or_null(Call->getDecl())) + OS << D->getDeclName(); + else + OS << "unknown"; + } + OS << "'"; + StackHint = std::make_unique( + Sym, "Returning; inner buffer was reallocated"); } Msg = OS.str(); break; @@ -3535,10 +3538,13 @@ if (!S) { assert(RSCurr->getAllocationFamily() == AF_InnerBuffer); auto PostImplCall = N->getLocation().getAs(); - if (!PostImplCall) + auto PostDtorCall = N->getLocation().getAs(); + if (!PostImplCall && !PostDtorCall) return nullptr; - Pos = PathDiagnosticLocation(PostImplCall->getLocation(), - BRC.getSourceManager()); + + SourceLocation Loc = PostImplCall ? PostImplCall->getLocation() + : PostDtorCall->getLocation(); + Pos = PathDiagnosticLocation(Loc, BRC.getSourceManager()); } else { Pos = PathDiagnosticLocation(S, BRC.getSourceManager(), N->getLocationContext()); Index: clang/lib/StaticAnalyzer/Core/BugReporter.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/BugReporter.cpp +++ clang/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -2421,6 +2421,8 @@ // If this is an implicit call, return the implicit call point location. if (Optional PIE = P.getAs()) return PathDiagnosticLocation(PIE->getLocation(), SM); + if (Optional PIE = P.getAs()) + return PathDiagnosticLocation(PIE->getLocation(), SM); if (auto FE = P.getAs()) { if (const ReturnStmt *RS = FE->getStmt()) return PathDiagnosticLocation::createBegin(RS, SM, LC); Index: clang/lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -297,6 +297,14 @@ assert(D && "Cannot get a program point without a statement or decl"); SourceLocation Loc = getSourceRange().getBegin(); + if (const auto *DtorCall = dyn_cast(this)) { + if (IsPreVisit) + return PreDestructorCall(D, DtorCall->getCXXThisVal().getAsRegion(), Loc, + getLocationContext(), Tag); + return PostDestructorCall(D, DtorCall->getCXXThisVal().getAsRegion(), Loc, + getLocationContext(), Tag); + } + if (IsPreVisit) return PreImplicitCall(D, Loc, getLocationContext(), Tag); return PostImplicitCall(D, Loc, getLocationContext(), Tag); Index: clang/lib/StaticAnalyzer/Core/CoreEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ clang/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -211,11 +211,9 @@ break; } default: - assert(Loc.getAs() || - Loc.getAs() || - Loc.getAs() || - Loc.getAs() || - Loc.getAs() || + assert(Loc.getAs() || Loc.getAs() || + Loc.getAs() || Loc.getAs() || + Loc.getAs() || Loc.getAs() || Loc.getAs()); HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred); break; @@ -555,7 +553,8 @@ // Do not create extra nodes. Move to the next CFG element. if (N->getLocation().getAs() || - N->getLocation().getAs()|| + N->getLocation().getAs() || + N->getLocation().getAs() || N->getLocation().getAs()) { WList->enqueue(N, Block, Idx+1); return; Index: clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -80,8 +80,8 @@ // (8) Expressions that are *not* lvalue expressions. // (9) The PostStmt isn't for a non-consumed Stmt or Expr. // (10) The successor is neither a CallExpr StmtPoint nor a CallEnter or - // PreImplicitCall (so that we would be able to find it when retrying a - // call with no inlining). + // PreImplicitCall or PreDestructorCall (so that we would be able to find + // it when retrying a call with no inlining). // FIXME: It may be safe to reclaim PreCall and PostCall nodes as well. // Conditions 1 and 2. @@ -144,7 +144,8 @@ return false; // Condition 10, continuation. - if (SuccLoc.getAs() || SuccLoc.getAs()) + if (SuccLoc.getAs() || SuccLoc.getAs() || + SuccLoc.getAs()) return false; return true; Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1361,7 +1361,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); + PostDestructorCall PP(DtorDecl, Region, varDecl->getLocation(), LCtx, + &PT); NodeBuilder Bldr(Pred, Dst, *currBldrCtx); Bldr.generateSink(PP, Pred->getState(), Pred); return; @@ -1378,7 +1379,7 @@ static SimpleProgramPointTag PT("ExprEngine", "Prepare for object destruction"); - PreImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, &PT); + PreDestructorCall PP(DtorDecl, Region, varDecl->getLocation(), LCtx, &PT); Pred = Bldr.generateNode(PP, state, Pred); if (!Pred) @@ -1406,7 +1407,7 @@ const CXXRecordDecl *RD = BTy->getAsCXXRecordDecl(); const CXXDestructorDecl *Dtor = RD->getDestructor(); - PostImplicitCall PP(Dtor, DE->getBeginLoc(), LCtx); + PostDestructorCall PP(Dtor, nullptr, DE->getBeginLoc(), LCtx); NodeBuilder Bldr(Pred, Dst, *currBldrCtx); Bldr.generateNode(PP, Pred->getState(), Pred); return; @@ -1439,7 +1440,8 @@ static SimpleProgramPointTag PT( "ExprEngine", "Skipping 0 length array delete destruction"); - PostImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, &PT); + PostDestructorCall PP(getDtorDecl(DTy), ArgR, DE->getBeginLoc(), LCtx, + &PT); NodeBuilder Bldr(Pred, Dst, *currBldrCtx); Bldr.generateNode(PP, Pred->getState(), Pred); return; @@ -1453,7 +1455,7 @@ NodeBuilder Bldr(Pred, Dst, getBuilderContext()); static SimpleProgramPointTag PT("ExprEngine", "Prepare for object destruction"); - PreImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, &PT); + PreDestructorCall PP(getDtorDecl(DTy), ArgR, DE->getBeginLoc(), LCtx, &PT); Pred = Bldr.generateNode(PP, State, Pred); if (!Pred) @@ -1513,7 +1515,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); + PostDestructorCall PP(DtorDecl, FieldVal.getAsRegion(), + Member->getLocation(), LCtx, &PT); NodeBuilder Bldr(Pred, Dst, *currBldrCtx); Bldr.generateSink(PP, Pred->getState(), Pred); return; @@ -1529,7 +1532,8 @@ static SimpleProgramPointTag PT("ExprEngine", "Prepare for object destruction"); - PreImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, &PT); + PreDestructorCall PP(DtorDecl, FieldVal.getAsRegion(), Member->getLocation(), + LCtx, &PT); Pred = Bldr.generateNode(PP, State, Pred); if (!Pred) @@ -1564,9 +1568,9 @@ if (isDestructorElided(State, BTE, LC)) { State = cleanupElidedDestructor(State, BTE, LC); NodeBuilder Bldr(Pred, Dst, *currBldrCtx); - PostImplicitCall PP(D.getDestructorDecl(getContext()), - D.getBindTemporaryExpr()->getBeginLoc(), - Pred->getLocationContext()); + PostDestructorCall PP(D.getDestructorDecl(getContext()), nullptr, + D.getBindTemporaryExpr()->getBeginLoc(), + Pred->getLocationContext()); Bldr.generateNode(PP, State, Pred); return; } @@ -2424,6 +2428,8 @@ continue; if (L.getAs()) continue; + if (L.getAs()) + continue; if (L.getAs()) continue; if (Optional SP = L.getAs()) Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -867,8 +867,9 @@ // Skip the invalid destructor. We cannot simply return because // 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); + // FIXME: PostDestructorCall with a null decl may crash elsewhere anyway. + PostDestructorCall PP(/*Decl=*/nullptr, /*TargetRegion=*/nullptr, + S->getEndLoc(), LCtx, &T); NodeBuilder Bldr(Pred, Dst, *currBldrCtx); Bldr.generateNode(PP, Pred->getState(), Pred); return; Index: clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -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