xref: /src/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
148675466SDimitry Andric //===- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -------------===//
2ec2b103cSEd Schouten //
322989816SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
422989816SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
522989816SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ec2b103cSEd Schouten //
7ec2b103cSEd Schouten //===----------------------------------------------------------------------===//
8ec2b103cSEd Schouten //
9ec2b103cSEd Schouten //  This file defines the template classes ExplodedNode and ExplodedGraph,
10ec2b103cSEd Schouten //  which represent a path-sensitive, intra-procedural "exploded graph."
11ec2b103cSEd Schouten //
12ec2b103cSEd Schouten //===----------------------------------------------------------------------===//
13ec2b103cSEd Schouten 
14bca07a45SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
1548675466SDimitry Andric #include "clang/AST/Expr.h"
1648675466SDimitry Andric #include "clang/AST/ExprObjC.h"
17809500fcSDimitry Andric #include "clang/AST/ParentMap.h"
18809500fcSDimitry Andric #include "clang/AST/Stmt.h"
19519fc96cSDimitry Andric #include "clang/Analysis/CFGStmtMap.h"
2048675466SDimitry Andric #include "clang/Analysis/ProgramPoint.h"
2148675466SDimitry Andric #include "clang/Analysis/Support/BumpVector.h"
2248675466SDimitry Andric #include "clang/Basic/LLVM.h"
2356d91b49SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
2436981b17SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
2548675466SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
26809500fcSDimitry Andric #include "llvm/ADT/DenseSet.h"
2748675466SDimitry Andric #include "llvm/ADT/FoldingSet.h"
2848675466SDimitry Andric #include "llvm/ADT/PointerUnion.h"
29ec2b103cSEd Schouten #include "llvm/ADT/SmallVector.h"
3048675466SDimitry Andric #include "llvm/Support/Casting.h"
3148675466SDimitry Andric #include <cassert>
3248675466SDimitry Andric #include <memory>
33e3b55780SDimitry Andric #include <optional>
34ec2b103cSEd Schouten 
35ec2b103cSEd Schouten using namespace clang;
36bca07a45SDimitry Andric using namespace ento;
37ec2b103cSEd Schouten 
38ec2b103cSEd Schouten //===----------------------------------------------------------------------===//
39bca07a45SDimitry Andric // Cleanup.
40bca07a45SDimitry Andric //===----------------------------------------------------------------------===//
41bca07a45SDimitry Andric 
4248675466SDimitry Andric ExplodedGraph::ExplodedGraph() = default;
43dbe13110SDimitry Andric 
4448675466SDimitry Andric ExplodedGraph::~ExplodedGraph() = default;
45bca07a45SDimitry Andric 
46bca07a45SDimitry Andric //===----------------------------------------------------------------------===//
47bca07a45SDimitry Andric // Node reclamation.
48bca07a45SDimitry Andric //===----------------------------------------------------------------------===//
49bca07a45SDimitry Andric 
isInterestingLValueExpr(const Expr * Ex)50809500fcSDimitry Andric bool ExplodedGraph::isInterestingLValueExpr(const Expr *Ex) {
51809500fcSDimitry Andric   if (!Ex->isLValue())
52809500fcSDimitry Andric     return false;
53c0981da4SDimitry Andric   return isa<DeclRefExpr, MemberExpr, ObjCIvarRefExpr, ArraySubscriptExpr>(Ex);
54809500fcSDimitry Andric }
55809500fcSDimitry Andric 
shouldCollect(const ExplodedNode * node)56dbe13110SDimitry Andric bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
57809500fcSDimitry Andric   // First, we only consider nodes for reclamation of the following
58809500fcSDimitry Andric   // conditions apply:
59bca07a45SDimitry Andric   //
60bca07a45SDimitry Andric   // (1) 1 predecessor (that has one successor)
61bca07a45SDimitry Andric   // (2) 1 successor (that has one predecessor)
62809500fcSDimitry Andric   //
63809500fcSDimitry Andric   // If a node has no successor it is on the "frontier", while a node
64809500fcSDimitry Andric   // with no predecessor is a root.
65809500fcSDimitry Andric   //
66809500fcSDimitry Andric   // After these prerequisites, we discard all "filler" nodes that
67809500fcSDimitry Andric   // are used only for intermediate processing, and are not essential
68809500fcSDimitry Andric   // for analyzer history:
69809500fcSDimitry Andric   //
70809500fcSDimitry Andric   // (a) PreStmtPurgeDeadSymbols
71809500fcSDimitry Andric   //
72809500fcSDimitry Andric   // We then discard all other nodes where *all* of the following conditions
73809500fcSDimitry Andric   // apply:
74809500fcSDimitry Andric   //
7513cc256eSDimitry Andric   // (3) The ProgramPoint is for a PostStmt, but not a PostStore.
76bca07a45SDimitry Andric   // (4) There is no 'tag' for the ProgramPoint.
77bca07a45SDimitry Andric   // (5) The 'store' is the same as the predecessor.
78bca07a45SDimitry Andric   // (6) The 'GDM' is the same as the predecessor.
79bca07a45SDimitry Andric   // (7) The LocationContext is the same as the predecessor.
80809500fcSDimitry Andric   // (8) Expressions that are *not* lvalue expressions.
81809500fcSDimitry Andric   // (9) The PostStmt isn't for a non-consumed Stmt or Expr.
829f4dbff6SDimitry Andric   // (10) The successor is neither a CallExpr StmtPoint nor a CallEnter or
839f4dbff6SDimitry Andric   //      PreImplicitCall (so that we would be able to find it when retrying a
849f4dbff6SDimitry Andric   //      call with no inlining).
8556d91b49SDimitry Andric   // FIXME: It may be safe to reclaim PreCall and PostCall nodes as well.
86bca07a45SDimitry Andric 
87bca07a45SDimitry Andric   // Conditions 1 and 2.
88bca07a45SDimitry Andric   if (node->pred_size() != 1 || node->succ_size() != 1)
89dbe13110SDimitry Andric     return false;
90bca07a45SDimitry Andric 
91dbe13110SDimitry Andric   const ExplodedNode *pred = *(node->pred_begin());
92bca07a45SDimitry Andric   if (pred->succ_size() != 1)
93dbe13110SDimitry Andric     return false;
94bca07a45SDimitry Andric 
95dbe13110SDimitry Andric   const ExplodedNode *succ = *(node->succ_begin());
96bca07a45SDimitry Andric   if (succ->pred_size() != 1)
97dbe13110SDimitry Andric     return false;
98bca07a45SDimitry Andric 
99809500fcSDimitry Andric   // Now reclaim any nodes that are (by definition) not essential to
100809500fcSDimitry Andric   // analysis history and are not consulted by any client code.
101bca07a45SDimitry Andric   ProgramPoint progPoint = node->getLocation();
102809500fcSDimitry Andric   if (progPoint.getAs<PreStmtPurgeDeadSymbols>())
103809500fcSDimitry Andric     return !progPoint.getTag();
104809500fcSDimitry Andric 
105809500fcSDimitry Andric   // Condition 3.
106809500fcSDimitry Andric   if (!progPoint.getAs<PostStmt>() || progPoint.getAs<PostStore>())
107dbe13110SDimitry Andric     return false;
108dbe13110SDimitry Andric 
109bca07a45SDimitry Andric   // Condition 4.
110809500fcSDimitry Andric   if (progPoint.getTag())
111dbe13110SDimitry Andric     return false;
112bca07a45SDimitry Andric 
113bca07a45SDimitry Andric   // Conditions 5, 6, and 7.
114dbe13110SDimitry Andric   ProgramStateRef state = node->getState();
115dbe13110SDimitry Andric   ProgramStateRef pred_state = pred->getState();
116bca07a45SDimitry Andric   if (state->store != pred_state->store || state->GDM != pred_state->GDM ||
117bca07a45SDimitry Andric       progPoint.getLocationContext() != pred->getLocationContext())
118dbe13110SDimitry Andric     return false;
119bca07a45SDimitry Andric 
120809500fcSDimitry Andric   // All further checks require expressions. As per #3, we know that we have
121809500fcSDimitry Andric   // a PostStmt.
122809500fcSDimitry Andric   const Expr *Ex = dyn_cast<Expr>(progPoint.castAs<PostStmt>().getStmt());
123809500fcSDimitry Andric   if (!Ex)
124809500fcSDimitry Andric     return false;
125809500fcSDimitry Andric 
126bca07a45SDimitry Andric   // Condition 8.
127809500fcSDimitry Andric   // Do not collect nodes for "interesting" lvalue expressions since they are
128809500fcSDimitry Andric   // used extensively for generating path diagnostics.
129809500fcSDimitry Andric   if (isInterestingLValueExpr(Ex))
130809500fcSDimitry Andric     return false;
131809500fcSDimitry Andric 
132809500fcSDimitry Andric   // Condition 9.
13313cc256eSDimitry Andric   // Do not collect nodes for non-consumed Stmt or Expr to ensure precise
13413cc256eSDimitry Andric   // diagnostic generation; specifically, so that we could anchor arrows
13513cc256eSDimitry Andric   // pointing to the beginning of statements (as written in code).
136519fc96cSDimitry Andric   const ParentMap &PM = progPoint.getLocationContext()->getParentMap();
137dbe13110SDimitry Andric   if (!PM.isConsumedExpr(Ex))
138dbe13110SDimitry Andric     return false;
139bca07a45SDimitry Andric 
140809500fcSDimitry Andric   // Condition 10.
14156d91b49SDimitry Andric   const ProgramPoint SuccLoc = succ->getLocation();
142e3b55780SDimitry Andric   if (std::optional<StmtPoint> SP = SuccLoc.getAs<StmtPoint>())
14313cc256eSDimitry Andric     if (CallEvent::isCallStmt(SP->getStmt()))
14456d91b49SDimitry Andric       return false;
14556d91b49SDimitry Andric 
1469f4dbff6SDimitry Andric   // Condition 10, continuation.
1479f4dbff6SDimitry Andric   if (SuccLoc.getAs<CallEnter>() || SuccLoc.getAs<PreImplicitCall>())
1489f4dbff6SDimitry Andric     return false;
1499f4dbff6SDimitry Andric 
150dbe13110SDimitry Andric   return true;
151dbe13110SDimitry Andric }
152dbe13110SDimitry Andric 
collectNode(ExplodedNode * node)153dbe13110SDimitry Andric void ExplodedGraph::collectNode(ExplodedNode *node) {
154dbe13110SDimitry Andric   // Removing a node means:
155bca07a45SDimitry Andric   // (a) changing the predecessors successor to the successor of this node
156bca07a45SDimitry Andric   // (b) changing the successors predecessor to the predecessor of this node
157bca07a45SDimitry Andric   // (c) Putting 'node' onto freeNodes.
158dbe13110SDimitry Andric   assert(node->pred_size() == 1 || node->succ_size() == 1);
159dbe13110SDimitry Andric   ExplodedNode *pred = *(node->pred_begin());
160dbe13110SDimitry Andric   ExplodedNode *succ = *(node->succ_begin());
161bca07a45SDimitry Andric   pred->replaceSuccessor(succ);
162bca07a45SDimitry Andric   succ->replacePredecessor(pred);
163dbe13110SDimitry Andric   FreeNodes.push_back(node);
164bca07a45SDimitry Andric   Nodes.RemoveNode(node);
165bca07a45SDimitry Andric   --NumNodes;
166bca07a45SDimitry Andric   node->~ExplodedNode();
167bca07a45SDimitry Andric }
168bca07a45SDimitry Andric 
reclaimRecentlyAllocatedNodes()169dbe13110SDimitry Andric void ExplodedGraph::reclaimRecentlyAllocatedNodes() {
170dbe13110SDimitry Andric   if (ChangedNodes.empty())
171dbe13110SDimitry Andric     return;
172dbe13110SDimitry Andric 
17313cc256eSDimitry Andric   // Only periodically reclaim nodes so that we can build up a set of
174dbe13110SDimitry Andric   // nodes that meet the reclamation criteria.  Freshly created nodes
175dbe13110SDimitry Andric   // by definition have no successor, and thus cannot be reclaimed (see below).
17613cc256eSDimitry Andric   assert(ReclaimCounter > 0);
17713cc256eSDimitry Andric   if (--ReclaimCounter != 0)
178dbe13110SDimitry Andric     return;
17913cc256eSDimitry Andric   ReclaimCounter = ReclaimNodeInterval;
180dbe13110SDimitry Andric 
18148675466SDimitry Andric   for (const auto node : ChangedNodes)
182dbe13110SDimitry Andric     if (shouldCollect(node))
183dbe13110SDimitry Andric       collectNode(node);
184dbe13110SDimitry Andric   ChangedNodes.clear();
185bca07a45SDimitry Andric }
186bca07a45SDimitry Andric 
187bca07a45SDimitry Andric //===----------------------------------------------------------------------===//
1884c8b2481SRoman Divacky // ExplodedNode.
189ec2b103cSEd Schouten //===----------------------------------------------------------------------===//
190ec2b103cSEd Schouten 
19113cc256eSDimitry Andric // An NodeGroup's storage type is actually very much like a TinyPtrVector:
19213cc256eSDimitry Andric // it can be either a pointer to a single ExplodedNode, or a pointer to a
19313cc256eSDimitry Andric // BumpVector allocated with the ExplodedGraph's allocator. This allows the
19413cc256eSDimitry Andric // common case of single-node NodeGroups to be implemented with no extra memory.
19513cc256eSDimitry Andric //
19613cc256eSDimitry Andric // Consequently, each of the NodeGroup methods have up to four cases to handle:
19713cc256eSDimitry Andric // 1. The flag is set and this group does not actually contain any nodes.
19813cc256eSDimitry Andric // 2. The group is empty, in which case the storage value is null.
19913cc256eSDimitry Andric // 3. The group contains a single node.
20013cc256eSDimitry Andric // 4. The group contains more than one node.
20148675466SDimitry Andric using ExplodedNodeVector = BumpVector<ExplodedNode *>;
20248675466SDimitry Andric using GroupStorage = llvm::PointerUnion<ExplodedNode *, ExplodedNodeVector *>;
203ec2b103cSEd Schouten 
addPredecessor(ExplodedNode * V,ExplodedGraph & G)2044c8b2481SRoman Divacky void ExplodedNode::addPredecessor(ExplodedNode *V, ExplodedGraph &G) {
205ec2b103cSEd Schouten   assert(!V->isSink());
2064c8b2481SRoman Divacky   Preds.addNode(V, G);
2074c8b2481SRoman Divacky   V->Succs.addNode(this, G);
208ec2b103cSEd Schouten }
209ec2b103cSEd Schouten 
replaceNode(ExplodedNode * node)210bca07a45SDimitry Andric void ExplodedNode::NodeGroup::replaceNode(ExplodedNode *node) {
21113cc256eSDimitry Andric   assert(!getFlag());
21213cc256eSDimitry Andric 
21313cc256eSDimitry Andric   GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P);
21413cc256eSDimitry Andric   assert(Storage.is<ExplodedNode *>());
21513cc256eSDimitry Andric   Storage = node;
21613cc256eSDimitry Andric   assert(Storage.is<ExplodedNode *>());
217bca07a45SDimitry Andric }
218bca07a45SDimitry Andric 
addNode(ExplodedNode * N,ExplodedGraph & G)2194c8b2481SRoman Divacky void ExplodedNode::NodeGroup::addNode(ExplodedNode *N, ExplodedGraph &G) {
220ec2b103cSEd Schouten   assert(!getFlag());
221ec2b103cSEd Schouten 
22213cc256eSDimitry Andric   GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P);
22313cc256eSDimitry Andric   if (Storage.isNull()) {
22413cc256eSDimitry Andric     Storage = N;
22513cc256eSDimitry Andric     assert(Storage.is<ExplodedNode *>());
22613cc256eSDimitry Andric     return;
22713cc256eSDimitry Andric   }
2284c8b2481SRoman Divacky 
22913cc256eSDimitry Andric   ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>();
23013cc256eSDimitry Andric 
23113cc256eSDimitry Andric   if (!V) {
23213cc256eSDimitry Andric     // Switch from single-node to multi-node representation.
23313cc256eSDimitry Andric     ExplodedNode *Old = Storage.get<ExplodedNode *>();
23413cc256eSDimitry Andric 
23513cc256eSDimitry Andric     BumpVectorContext &Ctx = G.getNodeAllocator();
2367fa27ce4SDimitry Andric     V = new (G.getAllocator()) ExplodedNodeVector(Ctx, 4);
23713cc256eSDimitry Andric     V->push_back(Old, Ctx);
23813cc256eSDimitry Andric 
23913cc256eSDimitry Andric     Storage = V;
24013cc256eSDimitry Andric     assert(!getFlag());
24113cc256eSDimitry Andric     assert(Storage.is<ExplodedNodeVector *>());
242ec2b103cSEd Schouten   }
24313cc256eSDimitry Andric 
24413cc256eSDimitry Andric   V->push_back(N, G.getNodeAllocator());
245ec2b103cSEd Schouten }
246ec2b103cSEd Schouten 
size() const2474c8b2481SRoman Divacky unsigned ExplodedNode::NodeGroup::size() const {
248ec2b103cSEd Schouten   if (getFlag())
249ec2b103cSEd Schouten     return 0;
250ec2b103cSEd Schouten 
25113cc256eSDimitry Andric   const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P);
25213cc256eSDimitry Andric   if (Storage.isNull())
25313cc256eSDimitry Andric     return 0;
25413cc256eSDimitry Andric   if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>())
25513cc256eSDimitry Andric     return V->size();
25613cc256eSDimitry Andric   return 1;
257ec2b103cSEd Schouten }
258ec2b103cSEd Schouten 
begin() const25913cc256eSDimitry Andric ExplodedNode * const *ExplodedNode::NodeGroup::begin() const {
260ec2b103cSEd Schouten   if (getFlag())
2619f4dbff6SDimitry Andric     return nullptr;
262ec2b103cSEd Schouten 
26313cc256eSDimitry Andric   const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P);
26413cc256eSDimitry Andric   if (Storage.isNull())
2659f4dbff6SDimitry Andric     return nullptr;
26613cc256eSDimitry Andric   if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>())
26713cc256eSDimitry Andric     return V->begin();
26813cc256eSDimitry Andric   return Storage.getAddrOfPtr1();
269ec2b103cSEd Schouten }
270ec2b103cSEd Schouten 
end() const27113cc256eSDimitry Andric ExplodedNode * const *ExplodedNode::NodeGroup::end() const {
272ec2b103cSEd Schouten   if (getFlag())
2739f4dbff6SDimitry Andric     return nullptr;
274ec2b103cSEd Schouten 
27513cc256eSDimitry Andric   const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P);
27613cc256eSDimitry Andric   if (Storage.isNull())
2779f4dbff6SDimitry Andric     return nullptr;
27813cc256eSDimitry Andric   if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>())
27913cc256eSDimitry Andric     return V->end();
28013cc256eSDimitry Andric   return Storage.getAddrOfPtr1() + 1;
281ec2b103cSEd Schouten }
282ec2b103cSEd Schouten 
isTrivial() const283676fbe81SDimitry Andric bool ExplodedNode::isTrivial() const {
284676fbe81SDimitry Andric   return pred_size() == 1 && succ_size() == 1 &&
285676fbe81SDimitry Andric          getFirstPred()->getState()->getID() == getState()->getID() &&
286676fbe81SDimitry Andric          getFirstPred()->succ_size() == 1;
287676fbe81SDimitry Andric }
288676fbe81SDimitry Andric 
getCFGBlock() const289519fc96cSDimitry Andric const CFGBlock *ExplodedNode::getCFGBlock() const {
290519fc96cSDimitry Andric   ProgramPoint P = getLocation();
291519fc96cSDimitry Andric   if (auto BEP = P.getAs<BlockEntrance>())
292519fc96cSDimitry Andric     return BEP->getBlock();
293519fc96cSDimitry Andric 
294519fc96cSDimitry Andric   // Find the node's current statement in the CFG.
295519fc96cSDimitry Andric   // FIXME: getStmtForDiagnostics() does nasty things in order to provide
296519fc96cSDimitry Andric   // a valid statement for body farms, do we need this behavior here?
297519fc96cSDimitry Andric   if (const Stmt *S = getStmtForDiagnostics())
298519fc96cSDimitry Andric     return getLocationContext()
299519fc96cSDimitry Andric         ->getAnalysisDeclContext()
300519fc96cSDimitry Andric         ->getCFGStmtMap()
301519fc96cSDimitry Andric         ->getBlock(S);
302519fc96cSDimitry Andric 
303519fc96cSDimitry Andric   return nullptr;
304519fc96cSDimitry Andric }
305519fc96cSDimitry Andric 
306519fc96cSDimitry Andric static const LocationContext *
findTopAutosynthesizedParentContext(const LocationContext * LC)307519fc96cSDimitry Andric findTopAutosynthesizedParentContext(const LocationContext *LC) {
308519fc96cSDimitry Andric   assert(LC->getAnalysisDeclContext()->isBodyAutosynthesized());
309519fc96cSDimitry Andric   const LocationContext *ParentLC = LC->getParent();
310519fc96cSDimitry Andric   assert(ParentLC && "We don't start analysis from autosynthesized code");
311519fc96cSDimitry Andric   while (ParentLC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
312519fc96cSDimitry Andric     LC = ParentLC;
313519fc96cSDimitry Andric     ParentLC = LC->getParent();
314519fc96cSDimitry Andric     assert(ParentLC && "We don't start analysis from autosynthesized code");
315519fc96cSDimitry Andric   }
316519fc96cSDimitry Andric   return LC;
317519fc96cSDimitry Andric }
318519fc96cSDimitry Andric 
getStmtForDiagnostics() const319519fc96cSDimitry Andric const Stmt *ExplodedNode::getStmtForDiagnostics() const {
320519fc96cSDimitry Andric   // We cannot place diagnostics on autosynthesized code.
321519fc96cSDimitry Andric   // Put them onto the call site through which we jumped into autosynthesized
322519fc96cSDimitry Andric   // code for the first time.
323519fc96cSDimitry Andric   const LocationContext *LC = getLocationContext();
324519fc96cSDimitry Andric   if (LC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
325519fc96cSDimitry Andric     // It must be a stack frame because we only autosynthesize functions.
326519fc96cSDimitry Andric     return cast<StackFrameContext>(findTopAutosynthesizedParentContext(LC))
327519fc96cSDimitry Andric         ->getCallSite();
328519fc96cSDimitry Andric   }
329519fc96cSDimitry Andric   // Otherwise, see if the node's program point directly points to a statement.
330519fc96cSDimitry Andric   // FIXME: Refactor into a ProgramPoint method?
331519fc96cSDimitry Andric   ProgramPoint P = getLocation();
332519fc96cSDimitry Andric   if (auto SP = P.getAs<StmtPoint>())
333519fc96cSDimitry Andric     return SP->getStmt();
334519fc96cSDimitry Andric   if (auto BE = P.getAs<BlockEdge>())
335519fc96cSDimitry Andric     return BE->getSrc()->getTerminatorStmt();
336519fc96cSDimitry Andric   if (auto CE = P.getAs<CallEnter>())
337519fc96cSDimitry Andric     return CE->getCallExpr();
338519fc96cSDimitry Andric   if (auto CEE = P.getAs<CallExitEnd>())
339519fc96cSDimitry Andric     return CEE->getCalleeContext()->getCallSite();
340519fc96cSDimitry Andric   if (auto PIPP = P.getAs<PostInitializer>())
341519fc96cSDimitry Andric     return PIPP->getInitializer()->getInit();
342519fc96cSDimitry Andric   if (auto CEB = P.getAs<CallExitBegin>())
343519fc96cSDimitry Andric     return CEB->getReturnStmt();
344519fc96cSDimitry Andric   if (auto FEP = P.getAs<FunctionExitPoint>())
345519fc96cSDimitry Andric     return FEP->getStmt();
346519fc96cSDimitry Andric 
347519fc96cSDimitry Andric   return nullptr;
348519fc96cSDimitry Andric }
349519fc96cSDimitry Andric 
getNextStmtForDiagnostics() const350519fc96cSDimitry Andric const Stmt *ExplodedNode::getNextStmtForDiagnostics() const {
351519fc96cSDimitry Andric   for (const ExplodedNode *N = getFirstSucc(); N; N = N->getFirstSucc()) {
352519fc96cSDimitry Andric     if (const Stmt *S = N->getStmtForDiagnostics()) {
353519fc96cSDimitry Andric       // Check if the statement is '?' or '&&'/'||'.  These are "merges",
354519fc96cSDimitry Andric       // not actual statement points.
355519fc96cSDimitry Andric       switch (S->getStmtClass()) {
356519fc96cSDimitry Andric         case Stmt::ChooseExprClass:
357519fc96cSDimitry Andric         case Stmt::BinaryConditionalOperatorClass:
358519fc96cSDimitry Andric         case Stmt::ConditionalOperatorClass:
359519fc96cSDimitry Andric           continue;
360519fc96cSDimitry Andric         case Stmt::BinaryOperatorClass: {
361519fc96cSDimitry Andric           BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode();
362519fc96cSDimitry Andric           if (Op == BO_LAnd || Op == BO_LOr)
363519fc96cSDimitry Andric             continue;
364519fc96cSDimitry Andric           break;
365519fc96cSDimitry Andric         }
366519fc96cSDimitry Andric         default:
367519fc96cSDimitry Andric           break;
368519fc96cSDimitry Andric       }
369519fc96cSDimitry Andric       // We found the statement, so return it.
370519fc96cSDimitry Andric       return S;
371519fc96cSDimitry Andric     }
372519fc96cSDimitry Andric   }
373519fc96cSDimitry Andric 
374519fc96cSDimitry Andric   return nullptr;
375519fc96cSDimitry Andric }
376519fc96cSDimitry Andric 
getPreviousStmtForDiagnostics() const377519fc96cSDimitry Andric const Stmt *ExplodedNode::getPreviousStmtForDiagnostics() const {
378519fc96cSDimitry Andric   for (const ExplodedNode *N = getFirstPred(); N; N = N->getFirstPred())
379519fc96cSDimitry Andric     if (const Stmt *S = N->getStmtForDiagnostics())
380519fc96cSDimitry Andric       return S;
381519fc96cSDimitry Andric 
382519fc96cSDimitry Andric   return nullptr;
383519fc96cSDimitry Andric }
384519fc96cSDimitry Andric 
getCurrentOrPreviousStmtForDiagnostics() const385519fc96cSDimitry Andric const Stmt *ExplodedNode::getCurrentOrPreviousStmtForDiagnostics() const {
386519fc96cSDimitry Andric   if (const Stmt *S = getStmtForDiagnostics())
387519fc96cSDimitry Andric     return S;
388519fc96cSDimitry Andric 
389519fc96cSDimitry Andric   return getPreviousStmtForDiagnostics();
390519fc96cSDimitry Andric }
391519fc96cSDimitry Andric 
getNode(const ProgramPoint & L,ProgramStateRef State,bool IsSink,bool * IsNew)3924c8b2481SRoman Divacky ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L,
393dbe13110SDimitry Andric                                      ProgramStateRef State,
394dbe13110SDimitry Andric                                      bool IsSink,
395dbe13110SDimitry Andric                                      bool* IsNew) {
3964c8b2481SRoman Divacky   // Profile 'State' to determine if we already have an existing node.
3974c8b2481SRoman Divacky   llvm::FoldingSetNodeID profile;
3989f4dbff6SDimitry Andric   void *InsertPos = nullptr;
3994c8b2481SRoman Divacky 
400dbe13110SDimitry Andric   NodeTy::Profile(profile, L, State, IsSink);
4014c8b2481SRoman Divacky   NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos);
4024c8b2481SRoman Divacky 
4034c8b2481SRoman Divacky   if (!V) {
404dbe13110SDimitry Andric     if (!FreeNodes.empty()) {
405dbe13110SDimitry Andric       V = FreeNodes.back();
406dbe13110SDimitry Andric       FreeNodes.pop_back();
407bca07a45SDimitry Andric     }
408bca07a45SDimitry Andric     else {
4094c8b2481SRoman Divacky       // Allocate a new node.
4107fa27ce4SDimitry Andric       V = getAllocator().Allocate<NodeTy>();
411bca07a45SDimitry Andric     }
412bca07a45SDimitry Andric 
413519fc96cSDimitry Andric     ++NumNodes;
414519fc96cSDimitry Andric     new (V) NodeTy(L, State, NumNodes, IsSink);
4154c8b2481SRoman Divacky 
41613cc256eSDimitry Andric     if (ReclaimNodeInterval)
417dbe13110SDimitry Andric       ChangedNodes.push_back(V);
418bca07a45SDimitry Andric 
4194c8b2481SRoman Divacky     // Insert the node into the node set and return it.
4204c8b2481SRoman Divacky     Nodes.InsertNode(V, InsertPos);
4214c8b2481SRoman Divacky 
4224c8b2481SRoman Divacky     if (IsNew) *IsNew = true;
4234c8b2481SRoman Divacky   }
4244c8b2481SRoman Divacky   else
4254c8b2481SRoman Divacky     if (IsNew) *IsNew = false;
4264c8b2481SRoman Divacky 
4274c8b2481SRoman Divacky   return V;
428ec2b103cSEd Schouten }
429ec2b103cSEd Schouten 
createUncachedNode(const ProgramPoint & L,ProgramStateRef State,int64_t Id,bool IsSink)4302b6b257fSDimitry Andric ExplodedNode *ExplodedGraph::createUncachedNode(const ProgramPoint &L,
4312b6b257fSDimitry Andric                                                 ProgramStateRef State,
432519fc96cSDimitry Andric                                                 int64_t Id,
4332b6b257fSDimitry Andric                                                 bool IsSink) {
4347fa27ce4SDimitry Andric   NodeTy *V = getAllocator().Allocate<NodeTy>();
435519fc96cSDimitry Andric   new (V) NodeTy(L, State, Id, IsSink);
4362b6b257fSDimitry Andric   return V;
4372b6b257fSDimitry Andric }
4382b6b257fSDimitry Andric 
43906d4ba38SDimitry Andric std::unique_ptr<ExplodedGraph>
trim(ArrayRef<const NodeTy * > Sinks,InterExplodedGraphMap * ForwardMap,InterExplodedGraphMap * InverseMap) const440809500fcSDimitry Andric ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
441809500fcSDimitry Andric                     InterExplodedGraphMap *ForwardMap,
442809500fcSDimitry Andric                     InterExplodedGraphMap *InverseMap) const {
443809500fcSDimitry Andric   if (Nodes.empty())
4449f4dbff6SDimitry Andric     return nullptr;
4454c8b2481SRoman Divacky 
44648675466SDimitry Andric   using Pass1Ty = llvm::DenseSet<const ExplodedNode *>;
447ec2b103cSEd Schouten   Pass1Ty Pass1;
448ec2b103cSEd Schouten 
44948675466SDimitry Andric   using Pass2Ty = InterExplodedGraphMap;
450809500fcSDimitry Andric   InterExplodedGraphMap Pass2Scratch;
451809500fcSDimitry Andric   Pass2Ty &Pass2 = ForwardMap ? *ForwardMap : Pass2Scratch;
452ec2b103cSEd Schouten 
45336981b17SDimitry Andric   SmallVector<const ExplodedNode*, 10> WL1, WL2;
454ec2b103cSEd Schouten 
455ec2b103cSEd Schouten   // ===- Pass 1 (reverse DFS) -===
45648675466SDimitry Andric   for (const auto Sink : Sinks)
45748675466SDimitry Andric     if (Sink)
45848675466SDimitry Andric       WL1.push_back(Sink);
459ec2b103cSEd Schouten 
460809500fcSDimitry Andric   // Process the first worklist until it is empty.
461ec2b103cSEd Schouten   while (!WL1.empty()) {
462bfef3995SDimitry Andric     const ExplodedNode *N = WL1.pop_back_val();
463ec2b103cSEd Schouten 
464ec2b103cSEd Schouten     // Have we already visited this node?  If so, continue to the next one.
46506d4ba38SDimitry Andric     if (!Pass1.insert(N).second)
466ec2b103cSEd Schouten       continue;
467ec2b103cSEd Schouten 
468ec2b103cSEd Schouten     // If this is a root enqueue it to the second worklist.
469ec2b103cSEd Schouten     if (N->Preds.empty()) {
470ec2b103cSEd Schouten       WL2.push_back(N);
471ec2b103cSEd Schouten       continue;
472ec2b103cSEd Schouten     }
473ec2b103cSEd Schouten 
474ec2b103cSEd Schouten     // Visit our predecessors and enqueue them.
47506d4ba38SDimitry Andric     WL1.append(N->Preds.begin(), N->Preds.end());
476ec2b103cSEd Schouten   }
477ec2b103cSEd Schouten 
478ec2b103cSEd Schouten   // We didn't hit a root? Return with a null pointer for the new graph.
479ec2b103cSEd Schouten   if (WL2.empty())
4809f4dbff6SDimitry Andric     return nullptr;
481ec2b103cSEd Schouten 
482ec2b103cSEd Schouten   // Create an empty graph.
48306d4ba38SDimitry Andric   std::unique_ptr<ExplodedGraph> G = MakeEmptyGraph();
484ec2b103cSEd Schouten 
485ec2b103cSEd Schouten   // ===- Pass 2 (forward DFS to construct the new graph) -===
486ec2b103cSEd Schouten   while (!WL2.empty()) {
487bfef3995SDimitry Andric     const ExplodedNode *N = WL2.pop_back_val();
488ec2b103cSEd Schouten 
489ec2b103cSEd Schouten     // Skip this node if we have already processed it.
4907fa27ce4SDimitry Andric     if (Pass2.contains(N))
491ec2b103cSEd Schouten       continue;
492ec2b103cSEd Schouten 
493ec2b103cSEd Schouten     // Create the corresponding node in the new graph and record the mapping
494ec2b103cSEd Schouten     // from the old node to the new node.
495519fc96cSDimitry Andric     ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State,
496519fc96cSDimitry Andric                                                N->getID(), N->isSink());
497ec2b103cSEd Schouten     Pass2[N] = NewN;
498ec2b103cSEd Schouten 
499ec2b103cSEd Schouten     // Also record the reverse mapping from the new node to the old node.
500ec2b103cSEd Schouten     if (InverseMap) (*InverseMap)[NewN] = N;
501ec2b103cSEd Schouten 
502ec2b103cSEd Schouten     // If this node is a root, designate it as such in the graph.
503ec2b103cSEd Schouten     if (N->Preds.empty())
504ec2b103cSEd Schouten       G->addRoot(NewN);
505ec2b103cSEd Schouten 
506ec2b103cSEd Schouten     // In the case that some of the intended predecessors of NewN have already
507ec2b103cSEd Schouten     // been created, we should hook them up as predecessors.
508ec2b103cSEd Schouten 
509ec2b103cSEd Schouten     // Walk through the predecessors of 'N' and hook up their corresponding
510ec2b103cSEd Schouten     // nodes in the new graph (if any) to the freshly created node.
5117fa27ce4SDimitry Andric     for (const ExplodedNode *Pred : N->Preds) {
5127fa27ce4SDimitry Andric       Pass2Ty::iterator PI = Pass2.find(Pred);
513ec2b103cSEd Schouten       if (PI == Pass2.end())
514ec2b103cSEd Schouten         continue;
515ec2b103cSEd Schouten 
516809500fcSDimitry Andric       NewN->addPredecessor(const_cast<ExplodedNode *>(PI->second), *G);
517ec2b103cSEd Schouten     }
518ec2b103cSEd Schouten 
519ec2b103cSEd Schouten     // In the case that some of the intended successors of NewN have already
520ec2b103cSEd Schouten     // been created, we should hook them up as successors.  Otherwise, enqueue
521ec2b103cSEd Schouten     // the new nodes from the original graph that should have nodes created
522ec2b103cSEd Schouten     // in the new graph.
5237fa27ce4SDimitry Andric     for (const ExplodedNode *Succ : N->Succs) {
5247fa27ce4SDimitry Andric       Pass2Ty::iterator PI = Pass2.find(Succ);
525ec2b103cSEd Schouten       if (PI != Pass2.end()) {
526809500fcSDimitry Andric         const_cast<ExplodedNode *>(PI->second)->addPredecessor(NewN, *G);
527ec2b103cSEd Schouten         continue;
528ec2b103cSEd Schouten       }
529ec2b103cSEd Schouten 
530ec2b103cSEd Schouten       // Enqueue nodes to the worklist that were marked during pass 1.
5317fa27ce4SDimitry Andric       if (Pass1.count(Succ))
5327fa27ce4SDimitry Andric         WL2.push_back(Succ);
533ec2b103cSEd Schouten     }
534ec2b103cSEd Schouten   }
535ec2b103cSEd Schouten 
536ec2b103cSEd Schouten   return G;
537ec2b103cSEd Schouten }
538