Skip to content

Commit 31e981c

Browse files
authored
[NFC][analyzer] Clarify that ExplodedGraph has only one root (#139903)
Previously the class `ExplodedGraph` had a data member called `Roots` which could (in theory) store multiple root nodes -- but in practice exploded graphs always had at most one root node (zero was possible for empty and partially copied graphs) and introducing a graph with multiple roots would've caused severe malfuncitons (e.g. in code that used the pattern `*roots_begin()` to refer to _the_ root node). I don't see any practical use case for adding multiple root nodes in a single graph (this seems to be yet another of the "generalize for the sake of generalization" decisions which were common in the early history of the analyzer), so this commit replaces the vector `Roots` with `ExplodedNode *Root` (which may be null when the graph is empty or under construction). Note that the complicated logic of `ExplodedGraph::trim` deserves a through cleanup, but I left that for a follow-up commit.
1 parent 9d5d715 commit 31e981c

File tree

7 files changed

+49
-59
lines changed

7 files changed

+49
-59
lines changed

clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -307,11 +307,9 @@ class ExplodedGraph {
307307
// Type definitions.
308308
using NodeVector = std::vector<ExplodedNode *>;
309309

310-
/// The roots of the simulation graph. Usually there will be only
311-
/// one, but clients are free to establish multiple subgraphs within a single
312-
/// SimulGraph. Moreover, these subgraphs can often merge when paths from
313-
/// different roots reach the same state at the same program location.
314-
NodeVector Roots;
310+
/// The root of the simulation graph. Can be nullptr if the graph is empty or
311+
/// if it was populated by `createUncachedNode()`.
312+
ExplodedNode *Root = nullptr;
315313

316314
/// The nodes in the simulation graph which have been
317315
/// specially marked as the endpoint of an abstract simulation path.
@@ -345,31 +343,31 @@ class ExplodedGraph {
345343
ExplodedGraph();
346344
~ExplodedGraph();
347345

348-
/// Retrieve the node associated with a (Location,State) pair,
349-
/// where the 'Location' is a ProgramPoint in the CFG. If no node for
350-
/// this pair exists, it is created. IsNew is set to true if
351-
/// the node was freshly created.
346+
/// Get the root node of the graph. This may return nullptr if the graph is
347+
/// empty or under construction.
348+
ExplodedNode *getRoot() const { return Root; }
349+
350+
/// Retrieve the node associated with a (Location, State) pair, where the
351+
/// 'Location' is a ProgramPoint in the CFG. If no node for this pair exists,
352+
/// it is created. IsNew is set to true if the node was freshly created.
352353
ExplodedNode *getNode(const ProgramPoint &L, ProgramStateRef State,
353354
bool IsSink = false,
354355
bool* IsNew = nullptr);
355356

356-
/// Create a node for a (Location, State) pair,
357-
/// but don't store it for deduplication later. This
358-
/// is useful when copying an already completed
359-
/// ExplodedGraph for further processing.
357+
/// Create a node for a (Location, State) pair, but don't store it for
358+
/// deduplication later. This is useful when copying some nodes from an
359+
/// already completed ExplodedGraph for further processing.
360360
ExplodedNode *createUncachedNode(const ProgramPoint &L,
361361
ProgramStateRef State,
362362
int64_t Id,
363363
bool IsSink = false);
364364

365-
std::unique_ptr<ExplodedGraph> MakeEmptyGraph() const {
366-
return std::make_unique<ExplodedGraph>();
367-
}
368-
369-
/// addRoot - Add an untyped node to the set of roots.
370-
ExplodedNode *addRoot(ExplodedNode *V) {
371-
Roots.push_back(V);
372-
return V;
365+
/// Mark a node as the root of the graph. Calling this is an error if the
366+
/// graph already has a root node.
367+
void designateAsRoot(ExplodedNode *V) {
368+
assert(V && "Cannot designate nullptr as root!");
369+
assert(!Root && "The graph already has a root, cannot designate another!");
370+
Root = V;
373371
}
374372

375373
/// addEndOfPath - Add an untyped node to the set of EOP nodes.
@@ -378,7 +376,6 @@ class ExplodedGraph {
378376
return V;
379377
}
380378

381-
unsigned num_roots() const { return Roots.size(); }
382379
unsigned num_eops() const { return EndNodes.size(); }
383380

384381
bool empty() const { return NumNodes == 0; }
@@ -389,8 +386,6 @@ class ExplodedGraph {
389386
// Iterators.
390387
using NodeTy = ExplodedNode;
391388
using AllNodesTy = llvm::FoldingSet<ExplodedNode>;
392-
using roots_iterator = NodeVector::iterator;
393-
using const_roots_iterator = NodeVector::const_iterator;
394389
using eop_iterator = NodeVector::iterator;
395390
using const_eop_iterator = NodeVector::const_iterator;
396391
using node_iterator = AllNodesTy::iterator;
@@ -400,14 +395,6 @@ class ExplodedGraph {
400395

401396
llvm::iterator_range<const_node_iterator> nodes() const { return Nodes; }
402397

403-
roots_iterator roots_begin() { return Roots.begin(); }
404-
405-
roots_iterator roots_end() { return Roots.end(); }
406-
407-
const_roots_iterator roots_begin() const { return Roots.begin(); }
408-
409-
const_roots_iterator roots_end() const { return Roots.end(); }
410-
411398
eop_iterator eop_begin() { return EndNodes.begin(); }
412399

413400
eop_iterator eop_end() { return EndNodes.end(); }
@@ -508,9 +495,7 @@ namespace llvm {
508495
using ChildIteratorType = clang::ento::ExplodedNode::succ_iterator;
509496
using nodes_iterator = llvm::df_iterator<GraphTy>;
510497

511-
static NodeRef getEntryNode(const GraphTy G) {
512-
return *G->roots_begin();
513-
}
498+
static NodeRef getEntryNode(const GraphTy G) { return G->getRoot(); }
514499

515500
static bool predecessorOfTrivial(NodeRef N) {
516501
return N->succ_size() == 1 && N->getFirstSucc()->isTrivial();

clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,8 @@ class ExprEngine {
222222
const Stmt *getStmt() const;
223223

224224
const LocationContext *getRootLocationContext() const {
225-
assert(G.roots_begin() != G.roots_end());
226-
return (*G.roots_begin())->getLocation().getLocationContext();
225+
assert(G.getRoot());
226+
return G.getRoot()->getLocation().getLocationContext();
227227
}
228228

229229
ConstCFGElementRef getCFGElementRef() const {

clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
4545
const SourceManager &SM = B.getSourceManager();
4646
llvm::SmallPtrSet<const CFGBlock*, 32> reachable;
4747

48-
// Root node should have the location context of the top most function.
49-
const ExplodedNode *GraphRoot = *G.roots_begin();
50-
const LocationContext *LC = GraphRoot->getLocation().getLocationContext();
48+
const LocationContext *LC = Eng.getRootLocationContext();
5149

5250
const Decl *D = LC->getDecl();
5351

clang/lib/StaticAnalyzer/Core/BugReporter.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2660,8 +2660,7 @@ BugPathGetter::BugPathGetter(const ExplodedGraph *OriginalGraph,
26602660
// Perform a forward BFS to find all the shortest paths.
26612661
std::queue<const ExplodedNode *> WS;
26622662

2663-
assert(TrimmedGraph->num_roots() == 1);
2664-
WS.push(*TrimmedGraph->roots_begin());
2663+
WS.push(TrimmedGraph->getRoot());
26652664
unsigned Priority = 0;
26662665

26672666
while (!WS.empty()) {
@@ -2722,7 +2721,9 @@ BugPathInfo *BugPathGetter::getNextBugPath() {
27222721

27232722
// Are we at the final node?
27242723
if (OrigN->pred_empty()) {
2725-
GNew->addRoot(NewN);
2724+
assert(OrigN == TrimmedGraph->getRoot() &&
2725+
"There should be only one root!");
2726+
GNew->designateAsRoot(NewN);
27262727
break;
27272728
}
27282729

clang/lib/StaticAnalyzer/Core/CoreEngine.cpp

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,9 @@ void CoreEngine::setBlockCounter(BlockCounter C) {
8787
/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps.
8888
bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned MaxSteps,
8989
ProgramStateRef InitState) {
90-
if (G.num_roots() == 0) { // Initialize the analysis by constructing
91-
// the root if none exists.
90+
if (G.empty()) {
91+
assert(!G.getRoot() && "empty graph must not have a root node");
92+
// Initialize the analysis by constructing the root if there are no nodes.
9293

9394
const CFGBlock *Entry = &(L->getCFG()->getEntry());
9495

@@ -117,7 +118,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned MaxSteps,
117118
bool IsNew;
118119
ExplodedNode *Node = G.getNode(StartLoc, InitState, false, &IsNew);
119120
assert(IsNew);
120-
G.addRoot(Node);
121+
G.designateAsRoot(Node);
121122

122123
NodeBuilderContext BuilderCtx(*this, StartLoc.getDst(), Node);
123124
ExplodedNodeSet DstBegin;
@@ -548,15 +549,11 @@ void CoreEngine::HandleVirtualBaseBranch(const CFGBlock *B,
548549
void CoreEngine::generateNode(const ProgramPoint &Loc,
549550
ProgramStateRef State,
550551
ExplodedNode *Pred) {
552+
assert(Pred);
551553
bool IsNew;
552554
ExplodedNode *Node = G.getNode(Loc, State, false, &IsNew);
553555

554-
if (Pred)
555-
Node->addPredecessor(Pred, G); // Link 'Node' with its predecessor.
556-
else {
557-
assert(IsNew);
558-
G.addRoot(Node); // 'Node' has no predecessor. Make it a root.
559-
}
556+
Node->addPredecessor(Pred, G); // Link 'Node' with its predecessor.
560557

561558
// Only add 'Node' to the worklist if it was freshly generated.
562559
if (IsNew) WList->enqueue(Node);

clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,10 @@ std::unique_ptr<ExplodedGraph>
442442
ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
443443
InterExplodedGraphMap *ForwardMap,
444444
InterExplodedGraphMap *InverseMap) const {
445+
// FIXME: The two-pass algorithm of this function (which was introduced in
446+
// 2008) is terribly overcomplicated and should be replaced by a single
447+
// (backward) pass.
448+
445449
if (Nodes.empty())
446450
return nullptr;
447451

@@ -467,8 +471,9 @@ ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
467471
if (!Pass1.insert(N).second)
468472
continue;
469473

470-
// If this is a root enqueue it to the second worklist.
474+
// If this is the root enqueue it to the second worklist.
471475
if (N->Preds.empty()) {
476+
assert(N == getRoot() && "Found non-root node with no predecessors!");
472477
WL2.push_back(N);
473478
continue;
474479
}
@@ -477,12 +482,14 @@ ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
477482
WL1.append(N->Preds.begin(), N->Preds.end());
478483
}
479484

480-
// We didn't hit a root? Return with a null pointer for the new graph.
485+
// We didn't hit the root? Return with a null pointer for the new graph.
481486
if (WL2.empty())
482487
return nullptr;
483488

489+
assert(WL2.size() == 1 && "There must be only one root!");
490+
484491
// Create an empty graph.
485-
std::unique_ptr<ExplodedGraph> G = MakeEmptyGraph();
492+
std::unique_ptr<ExplodedGraph> G = std::make_unique<ExplodedGraph>();
486493

487494
// ===- Pass 2 (forward DFS to construct the new graph) -===
488495
while (!WL2.empty()) {
@@ -503,9 +510,11 @@ ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
503510
// Also record the reverse mapping from the new node to the old node.
504511
if (InverseMap) (*InverseMap)[NewN] = N;
505512

506-
// If this node is a root, designate it as such in the graph.
507-
if (N->Preds.empty())
508-
G->addRoot(NewN);
513+
// If this node is the root, designate it as such in the graph.
514+
if (N->Preds.empty()) {
515+
assert(N == getRoot());
516+
G->designateAsRoot(NewN);
517+
}
509518

510519
// In the case that some of the intended predecessors of NewN have already
511520
// been created, we should hook them up as predecessors.

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2529,7 +2529,7 @@ static const LocationContext *getInlinedLocationContext(ExplodedNode *Node,
25292529
ExplodedGraph &G) {
25302530
const LocationContext *CalleeLC = Node->getLocation().getLocationContext();
25312531
const LocationContext *RootLC =
2532-
(*G.roots_begin())->getLocation().getLocationContext();
2532+
G.getRoot()->getLocation().getLocationContext();
25332533

25342534
if (CalleeLC->getStackFrame() == RootLC->getStackFrame())
25352535
return nullptr;

0 commit comments

Comments
 (0)