Index: include/clang/Analysis/AnalysisContext.h =================================================================== --- include/clang/Analysis/AnalysisContext.h +++ include/clang/Analysis/AnalysisContext.h @@ -426,6 +426,7 @@ bool addImplicitDtors = false, bool addInitializers = false, bool addTemporaryDtors = 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, // dtor kind AutomaticObjectDtor, @@ -167,6 +169,48 @@ } }; +/// Represents beginning of a scope implicitly generated +/// by the compiler on encountering a CompoundStmt +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's body +class CFGScopeEnd : public CFGElement { +public: + CFGScopeEnd() {} + CFGScopeEnd(const Stmt *S) + : CFGElement(ScopeEnd, S) {} + + // Get statement that triggered end of previously created 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 == ScopeEnd; + } +}; + /// CFGImplicitDtor - Represents C++ object destructor implicitly generated /// by compiler on various occasions. class CFGImplicitDtor : public CFGElement { @@ -685,6 +729,18 @@ Elements.push_back(CFGNewAllocator(NE), C); } + void appendScopeBegin(const Stmt *S, BumpVectorContext &C) { + Elements.push_back(CFGScopeBegin(S), C); + } + + void appendScopeEnd(const Stmt *S, BumpVectorContext &C) { + Elements.push_back(CFGScopeEnd(S), C); + } + + void prependScopeEnd(const Stmt *S, BumpVectorContext &C) { + Elements.insert(Elements.rbegin(), 1, CFGScopeEnd(S), C); + } + void appendBaseDtor(const CXXBaseSpecifier *BS, BumpVectorContext &C) { Elements.push_back(CFGBaseDtor(BS), C); } @@ -705,6 +761,8 @@ Elements.push_back(CFGDeleteDtor(RD, DE), C); } + void removeAllSuccessors() { Succs.clear(); } + // Destructors must be inserted in reversed order. So insertion is in two // steps. First we prepare space for some number of elements, then we insert // the elements beginning at the last position in prepared space. @@ -754,6 +812,7 @@ bool AddInitializers; bool AddImplicitDtors; bool AddTemporaryDtors; + bool AddScopes; bool AddStaticInitBranches; bool AddCXXNewAllocator; bool AddCXXDefaultInitExprInCtors; @@ -776,8 +835,9 @@ : forcedBlkExprs(nullptr), Observer(nullptr), PruneTriviallyFalseEdges(true), AddEHEdges(false), AddInitializers(false), AddImplicitDtors(false), - AddTemporaryDtors(false), AddStaticInitBranches(false), - AddCXXNewAllocator(false), AddCXXDefaultInitExprInCtors(false) {} + AddTemporaryDtors(false), AddScopes(false), + AddStaticInitBranches(false), AddCXXNewAllocator(false), + AddCXXDefaultInitExprInCtors(false) {} }; /// buildCFG - Builds a CFG from an AST. Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h =================================================================== --- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -211,6 +211,9 @@ /// \sa mayInlineCXXStandardLibrary Optional InlineCXXStandardLibrary; + /// \sa includeScopesInCFG + Optional IncludeScopesInCFG; + /// \sa mayInlineTemplateFunctions Optional InlineTemplateFunctions; @@ -395,6 +398,12 @@ /// accepts the values "true" and "false". bool includeTemporaryDtorsInCFG(); + /// 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 @@ -67,6 +67,7 @@ bool addImplicitDtors, bool addInitializers, bool addTemporaryDtors, + bool addScopes, bool synthesizeBodies, bool addStaticInitBranch, bool addCXXNewAllocator, @@ -77,6 +78,7 @@ cfgBuildOptions.AddImplicitDtors = addImplicitDtors; cfgBuildOptions.AddInitializers = addInitializers; cfgBuildOptions.AddTemporaryDtors = addTemporaryDtors; + cfgBuildOptions.AddScopes = addScopes; cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch; cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator; } Index: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ lib/Analysis/CFG.cpp @@ -245,11 +245,13 @@ /// Iterator to variable in previous scope that was declared just before /// begin of this scope. const_iterator Prev; + /// Statement that triggered creation of this LocalScope object + const Stmt *TriggerScopeStmt; public: /// Constructs empty scope linked to previous scope in specified place. - LocalScope(BumpVectorContext ctx, const_iterator P) - : ctx(std::move(ctx)), Vars(this->ctx, 4), Prev(P) {} + LocalScope(BumpVectorContext ctx, const_iterator P, Stmt *S) + : ctx(std::move(ctx)), Vars(this->ctx, 4), Prev(P), TriggerScopeStmt(S) {} /// Begin of scope in direction of CFG building (backwards). const_iterator begin() const { return const_iterator(*this, Vars.size()); } @@ -377,6 +379,11 @@ CFGBlock *SwitchTerminatedBlock; CFGBlock *DefaultCaseBlock; CFGBlock *TryTerminatedBlock; + CFGBlock *SwitchScopeBeginBlock; + CFGBlock *SwitchScopeEndBlock; + bool SwitchNeedsLocalScope; + bool FromSwitchStmt; + bool InSwitchNest; // Current position in local scope. LocalScope::const_iterator ScopePos; @@ -415,9 +422,11 @@ : 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), switchCond(nullptr), - cachedEntry(nullptr), lastLookup(nullptr) {} + TryTerminatedBlock(nullptr), SwitchScopeBeginBlock(nullptr), + SwitchScopeEndBlock(nullptr), SwitchNeedsLocalScope(false), + FromSwitchStmt(false), InSwitchNest(false), + badCFG(false), BuildOpts(buildOpts), switchExclusivelyCovered(false), + switchCond(0), cachedEntry(nullptr), lastLookup(nullptr) {} // buildCFG - Used by external clients to construct the CFG. std::unique_ptr buildCFG(const Decl *D, Stmt *Statement); @@ -488,6 +497,12 @@ CFGBlock *VisitChildren(Stmt *S); CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc); + void CreateScopeEndBlockForSwitchStmt(Stmt *S, bool *ShouldCreateScopeBeginPtr); + void CreateScopeBeginBlockForSwitchStmt(SwitchStmt *Terminator); + void CreateScopeEndBlockForIfStmt(IfStmt *I); + void CreateScopeEndBlockForReturnStmt(ReturnStmt *R); + void CreateScopeEndBlockForBreakOrContinueStmt(Stmt *B, bool IsBreak); + /// 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 @@ -578,18 +593,22 @@ } CFGBlock *addInitializer(CXXCtorInitializer *I); void addAutomaticObjDtors(LocalScope::const_iterator B, - LocalScope::const_iterator E, Stmt *S); + LocalScope::const_iterator E, + Stmt *TriggerAutoObjDtorsStmt, + Stmt *TriggerScopeStmt = nullptr); void addImplicitDtorsForDestructor(const CXXDestructorDecl *DD); // Local scopes creation. - LocalScope* createOrReuseLocalScope(LocalScope* Scope); + LocalScope *createOrReuseLocalScope(LocalScope *Scope, + Stmt *TriggerScopeStmt); - void addLocalScopeForStmt(Stmt *S); - LocalScope* addLocalScopeForDeclStmt(DeclStmt *DS, - LocalScope* Scope = nullptr); - LocalScope* addLocalScopeForVarDecl(VarDecl *VD, LocalScope* Scope = nullptr); + void addLocalScopeForStmt(Stmt *S, Stmt *TriggerScopeStmt); + LocalScope *addLocalScopeForDeclStmt(DeclStmt *DS, Stmt *TriggerScopeStmt, + LocalScope *Scope = nullptr); + LocalScope *addLocalScopeForVarDecl(VarDecl *VD, Stmt *TriggerScopeStmt, + LocalScope *Scope = nullptr); - void addLocalScopeAndDtors(Stmt *S); + void addLocalScopeAndDtors(Stmt *S, Stmt *TriggerScopeStmt); // Interface to CFGBlock - adding CFGElements. void appendStmt(CFGBlock *B, const Stmt *S) { @@ -606,6 +625,18 @@ void appendNewAllocator(CFGBlock *B, CXXNewExpr *NE) { B->appendNewAllocator(NE, cfg->getBumpVectorContext()); } + void appendScopeBegin(CFGBlock *B, const Stmt *S) { + if (BuildOpts.AddScopes) + B->appendScopeBegin(S, cfg->getBumpVectorContext()); + } + void appendScopeEnd(CFGBlock *B, const Stmt *S) { + if (BuildOpts.AddScopes) + B->appendScopeEnd(S, cfg->getBumpVectorContext()); + } + void prependScopeEnd(CFGBlock *B, const Stmt *S) { + if (BuildOpts.AddScopes) + B->prependScopeEnd(S, cfg->getBumpVectorContext()); + } void appendBaseDtor(CFGBlock *B, const CXXBaseSpecifier *BS) { B->appendBaseDtor(BS, cfg->getBumpVectorContext()); } @@ -850,6 +881,10 @@ return TryResult(); } + void removeAllSuccessors(CFGBlock *B) { + B->removeAllSuccessors(); + } + /// Try and evaluate an expression to an integer constant. bool tryEvaluate(Expr *S, Expr::EvalResult &outResult) { if (!BuildOpts.PruneTriviallyFalseEdges) @@ -1211,10 +1246,13 @@ } /// addAutomaticObjDtors - Add to current block automatic objects destructors -/// for objects in range of local scope positions. Use S as trigger statement -/// for destructors. +/// for objects in range of local scope positions. +/// Use `TriggerAutoObjDtorsStmt` as trigger statement for destructors, and +/// `TriggerScopeStmt` as trigger statement for ending scope. void CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B, - LocalScope::const_iterator E, Stmt *S) { + LocalScope::const_iterator E, + Stmt *TriggerAutoObjDtorsStmt, + Stmt *TriggerScopeStmt) { if (!BuildOpts.AddImplicitDtors) return; @@ -1247,7 +1285,7 @@ else autoCreateBlock(); - appendAutomaticObjDtor(Block, *I, S); + appendAutomaticObjDtor(Block, *I, TriggerAutoObjDtorsStmt); } } @@ -1298,17 +1336,18 @@ /// createOrReuseLocalScope - If Scope is NULL create new LocalScope. Either /// way return valid LocalScope object. -LocalScope* CFGBuilder::createOrReuseLocalScope(LocalScope* Scope) { +LocalScope *CFGBuilder::createOrReuseLocalScope(LocalScope *Scope, + Stmt *TriggerScopeStmt) { if (Scope) return Scope; llvm::BumpPtrAllocator &alloc = cfg->getAllocator(); return new (alloc.Allocate()) - LocalScope(BumpVectorContext(alloc), ScopePos); + LocalScope(BumpVectorContext(alloc), ScopePos, TriggerScopeStmt); } /// addLocalScopeForStmt - Add LocalScope to local scopes tree for statement -/// that should create implicit scope (e.g. if/else substatements). -void CFGBuilder::addLocalScopeForStmt(Stmt *S) { +/// that should create implicit scope (e.g. if/else substatements). +void CFGBuilder::addLocalScopeForStmt(Stmt *S, Stmt *TriggerScopeStmt) { if (!BuildOpts.AddImplicitDtors) return; @@ -1319,7 +1358,7 @@ for (auto *BI : CS->body()) { Stmt *SI = BI->stripLabelLikeStatements(); if (DeclStmt *DS = dyn_cast(SI)) - Scope = addLocalScopeForDeclStmt(DS, Scope); + Scope = addLocalScopeForDeclStmt(DS, TriggerScopeStmt, Scope); } return; } @@ -1327,19 +1366,20 @@ // For any other statement scope will be implicit and as such will be // interesting only for DeclStmt. if (DeclStmt *DS = dyn_cast(S->stripLabelLikeStatements())) - addLocalScopeForDeclStmt(DS); + addLocalScopeForDeclStmt(DS, TriggerScopeStmt); } /// addLocalScopeForDeclStmt - Add LocalScope for declaration statement. Will /// reuse Scope if not NULL. LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS, + Stmt *TriggerScopeStmt, LocalScope* Scope) { if (!BuildOpts.AddImplicitDtors) return Scope; for (auto *DI : DS->decls()) if (VarDecl *VD = dyn_cast(DI)) - Scope = addLocalScopeForVarDecl(VD, Scope); + Scope = addLocalScopeForVarDecl(VD, TriggerScopeStmt, Scope); return Scope; } @@ -1347,6 +1387,7 @@ /// create add scope for automatic objects and temporary objects bound to /// const reference. Will reuse Scope if not NULL. LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD, + Stmt *TriggerScopeStmt, LocalScope* Scope) { if (!BuildOpts.AddImplicitDtors) return Scope; @@ -1392,22 +1433,41 @@ if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl()) if (CD->hasDefinition() && !CD->hasTrivialDestructor()) { // Add the variable to scope - Scope = createOrReuseLocalScope(Scope); + Scope = createOrReuseLocalScope(Scope, TriggerScopeStmt); Scope->addVar(VD); ScopePos = Scope->begin(); } return Scope; } -/// addLocalScopeAndDtors - For given statement add local scope for it and -/// add destructors that will cleanup the scope. Will reuse Scope if not NULL. -void CFGBuilder::addLocalScopeAndDtors(Stmt *S) { +/// addLocalScopeAndDtors - For given statements `TriggerAutoObjDtorsStmt`, and +/// `TriggerScopeStmt`, add local scope for it and add destructors that will +/// cleanup the scope. Will reuse Scope if not NULL. +/// `TriggerAutoObjDtorsStmt` is the last statement in the present scope after +/// which auto obj destructors (if any) will be added to cleanup scope. +/// `TriggerScopeStmt` is the statement that led to the creation of the Scope +/// object. +/// +/// For example: +/// if (condition) { // TriggerScopeStmt +/// std::vector IntVec; +/// IntVec.push_back(0); // TriggerAutoObjDtorsStmt +/// } +/// +/// IfStmt is the `TriggerScopeStmt`; IntVec.push_back is +/// `TriggerAutoObjDtorsStmt`. +/// Eventually, `TriggerScopeStmt` is used inside CFGScopeBegin/End +/// blocks. In the example above, we will have CFGScope blocks, like +/// so: CFGScopeBegin(IfStmt) ... CFGScopeEnd(IfStmt). +void CFGBuilder::addLocalScopeAndDtors(Stmt *TriggerAutoObjDtorsStmt, + Stmt *TriggerScopeStmt) { if (!BuildOpts.AddImplicitDtors) return; LocalScope::const_iterator scopeBeginPos = ScopePos; - addLocalScopeForStmt(S); - addAutomaticObjDtors(ScopePos, scopeBeginPos, S); + addLocalScopeForStmt(TriggerAutoObjDtorsStmt, TriggerScopeStmt); + addAutomaticObjDtors(ScopePos, scopeBeginPos, TriggerAutoObjDtorsStmt, + TriggerScopeStmt); } /// prependAutomaticObjDtorsWithTerminator - Prepend destructor CFGElements for @@ -1802,6 +1862,18 @@ return Block; } +void CFGBuilder::CreateScopeEndBlockForBreakOrContinueStmt(Stmt *B, + bool IsBreak) { + CFGBlock *OldBlock = Block; + removeAllSuccessors(OldBlock); + Block = createBlock(false); + addSuccessor(OldBlock, Block); + addSuccessor(Block, + IsBreak ? BreakJumpTarget.block : ContinueJumpTarget.block); + appendScopeEnd(Block, B); + Block = OldBlock; +} + CFGBlock *CFGBuilder::VisitBreakStmt(BreakStmt *B) { // "break" is a control-flow statement. Thus we stop processing the current // block. @@ -1815,8 +1887,11 @@ // If there is no target for the break, then we are looking at an incomplete // AST. This means that the CFG cannot be constructed. if (BreakJumpTarget.block) { - addAutomaticObjDtors(ScopePos, BreakJumpTarget.scopePosition, B); - addSuccessor(Block, BreakJumpTarget.block); + addAutomaticObjDtors(ScopePos, BreakJumpTarget.scopePosition, B, B); + if (BuildOpts.AddScopes && !InSwitchNest) + CreateScopeEndBlockForBreakOrContinueStmt(B, true); + else + addSuccessor(Block, BreakJumpTarget.block); } else badCFG = true; @@ -1944,17 +2019,47 @@ return addStmt(C->getCond()); } +static bool shouldDeferScopeEnd(Stmt *S) { + return isa(S) || isa(S) || isa(S); +} + +void CFGBuilder::CreateScopeEndBlockForSwitchStmt(Stmt *S, + bool *ShouldCreateScopeBeginPtr) { + autoCreateBlock(); + appendScopeEnd(Block, S); + if (FromSwitchStmt && SwitchNeedsLocalScope) { + SwitchScopeEndBlock = Block; + Succ = SwitchScopeEndBlock; + *ShouldCreateScopeBeginPtr = false; + BreakJumpTarget = JumpTarget(SwitchScopeEndBlock, ScopePos); + } +} CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) { + SaveAndRestore save_break(BreakJumpTarget); LocalScope::const_iterator scopeBeginPos = ScopePos; if (BuildOpts.AddImplicitDtors) { - addLocalScopeForStmt(C); + addLocalScopeForStmt(C, C); } - if (!C->body_empty() && !isa(*C->body_rbegin())) { + + if (!C->body_empty() && !isa(*C->body_rbegin())) // If the body ends with a ReturnStmt, the dtors will be added in // VisitReturnStmt. - addAutomaticObjDtors(ScopePos, scopeBeginPos, C); + addAutomaticObjDtors(ScopePos, scopeBeginPos, C, C); + + bool ShouldCreateScopeBegin = true; + bool SavedFromSwitchStmt = FromSwitchStmt; + bool SavedInSwitchNest = InSwitchNest; + if (!C->body_empty() && BuildOpts.AddScopes && + (!shouldDeferScopeEnd(*C->body_rbegin()) || FromSwitchStmt)) + CreateScopeEndBlockForSwitchStmt(C, &ShouldCreateScopeBegin); + + if (BuildOpts.AddScopes) { + InSwitchNest = FromSwitchStmt; + if (FromSwitchStmt && SwitchNeedsLocalScope) + Block = nullptr; } + FromSwitchStmt = false; CFGBlock *LastBlock = Block; @@ -1969,6 +2074,16 @@ return nullptr; } + if (BuildOpts.AddScopes && !C->body_empty()) { + if (ShouldCreateScopeBegin) { + if (!LastBlock) + LastBlock = createBlock(); + appendScopeBegin(LastBlock, C); + } + FromSwitchStmt = SavedFromSwitchStmt; + InSwitchNest = SavedInSwitchNest; + } + return LastBlock; } @@ -2162,6 +2277,13 @@ return B; } +void CFGBuilder::CreateScopeEndBlockForIfStmt(IfStmt *I) { + autoCreateBlock(); + prependScopeEnd(Block, I); + Succ = Block; + Block = nullptr; +} + 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 @@ -2176,12 +2298,12 @@ // Create local scope for C++17 if init-stmt if one exists. if (Stmt *Init = I->getInit()) - addLocalScopeForStmt(Init); + addLocalScopeForStmt(Init, I); // Create local scope for possible condition variable. // Store scope position. Add implicit destructor. if (VarDecl *VD = I->getConditionVariable()) - addLocalScopeForVarDecl(VD); + addLocalScopeForVarDecl(VD, I); addAutomaticObjDtors(ScopePos, save_scope_pos.get(), I); @@ -2205,9 +2327,11 @@ // If branch is not a compound statement create implicit scope // and add destructors. - if (!isa(Else)) - addLocalScopeAndDtors(Else); - + if (!isa(Else)) { + addLocalScopeAndDtors(Else, I); + if (BuildOpts.AddScopes && !shouldDeferScopeEnd(Else)) + CreateScopeEndBlockForIfStmt(I); + } ElseBlock = addStmt(Else); if (!ElseBlock) // Can occur when the Else body has all NullStmts. @@ -2216,6 +2340,9 @@ if (badCFG) return nullptr; } + + if (!isa(Else)) + appendScopeBegin(ElseBlock, I); } // Process the true branch. @@ -2228,8 +2355,11 @@ // If branch is not a compound statement create implicit scope // and add destructors. - if (!isa(Then)) - addLocalScopeAndDtors(Then); + if (!isa(Then)) { + addLocalScopeAndDtors(Then, I); + if (BuildOpts.AddScopes && !shouldDeferScopeEnd(Then)) + CreateScopeEndBlockForIfStmt(I); + } ThenBlock = addStmt(Then); @@ -2243,6 +2373,9 @@ if (badCFG) return nullptr; } + + if (!isa(Then)) + appendScopeBegin(ThenBlock, I); } // Specially handle "if (expr1 || ...)" and "if (expr1 && ...)" by @@ -2296,6 +2429,14 @@ return LastBlock; } +void CFGBuilder::CreateScopeEndBlockForReturnStmt(ReturnStmt *R) { + CFGBlock *OldBlock = Block; + Block = createBlock(false); + addSuccessor(OldBlock, Block); + addSuccessor(Block, &cfg->getExit()); + appendScopeEnd(Block, R); + Block = OldBlock; +} CFGBlock *CFGBuilder::VisitReturnStmt(ReturnStmt *R) { // If we were in the middle of a block we stop processing that block. @@ -2308,12 +2449,16 @@ // Create the new block. Block = createBlock(false); - addAutomaticObjDtors(ScopePos, LocalScope::const_iterator(), R); + addAutomaticObjDtors(ScopePos, LocalScope::const_iterator(), R, R); // If the one of the destructors does not return, we already have the Exit // block as a successor. - if (!Block->hasNoReturnElement()) - addSuccessor(Block, &cfg->getExit()); + if (!Block->hasNoReturnElement()) { + if (BuildOpts.AddScopes) + CreateScopeEndBlockForReturnStmt(R); + else + addSuccessor(Block, &cfg->getExit()); + } // Add the return statement to the block. This may create new blocks if R // contains control-flow (short-circuit operations). @@ -2407,11 +2552,11 @@ // Add destructor for init statement and condition variable. // Store scope position for continue statement. if (Stmt *Init = F->getInit()) - addLocalScopeForStmt(Init); + addLocalScopeForStmt(Init, F); LocalScope::const_iterator LoopBeginScopePos = ScopePos; if (VarDecl *VD = F->getConditionVariable()) - addLocalScopeForVarDecl(VD); + addLocalScopeForVarDecl(VD, F); LocalScope::const_iterator ContinueScopePos = ScopePos; addAutomaticObjDtors(ScopePos, save_scope_pos.get(), F); @@ -2471,7 +2616,7 @@ // If body is not a compound statement create implicit scope // and add destructors. if (!isa(F->getBody())) - addLocalScopeAndDtors(F->getBody()); + addLocalScopeAndDtors(F->getBody(), F); // Now populate the body block, and in the process create new blocks as we // walk the body of the loop. @@ -2486,6 +2631,11 @@ return nullptr; } + if (!isa(F->getBody())) { + appendScopeBegin(BodyBlock, F); + if (!shouldDeferScopeEnd(F->getBody())) + prependScopeEnd(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. @@ -2554,7 +2704,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(InitBlock, I); + appendScopeEnd(LoopSuccessor, I); + return InitBlock; } // There is no loop initialization. We are thus basically a while loop. @@ -2752,7 +2905,7 @@ // Store scope position for continue statement. LocalScope::const_iterator LoopBeginScopePos = ScopePos; if (VarDecl *VD = W->getConditionVariable()) { - addLocalScopeForVarDecl(VD); + addLocalScopeForVarDecl(VD, W); addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W); } @@ -2793,7 +2946,7 @@ // If body is not a compound statement create implicit scope // and add destructors. if (!isa(W->getBody())) - addLocalScopeAndDtors(W->getBody()); + addLocalScopeAndDtors(W->getBody(), W); // Create the body. The returned block is the entry to the loop body. BodyBlock = addStmt(W->getBody()); @@ -2802,6 +2955,12 @@ BodyBlock = ContinueJumpTarget.block; // can happen for "while(...) ;" else if (Block && badCFG) return nullptr; + + if (!isa(W->getBody())) { + appendScopeBegin(BodyBlock, W); + if (!shouldDeferScopeEnd(W->getBody())) + prependScopeEnd(BodyBlock, W); + } } // Because of short-circuit evaluation, the condition of the loop can span @@ -2975,7 +3134,7 @@ // If body is not a compound statement create implicit scope // and add destructors. if (!isa(D->getBody())) - addLocalScopeAndDtors(D->getBody()); + addLocalScopeAndDtors(D->getBody(), D); // Create the body. The returned block is the entry to the loop body. BodyBlock = addStmt(D->getBody()); @@ -2987,6 +3146,11 @@ return nullptr; } + if (!isa(D->getBody())) { + appendScopeBegin(BodyBlock, D); + if (!shouldDeferScopeEnd(D->getBody())) + prependScopeEnd(BodyBlock, 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 @@ -3030,8 +3194,11 @@ // If there is no target for the continue, then we are looking at an // incomplete AST. This means the CFG cannot be constructed. if (ContinueJumpTarget.block) { - addAutomaticObjDtors(ScopePos, ContinueJumpTarget.scopePosition, C); - addSuccessor(Block, ContinueJumpTarget.block); + addAutomaticObjDtors(ScopePos, ContinueJumpTarget.scopePosition, C, C); + if (BuildOpts.AddScopes) + CFGBuilder::CreateScopeEndBlockForBreakOrContinueStmt(C, false); + else + addSuccessor(Block, ContinueJumpTarget.block); } else badCFG = true; @@ -3067,6 +3234,14 @@ return VisitCompoundStmt(SE->getSubStmt()); } +void CFGBuilder::CreateScopeBeginBlockForSwitchStmt(SwitchStmt *Terminator) { + SwitchScopeBeginBlock = createBlock(false); + SwitchScopeBeginBlock->setTerminator(Terminator); + addSuccessor(SwitchTerminatedBlock, SwitchScopeBeginBlock); + appendScopeBegin(SwitchScopeBeginBlock, Terminator->getBody()); + SwitchTerminatedBlock = SwitchScopeBeginBlock; +} + CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { // "switch" is a control-flow statement. Thus we stop processing the current // block. @@ -3078,12 +3253,12 @@ // Create local scope for C++17 switch init-stmt if one exists. if (Stmt *Init = Terminator->getInit()) - addLocalScopeForStmt(Init); + addLocalScopeForStmt(Init, Terminator); // Create local scope for possible condition variable. // Store scope position. Add implicit destructor. if (VarDecl *VD = Terminator->getConditionVariable()) - addLocalScopeForVarDecl(VD); + addLocalScopeForVarDecl(VD, Terminator); addAutomaticObjDtors(ScopePos, save_scope_pos.get(), Terminator); @@ -3132,7 +3307,22 @@ // If body is not a compound statement create implicit scope // and add destructors. if (!isa(Terminator->getBody())) - addLocalScopeAndDtors(Terminator->getBody()); + addLocalScopeAndDtors(Terminator->getBody(), Terminator); + + SwitchNeedsLocalScope = isa(Terminator->getBody()); + CFGBlock *SavedSwitchTerminatedBlock = SwitchTerminatedBlock; + + if (BuildOpts.AddScopes && SwitchNeedsLocalScope) + CreateScopeBeginBlockForSwitchStmt(Terminator); + + // If the SwitchStmt contains a condition variable, end its scope after switch. + if (VarDecl *VD = Terminator->getConditionVariable()) + if (Expr *Init = VD->getInit()) + appendScopeEnd(Succ, Terminator->getConditionVariableDeclStmt()); + + FromSwitchStmt = true; + CFGBlock *SavedSwitchScopeEndBlock = SwitchScopeEndBlock; + CFGBlock *SavedDefaultCaseBlock = DefaultCaseBlock; addStmt(Terminator->getBody()); if (Block) { @@ -3150,9 +3340,24 @@ SwitchAlwaysHasSuccessor |= switchExclusivelyCovered; SwitchAlwaysHasSuccessor |= Terminator->isAllEnumCasesCovered() && Terminator->getSwitchCaseList(); - addSuccessor(SwitchTerminatedBlock, DefaultCaseBlock, + + SwitchSuccessor = DefaultCaseBlock; + // If DefaultCaseBlock == SavedDefaultCaseBlock, then we don't have default + // case. + if (BuildOpts.AddScopes && (SavedSwitchScopeEndBlock != SwitchScopeEndBlock) && + (DefaultCaseBlock == SavedDefaultCaseBlock)) + SwitchSuccessor = SwitchScopeEndBlock; + + addSuccessor(SwitchTerminatedBlock, SwitchSuccessor, !SwitchAlwaysHasSuccessor); + // Restore SwitchNeedsLocalScope value + SwitchNeedsLocalScope = false; + SwitchTerminatedBlock = SavedSwitchTerminatedBlock; + SwitchScopeEndBlock = SavedSwitchScopeEndBlock; + DefaultCaseBlock = SavedDefaultCaseBlock; + FromSwitchStmt = false; + // Add the terminator and condition in the switch block. SwitchTerminatedBlock->setTerminator(Terminator); Block = SwitchTerminatedBlock; @@ -3163,8 +3368,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); } } @@ -3217,6 +3424,7 @@ // CaseStmts are essentially labels, so they are the first statement in a // block. CFGBlock *TopBlock = nullptr, *LastBlock = nullptr; + SaveAndRestore save_break(BreakJumpTarget); if (Stmt *Sub = CS->getSubStmt()) { // For deeply nested chains of CaseStmts, instead of doing a recursion @@ -3252,6 +3460,9 @@ // were processing (the "case XXX:" is the label). CaseBlock->setLabel(CS); + if (BuildOpts.AddScopes && SwitchNeedsLocalScope) + BreakJumpTarget = JumpTarget(SwitchScopeEndBlock, ScopePos); + if (badCFG) return nullptr; @@ -3277,6 +3488,13 @@ } CFGBlock *CFGBuilder::VisitDefaultStmt(DefaultStmt *Terminator) { + SaveAndRestore save_break(BreakJumpTarget); + if (BuildOpts.AddScopes && SwitchNeedsLocalScope) { + BreakJumpTarget = JumpTarget(SwitchScopeEndBlock, ScopePos); + Succ = SwitchScopeEndBlock; + Block = nullptr; + } + if (Terminator->getSubStmt()) addStmt(Terminator->getSubStmt()); @@ -3372,7 +3590,7 @@ // Store scope position. Add implicit destructor. if (VarDecl *VD = CS->getExceptionDecl()) { LocalScope::const_iterator BeginScopePos = ScopePos; - addLocalScopeForVarDecl(VD); + addLocalScopeForVarDecl(VD, CS); addAutomaticObjDtors(ScopePos, BeginScopePos, CS); } @@ -3422,11 +3640,11 @@ // Create local scopes and destructors for range, begin and end variables. if (Stmt *Range = S->getRangeStmt()) - addLocalScopeForStmt(Range); + addLocalScopeForStmt(Range, S); if (Stmt *Begin = S->getBeginStmt()) - addLocalScopeForStmt(Begin); + addLocalScopeForStmt(Begin, S); if (Stmt *End = S->getEndStmt()) - addLocalScopeForStmt(End); + addLocalScopeForStmt(End, S); addAutomaticObjDtors(ScopePos, save_scope_pos.get(), S); LocalScope::const_iterator ContinueScopePos = ScopePos; @@ -3498,7 +3716,7 @@ Block = nullptr; // Add implicit scope and dtors for loop variable. - addLocalScopeAndDtors(S->getLoopVarStmt()); + addLocalScopeAndDtors(S->getLoopVarStmt(), S); // Populate a new block to contain the loop body and loop variable. addStmt(S->getBody()); @@ -3898,6 +4116,8 @@ case CFGElement::Statement: case CFGElement::Initializer: case CFGElement::NewAllocator: + case CFGElement::ScopeBegin: + case CFGElement::ScopeEnd: llvm_unreachable("getDestructorDecl should only be used with " "ImplicitDtors"); case CFGElement::AutomaticObjectDtor: { @@ -4307,7 +4527,16 @@ OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()"; OS << " (Implicit destructor)\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 @@ -26,6 +26,7 @@ /*AddImplicitDtors=*/true, /*AddInitializers=*/true, Options.includeTemporaryDtorsInCFG(), + Options.includeScopesInCFG(), Options.shouldSynthesizeBodies(), Options.shouldConditionalizeStaticInitializers(), /*addCXXNewAllocator=*/true, Index: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp =================================================================== --- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -172,6 +172,12 @@ /* Default = */ false); } +bool AnalyzerOptions::includeScopesInCFG() { + return getBooleanOption(IncludeScopesInCFG, + "cfg-scopes", + /* Default = */ false); +} + bool AnalyzerOptions::mayInlineCXXStandardLibrary() { return getBooleanOption(InlineCXXStandardLibrary, "c++-stdlib-inlining", Index: test/Analysis/analyzer-config.c =================================================================== --- test/Analysis/analyzer-config.c +++ test/Analysis/analyzer-config.c @@ -12,6 +12,7 @@ // CHECK: [config] // CHECK-NEXT: cfg-conditional-static-initializers = true +// CHECK-NEXT: cfg-scopes = false // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -27,5 +28,5 @@ // CHECK-NEXT: region-store-small-struct-limit = 2 // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 15 +// CHECK-NEXT: num-entries = 16 Index: test/Analysis/analyzer-config.cpp =================================================================== --- test/Analysis/analyzer-config.cpp +++ test/Analysis/analyzer-config.cpp @@ -23,6 +23,7 @@ // CHECK-NEXT: c++-stdlib-inlining = true // CHECK-NEXT: c++-template-inlining = true // CHECK-NEXT: cfg-conditional-static-initializers = true +// CHECK-NEXT: cfg-scopes = false // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -38,4 +39,4 @@ // CHECK-NEXT: region-store-small-struct-limit = 2 // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 20 +// CHECK-NEXT: num-entries = 21 Index: test/Analysis/scopes-cfg-output.cpp =================================================================== --- /dev/null +++ test/Analysis/scopes-cfg-output.cpp @@ -0,0 +1,1468 @@ +// 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: [B3 (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: Preds (1): B3 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B2] +// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B1 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] + 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: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 13: [B1.11].~A() (Implicit destructor) +// CHECK-NEXT: 14: [B1.3].~A() (Implicit destructor) +// 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: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 7: [B1.3].~A() (Implicit destructor) +// 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: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 10: [B1.8].~A() (Implicit destructor) +// CHECK-NEXT: 11: [B1.6].~A() (Implicit destructor) +// CHECK-NEXT: 12: (CXXConstructExpr, class A) +// CHECK: 13: A b; +// CHECK: 14: CFGScopeEnd(CompoundStmt) +// CHECK: 15: [B1.13].~A() (Implicit destructor) +// CHECK: 16: [B1.3].~A() (Implicit destructor) +// 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: [B5 (ENTRY)] +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B1.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B4.5].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B4.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B4.5].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B4.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B4] +// 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: [B4.6] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B4.7] +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (2): B2 B1 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B3 +void test_return() { + A a; + A b; + if (UV) return; + A c; +} + +// CHECK: [B7 (ENTRY)] +// CHECK-NEXT: Succs (1): B6 +// CHECK: [B1] +// CHECK-NEXT: 1: [B6.7].~A() (Implicit destructor) +// CHECK-NEXT: 2: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 3: [B6.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B2 B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: [B3.3].~A() (Implicit destructor) +// CHECK-NEXT: 2: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Preds (1): B3 +// 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: Preds (1): B6 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: 2: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B6] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: a +// CHECK-NEXT: 5: [B6.4] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 6: [B6.5] (CXXConstructExpr, class A) +// CHECK-NEXT: 7: A b = a; +// CHECK-NEXT: 8: b +// CHECK-NEXT: 9: [B6.8] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 10: [B6.9].operator int +// CHECK-NEXT: 11: [B6.9] +// CHECK-NEXT: 12: [B6.11] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 13: [B6.12] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B6.13] +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (2): B5 B3 +// CHECK: [B0 (EXIT)] +void test_if_implicit_scope() { + A a; + if (A b = a) + A c; + else A c; +} + +// CHECK: [B11 (ENTRY)] +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B1] +// CHECK-NEXT: 1: [B10.7].~A() (Implicit destructor) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A e; +// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 5: [B1.3].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B10.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B2 B6 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B2.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B10.7].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B10.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: 4: UV +// CHECK-NEXT: 5: [B5.4] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B5.5] +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (2): B3 B2 +// CHECK: [B6] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B6.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B10.7].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B10.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B0 +// 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): B7 B6 +// CHECK: [B10] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: a +// CHECK-NEXT: 5: [B10.4] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 6: [B10.5] (CXXConstructExpr, class A) +// CHECK-NEXT: 7: A b = a; +// CHECK-NEXT: 8: b +// CHECK-NEXT: 9: [B10.8] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 10: [B10.9].operator int +// CHECK-NEXT: 11: [B10.9] +// CHECK-NEXT: 12: [B10.11] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 13: [B10.12] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B10.13] +// CHECK-NEXT: Preds (1): B11 +// CHECK-NEXT: Succs (2): B9 B5 +// 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: [B13 (ENTRY)] +// CHECK-NEXT: Succs (1): B12 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B2 B10 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Preds (2): B3 B7 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Preds (2): B4 B6 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: 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: char c = 'a'; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B6] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: a +// CHECK-NEXT: 3: [B6.2] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 4: [B6.3] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: 5: ![B6.4] +// CHECK-NEXT: T: if [B6.5] +// CHECK-NEXT: Preds (2): B8 B9 +// CHECK-NEXT: Succs (2): B5 B3 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: 2 +// CHECK-NEXT: 3: a +// CHECK-NEXT: 4: [B7.3] = [B7.2] +// CHECK-NEXT: 5: 3 +// CHECK-NEXT: 6: b +// CHECK-NEXT: 7: [B7.6] = [B7.5] +// CHECK-NEXT: 8: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B8] +// CHECK-NEXT: 1: b +// CHECK-NEXT: 2: [B8.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: [B8.2] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: 4: ![B8.3] +// CHECK-NEXT: T: if [B9.4] && [B8.4] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B7 B6 +// CHECK: [B9] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: a +// CHECK-NEXT: 3: [B9.2] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 4: [B9.3] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: [B9.4] && ... +// CHECK-NEXT: Preds (2): B11 B12 +// CHECK-NEXT: Succs (2): B8 B6 +// CHECK: [B10] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: 0 +// CHECK-NEXT: 3: a +// CHECK-NEXT: 4: [B10.3] = [B10.2] +// CHECK-NEXT: 5: 1 +// CHECK-NEXT: 6: b +// CHECK-NEXT: 7: [B10.6] = [B10.5] +// CHECK-NEXT: 8: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B11 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B11] +// CHECK-NEXT: 1: b +// CHECK-NEXT: 2: [B11.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: [B11.2] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B12.4] && [B11.3] +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (2): B10 B9 +// CHECK: [B12] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: a +// CHECK-NEXT: 3: [B12.2] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 4: [B12.3] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: [B12.4] && ... +// CHECK-NEXT: Preds (1): B13 +// CHECK-NEXT: Succs (2): B11 B9 +// 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: [B4.4].~A() (Implicit destructor) +// CHECK-NEXT: 2: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 3: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// 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.4].~A() (Implicit destructor) +// CHECK-NEXT: 6: CFGScopeEnd(WhileStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: a +// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B4.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A b = a; +// CHECK-NEXT: 5: b +// CHECK-NEXT: 6: [B4.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B4.6].operator int +// CHECK-NEXT: 8: [B4.6] +// CHECK-NEXT: 9: [B4.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK: 10: [B4.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: while [B4.10] +// 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: [B15 (ENTRY)] +// CHECK-NEXT: Succs (1): B14 +// CHECK: [B1] +// CHECK-NEXT: 1: [B13.4].~A() (Implicit destructor) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A e; +// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 5: [B1.3].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B14.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B11 B13 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (2): B3 B8 +// CHECK-NEXT: Succs (1): B13 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B3.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B13.4].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B13.4].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B14.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B6] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B6.2] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B13.4].~A() (Implicit destructor) +// CHECK-NEXT: T: continue; +// CHECK: Preds (1): B9 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeEnd(ContinueStmt) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B9] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B9.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.2] +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (2): B7 B6 +// CHECK: [B10] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: T: break; +// CHECK: Preds (1): B12 +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B11] +// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B12] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: 4: UV +// CHECK-NEXT: 5: [B12.4] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B12.5] +// CHECK-NEXT: Preds (1): B13 +// CHECK-NEXT: Succs (2): B10 B9 +// CHECK: [B13] +// CHECK-NEXT: 1: a +// CHECK-NEXT: 2: [B13.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B13.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A b = a; +// CHECK-NEXT: 5: b +// CHECK-NEXT: 6: [B13.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B13.6].operator int +// CHECK-NEXT: 8: [B13.6] +// CHECK-NEXT: 9: [B13.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 10: [B13.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: while [B13.10] +// CHECK-NEXT: Preds (2): B2 B14 +// CHECK-NEXT: Succs (2): B12 B1 +// CHECK: [B14] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: Preds (1): B15 +// CHECK-NEXT: Succs (1): B13 +// CHECK: [B0 (EXIT)] +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: [B15 (ENTRY)] +// CHECK-NEXT: Succs (1): B14 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B1.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B14.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B11 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 B8 +// CHECK-NEXT: Succs (2): B13 B1 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B3.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B14.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B6] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B6.2] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: T: continue; +// CHECK: Preds (1): B9 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeEnd(ContinueStmt) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B9] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B9.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.2] +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (2): B7 B6 +// CHECK: [B10] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: T: break; +// CHECK: Preds (1): B12 +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B11] +// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B12] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A b; +// CHECK-NEXT: 4: UV +// CHECK-NEXT: 5: [B12.4] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B12.5] +// CHECK-NEXT: Preds (2): B13 B14 +// CHECK-NEXT: Succs (2): B10 B9 +// CHECK: [B13] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B12 +// CHECK: [B14] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: Preds (1): B15 +// CHECK-NEXT: Succs (1): B12 +// CHECK: [B0 (EXIT) +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.4].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B5.4].~A() (Implicit destructor) +// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// 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.4].~A() (Implicit destructor) +// CHECK-NEXT: 6: CFGScopeEnd(ForStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: a +// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B4.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A b = a; +// CHECK-NEXT: 5: b +// CHECK-NEXT: 6: [B4.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B4.6].operator int +// CHECK-NEXT: 8: [B4.6] +// CHECK-NEXT: 9: [B4.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK: 10: [B4.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: for (...; [B4.10]; ) +// CHECK-NEXT: Preds (2): B2 B5 +// CHECK-NEXT: Succs (2): B3 B1 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: 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: [B15 (ENTRY)] +// CHECK-NEXT: Succs (1): B14 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: 2: [B13.4].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B14.6].~A() (Implicit destructor) +// CHECK-NEXT: 4: (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A f; +// CHECK-NEXT: 6: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 7: [B1.5].~A() (Implicit destructor) +// CHECK-NEXT: 8: [B14.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B11 B13 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (2): B3 B8 +// CHECK-NEXT: Succs (1): B13 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A e; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B3.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B13.4].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B13.4].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B14.6].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B14.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B6] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B6.2] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: T: continue; +// CHECK: Preds (1): B9 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeEnd(ContinueStmt) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B9] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B9.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.2] +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (2): B7 B6 +// CHECK: [B10] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: T: break; +// CHECK: Preds (1): B12 +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B11] +// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B12] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A d; +// CHECK-NEXT: 4: UV +// CHECK-NEXT: 5: [B12.4] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B12.5] +// CHECK-NEXT: Preds (1): B13 +// CHECK-NEXT: Succs (2): B10 B9 +// CHECK: [B13] +// CHECK-NEXT: 1: b +// CHECK-NEXT: 2: [B13.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B13.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A c = b; +// CHECK-NEXT: 5: c +// CHECK-NEXT: 6: [B13.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B13.6].operator int +// CHECK-NEXT: 8: [B13.6] +// CHECK-NEXT: 9: [B13.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 10: [B13.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: for (...; [B13.10]; ) +// CHECK-NEXT: Preds (2): B2 B14 +// CHECK-NEXT: Succs (2): B12 B1 +// CHECK: [B14] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 5: (CXXConstructExpr, class A) +// CHECK-NEXT: 6: A b; +// CHECK-NEXT: Preds (1): B15 +// CHECK-NEXT: Succs (1): B13 +// CHECK: [B0 (EXIT)] +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: [B10 (ENTRY)] +// CHECK-NEXT: Succs (1): B9 +// CHECK: [B1] +// CHECK-NEXT: l1: +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B1.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B8.2].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B2 B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A b; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B2.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B8.5].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B3 B5 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B8.5].~A() (Implicit destructor) +// CHECK-NEXT: T: goto l1; +// CHECK: Preds (1): B5 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B5] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B5.2] +// CHECK-NEXT: Preds (2): B6 B8 +// CHECK-NEXT: Succs (2): B4 B2 +// CHECK: [B6] +// CHECK-NEXT: 1: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B8.5].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B8.2].~A() (Implicit destructor) +// CHECK-NEXT: T: goto l0; +// CHECK: Preds (1): B8 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B8] +// CHECK-NEXT: l0: +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A b; +// CHECK-NEXT: 3: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 4: (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A a; +// CHECK-NEXT: 6: UV +// CHECK-NEXT: 7: [B8.6] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B8.7] +// CHECK-NEXT: Preds (2): B9 B7 +// CHECK-NEXT: Succs (2): B7 B5 +// CHECK: [B9] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B0 (EXIT)] +void test_goto() { + A a; +l0: + A b; + { A a; + if (UV) goto l0; + if (UV) goto l1; + A b; + } +l1: + A c; +} + +// CHECK: [B11 (ENTRY)] +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: 2: 1 +// CHECK-NEXT: 3: int k = 1; +// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: '1' +// CHECK-NEXT: 3: char c = '1'; +// CHECK-NEXT: 4: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 5: getX +// CHECK-NEXT: 6: [B2.5] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(void)) +// CHECK-NEXT: 7: [B2.6]() +// CHECK-NEXT: 8: int i = getX(); +// CHECK-NEXT: 9: i +// CHECK-NEXT: 10: [B2.9] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: T: switch [B2.10] +// CHECK-NEXT: Preds (1): B11 +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: T: switch [B2.10] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (5): B6 B7 B9 B10 B5 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (3): B5 B8 B9 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B5] +// CHECK-NEXT: default: +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: 0 +// CHECK-NEXT: 3: int a = 0; +// CHECK-NEXT: 4: i +// CHECK-NEXT: 5: ++[B5.4] +// CHECK-NEXT: 6: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B6 B3 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B6] +// CHECK-NEXT: case 3: +// CHECK-NEXT: 1: '2' +// CHECK-NEXT: 2: c +// CHECK-NEXT: 3: [B6.2] = [B6.1] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B7] +// CHECK-NEXT: case 2: +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: '2' +// CHECK-NEXT: 3: c +// CHECK-NEXT: 4: [B7.3] = [B7.2] +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B9] +// CHECK-NEXT: case 1: +// CHECK-NEXT: 1: '3' +// CHECK-NEXT: 2: c +// CHECK-NEXT: 3: [B9.2] = [B9.1] +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (2): B3 B10 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B10] +// CHECK-NEXT: case 0: +// CHECK-NEXT: 1: '2' +// CHECK-NEXT: 2: c +// CHECK-NEXT: 3: [B10.2] = [B10.1] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B9 +// CHECK: [B0 (EXIT)] +void test_switch_with_compound_with_default() { + char c = '1'; + switch (int i = getX()) { + case 0: + c = '2'; + case 1: + c = '3'; + break; + case 2: { + c = '2'; + break; + } + case 3: + c = '2'; + default: { + int a = 0; + ++i; + } + } + int k = 1; +} + +// CHECK: [B9 (ENTRY)] +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: 2: 3 +// CHECK-NEXT: 3: int k = 3; +// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: '1' +// CHECK-NEXT: 3: char c = '1'; +// CHECK-NEXT: 4: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 5: getX +// CHECK-NEXT: 6: [B2.5] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(void)) +// CHECK-NEXT: 7: [B2.6]() +// CHECK-NEXT: 8: int i = getX(); +// CHECK-NEXT: 9: i +// CHECK-NEXT: 10: [B2.9] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: T: switch [B2.10] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: T: switch [B2.10] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (4): B5 B6 B8 B4 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (3): B5 B7 B3 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B5] +// CHECK-NEXT: case 2: +// CHECK-NEXT: 1: '3' +// CHECK-NEXT: 2: c +// CHECK-NEXT: 3: [B5.2] = [B5.1] +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B6] +// CHECK-NEXT: case 1: +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: '1' +// CHECK-NEXT: 3: c +// CHECK-NEXT: 4: [B6.3] = [B6.2] +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (2): B3 B8 +// CHECK-NEXT: Succs (1): B7 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B8] +// CHECK-NEXT: case 0: +// CHECK-NEXT: 1: '2' +// CHECK-NEXT: 2: c +// CHECK-NEXT: 3: [B8.2] = [B8.1] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B6 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +int test_switch_with_compound_without_default() { + char c = '1'; + switch (int i = getX()) { + case 0: + c = '2'; + case 1: { + c = '1'; + break; + } + case 2: + c = '3'; + break; + } + int k = 3; +} + +// CHECK: [B5 (ENTRY)] +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: 2: 1 +// CHECK-NEXT: 3: int k = 1; +// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: '1' +// CHECK-NEXT: 3: char s = '1'; +// CHECK-NEXT: 4: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 5: getX +// CHECK-NEXT: 6: [B2.5] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(void)) +// CHECK-NEXT: 7: [B2.6]() +// CHECK-NEXT: 8: int i = getX(); +// CHECK-NEXT: 9: i +// CHECK-NEXT: 10: [B2.9] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: T: switch [B2.10] +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B3] +// CHECK-NEXT: default: +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: 0 +// CHECK-NEXT: 3: int a = 0; +// CHECK-NEXT: 4: i +// CHECK-NEXT: 5: ++[B3.4] +// CHECK-NEXT: 6: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B4 B2 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B4] +// CHECK-NEXT: case 0: +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_without_compound() { + char s = '1'; + switch (int i = getX()) + case 0: + default: { + int a = 0; + ++i; + } + int k = 1; +} + +// CHECK: [B8 (ENTRY)] +// CHECK-NEXT: Succs (1): B7 +// 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): B5 B6 +// 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): B6 +// 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: T: break; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B6] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 3 +// CHECK-NEXT: 4: [B6.2] < [B6.3] +// CHECK-NEXT: T: for (...; [B6.4]; ...) +// CHECK-NEXT: Preds (2): B2 B7 +// CHECK-NEXT: Succs (2): B4 B1 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 3: 0 +// CHECK-NEXT: 4: int i = 0; +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (1): B6 +// CHECK: [B0 (EXIT)] +void test_for_compound_and_break() { + for (int i = 0; i < 3; ++i) { + { + int unused1; + break; + } + } + { + int unused2; + } +} + +// CHECK: [B16 (ENTRY)] +// CHECK-NEXT: Succs (1): B15 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(BinaryOperator) +// CHECK-NEXT: 2: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B5 B14 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: ++[B2.1] +// CHECK-NEXT: Preds (2): B3 B11 +// CHECK-NEXT: Succs (1): B14 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: 5 +// CHECK-NEXT: 3: int z = 5; +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B6] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: x +// CHECK-NEXT: 3: [B6.2] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: T: switch [B6.3] +// CHECK-NEXT: Preds (1): B14 +// CHECK-NEXT: Succs (1): B7 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: T: switch [B6.3] +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (4): B10 B12 B13 B9 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B9 B12 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B9] +// CHECK-NEXT: default: +// CHECK-NEXT: 1: 3 +// CHECK-NEXT: 2: y +// CHECK-NEXT: 3: [B9.2] = [B9.1] +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B10] +// CHECK-NEXT: case 2: +// CHECK-NEXT: 1: 4 +// CHECK-NEXT: 2: y +// CHECK-NEXT: 3: [B10.2] = [B10.1] +// CHECK-NEXT: T: continue; +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B11] +// CHECK-NEXT: 1: CFGScopeEnd(ContinueStmt) +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B12] +// CHECK-NEXT: case 1: +// CHECK-NEXT: 1: 2 +// CHECK-NEXT: 2: y +// CHECK-NEXT: 3: [B12.2] = [B12.1] +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (2): B7 B13 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B13] +// CHECK-NEXT: case 0: +// CHECK-NEXT: 1: 1 +// CHECK-NEXT: 2: y +// CHECK-NEXT: 3: [B13.2] = [B13.1] +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B12 +// CHECK: [B14] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B14.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 1000 +// CHECK-NEXT: 4: [B14.2] < [B14.3] +// CHECK-NEXT: T: for (...; [B14.4]; ...) +// CHECK-NEXT: Preds (2): B2 B15 +// CHECK-NEXT: Succs (2): B6 B1 +// CHECK: [B15] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: int i; +// CHECK-NEXT: 3: int x; +// CHECK-NEXT: 4: int y; +// CHECK-NEXT: 5: CFGScopeBegin(BinaryOperator) +// CHECK-NEXT: 6: 0 +// CHECK-NEXT: 7: i +// CHECK-NEXT: 8: [B15.7] = [B15.6] +// CHECK-NEXT: Preds (1): B16 +// CHECK-NEXT: Succs (1): B14 +// CHECK: [B0 (EXIT)] +void test_for_switch_in_for() { + int i, x, y; + for (i = 0; i < 1000; ++i) { + switch (x) { + case 0: + y = 1; + case 1: + y = 2; + break; // break from switch + case 2: + y = 4; + continue; // continue in loop + default: + y = 3; + } + { + int z = 5; + break; // break from loop + } + } +} + +int vfork(); +void execl(char *, char *, int); +int _exit(int); +// CHECK: [B20 (ENTRY)] +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: CFGScopeBegin(WhileStmt) +// CHECK-NEXT: 2: CFGScopeEnd(WhileStmt) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B3] +// CHECK-NEXT: 1: 1 +// CHECK-NEXT: 2: [B3.1] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: while [B3.2] +// CHECK-NEXT: Preds (2): B2 B6 +// CHECK-NEXT: Succs (2): B2 NULL +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: x +// CHECK-NEXT: 3: [B4.2] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: T: switch [B4.3] +// CHECK-NEXT: Preds (1): B20 +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: T: switch [B4.3] +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (4): B12 B15 B19 B6 +// CHECK: [B6] +// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (4): B8 B14 B19 B5 +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B7] +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (2): B9 B12 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B6 +// CHECK: [B9] +// CHECK-NEXT: 1: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Succs (1): B7 +// CHECK: [B10] +// CHECK-NEXT: 1: CFGScopeBegin(WhileStmt) +// CHECK-NEXT: 2: CFGScopeEnd(WhileStmt) +// CHECK-NEXT: Preds (1): B11 +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B11] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: 1 +// CHECK-NEXT: 3: [B11.2] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: while [B11.3] +// CHECK-NEXT: Preds (2): B10 B12 +// CHECK-NEXT: Succs (2): B10 NULL +// CHECK: [B12] +// CHECK-NEXT: case 2: +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: int pid; +// CHECK-NEXT: 3: vfork +// CHECK-NEXT: 4: [B12.3] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(void)) +// CHECK-NEXT: 5: [B12.4]() +// CHECK-NEXT: 6: pid +// CHECK-NEXT: 7: [B12.6] = [B12.5] +// CHECK-NEXT: 8: ([B12.7]) (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 9: 0 +// CHECK-NEXT: 10: [B12.8] == [B12.9] +// CHECK-NEXT: T: if [B12.10] +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (2): B11 B7 +// CHECK: [B13] +// CHECK-NEXT: 1: 3 +// CHECK-NEXT: 2: x +// CHECK-NEXT: 3: [B13.2] = [B13.1] +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B17 +// CHECK-NEXT: Succs (1): B14 +// CHECK: [B14] +// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: Preds (1): B13 +// CHECK-NEXT: Succs (1): B6 +// CHECK: [B15] +// CHECK-NEXT: case 1: +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: char args[2]; +// CHECK-NEXT: 3: vfork +// CHECK-NEXT: 4: [B15.3] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(void)) +// CHECK-NEXT: 5: [B15.4]() +// CHECK-NEXT: T: switch [B15.5] +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B16 +// CHECK: [B16] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: T: switch [B15.5] +// CHECK-NEXT: Preds (1): B15 +// CHECK-NEXT: Succs (2): B18 B17 +// CHECK: [B17] +// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B18 B16 +// CHECK-NEXT: Succs (1): B13 +// CHECK: [B18] +// CHECK-NEXT: case 0: +// CHECK-NEXT: 1: 0 +// CHECK-NEXT: 2: [B18.1] (ImplicitCastExpr, IntegralCast, char) +// CHECK-NEXT: 3: args +// CHECK-NEXT: 4: [B18.3] (ImplicitCastExpr, ArrayToPointerDecay, char *) +// CHECK-NEXT: 5: 0 +// CHECK: 7: [B18.6] = [B18.2] +// CHECK-NEXT: 8: _exit +// CHECK-NEXT: 9: [B18.8] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(int)) +// CHECK-NEXT: 10: 1 +// CHECK-NEXT: 11: [B18.9]([B18.10]) +// CHECK-NEXT: Preds (1): B16 +// CHECK-NEXT: Succs (1): B17 +// CHECK: [B19] +// CHECK-NEXT: case 0: +// CHECK-NEXT: 1: vfork +// CHECK-NEXT: 2: [B19.1] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(void)) +// CHECK-NEXT: 3: [B19.2]() +// CHECK-NEXT: 4: 0 +// CHECK-NEXT: 5: x +// CHECK-NEXT: 6: [B19.5] = [B19.4] +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B6 +// CHECK: [B0 (EXIT)] +void test_nested_switches(int x) { + switch (x) { + case 0: + vfork(); + x = 0; + break; + + case 1: { + char args[2]; + switch (vfork()) { + case 0: + args[0] = 0; + _exit(1); + } + x = 3; + break; + } + + case 2: { + int pid; + if ((pid = vfork()) == 0) + while (1) + ; + break; + } + } + while (1) + ; +}