Index: include/clang/Analysis/AnalysisContext.h =================================================================== --- include/clang/Analysis/AnalysisContext.h +++ include/clang/Analysis/AnalysisContext.h @@ -425,6 +425,7 @@ bool addImplicitDtors = false, bool addInitializers = false, bool addTemporaryDtors = false, + bool addScopeInfo = 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 @@ Statement, Initializer, NewAllocator, + ScopeBegin, + ScopeEnd, // dtor kind AutomaticObjectDtor, DeleteDtor, @@ -166,6 +168,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 { @@ -666,6 +710,14 @@ Elements.push_back(CFGNewAllocator(NE), C); } + void appendScopeBegin(Stmt *S, BumpVectorContext &C) { + Elements.push_back(CFGScopeBegin(S), C); + } + + void appendScopeEnd(Stmt *S, BumpVectorContext &C) { + Elements.push_back(CFGScopeEnd(S), C); + } + void appendBaseDtor(const CXXBaseSpecifier *BS, BumpVectorContext &C) { Elements.push_back(CFGBaseDtor(BS), C); } @@ -735,6 +787,7 @@ bool AddInitializers; bool AddImplicitDtors; bool AddTemporaryDtors; + bool AddScopes; bool AddStaticInitBranches; bool AddCXXNewAllocator; bool AddCXXDefaultInitExprInCtors; @@ -757,8 +810,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) {} }; /// \brief Provides a custom implementation of the iterator class to have the Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h =================================================================== --- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -204,6 +204,9 @@ /// \sa includeTemporaryDtorsInCFG Optional IncludeTemporaryDtorsInCFG; + /// \sa includeScopesInCFG + Optional IncludeScopesInCFG; + /// \sa mayInlineCXXStandardLibrary Optional InlineCXXStandardLibrary; @@ -388,6 +391,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 *S; 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), S(s) {} /// Begin of scope in direction of CFG building (backwards). const_iterator begin() const { return const_iterator(*this, Vars.size()); } @@ -578,18 +580,20 @@ } CFGBlock *addInitializer(CXXCtorInitializer *I); void addAutomaticObjDtors(LocalScope::const_iterator B, - LocalScope::const_iterator E, Stmt *S); + LocalScope::const_iterator E, Stmt *TriggerStmt, + Stmt *ScopeCreator = nullptr, bool isScopeEnd = false); void addImplicitDtorsForDestructor(const CXXDestructorDecl *DD); // Local scopes creation. - LocalScope* createOrReuseLocalScope(LocalScope* Scope); + LocalScope* createOrReuseLocalScope(LocalScope* Scope, Stmt *ScopeCreator); - void addLocalScopeForStmt(Stmt *S); - LocalScope* addLocalScopeForDeclStmt(DeclStmt *DS, + void addLocalScopeForStmt(Stmt *S, Stmt *ScopeCreator); + LocalScope* addLocalScopeForDeclStmt(DeclStmt *DS, Stmt *ScopeCreator, LocalScope* Scope = nullptr); - LocalScope* addLocalScopeForVarDecl(VarDecl *VD, LocalScope* Scope = nullptr); + LocalScope* addLocalScopeForVarDecl(VarDecl *VD, Stmt *ScopeCreator, + LocalScope* Scope = nullptr); - void addLocalScopeAndDtors(Stmt *S); + void addLocalScopeAndDtors(Stmt *S, Stmt *ScopeCreator); // Interface to CFGBlock - adding CFGElements. void appendStmt(CFGBlock *B, const Stmt *S) { @@ -606,6 +610,12 @@ void appendNewAllocator(CFGBlock *B, CXXNewExpr *NE) { B->appendNewAllocator(NE, cfg->getBumpVectorContext()); } + void appendScopeBegin(CFGBlock *B, Stmt *S) { + B->appendScopeBegin(S, cfg->getBumpVectorContext()); + } + void appendScopeEnd(CFGBlock *B, Stmt *S) { + B->appendScopeEnd(S, cfg->getBumpVectorContext()); + } void appendBaseDtor(CFGBlock *B, const CXXBaseSpecifier *BS) { B->appendBaseDtor(BS, cfg->getBumpVectorContext()); } @@ -1211,7 +1221,15 @@ /// for objects in range of local scope positions. Use S as trigger statement /// for destructors. void CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B, - LocalScope::const_iterator E, Stmt *S) { + LocalScope::const_iterator E, + Stmt *TriggerStmt, Stmt *ScopeCreator, + bool isScopeEnd) { + // Signal addition of ScopeEnd CFG Element, creating `Block` if necessary. + if (BuildOpts.AddScopes && isScopeEnd) { + autoCreateBlock(); + appendScopeEnd(Block, ScopeCreator); + } + if (!BuildOpts.AddImplicitDtors) return; @@ -1244,7 +1262,7 @@ else autoCreateBlock(); - appendAutomaticObjDtor(Block, *I, S); + appendAutomaticObjDtor(Block, *I, TriggerStmt); } } @@ -1295,17 +1313,20 @@ /// 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 *ScopeCreator) { if (Scope) return Scope; + llvm::BumpPtrAllocator &alloc = cfg->getAllocator(); return new (alloc.Allocate()) - LocalScope(BumpVectorContext(alloc), ScopePos); + LocalScope(BumpVectorContext(alloc), ScopePos, ScopeCreator); } /// addLocalScopeForStmt - Add LocalScope to local scopes tree for statement /// that should create implicit scope (e.g. if/else substatements). -void CFGBuilder::addLocalScopeForStmt(Stmt *S) { +void CFGBuilder::addLocalScopeForStmt(Stmt *S, Stmt *ScopeCreator) { + if (!BuildOpts.AddImplicitDtors) return; @@ -1316,7 +1337,7 @@ for (auto *BI : CS->body()) { Stmt *SI = BI->stripLabelLikeStatements(); if (DeclStmt *DS = dyn_cast(SI)) - Scope = addLocalScopeForDeclStmt(DS, Scope); + Scope = addLocalScopeForDeclStmt(DS, ScopeCreator, Scope); } return; } @@ -1324,26 +1345,27 @@ // 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, ScopeCreator); } /// addLocalScopeForDeclStmt - Add LocalScope for declaration statement. Will /// reuse Scope if not NULL. LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS, + Stmt *ScopeCreator, 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, ScopeCreator, Scope); return Scope; } /// addLocalScopeForVarDecl - Add LocalScope for variable declaration. It will /// create add scope for automatic objects and temporary objects bound to /// const reference. Will reuse Scope if not NULL. -LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD, +LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD, Stmt *ScopeCreator, LocalScope* Scope) { if (!BuildOpts.AddImplicitDtors) return Scope; @@ -1390,22 +1412,31 @@ if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl()) if (!CD->hasTrivialDestructor()) { // Add the variable to scope - Scope = createOrReuseLocalScope(Scope); + Scope = createOrReuseLocalScope(Scope, ScopeCreator); Scope->addVar(VD); ScopePos = Scope->begin(); } return Scope; } +static bool deferAutomaticObjDtors(Stmt *S) { + if (isa(S) || isa(S) || isa(S)) + return true; + return false; +} + /// 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) { +void CFGBuilder::addLocalScopeAndDtors(Stmt *S, Stmt *ScopeCreator) { if (!BuildOpts.AddImplicitDtors) return; LocalScope::const_iterator scopeBeginPos = ScopePos; - addLocalScopeForStmt(S); - addAutomaticObjDtors(ScopePos, scopeBeginPos, S); + addLocalScopeForStmt(S, ScopeCreator); + // Disallow return statements from triggering end of scope because they + // directly invoke addAutomaticObjDtors that explicitly ends scope. + if (!deferAutomaticObjDtors(S)) + addAutomaticObjDtors(ScopePos, scopeBeginPos, S, ScopeCreator, true); } /// prependAutomaticObjDtorsWithTerminator - Prepend destructor CFGElements for @@ -1809,7 +1840,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) { - addAutomaticObjDtors(ScopePos, BreakJumpTarget.scopePosition, B); + addAutomaticObjDtors(ScopePos, BreakJumpTarget.scopePosition, B, B, + /*isScopeEnd=*/true); addSuccessor(Block, BreakJumpTarget.block); } else badCFG = true; @@ -1938,15 +1970,14 @@ return addStmt(C->getCond()); } - CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) { 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() || !deferAutomaticObjDtors(*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, /*isScopeEnd=*/true); } CFGBlock *LastBlock = Block; @@ -1962,6 +1993,12 @@ return nullptr; } + if (BuildOpts.AddScopes) { + if (!LastBlock) + LastBlock = createBlock(); + appendScopeBegin(LastBlock, C); + } + return LastBlock; } @@ -2172,7 +2209,7 @@ // Store scope position. Add implicit destructor. if (VarDecl *VD = I->getConditionVariable()) { LocalScope::const_iterator BeginScopePos = ScopePos; - addLocalScopeForVarDecl(VD); + addLocalScopeForVarDecl(VD, I); addAutomaticObjDtors(ScopePos, BeginScopePos, I); } @@ -2197,7 +2234,7 @@ // If branch is not a compound statement create implicit scope // and add destructors. if (!isa(Else)) - addLocalScopeAndDtors(Else); + addLocalScopeAndDtors(Else, I); ElseBlock = addStmt(Else); @@ -2207,6 +2244,9 @@ if (badCFG) return nullptr; } + + if (BuildOpts.AddScopes && !isa(Else)) + appendScopeBegin(ElseBlock, I); } // Process the true branch. @@ -2220,7 +2260,7 @@ // If branch is not a compound statement create implicit scope // and add destructors. if (!isa(Then)) - addLocalScopeAndDtors(Then); + addLocalScopeAndDtors(Then, I); ThenBlock = addStmt(Then); @@ -2234,6 +2274,9 @@ if (badCFG) return nullptr; } + + if (BuildOpts.AddScopes && !isa(Then)) + appendScopeBegin(ThenBlock, I); } // Specially handle "if (expr1 || ...)" and "if (expr1 && ...)" by @@ -2290,7 +2333,8 @@ // Create the new block. Block = createBlock(false); - addAutomaticObjDtors(ScopePos, LocalScope::const_iterator(), R); + addAutomaticObjDtors(ScopePos, LocalScope::const_iterator(), R, R, + /*isScopeEnd=*/true); // If the one of the destructors does not return, we already have the Exit // block as a successor. @@ -2389,11 +2433,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); @@ -2453,7 +2497,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. @@ -2466,6 +2510,9 @@ } else if (badCFG) return nullptr; + + if (BuildOpts.AddScopes && !isa(F->getBody())) + appendScopeBegin(BodyBlock, F); } // Because of short-circuit evaluation, the condition of the loop can span @@ -2734,7 +2781,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); } @@ -2775,7 +2822,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()); @@ -2784,6 +2831,9 @@ BodyBlock = ContinueJumpTarget.block; // can happen for "while(...) ;" else if (Block && badCFG) return nullptr; + + if (BuildOpts.AddScopes && !isa(W->getBody())) + appendScopeBegin(BodyBlock, W); } // Because of short-circuit evaluation, the condition of the loop can span @@ -2957,7 +3007,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()); @@ -2969,6 +3019,9 @@ return nullptr; } + if (BuildOpts.AddScopes && !isa(D->getBody())) + appendScopeBegin(BodyBlock, D); + if (!KnownVal.isFalse()) { // Add an intermediate block between the BodyBlock and the // ExitConditionBlock to represent the "loop back" transition. Create an @@ -3013,7 +3066,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) { - addAutomaticObjDtors(ScopePos, ContinueJumpTarget.scopePosition, C); + addAutomaticObjDtors(ScopePos, ContinueJumpTarget.scopePosition, C, C, + /*isScopeEnd=*/true); addSuccessor(Block, ContinueJumpTarget.block); } else badCFG = true; @@ -3063,7 +3117,7 @@ // Store scope position. Add implicit destructor. if (VarDecl *VD = Terminator->getConditionVariable()) { LocalScope::const_iterator SwitchBeginScopePos = ScopePos; - addLocalScopeForVarDecl(VD); + addLocalScopeForVarDecl(VD, Terminator); addAutomaticObjDtors(ScopePos, SwitchBeginScopePos, Terminator); } @@ -3112,7 +3166,7 @@ // 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) { @@ -3346,7 +3400,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); } @@ -3396,11 +3450,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; @@ -3470,7 +3524,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()); @@ -3868,6 +3922,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: { @@ -4273,6 +4329,16 @@ if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr()) AllocExpr->getType().print(OS, PrintingPolicy(Helper.getLangOpts())); OS << ")\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 DE = E.getAs()) { const CXXRecordDecl *RD = DE->getCXXRecordDecl(); if (!RD) 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 @@ -153,6 +153,12 @@ /* Default = */ false); } +bool AnalyzerOptions::includeScopesInCFG() { + return getBooleanOption(IncludeScopesInCFG, + "cfg-scopes", + /* Default = */ false); +} + bool AnalyzerOptions::mayInlineCXXStandardLibrary() { return getBooleanOption(InlineCXXStandardLibrary, "c++-stdlib-inlining", Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -310,6 +310,9 @@ case CFGElement::TemporaryDtor: ProcessImplicitDtor(E.castAs(), Pred); return; + case CFGElement::ScopeBegin: + case CFGElement::ScopeEnd: + llvm_unreachable("Local scopes not yet implemented."); } } Index: lib/StaticAnalyzer/Core/PathDiagnostic.cpp =================================================================== --- lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -570,6 +570,8 @@ } case CFGElement::TemporaryDtor: case CFGElement::NewAllocator: + case CFGElement::ScopeBegin: + case CFGElement::ScopeEnd: llvm_unreachable("not yet implemented!"); } 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,696 @@ +// 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: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B2 +// 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: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B2 +// 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; } +}; + +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: 11: const A &c = A(); +// CHECK: 12: [B1.11].~A() (Implicit destructor) +// CHECK: 13: [B1.3].~A() (Implicit destructor) +// CHECK: 14: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_const_ref() { + A a; + const A& b = a; + const A& c = A(); +} + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A [2]) +// CHECK-NEXT: 3: A a[2]; +// CHECK-NEXT: 4: (CXXConstructExpr, class A [0]) +// CHECK-NEXT: 5: A b[0]; +// CHECK-NEXT: 6: [B1.3].~A() (Implicit destructor) +// CHECK-NEXT: 7: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_array() { + A a[2]; + A b[0]; +} + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 5: (CXXConstructExpr, class A) +// CHECK-NEXT: 6: A c; +// CHECK-NEXT: 7: (CXXConstructExpr, class A) +// CHECK-NEXT: 8: A d; +// CHECK-NEXT: 9: [B1.8].~A() (Implicit destructor) +// CHECK-NEXT: 10: [B1.6].~A() (Implicit destructor) +// CHECK-NEXT: 11: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 12: (CXXConstructExpr, class A) +// CHECK: 13: A b; +// CHECK: 14: [B1.13].~A() (Implicit destructor) +// CHECK: 15: [B1.3].~A() (Implicit destructor) +// CHECK: 16: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_scope() { + A a; + { A c; + A d; + } + A b; +} + +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B1.2].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B3.5].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B3.3].~A() (Implicit destructor) +// CHECK-NEXT: 6: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B3.5].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B3.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A b; +// CHECK-NEXT: 6: UV +// CHECK-NEXT: 7: [B3.6] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B3.7] +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (2): B2 B1 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B2 +void test_return() { + A a; + A b; + if (UV) return; + A c; +} + +// CHECK: [B5 (ENTRY)] +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B1] +// CHECK-NEXT: 1: [B4.7].~A() (Implicit destructor) +// CHECK-NEXT: 2: [B4.3].~A() (Implicit destructor) +// CHECK-NEXT: 3: 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: a +// CHECK-NEXT: 5: [B4.4] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 6: [B4.5] (CXXConstructExpr, class A) +// CHECK-NEXT: 7: A b = a; +// CHECK-NEXT: 8: b +// CHECK-NEXT: 9: [B4.8] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 10: [B4.9].operator int +// CHECK: 11: [B4.9] +// CHECK: 12: [B4.11] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK: 13: [B4.12] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B4.13] +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (2): B3 B2 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +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: [B8.7].~A() (Implicit destructor) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A e; +// CHECK-NEXT: 4: [B1.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B8.3].~A() (Implicit destructor) +// CHECK-NEXT: 6: 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.7].~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.7].~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: a +// CHECK-NEXT: 5: [B8.4] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 6: [B8.5] (CXXConstructExpr, class A) +// CHECK-NEXT: 7: A b = a; +// CHECK-NEXT: 8: b +// CHECK-NEXT: 9: [B8.8] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 10: [B8.9].operator int +// CHECK: 11: [B8.9] +// CHECK: 12: [B8.11] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK: 13: [B8.12] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B8.13] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B7 B4 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (3): B1 B3 B6 +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: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B1] +// CHECK-NEXT: 1: [B4.4].~A() (Implicit destructor) +// CHECK-NEXT: 2: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: 3: 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(WhileStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: 4: [B3.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(WhileStmt) +// CHECK-NEXT: 6: [B4.4].~A() (Implicit destructor) +// 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: [B12 (ENTRY)] +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B1] +// CHECK-NEXT: 1: [B10.4].~A() (Implicit destructor) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A e; +// CHECK-NEXT: 4: [B1.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B11.3].~A() (Implicit destructor) +// CHECK-NEXT: 6: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B8 B10 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (2): B3 B6 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: [B3.2].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 6: [B10.4].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B10.4].~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.4].~A() (Implicit destructor) +// CHECK-NEXT: 4: CFGScopeEnd(ContinueStmt) +// CHECK-NEXT: T: continue; +// CHECK: 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: 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: a +// CHECK-NEXT: 2: [B10.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B10.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A b = a; +// CHECK-NEXT: 5: b +// CHECK-NEXT: 6: [B10.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B10.6].operator int +// CHECK-NEXT: 8: [B10.6] +// CHECK-NEXT: 9: [B10.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK: 10: [B10.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: while [B10.10] +// CHECK-NEXT: Preds (2): B2 B11 +// CHECK-NEXT: Succs (2): B9 B1 +// CHECK: [B11] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B4 +void test_while_jumps() { + A a; + while (A b = a) { + A c; + if (UV) break; + if (UV) continue; + if (UV) return; + A d; + } + A e; +} + +// CHECK: [B12 (ENTRY)] +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: [B1.2].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B11.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B8 B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: do ... while [B2.2] +// CHECK-NEXT: Preds (2): B3 B6 +// CHECK-NEXT: Succs (2): B10 B1 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B3.2].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B11.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B5] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B5.2] +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B6] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 3: CFGScopeEnd(ContinueStmt) +// CHECK-NEXT: T: continue; +// CHECK: 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: 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: [B4.4].~A() (Implicit destructor) +// CHECK-NEXT: 2: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: 3: 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: CFGScopeEnd(ForStmt) +// CHECK-NEXT: 6: [B4.4].~A() (Implicit destructor) +// 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: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_for_implicit_scope() { + for (A a; A b = a; ) + A c; +} + +// CHECK: [B12 (ENTRY)] +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B1] +// CHECK-NEXT: 1: [B10.4].~A() (Implicit destructor) +// CHECK-NEXT: 2: [B11.5].~A() (Implicit destructor) +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A f; +// CHECK-NEXT: 5: [B1.4].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B11.3].~A() (Implicit destructor) +// CHECK-NEXT: 7: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B8 B10 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (2): B3 B6 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A e; +// CHECK-NEXT: 3: [B3.2].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 6: [B10.4].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B10.4].~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: 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: 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: b +// CHECK-NEXT: 2: [B10.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B10.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A c = b; +// CHECK-NEXT: 5: c +// CHECK-NEXT: 6: [B10.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B10.6].operator int +// CHECK-NEXT: 8: [B10.6] +// CHECK-NEXT: 9: [B10.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK: 10: [B10.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: for (...; [B10.10]; ) +// CHECK-NEXT: Preds (2): B2 B11 +// CHECK-NEXT: Succs (2): B9 B1 +// CHECK: [B11] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A b; +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B4 +void test_for_jumps() { + A a; + for (A b; A c = b; ) { + A d; + if (UV) break; + if (UV) continue; + if (UV) return; + A e; + } + A f; +}