[clang] [NFC][analyzer] Improve tracking of the current Block & LocCtx (PR #185107)
DonĂ¡t Nagy via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 9 12:02:18 PDT 2026
https://github.com/NagyDonat updated https://github.com/llvm/llvm-project/pull/185107
>From ad055230fe514202b9cec9067132518a4f5075a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Fri, 6 Mar 2026 15:19:43 +0100
Subject: [PATCH 01/17] [NFC][analyzer] Add ExprEngine::getCurrentBlock()
This utility function will be useful in the future
---
.../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 2ca03174fbdc9..9b87b14f31d87 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -226,9 +226,12 @@ class ExprEngine {
return G.getRoot()->getLocation().getLocationContext();
}
+ const CFGBlock *getCurrentBlock() const {
+ return currBldrCtx ? currBldrCtx->getBlock() : nullptr;
+ }
+
ConstCFGElementRef getCFGElementRef() const {
- const CFGBlock *blockPtr = currBldrCtx ? currBldrCtx->getBlock() : nullptr;
- return {blockPtr, currStmtIdx};
+ return {getCurrentBlock(), currStmtIdx};
}
/// Dump graph to the specified filename.
>From 59b5dffed1dbc3823ea0c5e3b391d3b9b8005013 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Fri, 6 Mar 2026 15:29:47 +0100
Subject: [PATCH 02/17] [NFC][analyzer] Apply ExprEngine::getCurrentBlock()
---
.../StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 2 +-
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 14 +++++++-------
.../Core/ExprEngineCallAndReturn.cpp | 2 +-
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 9b87b14f31d87..84c16c81e748d 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -710,7 +710,7 @@ class ExprEngine {
/// Return the CFG element corresponding to the worklist element
/// that is currently being processed by ExprEngine.
CFGElement getCurrentCFGElement() {
- return (*currBldrCtx->getBlock())[currStmtIdx];
+ return (*getCurrentBlock())[currStmtIdx];
}
/// Create a new state in which the call return value is binded to the
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index bc8e9040444c9..53e311cfc7141 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1144,7 +1144,7 @@ void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) {
}
// Enqueue the new nodes onto the work list.
- Engine.enqueueStmtNodes(Dst, currBldrCtx->getBlock(), currStmtIdx);
+ Engine.enqueueStmtNodes(Dst, getCurrentBlock(), currStmtIdx);
}
void ExprEngine::ProcessLoopExit(const Stmt* S, ExplodedNode *Pred) {
@@ -1159,7 +1159,7 @@ void ExprEngine::ProcessLoopExit(const Stmt* S, ExplodedNode *Pred) {
LoopExit PP(S, Pred->getLocationContext());
ExplodedNode *N = Engine.makeNode(PP, NewState, Pred);
if (N && !N->isSink())
- Engine.enqueueStmtNode(N, currBldrCtx->getBlock(), currStmtIdx);
+ Engine.enqueueStmtNode(N, getCurrentBlock(), currStmtIdx);
}
void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit,
@@ -1248,7 +1248,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit,
for (ExplodedNode *Pred : Tmp)
Dst.Add(Engine.makeNode(PP, Pred->getState(), Pred));
// Enqueue the new nodes onto the work list.
- Engine.enqueueStmtNodes(Dst, currBldrCtx->getBlock(), currStmtIdx);
+ Engine.enqueueStmtNodes(Dst, getCurrentBlock(), currStmtIdx);
}
std::pair<ProgramStateRef, uint64_t>
@@ -1312,7 +1312,7 @@ void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D,
}
// Enqueue the new nodes onto the work list.
- Engine.enqueueStmtNodes(Dst, currBldrCtx->getBlock(), currStmtIdx);
+ Engine.enqueueStmtNodes(Dst, getCurrentBlock(), currStmtIdx);
}
void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE,
@@ -1331,7 +1331,7 @@ void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE,
getCFGElementRef());
Dst.Add(Engine.makeNode(PP, Pred->getState(), Pred));
}
- Engine.enqueueStmtNodes(Dst, currBldrCtx->getBlock(), currStmtIdx);
+ Engine.enqueueStmtNodes(Dst, getCurrentBlock(), currStmtIdx);
}
void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
@@ -1844,7 +1844,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPMetaDirectiveClass:
case Stmt::HLSLOutArgExprClass: {
const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
- Engine.addAbortedBlock(node, currBldrCtx->getBlock());
+ Engine.addAbortedBlock(node, getCurrentBlock());
break;
}
@@ -2120,7 +2120,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
} else {
const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
- Engine.addAbortedBlock(node, currBldrCtx->getBlock());
+ Engine.addAbortedBlock(node, getCurrentBlock());
}
break;
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index a407a923d7d0f..0e101af2cdf25 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -551,7 +551,7 @@ void ExprEngine::inlineCall(WorkList *WList, const CallEvent &Call,
// Construct a new stack frame for the callee.
AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D);
const StackFrameContext *CalleeSFC =
- CalleeADC->getStackFrame(ParentOfCallee, CallE, currBldrCtx->getBlock(),
+ CalleeADC->getStackFrame(ParentOfCallee, CallE, getCurrentBlock(),
currBldrCtx->blockCount(), currStmtIdx);
CallEnter Loc(CallE, CalleeSFC, CurLC);
>From cf3ab6e9b9b1e30fc52c2eb594194f68b1f77bfc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Fri, 6 Mar 2026 15:21:35 +0100
Subject: [PATCH 03/17] [NFC][analyzer] Add ExprEngine::getNumVisited()
---
.../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 84c16c81e748d..020c339b1b4c8 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -234,6 +234,12 @@ class ExprEngine {
return {getCurrentBlock(), currStmtIdx};
}
+ unsigned getNumVisited(const LocationContext *LC,
+ const CFGBlock *Block) const {
+ return Engine.WList->getBlockCounter().getNumVisited(LC->getStackFrame(),
+ Block->getBlockID());
+ }
+
/// Dump graph to the specified filename.
/// If filename is empty, generate a temporary one.
/// \return The filename the graph is written into.
>From 54b0c007ec72fb33defa5b181923d9cd0cc633cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Fri, 6 Mar 2026 15:35:20 +0100
Subject: [PATCH 04/17] [NFC][analyzer] Add
ExprEngine::getCurrentLocationContext()
---
.../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 020c339b1b4c8..926f11fd74973 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -226,6 +226,10 @@ class ExprEngine {
return G.getRoot()->getLocation().getLocationContext();
}
+ const LocationContext *getCurrentLocationContext() const {
+ return currBldrCtx ? currBldrCtx->getLocationContext() : nullptr;
+ }
+
const CFGBlock *getCurrentBlock() const {
return currBldrCtx ? currBldrCtx->getBlock() : nullptr;
}
>From 6b6bfe1bf7ad1949bc2d52699f5d65ee51be5409 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Fri, 6 Mar 2026 15:41:51 +0100
Subject: [PATCH 05/17] [NFC][analyzer] Add ExprEngine::getNumVisitedCurrent()
This is exactly equivalent to `currBldrCtx->blockCount()` but prepares
the ground for the eventual elimination of `currBldrCtx`.
The block count (i.e. how many times was the block visited within the
stack frame) has nothing to do with building exploded nodes so I'm
trying to distance them from the types `NodeBuilder` and
`NodeBuilderContext`.
---
.../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 926f11fd74973..d37f33da71ab9 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -244,6 +244,10 @@ class ExprEngine {
Block->getBlockID());
}
+ unsigned getNumVisitedCurrent() const {
+ return getNumVisited(getCurrentLocationContext(), getCurrentBlock());
+ }
+
/// Dump graph to the specified filename.
/// If filename is empty, generate a temporary one.
/// \return The filename the graph is written into.
>From b33ddc0a67bf981d589c2236f0c1db01eb8d6c72 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Fri, 6 Mar 2026 15:45:18 +0100
Subject: [PATCH 06/17] [NFC][analyzer] Apply
ExprEngine::getNumVisitedCurrent()
This replaces many references to `currBldrCtx`.
Most of the remaining references construct short-lived `NodeBuilder`
objects which are superfluous -- I intend to replace their use with
direct calls to `CoreEngine::makeNode`.
---
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 16 ++++++-------
clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 23 +++++++++----------
.../lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 6 ++---
.../Core/ExprEngineCallAndReturn.cpp | 6 ++---
.../StaticAnalyzer/Core/ExprEngineObjC.cpp | 11 +++++----
5 files changed, 31 insertions(+), 31 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 53e311cfc7141..1c5b968734e89 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -422,7 +422,7 @@ ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
case SubobjectAdjustment::MemberPointerAdjustment:
// FIXME: Unimplemented.
State = State->invalidateRegions(Reg, getCFGElementRef(),
- currBldrCtx->blockCount(), LC, true,
+ getNumVisitedCurrent(), LC, true,
nullptr, nullptr, nullptr);
return State;
}
@@ -439,7 +439,7 @@ ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
SVal InitVal = State->getSVal(Init, LC);
if (InitVal.isUnknown()) {
InitVal = getSValBuilder().conjureSymbolVal(
- getCFGElementRef(), LC, Init->getType(), currBldrCtx->blockCount());
+ getCFGElementRef(), LC, Init->getType(), getNumVisitedCurrent());
State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false);
// Then we'd need to take the value that certainly exists and bind it
@@ -449,7 +449,7 @@ ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
// compute the value.
InitValWithAdjustments = getSValBuilder().conjureSymbolVal(
getCFGElementRef(), LC, InitWithAdjustments->getType(),
- currBldrCtx->blockCount());
+ getNumVisitedCurrent());
}
State =
State->bindLoc(Reg.castAs<Loc>(), InitValWithAdjustments, LC, false);
@@ -1217,7 +1217,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit,
SValBuilder &SVB = getSValBuilder();
InitVal =
SVB.conjureSymbolVal(getCFGElementRef(), stackFrame,
- Field->getType(), currBldrCtx->blockCount());
+ Field->getType(), getNumVisitedCurrent());
}
} else {
InitVal = State->getSVal(BMI->getInit(), stackFrame);
@@ -2052,7 +2052,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
const LocationContext *LCtx = N->getLocationContext();
SVal result = svalBuilder.conjureSymbolVal(
/*symbolTag=*/nullptr, getCFGElementRef(), LCtx, resultType,
- currBldrCtx->blockCount());
+ getNumVisitedCurrent());
ProgramStateRef State = N->getState()->BindExpr(Ex, LCtx, result);
// Escape pointers passed into the list, unless it's an ObjC boxed
@@ -3601,7 +3601,7 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred,
}
State = State->invalidateRegions(ValuesToInvalidate, getCFGElementRef(),
- currBldrCtx->blockCount(), LCtx,
+ getNumVisitedCurrent(), LCtx,
/*CausedByPointerEscape*/ true,
/*Symbols=*/nullptr);
@@ -3950,7 +3950,7 @@ void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred,
if (std::optional<Loc> LV = X.getAs<Loc>())
state = state->invalidateRegions(*LV, getCFGElementRef(),
- currBldrCtx->blockCount(),
+ getNumVisitedCurrent(),
Pred->getLocationContext(),
/*CausedByPointerEscape=*/true);
}
@@ -3961,7 +3961,7 @@ void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred,
if (std::optional<Loc> LV = X.getAs<Loc>())
state = state->invalidateRegions(*LV, getCFGElementRef(),
- currBldrCtx->blockCount(),
+ getNumVisitedCurrent(),
Pred->getLocationContext(),
/*CausedByPointerEscape=*/true);
}
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index b7fac30500d26..67beed5dbb6fb 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -65,7 +65,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
// EXPERIMENTAL: "Conjured" symbols.
// FIXME: Handle structs.
if (RightV.isUnknown()) {
- unsigned Count = currBldrCtx->blockCount();
+ unsigned Count = getNumVisitedCurrent();
RightV = svalBuilder.conjureSymbolVal(nullptr, getCFGElementRef(), LCtx,
Count);
}
@@ -83,7 +83,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
if (B->isAdditiveOp()) {
// TODO: This can be removed after we enable history tracking with
// SymSymExpr.
- unsigned Count = currBldrCtx->blockCount();
+ unsigned Count = getNumVisitedCurrent();
RightV = conjureOffsetSymbolOnLocation(
RightV, LeftV, getCFGElementRef(), RHS->getType(), svalBuilder,
Count, LCtx);
@@ -170,7 +170,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
// LValue on the LHS will bind to.
LHSVal = svalBuilder.conjureSymbolVal(/*symbolTag=*/nullptr,
getCFGElementRef(), LCtx, LTy,
- currBldrCtx->blockCount());
+ getNumVisitedCurrent());
// However, we need to convert the symbol to the computation type.
Result = svalBuilder.evalCast(LHSVal, CTy, LTy);
} else {
@@ -201,9 +201,8 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
const BlockDecl *BD = BE->getBlockDecl();
// Get the value of the block itself.
- SVal V = svalBuilder.getBlockPointer(BD, T,
- Pred->getLocationContext(),
- currBldrCtx->blockCount());
+ SVal V = svalBuilder.getBlockPointer(BD, T, Pred->getLocationContext(),
+ getNumVisitedCurrent());
ProgramStateRef State = Pred->getState();
@@ -498,7 +497,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
if (val.isUnknown()) {
DefinedOrUnknownSVal NewSym = svalBuilder.conjureSymbolVal(
/*symbolTag=*/nullptr, getCFGElementRef(), LCtx, resultType,
- currBldrCtx->blockCount());
+ getNumVisitedCurrent());
state = state->BindExpr(CastE, LCtx, NewSym);
} else
// Else, bind to the derived region value.
@@ -522,7 +521,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
if (val.isUnknown()) {
val = svalBuilder.conjureSymbolVal(
/*symbolTag=*/nullptr, getCFGElementRef(), LCtx, resultType,
- currBldrCtx->blockCount());
+ getNumVisitedCurrent());
}
state = state->BindExpr(CastE, LCtx, val);
Bldr.generateNode(CastE, Pred, state);
@@ -568,7 +567,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
resultType = getContext().getPointerType(resultType);
SVal result = svalBuilder.conjureSymbolVal(
/*symbolTag=*/nullptr, getCFGElementRef(), LCtx, resultType,
- currBldrCtx->blockCount());
+ getNumVisitedCurrent());
state = state->BindExpr(CastE, LCtx, result);
Bldr.generateNode(CastE, Pred, state);
continue;
@@ -661,7 +660,7 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
InitVal = svalBuilder.conjureSymbolVal(
/*symbolTag=*/nullptr, getCFGElementRef(), LC, Ty,
- currBldrCtx->blockCount());
+ getNumVisitedCurrent());
}
@@ -831,7 +830,7 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex,
if (!hasValue)
V = svalBuilder.conjureSymbolVal(nullptr, getCFGElementRef(), LCtx,
- currBldrCtx->blockCount());
+ getNumVisitedCurrent());
// Generate a new node with the binding from the appropriate path.
B.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V, true));
@@ -1115,7 +1114,7 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,
if (Result.isUnknown()){
DefinedOrUnknownSVal SymVal = svalBuilder.conjureSymbolVal(
/*symbolTag=*/nullptr, getCFGElementRef(), LCtx,
- currBldrCtx->blockCount());
+ getNumVisitedCurrent());
Result = SymVal;
// If the value is a location, ++/-- should always preserve
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 07852984e4ee3..0866dda766667 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -254,7 +254,7 @@ SVal ExprEngine::computeObjectUnderConstruction(
QualType ReturnTy = RetE->getType();
QualType RegionTy = ACtx.getPointerType(ReturnTy);
return SVB.conjureSymbolVal(&TopLevelSymRegionTag, getCFGElementRef(),
- SFC, RegionTy, currBldrCtx->blockCount());
+ SFC, RegionTy, getNumVisitedCurrent());
}
llvm_unreachable("Unhandled return value construction context!");
}
@@ -975,7 +975,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
// really part of the CXXNewExpr because they happen BEFORE the
// CXXConstructExpr subexpression. See PR12014 for some discussion.
- unsigned blockCount = currBldrCtx->blockCount();
+ unsigned blockCount = getNumVisitedCurrent();
const LocationContext *LCtx = Pred->getLocationContext();
SVal symVal = UnknownVal();
FunctionDecl *FD = CNE->getOperatorNew();
@@ -1138,7 +1138,7 @@ void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred,
const LocationContext *LCtx = Pred->getLocationContext();
SVal V = svalBuilder.conjureSymbolVal(getCFGElementRef(), LCtx, VD->getType(),
- currBldrCtx->blockCount());
+ getNumVisitedCurrent());
ProgramStateRef state = Pred->getState();
state = state->bindLoc(state->getLValue(VD, LCtx), V, LCtx);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 0e101af2cdf25..312eca02e661a 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -552,7 +552,7 @@ void ExprEngine::inlineCall(WorkList *WList, const CallEvent &Call,
AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D);
const StackFrameContext *CalleeSFC =
CalleeADC->getStackFrame(ParentOfCallee, CallE, getCurrentBlock(),
- currBldrCtx->blockCount(), currStmtIdx);
+ getNumVisitedCurrent(), currStmtIdx);
CallEnter Loc(CallE, CalleeSFC, CurLC);
@@ -768,7 +768,7 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
SVal R;
QualType ResultTy = Call.getResultType();
- unsigned Count = currBldrCtx->blockCount();
+ unsigned Count = getNumVisitedCurrent();
if (auto RTC = getCurrentCFGElement().getAs<CFGCXXRecordTypedCall>()) {
// Conjure a temporary if the function returns an object by value.
SVal Target;
@@ -833,7 +833,7 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
// a conjured return value.
void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr,
ExplodedNode *Pred, ProgramStateRef State) {
- State = Call.invalidateRegions(currBldrCtx->blockCount(), State);
+ State = Call.invalidateRegions(getNumVisitedCurrent(), State);
State = bindReturnValue(Call, Pred->getLocationContext(), State);
// And make the result node.
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index bb0fd6ded2def..07703bdf38239 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -48,7 +48,7 @@ static void populateObjCForDestinationSet(ExplodedNodeSet &dstLocation,
const ObjCForCollectionStmt *S,
ConstCFGElementRef elem,
SVal elementV, SymbolManager &SymMgr,
- const NodeBuilderContext *currBldrCtx,
+ unsigned NumVisitedCurrent,
NodeBuilder &Bldr, bool hasElements) {
for (ExplodedNode *Pred : dstLocation) {
@@ -69,7 +69,7 @@ static void populateObjCForDestinationSet(ExplodedNodeSet &dstLocation,
SVal V;
if (hasElements) {
SymbolRef Sym =
- SymMgr.conjureSymbol(elem, LCtx, T, currBldrCtx->blockCount());
+ SymMgr.conjureSymbol(elem, LCtx, T, NumVisitedCurrent);
V = svalBuilder.makeLoc(Sym);
} else {
V = svalBuilder.makeIntVal(0, T);
@@ -136,12 +136,13 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
if (!isContainerNull)
populateObjCForDestinationSet(DstLocationSingleton, svalBuilder, S,
- elemRef, elementV, SymMgr, currBldrCtx,
- Bldr,
+ elemRef, elementV, SymMgr,
+ getNumVisitedCurrent(), Bldr,
/*hasElements=*/true);
populateObjCForDestinationSet(DstLocationSingleton, svalBuilder, S, elemRef,
- elementV, SymMgr, currBldrCtx, Bldr,
+ elementV, SymMgr, getNumVisitedCurrent(),
+ Bldr,
/*hasElements=*/false);
// Finally, run any custom checkers.
>From b46e232bddb4d1e7818d7601a62127d6c82325d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Fri, 6 Mar 2026 17:21:52 +0100
Subject: [PATCH 07/17] [NFC][analyzer] Make ExprEngine::getBuilderContext()
const
Not related to the rest of the changes, just a drive-by minor
improvement.
---
.../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index d37f33da71ab9..4ab7863b4cb26 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -215,8 +215,9 @@ class ExprEngine {
getCrossTranslationUnitContext() {
return &CTU;
}
+
- const NodeBuilderContext &getBuilderContext() {
+ const NodeBuilderContext &getBuilderContext() const {
assert(currBldrCtx);
return *currBldrCtx;
}
>From 2f0e40b5bd7740c58666fdd7dcf6d0c4d9632f49 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Fri, 6 Mar 2026 18:12:05 +0100
Subject: [PATCH 08/17] [NFC][analyzer] Introduce
setCurrLocationContextAndBlock
---
.../Core/PathSensitive/ExprEngine.h | 34 ++++++++++++++++++-
1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 4ab7863b4cb26..155f41c53d679 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -159,7 +159,28 @@ class ExprEngine {
SValBuilder &svalBuilder;
unsigned int currStmtIdx = 0;
+
+ /// Pointer to a so-called NodeBuilderContext object which has three
+ /// independent roles:
+ /// - It holds a pointer to the CFGBlock that is currently under analysis.
+ /// (This is the primary way to get the current block.)
+ /// - It holds a pointer to the current LocationContext. (This is rarely used
+ /// and may be 'stale', the location context is usually queried from an
+ /// ExplodedNode or a ProgramPoint.)
+ /// - It can be used for constructing `NodeBuilder`s. Practically all
+ /// `NodeBuilder` objects are useless complications in the code, so I
+ /// intend to replace them with direct use of `ExprEngine::makeNode`.
+ /// TODO: Eventually `currBldrCtx` should be replaced by two separate fields:
+ /// `const CFGBlock *CurrBlock` & `const LocationContext *CurrLocationContext`
+ /// that are kept up-to-date and are almost always non-null during the
+ /// analysis. I will switch to this more natural representation when
+ /// `NodeBuilder`s are eliminated from the code.
const NodeBuilderContext *currBldrCtx = nullptr;
+ /// Historically `currBldrCtx` pointed to a local variable in some stack
+ /// frame. This field is introduced as a temporary measure to allow a gradual
+ /// transition. Do not reference this outside of setLocationContextAndBlock!
+ /// TODO: Remove this temporary hack.
+ std::optional<NodeBuilderContext> OwnedCurrBldrCtx;
/// Helper object to determine if an Objective-C message expression
/// implicitly never returns.
@@ -215,7 +236,18 @@ class ExprEngine {
getCrossTranslationUnitContext() {
return &CTU;
}
-
+
+ // FIXME: Ideally the body of this method should look like
+ // CurrLocationContext = LC;
+ // CurrBlock = B;
+ // where CurrLocationContext and CurrBlock are new member variables that
+ // fulfill the roles of `currBldrCtx` in a more natural way.
+ // This implementation is a temporary measure to allow a gradual transition.
+ void setCurrLocationContextAndBlock(const LocationContext *LC,
+ const CFGBlock *B) {
+ OwnedCurrBldrCtx.emplace(Engine, B, LC);
+ currBldrCtx = &*OwnedCurrBldrCtx;
+ }
const NodeBuilderContext &getBuilderContext() const {
assert(currBldrCtx);
>From 0dfc00a9573a743a6ce72d2890a263146466f14e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Fri, 6 Mar 2026 18:26:02 +0100
Subject: [PATCH 09/17] [NFC][analyzer] Prefer currBldrCtx instead of local
aliases
In functions that save one of their parameters in `currBldrCtx`, prefer
using `currBldrCtx` instead of the local alias.
This prepares the ground for setting `currBldrCtx` _outside of the
function_ in a more central location and removing the NodeBuilderContext
parameter from the signature of these functions.
---
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 1c5b968734e89..184a733bb8bdb 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2844,7 +2844,7 @@ void ExprEngine::processBranch(
// Check for NULL conditions; e.g. "for(;;)"
if (!Condition) {
- BranchNodeBuilder NullCondBldr(Dst, BldCtx, DstT, DstF);
+ BranchNodeBuilder NullCondBldr(Dst, *currBldrCtx, DstT, DstF);
NullCondBldr.generateNode(Pred->getState(), true, Pred);
return;
}
@@ -2852,7 +2852,7 @@ void ExprEngine::processBranch(
if (const auto *Ex = dyn_cast<Expr>(Condition))
Condition = Ex->IgnoreParens();
- Condition = ResolveCondition(Condition, BldCtx.getBlock());
+ Condition = ResolveCondition(Condition, currBldrCtx->getBlock());
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
Condition->getBeginLoc(),
"Error evaluating branch");
@@ -2864,7 +2864,7 @@ void ExprEngine::processBranch(
if (CheckersOutSet.empty())
return;
- BranchNodeBuilder Builder(Dst, BldCtx, DstT, DstF);
+ BranchNodeBuilder Builder(Dst, *currBldrCtx, DstT, DstF);
for (ExplodedNode *PredN : CheckersOutSet) {
ProgramStateRef PrevState = PredN->getState();
@@ -2970,7 +2970,7 @@ void ExprEngine::processStaticInitializer(
const auto *VD = cast<VarDecl>(DS->getSingleDecl());
ProgramStateRef state = Pred->getState();
bool initHasRun = state->contains<InitializedGlobalsSet>(VD);
- BranchNodeBuilder Builder(Dst, BuilderCtx, DstT, DstF);
+ BranchNodeBuilder Builder(Dst, *currBldrCtx, DstT, DstF);
if (!initHasRun) {
state = state->add<InitializedGlobalsSet>(VD);
@@ -3100,7 +3100,7 @@ void ExprEngine::processSwitch(NodeBuilderContext &BC, const SwitchStmt *Switch,
currBldrCtx = &BC;
const Expr *Condition = Switch->getCond();
- SwitchNodeBuilder Builder(Dst, BC);
+ SwitchNodeBuilder Builder(Dst, *currBldrCtx);
ExplodedNodeSet CheckersOutSet;
getCheckerManager().runCheckersForBranchCondition(
>From 157edc3504752ca4530174529dd274902f6b713e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Fri, 6 Mar 2026 18:52:49 +0100
Subject: [PATCH 10/17] [NFC][analyzer] Eliminate local NodeBuilderContexts
part 1
Previously `ExprEngine::currBldrCtx` pointed to locally initialized
`NodeBuilderContext` instances. This commit replaces some of these with
calls to `ExprEngine::setCurrLocationContextAndBlock()``.
This simplifies the signature of some complex methods and prepares the
ground for the eventual removal of `NodeBuilderContext`.
---
.../Core/PathSensitive/ExprEngine.h | 17 +++++++---------
clang/lib/StaticAnalyzer/Core/CoreEngine.cpp | 14 ++++++-------
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 20 +++++++++----------
3 files changed, 23 insertions(+), 28 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 155f41c53d679..5d0f332288109 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -378,9 +378,9 @@ class ExprEngine {
/// processing the 'effects' of a branch condition. If the branch condition
/// is a loop condition, IterationsCompletedInLoop is the number of completed
/// iterations (otherwise it's std::nullopt).
- void processBranch(const Stmt *Condition, NodeBuilderContext &BuilderCtx,
- ExplodedNode *Pred, ExplodedNodeSet &Dst,
- const CFGBlock *DstT, const CFGBlock *DstF,
+ void processBranch(const Stmt *Condition, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst, const CFGBlock *DstT,
+ const CFGBlock *DstF,
std::optional<unsigned> IterationsCompletedInLoop);
/// Called by CoreEngine.
@@ -394,11 +394,8 @@ class ExprEngine {
/// Called by CoreEngine. Used to processing branching behavior
/// at static initializers.
- void processStaticInitializer(const DeclStmt *DS,
- NodeBuilderContext& BuilderCtx,
- ExplodedNode *Pred,
- ExplodedNodeSet &Dst,
- const CFGBlock *DstT,
+ void processStaticInitializer(const DeclStmt *DS, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst, const CFGBlock *DstT,
const CFGBlock *DstF);
/// processIndirectGoto - Called by CoreEngine. Used to generate successor
@@ -408,8 +405,8 @@ class ExprEngine {
/// ProcessSwitch - Called by CoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a switch statement.
- void processSwitch(NodeBuilderContext &BC, const SwitchStmt *Switch,
- ExplodedNode *Pred, ExplodedNodeSet &Dst);
+ void processSwitch(const SwitchStmt *Switch, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst);
/// Called by CoreEngine. Used to notify checkers that processing a
/// function has begun. Called for both inlined and top-level functions.
diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
index d32db1669e1fe..f3c8beb8e1fa8 100644
--- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -452,9 +452,9 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
return;
case Stmt::SwitchStmtClass: {
- NodeBuilderContext Ctx(*this, B, Pred);
+ ExprEng.setCurrLocationContextAndBlock(Pred->getLocationContext(), B);
ExplodedNodeSet Dst;
- ExprEng.processSwitch(Ctx, cast<SwitchStmt>(Term), Pred, Dst);
+ ExprEng.processSwitch(cast<SwitchStmt>(Term), Pred, Dst);
// Enqueue the new frontier onto the worklist.
enqueue(Dst);
return;
@@ -492,9 +492,9 @@ void CoreEngine::HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred) {
void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
const CFGBlock * B, ExplodedNode *Pred) {
assert(B->succ_size() == 2);
- NodeBuilderContext Ctx(*this, B, Pred);
+ ExprEng.setCurrLocationContextAndBlock(Pred->getLocationContext(), B);
ExplodedNodeSet Dst;
- ExprEng.processBranch(Cond, Ctx, Pred, Dst, *(B->succ_begin()),
+ ExprEng.processBranch(Cond, Pred, Dst, *(B->succ_begin()),
*(B->succ_begin() + 1),
getCompletedIterationCount(B, Pred));
// Enqueue the new frontier onto the worklist.
@@ -516,10 +516,10 @@ void CoreEngine::HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
ExplodedNode *Pred) {
assert(B->succ_size() == 2);
- NodeBuilderContext Ctx(*this, B, Pred);
+ ExprEng.setCurrLocationContextAndBlock(Pred->getLocationContext(), B);
ExplodedNodeSet Dst;
- ExprEng.processStaticInitializer(DS, Ctx, Pred, Dst,
- *(B->succ_begin()), *(B->succ_begin()+1));
+ ExprEng.processStaticInitializer(DS, Pred, Dst, *(B->succ_begin()),
+ *(B->succ_begin() + 1));
// Enqueue the new frontier onto the worklist.
enqueue(Dst);
}
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 184a733bb8bdb..3c96cfabb3f70 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2835,12 +2835,11 @@ assumeCondition(const Stmt *Condition, ExplodedNode *N) {
}
void ExprEngine::processBranch(
- const Stmt *Condition, NodeBuilderContext &BldCtx, ExplodedNode *Pred,
- ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF,
+ const Stmt *Condition, ExplodedNode *Pred, ExplodedNodeSet &Dst,
+ const CFGBlock *DstT, const CFGBlock *DstF,
std::optional<unsigned> IterationsCompletedInLoop) {
assert((!Condition || !isa<CXXBindTemporaryExpr>(Condition)) &&
"CXXBindTemporaryExprs are handled by processBindTemporary.");
- currBldrCtx = &BldCtx;
// Check for NULL conditions; e.g. "for(;;)"
if (!Condition) {
@@ -2962,11 +2961,11 @@ void ExprEngine::processBranch(
REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet,
llvm::ImmutableSet<const VarDecl *>)
-void ExprEngine::processStaticInitializer(
- const DeclStmt *DS, NodeBuilderContext &BuilderCtx, ExplodedNode *Pred,
- ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF) {
- currBldrCtx = &BuilderCtx;
-
+void ExprEngine::processStaticInitializer(const DeclStmt *DS,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst,
+ const CFGBlock *DstT,
+ const CFGBlock *DstF) {
const auto *VD = cast<VarDecl>(DS->getSingleDecl());
ProgramStateRef state = Pred->getState();
bool initHasRun = state->contains<InitializedGlobalsSet>(VD);
@@ -3095,9 +3094,8 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
/// ProcessSwitch - Called by CoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a switch statement.
-void ExprEngine::processSwitch(NodeBuilderContext &BC, const SwitchStmt *Switch,
- ExplodedNode *Pred, ExplodedNodeSet &Dst) {
- currBldrCtx = &BC;
+void ExprEngine::processSwitch(const SwitchStmt *Switch, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
const Expr *Condition = Switch->getCond();
SwitchNodeBuilder Builder(Dst, *currBldrCtx);
>From 116ca3848d41253f001575644737b559b7d8a8fa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Fri, 6 Mar 2026 20:42:03 +0100
Subject: [PATCH 11/17] [NFC][analyzer] Unify setCurrLocationContextAndBlock in
HandleBlockExit
The "bulk" of `CoreEngine::HandleBlockExit` is the switch within
`if const Stmt *Term = B->(getTerminatorStmt())`. In this big block
every `case` used equivalent `NodeBuilderContext` instances, so this
commit unifies their initialization to a single call to
`setCurrLocationContextAndBlock()`.
In the more complex branches (e.g. `HandleBranch`) the
`NodeBuilderContext` was initialized via via calls to
`setCurrLocationContextAndBlock` (which also makes it available as the
`currBldrCtx`); while the simple branches previously just had their
`NodeBuilderContext` in a local variable.
Note that at the very end of `HandleBlockExit` there is a path where it
calls `HandleVirtualBaseBranch`, which calls `HandelBlockEdge` and sets
up its `NodeBuilderContext` in a different way (there "the" block is the
_destination_ block, not the exited block). (That logic is not changed.)
Also, fix a const correctness issue in the constructor of
`IndirectGotoNodeBuilder` (which was introduced by one of my earlier
commits).
---
.../Core/PathSensitive/CoreEngine.h | 5 +++--
.../Core/PathSensitive/ExprEngine.h | 1 -
clang/lib/StaticAnalyzer/Core/CoreEngine.cpp | 15 ++++++---------
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 3 +--
4 files changed, 10 insertions(+), 14 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
index 21749b0f42b2b..6b70fda42819c 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
@@ -339,8 +339,9 @@ class IndirectGotoNodeBuilder : public NodeBuilder {
const Expr *Target;
public:
- IndirectGotoNodeBuilder(ExplodedNodeSet &DstSet, NodeBuilderContext &Ctx,
- const Expr *Tgt, const CFGBlock *Dispatch)
+ IndirectGotoNodeBuilder(ExplodedNodeSet &DstSet,
+ const NodeBuilderContext &Ctx, const Expr *Tgt,
+ const CFGBlock *Dispatch)
: NodeBuilder(DstSet, Ctx), DispatchBlock(*Dispatch), Target(Tgt) {}
using iterator = CFGBlock::const_succ_iterator;
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 5d0f332288109..45459e79546b0 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -387,7 +387,6 @@ class ExprEngine {
/// Used to generate successor nodes for temporary destructors depending
/// on whether the corresponding constructor was visited.
void processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
- NodeBuilderContext &BldCtx,
ExplodedNode *Pred, ExplodedNodeSet &Dst,
const CFGBlock *DstT,
const CFGBlock *DstF);
diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
index f3c8beb8e1fa8..cf39008387bae 100644
--- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -357,6 +357,8 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,
void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
if (const Stmt *Term = B->getTerminatorStmt()) {
+ ExprEng.setCurrLocationContextAndBlock(Pred->getLocationContext(), B);
+
switch (Term->getStmtClass()) {
default:
llvm_unreachable("Analysis for this terminator not implemented.");
@@ -425,11 +427,10 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
case Stmt::IndirectGotoStmtClass: {
// Only 1 successor: the indirect goto dispatch block.
assert(B->succ_size() == 1);
- NodeBuilderContext Ctx(*this, B, Pred);
ExplodedNodeSet Dst;
IndirectGotoNodeBuilder Builder(
- Dst, Ctx, cast<IndirectGotoStmt>(Term)->getTarget(),
- *(B->succ_begin()));
+ Dst, ExprEng.getBuilderContext(),
+ cast<IndirectGotoStmt>(Term)->getTarget(), *(B->succ_begin()));
ExprEng.processIndirectGoto(Builder, Pred);
// Enqueue the new frontier onto the worklist.
@@ -452,7 +453,6 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
return;
case Stmt::SwitchStmtClass: {
- ExprEng.setCurrLocationContextAndBlock(Pred->getLocationContext(), B);
ExplodedNodeSet Dst;
ExprEng.processSwitch(cast<SwitchStmt>(Term), Pred, Dst);
// Enqueue the new frontier onto the worklist.
@@ -492,7 +492,6 @@ void CoreEngine::HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred) {
void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
const CFGBlock * B, ExplodedNode *Pred) {
assert(B->succ_size() == 2);
- ExprEng.setCurrLocationContextAndBlock(Pred->getLocationContext(), B);
ExplodedNodeSet Dst;
ExprEng.processBranch(Cond, Pred, Dst, *(B->succ_begin()),
*(B->succ_begin() + 1),
@@ -505,10 +504,9 @@ void CoreEngine::HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
const CFGBlock *B,
ExplodedNode *Pred) {
assert(B->succ_size() == 2);
- NodeBuilderContext Ctx(*this, B, Pred);
ExplodedNodeSet Dst;
- ExprEng.processCleanupTemporaryBranch(BTE, Ctx, Pred, Dst, *(B->succ_begin()),
- *(B->succ_begin() + 1));
+ ExprEng.processCleanupTemporaryBranch(BTE, Pred, Dst, *(B->succ_begin()),
+ *(B->succ_begin() + 1));
// Enqueue the new frontier onto the worklist.
enqueue(Dst);
}
@@ -516,7 +514,6 @@ void CoreEngine::HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
ExplodedNode *Pred) {
assert(B->succ_size() == 2);
- ExprEng.setCurrLocationContextAndBlock(Pred->getLocationContext(), B);
ExplodedNodeSet Dst;
ExprEng.processStaticInitializer(DS, Pred, Dst, *(B->succ_begin()),
*(B->succ_begin() + 1));
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 3c96cfabb3f70..121f8913f9389 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1638,12 +1638,11 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
}
void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
- NodeBuilderContext &BldCtx,
ExplodedNode *Pred,
ExplodedNodeSet &Dst,
const CFGBlock *DstT,
const CFGBlock *DstF) {
- BranchNodeBuilder TempDtorBuilder(Dst, BldCtx, DstT, DstF);
+ BranchNodeBuilder TempDtorBuilder(Dst, *currBldrCtx, DstT, DstF);
ProgramStateRef State = Pred->getState();
const LocationContext *LC = Pred->getLocationContext();
if (getObjectUnderConstruction(State, BTE, LC)) {
>From 7474806d57eda2dfd5f67b20fa2317c414ea7b7c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Mon, 9 Mar 2026 17:11:48 +0100
Subject: [PATCH 12/17] [NFC][analyzer] Add resetCurrLocationContextAndBlock()
This is a paranoid safeguard to ensure that the current
`LocationContext` and `Block` of one work item cannot influence the
behavior of the next one.
---
.../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 7 ++++++-
clang/lib/StaticAnalyzer/Core/CoreEngine.cpp | 8 ++++++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 45459e79546b0..80334d1d16625 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -178,7 +178,7 @@ class ExprEngine {
const NodeBuilderContext *currBldrCtx = nullptr;
/// Historically `currBldrCtx` pointed to a local variable in some stack
/// frame. This field is introduced as a temporary measure to allow a gradual
- /// transition. Do not reference this outside of setLocationContextAndBlock!
+ /// transition. Only use this in {re,}setLocationContextAndBlock!
/// TODO: Remove this temporary hack.
std::optional<NodeBuilderContext> OwnedCurrBldrCtx;
@@ -249,6 +249,11 @@ class ExprEngine {
currBldrCtx = &*OwnedCurrBldrCtx;
}
+ void resetCurrLocationContextAndBlock() {
+ currBldrCtx = nullptr;
+ OwnedCurrBldrCtx = std::nullopt;
+ }
+
const NodeBuilderContext &getBuilderContext() const {
assert(currBldrCtx);
return *currBldrCtx;
diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
index cf39008387bae..46d719b779669 100644
--- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -216,6 +216,14 @@ void CoreEngine::dispatchWorkItem(ExplodedNode *Pred, ProgramPoint Loc,
return timeTraceMetadata(Pred, Loc);
}};
PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext());
+
+ // This work item is not necessarily related to the previous one, so
+ // the old current LocationContext and Block is no longer relevant.
+ // The new current LocationContext and Block should be set soon, but this
+ // guarantees that buggy access before that will trigger loud crashes instead
+ // of silently using stale data.
+ ExprEng.resetCurrLocationContextAndBlock();
+
// Dispatch on the location type.
switch (Loc.getKind()) {
case ProgramPoint::BlockEdgeKind:
>From bbf60c500382b2d77837018032f3a5b411ef828c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Mon, 9 Mar 2026 19:00:57 +0100
Subject: [PATCH 13/17] [NFC][analyzer] Use setCurrLocationContextAndBlock in
HandleBlockEdge
The method `CoreEngine::HandleBlockEdge` initialized a single
`NodeBuilderContext` at its beginning, which was used by all the methods
"under" it. After cluttering the parameter lists for some time, it
eventually ended up as the `currBldrCtx` through at least three
pathways.
This commit eliminates these complications and immediately installs this
`NodeBuilderContext` (i.e. location context + block combination) as the
current one. It will stay the current one until the end of the handling
of this work item; then the next call to `dispatchWorkItem` will
guarantee that there are no stale references to it.
---
.../clang/StaticAnalyzer/Core/CheckerManager.h | 7 ++-----
.../Core/PathSensitive/ExprEngine.h | 11 +++--------
clang/lib/StaticAnalyzer/Core/CheckerManager.cpp | 5 ++---
clang/lib/StaticAnalyzer/Core/CoreEngine.cpp | 14 ++++++--------
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 16 ++++++----------
.../Core/ExprEngineCallAndReturn.cpp | 4 +---
6 files changed, 20 insertions(+), 37 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h
index bf33ce616d954..348c7eff09161 100644
--- a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h
+++ b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h
@@ -360,11 +360,8 @@ class CheckerManager {
ExprEngine &Eng);
/// Run checkers on end of function.
- void runCheckersForEndFunction(NodeBuilderContext &BC,
- ExplodedNodeSet &Dst,
- ExplodedNode *Pred,
- ExprEngine &Eng,
- const ReturnStmt *RS);
+ void runCheckersForEndFunction(ExplodedNodeSet &Dst, ExplodedNode *Pred,
+ ExprEngine &Eng, const ReturnStmt *RS);
/// Run checkers for branch condition.
void runCheckersForBranchCondition(const Stmt *condition,
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 80334d1d16625..fd016cf479d44 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -375,8 +375,7 @@ class ExprEngine {
void processCFGBlockEntrance(const BlockEdge &L, const BlockEntrance &BE,
NodeBuilder &Builder, ExplodedNode *Pred);
- void runCheckersForBlockEntrance(const NodeBuilderContext &BldCtx,
- const BlockEntrance &Entrance,
+ void runCheckersForBlockEntrance(const BlockEntrance &Entrance,
ExplodedNode *Pred, ExplodedNodeSet &Dst);
/// ProcessBranch - Called by CoreEngine. Used to generate successor nodes by
@@ -420,14 +419,10 @@ class ExprEngine {
/// Called by CoreEngine. Used to notify checkers that processing a
/// function has ended. Called for both inlined and top-level functions.
- void processEndOfFunction(NodeBuilderContext& BC,
- ExplodedNode *Pred,
- const ReturnStmt *RS = nullptr);
+ void processEndOfFunction(ExplodedNode *Pred, const ReturnStmt *RS = nullptr);
/// Remove dead bindings/symbols before exiting a function.
- void removeDeadOnEndOfFunction(NodeBuilderContext& BC,
- ExplodedNode *Pred,
- ExplodedNodeSet &Dst);
+ void removeDeadOnEndOfFunction(ExplodedNode *Pred, ExplodedNodeSet &Dst);
/// Generate the entry node of the callee.
void processCallEnter(NodeBuilderContext& BC, CallEnter CE,
diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
index 8ee4832643b91..20a906dde38b1 100644
--- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -505,15 +505,14 @@ void CheckerManager::runCheckersForBeginFunction(ExplodedNodeSet &Dst,
/// Run checkers for end of path.
// Note, We do not chain the checker output (like in expandGraphWithCheckers)
// for this callback since end of path nodes are expected to be final.
-void CheckerManager::runCheckersForEndFunction(NodeBuilderContext &BC,
- ExplodedNodeSet &Dst,
+void CheckerManager::runCheckersForEndFunction(ExplodedNodeSet &Dst,
ExplodedNode *Pred,
ExprEngine &Eng,
const ReturnStmt *RS) {
// We define the builder outside of the loop because if at least one checker
// creates a successor for Pred, we do not need to generate an
// autotransition for it.
- NodeBuilder Bldr(Pred, Dst, BC);
+ NodeBuilder Bldr(Pred, Dst, Eng.getBuilderContext());
for (const auto &checkFn : EndFunctionCheckers) {
const ProgramPoint &L =
FunctionExitPoint(RS, Pred->getLocationContext(), checkFn.Checker);
diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
index 46d719b779669..c9e5d1162265e 100644
--- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -267,7 +267,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode *Pred, ProgramPoint Loc,
void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
const CFGBlock *Blk = L.getDst();
- NodeBuilderContext BuilderCtx(*this, Blk, Pred);
+ ExprEng.setCurrLocationContextAndBlock(Pred->getLocationContext(), Blk);
// Mark this block as visited.
const LocationContext *LC = Pred->getLocationContext();
@@ -288,9 +288,7 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
},
/*IsPrunable=*/true));
// Perform the transition.
- ExplodedNodeSet Dst;
- NodeBuilder Bldr(Pred, Dst, BuilderCtx);
- Pred = Bldr.generateNode(P, Pred->getState(), Pred);
+ Pred = makeNode(P, Pred->getState(), Pred);
if (!Pred)
return;
}
@@ -314,11 +312,11 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
ExplodedNodeSet CheckerNodes;
BlockEntrance BE(L.getSrc(), L.getDst(), Pred->getLocationContext());
- ExprEng.runCheckersForBlockEntrance(BuilderCtx, BE, Pred, CheckerNodes);
+ ExprEng.runCheckersForBlockEntrance(BE, Pred, CheckerNodes);
// Process the final state transition.
for (ExplodedNode *P : CheckerNodes) {
- ExprEng.processEndOfFunction(BuilderCtx, P, RS);
+ ExprEng.processEndOfFunction(P, RS);
}
// This path is done. Don't enqueue any more nodes.
@@ -328,7 +326,7 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
// Call into the ExprEngine to process entering the CFGBlock.
BlockEntrance BE(L.getSrc(), L.getDst(), Pred->getLocationContext());
ExplodedNodeSet DstNodes;
- NodeBuilder Builder(Pred, DstNodes, BuilderCtx);
+ NodeBuilder Builder(Pred, DstNodes, ExprEng.getBuilderContext());
ExprEng.processCFGBlockEntrance(L, BE, Builder, Pred);
// Auto-generate a node.
@@ -338,7 +336,7 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
ExplodedNodeSet CheckerNodes;
for (auto *N : DstNodes) {
- ExprEng.runCheckersForBlockEntrance(BuilderCtx, BE, N, CheckerNodes);
+ ExprEng.runCheckersForBlockEntrance(BE, N, CheckerNodes);
}
// Enqueue nodes onto the worklist.
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 121f8913f9389..d7c038f2cb6f2 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2639,17 +2639,14 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
}
}
-void ExprEngine::runCheckersForBlockEntrance(const NodeBuilderContext &BldCtx,
- const BlockEntrance &Entrance,
+void ExprEngine::runCheckersForBlockEntrance(const BlockEntrance &Entrance,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
llvm::PrettyStackTraceFormat CrashInfo(
"Processing block entrance B%d -> B%d",
Entrance.getPreviousBlock()->getBlockID(),
Entrance.getBlock()->getBlockID());
- currBldrCtx = &BldCtx;
getCheckerManager().runCheckersForBlockEntrance(Dst, Pred, Entrance, *this);
- currBldrCtx = nullptr;
}
//===----------------------------------------------------------------------===//
@@ -3022,8 +3019,7 @@ void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC,
/// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path
/// nodes when the control reaches the end of a function.
-void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
- ExplodedNode *Pred,
+void ExprEngine::processEndOfFunction(ExplodedNode *Pred,
const ReturnStmt *RS) {
ProgramStateRef State = Pred->getState();
@@ -3063,7 +3059,7 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
// Perform the transition with cleanups.
if (State != Pred->getState()) {
ExplodedNodeSet PostCleanup;
- NodeBuilder Bldr(Pred, PostCleanup, BC);
+ NodeBuilder Bldr(Pred, PostCleanup, *currBldrCtx);
Pred = Bldr.generateNode(Pred->getLocation(), State, Pred);
if (!Pred) {
// The node with clean temporaries already exists. We might have reached
@@ -3079,13 +3075,13 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
if (Pred->getLocationContext()->inTopFrame()) {
// Remove dead symbols.
ExplodedNodeSet AfterRemovedDead;
- removeDeadOnEndOfFunction(BC, Pred, AfterRemovedDead);
+ removeDeadOnEndOfFunction(Pred, AfterRemovedDead);
// Notify checkers.
for (const auto I : AfterRemovedDead)
- getCheckerManager().runCheckersForEndFunction(BC, Dst, I, *this, RS);
+ getCheckerManager().runCheckersForEndFunction(Dst, I, *this, RS);
} else {
- getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this, RS);
+ getCheckerManager().runCheckersForEndFunction(Dst, Pred, *this, RS);
}
Engine.enqueueEndOfFunction(Dst, RS);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 312eca02e661a..7e3f10c3f57ee 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -158,8 +158,7 @@ static SVal adjustReturnValue(SVal V, QualType ExpectedTy, QualType ActualTy,
return UnknownVal();
}
-void ExprEngine::removeDeadOnEndOfFunction(NodeBuilderContext& BC,
- ExplodedNode *Pred,
+void ExprEngine::removeDeadOnEndOfFunction(ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
// Find the last statement in the function and the corresponding basic block.
const Stmt *LastSt = nullptr;
@@ -175,7 +174,6 @@ void ExprEngine::removeDeadOnEndOfFunction(NodeBuilderContext& BC,
// point will be associated. However, we only want to use LastStmt as a
// reference for what to clean up if it's a ReturnStmt; otherwise, everything
// is dead.
- SaveAndRestore<const NodeBuilderContext *> NodeContextRAII(currBldrCtx, &BC);
const LocationContext *LCtx = Pred->getLocationContext();
removeDead(Pred, Dst, dyn_cast<ReturnStmt>(LastSt), LCtx,
LCtx->getAnalysisDeclContext()->getBody(),
>From f15aef6c50025eb3e89b13b8177a4eaf5eb2bf9b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Mon, 9 Mar 2026 19:02:48 +0100
Subject: [PATCH 14/17] [NFC][analyzer] Unrelated simplification
---
clang/lib/StaticAnalyzer/Core/CoreEngine.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
index c9e5d1162265e..7807df609e193 100644
--- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -294,9 +294,9 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
}
// Check if we are entering the EXIT block.
- if (Blk == &(L.getLocationContext()->getCFG()->getExit())) {
- assert(L.getLocationContext()->getCFG()->getExit().empty() &&
- "EXIT block cannot contain Stmts.");
+ const CFGBlock &ExitBlk = L.getLocationContext()->getCFG()->getExit();
+ if (Blk == &ExitBlk) {
+ assert(ExitBlk.empty() && "EXIT block cannot contain Stmts.");
// Get return statement..
const ReturnStmt *RS = nullptr;
>From 49b47e5d187a59a4104c41368938a751a6456990 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Mon, 9 Mar 2026 19:03:59 +0100
Subject: [PATCH 15/17] [NFC][analyzer] Simplify with CoreEngine::makeNode
---
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index d7c038f2cb6f2..eacb4432ad8c8 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3058,9 +3058,7 @@ void ExprEngine::processEndOfFunction(ExplodedNode *Pred,
// Perform the transition with cleanups.
if (State != Pred->getState()) {
- ExplodedNodeSet PostCleanup;
- NodeBuilder Bldr(Pred, PostCleanup, *currBldrCtx);
- Pred = Bldr.generateNode(Pred->getLocation(), State, Pred);
+ Pred = Engine.makeNode(Pred->getLocation(), State, Pred);
if (!Pred) {
// The node with clean temporaries already exists. We might have reached
// it on a path on which we initialize different temporaries.
>From e4fe6a50889002e1b48781676794645c8b331be2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Mon, 9 Mar 2026 19:21:49 +0100
Subject: [PATCH 16/17] [NFC][analyzer] setCurrLocationContextAndBlock before
processCFGElement
Instead of creating a `NodeBuilderContext` and passing it as an
argument which will be assigned to `currBldrCtx` inside the function,
record the current `LocationContext` and `Block` outside.
Note that this can be _almost_ unified by the call to
`setCurrLocationContextAndBlock` in `HandleBlockExit` (which is called
"instead of" `processCFGElement` in both functions that call it). The
only exception is that at at the end of `HandleBlockExit` there is the
`HandleVirtualBaseBranch` case that directly invokes `HandleBlockEdge`
(instead of just `enqueue`ing a `BlockEdge` node) and `HandleBlockEdge`
sets up the "current block" in a different way (there current =
destination).
---
.../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 2 +-
clang/lib/StaticAnalyzer/Core/CoreEngine.cpp | 9 +++++----
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 3 +--
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index fd016cf479d44..e967261b353bc 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -348,7 +348,7 @@ class ExprEngine {
/// processCFGElement - Called by CoreEngine. Used to generate new successor
/// nodes by processing the 'effects' of a CFG element.
void processCFGElement(const CFGElement E, ExplodedNode *Pred,
- unsigned StmtIdx, NodeBuilderContext *Ctx);
+ unsigned StmtIdx);
void ProcessStmt(const Stmt *S, ExplodedNode *Pred);
diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
index 7807df609e193..0c545d0cfaa43 100644
--- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -355,8 +355,9 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,
// Process the entrance of the block.
if (std::optional<CFGElement> E = L.getFirstElement()) {
- NodeBuilderContext Ctx(*this, L.getBlock(), Pred);
- ExprEng.processCFGElement(*E, Pred, 0, &Ctx);
+ ExprEng.setCurrLocationContextAndBlock(Pred->getLocationContext(),
+ L.getBlock());
+ ExprEng.processCFGElement(*E, Pred, 0);
} else
HandleBlockExit(L.getBlock(), Pred);
}
@@ -541,8 +542,8 @@ void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
if (StmtIdx == B->size())
HandleBlockExit(B, Pred);
else {
- NodeBuilderContext Ctx(*this, B, Pred);
- ExprEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx);
+ ExprEng.setCurrLocationContextAndBlock(Pred->getLocationContext(), B);
+ ExprEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx);
}
}
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index eacb4432ad8c8..4f4aa10daa254 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -966,9 +966,8 @@ void ExprEngine::processEndWorklist() {
}
void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred,
- unsigned StmtIdx, NodeBuilderContext *Ctx) {
+ unsigned StmtIdx) {
currStmtIdx = StmtIdx;
- currBldrCtx = Ctx;
switch (E.getKind()) {
case CFGElement::Statement:
>From e0ea983e81ba7dd022ec94424a8ddcf590e3e9b9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <donat.nagy at ericsson.com>
Date: Mon, 9 Mar 2026 19:59:55 +0100
Subject: [PATCH 17/17] [NFC][analyzer] Use setCurrLocationContextAndBlock in
HandleCallEnter
... and in ExecuteWorkList.
Note that these parts of the engine have unrelated code quality issues.
---
.../StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 6 ++----
clang/lib/StaticAnalyzer/Core/CoreEngine.cpp | 10 ++++++----
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 4 +---
.../StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 10 +++++++---
4 files changed, 16 insertions(+), 14 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index e967261b353bc..d197378876e5e 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -413,8 +413,7 @@ class ExprEngine {
/// Called by CoreEngine. Used to notify checkers that processing a
/// function has begun. Called for both inlined and top-level functions.
- void processBeginOfFunction(NodeBuilderContext &BC,
- ExplodedNode *Pred, ExplodedNodeSet &Dst,
+ void processBeginOfFunction(ExplodedNode *Pred, ExplodedNodeSet &Dst,
const BlockEdge &L);
/// Called by CoreEngine. Used to notify checkers that processing a
@@ -425,8 +424,7 @@ class ExprEngine {
void removeDeadOnEndOfFunction(ExplodedNode *Pred, ExplodedNodeSet &Dst);
/// Generate the entry node of the callee.
- void processCallEnter(NodeBuilderContext& BC, CallEnter CE,
- ExplodedNode *Pred);
+ void processCallEnter(CallEnter CE, ExplodedNode *Pred);
/// Generate the sequence of nodes that simulate the call exit and the post
/// visit for CallExpr.
diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
index 0c545d0cfaa43..3dc9c53bd30d7 100644
--- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -119,9 +119,10 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned MaxSteps,
assert(IsNew);
G.designateAsRoot(Node);
- NodeBuilderContext BuilderCtx(*this, StartLoc.getDst(), Node);
+ ExprEng.setCurrLocationContextAndBlock(Node->getLocationContext(), Succ);
+
ExplodedNodeSet DstBegin;
- ExprEng.processBeginOfFunction(BuilderCtx, Node, DstBegin, StartLoc);
+ ExprEng.processBeginOfFunction(Node, DstBegin, StartLoc);
enqueue(DstBegin);
}
@@ -492,8 +493,9 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
}
void CoreEngine::HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred) {
- NodeBuilderContext BuilderCtx(*this, CE.getEntry(), Pred);
- ExprEng.processCallEnter(BuilderCtx, CE, Pred);
+ ExprEng.setCurrLocationContextAndBlock(Pred->getLocationContext(),
+ CE.getEntry());
+ ExprEng.processCallEnter(CE, Pred);
}
void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 4f4aa10daa254..caa89dd73db19 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3008,11 +3008,9 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &Builder,
Builder.generateNode(Succ, State, Pred);
}
-void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC,
- ExplodedNode *Pred,
+void ExprEngine::processBeginOfFunction(ExplodedNode *Pred,
ExplodedNodeSet &Dst,
const BlockEdge &L) {
- SaveAndRestore<const NodeBuilderContext *> NodeContextRAII(currBldrCtx, &BC);
getCheckerManager().runCheckersForBeginFunction(Dst, L, Pred, *this);
}
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 7e3f10c3f57ee..49b02d0fb018c 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -39,8 +39,7 @@ STAT_COUNTER(NumInlinedCalls, "The # of times we inlined a call");
STAT_COUNTER(NumReachedInlineCountMax,
"The # of times we reached inline count maximum");
-void ExprEngine::processCallEnter(NodeBuilderContext& BC, CallEnter CE,
- ExplodedNode *Pred) {
+void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
// Get the entry block in the CFG of the callee.
const CFGBlock *Entry = CE.getEntry();
@@ -62,8 +61,13 @@ void ExprEngine::processCallEnter(NodeBuilderContext& BC, CallEnter CE,
ExplodedNode *Node = G.getNode(Loc, state, false, &isNew);
Node->addPredecessor(Pred, G);
if (isNew) {
+ // FIXME: In the `processBeginOfFunction` callback
+ // `ExprEngine::getCurrLocationContext()` can be different from the
+ // `LocationContext` queried from e.g. the `ExplodedNode`s. I'm not
+ // touching this now because this commit is NFC; but in the future it would
+ // be nice to avoid this inconsistency.
ExplodedNodeSet DstBegin;
- processBeginOfFunction(BC, Node, DstBegin, Loc);
+ processBeginOfFunction(Node, DstBegin, Loc);
Engine.enqueue(DstBegin);
}
}
More information about the cfe-commits
mailing list