CoreEngine.cpp revision 868e78d59d2dfaf9cda511925e5a58f3a712db96
1//==- GRCoreEngine.cpp - Path-Sensitive Dataflow Engine ------------*- C++ -*-// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file defines a generic engine for intraprocedural, path-sensitive, 11// dataflow analysis via graph reachability engine. 12// 13//===----------------------------------------------------------------------===// 14 15#include "clang/Checker/PathSensitive/GRCoreEngine.h" 16#include "clang/Checker/PathSensitive/GRExprEngine.h" 17#include "clang/AST/Expr.h" 18#include "llvm/Support/Casting.h" 19#include "llvm/ADT/DenseMap.h" 20#include <vector> 21#include <queue> 22 23using llvm::cast; 24using llvm::isa; 25using namespace clang; 26 27//===----------------------------------------------------------------------===// 28// Worklist classes for exploration of reachable states. 29//===----------------------------------------------------------------------===// 30 31namespace { 32class DFS : public GRWorkList { 33 llvm::SmallVector<GRWorkListUnit,20> Stack; 34public: 35 virtual bool hasWork() const { 36 return !Stack.empty(); 37 } 38 39 virtual void Enqueue(const GRWorkListUnit& U) { 40 Stack.push_back(U); 41 } 42 43 virtual GRWorkListUnit Dequeue() { 44 assert (!Stack.empty()); 45 const GRWorkListUnit& U = Stack.back(); 46 Stack.pop_back(); // This technically "invalidates" U, but we are fine. 47 return U; 48 } 49}; 50 51class BFS : public GRWorkList { 52 std::queue<GRWorkListUnit> Queue; 53public: 54 virtual bool hasWork() const { 55 return !Queue.empty(); 56 } 57 58 virtual void Enqueue(const GRWorkListUnit& U) { 59 Queue.push(U); 60 } 61 62 virtual GRWorkListUnit Dequeue() { 63 // Don't use const reference. The subsequent pop_back() might make it 64 // unsafe. 65 GRWorkListUnit U = Queue.front(); 66 Queue.pop(); 67 return U; 68 } 69}; 70 71} // end anonymous namespace 72 73// Place the dstor for GRWorkList here because it contains virtual member 74// functions, and we the code for the dstor generated in one compilation unit. 75GRWorkList::~GRWorkList() {} 76 77GRWorkList *GRWorkList::MakeDFS() { return new DFS(); } 78GRWorkList *GRWorkList::MakeBFS() { return new BFS(); } 79 80namespace { 81 class BFSBlockDFSContents : public GRWorkList { 82 std::queue<GRWorkListUnit> Queue; 83 llvm::SmallVector<GRWorkListUnit,20> Stack; 84 public: 85 virtual bool hasWork() const { 86 return !Queue.empty() || !Stack.empty(); 87 } 88 89 virtual void Enqueue(const GRWorkListUnit& U) { 90 if (isa<BlockEntrance>(U.getNode()->getLocation())) 91 Queue.push(U); 92 else 93 Stack.push_back(U); 94 } 95 96 virtual GRWorkListUnit Dequeue() { 97 // Process all basic blocks to completion. 98 if (!Stack.empty()) { 99 const GRWorkListUnit& U = Stack.back(); 100 Stack.pop_back(); // This technically "invalidates" U, but we are fine. 101 return U; 102 } 103 104 assert(!Queue.empty()); 105 // Don't use const reference. The subsequent pop_back() might make it 106 // unsafe. 107 GRWorkListUnit U = Queue.front(); 108 Queue.pop(); 109 return U; 110 } 111 }; 112} // end anonymous namespace 113 114GRWorkList* GRWorkList::MakeBFSBlockDFSContents() { 115 return new BFSBlockDFSContents(); 116} 117 118//===----------------------------------------------------------------------===// 119// Core analysis engine. 120//===----------------------------------------------------------------------===// 121void GRCoreEngine::ProcessEndPath(GREndPathNodeBuilder& Builder) { 122 SubEngine.ProcessEndPath(Builder); 123} 124 125void GRCoreEngine::ProcessStmt(CFGElement E, GRStmtNodeBuilder& Builder) { 126 SubEngine.ProcessStmt(E, Builder); 127} 128 129bool GRCoreEngine::ProcessBlockEntrance(CFGBlock* Blk, const ExplodedNode *Pred, 130 GRBlockCounter BC) { 131 return SubEngine.ProcessBlockEntrance(Blk, Pred, BC); 132} 133 134void GRCoreEngine::ProcessBranch(Stmt* Condition, Stmt* Terminator, 135 GRBranchNodeBuilder& Builder) { 136 SubEngine.ProcessBranch(Condition, Terminator, Builder); 137} 138 139void GRCoreEngine::ProcessIndirectGoto(GRIndirectGotoNodeBuilder& Builder) { 140 SubEngine.ProcessIndirectGoto(Builder); 141} 142 143void GRCoreEngine::ProcessSwitch(GRSwitchNodeBuilder& Builder) { 144 SubEngine.ProcessSwitch(Builder); 145} 146 147void GRCoreEngine::ProcessCallEnter(GRCallEnterNodeBuilder &Builder) { 148 SubEngine.ProcessCallEnter(Builder); 149} 150 151void GRCoreEngine::ProcessCallExit(GRCallExitNodeBuilder &Builder) { 152 SubEngine.ProcessCallExit(Builder); 153} 154 155/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. 156bool GRCoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps) { 157 158 if (G->num_roots() == 0) { // Initialize the analysis by constructing 159 // the root if none exists. 160 161 CFGBlock* Entry = &(L->getCFG()->getEntry()); 162 163 assert (Entry->empty() && 164 "Entry block must be empty."); 165 166 assert (Entry->succ_size() == 1 && 167 "Entry block must have 1 successor."); 168 169 // Get the solitary successor. 170 CFGBlock* Succ = *(Entry->succ_begin()); 171 172 // Construct an edge representing the 173 // starting location in the function. 174 BlockEdge StartLoc(Entry, Succ, L); 175 176 // Set the current block counter to being empty. 177 WList->setBlockCounter(BCounterFactory.GetEmptyCounter()); 178 179 // Generate the root. 180 GenerateNode(StartLoc, getInitialState(L), 0); 181 } 182 183 while (Steps && WList->hasWork()) { 184 --Steps; 185 const GRWorkListUnit& WU = WList->Dequeue(); 186 187 // Set the current block counter. 188 WList->setBlockCounter(WU.getBlockCounter()); 189 190 // Retrieve the node. 191 ExplodedNode* Node = WU.getNode(); 192 193 // Dispatch on the location type. 194 switch (Node->getLocation().getKind()) { 195 case ProgramPoint::BlockEdgeKind: 196 HandleBlockEdge(cast<BlockEdge>(Node->getLocation()), Node); 197 break; 198 199 case ProgramPoint::BlockEntranceKind: 200 HandleBlockEntrance(cast<BlockEntrance>(Node->getLocation()), Node); 201 break; 202 203 case ProgramPoint::BlockExitKind: 204 assert (false && "BlockExit location never occur in forward analysis."); 205 break; 206 207 case ProgramPoint::CallEnterKind: 208 HandleCallEnter(cast<CallEnter>(Node->getLocation()), WU.getBlock(), 209 WU.getIndex(), Node); 210 break; 211 212 case ProgramPoint::CallExitKind: 213 HandleCallExit(cast<CallExit>(Node->getLocation()), Node); 214 break; 215 216 default: 217 assert(isa<PostStmt>(Node->getLocation())); 218 HandlePostStmt(cast<PostStmt>(Node->getLocation()), WU.getBlock(), 219 WU.getIndex(), Node); 220 break; 221 } 222 } 223 224 return WList->hasWork(); 225} 226 227void GRCoreEngine::HandleCallEnter(const CallEnter &L, const CFGBlock *Block, 228 unsigned Index, ExplodedNode *Pred) { 229 GRCallEnterNodeBuilder Builder(*this, Pred, L.getCallExpr(), L.getCallee(), 230 Block, Index); 231 ProcessCallEnter(Builder); 232} 233 234void GRCoreEngine::HandleCallExit(const CallExit &L, ExplodedNode *Pred) { 235 GRCallExitNodeBuilder Builder(*this, Pred); 236 ProcessCallExit(Builder); 237} 238 239void GRCoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) { 240 241 CFGBlock* Blk = L.getDst(); 242 243 // Check if we are entering the EXIT block. 244 if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { 245 246 assert (L.getLocationContext()->getCFG()->getExit().size() == 0 247 && "EXIT block cannot contain Stmts."); 248 249 // Process the final state transition. 250 GREndPathNodeBuilder Builder(Blk, Pred, this); 251 ProcessEndPath(Builder); 252 253 // This path is done. Don't enqueue any more nodes. 254 return; 255 } 256 257 // FIXME: Should we allow ProcessBlockEntrance to also manipulate state? 258 259 if (ProcessBlockEntrance(Blk, Pred, WList->getBlockCounter())) 260 GenerateNode(BlockEntrance(Blk, Pred->getLocationContext()), Pred->State, Pred); 261} 262 263void GRCoreEngine::HandleBlockEntrance(const BlockEntrance& L, 264 ExplodedNode* Pred) { 265 266 // Increment the block counter. 267 GRBlockCounter Counter = WList->getBlockCounter(); 268 Counter = BCounterFactory.IncrementCount(Counter, 269 Pred->getLocationContext()->getCurrentStackFrame(), 270 L.getBlock()->getBlockID()); 271 WList->setBlockCounter(Counter); 272 273 // Process the entrance of the block. 274 if (CFGElement E = L.getFirstElement()) { 275 GRStmtNodeBuilder Builder(L.getBlock(), 0, Pred, this, 276 SubEngine.getStateManager()); 277 ProcessStmt(E, Builder); 278 } 279 else 280 HandleBlockExit(L.getBlock(), Pred); 281} 282 283void GRCoreEngine::HandleBlockExit(CFGBlock * B, ExplodedNode* Pred) { 284 285 if (Stmt* Term = B->getTerminator()) { 286 switch (Term->getStmtClass()) { 287 default: 288 assert(false && "Analysis for this terminator not implemented."); 289 break; 290 291 case Stmt::BinaryOperatorClass: // '&&' and '||' 292 HandleBranch(cast<BinaryOperator>(Term)->getLHS(), Term, B, Pred); 293 return; 294 295 case Stmt::ConditionalOperatorClass: 296 HandleBranch(cast<ConditionalOperator>(Term)->getCond(), Term, B, Pred); 297 return; 298 299 // FIXME: Use constant-folding in CFG construction to simplify this 300 // case. 301 302 case Stmt::ChooseExprClass: 303 HandleBranch(cast<ChooseExpr>(Term)->getCond(), Term, B, Pred); 304 return; 305 306 case Stmt::DoStmtClass: 307 HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred); 308 return; 309 310 case Stmt::ForStmtClass: 311 HandleBranch(cast<ForStmt>(Term)->getCond(), Term, B, Pred); 312 return; 313 314 case Stmt::ContinueStmtClass: 315 case Stmt::BreakStmtClass: 316 case Stmt::GotoStmtClass: 317 break; 318 319 case Stmt::IfStmtClass: 320 HandleBranch(cast<IfStmt>(Term)->getCond(), Term, B, Pred); 321 return; 322 323 case Stmt::IndirectGotoStmtClass: { 324 // Only 1 successor: the indirect goto dispatch block. 325 assert (B->succ_size() == 1); 326 327 GRIndirectGotoNodeBuilder 328 builder(Pred, B, cast<IndirectGotoStmt>(Term)->getTarget(), 329 *(B->succ_begin()), this); 330 331 ProcessIndirectGoto(builder); 332 return; 333 } 334 335 case Stmt::ObjCForCollectionStmtClass: { 336 // In the case of ObjCForCollectionStmt, it appears twice in a CFG: 337 // 338 // (1) inside a basic block, which represents the binding of the 339 // 'element' variable to a value. 340 // (2) in a terminator, which represents the branch. 341 // 342 // For (1), subengines will bind a value (i.e., 0 or 1) indicating 343 // whether or not collection contains any more elements. We cannot 344 // just test to see if the element is nil because a container can 345 // contain nil elements. 346 HandleBranch(Term, Term, B, Pred); 347 return; 348 } 349 350 case Stmt::SwitchStmtClass: { 351 GRSwitchNodeBuilder builder(Pred, B, cast<SwitchStmt>(Term)->getCond(), 352 this); 353 354 ProcessSwitch(builder); 355 return; 356 } 357 358 case Stmt::WhileStmtClass: 359 HandleBranch(cast<WhileStmt>(Term)->getCond(), Term, B, Pred); 360 return; 361 } 362 } 363 364 assert (B->succ_size() == 1 && 365 "Blocks with no terminator should have at most 1 successor."); 366 367 GenerateNode(BlockEdge(B, *(B->succ_begin()), Pred->getLocationContext()), 368 Pred->State, Pred); 369} 370 371void GRCoreEngine::HandleBranch(Stmt* Cond, Stmt* Term, CFGBlock * B, 372 ExplodedNode* Pred) { 373 assert (B->succ_size() == 2); 374 375 GRBranchNodeBuilder Builder(B, *(B->succ_begin()), *(B->succ_begin()+1), 376 Pred, this); 377 378 ProcessBranch(Cond, Term, Builder); 379} 380 381void GRCoreEngine::HandlePostStmt(const PostStmt& L, CFGBlock* B, 382 unsigned StmtIdx, ExplodedNode* Pred) { 383 384 assert (!B->empty()); 385 386 if (StmtIdx == B->size()) 387 HandleBlockExit(B, Pred); 388 else { 389 GRStmtNodeBuilder Builder(B, StmtIdx, Pred, this, 390 SubEngine.getStateManager()); 391 ProcessStmt((*B)[StmtIdx], Builder); 392 } 393} 394 395/// GenerateNode - Utility method to generate nodes, hook up successors, 396/// and add nodes to the worklist. 397void GRCoreEngine::GenerateNode(const ProgramPoint& Loc, 398 const GRState* State, ExplodedNode* Pred) { 399 400 bool IsNew; 401 ExplodedNode* Node = G->getNode(Loc, State, &IsNew); 402 403 if (Pred) 404 Node->addPredecessor(Pred, *G); // Link 'Node' with its predecessor. 405 else { 406 assert (IsNew); 407 G->addRoot(Node); // 'Node' has no predecessor. Make it a root. 408 } 409 410 // Only add 'Node' to the worklist if it was freshly generated. 411 if (IsNew) WList->Enqueue(Node); 412} 413 414GRStmtNodeBuilder::GRStmtNodeBuilder(CFGBlock* b, unsigned idx, 415 ExplodedNode* N, GRCoreEngine* e, 416 GRStateManager &mgr) 417 : Eng(*e), B(*b), Idx(idx), Pred(N), Mgr(mgr), Auditor(0), 418 PurgingDeadSymbols(false), BuildSinks(false), HasGeneratedNode(false), 419 PointKind(ProgramPoint::PostStmtKind), Tag(0) { 420 Deferred.insert(N); 421 CleanedState = Pred->getState(); 422} 423 424GRStmtNodeBuilder::~GRStmtNodeBuilder() { 425 for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) 426 if (!(*I)->isSink()) 427 GenerateAutoTransition(*I); 428} 429 430void GRStmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) { 431 assert (!N->isSink()); 432 433 // Check if this node entered a callee. 434 if (isa<CallEnter>(N->getLocation())) { 435 // Still use the index of the CallExpr. It's needed to create the callee 436 // StackFrameContext. 437 Eng.WList->Enqueue(N, B, Idx); 438 return; 439 } 440 441 PostStmt Loc(getStmt(), N->getLocationContext()); 442 443 if (Loc == N->getLocation()) { 444 // Note: 'N' should be a fresh node because otherwise it shouldn't be 445 // a member of Deferred. 446 Eng.WList->Enqueue(N, B, Idx+1); 447 return; 448 } 449 450 bool IsNew; 451 ExplodedNode* Succ = Eng.G->getNode(Loc, N->State, &IsNew); 452 Succ->addPredecessor(N, *Eng.G); 453 454 if (IsNew) 455 Eng.WList->Enqueue(Succ, B, Idx+1); 456} 457 458ExplodedNode* GRStmtNodeBuilder::MakeNode(ExplodedNodeSet& Dst, Stmt* S, 459 ExplodedNode* Pred, const GRState* St, 460 ProgramPoint::Kind K) { 461 const GRState* PredState = GetState(Pred); 462 463 // If the state hasn't changed, don't generate a new node. 464 if (!BuildSinks && St == PredState && Auditor == 0) { 465 Dst.Add(Pred); 466 return NULL; 467 } 468 469 ExplodedNode* N = generateNode(S, St, Pred, K); 470 471 if (N) { 472 if (BuildSinks) 473 N->markAsSink(); 474 else { 475 if (Auditor && Auditor->Audit(N, Mgr)) 476 N->markAsSink(); 477 478 Dst.Add(N); 479 } 480 } 481 482 return N; 483} 484 485static ProgramPoint GetProgramPoint(const Stmt *S, ProgramPoint::Kind K, 486 const LocationContext *LC, const void *tag){ 487 switch (K) { 488 default: 489 assert(false && "Unhandled ProgramPoint kind"); 490 case ProgramPoint::PreStmtKind: 491 return PreStmt(S, LC, tag); 492 case ProgramPoint::PostStmtKind: 493 return PostStmt(S, LC, tag); 494 case ProgramPoint::PreLoadKind: 495 return PreLoad(S, LC, tag); 496 case ProgramPoint::PostLoadKind: 497 return PostLoad(S, LC, tag); 498 case ProgramPoint::PreStoreKind: 499 return PreStore(S, LC, tag); 500 case ProgramPoint::PostStoreKind: 501 return PostStore(S, LC, tag); 502 case ProgramPoint::PostLValueKind: 503 return PostLValue(S, LC, tag); 504 case ProgramPoint::PostPurgeDeadSymbolsKind: 505 return PostPurgeDeadSymbols(S, LC, tag); 506 } 507} 508 509ExplodedNode* 510GRStmtNodeBuilder::generateNodeInternal(const Stmt* S, const GRState* state, 511 ExplodedNode* Pred, 512 ProgramPoint::Kind K, 513 const void *tag) { 514 515 const ProgramPoint &L = GetProgramPoint(S, K, Pred->getLocationContext(),tag); 516 return generateNodeInternal(L, state, Pred); 517} 518 519ExplodedNode* 520GRStmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc, 521 const GRState* State, 522 ExplodedNode* Pred) { 523 bool IsNew; 524 ExplodedNode* N = Eng.G->getNode(Loc, State, &IsNew); 525 N->addPredecessor(Pred, *Eng.G); 526 Deferred.erase(Pred); 527 528 if (IsNew) { 529 Deferred.insert(N); 530 return N; 531 } 532 533 return NULL; 534} 535 536ExplodedNode* GRBranchNodeBuilder::generateNode(const GRState* State, 537 bool branch) { 538 539 // If the branch has been marked infeasible we should not generate a node. 540 if (!isFeasible(branch)) 541 return NULL; 542 543 bool IsNew; 544 545 ExplodedNode* Succ = 546 Eng.G->getNode(BlockEdge(Src,branch ? DstT:DstF,Pred->getLocationContext()), 547 State, &IsNew); 548 549 Succ->addPredecessor(Pred, *Eng.G); 550 551 if (branch) 552 GeneratedTrue = true; 553 else 554 GeneratedFalse = true; 555 556 if (IsNew) { 557 Deferred.push_back(Succ); 558 return Succ; 559 } 560 561 return NULL; 562} 563 564GRBranchNodeBuilder::~GRBranchNodeBuilder() { 565 if (!GeneratedTrue) generateNode(Pred->State, true); 566 if (!GeneratedFalse) generateNode(Pred->State, false); 567 568 for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) 569 if (!(*I)->isSink()) Eng.WList->Enqueue(*I); 570} 571 572 573ExplodedNode* 574GRIndirectGotoNodeBuilder::generateNode(const iterator& I, const GRState* St, 575 bool isSink) { 576 bool IsNew; 577 578 ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), 579 Pred->getLocationContext()), St, &IsNew); 580 581 Succ->addPredecessor(Pred, *Eng.G); 582 583 if (IsNew) { 584 585 if (isSink) 586 Succ->markAsSink(); 587 else 588 Eng.WList->Enqueue(Succ); 589 590 return Succ; 591 } 592 593 return NULL; 594} 595 596 597ExplodedNode* 598GRSwitchNodeBuilder::generateCaseStmtNode(const iterator& I, const GRState* St){ 599 600 bool IsNew; 601 602 ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), 603 Pred->getLocationContext()), St, &IsNew); 604 Succ->addPredecessor(Pred, *Eng.G); 605 606 if (IsNew) { 607 Eng.WList->Enqueue(Succ); 608 return Succ; 609 } 610 611 return NULL; 612} 613 614 615ExplodedNode* 616GRSwitchNodeBuilder::generateDefaultCaseNode(const GRState* St, bool isSink) { 617 618 // Get the block for the default case. 619 assert (Src->succ_rbegin() != Src->succ_rend()); 620 CFGBlock* DefaultBlock = *Src->succ_rbegin(); 621 622 bool IsNew; 623 624 ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock, 625 Pred->getLocationContext()), St, &IsNew); 626 Succ->addPredecessor(Pred, *Eng.G); 627 628 if (IsNew) { 629 if (isSink) 630 Succ->markAsSink(); 631 else 632 Eng.WList->Enqueue(Succ); 633 634 return Succ; 635 } 636 637 return NULL; 638} 639 640GREndPathNodeBuilder::~GREndPathNodeBuilder() { 641 // Auto-generate an EOP node if one has not been generated. 642 if (!HasGeneratedNode) { 643 // If we are in an inlined call, generate CallExit node. 644 if (Pred->getLocationContext()->getParent()) 645 GenerateCallExitNode(Pred->State); 646 else 647 generateNode(Pred->State); 648 } 649} 650 651ExplodedNode* 652GREndPathNodeBuilder::generateNode(const GRState* State, const void *tag, 653 ExplodedNode* P) { 654 HasGeneratedNode = true; 655 bool IsNew; 656 657 ExplodedNode* Node = Eng.G->getNode(BlockEntrance(&B, 658 Pred->getLocationContext(), tag), State, &IsNew); 659 660 Node->addPredecessor(P ? P : Pred, *Eng.G); 661 662 if (IsNew) { 663 Eng.G->addEndOfPath(Node); 664 return Node; 665 } 666 667 return NULL; 668} 669 670void GREndPathNodeBuilder::GenerateCallExitNode(const GRState *state) { 671 HasGeneratedNode = true; 672 // Create a CallExit node and enqueue it. 673 const StackFrameContext *LocCtx 674 = cast<StackFrameContext>(Pred->getLocationContext()); 675 const Stmt *CE = LocCtx->getCallSite(); 676 677 // Use the the callee location context. 678 CallExit Loc(CE, LocCtx); 679 680 bool isNew; 681 ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); 682 Node->addPredecessor(Pred, *Eng.G); 683 684 if (isNew) 685 Eng.WList->Enqueue(Node); 686} 687 688 689void GRCallEnterNodeBuilder::GenerateNode(const GRState *state, 690 const LocationContext *LocCtx) { 691 // Get the callee entry block. 692 const CFGBlock *Entry = &(LocCtx->getCFG()->getEntry()); 693 assert(Entry->empty()); 694 assert(Entry->succ_size() == 1); 695 696 // Get the solitary successor. 697 const CFGBlock *SuccB = *(Entry->succ_begin()); 698 699 // Construct an edge representing the starting location in the callee. 700 BlockEdge Loc(Entry, SuccB, LocCtx); 701 702 bool isNew; 703 ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); 704 Node->addPredecessor(const_cast<ExplodedNode*>(Pred), *Eng.G); 705 706 if (isNew) 707 Eng.WList->Enqueue(Node); 708} 709 710void GRCallExitNodeBuilder::GenerateNode(const GRState *state) { 711 // Get the callee's location context. 712 const StackFrameContext *LocCtx 713 = cast<StackFrameContext>(Pred->getLocationContext()); 714 715 PostStmt Loc(LocCtx->getCallSite(), LocCtx->getParent()); 716 bool isNew; 717 ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); 718 Node->addPredecessor(const_cast<ExplodedNode*>(Pred), *Eng.G); 719 if (isNew) 720 Eng.WList->Enqueue(Node, *const_cast<CFGBlock*>(LocCtx->getCallSiteBlock()), 721 LocCtx->getIndex() + 1); 722} 723