m.ostapenko updated this revision to Diff 129985. m.ostapenko added a comment.
Hi Devin, now I'm very sorry for a such long delay. Now I have a bunch of time to proceed development of this patch (if scope contexts are still needed, of course). Regarding to approach you suggested (reuse LocalScope infrastructure and use first VarDecl for ScopeBegin): it seems very reasonable for me, but perhaps I need more advisory here. I've implemented a draft patch to make sure I've understood you correctly (this is not a final version, the test case is completely inadequate for now). While testing, I've discovered several questions that I would like to discuss: 1. Do we need to have one-to-one mapping between ScopeBegins and corresponding ScopeEnds or is it OK to assume that ScopeEnd can terminate several nested scopes? 2. In the following example: class A { public: A() {} ~A() {} operator int() const { return 1; } }; bool UV; void test_for_implicit_scope() { for (A a; A b = a; ) if (UV) continue; A c; } it seems that lifetime of **b** ends when we branch out from the loop body (if entered, of course), but it seems that in current implementation we don't generate an implicit destructor for **b** just before **continue**. Is this a bug, or perhaps I'm missing something? # The approach with first VarDecl works not so well with the following test case: void test_goto() { A f; l0: A d; { A a; if (UV) goto l0; if (UV) goto l1; A b; } l1: A c; } in this approach we'll don't emit a ScopeBegin for **d** because the first VarDecl is **f**. However IMHO we still need to add ScopeBegin for **d** in order to handle d's scope end occurring when we jumping to //l0// via **if (UV) goto l0;**. I think this can be solved by adding ScopeBegin for **d** when backpatching blocks late in **buildCFG** routine (when we know all targets of all gotos). Does this approach look reasonable for you? Thanks. https://reviews.llvm.org/D16403 Files: include/clang/Analysis/AnalysisDeclContext.h include/clang/Analysis/CFG.h include/clang/StaticAnalyzer/Core/AnalyzerOptions.h lib/Analysis/AnalysisDeclContext.cpp lib/Analysis/CFG.cpp lib/StaticAnalyzer/Core/AnalysisManager.cpp lib/StaticAnalyzer/Core/AnalyzerOptions.cpp test/Analysis/analyzer-config.c test/Analysis/analyzer-config.cpp test/Analysis/scopes-cfg-output.cpp
Index: test/Analysis/scopes-cfg-output.cpp =================================================================== --- /dev/null +++ test/Analysis/scopes-cfg-output.cpp @@ -0,0 +1,909 @@ +// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -analyze -analyzer-checker=debug.DumpCFG -analyzer-config cfg-scopes=true %s > %t 2>&1 +// RUN: FileCheck --input-file=%t %s + +class A { +public: +// CHECK: [B1 (ENTRY)] +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 + A() {} + +// CHECK: [B1 (ENTRY)] +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 + ~A() {} + +// CHECK: [B3 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: 1 +// CHECK-NEXT: 3: return [B1.2]; +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B2] +// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B1 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] + operator int() const { return 1; } +}; + +int getX(); +extern const bool UV; + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A [2]) +// CHECK-NEXT: 3: A a[2]; +// CHECK-NEXT: 4: (CXXConstructExpr, class A [0]) +// CHECK-NEXT: 5: A b[0]; +// CHECK-NEXT: 6: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 7: [B1.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_array() { + A a[2]; + A b[0]; +} + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 5: (CXXConstructExpr, class A) +// CHECK-NEXT: 6: A c; +// CHECK-NEXT: 7: (CXXConstructExpr, class A) +// CHECK-NEXT: 8: A d; +// CHECK-NEXT: 9: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 10: [B1.8].~A() (Implicit destructor) +// CHECK-NEXT: 11: [B1.6].~A() (Implicit destructor) +// CHECK-NEXT: 12: (CXXConstructExpr, class A) +// CHECK: 13: A b; +// CHECK: 14: CFGScopeEnd(CompoundStmt) +// CHECK: 15: [B1.13].~A() (Implicit destructor) +// CHECK: 16: [B1.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_scope() { + A a; + { A c; + A d; + } + A b; +} + +// CHECK: [B5 (ENTRY)] +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B1.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B4.5].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B4.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B4.5].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B4.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A b; +// CHECK-NEXT: 6: UV +// CHECK-NEXT: 7: [B4.6] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B4.7] +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (2): B2 B1 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B3 +void test_return() { + A a; + A b; + if (UV) return; + A c; +} + +// CHECK: [B7 (ENTRY)] +// CHECK-NEXT: Succs (1): B6 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: 2: [B6.8].~A() (Implicit destructor) +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B6.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B2 B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: [B3.3].~A() (Implicit destructor) +// CHECK-NEXT: 2: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: 2: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B6] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 5: a +// CHECK-NEXT: 6: [B6.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B6.6] (CXXConstructExpr, class A) +// CHECK-NEXT: 8: A b = a; +// CHECK-NEXT: 9: b +// CHECK-NEXT: 10: [B6.9] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 11: [B6.10].operator int +// CHECK-NEXT: 12: [B6.10] +// CHECK-NEXT: 13: [B6.12] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 14: [B6.13] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B6.14] +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (2): B5 B3 +// 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: [B11 (ENTRY)] +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: 2: [B10.8].~A() (Implicit destructor) +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A e; +// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 6: [B1.4].~A() (Implicit destructor) +// CHECK-NEXT: 7: [B10.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B2 B6 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B2.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B10.8].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B10.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: 4: UV +// CHECK-NEXT: 5: [B5.4] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B5.5] +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (2): B3 B2 +// CHECK: [B6] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B6.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B10.8].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B10.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B9] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: 4: UV +// CHECK-NEXT: 5: [B9.4] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.5] +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (2): B7 B6 +// CHECK: [B10] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 5: a +// CHECK-NEXT: 6: [B10.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B10.6] (CXXConstructExpr, class A) +// CHECK-NEXT: 8: A b = a; +// CHECK-NEXT: 9: b +// CHECK-NEXT: 10: [B10.9] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 11: [B10.10].operator int +// CHECK-NEXT: 12: [B10.10] +// CHECK-NEXT: 13: [B10.12] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 14: [B10.13] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B10.14] +// CHECK-NEXT: Preds (1): B11 +// CHECK-NEXT: Succs (2): B9 B5 +// CHECK: [B0 (EXIT)] +void test_if_jumps() { + A a; + if (A b = a) { + A c; + if (UV) return; + A d; + } else { + A c; + if (UV) return; + A d; + } + A e; +} + +// CHECK: [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: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeBegin(WhileStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: 4: [B3.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B4.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)] +// CHECK-NEXT: Preds (1): B1 +void test_while_implicit_scope() { + A a; + while (A b = a) + A c; +} + +// CHECK: [B15 (ENTRY)] +// CHECK-NEXT: Succs (1): B14 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(CXXConstructExpr) +// CHECK-NEXT: 2: [B13.5].~A() (Implicit destructor) +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A e; +// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 6: [B1.4].~A() (Implicit destructor) +// CHECK-NEXT: 7: [B14.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B11 B13 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (2): B3 B8 +// CHECK-NEXT: Succs (1): B13 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B3.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B13.5].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B13.5].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B14.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B6] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B6.2] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B13.5].~A() (Implicit destructor) +// CHECK-NEXT: T: continue; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeEnd(ContinueStmt) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B9] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B9.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.2] +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (2): B7 B6 +// CHECK: [B10] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B11] +// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B12] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: 4: UV +// CHECK-NEXT: 5: [B12.4] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B12.5] +// CHECK-NEXT: Preds (1): B13 +// CHECK-NEXT: Succs (2): B10 B9 +// CHECK: [B13] +// CHECK-NEXT: 1: CFGScopeBegin(CXXConstructExpr) +// CHECK-NEXT: 2: a +// CHECK-NEXT: 3: [B13.2] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 4: [B13.3] (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A b = a; +// CHECK-NEXT: 6: b +// CHECK-NEXT: 7: [B13.6] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 8: [B13.7].operator int +// CHECK-NEXT: 9: [B13.7] +// CHECK-NEXT: 10: [B13.9] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 11: [B13.10] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: while [B13.11] +// CHECK-NEXT: Preds (2): B2 B14 +// CHECK-NEXT: Succs (2): B12 B1 +// CHECK: [B14] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: Preds (1): B15 +// CHECK-NEXT: Succs (1): B13 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B5 +void test_while_jumps() { + A a; + while (A b = a) { + A c; + if (UV) break; + if (UV) continue; + if (UV) return; + A d; + } + A e; +} + +// CHECK: [B15 (ENTRY)] +// CHECK-NEXT: Succs (1): B14 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B1.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B14.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B11 B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: do ... while [B2.2] +// CHECK-NEXT: Preds (2): B3 B8 +// CHECK-NEXT: Succs (2): B13 B1 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B3.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B14.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B6] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B6.2] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: T: continue; +// CHECK: Preds (1): B9 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeEnd(ContinueStmt) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B9] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B9.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.2] +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (2): B7 B6 +// CHECK: [B10] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: T: break; +// CHECK: Preds (1): B12 +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B11] +// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B12] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A b; +// CHECK-NEXT: 4: UV +// CHECK-NEXT: 5: [B12.4] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B12.5] +// CHECK-NEXT: Preds (2): B13 B14 +// CHECK-NEXT: Succs (2): B10 B9 +// CHECK: [B13] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B12 +// CHECK: [B14] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: Preds (1): B15 +// CHECK-NEXT: Succs (1): B12 +// CHECK: [B0 (EXIT) +void test_do_jumps() { + A a; + do { + A b; + if (UV) break; + if (UV) continue; + if (UV) return; + A c; + } while (UV); + A d; +} + +// CHECK: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: 2: [B4.4].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B5.4].~A() (Implicit destructor) +// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeBegin(ForStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A c; +// CHECK-NEXT: 4: [B3.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B4.4].~A() (Implicit destructor) +// CHECK-NEXT: 6: CFGScopeEnd(ForStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: a +// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B4.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A b = a; +// CHECK-NEXT: 5: b +// CHECK-NEXT: 6: [B4.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B4.6].operator int +// CHECK-NEXT: 8: [B4.6] +// CHECK-NEXT: 9: [B4.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK: 10: [B4.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: for (...; [B4.10]; ) +// CHECK-NEXT: Preds (2): B2 B5 +// CHECK-NEXT: Succs (2): B3 B1 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A a; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_for_implicit_scope() { + for (A a; A b = a; ) + A c; +} + +// CHECK: [B15 (ENTRY)] +// CHECK-NEXT: Succs (1): B14 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: 2: [B13.4].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B14.6].~A() (Implicit destructor) +// CHECK-NEXT: 4: (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A f; +// CHECK-NEXT: 6: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 7: [B1.5].~A() (Implicit destructor) +// CHECK-NEXT: 8: [B14.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B11 B13 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (2): B3 B8 +// CHECK-NEXT: Succs (1): B13 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A e; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B3.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B13.4].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: 3: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: 4: [B13.4].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B14.6].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B14.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B6] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B6.2] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: T: continue; +// CHECK: Preds (1): B9 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B8] +// CHECK-NEXT: 1: CFGScopeEnd(ContinueStmt) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B9] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B9.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.2] +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (2): B7 B6 +// CHECK: [B10] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor) +// CHECK-NEXT: T: break; +// CHECK: Preds (1): B12 +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B11] +// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B12] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A d; +// CHECK-NEXT: 4: UV +// CHECK-NEXT: 5: [B12.4] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B12.5] +// CHECK-NEXT: Preds (1): B13 +// CHECK-NEXT: Succs (2): B10 B9 +// CHECK: [B13] +// CHECK-NEXT: 1: b +// CHECK-NEXT: 2: [B13.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B13.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A c = b; +// CHECK-NEXT: 5: c +// CHECK-NEXT: 6: [B13.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B13.6].operator int +// CHECK-NEXT: 8: [B13.6] +// CHECK-NEXT: 9: [B13.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 10: [B13.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: for (...; [B13.10]; ) +// CHECK-NEXT: Preds (2): B2 B14 +// CHECK-NEXT: Succs (2): B12 B1 +// CHECK: [B14] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: 4: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 5: (CXXConstructExpr, class A) +// CHECK-NEXT: 6: A b; +// CHECK-NEXT: Preds (1): B15 +// CHECK-NEXT: Succs (1): B13 +// CHECK: [B0 (EXIT)] +void test_for_jumps() { + A a; + for (A b; A c = b; ) { + A d; + if (UV) break; + if (UV) continue; + if (UV) return; + A e; + } + A f; +} + +// CHECK: [B10 (ENTRY)] +// CHECK-NEXT: Succs (1): B9 +// CHECK: [B1] +// CHECK-NEXT: l1: +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B1.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B8.2].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B9.3].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B2 B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A b; +// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 4: [B2.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B8.5].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B3 B5 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B8.5].~A() (Implicit destructor) +// CHECK-NEXT: T: goto l1; +// CHECK: Preds (1): B5 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B5] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B5.2] +// CHECK-NEXT: Preds (2): B6 B8 +// CHECK-NEXT: Succs (2): B4 B2 +// CHECK: [B6] +// CHECK-NEXT: 1: CFGScopeEnd(IfStmt) +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeBegin(IfStmt) +// CHECK-NEXT: 2: [B8.5].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B8.2].~A() (Implicit destructor) +// CHECK-NEXT: T: goto l0; +// CHECK: Preds (1): B8 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B8] +// CHECK-NEXT: l0: +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A b; +// CHECK-NEXT: 3: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 4: (CXXConstructExpr, class A) +// CHECK-NEXT: 5: A a; +// CHECK-NEXT: 6: UV +// CHECK-NEXT: 7: [B8.6] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B8.7] +// CHECK-NEXT: Preds (2): B9 B7 +// CHECK-NEXT: Succs (2): B7 B5 +// CHECK: [B9] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A a; +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B0 (EXIT)] +void test_goto() { + A a; +l0: + A b; + { A a; + if (UV) goto l0; + if (UV) goto l1; + A b; + } +l1: + A c; +} + +// CHECK: [B8 (ENTRY)] +// CHECK-NEXT: Succs (1): B7 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt) +// CHECK-NEXT: 2: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 3: int unused2; +// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Preds (2): B5 B6 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: ++[B2.1] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B6 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 3: int unused1; +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B5] +// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B6] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 3 +// CHECK-NEXT: 4: [B6.2] < [B6.3] +// CHECK-NEXT: T: for (...; [B6.4]; ...) +// CHECK-NEXT: Preds (2): B2 B7 +// CHECK-NEXT: Succs (2): B4 B1 +// CHECK: [B7] +// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt) +// CHECK-NEXT: 2: CFGScopeBegin(DeclStmt) +// CHECK-NEXT: 3: 0 +// CHECK-NEXT: 4: int i = 0; +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (1): B6 +// CHECK: [B0 (EXIT)] +void test_for_compound_and_break() { + for (int i = 0; i < 3; ++i) { + { + int unused1; + break; + } + } + { + int unused2; + } +} + +// CHECK: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt) +// CHECK-NEXT: 2: [B5.3].~A() (Implicit destructor) +// 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.11]; +// 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: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: CFGScopeBegin(CXXForRangeStmt) +// CHECK-NEXT: 2: __begin +// CHECK-NEXT: 3: [B4.2] (ImplicitCastExpr, LValueToRValue, class A *) +// CHECK-NEXT: 4: *[B4.3] +// CHECK-NEXT: 5: auto &i = *__begin; +// CHECK-NEXT: 6: operator= +// CHECK-NEXT: 7: [B4.6] (ImplicitCastExpr, FunctionToPointerDecay, class A &(*)(const class A &) throw()) +// CHECK-NEXT: 8: i +// CHECK-NEXT: 9: b +// CHECK-NEXT: 10: [B4.9] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 11: [B4.8] = [B4.10] (OperatorCall) +// CHECK-NEXT: 12: 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)] +// CHECK-NEXT: Preds (1): B1 +void test_range_for(A &b) { + A a[10]; + for (auto &i : a) + i = b; +} Index: test/Analysis/analyzer-config.cpp =================================================================== --- test/Analysis/analyzer-config.cpp +++ test/Analysis/analyzer-config.cpp @@ -26,6 +26,7 @@ // CHECK-NEXT: cfg-implicit-dtors = true // CHECK-NEXT: cfg-lifetime = false // CHECK-NEXT: cfg-loopexit = false +// CHECK-NEXT: cfg-scopes = false // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -42,4 +43,4 @@ // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 24 +// CHECK-NEXT: num-entries = 25 Index: test/Analysis/analyzer-config.c =================================================================== --- test/Analysis/analyzer-config.c +++ test/Analysis/analyzer-config.c @@ -15,6 +15,7 @@ // CHECK-NEXT: cfg-implicit-dtors = true // CHECK-NEXT: cfg-lifetime = false // CHECK-NEXT: cfg-loopexit = false +// CHECK-NEXT: cfg-scopes = false // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -31,4 +32,4 @@ // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 19 +// CHECK-NEXT: num-entries = 20 Index: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp =================================================================== --- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -188,6 +188,12 @@ /* Default = */ false); } +bool AnalyzerOptions::includeScopesInCFG() { + return getBooleanOption(IncludeScopesInCFG, + "cfg-scopes", + /* Default = */ false); +} + bool AnalyzerOptions::mayInlineCXXStandardLibrary() { return getBooleanOption(InlineCXXStandardLibrary, "c++-stdlib-inlining", Index: lib/StaticAnalyzer/Core/AnalysisManager.cpp =================================================================== --- lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -26,6 +26,7 @@ // Adding LoopExit elements to the CFG is a requirement for loop // unrolling. Options.includeLoopExitInCFG() || Options.shouldUnrollLoops(), + Options.includeScopesInCFG(), Options.shouldSynthesizeBodies(), Options.shouldConditionalizeStaticInitializers(), /*addCXXNewAllocator=*/true, Index: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ lib/Analysis/CFG.cpp @@ -233,6 +233,13 @@ assert(VarIter != 0 && "Iterator has invalid value of VarIter member"); return &Scope->Vars[VarIter - 1]; } + + const VarDecl *getFirstVarInScope() const { + assert(Scope && "Dereferencing invalid iterator is not allowed"); + assert(VarIter != 0 && "Iterator has invalid value of VarIter member"); + return Scope->Vars[0]; + } + VarDecl *operator*() const { return *this->operator->(); } @@ -266,6 +273,7 @@ int distance(const_iterator L); const_iterator shared_parent(const_iterator L); + bool pointsToFirstDeclaredVar() { return VarIter == 1; } }; private: @@ -566,6 +574,12 @@ CFGBlock *VisitChildren(Stmt *S); CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc); + void maybeAddScopeBeginForVarDecl(CFGBlock *B, const VarDecl *VD, + const Stmt *S) { + if (ScopePos && (VD == ScopePos.getFirstVarInScope())) + appendScopeBegin(B, VD, S); + } + /// 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 @@ -654,20 +668,22 @@ CFGBlock *addInitializer(CXXCtorInitializer *I); void addLoopExit(const Stmt *LoopStmt); void addAutomaticObjDtors(LocalScope::const_iterator B, - LocalScope::const_iterator E, Stmt *S); + LocalScope::const_iterator E, + Stmt *TriggerAutoObjDtorsStmt); void addLifetimeEnds(LocalScope::const_iterator B, LocalScope::const_iterator E, Stmt *S); void addAutomaticObjHandling(LocalScope::const_iterator B, LocalScope::const_iterator E, Stmt *S); void addImplicitDtorsForDestructor(const CXXDestructorDecl *DD); + void addScopesEnd(LocalScope::const_iterator B, LocalScope::const_iterator E, + Stmt *S); // Local scopes creation. - LocalScope* createOrReuseLocalScope(LocalScope* Scope); + LocalScope *createOrReuseLocalScope(LocalScope *Scope); void addLocalScopeForStmt(Stmt *S); - LocalScope* addLocalScopeForDeclStmt(DeclStmt *DS, - LocalScope* Scope = nullptr); - LocalScope* addLocalScopeForVarDecl(VarDecl *VD, LocalScope* Scope = nullptr); + LocalScope *addLocalScopeForDeclStmt(DeclStmt *DS, LocalScope *Scope = nullptr); + LocalScope *addLocalScopeForVarDecl(VarDecl *VD, LocalScope *Scope = nullptr); void addLocalScopeAndDtors(Stmt *S); @@ -725,6 +741,11 @@ LocalScope::const_iterator B, LocalScope::const_iterator E); + const VarDecl * + prependAutomaticObjScopeEndWithTerminator(CFGBlock *Blk, + LocalScope::const_iterator B, + LocalScope::const_iterator E); + void addSuccessor(CFGBlock *B, CFGBlock *S, bool IsReachable = true) { B->addSuccessor(CFGBlock::AdjacentBlock(S, IsReachable), cfg->getBumpVectorContext()); @@ -737,6 +758,26 @@ cfg->getBumpVectorContext()); } + void appendScopeBegin(CFGBlock *B, const VarDecl *VD, const Stmt *S) { + if (BuildOpts.AddScopes) + B->appendScopeBegin(VD, S, cfg->getBumpVectorContext()); + } + + void prependScopeBegin(CFGBlock *B, const VarDecl *VD, const Stmt *S) { + if (BuildOpts.AddScopes) + B->prependScopeBegin(VD, S, cfg->getBumpVectorContext()); + } + + void appendScopeEnd(CFGBlock *B, const VarDecl *VD, const Stmt *S) { + if (BuildOpts.AddScopes) + B->appendScopeEnd(VD, S, cfg->getBumpVectorContext()); + } + + void prependScopeEnd(CFGBlock *B, const VarDecl *VD, const Stmt *S) { + if (BuildOpts.AddScopes) + B->prependScopeEnd(VD, S, cfg->getBumpVectorContext()); + } + /// \brief Find a relational comparison with an expression evaluating to a /// boolean and a constant other than 0 and 1. /// e.g. if ((x < y) == 10) @@ -949,6 +990,10 @@ return {}; } + void removeAllSuccessors(CFGBlock *B) { + B->removeAllSuccessors(); + } + /// Try and evaluate an expression to an integer constant. bool tryEvaluate(Expr *S, Expr::EvalResult &outResult) { if (!BuildOpts.PruneTriviallyFalseEdges) @@ -1176,6 +1221,9 @@ JT.scopePosition); prependAutomaticObjDtorsWithTerminator(B, I->scopePosition, JT.scopePosition); + const VarDecl *VD = prependAutomaticObjScopeEndWithTerminator( + B, I->scopePosition, JT.scopePosition); + appendScopeBegin(JT.block, VD, G); addSuccessor(B, JT.block); } @@ -1332,6 +1380,8 @@ addAutomaticObjDtors(B, E, S); if (BuildOpts.AddLifetime) addLifetimeEnds(B, E, S); + if (BuildOpts.AddScopes) + addScopesEnd(B, E, S); } /// Add to current block automatic objects that leave the scope. @@ -1379,6 +1429,39 @@ appendLifetimeEnds(Block, *I, S); } +void CFGBuilder::addScopesEnd(LocalScope::const_iterator B, + LocalScope::const_iterator E, Stmt *S) { + if (!BuildOpts.AddScopes) + return; + + if (B == E) + return; + + // To go from B to E, one first goes up the scopes from B to P + // then sideways in one scope from P to P' and then down + // the scopes from P' to E. + // The lifetime of all objects between B and P end. + LocalScope::const_iterator P = B.shared_parent(E); + int Dist = B.distance(P); + if (Dist <= 0) + return; + + // We need to perform the scope leaving in reverse order + SmallVector<VarDecl *, 10> DeclsWithEndedScope; + DeclsWithEndedScope.reserve(Dist); + + for (LocalScope::const_iterator I = B; I != P; ++I) + if (I.pointsToFirstDeclaredVar()) + DeclsWithEndedScope.push_back(*I); + + autoCreateBlock(); + + for (const auto *VD : DeclsWithEndedScope) + appendScopeEnd(Block, VD, S); + + return; +} + /// addAutomaticObjDtors - Add to current block automatic objects destructors /// for objects in range of local scope positions. Use S as trigger statement /// for destructors. @@ -1467,7 +1550,7 @@ /// createOrReuseLocalScope - If Scope is NULL create new LocalScope. Either /// way return valid LocalScope object. -LocalScope* CFGBuilder::createOrReuseLocalScope(LocalScope* Scope) { +LocalScope *CFGBuilder::createOrReuseLocalScope(LocalScope *Scope) { if (Scope) return Scope; llvm::BumpPtrAllocator &alloc = cfg->getAllocator(); @@ -1478,7 +1561,8 @@ /// addLocalScopeForStmt - Add LocalScope to local scopes tree for statement /// that should create implicit scope (e.g. if/else substatements). void CFGBuilder::addLocalScopeForStmt(Stmt *S) { - if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) + if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime && + !BuildOpts.AddScopes) return; LocalScope *Scope = nullptr; @@ -1503,7 +1587,8 @@ /// reuse Scope if not NULL. LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS, LocalScope* Scope) { - if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) + if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime && + !BuildOpts.AddScopes) return Scope; for (auto *DI : DS->decls()) @@ -1555,7 +1640,8 @@ LocalScope* Scope) { assert(!(BuildOpts.AddImplicitDtors && BuildOpts.AddLifetime) && "AddImplicitDtors and AddLifetime cannot be used at the same time"); - if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) + if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime && + !BuildOpts.AddScopes) return Scope; // Check if variable is local. @@ -1568,7 +1654,7 @@ } if (BuildOpts.AddImplicitDtors) { - if (!hasTrivialDestructor(VD)) { + if (!hasTrivialDestructor(VD) || BuildOpts.AddScopes) { // Add the variable to scope Scope = createOrReuseLocalScope(Scope); Scope->addVar(VD); @@ -1628,6 +1714,26 @@ InsertPos = Blk->insertLifetimeEnds(InsertPos, *I, Blk->getTerminator()); } +/// prependAutomaticObjScopeEndWithTerminator - Prepend scope end CFGElements for +/// variables with automatic storage duration to CFGBlock's elements vector. +/// Elements will be prepended to physical beginning of the vector which +/// happens to be logical end. Use blocks terminator as statement that specifies +/// where scope ends. +const VarDecl * +CFGBuilder::prependAutomaticObjScopeEndWithTerminator( + CFGBlock *Blk, LocalScope::const_iterator B, LocalScope::const_iterator E) { + if (!BuildOpts.AddScopes) + return nullptr; + BumpVectorContext &C = cfg->getBumpVectorContext(); + CFGBlock::iterator InsertPos = + Blk->beginScopeEndInsert(Blk->end(), 1, C); + LocalScope::const_iterator PlaceToInsert = B; + for (LocalScope::const_iterator I = B; I != E; ++I) + PlaceToInsert = I; + Blk->insertScopeEnd(InsertPos, *PlaceToInsert, Blk->getTerminator()); + return *PlaceToInsert; +} + /// Visit - Walk the subtree of a statement and add extra /// blocks for ternary operators, &&, and ||. We also process "," and /// DeclStmts (which may contain nested control-flow). @@ -2353,6 +2459,8 @@ LastBlock = newBlock; } + maybeAddScopeBeginForVarDecl(Block, VD, DS); + // Remove variable from local scope. if (ScopePos && VD == *ScopePos) ++ScopePos; @@ -2813,6 +2921,7 @@ do { Expr *C = F->getCond(); + SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); // Specially handle logical operators, which have a slightly // more optimal CFG representation. @@ -2846,6 +2955,7 @@ appendStmt(Block, F->getConditionVariableDeclStmt()); EntryConditionBlock = addStmt(Init); assert(Block == EntryConditionBlock); + maybeAddScopeBeginForVarDecl(EntryConditionBlock, VD, C); } } @@ -2872,6 +2982,8 @@ // If the loop contains initialization, create a new block for those // statements. This block can also contain statements that precede the loop. if (Stmt *I = F->getInit()) { + SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); + ScopePos = LoopBeginScopePos; Block = createBlock(); return addStmt(I); } @@ -3158,6 +3270,7 @@ appendStmt(Block, W->getConditionVariableDeclStmt()); EntryConditionBlock = addStmt(Init); assert(Block == EntryConditionBlock); + maybeAddScopeBeginForVarDecl(EntryConditionBlock, VD, C); } } @@ -4215,6 +4328,8 @@ case CFGElement::NewAllocator: case CFGElement::LoopExit: case CFGElement::LifetimeEnds: + case CFGElement::ScopeBegin: + case CFGElement::ScopeEnd: llvm_unreachable("getDestructorDecl should only be used with " "ImplicitDtors"); case CFGElement::AutomaticObjectDtor: { @@ -4630,6 +4745,16 @@ } else if (Optional<CFGLoopExit> LE = E.getAs<CFGLoopExit>()) { const Stmt *LoopStmt = LE->getLoopStmt(); OS << LoopStmt->getStmtClassName() << " (LoopExit)\n"; + } else if (Optional<CFGScopeBegin> SB = E.getAs<CFGScopeBegin>()) { + OS << "CFGScopeBegin("; + if (const VarDecl *VD = SB->getVarDecl()) + OS << VD->getQualifiedNameAsString(); + OS << ")\n"; + } else if (Optional<CFGScopeEnd> SE = E.getAs<CFGScopeEnd>()) { + OS << "CFGScopeEnd("; + if (const VarDecl *VD = SE->getVarDecl()) + OS << VD->getQualifiedNameAsString(); + OS << ")\n"; } else if (Optional<CFGNewAllocator> NE = E.getAs<CFGNewAllocator>()) { OS << "CFGNewAllocator("; if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr()) Index: lib/Analysis/AnalysisDeclContext.cpp =================================================================== --- lib/Analysis/AnalysisDeclContext.cpp +++ lib/Analysis/AnalysisDeclContext.cpp @@ -66,16 +66,17 @@ AnalysisDeclContextManager::AnalysisDeclContextManager( ASTContext &ASTCtx, bool useUnoptimizedCFG, bool addImplicitDtors, bool addInitializers, bool addTemporaryDtors, bool addLifetime, - bool addLoopExit, bool synthesizeBodies, bool addStaticInitBranch, - bool addCXXNewAllocator, CodeInjector *injector) + bool addLoopExit, bool addScopes, bool synthesizeBodies, + bool addStaticInitBranch, bool addCXXNewAllocator, CodeInjector *injector) : Injector(injector), FunctionBodyFarm(ASTCtx, injector), SynthesizeBodies(synthesizeBodies) { cfgBuildOptions.PruneTriviallyFalseEdges = !useUnoptimizedCFG; cfgBuildOptions.AddImplicitDtors = addImplicitDtors; cfgBuildOptions.AddInitializers = addInitializers; cfgBuildOptions.AddTemporaryDtors = addTemporaryDtors; cfgBuildOptions.AddLifetime = addLifetime; cfgBuildOptions.AddLoopExit = addLoopExit; + cfgBuildOptions.AddScopes = addScopes; cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch; cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator; } Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h =================================================================== --- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -220,6 +220,9 @@ /// \sa mayInlineCXXStandardLibrary Optional<bool> InlineCXXStandardLibrary; + /// \sa includeScopesInCFG + Optional<bool> IncludeScopesInCFG; + /// \sa mayInlineTemplateFunctions Optional<bool> InlineTemplateFunctions; @@ -428,6 +431,12 @@ /// the values "true" and "false". bool includeLoopExitInCFG(); + /// Returns whether or not scope information should be included in the CFG. + /// + /// This is controlled by the 'cfg-scope-info' config option, which accepts + /// the values "true" and "false". + bool includeScopesInCFG(); + /// Returns whether or not C++ standard library functions may be considered /// for inlining. /// Index: include/clang/Analysis/CFG.h =================================================================== --- include/clang/Analysis/CFG.h +++ include/clang/Analysis/CFG.h @@ -57,6 +57,8 @@ // main kind Statement, Initializer, + ScopeBegin, + ScopeEnd, NewAllocator, LifetimeEnds, LoopExit, @@ -223,6 +225,55 @@ } }; +/// Represents beginning of a scope implicitly generated +/// by the compiler on encountering a CompoundStmt +class CFGScopeBegin : public CFGElement { +public: + CFGScopeBegin() {} + CFGScopeBegin(const VarDecl *VD, const Stmt *S) + : CFGElement(ScopeBegin, VD, S) {} + + // Get statement that triggered a new scope. + const Stmt *getTriggerStmt() const { + return static_cast<Stmt*>(Data2.getPointer()); + } + + // Get VD that triggered a new scope. + const VarDecl *getVarDecl() const { + return static_cast<VarDecl *>(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 VarDecl *VD, const Stmt *S) : CFGElement(ScopeEnd, VD, S) {} + + const VarDecl *getVarDecl() const { + return static_cast<VarDecl *>(Data1.getPointer()); + } + + const Stmt *getTriggerStmt() const { + return static_cast<Stmt *>(Data2.getPointer()); + } + +private: + friend class CFGElement; + static bool isKind(const CFGElement &E) { + Kind kind = E.getKind(); + return kind == ScopeEnd; + } +}; + /// CFGImplicitDtor - Represents C++ object destructor implicitly generated /// by compiler on various occasions. class CFGImplicitDtor : public CFGElement { @@ -757,6 +808,24 @@ Elements.push_back(CFGNewAllocator(NE), C); } + void appendScopeBegin(const VarDecl *VD, const Stmt *S, + BumpVectorContext &C) { + Elements.push_back(CFGScopeBegin(VD, S), C); + } + + void prependScopeBegin(const VarDecl *VD, const Stmt *S, + BumpVectorContext &C) { + Elements.insert(Elements.rbegin(), 1, CFGScopeBegin(VD, S), C); + } + + void appendScopeEnd(const VarDecl *VD, const Stmt *S, BumpVectorContext &C) { + Elements.push_back(CFGScopeEnd(VD, S), C); + } + + void prependScopeEnd(const VarDecl *VD, const Stmt *S, BumpVectorContext &C) { + Elements.insert(Elements.rbegin(), 1, CFGScopeEnd(VD, S), C); + } + void appendBaseDtor(const CXXBaseSpecifier *BS, BumpVectorContext &C) { Elements.push_back(CFGBaseDtor(BS), C); } @@ -785,6 +854,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. @@ -810,6 +881,20 @@ *I = CFGLifetimeEnds(VD, S); return ++I; } + + // Scope leaving must be performed 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. + iterator beginScopeEndInsert(iterator I, size_t Cnt, + BumpVectorContext &C) { + return iterator( + Elements.insert(I.base(), Cnt, CFGScopeEnd(nullptr, nullptr), C)); + } + iterator insertScopeEnd(iterator I, VarDecl *VD, Stmt *S) { + *I = CFGScopeEnd(VD, S); + return ++I; + } + }; /// \brief CFGCallback defines methods that should be called when a logical @@ -852,6 +937,7 @@ bool AddLifetime = false; bool AddLoopExit = false; bool AddTemporaryDtors = false; + bool AddScopes = false; bool AddStaticInitBranches = false; bool AddCXXNewAllocator = false; bool AddCXXDefaultInitExprInCtors = false; Index: include/clang/Analysis/AnalysisDeclContext.h =================================================================== --- include/clang/Analysis/AnalysisDeclContext.h +++ include/clang/Analysis/AnalysisDeclContext.h @@ -431,7 +431,9 @@ bool addImplicitDtors = false, bool addInitializers = false, bool addTemporaryDtors = false, - bool addLifetime = false, bool addLoopExit = false, + bool addLifetime = false, + bool addLoopExit = false, + bool addScopes = false, bool synthesizeBodies = false, bool addStaticInitBranches = false, bool addCXXNewAllocator = true,
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits