Author: DonĂ¡t Nagy Date: 2026-03-23T15:09:32+01:00 New Revision: a6caf52908bf499fd7fc3ae96dcd3ef8c41d5ce1
URL: https://github.com/llvm/llvm-project/commit/a6caf52908bf499fd7fc3ae96dcd3ef8c41d5ce1 DIFF: https://github.com/llvm/llvm-project/commit/a6caf52908bf499fd7fc3ae96dcd3ef8c41d5ce1.diff LOG: [NFC][analyzer] Clean up and document `ExplodedNodeSet` (#187742) `ExplodedNodeSet` is a simple and useful utility type in the analyzer, but its insertion methods were a bit confusing, so this commit clarifies them (and adds doc-comments for this class). Previously this class had `void Add(ExplodedNode*)` for inserting single nodes and `void insert(const ExplodedNodeSet &)` for inserting all nodes from another set; but `ExplodedNode*` is implicitly convertible to `ExplodedNodeSet`, so it was also possible to insert single nodes with `insert`. There was also a subtle difference between `Set.Add(Node)` and `Set.insert(Node)`: `Add` accepted and ignored nullpointers and sink nodes (which is often useful) while the constructor `ExplodedNodeSet(ExplodedNode*)` enforced the same invariant in a less helpful way, with an assertion. This commit eliminates the name `Add` (because `insert` is more customary for set types), but standardizes its "null or sink nodes are silently ignored" behavior which is very useful in practice. After this commit `insert` has two overloads: `ExplodedNodeSet::insert(ExplodedNode*)` which inserts a single node (or does nothing if the argument is null or sink) and `ExplodedNodeSet::insert(const ExplodedNodeSet&)` which inserts all nodes from the other set. Note that around half of the calls to `Add` were recently introduced by my `NodeBuilder` cleanup commit series; I expect to introduce dozens of calls in the foreseeable future -- this commit can be considered as preparation for those. Added: Modified: clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h clang/lib/StaticAnalyzer/Core/CoreEngine.cpp clang/lib/StaticAnalyzer/Core/ExprEngine.cpp clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp Removed: ################################################################################ diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h index 8d5dcc1ca8b13..76bd8346f269e 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -259,7 +259,7 @@ class NodeBuilder { NodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, const NodeBuilderContext &Ctx) : NodeBuilder(DstSet, Ctx) { - Frontier.Add(SrcNode); + Frontier.insert(SrcNode); } NodeBuilder(const ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, @@ -314,7 +314,7 @@ class NodeBuilder { void takeNodes(ExplodedNode *N) { Frontier.erase(N); } void addNodes(const ExplodedNodeSet &S) { Frontier.insert(S); } - void addNodes(ExplodedNode *N) { Frontier.Add(N); } + void addNodes(ExplodedNode *N) { Frontier.insert(N); } }; /// BranchNodeBuilder is responsible for constructing the nodes diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h index a536f5ee046e1..bb2297a6889c9 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h @@ -440,23 +440,23 @@ class ExplodedGraph { void collectNode(ExplodedNode *node); }; +/// ExplodedNodeSet is a set of `ExplodedNode *` elements with the invariant +/// that its elements cannot be nullpointers or sink nodes. Insertion of null +/// or sink nodes is silently ignored (which is comfortable in many use cases). +/// Note that `ExplodedNode *` is implicitly convertible to an +/// `ExplodedNodeSet` containing 0 or 1 elements (where null pointers and sink +/// nodes converted to the empty set). +/// This type has set semantics (repeated insertions are ignored), but the +/// iteration order is always the order of (first) insertion. class ExplodedNodeSet { using ImplTy = llvm::SmallSetVector<ExplodedNode *, 4>; ImplTy Impl; public: - ExplodedNodeSet(ExplodedNode *N) { - assert(N && !N->isSink()); - Impl.insert(N); - } + ExplodedNodeSet(ExplodedNode *N) { insert(N); } ExplodedNodeSet() = default; - void Add(ExplodedNode *N) { - if (N && !N->isSink()) - Impl.insert(N); - } - using iterator = ImplTy::iterator; using const_iterator = ImplTy::const_iterator; @@ -466,8 +466,14 @@ class ExplodedNodeSet { void clear() { Impl.clear(); } + void insert(ExplodedNode *N) { + if (N && !N->isSink()) + Impl.insert(N); + } + void insert(const ExplodedNodeSet &S) { - assert(&S != this); + if (&S == this) + return; if (empty()) Impl = S.Impl; else diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp index 26672ff75996c..a348f7947ad98 100644 --- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -689,7 +689,7 @@ ExplodedNode *NodeBuilder::generateNode(const ProgramPoint &Loc, Frontier.erase(FromN); ExplodedNode *N = C.getEngine().makeNode(Loc, State, FromN, MarkAsSink); - Frontier.Add(N); + Frontier.insert(N); return N; } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 0fb8f1d00b618..e9522a7975515 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1131,7 +1131,7 @@ void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) { removeDead(Pred, CleanedStates, currStmt, Pred->getLocationContext()); } else - CleanedStates.Add(Pred); + CleanedStates.insert(Pred); // Visit the statement. ExplodedNodeSet Dst; @@ -1190,7 +1190,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit, // But we still need to stop tracking the object under construction. State = finishObjectConstruction(State, BMI, LC); PostStore PS(Init, LC, /*Loc*/ nullptr, /*tag*/ nullptr); - Tmp.Add(Engine.makeNode(PS, State, Pred)); + Tmp.insert(Engine.makeNode(PS, State, Pred)); } else { const ValueDecl *Field; if (BMI->isIndirectMemberInitializer()) { @@ -1245,7 +1245,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit, ExplodedNodeSet Dst; for (ExplodedNode *Pred : Tmp) - Dst.Add(Engine.makeNode(PP, Pred->getState(), Pred)); + Dst.insert(Engine.makeNode(PP, Pred->getState(), Pred)); // Enqueue the new nodes onto the work list. Engine.enqueueStmtNodes(Dst, getCurrBlock(), currStmtIdx); } @@ -1328,7 +1328,7 @@ void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE, const LocationContext *LCtx = Pred->getLocationContext(); PostImplicitCall PP(NE->getOperatorNew(), NE->getBeginLoc(), LCtx, getCFGElementRef()); - Dst.Add(Engine.makeNode(PP, Pred->getState(), Pred)); + Dst.insert(Engine.makeNode(PP, Pred->getState(), Pred)); } Engine.enqueueStmtNodes(Dst, getCurrBlock(), currStmtIdx); } @@ -2999,7 +2999,7 @@ void ExprEngine::processIndirectGoto(ExplodedNodeSet &Dst, const Expr *Tgt, // FIXME: If 'V' was a symbolic value, then record that on this execution // path it is equal to the address of the label leading to 'Succ'. BlockEdge BE(getCurrBlock(), Succ, Pred->getLocationContext()); - Dst.Add(Engine.makeNode(BE, State, Pred)); + Dst.insert(Engine.makeNode(BE, State, Pred)); } } } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index cf22b62225f2f..701c7fdc88497 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -93,7 +93,7 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, // 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.Add(Pred); + Dst.insert(Pred); } PostStmt PS(CallExpr, LCtx); @@ -1130,7 +1130,7 @@ void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred, ExplodedNodeSet &Dst) { const VarDecl *VD = CS->getExceptionDecl(); if (!VD) { - Dst.Add(Pred); + Dst.insert(Pred); return; } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index f6ba3699312ec..b4dc4fa8a077d 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -169,7 +169,7 @@ void ExprEngine::removeDeadOnEndOfFunction(ExplodedNode *Pred, const CFGBlock *Blk = nullptr; std::tie(LastSt, Blk) = getLastStmt(Pred); if (!Blk || !LastSt) { - Dst.Add(Pred); + Dst.insert(Pred); return; } @@ -369,7 +369,7 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { /*DiagnosticStmt=*/CalleeCtx->getAnalysisDeclContext()->getBody(), ProgramPoint::PostStmtPurgeDeadSymbolsKind); } else { - CleanedNodes.Add(CEBNode); + CleanedNodes.insert(CEBNode); } // The second half of this process happens in the caller context. This is an _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
