https://github.com/NagyDonat updated https://github.com/llvm/llvm-project/pull/203923
From 77fc48d9972d8ce9ab820ccef1078a29506b4e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]> Date: Thu, 11 Jun 2026 14:31:09 +0200 Subject: [PATCH 01/13] Remove NodeBuilder from ExprEngine::inlineCall This method took a NodeBuilder as an argument, unconditionally called `Bldr.takeNodes(Pred)` on it and did nothing else with it. This commit removes the `NodeBuilder` argument and temporarily places the `Bldr.takeNodes(Pred)` calls just before the call sites of `inlineCall`. Follow-up commits will consolidate (in fact, eliminate) these repeated calls. --- .../Core/PathSensitive/ExprEngine.h | 2 +- .../Core/ExprEngineCallAndReturn.cpp | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index bb2de45cec92a..5667375d37bb3 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -908,7 +908,7 @@ class ExprEngine { const StackFrame *SF); void inlineCall(WorkList *WList, const CallEvent &Call, const Decl *D, - NodeBuilder &Bldr, ExplodedNode *Pred, ProgramStateRef State); + ExplodedNode *Pred, ProgramStateRef State); void ctuBifurcate(const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, ExplodedNode *Pred, ProgramStateRef State); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 4cbcaa2721639..50e82c8d4b220 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -502,13 +502,15 @@ void ExprEngine::ctuBifurcate(const CallEvent &Call, const Decl *D, (IK == CTUPhase1InliningKind::Small && isSmall(AMgr.getAnalysisDeclContext(D))); if (DoInline) { - inlineCall(Engine.getWorkList(), Call, D, Bldr, Pred, State); + Bldr.takeNodes(Pred); + inlineCall(Engine.getWorkList(), Call, D, Pred, State); return; } const bool BState = State->get<CTUDispatchBifurcation>(); if (!BState) { // This is the first time we see this foreign function. // Enqueue it to be analyzed in the second (ctu) phase. - inlineCall(Engine.getCTUWorkList(), Call, D, Bldr, Pred, State); + Bldr.takeNodes(Pred); + inlineCall(Engine.getCTUWorkList(), Call, D, Pred, State); // Conservatively evaluate in the first phase. ConservativeEvalState = State->set<CTUDispatchBifurcation>(true); conservativeEvalCall(Call, Bldr, Pred, ConservativeEvalState); @@ -517,12 +519,13 @@ void ExprEngine::ctuBifurcate(const CallEvent &Call, const Decl *D, } return; } - inlineCall(Engine.getWorkList(), Call, D, Bldr, Pred, State); + Bldr.takeNodes(Pred); + inlineCall(Engine.getWorkList(), Call, D, Pred, State); } void ExprEngine::inlineCall(WorkList *WList, const CallEvent &Call, - const Decl *D, NodeBuilder &Bldr, - ExplodedNode *Pred, ProgramStateRef State) { + const Decl *D, ExplodedNode *Pred, + ProgramStateRef State) { assert(D); const StackFrame *CallerSF = Pred->getStackFrame(); @@ -556,10 +559,6 @@ void ExprEngine::inlineCall(WorkList *WList, const CallEvent &Call, WList->enqueue(N); } - // If we decided to inline the call, the successor has been manually - // added onto the work list so remove it from the node builder. - Bldr.takeNodes(Pred); - NumInlinedCalls++; Engine.FunctionSummaries->bumpNumTimesInlined(D); From 930c1bfb2e3ec0097409b398fe27f5b04f615fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]> Date: Thu, 11 Jun 2026 15:56:17 +0200 Subject: [PATCH 02/13] Remove NodeBuilder from conservativeEvalCall Previously `ExprEngine::conservativeEvalCall` used `generateNode` to make a single node and place it in the `Frontier` (destination set) of the `NodeBuilder`. The method `NodeBuilder::generateNode` removes the predecessor (`Pred`) from the `Frontier` of the `NodeBuilder` instance, makes a new node and then adds it to the `Frontier`, which is equivalent to a chain of `takeNodes(Pred)` then `makeNode` then `addNodes`. This commit decomposes the `generateNode` call into these steps and moves the `NodeBuilder` manipulation out of `conservativeEvalCall`, which will now just make a node (after creating the right `State`) and return it. Follow-up commits will consolidate and eliminate the `takeNodes` calls which now appear separately on practically every each execution path. (And the `addNodes` call will be replaced with `ExplodedNodeSet::insert` when the node builder is eliminated.) --- .../Core/PathSensitive/ExprEngine.h | 4 ++-- .../Core/ExprEngineCallAndReturn.cpp | 24 ++++++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 5667375d37bb3..ae29f35a950b3 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -918,8 +918,8 @@ class ExprEngine { /// Conservatively evaluate call by invalidating regions and binding /// a conjured return value. - void conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, - ExplodedNode *Pred, ProgramStateRef State); + ExplodedNode *conservativeEvalCall(const CallEvent &Call, ExplodedNode *Pred, + ProgramStateRef State); /// Either inline or process the call conservatively (or both), based /// on DynamicDispatchBifurcation data. diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 50e82c8d4b220..2d5caa3b189f6 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -513,9 +513,10 @@ void ExprEngine::ctuBifurcate(const CallEvent &Call, const Decl *D, inlineCall(Engine.getCTUWorkList(), Call, D, Pred, State); // Conservatively evaluate in the first phase. ConservativeEvalState = State->set<CTUDispatchBifurcation>(true); - conservativeEvalCall(Call, Bldr, Pred, ConservativeEvalState); + Bldr.addNodes(conservativeEvalCall(Call, Pred, ConservativeEvalState)); } else { - conservativeEvalCall(Call, Bldr, Pred, State); + Bldr.takeNodes(Pred); + Bldr.addNodes(conservativeEvalCall(Call, Pred, State)); } return; } @@ -836,14 +837,15 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, // Conservatively evaluate call by invalidating regions and binding // a conjured return value. -void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, - ExplodedNode *Pred, ProgramStateRef State) { +ExplodedNode *ExprEngine::conservativeEvalCall(const CallEvent &Call, + ExplodedNode *Pred, + ProgramStateRef State) { State = Call.invalidateRegions(getNumVisitedCurrent(), State); State = bindReturnValue(Call, Pred->getStackFrame(), State); // And make the result node. static SimpleProgramPointTag PT("ExprEngine", "Conservative eval call"); - Bldr.generateNode(Call.getProgramPoint(false, &PT), State, Pred); + return Engine.makeNode(Call.getProgramPoint(false, &PT), State, Pred); } ExprEngine::CallInlinePolicy @@ -1252,7 +1254,8 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, // Don't inline if we're not in any dynamic dispatch mode. if (Options.getIPAMode() != IPAK_DynamicDispatch) { - conservativeEvalCall(Call, Bldr, Pred, State); + Bldr.takeNodes(Pred); + Bldr.addNodes(conservativeEvalCall(Call, Pred, State)); return; } } @@ -1267,7 +1270,8 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, State, dyn_cast_or_null<CXXConstructExpr>(E), Call.getStackFrame()); // Also handle the return value and invalidate the regions. - conservativeEvalCall(Call, Bldr, Pred, State); + Bldr.takeNodes(Pred); + Bldr.addNodes(conservativeEvalCall(Call, Pred, State)); } void ExprEngine::BifurcateCall(const MemRegion *BifurReg, @@ -1288,7 +1292,8 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg, // If inline failed, or we are on the path where we assume we // don't have enough info about the receiver to inline, conjure the // return value and invalidate the regions. - conservativeEvalCall(Call, Bldr, Pred, State); + Bldr.takeNodes(Pred); + Bldr.addNodes(conservativeEvalCall(Call, Pred, State)); return; } @@ -1302,7 +1307,8 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg, ProgramStateRef NoIState = State->set<DynamicDispatchBifurcationMap>(BifurReg, DynamicDispatchModeConservative); - conservativeEvalCall(Call, Bldr, Pred, NoIState); + Bldr.takeNodes(Pred); + Bldr.addNodes(conservativeEvalCall(Call, Pred, NoIState)); NumOfDynamicDispatchPathSplits++; } From 6263ca71fbfbe616ab770843ca489a75e25502b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]> Date: Fri, 12 Jun 2026 16:15:06 +0200 Subject: [PATCH 03/13] Rename variable Dst to DstEval in performTrivialCopy ...because I want to replace the `NodeBuilder` (which currently collects the new nodes) with a customary out-parameter `ExplodedNodeSet &Dst` in a follow-up change, and this local occupies that name. --- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 291533d0c3289..3f259a3b5b769 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -67,7 +67,7 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, const StackFrame *SF = Pred->getStackFrame(); const Expr *CallExpr = Call.getOriginExpr(); - ExplodedNodeSet Dst; + ExplodedNodeSet DstEval; Bldr.takeNodes(Pred); assert(ThisRD); @@ -87,17 +87,17 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, evalLocation(Tmp, CallExpr, VExpr, Pred, Pred->getState(), V, /*isLoad=*/true); for (ExplodedNode *N : Tmp) - evalBind(Dst, CallExpr, N, ThisVal, V, !AlwaysReturnsLValue); + evalBind(DstEval, CallExpr, N, ThisVal, V, !AlwaysReturnsLValue); } else { // We can't copy empty classes because of empty base class optimization. // In that case, copying the empty base class subobject would overwrite the // object that it overlaps with - so let's not do that. // See issue-157467.cpp for an example. - Dst.insert(Pred); + DstEval.insert(Pred); } PostStmt PS(CallExpr, SF); - for (ExplodedNode *N : Dst) { + for (ExplodedNode *N : DstEval) { ProgramStateRef State = N->getState(); if (AlwaysReturnsLValue) State = State->BindExpr(CallExpr, SF, ThisVal); From 9ca2b560f6bba60bd59ff6360c8f29cab63e5541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]> Date: Fri, 12 Jun 2026 16:21:02 +0200 Subject: [PATCH 04/13] Extract Bldr.takeNodes(Pred) from performTrivialCopy `performTrivialCopy` unconditionally calls `Bldr.takeNodes(Pred)` so this commit moves it out of the function. In `handleConstructor` this "cancels out" the side effect of the constructor of the node builder `Bldr`, which inserted all elements of `DstPreCall` into `DstEvaluated`. (Removing `DstPreCall` from the argument list of the constructor of `Bldr` means skipping this initial insertion -- which is equivalent to inserting them and taking out each node in the subsequent for loop.) In `defaultEvalCall` this commit just places `Bldr.takeNodes(Pred)` before the call to `performTrivialCopy`. As this also appears on many alternative execution paths, follow-up commits will be able to consolidate its uses. --- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 3 +-- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 3f259a3b5b769..e1d5869d95e21 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -68,7 +68,6 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, const Expr *CallExpr = Call.getOriginExpr(); ExplodedNodeSet DstEval; - Bldr.takeNodes(Pred); assert(ThisRD); @@ -729,7 +728,7 @@ void ExprEngine::handleConstructor(const Expr *E, if (CE && CE->getConstructor()->isTrivial() && CE->getConstructor()->isCopyOrMoveConstructor() && !CallOpts.IsArrayCtorOrDtor) { - NodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx); + NodeBuilder Bldr(DstEvaluated, *currBldrCtx); // FIXME: Handle other kinds of trivial constructors as well. for (ExplodedNode *N : DstPreCall) performTrivialCopy(Bldr, N, *Call); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 2d5caa3b189f6..a111db8653f07 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -1228,6 +1228,7 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, // Special-case trivial assignment operators. if (isTrivialObjectAssignment(Call)) { + Bldr.takeNodes(Pred); performTrivialCopy(Bldr, Pred, Call); return; } From 1b5c08d558b2f14676f8ce1bf10c86d27f3540f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]> Date: Fri, 12 Jun 2026 17:04:19 +0200 Subject: [PATCH 05/13] Extract Bldr.takeNodes(Pred) from ctuBifurcate It was called on all paths within `ctuBifurcate`, place it before each call to `ctuBifurcate` instead. These will be moved further by the follow-up commits. --- .../StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index a111db8653f07..159e642f91c83 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -502,25 +502,21 @@ void ExprEngine::ctuBifurcate(const CallEvent &Call, const Decl *D, (IK == CTUPhase1InliningKind::Small && isSmall(AMgr.getAnalysisDeclContext(D))); if (DoInline) { - Bldr.takeNodes(Pred); inlineCall(Engine.getWorkList(), Call, D, Pred, State); return; } const bool BState = State->get<CTUDispatchBifurcation>(); if (!BState) { // This is the first time we see this foreign function. // Enqueue it to be analyzed in the second (ctu) phase. - Bldr.takeNodes(Pred); inlineCall(Engine.getCTUWorkList(), Call, D, Pred, State); // Conservatively evaluate in the first phase. ConservativeEvalState = State->set<CTUDispatchBifurcation>(true); Bldr.addNodes(conservativeEvalCall(Call, Pred, ConservativeEvalState)); } else { - Bldr.takeNodes(Pred); Bldr.addNodes(conservativeEvalCall(Call, Pred, State)); } return; } - Bldr.takeNodes(Pred); inlineCall(Engine.getWorkList(), Call, D, Pred, State); } @@ -1260,6 +1256,7 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, return; } } + Bldr.takeNodes(Pred); ctuBifurcate(Call, D, Bldr, Pred, State); return; } @@ -1288,8 +1285,10 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg, State->get<DynamicDispatchBifurcationMap>(BifurReg); if (BState) { // If we are on "inline path", keep inlining if possible. - if (*BState == DynamicDispatchModeInlined) + if (*BState == DynamicDispatchModeInlined) { + Bldr.takeNodes(Pred); ctuBifurcate(Call, D, Bldr, Pred, State); + } // If inline failed, or we are on the path where we assume we // don't have enough info about the receiver to inline, conjure the // return value and invalidate the regions. @@ -1303,6 +1302,7 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg, ProgramStateRef IState = State->set<DynamicDispatchBifurcationMap>(BifurReg, DynamicDispatchModeInlined); + Bldr.takeNodes(Pred); ctuBifurcate(Call, D, Bldr, Pred, IState); ProgramStateRef NoIState = From fbbfeb16f6878a591fc5650d21eeb81605bbb0a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]> Date: Fri, 12 Jun 2026 17:07:48 +0200 Subject: [PATCH 06/13] Extract Bldr.takeNodes(Pred) from BifurcateCall It was called on all paths within `BifurcateCall`, place it before each call to `BifurcateCall` instead. These will be moved further by the follow-up commits. --- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 159e642f91c83..640b7ce52c6ef 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -1245,6 +1245,7 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, // Explore with and without inlining the call. if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) { + Bldr.takeNodes(Pred); BifurcateCall(RD.getDispatchRegion(), Call, D, Bldr, Pred); return; } @@ -1285,14 +1286,11 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg, State->get<DynamicDispatchBifurcationMap>(BifurReg); if (BState) { // If we are on "inline path", keep inlining if possible. - if (*BState == DynamicDispatchModeInlined) { - Bldr.takeNodes(Pred); + if (*BState == DynamicDispatchModeInlined) ctuBifurcate(Call, D, Bldr, Pred, State); - } // If inline failed, or we are on the path where we assume we // don't have enough info about the receiver to inline, conjure the // return value and invalidate the regions. - Bldr.takeNodes(Pred); Bldr.addNodes(conservativeEvalCall(Call, Pred, State)); return; } @@ -1302,13 +1300,11 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg, ProgramStateRef IState = State->set<DynamicDispatchBifurcationMap>(BifurReg, DynamicDispatchModeInlined); - Bldr.takeNodes(Pred); ctuBifurcate(Call, D, Bldr, Pred, IState); ProgramStateRef NoIState = State->set<DynamicDispatchBifurcationMap>(BifurReg, DynamicDispatchModeConservative); - Bldr.takeNodes(Pred); Bldr.addNodes(conservativeEvalCall(Call, Pred, NoIState)); NumOfDynamicDispatchPathSplits++; From 1a9f23af517c4ac5b53b24be4a08a89691c41bc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]> Date: Fri, 12 Jun 2026 17:12:35 +0200 Subject: [PATCH 07/13] Extract Bldr.takeNodes(Pred) from defaultEvalCall `defaultEvalCall` called `Bldr.takeNodes(Pred)` on every execution path, so this commit moves it out of the function. In most call sites the node builder was constructed with the 3-argument constructor that inserts all nodes from the first argument into the second argument (which is the `Frontier`, the out-parameter of the node builer) -- which was followed by a for loop that took out each of these nodes from the `Frontier` with the `takeNodes` calls in `defaultEvalCall`. This commit "cancels out" these back-and-forth set manipulations. In `VisitObjCMessage` the analogous loop is more complex, and will be refactored in a separate commit; this change just temporarily places `Bldr.takeNodes(Pred)` in front of the `defaultEvalCall` invocation. --- clang/lib/StaticAnalyzer/Core/CheckerManager.cpp | 2 +- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 6 +++--- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 5 ----- clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp | 1 + 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp index 80c03899d1e39..1a57a40c02441 100644 --- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -809,7 +809,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, // If none of the checkers evaluated the call, ask ExprEngine to handle it. if (!evaluatorChecker) { - NodeBuilder B(Pred, Dst, Eng.getBuilderContext()); + NodeBuilder B(Dst, Eng.getBuilderContext()); Eng.defaultEvalCall(B, Pred, *UpdatedCall, CallOpts); } } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index e1d5869d95e21..a202d92b8b74c 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -861,7 +861,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, *Call, *this); ExplodedNodeSet DstInvalidated; - NodeBuilder Bldr(DstPreCall, DstInvalidated, *currBldrCtx); + NodeBuilder Bldr(DstInvalidated, *currBldrCtx); for (ExplodedNode *N : DstPreCall) defaultEvalCall(Bldr, N, *Call, CallOpts); @@ -886,7 +886,7 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, *Call, *this); ExplodedNodeSet DstPostCall; - NodeBuilder CallBldr(DstPreCall, DstPostCall, *currBldrCtx); + NodeBuilder CallBldr(DstPostCall, *currBldrCtx); for (ExplodedNode *I : DstPreCall) { // Operator new calls (CXXNewExpr) are intentionally not eval-called, // because it does not make sense to eval-call user-provided functions. @@ -1094,7 +1094,7 @@ void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, ExplodedNodeSet DstPostCall; if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator) { - NodeBuilder Bldr(DstPreCall, DstPostCall, *currBldrCtx); + NodeBuilder Bldr(DstPostCall, *currBldrCtx); for (ExplodedNode *I : DstPreCall) { // Intentionally either inline or conservative eval-call the operator // delete, but avoid triggering an eval-call event for checkers. diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 640b7ce52c6ef..47f67f17b17ba 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -1224,7 +1224,6 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, // Special-case trivial assignment operators. if (isTrivialObjectAssignment(Call)) { - Bldr.takeNodes(Pred); performTrivialCopy(Bldr, Pred, Call); return; } @@ -1245,19 +1244,16 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, // Explore with and without inlining the call. if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) { - Bldr.takeNodes(Pred); BifurcateCall(RD.getDispatchRegion(), Call, D, Bldr, Pred); return; } // Don't inline if we're not in any dynamic dispatch mode. if (Options.getIPAMode() != IPAK_DynamicDispatch) { - Bldr.takeNodes(Pred); Bldr.addNodes(conservativeEvalCall(Call, Pred, State)); return; } } - Bldr.takeNodes(Pred); ctuBifurcate(Call, D, Bldr, Pred, State); return; } @@ -1269,7 +1265,6 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, State, dyn_cast_or_null<CXXConstructExpr>(E), Call.getStackFrame()); // Also handle the return value and invalidate the regions. - Bldr.takeNodes(Pred); Bldr.addNodes(conservativeEvalCall(Call, Pred, State)); } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index 15f0275691e92..15b970fc673d5 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -285,6 +285,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, } } + Bldr.takeNodes(Pred); defaultEvalCall(Bldr, Pred, *UpdatedMsg); } From 46ff8b9a3293edc0a28ed43c3a719f235df585b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]> Date: Wed, 10 Jun 2026 14:51:02 +0200 Subject: [PATCH 08/13] Simplify an old-style loop --- clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index 15b970fc673d5..e20e9e3ec5c92 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -258,9 +258,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, ExplodedNodeSet dstEval; NodeBuilder Bldr(dstGenericPrevisit, dstEval, *currBldrCtx); - for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(), - DE = dstGenericPrevisit.end(); DI != DE; ++DI) { - ExplodedNode *Pred = *DI; + for (ExplodedNode *Pred : dstGenericPrevisit) { ProgramStateRef State = Pred->getState(); CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State); From e96622dba9022e333a46cb6e89337200a4856441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]> Date: Mon, 15 Jun 2026 13:52:07 +0200 Subject: [PATCH 09/13] Simplify last builder in VisitObjCMessage The constructor of this node builder inserted the exploded node set `dstGenericPrevisit` into `dstEval` but then each of these nodes was removed from the set by either one of the two `generateSink` calls or by the call `Bldr.takeNodes(Pred)`. As this temporary presence of the nodes in the set doesn't affect anything, this commit: - removes this first argument of the `NodeBuilder` constructor; - removes `Bldr.takeNodes(Pred)`; - replaces the `generateSink` calls with `makePostStmtNode` calls where `MarkAsSink` is true. Note that a `generatNode` call has three effects: removing the old node from the `Frontier`, making the new node and inserting the new node into the `Frontier` -- but the third one is irrelevant for sinks because `ExplodedNodeSet`s ignore the insertion of sink nodes. Therefore "cancelling out" the removal of the old node leaves just the node-making call (`makePostStmtNode` in this case). --- clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index e20e9e3ec5c92..d5c38890800db 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -256,7 +256,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, // Proceed with evaluate the message expression. ExplodedNodeSet dstEval; - NodeBuilder Bldr(dstGenericPrevisit, dstEval, *currBldrCtx); + NodeBuilder Bldr(dstEval, *currBldrCtx); for (ExplodedNode *Pred : dstGenericPrevisit) { ProgramStateRef State = Pred->getState(); @@ -268,7 +268,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, if (ObjCNoRet.isImplicitNoReturn(ME)) { // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. - Bldr.generateSink(ME, Pred, State); + Engine.makePostStmtNode(ME, State, Pred, /*MarkAsSink=*/true); continue; } } @@ -278,12 +278,11 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, if (ObjCNoRet.isImplicitNoReturn(ME)) { // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. - Bldr.generateSink(ME, Pred, Pred->getState()); + Engine.makePostStmtNode(ME, State, Pred, /*MarkAsSink=*/true); continue; } } - Bldr.takeNodes(Pred); defaultEvalCall(Bldr, Pred, *UpdatedMsg); } From ee726d4ad9c3a0c3f751b4ba60fa392f6185dd91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]> Date: Mon, 15 Jun 2026 16:34:57 +0200 Subject: [PATCH 10/13] Remove NodeBuilders from the defaultEvalCall function family Instead of passing around the NodeBuilder, use its destination (a/k/a `Frontier`) set directly. Previous changes reduced the usage of these node builders to a few `addNodes` calls, which directly correspond to `ExplodedNodeSet::insert`. --- .../Core/PathSensitive/ExprEngine.h | 11 +++---- .../StaticAnalyzer/Core/CheckerManager.cpp | 6 ++-- .../lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 17 ++++------ .../Core/ExprEngineCallAndReturn.cpp | 32 +++++++++---------- .../StaticAnalyzer/Core/ExprEngineObjC.cpp | 3 +- 5 files changed, 30 insertions(+), 39 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index ae29f35a950b3..5f19784158ed2 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -772,7 +772,7 @@ class ExprEngine { const CallEvent &Call); /// Default implementation of call evaluation. - void defaultEvalCall(NodeBuilder &B, ExplodedNode *Pred, + void defaultEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, const CallEvent &Call, const EvalCallOptions &CallOpts = {}); @@ -910,7 +910,7 @@ class ExprEngine { void inlineCall(WorkList *WList, const CallEvent &Call, const Decl *D, ExplodedNode *Pred, ProgramStateRef State); - void ctuBifurcate(const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, + void ctuBifurcate(const CallEvent &Call, const Decl *D, ExplodedNodeSet &Dst, ExplodedNode *Pred, ProgramStateRef State); /// Returns true if the CTU analysis is running its second phase. @@ -923,15 +923,14 @@ class ExprEngine { /// Either inline or process the call conservatively (or both), based /// on DynamicDispatchBifurcation data. - void BifurcateCall(const MemRegion *BifurReg, - const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, - ExplodedNode *Pred); + void BifurcateCall(const MemRegion *BifurReg, const CallEvent &Call, + const Decl *D, ExplodedNodeSet &Dst, ExplodedNode *Pred); bool replayWithoutInlining(ExplodedNode *P, const StackFrame *CalleeSF); /// Models a trivial copy or move constructor or trivial assignment operator /// call with a simple bind. - void performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, + void performTrivialCopy(ExplodedNodeSet &Dst, ExplodedNode *Pred, const CallEvent &Call); /// If the value of the given expression \p InitWithAdjustments is a NonLoc, diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp index 1a57a40c02441..4db6b6ecaa9f7 100644 --- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -808,10 +808,8 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, } // If none of the checkers evaluated the call, ask ExprEngine to handle it. - if (!evaluatorChecker) { - NodeBuilder B(Dst, Eng.getBuilderContext()); - Eng.defaultEvalCall(B, Pred, *UpdatedCall, CallOpts); - } + if (!evaluatorChecker) + Eng.defaultEvalCall(Dst, Pred, *UpdatedCall, CallOpts); } } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index a202d92b8b74c..b4873a2280ff0 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -44,7 +44,7 @@ void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, // FIXME: This is the sort of code that should eventually live in a Core // checker rather than as a special case in ExprEngine. -void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, +void ExprEngine::performTrivialCopy(ExplodedNodeSet &Dst, ExplodedNode *Pred, const CallEvent &Call) { SVal ThisVal; bool AlwaysReturnsLValue; @@ -95,14 +95,13 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, DstEval.insert(Pred); } - PostStmt PS(CallExpr, SF); for (ExplodedNode *N : DstEval) { ProgramStateRef State = N->getState(); if (AlwaysReturnsLValue) State = State->BindExpr(CallExpr, SF, ThisVal); else State = bindReturnValue(Call, SF, State); - Bldr.generateNode(PS, State, N); + Dst.insert(Engine.makePostStmtNode(CallExpr, State, Pred)); } } @@ -728,10 +727,9 @@ void ExprEngine::handleConstructor(const Expr *E, if (CE && CE->getConstructor()->isTrivial() && CE->getConstructor()->isCopyOrMoveConstructor() && !CallOpts.IsArrayCtorOrDtor) { - NodeBuilder Bldr(DstEvaluated, *currBldrCtx); // FIXME: Handle other kinds of trivial constructors as well. for (ExplodedNode *N : DstPreCall) - performTrivialCopy(Bldr, N, *Call); + performTrivialCopy(DstEvaluated, N, *Call); } else { for (ExplodedNode *N : DstPreCall) @@ -861,9 +859,8 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, *Call, *this); ExplodedNodeSet DstInvalidated; - NodeBuilder Bldr(DstInvalidated, *currBldrCtx); for (ExplodedNode *N : DstPreCall) - defaultEvalCall(Bldr, N, *Call, CallOpts); + defaultEvalCall(DstInvalidated, N, *Call, CallOpts); getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, *Call, *this); @@ -886,7 +883,6 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, *Call, *this); ExplodedNodeSet DstPostCall; - NodeBuilder CallBldr(DstPostCall, *currBldrCtx); for (ExplodedNode *I : DstPreCall) { // Operator new calls (CXXNewExpr) are intentionally not eval-called, // because it does not make sense to eval-call user-provided functions. @@ -896,7 +892,7 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, // is what we want anyway. // So the best is to not allow eval-calling CXXNewExprs from checkers. // Checkers can provide their pre/post-call callbacks if needed. - defaultEvalCall(CallBldr, I, *Call); + defaultEvalCall(DstPostCall, I, *Call); } // If the call is inlined, DstPostCall will be empty and we bail out now. @@ -1094,13 +1090,12 @@ void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, ExplodedNodeSet DstPostCall; if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator) { - NodeBuilder Bldr(DstPostCall, *currBldrCtx); for (ExplodedNode *I : DstPreCall) { // Intentionally either inline or conservative eval-call the operator // delete, but avoid triggering an eval-call event for checkers. // As detailed at handling CXXNewExprs, in short, because it does not // really make sense to eval-call user-provided functions. - defaultEvalCall(Bldr, I, *Call); + defaultEvalCall(DstPostCall, I, *Call); } } else { DstPostCall = std::move(DstPreCall); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 47f67f17b17ba..099db8d081dfe 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -493,7 +493,7 @@ REGISTER_MAP_WITH_PROGRAMSTATE(DynamicDispatchBifurcationMap, REGISTER_TRAIT_WITH_PROGRAMSTATE(CTUDispatchBifurcation, bool) void ExprEngine::ctuBifurcate(const CallEvent &Call, const Decl *D, - NodeBuilder &Bldr, ExplodedNode *Pred, + ExplodedNodeSet &Dst, ExplodedNode *Pred, ProgramStateRef State) { ProgramStateRef ConservativeEvalState = nullptr; if (Call.isForeign() && !isSecondPhaseCTU()) { @@ -511,9 +511,9 @@ void ExprEngine::ctuBifurcate(const CallEvent &Call, const Decl *D, inlineCall(Engine.getCTUWorkList(), Call, D, Pred, State); // Conservatively evaluate in the first phase. ConservativeEvalState = State->set<CTUDispatchBifurcation>(true); - Bldr.addNodes(conservativeEvalCall(Call, Pred, ConservativeEvalState)); + Dst.insert(conservativeEvalCall(Call, Pred, ConservativeEvalState)); } else { - Bldr.addNodes(conservativeEvalCall(Call, Pred, State)); + Dst.insert(conservativeEvalCall(Call, Pred, State)); } return; } @@ -1216,7 +1216,7 @@ static bool isTrivialObjectAssignment(const CallEvent &Call) { return MD->isTrivial(); } -void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, +void ExprEngine::defaultEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, const CallEvent &Call, const EvalCallOptions &CallOpts) { // Make sure we have the most recent state attached to the call. @@ -1224,7 +1224,7 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, // Special-case trivial assignment operators. if (isTrivialObjectAssignment(Call)) { - performTrivialCopy(Bldr, Pred, Call); + performTrivialCopy(Dst, Pred, Call); return; } @@ -1244,17 +1244,17 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, // Explore with and without inlining the call. if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) { - BifurcateCall(RD.getDispatchRegion(), Call, D, Bldr, Pred); + BifurcateCall(RD.getDispatchRegion(), Call, D, Dst, Pred); return; } // Don't inline if we're not in any dynamic dispatch mode. if (Options.getIPAMode() != IPAK_DynamicDispatch) { - Bldr.addNodes(conservativeEvalCall(Call, Pred, State)); + Dst.insert(conservativeEvalCall(Call, Pred, State)); return; } } - ctuBifurcate(Call, D, Bldr, Pred, State); + ctuBifurcate(Call, D, Dst, Pred, State); return; } } @@ -1265,12 +1265,12 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, State, dyn_cast_or_null<CXXConstructExpr>(E), Call.getStackFrame()); // Also handle the return value and invalidate the regions. - Bldr.addNodes(conservativeEvalCall(Call, Pred, State)); + Dst.insert(conservativeEvalCall(Call, Pred, State)); } -void ExprEngine::BifurcateCall(const MemRegion *BifurReg, - const CallEvent &Call, const Decl *D, - NodeBuilder &Bldr, ExplodedNode *Pred) { +void ExprEngine::BifurcateCall(const MemRegion *BifurReg, const CallEvent &Call, + const Decl *D, ExplodedNodeSet &Dst, + ExplodedNode *Pred) { assert(BifurReg); BifurReg = BifurReg->StripCasts(); @@ -1282,11 +1282,11 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg, if (BState) { // If we are on "inline path", keep inlining if possible. if (*BState == DynamicDispatchModeInlined) - ctuBifurcate(Call, D, Bldr, Pred, State); + ctuBifurcate(Call, D, Dst, Pred, State); // If inline failed, or we are on the path where we assume we // don't have enough info about the receiver to inline, conjure the // return value and invalidate the regions. - Bldr.addNodes(conservativeEvalCall(Call, Pred, State)); + Dst.insert(conservativeEvalCall(Call, Pred, State)); return; } @@ -1295,12 +1295,12 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg, ProgramStateRef IState = State->set<DynamicDispatchBifurcationMap>(BifurReg, DynamicDispatchModeInlined); - ctuBifurcate(Call, D, Bldr, Pred, IState); + ctuBifurcate(Call, D, Dst, Pred, IState); ProgramStateRef NoIState = State->set<DynamicDispatchBifurcationMap>(BifurReg, DynamicDispatchModeConservative); - Bldr.addNodes(conservativeEvalCall(Call, Pred, NoIState)); + Dst.insert(conservativeEvalCall(Call, Pred, NoIState)); NumOfDynamicDispatchPathSplits++; } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index d5c38890800db..d4be050427b8b 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -256,7 +256,6 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, // Proceed with evaluate the message expression. ExplodedNodeSet dstEval; - NodeBuilder Bldr(dstEval, *currBldrCtx); for (ExplodedNode *Pred : dstGenericPrevisit) { ProgramStateRef State = Pred->getState(); @@ -283,7 +282,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, } } - defaultEvalCall(Bldr, Pred, *UpdatedMsg); + defaultEvalCall(dstEval, Pred, *UpdatedMsg); } // If there were constructors called for object-type arguments, clean them up. From ea80be6a48101945603695acff744a87ad4d96a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]> Date: Mon, 15 Jun 2026 16:46:16 +0200 Subject: [PATCH 11/13] [side] Simplify isImplicitNoReturn logic in VisitObjCMessage Unify two `if` blocks that had identical bodies and related conditions. --- .../StaticAnalyzer/Core/ExprEngineObjC.cpp | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index d4be050427b8b..26b8ca3e1f2bc 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -261,25 +261,13 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, ProgramStateRef State = Pred->getState(); CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State); - if (UpdatedMsg->isInstanceMessage()) { - SVal recVal = UpdatedMsg->getReceiverSVal(); - if (!recVal.isUndef()) { - if (ObjCNoRet.isImplicitNoReturn(ME)) { - // If we raise an exception, for now treat it as a sink. - // Eventually we will want to handle exceptions properly. - Engine.makePostStmtNode(ME, State, Pred, /*MarkAsSink=*/true); - continue; - } - } - } else { - // Check for special class methods that are known to not return - // and that we should treat as a sink. - if (ObjCNoRet.isImplicitNoReturn(ME)) { - // If we raise an exception, for now treat it as a sink. - // Eventually we will want to handle exceptions properly. - Engine.makePostStmtNode(ME, State, Pred, /*MarkAsSink=*/true); - continue; - } + if (ObjCNoRet.isImplicitNoReturn(ME) && + !(UpdatedMsg->isInstanceMessage() && + UpdatedMsg->getReceiverSVal().isUndef())) { + // If we raise an exception, for now treat it as a sink. + // Eventually we will want to handle exceptions properly. + Engine.makePostStmtNode(ME, State, Pred, /*MarkAsSink=*/true); + continue; } defaultEvalCall(dstEval, Pred, *UpdatedMsg); From ca59d8dd6c15aff5ac73954824e38a471b4ab273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]> Date: Mon, 15 Jun 2026 16:52:51 +0200 Subject: [PATCH 12/13] [side] Simplify ctuBifurcate Avoid duplicating the call of `conservativeEvalCall`. --- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 099db8d081dfe..33eba2e69e58e 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -495,7 +495,6 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(CTUDispatchBifurcation, bool) void ExprEngine::ctuBifurcate(const CallEvent &Call, const Decl *D, ExplodedNodeSet &Dst, ExplodedNode *Pred, ProgramStateRef State) { - ProgramStateRef ConservativeEvalState = nullptr; if (Call.isForeign() && !isSecondPhaseCTU()) { const auto IK = AMgr.options.getCTUPhase1Inlining(); const bool DoInline = IK == CTUPhase1InliningKind::All || @@ -510,11 +509,9 @@ void ExprEngine::ctuBifurcate(const CallEvent &Call, const Decl *D, // Enqueue it to be analyzed in the second (ctu) phase. inlineCall(Engine.getCTUWorkList(), Call, D, Pred, State); // Conservatively evaluate in the first phase. - ConservativeEvalState = State->set<CTUDispatchBifurcation>(true); - Dst.insert(conservativeEvalCall(Call, Pred, ConservativeEvalState)); - } else { - Dst.insert(conservativeEvalCall(Call, Pred, State)); + State = State->set<CTUDispatchBifurcation>(true); } + Dst.insert(conservativeEvalCall(Call, Pred, State)); return; } inlineCall(Engine.getWorkList(), Call, D, Pred, State); From 29cc1fc16a0608eae3fdea170fb4c46f4f955ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]> Date: Mon, 15 Jun 2026 16:57:39 +0200 Subject: [PATCH 13/13] [side] Rename BifurcateCall to dynDispatchBifurcate As I'm already changing the parametrization of this method, I take this opportunity to update its name to clarify its role and conform to the naming conventions. This is an old method and when it was originally introduced, 'BifurcateCall' was a reasonable name for it, but since then we also have 'ctuBifurcate', so it is better to use a more specific name for this method. --- .../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 5 +++-- .../lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 5f19784158ed2..ce9b20185444e 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -923,8 +923,9 @@ class ExprEngine { /// Either inline or process the call conservatively (or both), based /// on DynamicDispatchBifurcation data. - void BifurcateCall(const MemRegion *BifurReg, const CallEvent &Call, - const Decl *D, ExplodedNodeSet &Dst, ExplodedNode *Pred); + void dynDispatchBifurcate(const MemRegion *BifurReg, const CallEvent &Call, + const Decl *D, ExplodedNodeSet &Dst, + ExplodedNode *Pred); bool replayWithoutInlining(ExplodedNode *P, const StackFrame *CalleeSF); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 33eba2e69e58e..d5733d40ff60d 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -1241,7 +1241,7 @@ void ExprEngine::defaultEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, // Explore with and without inlining the call. if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) { - BifurcateCall(RD.getDispatchRegion(), Call, D, Dst, Pred); + dynDispatchBifurcate(RD.getDispatchRegion(), Call, D, Dst, Pred); return; } @@ -1265,9 +1265,10 @@ void ExprEngine::defaultEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, Dst.insert(conservativeEvalCall(Call, Pred, State)); } -void ExprEngine::BifurcateCall(const MemRegion *BifurReg, const CallEvent &Call, - const Decl *D, ExplodedNodeSet &Dst, - ExplodedNode *Pred) { +void ExprEngine::dynDispatchBifurcate(const MemRegion *BifurReg, + const CallEvent &Call, const Decl *D, + ExplodedNodeSet &Dst, + ExplodedNode *Pred) { assert(BifurReg); BifurReg = BifurReg->StripCasts(); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
