[clang] [NFC][analyzer] Introduce specialized variants of makeNode (PR #194459)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 27 13:48:34 PDT 2026
=?utf-8?q?Donát?= Nagy <donat.nagy at ericsson.com>,
=?utf-8?q?Donát?= Nagy <donat.nagy at ericsson.com>,
=?utf-8?q?Donát?= Nagy <donat.nagy at ericsson.com>,
=?utf-8?q?Donát?= Nagy <donat.nagy at ericsson.com>,
=?utf-8?q?Donát?= Nagy <donat.nagy at ericsson.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/194459 at github.com>
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Donát Nagy (NagyDonat)
<details>
<summary>Changes</summary>
This commit introduces new methods `makePostStmtNode` and `makeNodeWithBinding` of `CoreEngine`, which will be used instead of the 5-parameter overloads of `NodeBuilder::generateNode` and `NodeBuilder::generateSink` (which were originally methods of the class `StmtNodeBuilder` that was deleted in commit fb46677a858697afa116c4252e84050a07bc6a70).
This commit applies the newly introduced methods in a few places (as examples), but there are 80+ call sites that use the 5-parameter `NodeBuilder::generateNode` or `generateSink`, so this transition will be completed in multiple follow-up commits.
The parametrization of these methods was designed to provide the features that will be used frequently. E.g. `makeNodeWithBinding` was introduced because there are 30+ call sites that use `BindExpr` just before the `generateNode` call -- but there is no support for specifying a `tag` because only a few call sites would use it.
----
For the reviewer's convenience, the [5-parameter `generateNode`](https://github.com/llvm/llvm-project/blob/5e625826d6d394e65a767bf94d21739b18dade30/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h#L285) is defined as:
```c++
ExplodedNode *generateNode(const Stmt *S,
ExplodedNode *Pred,
ProgramStateRef St,
const ProgramPointTag *tag = nullptr,
ProgramPoint::Kind K = ProgramPoint::PostStmtKind){
const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
Pred->getLocationContext(), tag);
return generateNode(L, St, Pred);
}
```
---
Full diff: https://github.com/llvm/llvm-project/pull/194459.diff
2 Files Affected:
- (modified) clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h (+23)
- (modified) clang/lib/StaticAnalyzer/Core/ExprEngine.cpp (+29-37)
``````````diff
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
index 57b0947093b78..1068cb2353f90 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
@@ -170,6 +170,29 @@ class CoreEngine {
ExplodedNode *makeNode(const ProgramPoint &Loc, ProgramStateRef State,
ExplodedNode *Pred, bool MarkAsSink = false) const;
+ ExplodedNode *makePostStmtNode(const Stmt *S, ProgramStateRef State,
+ ExplodedNode *Pred,
+ bool MarkAsSink = false) const {
+ PostStmt Loc(S, Pred->getLocationContext(), /*tag=*/nullptr);
+ return makeNode(Loc, State, Pred, MarkAsSink);
+ }
+
+ ExplodedNode *
+ makeNodeWithBinding(ExplodedNode *Pred, const Expr *E, SVal V,
+ ProgramStateRef State,
+ ProgramPoint::Kind K = ProgramPoint::PostStmtKind) const {
+ const LocationContext *LC = Pred->getLocationContext();
+ State = State->BindExpr(E, LC, V);
+ const auto &L = ProgramPoint::getProgramPoint(E, K, LC, /*tag=*/nullptr);
+ return makeNode(L, State, Pred);
+ }
+
+ ExplodedNode *
+ makeNodeWithBinding(ExplodedNode *Pred, const Expr *E, SVal V,
+ ProgramPoint::Kind K = ProgramPoint::PostStmtKind) const {
+ return makeNodeWithBinding(Pred, E, V, Pred->getState(), K);
+ }
+
/// Enqueue the given set of nodes onto the work list.
void enqueue(ExplodedNodeSet &Set);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index b69b45acb7989..9796119b05cc3 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1568,13 +1568,11 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
const LocationContext *LC = Pred->getLocationContext();
const MemRegion *MR = nullptr;
- if (std::optional<SVal> V = getObjectUnderConstruction(
- State, D.getBindTemporaryExpr(), Pred->getLocationContext())) {
+ if (std::optional<SVal> V = getObjectUnderConstruction(State, BTE, LC)) {
// FIXME: Currently we insert temporary destructors for default parameters,
// but we don't insert the constructors, so the entry in
// ObjectsUnderConstruction may be missing.
- State = finishObjectConstruction(State, D.getBindTemporaryExpr(),
- Pred->getLocationContext());
+ State = finishObjectConstruction(State, BTE, LC);
MR = V->getAsRegion();
}
@@ -1582,24 +1580,21 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
// destructor was elided, we need to skip the destructor as well.
if (isDestructorElided(State, BTE, LC)) {
State = cleanupElidedDestructor(State, BTE, LC);
- NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
- PostImplicitCall PP(D.getDestructorDecl(getContext()),
- D.getBindTemporaryExpr()->getBeginLoc(),
- Pred->getLocationContext(), getCFGElementRef());
- Bldr.generateNode(PP, State, Pred);
+ PostImplicitCall PP(D.getDestructorDecl(getContext()), BTE->getBeginLoc(),
+ LC, getCFGElementRef());
+ Dst.insert(Engine.makeNode(PP, State, Pred));
return;
}
- ExplodedNodeSet CleanDtorState;
- NodeBuilder Builder(Pred, CleanDtorState, *currBldrCtx);
- Builder.generateNode(D.getBindTemporaryExpr(), Pred, State);
+ ExplodedNode *CleanPred = Engine.makePostStmtNode(BTE, State, Pred);
+ if (!CleanPred || CleanPred->isSink()) {
+ // FIXME: We can get a null node here due to temporaries being
+ // bound to default parameters.
+ // Sink check is just PosteriorlyOverconstrained paranoia.
+ CleanPred = Pred;
+ }
- QualType T = D.getBindTemporaryExpr()->getSubExpr()->getType();
- // FIXME: Currently CleanDtorState can be empty here due to temporaries being
- // bound to default parameters.
- assert(CleanDtorState.size() <= 1);
- ExplodedNode *CleanPred =
- CleanDtorState.empty() ? Pred : *CleanDtorState.begin();
+ QualType T = BTE->getSubExpr()->getType();
EvalCallOptions CallOpts;
CallOpts.IsTemporaryCtorOrDtor = true;
@@ -1632,7 +1627,7 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
// but for now we don't have the respective construction contexts,
// so MR would always be null in this case. Do nothing for now.
}
- VisitCXXDestructor(T, MR, D.getBindTemporaryExpr(),
+ VisitCXXDestructor(T, MR, BTE,
/*IsBase=*/false, CleanPred, Dst, CallOpts);
}
@@ -3194,8 +3189,6 @@ void ExprEngine::processSwitch(const SwitchStmt *Switch, ExplodedNode *Pred,
void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
- NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
-
ProgramStateRef state = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
@@ -3228,12 +3221,11 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
// C permits "extern void v", and if you cast the address to a valid type,
// you can even do things with it. We simply pretend
assert(Ex->isGLValue() || VD->getType()->isVoidType());
- const LocationContext *LocCtxt = Pred->getLocationContext();
std::optional<std::pair<SVal, QualType>> VInfo =
resolveAsLambdaCapturedVar(VD);
if (!VInfo)
- VInfo = std::make_pair(state->getLValue(VD, LocCtxt), VD->getType());
+ VInfo = std::make_pair(state->getLValue(VD, LCtx), VD->getType());
SVal V = VInfo->first;
bool IsReference = VInfo->second->isReferenceType();
@@ -3247,25 +3239,26 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
V = UnknownVal();
}
- Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
- ProgramPoint::PostLValueKind);
+ Dst.insert(
+ Engine.makeNodeWithBinding(Pred, Ex, V, ProgramPoint::PostLValueKind));
return;
}
if (const auto *ED = dyn_cast<EnumConstantDecl>(D)) {
assert(!Ex->isGLValue());
SVal V = svalBuilder.makeIntVal(ED->getInitVal());
- Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V));
+ Dst.insert(Engine.makeNodeWithBinding(Pred, Ex, V));
return;
}
if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
SVal V = svalBuilder.getFunctionPointer(FD);
- Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
- ProgramPoint::PostLValueKind);
+ Dst.insert(
+ Engine.makeNodeWithBinding(Pred, Ex, V, ProgramPoint::PostLValueKind));
return;
}
if (isa<FieldDecl, IndirectFieldDecl>(D)) {
// Delegate all work related to pointer to members to the surrounding
// operator&.
+ Dst.insert(Pred);
return;
}
if (const auto *BD = dyn_cast<BindingDecl>(D)) {
@@ -3281,8 +3274,8 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
V = UnknownVal();
}
- Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
- ProgramPoint::PostLValueKind);
+ Dst.insert(Engine.makeNodeWithBinding(Pred, Ex, V,
+ ProgramPoint::PostLValueKind));
return;
}
@@ -3336,15 +3329,15 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
V = UnknownVal();
}
- Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
- ProgramPoint::PostLValueKind);
-
+ Dst.insert(
+ Engine.makeNodeWithBinding(Pred, Ex, V, ProgramPoint::PostLValueKind));
return;
}
if (const auto *TPO = dyn_cast<TemplateParamObjectDecl>(D)) {
// FIXME: We should meaningfully implement this.
(void)TPO;
+ Dst.insert(Pred);
return;
}
@@ -3460,7 +3453,6 @@ void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A,
getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, A, *this);
ExplodedNodeSet EvalSet;
- NodeBuilder Bldr(CheckerPreStmt, EvalSet, *currBldrCtx);
bool IsVectorType = A->getBase()->getType()->isVectorType();
@@ -3486,11 +3478,11 @@ void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A,
SVal V = state->getLValue(T,
state->getSVal(Idx, LCtx),
state->getSVal(Base, LCtx));
- Bldr.generateNode(A, Node, state->BindExpr(A, LCtx, V), nullptr,
- ProgramPoint::PostLValueKind);
+ EvalSet.insert(
+ Engine.makeNodeWithBinding(Node, A, V, ProgramPoint::PostLValueKind));
} else if (IsVectorType) {
// FIXME: non-glvalue vector reads are not modelled.
- Bldr.generateNode(A, Node, state, nullptr);
+ EvalSet.insert(Engine.makePostStmtNode(A, state, Node));
} else {
llvm_unreachable("Array subscript should be an lValue when not \
a vector and not a forbidden lvalue type");
``````````
</details>
https://github.com/llvm/llvm-project/pull/194459
More information about the cfe-commits
mailing list