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