[clang] [NFC][analyzer] Cleanup checker calls in `ExprEngine::ProcessStmt`, `ExprEngine::Visit*` (PR #218691)
Gábor Tóthvári via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 1 07:13:51 PDT 2026
https://github.com/tigbr updated https://github.com/llvm/llvm-project/pull/218691
>From cd8de4a86d37c856cb2b4f9025ae9cf10f48c791 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
<gabor.tothvari at ericsson.com>
Date: Wed, 5 Aug 2026 14:12:55 +0200
Subject: [PATCH 01/39] 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?=
<gabor.tothvari at ericsson.com>
Date: Wed, 5 Aug 2026 15:19:47 +0200
Subject: [PATCH 02/39] `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<CXXBindTemporaryExpr>(S), PreVisit, Next);
- getCheckerManager().runCheckersForPostStmt(Dst, Next, S, *this);
+ case Stmt::CXXBindTemporaryExprClass:
+ VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), Pred, Dst);
break;
- }
case Stmt::ArrayInitLoopExprClass:
VisitArrayInitLoopExpr(cast<ArrayInitLoopExpr>(S), Pred, Dst);
>From c24938ee56578c57d1055a8719b5eca52f09cf51 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
<gabor.tothvari at ericsson.com>
Date: Fri, 7 Aug 2026 17:19:10 +0200
Subject: [PATCH 03/39] `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<StmtExpr>(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<Expr>(*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<StmtExpr>(S), Pred, Dst);
break;
- }
case Stmt::UnaryOperatorClass: {
const auto *U = cast<UnaryOperator>(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<StmtExpr>(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<Expr>(*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?=
<gabor.tothvari at ericsson.com>
Date: Fri, 7 Aug 2026 17:39:22 +0200
Subject: [PATCH 04/39] `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<ReturnStmt>(S), Pred, Dst);
break;
- case Stmt::OffsetOfExprClass: {
- ExplodedNodeSet PreVisit;
- getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
-
- ExplodedNodeSet PostVisit;
- for (const auto Node : PreVisit)
- VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Node, PostVisit);
-
- getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this);
+ case Stmt::OffsetOfExprClass:
+ VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Pred, Dst);
break;
- }
case Stmt::UnaryExprOrTypeTraitExprClass:
VisitUnaryExprOrTypeTraitExpr(cast<UnaryExprOrTypeTraitExpr>(S), Pred,
>From 19d7f0cd055ab130057c2ed6d6f378acbab661cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
<gabor.tothvari at ericsson.com>
Date: Mon, 10 Aug 2026 14:42:21 +0200
Subject: [PATCH 05/39] `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<StmtExpr>(S), Pred, Dst);
break;
- case Stmt::UnaryOperatorClass: {
- const auto *U = cast<UnaryOperator>(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<UnaryOperator>(S), Pred, Dst);
break;
- }
case Stmt::PseudoObjectExprClass: {
const auto *PE = cast<PseudoObjectExpr>(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<CXXMethodDecl, FieldDecl, IndirectFieldDecl>(VD)) {
SVal SV = svalBuilder.getMemberPointer(cast<NamedDecl>(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<NonLoc>()));
- break;
- case UO_Minus:
- // FIXME: Do we need to handle promotions?
- state =
- state->BindExpr(U, SF, svalBuilder.evalMinus(V.castAs<NonLoc>()));
- 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<Loc> LV = V.getAs<Loc>()) {
+ default:
+ llvm_unreachable("Invalid Opcode.");
+ case UO_Not:
+ // FIXME: Do we need to handle promotions?
+ state = state->BindExpr(U, SF,
+ svalBuilder.evalComplement(V.castAs<NonLoc>()));
+ break;
+ case UO_Minus:
+ // FIXME: Do we need to handle promotions?
+ state =
+ state->BindExpr(U, SF, svalBuilder.evalMinus(V.castAs<NonLoc>()));
+ 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<Loc> LV = V.getAs<Loc>()) {
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<NonLoc>(), 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?=
<gabor.tothvari at ericsson.com>
Date: Mon, 10 Aug 2026 14:59:53 +0200
Subject: [PATCH 06/39] `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<UnaryOperator>(S), Pred, Dst);
break;
- case Stmt::PseudoObjectExprClass: {
- const auto *PE = cast<PseudoObjectExpr>(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<PseudoObjectExpr>(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?=
<gabor.tothvari at ericsson.com>
Date: Mon, 10 Aug 2026 15:17:49 +0200
Subject: [PATCH 07/39] `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<PseudoObjectExpr>(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<ObjCIndirectCopyRestoreExpr>(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<ObjCIndirectCopyRestoreExpr>(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?=
<gabor.tothvari at ericsson.com>
Date: Mon, 10 Aug 2026 15:56:56 +0200
Subject: [PATCH 08/39] `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<Expr *> 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<MaterializeTemporaryExpr>(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<MaterializeTemporaryExpr>(S), Pred,
+ Dst);
break;
- }
case Stmt::InitListExprClass: {
const InitListExpr *E = cast<InitListExpr>(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?=
<gabor.tothvari at ericsson.com>
Date: Mon, 10 Aug 2026 16:15:59 +0200
Subject: [PATCH 09/39] `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<CXXParenListInitExpr>(S);
- ConstructInitList(E, E->getInitExprs(), /*IsTransparent*/ false, Pred,
- Dst);
+ case Expr::CXXParenListInitExprClass:
+ VisitCXXParenListInitExpr(cast<CXXParenListInitExpr>(S), Pred, Dst);
break;
- }
case Stmt::MemberExprClass:
VisitMemberExpr(cast<MemberExpr>(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?=
<gabor.tothvari at ericsson.com>
Date: Mon, 10 Aug 2026 18:15:47 +0200
Subject: [PATCH 10/39] `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<CastExpr>(S);
- ExplodedNodeSet dstExpr;
- VisitCast(C, C->getSubExpr(), Pred, dstExpr);
-
- // Handle the postvisit checks.
- getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this);
+ case Stmt::CXXAddrspaceCastExprClass:
+ VisitCastExpr(cast<CastExpr>(S), Pred, Dst);
break;
- }
case Expr::MaterializeTemporaryExprClass:
VisitMaterializeTemporaryExpr(cast<MaterializeTemporaryExpr>(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<ExplicitCastExpr>(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<nonloc::PointerToMember>();
- 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<nonloc::PointerToMember>(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<SVal> 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<nonloc::PointerToMember>();
+ 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<nonloc::PointerToMember>(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<SVal> 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<SVal> 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<nonloc::PointerToMember>()) {
- 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<SVal> 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<nonloc::PointerToMember>()) {
+ 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?=
<gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 14:24:28 +0200
Subject: [PATCH 11/39] `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<CXXNewExpr>(S), i, PostVisit);
-
- getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this);
+ case Stmt::CXXNewExprClass:
+ VisitCXXNewExpr(cast<CXXNewExpr>(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?=
<gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 15:24:46 +0200
Subject: [PATCH 12/39] `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<LambdaExpr>(S), Pred, Dst);
- } else {
- const ExplodedNode *Node = Engine.makePostStmtNode(
- S, Pred->getState(), Pred, /*MarkAsSink=*/true);
- Engine.addAbortedBlock(Node, getCurrBlock());
- }
+ VisitLambdaExpr(cast<LambdaExpr>(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?=
<gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 15:44:42 +0200
Subject: [PATCH 13/39] `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<GCCAsmStmt>(S), N, PostVisit);
- getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this);
+ case Stmt::GCCAsmStmtClass:
+ VisitGCCAsmStmt(cast<GCCAsmStmt>(S), Pred, Dst);
break;
- }
case Stmt::MSAsmStmtClass:
VisitMSAsmStmt(cast<MSAsmStmt>(S), Pred, Dst);
>From 7b68df1c087f12c23103ec4495f98c54ad38596b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
<gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 15:59:14 +0200
Subject: [PATCH 14/39] `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<ObjCObjectType>()) {
- // 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<ObjCObjectType>()) {
+ // 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?=
<gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 16:14:34 +0200
Subject: [PATCH 15/39] `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?=
<gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 16:45:16 +0200
Subject: [PATCH 16/39] `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<SVal, 8> ValuesToInvalidate;
- for (const Stmt *SubExpr : AE->children()) {
- SVal SubExprVal = State->getSVal(cast<Expr>(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<SVal, 8> ValuesToInvalidate;
+ for (const Stmt *SubExpr : AE->children()) {
+ SVal SubExprVal = State->getSVal(cast<Expr>(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?=
<gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 16:53:59 +0200
Subject: [PATCH 17/39] `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<VarDecl, EnumConstantDecl>(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<CXXMethodDecl>(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<CXXMethodDecl>(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<SymbolicRegion>(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<FieldDecl>(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<ImplicitCastExpr>(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<SymbolicRegion>(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<FieldDecl>(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<ImplicitCastExpr>(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?=
<gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 17:58:53 +0200
Subject: [PATCH 18/39] `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?=
<gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 18:16:44 +0200
Subject: [PATCH 19/39] `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?=
<gabor.tothvari at ericsson.com>
Date: Wed, 12 Aug 2026 18:38:14 +0200
Subject: [PATCH 20/39] `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<AttributedStmt>(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<CXXAssumeAttr>(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<DefinedOrUnknownSVal>()) {
- State = State->assume(*ValidAssumedVal, true);
- }
+ ProgramStateRef State = Pred->getState();
- if (!State)
- break;
+ for (const auto *Attr : getSpecificAttrs<CXXAssumeAttr>(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<DefinedOrUnknownSVal>()) {
+ 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?=
<gabor.tothvari at ericsson.com>
Date: Thu, 13 Aug 2026 14:45:05 +0200
Subject: [PATCH 21/39] `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<CXXConstructExpr>(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<MemberExpr>(Arr)) {
- Expr *MEBase = ME->getBase();
-
- // Move ctor
- if (auto CXXSCE = dyn_cast<CXXStaticCastExpr>(MEBase)) {
- MEBase = CXXSCE->getSubExpr();
- }
+ SVal Base = UnknownVal();
- auto ObjDeclExpr = cast<DeclRefExpr>(MEBase);
- SVal Obj = state->getLValue(cast<VarDecl>(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<FieldDecl>(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<MemberExpr>(Arr)) {
+ Expr *MEBase = ME->getBase();
+
+ // Move ctor
+ if (auto CXXSCE = dyn_cast<CXXStaticCastExpr>(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<DeclRefExpr>(Arr))
- Base = state->getLValue(cast<VarDecl>(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<DeclRefExpr>(MEBase);
+ SVal Obj = state->getLValue(cast<VarDecl>(ObjDeclExpr->getDecl()), SF);
- EvalSet.insert(Engine.makeNodeWithBinding(Node, Ex, Base));
+ Base = state->getLValue(cast<FieldDecl>(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<DeclRefExpr>(Arr))
+ Base = state->getLValue(cast<VarDecl>(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?=
<gabor.tothvari at ericsson.com>
Date: Thu, 13 Aug 2026 15:03:10 +0200
Subject: [PATCH 22/39] 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?=
<gabor.tothvari at ericsson.com>
Date: Thu, 13 Aug 2026 16:00:04 +0200
Subject: [PATCH 23/39] `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<CXXDefaultArgExpr>(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<Expr>(S), SF, *ConstantVal);
- if (IsTemporary)
- State = createTemporaryRegionIfNeeded(State, SF, cast<Expr>(S),
- cast<Expr>(S));
- Tmp.insert(Engine.makePostStmtNode(S, State, I));
- }
+ ProgramStateRef State = Pred->getState();
+ State = State->BindExpr(cast<Expr>(S), SF, *ConstantVal);
+ if (IsTemporary)
+ State = createTemporaryRegionIfNeeded(State, SF, cast<Expr>(S),
+ cast<Expr>(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?=
<gabor.tothvari at ericsson.com>
Date: Thu, 13 Aug 2026 16:15:42 +0200
Subject: [PATCH 24/39] `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<Expr>(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<ObjCBoxedExpr>(Ex) &&
- !cast<ObjCBoxedExpr>(Ex)->getSubExpr()
- ->getType()->isRecordType()))
- for (auto Child : Ex->children()) {
- assert(Child);
- const auto *ChildExpr = dyn_cast<Expr>(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<ObjCBoxedExpr>(Ex) &&
+ !cast<ObjCBoxedExpr>(Ex)->getSubExpr()
+ ->getType()->isRecordType()))
+ for (auto Child : Ex->children()) {
+ assert(Child);
+ const auto *ChildExpr = dyn_cast<Expr>(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?=
<gabor.tothvari at ericsson.com>
Date: Thu, 13 Aug 2026 16:45:53 +0200
Subject: [PATCH 25/39] `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?=
<gabor.tothvari at ericsson.com>
Date: Thu, 13 Aug 2026 16:54:10 +0200
Subject: [PATCH 26/39] `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?=
<gabor.tothvari at ericsson.com>
Date: Fri, 14 Aug 2026 14:35:24 +0200
Subject: [PATCH 27/39] `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?=
<gabor.tothvari at ericsson.com>
Date: Fri, 14 Aug 2026 18:10:03 +0200
Subject: [PATCH 28/39] 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?=
<gabor.tothvari at ericsson.com>
Date: Mon, 24 Aug 2026 10:32:43 +0200
Subject: [PATCH 29/39] 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?=
<gabor.tothvari at ericsson.com>
Date: Mon, 24 Aug 2026 10:38:47 +0200
Subject: [PATCH 30/39] `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<CXXNewExpr>(S), Pred, Dst);
break;
- case Stmt::CXXDeleteExprClass: {
- ExplodedNodeSet PreVisit;
- const auto *CDE = cast<CXXDeleteExpr>(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<CXXDeleteExpr>(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<ChooseExpr>(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?=
<gabor.tothvari at ericsson.com>
Date: Tue, 25 Aug 2026 16:08:53 +0200
Subject: [PATCH 31/39] 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<ObjCBoxedExpr>(Ex) &&
- !cast<ObjCBoxedExpr>(Ex)->getSubExpr()
- ->getType()->isRecordType()))
+ !cast<ObjCBoxedExpr>(Ex)->getSubExpr()->getType()->isRecordType()))
for (auto Child : Ex->children()) {
assert(Child);
const auto *ChildExpr = dyn_cast<Expr>(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<ImplicitCastExpr>(Pred->getParentMap().getParentIgnoreParens(M));
+ const auto *PE = dyn_cast<ImplicitCastExpr>(
+ 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);
}
>From 492717d67933965a8587da50891a6797e8f64173 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
<gabor.tothvari at ericsson.com>
Date: Mon, 31 Aug 2026 16:58:31 +0200
Subject: [PATCH 32/39] Simplify switch in `shouldJustCallCheckers`
return true by default and return true for unsupported statement kinds
---
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 302 +------------------
1 file changed, 1 insertion(+), 301 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index dc479b00162b6..f596ded35fd13 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1109,180 +1109,7 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
switch (S->getStmtClass()) {
- // 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::DependentTemplateIdExprClass:
- 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::OMPOrderedStandaloneDirectiveClass:
- case Stmt::OMPOrderedBlockAssocDirectiveClass:
- 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;
-
- // ParenExprs already handled.
- case Stmt::ParenExprClass:
- return false;
-
- // GenericSelectionExprs already handled.
- case Stmt::GenericSelectionExprClass:
- return false;
-
- // 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;
+ default: return true;
// FIXME: Does not call checkers
case Stmt::GNUNullExprClass:
@@ -1297,95 +1124,6 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
case Stmt::ExprWithCleanupsClass:
return false;
- case Stmt::CXXBindTemporaryExprClass:
- return true;
-
- case Stmt::ArrayInitLoopExprClass:
- return true;
-
- // 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:
- return true;
-
- // 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:
- return true;
-
- 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:
- 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;
@@ -1429,12 +1167,6 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
case Stmt::CXXInheritedCtorInitExprClass:
return false;
- case Stmt::CXXNewExprClass:
- return true;
-
- case Stmt::CXXDeleteExprClass:
- return true;
-
// FIXME: Does not call checkers
case Stmt::ChooseExprClass:
return false;
@@ -1465,33 +1197,12 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
case Stmt::DeclStmtClass:
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:
- 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;
@@ -1513,19 +1224,10 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
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;
@@ -1534,8 +1236,6 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
case Expr::ObjCIndirectCopyRestoreExprClass:
return false;
}
-
- return false;
}
void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) {
>From 7cbe3020a561dbd106c58dfebfe5ab230d341ee8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
<gabor.tothvari at ericsson.com>
Date: Mon, 31 Aug 2026 17:50:41 +0200
Subject: [PATCH 33/39] Revert "`ExprEngine::VisitUnaryOperator`"
This reverts commit 19d7f0cd055ab130057c2ed6d6f378acbab661cf.
---
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 19 +++-
clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 93 ++++++++++---------
2 files changed, 63 insertions(+), 49 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index f596ded35fd13..94d23c74c80fc 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1109,7 +1109,8 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
switch (S->getStmtClass()) {
- default: return true;
+ default:
+ return true;
// FIXME: Does not call checkers
case Stmt::GNUNullExprClass:
@@ -1228,6 +1229,10 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
case Stmt::StmtExprClass:
return false;
+ // Checkers are called manually with custom logic in this case
+ case Stmt::UnaryOperatorClass:
+ return false;
+
// FIXME: Does not call checkers
case Stmt::PseudoObjectExprClass:
return false;
@@ -2351,9 +2356,17 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
VisitStmtExpr(cast<StmtExpr>(S), Pred, Dst);
break;
- case Stmt::UnaryOperatorClass:
- VisitUnaryOperator(cast<UnaryOperator>(S), Pred, Dst);
+ case Stmt::UnaryOperatorClass: {
+ const auto *U = cast<UnaryOperator>(S);
+ if (AMgr.options.ShouldEagerlyAssume && (U->getOpcode() == UO_LNot)) {
+ ExplodedNodeSet Tmp;
+ VisitUnaryOperator(U, Pred, Tmp);
+ evalEagerlyAssumeBifurcation(Dst, Tmp, U);
+ }
+ else
+ VisitUnaryOperator(U, Pred, Dst);
break;
+ }
case Stmt::PseudoObjectExprClass:
VisitPseudoObjectExpr(cast<PseudoObjectExpr>(S), Pred, Dst);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 4e6f93d783fc1..e5ced3806de9b 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -849,8 +849,13 @@ 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) {
@@ -859,11 +864,12 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred,
return Engine.makeNodeWithBinding(N, U, SV);
};
- auto VisitUnaryOperatorImpl = [U, Pred, &Engine = Engine, this,
- MakeNodeForIdentityOp](ExplodedNodeSet &Dst) {
+ for (ExplodedNode *N : CheckedSet) {
switch (U->getOpcode()) {
default: {
- VisitIncrementDecrementOperator(U, Pred, Dst);
+ ExplodedNodeSet Tmp;
+ VisitIncrementDecrementOperator(U, N, Tmp);
+ EvalSet.insert(Tmp);
break;
}
case UO_Real: {
@@ -872,13 +878,13 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred,
// FIXME: We don't have complex SValues yet.
if (Ex->getType()->isAnyComplexType()) {
// Just report "Unknown."
- Dst.insert(Pred);
+ EvalSet.insert(N);
break;
}
// For all other types, UO_Real is an identity operation.
assert (U->getType() == Ex->getType());
- Dst.insert(MakeNodeForIdentityOp(Pred));
+ EvalSet.insert(MakeNodeForIdentityOp(N));
break;
}
@@ -887,12 +893,12 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred,
// FIXME: We don't have complex SValues yet.
if (Ex->getType()->isAnyComplexType()) {
// Just report "Unknown."
- Dst.insert(Pred);
+ EvalSet.insert(N);
break;
}
// For all other types, UO_Imag returns 0.
SVal X = svalBuilder.makeZeroVal(Ex->getType());
- Dst.insert(Engine.makeNodeWithBinding(Pred, U, X));
+ EvalSet.insert(Engine.makeNodeWithBinding(N, U, X));
break;
}
@@ -904,12 +910,12 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred,
if (isa<CXXMethodDecl, FieldDecl, IndirectFieldDecl>(VD)) {
SVal SV = svalBuilder.getMemberPointer(cast<NamedDecl>(VD));
- Dst.insert(Engine.makeNodeWithBinding(Pred, U, SV));
+ EvalSet.insert(Engine.makeNodeWithBinding(N, U, SV));
break;
}
}
// Explicitly proceed with default handler for this case cascade.
- Dst.insert(MakeNodeForIdentityOp(Pred));
+ EvalSet.insert(MakeNodeForIdentityOp(N));
break;
}
case UO_Plus:
@@ -917,7 +923,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred,
[[fallthrough]];
case UO_Deref:
case UO_Extension: {
- Dst.insert(MakeNodeForIdentityOp(Pred));
+ EvalSet.insert(MakeNodeForIdentityOp(N));
break;
}
@@ -926,62 +932,57 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator *U, ExplodedNode *Pred,
case UO_Not: {
assert (!U->isGLValue());
const Expr *Ex = U->getSubExpr()->IgnoreParens();
- ProgramStateRef state = Pred->getState();
- const StackFrame *SF = Pred->getStackFrame();
+ ProgramStateRef state = N->getState();
+ const StackFrame *SF = N->getStackFrame();
// Get the value of the subexpression.
SVal V = state->getSVal(Ex, SF);
if (V.isUnknownOrUndef()) {
- Dst.insert(Engine.makeNodeWithBinding(Pred, U, V));
+ EvalSet.insert(Engine.makeNodeWithBinding(N, 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<NonLoc>()));
- break;
- case UO_Minus:
- // FIXME: Do we need to handle promotions?
- state =
- state->BindExpr(U, SF, svalBuilder.evalMinus(V.castAs<NonLoc>()));
- 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<Loc> LV = V.getAs<Loc>()) {
+ default:
+ llvm_unreachable("Invalid Opcode.");
+ case UO_Not:
+ // FIXME: Do we need to handle promotions?
+ state = state->BindExpr(
+ U, SF, svalBuilder.evalComplement(V.castAs<NonLoc>()));
+ break;
+ case UO_Minus:
+ // FIXME: Do we need to handle promotions?
+ state =
+ state->BindExpr(U, SF, svalBuilder.evalMinus(V.castAs<NonLoc>()));
+ 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<Loc> LV = V.getAs<Loc>()) {
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<NonLoc>(), X, U->getType());
- }
+ }
- state = state->BindExpr(U, SF, Result);
- break;
+ state = state->BindExpr(U, SF, Result);
+ break;
}
- Dst.insert(Engine.makePostStmtNode(U, state, Pred));
+ EvalSet.insert(Engine.makePostStmtNode(U, state, N));
break;
}
}
- };
+ }
- if (AMgr.options.ShouldEagerlyAssume && (U->getOpcode() == UO_LNot)) {
- ExplodedNodeSet Tmp;
- VisitUnaryOperatorImpl(Tmp);
- evalEagerlyAssumeBifurcation(Dst, Tmp, U);
- } else
- VisitUnaryOperatorImpl(Dst);
+ getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, U, *this);
}
void ExprEngine::VisitPseudoObjectExpr(const PseudoObjectExpr *PE,
>From bfa1a108584d439b62d96af76abc0329bfd0d800 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
<gabor.tothvari at ericsson.com>
Date: Mon, 31 Aug 2026 18:17:31 +0200
Subject: [PATCH 34/39] Undo accidental change of `llvm_unreachable` message.
---
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 94d23c74c80fc..598ce5b12758b 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3360,7 +3360,7 @@ void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A,
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");
+a vector and not a forbidden lvalue type");
}
}
>From 54d39c4d5d150b4a3ce42c1d9c5c7285915919bb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
<gabor.tothvari at ericsson.com>
Date: Tue, 1 Sep 2026 14:11:56 +0200
Subject: [PATCH 35/39] Delete redundant cast to `StmtExpr`
---
.../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 2 +-
clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 52d94d25ee096..a6281a54c2b29 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -619,7 +619,7 @@ class ExprEngine {
void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex,
ExplodedNode *Pred, ExplodedNodeSet &Dst);
- void VisitStmtExpr(const StmtExpr *S, ExplodedNode *Pred,
+ void VisitStmtExpr(const StmtExpr *SE, ExplodedNode *Pred,
ExplodedNodeSet &Dst);
/// VisitUnaryOperator - Transfer function logic for unary operators.
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index e5ced3806de9b..2dfc551fca398 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -834,9 +834,8 @@ void ExprEngine::VisitUnaryExprOrTypeTraitExpr(
Dst.insert(Engine.makeNodeWithBinding(Pred, Ex, V));
}
-void ExprEngine::VisitStmtExpr(const StmtExpr *S, ExplodedNode *Pred,
+void ExprEngine::VisitStmtExpr(const StmtExpr *SE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
- const auto *SE = cast<StmtExpr>(S);
if (SE->getSubStmt()->body_empty()) {
// Empty statement expression.
assert(SE->getType() == getContext().VoidTy &&
>From 7a4fe89964699d4e82e82c9070446db19b24bf70 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
<gabor.tothvari at ericsson.com>
Date: Tue, 1 Sep 2026 14:26:33 +0200
Subject: [PATCH 36/39] Change `VisitObjCIndirectCopyRestoreClass` suffix to
`Expr`
---
.../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 2 +-
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 2 +-
clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index a6281a54c2b29..13abe41378dac 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -629,7 +629,7 @@ class ExprEngine {
void VisitPseudoObjectExpr(const PseudoObjectExpr *PE, ExplodedNode *Pred,
ExplodedNodeSet &Dst);
- void VisitObjCIndirectCopyRestoreClass(const ObjCIndirectCopyRestoreExpr *OIE,
+ void VisitObjCIndirectCopyRestoreExpr(const ObjCIndirectCopyRestoreExpr *OIE,
ExplodedNode *Pred,
ExplodedNodeSet &Dst);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 598ce5b12758b..f895b80db1590 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2373,7 +2373,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
break;
case Expr::ObjCIndirectCopyRestoreExprClass:
- VisitObjCIndirectCopyRestoreClass(cast<ObjCIndirectCopyRestoreExpr>(S),
+ VisitObjCIndirectCopyRestoreExpr(cast<ObjCIndirectCopyRestoreExpr>(S),
Pred, Dst);
break;
}
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 2dfc551fca398..c9a0313dd32ba 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -993,7 +993,7 @@ void ExprEngine::VisitPseudoObjectExpr(const PseudoObjectExpr *PE,
Dst.insert(Engine.makeNodeWithBinding(Pred, PE, V));
}
-void ExprEngine::VisitObjCIndirectCopyRestoreClass(
+void ExprEngine::VisitObjCIndirectCopyRestoreExpr(
const ObjCIndirectCopyRestoreExpr *OIE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
// ObjCIndirectCopyRestoreExpr implies passing a temporary for
>From 7375eb3d994788295cc5dfd8061e84915e80f553 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
<gabor.tothvari at ericsson.com>
Date: Tue, 1 Sep 2026 14:44:21 +0200
Subject: [PATCH 37/39] Hoist redundant declarations of `StackFrame` and
`ProgramStateRef`
---
clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 45 ++++++++-----------
1 file changed, 19 insertions(+), 26 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index c9a0313dd32ba..c60dbeabf74d4 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -250,10 +250,10 @@ ExprEngine::handleLValueBitCast(ProgramStateRef state, const Expr *Ex,
void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
const Expr *Ex = CastE->getSubExpr();
+ ProgramStateRef State = Pred->getState();
+ const StackFrame *SF = Pred->getStackFrame();
if (CastE->getCastKind() == CK_LValueToRValue) {
- ProgramStateRef State = Pred->getState();
- const StackFrame *SF = Pred->getStackFrame();
evalLoad(Dst, CastE, CastE, Pred, State, State->getSVal(Ex, SF));
return;
}
@@ -262,8 +262,6 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
ExplodedNodeSet DstEvalLoc;
// Simulate the lvalue-to-rvalue conversion on `Ex`:
- 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
@@ -294,9 +292,6 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE))
T = ExCast->getTypeAsWritten();
- ProgramStateRef state = Pred->getState();
- const StackFrame *SF = Pred->getStackFrame();
-
switch (CastE->getCastKind()) {
case CK_LValueToRValue:
case CK_LValueToRValueBitCast:
@@ -324,15 +319,13 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
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);
+ 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);
+ SVal V = State->getSVal(Ex, SF);
auto PTMSV = V.getAs<nonloc::PointerToMember>();
if (PTMSV)
V = svalBuilder.makeTruthVal(!PTMSV->isNullMemberPointer(), ExTy);
@@ -341,7 +334,7 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
return;
}
// Explicitly proceed with default handler for this case cascade.
- state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred);
+ State = handleLValueBitCast(State, Ex, SF, T, ExTy, CastE, Dst, Pred);
return;
}
case CK_Dependent:
@@ -351,13 +344,13 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
case CK_BooleanToSignedIntegral:
case CK_IntegralToPointer:
case CK_PointerToIntegral: {
- SVal V = state->getSVal(Ex, SF);
+ SVal V = State->getSVal(Ex, SF);
if (isa<nonloc::PointerToMember>(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);
+ State = handleLValueBitCast(State, Ex, SF, T, ExTy, CastE, Dst, Pred);
return;
}
case CK_IntegralToBoolean:
@@ -388,30 +381,30 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
case CK_FixedPointToBoolean:
case CK_FixedPointToIntegral:
case CK_IntegralToFixedPoint: {
- state = handleLValueBitCast(state, Ex, SF, T, ExTy, CastE, Dst, Pred);
+ 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);
+ SVal V = State->getSVal(Ex, SF);
if (AMgr.options.analyzerSymbolicIntegerCasts())
V = svalBuilder.evalCast(V, T, ExTy);
else
- V = svalBuilder.evalIntegralCast(state, V, T, ExTy);
+ 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);
+ 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);
+ SVal val = State->getSVal(Ex, SF);
// Compute the type of the result.
QualType resultType = CastE->getType();
@@ -432,11 +425,11 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
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);
+ Engine.makePostStmtNode(CastE, State, Pred, /*MarkAsSink=*/true);
return;
} else {
// If the cast fails on a pointer, bind to 0.
- state = state->BindExpr(CastE, SF,
+ State = State->BindExpr(CastE, SF,
svalBuilder.makeNullWithType(resultType));
}
} else {
@@ -445,16 +438,16 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
DefinedOrUnknownSVal NewSym = svalBuilder.conjureSymbolVal(
/*symbolTag=*/nullptr, getCFGElementRef(), SF, resultType,
getNumVisitedCurrent());
- state = state->BindExpr(CastE, SF, NewSym);
+ State = State->BindExpr(CastE, SF, NewSym);
} else
// Else, bind to the derived region value.
- state = state->BindExpr(CastE, SF, val);
+ State = State->BindExpr(CastE, SF, val);
}
- Dst.insert(Engine.makePostStmtNode(CastE, state, Pred));
+ Dst.insert(Engine.makePostStmtNode(CastE, State, Pred));
return;
}
case CK_BaseToDerived: {
- SVal val = state->getSVal(Ex, SF);
+ SVal val = State->getSVal(Ex, SF);
QualType resultType = CastE->getType();
if (CastE->isGLValue())
resultType = getContext().getPointerType(resultType);
@@ -486,7 +479,7 @@ void ExprEngine::VisitCastExpr(const CastExpr *CastE, ExplodedNode *Pred,
case CK_DerivedToBaseMemberPointer:
case CK_BaseToDerivedMemberPointer:
case CK_ReinterpretMemberPointer: {
- SVal V = state->getSVal(Ex, SF);
+ SVal V = State->getSVal(Ex, SF);
if (auto PTMSV = V.getAs<nonloc::PointerToMember>()) {
SVal CastedPTMSV =
svalBuilder.makePointerToMember(getBasicVals().accumCXXBase(
>From 7edb218b9d4b84ea09982ea2bd10b00aea7eee3d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
<gabor.tothvari at ericsson.com>
Date: Tue, 1 Sep 2026 14:49:12 +0200
Subject: [PATCH 38/39] Fix PostVisiKind typo
---
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index f895b80db1590..cbbee66b6598f 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1135,7 +1135,7 @@ static bool shouldJustCallCheckers(const Stmt *S, VisitKind K) {
// FIXME: Does not call PreVisit checkers
// Currently the engine does not call PostVisit checkers when
- // lambda inlining is disabled, so K == PostVisiKind
+ // lambda inlining is disabled, so K == PostVisitKind
// cannot be returned here.
case Stmt::LambdaExprClass:
return false;
>From 5aab0028cc627e0f412ed1c486d87743020eb899 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?=
<gabor.tothvari at ericsson.com>
Date: Tue, 1 Sep 2026 16:12:29 +0200
Subject: [PATCH 39/39] Fix formatting.
---
.../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 4 ++--
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 13abe41378dac..7be3c14bc7dfe 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -630,8 +630,8 @@ class ExprEngine {
ExplodedNodeSet &Dst);
void VisitObjCIndirectCopyRestoreExpr(const ObjCIndirectCopyRestoreExpr *OIE,
- ExplodedNode *Pred,
- ExplodedNodeSet &Dst);
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst);
/// Handle ++ and -- (both pre- and post-increment).
void VisitIncrementDecrementOperator(const UnaryOperator* U,
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index cbbee66b6598f..48d4e1b39fc9f 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2374,7 +2374,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Expr::ObjCIndirectCopyRestoreExprClass:
VisitObjCIndirectCopyRestoreExpr(cast<ObjCIndirectCopyRestoreExpr>(S),
- Pred, Dst);
+ Pred, Dst);
break;
}
}
More information about the cfe-commits
mailing list