Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 14 | pmbaty | 1 | //===- ExplodedGraph.h - Local, Path-Sens. "Exploded Graph" -----*- C++ -*-===// |
| 2 | // |
||
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
||
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
||
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
||
| 6 | // |
||
| 7 | //===----------------------------------------------------------------------===// |
||
| 8 | // |
||
| 9 | // This file defines the template classes ExplodedNode and ExplodedGraph, |
||
| 10 | // which represent a path-sensitive, intra-procedural "exploded graph." |
||
| 11 | // See "Precise interprocedural dataflow analysis via graph reachability" |
||
| 12 | // by Reps, Horwitz, and Sagiv |
||
| 13 | // (http://portal.acm.org/citation.cfm?id=199462) for the definition of an |
||
| 14 | // exploded graph. |
||
| 15 | // |
||
| 16 | //===----------------------------------------------------------------------===// |
||
| 17 | |||
| 18 | #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPLODEDGRAPH_H |
||
| 19 | #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPLODEDGRAPH_H |
||
| 20 | |||
| 21 | #include "clang/Analysis/AnalysisDeclContext.h" |
||
| 22 | #include "clang/Analysis/ProgramPoint.h" |
||
| 23 | #include "clang/Analysis/Support/BumpVector.h" |
||
| 24 | #include "clang/Basic/LLVM.h" |
||
| 25 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" |
||
| 26 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" |
||
| 27 | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" |
||
| 28 | #include "llvm/ADT/ArrayRef.h" |
||
| 29 | #include "llvm/ADT/DenseMap.h" |
||
| 30 | #include "llvm/ADT/DepthFirstIterator.h" |
||
| 31 | #include "llvm/ADT/FoldingSet.h" |
||
| 32 | #include "llvm/ADT/GraphTraits.h" |
||
| 33 | #include "llvm/ADT/STLExtras.h" |
||
| 34 | #include "llvm/ADT/SetVector.h" |
||
| 35 | #include "llvm/Support/Allocator.h" |
||
| 36 | #include "llvm/Support/Compiler.h" |
||
| 37 | #include <cassert> |
||
| 38 | #include <cstdint> |
||
| 39 | #include <memory> |
||
| 40 | #include <optional> |
||
| 41 | #include <utility> |
||
| 42 | #include <vector> |
||
| 43 | |||
| 44 | namespace clang { |
||
| 45 | |||
| 46 | class CFG; |
||
| 47 | class Decl; |
||
| 48 | class Expr; |
||
| 49 | class ParentMap; |
||
| 50 | class Stmt; |
||
| 51 | |||
| 52 | namespace ento { |
||
| 53 | |||
| 54 | class ExplodedGraph; |
||
| 55 | |||
| 56 | //===----------------------------------------------------------------------===// |
||
| 57 | // ExplodedGraph "implementation" classes. These classes are not typed to |
||
| 58 | // contain a specific kind of state. Typed-specialized versions are defined |
||
| 59 | // on top of these classes. |
||
| 60 | //===----------------------------------------------------------------------===// |
||
| 61 | |||
| 62 | // ExplodedNode is not constified all over the engine because we need to add |
||
| 63 | // successors to it at any time after creating it. |
||
| 64 | |||
| 65 | class ExplodedNode : public llvm::FoldingSetNode { |
||
| 66 | friend class BranchNodeBuilder; |
||
| 67 | friend class CoreEngine; |
||
| 68 | friend class EndOfFunctionNodeBuilder; |
||
| 69 | friend class ExplodedGraph; |
||
| 70 | friend class IndirectGotoNodeBuilder; |
||
| 71 | friend class NodeBuilder; |
||
| 72 | friend class SwitchNodeBuilder; |
||
| 73 | |||
| 74 | /// Efficiently stores a list of ExplodedNodes, or an optional flag. |
||
| 75 | /// |
||
| 76 | /// NodeGroup provides opaque storage for a list of ExplodedNodes, optimizing |
||
| 77 | /// for the case when there is only one node in the group. This is a fairly |
||
| 78 | /// common case in an ExplodedGraph, where most nodes have only one |
||
| 79 | /// predecessor and many have only one successor. It can also be used to |
||
| 80 | /// store a flag rather than a node list, which ExplodedNode uses to mark |
||
| 81 | /// whether a node is a sink. If the flag is set, the group is implicitly |
||
| 82 | /// empty and no nodes may be added. |
||
| 83 | class NodeGroup { |
||
| 84 | // Conceptually a discriminated union. If the low bit is set, the node is |
||
| 85 | // a sink. If the low bit is not set, the pointer refers to the storage |
||
| 86 | // for the nodes in the group. |
||
| 87 | // This is not a PointerIntPair in order to keep the storage type opaque. |
||
| 88 | uintptr_t P; |
||
| 89 | |||
| 90 | public: |
||
| 91 | NodeGroup(bool Flag = false) : P(Flag) { |
||
| 92 | assert(getFlag() == Flag); |
||
| 93 | } |
||
| 94 | |||
| 95 | ExplodedNode * const *begin() const; |
||
| 96 | |||
| 97 | ExplodedNode * const *end() const; |
||
| 98 | |||
| 99 | unsigned size() const; |
||
| 100 | |||
| 101 | bool empty() const { return P == 0 || getFlag() != 0; } |
||
| 102 | |||
| 103 | /// Adds a node to the list. |
||
| 104 | /// |
||
| 105 | /// The group must not have been created with its flag set. |
||
| 106 | void addNode(ExplodedNode *N, ExplodedGraph &G); |
||
| 107 | |||
| 108 | /// Replaces the single node in this group with a new node. |
||
| 109 | /// |
||
| 110 | /// Note that this should only be used when you know the group was not |
||
| 111 | /// created with its flag set, and that the group is empty or contains |
||
| 112 | /// only a single node. |
||
| 113 | void replaceNode(ExplodedNode *node); |
||
| 114 | |||
| 115 | /// Returns whether this group was created with its flag set. |
||
| 116 | bool getFlag() const { |
||
| 117 | return (P & 1); |
||
| 118 | } |
||
| 119 | }; |
||
| 120 | |||
| 121 | /// Location - The program location (within a function body) associated |
||
| 122 | /// with this node. |
||
| 123 | const ProgramPoint Location; |
||
| 124 | |||
| 125 | /// State - The state associated with this node. |
||
| 126 | ProgramStateRef State; |
||
| 127 | |||
| 128 | /// Preds - The predecessors of this node. |
||
| 129 | NodeGroup Preds; |
||
| 130 | |||
| 131 | /// Succs - The successors of this node. |
||
| 132 | NodeGroup Succs; |
||
| 133 | |||
| 134 | int64_t Id; |
||
| 135 | |||
| 136 | public: |
||
| 137 | explicit ExplodedNode(const ProgramPoint &loc, ProgramStateRef state, |
||
| 138 | int64_t Id, bool IsSink) |
||
| 139 | : Location(loc), State(std::move(state)), Succs(IsSink), Id(Id) { |
||
| 140 | assert(isSink() == IsSink); |
||
| 141 | } |
||
| 142 | |||
| 143 | /// getLocation - Returns the edge associated with the given node. |
||
| 144 | ProgramPoint getLocation() const { return Location; } |
||
| 145 | |||
| 146 | const LocationContext *getLocationContext() const { |
||
| 147 | return getLocation().getLocationContext(); |
||
| 148 | } |
||
| 149 | |||
| 150 | const StackFrameContext *getStackFrame() const { |
||
| 151 | return getLocation().getStackFrame(); |
||
| 152 | } |
||
| 153 | |||
| 154 | const Decl &getCodeDecl() const { return *getLocationContext()->getDecl(); } |
||
| 155 | |||
| 156 | CFG &getCFG() const { return *getLocationContext()->getCFG(); } |
||
| 157 | |||
| 158 | const CFGBlock *getCFGBlock() const; |
||
| 159 | |||
| 160 | const ParentMap &getParentMap() const { |
||
| 161 | return getLocationContext()->getParentMap(); |
||
| 162 | } |
||
| 163 | |||
| 164 | template <typename T> T &getAnalysis() const { |
||
| 165 | return *getLocationContext()->getAnalysis<T>(); |
||
| 166 | } |
||
| 167 | |||
| 168 | const ProgramStateRef &getState() const { return State; } |
||
| 169 | |||
| 170 | template <typename T> std::optional<T> getLocationAs() const & { |
||
| 171 | return Location.getAs<T>(); |
||
| 172 | } |
||
| 173 | |||
| 174 | /// Get the value of an arbitrary expression at this node. |
||
| 175 | SVal getSVal(const Stmt *S) const { |
||
| 176 | return getState()->getSVal(S, getLocationContext()); |
||
| 177 | } |
||
| 178 | |||
| 179 | static void Profile(llvm::FoldingSetNodeID &ID, |
||
| 180 | const ProgramPoint &Loc, |
||
| 181 | const ProgramStateRef &state, |
||
| 182 | bool IsSink) { |
||
| 183 | ID.Add(Loc); |
||
| 184 | ID.AddPointer(state.get()); |
||
| 185 | ID.AddBoolean(IsSink); |
||
| 186 | } |
||
| 187 | |||
| 188 | void Profile(llvm::FoldingSetNodeID& ID) const { |
||
| 189 | // We avoid copy constructors by not using accessors. |
||
| 190 | Profile(ID, Location, State, isSink()); |
||
| 191 | } |
||
| 192 | |||
| 193 | /// addPredeccessor - Adds a predecessor to the current node, and |
||
| 194 | /// in tandem add this node as a successor of the other node. |
||
| 195 | void addPredecessor(ExplodedNode *V, ExplodedGraph &G); |
||
| 196 | |||
| 197 | unsigned succ_size() const { return Succs.size(); } |
||
| 198 | unsigned pred_size() const { return Preds.size(); } |
||
| 199 | bool succ_empty() const { return Succs.empty(); } |
||
| 200 | bool pred_empty() const { return Preds.empty(); } |
||
| 201 | |||
| 202 | bool isSink() const { return Succs.getFlag(); } |
||
| 203 | |||
| 204 | bool hasSinglePred() const { |
||
| 205 | return (pred_size() == 1); |
||
| 206 | } |
||
| 207 | |||
| 208 | ExplodedNode *getFirstPred() { |
||
| 209 | return pred_empty() ? nullptr : *(pred_begin()); |
||
| 210 | } |
||
| 211 | |||
| 212 | const ExplodedNode *getFirstPred() const { |
||
| 213 | return const_cast<ExplodedNode*>(this)->getFirstPred(); |
||
| 214 | } |
||
| 215 | |||
| 216 | ExplodedNode *getFirstSucc() { |
||
| 217 | return succ_empty() ? nullptr : *(succ_begin()); |
||
| 218 | } |
||
| 219 | |||
| 220 | const ExplodedNode *getFirstSucc() const { |
||
| 221 | return const_cast<ExplodedNode*>(this)->getFirstSucc(); |
||
| 222 | } |
||
| 223 | |||
| 224 | // Iterators over successor and predecessor vertices. |
||
| 225 | using succ_iterator = ExplodedNode * const *; |
||
| 226 | using succ_range = llvm::iterator_range<succ_iterator>; |
||
| 227 | |||
| 228 | using const_succ_iterator = const ExplodedNode * const *; |
||
| 229 | using const_succ_range = llvm::iterator_range<const_succ_iterator>; |
||
| 230 | |||
| 231 | using pred_iterator = ExplodedNode * const *; |
||
| 232 | using pred_range = llvm::iterator_range<pred_iterator>; |
||
| 233 | |||
| 234 | using const_pred_iterator = const ExplodedNode * const *; |
||
| 235 | using const_pred_range = llvm::iterator_range<const_pred_iterator>; |
||
| 236 | |||
| 237 | pred_iterator pred_begin() { return Preds.begin(); } |
||
| 238 | pred_iterator pred_end() { return Preds.end(); } |
||
| 239 | pred_range preds() { return {Preds.begin(), Preds.end()}; } |
||
| 240 | |||
| 241 | const_pred_iterator pred_begin() const { |
||
| 242 | return const_cast<ExplodedNode*>(this)->pred_begin(); |
||
| 243 | } |
||
| 244 | const_pred_iterator pred_end() const { |
||
| 245 | return const_cast<ExplodedNode*>(this)->pred_end(); |
||
| 246 | } |
||
| 247 | const_pred_range preds() const { return {Preds.begin(), Preds.end()}; } |
||
| 248 | |||
| 249 | succ_iterator succ_begin() { return Succs.begin(); } |
||
| 250 | succ_iterator succ_end() { return Succs.end(); } |
||
| 251 | succ_range succs() { return {Succs.begin(), Succs.end()}; } |
||
| 252 | |||
| 253 | const_succ_iterator succ_begin() const { |
||
| 254 | return const_cast<ExplodedNode*>(this)->succ_begin(); |
||
| 255 | } |
||
| 256 | const_succ_iterator succ_end() const { |
||
| 257 | return const_cast<ExplodedNode*>(this)->succ_end(); |
||
| 258 | } |
||
| 259 | const_succ_range succs() const { return {Succs.begin(), Succs.end()}; } |
||
| 260 | |||
| 261 | int64_t getID() const { return Id; } |
||
| 262 | |||
| 263 | /// The node is trivial if it has only one successor, only one predecessor, |
||
| 264 | /// it's predecessor has only one successor, |
||
| 265 | /// and its program state is the same as the program state of the previous |
||
| 266 | /// node. |
||
| 267 | /// Trivial nodes may be skipped while printing exploded graph. |
||
| 268 | bool isTrivial() const; |
||
| 269 | |||
| 270 | /// If the node's program point corresponds to a statement, retrieve that |
||
| 271 | /// statement. Useful for figuring out where to put a warning or a note. |
||
| 272 | /// If the statement belongs to a body-farmed definition, |
||
| 273 | /// retrieve the call site for that definition. |
||
| 274 | const Stmt *getStmtForDiagnostics() const; |
||
| 275 | |||
| 276 | /// Find the next statement that was executed on this node's execution path. |
||
| 277 | /// Useful for explaining control flow that follows the current node. |
||
| 278 | /// If the statement belongs to a body-farmed definition, retrieve the |
||
| 279 | /// call site for that definition. |
||
| 280 | const Stmt *getNextStmtForDiagnostics() const; |
||
| 281 | |||
| 282 | /// Find the statement that was executed immediately before this node. |
||
| 283 | /// Useful when the node corresponds to a CFG block entrance. |
||
| 284 | /// If the statement belongs to a body-farmed definition, retrieve the |
||
| 285 | /// call site for that definition. |
||
| 286 | const Stmt *getPreviousStmtForDiagnostics() const; |
||
| 287 | |||
| 288 | /// Find the statement that was executed at or immediately before this node. |
||
| 289 | /// Useful when any nearby statement will do. |
||
| 290 | /// If the statement belongs to a body-farmed definition, retrieve the |
||
| 291 | /// call site for that definition. |
||
| 292 | const Stmt *getCurrentOrPreviousStmtForDiagnostics() const; |
||
| 293 | |||
| 294 | private: |
||
| 295 | void replaceSuccessor(ExplodedNode *node) { Succs.replaceNode(node); } |
||
| 296 | void replacePredecessor(ExplodedNode *node) { Preds.replaceNode(node); } |
||
| 297 | }; |
||
| 298 | |||
| 299 | using InterExplodedGraphMap = |
||
| 300 | llvm::DenseMap<const ExplodedNode *, const ExplodedNode *>; |
||
| 301 | |||
| 302 | class ExplodedGraph { |
||
| 303 | protected: |
||
| 304 | friend class CoreEngine; |
||
| 305 | |||
| 306 | // Type definitions. |
||
| 307 | using NodeVector = std::vector<ExplodedNode *>; |
||
| 308 | |||
| 309 | /// The roots of the simulation graph. Usually there will be only |
||
| 310 | /// one, but clients are free to establish multiple subgraphs within a single |
||
| 311 | /// SimulGraph. Moreover, these subgraphs can often merge when paths from |
||
| 312 | /// different roots reach the same state at the same program location. |
||
| 313 | NodeVector Roots; |
||
| 314 | |||
| 315 | /// The nodes in the simulation graph which have been |
||
| 316 | /// specially marked as the endpoint of an abstract simulation path. |
||
| 317 | NodeVector EndNodes; |
||
| 318 | |||
| 319 | /// Nodes - The nodes in the graph. |
||
| 320 | llvm::FoldingSet<ExplodedNode> Nodes; |
||
| 321 | |||
| 322 | /// BVC - Allocator and context for allocating nodes and their predecessor |
||
| 323 | /// and successor groups. |
||
| 324 | BumpVectorContext BVC; |
||
| 325 | |||
| 326 | /// NumNodes - The number of nodes in the graph. |
||
| 327 | int64_t NumNodes = 0; |
||
| 328 | |||
| 329 | /// A list of recently allocated nodes that can potentially be recycled. |
||
| 330 | NodeVector ChangedNodes; |
||
| 331 | |||
| 332 | /// A list of nodes that can be reused. |
||
| 333 | NodeVector FreeNodes; |
||
| 334 | |||
| 335 | /// Determines how often nodes are reclaimed. |
||
| 336 | /// |
||
| 337 | /// If this is 0, nodes will never be reclaimed. |
||
| 338 | unsigned ReclaimNodeInterval = 0; |
||
| 339 | |||
| 340 | /// Counter to determine when to reclaim nodes. |
||
| 341 | unsigned ReclaimCounter; |
||
| 342 | |||
| 343 | public: |
||
| 344 | ExplodedGraph(); |
||
| 345 | ~ExplodedGraph(); |
||
| 346 | |||
| 347 | /// Retrieve the node associated with a (Location,State) pair, |
||
| 348 | /// where the 'Location' is a ProgramPoint in the CFG. If no node for |
||
| 349 | /// this pair exists, it is created. IsNew is set to true if |
||
| 350 | /// the node was freshly created. |
||
| 351 | ExplodedNode *getNode(const ProgramPoint &L, ProgramStateRef State, |
||
| 352 | bool IsSink = false, |
||
| 353 | bool* IsNew = nullptr); |
||
| 354 | |||
| 355 | /// Create a node for a (Location, State) pair, |
||
| 356 | /// but don't store it for deduplication later. This |
||
| 357 | /// is useful when copying an already completed |
||
| 358 | /// ExplodedGraph for further processing. |
||
| 359 | ExplodedNode *createUncachedNode(const ProgramPoint &L, |
||
| 360 | ProgramStateRef State, |
||
| 361 | int64_t Id, |
||
| 362 | bool IsSink = false); |
||
| 363 | |||
| 364 | std::unique_ptr<ExplodedGraph> MakeEmptyGraph() const { |
||
| 365 | return std::make_unique<ExplodedGraph>(); |
||
| 366 | } |
||
| 367 | |||
| 368 | /// addRoot - Add an untyped node to the set of roots. |
||
| 369 | ExplodedNode *addRoot(ExplodedNode *V) { |
||
| 370 | Roots.push_back(V); |
||
| 371 | return V; |
||
| 372 | } |
||
| 373 | |||
| 374 | /// addEndOfPath - Add an untyped node to the set of EOP nodes. |
||
| 375 | ExplodedNode *addEndOfPath(ExplodedNode *V) { |
||
| 376 | EndNodes.push_back(V); |
||
| 377 | return V; |
||
| 378 | } |
||
| 379 | |||
| 380 | unsigned num_roots() const { return Roots.size(); } |
||
| 381 | unsigned num_eops() const { return EndNodes.size(); } |
||
| 382 | |||
| 383 | bool empty() const { return NumNodes == 0; } |
||
| 384 | unsigned size() const { return NumNodes; } |
||
| 385 | |||
| 386 | void reserve(unsigned NodeCount) { Nodes.reserve(NodeCount); } |
||
| 387 | |||
| 388 | // Iterators. |
||
| 389 | using NodeTy = ExplodedNode; |
||
| 390 | using AllNodesTy = llvm::FoldingSet<ExplodedNode>; |
||
| 391 | using roots_iterator = NodeVector::iterator; |
||
| 392 | using const_roots_iterator = NodeVector::const_iterator; |
||
| 393 | using eop_iterator = NodeVector::iterator; |
||
| 394 | using const_eop_iterator = NodeVector::const_iterator; |
||
| 395 | using node_iterator = AllNodesTy::iterator; |
||
| 396 | using const_node_iterator = AllNodesTy::const_iterator; |
||
| 397 | |||
| 398 | node_iterator nodes_begin() { return Nodes.begin(); } |
||
| 399 | |||
| 400 | node_iterator nodes_end() { return Nodes.end(); } |
||
| 401 | |||
| 402 | const_node_iterator nodes_begin() const { return Nodes.begin(); } |
||
| 403 | |||
| 404 | const_node_iterator nodes_end() const { return Nodes.end(); } |
||
| 405 | |||
| 406 | roots_iterator roots_begin() { return Roots.begin(); } |
||
| 407 | |||
| 408 | roots_iterator roots_end() { return Roots.end(); } |
||
| 409 | |||
| 410 | const_roots_iterator roots_begin() const { return Roots.begin(); } |
||
| 411 | |||
| 412 | const_roots_iterator roots_end() const { return Roots.end(); } |
||
| 413 | |||
| 414 | eop_iterator eop_begin() { return EndNodes.begin(); } |
||
| 415 | |||
| 416 | eop_iterator eop_end() { return EndNodes.end(); } |
||
| 417 | |||
| 418 | const_eop_iterator eop_begin() const { return EndNodes.begin(); } |
||
| 419 | |||
| 420 | const_eop_iterator eop_end() const { return EndNodes.end(); } |
||
| 421 | |||
| 422 | llvm::BumpPtrAllocator & getAllocator() { return BVC.getAllocator(); } |
||
| 423 | BumpVectorContext &getNodeAllocator() { return BVC; } |
||
| 424 | |||
| 425 | using NodeMap = llvm::DenseMap<const ExplodedNode *, ExplodedNode *>; |
||
| 426 | |||
| 427 | /// Creates a trimmed version of the graph that only contains paths leading |
||
| 428 | /// to the given nodes. |
||
| 429 | /// |
||
| 430 | /// \param Nodes The nodes which must appear in the final graph. Presumably |
||
| 431 | /// these are end-of-path nodes (i.e. they have no successors). |
||
| 432 | /// \param[out] ForwardMap A optional map from nodes in this graph to nodes in |
||
| 433 | /// the returned graph. |
||
| 434 | /// \param[out] InverseMap An optional map from nodes in the returned graph to |
||
| 435 | /// nodes in this graph. |
||
| 436 | /// \returns The trimmed graph |
||
| 437 | std::unique_ptr<ExplodedGraph> |
||
| 438 | trim(ArrayRef<const NodeTy *> Nodes, |
||
| 439 | InterExplodedGraphMap *ForwardMap = nullptr, |
||
| 440 | InterExplodedGraphMap *InverseMap = nullptr) const; |
||
| 441 | |||
| 442 | /// Enable tracking of recently allocated nodes for potential reclamation |
||
| 443 | /// when calling reclaimRecentlyAllocatedNodes(). |
||
| 444 | void enableNodeReclamation(unsigned Interval) { |
||
| 445 | ReclaimCounter = ReclaimNodeInterval = Interval; |
||
| 446 | } |
||
| 447 | |||
| 448 | /// Reclaim "uninteresting" nodes created since the last time this method |
||
| 449 | /// was called. |
||
| 450 | void reclaimRecentlyAllocatedNodes(); |
||
| 451 | |||
| 452 | /// Returns true if nodes for the given expression kind are always |
||
| 453 | /// kept around. |
||
| 454 | static bool isInterestingLValueExpr(const Expr *Ex); |
||
| 455 | |||
| 456 | private: |
||
| 457 | bool shouldCollect(const ExplodedNode *node); |
||
| 458 | void collectNode(ExplodedNode *node); |
||
| 459 | }; |
||
| 460 | |||
| 461 | class ExplodedNodeSet { |
||
| 462 | using ImplTy = llvm::SmallSetVector<ExplodedNode *, 4>; |
||
| 463 | ImplTy Impl; |
||
| 464 | |||
| 465 | public: |
||
| 466 | ExplodedNodeSet(ExplodedNode *N) { |
||
| 467 | assert(N && !static_cast<ExplodedNode*>(N)->isSink()); |
||
| 468 | Impl.insert(N); |
||
| 469 | } |
||
| 470 | |||
| 471 | ExplodedNodeSet() = default; |
||
| 472 | |||
| 473 | void Add(ExplodedNode *N) { |
||
| 474 | if (N && !static_cast<ExplodedNode*>(N)->isSink()) Impl.insert(N); |
||
| 475 | } |
||
| 476 | |||
| 477 | using iterator = ImplTy::iterator; |
||
| 478 | using const_iterator = ImplTy::const_iterator; |
||
| 479 | |||
| 480 | unsigned size() const { return Impl.size(); } |
||
| 481 | bool empty() const { return Impl.empty(); } |
||
| 482 | bool erase(ExplodedNode *N) { return Impl.remove(N); } |
||
| 483 | |||
| 484 | void clear() { Impl.clear(); } |
||
| 485 | |||
| 486 | void insert(const ExplodedNodeSet &S) { |
||
| 487 | assert(&S != this); |
||
| 488 | if (empty()) |
||
| 489 | Impl = S.Impl; |
||
| 490 | else |
||
| 491 | Impl.insert(S.begin(), S.end()); |
||
| 492 | } |
||
| 493 | |||
| 494 | iterator begin() { return Impl.begin(); } |
||
| 495 | iterator end() { return Impl.end(); } |
||
| 496 | |||
| 497 | const_iterator begin() const { return Impl.begin(); } |
||
| 498 | const_iterator end() const { return Impl.end(); } |
||
| 499 | }; |
||
| 500 | |||
| 501 | } // namespace ento |
||
| 502 | |||
| 503 | } // namespace clang |
||
| 504 | |||
| 505 | // GraphTraits |
||
| 506 | |||
| 507 | namespace llvm { |
||
| 508 | template <> struct GraphTraits<clang::ento::ExplodedGraph *> { |
||
| 509 | using GraphTy = clang::ento::ExplodedGraph *; |
||
| 510 | using NodeRef = clang::ento::ExplodedNode *; |
||
| 511 | using ChildIteratorType = clang::ento::ExplodedNode::succ_iterator; |
||
| 512 | using nodes_iterator = llvm::df_iterator<GraphTy>; |
||
| 513 | |||
| 514 | static NodeRef getEntryNode(const GraphTy G) { |
||
| 515 | return *G->roots_begin(); |
||
| 516 | } |
||
| 517 | |||
| 518 | static bool predecessorOfTrivial(NodeRef N) { |
||
| 519 | return N->succ_size() == 1 && N->getFirstSucc()->isTrivial(); |
||
| 520 | } |
||
| 521 | |||
| 522 | static ChildIteratorType child_begin(NodeRef N) { |
||
| 523 | if (predecessorOfTrivial(N)) |
||
| 524 | return child_begin(*N->succ_begin()); |
||
| 525 | return N->succ_begin(); |
||
| 526 | } |
||
| 527 | |||
| 528 | static ChildIteratorType child_end(NodeRef N) { |
||
| 529 | if (predecessorOfTrivial(N)) |
||
| 530 | return child_end(N->getFirstSucc()); |
||
| 531 | return N->succ_end(); |
||
| 532 | } |
||
| 533 | |||
| 534 | static nodes_iterator nodes_begin(const GraphTy G) { |
||
| 535 | return df_begin(G); |
||
| 536 | } |
||
| 537 | |||
| 538 | static nodes_iterator nodes_end(const GraphTy G) { |
||
| 539 | return df_end(G); |
||
| 540 | } |
||
| 541 | }; |
||
| 542 | } // namespace llvm |
||
| 543 | |||
| 544 | #endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_EXPLODEDGRAPH_H |