Index: include/clang/Analysis/AnalysisContext.h =================================================================== --- include/clang/Analysis/AnalysisContext.h +++ include/clang/Analysis/AnalysisContext.h @@ -427,6 +427,7 @@ bool addInitializers = false, bool addTemporaryDtors = false, bool addLifetime = 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, // dtor kind @@ -190,6 +192,52 @@ } }; +/// 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 *TriggerStmt, const Stmt *TerminatedStmt) + : CFGElement(ScopeEnd, TriggerStmt), TerminatedStmt(TerminatedStmt) {} + + // Get statement that triggered end of previously created scope. + const Stmt *getTriggerStmt() const { + return static_cast(Data1.getPointer()); + } + const Stmt *getTerminatedStmt() const { + return TerminatedStmt; + } + +private: + friend class CFGElement; + static bool isKind(const CFGElement &E) { + Kind kind = E.getKind(); + return kind == ScopeEnd; + } + const Stmt *TerminatedStmt; +}; + /// CFGImplicitDtor - Represents C++ object destructor implicitly generated /// by compiler on various occasions. class CFGImplicitDtor : public CFGElement { @@ -708,6 +756,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); } @@ -732,6 +795,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. @@ -795,6 +860,7 @@ bool AddImplicitDtors; bool AddLifetime; bool AddTemporaryDtors; + bool AddScopes; bool AddStaticInitBranches; bool AddCXXNewAllocator; bool AddCXXDefaultInitExprInCtors; @@ -819,7 +885,8 @@ AddEHEdges(false), AddInitializers(false), AddImplicitDtors(false), AddLifetime(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 @@ -217,6 +217,9 @@ /// \sa mayInlineCXXStandardLibrary Optional InlineCXXStandardLibrary; + /// \sa includeScopesInCFG + Optional IncludeScopesInCFG; + /// \sa mayInlineTemplateFunctions Optional InlineTemplateFunctions; @@ -415,6 +418,12 @@ /// the values "true" and "false". bool includeLifetimeInCFG(); + /// 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 @@ -68,6 +68,7 @@ bool addInitializers, bool addTemporaryDtors, bool addLifetime, + bool addScopes, bool synthesizeBodies, bool addStaticInitBranch, bool addCXXNewAllocator, @@ -79,6 +80,7 @@ cfgBuildOptions.AddInitializers = addInitializers; cfgBuildOptions.AddTemporaryDtors = addTemporaryDtors; cfgBuildOptions.AddLifetime = addLifetime; + cfgBuildOptions.AddScopes = addScopes; cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch; cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator; } Index: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ lib/Analysis/CFG.cpp @@ -246,11 +246,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()); } @@ -402,6 +404,8 @@ CFGBlock *SwitchTerminatedBlock; CFGBlock *DefaultCaseBlock; CFGBlock *TryTerminatedBlock; + Stmt *TerminatedLoop; + bool InSwitchNest; // Current position in local scope. LocalScope::const_iterator ScopePos; @@ -440,9 +444,10 @@ : 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), TerminatedLoop(nullptr), 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); @@ -513,6 +518,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 @@ -603,22 +610,28 @@ } 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 addLifetimeEnds(LocalScope::const_iterator B, LocalScope::const_iterator E, Stmt *S); void addAutomaticObjHandling(LocalScope::const_iterator B, - LocalScope::const_iterator E, Stmt *S); + LocalScope::const_iterator E, + Stmt *TriggerAutoObjDtorsStmt = nullptr, + 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) { @@ -635,6 +648,26 @@ void appendNewAllocator(CFGBlock *B, CXXNewExpr *NE) { B->appendNewAllocator(NE, cfg->getBumpVectorContext()); } + bool needAddScopes() { + // FIXME: Support SwitchStmt + return !InSwitchNest && BuildOpts.AddScopes; + } + void appendScopeBegin(CFGBlock *B, const Stmt *S) { + if (needAddScopes()) + B->appendScopeBegin(S, cfg->getBumpVectorContext()); + } + void appendScopeEnd(CFGBlock *B, const Stmt *TriggerStmt, + const Stmt *TerminatedStmt) { + if (needAddScopes()) + B->appendScopeEnd(TriggerStmt, TerminatedStmt, + cfg->getBumpVectorContext()); + } + void prependScopeEnd(CFGBlock *B, const Stmt *TriggerStmt, + const Stmt *TerminatedStmt) { + if (needAddScopes()) + B->prependScopeEnd(TriggerStmt, TerminatedStmt, + cfg->getBumpVectorContext()); + } void appendBaseDtor(CFGBlock *B, const CXXBaseSpecifier *BS) { B->appendBaseDtor(BS, cfg->getBumpVectorContext()); } @@ -1255,11 +1288,12 @@ void CFGBuilder::addAutomaticObjHandling(LocalScope::const_iterator B, LocalScope::const_iterator E, - Stmt *S) { + Stmt *TriggerAutoObjDtorsStmt, + Stmt *TriggerScopeStmt) { if (BuildOpts.AddImplicitDtors) - addAutomaticObjDtors(B, E, S); + addAutomaticObjDtors(B, E, TriggerAutoObjDtorsStmt, TriggerScopeStmt); if (BuildOpts.AddLifetime) - addLifetimeEnds(B, E, S); + addLifetimeEnds(B, E, TriggerScopeStmt); } /// Add to current block automatic objects that leave the scope. @@ -1308,10 +1342,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; @@ -1344,7 +1381,7 @@ else autoCreateBlock(); - appendAutomaticObjDtor(Block, *I, S); + appendAutomaticObjDtor(Block, *I, TriggerAutoObjDtorsStmt); } } @@ -1395,17 +1432,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 && !BuildOpts.AddLifetime) return; @@ -1416,7 +1454,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; } @@ -1424,19 +1462,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 && !BuildOpts.AddLifetime) return Scope; for (auto *DI : DS->decls()) if (VarDecl *VD = dyn_cast(DI)) - Scope = addLocalScopeForVarDecl(VD, Scope); + Scope = addLocalScopeForVarDecl(VD, TriggerScopeStmt, Scope); return Scope; } @@ -1480,6 +1519,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) { assert(!(BuildOpts.AddImplicitDtors && BuildOpts.AddLifetime) && "AddImplicitDtors and AddLifetime cannot be used at the same time"); @@ -1498,7 +1538,7 @@ if (BuildOpts.AddImplicitDtors) { if (!hasTrivialDestructor(VD)) { // Add the variable to scope - Scope = createOrReuseLocalScope(Scope); + Scope = createOrReuseLocalScope(Scope, TriggerScopeStmt); Scope->addVar(VD); ScopePos = Scope->begin(); } @@ -1507,18 +1547,37 @@ assert(BuildOpts.AddLifetime); // 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) { LocalScope::const_iterator scopeBeginPos = ScopePos; - addLocalScopeForStmt(S); - addAutomaticObjHandling(ScopePos, scopeBeginPos, S); + addLocalScopeForStmt(TriggerAutoObjDtorsStmt, TriggerScopeStmt); + addAutomaticObjHandling(ScopePos, scopeBeginPos, TriggerAutoObjDtorsStmt, + TriggerScopeStmt); } /// prependAutomaticObjDtorsWithTerminator - Prepend destructor CFGElements for @@ -1943,7 +2002,8 @@ // 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) { - addAutomaticObjHandling(ScopePos, BreakJumpTarget.scopePosition, B); + addAutomaticObjHandling(ScopePos, BreakJumpTarget.scopePosition, B, B); + prependScopeEnd(Block, B, TerminatedLoop); addSuccessor(Block, BreakJumpTarget.block); } else badCFG = true; @@ -2072,15 +2132,26 @@ 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); + 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. - addAutomaticObjHandling(ScopePos, scopeBeginPos, C); + addAutomaticObjHandling(ScopePos, scopeBeginPos, C, C); + + if (!C->body_empty() && needAddScopes() && + !shouldDeferScopeEnd(*C->body_rbegin())) { + autoCreateBlock(); + prependScopeEnd(Block, C, C); } CFGBlock *LastBlock = Block; @@ -2096,6 +2167,12 @@ return nullptr; } + if (needAddScopes() && !C->body_empty()) { + if (!LastBlock) + LastBlock = createBlock(); + appendScopeBegin(LastBlock, C); + } + return LastBlock; } @@ -2289,6 +2366,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 @@ -2303,12 +2386,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); addAutomaticObjHandling(ScopePos, save_scope_pos.get(), I); @@ -2320,6 +2403,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; @@ -2332,9 +2419,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 (needAddScopes() && !shouldDeferScopeEnd(Else)) + CreateScopeEndBlockForIfStmt(I); + } ElseBlock = addStmt(Else); if (!ElseBlock) // Can occur when the Else body has all NullStmts. @@ -2343,6 +2432,9 @@ if (badCFG) return nullptr; } + + if (!isa(Else)) + appendScopeBegin(ElseBlock, I); } // Process the true branch. @@ -2355,8 +2447,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 (needAddScopes() && !shouldDeferScopeEnd(Then)) + CreateScopeEndBlockForIfStmt(I); + } ThenBlock = addStmt(Then); @@ -2370,6 +2465,9 @@ if (badCFG) return nullptr; } + + if (!isa(Then)) + appendScopeBegin(ThenBlock, I); } // Specially handle "if (expr1 || ...)" and "if (expr1 && ...)" by @@ -2411,6 +2509,7 @@ if (const DeclStmt* DS = I->getConditionVariableDeclStmt()) { autoCreateBlock(); LastBlock = addStmt(const_cast(DS)); + appendScopeBegin(LastBlock, DS); } } @@ -2435,12 +2534,14 @@ // Create the new block. Block = createBlock(false); - addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), R); + addAutomaticObjHandling(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()) + 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). @@ -2534,11 +2635,12 @@ // 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; addAutomaticObjHandling(ScopePos, save_scope_pos.get(), F); @@ -2598,11 +2700,16 @@ // 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. - BodyBlock = addStmt(F->getBody()); + { + SaveAndRestore save_TerminatedLoop(TerminatedLoop); + TerminatedLoop = 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. @@ -2613,6 +2720,11 @@ return nullptr; } + if (!isa(F->getBody())) { + appendScopeBegin(BodyBlock, F); + if (!shouldDeferScopeEnd(F->getBody())) + prependScopeEnd(BodyBlock, F, 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. @@ -2681,7 +2793,11 @@ // 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); + prependScopeEnd(TransitionBlock, I, I); + appendScopeEnd(LoopSuccessor, I, I); + return InitBlock; } // There is no loop initialization. We are thus basically a while loop. @@ -2879,7 +2995,7 @@ // Store scope position for continue statement. LocalScope::const_iterator LoopBeginScopePos = ScopePos; if (VarDecl *VD = W->getConditionVariable()) { - addLocalScopeForVarDecl(VD); + addLocalScopeForVarDecl(VD, W); addAutomaticObjHandling(ScopePos, LoopBeginScopePos, W); } @@ -2896,6 +3012,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()); @@ -2920,15 +3041,26 @@ // If body is not a compound statement create implicit scope // and add destructors. if (!isa(W->getBody())) - addLocalScopeAndDtors(W->getBody()); + addLocalScopeAndDtors(W->getBody(), W); + + { + SaveAndRestore save_TerminatedLoop(TerminatedLoop); + TerminatedLoop = W; - // Create the body. The returned block is the entry to the loop body. - BodyBlock = addStmt(W->getBody()); + // 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); + if (!shouldDeferScopeEnd(W->getBody())) + prependScopeEnd(BodyBlock, W, W); + } } // Because of short-circuit evaluation, the condition of the loop can span @@ -2965,6 +3097,8 @@ autoCreateBlock(); appendStmt(Block, W->getConditionVariableDeclStmt()); EntryConditionBlock = addStmt(Init); + appendScopeBegin(EntryConditionBlock, Init); + appendScopeEnd(TransitionBlock, Init, Init); assert(Block == EntryConditionBlock); } } @@ -3102,10 +3236,15 @@ // If body is not a compound statement create implicit scope // and add destructors. if (!isa(D->getBody())) - addLocalScopeAndDtors(D->getBody()); + addLocalScopeAndDtors(D->getBody(), D); + + { + SaveAndRestore save_TerminatedLoop(TerminatedLoop); + TerminatedLoop = D; - // Create the body. The returned block is the entry to the loop body. - BodyBlock = addStmt(D->getBody()); + // 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(...)" @@ -3114,6 +3253,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 @@ -3157,7 +3301,8 @@ // 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) { - addAutomaticObjHandling(ScopePos, ContinueJumpTarget.scopePosition, C); + addAutomaticObjHandling(ScopePos, ContinueJumpTarget.scopePosition, C, C); + prependScopeEnd(Block, C, TerminatedLoop); addSuccessor(Block, ContinueJumpTarget.block); } else badCFG = true; @@ -3205,12 +3350,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); addAutomaticObjHandling(ScopePos, save_scope_pos.get(), Terminator); @@ -3259,26 +3404,37 @@ // If body is not a compound statement create implicit scope // and add destructors. if (!isa(Terminator->getBody())) - addLocalScopeAndDtors(Terminator->getBody()); + addLocalScopeAndDtors(Terminator->getBody(), Terminator); - 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 (Expr *Init = VD->getInit()) + appendScopeEnd(Succ, Terminator->getConditionVariableDeclStmt(), Init); - // 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); @@ -3290,8 +3446,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); } } @@ -3499,7 +3657,7 @@ // Store scope position. Add implicit destructor. if (VarDecl *VD = CS->getExceptionDecl()) { LocalScope::const_iterator BeginScopePos = ScopePos; - addLocalScopeForVarDecl(VD); + addLocalScopeForVarDecl(VD, CS); addAutomaticObjHandling(ScopePos, BeginScopePos, CS); } @@ -3549,15 +3707,17 @@ // 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); addAutomaticObjHandling(ScopePos, save_scope_pos.get(), S); 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; @@ -3610,6 +3770,7 @@ // continue statements. Block = nullptr; Succ = addStmt(S->getInc()); + prependScopeEnd(Succ, LoopVarStmt, LoopVarStmt); if (badCFG) return nullptr; ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos); @@ -3625,16 +3786,29 @@ Block = nullptr; // 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()); + addLocalScopeAndDtors(S->getLoopVarStmt(), S); + + CFGBlock *BodyBlock = nullptr; + { + SaveAndRestore save_TerminatedLoop(TerminatedLoop); + TerminatedLoop = 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); + } + + appendScopeBegin(BodyBlock, LoopVarStmt); + // This new body block is a successor to our condition block. addSuccessor(ConditionBlock, KnownVal.isFalse() ? nullptr : LoopVarStmtBlock); @@ -4026,6 +4200,8 @@ case CFGElement::Initializer: case CFGElement::NewAllocator: case CFGElement::LifetimeEnds: + case CFGElement::ScopeBegin: + case CFGElement::ScopeEnd: llvm_unreachable("getDestructorDecl should only be used with " "ImplicitDtors"); case CFGElement::AutomaticObjectDtor: { @@ -4442,6 +4618,16 @@ OS << " (Lifetime ends)\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 @@ -27,6 +27,7 @@ /*AddInitializers=*/true, Options.includeTemporaryDtorsInCFG(), Options.includeLifetimeInCFG(), + Options.includeScopesInCFG(), Options.shouldSynthesizeBodies(), Options.shouldConditionalizeStaticInitializers(), /*addCXXNewAllocator=*/true, Index: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp =================================================================== --- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -183,6 +183,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 @@ -14,6 +14,7 @@ // CHECK-NEXT: cfg-conditional-static-initializers = true // CHECK-NEXT: cfg-implicit-dtors = true // CHECK-NEXT: cfg-lifetime = false +// CHECK-NEXT: cfg-scopes = false // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -29,4 +30,4 @@ // CHECK-NEXT: region-store-small-struct-limit = 2 // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 17 +// CHECK-NEXT: num-entries = 18 Index: test/Analysis/analyzer-config.cpp =================================================================== --- test/Analysis/analyzer-config.cpp +++ test/Analysis/analyzer-config.cpp @@ -25,6 +25,7 @@ // CHECK-NEXT: cfg-conditional-static-initializers = true // CHECK-NEXT: cfg-implicit-dtors = true // CHECK-NEXT: cfg-lifetime = false +// CHECK-NEXT: cfg-scopes = false // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -40,4 +41,4 @@ // CHECK-NEXT: region-store-small-struct-limit = 2 // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 22 +// CHECK-NEXT: num-entries = 23 Index: test/Analysis/scopes-cfg-output.cpp =================================================================== --- /dev/null +++ test/Analysis/scopes-cfg-output.cpp @@ -0,0 +1,1030 @@ +// 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: (CXXConstructExpr, class A) +// CHECK-NEXT: 12: A b; +// CHECK-NEXT: 13: [B1.12].~A() (Implicit destructor) +// CHECK-NEXT: 14: [B1.3].~A() (Implicit destructor) +// CHECK-NEXT: 15: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 16: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +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: [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(CXXConstructExpr) +// 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: 6: CFGScopeEnd(WhileStmt) +// 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)] +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: 1: CFGScopeEnd(CXXConstructExpr) +// 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: [B10.5].~A() (Implicit destructor) +// CHECK-NEXT: 6: 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: [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)] +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(DeclStmt) +// 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: 6: CFGScopeEnd(ForStmt) +// 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)] +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: 1: CFGScopeEnd(DeclStmt) +// 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: [B10.5].~A() (Implicit destructor) +// CHECK-NEXT: 6: 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: [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)] +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: 3: CFGScopeEnd(DeclStmt) +// 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)] +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: 3: CFGScopeEnd(DeclStmt) +// 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: 1: CFGScopeEnd(CXXConstructExpr) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B7] +// CHECK-NEXT: 1: j +// CHECK-NEXT: 2: ++[B7.1] +// CHECK-NEXT: 3: [B10.5].~A() (Implicit destructor) +// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt) +// 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; + } +}