xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
10b57cec5SDimitry Andric //===- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric //  This file defines the template classes ExplodedNode and ExplodedGraph,
100b57cec5SDimitry Andric //  which represent a path-sensitive, intra-procedural "exploded graph."
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
150b57cec5SDimitry Andric #include "clang/AST/Expr.h"
160b57cec5SDimitry Andric #include "clang/AST/ExprObjC.h"
170b57cec5SDimitry Andric #include "clang/AST/ParentMap.h"
180b57cec5SDimitry Andric #include "clang/AST/Stmt.h"
19a7dea167SDimitry Andric #include "clang/Analysis/CFGStmtMap.h"
200b57cec5SDimitry Andric #include "clang/Analysis/ProgramPoint.h"
210b57cec5SDimitry Andric #include "clang/Analysis/Support/BumpVector.h"
220b57cec5SDimitry Andric #include "clang/Basic/LLVM.h"
230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
240b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
250b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
260b57cec5SDimitry Andric #include "llvm/ADT/DenseSet.h"
270b57cec5SDimitry Andric #include "llvm/ADT/FoldingSet.h"
280b57cec5SDimitry Andric #include "llvm/ADT/PointerUnion.h"
290b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
300b57cec5SDimitry Andric #include "llvm/Support/Casting.h"
310b57cec5SDimitry Andric #include <cassert>
320b57cec5SDimitry Andric #include <memory>
33bdd1243dSDimitry Andric #include <optional>
340b57cec5SDimitry Andric 
350b57cec5SDimitry Andric using namespace clang;
360b57cec5SDimitry Andric using namespace ento;
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
390b57cec5SDimitry Andric // Cleanup.
400b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
410b57cec5SDimitry Andric 
420b57cec5SDimitry Andric ExplodedGraph::ExplodedGraph() = default;
430b57cec5SDimitry Andric 
440b57cec5SDimitry Andric ExplodedGraph::~ExplodedGraph() = default;
450b57cec5SDimitry Andric 
460b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
470b57cec5SDimitry Andric // Node reclamation.
480b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
490b57cec5SDimitry Andric 
isInterestingLValueExpr(const Expr * Ex)500b57cec5SDimitry Andric bool ExplodedGraph::isInterestingLValueExpr(const Expr *Ex) {
510b57cec5SDimitry Andric   if (!Ex->isLValue())
520b57cec5SDimitry Andric     return false;
53349cc55cSDimitry Andric   return isa<DeclRefExpr, MemberExpr, ObjCIvarRefExpr, ArraySubscriptExpr>(Ex);
540b57cec5SDimitry Andric }
550b57cec5SDimitry Andric 
shouldCollect(const ExplodedNode * node)560b57cec5SDimitry Andric bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
570b57cec5SDimitry Andric   // First, we only consider nodes for reclamation of the following
580b57cec5SDimitry Andric   // conditions apply:
590b57cec5SDimitry Andric   //
600b57cec5SDimitry Andric   // (1) 1 predecessor (that has one successor)
610b57cec5SDimitry Andric   // (2) 1 successor (that has one predecessor)
620b57cec5SDimitry Andric   //
630b57cec5SDimitry Andric   // If a node has no successor it is on the "frontier", while a node
640b57cec5SDimitry Andric   // with no predecessor is a root.
650b57cec5SDimitry Andric   //
660b57cec5SDimitry Andric   // After these prerequisites, we discard all "filler" nodes that
670b57cec5SDimitry Andric   // are used only for intermediate processing, and are not essential
680b57cec5SDimitry Andric   // for analyzer history:
690b57cec5SDimitry Andric   //
700b57cec5SDimitry Andric   // (a) PreStmtPurgeDeadSymbols
710b57cec5SDimitry Andric   //
720b57cec5SDimitry Andric   // We then discard all other nodes where *all* of the following conditions
730b57cec5SDimitry Andric   // apply:
740b57cec5SDimitry Andric   //
750b57cec5SDimitry Andric   // (3) The ProgramPoint is for a PostStmt, but not a PostStore.
760b57cec5SDimitry Andric   // (4) There is no 'tag' for the ProgramPoint.
770b57cec5SDimitry Andric   // (5) The 'store' is the same as the predecessor.
780b57cec5SDimitry Andric   // (6) The 'GDM' is the same as the predecessor.
790b57cec5SDimitry Andric   // (7) The LocationContext is the same as the predecessor.
800b57cec5SDimitry Andric   // (8) Expressions that are *not* lvalue expressions.
810b57cec5SDimitry Andric   // (9) The PostStmt isn't for a non-consumed Stmt or Expr.
820b57cec5SDimitry Andric   // (10) The successor is neither a CallExpr StmtPoint nor a CallEnter or
830b57cec5SDimitry Andric   //      PreImplicitCall (so that we would be able to find it when retrying a
840b57cec5SDimitry Andric   //      call with no inlining).
850b57cec5SDimitry Andric   // FIXME: It may be safe to reclaim PreCall and PostCall nodes as well.
860b57cec5SDimitry Andric 
870b57cec5SDimitry Andric   // Conditions 1 and 2.
880b57cec5SDimitry Andric   if (node->pred_size() != 1 || node->succ_size() != 1)
890b57cec5SDimitry Andric     return false;
900b57cec5SDimitry Andric 
910b57cec5SDimitry Andric   const ExplodedNode *pred = *(node->pred_begin());
920b57cec5SDimitry Andric   if (pred->succ_size() != 1)
930b57cec5SDimitry Andric     return false;
940b57cec5SDimitry Andric 
950b57cec5SDimitry Andric   const ExplodedNode *succ = *(node->succ_begin());
960b57cec5SDimitry Andric   if (succ->pred_size() != 1)
970b57cec5SDimitry Andric     return false;
980b57cec5SDimitry Andric 
990b57cec5SDimitry Andric   // Now reclaim any nodes that are (by definition) not essential to
1000b57cec5SDimitry Andric   // analysis history and are not consulted by any client code.
1010b57cec5SDimitry Andric   ProgramPoint progPoint = node->getLocation();
1020b57cec5SDimitry Andric   if (progPoint.getAs<PreStmtPurgeDeadSymbols>())
1030b57cec5SDimitry Andric     return !progPoint.getTag();
1040b57cec5SDimitry Andric 
1050b57cec5SDimitry Andric   // Condition 3.
1060b57cec5SDimitry Andric   if (!progPoint.getAs<PostStmt>() || progPoint.getAs<PostStore>())
1070b57cec5SDimitry Andric     return false;
1080b57cec5SDimitry Andric 
1090b57cec5SDimitry Andric   // Condition 4.
1100b57cec5SDimitry Andric   if (progPoint.getTag())
1110b57cec5SDimitry Andric     return false;
1120b57cec5SDimitry Andric 
1130b57cec5SDimitry Andric   // Conditions 5, 6, and 7.
1140b57cec5SDimitry Andric   ProgramStateRef state = node->getState();
1150b57cec5SDimitry Andric   ProgramStateRef pred_state = pred->getState();
1160b57cec5SDimitry Andric   if (state->store != pred_state->store || state->GDM != pred_state->GDM ||
1170b57cec5SDimitry Andric       progPoint.getLocationContext() != pred->getLocationContext())
1180b57cec5SDimitry Andric     return false;
1190b57cec5SDimitry Andric 
1200b57cec5SDimitry Andric   // All further checks require expressions. As per #3, we know that we have
1210b57cec5SDimitry Andric   // a PostStmt.
1220b57cec5SDimitry Andric   const Expr *Ex = dyn_cast<Expr>(progPoint.castAs<PostStmt>().getStmt());
1230b57cec5SDimitry Andric   if (!Ex)
1240b57cec5SDimitry Andric     return false;
1250b57cec5SDimitry Andric 
1260b57cec5SDimitry Andric   // Condition 8.
1270b57cec5SDimitry Andric   // Do not collect nodes for "interesting" lvalue expressions since they are
1280b57cec5SDimitry Andric   // used extensively for generating path diagnostics.
1290b57cec5SDimitry Andric   if (isInterestingLValueExpr(Ex))
1300b57cec5SDimitry Andric     return false;
1310b57cec5SDimitry Andric 
1320b57cec5SDimitry Andric   // Condition 9.
1330b57cec5SDimitry Andric   // Do not collect nodes for non-consumed Stmt or Expr to ensure precise
1340b57cec5SDimitry Andric   // diagnostic generation; specifically, so that we could anchor arrows
1350b57cec5SDimitry Andric   // pointing to the beginning of statements (as written in code).
136a7dea167SDimitry Andric   const ParentMap &PM = progPoint.getLocationContext()->getParentMap();
1370b57cec5SDimitry Andric   if (!PM.isConsumedExpr(Ex))
1380b57cec5SDimitry Andric     return false;
1390b57cec5SDimitry Andric 
1400b57cec5SDimitry Andric   // Condition 10.
1410b57cec5SDimitry Andric   const ProgramPoint SuccLoc = succ->getLocation();
142bdd1243dSDimitry Andric   if (std::optional<StmtPoint> SP = SuccLoc.getAs<StmtPoint>())
1430b57cec5SDimitry Andric     if (CallEvent::isCallStmt(SP->getStmt()))
1440b57cec5SDimitry Andric       return false;
1450b57cec5SDimitry Andric 
1460b57cec5SDimitry Andric   // Condition 10, continuation.
1470b57cec5SDimitry Andric   if (SuccLoc.getAs<CallEnter>() || SuccLoc.getAs<PreImplicitCall>())
1480b57cec5SDimitry Andric     return false;
1490b57cec5SDimitry Andric 
1500b57cec5SDimitry Andric   return true;
1510b57cec5SDimitry Andric }
1520b57cec5SDimitry Andric 
collectNode(ExplodedNode * node)1530b57cec5SDimitry Andric void ExplodedGraph::collectNode(ExplodedNode *node) {
1540b57cec5SDimitry Andric   // Removing a node means:
1550b57cec5SDimitry Andric   // (a) changing the predecessors successor to the successor of this node
1560b57cec5SDimitry Andric   // (b) changing the successors predecessor to the predecessor of this node
1570b57cec5SDimitry Andric   // (c) Putting 'node' onto freeNodes.
1580b57cec5SDimitry Andric   assert(node->pred_size() == 1 || node->succ_size() == 1);
1590b57cec5SDimitry Andric   ExplodedNode *pred = *(node->pred_begin());
1600b57cec5SDimitry Andric   ExplodedNode *succ = *(node->succ_begin());
1610b57cec5SDimitry Andric   pred->replaceSuccessor(succ);
1620b57cec5SDimitry Andric   succ->replacePredecessor(pred);
1630b57cec5SDimitry Andric   FreeNodes.push_back(node);
1640b57cec5SDimitry Andric   Nodes.RemoveNode(node);
1650b57cec5SDimitry Andric   --NumNodes;
1660b57cec5SDimitry Andric   node->~ExplodedNode();
1670b57cec5SDimitry Andric }
1680b57cec5SDimitry Andric 
reclaimRecentlyAllocatedNodes()1690b57cec5SDimitry Andric void ExplodedGraph::reclaimRecentlyAllocatedNodes() {
1700b57cec5SDimitry Andric   if (ChangedNodes.empty())
1710b57cec5SDimitry Andric     return;
1720b57cec5SDimitry Andric 
1730b57cec5SDimitry Andric   // Only periodically reclaim nodes so that we can build up a set of
1740b57cec5SDimitry Andric   // nodes that meet the reclamation criteria.  Freshly created nodes
1750b57cec5SDimitry Andric   // by definition have no successor, and thus cannot be reclaimed (see below).
1760b57cec5SDimitry Andric   assert(ReclaimCounter > 0);
1770b57cec5SDimitry Andric   if (--ReclaimCounter != 0)
1780b57cec5SDimitry Andric     return;
1790b57cec5SDimitry Andric   ReclaimCounter = ReclaimNodeInterval;
1800b57cec5SDimitry Andric 
1810b57cec5SDimitry Andric   for (const auto node : ChangedNodes)
1820b57cec5SDimitry Andric     if (shouldCollect(node))
1830b57cec5SDimitry Andric       collectNode(node);
1840b57cec5SDimitry Andric   ChangedNodes.clear();
1850b57cec5SDimitry Andric }
1860b57cec5SDimitry Andric 
1870b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1880b57cec5SDimitry Andric // ExplodedNode.
1890b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1900b57cec5SDimitry Andric 
1910b57cec5SDimitry Andric // An NodeGroup's storage type is actually very much like a TinyPtrVector:
1920b57cec5SDimitry Andric // it can be either a pointer to a single ExplodedNode, or a pointer to a
1930b57cec5SDimitry Andric // BumpVector allocated with the ExplodedGraph's allocator. This allows the
1940b57cec5SDimitry Andric // common case of single-node NodeGroups to be implemented with no extra memory.
1950b57cec5SDimitry Andric //
1960b57cec5SDimitry Andric // Consequently, each of the NodeGroup methods have up to four cases to handle:
1970b57cec5SDimitry Andric // 1. The flag is set and this group does not actually contain any nodes.
1980b57cec5SDimitry Andric // 2. The group is empty, in which case the storage value is null.
1990b57cec5SDimitry Andric // 3. The group contains a single node.
2000b57cec5SDimitry Andric // 4. The group contains more than one node.
2010b57cec5SDimitry Andric using ExplodedNodeVector = BumpVector<ExplodedNode *>;
2020b57cec5SDimitry Andric using GroupStorage = llvm::PointerUnion<ExplodedNode *, ExplodedNodeVector *>;
2030b57cec5SDimitry Andric 
addPredecessor(ExplodedNode * V,ExplodedGraph & G)2040b57cec5SDimitry Andric void ExplodedNode::addPredecessor(ExplodedNode *V, ExplodedGraph &G) {
2050b57cec5SDimitry Andric   assert(!V->isSink());
2060b57cec5SDimitry Andric   Preds.addNode(V, G);
2070b57cec5SDimitry Andric   V->Succs.addNode(this, G);
2080b57cec5SDimitry Andric }
2090b57cec5SDimitry Andric 
replaceNode(ExplodedNode * node)2100b57cec5SDimitry Andric void ExplodedNode::NodeGroup::replaceNode(ExplodedNode *node) {
2110b57cec5SDimitry Andric   assert(!getFlag());
2120b57cec5SDimitry Andric 
2130b57cec5SDimitry Andric   GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P);
2140b57cec5SDimitry Andric   assert(Storage.is<ExplodedNode *>());
2150b57cec5SDimitry Andric   Storage = node;
2160b57cec5SDimitry Andric   assert(Storage.is<ExplodedNode *>());
2170b57cec5SDimitry Andric }
2180b57cec5SDimitry Andric 
addNode(ExplodedNode * N,ExplodedGraph & G)2190b57cec5SDimitry Andric void ExplodedNode::NodeGroup::addNode(ExplodedNode *N, ExplodedGraph &G) {
2200b57cec5SDimitry Andric   assert(!getFlag());
2210b57cec5SDimitry Andric 
2220b57cec5SDimitry Andric   GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P);
2230b57cec5SDimitry Andric   if (Storage.isNull()) {
2240b57cec5SDimitry Andric     Storage = N;
2250b57cec5SDimitry Andric     assert(Storage.is<ExplodedNode *>());
2260b57cec5SDimitry Andric     return;
2270b57cec5SDimitry Andric   }
2280b57cec5SDimitry Andric 
2290b57cec5SDimitry Andric   ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>();
2300b57cec5SDimitry Andric 
2310b57cec5SDimitry Andric   if (!V) {
2320b57cec5SDimitry Andric     // Switch from single-node to multi-node representation.
2330b57cec5SDimitry Andric     ExplodedNode *Old = Storage.get<ExplodedNode *>();
2340b57cec5SDimitry Andric 
2350b57cec5SDimitry Andric     BumpVectorContext &Ctx = G.getNodeAllocator();
236*06c3fb27SDimitry Andric     V = new (G.getAllocator()) ExplodedNodeVector(Ctx, 4);
2370b57cec5SDimitry Andric     V->push_back(Old, Ctx);
2380b57cec5SDimitry Andric 
2390b57cec5SDimitry Andric     Storage = V;
2400b57cec5SDimitry Andric     assert(!getFlag());
2410b57cec5SDimitry Andric     assert(Storage.is<ExplodedNodeVector *>());
2420b57cec5SDimitry Andric   }
2430b57cec5SDimitry Andric 
2440b57cec5SDimitry Andric   V->push_back(N, G.getNodeAllocator());
2450b57cec5SDimitry Andric }
2460b57cec5SDimitry Andric 
size() const2470b57cec5SDimitry Andric unsigned ExplodedNode::NodeGroup::size() const {
2480b57cec5SDimitry Andric   if (getFlag())
2490b57cec5SDimitry Andric     return 0;
2500b57cec5SDimitry Andric 
2510b57cec5SDimitry Andric   const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P);
2520b57cec5SDimitry Andric   if (Storage.isNull())
2530b57cec5SDimitry Andric     return 0;
2540b57cec5SDimitry Andric   if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>())
2550b57cec5SDimitry Andric     return V->size();
2560b57cec5SDimitry Andric   return 1;
2570b57cec5SDimitry Andric }
2580b57cec5SDimitry Andric 
begin() const2590b57cec5SDimitry Andric ExplodedNode * const *ExplodedNode::NodeGroup::begin() const {
2600b57cec5SDimitry Andric   if (getFlag())
2610b57cec5SDimitry Andric     return nullptr;
2620b57cec5SDimitry Andric 
2630b57cec5SDimitry Andric   const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P);
2640b57cec5SDimitry Andric   if (Storage.isNull())
2650b57cec5SDimitry Andric     return nullptr;
2660b57cec5SDimitry Andric   if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>())
2670b57cec5SDimitry Andric     return V->begin();
2680b57cec5SDimitry Andric   return Storage.getAddrOfPtr1();
2690b57cec5SDimitry Andric }
2700b57cec5SDimitry Andric 
end() const2710b57cec5SDimitry Andric ExplodedNode * const *ExplodedNode::NodeGroup::end() const {
2720b57cec5SDimitry Andric   if (getFlag())
2730b57cec5SDimitry Andric     return nullptr;
2740b57cec5SDimitry Andric 
2750b57cec5SDimitry Andric   const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P);
2760b57cec5SDimitry Andric   if (Storage.isNull())
2770b57cec5SDimitry Andric     return nullptr;
2780b57cec5SDimitry Andric   if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>())
2790b57cec5SDimitry Andric     return V->end();
2800b57cec5SDimitry Andric   return Storage.getAddrOfPtr1() + 1;
2810b57cec5SDimitry Andric }
2820b57cec5SDimitry Andric 
isTrivial() const2830b57cec5SDimitry Andric bool ExplodedNode::isTrivial() const {
2840b57cec5SDimitry Andric   return pred_size() == 1 && succ_size() == 1 &&
2850b57cec5SDimitry Andric          getFirstPred()->getState()->getID() == getState()->getID() &&
2860b57cec5SDimitry Andric          getFirstPred()->succ_size() == 1;
2870b57cec5SDimitry Andric }
2880b57cec5SDimitry Andric 
getCFGBlock() const289a7dea167SDimitry Andric const CFGBlock *ExplodedNode::getCFGBlock() const {
290a7dea167SDimitry Andric   ProgramPoint P = getLocation();
291a7dea167SDimitry Andric   if (auto BEP = P.getAs<BlockEntrance>())
292a7dea167SDimitry Andric     return BEP->getBlock();
293a7dea167SDimitry Andric 
294a7dea167SDimitry Andric   // Find the node's current statement in the CFG.
295a7dea167SDimitry Andric   // FIXME: getStmtForDiagnostics() does nasty things in order to provide
296a7dea167SDimitry Andric   // a valid statement for body farms, do we need this behavior here?
297a7dea167SDimitry Andric   if (const Stmt *S = getStmtForDiagnostics())
298a7dea167SDimitry Andric     return getLocationContext()
299a7dea167SDimitry Andric         ->getAnalysisDeclContext()
300a7dea167SDimitry Andric         ->getCFGStmtMap()
301a7dea167SDimitry Andric         ->getBlock(S);
302a7dea167SDimitry Andric 
303a7dea167SDimitry Andric   return nullptr;
304a7dea167SDimitry Andric }
305a7dea167SDimitry Andric 
306a7dea167SDimitry Andric static const LocationContext *
findTopAutosynthesizedParentContext(const LocationContext * LC)307a7dea167SDimitry Andric findTopAutosynthesizedParentContext(const LocationContext *LC) {
308a7dea167SDimitry Andric   assert(LC->getAnalysisDeclContext()->isBodyAutosynthesized());
309a7dea167SDimitry Andric   const LocationContext *ParentLC = LC->getParent();
310a7dea167SDimitry Andric   assert(ParentLC && "We don't start analysis from autosynthesized code");
311a7dea167SDimitry Andric   while (ParentLC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
312a7dea167SDimitry Andric     LC = ParentLC;
313a7dea167SDimitry Andric     ParentLC = LC->getParent();
314a7dea167SDimitry Andric     assert(ParentLC && "We don't start analysis from autosynthesized code");
315a7dea167SDimitry Andric   }
316a7dea167SDimitry Andric   return LC;
317a7dea167SDimitry Andric }
318a7dea167SDimitry Andric 
getStmtForDiagnostics() const319a7dea167SDimitry Andric const Stmt *ExplodedNode::getStmtForDiagnostics() const {
320a7dea167SDimitry Andric   // We cannot place diagnostics on autosynthesized code.
321a7dea167SDimitry Andric   // Put them onto the call site through which we jumped into autosynthesized
322a7dea167SDimitry Andric   // code for the first time.
323a7dea167SDimitry Andric   const LocationContext *LC = getLocationContext();
324a7dea167SDimitry Andric   if (LC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
325a7dea167SDimitry Andric     // It must be a stack frame because we only autosynthesize functions.
326a7dea167SDimitry Andric     return cast<StackFrameContext>(findTopAutosynthesizedParentContext(LC))
327a7dea167SDimitry Andric         ->getCallSite();
328a7dea167SDimitry Andric   }
329a7dea167SDimitry Andric   // Otherwise, see if the node's program point directly points to a statement.
330a7dea167SDimitry Andric   // FIXME: Refactor into a ProgramPoint method?
331a7dea167SDimitry Andric   ProgramPoint P = getLocation();
332a7dea167SDimitry Andric   if (auto SP = P.getAs<StmtPoint>())
333a7dea167SDimitry Andric     return SP->getStmt();
334a7dea167SDimitry Andric   if (auto BE = P.getAs<BlockEdge>())
335a7dea167SDimitry Andric     return BE->getSrc()->getTerminatorStmt();
336a7dea167SDimitry Andric   if (auto CE = P.getAs<CallEnter>())
337a7dea167SDimitry Andric     return CE->getCallExpr();
338a7dea167SDimitry Andric   if (auto CEE = P.getAs<CallExitEnd>())
339a7dea167SDimitry Andric     return CEE->getCalleeContext()->getCallSite();
340a7dea167SDimitry Andric   if (auto PIPP = P.getAs<PostInitializer>())
341a7dea167SDimitry Andric     return PIPP->getInitializer()->getInit();
342a7dea167SDimitry Andric   if (auto CEB = P.getAs<CallExitBegin>())
343a7dea167SDimitry Andric     return CEB->getReturnStmt();
344a7dea167SDimitry Andric   if (auto FEP = P.getAs<FunctionExitPoint>())
345a7dea167SDimitry Andric     return FEP->getStmt();
346a7dea167SDimitry Andric 
347a7dea167SDimitry Andric   return nullptr;
348a7dea167SDimitry Andric }
349a7dea167SDimitry Andric 
getNextStmtForDiagnostics() const350a7dea167SDimitry Andric const Stmt *ExplodedNode::getNextStmtForDiagnostics() const {
351a7dea167SDimitry Andric   for (const ExplodedNode *N = getFirstSucc(); N; N = N->getFirstSucc()) {
352a7dea167SDimitry Andric     if (const Stmt *S = N->getStmtForDiagnostics()) {
353a7dea167SDimitry Andric       // Check if the statement is '?' or '&&'/'||'.  These are "merges",
354a7dea167SDimitry Andric       // not actual statement points.
355a7dea167SDimitry Andric       switch (S->getStmtClass()) {
356a7dea167SDimitry Andric         case Stmt::ChooseExprClass:
357a7dea167SDimitry Andric         case Stmt::BinaryConditionalOperatorClass:
358a7dea167SDimitry Andric         case Stmt::ConditionalOperatorClass:
359a7dea167SDimitry Andric           continue;
360a7dea167SDimitry Andric         case Stmt::BinaryOperatorClass: {
361a7dea167SDimitry Andric           BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode();
362a7dea167SDimitry Andric           if (Op == BO_LAnd || Op == BO_LOr)
363a7dea167SDimitry Andric             continue;
364a7dea167SDimitry Andric           break;
365a7dea167SDimitry Andric         }
366a7dea167SDimitry Andric         default:
367a7dea167SDimitry Andric           break;
368a7dea167SDimitry Andric       }
369a7dea167SDimitry Andric       // We found the statement, so return it.
370a7dea167SDimitry Andric       return S;
371a7dea167SDimitry Andric     }
372a7dea167SDimitry Andric   }
373a7dea167SDimitry Andric 
374a7dea167SDimitry Andric   return nullptr;
375a7dea167SDimitry Andric }
376a7dea167SDimitry Andric 
getPreviousStmtForDiagnostics() const377a7dea167SDimitry Andric const Stmt *ExplodedNode::getPreviousStmtForDiagnostics() const {
378a7dea167SDimitry Andric   for (const ExplodedNode *N = getFirstPred(); N; N = N->getFirstPred())
379a7dea167SDimitry Andric     if (const Stmt *S = N->getStmtForDiagnostics())
380a7dea167SDimitry Andric       return S;
381a7dea167SDimitry Andric 
382a7dea167SDimitry Andric   return nullptr;
383a7dea167SDimitry Andric }
384a7dea167SDimitry Andric 
getCurrentOrPreviousStmtForDiagnostics() const385a7dea167SDimitry Andric const Stmt *ExplodedNode::getCurrentOrPreviousStmtForDiagnostics() const {
386a7dea167SDimitry Andric   if (const Stmt *S = getStmtForDiagnostics())
387a7dea167SDimitry Andric     return S;
388a7dea167SDimitry Andric 
389a7dea167SDimitry Andric   return getPreviousStmtForDiagnostics();
390a7dea167SDimitry Andric }
391a7dea167SDimitry Andric 
getNode(const ProgramPoint & L,ProgramStateRef State,bool IsSink,bool * IsNew)3920b57cec5SDimitry Andric ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L,
3930b57cec5SDimitry Andric                                      ProgramStateRef State,
3940b57cec5SDimitry Andric                                      bool IsSink,
3950b57cec5SDimitry Andric                                      bool* IsNew) {
3960b57cec5SDimitry Andric   // Profile 'State' to determine if we already have an existing node.
3970b57cec5SDimitry Andric   llvm::FoldingSetNodeID profile;
3980b57cec5SDimitry Andric   void *InsertPos = nullptr;
3990b57cec5SDimitry Andric 
4000b57cec5SDimitry Andric   NodeTy::Profile(profile, L, State, IsSink);
4010b57cec5SDimitry Andric   NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos);
4020b57cec5SDimitry Andric 
4030b57cec5SDimitry Andric   if (!V) {
4040b57cec5SDimitry Andric     if (!FreeNodes.empty()) {
4050b57cec5SDimitry Andric       V = FreeNodes.back();
4060b57cec5SDimitry Andric       FreeNodes.pop_back();
4070b57cec5SDimitry Andric     }
4080b57cec5SDimitry Andric     else {
4090b57cec5SDimitry Andric       // Allocate a new node.
410*06c3fb27SDimitry Andric       V = getAllocator().Allocate<NodeTy>();
4110b57cec5SDimitry Andric     }
4120b57cec5SDimitry Andric 
413a7dea167SDimitry Andric     ++NumNodes;
414a7dea167SDimitry Andric     new (V) NodeTy(L, State, NumNodes, IsSink);
4150b57cec5SDimitry Andric 
4160b57cec5SDimitry Andric     if (ReclaimNodeInterval)
4170b57cec5SDimitry Andric       ChangedNodes.push_back(V);
4180b57cec5SDimitry Andric 
4190b57cec5SDimitry Andric     // Insert the node into the node set and return it.
4200b57cec5SDimitry Andric     Nodes.InsertNode(V, InsertPos);
4210b57cec5SDimitry Andric 
4220b57cec5SDimitry Andric     if (IsNew) *IsNew = true;
4230b57cec5SDimitry Andric   }
4240b57cec5SDimitry Andric   else
4250b57cec5SDimitry Andric     if (IsNew) *IsNew = false;
4260b57cec5SDimitry Andric 
4270b57cec5SDimitry Andric   return V;
4280b57cec5SDimitry Andric }
4290b57cec5SDimitry Andric 
createUncachedNode(const ProgramPoint & L,ProgramStateRef State,int64_t Id,bool IsSink)4300b57cec5SDimitry Andric ExplodedNode *ExplodedGraph::createUncachedNode(const ProgramPoint &L,
4310b57cec5SDimitry Andric                                                 ProgramStateRef State,
432a7dea167SDimitry Andric                                                 int64_t Id,
4330b57cec5SDimitry Andric                                                 bool IsSink) {
434*06c3fb27SDimitry Andric   NodeTy *V = getAllocator().Allocate<NodeTy>();
435a7dea167SDimitry Andric   new (V) NodeTy(L, State, Id, IsSink);
4360b57cec5SDimitry Andric   return V;
4370b57cec5SDimitry Andric }
4380b57cec5SDimitry Andric 
4390b57cec5SDimitry Andric std::unique_ptr<ExplodedGraph>
trim(ArrayRef<const NodeTy * > Sinks,InterExplodedGraphMap * ForwardMap,InterExplodedGraphMap * InverseMap) const4400b57cec5SDimitry Andric ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
4410b57cec5SDimitry Andric                     InterExplodedGraphMap *ForwardMap,
4420b57cec5SDimitry Andric                     InterExplodedGraphMap *InverseMap) const {
4430b57cec5SDimitry Andric   if (Nodes.empty())
4440b57cec5SDimitry Andric     return nullptr;
4450b57cec5SDimitry Andric 
4460b57cec5SDimitry Andric   using Pass1Ty = llvm::DenseSet<const ExplodedNode *>;
4470b57cec5SDimitry Andric   Pass1Ty Pass1;
4480b57cec5SDimitry Andric 
4490b57cec5SDimitry Andric   using Pass2Ty = InterExplodedGraphMap;
4500b57cec5SDimitry Andric   InterExplodedGraphMap Pass2Scratch;
4510b57cec5SDimitry Andric   Pass2Ty &Pass2 = ForwardMap ? *ForwardMap : Pass2Scratch;
4520b57cec5SDimitry Andric 
4530b57cec5SDimitry Andric   SmallVector<const ExplodedNode*, 10> WL1, WL2;
4540b57cec5SDimitry Andric 
4550b57cec5SDimitry Andric   // ===- Pass 1 (reverse DFS) -===
4560b57cec5SDimitry Andric   for (const auto Sink : Sinks)
4570b57cec5SDimitry Andric     if (Sink)
4580b57cec5SDimitry Andric       WL1.push_back(Sink);
4590b57cec5SDimitry Andric 
4600b57cec5SDimitry Andric   // Process the first worklist until it is empty.
4610b57cec5SDimitry Andric   while (!WL1.empty()) {
4620b57cec5SDimitry Andric     const ExplodedNode *N = WL1.pop_back_val();
4630b57cec5SDimitry Andric 
4640b57cec5SDimitry Andric     // Have we already visited this node?  If so, continue to the next one.
4650b57cec5SDimitry Andric     if (!Pass1.insert(N).second)
4660b57cec5SDimitry Andric       continue;
4670b57cec5SDimitry Andric 
4680b57cec5SDimitry Andric     // If this is a root enqueue it to the second worklist.
4690b57cec5SDimitry Andric     if (N->Preds.empty()) {
4700b57cec5SDimitry Andric       WL2.push_back(N);
4710b57cec5SDimitry Andric       continue;
4720b57cec5SDimitry Andric     }
4730b57cec5SDimitry Andric 
4740b57cec5SDimitry Andric     // Visit our predecessors and enqueue them.
4750b57cec5SDimitry Andric     WL1.append(N->Preds.begin(), N->Preds.end());
4760b57cec5SDimitry Andric   }
4770b57cec5SDimitry Andric 
4780b57cec5SDimitry Andric   // We didn't hit a root? Return with a null pointer for the new graph.
4790b57cec5SDimitry Andric   if (WL2.empty())
4800b57cec5SDimitry Andric     return nullptr;
4810b57cec5SDimitry Andric 
4820b57cec5SDimitry Andric   // Create an empty graph.
4830b57cec5SDimitry Andric   std::unique_ptr<ExplodedGraph> G = MakeEmptyGraph();
4840b57cec5SDimitry Andric 
4850b57cec5SDimitry Andric   // ===- Pass 2 (forward DFS to construct the new graph) -===
4860b57cec5SDimitry Andric   while (!WL2.empty()) {
4870b57cec5SDimitry Andric     const ExplodedNode *N = WL2.pop_back_val();
4880b57cec5SDimitry Andric 
4890b57cec5SDimitry Andric     // Skip this node if we have already processed it.
490*06c3fb27SDimitry Andric     if (Pass2.contains(N))
4910b57cec5SDimitry Andric       continue;
4920b57cec5SDimitry Andric 
4930b57cec5SDimitry Andric     // Create the corresponding node in the new graph and record the mapping
4940b57cec5SDimitry Andric     // from the old node to the new node.
495a7dea167SDimitry Andric     ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State,
496a7dea167SDimitry Andric                                                N->getID(), N->isSink());
4970b57cec5SDimitry Andric     Pass2[N] = NewN;
4980b57cec5SDimitry Andric 
4990b57cec5SDimitry Andric     // Also record the reverse mapping from the new node to the old node.
5000b57cec5SDimitry Andric     if (InverseMap) (*InverseMap)[NewN] = N;
5010b57cec5SDimitry Andric 
5020b57cec5SDimitry Andric     // If this node is a root, designate it as such in the graph.
5030b57cec5SDimitry Andric     if (N->Preds.empty())
5040b57cec5SDimitry Andric       G->addRoot(NewN);
5050b57cec5SDimitry Andric 
5060b57cec5SDimitry Andric     // In the case that some of the intended predecessors of NewN have already
5070b57cec5SDimitry Andric     // been created, we should hook them up as predecessors.
5080b57cec5SDimitry Andric 
5090b57cec5SDimitry Andric     // Walk through the predecessors of 'N' and hook up their corresponding
5100b57cec5SDimitry Andric     // nodes in the new graph (if any) to the freshly created node.
511*06c3fb27SDimitry Andric     for (const ExplodedNode *Pred : N->Preds) {
512*06c3fb27SDimitry Andric       Pass2Ty::iterator PI = Pass2.find(Pred);
5130b57cec5SDimitry Andric       if (PI == Pass2.end())
5140b57cec5SDimitry Andric         continue;
5150b57cec5SDimitry Andric 
5160b57cec5SDimitry Andric       NewN->addPredecessor(const_cast<ExplodedNode *>(PI->second), *G);
5170b57cec5SDimitry Andric     }
5180b57cec5SDimitry Andric 
5190b57cec5SDimitry Andric     // In the case that some of the intended successors of NewN have already
5200b57cec5SDimitry Andric     // been created, we should hook them up as successors.  Otherwise, enqueue
5210b57cec5SDimitry Andric     // the new nodes from the original graph that should have nodes created
5220b57cec5SDimitry Andric     // in the new graph.
523*06c3fb27SDimitry Andric     for (const ExplodedNode *Succ : N->Succs) {
524*06c3fb27SDimitry Andric       Pass2Ty::iterator PI = Pass2.find(Succ);
5250b57cec5SDimitry Andric       if (PI != Pass2.end()) {
5260b57cec5SDimitry Andric         const_cast<ExplodedNode *>(PI->second)->addPredecessor(NewN, *G);
5270b57cec5SDimitry Andric         continue;
5280b57cec5SDimitry Andric       }
5290b57cec5SDimitry Andric 
5300b57cec5SDimitry Andric       // Enqueue nodes to the worklist that were marked during pass 1.
531*06c3fb27SDimitry Andric       if (Pass1.count(Succ))
532*06c3fb27SDimitry Andric         WL2.push_back(Succ);
5330b57cec5SDimitry Andric     }
5340b57cec5SDimitry Andric   }
5350b57cec5SDimitry Andric 
5360b57cec5SDimitry Andric   return G;
5370b57cec5SDimitry Andric }
538