diff --git a/include/clang/Analysis/AnalysisContext.h b/include/clang/Analysis/AnalysisContext.h index ec7549d4535cc80c2047c957e9bdf40bfe8087f8..cc08e05822d41138a0162578d198d1c8fbda3c1a 100644 --- a/include/clang/Analysis/AnalysisContext.h +++ b/include/clang/Analysis/AnalysisContext.h @@ -427,6 +427,7 @@ public: bool addInitializers = false, bool addTemporaryDtors = false, bool addLifetime = false, + bool addLoopExit = false, bool synthesizeBodies = false, bool addStaticInitBranches = false, bool addCXXNewAllocator = true, diff --git a/include/clang/Analysis/CFG.h b/include/clang/Analysis/CFG.h index 97639bbfade206aa1b51b129723f8386c38cb3a2..8cdd782671603fb9f9485cb851d2a823734ae5e0 100644 --- a/include/clang/Analysis/CFG.h +++ b/include/clang/Analysis/CFG.h @@ -59,6 +59,7 @@ public: Initializer, NewAllocator, LifetimeEnds, + LoopExit, // dtor kind AutomaticObjectDtor, DeleteDtor, @@ -168,6 +169,29 @@ private: } }; +/// Represents the point where a loop ends. +/// This element is is only produced when building the CFG for the static +/// analyzer and hidden behind the 'cfg-loopexit' analyzer config flag. +/// +/// Note: a loop exit element can be reached even when the loop body was never +/// entered. +class CFGLoopExit : public CFGElement { +public: + explicit CFGLoopExit(const Stmt *stmt) + : CFGElement(LoopExit, stmt) {} + + const Stmt *getLoopStmt() const { + return static_cast<Stmt *>(Data1.getPointer()); + } + +private: + friend class CFGElement; + CFGLoopExit() {} + static bool isKind(const CFGElement &elem) { + return elem.getKind() == LoopExit; + } +}; + /// Represents the point where the lifetime of an automatic object ends class CFGLifetimeEnds : public CFGElement { public: @@ -728,6 +752,10 @@ public: Elements.push_back(CFGLifetimeEnds(VD, S), C); } + void appendLoopExit(const Stmt *LoopStmt, BumpVectorContext &C) { + Elements.push_back(CFGLoopExit(LoopStmt), C); + } + void appendDeleteDtor(CXXRecordDecl *RD, CXXDeleteExpr *DE, BumpVectorContext &C) { Elements.push_back(CFGDeleteDtor(RD, DE), C); } @@ -794,6 +822,7 @@ public: bool AddInitializers; bool AddImplicitDtors; bool AddLifetime; + bool AddLoopExit; bool AddTemporaryDtors; bool AddStaticInitBranches; bool AddCXXNewAllocator; @@ -818,7 +847,7 @@ public: PruneTriviallyFalseEdges(true), AddEHEdges(false), AddInitializers(false), AddImplicitDtors(false), - AddLifetime(false), + AddLifetime(false), AddLoopExit(false), AddTemporaryDtors(false), AddStaticInitBranches(false), AddCXXNewAllocator(false), AddCXXDefaultInitExprInCtors(false) {} }; diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h index 66e1a746ffa0a697ba7e0d03e47211e2075acfef..ce50cc582d1e00389140abd881875f85a8be9bba 100644 --- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -214,6 +214,9 @@ private: /// \sa IncludeLifetimeInCFG Optional<bool> IncludeLifetimeInCFG; + /// \sa IncludeLoopExitInCFG + Optional<bool> IncludeLoopExitInCFG; + /// \sa mayInlineCXXStandardLibrary Optional<bool> InlineCXXStandardLibrary; @@ -418,6 +421,13 @@ public: /// the values "true" and "false". bool includeLifetimeInCFG(); + /// Returns whether or not the end of the loop information should be included + /// in the CFG. + /// + /// This is controlled by the 'cfg-loopexit' config option, which accepts + /// the values "true" and "false". + bool includeLoopExitInCFG(); + /// Returns whether or not C++ standard library functions may be considered /// for inlining. /// diff --git a/lib/Analysis/AnalysisDeclContext.cpp b/lib/Analysis/AnalysisDeclContext.cpp index ec15f34fb231d259641340da0f9ce3ca27e40739..0f123fdc4b32708630ce84df6ffcba6197529d80 100644 --- a/lib/Analysis/AnalysisDeclContext.cpp +++ b/lib/Analysis/AnalysisDeclContext.cpp @@ -68,6 +68,7 @@ AnalysisDeclContextManager::AnalysisDeclContextManager(bool useUnoptimizedCFG, bool addInitializers, bool addTemporaryDtors, bool addLifetime, + bool addLoopExit, bool synthesizeBodies, bool addStaticInitBranch, bool addCXXNewAllocator, @@ -79,6 +80,7 @@ AnalysisDeclContextManager::AnalysisDeclContextManager(bool useUnoptimizedCFG, cfgBuildOptions.AddInitializers = addInitializers; cfgBuildOptions.AddTemporaryDtors = addTemporaryDtors; cfgBuildOptions.AddLifetime = addLifetime; + cfgBuildOptions.AddLoopExit = addLoopExit; cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch; cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator; } diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 6a77455edeef670e8f54735130a59daac054fec0..c47a917bef63dec4f66ed7d4094b4763c8393fbe 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -602,6 +602,7 @@ private: return Visit(S, AddStmtChoice::AlwaysAdd); } CFGBlock *addInitializer(CXXCtorInitializer *I); + void addLoopExit(const Stmt *LoopStmt); void addAutomaticObjDtors(LocalScope::const_iterator B, LocalScope::const_iterator E, Stmt *S); void addLifetimeEnds(LocalScope::const_iterator B, @@ -652,6 +653,10 @@ private: B->appendLifetimeEnds(VD, S, cfg->getBumpVectorContext()); } + void appendLoopExit(CFGBlock *B, const Stmt *LoopStmt) { + B->appendLoopExit(LoopStmt, cfg->getBumpVectorContext()); + } + void appendDeleteDtor(CFGBlock *B, CXXRecordDecl *RD, CXXDeleteExpr *DE) { B->appendDeleteDtor(RD, DE, cfg->getBumpVectorContext()); } @@ -1253,6 +1258,16 @@ static QualType getReferenceInitTemporaryType(ASTContext &Context, return Init->getType(); } + +// TODO: Support adding LoopExit element to the CFG in case where the loop is +// ended by ReturnStmt, GotoStmt or ThrowExpr. +void CFGBuilder::addLoopExit(const Stmt *LoopStmt){ + if(!BuildOpts.AddLoopExit) + return; + autoCreateBlock(); + appendLoopExit(Block, LoopStmt); +} + void CFGBuilder::addAutomaticObjHandling(LocalScope::const_iterator B, LocalScope::const_iterator E, Stmt *S) { @@ -2543,6 +2558,8 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { addAutomaticObjHandling(ScopePos, save_scope_pos.get(), F); + addLoopExit(F); + // "for" is a control-flow statement. Thus we stop processing the current // block. if (Block) { @@ -2882,6 +2899,7 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { addLocalScopeForVarDecl(VD); addAutomaticObjHandling(ScopePos, LoopBeginScopePos, W); } + addLoopExit(W); // "while" is a control-flow statement. Thus we stop processing the current // block. @@ -3045,6 +3063,8 @@ CFGBlock *CFGBuilder::VisitCXXThrowExpr(CXXThrowExpr *T) { CFGBlock *CFGBuilder::VisitDoStmt(DoStmt *D) { CFGBlock *LoopSuccessor = nullptr; + addLoopExit(D); + // "do...while" is a control-flow statement. Thus we stop processing the // current block. if (Block) { @@ -4025,6 +4045,7 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const { case CFGElement::Statement: case CFGElement::Initializer: case CFGElement::NewAllocator: + case CFGElement::LoopExit: case CFGElement::LifetimeEnds: llvm_unreachable("getDestructorDecl should only be used with " "ImplicitDtors"); @@ -4442,6 +4463,9 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, OS << " (Lifetime ends)\n"; + } else if (Optional<CFGLoopExit> LE = E.getAs<CFGLoopExit>()) { + const Stmt *LoopStmt = LE->getLoopStmt(); + OS << LoopStmt->getStmtClassName() << " (LoopExit)\n"; } else if (Optional<CFGNewAllocator> NE = E.getAs<CFGNewAllocator>()) { OS << "CFGNewAllocator("; if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr()) diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 83e67662e614c322af8af595d06f90541fbc4e2c..10cf079b3462625188214d4d25c74734d1b24a69 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -26,7 +26,8 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, Options.includeImplicitDtorsInCFG(), /*AddInitializers=*/true, Options.includeTemporaryDtorsInCFG(), - Options.includeLifetimeInCFG(), + Options.includeLifetimeInCFG(), + Options.includeLoopExitInCFG(), Options.shouldSynthesizeBodies(), Options.shouldConditionalizeStaticInitializers(), /*addCXXNewAllocator=*/true, diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index d5d6527f4fc5645d110fd39144ffa1d6a1f3e06f..48e3e22af04ab6b0cf8022eb67d958a520e8a934 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -183,6 +183,11 @@ bool AnalyzerOptions::includeLifetimeInCFG() { /* Default = */ false); } +bool AnalyzerOptions::includeLoopExitInCFG() { + return getBooleanOption(IncludeLoopExitInCFG, "cfg-loopexit", + /* Default = */ false); +} + bool AnalyzerOptions::mayInlineCXXStandardLibrary() { return getBooleanOption(InlineCXXStandardLibrary, "c++-stdlib-inlining", diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 835b3536865215504ebe91163d1f8901e0dc412e..547978ad43c7d8d6ad0978314ab30f1b7c8ed056 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -365,6 +365,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, ProcessImplicitDtor(E.castAs<CFGImplicitDtor>(), Pred); return; case CFGElement::LifetimeEnds: + case CFGElement::LoopExit: return; } } diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index d91786f749198d2b2f5f92e30400edcc294b595b..907755683f47c60c8e94a38f2da0f1e47e60802b 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -578,8 +578,10 @@ getLocationForCaller(const StackFrameContext *SFC, } case CFGElement::TemporaryDtor: case CFGElement::NewAllocator: - case CFGElement::LifetimeEnds: llvm_unreachable("not yet implemented!"); + case CFGElement::LifetimeEnds: + case CFGElement::LoopExit: + llvm_unreachable("CFGElement kind should not be on callsite!"); } llvm_unreachable("Unknown CFGElement kind"); diff --git a/test/Analysis/analyzer-config.c b/test/Analysis/analyzer-config.c index 7bed7cb5e3bade742a72114b2af32ea9cc412b1a..4daea898ecd0d4388e56971988024847e5ee6884 100644 --- a/test/Analysis/analyzer-config.c +++ b/test/Analysis/analyzer-config.c @@ -14,6 +14,7 @@ void foo() { // CHECK-NEXT: cfg-conditional-static-initializers = true // CHECK-NEXT: cfg-implicit-dtors = true // CHECK-NEXT: cfg-lifetime = false +// CHECK-NEXT: cfg-loopexit = false // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -30,4 +31,4 @@ void foo() { // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 18 +// CHECK-NEXT: num-entries = 19 diff --git a/test/Analysis/analyzer-config.cpp b/test/Analysis/analyzer-config.cpp index 155ee02e839ee2c839fc0e4cfb7c30d9d27ceb0c..a08e85e53e48b0bd83f63e8ebf43099ad771ee64 100644 --- a/test/Analysis/analyzer-config.cpp +++ b/test/Analysis/analyzer-config.cpp @@ -25,6 +25,7 @@ public: // CHECK-NEXT: cfg-conditional-static-initializers = true // CHECK-NEXT: cfg-implicit-dtors = true // CHECK-NEXT: cfg-lifetime = false +// CHECK-NEXT: cfg-loopexit = false // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -41,4 +42,4 @@ public: // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 23 +// CHECK-NEXT: num-entries = 24 diff --git a/test/Analysis/loopexit-cfg-output.cpp b/test/Analysis/loopexit-cfg-output.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8e53ce3066b7f7ab0e78eaaba5ca72e65562c09e --- /dev/null +++ b/test/Analysis/loopexit-cfg-output.cpp @@ -0,0 +1,476 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCFG -analyzer-config cfg-loopexit=true %s > %t 2>&1 +// RUN: FileCheck --input-file=%t %s + +// CHECK: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 + +// CHECK: [B1] +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B2.1]++ +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B4 + +// CHECK: [B3] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B3.1]++ +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 + +// CHECK: [B4] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 12 +// CHECK-NEXT: 4: [B4.2] < [B4.3] +// CHECK-NEXT: T: for (...; [B4.4]; ...) +// CHECK-NEXT: Preds (2): B2 B5 +// CHECK-NEXT: Succs (2): B3 B1 + +// CHECK: [B5] +// CHECK-NEXT: 1: 0 +// CHECK-NEXT: 2: int i = 0; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_forloop1() { + for (int i = 0; i < 12; i++) { + i++; + } + return; +} + +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B1] +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B3] +// CHECK-NEXT: T: for (; ; ) +// CHECK-NEXT: Preds (2): B2 B4 +// CHECK-NEXT: Succs (2): B2 NULL + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_forloop2() { + for (;;) + ; +} + +// CHECK: [B5 (ENTRY)] +// CHECK-NEXT: Succs (1): B4 + +// CHECK: [B1] +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B4 + +// CHECK: [B3] +// CHECK-NEXT: 1: int i; +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 + +// CHECK: [B4] +// CHECK-NEXT: 1: true +// CHECK-NEXT: T: while [B4.1] +// CHECK-NEXT: Preds (2): B2 B5 +// CHECK-NEXT: Succs (2): B3 NULL + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_while1() { + while (true) { + int i; + } +} + +// CHECK: [B5 (ENTRY)] +// CHECK-NEXT: Succs (1): B4 + +// CHECK: [B1] +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: 2: 2 +// CHECK-NEXT: 3: int k = 2; +// CHECK-NEXT: 4: return; +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B3] +// CHECK-NEXT: 1: l +// CHECK-NEXT: 2: [B3.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 42 +// CHECK-NEXT: 4: [B3.2] < [B3.3] +// CHECK-NEXT: T: while [B3.4] +// CHECK-NEXT: Preds (2): B2 B4 +// CHECK-NEXT: Succs (2): B2 B1 + +// CHECK: [B4] +// CHECK-NEXT: 1: int l; +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_while2() { + int l; + while (l < 42) + ; + int k = 2; + return; +} + +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B1] +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B3] +// CHECK-NEXT: 1: false +// CHECK-NEXT: T: while [B3.1] +// CHECK-NEXT: Preds (2): B2 B4 +// CHECK-NEXT: Succs (2): NULL B1 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_while3() { + while (false) { + ; + } +} + +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B2 + +// CHECK: [B1] +// CHECK-NEXT: 1: DoStmt (LoopExit) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: 1: false +// CHECK-NEXT: T: do ... while [B2.1] +// CHECK-NEXT: Preds (2): B3 B4 +// CHECK-NEXT: Succs (2): NULL B1 + +// CHECK: [B3] +// CHECK-NEXT: Succs (1): B2 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_dowhile1() { + do { + } while (false); +} + +// CHECK: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 + +// CHECK: [B1] +// CHECK-NEXT: 1: DoStmt (LoopExit) +// CHECK-NEXT: 2: j +// CHECK-NEXT: 3: [B1.2]-- +// CHECK-NEXT: 4: return; +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: 1: j +// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 20 +// CHECK-NEXT: 4: [B2.2] < [B2.3] +// CHECK-NEXT: T: do ... while [B2.4] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (2): B4 B1 + +// CHECK: [B3] +// CHECK-NEXT: 1: j +// CHECK-NEXT: 2: 2 +// CHECK-NEXT: 3: [B3.1] += [B3.2] +// CHECK-NEXT: Preds (2): B4 B5 +// CHECK-NEXT: Succs (1): B2 + +// CHECK: [B4] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B5] +// CHECK-NEXT: 1: 2 +// CHECK-NEXT: 2: int j = 2; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_dowhile2() { + int j = 2; + do { + j += 2; + } while (j < 20); + j--; + return; +} + +// CHECK: [B10 (ENTRY)] +// CHECK-NEXT: Succs (1): B9 + +// CHECK: [B1] +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B8 + +// CHECK: [B3] +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B2 + +// CHECK: [B4] +// CHECK-NEXT: 1: j +// CHECK-NEXT: 2: [B4.1]++ +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B6 + +// CHECK: [B5] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B5.1]++ +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 + +// CHECK: [B6] +// CHECK-NEXT: 1: j +// CHECK-NEXT: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 6 +// CHECK-NEXT: 4: [B6.2] < [B6.3] +// CHECK-NEXT: T: for (...; [B6.4]; ...) +// CHECK-NEXT: Preds (2): B4 B7 +// CHECK-NEXT: Succs (2): B5 B3 + +// CHECK: [B7] +// CHECK-NEXT: 1: 1 +// CHECK-NEXT: 2: int j = 1; +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (1): B6 + +// CHECK: [B8] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B8.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 2 +// CHECK-NEXT: 4: [B8.2] < [B8.3] +// CHECK-NEXT: T: while [B8.4] +// CHECK-NEXT: Preds (2): B2 B9 +// CHECK-NEXT: Succs (2): B7 B1 + +// CHECK: [B9] +// CHECK-NEXT: 1: 40 +// CHECK-NEXT: 2: -[B9.1] +// CHECK-NEXT: 3: int i = -40; +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (1): B8 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void nested_loops1() { + int i = -40; + while (i < 2) { + for (int j = 1; j < 6; j++) + i++; + } +} + +// CHECK: [B9 (ENTRY)] +// CHECK-NEXT: Succs (1): B8 + +// CHECK: [B1] +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: 1: j +// CHECK-NEXT: 2: [B2.1]++ +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B7 + +// CHECK: [B3] +// CHECK-NEXT: 1: DoStmt (LoopExit) +// CHECK-NEXT: 2: i +// CHECK-NEXT: 3: [B3.2]-- +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 + +// CHECK: [B4] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 2 +// CHECK-NEXT: 4: [B4.2] < [B4.3] +// CHECK-NEXT: T: do ... while [B4.4] +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (2): B6 B3 + +// CHECK: [B5] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B5.1]++ +// CHECK-NEXT: Preds (2): B6 B7 +// CHECK-NEXT: Succs (1): B4 + +// CHECK: [B6] +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B5 + +// CHECK: [B7] +// CHECK-NEXT: 1: j +// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 6 +// CHECK-NEXT: 4: [B7.2] < [B7.3] +// CHECK-NEXT: T: for (...; [B7.4]; ...) +// CHECK-NEXT: Preds (2): B2 B8 +// CHECK-NEXT: Succs (2): B5 B1 + +// CHECK: [B8] +// CHECK-NEXT: 1: 40 +// CHECK-NEXT: 2: -[B8.1] +// CHECK-NEXT: 3: int i = -40; +// CHECK-NEXT: 4: 1 +// CHECK-NEXT: 5: int j = 1; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B7 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void nested_loops2() { + int i = -40; + for (int j = 1; j < 6; j++) { + do { + i++; + } while (i < 2); + i--; + } +} + +// CHECK: [B12 (ENTRY)] +// CHECK-NEXT: Succs (1): B11 + +// CHECK: [B1] +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: Preds (2): B3 B5 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B5 + +// CHECK: [B3] +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 + +// CHECK: [B4] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B4.1]++ +// CHECK-NEXT: 3: i +// CHECK-NEXT: 4: [B4.3] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 5: 2 +// CHECK-NEXT: 6: [B4.4] % [B4.5] +// CHECK-NEXT: 7: [B4.6] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B4.7] +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (2): B3 B2 + +// CHECK: [B5] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 5 +// CHECK-NEXT: 4: [B5.2] < [B5.3] +// CHECK-NEXT: T: while [B5.4] +// CHECK-NEXT: Preds (2): B2 B6 +// CHECK-NEXT: Succs (2): B4 B1 + +// CHECK: [B6] +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: 2: 1 +// CHECK-NEXT: 3: int i = 1; +// CHECK-NEXT: Preds (2): B8 B10 +// CHECK-NEXT: Succs (1): B5 + +// CHECK: [B7] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B7.1]++ +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B10 + +// CHECK: [B8] +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B6 + +// CHECK: [B9] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B9.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 4 +// CHECK-NEXT: 4: [B9.2] == [B9.3] +// CHECK-NEXT: T: if [B9.4] +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (2): B8 B7 + +// CHECK: [B10] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B10.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 6 +// CHECK-NEXT: 4: [B10.2] < [B10.3] +// CHECK-NEXT: T: for (...; [B10.4]; ...) +// CHECK-NEXT: Preds (2): B7 B11 +// CHECK-NEXT: Succs (2): B9 B6 + +// CHECK: [B11] +// CHECK-NEXT: 1: 2 +// CHECK-NEXT: 2: int i = 2; +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B10 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_break() +{ + for(int i = 2; i < 6; i++) { + if(i == 4) + break; + } + + int i = 1; + while(i<5){ + i++; + if(i%2) + break; + } + + return; +}