[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