From cd8de4a86d37c856cb2b4f9025ae9cf10f48c791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Wed, 5 Aug 2026 14:12:55 +0200 Subject: [PATCH 01/31] Introduce refactored algorithmic scheme in `ExprEngine::ProcessStmt` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 36 ++++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index e6349eb4eba2a..1054129110698 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1100,6 +1100,10 @@ const ProgramPointTag *ExprEngine::cleanupNodeTag() { return &cleanupTag; } +static bool justRunCheckersAsPreVisit(const Stmt *S) { return false; } + +static bool justRunCheckersAsPostVisit(const Stmt *S) { return false; } + void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) { // Reclaim any unnecessary nodes in the ExplodedGraph. G.reclaimRecentlyAllocatedNodes(); @@ -1115,17 +1119,35 @@ void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) { } else CleanedStates.insert(Pred); - // Visit the statement. - ExplodedNodeSet Dst; + ExplodedNodeSet PreVisited; for (const auto I : CleanedStates) { - ExplodedNodeSet DstI; - // Visit the statement. - Visit(currStmt, I, DstI); - Dst.insert(DstI); + ExplodedNodeSet Tmp; + if (justRunCheckersAsPreVisit(currStmt)) { + getCheckerManager().runCheckersForPreStmt(Tmp, I, currStmt, *this); + PreVisited.insert(Tmp); + } else + PreVisited.insert(I); + } + + ExplodedNodeSet Visited; + for (const auto I : PreVisited) { + ExplodedNodeSet Tmp; + Visit(currStmt, I, Tmp); + Visited.insert(Tmp); + } + + ExplodedNodeSet PostVisited; + for (const auto I : Visited) { + ExplodedNodeSet Tmp; + if (justRunCheckersAsPostVisit(currStmt)) { + getCheckerManager().runCheckersForPostStmt(Tmp, I, currStmt, *this); + PostVisited.insert(Tmp); + } else + PostVisited.insert(I); } // Enqueue the new nodes onto the work list. - Engine.enqueueStmtNodes(Dst, getCurrBlock(), currStmtIdx); + Engine.enqueueStmtNodes(PostVisited, getCurrBlock(), currStmtIdx); } void ExprEngine::ProcessLoopExit(const Stmt* S, ExplodedNode *Pred) { From 9477afa996830090f15e54bd681af1ea0007662a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Wed, 5 Aug 2026 15:19:47 +0200 Subject: [PATCH 02/31] `ExprEngine::VisitCXXBindTemporaryExpr` --- .../Core/PathSensitive/ExprEngine.h | 3 +- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 51 +++++++++++-------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 68d4362aca941..63447450a8962 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -628,8 +628,7 @@ class ExprEngine { ExplodedNodeSet &Dst); void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE, - ExplodedNodeSet &PreVisit, - ExplodedNodeSet &Dst); + ExplodedNode *Pred, ExplodedNodeSet &Dst); void VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred, ExplodedNodeSet &Dst); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 1054129110698..d7ff19ace0cde 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1100,9 +1100,23 @@ const ProgramPointTag *ExprEngine::cleanupNodeTag() { return &cleanupTag; } -static bool justRunCheckersAsPreVisit(const Stmt *S) { return false; } +static bool justRunCheckersAsPreVisit(const Stmt *S) { + switch (S->getStmtClass()) { + default: + return false; + case Stmt::CXXBindTemporaryExprClass: + return true; + } +} -static bool justRunCheckersAsPostVisit(const Stmt *S) { return false; } +static bool justRunCheckersAsPostVisit(const Stmt *S) { + switch (S->getStmtClass()) { + default: + return false; + case Stmt::CXXBindTemporaryExprClass: + return true; + } +} void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) { // Reclaim any unnecessary nodes in the ExplodedGraph. @@ -1648,7 +1662,7 @@ void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, } void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE, - ExplodedNodeSet &PreVisit, + ExplodedNode *Pred, ExplodedNodeSet &Dst) { // This is a fallback solution in case we didn't have a construction // context when we were constructing the temporary. Otherwise the map should @@ -1656,21 +1670,19 @@ void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE, if (!getAnalysisManager().options.ShouldIncludeTemporaryDtorsInCFG) { // In case we don't have temporary destructors in the CFG, do not mark // the initialization - we would otherwise never clean it up. - Dst = PreVisit; + Dst.insert(Pred); return; } - for (ExplodedNode *Node : PreVisit) { - ProgramStateRef State = Node->getState(); - const StackFrame *SF = Node->getStackFrame(); - if (!getObjectUnderConstruction(State, BTE, SF)) { - // FIXME: Currently the state might also already contain the marker due to - // incorrect handling of temporaries bound to default parameters; for - // those, we currently skip the CXXBindTemporaryExpr but rely on adding - // temporary destructor nodes. - State = addObjectUnderConstruction(State, BTE, SF, UnknownVal()); - } - Dst.insert(Engine.makePostStmtNode(BTE, State, Node)); + ProgramStateRef State = Pred->getState(); + const StackFrame *SF = Pred->getStackFrame(); + if (!getObjectUnderConstruction(State, BTE, SF)) { + // FIXME: Currently the state might also already contain the marker due to + // incorrect handling of temporaries bound to default parameters; for + // those, we currently skip the CXXBindTemporaryExpr but rely on adding + // temporary destructor nodes. + State = addObjectUnderConstruction(State, BTE, SF, UnknownVal()); } + Dst.insert(Engine.makePostStmtNode(BTE, State, Pred)); } ProgramStateRef ExprEngine::escapeValues(ProgramStateRef State, @@ -1898,14 +1910,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // Handled due to fully linearised CFG. break; - case Stmt::CXXBindTemporaryExprClass: { - ExplodedNodeSet PreVisit; - getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); - ExplodedNodeSet Next; - VisitCXXBindTemporaryExpr(cast(S), PreVisit, Next); - getCheckerManager().runCheckersForPostStmt(Dst, Next, S, *this); + case Stmt::CXXBindTemporaryExprClass: + VisitCXXBindTemporaryExpr(cast(S), Pred, Dst); break; - } case Stmt::ArrayInitLoopExprClass: VisitArrayInitLoopExpr(cast(S), Pred, Dst); From c24938ee56578c57d1055a8719b5eca52f09cf51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Fri, 7 Aug 2026 17:19:10 +0200 Subject: [PATCH 03/31] `ExprEngine::VisitStmtExpr` --- .../Core/PathSensitive/ExprEngine.h | 3 +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 16 ++-------------- clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 63447450a8962..ca48f72c475bb 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -618,6 +618,9 @@ class ExprEngine { void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst); + void VisitStmtExpr(const StmtExpr *S, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + /// VisitUnaryOperator - Transfer function logic for unary operators. void VisitUnaryOperator(const UnaryOperator* B, ExplodedNode *Pred, ExplodedNodeSet &Dst); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index d7ff19ace0cde..a64d8eb3feddb 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -2300,21 +2300,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Dst); break; - case Stmt::StmtExprClass: { - const auto *SE = cast(S); - - if (SE->getSubStmt()->body_empty()) { - // Empty statement expression. - assert(SE->getType() == getContext().VoidTy - && "Empty statement expression must have void type."); - } else if (const auto *LastExpr = - dyn_cast(*SE->getSubStmt()->body_rbegin())) { - SVal Val = Pred->getState()->getSVal(LastExpr, Pred->getStackFrame()); - Pred = Engine.makeNodeWithBinding(Pred, SE, Val); - } - Dst.insert(Pred); + case Stmt::StmtExprClass: + VisitStmtExpr(cast(S), Pred, Dst); break; - } case Stmt::UnaryOperatorClass: { const auto *U = cast(S); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 6127328cefe23..aaadd4c9167ed 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -855,6 +855,21 @@ VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this); } +void ExprEngine::VisitStmtExpr(const StmtExpr *S, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const auto *SE = cast(S); + if (SE->getSubStmt()->body_empty()) { + // Empty statement expression. + assert(SE->getType() == getContext().VoidTy && + "Empty statement expression must have void type."); + } else if (const auto *LastExpr = + dyn_cast(*SE->getSubStmt()->body_rbegin())) { + SVal Val = Pred->getState()->getSVal(LastExpr, Pred->getStackFrame()); + Pred = Engine.makeNodeWithBinding(Pred, SE, Val); + } + Dst.insert(Pred); +} + void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, ExplodedNodeSet &Dst) { // FIXME: Prechecks eventually go in ::Visit(). From 669559997cd4ca7b6cef84c45936cf1de879e863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Fri, 7 Aug 2026 17:39:22 +0200 Subject: [PATCH 04/31] `ExprEngine::OffsetOfExpr` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index a64d8eb3feddb..646c917a86fad 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1105,6 +1105,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { default: return false; case Stmt::CXXBindTemporaryExprClass: + case Stmt::OffsetOfExprClass: return true; } } @@ -1114,6 +1115,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { default: return false; case Stmt::CXXBindTemporaryExprClass: + case Stmt::OffsetOfExprClass: return true; } } @@ -2283,17 +2285,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, VisitReturnStmt(cast(S), Pred, Dst); break; - case Stmt::OffsetOfExprClass: { - ExplodedNodeSet PreVisit; - getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); - - ExplodedNodeSet PostVisit; - for (const auto Node : PreVisit) - VisitOffsetOfExpr(cast(S), Node, PostVisit); - - getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); + case Stmt::OffsetOfExprClass: + VisitOffsetOfExpr(cast(S), Pred, Dst); break; - } case Stmt::UnaryExprOrTypeTraitExprClass: VisitUnaryExprOrTypeTraitExpr(cast(S), Pred, From 19d7f0cd055ab130057c2ed6d6f378acbab661cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Mon, 10 Aug 2026 14:42:21 +0200 Subject: [PATCH 05/31] `ExprEngine::VisitUnaryOperator` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 14 +-- clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 93 +++++++++---------- 2 files changed, 50 insertions(+), 57 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 646c917a86fad..b59023468685e 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1106,6 +1106,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { return false; case Stmt::CXXBindTemporaryExprClass: case Stmt::OffsetOfExprClass: + case Stmt::UnaryOperatorClass: return true; } } @@ -1116,6 +1117,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { return false; case Stmt::CXXBindTemporaryExprClass: case Stmt::OffsetOfExprClass: + case Stmt::UnaryOperatorClass: return true; } } @@ -2298,17 +2300,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, VisitStmtExpr(cast(S), Pred, Dst); break; - case Stmt::UnaryOperatorClass: { - const auto *U = cast(S); - if (AMgr.options.ShouldEagerlyAssume && (U->getOpcode() == UO_LNot)) { - ExplodedNodeSet Tmp; - VisitUnaryOperator(U, Pred, Tmp); - evalEagerlyAssumeBifurcation(Dst, Tmp, U); - } - else - VisitUnaryOperator(U, Pred, Dst); + case Stmt::UnaryOperatorClass: + VisitUnaryOperator(cast(S), Pred, Dst); break; - } case Stmt::PseudoObjectExprClass: { const auto *PE = cast(S); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index aaadd4c9167ed..771009b9139a6 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -870,13 +870,8 @@ void ExprEngine::VisitStmtExpr(const StmtExpr *S, ExplodedNode *Pred, Dst.insert(Pred); } -void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, +void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - // FIXME: Prechecks eventually go in ::Visit(). - ExplodedNodeSet CheckedSet; - getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, U, *this); - - ExplodedNodeSet EvalSet; // Lambda for handling the case when the operand is returned unchanged. auto MakeNodeForIdentityOp = [U, &Engine = Engine](ExplodedNode *N) { @@ -885,12 +880,11 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, return Engine.makeNodeWithBinding(N, U, SV); }; - for (ExplodedNode *N : CheckedSet) { + auto VisitUnaryOperatorImpl = [U, Pred, &Engine = Engine, this, + MakeNodeForIdentityOp](ExplodedNodeSet &Dst) { switch (U->getOpcode()) { default: { - ExplodedNodeSet Tmp; - VisitIncrementDecrementOperator(U, N, Tmp); - EvalSet.insert(Tmp); + VisitIncrementDecrementOperator(U, Pred, Dst); break; } case UO_Real: { @@ -899,13 +893,13 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, // FIXME: We don't have complex SValues yet. if (Ex->getType()->isAnyComplexType()) { // Just report "Unknown." - EvalSet.insert(N); + Dst.insert(Pred); break; } // For all other types, UO_Real is an identity operation. assert (U->getType() == Ex->getType()); - EvalSet.insert(MakeNodeForIdentityOp(N)); + Dst.insert(MakeNodeForIdentityOp(Pred)); break; } @@ -914,12 +908,12 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, // FIXME: We don't have complex SValues yet. if (Ex->getType()->isAnyComplexType()) { // Just report "Unknown." - EvalSet.insert(N); + Dst.insert(Pred); break; } // For all other types, UO_Imag returns 0. SVal X = svalBuilder.makeZeroVal(Ex->getType()); - EvalSet.insert(Engine.makeNodeWithBinding(N, U, X)); + Dst.insert(Engine.makeNodeWithBinding(Pred, U, X)); break; } @@ -931,12 +925,12 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, if (isa(VD)) { SVal SV = svalBuilder.getMemberPointer(cast(VD)); - EvalSet.insert(Engine.makeNodeWithBinding(N, U, SV)); + Dst.insert(Engine.makeNodeWithBinding(Pred, U, SV)); break; } } // Explicitly proceed with default handler for this case cascade. - EvalSet.insert(MakeNodeForIdentityOp(N)); + Dst.insert(MakeNodeForIdentityOp(Pred)); break; } case UO_Plus: @@ -944,7 +938,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, [[fallthrough]]; case UO_Deref: case UO_Extension: { - EvalSet.insert(MakeNodeForIdentityOp(N)); + Dst.insert(MakeNodeForIdentityOp(Pred)); break; } @@ -953,57 +947,62 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, case UO_Not: { assert (!U->isGLValue()); const Expr *Ex = U->getSubExpr()->IgnoreParens(); - ProgramStateRef state = N->getState(); - const StackFrame *SF = N->getStackFrame(); + ProgramStateRef state = Pred->getState(); + const StackFrame *SF = Pred->getStackFrame(); // Get the value of the subexpression. SVal V = state->getSVal(Ex, SF); if (V.isUnknownOrUndef()) { - EvalSet.insert(Engine.makeNodeWithBinding(N, U, V)); + Dst.insert(Engine.makeNodeWithBinding(Pred, U, V)); break; } switch (U->getOpcode()) { - default: - llvm_unreachable("Invalid Opcode."); - case UO_Not: - // FIXME: Do we need to handle promotions? - state = state->BindExpr( - U, SF, svalBuilder.evalComplement(V.castAs())); - break; - case UO_Minus: - // FIXME: Do we need to handle promotions? - state = - state->BindExpr(U, SF, svalBuilder.evalMinus(V.castAs())); - break; - case UO_LNot: - // C99 6.5.3.3: "The expression !E is equivalent to (0==E)." - // - // Note: technically we do "E == 0", but this is the same in the - // transfer functions as "0 == E". - SVal Result; - if (std::optional LV = V.getAs()) { + default: + llvm_unreachable("Invalid Opcode."); + case UO_Not: + // FIXME: Do we need to handle promotions? + state = state->BindExpr(U, SF, + svalBuilder.evalComplement(V.castAs())); + break; + case UO_Minus: + // FIXME: Do we need to handle promotions? + state = + state->BindExpr(U, SF, svalBuilder.evalMinus(V.castAs())); + break; + case UO_LNot: + // C99 6.5.3.3: "The expression !E is equivalent to (0==E)." + // + // Note: technically we do "E == 0", but this is the same in the + // transfer functions as "0 == E". + SVal Result; + if (std::optional LV = V.getAs()) { Loc X = svalBuilder.makeNullWithType(Ex->getType()); Result = evalBinOp(state, BO_EQ, *LV, X, U->getType()); - } else if (Ex->getType()->isFloatingType()) { + } else if (Ex->getType()->isFloatingType()) { // FIXME: handle floating point types. Result = UnknownVal(); - } else { + } else { nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); Result = evalBinOp(state, BO_EQ, V.castAs(), X, U->getType()); - } + } - state = state->BindExpr(U, SF, Result); - break; + state = state->BindExpr(U, SF, Result); + break; } - EvalSet.insert(Engine.makePostStmtNode(U, state, N)); + Dst.insert(Engine.makePostStmtNode(U, state, Pred)); break; } } - } + }; - getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, U, *this); + if (AMgr.options.ShouldEagerlyAssume && (U->getOpcode() == UO_LNot)) { + ExplodedNodeSet Tmp; + VisitUnaryOperatorImpl(Tmp); + evalEagerlyAssumeBifurcation(Dst, Tmp, U); + } else + VisitUnaryOperatorImpl(Dst); } void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, From f26640fd93b0de980e72b727fded2953dcf07926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Mon, 10 Aug 2026 14:59:53 +0200 Subject: [PATCH 06/31] `ExprEngine::VisitPseudoObjectExpr` --- .../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 3 +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 9 ++------- clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 9 +++++++++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index ca48f72c475bb..2b49948adce59 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -625,6 +625,9 @@ class ExprEngine { void VisitUnaryOperator(const UnaryOperator* B, ExplodedNode *Pred, ExplodedNodeSet &Dst); + void VisitPseudoObjectExpr(const PseudoObjectExpr *PE, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + /// Handle ++ and -- (both pre- and post-increment). void VisitIncrementDecrementOperator(const UnaryOperator* U, ExplodedNode *Pred, diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index b59023468685e..348944b1a5040 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -2304,14 +2304,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, VisitUnaryOperator(cast(S), Pred, Dst); break; - case Stmt::PseudoObjectExprClass: { - const auto *PE = cast(S); - SVal V = UnknownVal(); - if (const Expr *Result = PE->getResultExpr()) - V = Pred->getState()->getSVal(Result, Pred->getStackFrame()); - Dst.insert(Engine.makeNodeWithBinding(Pred, PE, V)); + case Stmt::PseudoObjectExprClass: + VisitPseudoObjectExpr(cast(S), Pred, Dst); break; - } case Expr::ObjCIndirectCopyRestoreExprClass: { // ObjCIndirectCopyRestoreExpr implies passing a temporary for diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 771009b9139a6..f72340e07028f 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -1005,6 +1005,15 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred, VisitUnaryOperatorImpl(Dst); } +void ExprEngine::VisitPseudoObjectExpr(const PseudoObjectExpr *PE, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + SVal V = UnknownVal(); + if (const Expr *Result = PE->getResultExpr()) + V = Pred->getState()->getSVal(Result, Pred->getStackFrame()); + Dst.insert(Engine.makeNodeWithBinding(Pred, PE, V)); +} + void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, ExplodedNode *Pred, ExplodedNodeSet &Dst) { From 4876853f8f0fa3f4c958fa79b7e69312736d0409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Mon, 10 Aug 2026 15:17:49 +0200 Subject: [PATCH 07/31] `ExprEngine::VisitObjCIndirectCopyRestoreClass` --- .../StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 4 ++++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 12 +++--------- clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 11 +++++++++++ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 2b49948adce59..d9e626b51f38e 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -628,6 +628,10 @@ class ExprEngine { void VisitPseudoObjectExpr(const PseudoObjectExpr *PE, ExplodedNode *Pred, ExplodedNodeSet &Dst); + void VisitObjCIndirectCopyRestoreClass(const ObjCIndirectCopyRestoreExpr *OIE, + ExplodedNode *Pred, + ExplodedNodeSet &Dst); + /// Handle ++ and -- (both pre- and post-increment). void VisitIncrementDecrementOperator(const UnaryOperator* U, ExplodedNode *Pred, diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 348944b1a5040..61c911495e4ec 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -2308,17 +2308,11 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, VisitPseudoObjectExpr(cast(S), Pred, Dst); break; - case Expr::ObjCIndirectCopyRestoreExprClass: { - // ObjCIndirectCopyRestoreExpr implies passing a temporary for - // correctness of lifetime management. Due to limited analysis - // of ARC, this is implemented as direct arg passing. - const auto *OIE = cast(S); - const Expr *E = OIE->getSubExpr(); - SVal V = Pred->getState()->getSVal(E, Pred->getStackFrame()); - Dst.insert(Engine.makeNodeWithBinding(Pred, OIE, V)); + case Expr::ObjCIndirectCopyRestoreExprClass: + VisitObjCIndirectCopyRestoreClass(cast(S), + Pred, Dst); break; } - } } bool ExprEngine::replayWithoutInlining(ExplodedNode *N, diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index f72340e07028f..c2abbed0942bd 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -1014,6 +1014,17 @@ void ExprEngine::VisitPseudoObjectExpr(const PseudoObjectExpr *PE, Dst.insert(Engine.makeNodeWithBinding(Pred, PE, V)); } +void ExprEngine::VisitObjCIndirectCopyRestoreClass( + const ObjCIndirectCopyRestoreExpr *OIE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + // ObjCIndirectCopyRestoreExpr implies passing a temporary for + // correctness of lifetime management. Due to limited analysis + // of ARC, this is implemented as direct arg passing. + const Expr *E = OIE->getSubExpr(); + SVal V = Pred->getState()->getSVal(E, Pred->getStackFrame()); + Dst.insert(Engine.makeNodeWithBinding(Pred, OIE, V)); +} + void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, ExplodedNode *Pred, ExplodedNodeSet &Dst) { From ab222e44277d10e5f2b0a5a7fc173824f2e33300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Mon, 10 Aug 2026 15:56:56 +0200 Subject: [PATCH 08/31] `ExprEngine::VisitMaterializeTemporaryExpr` --- .../StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 5 ++--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 14 +++++--------- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 12 ++++++------ 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index d9e626b51f38e..08cac076ffad4 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -668,9 +668,8 @@ class ExprEngine { ExplodedNodeSet &Dst); /// Create a C++ temporary object for an rvalue. - void CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, - ExplodedNode *Pred, - ExplodedNodeSet &Dst); + void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE, + ExplodedNode *Pred, ExplodedNodeSet &Dst); void ConstructInitList(const Expr *Source, ArrayRef Args, bool IsTransparent, ExplodedNode *Pred, diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 61c911495e4ec..181900465d6a4 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1105,6 +1105,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { default: return false; case Stmt::CXXBindTemporaryExprClass: + case Stmt::MaterializeTemporaryExprClass: case Stmt::OffsetOfExprClass: case Stmt::UnaryOperatorClass: return true; @@ -1116,6 +1117,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { default: return false; case Stmt::CXXBindTemporaryExprClass: + case Stmt::MaterializeTemporaryExprClass: case Stmt::OffsetOfExprClass: case Stmt::UnaryOperatorClass: return true; @@ -2232,16 +2234,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; } - case Expr::MaterializeTemporaryExprClass: { - const auto *MTE = cast(S); - ExplodedNodeSet dstPrevisit; - getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, MTE, *this); - ExplodedNodeSet dstExpr; - for (const auto i : dstPrevisit) - CreateCXXTemporaryObject(MTE, i, dstExpr); - getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, MTE, *this); + case Expr::MaterializeTemporaryExprClass: + VisitMaterializeTemporaryExpr(cast(S), Pred, + Dst); break; - } case Stmt::InitListExprClass: { const InitListExpr *E = cast(S); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 3df2d3d9e3674..4c6da8bc84c93 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -30,15 +30,15 @@ using namespace clang; using namespace ento; -void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, - ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - const Expr *tempExpr = ME->getSubExpr()->IgnoreParens(); +void ExprEngine::VisitMaterializeTemporaryExpr( + const MaterializeTemporaryExpr *MTE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const Expr *tempExpr = MTE->getSubExpr()->IgnoreParens(); ProgramStateRef state = Pred->getState(); const StackFrame *SF = Pred->getStackFrame(); - state = createTemporaryRegionIfNeeded(state, SF, tempExpr, ME); - Dst.insert(Engine.makePostStmtNode(ME, state, Pred)); + state = createTemporaryRegionIfNeeded(state, SF, tempExpr, MTE); + Dst.insert(Engine.makePostStmtNode(MTE, state, Pred)); } void ExprEngine::performTrivialCopy(ExplodedNodeSet &Dst, ExplodedNode *Pred, From 6db5fb6bed563b30029ebd40b9d01750cdc52673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Mon, 10 Aug 2026 16:15:59 +0200 Subject: [PATCH 09/31] `ExprEngine::VisitCXXParenListInitExpr` --- .../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 3 +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 7 ++----- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 6 ++++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 08cac076ffad4..9abf64a75b808 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -643,6 +643,9 @@ class ExprEngine { void VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred, ExplodedNodeSet &Dst); + void VisitCXXParenListInitExpr(const CXXParenListInitExpr *E, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + void VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, ExplodedNodeSet & Dst); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 181900465d6a4..01dd5a4659817 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -2245,12 +2245,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; } - case Expr::CXXParenListInitExprClass: { - const CXXParenListInitExpr *E = cast(S); - ConstructInitList(E, E->getInitExprs(), /*IsTransparent*/ false, Pred, - Dst); + case Expr::CXXParenListInitExprClass: + VisitCXXParenListInitExpr(cast(S), Pred, Dst); break; - } case Stmt::MemberExprClass: VisitMemberExpr(cast(S), Pred, Dst); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 4c6da8bc84c93..2f459f76c3fd0 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -1102,6 +1102,12 @@ void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred, Dst.insert(Engine.makePostStmtNode(CS, state, Pred)); } +void ExprEngine::VisitCXXParenListInitExpr(const CXXParenListInitExpr *E, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + ConstructInitList(E, E->getInitExprs(), /*IsTransparent*/ false, Pred, Dst); +} + void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { // Get the this object region from StoreManager. From da0c6f92054c5058da050dc98c1c6b63fb99fa2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Mon, 10 Aug 2026 18:15:47 +0200 Subject: [PATCH 10/31] `ExprEngine::VisitCastExpr` --- .../Core/PathSensitive/ExprEngine.h | 7 +- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 30 +- clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 452 +++++++++--------- 3 files changed, 248 insertions(+), 241 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 9abf64a75b808..52d94d25ee096 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -546,9 +546,10 @@ class ExprEngine { void VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, ExplodedNodeSet &Dst); - /// VisitCast - Transfer function logic for all casts (implicit and explicit). - void VisitCast(const CastExpr *CastE, const Expr *Ex, ExplodedNode *Pred, - ExplodedNodeSet &Dst); + /// VisitCastExpr - Transfer function logic for all casts (implicit and + /// explicit). + void VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred, + ExplodedNodeSet &Dst); /// VisitCompoundLiteralExpr - Transfer function logic for compound literals. void VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 01dd5a4659817..f244eb164e5de 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1104,6 +1104,16 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { switch (S->getStmtClass()) { default: return false; + case Stmt::ImplicitCastExprClass: + case Stmt::CStyleCastExprClass: + case Stmt::CXXStaticCastExprClass: + case Stmt::CXXDynamicCastExprClass: + case Stmt::CXXReinterpretCastExprClass: + case Stmt::CXXConstCastExprClass: + case Stmt::CXXFunctionalCastExprClass: + case Stmt::BuiltinBitCastExprClass: + case Stmt::ObjCBridgedCastExprClass: + case Stmt::CXXAddrspaceCastExprClass: case Stmt::CXXBindTemporaryExprClass: case Stmt::MaterializeTemporaryExprClass: case Stmt::OffsetOfExprClass: @@ -1116,6 +1126,16 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { switch (S->getStmtClass()) { default: return false; + case Stmt::ImplicitCastExprClass: + case Stmt::CStyleCastExprClass: + case Stmt::CXXStaticCastExprClass: + case Stmt::CXXDynamicCastExprClass: + case Stmt::CXXReinterpretCastExprClass: + case Stmt::CXXConstCastExprClass: + case Stmt::CXXFunctionalCastExprClass: + case Stmt::BuiltinBitCastExprClass: + case Stmt::ObjCBridgedCastExprClass: + case Stmt::CXXAddrspaceCastExprClass: case Stmt::CXXBindTemporaryExprClass: case Stmt::MaterializeTemporaryExprClass: case Stmt::OffsetOfExprClass: @@ -2224,15 +2244,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXFunctionalCastExprClass: case Stmt::BuiltinBitCastExprClass: case Stmt::ObjCBridgedCastExprClass: - case Stmt::CXXAddrspaceCastExprClass: { - const auto *C = cast(S); - ExplodedNodeSet dstExpr; - VisitCast(C, C->getSubExpr(), Pred, dstExpr); - - // Handle the postvisit checks. - getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this); + case Stmt::CXXAddrspaceCastExprClass: + VisitCastExpr(cast(S), Pred, Dst); break; - } case Expr::MaterializeTemporaryExprClass: VisitMaterializeTemporaryExpr(cast(S), Pred, diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index c2abbed0942bd..45c3fa14f5cf8 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -250,18 +250,14 @@ ExprEngine::handleLValueBitCast(ProgramStateRef state, const Expr *Ex, return state; } -void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, - ExplodedNode *Pred, ExplodedNodeSet &Dst) { - - ExplodedNodeSet DstPreStmt; - getCheckerManager().runCheckersForPreStmt(DstPreStmt, Pred, CastE, *this); +void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const Expr *Ex = CastE->getSubExpr(); if (CastE->getCastKind() == CK_LValueToRValue) { - for (ExplodedNode *Node : DstPreStmt) { - ProgramStateRef State = Node->getState(); - const StackFrame *SF = Node->getStackFrame(); - evalLoad(Dst, CastE, CastE, Node, State, State->getSVal(Ex, SF)); - } + ProgramStateRef State = Pred->getState(); + const StackFrame *SF = Pred->getStackFrame(); + evalLoad(Dst, CastE, CastE, Pred, State, State->getSVal(Ex, SF)); return; } if (CastE->getCastKind() == CK_LValueToRValueBitCast) { @@ -269,12 +265,10 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, ExplodedNodeSet DstEvalLoc; // Simulate the lvalue-to-rvalue conversion on `Ex`: - for (ExplodedNode *Node : DstPreStmt) { - ProgramStateRef State = Node->getState(); - const StackFrame *SF = Node->getStackFrame(); - evalLocation(DstEvalLoc, CastE, Ex, Node, State, State->getSVal(Ex, SF), - true); - } + ProgramStateRef State = Pred->getState(); + const StackFrame *SF = Pred->getStackFrame(); + evalLocation(DstEvalLoc, CastE, Ex, Pred, State, State->getSVal(Ex, SF), + true); // Simulate the operation that actually casts the original value to a new // value of the destination type : @@ -303,228 +297,226 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, if (const ExplicitCastExpr *ExCast=dyn_cast_or_null(CastE)) T = ExCast->getTypeAsWritten(); - for (ExplodedNode *Pred : DstPreStmt) { + ProgramStateRef state = Pred->getState(); + const StackFrame *SF = Pred->getStackFrame(); + + switch (CastE->getCastKind()) { + case CK_LValueToRValue: + case CK_LValueToRValueBitCast: + llvm_unreachable("LValueToRValue casts handled earlier."); + case CK_ToVoid: + Dst.insert(Pred); + return; + // The analyzer doesn't do anything special with these casts, + // since it understands retain/release semantics already. + case CK_ARCProduceObject: + case CK_ARCConsumeObject: + case CK_ARCReclaimReturnedObject: + case CK_ARCExtendBlockObject: // Fall-through. + case CK_CopyAndAutoreleaseBlockObject: + // The analyser can ignore atomic casts for now, although some future + // checkers may want to make certain that you're not modifying the same + // value through atomic and nonatomic pointers. + case CK_AtomicToNonAtomic: + case CK_NonAtomicToAtomic: + // True no-ops. + case CK_NoOp: + case CK_ConstructorConversion: + case CK_UserDefinedConversion: + case CK_FunctionToPointerDecay: + case CK_BuiltinFnToFnPtr: + case CK_HLSLArrayRValue: { + // Copy the SVal of Ex to CastE. ProgramStateRef state = Pred->getState(); const StackFrame *SF = Pred->getStackFrame(); - - switch (CastE->getCastKind()) { - case CK_LValueToRValue: - case CK_LValueToRValueBitCast: - llvm_unreachable("LValueToRValue casts handled earlier."); - case CK_ToVoid: - Dst.insert(Pred); - continue; - // The analyzer doesn't do anything special with these casts, - // since it understands retain/release semantics already. - case CK_ARCProduceObject: - case CK_ARCConsumeObject: - case CK_ARCReclaimReturnedObject: - case CK_ARCExtendBlockObject: // Fall-through. - case CK_CopyAndAutoreleaseBlockObject: - // The analyser can ignore atomic casts for now, although some future - // checkers may want to make certain that you're not modifying the same - // value through atomic and nonatomic pointers. - case CK_AtomicToNonAtomic: - case CK_NonAtomicToAtomic: - // True no-ops. - case CK_NoOp: - case CK_ConstructorConversion: - case CK_UserDefinedConversion: - case CK_FunctionToPointerDecay: - case CK_BuiltinFnToFnPtr: - case CK_HLSLArrayRValue: { - // Copy the SVal of Ex to CastE. - ProgramStateRef state = Pred->getState(); - const StackFrame *SF = Pred->getStackFrame(); - SVal V = state->getSVal(Ex, SF); - Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V)); - continue; - } - case CK_MemberPointerToBoolean: - case CK_PointerToBoolean: { - SVal V = state->getSVal(Ex, SF); - auto PTMSV = V.getAs(); - if (PTMSV) - V = svalBuilder.makeTruthVal(!PTMSV->isNullMemberPointer(), ExTy); - if (V.isUndef() || PTMSV) { - Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V)); - continue; - } - // Explicitly proceed with default handler for this case cascade. - state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred); - continue; - } - case CK_Dependent: - case CK_ArrayToPointerDecay: - case CK_BitCast: - case CK_AddressSpaceConversion: - case CK_BooleanToSignedIntegral: - case CK_IntegralToPointer: - case CK_PointerToIntegral: { - SVal V = state->getSVal(Ex, SF); - if (isa(V)) { - Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, UnknownVal())); - continue; - } - // Explicitly proceed with default handler for this case cascade. - state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred); - continue; - } - case CK_IntegralToBoolean: - case CK_IntegralToFloating: - case CK_FloatingToIntegral: - case CK_FloatingToBoolean: - case CK_FloatingCast: - case CK_FloatingRealToComplex: - case CK_FloatingComplexToReal: - case CK_FloatingComplexToBoolean: - case CK_FloatingComplexCast: - case CK_FloatingComplexToIntegralComplex: - case CK_IntegralRealToComplex: - case CK_IntegralComplexToReal: - case CK_IntegralComplexToBoolean: - case CK_IntegralComplexCast: - case CK_IntegralComplexToFloatingComplex: - case CK_CPointerToObjCPointerCast: - case CK_BlockPointerToObjCPointerCast: - case CK_AnyPointerToBlockPointerCast: - case CK_ObjCObjectLValueCast: - case CK_ZeroToOCLOpaqueType: - case CK_IntToOCLSampler: - case CK_LValueBitCast: - case CK_FloatingToFixedPoint: - case CK_FixedPointToFloating: - case CK_FixedPointCast: - case CK_FixedPointToBoolean: - case CK_FixedPointToIntegral: - case CK_IntegralToFixedPoint: { - state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred); - continue; - } - case CK_IntegralCast: { - // Delegate to SValBuilder to process. - SVal V = state->getSVal(Ex, SF); - if (AMgr.options.analyzerSymbolicIntegerCasts()) - V = svalBuilder.evalCast(V, T, ExTy); - else - V = svalBuilder.evalIntegralCast(state, V, T, ExTy); - Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V)); - continue; - } - case CK_DerivedToBase: - case CK_UncheckedDerivedToBase: { - // For DerivedToBase cast, delegate to the store manager. - SVal val = state->getSVal(Ex, SF); - val = getStoreManager().evalDerivedToBase(val, CastE); - Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, val)); - continue; - } - // Handle C++ dyn_cast. - case CK_Dynamic: { - SVal val = state->getSVal(Ex, SF); - - // Compute the type of the result. - QualType resultType = CastE->getType(); - if (CastE->isGLValue()) - resultType = getContext().getPointerType(resultType); - - bool Failed = true; - - // Check if the value being cast does not evaluates to 0. - if (!val.isZeroConstant()) - if (std::optional V = - StateMgr.getStoreManager().evalBaseToDerived(val, T)) { - val = *V; - Failed = false; - } - - if (Failed) { - if (T->isReferenceType()) { - // A bad_cast exception is thrown if input value is a reference. - // Currently, we model this, by generating a sink. - Engine.makePostStmtNode(CastE, state, Pred, /*MarkAsSink=*/true); - continue; - } else { - // If the cast fails on a pointer, bind to 0. - state = state->BindExpr(CastE, SF, - svalBuilder.makeNullWithType(resultType)); - } - } else { - // If we don't know if the cast succeeded, conjure a new symbol. - if (val.isUnknown()) { - DefinedOrUnknownSVal NewSym = svalBuilder.conjureSymbolVal( - /*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType, - getNumVisitedCurrent()); - state = state->BindExpr(CastE, SF, NewSym); - } else - // Else, bind to the derived region value. - state = state->BindExpr(CastE, SF, val); - } - Dst.insert(Engine.makePostStmtNode(CastE, state, Pred)); - continue; + SVal V = state->getSVal(Ex, SF); + Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V)); + return; + } + case CK_MemberPointerToBoolean: + case CK_PointerToBoolean: { + SVal V = state->getSVal(Ex, SF); + auto PTMSV = V.getAs(); + if (PTMSV) + V = svalBuilder.makeTruthVal(!PTMSV->isNullMemberPointer(), ExTy); + if (V.isUndef() || PTMSV) { + Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V)); + return; + } + // Explicitly proceed with default handler for this case cascade. + state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred); + return; + } + case CK_Dependent: + case CK_ArrayToPointerDecay: + case CK_BitCast: + case CK_AddressSpaceConversion: + case CK_BooleanToSignedIntegral: + case CK_IntegralToPointer: + case CK_PointerToIntegral: { + SVal V = state->getSVal(Ex, SF); + if (isa(V)) { + Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, UnknownVal())); + return; + } + // Explicitly proceed with default handler for this case cascade. + state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred); + return; + } + case CK_IntegralToBoolean: + case CK_IntegralToFloating: + case CK_FloatingToIntegral: + case CK_FloatingToBoolean: + case CK_FloatingCast: + case CK_FloatingRealToComplex: + case CK_FloatingComplexToReal: + case CK_FloatingComplexToBoolean: + case CK_FloatingComplexCast: + case CK_FloatingComplexToIntegralComplex: + case CK_IntegralRealToComplex: + case CK_IntegralComplexToReal: + case CK_IntegralComplexToBoolean: + case CK_IntegralComplexCast: + case CK_IntegralComplexToFloatingComplex: + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_ObjCObjectLValueCast: + case CK_ZeroToOCLOpaqueType: + case CK_IntToOCLSampler: + case CK_LValueBitCast: + case CK_FloatingToFixedPoint: + case CK_FixedPointToFloating: + case CK_FixedPointCast: + case CK_FixedPointToBoolean: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: { + state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred); + return; + } + case CK_IntegralCast: { + // Delegate to SValBuilder to process. + SVal V = state->getSVal(Ex, SF); + if (AMgr.options.analyzerSymbolicIntegerCasts()) + V = svalBuilder.evalCast(V, T, ExTy); + else + V = svalBuilder.evalIntegralCast(state, V, T, ExTy); + Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V)); + return; + } + case CK_DerivedToBase: + case CK_UncheckedDerivedToBase: { + // For DerivedToBase cast, delegate to the store manager. + SVal val = state->getSVal(Ex, SF); + val = getStoreManager().evalDerivedToBase(val, CastE); + Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, val)); + return; + } + // Handle C++ dyn_cast. + case CK_Dynamic: { + SVal val = state->getSVal(Ex, SF); + + // Compute the type of the result. + QualType resultType = CastE->getType(); + if (CastE->isGLValue()) + resultType = getContext().getPointerType(resultType); + + bool Failed = true; + + // Check if the value being cast does not evaluates to 0. + if (!val.isZeroConstant()) + if (std::optional V = + StateMgr.getStoreManager().evalBaseToDerived(val, T)) { + val = *V; + Failed = false; } - case CK_BaseToDerived: { - SVal val = state->getSVal(Ex, SF); - QualType resultType = CastE->getType(); - if (CastE->isGLValue()) - resultType = getContext().getPointerType(resultType); - - if (!val.isConstant()) { - std::optional V = getStoreManager().evalBaseToDerived(val, T); - val = V ? *V : UnknownVal(); - } - // Failed to cast or the result is unknown, fall back to conservative. - if (val.isUnknown()) { - val = svalBuilder.conjureSymbolVal( - /*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType, - getNumVisitedCurrent()); - } - Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, val)); - continue; - } - case CK_NullToPointer: { - SVal V = svalBuilder.makeNullWithType(CastE->getType()); - Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V)); - continue; - } - case CK_NullToMemberPointer: { - SVal V = svalBuilder.getMemberPointer(nullptr); - Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V)); - continue; - } - case CK_DerivedToBaseMemberPointer: - case CK_BaseToDerivedMemberPointer: - case CK_ReinterpretMemberPointer: { - SVal V = state->getSVal(Ex, SF); - if (auto PTMSV = V.getAs()) { - SVal CastedPTMSV = - svalBuilder.makePointerToMember(getBasicVals().accumCXXBase( - CastE->path(), *PTMSV, CastE->getCastKind())); - Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, CastedPTMSV)); - continue; - } - // Explicitly proceed with default handler for this case cascade. + if (Failed) { + if (T->isReferenceType()) { + // A bad_cast exception is thrown if input value is a reference. + // Currently, we model this, by generating a sink. + Engine.makePostStmtNode(CastE, state, Pred, /*MarkAsSink=*/true); + return; + } else { + // If the cast fails on a pointer, bind to 0. + state = state->BindExpr(CastE, SF, + svalBuilder.makeNullWithType(resultType)); } - [[fallthrough]]; - // Various C++ casts that are not handled yet. - case CK_ToUnion: - case CK_MatrixCast: - case CK_VectorSplat: - case CK_HLSLElementwiseCast: - case CK_HLSLAggregateSplatCast: - case CK_HLSLMatrixTruncation: - case CK_HLSLVectorTruncation: { - QualType resultType = CastE->getType(); - if (CastE->isGLValue()) - resultType = getContext().getPointerType(resultType); - SVal result = svalBuilder.conjureSymbolVal( + } else { + // If we don't know if the cast succeeded, conjure a new symbol. + if (val.isUnknown()) { + DefinedOrUnknownSVal NewSym = svalBuilder.conjureSymbolVal( /*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType, getNumVisitedCurrent()); - Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, result)); - continue; - } + state = state->BindExpr(CastE, SF, NewSym); + } else + // Else, bind to the derived region value. + state = state->BindExpr(CastE, SF, val); + } + Dst.insert(Engine.makePostStmtNode(CastE, state, Pred)); + return; + } + case CK_BaseToDerived: { + SVal val = state->getSVal(Ex, SF); + QualType resultType = CastE->getType(); + if (CastE->isGLValue()) + resultType = getContext().getPointerType(resultType); + + if (!val.isConstant()) { + std::optional V = getStoreManager().evalBaseToDerived(val, T); + val = V ? *V : UnknownVal(); + } + + // Failed to cast or the result is unknown, fall back to conservative. + if (val.isUnknown()) { + val = svalBuilder.conjureSymbolVal( + /*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType, + getNumVisitedCurrent()); } + Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, val)); + return; + } + case CK_NullToPointer: { + SVal V = svalBuilder.makeNullWithType(CastE->getType()); + Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V)); + return; + } + case CK_NullToMemberPointer: { + SVal V = svalBuilder.getMemberPointer(nullptr); + Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, V)); + return; + } + case CK_DerivedToBaseMemberPointer: + case CK_BaseToDerivedMemberPointer: + case CK_ReinterpretMemberPointer: { + SVal V = state->getSVal(Ex, SF); + if (auto PTMSV = V.getAs()) { + SVal CastedPTMSV = + svalBuilder.makePointerToMember(getBasicVals().accumCXXBase( + CastE->path(), *PTMSV, CastE->getCastKind())); + Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, CastedPTMSV)); + return; + } + // Explicitly proceed with default handler for this case cascade. + } + [[fallthrough]]; + // Various C++ casts that are not handled yet. + case CK_ToUnion: + case CK_MatrixCast: + case CK_VectorSplat: + case CK_HLSLElementwiseCast: + case CK_HLSLAggregateSplatCast: + case CK_HLSLMatrixTruncation: + case CK_HLSLVectorTruncation: { + QualType resultType = CastE->getType(); + if (CastE->isGLValue()) + resultType = getContext().getPointerType(resultType); + SVal result = svalBuilder.conjureSymbolVal( + /*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType, + getNumVisitedCurrent()); + Dst.insert(Engine.makeNodeWithBinding(Pred, CastE, result)); + return; + } } } From 17716d65a33025e142e8d5d277d874781b2a4198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Wed, 12 Aug 2026 14:24:28 +0200 Subject: [PATCH 11/31] `ExprEngine::VisitCXXNewExpr` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index f244eb164e5de..0b9000c67a545 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1115,6 +1115,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { case Stmt::ObjCBridgedCastExprClass: case Stmt::CXXAddrspaceCastExprClass: case Stmt::CXXBindTemporaryExprClass: + case Stmt::CXXNewExprClass: case Stmt::MaterializeTemporaryExprClass: case Stmt::OffsetOfExprClass: case Stmt::UnaryOperatorClass: @@ -1137,6 +1138,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { case Stmt::ObjCBridgedCastExprClass: case Stmt::CXXAddrspaceCastExprClass: case Stmt::CXXBindTemporaryExprClass: + case Stmt::CXXNewExprClass: case Stmt::MaterializeTemporaryExprClass: case Stmt::OffsetOfExprClass: case Stmt::UnaryOperatorClass: @@ -2172,18 +2174,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Dst); break; - case Stmt::CXXNewExprClass: { - - ExplodedNodeSet PreVisit; - getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); - - ExplodedNodeSet PostVisit; - for (const auto i : PreVisit) - VisitCXXNewExpr(cast(S), i, PostVisit); - - getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); + case Stmt::CXXNewExprClass: + VisitCXXNewExpr(cast(S), Pred, Dst); break; - } case Stmt::CXXDeleteExprClass: { ExplodedNodeSet PreVisit; From 1db773a6300323f2ea67a26b09a64e98a35f1f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Wed, 12 Aug 2026 15:24:46 +0200 Subject: [PATCH 12/31] `ExprEngine::VisitLambdaExpr` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 8 +------- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 8 ++++++++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 0b9000c67a545..11cc3aabd9200 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -2120,13 +2120,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; case Stmt::LambdaExprClass: - if (AMgr.options.ShouldInlineLambdas) { - VisitLambdaExpr(cast(S), Pred, Dst); - } else { - const ExplodedNode *Node = Engine.makePostStmtNode( - S, Pred->getState(), Pred, /*MarkAsSink=*/true); - Engine.addAbortedBlock(Node, getCurrBlock()); - } + VisitLambdaExpr(cast(S), Pred, Dst); break; case Stmt::BinaryOperatorClass: { diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 2f459f76c3fd0..1d525e416b568 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -1122,6 +1122,14 @@ void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + + if (!AMgr.options.ShouldInlineLambdas) { + const ExplodedNode *Node = Engine.makePostStmtNode( + LE, Pred->getState(), Pred, /*MarkAsSink=*/true); + Engine.addAbortedBlock(Node, getCurrBlock()); + return; + } + const StackFrame *SF = Pred->getStackFrame(); // Get the region of the lambda itself. From 5717201bda33fdcf799fea70219409dd3af8947b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Wed, 12 Aug 2026 15:44:42 +0200 Subject: [PATCH 13/31] `ExprEngine::VisitGCCAsmStmt` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 11cc3aabd9200..0eab61cc49e53 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1115,6 +1115,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { case Stmt::ObjCBridgedCastExprClass: case Stmt::CXXAddrspaceCastExprClass: case Stmt::CXXBindTemporaryExprClass: + case Stmt::GCCAsmStmtClass: case Stmt::CXXNewExprClass: case Stmt::MaterializeTemporaryExprClass: case Stmt::OffsetOfExprClass: @@ -1138,6 +1139,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { case Stmt::ObjCBridgedCastExprClass: case Stmt::CXXAddrspaceCastExprClass: case Stmt::CXXBindTemporaryExprClass: + case Stmt::GCCAsmStmtClass: case Stmt::CXXNewExprClass: case Stmt::MaterializeTemporaryExprClass: case Stmt::OffsetOfExprClass: @@ -2101,15 +2103,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, llvm_unreachable("Support for MatrixSubscriptExpr is not implemented."); break; - case Stmt::GCCAsmStmtClass: { - ExplodedNodeSet PreVisit; - getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); - ExplodedNodeSet PostVisit; - for (ExplodedNode *const N : PreVisit) - VisitGCCAsmStmt(cast(S), N, PostVisit); - getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); + case Stmt::GCCAsmStmtClass: + VisitGCCAsmStmt(cast(S), Pred, Dst); break; - } case Stmt::MSAsmStmtClass: VisitMSAsmStmt(cast(S), Pred, Dst); From 7b68df1c087f12c23103ec4495f98c54ad38596b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Wed, 12 Aug 2026 15:59:14 +0200 Subject: [PATCH 14/31] `ExprEngine::VisitUnaryExprOrTypeTraitExpr` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 2 + clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 56 ++++++++----------- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 0eab61cc49e53..e968ab8d5fa8d 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1119,6 +1119,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { case Stmt::CXXNewExprClass: case Stmt::MaterializeTemporaryExprClass: case Stmt::OffsetOfExprClass: + case Stmt::UnaryExprOrTypeTraitExprClass: case Stmt::UnaryOperatorClass: return true; } @@ -1143,6 +1144,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { case Stmt::CXXNewExprClass: case Stmt::MaterializeTemporaryExprClass: case Stmt::OffsetOfExprClass: + case Stmt::UnaryExprOrTypeTraitExprClass: case Stmt::UnaryOperatorClass: return true; } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 45c3fa14f5cf8..da7bbb768ef3d 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -807,44 +807,34 @@ void ExprEngine::VisitOffsetOfExpr(const OffsetOfExpr *OOE, ExplodedNode *Pred, } } -void ExprEngine:: -VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, - ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - // FIXME: Prechecks eventually go in ::Visit(). - ExplodedNodeSet CheckedSet; - getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, Ex, *this); - - ExplodedNodeSet EvalSet; +void ExprEngine::VisitUnaryExprOrTypeTraitExpr( + const UnaryExprOrTypeTraitExpr *Ex, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { QualType T = Ex->getTypeOfArgument(); - for (ExplodedNode *N : CheckedSet) { - if (Ex->getKind() == UETT_SizeOf || Ex->getKind() == UETT_DataSizeOf || - Ex->getKind() == UETT_CountOf) { - if (!T->isIncompleteType() && !T->isConstantSizeType()) { - assert(T->isVariableArrayType() && "Unknown non-constant-sized type."); - - // FIXME: Add support for VLA type arguments and VLA expressions. - // When that happens, we should probably refactor VLASizeChecker's code. - EvalSet.insert(N); - continue; - } else if (T->getAs()) { - // Some code tries to take the sizeof an ObjCObjectType, relying that - // the compiler has laid out its representation. Just report Unknown - // for these. - EvalSet.insert(N); - continue; - } - } + if (Ex->getKind() == UETT_SizeOf || Ex->getKind() == UETT_DataSizeOf || + Ex->getKind() == UETT_CountOf) { + if (!T->isIncompleteType() && !T->isConstantSizeType()) { + assert(T->isVariableArrayType() && "Unknown non-constant-sized type."); - APSInt Value = Ex->EvaluateKnownConstInt(getContext()); - CharUnits amt = CharUnits::fromQuantity(Value.getZExtValue()); - - SVal V = svalBuilder.makeIntVal(amt.getQuantity(), Ex->getType()); - EvalSet.insert(Engine.makeNodeWithBinding(N, Ex, V)); + // FIXME: Add support for VLA type arguments and VLA expressions. + // When that happens, we should probably refactor VLASizeChecker's code. + Dst.insert(Pred); + return; + } else if (T->getAs()) { + // Some code tries to take the sizeof an ObjCObjectType, relying that + // the compiler has laid out its representation. Just report Unknown + // for these. + Dst.insert(Pred); + return; + } } - getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this); + APSInt Value = Ex->EvaluateKnownConstInt(getContext()); + CharUnits amt = CharUnits::fromQuantity(Value.getZExtValue()); + + SVal V = svalBuilder.makeIntVal(amt.getQuantity(), Ex->getType()); + Dst.insert(Engine.makeNodeWithBinding(Pred, Ex, V)); } void ExprEngine::VisitStmtExpr(const StmtExpr *S, ExplodedNode *Pred, From d6a329efc0105a78b22a6307ab58a1ca390086a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Wed, 12 Aug 2026 16:14:34 +0200 Subject: [PATCH 15/31] `ExprEngine::VisitReturnStmt` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + .../lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 9 ++------- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index e968ab8d5fa8d..6ccb51beeef1a 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1118,6 +1118,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { case Stmt::GCCAsmStmtClass: case Stmt::CXXNewExprClass: case Stmt::MaterializeTemporaryExprClass: + case Stmt::ReturnStmtClass: case Stmt::OffsetOfExprClass: case Stmt::UnaryExprOrTypeTraitExprClass: case Stmt::UnaryOperatorClass: diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index e0139484b2b48..983e45e80fd04 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -1304,14 +1304,9 @@ void ExprEngine::dynDispatchBifurcate(const MemRegion *BifurReg, void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - ExplodedNodeSet DstPreVisit; - getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, RS, *this); - if (RS->getRetValue()) { - for (ExplodedNode *N : DstPreVisit) { - Dst.insert(Engine.makePostStmtNode(RS, N->getState(), N)); - } + Dst.insert(Engine.makePostStmtNode(RS, Pred->getState(), Pred)); } else { - Dst.insert(DstPreVisit); + Dst.insert(Pred); } } From 1fea98fa136115ce6cd027d069719e79eb522662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Wed, 12 Aug 2026 16:45:16 +0200 Subject: [PATCH 16/31] `ExprEngine::VisitAtomicExpr` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 36 ++++++++------------ 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 6ccb51beeef1a..a702d74c90f44 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1104,6 +1104,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { switch (S->getStmtClass()) { default: return false; + case Stmt::AtomicExprClass: case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: case Stmt::CXXStaticCastExprClass: @@ -1130,6 +1131,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { switch (S->getStmtClass()) { default: return false; + case Stmt::AtomicExprClass: case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: case Stmt::CXXStaticCastExprClass: @@ -3396,34 +3398,24 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - ExplodedNodeSet AfterPreSet; - getCheckerManager().runCheckersForPreStmt(AfterPreSet, Pred, AE, *this); - // For now, treat all the arguments to C11 atomics as escaping. // FIXME: Ideally we should model the behavior of the atomics precisely here. - ExplodedNodeSet AfterInvalidateSet; - - for (const auto I : AfterPreSet) { - ProgramStateRef State = I->getState(); - const StackFrame *SF = I->getStackFrame(); - - SmallVector ValuesToInvalidate; - for (const Stmt *SubExpr : AE->children()) { - SVal SubExprVal = State->getSVal(cast(SubExpr), SF); - ValuesToInvalidate.push_back(SubExprVal); - } - - State = State->invalidateRegions(ValuesToInvalidate, getCFGElementRef(), - getNumVisitedCurrent(), SF, - /*CausedByPointerEscape*/ true, - /*Symbols=*/nullptr); + ProgramStateRef State = Pred->getState(); + const StackFrame *SF = Pred->getStackFrame(); - AfterInvalidateSet.insert( - Engine.makeNodeWithBinding(I, AE, UnknownVal(), State)); + SmallVector ValuesToInvalidate; + for (const Stmt *SubExpr : AE->children()) { + SVal SubExprVal = State->getSVal(cast(SubExpr), SF); + ValuesToInvalidate.push_back(SubExprVal); } - getCheckerManager().runCheckersForPostStmt(Dst, AfterInvalidateSet, AE, *this); + State = State->invalidateRegions(ValuesToInvalidate, getCFGElementRef(), + getNumVisitedCurrent(), SF, + /*CausedByPointerEscape*/ true, + /*Symbols=*/nullptr); + + Dst.insert(Engine.makeNodeWithBinding(Pred, AE, UnknownVal(), State)); } // A value escapes in four possible cases: From a543f02365f72b7f6fac352a74bc4d5c5833a1cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Wed, 12 Aug 2026 16:53:59 +0200 Subject: [PATCH 17/31] `ExprEngine::VisitMemberExpr` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 116 +++++++++---------- 1 file changed, 54 insertions(+), 62 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index a702d74c90f44..3df3b081cdffc 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1119,6 +1119,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { case Stmt::GCCAsmStmtClass: case Stmt::CXXNewExprClass: case Stmt::MaterializeTemporaryExprClass: + case Stmt::MemberExprClass: case Stmt::ReturnStmtClass: case Stmt::OffsetOfExprClass: case Stmt::UnaryExprOrTypeTraitExprClass: @@ -1146,6 +1147,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { case Stmt::GCCAsmStmtClass: case Stmt::CXXNewExprClass: case Stmt::MaterializeTemporaryExprClass: + case Stmt::MemberExprClass: case Stmt::OffsetOfExprClass: case Stmt::UnaryExprOrTypeTraitExprClass: case Stmt::UnaryOperatorClass: @@ -3315,85 +3317,75 @@ a vector and not a forbidden lvalue type"); /// VisitMemberExpr - Transfer function for member expressions. void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - // FIXME: Prechecks eventually go in ::Visit(). - ExplodedNodeSet CheckedSet; - getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, M, *this); - - ExplodedNodeSet EvalSet; ValueDecl *Member = M->getMemberDecl(); // Handle static member variables and enum constants accessed via // member syntax. if (isa(Member)) { - for (const auto I : CheckedSet) - VisitCommonDeclRefExpr(M, Member, I, EvalSet); + VisitCommonDeclRefExpr(M, Member, Pred, Dst); } else { - for (const auto I : CheckedSet) { - ProgramStateRef state = I->getState(); - const StackFrame *SF = I->getStackFrame(); - Expr *BaseExpr = M->getBase(); - - // Handle C++ method calls. - if (const auto *MD = dyn_cast(Member)) { - if (MD->isImplicitObjectMemberFunction()) - state = createTemporaryRegionIfNeeded(state, SF, BaseExpr); + ProgramStateRef state = Pred->getState(); + const StackFrame *SF = Pred->getStackFrame(); + Expr *BaseExpr = M->getBase(); - SVal MDVal = svalBuilder.getFunctionPointer(MD); + // Handle C++ method calls. + if (const auto *MD = dyn_cast(Member)) { + if (MD->isImplicitObjectMemberFunction()) + state = createTemporaryRegionIfNeeded(state, SF, BaseExpr); - EvalSet.insert(Engine.makeNodeWithBinding(I, M, MDVal, state)); - continue; - } + SVal MDVal = svalBuilder.getFunctionPointer(MD); - // Handle regular struct fields / member variables. - const SubRegion *MR = nullptr; - state = createTemporaryRegionIfNeeded(state, SF, BaseExpr, - /*Result=*/nullptr, - /*OutRegionWithAdjustments=*/&MR); - SVal baseExprVal = - MR ? loc::MemRegionVal(MR) : state->getSVal(BaseExpr, SF); - - // FIXME: Copied from RegionStoreManager::bind() - if (const auto *SR = - dyn_cast_or_null(baseExprVal.getAsRegion())) { - QualType T = SR->getPointeeStaticType(); - baseExprVal = - loc::MemRegionVal(getStoreManager().GetElementZeroRegion(SR, T)); - } + Dst.insert(Engine.makeNodeWithBinding(Pred, M, MDVal, state)); + return; + } - const auto *field = cast(Member); - SVal L = state->getLValue(field, baseExprVal); - - if (M->isGLValue() || M->getType()->isArrayType()) { - // We special-case rvalues of array type because the analyzer cannot - // reason about them, since we expect all regions to be wrapped in Locs. - // We instead treat these as lvalues and assume that they will decay to - // pointers as soon as they are used. - if (!M->isGLValue()) { - assert(M->getType()->isArrayType()); - const auto *PE = - dyn_cast(I->getParentMap().getParentIgnoreParens(M)); - if (!PE || PE->getCastKind() != CK_ArrayToPointerDecay) { - llvm_unreachable("should always be wrapped in ArrayToPointerDecay"); - } - } + // Handle regular struct fields / member variables. + const SubRegion *MR = nullptr; + state = createTemporaryRegionIfNeeded(state, SF, BaseExpr, + /*Result=*/nullptr, + /*OutRegionWithAdjustments=*/&MR); + SVal baseExprVal = + MR ? loc::MemRegionVal(MR) : state->getSVal(BaseExpr, SF); + + // FIXME: Copied from RegionStoreManager::bind() + if (const auto *SR = + dyn_cast_or_null(baseExprVal.getAsRegion())) { + QualType T = SR->getPointeeStaticType(); + baseExprVal = + loc::MemRegionVal(getStoreManager().GetElementZeroRegion(SR, T)); + } - if (field->getType()->isReferenceType()) { - if (const MemRegion *R = L.getAsRegion()) - L = state->getSVal(R); - else - L = UnknownVal(); + const auto *field = cast(Member); + SVal L = state->getLValue(field, baseExprVal); + + if (M->isGLValue() || M->getType()->isArrayType()) { + // We special-case rvalues of array type because the analyzer cannot + // reason about them, since we expect all regions to be wrapped in Locs. + // We instead treat these as lvalues and assume that they will decay to + // pointers as soon as they are used. + if (!M->isGLValue()) { + assert(M->getType()->isArrayType()); + const auto *PE = + dyn_cast(Pred->getParentMap().getParentIgnoreParens(M)); + if (!PE || PE->getCastKind() != CK_ArrayToPointerDecay) { + llvm_unreachable("should always be wrapped in ArrayToPointerDecay"); } + } - EvalSet.insert(Engine.makeNodeWithBinding( - I, M, L, state, ProgramPoint::PostLValueKind)); - } else { - evalLoad(EvalSet, M, M, I, state, L); + if (field->getType()->isReferenceType()) { + if (const MemRegion *R = L.getAsRegion()) + L = state->getSVal(R); + else + L = UnknownVal(); } + + Dst.insert(Engine.makeNodeWithBinding( + Pred, M, L, state, ProgramPoint::PostLValueKind)); + } else { + evalLoad(Dst, M, M, Pred, state, L); } } - - getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, M, *this); } void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred, From eddd69459b8be072b91b60bcd8281695a64ae5f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Wed, 12 Aug 2026 17:58:53 +0200 Subject: [PATCH 18/31] `ExprEngine::VisitBlockExpr` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 3df3b081cdffc..32174b46bbc34 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1133,6 +1133,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { default: return false; case Stmt::AtomicExprClass: + case Stmt::BlockExprClass: case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: case Stmt::CXXStaticCastExprClass: diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index da7bbb768ef3d..4e6f93d783fc1 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -214,11 +214,8 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, } } - ExplodedNode *N = Engine.makeNodeWithBinding(Pred, BE, V, State, - ProgramPoint::PostLValueKind); - - // FIXME: Move all post/pre visits to ::Visit(). - getCheckerManager().runCheckersForPostStmt(Dst, N, BE, *this); + Dst.insert(Engine.makeNodeWithBinding(Pred, BE, V, State, + ProgramPoint::PostLValueKind)); } ProgramStateRef From b5ff694431054e5bdae7d220ac79908757d49fdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Wed, 12 Aug 2026 18:16:44 +0200 Subject: [PATCH 19/31] `ExprEngine::VisitArraySubscriptExpr` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 57 +++++++++----------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 32174b46bbc34..1de0a0a7e68a2 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1104,6 +1104,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { switch (S->getStmtClass()) { default: return false; + case Stmt::ArraySubscriptExprClass: case Stmt::AtomicExprClass: case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: @@ -1132,6 +1133,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { switch (S->getStmtClass()) { default: return false; + case Stmt::ArraySubscriptExprClass: case Stmt::AtomicExprClass: case Stmt::BlockExprClass: case Stmt::ImplicitCastExprClass: @@ -3268,16 +3270,11 @@ void ExprEngine::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *Ex, /// VisitArraySubscriptExpr - Transfer function for array accesses void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A, - ExplodedNode *Pred, - ExplodedNodeSet &Dst){ + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { const Expr *Base = A->getBase()->IgnoreParens(); const Expr *Idx = A->getIdx()->IgnoreParens(); - ExplodedNodeSet CheckerPreStmt; - getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, A, *this); - - ExplodedNodeSet EvalSet; - bool IsVectorType = A->getBase()->getType()->isVectorType(); // The "like" case is for situations where C standard prohibits the type to @@ -3286,33 +3283,29 @@ void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A, bool IsGLValueLike = A->isGLValue() || (A->getType().isCForbiddenLValueType() && !AMgr.getLangOpts().CPlusPlus); - for (auto *Node : CheckerPreStmt) { - const StackFrame *SF = Node->getStackFrame(); - ProgramStateRef state = Node->getState(); + const StackFrame *SF = Pred->getStackFrame(); + ProgramStateRef state = Pred->getState(); - if (IsGLValueLike) { - QualType T = A->getType(); - - // One of the forbidden LValue types! We still need to have sensible - // symbolic locations to represent this stuff. Note that arithmetic on - // void pointers is a GCC extension. - if (T->isVoidType()) - T = getContext().CharTy; - - SVal V = state->getLValue(T, state->getSVal(Idx, SF), - state->getSVal(Base, SF)); - EvalSet.insert( - Engine.makeNodeWithBinding(Node, A, V, ProgramPoint::PostLValueKind)); - } else if (IsVectorType) { - // FIXME: non-glvalue vector reads are not modelled. - EvalSet.insert(Engine.makePostStmtNode(A, state, Node)); - } else { - llvm_unreachable("Array subscript should be an lValue when not \ -a vector and not a forbidden lvalue type"); - } - } + if (IsGLValueLike) { + QualType T = A->getType(); - getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, A, *this); + // One of the forbidden LValue types! We still need to have sensible + // symbolic locations to represent this stuff. Note that arithmetic on + // void pointers is a GCC extension. + if (T->isVoidType()) + T = getContext().CharTy; + + SVal V = + state->getLValue(T, state->getSVal(Idx, SF), state->getSVal(Base, SF)); + Dst.insert( + Engine.makeNodeWithBinding(Pred, A, V, ProgramPoint::PostLValueKind)); + } else if (IsVectorType) { + // FIXME: non-glvalue vector reads are not modelled. + Dst.insert(Engine.makePostStmtNode(A, state, Pred)); + } else { + llvm_unreachable("Array subscript should be an lValue when not \ +a ctor and not a forbidden lvalue type"); + } } /// VisitMemberExpr - Transfer function for member expressions. From a5be3d4a30e81d4188dd7bae5e44a16065d8d9ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Wed, 12 Aug 2026 18:38:14 +0200 Subject: [PATCH 20/31] `ExprEngine::VisitAttributedStmt` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 5 ++-- .../lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 30 +++++++------------ 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 1de0a0a7e68a2..c7089452ed7eb 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1105,6 +1105,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { default: return false; case Stmt::ArraySubscriptExprClass: + case Stmt::AttributedStmtClass: case Stmt::AtomicExprClass: case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: @@ -1134,6 +1135,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { default: return false; case Stmt::ArraySubscriptExprClass: + case Stmt::AttributedStmtClass: case Stmt::AtomicExprClass: case Stmt::BlockExprClass: case Stmt::ImplicitCastExprClass: @@ -2017,10 +2019,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; } - case Stmt::AttributedStmtClass: { + case Stmt::AttributedStmtClass: VisitAttributedStmt(cast(S), Pred, Dst); break; - } case Stmt::CXXDefaultArgExprClass: case Stmt::CXXDefaultInitExprClass: { diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 1d525e416b568..a6edc861c291f 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -1201,28 +1201,20 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, void ExprEngine::VisitAttributedStmt(const AttributedStmt *A, ExplodedNode *Pred, ExplodedNodeSet &Dst) { const StackFrame *SF = Pred->getStackFrame(); - ExplodedNodeSet CheckerPreStmt; - getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, A, *this); - - ExplodedNodeSet EvalSet; - - for (ExplodedNode *N : CheckerPreStmt) { - ProgramStateRef State = N->getState(); - for (const auto *Attr : getSpecificAttrs(A->getAttrs())) { - SVal AssumedVal = State->getSVal(Attr->getAssumption(), SF); - // This code ignores assumptions that evaluate to UndefinedVal. - // Perhaps there should be a checker that reports this situation. - if (auto ValidAssumedVal = AssumedVal.getAs()) { - State = State->assume(*ValidAssumedVal, true); - } + ProgramStateRef State = Pred->getState(); - if (!State) - break; + for (const auto *Attr : getSpecificAttrs(A->getAttrs())) { + SVal AssumedVal = State->getSVal(Attr->getAssumption(), SF); + // This code ignores assumptions that evaluate to UndefinedVal. + // Perhaps there should be a checker that reports this situation. + if (auto ValidAssumedVal = AssumedVal.getAs()) { + State = State->assume(*ValidAssumedVal, true); } - if (State) - EvalSet.insert(Engine.makePostStmtNode(A, State, N)); + if (!State) + break; } - getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, A, *this); + if (State) + Dst.insert(Engine.makePostStmtNode(A, State, Pred)); } From 01b503f7b4e3f0dc19ede3f48a80667638cd2b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Thu, 13 Aug 2026 14:45:05 +0200 Subject: [PATCH 21/31] `ExprEngine::VisitArrayInitLoopExpr` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 152 +++++++++---------- 1 file changed, 73 insertions(+), 79 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index c7089452ed7eb..fa53f51d8f82e 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1104,6 +1104,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { switch (S->getStmtClass()) { default: return false; + case Stmt::ArrayInitLoopExprClass: case Stmt::ArraySubscriptExprClass: case Stmt::AttributedStmtClass: case Stmt::AtomicExprClass: @@ -1134,6 +1135,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { switch (S->getStmtClass()) { default: return false; + case Stmt::ArrayInitLoopExprClass: case Stmt::ArraySubscriptExprClass: case Stmt::AttributedStmtClass: case Stmt::AtomicExprClass: @@ -3177,96 +3179,88 @@ void ExprEngine::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *Ex, ExplodedNodeSet &Dst) { const Expr *Arr = Ex->getCommonExpr()->getSourceExpr(); - ExplodedNodeSet CheckerPreStmt; - getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, Ex, *this); - - ExplodedNodeSet EvalSet; + // The constructor visitor has already handled everything, so let's skip + // forward to PostStmt handling. if (isa(Ex->getSubExpr())) { - // The constructor visitor has already handled everything, so let's skip - // forward to PostStmt handling by clearing the range of the 'for' loop. - EvalSet.insert(CheckerPreStmt); - CheckerPreStmt.clear(); + Dst.insert(Pred); + return; } - for (auto *Node : CheckerPreStmt) { - const StackFrame *SF = Node->getStackFrame(); - ProgramStateRef state = Node->getState(); - - SVal Base = UnknownVal(); - - // As in case of this expression the sub-expressions are not visited by any - // other transfer functions, they are handled by matching their AST. + const StackFrame *SF = Pred->getStackFrame(); + ProgramStateRef state = Pred->getState(); - // Case of implicit copy or move ctor of object with array member - // - // Note: ExprEngine::VisitMemberExpr is not able to bind the array to the - // environment. - // - // struct S { - // int arr[2]; - // }; - // - // - // S a; - // S b = a; - // - // The AST in case of a *copy constructor* looks like this: - // ArrayInitLoopExpr - // |-OpaqueValueExpr - // | `-MemberExpr <-- match this - // | `-DeclRefExpr - // ` ... - // - // - // S c; - // S d = std::move(d); - // - // In case of a *move constructor* the resulting AST looks like: - // ArrayInitLoopExpr - // |-OpaqueValueExpr - // | `-MemberExpr <-- match this first - // | `-CXXStaticCastExpr <-- match this after - // | `-DeclRefExpr - // ` ... - if (const auto *ME = dyn_cast(Arr)) { - Expr *MEBase = ME->getBase(); - - // Move ctor - if (auto CXXSCE = dyn_cast(MEBase)) { - MEBase = CXXSCE->getSubExpr(); - } + SVal Base = UnknownVal(); - auto ObjDeclExpr = cast(MEBase); - SVal Obj = state->getLValue(cast(ObjDeclExpr->getDecl()), SF); + // As in case of this expression the sub-expressions are not visited by any + // other transfer functions, they are handled by matching their AST. - Base = state->getLValue(cast(ME->getMemberDecl()), Obj); + // Case of implicit copy or move ctor of object with array member + // + // Note: ExprEngine::VisitMemberExpr is not able to bind the array to the + // environment. + // + // struct S { + // int arr[2]; + // }; + // + // + // S a; + // S b = a; + // + // The AST in case of a *copy constructor* looks like this: + // ArrayInitLoopExpr + // |-OpaqueValueExpr + // | `-MemberExpr <-- match this + // | `-DeclRefExpr + // ` ... + // + // + // S c; + // S d = std::move(d); + // + // In case of a *move constructor* the resulting AST looks like: + // ArrayInitLoopExpr + // |-OpaqueValueExpr + // | `-MemberExpr <-- match this first + // | `-CXXStaticCastExpr <-- match this after + // | `-DeclRefExpr + // ` ... + if (const auto *ME = dyn_cast(Arr)) { + Expr *MEBase = ME->getBase(); + + // Move ctor + if (auto CXXSCE = dyn_cast(MEBase)) { + MEBase = CXXSCE->getSubExpr(); } - // Case of lambda capture and decomposition declaration - // - // int arr[2]; - // - // [arr]{ int a = arr[0]; }(); - // auto[a, b] = arr; - // - // In both of these cases the AST looks like the following: - // ArrayInitLoopExpr - // |-OpaqueValueExpr - // | `-DeclRefExpr <-- match this - // ` ... - if (const DeclRefExpr *DRE = dyn_cast(Arr)) - Base = state->getLValue(cast(DRE->getDecl()), SF); - - // Create a lazy compound value to the original array - if (const MemRegion *R = Base.getAsRegion()) - Base = state->getSVal(R); - else - Base = UnknownVal(); + auto ObjDeclExpr = cast(MEBase); + SVal Obj = state->getLValue(cast(ObjDeclExpr->getDecl()), SF); - EvalSet.insert(Engine.makeNodeWithBinding(Node, Ex, Base)); + Base = state->getLValue(cast(ME->getMemberDecl()), Obj); } - getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this); + // Case of lambda capture and decomposition declaration + // + // int arr[2]; + // + // [arr]{ int a = arr[0]; }(); + // auto[a, b] = arr; + // + // In both of these cases the AST looks like the following: + // ArrayInitLoopExpr + // |-OpaqueValueExpr + // | `-DeclRefExpr <-- match this + // ` ... + if (const DeclRefExpr *DRE = dyn_cast(Arr)) + Base = state->getLValue(cast(DRE->getDecl()), SF); + + // Create a lazy compound value to the original array + if (const MemRegion *R = Base.getAsRegion()) + Base = state->getSVal(R); + else + Base = UnknownVal(); + + Dst.insert(Engine.makeNodeWithBinding(Pred, Ex, Base)); } /// VisitArraySubscriptExpr - Transfer function for array accesses From 75032f19460fd21154a9395b29724148ff5daf44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Thu, 13 Aug 2026 15:03:10 +0200 Subject: [PATCH 22/31] Refactor cases that are unsupported or that do not have to be explicitly evaluated --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 110 +++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index fa53f51d8f82e..8dcd4046b4564 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1104,6 +1104,61 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { switch (S->getStmtClass()) { default: return false; + + // Cases not handled yet; but will handle some day. + case Stmt::DesignatedInitExprClass: + case Stmt::DesignatedInitUpdateExprClass: + case Stmt::ArrayInitIndexExprClass: + case Stmt::ExtVectorElementExprClass: + case Stmt::MatrixElementExprClass: + case Stmt::ImaginaryLiteralClass: + case Stmt::ObjCAtCatchStmtClass: + case Stmt::ObjCAtFinallyStmtClass: + case Stmt::ObjCAtTryStmtClass: + case Stmt::ObjCAutoreleasePoolStmtClass: + case Stmt::ObjCEncodeExprClass: + case Stmt::ObjCIsaExprClass: + case Stmt::ObjCProtocolExprClass: + case Stmt::ObjCSelectorExprClass: + case Stmt::ParenListExprClass: + case Stmt::ShuffleVectorExprClass: + case Stmt::ConvertVectorExprClass: + case Stmt::VAArgExprClass: + case Stmt::CUDAKernelCallExprClass: + case Stmt::OpaqueValueExprClass: + case Stmt::AsTypeExprClass: + case Stmt::ConceptSpecializationExprClass: + case Stmt::CXXRewrittenBinaryOperatorClass: + case Stmt::RequiresExprClass: + case Stmt::EmbedExprClass: + + // Cases we intentionally don't evaluate, since they don't need + // to be explicitly evaluated. + case Stmt::PredefinedExprClass: + case Stmt::AddrLabelExprClass: + case Stmt::IntegerLiteralClass: + case Stmt::FixedPointLiteralClass: + case Stmt::CharacterLiteralClass: + case Stmt::CXXScalarValueInitExprClass: + case Stmt::CXXBoolLiteralExprClass: + case Stmt::ObjCBoolLiteralExprClass: + case Stmt::ObjCAvailabilityCheckExprClass: + case Stmt::FloatingLiteralClass: + case Stmt::NoInitExprClass: + case Stmt::SizeOfPackExprClass: + case Stmt::StringLiteralClass: + case Stmt::SourceLocExprClass: + case Stmt::ObjCStringLiteralClass: + case Stmt::CXXPseudoDestructorExprClass: + case Stmt::SubstNonTypeTemplateParmExprClass: + case Stmt::CXXNullPtrLiteralExprClass: + case Stmt::ArraySectionExprClass: + case Stmt::OMPArrayShapingExprClass: + case Stmt::OMPIteratorExprClass: + case Stmt::SYCLUniqueStableNameExprClass: + case Stmt::OpenACCAsteriskSizeExprClass: + case Stmt::TypeTraitExprClass: + case Stmt::ArrayInitLoopExprClass: case Stmt::ArraySubscriptExprClass: case Stmt::AttributedStmtClass: @@ -1135,6 +1190,61 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { switch (S->getStmtClass()) { default: return false; + + // Cases not handled yet; but will handle some day. + case Stmt::DesignatedInitExprClass: + case Stmt::DesignatedInitUpdateExprClass: + case Stmt::ArrayInitIndexExprClass: + case Stmt::ExtVectorElementExprClass: + case Stmt::MatrixElementExprClass: + case Stmt::ImaginaryLiteralClass: + case Stmt::ObjCAtCatchStmtClass: + case Stmt::ObjCAtFinallyStmtClass: + case Stmt::ObjCAtTryStmtClass: + case Stmt::ObjCAutoreleasePoolStmtClass: + case Stmt::ObjCEncodeExprClass: + case Stmt::ObjCIsaExprClass: + case Stmt::ObjCProtocolExprClass: + case Stmt::ObjCSelectorExprClass: + case Stmt::ParenListExprClass: + case Stmt::ShuffleVectorExprClass: + case Stmt::ConvertVectorExprClass: + case Stmt::VAArgExprClass: + case Stmt::CUDAKernelCallExprClass: + case Stmt::OpaqueValueExprClass: + case Stmt::AsTypeExprClass: + case Stmt::ConceptSpecializationExprClass: + case Stmt::CXXRewrittenBinaryOperatorClass: + case Stmt::RequiresExprClass: + case Stmt::EmbedExprClass: + + // Cases we intentionally don't evaluate, since they don't need + // to be explicitly evaluated. + case Stmt::PredefinedExprClass: + case Stmt::AddrLabelExprClass: + case Stmt::IntegerLiteralClass: + case Stmt::FixedPointLiteralClass: + case Stmt::CharacterLiteralClass: + case Stmt::CXXScalarValueInitExprClass: + case Stmt::CXXBoolLiteralExprClass: + case Stmt::ObjCBoolLiteralExprClass: + case Stmt::ObjCAvailabilityCheckExprClass: + case Stmt::FloatingLiteralClass: + case Stmt::NoInitExprClass: + case Stmt::SizeOfPackExprClass: + case Stmt::StringLiteralClass: + case Stmt::SourceLocExprClass: + case Stmt::ObjCStringLiteralClass: + case Stmt::CXXPseudoDestructorExprClass: + case Stmt::SubstNonTypeTemplateParmExprClass: + case Stmt::CXXNullPtrLiteralExprClass: + case Stmt::ArraySectionExprClass: + case Stmt::OMPArrayShapingExprClass: + case Stmt::OMPIteratorExprClass: + case Stmt::SYCLUniqueStableNameExprClass: + case Stmt::OpenACCAsteriskSizeExprClass: + case Stmt::TypeTraitExprClass: + case Stmt::ArrayInitLoopExprClass: case Stmt::ArraySubscriptExprClass: case Stmt::AttributedStmtClass: From 9ebb3b4d273ee84542649439383588f0a8818d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Thu, 13 Aug 2026 16:00:04 +0200 Subject: [PATCH 23/31] `Stmt::CXXDefaultArgExprClass`, `Stmt::CXXDefaultInitExprClass` These are handled by the same code and currently implemented inline in the switch, i.e. have no dedicated visit method. --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 27 ++++++++------------ 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 8dcd4046b4564..1a191527f2c66 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1162,6 +1162,8 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { case Stmt::ArrayInitLoopExprClass: case Stmt::ArraySubscriptExprClass: case Stmt::AttributedStmtClass: + case Stmt::CXXDefaultArgExprClass: + case Stmt::CXXDefaultInitExprClass: case Stmt::AtomicExprClass: case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: @@ -1248,6 +1250,8 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { case Stmt::ArrayInitLoopExprClass: case Stmt::ArraySubscriptExprClass: case Stmt::AttributedStmtClass: + case Stmt::CXXDefaultArgExprClass: + case Stmt::CXXDefaultInitExprClass: case Stmt::AtomicExprClass: case Stmt::BlockExprClass: case Stmt::ImplicitCastExprClass: @@ -2125,9 +2129,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::SYCLUniqueStableNameExprClass: case Stmt::OpenACCAsteriskSizeExprClass: case Stmt::TypeTraitExprClass: { - ExplodedNodeSet preVisit; - getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); - getCheckerManager().runCheckersForPostStmt(Dst, preVisit, S, *this); + Dst.insert(Pred); break; } @@ -2137,10 +2139,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXDefaultArgExprClass: case Stmt::CXXDefaultInitExprClass: { - ExplodedNodeSet PreVisit; - getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); - - ExplodedNodeSet Tmp; const Expr *ArgE; if (const auto *DefE = dyn_cast(S)) @@ -2161,16 +2159,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ConstantVal = UnknownVal(); const StackFrame *SF = Pred->getStackFrame(); - for (const auto I : PreVisit) { - ProgramStateRef State = I->getState(); - State = State->BindExpr(cast(S), SF, *ConstantVal); - if (IsTemporary) - State = createTemporaryRegionIfNeeded(State, SF, cast(S), - cast(S)); - Tmp.insert(Engine.makePostStmtNode(S, State, I)); - } + ProgramStateRef State = Pred->getState(); + State = State->BindExpr(cast(S), SF, *ConstantVal); + if (IsTemporary) + State = createTemporaryRegionIfNeeded(State, SF, cast(S), + cast(S)); + Dst.insert(Engine.makePostStmtNode(S, State, Pred)); - getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); break; } From a6d17293ebd60d6ba3faa231569f541b5ae3a838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Thu, 13 Aug 2026 16:15:42 +0200 Subject: [PATCH 24/31] `Stmt::CXXStdInitializerListExprClass`, `Expr::ObjCArrayLiteralClass`, `Expr::ObjCDictionaryLiteralClass`, `Expr::ObjCBoxedExprClass`, These are handled by the same code and currently implemented inline in the switch, i.e. have no dedicated visit method. --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 53 ++++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 1a191527f2c66..1453bc35b62c3 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1164,6 +1164,10 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { case Stmt::AttributedStmtClass: case Stmt::CXXDefaultArgExprClass: case Stmt::CXXDefaultInitExprClass: + case Stmt::CXXStdInitializerListExprClass: + case Expr::ObjCArrayLiteralClass: + case Expr::ObjCDictionaryLiteralClass: + case Expr::ObjCBoxedExprClass: case Stmt::AtomicExprClass: case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: @@ -1252,6 +1256,10 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { case Stmt::AttributedStmtClass: case Stmt::CXXDefaultArgExprClass: case Stmt::CXXDefaultInitExprClass: + case Stmt::CXXStdInitializerListExprClass: + case Expr::ObjCArrayLiteralClass: + case Expr::ObjCDictionaryLiteralClass: + case Expr::ObjCBoxedExprClass: case Stmt::AtomicExprClass: case Stmt::BlockExprClass: case Stmt::ImplicitCastExprClass: @@ -2174,37 +2182,28 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: case Expr::ObjCBoxedExprClass: { - ExplodedNodeSet preVisit; - getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); - - ExplodedNodeSet Tmp; - const auto *Ex = cast(S); QualType resultType = Ex->getType(); - for (const auto N : preVisit) { - const StackFrame *SF = N->getStackFrame(); - SVal result = svalBuilder.conjureSymbolVal( - /*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType, - getNumVisitedCurrent()); - ProgramStateRef State = N->getState()->BindExpr(Ex, SF, result); - - // Escape pointers passed into the list, unless it's an ObjC boxed - // expression which is not a boxable C structure. - if (!(isa(Ex) && - !cast(Ex)->getSubExpr() - ->getType()->isRecordType())) - for (auto Child : Ex->children()) { - assert(Child); - const auto *ChildExpr = dyn_cast(Child); - SVal Val = ChildExpr ? State->getSVal(ChildExpr, SF) : UnknownVal(); - State = escapeValues(State, Val, PSK_EscapeOther); - } - - Tmp.insert(Engine.makePostStmtNode(S, State, N)); - } + const StackFrame *SF = Pred->getStackFrame(); + SVal result = svalBuilder.conjureSymbolVal( + /*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType, + getNumVisitedCurrent()); + ProgramStateRef State = Pred->getState()->BindExpr(Ex, SF, result); + + // Escape pointers passed into the list, unless it's an ObjC boxed + // expression which is not a boxable C structure. + if (!(isa(Ex) && + !cast(Ex)->getSubExpr() + ->getType()->isRecordType())) + for (auto Child : Ex->children()) { + assert(Child); + const auto *ChildExpr = dyn_cast(Child); + SVal Val = ChildExpr ? State->getSVal(ChildExpr, SF) : UnknownVal(); + State = escapeValues(State, Val, PSK_EscapeOther); + } - getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); + Dst.insert(Engine.makePostStmtNode(S, State, Pred)); break; } From 4542665d2c15111e8973b60ccf040dddab4774af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Thu, 13 Aug 2026 16:45:53 +0200 Subject: [PATCH 25/31] `ExprEngine::VisitLvalObjCIvarRefExpr` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp | 7 +------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 1453bc35b62c3..79957c0943e85 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1261,6 +1261,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { case Expr::ObjCDictionaryLiteralClass: case Expr::ObjCBoxedExprClass: case Stmt::AtomicExprClass: + case Stmt::ObjCIvarRefExprClass: case Stmt::BlockExprClass: case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index bc214f4aa7054..99b6d5f5e8ea2 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -25,12 +25,7 @@ void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex, const StackFrame *SF = Pred->getStackFrame(); SVal baseVal = state->getSVal(Ex->getBase(), SF); SVal location = state->getLValue(Ex->getDecl(), baseVal); - - ExplodedNode *N = Engine.makeNodeWithBinding(Pred, Ex, location); - - // Perform the post-condition check of the ObjCIvarRefExpr and store - // the created nodes in 'Dst'. - getCheckerManager().runCheckersForPostStmt(Dst, N, Ex, *this); + Dst.insert(Engine.makeNodeWithBinding(Pred, Ex, location)); } void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S, From 40570db16afe6ba3e81648d5e90daa6b5c8dfccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Thu, 13 Aug 2026 16:54:10 +0200 Subject: [PATCH 26/31] `ExprEngine::VisitObjCForCollectionStmt` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 79957c0943e85..b09b088e3b374 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1262,6 +1262,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { case Expr::ObjCBoxedExprClass: case Stmt::AtomicExprClass: case Stmt::ObjCIvarRefExprClass: + case Stmt::ObjCForCollectionStmtClass: case Stmt::BlockExprClass: case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index 99b6d5f5e8ea2..88cb1ef625674 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -123,9 +123,7 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, populateObjCForDestinationSet(S, N, Tmp, elementV, /*hasElements=*/false); - // Finally, run any custom checkers. - // FIXME: Eventually all pre- and post-checks should live in VisitStmt. - getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); + Dst.insert(Tmp); } } From db5a00955538660b3d606e3dd56671c8696ce705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Fri, 14 Aug 2026 14:35:24 +0200 Subject: [PATCH 27/31] `ExprEngine::VisitObjCAtSynchronizedStmt` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index b09b088e3b374..edfc7c26a6adb 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1166,6 +1166,7 @@ static bool justRunCheckersAsPreVisit(const Stmt *S) { case Stmt::CXXDefaultInitExprClass: case Stmt::CXXStdInitializerListExprClass: case Expr::ObjCArrayLiteralClass: + case Stmt::ObjCAtSynchronizedStmtClass: case Expr::ObjCDictionaryLiteralClass: case Expr::ObjCBoxedExprClass: case Stmt::AtomicExprClass: diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index 88cb1ef625674..10ebdfeff0ab9 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -31,7 +31,7 @@ void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex, void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - getCheckerManager().runCheckersForPreStmt(Dst, Pred, S, *this); + Dst.insert(Pred); } void ExprEngine::populateObjCForDestinationSet(const ObjCForCollectionStmt *S, From c602cc03464b355be70ed54ce7ec497c30d30223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Fri, 14 Aug 2026 18:10:03 +0200 Subject: [PATCH 28/31] Merge functions `justRunCheckersAsPreVisit` and `justRunCheckersAsPostVisit` into the function `shouldJustCallCheckers` and also document status of each statement kind. --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 454 +++++++++++++++---- 1 file changed, 355 insertions(+), 99 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index edfc7c26a6adb..d43bff3c27507 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1100,103 +1100,206 @@ const ProgramPointTag *ExprEngine::cleanupNodeTag() { return &cleanupTag; } -static bool justRunCheckersAsPreVisit(const Stmt *S) { +enum VisitKind { + PreVisitKind, + PostVisitKind, +}; + +static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) { + switch (S->getStmtClass()) { - default: + + // C++, OpenMP and ARC stuff we don't support yet. + case Stmt::CXXDependentScopeMemberExprClass: + case Stmt::CXXReflectExprClass: + case Stmt::CXXTryStmtClass: + case Stmt::CXXTypeidExprClass: + case Stmt::CXXUuidofExprClass: + case Stmt::CXXFoldExprClass: + case Stmt::MSPropertyRefExprClass: + case Stmt::MSPropertySubscriptExprClass: + case Stmt::CXXUnresolvedConstructExprClass: + case Stmt::DependentScopeDeclRefExprClass: + case Stmt::ArrayTypeTraitExprClass: + case Stmt::ExpressionTraitExprClass: + case Stmt::UnresolvedLookupExprClass: + case Stmt::UnresolvedMemberExprClass: + case Stmt::RecoveryExprClass: + case Stmt::CXXNoexceptExprClass: + case Stmt::PackExpansionExprClass: + case Stmt::PackIndexingExprClass: + case Stmt::SubstNonTypeTemplateParmPackExprClass: + case Stmt::FunctionParmPackExprClass: + case Stmt::CoroutineBodyStmtClass: + case Stmt::CoawaitExprClass: + case Stmt::DependentCoawaitExprClass: + case Stmt::CoreturnStmtClass: + case Stmt::CoyieldExprClass: + case Stmt::SEHTryStmtClass: + case Stmt::SEHExceptStmtClass: + case Stmt::SEHLeaveStmtClass: + case Stmt::SEHFinallyStmtClass: + case Stmt::CXXExpansionStmtPatternClass: + case Stmt::CXXExpansionStmtInstantiationClass: + case Stmt::CXXExpansionSelectExprClass: + case Stmt::OMPCanonicalLoopClass: + case Stmt::OMPParallelDirectiveClass: + case Stmt::OMPSimdDirectiveClass: + case Stmt::OMPForDirectiveClass: + case Stmt::OMPForSimdDirectiveClass: + case Stmt::OMPSectionsDirectiveClass: + case Stmt::OMPSectionDirectiveClass: + case Stmt::OMPScopeDirectiveClass: + case Stmt::OMPSingleDirectiveClass: + case Stmt::OMPMasterDirectiveClass: + case Stmt::OMPCriticalDirectiveClass: + case Stmt::OMPParallelForDirectiveClass: + case Stmt::OMPParallelForSimdDirectiveClass: + case Stmt::OMPParallelSectionsDirectiveClass: + case Stmt::OMPParallelMasterDirectiveClass: + case Stmt::OMPParallelMaskedDirectiveClass: + case Stmt::OMPTaskDirectiveClass: + case Stmt::OMPTaskyieldDirectiveClass: + case Stmt::OMPBarrierDirectiveClass: + case Stmt::OMPTaskwaitDirectiveClass: + case Stmt::OMPErrorDirectiveClass: + case Stmt::OMPTaskgroupDirectiveClass: + case Stmt::OMPFlushDirectiveClass: + case Stmt::OMPDepobjDirectiveClass: + case Stmt::OMPScanDirectiveClass: + case Stmt::OMPOrderedDirectiveClass: + case Stmt::OMPAtomicDirectiveClass: + case Stmt::OMPAssumeDirectiveClass: + case Stmt::OMPTargetDirectiveClass: + case Stmt::OMPTargetDataDirectiveClass: + case Stmt::OMPTargetEnterDataDirectiveClass: + case Stmt::OMPTargetExitDataDirectiveClass: + case Stmt::OMPTargetParallelDirectiveClass: + case Stmt::OMPTargetParallelForDirectiveClass: + case Stmt::OMPTargetUpdateDirectiveClass: + case Stmt::OMPTeamsDirectiveClass: + case Stmt::OMPCancellationPointDirectiveClass: + case Stmt::OMPCancelDirectiveClass: + case Stmt::OMPTaskLoopDirectiveClass: + case Stmt::OMPTaskLoopSimdDirectiveClass: + case Stmt::OMPMasterTaskLoopDirectiveClass: + case Stmt::OMPMaskedTaskLoopDirectiveClass: + case Stmt::OMPMasterTaskLoopSimdDirectiveClass: + case Stmt::OMPMaskedTaskLoopSimdDirectiveClass: + case Stmt::OMPParallelMasterTaskLoopDirectiveClass: + case Stmt::OMPParallelMaskedTaskLoopDirectiveClass: + case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass: + case Stmt::OMPParallelMaskedTaskLoopSimdDirectiveClass: + case Stmt::OMPDistributeDirectiveClass: + case Stmt::OMPDistributeParallelForDirectiveClass: + case Stmt::OMPDistributeParallelForSimdDirectiveClass: + case Stmt::OMPDistributeSimdDirectiveClass: + case Stmt::OMPTargetParallelForSimdDirectiveClass: + case Stmt::OMPTargetSimdDirectiveClass: + case Stmt::OMPTeamsDistributeDirectiveClass: + case Stmt::OMPTeamsDistributeSimdDirectiveClass: + case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass: + case Stmt::OMPTeamsDistributeParallelForDirectiveClass: + case Stmt::OMPTargetTeamsDirectiveClass: + case Stmt::OMPTargetTeamsDistributeDirectiveClass: + case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: + case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: + case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: + case Stmt::OMPReverseDirectiveClass: + case Stmt::OMPStripeDirectiveClass: + case Stmt::OMPTileDirectiveClass: + case Stmt::OMPInterchangeDirectiveClass: + case Stmt::OMPSplitDirectiveClass: + case Stmt::OMPFuseDirectiveClass: + case Stmt::OMPInteropDirectiveClass: + case Stmt::OMPDispatchDirectiveClass: + case Stmt::OMPMaskedDirectiveClass: + case Stmt::OMPGenericLoopDirectiveClass: + case Stmt::OMPTeamsGenericLoopDirectiveClass: + case Stmt::OMPTargetTeamsGenericLoopDirectiveClass: + case Stmt::OMPParallelGenericLoopDirectiveClass: + case Stmt::OMPTargetParallelGenericLoopDirectiveClass: + case Stmt::CapturedStmtClass: + case Stmt::SYCLKernelCallStmtClass: + case Stmt::UnresolvedSYCLKernelCallStmtClass: + case Stmt::OpenACCComputeConstructClass: + case Stmt::OpenACCLoopConstructClass: + case Stmt::OpenACCCombinedConstructClass: + case Stmt::OpenACCDataConstructClass: + case Stmt::OpenACCEnterDataConstructClass: + case Stmt::OpenACCExitDataConstructClass: + case Stmt::OpenACCHostDataConstructClass: + case Stmt::OpenACCWaitConstructClass: + case Stmt::OpenACCCacheConstructClass: + case Stmt::OpenACCInitConstructClass: + case Stmt::OpenACCShutdownConstructClass: + case Stmt::OpenACCSetConstructClass: + case Stmt::OpenACCUpdateConstructClass: + case Stmt::OpenACCAtomicConstructClass: + case Stmt::OMPUnrollDirectiveClass: + case Stmt::OMPMetaDirectiveClass: + case Stmt::HLSLOutArgExprClass: return false; - // Cases not handled yet; but will handle some day. - case Stmt::DesignatedInitExprClass: - case Stmt::DesignatedInitUpdateExprClass: - case Stmt::ArrayInitIndexExprClass: - case Stmt::ExtVectorElementExprClass: - case Stmt::MatrixElementExprClass: - case Stmt::ImaginaryLiteralClass: - case Stmt::ObjCAtCatchStmtClass: - case Stmt::ObjCAtFinallyStmtClass: - case Stmt::ObjCAtTryStmtClass: - case Stmt::ObjCAutoreleasePoolStmtClass: - case Stmt::ObjCEncodeExprClass: - case Stmt::ObjCIsaExprClass: - case Stmt::ObjCProtocolExprClass: - case Stmt::ObjCSelectorExprClass: - case Stmt::ParenListExprClass: - case Stmt::ShuffleVectorExprClass: - case Stmt::ConvertVectorExprClass: - case Stmt::VAArgExprClass: - case Stmt::CUDAKernelCallExprClass: - case Stmt::OpaqueValueExprClass: - case Stmt::AsTypeExprClass: - case Stmt::ConceptSpecializationExprClass: - case Stmt::CXXRewrittenBinaryOperatorClass: - case Stmt::RequiresExprClass: - case Stmt::EmbedExprClass: + // ParenExprs already handled. + case Stmt::ParenExprClass: + return false; - // Cases we intentionally don't evaluate, since they don't need - // to be explicitly evaluated. - case Stmt::PredefinedExprClass: - case Stmt::AddrLabelExprClass: - case Stmt::IntegerLiteralClass: - case Stmt::FixedPointLiteralClass: - case Stmt::CharacterLiteralClass: - case Stmt::CXXScalarValueInitExprClass: - case Stmt::CXXBoolLiteralExprClass: - case Stmt::ObjCBoolLiteralExprClass: - case Stmt::ObjCAvailabilityCheckExprClass: - case Stmt::FloatingLiteralClass: - case Stmt::NoInitExprClass: - case Stmt::SizeOfPackExprClass: - case Stmt::StringLiteralClass: - case Stmt::SourceLocExprClass: - case Stmt::ObjCStringLiteralClass: - case Stmt::CXXPseudoDestructorExprClass: - case Stmt::SubstNonTypeTemplateParmExprClass: - case Stmt::CXXNullPtrLiteralExprClass: - case Stmt::ArraySectionExprClass: - case Stmt::OMPArrayShapingExprClass: - case Stmt::OMPIteratorExprClass: - case Stmt::SYCLUniqueStableNameExprClass: - case Stmt::OpenACCAsteriskSizeExprClass: - case Stmt::TypeTraitExprClass: + // GenericSelectionExprs already handled. + case Stmt::GenericSelectionExprClass: + return false; - case Stmt::ArrayInitLoopExprClass: - case Stmt::ArraySubscriptExprClass: - case Stmt::AttributedStmtClass: - case Stmt::CXXDefaultArgExprClass: - case Stmt::CXXDefaultInitExprClass: - case Stmt::CXXStdInitializerListExprClass: - case Expr::ObjCArrayLiteralClass: + // Cases that should never be evaluated simply because they shouldn't + // appear in the CFG. + case Stmt::BreakStmtClass: + case Stmt::CaseStmtClass: + case Stmt::CompoundStmtClass: + case Stmt::ContinueStmtClass: + case Stmt::CXXForRangeStmtClass: + case Stmt::DefaultStmtClass: + case Stmt::DoStmtClass: + case Stmt::ForStmtClass: + case Stmt::GotoStmtClass: + case Stmt::IfStmtClass: + case Stmt::IndirectGotoStmtClass: + case Stmt::LabelStmtClass: + case Stmt::NoStmtClass: + case Stmt::NullStmtClass: + case Stmt::SwitchStmtClass: + case Stmt::WhileStmtClass: + case Stmt::DeferStmtClass: + case Expr::MSDependentExistsStmtClass: + return false; + + // These nodes are shared in the CFG and would case caching out. + // Moreover, no additional evaluation required for them, the + // analyzer can reconstruct these values from the AST. + case Stmt::ImplicitValueInitExprClass: + return false; + + // These are handled by PseudoObjectExpr + case Stmt::ObjCSubscriptRefExprClass: + case Stmt::ObjCPropertyRefExprClass: + return false; + + // FIXME: Does not call checkers + case Stmt::GNUNullExprClass: + return false; + + // FIXME: Does not call PostVisit checkers case Stmt::ObjCAtSynchronizedStmtClass: - case Expr::ObjCDictionaryLiteralClass: - case Expr::ObjCBoxedExprClass: - case Stmt::AtomicExprClass: - case Stmt::ImplicitCastExprClass: - case Stmt::CStyleCastExprClass: - case Stmt::CXXStaticCastExprClass: - case Stmt::CXXDynamicCastExprClass: - case Stmt::CXXReinterpretCastExprClass: - case Stmt::CXXConstCastExprClass: - case Stmt::CXXFunctionalCastExprClass: - case Stmt::BuiltinBitCastExprClass: - case Stmt::ObjCBridgedCastExprClass: - case Stmt::CXXAddrspaceCastExprClass: + return K == PreVisitKind; + + // FIXME: They do not call checkers + case Expr::ConstantExprClass: + case Stmt::ExprWithCleanupsClass: + return false; + case Stmt::CXXBindTemporaryExprClass: - case Stmt::GCCAsmStmtClass: - case Stmt::CXXNewExprClass: - case Stmt::MaterializeTemporaryExprClass: - case Stmt::MemberExprClass: - case Stmt::ReturnStmtClass: - case Stmt::OffsetOfExprClass: - case Stmt::UnaryExprOrTypeTraitExprClass: - case Stmt::UnaryOperatorClass: return true; - } -} -static bool justRunCheckersAsPostVisit(const Stmt *S) { - switch (S->getStmtClass()) { - default: - return false; + case Stmt::ArrayInitLoopExprClass: + return true; // Cases not handled yet; but will handle some day. case Stmt::DesignatedInitExprClass: @@ -1224,6 +1327,7 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { case Stmt::CXXRewrittenBinaryOperatorClass: case Stmt::RequiresExprClass: case Stmt::EmbedExprClass: + return true; // Cases we intentionally don't evaluate, since they don't need // to be explicitly evaluated. @@ -1251,20 +1355,121 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { case Stmt::SYCLUniqueStableNameExprClass: case Stmt::OpenACCAsteriskSizeExprClass: case Stmt::TypeTraitExprClass: + return true; - case Stmt::ArrayInitLoopExprClass: - case Stmt::ArraySubscriptExprClass: case Stmt::AttributedStmtClass: + return true; + case Stmt::CXXDefaultArgExprClass: case Stmt::CXXDefaultInitExprClass: + return true; + case Stmt::CXXStdInitializerListExprClass: case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: case Expr::ObjCBoxedExprClass: - case Stmt::AtomicExprClass: - case Stmt::ObjCIvarRefExprClass: - case Stmt::ObjCForCollectionStmtClass: + return true; + + case Stmt::ArraySubscriptExprClass: + return true; + + // Support for MatrixSingleSubscriptExprClass is not implemented. + case Stmt::MatrixSingleSubscriptExprClass: + return false; + + // Support for MatrixSubscriptExpr is not implemented. + case Stmt::MatrixSubscriptExprClass: + return false; + + case Stmt::GCCAsmStmtClass: + return true; + + // FIXME: Does not call checkers + case Stmt::MSAsmStmtClass: + return false; + + // FIXME: Does not call PreVisit checkers case Stmt::BlockExprClass: + return K == PostVisitKind; + + // FIXME: Does not call PreVisit checkers + // Currently the engine does not call PostVisit checkers when + // lambda inlining is disabled, so K == PostVisiKind + // cannot be returned here. + case Stmt::LambdaExprClass: + return false; + + // Checkers are called manually with custom logic when this calls + // VisitBinaryOperator, but calls no checkers during VisitLogicalExpr + case Stmt::BinaryOperatorClass: + return false; + + // Checkers are called manually with custom logic in these cases + // (VisitCallExpr) + case Stmt::CXXOperatorCallExprClass: + case Stmt::CallExprClass: + case Stmt::CXXMemberCallExprClass: + case Stmt::UserDefinedLiteralClass: + return false; + + // FIXME: Does not call checkers + case Stmt::CXXCatchStmtClass: + return false; + + // Checkers are called manually with custom logic in these cases + // (handleConstructor) + case Stmt::CXXTemporaryObjectExprClass: + case Stmt::CXXConstructExprClass: + return false; + + // Checkers are called manually with custom logic in this case + // (handleConstructor) + case Stmt::CXXInheritedCtorInitExprClass: + return false; + + case Stmt::CXXNewExprClass: + return true; + + // FIXME: + // The engine calls both PreStmt and PostStmt checkers before + // actually evaluating this kind of expression. In a separate + // non NFC patch, the order should be modified to calling PreStmt + // checkers, then evaluating and finally calling PostStmt. + // Once that patch is merged, this stmt kind can also be refactored + // to the new algorithmic scheme. + case Stmt::CXXDeleteExprClass: + return false; + + // FIXME: Does not call checkers + case Stmt::ChooseExprClass: + return false; + + // Checkers are called manually with custom logic in this case + // (VisitBinaryOperator) + case Stmt::CompoundAssignOperatorClass: + return false; + + // FIXME: Does not call checkers + case Stmt::CompoundLiteralExprClass: + return false; + + // FIXME: These do not call checkers + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: + return false; + + // FIXME: Does not call checkers + case Stmt::CXXThisExprClass: + return false; + + // FIXME: Does not call checkers + case Stmt::DeclRefExprClass: + return false; + + // Checkers are called manually with custom logic in this case + case Stmt::DeclStmtClass: + return false; + case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: case Stmt::CXXStaticCastExprClass: @@ -1275,16 +1480,67 @@ static bool justRunCheckersAsPostVisit(const Stmt *S) { case Stmt::BuiltinBitCastExprClass: case Stmt::ObjCBridgedCastExprClass: case Stmt::CXXAddrspaceCastExprClass: - case Stmt::CXXBindTemporaryExprClass: - case Stmt::GCCAsmStmtClass: - case Stmt::CXXNewExprClass: - case Stmt::MaterializeTemporaryExprClass: + return true; + + case Expr::MaterializeTemporaryExprClass: + return true; + + // FIXME: These do not call checkers + // (ConstructInitList) + case Stmt::InitListExprClass: + case Expr::CXXParenListInitExprClass: + return false; + case Stmt::MemberExprClass: + return true; + + case Stmt::AtomicExprClass: + return true; + + // FIXME: Does not call PreVisit checkers + case Stmt::ObjCIvarRefExprClass: + return K == PostVisitKind; + + // FIXME: Does not call PreVisit checkers + case Stmt::ObjCForCollectionStmtClass: + return K == PostVisitKind; + + // FIXME: Does not call checkers + case Stmt::ObjCMessageExprClass: + return false; + + // FIXME: These do not call checkers + case Stmt::ObjCAtThrowStmtClass: + case Stmt::CXXThrowExprClass: + return false; + + // FIXME: Does not call PostVisit checkers + case Stmt::ReturnStmtClass: + return K == PreVisitKind; + case Stmt::OffsetOfExprClass: + return true; + case Stmt::UnaryExprOrTypeTraitExprClass: + return true; + + // FIXME: Does not call checkers + case Stmt::StmtExprClass: + return false; + case Stmt::UnaryOperatorClass: return true; + + // FIXME: Does not call checkers + case Stmt::PseudoObjectExprClass: + return false; + + // FIXME: Does not call checkers + case Expr::ObjCIndirectCopyRestoreExprClass: + return false; } + + return false; } void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) { @@ -1305,7 +1561,7 @@ void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) { ExplodedNodeSet PreVisited; for (const auto I : CleanedStates) { ExplodedNodeSet Tmp; - if (justRunCheckersAsPreVisit(currStmt)) { + if (shouldJustCallCheckers(currStmt, PreVisitKind)) { getCheckerManager().runCheckersForPreStmt(Tmp, I, currStmt, *this); PreVisited.insert(Tmp); } else @@ -1322,7 +1578,7 @@ void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) { ExplodedNodeSet PostVisited; for (const auto I : Visited) { ExplodedNodeSet Tmp; - if (justRunCheckersAsPostVisit(currStmt)) { + if (shouldJustCallCheckers(currStmt, PostVisitKind)) { getCheckerManager().runCheckersForPostStmt(Tmp, I, currStmt, *this); PostVisited.insert(Tmp); } else From 9da48e90e63017bf172c8833e85d61f61d39709e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Mon, 24 Aug 2026 10:32:43 +0200 Subject: [PATCH 29/31] Handle new statement kinds after rebase. --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index d43bff3c27507..46075efddb4e7 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1124,6 +1124,7 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) { case Stmt::ExpressionTraitExprClass: case Stmt::UnresolvedLookupExprClass: case Stmt::UnresolvedMemberExprClass: + case Stmt::DependentTemplateIdExprClass: case Stmt::RecoveryExprClass: case Stmt::CXXNoexceptExprClass: case Stmt::PackExpansionExprClass: @@ -1167,7 +1168,8 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) { case Stmt::OMPFlushDirectiveClass: case Stmt::OMPDepobjDirectiveClass: case Stmt::OMPScanDirectiveClass: - case Stmt::OMPOrderedDirectiveClass: + case Stmt::OMPOrderedStandaloneDirectiveClass: + case Stmt::OMPOrderedBlockAssocDirectiveClass: case Stmt::OMPAtomicDirectiveClass: case Stmt::OMPAssumeDirectiveClass: case Stmt::OMPTargetDirectiveClass: From 915e3623568486332a032962eeea1b939ffd493e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Mon, 24 Aug 2026 10:38:47 +0200 Subject: [PATCH 30/31] `ExprEngine::VisitCXXDeleteExpr` --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 26 ++++---------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 46075efddb4e7..41513efaf98b8 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1432,15 +1432,8 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) { case Stmt::CXXNewExprClass: return true; - // FIXME: - // The engine calls both PreStmt and PostStmt checkers before - // actually evaluating this kind of expression. In a separate - // non NFC patch, the order should be modified to calling PreStmt - // checkers, then evaluating and finally calling PostStmt. - // Once that patch is merged, this stmt kind can also be refactored - // to the new algorithmic scheme. case Stmt::CXXDeleteExprClass: - return false; + return true; // FIXME: Does not call checkers case Stmt::ChooseExprClass: @@ -2546,21 +2539,12 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, VisitCXXNewExpr(cast(S), Pred, Dst); break; - case Stmt::CXXDeleteExprClass: { - ExplodedNodeSet PreVisit; - const auto *CDE = cast(S); - getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); - - ExplodedNodeSet PostVisit; - for (const auto i : PreVisit) - VisitCXXDeleteExpr(CDE, i, PostVisit); - - getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); + case Stmt::CXXDeleteExprClass: + VisitCXXDeleteExpr(cast(S), Pred, Dst); break; - } - // FIXME: ChooseExpr is really a constant. We need to fix - // the CFG do not model them as explicit control-flow. + // FIXME: ChooseExpr is really a constant. We need to fix + // the CFG do not model them as explicit control-flow. case Stmt::ChooseExprClass: { // __builtin_choose_expr const auto *C = cast(S); VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); From 89b94721179e18fae14c97c38546d2d1d40dcfa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= Date: Tue, 25 Aug 2026 16:08:53 +0200 Subject: [PATCH 31/31] Fix formatting. --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 41513efaf98b8..dc479b00162b6 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -2448,8 +2448,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // Escape pointers passed into the list, unless it's an ObjC boxed // expression which is not a boxable C structure. if (!(isa(Ex) && - !cast(Ex)->getSubExpr() - ->getType()->isRecordType())) + !cast(Ex)->getSubExpr()->getType()->isRecordType())) for (auto Child : Ex->children()) { assert(Child); const auto *ChildExpr = dyn_cast(Child); @@ -3704,8 +3703,8 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, // pointers as soon as they are used. if (!M->isGLValue()) { assert(M->getType()->isArrayType()); - const auto *PE = - dyn_cast(Pred->getParentMap().getParentIgnoreParens(M)); + const auto *PE = dyn_cast( + Pred->getParentMap().getParentIgnoreParens(M)); if (!PE || PE->getCastKind() != CK_ArrayToPointerDecay) { llvm_unreachable("should always be wrapped in ArrayToPointerDecay"); } @@ -3718,8 +3717,8 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, L = UnknownVal(); } - Dst.insert(Engine.makeNodeWithBinding( - Pred, M, L, state, ProgramPoint::PostLValueKind)); + Dst.insert(Engine.makeNodeWithBinding(Pred, M, L, state, + ProgramPoint::PostLValueKind)); } else { evalLoad(Dst, M, M, Pred, state, L); }