1 //===- CoreEngine.cpp - Path-Sensitive Dataflow Engine --------------------===// 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/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" 16 #include "clang/AST/Expr.h" 17 #include "clang/AST/ExprCXX.h" 18 #include "clang/AST/Stmt.h" 19 #include "clang/AST/StmtCXX.h" 20 #include "clang/Analysis/AnalysisDeclContext.h" 21 #include "clang/Analysis/CFG.h" 22 #include "clang/Analysis/ProgramPoint.h" 23 #include "clang/Basic/LLVM.h" 24 #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" 25 #include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h" 26 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 27 #include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h" 28 #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" 29 #include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" 30 #include "llvm/ADT/Optional.h" 31 #include "llvm/ADT/STLExtras.h" 32 #include "llvm/ADT/Statistic.h" 33 #include "llvm/Support/Casting.h" 34 #include "llvm/Support/ErrorHandling.h" 35 #include <algorithm> 36 #include <cassert> 37 #include <memory> 38 #include <utility> 39 40 using namespace clang; 41 using namespace ento; 42 43 #define DEBUG_TYPE "CoreEngine" 44 45 STATISTIC(NumSteps, 46 "The # of steps executed."); 47 STATISTIC(NumReachedMaxSteps, 48 "The # of times we reached the max number of steps."); 49 STATISTIC(NumPathsExplored, 50 "The # of paths explored by the analyzer."); 51 52 //===----------------------------------------------------------------------===// 53 // Core analysis engine. 54 //===----------------------------------------------------------------------===// 55 56 static std::unique_ptr<WorkList> generateWorkList(AnalyzerOptions &Opts, 57 SubEngine &subengine) { 58 switch (Opts.getExplorationStrategy()) { 59 case AnalyzerOptions::ExplorationStrategyKind::DFS: 60 return WorkList::makeDFS(); 61 case AnalyzerOptions::ExplorationStrategyKind::BFS: 62 return WorkList::makeBFS(); 63 case AnalyzerOptions::ExplorationStrategyKind::BFSBlockDFSContents: 64 return WorkList::makeBFSBlockDFSContents(); 65 case AnalyzerOptions::ExplorationStrategyKind::UnexploredFirst: 66 return WorkList::makeUnexploredFirst(); 67 case AnalyzerOptions::ExplorationStrategyKind::UnexploredFirstQueue: 68 return WorkList::makeUnexploredFirstPriorityQueue(); 69 case AnalyzerOptions::ExplorationStrategyKind::UnexploredFirstLocationQueue: 70 return WorkList::makeUnexploredFirstPriorityLocationQueue(); 71 } 72 } 73 74 CoreEngine::CoreEngine(SubEngine &subengine, FunctionSummariesTy *FS, 75 AnalyzerOptions &Opts) 76 : SubEng(subengine), WList(generateWorkList(Opts, subengine)), 77 BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {} 78 79 /// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. 80 bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, 81 ProgramStateRef InitState) { 82 if (G.num_roots() == 0) { // Initialize the analysis by constructing 83 // the root if none exists. 84 85 const CFGBlock *Entry = &(L->getCFG()->getEntry()); 86 87 assert(Entry->empty() && "Entry block must be empty."); 88 89 assert(Entry->succ_size() == 1 && "Entry block must have 1 successor."); 90 91 // Mark the entry block as visited. 92 FunctionSummaries->markVisitedBasicBlock(Entry->getBlockID(), 93 L->getDecl(), 94 L->getCFG()->getNumBlockIDs()); 95 96 // Get the solitary successor. 97 const CFGBlock *Succ = *(Entry->succ_begin()); 98 99 // Construct an edge representing the 100 // starting location in the function. 101 BlockEdge StartLoc(Entry, Succ, L); 102 103 // Set the current block counter to being empty. 104 WList->setBlockCounter(BCounterFactory.GetEmptyCounter()); 105 106 if (!InitState) 107 InitState = SubEng.getInitialState(L); 108 109 bool IsNew; 110 ExplodedNode *Node = G.getNode(StartLoc, InitState, false, &IsNew); 111 assert(IsNew); 112 G.addRoot(Node); 113 114 NodeBuilderContext BuilderCtx(*this, StartLoc.getDst(), Node); 115 ExplodedNodeSet DstBegin; 116 SubEng.processBeginOfFunction(BuilderCtx, Node, DstBegin, StartLoc); 117 118 enqueue(DstBegin); 119 } 120 121 // Check if we have a steps limit 122 bool UnlimitedSteps = Steps == 0; 123 // Cap our pre-reservation in the event that the user specifies 124 // a very large number of maximum steps. 125 const unsigned PreReservationCap = 4000000; 126 if(!UnlimitedSteps) 127 G.reserve(std::min(Steps,PreReservationCap)); 128 129 while (WList->hasWork()) { 130 if (!UnlimitedSteps) { 131 if (Steps == 0) { 132 NumReachedMaxSteps++; 133 break; 134 } 135 --Steps; 136 } 137 138 NumSteps++; 139 140 const WorkListUnit& WU = WList->dequeue(); 141 142 // Set the current block counter. 143 WList->setBlockCounter(WU.getBlockCounter()); 144 145 // Retrieve the node. 146 ExplodedNode *Node = WU.getNode(); 147 148 dispatchWorkItem(Node, Node->getLocation(), WU); 149 } 150 SubEng.processEndWorklist(); 151 return WList->hasWork(); 152 } 153 154 void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, 155 const WorkListUnit& WU) { 156 // Dispatch on the location type. 157 switch (Loc.getKind()) { 158 case ProgramPoint::BlockEdgeKind: 159 HandleBlockEdge(Loc.castAs<BlockEdge>(), Pred); 160 break; 161 162 case ProgramPoint::BlockEntranceKind: 163 HandleBlockEntrance(Loc.castAs<BlockEntrance>(), Pred); 164 break; 165 166 case ProgramPoint::BlockExitKind: 167 assert(false && "BlockExit location never occur in forward analysis."); 168 break; 169 170 case ProgramPoint::CallEnterKind: 171 HandleCallEnter(Loc.castAs<CallEnter>(), Pred); 172 break; 173 174 case ProgramPoint::CallExitBeginKind: 175 SubEng.processCallExit(Pred); 176 break; 177 178 case ProgramPoint::EpsilonKind: { 179 assert(Pred->hasSinglePred() && 180 "Assume epsilon has exactly one predecessor by construction"); 181 ExplodedNode *PNode = Pred->getFirstPred(); 182 dispatchWorkItem(Pred, PNode->getLocation(), WU); 183 break; 184 } 185 default: 186 assert(Loc.getAs<PostStmt>() || 187 Loc.getAs<PostInitializer>() || 188 Loc.getAs<PostImplicitCall>() || 189 Loc.getAs<CallExitEnd>() || 190 Loc.getAs<LoopExit>() || 191 Loc.getAs<PostAllocatorCall>()); 192 HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred); 193 break; 194 } 195 } 196 197 bool CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, 198 unsigned Steps, 199 ProgramStateRef InitState, 200 ExplodedNodeSet &Dst) { 201 bool DidNotFinish = ExecuteWorkList(L, Steps, InitState); 202 for (ExplodedGraph::eop_iterator I = G.eop_begin(), E = G.eop_end(); I != E; 203 ++I) { 204 Dst.Add(*I); 205 } 206 return DidNotFinish; 207 } 208 209 void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { 210 const CFGBlock *Blk = L.getDst(); 211 NodeBuilderContext BuilderCtx(*this, Blk, Pred); 212 213 // Mark this block as visited. 214 const LocationContext *LC = Pred->getLocationContext(); 215 FunctionSummaries->markVisitedBasicBlock(Blk->getBlockID(), 216 LC->getDecl(), 217 LC->getCFG()->getNumBlockIDs()); 218 219 // Check if we are entering the EXIT block. 220 if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { 221 assert(L.getLocationContext()->getCFG()->getExit().empty() && 222 "EXIT block cannot contain Stmts."); 223 224 // Get return statement.. 225 const ReturnStmt *RS = nullptr; 226 if (!L.getSrc()->empty()) { 227 CFGElement LastElement = L.getSrc()->back(); 228 if (Optional<CFGStmt> LastStmt = LastElement.getAs<CFGStmt>()) { 229 RS = dyn_cast<ReturnStmt>(LastStmt->getStmt()); 230 } else if (Optional<CFGAutomaticObjDtor> AutoDtor = 231 LastElement.getAs<CFGAutomaticObjDtor>()) { 232 RS = dyn_cast<ReturnStmt>(AutoDtor->getTriggerStmt()); 233 } 234 } 235 236 // Process the final state transition. 237 SubEng.processEndOfFunction(BuilderCtx, Pred, RS); 238 239 // This path is done. Don't enqueue any more nodes. 240 return; 241 } 242 243 // Call into the SubEngine to process entering the CFGBlock. 244 ExplodedNodeSet dstNodes; 245 BlockEntrance BE(Blk, Pred->getLocationContext()); 246 NodeBuilderWithSinks nodeBuilder(Pred, dstNodes, BuilderCtx, BE); 247 SubEng.processCFGBlockEntrance(L, nodeBuilder, Pred); 248 249 // Auto-generate a node. 250 if (!nodeBuilder.hasGeneratedNodes()) { 251 nodeBuilder.generateNode(Pred->State, Pred); 252 } 253 254 // Enqueue nodes onto the worklist. 255 enqueue(dstNodes); 256 } 257 258 void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, 259 ExplodedNode *Pred) { 260 // Increment the block counter. 261 const LocationContext *LC = Pred->getLocationContext(); 262 unsigned BlockId = L.getBlock()->getBlockID(); 263 BlockCounter Counter = WList->getBlockCounter(); 264 Counter = BCounterFactory.IncrementCount(Counter, LC->getStackFrame(), 265 BlockId); 266 WList->setBlockCounter(Counter); 267 268 // Process the entrance of the block. 269 if (Optional<CFGElement> E = L.getFirstElement()) { 270 NodeBuilderContext Ctx(*this, L.getBlock(), Pred); 271 SubEng.processCFGElement(*E, Pred, 0, &Ctx); 272 } 273 else 274 HandleBlockExit(L.getBlock(), Pred); 275 } 276 277 void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { 278 if (const Stmt *Term = B->getTerminator()) { 279 switch (Term->getStmtClass()) { 280 default: 281 llvm_unreachable("Analysis for this terminator not implemented."); 282 283 case Stmt::CXXBindTemporaryExprClass: 284 HandleCleanupTemporaryBranch( 285 cast<CXXBindTemporaryExpr>(B->getTerminator().getStmt()), B, Pred); 286 return; 287 288 // Model static initializers. 289 case Stmt::DeclStmtClass: 290 HandleStaticInit(cast<DeclStmt>(Term), B, Pred); 291 return; 292 293 case Stmt::BinaryOperatorClass: // '&&' and '||' 294 HandleBranch(cast<BinaryOperator>(Term)->getLHS(), Term, B, Pred); 295 return; 296 297 case Stmt::BinaryConditionalOperatorClass: 298 case Stmt::ConditionalOperatorClass: 299 HandleBranch(cast<AbstractConditionalOperator>(Term)->getCond(), 300 Term, B, Pred); 301 return; 302 303 // FIXME: Use constant-folding in CFG construction to simplify this 304 // case. 305 306 case Stmt::ChooseExprClass: 307 HandleBranch(cast<ChooseExpr>(Term)->getCond(), Term, B, Pred); 308 return; 309 310 case Stmt::CXXTryStmtClass: 311 // Generate a node for each of the successors. 312 // Our logic for EH analysis can certainly be improved. 313 for (CFGBlock::const_succ_iterator it = B->succ_begin(), 314 et = B->succ_end(); it != et; ++it) { 315 if (const CFGBlock *succ = *it) { 316 generateNode(BlockEdge(B, succ, Pred->getLocationContext()), 317 Pred->State, Pred); 318 } 319 } 320 return; 321 322 case Stmt::DoStmtClass: 323 HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred); 324 return; 325 326 case Stmt::CXXForRangeStmtClass: 327 HandleBranch(cast<CXXForRangeStmt>(Term)->getCond(), Term, B, Pred); 328 return; 329 330 case Stmt::ForStmtClass: 331 HandleBranch(cast<ForStmt>(Term)->getCond(), Term, B, Pred); 332 return; 333 334 case Stmt::ContinueStmtClass: 335 case Stmt::BreakStmtClass: 336 case Stmt::GotoStmtClass: 337 break; 338 339 case Stmt::IfStmtClass: 340 HandleBranch(cast<IfStmt>(Term)->getCond(), Term, B, Pred); 341 return; 342 343 case Stmt::IndirectGotoStmtClass: { 344 // Only 1 successor: the indirect goto dispatch block. 345 assert(B->succ_size() == 1); 346 347 IndirectGotoNodeBuilder 348 builder(Pred, B, cast<IndirectGotoStmt>(Term)->getTarget(), 349 *(B->succ_begin()), this); 350 351 SubEng.processIndirectGoto(builder); 352 return; 353 } 354 355 case Stmt::ObjCForCollectionStmtClass: 356 // In the case of ObjCForCollectionStmt, it appears twice in a CFG: 357 // 358 // (1) inside a basic block, which represents the binding of the 359 // 'element' variable to a value. 360 // (2) in a terminator, which represents the branch. 361 // 362 // For (1), subengines will bind a value (i.e., 0 or 1) indicating 363 // whether or not collection contains any more elements. We cannot 364 // just test to see if the element is nil because a container can 365 // contain nil elements. 366 HandleBranch(Term, Term, B, Pred); 367 return; 368 369 case Stmt::SwitchStmtClass: { 370 SwitchNodeBuilder builder(Pred, B, cast<SwitchStmt>(Term)->getCond(), 371 this); 372 373 SubEng.processSwitch(builder); 374 return; 375 } 376 377 case Stmt::WhileStmtClass: 378 HandleBranch(cast<WhileStmt>(Term)->getCond(), Term, B, Pred); 379 return; 380 } 381 } 382 383 assert(B->succ_size() == 1 && 384 "Blocks with no terminator should have at most 1 successor."); 385 386 generateNode(BlockEdge(B, *(B->succ_begin()), Pred->getLocationContext()), 387 Pred->State, Pred); 388 } 389 390 void CoreEngine::HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred) { 391 NodeBuilderContext BuilderCtx(*this, CE.getEntry(), Pred); 392 SubEng.processCallEnter(BuilderCtx, CE, Pred); 393 } 394 395 void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term, 396 const CFGBlock * B, ExplodedNode *Pred) { 397 assert(B->succ_size() == 2); 398 NodeBuilderContext Ctx(*this, B, Pred); 399 ExplodedNodeSet Dst; 400 SubEng.processBranch(Cond, Ctx, Pred, Dst, *(B->succ_begin()), 401 *(B->succ_begin() + 1)); 402 // Enqueue the new frontier onto the worklist. 403 enqueue(Dst); 404 } 405 406 void CoreEngine::HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, 407 const CFGBlock *B, 408 ExplodedNode *Pred) { 409 assert(B->succ_size() == 2); 410 NodeBuilderContext Ctx(*this, B, Pred); 411 ExplodedNodeSet Dst; 412 SubEng.processCleanupTemporaryBranch(BTE, Ctx, Pred, Dst, *(B->succ_begin()), 413 *(B->succ_begin() + 1)); 414 // Enqueue the new frontier onto the worklist. 415 enqueue(Dst); 416 } 417 418 void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, 419 ExplodedNode *Pred) { 420 assert(B->succ_size() == 2); 421 NodeBuilderContext Ctx(*this, B, Pred); 422 ExplodedNodeSet Dst; 423 SubEng.processStaticInitializer(DS, Ctx, Pred, Dst, 424 *(B->succ_begin()), *(B->succ_begin()+1)); 425 // Enqueue the new frontier onto the worklist. 426 enqueue(Dst); 427 } 428 429 void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, 430 ExplodedNode *Pred) { 431 assert(B); 432 assert(!B->empty()); 433 434 if (StmtIdx == B->size()) 435 HandleBlockExit(B, Pred); 436 else { 437 NodeBuilderContext Ctx(*this, B, Pred); 438 SubEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx); 439 } 440 } 441 442 /// generateNode - Utility method to generate nodes, hook up successors, 443 /// and add nodes to the worklist. 444 void CoreEngine::generateNode(const ProgramPoint &Loc, 445 ProgramStateRef State, 446 ExplodedNode *Pred) { 447 bool IsNew; 448 ExplodedNode *Node = G.getNode(Loc, State, false, &IsNew); 449 450 if (Pred) 451 Node->addPredecessor(Pred, G); // Link 'Node' with its predecessor. 452 else { 453 assert(IsNew); 454 G.addRoot(Node); // 'Node' has no predecessor. Make it a root. 455 } 456 457 // Only add 'Node' to the worklist if it was freshly generated. 458 if (IsNew) WList->enqueue(Node); 459 } 460 461 void CoreEngine::enqueueStmtNode(ExplodedNode *N, 462 const CFGBlock *Block, unsigned Idx) { 463 assert(Block); 464 assert(!N->isSink()); 465 466 // Check if this node entered a callee. 467 if (N->getLocation().getAs<CallEnter>()) { 468 // Still use the index of the CallExpr. It's needed to create the callee 469 // StackFrameContext. 470 WList->enqueue(N, Block, Idx); 471 return; 472 } 473 474 // Do not create extra nodes. Move to the next CFG element. 475 if (N->getLocation().getAs<PostInitializer>() || 476 N->getLocation().getAs<PostImplicitCall>()|| 477 N->getLocation().getAs<LoopExit>()) { 478 WList->enqueue(N, Block, Idx+1); 479 return; 480 } 481 482 if (N->getLocation().getAs<EpsilonPoint>()) { 483 WList->enqueue(N, Block, Idx); 484 return; 485 } 486 487 if ((*Block)[Idx].getKind() == CFGElement::NewAllocator) { 488 WList->enqueue(N, Block, Idx+1); 489 return; 490 } 491 492 // At this point, we know we're processing a normal statement. 493 CFGStmt CS = (*Block)[Idx].castAs<CFGStmt>(); 494 PostStmt Loc(CS.getStmt(), N->getLocationContext()); 495 496 if (Loc == N->getLocation().withTag(nullptr)) { 497 // Note: 'N' should be a fresh node because otherwise it shouldn't be 498 // a member of Deferred. 499 WList->enqueue(N, Block, Idx+1); 500 return; 501 } 502 503 bool IsNew; 504 ExplodedNode *Succ = G.getNode(Loc, N->getState(), false, &IsNew); 505 Succ->addPredecessor(N, G); 506 507 if (IsNew) 508 WList->enqueue(Succ, Block, Idx+1); 509 } 510 511 ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N, 512 const ReturnStmt *RS) { 513 // Create a CallExitBegin node and enqueue it. 514 const auto *LocCtx = cast<StackFrameContext>(N->getLocationContext()); 515 516 // Use the callee location context. 517 CallExitBegin Loc(LocCtx, RS); 518 519 bool isNew; 520 ExplodedNode *Node = G.getNode(Loc, N->getState(), false, &isNew); 521 Node->addPredecessor(N, G); 522 return isNew ? Node : nullptr; 523 } 524 525 void CoreEngine::enqueue(ExplodedNodeSet &Set) { 526 for (const auto I : Set) 527 WList->enqueue(I); 528 } 529 530 void CoreEngine::enqueue(ExplodedNodeSet &Set, 531 const CFGBlock *Block, unsigned Idx) { 532 for (const auto I : Set) 533 enqueueStmtNode(I, Block, Idx); 534 } 535 536 void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS) { 537 for (auto I : Set) { 538 // If we are in an inlined call, generate CallExitBegin node. 539 if (I->getLocationContext()->getParent()) { 540 I = generateCallExitBeginNode(I, RS); 541 if (I) 542 WList->enqueue(I); 543 } else { 544 // TODO: We should run remove dead bindings here. 545 G.addEndOfPath(I); 546 NumPathsExplored++; 547 } 548 } 549 } 550 551 void NodeBuilder::anchor() {} 552 553 ExplodedNode* NodeBuilder::generateNodeImpl(const ProgramPoint &Loc, 554 ProgramStateRef State, 555 ExplodedNode *FromN, 556 bool MarkAsSink) { 557 HasGeneratedNodes = true; 558 bool IsNew; 559 ExplodedNode *N = C.Eng.G.getNode(Loc, State, MarkAsSink, &IsNew); 560 N->addPredecessor(FromN, C.Eng.G); 561 Frontier.erase(FromN); 562 563 if (!IsNew) 564 return nullptr; 565 566 if (!MarkAsSink) 567 Frontier.Add(N); 568 569 return N; 570 } 571 572 void NodeBuilderWithSinks::anchor() {} 573 574 StmtNodeBuilder::~StmtNodeBuilder() { 575 if (EnclosingBldr) 576 for (const auto I : Frontier) 577 EnclosingBldr->addNodes(I); 578 } 579 580 void BranchNodeBuilder::anchor() {} 581 582 ExplodedNode *BranchNodeBuilder::generateNode(ProgramStateRef State, 583 bool branch, 584 ExplodedNode *NodePred) { 585 // If the branch has been marked infeasible we should not generate a node. 586 if (!isFeasible(branch)) 587 return nullptr; 588 589 ProgramPoint Loc = BlockEdge(C.Block, branch ? DstT:DstF, 590 NodePred->getLocationContext()); 591 ExplodedNode *Succ = generateNodeImpl(Loc, State, NodePred); 592 return Succ; 593 } 594 595 ExplodedNode* 596 IndirectGotoNodeBuilder::generateNode(const iterator &I, 597 ProgramStateRef St, 598 bool IsSink) { 599 bool IsNew; 600 ExplodedNode *Succ = 601 Eng.G.getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()), 602 St, IsSink, &IsNew); 603 Succ->addPredecessor(Pred, Eng.G); 604 605 if (!IsNew) 606 return nullptr; 607 608 if (!IsSink) 609 Eng.WList->enqueue(Succ); 610 611 return Succ; 612 } 613 614 ExplodedNode* 615 SwitchNodeBuilder::generateCaseStmtNode(const iterator &I, 616 ProgramStateRef St) { 617 bool IsNew; 618 ExplodedNode *Succ = 619 Eng.G.getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()), 620 St, false, &IsNew); 621 Succ->addPredecessor(Pred, Eng.G); 622 if (!IsNew) 623 return nullptr; 624 625 Eng.WList->enqueue(Succ); 626 return Succ; 627 } 628 629 ExplodedNode* 630 SwitchNodeBuilder::generateDefaultCaseNode(ProgramStateRef St, 631 bool IsSink) { 632 // Get the block for the default case. 633 assert(Src->succ_rbegin() != Src->succ_rend()); 634 CFGBlock *DefaultBlock = *Src->succ_rbegin(); 635 636 // Sanity check for default blocks that are unreachable and not caught 637 // by earlier stages. 638 if (!DefaultBlock) 639 return nullptr; 640 641 bool IsNew; 642 ExplodedNode *Succ = 643 Eng.G.getNode(BlockEdge(Src, DefaultBlock, Pred->getLocationContext()), 644 St, IsSink, &IsNew); 645 Succ->addPredecessor(Pred, Eng.G); 646 647 if (!IsNew) 648 return nullptr; 649 650 if (!IsSink) 651 Eng.WList->enqueue(Succ); 652 653 return Succ; 654 } 655