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