https://github.com/T-Gruber updated https://github.com/llvm/llvm-project/pull/129016
>From 79d8f061476c6ba21bf48f55597eaaef345c2e80 Mon Sep 17 00:00:00 2001 From: "tobias.gruber" <tobias.gru...@concentrio.io> Date: Wed, 26 Feb 2025 18:00:21 +0100 Subject: [PATCH 1/5] Trigger checkLocation for RHS of copy construction --- .../lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 250 +++++++++--------- 1 file changed, 121 insertions(+), 129 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index f7020da2e6da2..fbb5e316bb068 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -69,6 +69,7 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, assert(ThisRD); SVal V = Call.getArgSVal(0); + const Expr *VExpr = Call.getArgExpr(0); // If the value being copied is not unknown, load from its location to get // an aggregate rvalue. @@ -76,7 +77,11 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, V = Pred->getState()->getSVal(*L); else assert(V.isUnknownOrUndef()); - evalBind(Dst, CallExpr, Pred, ThisVal, V, true); + + ExplodedNodeSet Tmp; + evalLocation(Tmp, CallExpr, VExpr, Pred, Pred->getState(), V, true); + for (ExplodedNode *N : Tmp) + evalBind(Dst, CallExpr, N, ThisVal, V, true); PostStmt PS(CallExpr, LCtx); for (ExplodedNode *N : Dst) { @@ -141,10 +146,9 @@ SVal ExprEngine::computeObjectUnderConstruction( if (Init->isBaseInitializer()) { const auto *ThisReg = cast<SubRegion>(ThisVal.getAsRegion()); const CXXRecordDecl *BaseClass = - Init->getBaseClass()->getAsCXXRecordDecl(); - const auto *BaseReg = - MRMgr.getCXXBaseObjectRegion(BaseClass, ThisReg, - Init->isBaseVirtual()); + Init->getBaseClass()->getAsCXXRecordDecl(); + const auto *BaseReg = MRMgr.getCXXBaseObjectRegion( + BaseClass, ThisReg, Init->isBaseVirtual()); return SVB.makeLoc(BaseReg); } if (Init->isDelegatingInitializer()) @@ -183,7 +187,7 @@ SVal ExprEngine::computeObjectUnderConstruction( return loc::MemRegionVal(R); } - return V; + return V; } // TODO: Detect when the allocator returns a null pointer. // Constructor shall not be called in this case. @@ -405,99 +409,99 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction( case ConstructionContext::SimpleVariableKind: { const auto *DSCC = cast<VariableConstructionContext>(CC); return addObjectUnderConstruction(State, DSCC->getDeclStmt(), LCtx, V); - } - case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind: - case ConstructionContext::SimpleConstructorInitializerKind: { - const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC); - const auto *Init = ICC->getCXXCtorInitializer(); - // Base and delegating initializers handled above - assert(Init->isAnyMemberInitializer() && - "Base and delegating initializers should have been handled by" - "computeObjectUnderConstruction()"); - return addObjectUnderConstruction(State, Init, LCtx, V); - } - case ConstructionContext::NewAllocatedObjectKind: { + } + case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind: + case ConstructionContext::SimpleConstructorInitializerKind: { + const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC); + const auto *Init = ICC->getCXXCtorInitializer(); + // Base and delegating initializers handled above + assert(Init->isAnyMemberInitializer() && + "Base and delegating initializers should have been handled by" + "computeObjectUnderConstruction()"); + return addObjectUnderConstruction(State, Init, LCtx, V); + } + case ConstructionContext::NewAllocatedObjectKind: { + return State; + } + case ConstructionContext::SimpleReturnedValueKind: + case ConstructionContext::CXX17ElidedCopyReturnedValueKind: { + const StackFrameContext *SFC = LCtx->getStackFrame(); + const LocationContext *CallerLCtx = SFC->getParent(); + if (!CallerLCtx) { + // No extra work is necessary in top frame. return State; } - case ConstructionContext::SimpleReturnedValueKind: - case ConstructionContext::CXX17ElidedCopyReturnedValueKind: { - const StackFrameContext *SFC = LCtx->getStackFrame(); - const LocationContext *CallerLCtx = SFC->getParent(); - if (!CallerLCtx) { - // No extra work is necessary in top frame. - return State; - } - auto RTC = (*SFC->getCallSiteBlock())[SFC->getIndex()] - .getAs<CFGCXXRecordTypedCall>(); - assert(RTC && "Could not have had a target region without it"); - if (isa<BlockInvocationContext>(CallerLCtx)) { - // Unwrap block invocation contexts. They're mostly part of - // the current stack frame. - CallerLCtx = CallerLCtx->getParent(); - assert(!isa<BlockInvocationContext>(CallerLCtx)); - } - - return updateObjectsUnderConstruction(V, - cast<Expr>(SFC->getCallSite()), State, CallerLCtx, - RTC->getConstructionContext(), CallOpts); - } - case ConstructionContext::ElidedTemporaryObjectKind: { - assert(AMgr.getAnalyzerOptions().ShouldElideConstructors); - if (!CallOpts.IsElidableCtorThatHasNotBeenElided) { - const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC); - State = updateObjectsUnderConstruction( - V, TCC->getConstructorAfterElision(), State, LCtx, - TCC->getConstructionContextAfterElision(), CallOpts); - - // Remember that we've elided the constructor. - State = addObjectUnderConstruction( - State, TCC->getConstructorAfterElision(), LCtx, V); - - // Remember that we've elided the destructor. - if (const auto *BTE = TCC->getCXXBindTemporaryExpr()) - State = elideDestructor(State, BTE, LCtx); - - // Instead of materialization, shamelessly return - // the final object destination. - if (const auto *MTE = TCC->getMaterializedTemporaryExpr()) - State = addObjectUnderConstruction(State, MTE, LCtx, V); - - return State; - } - // If we decided not to elide the constructor, proceed as if - // it's a simple temporary. - [[fallthrough]]; + auto RTC = (*SFC->getCallSiteBlock())[SFC->getIndex()] + .getAs<CFGCXXRecordTypedCall>(); + assert(RTC && "Could not have had a target region without it"); + if (isa<BlockInvocationContext>(CallerLCtx)) { + // Unwrap block invocation contexts. They're mostly part of + // the current stack frame. + CallerLCtx = CallerLCtx->getParent(); + assert(!isa<BlockInvocationContext>(CallerLCtx)); } - case ConstructionContext::SimpleTemporaryObjectKind: { - const auto *TCC = cast<TemporaryObjectConstructionContext>(CC); + + return updateObjectsUnderConstruction( + V, cast<Expr>(SFC->getCallSite()), State, CallerLCtx, + RTC->getConstructionContext(), CallOpts); + } + case ConstructionContext::ElidedTemporaryObjectKind: { + assert(AMgr.getAnalyzerOptions().ShouldElideConstructors); + if (!CallOpts.IsElidableCtorThatHasNotBeenElided) { + const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC); + State = updateObjectsUnderConstruction( + V, TCC->getConstructorAfterElision(), State, LCtx, + TCC->getConstructionContextAfterElision(), CallOpts); + + // Remember that we've elided the constructor. + State = addObjectUnderConstruction( + State, TCC->getConstructorAfterElision(), LCtx, V); + + // Remember that we've elided the destructor. if (const auto *BTE = TCC->getCXXBindTemporaryExpr()) - State = addObjectUnderConstruction(State, BTE, LCtx, V); + State = elideDestructor(State, BTE, LCtx); + // Instead of materialization, shamelessly return + // the final object destination. if (const auto *MTE = TCC->getMaterializedTemporaryExpr()) State = addObjectUnderConstruction(State, MTE, LCtx, V); return State; } - case ConstructionContext::LambdaCaptureKind: { - const auto *LCC = cast<LambdaCaptureConstructionContext>(CC); + // If we decided not to elide the constructor, proceed as if + // it's a simple temporary. + [[fallthrough]]; + } + case ConstructionContext::SimpleTemporaryObjectKind: { + const auto *TCC = cast<TemporaryObjectConstructionContext>(CC); + if (const auto *BTE = TCC->getCXXBindTemporaryExpr()) + State = addObjectUnderConstruction(State, BTE, LCtx, V); - // If we capture and array, we want to store the super region, not a - // sub-region. - if (const auto *EL = dyn_cast_or_null<ElementRegion>(V.getAsRegion())) - V = loc::MemRegionVal(EL->getSuperRegion()); + if (const auto *MTE = TCC->getMaterializedTemporaryExpr()) + State = addObjectUnderConstruction(State, MTE, LCtx, V); - return addObjectUnderConstruction( - State, {LCC->getLambdaExpr(), LCC->getIndex()}, LCtx, V); - } - case ConstructionContext::ArgumentKind: { - const auto *ACC = cast<ArgumentConstructionContext>(CC); - if (const auto *BTE = ACC->getCXXBindTemporaryExpr()) - State = addObjectUnderConstruction(State, BTE, LCtx, V); + return State; + } + case ConstructionContext::LambdaCaptureKind: { + const auto *LCC = cast<LambdaCaptureConstructionContext>(CC); - return addObjectUnderConstruction( - State, {ACC->getCallLikeExpr(), ACC->getIndex()}, LCtx, V); - } + // If we capture and array, we want to store the super region, not a + // sub-region. + if (const auto *EL = dyn_cast_or_null<ElementRegion>(V.getAsRegion())) + V = loc::MemRegionVal(EL->getSuperRegion()); + + return addObjectUnderConstruction( + State, {LCC->getLambdaExpr(), LCC->getIndex()}, LCtx, V); + } + case ConstructionContext::ArgumentKind: { + const auto *ACC = cast<ArgumentConstructionContext>(CC); + if (const auto *BTE = ACC->getCXXBindTemporaryExpr()) + State = addObjectUnderConstruction(State, BTE, LCtx, V); + + return addObjectUnderConstruction( + State, {ACC->getCallLikeExpr(), ACC->getIndex()}, LCtx, V); + } } llvm_unreachable("Unhandled construction context!"); } @@ -526,8 +530,7 @@ bindRequiredArrayElementToEnvironment(ProgramStateRef State, loc::MemRegionVal(ElementRegion)); } -void ExprEngine::handleConstructor(const Expr *E, - ExplodedNode *Pred, +void ExprEngine::handleConstructor(const Expr *E, ExplodedNode *Pred, ExplodedNodeSet &destNodes) { const auto *CE = dyn_cast<CXXConstructExpr>(E); const auto *CIE = dyn_cast<CXXInheritedCtorInitExpr>(E); @@ -541,16 +544,16 @@ void ExprEngine::handleConstructor(const Expr *E, if (CE) { if (std::optional<SVal> ElidedTarget = getObjectUnderConstruction(State, CE, LCtx)) { - // We've previously modeled an elidable constructor by pretending that - // it in fact constructs into the correct target. This constructor can - // therefore be skipped. - Target = *ElidedTarget; - StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx); - State = finishObjectConstruction(State, CE, LCtx); - if (auto L = Target.getAs<Loc>()) - State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType())); - Bldr.generateNode(CE, Pred, State); - return; + // We've previously modeled an elidable constructor by pretending that + // it in fact constructs into the correct target. This constructor can + // therefore be skipped. + Target = *ElidedTarget; + StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx); + State = finishObjectConstruction(State, CE, LCtx); + if (auto L = Target.getAs<Loc>()) + State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType())); + Bldr.generateNode(CE, Pred, State); + return; } } @@ -648,8 +651,7 @@ void ExprEngine::handleConstructor(const Expr *E, [[fallthrough]]; case CXXConstructionKind::Delegating: { const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); - Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, - LCtx->getStackFrame()); + Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, LCtx->getStackFrame()); SVal ThisVal = State->getSVal(ThisPtr); if (CK == CXXConstructionKind::Delegating) { @@ -718,8 +720,8 @@ void ExprEngine::handleConstructor(const Expr *E, } ExplodedNodeSet DstPreCall; - getCheckerManager().runCheckersForPreCall(DstPreCall, PreInitialized, - *Call, *this); + getCheckerManager().runCheckersForPreCall(DstPreCall, PreInitialized, *Call, + *this); ExplodedNodeSet DstEvaluated; @@ -782,9 +784,8 @@ void ExprEngine::handleConstructor(const Expr *E, // If there were other constructors called for object-type arguments // of this constructor, clean them up. ExplodedNodeSet DstPostCall; - getCheckerManager().runCheckersForPostCall(DstPostCall, - DstPostArgumentCleanup, - *Call, *this); + getCheckerManager().runCheckersForPostCall( + DstPostCall, DstPostArgumentCleanup, *Call, *this); getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, E, *this); } @@ -800,12 +801,9 @@ void ExprEngine::VisitCXXInheritedCtorInitExpr( handleConstructor(CE, Pred, Dst); } -void ExprEngine::VisitCXXDestructor(QualType ObjectType, - const MemRegion *Dest, - const Stmt *S, - bool IsBaseDtor, - ExplodedNode *Pred, - ExplodedNodeSet &Dst, +void ExprEngine::VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest, + const Stmt *S, bool IsBaseDtor, + ExplodedNode *Pred, ExplodedNodeSet &Dst, EvalCallOptions &CallOpts) { assert(S && "A destructor without a trigger!"); const LocationContext *LCtx = Pred->getLocationContext(); @@ -840,8 +838,8 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, } else { static SimpleProgramPointTag T("ExprEngine", "SkipInvalidDestructor"); NodeBuilder Bldr(Pred, Dst, *currBldrCtx); - Bldr.generateSink(Pred->getLocation().withTag(&T), - Pred->getState(), Pred); + Bldr.generateSink(Pred->getLocation().withTag(&T), Pred->getState(), + Pred); return; } } @@ -855,16 +853,14 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, "Error evaluating destructor"); ExplodedNodeSet DstPreCall; - getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, - *Call, *this); + getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, *Call, *this); ExplodedNodeSet DstInvalidated; StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currBldrCtx); for (ExplodedNode *N : DstPreCall) defaultEvalCall(Bldr, N, *Call, CallOpts); - getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, - *Call, *this); + getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, *Call, *this); } void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, @@ -880,8 +876,7 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, CEMgr.getCXXAllocatorCall(CNE, State, LCtx, getCFGElementRef()); ExplodedNodeSet DstPreCall; - getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, - *Call, *this); + getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, *Call, *this); ExplodedNodeSet DstPostCall; StmtNodeBuilder CallBldr(DstPreCall, DstPostCall, *currBldrCtx); @@ -940,7 +935,7 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, } void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { + ExplodedNodeSet &Dst) { // FIXME: Much of this should eventually migrate to CXXAllocatorCall. // Also, we need to decide how allocators actually work -- they're not // really part of the CXXNewExpr because they happen BEFORE the @@ -1112,15 +1107,13 @@ void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred, } void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { + ExplodedNodeSet &Dst) { StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); // Get the this object region from StoreManager. const LocationContext *LCtx = Pred->getLocationContext(); - const MemRegion *R = - svalBuilder.getRegionManager().getCXXThisRegion( - getContext().getCanonicalType(TE->getType()), - LCtx); + const MemRegion *R = svalBuilder.getRegionManager().getCXXThisRegion( + getContext().getCanonicalType(TE->getType()), LCtx); ProgramStateRef state = Pred->getState(); SVal V = state->getSVal(loc::MemRegionVal(R)); @@ -1132,8 +1125,8 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, const LocationContext *LocCtxt = Pred->getLocationContext(); // Get the region of the lambda itself. - const MemRegion *R = svalBuilder.getRegionManager().getCXXTempObjectRegion( - LE, LocCtxt); + const MemRegion *R = + svalBuilder.getRegionManager().getCXXTempObjectRegion(LE, LocCtxt); SVal V = loc::MemRegionVal(R); ProgramStateRef State = Pred->getState(); @@ -1193,9 +1186,8 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, ExplodedNodeSet Tmp; StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx); // FIXME: is this the right program point kind? - Bldr.generateNode(LE, Pred, - State->BindExpr(LE, LocCtxt, LambdaRVal), - nullptr, ProgramPoint::PostLValueKind); + Bldr.generateNode(LE, Pred, State->BindExpr(LE, LocCtxt, LambdaRVal), nullptr, + ProgramPoint::PostLValueKind); // FIXME: Move all post/pre visits to ::Visit(). getCheckerManager().runCheckersForPostStmt(Dst, Tmp, LE, *this); >From 1bad0e4094e87401b550a90922626d341ba2547c Mon Sep 17 00:00:00 2001 From: "tobias.gruber" <tobias.gru...@concentrio.io> Date: Thu, 27 Feb 2025 07:26:23 +0100 Subject: [PATCH 2/5] Unittest for default copy construction --- .../StaticAnalyzer/ExprEngineVisitTest.cpp | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/clang/unittests/StaticAnalyzer/ExprEngineVisitTest.cpp b/clang/unittests/StaticAnalyzer/ExprEngineVisitTest.cpp index a8579f9d0f90c..19de9919701a3 100644 --- a/clang/unittests/StaticAnalyzer/ExprEngineVisitTest.cpp +++ b/clang/unittests/StaticAnalyzer/ExprEngineVisitTest.cpp @@ -12,6 +12,8 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/Support/Casting.h" #include "gtest/gtest.h" using namespace clang; @@ -27,6 +29,14 @@ void emitErrorReport(CheckerContext &C, const BugType &Bug, } } +inline std::string getMemRegionName(const SVal &Val) { + if (auto MemVal = llvm::dyn_cast<loc::MemRegionVal>(Val)) + return MemVal->getRegion()->getDescriptiveName(false); + if (auto ComVal = llvm::dyn_cast<nonloc::LazyCompoundVal>(Val)) + return ComVal->getRegion()->getDescriptiveName(false); + return ""; +} + #define CREATE_EXPR_ENGINE_CHECKER(CHECKER_NAME, CALLBACK, STMT_TYPE, \ BUG_NAME) \ class CHECKER_NAME : public Checker<check::CALLBACK<STMT_TYPE>> { \ @@ -44,6 +54,21 @@ CREATE_EXPR_ENGINE_CHECKER(ExprEngineVisitPreChecker, PreStmt, GCCAsmStmt, CREATE_EXPR_ENGINE_CHECKER(ExprEngineVisitPostChecker, PostStmt, GCCAsmStmt, "GCCAsmStmtBug") +class MemAccessChecker : public Checker<check::Location, check::Bind> { +public: + void checkLocation(const SVal &Loc, bool IsLoad, const Stmt *S, + CheckerContext &C) const { + emitErrorReport(C, Bug, "checkLocation: Loc = " + getMemRegionName(Loc)); + } + + void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const { + emitErrorReport(C, Bug, "checkBind: Loc = " + getMemRegionName(Loc)); + } + +private: + const BugType Bug{this, "MemAccess"}; +}; + void addExprEngineVisitPreChecker(AnalysisASTConsumer &AnalysisConsumer, AnalyzerOptions &AnOpts) { AnOpts.CheckersAndPackages = {{"ExprEngineVisitPreChecker", true}}; @@ -62,6 +87,15 @@ void addExprEngineVisitPostChecker(AnalysisASTConsumer &AnalysisConsumer, }); } +void addMemAccessChecker(AnalysisASTConsumer &AnalysisConsumer, + AnalyzerOptions &AnOpts) { + AnOpts.CheckersAndPackages = {{"MemAccessChecker", true}}; + AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { + Registry.addChecker<MemAccessChecker>("MemAccessChecker", "Desc", + "DocsURI"); + }); +} + TEST(ExprEngineVisitTest, checkPreStmtGCCAsmStmt) { std::string Diags; EXPECT_TRUE(runCheckerOnCode<addExprEngineVisitPreChecker>(R"( @@ -84,4 +118,24 @@ TEST(ExprEngineVisitTest, checkPostStmtGCCAsmStmt) { EXPECT_EQ(Diags, "ExprEngineVisitPostChecker: checkPostStmt<GCCAsmStmt>\n"); } +TEST(ExprEngineVisitTest, checkLocationAndBind) { + std::string Diags; + EXPECT_TRUE(runCheckerOnCode<addMemAccessChecker>(R"( + class MyClass{ + public: + int Value; + }; + extern MyClass MyClassWrite, MyClassRead; + void top() { + MyClassWrite = MyClassRead; + } + )", + Diags)); + + std::string RHSMsg = "checkLocation: Loc = MyClassRead"; + std::string LHSMsg = "checkBind: Loc = MyClassWrite"; + EXPECT_NE(Diags.find(RHSMsg), std::string::npos); + EXPECT_NE(Diags.find(LHSMsg), std::string::npos); +} + } // namespace >From cfa8d3491f8d436530c6c995ed07412623809590 Mon Sep 17 00:00:00 2001 From: "tobias.gruber" <tobias.gru...@concentrio.io> Date: Thu, 27 Feb 2025 07:50:54 +0100 Subject: [PATCH 3/5] Undo format changes --- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index fbb5e316bb068..f9b0f9d8dd93e 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -1191,4 +1191,4 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, // FIXME: Move all post/pre visits to ::Visit(). getCheckerManager().runCheckersForPostStmt(Dst, Tmp, LE, *this); -} +} \ No newline at end of file >From 57b4517b29bd6d8d7ba2fafa94b6b7b17494050b Mon Sep 17 00:00:00 2001 From: "tobias.gruber" <tobias.gru...@concentrio.io> Date: Thu, 27 Feb 2025 07:57:36 +0100 Subject: [PATCH 4/5] Undo format changes --- .../lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 245 +++++++++--------- 1 file changed, 129 insertions(+), 116 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index f9b0f9d8dd93e..47888cf9689c7 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -77,7 +77,7 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, V = Pred->getState()->getSVal(*L); else assert(V.isUnknownOrUndef()); - + ExplodedNodeSet Tmp; evalLocation(Tmp, CallExpr, VExpr, Pred, Pred->getState(), V, true); for (ExplodedNode *N : Tmp) @@ -146,9 +146,10 @@ SVal ExprEngine::computeObjectUnderConstruction( if (Init->isBaseInitializer()) { const auto *ThisReg = cast<SubRegion>(ThisVal.getAsRegion()); const CXXRecordDecl *BaseClass = - Init->getBaseClass()->getAsCXXRecordDecl(); - const auto *BaseReg = MRMgr.getCXXBaseObjectRegion( - BaseClass, ThisReg, Init->isBaseVirtual()); + Init->getBaseClass()->getAsCXXRecordDecl(); + const auto *BaseReg = + MRMgr.getCXXBaseObjectRegion(BaseClass, ThisReg, + Init->isBaseVirtual()); return SVB.makeLoc(BaseReg); } if (Init->isDelegatingInitializer()) @@ -187,7 +188,7 @@ SVal ExprEngine::computeObjectUnderConstruction( return loc::MemRegionVal(R); } - return V; + return V; } // TODO: Detect when the allocator returns a null pointer. // Constructor shall not be called in this case. @@ -409,99 +410,99 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction( case ConstructionContext::SimpleVariableKind: { const auto *DSCC = cast<VariableConstructionContext>(CC); return addObjectUnderConstruction(State, DSCC->getDeclStmt(), LCtx, V); - } - case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind: - case ConstructionContext::SimpleConstructorInitializerKind: { - const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC); - const auto *Init = ICC->getCXXCtorInitializer(); - // Base and delegating initializers handled above - assert(Init->isAnyMemberInitializer() && - "Base and delegating initializers should have been handled by" - "computeObjectUnderConstruction()"); - return addObjectUnderConstruction(State, Init, LCtx, V); - } - case ConstructionContext::NewAllocatedObjectKind: { - return State; - } - case ConstructionContext::SimpleReturnedValueKind: - case ConstructionContext::CXX17ElidedCopyReturnedValueKind: { - const StackFrameContext *SFC = LCtx->getStackFrame(); - const LocationContext *CallerLCtx = SFC->getParent(); - if (!CallerLCtx) { - // No extra work is necessary in top frame. - return State; } - - auto RTC = (*SFC->getCallSiteBlock())[SFC->getIndex()] - .getAs<CFGCXXRecordTypedCall>(); - assert(RTC && "Could not have had a target region without it"); - if (isa<BlockInvocationContext>(CallerLCtx)) { - // Unwrap block invocation contexts. They're mostly part of - // the current stack frame. - CallerLCtx = CallerLCtx->getParent(); - assert(!isa<BlockInvocationContext>(CallerLCtx)); + case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind: + case ConstructionContext::SimpleConstructorInitializerKind: { + const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC); + const auto *Init = ICC->getCXXCtorInitializer(); + // Base and delegating initializers handled above + assert(Init->isAnyMemberInitializer() && + "Base and delegating initializers should have been handled by" + "computeObjectUnderConstruction()"); + return addObjectUnderConstruction(State, Init, LCtx, V); } + case ConstructionContext::NewAllocatedObjectKind: { + return State; + } + case ConstructionContext::SimpleReturnedValueKind: + case ConstructionContext::CXX17ElidedCopyReturnedValueKind: { + const StackFrameContext *SFC = LCtx->getStackFrame(); + const LocationContext *CallerLCtx = SFC->getParent(); + if (!CallerLCtx) { + // No extra work is necessary in top frame. + return State; + } - return updateObjectsUnderConstruction( - V, cast<Expr>(SFC->getCallSite()), State, CallerLCtx, - RTC->getConstructionContext(), CallOpts); - } - case ConstructionContext::ElidedTemporaryObjectKind: { - assert(AMgr.getAnalyzerOptions().ShouldElideConstructors); - if (!CallOpts.IsElidableCtorThatHasNotBeenElided) { - const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC); - State = updateObjectsUnderConstruction( - V, TCC->getConstructorAfterElision(), State, LCtx, - TCC->getConstructionContextAfterElision(), CallOpts); - - // Remember that we've elided the constructor. - State = addObjectUnderConstruction( - State, TCC->getConstructorAfterElision(), LCtx, V); + auto RTC = (*SFC->getCallSiteBlock())[SFC->getIndex()] + .getAs<CFGCXXRecordTypedCall>(); + assert(RTC && "Could not have had a target region without it"); + if (isa<BlockInvocationContext>(CallerLCtx)) { + // Unwrap block invocation contexts. They're mostly part of + // the current stack frame. + CallerLCtx = CallerLCtx->getParent(); + assert(!isa<BlockInvocationContext>(CallerLCtx)); + } - // Remember that we've elided the destructor. + return updateObjectsUnderConstruction(V, + cast<Expr>(SFC->getCallSite()), State, CallerLCtx, + RTC->getConstructionContext(), CallOpts); + } + case ConstructionContext::ElidedTemporaryObjectKind: { + assert(AMgr.getAnalyzerOptions().ShouldElideConstructors); + if (!CallOpts.IsElidableCtorThatHasNotBeenElided) { + const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC); + State = updateObjectsUnderConstruction( + V, TCC->getConstructorAfterElision(), State, LCtx, + TCC->getConstructionContextAfterElision(), CallOpts); + + // Remember that we've elided the constructor. + State = addObjectUnderConstruction( + State, TCC->getConstructorAfterElision(), LCtx, V); + + // Remember that we've elided the destructor. + if (const auto *BTE = TCC->getCXXBindTemporaryExpr()) + State = elideDestructor(State, BTE, LCtx); + + // Instead of materialization, shamelessly return + // the final object destination. + if (const auto *MTE = TCC->getMaterializedTemporaryExpr()) + State = addObjectUnderConstruction(State, MTE, LCtx, V); + + return State; + } + // If we decided not to elide the constructor, proceed as if + // it's a simple temporary. + [[fallthrough]]; + } + case ConstructionContext::SimpleTemporaryObjectKind: { + const auto *TCC = cast<TemporaryObjectConstructionContext>(CC); if (const auto *BTE = TCC->getCXXBindTemporaryExpr()) - State = elideDestructor(State, BTE, LCtx); + State = addObjectUnderConstruction(State, BTE, LCtx, V); - // Instead of materialization, shamelessly return - // the final object destination. if (const auto *MTE = TCC->getMaterializedTemporaryExpr()) State = addObjectUnderConstruction(State, MTE, LCtx, V); return State; } - // If we decided not to elide the constructor, proceed as if - // it's a simple temporary. - [[fallthrough]]; - } - case ConstructionContext::SimpleTemporaryObjectKind: { - const auto *TCC = cast<TemporaryObjectConstructionContext>(CC); - if (const auto *BTE = TCC->getCXXBindTemporaryExpr()) - State = addObjectUnderConstruction(State, BTE, LCtx, V); - - if (const auto *MTE = TCC->getMaterializedTemporaryExpr()) - State = addObjectUnderConstruction(State, MTE, LCtx, V); - - return State; - } - case ConstructionContext::LambdaCaptureKind: { - const auto *LCC = cast<LambdaCaptureConstructionContext>(CC); + case ConstructionContext::LambdaCaptureKind: { + const auto *LCC = cast<LambdaCaptureConstructionContext>(CC); - // If we capture and array, we want to store the super region, not a - // sub-region. - if (const auto *EL = dyn_cast_or_null<ElementRegion>(V.getAsRegion())) - V = loc::MemRegionVal(EL->getSuperRegion()); + // If we capture and array, we want to store the super region, not a + // sub-region. + if (const auto *EL = dyn_cast_or_null<ElementRegion>(V.getAsRegion())) + V = loc::MemRegionVal(EL->getSuperRegion()); - return addObjectUnderConstruction( - State, {LCC->getLambdaExpr(), LCC->getIndex()}, LCtx, V); - } - case ConstructionContext::ArgumentKind: { - const auto *ACC = cast<ArgumentConstructionContext>(CC); - if (const auto *BTE = ACC->getCXXBindTemporaryExpr()) - State = addObjectUnderConstruction(State, BTE, LCtx, V); + return addObjectUnderConstruction( + State, {LCC->getLambdaExpr(), LCC->getIndex()}, LCtx, V); + } + case ConstructionContext::ArgumentKind: { + const auto *ACC = cast<ArgumentConstructionContext>(CC); + if (const auto *BTE = ACC->getCXXBindTemporaryExpr()) + State = addObjectUnderConstruction(State, BTE, LCtx, V); - return addObjectUnderConstruction( - State, {ACC->getCallLikeExpr(), ACC->getIndex()}, LCtx, V); - } + return addObjectUnderConstruction( + State, {ACC->getCallLikeExpr(), ACC->getIndex()}, LCtx, V); + } } llvm_unreachable("Unhandled construction context!"); } @@ -530,7 +531,8 @@ bindRequiredArrayElementToEnvironment(ProgramStateRef State, loc::MemRegionVal(ElementRegion)); } -void ExprEngine::handleConstructor(const Expr *E, ExplodedNode *Pred, +void ExprEngine::handleConstructor(const Expr *E, + ExplodedNode *Pred, ExplodedNodeSet &destNodes) { const auto *CE = dyn_cast<CXXConstructExpr>(E); const auto *CIE = dyn_cast<CXXInheritedCtorInitExpr>(E); @@ -544,16 +546,16 @@ void ExprEngine::handleConstructor(const Expr *E, ExplodedNode *Pred, if (CE) { if (std::optional<SVal> ElidedTarget = getObjectUnderConstruction(State, CE, LCtx)) { - // We've previously modeled an elidable constructor by pretending that - // it in fact constructs into the correct target. This constructor can - // therefore be skipped. - Target = *ElidedTarget; - StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx); - State = finishObjectConstruction(State, CE, LCtx); - if (auto L = Target.getAs<Loc>()) - State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType())); - Bldr.generateNode(CE, Pred, State); - return; + // We've previously modeled an elidable constructor by pretending that + // it in fact constructs into the correct target. This constructor can + // therefore be skipped. + Target = *ElidedTarget; + StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx); + State = finishObjectConstruction(State, CE, LCtx); + if (auto L = Target.getAs<Loc>()) + State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType())); + Bldr.generateNode(CE, Pred, State); + return; } } @@ -651,7 +653,8 @@ void ExprEngine::handleConstructor(const Expr *E, ExplodedNode *Pred, [[fallthrough]]; case CXXConstructionKind::Delegating: { const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); - Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, LCtx->getStackFrame()); + Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, + LCtx->getStackFrame()); SVal ThisVal = State->getSVal(ThisPtr); if (CK == CXXConstructionKind::Delegating) { @@ -720,8 +723,8 @@ void ExprEngine::handleConstructor(const Expr *E, ExplodedNode *Pred, } ExplodedNodeSet DstPreCall; - getCheckerManager().runCheckersForPreCall(DstPreCall, PreInitialized, *Call, - *this); + getCheckerManager().runCheckersForPreCall(DstPreCall, PreInitialized, + *Call, *this); ExplodedNodeSet DstEvaluated; @@ -784,8 +787,9 @@ void ExprEngine::handleConstructor(const Expr *E, ExplodedNode *Pred, // If there were other constructors called for object-type arguments // of this constructor, clean them up. ExplodedNodeSet DstPostCall; - getCheckerManager().runCheckersForPostCall( - DstPostCall, DstPostArgumentCleanup, *Call, *this); + getCheckerManager().runCheckersForPostCall(DstPostCall, + DstPostArgumentCleanup, + *Call, *this); getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, E, *this); } @@ -801,9 +805,12 @@ void ExprEngine::VisitCXXInheritedCtorInitExpr( handleConstructor(CE, Pred, Dst); } -void ExprEngine::VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest, - const Stmt *S, bool IsBaseDtor, - ExplodedNode *Pred, ExplodedNodeSet &Dst, +void ExprEngine::VisitCXXDestructor(QualType ObjectType, + const MemRegion *Dest, + const Stmt *S, + bool IsBaseDtor, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, EvalCallOptions &CallOpts) { assert(S && "A destructor without a trigger!"); const LocationContext *LCtx = Pred->getLocationContext(); @@ -838,8 +845,8 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest, } else { static SimpleProgramPointTag T("ExprEngine", "SkipInvalidDestructor"); NodeBuilder Bldr(Pred, Dst, *currBldrCtx); - Bldr.generateSink(Pred->getLocation().withTag(&T), Pred->getState(), - Pred); + Bldr.generateSink(Pred->getLocation().withTag(&T), + Pred->getState(), Pred); return; } } @@ -853,14 +860,16 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest, "Error evaluating destructor"); ExplodedNodeSet DstPreCall; - getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, *Call, *this); + getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, + *Call, *this); ExplodedNodeSet DstInvalidated; StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currBldrCtx); for (ExplodedNode *N : DstPreCall) defaultEvalCall(Bldr, N, *Call, CallOpts); - getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, *Call, *this); + getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, + *Call, *this); } void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, @@ -876,7 +885,8 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, CEMgr.getCXXAllocatorCall(CNE, State, LCtx, getCFGElementRef()); ExplodedNodeSet DstPreCall; - getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, *Call, *this); + getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, + *Call, *this); ExplodedNodeSet DstPostCall; StmtNodeBuilder CallBldr(DstPreCall, DstPostCall, *currBldrCtx); @@ -935,7 +945,7 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, } void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { + ExplodedNodeSet &Dst) { // FIXME: Much of this should eventually migrate to CXXAllocatorCall. // Also, we need to decide how allocators actually work -- they're not // really part of the CXXNewExpr because they happen BEFORE the @@ -1107,13 +1117,15 @@ void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred, } void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { + ExplodedNodeSet &Dst) { StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); // Get the this object region from StoreManager. const LocationContext *LCtx = Pred->getLocationContext(); - const MemRegion *R = svalBuilder.getRegionManager().getCXXThisRegion( - getContext().getCanonicalType(TE->getType()), LCtx); + const MemRegion *R = + svalBuilder.getRegionManager().getCXXThisRegion( + getContext().getCanonicalType(TE->getType()), + LCtx); ProgramStateRef state = Pred->getState(); SVal V = state->getSVal(loc::MemRegionVal(R)); @@ -1125,8 +1137,8 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, const LocationContext *LocCtxt = Pred->getLocationContext(); // Get the region of the lambda itself. - const MemRegion *R = - svalBuilder.getRegionManager().getCXXTempObjectRegion(LE, LocCtxt); + const MemRegion *R = svalBuilder.getRegionManager().getCXXTempObjectRegion( + LE, LocCtxt); SVal V = loc::MemRegionVal(R); ProgramStateRef State = Pred->getState(); @@ -1186,8 +1198,9 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, ExplodedNodeSet Tmp; StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx); // FIXME: is this the right program point kind? - Bldr.generateNode(LE, Pred, State->BindExpr(LE, LocCtxt, LambdaRVal), nullptr, - ProgramPoint::PostLValueKind); + Bldr.generateNode(LE, Pred, + State->BindExpr(LE, LocCtxt, LambdaRVal), + nullptr, ProgramPoint::PostLValueKind); // FIXME: Move all post/pre visits to ::Visit(). getCheckerManager().runCheckersForPostStmt(Dst, Tmp, LE, *this); >From 0af9569813f0f9e402372a1dde0e28ce9b7ff921 Mon Sep 17 00:00:00 2001 From: "tobias.gruber" <tobias.gru...@concentrio.io> Date: Thu, 27 Feb 2025 08:22:47 +0100 Subject: [PATCH 5/5] Fix formatting --- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 47888cf9689c7..0dbeee10ec20e 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -77,7 +77,7 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, V = Pred->getState()->getSVal(*L); else assert(V.isUnknownOrUndef()); - + ExplodedNodeSet Tmp; evalLocation(Tmp, CallExpr, VExpr, Pred, Pred->getState(), V, true); for (ExplodedNode *N : Tmp) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits