Index: include/clang/Analysis/AnalysisContext.h =================================================================== --- include/clang/Analysis/AnalysisContext.h +++ include/clang/Analysis/AnalysisContext.h @@ -428,6 +428,7 @@ bool addTemporaryDtors = false, bool addLifetime = false, bool addLoopExit = false, + bool addScopes = false, bool synthesizeBodies = false, bool addStaticInitBranches = false, bool addCXXNewAllocator = true, Index: include/clang/Analysis/CFG.h =================================================================== --- include/clang/Analysis/CFG.h +++ include/clang/Analysis/CFG.h @@ -57,6 +57,8 @@ // main kind Statement, Initializer, + ScopeBegin, + ScopeEnd, NewAllocator, LifetimeEnds, LoopExit, @@ -214,6 +216,55 @@ } }; +/// Represents beginning of a scope implicitly generated by the compiler on +/// encountering a CompoundStmt, IfStmt, ForStmt, WhileStmt and DoStmt. +class CFGScopeBegin : public CFGElement { +public: + CFGScopeBegin() {} + CFGScopeBegin(const Stmt *S) + : CFGElement(ScopeBegin, S) {} + + // Get statement that triggered a new scope. + const Stmt *getTriggerStmt() const { + return static_cast(Data1.getPointer()); + } + +private: + friend class CFGElement; + static bool isKind(const CFGElement &E) { + Kind kind = E.getKind(); + return kind == ScopeBegin; + } +}; + +/// Represents end of a scope implicitly generated by the compiler after the +/// last Stmt in a CompoundStmt, IfStmt, ForStmt, WhileStmt, DoStmt body or +/// after encountering a ReturnStmt, BreakStmt or ContinueStmt. +class CFGScopeEnd : public CFGElement { +public: + CFGScopeEnd() {} + CFGScopeEnd(const Stmt *TriggerStmt, const void *TerminatedStmt) + : CFGElement(ScopeEnd, TriggerStmt, TerminatedStmt) {} + + // Get statement that triggered end of previously created scope. + const Stmt *getTriggerStmt() const { + return static_cast(Data1.getPointer()); + } + // Get statement that was terminated by this CFGScopeEnd. For CompoundStmt, + // IfStmt, ForStmt, WhileStmt and DoStmt this would be just a TriggerStmt, + // while for BreakStmt and ContinueStmt that would be a terminated loop stmt. + const Stmt *getTerminatedStmt() const { + return static_cast(Data2.getPointer()); + } + +private: + friend class CFGElement; + static bool isKind(const CFGElement &E) { + Kind kind = E.getKind(); + return kind == ScopeEnd; + } +}; + /// CFGImplicitDtor - Represents C++ object destructor implicitly generated /// by compiler on various occasions. class CFGImplicitDtor : public CFGElement { @@ -732,6 +783,21 @@ Elements.push_back(CFGNewAllocator(NE), C); } + void appendScopeBegin(const Stmt *S, BumpVectorContext &C) { + Elements.push_back(CFGScopeBegin(S), C); + } + + void appendScopeEnd(const Stmt *TriggerStmt, const Stmt *TerminatedStmt, + BumpVectorContext &C) { + Elements.push_back(CFGScopeEnd(TriggerStmt, TerminatedStmt), C); + } + + void prependScopeEnd(const Stmt *TriggerStmt, const Stmt *TerminatedStmt, + BumpVectorContext &C) { + Elements.insert(Elements.rbegin(), 1, + CFGScopeEnd(TriggerStmt, TerminatedStmt), C); + } + void appendBaseDtor(const CXXBaseSpecifier *BS, BumpVectorContext &C) { Elements.push_back(CFGBaseDtor(BS), C); } @@ -824,6 +890,7 @@ bool AddLifetime; bool AddLoopExit; bool AddTemporaryDtors; + bool AddScopes; bool AddStaticInitBranches; bool AddCXXNewAllocator; bool AddCXXDefaultInitExprInCtors; @@ -848,7 +915,8 @@ AddEHEdges(false), AddInitializers(false), AddImplicitDtors(false), AddLifetime(false), AddLoopExit(false), - AddTemporaryDtors(false), AddStaticInitBranches(false), + AddTemporaryDtors(false), AddScopes(false), + AddStaticInitBranches(false), AddCXXNewAllocator(false), AddCXXDefaultInitExprInCtors(false) {} }; Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h =================================================================== --- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -220,6 +220,9 @@ /// \sa mayInlineCXXStandardLibrary Optional InlineCXXStandardLibrary; + /// \sa includeScopesInCFG + Optional IncludeScopesInCFG; + /// \sa mayInlineTemplateFunctions Optional InlineTemplateFunctions; @@ -428,6 +431,12 @@ /// the values "true" and "false". bool includeLoopExitInCFG(); + /// Returns whether or not scope information should be included in the CFG. + /// + /// This is controlled by the 'cfg-scope-info' config option, which accepts + /// the values "true" and "false". + bool includeScopesInCFG(); + /// Returns whether or not C++ standard library functions may be considered /// for inlining. /// Index: lib/Analysis/AnalysisDeclContext.cpp =================================================================== --- lib/Analysis/AnalysisDeclContext.cpp +++ lib/Analysis/AnalysisDeclContext.cpp @@ -69,6 +69,7 @@ bool addTemporaryDtors, bool addLifetime, bool addLoopExit, + bool addScopes, bool synthesizeBodies, bool addStaticInitBranch, bool addCXXNewAllocator, @@ -81,6 +82,7 @@ cfgBuildOptions.AddTemporaryDtors = addTemporaryDtors; cfgBuildOptions.AddLifetime = addLifetime; cfgBuildOptions.AddLoopExit = addLoopExit; + cfgBuildOptions.AddScopes = addScopes; cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch; cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator; } @@ -221,6 +223,14 @@ if (!builtCFG) { cfg = CFG::buildCFG(D, getBody(), &D->getASTContext(), cfgBuildOptions); + // If we tried to generate scopes in CFG and failed for some reason + // (e.g. faced GotoStmt), retry with scopes disabled. + if (!cfg && cfgBuildOptions.AddScopes) { + SaveAndRestore SavedAddScopes(cfgBuildOptions.AddScopes); + cfgBuildOptions.AddScopes = false; + cfg = CFG::buildCFG(D, getBody(), &D->getASTContext(), cfgBuildOptions); + } + // Even when the cfg is not successfully built, we don't // want to try building it again. builtCFG = true; Index: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ lib/Analysis/CFG.cpp @@ -406,6 +406,9 @@ // This can point either to a try or a __try block. The frontend forbids // mixing both kinds in one function, so having one for both is enough. CFGBlock *TryTerminatedBlock; + Stmt *TerminatedLoop; + bool InSwitchNest; + bool InForIncExpr; // Current position in local scope. LocalScope::const_iterator ScopePos; @@ -443,8 +446,9 @@ const CFG::BuildOptions &buildOpts) : Context(astContext), cfg(new CFG()), // crew a new CFG Block(nullptr), Succ(nullptr), SwitchTerminatedBlock(nullptr), - DefaultCaseBlock(nullptr), TryTerminatedBlock(nullptr), badCFG(false), - BuildOpts(buildOpts), switchExclusivelyCovered(false), + DefaultCaseBlock(nullptr), TryTerminatedBlock(nullptr), + TerminatedLoop(nullptr), InSwitchNest(false), InForIncExpr(false), + badCFG(false), BuildOpts(buildOpts), switchExclusivelyCovered(false), switchCond(nullptr), cachedEntry(nullptr), lastLookup(nullptr) {} // buildCFG - Used by external clients to construct the CFG. @@ -520,6 +524,8 @@ CFGBlock *VisitChildren(Stmt *S); CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc); + void CreateScopeEndBlockForIfStmt(IfStmt *I); + /// When creating the CFG for temporary destructors, we want to mirror the /// branch structure of the corresponding constructor calls. /// Thus, while visiting a statement for temporary destructors, we keep a @@ -643,6 +649,26 @@ void appendNewAllocator(CFGBlock *B, CXXNewExpr *NE) { B->appendNewAllocator(NE, cfg->getBumpVectorContext()); } + bool needAddScopes() { + // FIXME: Support SwitchStmt + return !InSwitchNest && !InForIncExpr && BuildOpts.AddScopes; + } + void appendScopeBegin(CFGBlock *B, const Stmt *S) { + if (B && needAddScopes()) + B->appendScopeBegin(S, cfg->getBumpVectorContext()); + } + void appendScopeEnd(CFGBlock *B, const Stmt *TriggerStmt, + const Stmt *TerminatedStmt) { + if (B && needAddScopes()) + B->appendScopeEnd(TriggerStmt, TerminatedStmt, + cfg->getBumpVectorContext()); + } + void prependScopeEnd(CFGBlock *B, const Stmt *TriggerStmt, + const Stmt *TerminatedStmt) { + if (B && needAddScopes()) + B->prependScopeEnd(TriggerStmt, TerminatedStmt, + cfg->getBumpVectorContext()); + } void appendBaseDtor(CFGBlock *B, const CXXBaseSpecifier *BS) { B->appendBaseDtor(BS, cfg->getBumpVectorContext()); } @@ -1426,7 +1452,7 @@ } /// addLocalScopeForStmt - Add LocalScope to local scopes tree for statement -/// that should create implicit scope (e.g. if/else substatements). +/// that should create implicit scope (e.g. if/else substatements). void CFGBuilder::addLocalScopeForStmt(Stmt *S) { if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) return; @@ -1978,6 +2004,7 @@ // AST. This means that the CFG cannot be constructed. if (BreakJumpTarget.block) { addAutomaticObjHandling(ScopePos, BreakJumpTarget.scopePosition, B); + prependScopeEnd(Block, B, TerminatedLoop); addSuccessor(Block, BreakJumpTarget.block); } else badCFG = true; @@ -2106,17 +2133,28 @@ return addStmt(C->getCond()); } +// If some block is terminated by break, continue or return don't need to emit +// ScopeEnd right away and we leave this to corresponding visitor. +static bool shouldDeferScopeEnd(Stmt *S) { + return isa(S) || isa(S) || isa(S); +} CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) { + SaveAndRestore save_break(BreakJumpTarget); LocalScope::const_iterator scopeBeginPos = ScopePos; addLocalScopeForStmt(C); - if (!C->body_empty() && !isa(*C->body_rbegin())) { - // If the body ends with a ReturnStmt, the dtors will be added in - // VisitReturnStmt. - addAutomaticObjHandling(ScopePos, scopeBeginPos, C); + if (!C->body_empty() && needAddScopes() && + !shouldDeferScopeEnd(*C->body_rbegin())) { + autoCreateBlock(); + appendScopeEnd(Block, C, C); } + if (!C->body_empty() && !isa(*C->body_rbegin())) + // If the body ends with a ReturnStmt, the dtors will be added in + // VisitReturnStmt. + addAutomaticObjHandling(ScopePos, scopeBeginPos, C); + CFGBlock *LastBlock = Block; for (CompoundStmt::reverse_body_iterator I=C->body_rbegin(), E=C->body_rend(); @@ -2130,6 +2168,12 @@ return nullptr; } + if (needAddScopes() && !C->body_empty()) { + if (!LastBlock) + LastBlock = createBlock(); + appendScopeBegin(LastBlock, C); + } + return LastBlock; } @@ -2323,6 +2367,12 @@ return B; } +void CFGBuilder::CreateScopeEndBlockForIfStmt(IfStmt *I) { + autoCreateBlock(); + prependScopeEnd(Block, I, I); + Succ = Block; +} + CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { // We may see an if statement in the middle of a basic block, or it may be the // first statement we are processing. In either case, we create a new basic @@ -2354,6 +2404,10 @@ return nullptr; } + // If the IfStmt contains a condition variable, its scope to the CFG. + if (const DeclStmt *DS = I->getConditionVariableDeclStmt()) + appendScopeEnd(Succ, DS, DS); + // Process the false branch. CFGBlock *ElseBlock = Succ; @@ -2366,9 +2420,11 @@ // If branch is not a compound statement create implicit scope // and add destructors. - if (!isa(Else)) + if (!isa(Else)) { addLocalScopeAndDtors(Else); - + if (needAddScopes() && !shouldDeferScopeEnd(Else)) + CreateScopeEndBlockForIfStmt(I); + } ElseBlock = addStmt(Else); if (!ElseBlock) // Can occur when the Else body has all NullStmts. @@ -2377,6 +2433,9 @@ if (badCFG) return nullptr; } + + if (!isa(Else)) + appendScopeBegin(ElseBlock, I); } // Process the true branch. @@ -2389,8 +2448,11 @@ // If branch is not a compound statement create implicit scope // and add destructors. - if (!isa(Then)) + if (!isa(Then)) { addLocalScopeAndDtors(Then); + if (needAddScopes() && !shouldDeferScopeEnd(Then)) + CreateScopeEndBlockForIfStmt(I); + } ThenBlock = addStmt(Then); @@ -2404,6 +2466,9 @@ if (badCFG) return nullptr; } + + if (!isa(Then)) + appendScopeBegin(ThenBlock, I); } // Specially handle "if (expr1 || ...)" and "if (expr1 && ...)" by @@ -2445,6 +2510,7 @@ if (const DeclStmt* DS = I->getConditionVariableDeclStmt()) { autoCreateBlock(); LastBlock = addStmt(const_cast(DS)); + appendScopeBegin(LastBlock, DS); } } @@ -2473,8 +2539,10 @@ // If the one of the destructors does not return, we already have the Exit // block as a successor. - if (!Block->hasNoReturnElement()) + if (!Block->hasNoReturnElement()) { + prependScopeEnd(Block, R, R); addSuccessor(Block, &cfg->getExit()); + } // Add the return statement to the block. This may create new blocks if R // contains control-flow (short-circuit operations). @@ -2647,6 +2715,11 @@ } CFGBlock *CFGBuilder::VisitGotoStmt(GotoStmt *G) { + // FIXME: support scopes for GotoStmt + if (needAddScopes()) { + badCFG = true; + return Block; + } // Goto is a control-flow statement. Thus we stop processing the current // block and create a new one. @@ -2721,6 +2794,8 @@ TransitionBlock->setLoopTarget(F); if (Stmt *I = F->getInc()) { + SaveAndRestore save_InForIncExpr(InForIncExpr); + InForIncExpr = true; // Generate increment code in its own basic block. This is the target of // continue statements. Succ = addStmt(I); @@ -2747,9 +2822,14 @@ if (!isa(F->getBody())) addLocalScopeAndDtors(F->getBody()); - // Now populate the body block, and in the process create new blocks as we - // walk the body of the loop. - BodyBlock = addStmt(F->getBody()); + { + SaveAndRestore save_TerminatedLoop(TerminatedLoop); + TerminatedLoop = isa(F->getBody()) ? F->getBody() : F; + + // Now populate the body block, and in the process create new blocks as we + // walk the body of the loop. + BodyBlock = addStmt(F->getBody()); + } if (!BodyBlock) { // In the case of "for (...;...;...);" we can have a null BodyBlock. @@ -2759,7 +2839,10 @@ else if (badCFG) return nullptr; } - + + if (!isa(F->getBody())) + appendScopeBegin(BodyBlock, F); + // Because of short-circuit evaluation, the condition of the loop can span // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that // evaluate the condition. @@ -2820,6 +2903,9 @@ // Link up the loop-back block to the entry condition block. addSuccessor(TransitionBlock, EntryConditionBlock); + + if (!isa(F->getBody())) + prependScopeEnd(TransitionBlock, F, F); // The condition block is the implicit successor for any code above the loop. Succ = EntryConditionBlock; @@ -2828,7 +2914,10 @@ // statements. This block can also contain statements that precede the loop. if (Stmt *I = F->getInit()) { Block = createBlock(); - return addStmt(I); + CFGBlock *InitBlock = addStmt(I); + appendScopeBegin(EntryConditionBlock, I); + appendScopeEnd(LoopSuccessor, I, I); + return InitBlock; } // There is no loop initialization. We are thus basically a while loop. @@ -2933,7 +3022,12 @@ BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); ContinueJumpTarget = JumpTarget(Succ, ScopePos); - CFGBlock *BodyBlock = addStmt(S->getBody()); + CFGBlock *BodyBlock = NULL; + { + SaveAndRestore save_TerminatedLoop(TerminatedLoop); + TerminatedLoop = isa(S->getBody()) ? S->getBody() : S; + BodyBlock = addStmt(S->getBody()); + } if (!BodyBlock) BodyBlock = ContinueJumpTarget.block; // can happen for "for (X in Y) ;" @@ -3044,6 +3138,11 @@ CFGBlock *BodyBlock = nullptr, *TransitionBlock = nullptr; + // If this block contains a condition variable, add scope end to it now. + if (VarDecl *VD = W->getConditionVariable()) + if (Expr *Init = VD->getInit()) + appendScopeEnd(LoopSuccessor, Init, Init); + // Process the loop body. { assert(W->getBody()); @@ -3070,13 +3169,21 @@ if (!isa(W->getBody())) addLocalScopeAndDtors(W->getBody()); - // Create the body. The returned block is the entry to the loop body. - BodyBlock = addStmt(W->getBody()); + { + SaveAndRestore save_TerminatedLoop(TerminatedLoop); + TerminatedLoop = isa(W->getBody()) ? W->getBody() : W; + + // Create the body. The returned block is the entry to the loop body. + BodyBlock = addStmt(W->getBody()); + } if (!BodyBlock) BodyBlock = ContinueJumpTarget.block; // can happen for "while(...) ;" else if (Block && badCFG) return nullptr; + + if (!isa(W->getBody())) + appendScopeBegin(BodyBlock, W); } // Because of short-circuit evaluation, the condition of the loop can span @@ -3113,6 +3220,7 @@ autoCreateBlock(); appendStmt(Block, W->getConditionVariableDeclStmt()); EntryConditionBlock = addStmt(Init); + appendScopeBegin(EntryConditionBlock, Init); assert(Block == EntryConditionBlock); } } @@ -3135,6 +3243,9 @@ // Link up the loop-back block to the entry condition block. addSuccessor(TransitionBlock, EntryConditionBlock); + if (!isa(W->getBody())) + prependScopeEnd(TransitionBlock, W, W); + // There can be no more statements in the condition block since we loop back // to this block. NULL out Block to force lazy creation of another block. Block = nullptr; @@ -3254,8 +3365,13 @@ if (!isa(D->getBody())) addLocalScopeAndDtors(D->getBody()); - // Create the body. The returned block is the entry to the loop body. - BodyBlock = addStmt(D->getBody()); + { + SaveAndRestore save_TerminatedLoop(TerminatedLoop); + TerminatedLoop = isa(D->getBody()) ? D->getBody() : D; + + // Create the body. The returned block is the entry to the loop body. + BodyBlock = addStmt(D->getBody()); + } if (!BodyBlock) BodyBlock = EntryConditionBlock; // can happen for "do ; while(...)" @@ -3264,6 +3380,11 @@ return nullptr; } + if (!isa(D->getBody())) { + appendScopeBegin(BodyBlock, D); + if (!shouldDeferScopeEnd(D->getBody())) + prependScopeEnd(BodyBlock, D, D); + } // Add an intermediate block between the BodyBlock and the // ExitConditionBlock to represent the "loop back" transition. Create an // empty block to represent the transition block for looping back to the @@ -3308,6 +3429,7 @@ // incomplete AST. This means the CFG cannot be constructed. if (ContinueJumpTarget.block) { addAutomaticObjHandling(ScopePos, ContinueJumpTarget.scopePosition, C); + prependScopeEnd(Block, C, TerminatedLoop); addSuccessor(Block, ContinueJumpTarget.block); } else badCFG = true; @@ -3411,24 +3533,37 @@ if (!isa(Terminator->getBody())) addLocalScopeAndDtors(Terminator->getBody()); - addStmt(Terminator->getBody()); - if (Block) { - if (badCFG) - return nullptr; + // If the SwitchStmt contains a condition variable, end its scope after switch. + if (VarDecl *VD = Terminator->getConditionVariable()) { + if (VD->getInit()) { + auto VDDeclStmt = Terminator->getConditionVariableDeclStmt(); + appendScopeEnd(Succ, VDDeclStmt, VDDeclStmt); + } } - // If we have no "default:" case, the default transition is to the code - // following the switch body. Moreover, take into account if all the - // cases of a switch are covered (e.g., switching on an enum value). - // - // Note: We add a successor to a switch that is considered covered yet has no - // case statements if the enumeration has no enumerators. - bool SwitchAlwaysHasSuccessor = false; - SwitchAlwaysHasSuccessor |= switchExclusivelyCovered; - SwitchAlwaysHasSuccessor |= Terminator->isAllEnumCasesCovered() && - Terminator->getSwitchCaseList(); - addSuccessor(SwitchTerminatedBlock, DefaultCaseBlock, - !SwitchAlwaysHasSuccessor); + { + SaveAndRestore save_InSwitchNest(InSwitchNest); + InSwitchNest = true; + + addStmt(Terminator->getBody()); + if (Block) { + if (badCFG) + return nullptr; + } + + // If we have no "default:" case, the default transition is to the code + // following the switch body. Moreover, take into account if all the + // cases of a switch are covered (e.g., switching on an enum value). + // + // Note: We add a successor to a switch that is considered covered yet has + // no case statements if the enumeration has no enumerators. + bool SwitchAlwaysHasSuccessor = false; + SwitchAlwaysHasSuccessor |= switchExclusivelyCovered; + SwitchAlwaysHasSuccessor |= + Terminator->isAllEnumCasesCovered() && Terminator->getSwitchCaseList(); + addSuccessor(SwitchTerminatedBlock, DefaultCaseBlock, + !SwitchAlwaysHasSuccessor); + } // Add the terminator and condition in the switch block. SwitchTerminatedBlock->setTerminator(Terminator); @@ -3440,8 +3575,10 @@ if (VarDecl *VD = Terminator->getConditionVariable()) { if (Expr *Init = VD->getInit()) { autoCreateBlock(); - appendStmt(Block, Terminator->getConditionVariableDeclStmt()); + auto VDDeclStmt = Terminator->getConditionVariableDeclStmt(); + appendStmt(Block, VDDeclStmt); LastBlock = addStmt(Init); + appendScopeBegin(LastBlock, VDDeclStmt); } } @@ -3708,6 +3845,8 @@ LocalScope::const_iterator ContinueScopePos = ScopePos; + Stmt *LoopVarStmt = S->getLoopVarStmt(); + // "for" is a control-flow statement. Thus we stop processing the current // block. CFGBlock *LoopSuccessor = nullptr; @@ -3760,6 +3899,7 @@ // continue statements. Block = nullptr; Succ = addStmt(S->getInc()); + prependScopeEnd(Succ, LoopVarStmt, LoopVarStmt); if (badCFG) return nullptr; ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos); @@ -3777,17 +3917,29 @@ // Add implicit scope and dtors for loop variable. addLocalScopeAndDtors(S->getLoopVarStmt()); - // Populate a new block to contain the loop body and loop variable. - addStmt(S->getBody()); + CFGBlock *BodyBlock = nullptr; + { + SaveAndRestore save_TerminatedLoop(TerminatedLoop); + TerminatedLoop = isa(S->getBody()) ? S->getBody() : S; + // Populate a new block to contain the loop body and loop variable. + BodyBlock = addStmt(S->getBody()); + } if (badCFG) return nullptr; CFGBlock *LoopVarStmtBlock = addStmt(S->getLoopVarStmt()); if (badCFG) return nullptr; + if (!isa(S->getBody())) { + appendScopeBegin(BodyBlock, S); + if (!shouldDeferScopeEnd(S->getBody())) + prependScopeEnd(BodyBlock, S, S); + } + // This new body block is a successor to our condition block. addSuccessor(ConditionBlock, KnownVal.isFalse() ? nullptr : LoopVarStmtBlock); + appendScopeBegin(LoopVarStmtBlock, LoopVarStmt); } // Link up the condition block with the code that follows the loop (the @@ -4177,6 +4329,8 @@ case CFGElement::NewAllocator: case CFGElement::LoopExit: case CFGElement::LifetimeEnds: + case CFGElement::ScopeBegin: + case CFGElement::ScopeEnd: llvm_unreachable("getDestructorDecl should only be used with " "ImplicitDtors"); case CFGElement::AutomaticObjectDtor: { @@ -4600,6 +4754,16 @@ } else if (Optional LE = E.getAs()) { const Stmt *LoopStmt = LE->getLoopStmt(); OS << LoopStmt->getStmtClassName() << " (LoopExit)\n"; + } else if (Optional SB = E.getAs()) { + OS << "CFGScopeBegin("; + if (const Stmt *S = SB->getTriggerStmt()) + OS << S->getStmtClassName(); + OS << ")\n"; + } else if (Optional SE = E.getAs()) { + OS << "CFGScopeEnd("; + if (const Stmt *S = SE->getTriggerStmt()) + OS << S->getStmtClassName(); + OS << ")\n"; } else if (Optional NE = E.getAs()) { OS << "CFGNewAllocator("; if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr()) Index: lib/StaticAnalyzer/Core/AnalysisManager.cpp =================================================================== --- lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -28,6 +28,7 @@ Options.includeTemporaryDtorsInCFG(), Options.includeLifetimeInCFG(), Options.includeLoopExitInCFG(), + Options.includeScopesInCFG(), Options.shouldSynthesizeBodies(), Options.shouldConditionalizeStaticInitializers(), /*addCXXNewAllocator=*/true, Index: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp =================================================================== --- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -188,6 +188,12 @@ /* Default = */ false); } +bool AnalyzerOptions::includeScopesInCFG() { + return getBooleanOption(IncludeScopesInCFG, + "cfg-scopes", + /* Default = */ false); +} + bool AnalyzerOptions::mayInlineCXXStandardLibrary() { return getBooleanOption(InlineCXXStandardLibrary, "c++-stdlib-inlining", Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -368,6 +368,8 @@ ProcessLoopExit(E.castAs().getLoopStmt(), Pred); return; case CFGElement::LifetimeEnds: + case CFGElement::ScopeBegin: + case CFGElement::ScopeEnd: return; } } Index: lib/StaticAnalyzer/Core/PathDiagnostic.cpp =================================================================== --- lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -578,8 +578,10 @@ } case CFGElement::TemporaryDtor: case CFGElement::NewAllocator: - llvm_unreachable("not yet implemented!"); case CFGElement::LifetimeEnds: + case CFGElement::ScopeBegin: + case CFGElement::ScopeEnd: + llvm_unreachable("not yet implemented!"); case CFGElement::LoopExit: llvm_unreachable("CFGElement kind should not be on callsite!"); } Index: test/Analysis/analyzer-config.c =================================================================== --- test/Analysis/analyzer-config.c +++ test/Analysis/analyzer-config.c @@ -15,6 +15,7 @@ // CHECK-NEXT: cfg-implicit-dtors = true // CHECK-NEXT: cfg-lifetime = false // CHECK-NEXT: cfg-loopexit = false +// CHECK-NEXT: cfg-scopes = false // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -31,4 +32,4 @@ // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 19 +// CHECK-NEXT: num-entries = 20 Index: test/Analysis/analyzer-config.cpp =================================================================== --- test/Analysis/analyzer-config.cpp +++ test/Analysis/analyzer-config.cpp @@ -26,6 +26,7 @@ // CHECK-NEXT: cfg-implicit-dtors = true // CHECK-NEXT: cfg-lifetime = false // CHECK-NEXT: cfg-loopexit = false +// CHECK-NEXT: cfg-scopes = false // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -42,4 +43,4 @@ // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 24 +// CHECK-NEXT: num-entries = 25 Index: test/Analysis/scopes-cfg-output.cpp =================================================================== --- /dev/null +++ test/Analysis/scopes-cfg-output.cpp @@ -0,0 +1,1098 @@ +// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -analyze -analyzer-checker=debug.DumpCFG -analyzer-config cfg-scopes=true %s > %t 2>&1 +// RUN: FileCheck --input-file=%t %s + +class A { +public: +// CHECK: [B1 (ENTRY)] +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 + A() {} + +// CHECK: [B1 (ENTRY)] +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 + ~A() {} + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: 1 +// CHECK-NEXT: 3: return [B1.2]; +// CHECK-NEXT: 4: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 + operator int() const { return 1; } +}; + +int getX(); +extern const bool UV; + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: a +// CHECK-NEXT: 5: [B1.4] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 6: const A &b = a; +// CHECK-NEXT: 7: A() (CXXConstructExpr, class A) +// CHECK-NEXT: 8: [B1.7] (BindTemporary) +// CHECK-NEXT: 9: [B1.8] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 10: [B1.9] +// CHECK-NEXT: 11: const A &c = A(); +// CHECK-NEXT: 12: [B1.11].~A() (Implicit destructor) +// CHECK-NEXT: 13: [B1.3].~A() (Implicit destructor) +// CHECK-NEXT: 14: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +void test_const_ref() { + A a; + const A& b = a; + const A& c = A(); +} + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A [2]) +// CHECK-NEXT: 3: A a[2]; +// CHECK-NEXT: 4: (CXXConstructExpr, class A [0]) +// CHECK-NEXT: 5: A b[0]; +// CHECK-NEXT: 6: [B1.3].~A() (Implicit destructor) +// CHECK-NEXT: 7: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_array() { + A a[2]; + A b[0]; +} + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 5: (CXXConstructExpr, class A) +// CHECK-NEXT: 6: A c; +// CHECK-NEXT: 7: (CXXConstructExpr, class A) +// CHECK-NEXT: 8: A d; +// CHECK-NEXT: 9: [B1.8].~A() (Implicit destructor) +// CHECK-NEXT: 10: [B1.6].~A() (Implicit destructor) +// CHECK-NEXT: 11: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 12: (CXXConstructExpr, class A) +// CHECK-NEXT: 13: A b; +// CHECK-NEXT: 14: [B1.13].~A() (Implicit destructor) +// CHECK-NEXT: 15: [B1.3].~A() (Implicit destructor) +// CHECK-NEXT: 16: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_scope() { + A a; + { A c; + A d; + } + A b; +} + +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B1.2].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B3.5].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B3.3].~A() (Implicit destructor) +// CHECK-NEXT: 6: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B3.5].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B3.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A b; +// CHECK-NEXT: 6: UV +// CHECK-NEXT: 7: [B3.6] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B3.7] +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (2): B2 B1 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B2 +void test_return() { + A a; + A b; + if (UV) return; + A c; +} + +// CHECK: [B5 (ENTRY)] +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: 2: [B4.8].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B4.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B2 B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: 4: [B2.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: 4: [B3.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 5: a +// CHECK-NEXT: 6: [B4.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B4.6] (CXXConstructExpr, class A) +// CHECK-NEXT: 8: A b = a; +// CHECK-NEXT: 9: b +// CHECK-NEXT: 10: [B4.9] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 11: [B4.10].operator int +// CHECK-NEXT: 12: [B4.10] +// CHECK-NEXT: 13: [B4.12] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 14: [B4.13] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B4.14] +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (2): B3 B2 +// CHECK: [B0 (EXIT)] +void test_if_implicit_scope() { + A a; + if (A b = a) + A c; + else A c; +} + +// CHECK: [B8 (ENTRY)] +// CHECK-NEXT: Succs (1): B7 +// CHECK: [B1] +// CHECK-NEXT: l1: +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B1.2].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B6.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B7.2].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B2 B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A b; +// CHECK-NEXT: 3: [B2.2].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B6.4].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: [B6.4].~A() (Implicit destructor) +// CHECK-NEXT: T: goto l1; +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B4] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B4.2] +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (2): B3 B2 +// CHECK: [B5] +// CHECK-NEXT: 1: [B6.4].~A() (Implicit destructor) +// CHECK-NEXT: 2: [B6.2].~A() (Implicit destructor) +// CHECK-NEXT: T: goto l0; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B6 +// CHECK: [B6] +// CHECK-NEXT: l0: +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A b; +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A a; +// CHECK-NEXT: 5: UV +// CHECK-NEXT: 6: [B6.5] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B6.6] +// CHECK-NEXT: Preds (2): B7 B5 +// CHECK-NEXT: Succs (2): B5 B4 +// CHECK: [B7] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (1): B6 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_goto() { + A a; +l0: + A b; + { + A a; + if (UV) + goto l0; + if (UV) + goto l1; + A b; + } +l1: + A c; +} + +// CHECK: [B9 (ENTRY)] +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: 2: [B8.8].~A() (Implicit destructor) +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A e; +// CHECK-NEXT: 5: [B1.4].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B8.3].~A() (Implicit destructor) +// CHECK-NEXT: 7: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B2 B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: [B2.2].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B4.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B4.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B8.8].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B8.3].~A() (Implicit destructor) +// CHECK-NEXT: 6: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: 4: UV +// CHECK-NEXT: 5: [B4.4] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B4.5] +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (2): B3 B2 +// CHECK: [B5] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: [B5.2].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B7.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B6] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B7.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B8.8].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B8.3].~A() (Implicit destructor) +// CHECK-NEXT: 6: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: 4: UV +// CHECK-NEXT: 5: [B7.4] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B7.5] +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (2): B6 B5 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 5: a +// CHECK-NEXT: 6: [B8.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B8.6] (CXXConstructExpr, class A) +// CHECK-NEXT: 8: A b = a; +// CHECK-NEXT: 9: b +// CHECK-NEXT: 10: [B8.9] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 11: [B8.10].operator int +// CHECK-NEXT: 12: [B8.10] +// CHECK-NEXT: 13: [B8.12] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 14: [B8.13] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B8.14] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B7 B4 +// CHECK: [B0 (EXIT)] +void test_if_jumps() { + A a; + if (A b = a) { + A c; + if (UV) return; + A d; + } else { + A c; + if (UV) return; + A d; + } + A e; +} + +// CHECK: [B12 (ENTRY)] +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B2 B9 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Preds (2): B3 B6 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Preds (2): B4 B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: 'a' +// CHECK-NEXT: 3: char c = 'a'; +// CHECK-NEXT: 4: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: a +// CHECK-NEXT: 3: [B5.2] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 4: [B5.3] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: 5: ![B5.4] +// CHECK-NEXT: T: if [B5.5] +// CHECK-NEXT: Preds (2): B7 B8 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B6] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: 2 +// CHECK-NEXT: 3: a +// CHECK-NEXT: 4: [B6.3] = [B6.2] +// CHECK-NEXT: 5: 3 +// CHECK-NEXT: 6: b +// CHECK-NEXT: 7: [B6.6] = [B6.5] +// CHECK-NEXT: 8: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B7] +// CHECK-NEXT: 1: b +// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: [B7.2] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: 4: ![B7.3] +// CHECK-NEXT: T: if [B8.4] && [B7.4] +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (2): B6 B5 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: a +// CHECK-NEXT: 3: [B8.2] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 4: [B8.3] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: [B8.4] && ... +// CHECK-NEXT: Preds (2): B10 B11 +// CHECK-NEXT: Succs (2): B7 B5 +// CHECK: [B9] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: 0 +// CHECK-NEXT: 3: a +// CHECK-NEXT: 4: [B9.3] = [B9.2] +// CHECK-NEXT: 5: 1 +// CHECK-NEXT: 6: b +// CHECK-NEXT: 7: [B9.6] = [B9.5] +// CHECK-NEXT: 8: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B10] +// CHECK-NEXT: 1: b +// CHECK-NEXT: 2: [B10.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: [B10.2] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B11.4] && [B10.3] +// CHECK-NEXT: Preds (1): B11 +// CHECK-NEXT: Succs (2): B9 B8 +// CHECK: [B11] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: a +// CHECK-NEXT: 3: [B11.2] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 4: [B11.3] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: [B11.4] && ... +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (2): B10 B8 +// CHECK: [B0 (EXIT)] +int a, b; +void text_if_with_and() { + if (a && b) { + a = 0; + b = 1; + } else if (a && !b) { + a = 2; + b = 3; + } else if (!a) + char c = 'a'; +} + +// CHECK: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(CXXConstructExpr) +// CHECK-NEXT: 2: [B4.5].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: CFGScopeEnd(WhileStmt) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeBegin(WhileStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: 4: [B3.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B4.5].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(CXXConstructExpr) +// CHECK-NEXT: 2: a +// CHECK-NEXT: 3: [B4.2] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 4: [B4.3] (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A b = a; +// CHECK-NEXT: 6: b +// CHECK-NEXT: 7: [B4.6] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 8: [B4.7].operator int +// CHECK-NEXT: 9: [B4.7] +// CHECK-NEXT: 10: [B4.9] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 11: [B4.10] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: while [B4.11] +// CHECK-NEXT: Preds (2): B2 B5 +// CHECK-NEXT: Succs (2): B3 B1 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_while_implicit_scope() { + A a; + while (A b = a) + A c; +} + +// CHECK: [B12 (ENTRY)] +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(CXXConstructExpr) +// CHECK-NEXT: 2: [B10.5].~A() (Implicit destructor) +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A e; +// CHECK-NEXT: 5: [B1.4].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B11.3].~A() (Implicit destructor) +// CHECK-NEXT: 7: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B8 B10 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (2): B3 B6 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: [B3.2].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 6: [B10.5].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B10.5].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B11.3].~A() (Implicit destructor) +// CHECK-NEXT: 6: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B5] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B5.2] +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B6] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B10.5].~A() (Implicit destructor) +// CHECK-NEXT: 4: CFGScopeEnd(ContinueStmt) +// CHECK-NEXT: T: continue; +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B7] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B7.2] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B6 B5 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 3: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B9] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: 4: UV +// CHECK-NEXT: 5: [B9.4] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.5] +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (2): B8 B7 +// CHECK: [B10] +// CHECK-NEXT: 1: CFGScopeBegin(CXXConstructExpr) +// CHECK-NEXT: 2: a +// CHECK-NEXT: 3: [B10.2] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 4: [B10.3] (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A b = a; +// CHECK-NEXT: 6: b +// CHECK-NEXT: 7: [B10.6] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 8: [B10.7].operator int +// CHECK-NEXT: 9: [B10.7] +// CHECK-NEXT: 10: [B10.9] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 11: [B10.10] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: while [B10.11] +// CHECK-NEXT: Preds (2): B2 B11 +// CHECK-NEXT: Succs (2): B9 B1 +// CHECK: [B11] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B4 +void test_while_jumps() { + A a; + while (A b = a) { + A c; + if (UV) break; + if (UV) continue; + if (UV) return; + A d; + } + A e; +} + +// CHECK: [B12 (ENTRY)] +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: [B1.2].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B11.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B8 B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: do ... while [B2.2] +// CHECK-NEXT: Preds (2): B3 B6 +// CHECK-NEXT: Succs (2): B10 B1 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B3.2].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B11.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B5] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B5.2] +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B6] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 3: CFGScopeEnd(ContinueStmt) +// CHECK-NEXT: T: continue; +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B7] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B7.2] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B6 B5 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 3: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B9] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A b; +// CHECK-NEXT: 4: UV +// CHECK-NEXT: 5: [B9.4] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.5] +// CHECK-NEXT: Preds (2): B10 B11 +// CHECK-NEXT: Succs (2): B8 B7 +// CHECK: [B10] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B9 +// CHECK: [B11] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B9 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B4 +void test_do_jumps() { + A a; + do { + A b; + if (UV) break; + if (UV) continue; + if (UV) return; + A c; + } while (UV); + A d; +} + +// CHECK: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: 2: [B4.5].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: CFGScopeEnd(ForStmt) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeBegin(ForStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: 4: [B3.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B4.5].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 2: a +// CHECK-NEXT: 3: [B4.2] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 4: [B4.3] (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A b = a; +// CHECK-NEXT: 6: b +// CHECK-NEXT: 7: [B4.6] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 8: [B4.7].operator int +// CHECK-NEXT: 9: [B4.7] +// CHECK-NEXT: 10: [B4.9] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 11: [B4.10] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: for (...; [B4.11]; ) +// CHECK-NEXT: Preds (2): B2 B5 +// CHECK-NEXT: Succs (2): B3 B1 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_for_implicit_scope() { + for (A a; A b = a; ) + A c; +} + +// CHECK: [B12 (ENTRY)] +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: 2: [B10.5].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B11.5].~A() (Implicit destructor) +// CHECK-NEXT: 4: (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A f; +// CHECK-NEXT: 6: [B1.5].~A() (Implicit destructor) +// CHECK-NEXT: 7: [B11.3].~A() (Implicit destructor) +// CHECK-NEXT: 8: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B8 B10 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (2): B3 B6 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A e; +// CHECK-NEXT: 3: [B3.2].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 6: [B10.5].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B10.5].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B11.5].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B11.3].~A() (Implicit destructor) +// CHECK-NEXT: 7: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B5] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B5.2] +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B6] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 3: CFGScopeEnd(ContinueStmt) +// CHECK-NEXT: T: continue; +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B7] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B7.2] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B6 B5 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 3: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B9] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A d; +// CHECK-NEXT: 4: UV +// CHECK-NEXT: 5: [B9.4] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.5] +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (2): B8 B7 +// CHECK: [B10] +// CHECK-NEXT: 1: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 2: b +// CHECK-NEXT: 3: [B10.2] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 4: [B10.3] (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A c = b; +// CHECK-NEXT: 6: c +// CHECK-NEXT: 7: [B10.6] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 8: [B10.7].operator int +// CHECK-NEXT: 9: [B10.7] +// CHECK-NEXT: 10: [B10.9] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 11: [B10.10] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: for (...; [B10.11]; ) +// CHECK-NEXT: Preds (2): B2 B11 +// CHECK-NEXT: Succs (2): B9 B1 +// CHECK: [B11] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A b; +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B4 +void test_for_jumps() { + A a; + for (A b; A c = b; ) { + A d; + if (UV) break; + if (UV) continue; + if (UV) return; + A e; + } + A f; +} + +// CHECK: [B7 (ENTRY)] +// CHECK-NEXT: Succs (1): B6 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: 2: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 3: int unused2; +// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B4 B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: ++[B2.1] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 3: int unused1; +// CHECK-NEXT: 4: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 2: i +// CHECK-NEXT: 3: [B5.2] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 4: 3 +// CHECK-NEXT: 5: [B5.3] < [B5.4] +// CHECK-NEXT: T: for (...; [B5.5]; ...) +// CHECK-NEXT: Preds (2): B2 B6 +// CHECK-NEXT: Succs (2): B4 B1 +// CHECK: [B6] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: 0 +// CHECK-NEXT: 3: int i = 0; +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_for_compound_and_break() { + for (int i = 0; i < 3; ++i) { + { + int unused1; + break; + } + } + { + int unused2; + } +} + +// CHECK: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B1] +// CHECK-NEXT: 1: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: 2: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: __begin +// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, LValueToRValue, class A *) +// CHECK-NEXT: 3: __end +// CHECK-NEXT: 4: [B2.3] (ImplicitCastExpr, LValueToRValue, class A *) +// CHECK-NEXT: 5: [B2.2] != [B2.4] +// CHECK-NEXT: T: for (auto &i : [B5.4]) { +// CHECK: [B4.12]; +// CHECK-NEXT: } +// CHECK-NEXT: Preds (2): B3 B5 +// CHECK-NEXT: Succs (2): B4 B1 +// CHECK: [B3] +// CHECK-NEXT: 1: __begin +// CHECK-NEXT: 2: ++[B3.1] +// CHECK-NEXT: 3: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 2: CFGScopeBegin(CXXForRangeStmt) +// CHECK-NEXT: 3: __begin +// CHECK-NEXT: 4: [B4.3] (ImplicitCastExpr, LValueToRValue, class A *) +// CHECK-NEXT: 5: *[B4.4] +// CHECK-NEXT: 6: auto &i = *__begin; +// CHECK-NEXT: 7: operator= +// CHECK-NEXT: 8: [B4.7] (ImplicitCastExpr, FunctionToPointerDecay, class A &(*)(const class A &) throw()) +// CHECK-NEXT: 9: i +// CHECK-NEXT: 10: b +// CHECK-NEXT: 11: [B4.10] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 12: [B4.9] = [B4.11] (OperatorCall) +// CHECK-NEXT: 13: CFGScopeEnd(CXXForRangeStmt) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A [10]) +// CHECK-NEXT: 3: A a[10]; +// CHECK-NEXT: 4: a +// CHECK-NEXT: 5: auto &&__range = a; +// CHECK-NEXT: 6: __range +// CHECK-NEXT: 7: [B5.6] (ImplicitCastExpr, ArrayToPointerDecay, class A *) +// CHECK-NEXT: 8: 10L +// CHECK-NEXT: 9: [B5.7] + [B5.8] +// CHECK-NEXT: 10: auto __end = __range + 10L; +// CHECK-NEXT: 11: __range +// CHECK-NEXT: 12: [B5.11] (ImplicitCastExpr, ArrayToPointerDecay, class A *) +// CHECK-NEXT: 13: auto __begin = __range; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B0 (EXIT)] +void test_range_for(A &b) { + A a[10]; + for (auto &i : a) + i = b; +} + +// CHECK: [B14 (ENTRY)] +// CHECK-NEXT: Succs (1): B13 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: 2: [B13.3].~A() (Implicit destructor) +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: ++[B2.1] +// CHECK-NEXT: Preds (2): B3 B4 +// CHECK-NEXT: Succs (1): B12 +// CHECK: [B3] +// CHECK-NEXT: 1: j +// CHECK-NEXT: 2: ++[B3.1] +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: CFGScopeEnd(ContinueStmt) +// CHECK-NEXT: T: continue; +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeEnd(CXXConstructExpr) +// CHECK-NEXT: 2: [B10.5].~A() (Implicit destructor) +// CHECK-NEXT: 3: i +// CHECK-NEXT: 4: [B5.3] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 5: 3 +// CHECK-NEXT: 6: [B5.4] > [B5.5] +// CHECK-NEXT: T: if [B5.6] +// CHECK-NEXT: Preds (2): B8 B10 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B6] +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B7] +// CHECK-NEXT: 1: j +// CHECK-NEXT: 2: ++[B7.1] +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B10.5].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B6 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B9] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: int unused2; +// CHECK-NEXT: 3: j +// CHECK-NEXT: 4: [B9.3] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 5: 13 +// CHECK-NEXT: 6: [B9.4] > [B9.5] +// CHECK-NEXT: T: if [B9.6] +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (2): B8 B7 +// CHECK: [B10] +// CHECK-NEXT: 1: CFGScopeBegin(CXXConstructExpr) +// CHECK-NEXT: 2: a +// CHECK-NEXT: 3: [B10.2] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 4: [B10.3] (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A b = a; +// CHECK-NEXT: 6: b +// CHECK-NEXT: 7: [B10.6] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 8: [B10.7].operator int +// CHECK-NEXT: 9: [B10.7] +// CHECK-NEXT: 10: [B10.9] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 11: [B10.10] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: while [B10.11] +// CHECK-NEXT: Preds (2): B6 B11 +// CHECK-NEXT: Succs (2): B9 B5 +// CHECK: [B11] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: int unused1; +// CHECK-NEXT: 3: i +// CHECK-NEXT: 4: [B11.3] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 5: int j = i; +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B12] +// CHECK-NEXT: 1: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 2: i +// CHECK-NEXT: 3: [B12.2] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 4: 10 +// CHECK-NEXT: 5: [B12.3] < [B12.4] +// CHECK-NEXT: T: for (...; [B12.5]; ...) +// CHECK-NEXT: Preds (2): B2 B13 +// CHECK-NEXT: Succs (2): B11 B1 +// CHECK: [B13] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: 0 +// CHECK-NEXT: 5: int i = 0; +// CHECK-NEXT: Preds (1): B14 +// CHECK-NEXT: Succs (1): B12 +// CHECK: [B0 (EXIT) +void test_nested_loops() { + A a; + for (int i = 0; i < 10; ++i) { + int unused1; + int j = i; + while (A b = a) { + int unused2; + if (j > 13) + break; // Break from while loop + ++j; + } + if (i > 3) + continue; // Continue for loop + ++j; + } +}