[NFC][analyzer] Introduce specialized variants of makeNode (#194459)
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.
I decided to introduce these methods because after the transition there
will be 20+ calls to `makePostStmtNode` and 30+ calls to
`makeNodeWithBinding` and it would be cumbersome to use plain `makeNode`
instead of these specialized variants.
(On the other hand these new methods don't support specifying a `tag`
because only a few call sites would use that feature of the 5-parameter
`generateNode`.)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
index 57b0947..1068cb2 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
@@ -170,6 +170,29 @@
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 4ebc591..1efe7e6 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1557,13 +1557,11 @@
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();
}
@@ -1571,24 +1569,21 @@
// 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;
@@ -1621,7 +1616,7 @@
// 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);
}
@@ -3189,8 +3184,6 @@
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();
@@ -3223,12 +3216,11 @@
// 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();
@@ -3242,25 +3234,26 @@
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)) {
@@ -3276,8 +3269,8 @@
V = UnknownVal();
}
- Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
- ProgramPoint::PostLValueKind);
+ Dst.insert(Engine.makeNodeWithBinding(Pred, Ex, V,
+ ProgramPoint::PostLValueKind));
return;
}
@@ -3331,15 +3324,15 @@
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;
}
@@ -3455,7 +3448,6 @@
getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, A, *this);
ExplodedNodeSet EvalSet;
- NodeBuilder Bldr(CheckerPreStmt, EvalSet, *currBldrCtx);
bool IsVectorType = A->getBase()->getType()->isVectorType();
@@ -3481,11 +3473,11 @@
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");