1 //===- CoreEngine.h - Path-Sensitive Dataflow Engine ------------*- 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 a generic engine for intraprocedural, path-sensitive, 10 // dataflow analysis via graph reachability. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H 15 #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H 16 17 #include "clang/AST/Stmt.h" 18 #include "clang/Analysis/AnalysisDeclContext.h" 19 #include "clang/Analysis/CFG.h" 20 #include "clang/Analysis/ProgramPoint.h" 21 #include "clang/Basic/LLVM.h" 22 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 23 #include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h" 24 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 25 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" 26 #include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" 27 #include "llvm/ADT/SmallVector.h" 28 #include "llvm/ADT/iterator_range.h" 29 #include "llvm/Support/Casting.h" 30 #include <cassert> 31 #include <memory> 32 #include <utility> 33 #include <vector> 34 35 namespace clang { 36 37 class AnalyzerOptions; 38 class CXXBindTemporaryExpr; 39 class Expr; 40 class LabelDecl; 41 42 namespace ento { 43 44 class FunctionSummariesTy; 45 class ExprEngine; 46 47 //===----------------------------------------------------------------------===// 48 /// CoreEngine - Implements the core logic of the graph-reachability 49 /// analysis. It traverses the CFG and generates the ExplodedGraph. 50 /// Program "states" are treated as opaque void pointers. 51 /// The template class CoreEngine (which subclasses CoreEngine) 52 /// provides the matching component to the engine that knows the actual types 53 /// for states. Note that this engine only dispatches to transfer functions 54 /// at the statement and block-level. The analyses themselves must implement 55 /// any transfer function logic and the sub-expression level (if any). 56 class CoreEngine { 57 friend class CommonNodeBuilder; 58 friend class EndOfFunctionNodeBuilder; 59 friend class ExprEngine; 60 friend class IndirectGotoNodeBuilder; 61 friend class NodeBuilder; 62 friend class NodeBuilderContext; 63 friend class SwitchNodeBuilder; 64 65 public: 66 using BlocksExhausted = 67 std::vector<std::pair<BlockEdge, const ExplodedNode *>>; 68 69 using BlocksAborted = 70 std::vector<std::pair<const CFGBlock *, const ExplodedNode *>>; 71 72 private: 73 ExprEngine &ExprEng; 74 75 /// G - The simulation graph. Each node is a (location,state) pair. 76 mutable ExplodedGraph G; 77 78 /// WList - A set of queued nodes that need to be processed by the 79 /// worklist algorithm. It is up to the implementation of WList to decide 80 /// the order that nodes are processed. 81 std::unique_ptr<WorkList> WList; 82 std::unique_ptr<WorkList> CTUWList; 83 84 /// BCounterFactory - A factory object for created BlockCounter objects. 85 /// These are used to record for key nodes in the ExplodedGraph the 86 /// number of times different CFGBlocks have been visited along a path. 87 BlockCounter::Factory BCounterFactory; 88 89 /// The locations where we stopped doing work because we visited a location 90 /// too many times. 91 BlocksExhausted blocksExhausted; 92 93 /// The locations where we stopped because the engine aborted analysis, 94 /// usually because it could not reason about something. 95 BlocksAborted blocksAborted; 96 97 /// The information about functions shared by the whole translation unit. 98 /// (This data is owned by AnalysisConsumer.) 99 FunctionSummariesTy *FunctionSummaries; 100 101 /// Add path tags with some useful data along the path when we see that 102 /// something interesting is happening. This field is the allocator for such 103 /// tags. 104 DataTag::Factory DataTags; 105 106 void setBlockCounter(BlockCounter C); 107 108 void generateNode(const ProgramPoint &Loc, 109 ProgramStateRef State, 110 ExplodedNode *Pred); 111 112 void HandleBlockEdge(const BlockEdge &E, ExplodedNode *Pred); 113 void HandleBlockEntrance(const BlockEntrance &E, ExplodedNode *Pred); 114 void HandleBlockExit(const CFGBlock *B, ExplodedNode *Pred); 115 116 void HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred); 117 118 void HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, ExplodedNode *Pred); 119 120 void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B, 121 ExplodedNode *Pred); 122 void HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, 123 const CFGBlock *B, ExplodedNode *Pred); 124 125 /// Handle conditional logic for running static initializers. 126 void HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, 127 ExplodedNode *Pred); 128 129 void HandleVirtualBaseBranch(const CFGBlock *B, ExplodedNode *Pred); 130 131 private: 132 ExplodedNode *generateCallExitBeginNode(ExplodedNode *N, 133 const ReturnStmt *RS); 134 135 public: 136 /// Construct a CoreEngine object to analyze the provided CFG. 137 CoreEngine(ExprEngine &exprengine, 138 FunctionSummariesTy *FS, 139 AnalyzerOptions &Opts); 140 141 CoreEngine(const CoreEngine &) = delete; 142 CoreEngine &operator=(const CoreEngine &) = delete; 143 144 /// getGraph - Returns the exploded graph. getGraph()145 ExplodedGraph &getGraph() { return G; } 146 147 /// ExecuteWorkList - Run the worklist algorithm for a maximum number of 148 /// steps. Returns true if there is still simulation state on the worklist. 149 bool ExecuteWorkList(const LocationContext *L, unsigned Steps, 150 ProgramStateRef InitState); 151 152 /// Dispatch the work list item based on the given location information. 153 /// Use Pred parameter as the predecessor state. 154 void dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, 155 const WorkListUnit& WU); 156 157 // Functions for external checking of whether we have unfinished work wasBlockAborted()158 bool wasBlockAborted() const { return !blocksAborted.empty(); } wasBlocksExhausted()159 bool wasBlocksExhausted() const { return !blocksExhausted.empty(); } hasWorkRemaining()160 bool hasWorkRemaining() const { return wasBlocksExhausted() || 161 WList->hasWork() || 162 wasBlockAborted(); } 163 164 /// Inform the CoreEngine that a basic block was aborted because 165 /// it could not be completely analyzed. addAbortedBlock(const ExplodedNode * node,const CFGBlock * block)166 void addAbortedBlock(const ExplodedNode *node, const CFGBlock *block) { 167 blocksAborted.push_back(std::make_pair(block, node)); 168 } 169 getWorkList()170 WorkList *getWorkList() const { return WList.get(); } getCTUWorkList()171 WorkList *getCTUWorkList() const { return CTUWList.get(); } 172 exhausted_blocks()173 auto exhausted_blocks() const { 174 return llvm::iterator_range(blocksExhausted); 175 } 176 aborted_blocks()177 auto aborted_blocks() const { return llvm::iterator_range(blocksAborted); } 178 179 /// Enqueue the given set of nodes onto the work list. 180 void enqueue(ExplodedNodeSet &Set); 181 182 /// Enqueue nodes that were created as a result of processing 183 /// a statement onto the work list. 184 void enqueue(ExplodedNodeSet &Set, const CFGBlock *Block, unsigned Idx); 185 186 /// enqueue the nodes corresponding to the end of function onto the 187 /// end of path / work list. 188 void enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS); 189 190 /// Enqueue a single node created as a result of statement processing. 191 void enqueueStmtNode(ExplodedNode *N, const CFGBlock *Block, unsigned Idx); 192 getDataTags()193 DataTag::Factory &getDataTags() { return DataTags; } 194 }; 195 196 class NodeBuilderContext { 197 const CoreEngine &Eng; 198 const CFGBlock *Block; 199 const LocationContext *LC; 200 201 public: NodeBuilderContext(const CoreEngine & E,const CFGBlock * B,const LocationContext * L)202 NodeBuilderContext(const CoreEngine &E, const CFGBlock *B, 203 const LocationContext *L) 204 : Eng(E), Block(B), LC(L) { 205 assert(B); 206 } 207 NodeBuilderContext(const CoreEngine & E,const CFGBlock * B,ExplodedNode * N)208 NodeBuilderContext(const CoreEngine &E, const CFGBlock *B, ExplodedNode *N) 209 : NodeBuilderContext(E, B, N->getLocationContext()) {} 210 211 /// Return the CoreEngine associated with this builder. getEngine()212 const CoreEngine &getEngine() const { return Eng; } 213 214 /// Return the CFGBlock associated with this builder. getBlock()215 const CFGBlock *getBlock() const { return Block; } 216 217 /// Return the location context associated with this builder. getLocationContext()218 const LocationContext *getLocationContext() const { return LC; } 219 220 /// Returns the number of times the current basic block has been 221 /// visited on the exploded graph path. blockCount()222 unsigned blockCount() const { 223 return Eng.WList->getBlockCounter().getNumVisited( 224 LC->getStackFrame(), 225 Block->getBlockID()); 226 } 227 }; 228 229 /// \class NodeBuilder 230 /// This is the simplest builder which generates nodes in the 231 /// ExplodedGraph. 232 /// 233 /// The main benefit of the builder is that it automatically tracks the 234 /// frontier nodes (or destination set). This is the set of nodes which should 235 /// be propagated to the next step / builder. They are the nodes which have been 236 /// added to the builder (either as the input node set or as the newly 237 /// constructed nodes) but did not have any outgoing transitions added. 238 class NodeBuilder { 239 virtual void anchor(); 240 241 protected: 242 const NodeBuilderContext &C; 243 244 /// Specifies if the builder results have been finalized. For example, if it 245 /// is set to false, autotransitions are yet to be generated. 246 bool Finalized; 247 248 bool HasGeneratedNodes = false; 249 250 /// The frontier set - a set of nodes which need to be propagated after 251 /// the builder dies. 252 ExplodedNodeSet &Frontier; 253 254 /// Checks if the results are ready. checkResults()255 virtual bool checkResults() { 256 return Finalized; 257 } 258 hasNoSinksInFrontier()259 bool hasNoSinksInFrontier() { 260 for (const auto I : Frontier) 261 if (I->isSink()) 262 return false; 263 return true; 264 } 265 266 /// Allow subclasses to finalize results before result_begin() is executed. finalizeResults()267 virtual void finalizeResults() {} 268 269 ExplodedNode *generateNodeImpl(const ProgramPoint &PP, 270 ProgramStateRef State, 271 ExplodedNode *Pred, 272 bool MarkAsSink = false); 273 274 public: 275 NodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, 276 const NodeBuilderContext &Ctx, bool F = true) C(Ctx)277 : C(Ctx), Finalized(F), Frontier(DstSet) { 278 Frontier.Add(SrcNode); 279 } 280 281 NodeBuilder(const ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, 282 const NodeBuilderContext &Ctx, bool F = true) C(Ctx)283 : C(Ctx), Finalized(F), Frontier(DstSet) { 284 Frontier.insert(SrcSet); 285 assert(hasNoSinksInFrontier()); 286 } 287 288 virtual ~NodeBuilder() = default; 289 290 /// Generates a node in the ExplodedGraph. generateNode(const ProgramPoint & PP,ProgramStateRef State,ExplodedNode * Pred)291 ExplodedNode *generateNode(const ProgramPoint &PP, 292 ProgramStateRef State, 293 ExplodedNode *Pred) { 294 return generateNodeImpl( 295 PP, State, Pred, 296 /*MarkAsSink=*/State->isPosteriorlyOverconstrained()); 297 } 298 299 /// Generates a sink in the ExplodedGraph. 300 /// 301 /// When a node is marked as sink, the exploration from the node is stopped - 302 /// the node becomes the last node on the path and certain kinds of bugs are 303 /// suppressed. generateSink(const ProgramPoint & PP,ProgramStateRef State,ExplodedNode * Pred)304 ExplodedNode *generateSink(const ProgramPoint &PP, 305 ProgramStateRef State, 306 ExplodedNode *Pred) { 307 return generateNodeImpl(PP, State, Pred, true); 308 } 309 getResults()310 const ExplodedNodeSet &getResults() { 311 finalizeResults(); 312 assert(checkResults()); 313 return Frontier; 314 } 315 316 using iterator = ExplodedNodeSet::iterator; 317 318 /// Iterators through the results frontier. begin()319 iterator begin() { 320 finalizeResults(); 321 assert(checkResults()); 322 return Frontier.begin(); 323 } 324 end()325 iterator end() { 326 finalizeResults(); 327 return Frontier.end(); 328 } 329 getContext()330 const NodeBuilderContext &getContext() { return C; } hasGeneratedNodes()331 bool hasGeneratedNodes() { return HasGeneratedNodes; } 332 takeNodes(const ExplodedNodeSet & S)333 void takeNodes(const ExplodedNodeSet &S) { 334 for (const auto I : S) 335 Frontier.erase(I); 336 } 337 takeNodes(ExplodedNode * N)338 void takeNodes(ExplodedNode *N) { Frontier.erase(N); } addNodes(const ExplodedNodeSet & S)339 void addNodes(const ExplodedNodeSet &S) { Frontier.insert(S); } addNodes(ExplodedNode * N)340 void addNodes(ExplodedNode *N) { Frontier.Add(N); } 341 }; 342 343 /// \class NodeBuilderWithSinks 344 /// This node builder keeps track of the generated sink nodes. 345 class NodeBuilderWithSinks: public NodeBuilder { 346 void anchor() override; 347 348 protected: 349 SmallVector<ExplodedNode*, 2> sinksGenerated; 350 ProgramPoint &Location; 351 352 public: NodeBuilderWithSinks(ExplodedNode * Pred,ExplodedNodeSet & DstSet,const NodeBuilderContext & Ctx,ProgramPoint & L)353 NodeBuilderWithSinks(ExplodedNode *Pred, ExplodedNodeSet &DstSet, 354 const NodeBuilderContext &Ctx, ProgramPoint &L) 355 : NodeBuilder(Pred, DstSet, Ctx), Location(L) {} 356 357 ExplodedNode *generateNode(ProgramStateRef State, 358 ExplodedNode *Pred, 359 const ProgramPointTag *Tag = nullptr) { 360 const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location); 361 return NodeBuilder::generateNode(LocalLoc, State, Pred); 362 } 363 364 ExplodedNode *generateSink(ProgramStateRef State, ExplodedNode *Pred, 365 const ProgramPointTag *Tag = nullptr) { 366 const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location); 367 ExplodedNode *N = NodeBuilder::generateSink(LocalLoc, State, Pred); 368 if (N && N->isSink()) 369 sinksGenerated.push_back(N); 370 return N; 371 } 372 getSinks()373 const SmallVectorImpl<ExplodedNode*> &getSinks() const { 374 return sinksGenerated; 375 } 376 }; 377 378 /// \class StmtNodeBuilder 379 /// This builder class is useful for generating nodes that resulted from 380 /// visiting a statement. The main difference from its parent NodeBuilder is 381 /// that it creates a statement specific ProgramPoint. 382 class StmtNodeBuilder: public NodeBuilder { 383 NodeBuilder *EnclosingBldr; 384 385 public: 386 /// Constructs a StmtNodeBuilder. If the builder is going to process 387 /// nodes currently owned by another builder(with larger scope), use 388 /// Enclosing builder to transfer ownership. 389 StmtNodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, 390 const NodeBuilderContext &Ctx, 391 NodeBuilder *Enclosing = nullptr) NodeBuilder(SrcNode,DstSet,Ctx)392 : NodeBuilder(SrcNode, DstSet, Ctx), EnclosingBldr(Enclosing) { 393 if (EnclosingBldr) 394 EnclosingBldr->takeNodes(SrcNode); 395 } 396 397 StmtNodeBuilder(ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, 398 const NodeBuilderContext &Ctx, 399 NodeBuilder *Enclosing = nullptr) NodeBuilder(SrcSet,DstSet,Ctx)400 : NodeBuilder(SrcSet, DstSet, Ctx), EnclosingBldr(Enclosing) { 401 if (EnclosingBldr) 402 for (const auto I : SrcSet) 403 EnclosingBldr->takeNodes(I); 404 } 405 406 ~StmtNodeBuilder() override; 407 408 using NodeBuilder::generateNode; 409 using NodeBuilder::generateSink; 410 411 ExplodedNode *generateNode(const Stmt *S, 412 ExplodedNode *Pred, 413 ProgramStateRef St, 414 const ProgramPointTag *tag = nullptr, 415 ProgramPoint::Kind K = ProgramPoint::PostStmtKind){ 416 const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, 417 Pred->getLocationContext(), tag); 418 return NodeBuilder::generateNode(L, St, Pred); 419 } 420 421 ExplodedNode *generateSink(const Stmt *S, 422 ExplodedNode *Pred, 423 ProgramStateRef St, 424 const ProgramPointTag *tag = nullptr, 425 ProgramPoint::Kind K = ProgramPoint::PostStmtKind){ 426 const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, 427 Pred->getLocationContext(), tag); 428 return NodeBuilder::generateSink(L, St, Pred); 429 } 430 }; 431 432 /// BranchNodeBuilder is responsible for constructing the nodes 433 /// corresponding to the two branches of the if statement - true and false. 434 class BranchNodeBuilder: public NodeBuilder { 435 const CFGBlock *DstT; 436 const CFGBlock *DstF; 437 438 bool InFeasibleTrue; 439 bool InFeasibleFalse; 440 441 void anchor() override; 442 443 public: BranchNodeBuilder(ExplodedNode * SrcNode,ExplodedNodeSet & DstSet,const NodeBuilderContext & C,const CFGBlock * dstT,const CFGBlock * dstF)444 BranchNodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, 445 const NodeBuilderContext &C, 446 const CFGBlock *dstT, const CFGBlock *dstF) 447 : NodeBuilder(SrcNode, DstSet, C), DstT(dstT), DstF(dstF), 448 InFeasibleTrue(!DstT), InFeasibleFalse(!DstF) { 449 // The branch node builder does not generate autotransitions. 450 // If there are no successors it means that both branches are infeasible. 451 takeNodes(SrcNode); 452 } 453 BranchNodeBuilder(const ExplodedNodeSet & SrcSet,ExplodedNodeSet & DstSet,const NodeBuilderContext & C,const CFGBlock * dstT,const CFGBlock * dstF)454 BranchNodeBuilder(const ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, 455 const NodeBuilderContext &C, 456 const CFGBlock *dstT, const CFGBlock *dstF) 457 : NodeBuilder(SrcSet, DstSet, C), DstT(dstT), DstF(dstF), 458 InFeasibleTrue(!DstT), InFeasibleFalse(!DstF) { 459 takeNodes(SrcSet); 460 } 461 462 ExplodedNode *generateNode(ProgramStateRef State, bool branch, 463 ExplodedNode *Pred); 464 getTargetBlock(bool branch)465 const CFGBlock *getTargetBlock(bool branch) const { 466 return branch ? DstT : DstF; 467 } 468 markInfeasible(bool branch)469 void markInfeasible(bool branch) { 470 if (branch) 471 InFeasibleTrue = true; 472 else 473 InFeasibleFalse = true; 474 } 475 isFeasible(bool branch)476 bool isFeasible(bool branch) { 477 return branch ? !InFeasibleTrue : !InFeasibleFalse; 478 } 479 }; 480 481 class IndirectGotoNodeBuilder { 482 CoreEngine& Eng; 483 const CFGBlock *Src; 484 const CFGBlock &DispatchBlock; 485 const Expr *E; 486 ExplodedNode *Pred; 487 488 public: IndirectGotoNodeBuilder(ExplodedNode * pred,const CFGBlock * src,const Expr * e,const CFGBlock * dispatch,CoreEngine * eng)489 IndirectGotoNodeBuilder(ExplodedNode *pred, const CFGBlock *src, 490 const Expr *e, const CFGBlock *dispatch, CoreEngine* eng) 491 : Eng(*eng), Src(src), DispatchBlock(*dispatch), E(e), Pred(pred) {} 492 493 class iterator { 494 friend class IndirectGotoNodeBuilder; 495 496 CFGBlock::const_succ_iterator I; 497 iterator(CFGBlock::const_succ_iterator i)498 iterator(CFGBlock::const_succ_iterator i) : I(i) {} 499 500 public: 501 // This isn't really a conventional iterator. 502 // We just implement the deref as a no-op for now to make range-based for 503 // loops work. 504 const iterator &operator*() const { return *this; } 505 506 iterator &operator++() { ++I; return *this; } 507 bool operator!=(const iterator &X) const { return I != X.I; } 508 getLabel()509 const LabelDecl *getLabel() const { 510 return cast<LabelStmt>((*I)->getLabel())->getDecl(); 511 } 512 getBlock()513 const CFGBlock *getBlock() const { 514 return *I; 515 } 516 }; 517 begin()518 iterator begin() { return iterator(DispatchBlock.succ_begin()); } end()519 iterator end() { return iterator(DispatchBlock.succ_end()); } 520 521 ExplodedNode *generateNode(const iterator &I, 522 ProgramStateRef State, 523 bool isSink = false); 524 getTarget()525 const Expr *getTarget() const { return E; } 526 getState()527 ProgramStateRef getState() const { return Pred->State; } 528 getLocationContext()529 const LocationContext *getLocationContext() const { 530 return Pred->getLocationContext(); 531 } 532 }; 533 534 class SwitchNodeBuilder { 535 CoreEngine& Eng; 536 const CFGBlock *Src; 537 const Expr *Condition; 538 ExplodedNode *Pred; 539 540 public: SwitchNodeBuilder(ExplodedNode * pred,const CFGBlock * src,const Expr * condition,CoreEngine * eng)541 SwitchNodeBuilder(ExplodedNode *pred, const CFGBlock *src, 542 const Expr *condition, CoreEngine* eng) 543 : Eng(*eng), Src(src), Condition(condition), Pred(pred) {} 544 545 class iterator { 546 friend class SwitchNodeBuilder; 547 548 CFGBlock::const_succ_reverse_iterator I; 549 iterator(CFGBlock::const_succ_reverse_iterator i)550 iterator(CFGBlock::const_succ_reverse_iterator i) : I(i) {} 551 552 public: 553 iterator &operator++() { ++I; return *this; } 554 bool operator!=(const iterator &X) const { return I != X.I; } 555 bool operator==(const iterator &X) const { return I == X.I; } 556 getCase()557 const CaseStmt *getCase() const { 558 return cast<CaseStmt>((*I)->getLabel()); 559 } 560 getBlock()561 const CFGBlock *getBlock() const { 562 return *I; 563 } 564 }; 565 begin()566 iterator begin() { return iterator(Src->succ_rbegin()+1); } end()567 iterator end() { return iterator(Src->succ_rend()); } 568 getSwitch()569 const SwitchStmt *getSwitch() const { 570 return cast<SwitchStmt>(Src->getTerminator()); 571 } 572 573 ExplodedNode *generateCaseStmtNode(const iterator &I, 574 ProgramStateRef State); 575 576 ExplodedNode *generateDefaultCaseNode(ProgramStateRef State, 577 bool isSink = false); 578 getCondition()579 const Expr *getCondition() const { return Condition; } 580 getState()581 ProgramStateRef getState() const { return Pred->State; } 582 getLocationContext()583 const LocationContext *getLocationContext() const { 584 return Pred->getLocationContext(); 585 } 586 }; 587 588 } // namespace ento 589 590 } // namespace clang 591 592 #endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H 593