m.ostapenko updated this revision to Diff 103719.
m.ostapenko set the repository for this revision to rL LLVM.
m.ostapenko added a project: clang.
m.ostapenko added a comment.
So, updating the diff. This is still a very experimental version and any
feedback would be greatly appreciated.
Current patch should support basic {If, While, For, Compound, Switch}Stmts as
well as their interactions with {Break, Continue, Return}Stmts.
GotoStmt and CXXForRangeStmt are not supported at this moment.
Repository:
rL LLVM
https://reviews.llvm.org/D16403
Files:
include/clang/Analysis/AnalysisContext.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,1468 @@
+// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -analyze -analyzer-checker=debug.DumpCFG -analyzer-config cfg-scopes=true %s > %t 2>&1
+// RUN: FileCheck --input-file=%t %s
+
+class A {
+public:
+// CHECK: [B1 (ENTRY)]
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+ A() {}
+
+// CHECK: [B1 (ENTRY)]
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+ ~A() {}
+
+// CHECK: [B3 (ENTRY)]
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: 1
+// CHECK-NEXT: 3: return [B1.2];
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B2]
+// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt)
+// CHECK-NEXT: Preds (1): B1
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+ operator int() const { return 1; }
+};
+
+int getX();
+extern const bool UV;
+
+// CHECK: [B2 (ENTRY)]
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: 4: a
+// CHECK-NEXT: 5: [B1.4] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 6: const A &b = a;
+// CHECK-NEXT: 7: A() (CXXConstructExpr, class A)
+// CHECK-NEXT: 8: [B1.7] (BindTemporary)
+// CHECK-NEXT: 9: [B1.8] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 10: [B1.9]
+// CHECK-NEXT: 11: const A &c = A();
+// CHECK-NEXT: 12: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 13: [B1.11].~A() (Implicit destructor)
+// CHECK-NEXT: 14: [B1.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+void test_const_ref() {
+ A a;
+ const A& b = a;
+ const A& c = A();
+}
+
+// CHECK: [B2 (ENTRY)]
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A [2])
+// CHECK-NEXT: 3: A a[2];
+// CHECK-NEXT: 4: (CXXConstructExpr, class A [0])
+// CHECK-NEXT: 5: A b[0];
+// CHECK-NEXT: 6: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 7: [B1.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+void test_array() {
+ A a[2];
+ A b[0];
+}
+
+// CHECK: [B2 (ENTRY)]
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: 4: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 5: (CXXConstructExpr, class A)
+// CHECK-NEXT: 6: A c;
+// CHECK-NEXT: 7: (CXXConstructExpr, class A)
+// CHECK-NEXT: 8: A d;
+// CHECK-NEXT: 9: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 10: [B1.8].~A() (Implicit destructor)
+// CHECK-NEXT: 11: [B1.6].~A() (Implicit destructor)
+// CHECK-NEXT: 12: (CXXConstructExpr, class A)
+// CHECK: 13: A b;
+// CHECK: 14: CFGScopeEnd(CompoundStmt)
+// CHECK: 15: [B1.13].~A() (Implicit destructor)
+// CHECK: 16: [B1.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+void test_scope() {
+ A a;
+ { A c;
+ A d;
+ }
+ A b;
+}
+
+// CHECK: [B5 (ENTRY)]
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B1]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A c;
+// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 4: [B1.2].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B4.5].~A() (Implicit destructor)
+// CHECK-NEXT: 6: [B4.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: return;
+// CHECK-NEXT: 3: [B4.5].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B4.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B3
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt)
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: 4: (CXXConstructExpr, class A)
+// CHECK-NEXT: 5: A b;
+// CHECK-NEXT: 6: UV
+// CHECK-NEXT: 7: [B4.6] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B4.7]
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (2): B2 B1
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (2): B1 B3
+void test_return() {
+ A a;
+ A b;
+ if (UV) return;
+ A c;
+}
+
+// CHECK: [B7 (ENTRY)]
+// CHECK-NEXT: Succs (1): B6
+// CHECK: [B1]
+// CHECK-NEXT: 1: [B6.7].~A() (Implicit destructor)
+// CHECK-NEXT: 2: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 3: [B6.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (2): B2 B4
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: [B3.3].~A() (Implicit destructor)
+// CHECK-NEXT: 2: CFGScopeEnd(IfStmt)
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A c;
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: [B5.3].~A() (Implicit destructor)
+// CHECK-NEXT: 2: CFGScopeEnd(IfStmt)
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A c;
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B6]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: 4: a
+// CHECK-NEXT: 5: [B6.4] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 6: [B6.5] (CXXConstructExpr, class A)
+// CHECK-NEXT: 7: A b = a;
+// CHECK-NEXT: 8: b
+// CHECK-NEXT: 9: [B6.8] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 10: [B6.9].operator int
+// CHECK-NEXT: 11: [B6.9]
+// CHECK-NEXT: 12: [B6.11] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT: 13: [B6.12] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: if [B6.13]
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (2): B5 B3
+// CHECK: [B0 (EXIT)]
+void test_if_implicit_scope() {
+ A a;
+ if (A b = a)
+ A c;
+ else A c;
+}
+
+// CHECK: [B11 (ENTRY)]
+// CHECK-NEXT: Succs (1): B10
+// CHECK: [B1]
+// CHECK-NEXT: 1: [B10.7].~A() (Implicit destructor)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A e;
+// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 5: [B1.3].~A() (Implicit destructor)
+// CHECK-NEXT: 6: [B10.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (2): B2 B6
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A d;
+// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 4: [B2.2].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B5.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: return;
+// CHECK-NEXT: 3: [B5.3].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B10.7].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B10.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt)
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A c;
+// CHECK-NEXT: 4: UV
+// CHECK-NEXT: 5: [B5.4] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B5.5]
+// CHECK-NEXT: Preds (1): B10
+// CHECK-NEXT: Succs (2): B3 B2
+// CHECK: [B6]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A d;
+// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 4: [B6.2].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B9.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B7]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: return;
+// CHECK-NEXT: 3: [B9.3].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B10.7].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B10.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (1): B8
+// CHECK: [B8]
+// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt)
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B9]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A c;
+// CHECK-NEXT: 4: UV
+// CHECK-NEXT: 5: [B9.4] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B9.5]
+// CHECK-NEXT: Preds (1): B10
+// CHECK-NEXT: Succs (2): B7 B6
+// CHECK: [B10]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: 4: a
+// CHECK-NEXT: 5: [B10.4] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 6: [B10.5] (CXXConstructExpr, class A)
+// CHECK-NEXT: 7: A b = a;
+// CHECK-NEXT: 8: b
+// CHECK-NEXT: 9: [B10.8] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 10: [B10.9].operator int
+// CHECK-NEXT: 11: [B10.9]
+// CHECK-NEXT: 12: [B10.11] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT: 13: [B10.12] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: if [B10.13]
+// CHECK-NEXT: Preds (1): B11
+// CHECK-NEXT: Succs (2): B9 B5
+// CHECK: [B0 (EXIT)]
+void test_if_jumps() {
+ A a;
+ if (A b = a) {
+ A c;
+ if (UV) return;
+ A d;
+ } else {
+ A c;
+ if (UV) return;
+ A d;
+ }
+ A e;
+}
+
+// CHECK: [B13 (ENTRY)]
+// CHECK-NEXT: Succs (1): B12
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (2): B2 B10
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: CFGScopeEnd(IfStmt)
+// CHECK-NEXT: Preds (2): B3 B7
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeEnd(IfStmt)
+// CHECK-NEXT: Preds (2): B4 B6
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeEnd(IfStmt)
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B3
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: 'a'
+// CHECK-NEXT: 3: char c = 'a';
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B6]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: a
+// CHECK-NEXT: 3: [B6.2] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 4: [B6.3] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: 5: ![B6.4]
+// CHECK-NEXT: T: if [B6.5]
+// CHECK-NEXT: Preds (2): B8 B9
+// CHECK-NEXT: Succs (2): B5 B3
+// CHECK: [B7]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: 2
+// CHECK-NEXT: 3: a
+// CHECK-NEXT: 4: [B7.3] = [B7.2]
+// CHECK-NEXT: 5: 3
+// CHECK-NEXT: 6: b
+// CHECK-NEXT: 7: [B7.6] = [B7.5]
+// CHECK-NEXT: 8: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B8
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B8]
+// CHECK-NEXT: 1: b
+// CHECK-NEXT: 2: [B8.1] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 3: [B8.2] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: 4: ![B8.3]
+// CHECK-NEXT: T: if [B9.4] && [B8.4]
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (2): B7 B6
+// CHECK: [B9]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: a
+// CHECK-NEXT: 3: [B9.2] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 4: [B9.3] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: [B9.4] && ...
+// CHECK-NEXT: Preds (2): B11 B12
+// CHECK-NEXT: Succs (2): B8 B6
+// CHECK: [B10]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: 0
+// CHECK-NEXT: 3: a
+// CHECK-NEXT: 4: [B10.3] = [B10.2]
+// CHECK-NEXT: 5: 1
+// CHECK-NEXT: 6: b
+// CHECK-NEXT: 7: [B10.6] = [B10.5]
+// CHECK-NEXT: 8: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B11
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B11]
+// CHECK-NEXT: 1: b
+// CHECK-NEXT: 2: [B11.1] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 3: [B11.2] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: if [B12.4] && [B11.3]
+// CHECK-NEXT: Preds (1): B12
+// CHECK-NEXT: Succs (2): B10 B9
+// CHECK: [B12]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: a
+// CHECK-NEXT: 3: [B12.2] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 4: [B12.3] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: [B12.4] && ...
+// CHECK-NEXT: Preds (1): B13
+// CHECK-NEXT: Succs (2): B11 B9
+// CHECK: [B0 (EXIT)]
+
+int a, b;
+void text_if_with_and() {
+ if (a && b) {
+ a = 0;
+ b = 1;
+ } else if (a && !b) {
+ a = 2;
+ b = 3;
+ } else if (!a)
+ char c = 'a';
+}
+
+// CHECK: [B6 (ENTRY)]
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B1]
+// CHECK-NEXT: 1: [B4.4].~A() (Implicit destructor)
+// CHECK-NEXT: 2: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 3: [B5.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeBegin(WhileStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A c;
+// CHECK-NEXT: 4: [B3.3].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B4.4].~A() (Implicit destructor)
+// CHECK-NEXT: 6: CFGScopeEnd(WhileStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: a
+// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 3: [B4.2] (CXXConstructExpr, class A)
+// CHECK-NEXT: 4: A b = a;
+// CHECK-NEXT: 5: b
+// CHECK-NEXT: 6: [B4.5] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 7: [B4.6].operator int
+// CHECK-NEXT: 8: [B4.6]
+// CHECK-NEXT: 9: [B4.8] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK: 10: [B4.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: while [B4.10]
+// CHECK-NEXT: Preds (2): B2 B5
+// CHECK-NEXT: Succs (2): B3 B1
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+void test_while_implicit_scope() {
+ A a;
+ while (A b = a)
+ A c;
+}
+
+// CHECK: [B15 (ENTRY)]
+// CHECK-NEXT: Succs (1): B14
+// CHECK: [B1]
+// CHECK-NEXT: 1: [B13.4].~A() (Implicit destructor)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A e;
+// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 5: [B1.3].~A() (Implicit destructor)
+// CHECK-NEXT: 6: [B14.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (2): B11 B13
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: Preds (2): B3 B8
+// CHECK-NEXT: Succs (1): B13
+// CHECK: [B3]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A d;
+// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 4: [B3.2].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B12.3].~A() (Implicit destructor)
+// CHECK-NEXT: 6: [B13.4].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: return;
+// CHECK-NEXT: 3: [B12.3].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B13.4].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B14.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B6]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B6.2]
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (2): B4 B3
+// CHECK: [B7]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor)
+// CHECK-NEXT: 3: [B13.4].~A() (Implicit destructor)
+// CHECK-NEXT: T: continue;
+// CHECK: Preds (1): B9
+// CHECK-NEXT: Succs (1): B8
+// CHECK: [B8]
+// CHECK-NEXT: 1: CFGScopeEnd(ContinueStmt)
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B9]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B9.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B9.2]
+// CHECK-NEXT: Preds (1): B12
+// CHECK-NEXT: Succs (2): B7 B6
+// CHECK: [B10]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor)
+// CHECK-NEXT: T: break;
+// CHECK: Preds (1): B12
+// CHECK-NEXT: Succs (1): B11
+// CHECK: [B11]
+// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt)
+// CHECK-NEXT: Preds (1): B10
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B12]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A c;
+// CHECK-NEXT: 4: UV
+// CHECK-NEXT: 5: [B12.4] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B12.5]
+// CHECK-NEXT: Preds (1): B13
+// CHECK-NEXT: Succs (2): B10 B9
+// CHECK: [B13]
+// CHECK-NEXT: 1: a
+// CHECK-NEXT: 2: [B13.1] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 3: [B13.2] (CXXConstructExpr, class A)
+// CHECK-NEXT: 4: A b = a;
+// CHECK-NEXT: 5: b
+// CHECK-NEXT: 6: [B13.5] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 7: [B13.6].operator int
+// CHECK-NEXT: 8: [B13.6]
+// CHECK-NEXT: 9: [B13.8] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT: 10: [B13.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: while [B13.10]
+// CHECK-NEXT: Preds (2): B2 B14
+// CHECK-NEXT: Succs (2): B12 B1
+// CHECK: [B14]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: Preds (1): B15
+// CHECK-NEXT: Succs (1): B13
+// CHECK: [B0 (EXIT)]
+void test_while_jumps() {
+ A a;
+ while (A b = a) {
+ A c;
+ if (UV) break;
+ if (UV) continue;
+ if (UV) return;
+ A d;
+ }
+ A e;
+}
+
+// CHECK: [B15 (ENTRY)]
+// CHECK-NEXT: Succs (1): B14
+// CHECK: [B1]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A d;
+// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 4: [B1.2].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B14.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (2): B11 B2
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: do ... while [B2.2]
+// CHECK-NEXT: Preds (2): B3 B8
+// CHECK-NEXT: Succs (2): B13 B1
+// CHECK: [B3]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A c;
+// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 4: [B3.2].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B12.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: return;
+// CHECK-NEXT: 3: [B12.3].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B14.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B6]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B6.2]
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (2): B4 B3
+// CHECK: [B7]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor)
+// CHECK-NEXT: T: continue;
+// CHECK: Preds (1): B9
+// CHECK-NEXT: Succs (1): B8
+// CHECK: [B8]
+// CHECK-NEXT: 1: CFGScopeEnd(ContinueStmt)
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B9]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B9.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B9.2]
+// CHECK-NEXT: Preds (1): B12
+// CHECK-NEXT: Succs (2): B7 B6
+// CHECK: [B10]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor)
+// CHECK-NEXT: T: break;
+// CHECK: Preds (1): B12
+// CHECK-NEXT: Succs (1): B11
+// CHECK: [B11]
+// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt)
+// CHECK-NEXT: Preds (1): B10
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B12]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A b;
+// CHECK-NEXT: 4: UV
+// CHECK-NEXT: 5: [B12.4] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B12.5]
+// CHECK-NEXT: Preds (2): B13 B14
+// CHECK-NEXT: Succs (2): B10 B9
+// CHECK: [B13]
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B12
+// CHECK: [B14]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: Preds (1): B15
+// CHECK-NEXT: Succs (1): B12
+// CHECK: [B0 (EXIT)
+void test_do_jumps() {
+ A a;
+ do {
+ A b;
+ if (UV) break;
+ if (UV) continue;
+ if (UV) return;
+ A c;
+ } while (UV);
+ A d;
+}
+
+// CHECK: [B6 (ENTRY)]
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: 2: [B4.4].~A() (Implicit destructor)
+// CHECK-NEXT: 3: [B5.4].~A() (Implicit destructor)
+// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeBegin(ForStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A c;
+// CHECK-NEXT: 4: [B3.3].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B4.4].~A() (Implicit destructor)
+// CHECK-NEXT: 6: CFGScopeEnd(ForStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: a
+// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 3: [B4.2] (CXXConstructExpr, class A)
+// CHECK-NEXT: 4: A b = a;
+// CHECK-NEXT: 5: b
+// CHECK-NEXT: 6: [B4.5] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 7: [B4.6].operator int
+// CHECK-NEXT: 8: [B4.6]
+// CHECK-NEXT: 9: [B4.8] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK: 10: [B4.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: for (...; [B4.10]; )
+// CHECK-NEXT: Preds (2): B2 B5
+// CHECK-NEXT: Succs (2): B3 B1
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: CFGScopeBegin(DeclStmt)
+// CHECK-NEXT: 3: (CXXConstructExpr, class A)
+// CHECK-NEXT: 4: A a;
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+void test_for_implicit_scope() {
+ for (A a; A b = a; )
+ A c;
+}
+
+// CHECK: [B15 (ENTRY)]
+// CHECK-NEXT: Succs (1): B14
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: 2: [B13.4].~A() (Implicit destructor)
+// CHECK-NEXT: 3: [B14.6].~A() (Implicit destructor)
+// CHECK-NEXT: 4: (CXXConstructExpr, class A)
+// CHECK-NEXT: 5: A f;
+// CHECK-NEXT: 6: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 7: [B1.5].~A() (Implicit destructor)
+// CHECK-NEXT: 8: [B14.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (2): B11 B13
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: Preds (2): B3 B8
+// CHECK-NEXT: Succs (1): B13
+// CHECK: [B3]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A e;
+// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 4: [B3.2].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B12.3].~A() (Implicit destructor)
+// CHECK-NEXT: 6: [B13.4].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: return;
+// CHECK-NEXT: 3: [B12.3].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B13.4].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B14.6].~A() (Implicit destructor)
+// CHECK-NEXT: 6: [B14.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeEnd(ReturnStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B6]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B6.2]
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (2): B4 B3
+// CHECK: [B7]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor)
+// CHECK-NEXT: T: continue;
+// CHECK: Preds (1): B9
+// CHECK-NEXT: Succs (1): B8
+// CHECK: [B8]
+// CHECK-NEXT: 1: CFGScopeEnd(ContinueStmt)
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B9]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B9.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B9.2]
+// CHECK-NEXT: Preds (1): B12
+// CHECK-NEXT: Succs (2): B7 B6
+// CHECK: [B10]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: [B12.3].~A() (Implicit destructor)
+// CHECK-NEXT: T: break;
+// CHECK: Preds (1): B12
+// CHECK-NEXT: Succs (1): B11
+// CHECK: [B11]
+// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt)
+// CHECK-NEXT: Preds (1): B10
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B12]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A d;
+// CHECK-NEXT: 4: UV
+// CHECK-NEXT: 5: [B12.4] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B12.5]
+// CHECK-NEXT: Preds (1): B13
+// CHECK-NEXT: Succs (2): B10 B9
+// CHECK: [B13]
+// CHECK-NEXT: 1: b
+// CHECK-NEXT: 2: [B13.1] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 3: [B13.2] (CXXConstructExpr, class A)
+// CHECK-NEXT: 4: A c = b;
+// CHECK-NEXT: 5: c
+// CHECK-NEXT: 6: [B13.5] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 7: [B13.6].operator int
+// CHECK-NEXT: 8: [B13.6]
+// CHECK-NEXT: 9: [B13.8] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT: 10: [B13.9] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: for (...; [B13.10]; )
+// CHECK-NEXT: Preds (2): B2 B14
+// CHECK-NEXT: Succs (2): B12 B1
+// CHECK: [B14]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: 4: CFGScopeBegin(DeclStmt)
+// CHECK-NEXT: 5: (CXXConstructExpr, class A)
+// CHECK-NEXT: 6: A b;
+// CHECK-NEXT: Preds (1): B15
+// CHECK-NEXT: Succs (1): B13
+// CHECK: [B0 (EXIT)]
+void test_for_jumps() {
+ A a;
+ for (A b; A c = b; ) {
+ A d;
+ if (UV) break;
+ if (UV) continue;
+ if (UV) return;
+ A e;
+ }
+ A f;
+}
+
+// CHECK: [B10 (ENTRY)]
+// CHECK-NEXT: Succs (1): B9
+// CHECK: [B1]
+// CHECK-NEXT: l1:
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A c;
+// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 4: [B1.2].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B8.2].~A() (Implicit destructor)
+// CHECK-NEXT: 6: [B9.3].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (2): B2 B4
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A b;
+// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 4: [B2.2].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B8.5].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (2): B3 B5
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeEnd(IfStmt)
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: [B8.5].~A() (Implicit destructor)
+// CHECK-NEXT: T: goto l1;
+// CHECK: Preds (1): B5
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B5]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B5.2]
+// CHECK-NEXT: Preds (2): B6 B8
+// CHECK-NEXT: Succs (2): B4 B2
+// CHECK: [B6]
+// CHECK-NEXT: 1: CFGScopeEnd(IfStmt)
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B7]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: [B8.5].~A() (Implicit destructor)
+// CHECK-NEXT: 3: [B8.2].~A() (Implicit destructor)
+// CHECK-NEXT: T: goto l0;
+// CHECK: Preds (1): B8
+// CHECK-NEXT: Succs (1): B8
+// CHECK: [B8]
+// CHECK-NEXT: l0:
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A b;
+// CHECK-NEXT: 3: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 4: (CXXConstructExpr, class A)
+// CHECK-NEXT: 5: A a;
+// CHECK-NEXT: 6: UV
+// CHECK-NEXT: 7: [B8.6] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B8.7]
+// CHECK-NEXT: Preds (2): B9 B7
+// CHECK-NEXT: Succs (2): B7 B5
+// CHECK: [B9]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: Preds (1): B10
+// CHECK-NEXT: Succs (1): B8
+// CHECK: [B0 (EXIT)]
+void test_goto() {
+ A a;
+l0:
+ A b;
+ { A a;
+ if (UV) goto l0;
+ if (UV) goto l1;
+ A b;
+ }
+l1:
+ A c;
+}
+
+// CHECK: [B11 (ENTRY)]
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: 2: 1
+// CHECK-NEXT: 3: int k = 1;
+// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: '1'
+// CHECK-NEXT: 3: char c = '1';
+// CHECK-NEXT: 4: CFGScopeBegin(DeclStmt)
+// CHECK-NEXT: 5: getX
+// CHECK-NEXT: 6: [B2.5] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(void))
+// CHECK-NEXT: 7: [B2.6]()
+// CHECK-NEXT: 8: int i = getX();
+// CHECK-NEXT: 9: i
+// CHECK-NEXT: 10: [B2.9] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: T: switch [B2.10]
+// CHECK-NEXT: Preds (1): B11
+// CHECK-NEXT: Succs (1): B3
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: T: switch [B2.10]
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (5): B6 B7 B9 B10 B5
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (3): B5 B8 B9
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B5]
+// CHECK-NEXT: default:
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: 0
+// CHECK-NEXT: 3: int a = 0;
+// CHECK-NEXT: 4: i
+// CHECK-NEXT: 5: ++[B5.4]
+// CHECK-NEXT: 6: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (2): B6 B3
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B6]
+// CHECK-NEXT: case 3:
+// CHECK-NEXT: 1: '2'
+// CHECK-NEXT: 2: c
+// CHECK-NEXT: 3: [B6.2] = [B6.1]
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B7]
+// CHECK-NEXT: case 2:
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: '2'
+// CHECK-NEXT: 3: c
+// CHECK-NEXT: 4: [B7.3] = [B7.2]
+// CHECK-NEXT: T: break;
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B8
+// CHECK: [B8]
+// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt)
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B9]
+// CHECK-NEXT: case 1:
+// CHECK-NEXT: 1: '3'
+// CHECK-NEXT: 2: c
+// CHECK-NEXT: 3: [B9.2] = [B9.1]
+// CHECK-NEXT: T: break;
+// CHECK-NEXT: Preds (2): B3 B10
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B10]
+// CHECK-NEXT: case 0:
+// CHECK-NEXT: 1: '2'
+// CHECK-NEXT: 2: c
+// CHECK-NEXT: 3: [B10.2] = [B10.1]
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B9
+// CHECK: [B0 (EXIT)]
+void test_switch_with_compound_with_default() {
+ char c = '1';
+ switch (int i = getX()) {
+ case 0:
+ c = '2';
+ case 1:
+ c = '3';
+ break;
+ case 2: {
+ c = '2';
+ break;
+ }
+ case 3:
+ c = '2';
+ default: {
+ int a = 0;
+ ++i;
+ }
+ }
+ int k = 1;
+}
+
+// CHECK: [B9 (ENTRY)]
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: 2: 3
+// CHECK-NEXT: 3: int k = 3;
+// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: '1'
+// CHECK-NEXT: 3: char c = '1';
+// CHECK-NEXT: 4: CFGScopeBegin(DeclStmt)
+// CHECK-NEXT: 5: getX
+// CHECK-NEXT: 6: [B2.5] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(void))
+// CHECK-NEXT: 7: [B2.6]()
+// CHECK-NEXT: 8: int i = getX();
+// CHECK-NEXT: 9: i
+// CHECK-NEXT: 10: [B2.9] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: T: switch [B2.10]
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (1): B3
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: T: switch [B2.10]
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (4): B5 B6 B8 B4
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (3): B5 B7 B3
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B5]
+// CHECK-NEXT: case 2:
+// CHECK-NEXT: 1: '3'
+// CHECK-NEXT: 2: c
+// CHECK-NEXT: 3: [B5.2] = [B5.1]
+// CHECK-NEXT: T: break;
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B6]
+// CHECK-NEXT: case 1:
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: '1'
+// CHECK-NEXT: 3: c
+// CHECK-NEXT: 4: [B6.3] = [B6.2]
+// CHECK-NEXT: T: break;
+// CHECK-NEXT: Preds (2): B3 B8
+// CHECK-NEXT: Succs (1): B7
+// CHECK: [B7]
+// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt)
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B8]
+// CHECK-NEXT: case 0:
+// CHECK-NEXT: 1: '2'
+// CHECK-NEXT: 2: c
+// CHECK-NEXT: 3: [B8.2] = [B8.1]
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B6
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+int test_switch_with_compound_without_default() {
+ char c = '1';
+ switch (int i = getX()) {
+ case 0:
+ c = '2';
+ case 1: {
+ c = '1';
+ break;
+ }
+ case 2:
+ c = '3';
+ break;
+ }
+ int k = 3;
+}
+
+// CHECK: [B5 (ENTRY)]
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: 2: 1
+// CHECK-NEXT: 3: int k = 1;
+// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: '1'
+// CHECK-NEXT: 3: char s = '1';
+// CHECK-NEXT: 4: CFGScopeBegin(DeclStmt)
+// CHECK-NEXT: 5: getX
+// CHECK-NEXT: 6: [B2.5] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(void))
+// CHECK-NEXT: 7: [B2.6]()
+// CHECK-NEXT: 8: int i = getX();
+// CHECK-NEXT: 9: i
+// CHECK-NEXT: 10: [B2.9] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: T: switch [B2.10]
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (2): B4 B3
+// CHECK: [B3]
+// CHECK-NEXT: default:
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: 0
+// CHECK-NEXT: 3: int a = 0;
+// CHECK-NEXT: 4: i
+// CHECK-NEXT: 5: ++[B3.4]
+// CHECK-NEXT: 6: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (2): B4 B2
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B4]
+// CHECK-NEXT: case 0:
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B3
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+void test_without_compound() {
+ char s = '1';
+ switch (int i = getX())
+ case 0:
+ default: {
+ int a = 0;
+ ++i;
+ }
+ int k = 1;
+}
+
+// CHECK: [B8 (ENTRY)]
+// CHECK-NEXT: Succs (1): B7
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: 2: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 3: int unused2;
+// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (2): B5 B6
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: i
+// CHECK-NEXT: 2: ++[B2.1]
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B6
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 3: int unused1;
+// CHECK-NEXT: T: break;
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B6]
+// CHECK-NEXT: 1: i
+// CHECK-NEXT: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 3: 3
+// CHECK-NEXT: 4: [B6.2] < [B6.3]
+// CHECK-NEXT: T: for (...; [B6.4]; ...)
+// CHECK-NEXT: Preds (2): B2 B7
+// CHECK-NEXT: Succs (2): B4 B1
+// CHECK: [B7]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: CFGScopeBegin(DeclStmt)
+// CHECK-NEXT: 3: 0
+// CHECK-NEXT: 4: int i = 0;
+// CHECK-NEXT: Preds (1): B8
+// CHECK-NEXT: Succs (1): B6
+// CHECK: [B0 (EXIT)]
+void test_for_compound_and_break() {
+ for (int i = 0; i < 3; ++i) {
+ {
+ int unused1;
+ break;
+ }
+ }
+ {
+ int unused2;
+ }
+}
+
+// CHECK: [B16 (ENTRY)]
+// CHECK-NEXT: Succs (1): B15
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(BinaryOperator)
+// CHECK-NEXT: 2: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (2): B5 B14
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: i
+// CHECK-NEXT: 2: ++[B2.1]
+// CHECK-NEXT: Preds (2): B3 B11
+// CHECK-NEXT: Succs (1): B14
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: 5
+// CHECK-NEXT: 3: int z = 5;
+// CHECK-NEXT: T: break;
+// CHECK-NEXT: Preds (1): B8
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B6]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: x
+// CHECK-NEXT: 3: [B6.2] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: T: switch [B6.3]
+// CHECK-NEXT: Preds (1): B14
+// CHECK-NEXT: Succs (1): B7
+// CHECK: [B7]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: T: switch [B6.3]
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (4): B10 B12 B13 B9
+// CHECK: [B8]
+// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (2): B9 B12
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B9]
+// CHECK-NEXT: default:
+// CHECK-NEXT: 1: 3
+// CHECK-NEXT: 2: y
+// CHECK-NEXT: 3: [B9.2] = [B9.1]
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B8
+// CHECK: [B10]
+// CHECK-NEXT: case 2:
+// CHECK-NEXT: 1: 4
+// CHECK-NEXT: 2: y
+// CHECK-NEXT: 3: [B10.2] = [B10.1]
+// CHECK-NEXT: T: continue;
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B11
+// CHECK: [B11]
+// CHECK-NEXT: 1: CFGScopeEnd(ContinueStmt)
+// CHECK-NEXT: Preds (1): B10
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B12]
+// CHECK-NEXT: case 1:
+// CHECK-NEXT: 1: 2
+// CHECK-NEXT: 2: y
+// CHECK-NEXT: 3: [B12.2] = [B12.1]
+// CHECK-NEXT: T: break;
+// CHECK-NEXT: Preds (2): B7 B13
+// CHECK-NEXT: Succs (1): B8
+// CHECK: [B13]
+// CHECK-NEXT: case 0:
+// CHECK-NEXT: 1: 1
+// CHECK-NEXT: 2: y
+// CHECK-NEXT: 3: [B13.2] = [B13.1]
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B12
+// CHECK: [B14]
+// CHECK-NEXT: 1: i
+// CHECK-NEXT: 2: [B14.1] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 3: 1000
+// CHECK-NEXT: 4: [B14.2] < [B14.3]
+// CHECK-NEXT: T: for (...; [B14.4]; ...)
+// CHECK-NEXT: Preds (2): B2 B15
+// CHECK-NEXT: Succs (2): B6 B1
+// CHECK: [B15]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: int i;
+// CHECK-NEXT: 3: int x;
+// CHECK-NEXT: 4: int y;
+// CHECK-NEXT: 5: CFGScopeBegin(BinaryOperator)
+// CHECK-NEXT: 6: 0
+// CHECK-NEXT: 7: i
+// CHECK-NEXT: 8: [B15.7] = [B15.6]
+// CHECK-NEXT: Preds (1): B16
+// CHECK-NEXT: Succs (1): B14
+// CHECK: [B0 (EXIT)]
+void test_for_switch_in_for() {
+ int i, x, y;
+ for (i = 0; i < 1000; ++i) {
+ switch (x) {
+ case 0:
+ y = 1;
+ case 1:
+ y = 2;
+ break; // break from switch
+ case 2:
+ y = 4;
+ continue; // continue in loop
+ default:
+ y = 3;
+ }
+ {
+ int z = 5;
+ break; // break from loop
+ }
+ }
+}
+
+int vfork();
+void execl(char *, char *, int);
+int _exit(int);
+// CHECK: [B20 (ENTRY)]
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: CFGScopeBegin(WhileStmt)
+// CHECK-NEXT: 2: CFGScopeEnd(WhileStmt)
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B3
+// CHECK: [B3]
+// CHECK-NEXT: 1: 1
+// CHECK-NEXT: 2: [B3.1] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: while [B3.2]
+// CHECK-NEXT: Preds (2): B2 B6
+// CHECK-NEXT: Succs (2): B2 NULL
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: x
+// CHECK-NEXT: 3: [B4.2] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: T: switch [B4.3]
+// CHECK-NEXT: Preds (1): B20
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: T: switch [B4.3]
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (4): B12 B15 B19 B6
+// CHECK: [B6]
+// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (4): B8 B14 B19 B5
+// CHECK-NEXT: Succs (1): B3
+// CHECK: [B7]
+// CHECK-NEXT: T: break;
+// CHECK-NEXT: Preds (2): B9 B12
+// CHECK-NEXT: Succs (1): B8
+// CHECK: [B8]
+// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt)
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B6
+// CHECK: [B9]
+// CHECK-NEXT: 1: CFGScopeEnd(IfStmt)
+// CHECK-NEXT: Succs (1): B7
+// CHECK: [B10]
+// CHECK-NEXT: 1: CFGScopeBegin(WhileStmt)
+// CHECK-NEXT: 2: CFGScopeEnd(WhileStmt)
+// CHECK-NEXT: Preds (1): B11
+// CHECK-NEXT: Succs (1): B11
+// CHECK: [B11]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: 1
+// CHECK-NEXT: 3: [B11.2] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: while [B11.3]
+// CHECK-NEXT: Preds (2): B10 B12
+// CHECK-NEXT: Succs (2): B10 NULL
+// CHECK: [B12]
+// CHECK-NEXT: case 2:
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: int pid;
+// CHECK-NEXT: 3: vfork
+// CHECK-NEXT: 4: [B12.3] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(void))
+// CHECK-NEXT: 5: [B12.4]()
+// CHECK-NEXT: 6: pid
+// CHECK-NEXT: 7: [B12.6] = [B12.5]
+// CHECK-NEXT: 8: ([B12.7]) (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 9: 0
+// CHECK-NEXT: 10: [B12.8] == [B12.9]
+// CHECK-NEXT: T: if [B12.10]
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (2): B11 B7
+// CHECK: [B13]
+// CHECK-NEXT: 1: 3
+// CHECK-NEXT: 2: x
+// CHECK-NEXT: 3: [B13.2] = [B13.1]
+// CHECK-NEXT: T: break;
+// CHECK-NEXT: Preds (1): B17
+// CHECK-NEXT: Succs (1): B14
+// CHECK: [B14]
+// CHECK-NEXT: 1: CFGScopeEnd(BreakStmt)
+// CHECK-NEXT: Preds (1): B13
+// CHECK-NEXT: Succs (1): B6
+// CHECK: [B15]
+// CHECK-NEXT: case 1:
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: char args[2];
+// CHECK-NEXT: 3: vfork
+// CHECK-NEXT: 4: [B15.3] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(void))
+// CHECK-NEXT: 5: [B15.4]()
+// CHECK-NEXT: T: switch [B15.5]
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B16
+// CHECK: [B16]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: T: switch [B15.5]
+// CHECK-NEXT: Preds (1): B15
+// CHECK-NEXT: Succs (2): B18 B17
+// CHECK: [B17]
+// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (2): B18 B16
+// CHECK-NEXT: Succs (1): B13
+// CHECK: [B18]
+// CHECK-NEXT: case 0:
+// CHECK-NEXT: 1: 0
+// CHECK-NEXT: 2: [B18.1] (ImplicitCastExpr, IntegralCast, char)
+// CHECK-NEXT: 3: args
+// CHECK-NEXT: 4: [B18.3] (ImplicitCastExpr, ArrayToPointerDecay, char *)
+// CHECK-NEXT: 5: 0
+// CHECK: 7: [B18.6] = [B18.2]
+// CHECK-NEXT: 8: _exit
+// CHECK-NEXT: 9: [B18.8] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(int))
+// CHECK-NEXT: 10: 1
+// CHECK-NEXT: 11: [B18.9]([B18.10])
+// CHECK-NEXT: Preds (1): B16
+// CHECK-NEXT: Succs (1): B17
+// CHECK: [B19]
+// CHECK-NEXT: case 0:
+// CHECK-NEXT: 1: vfork
+// CHECK-NEXT: 2: [B19.1] (ImplicitCastExpr, FunctionToPointerDecay, int (*)(void))
+// CHECK-NEXT: 3: [B19.2]()
+// CHECK-NEXT: 4: 0
+// CHECK-NEXT: 5: x
+// CHECK-NEXT: 6: [B19.5] = [B19.4]
+// CHECK-NEXT: T: break;
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B6
+// CHECK: [B0 (EXIT)]
+void test_nested_switches(int x) {
+ switch (x) {
+ case 0:
+ vfork();
+ x = 0;
+ break;
+
+ case 1: {
+ char args[2];
+ switch (vfork()) {
+ case 0:
+ args[0] = 0;
+ _exit(1);
+ }
+ x = 3;
+ break;
+ }
+
+ case 2: {
+ int pid;
+ if ((pid = vfork()) == 0)
+ while (1)
+ ;
+ break;
+ }
+ }
+ while (1)
+ ;
+}
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/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: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
===================================================================
--- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -172,6 +172,12 @@
/* Default = */ false);
}
+bool AnalyzerOptions::includeScopesInCFG() {
+ return getBooleanOption(IncludeScopesInCFG,
+ "cfg-scopes",
+ /* Default = */ false);
+}
+
bool AnalyzerOptions::mayInlineCXXStandardLibrary() {
return getBooleanOption(InlineCXXStandardLibrary,
"c++-stdlib-inlining",
Index: 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/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -245,11 +245,13 @@
/// Iterator to variable in previous scope that was declared just before
/// begin of this scope.
const_iterator Prev;
+ /// Statement that triggered creation of this LocalScope object
+ const Stmt *TriggerScopeStmt;
public:
/// Constructs empty scope linked to previous scope in specified place.
- LocalScope(BumpVectorContext ctx, const_iterator P)
- : ctx(std::move(ctx)), Vars(this->ctx, 4), Prev(P) {}
+ LocalScope(BumpVectorContext ctx, const_iterator P, Stmt *S)
+ : ctx(std::move(ctx)), Vars(this->ctx, 4), Prev(P), TriggerScopeStmt(S) {}
/// Begin of scope in direction of CFG building (backwards).
const_iterator begin() const { return const_iterator(*this, Vars.size()); }
@@ -377,6 +379,11 @@
CFGBlock *SwitchTerminatedBlock;
CFGBlock *DefaultCaseBlock;
CFGBlock *TryTerminatedBlock;
+ CFGBlock *SwitchScopeBeginBlock;
+ CFGBlock *SwitchScopeEndBlock;
+ bool SwitchNeedsLocalScope;
+ bool FromSwitchStmt;
+ bool InSwitchNest;
// Current position in local scope.
LocalScope::const_iterator ScopePos;
@@ -415,9 +422,11 @@
: Context(astContext), cfg(new CFG()), // crew a new CFG
Block(nullptr), Succ(nullptr),
SwitchTerminatedBlock(nullptr), DefaultCaseBlock(nullptr),
- TryTerminatedBlock(nullptr), badCFG(false), BuildOpts(buildOpts),
- switchExclusivelyCovered(false), switchCond(nullptr),
- cachedEntry(nullptr), lastLookup(nullptr) {}
+ TryTerminatedBlock(nullptr), SwitchScopeBeginBlock(nullptr),
+ SwitchScopeEndBlock(nullptr), SwitchNeedsLocalScope(false),
+ FromSwitchStmt(false), InSwitchNest(false),
+ badCFG(false), BuildOpts(buildOpts), switchExclusivelyCovered(false),
+ switchCond(0), cachedEntry(nullptr), lastLookup(nullptr) {}
// buildCFG - Used by external clients to construct the CFG.
std::unique_ptr<CFG> buildCFG(const Decl *D, Stmt *Statement);
@@ -488,6 +497,12 @@
CFGBlock *VisitChildren(Stmt *S);
CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc);
+ void CreateScopeEndBlockForSwitchStmt(Stmt *S, bool *ShouldCreateScopeBeginPtr);
+ void CreateScopeBeginBlockForSwitchStmt(SwitchStmt *Terminator);
+ void CreateScopeEndBlockForIfStmt(IfStmt *I);
+ void CreateScopeEndBlockForReturnStmt(ReturnStmt *R);
+ void CreateScopeEndBlockForBreakOrContinueStmt(Stmt *B, bool IsBreak);
+
/// When creating the CFG for temporary destructors, we want to mirror the
/// branch structure of the corresponding constructor calls.
/// Thus, while visiting a statement for temporary destructors, we keep a
@@ -578,18 +593,22 @@
}
CFGBlock *addInitializer(CXXCtorInitializer *I);
void addAutomaticObjDtors(LocalScope::const_iterator B,
- LocalScope::const_iterator E, Stmt *S);
+ LocalScope::const_iterator E,
+ Stmt *TriggerAutoObjDtorsStmt,
+ Stmt *TriggerScopeStmt = nullptr);
void addImplicitDtorsForDestructor(const CXXDestructorDecl *DD);
// Local scopes creation.
- LocalScope* createOrReuseLocalScope(LocalScope* Scope);
+ LocalScope *createOrReuseLocalScope(LocalScope *Scope,
+ Stmt *TriggerScopeStmt);
- void addLocalScopeForStmt(Stmt *S);
- LocalScope* addLocalScopeForDeclStmt(DeclStmt *DS,
- LocalScope* Scope = nullptr);
- LocalScope* addLocalScopeForVarDecl(VarDecl *VD, LocalScope* Scope = nullptr);
+ void addLocalScopeForStmt(Stmt *S, Stmt *TriggerScopeStmt);
+ LocalScope *addLocalScopeForDeclStmt(DeclStmt *DS, Stmt *TriggerScopeStmt,
+ LocalScope *Scope = nullptr);
+ LocalScope *addLocalScopeForVarDecl(VarDecl *VD, Stmt *TriggerScopeStmt,
+ LocalScope *Scope = nullptr);
- void addLocalScopeAndDtors(Stmt *S);
+ void addLocalScopeAndDtors(Stmt *S, Stmt *TriggerScopeStmt);
// Interface to CFGBlock - adding CFGElements.
void appendStmt(CFGBlock *B, const Stmt *S) {
@@ -606,6 +625,18 @@
void appendNewAllocator(CFGBlock *B, CXXNewExpr *NE) {
B->appendNewAllocator(NE, cfg->getBumpVectorContext());
}
+ void appendScopeBegin(CFGBlock *B, const Stmt *S) {
+ if (BuildOpts.AddScopes)
+ B->appendScopeBegin(S, cfg->getBumpVectorContext());
+ }
+ void appendScopeEnd(CFGBlock *B, const Stmt *S) {
+ if (BuildOpts.AddScopes)
+ B->appendScopeEnd(S, cfg->getBumpVectorContext());
+ }
+ void prependScopeEnd(CFGBlock *B, const Stmt *S) {
+ if (BuildOpts.AddScopes)
+ B->prependScopeEnd(S, cfg->getBumpVectorContext());
+ }
void appendBaseDtor(CFGBlock *B, const CXXBaseSpecifier *BS) {
B->appendBaseDtor(BS, cfg->getBumpVectorContext());
}
@@ -850,6 +881,10 @@
return TryResult();
}
+ void removeAllSuccessors(CFGBlock *B) {
+ B->removeAllSuccessors();
+ }
+
/// Try and evaluate an expression to an integer constant.
bool tryEvaluate(Expr *S, Expr::EvalResult &outResult) {
if (!BuildOpts.PruneTriviallyFalseEdges)
@@ -1211,10 +1246,13 @@
}
/// addAutomaticObjDtors - Add to current block automatic objects destructors
-/// for objects in range of local scope positions. Use S as trigger statement
-/// for destructors.
+/// for objects in range of local scope positions.
+/// Use `TriggerAutoObjDtorsStmt` as trigger statement for destructors, and
+/// `TriggerScopeStmt` as trigger statement for ending scope.
void CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B,
- LocalScope::const_iterator E, Stmt *S) {
+ LocalScope::const_iterator E,
+ Stmt *TriggerAutoObjDtorsStmt,
+ Stmt *TriggerScopeStmt) {
if (!BuildOpts.AddImplicitDtors)
return;
@@ -1247,7 +1285,7 @@
else
autoCreateBlock();
- appendAutomaticObjDtor(Block, *I, S);
+ appendAutomaticObjDtor(Block, *I, TriggerAutoObjDtorsStmt);
}
}
@@ -1298,17 +1336,18 @@
/// createOrReuseLocalScope - If Scope is NULL create new LocalScope. Either
/// way return valid LocalScope object.
-LocalScope* CFGBuilder::createOrReuseLocalScope(LocalScope* Scope) {
+LocalScope *CFGBuilder::createOrReuseLocalScope(LocalScope *Scope,
+ Stmt *TriggerScopeStmt) {
if (Scope)
return Scope;
llvm::BumpPtrAllocator &alloc = cfg->getAllocator();
return new (alloc.Allocate<LocalScope>())
- LocalScope(BumpVectorContext(alloc), ScopePos);
+ LocalScope(BumpVectorContext(alloc), ScopePos, TriggerScopeStmt);
}
/// addLocalScopeForStmt - Add LocalScope to local scopes tree for statement
-/// that should create implicit scope (e.g. if/else substatements).
-void CFGBuilder::addLocalScopeForStmt(Stmt *S) {
+/// that should create implicit scope (e.g. if/else substatements).
+void CFGBuilder::addLocalScopeForStmt(Stmt *S, Stmt *TriggerScopeStmt) {
if (!BuildOpts.AddImplicitDtors)
return;
@@ -1319,34 +1358,36 @@
for (auto *BI : CS->body()) {
Stmt *SI = BI->stripLabelLikeStatements();
if (DeclStmt *DS = dyn_cast<DeclStmt>(SI))
- Scope = addLocalScopeForDeclStmt(DS, Scope);
+ Scope = addLocalScopeForDeclStmt(DS, TriggerScopeStmt, Scope);
}
return;
}
// For any other statement scope will be implicit and as such will be
// interesting only for DeclStmt.
if (DeclStmt *DS = dyn_cast<DeclStmt>(S->stripLabelLikeStatements()))
- addLocalScopeForDeclStmt(DS);
+ addLocalScopeForDeclStmt(DS, TriggerScopeStmt);
}
/// addLocalScopeForDeclStmt - Add LocalScope for declaration statement. Will
/// reuse Scope if not NULL.
LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS,
+ Stmt *TriggerScopeStmt,
LocalScope* Scope) {
if (!BuildOpts.AddImplicitDtors)
return Scope;
for (auto *DI : DS->decls())
if (VarDecl *VD = dyn_cast<VarDecl>(DI))
- Scope = addLocalScopeForVarDecl(VD, Scope);
+ Scope = addLocalScopeForVarDecl(VD, TriggerScopeStmt, 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,
+ Stmt *TriggerScopeStmt,
LocalScope* Scope) {
if (!BuildOpts.AddImplicitDtors)
return Scope;
@@ -1392,22 +1433,41 @@
if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl())
if (CD->hasDefinition() && !CD->hasTrivialDestructor()) {
// Add the variable to scope
- Scope = createOrReuseLocalScope(Scope);
+ Scope = createOrReuseLocalScope(Scope, TriggerScopeStmt);
Scope->addVar(VD);
ScopePos = Scope->begin();
}
return Scope;
}
-/// addLocalScopeAndDtors - For given statement add local scope for it and
-/// add destructors that will cleanup the scope. Will reuse Scope if not NULL.
-void CFGBuilder::addLocalScopeAndDtors(Stmt *S) {
+/// addLocalScopeAndDtors - For given statements `TriggerAutoObjDtorsStmt`, and
+/// `TriggerScopeStmt`, add local scope for it and add destructors that will
+/// cleanup the scope. Will reuse Scope if not NULL.
+/// `TriggerAutoObjDtorsStmt` is the last statement in the present scope after
+/// which auto obj destructors (if any) will be added to cleanup scope.
+/// `TriggerScopeStmt` is the statement that led to the creation of the Scope
+/// object.
+///
+/// For example:
+/// if (condition) { // TriggerScopeStmt
+/// std::vector<int> IntVec;
+/// IntVec.push_back(0); // TriggerAutoObjDtorsStmt
+/// }
+///
+/// IfStmt is the `TriggerScopeStmt`; IntVec.push_back is
+/// `TriggerAutoObjDtorsStmt`.
+/// Eventually, `TriggerScopeStmt` is used inside CFGScopeBegin/End
+/// blocks. In the example above, we will have CFGScope blocks, like
+/// so: CFGScopeBegin(IfStmt) ... CFGScopeEnd(IfStmt).
+void CFGBuilder::addLocalScopeAndDtors(Stmt *TriggerAutoObjDtorsStmt,
+ Stmt *TriggerScopeStmt) {
if (!BuildOpts.AddImplicitDtors)
return;
LocalScope::const_iterator scopeBeginPos = ScopePos;
- addLocalScopeForStmt(S);
- addAutomaticObjDtors(ScopePos, scopeBeginPos, S);
+ addLocalScopeForStmt(TriggerAutoObjDtorsStmt, TriggerScopeStmt);
+ addAutomaticObjDtors(ScopePos, scopeBeginPos, TriggerAutoObjDtorsStmt,
+ TriggerScopeStmt);
}
/// prependAutomaticObjDtorsWithTerminator - Prepend destructor CFGElements for
@@ -1802,6 +1862,18 @@
return Block;
}
+void CFGBuilder::CreateScopeEndBlockForBreakOrContinueStmt(Stmt *B,
+ bool IsBreak) {
+ CFGBlock *OldBlock = Block;
+ removeAllSuccessors(OldBlock);
+ Block = createBlock(false);
+ addSuccessor(OldBlock, Block);
+ addSuccessor(Block,
+ IsBreak ? BreakJumpTarget.block : ContinueJumpTarget.block);
+ appendScopeEnd(Block, B);
+ Block = OldBlock;
+}
+
CFGBlock *CFGBuilder::VisitBreakStmt(BreakStmt *B) {
// "break" is a control-flow statement. Thus we stop processing the current
// block.
@@ -1815,8 +1887,11 @@
// If there is no target for the break, then we are looking at an incomplete
// AST. This means that the CFG cannot be constructed.
if (BreakJumpTarget.block) {
- addAutomaticObjDtors(ScopePos, BreakJumpTarget.scopePosition, B);
- addSuccessor(Block, BreakJumpTarget.block);
+ addAutomaticObjDtors(ScopePos, BreakJumpTarget.scopePosition, B, B);
+ if (BuildOpts.AddScopes && !InSwitchNest)
+ CreateScopeEndBlockForBreakOrContinueStmt(B, true);
+ else
+ addSuccessor(Block, BreakJumpTarget.block);
} else
badCFG = true;
@@ -1944,17 +2019,47 @@
return addStmt(C->getCond());
}
+static bool shouldDeferScopeEnd(Stmt *S) {
+ return isa<BreakStmt>(S) || isa<ContinueStmt>(S) || isa<ReturnStmt>(S);
+}
+
+void CFGBuilder::CreateScopeEndBlockForSwitchStmt(Stmt *S,
+ bool *ShouldCreateScopeBeginPtr) {
+ autoCreateBlock();
+ appendScopeEnd(Block, S);
+ if (FromSwitchStmt && SwitchNeedsLocalScope) {
+ SwitchScopeEndBlock = Block;
+ Succ = SwitchScopeEndBlock;
+ *ShouldCreateScopeBeginPtr = false;
+ BreakJumpTarget = JumpTarget(SwitchScopeEndBlock, ScopePos);
+ }
+}
CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) {
+ SaveAndRestore<JumpTarget> save_break(BreakJumpTarget);
LocalScope::const_iterator scopeBeginPos = ScopePos;
if (BuildOpts.AddImplicitDtors) {
- addLocalScopeForStmt(C);
+ addLocalScopeForStmt(C, C);
}
- if (!C->body_empty() && !isa<ReturnStmt>(*C->body_rbegin())) {
+
+ if (!C->body_empty() && !isa<ReturnStmt>(*C->body_rbegin()))
// If the body ends with a ReturnStmt, the dtors will be added in
// VisitReturnStmt.
- addAutomaticObjDtors(ScopePos, scopeBeginPos, C);
+ addAutomaticObjDtors(ScopePos, scopeBeginPos, C, C);
+
+ bool ShouldCreateScopeBegin = true;
+ bool SavedFromSwitchStmt = FromSwitchStmt;
+ bool SavedInSwitchNest = InSwitchNest;
+ if (!C->body_empty() && BuildOpts.AddScopes &&
+ (!shouldDeferScopeEnd(*C->body_rbegin()) || FromSwitchStmt))
+ CreateScopeEndBlockForSwitchStmt(C, &ShouldCreateScopeBegin);
+
+ if (BuildOpts.AddScopes) {
+ InSwitchNest = FromSwitchStmt;
+ if (FromSwitchStmt && SwitchNeedsLocalScope)
+ Block = nullptr;
}
+ FromSwitchStmt = false;
CFGBlock *LastBlock = Block;
@@ -1969,6 +2074,16 @@
return nullptr;
}
+ if (BuildOpts.AddScopes && !C->body_empty()) {
+ if (ShouldCreateScopeBegin) {
+ if (!LastBlock)
+ LastBlock = createBlock();
+ appendScopeBegin(LastBlock, C);
+ }
+ FromSwitchStmt = SavedFromSwitchStmt;
+ InSwitchNest = SavedInSwitchNest;
+ }
+
return LastBlock;
}
@@ -2162,6 +2277,13 @@
return B;
}
+void CFGBuilder::CreateScopeEndBlockForIfStmt(IfStmt *I) {
+ autoCreateBlock();
+ prependScopeEnd(Block, I);
+ Succ = Block;
+ Block = nullptr;
+}
+
CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) {
// We may see an if statement in the middle of a basic block, or it may be the
// first statement we are processing. In either case, we create a new basic
@@ -2176,12 +2298,12 @@
// Create local scope for C++17 if init-stmt if one exists.
if (Stmt *Init = I->getInit())
- addLocalScopeForStmt(Init);
+ addLocalScopeForStmt(Init, I);
// Create local scope for possible condition variable.
// Store scope position. Add implicit destructor.
if (VarDecl *VD = I->getConditionVariable())
- addLocalScopeForVarDecl(VD);
+ addLocalScopeForVarDecl(VD, I);
addAutomaticObjDtors(ScopePos, save_scope_pos.get(), I);
@@ -2205,17 +2327,22 @@
// If branch is not a compound statement create implicit scope
// and add destructors.
- if (!isa<CompoundStmt>(Else))
- addLocalScopeAndDtors(Else);
-
+ if (!isa<CompoundStmt>(Else)) {
+ addLocalScopeAndDtors(Else, I);
+ if (BuildOpts.AddScopes && !shouldDeferScopeEnd(Else))
+ CreateScopeEndBlockForIfStmt(I);
+ }
ElseBlock = addStmt(Else);
if (!ElseBlock) // Can occur when the Else body has all NullStmts.
ElseBlock = sv.get();
else if (Block) {
if (badCFG)
return nullptr;
}
+
+ if (!isa<CompoundStmt>(Else))
+ appendScopeBegin(ElseBlock, I);
}
// Process the true branch.
@@ -2228,8 +2355,11 @@
// If branch is not a compound statement create implicit scope
// and add destructors.
- if (!isa<CompoundStmt>(Then))
- addLocalScopeAndDtors(Then);
+ if (!isa<CompoundStmt>(Then)) {
+ addLocalScopeAndDtors(Then, I);
+ if (BuildOpts.AddScopes && !shouldDeferScopeEnd(Then))
+ CreateScopeEndBlockForIfStmt(I);
+ }
ThenBlock = addStmt(Then);
@@ -2243,6 +2373,9 @@
if (badCFG)
return nullptr;
}
+
+ if (!isa<CompoundStmt>(Then))
+ appendScopeBegin(ThenBlock, I);
}
// Specially handle "if (expr1 || ...)" and "if (expr1 && ...)" by
@@ -2296,6 +2429,14 @@
return LastBlock;
}
+void CFGBuilder::CreateScopeEndBlockForReturnStmt(ReturnStmt *R) {
+ CFGBlock *OldBlock = Block;
+ Block = createBlock(false);
+ addSuccessor(OldBlock, Block);
+ addSuccessor(Block, &cfg->getExit());
+ appendScopeEnd(Block, R);
+ Block = OldBlock;
+}
CFGBlock *CFGBuilder::VisitReturnStmt(ReturnStmt *R) {
// If we were in the middle of a block we stop processing that block.
@@ -2308,12 +2449,16 @@
// Create the new block.
Block = createBlock(false);
- addAutomaticObjDtors(ScopePos, LocalScope::const_iterator(), R);
+ addAutomaticObjDtors(ScopePos, LocalScope::const_iterator(), R, R);
// If the one of the destructors does not return, we already have the Exit
// block as a successor.
- if (!Block->hasNoReturnElement())
- addSuccessor(Block, &cfg->getExit());
+ if (!Block->hasNoReturnElement()) {
+ if (BuildOpts.AddScopes)
+ CreateScopeEndBlockForReturnStmt(R);
+ else
+ addSuccessor(Block, &cfg->getExit());
+ }
// Add the return statement to the block. This may create new blocks if R
// contains control-flow (short-circuit operations).
@@ -2407,11 +2552,11 @@
// Add destructor for init statement and condition variable.
// Store scope position for continue statement.
if (Stmt *Init = F->getInit())
- addLocalScopeForStmt(Init);
+ addLocalScopeForStmt(Init, F);
LocalScope::const_iterator LoopBeginScopePos = ScopePos;
if (VarDecl *VD = F->getConditionVariable())
- addLocalScopeForVarDecl(VD);
+ addLocalScopeForVarDecl(VD, F);
LocalScope::const_iterator ContinueScopePos = ScopePos;
addAutomaticObjDtors(ScopePos, save_scope_pos.get(), F);
@@ -2471,7 +2616,7 @@
// If body is not a compound statement create implicit scope
// and add destructors.
if (!isa<CompoundStmt>(F->getBody()))
- addLocalScopeAndDtors(F->getBody());
+ addLocalScopeAndDtors(F->getBody(), F);
// Now populate the body block, and in the process create new blocks as we
// walk the body of the loop.
@@ -2486,6 +2631,11 @@
return nullptr;
}
+ if (!isa<CompoundStmt>(F->getBody())) {
+ appendScopeBegin(BodyBlock, F);
+ if (!shouldDeferScopeEnd(F->getBody()))
+ prependScopeEnd(BodyBlock, F);
+ }
// Because of short-circuit evaluation, the condition of the loop can span
// multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that
// evaluate the condition.
@@ -2554,7 +2704,10 @@
// statements. This block can also contain statements that precede the loop.
if (Stmt *I = F->getInit()) {
Block = createBlock();
- return addStmt(I);
+ CFGBlock *InitBlock = addStmt(I);
+ appendScopeBegin(InitBlock, I);
+ appendScopeEnd(LoopSuccessor, I);
+ return InitBlock;
}
// There is no loop initialization. We are thus basically a while loop.
@@ -2752,7 +2905,7 @@
// Store scope position for continue statement.
LocalScope::const_iterator LoopBeginScopePos = ScopePos;
if (VarDecl *VD = W->getConditionVariable()) {
- addLocalScopeForVarDecl(VD);
+ addLocalScopeForVarDecl(VD, W);
addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W);
}
@@ -2793,15 +2946,21 @@
// If body is not a compound statement create implicit scope
// and add destructors.
if (!isa<CompoundStmt>(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());
if (!BodyBlock)
BodyBlock = ContinueJumpTarget.block; // can happen for "while(...) ;"
else if (Block && badCFG)
return nullptr;
+
+ if (!isa<CompoundStmt>(W->getBody())) {
+ appendScopeBegin(BodyBlock, W);
+ if (!shouldDeferScopeEnd(W->getBody()))
+ prependScopeEnd(BodyBlock, W);
+ }
}
// Because of short-circuit evaluation, the condition of the loop can span
@@ -2975,7 +3134,7 @@
// If body is not a compound statement create implicit scope
// and add destructors.
if (!isa<CompoundStmt>(D->getBody()))
- addLocalScopeAndDtors(D->getBody());
+ addLocalScopeAndDtors(D->getBody(), D);
// Create the body. The returned block is the entry to the loop body.
BodyBlock = addStmt(D->getBody());
@@ -2987,6 +3146,11 @@
return nullptr;
}
+ if (!isa<CompoundStmt>(D->getBody())) {
+ appendScopeBegin(BodyBlock, D);
+ if (!shouldDeferScopeEnd(D->getBody()))
+ prependScopeEnd(BodyBlock, D);
+ }
// Add an intermediate block between the BodyBlock and the
// ExitConditionBlock to represent the "loop back" transition. Create an
// empty block to represent the transition block for looping back to the
@@ -3030,8 +3194,11 @@
// If there is no target for the continue, then we are looking at an
// incomplete AST. This means the CFG cannot be constructed.
if (ContinueJumpTarget.block) {
- addAutomaticObjDtors(ScopePos, ContinueJumpTarget.scopePosition, C);
- addSuccessor(Block, ContinueJumpTarget.block);
+ addAutomaticObjDtors(ScopePos, ContinueJumpTarget.scopePosition, C, C);
+ if (BuildOpts.AddScopes)
+ CFGBuilder::CreateScopeEndBlockForBreakOrContinueStmt(C, false);
+ else
+ addSuccessor(Block, ContinueJumpTarget.block);
} else
badCFG = true;
@@ -3067,6 +3234,14 @@
return VisitCompoundStmt(SE->getSubStmt());
}
+void CFGBuilder::CreateScopeBeginBlockForSwitchStmt(SwitchStmt *Terminator) {
+ SwitchScopeBeginBlock = createBlock(false);
+ SwitchScopeBeginBlock->setTerminator(Terminator);
+ addSuccessor(SwitchTerminatedBlock, SwitchScopeBeginBlock);
+ appendScopeBegin(SwitchScopeBeginBlock, Terminator->getBody());
+ SwitchTerminatedBlock = SwitchScopeBeginBlock;
+}
+
CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) {
// "switch" is a control-flow statement. Thus we stop processing the current
// block.
@@ -3078,12 +3253,12 @@
// Create local scope for C++17 switch init-stmt if one exists.
if (Stmt *Init = Terminator->getInit())
- addLocalScopeForStmt(Init);
+ addLocalScopeForStmt(Init, Terminator);
// Create local scope for possible condition variable.
// Store scope position. Add implicit destructor.
if (VarDecl *VD = Terminator->getConditionVariable())
- addLocalScopeForVarDecl(VD);
+ addLocalScopeForVarDecl(VD, Terminator);
addAutomaticObjDtors(ScopePos, save_scope_pos.get(), Terminator);
@@ -3132,7 +3307,22 @@
// If body is not a compound statement create implicit scope
// and add destructors.
if (!isa<CompoundStmt>(Terminator->getBody()))
- addLocalScopeAndDtors(Terminator->getBody());
+ addLocalScopeAndDtors(Terminator->getBody(), Terminator);
+
+ SwitchNeedsLocalScope = isa<CompoundStmt>(Terminator->getBody());
+ CFGBlock *SavedSwitchTerminatedBlock = SwitchTerminatedBlock;
+
+ if (BuildOpts.AddScopes && SwitchNeedsLocalScope)
+ CreateScopeBeginBlockForSwitchStmt(Terminator);
+
+ // If the SwitchStmt contains a condition variable, end its scope after switch.
+ if (VarDecl *VD = Terminator->getConditionVariable())
+ if (Expr *Init = VD->getInit())
+ appendScopeEnd(Succ, Terminator->getConditionVariableDeclStmt());
+
+ FromSwitchStmt = true;
+ CFGBlock *SavedSwitchScopeEndBlock = SwitchScopeEndBlock;
+ CFGBlock *SavedDefaultCaseBlock = DefaultCaseBlock;
addStmt(Terminator->getBody());
if (Block) {
@@ -3150,9 +3340,24 @@
SwitchAlwaysHasSuccessor |= switchExclusivelyCovered;
SwitchAlwaysHasSuccessor |= Terminator->isAllEnumCasesCovered() &&
Terminator->getSwitchCaseList();
- addSuccessor(SwitchTerminatedBlock, DefaultCaseBlock,
+
+ SwitchSuccessor = DefaultCaseBlock;
+ // If DefaultCaseBlock == SavedDefaultCaseBlock, then we don't have default
+ // case.
+ if (BuildOpts.AddScopes && (SavedSwitchScopeEndBlock != SwitchScopeEndBlock) &&
+ (DefaultCaseBlock == SavedDefaultCaseBlock))
+ SwitchSuccessor = SwitchScopeEndBlock;
+
+ addSuccessor(SwitchTerminatedBlock, SwitchSuccessor,
!SwitchAlwaysHasSuccessor);
+ // Restore SwitchNeedsLocalScope value
+ SwitchNeedsLocalScope = false;
+ SwitchTerminatedBlock = SavedSwitchTerminatedBlock;
+ SwitchScopeEndBlock = SavedSwitchScopeEndBlock;
+ DefaultCaseBlock = SavedDefaultCaseBlock;
+ FromSwitchStmt = false;
+
// Add the terminator and condition in the switch block.
SwitchTerminatedBlock->setTerminator(Terminator);
Block = SwitchTerminatedBlock;
@@ -3163,8 +3368,10 @@
if (VarDecl *VD = Terminator->getConditionVariable()) {
if (Expr *Init = VD->getInit()) {
autoCreateBlock();
- appendStmt(Block, Terminator->getConditionVariableDeclStmt());
+ auto VDDeclStmt = Terminator->getConditionVariableDeclStmt();
+ appendStmt(Block, VDDeclStmt);
LastBlock = addStmt(Init);
+ appendScopeBegin(LastBlock, VDDeclStmt);
}
}
@@ -3217,6 +3424,7 @@
// CaseStmts are essentially labels, so they are the first statement in a
// block.
CFGBlock *TopBlock = nullptr, *LastBlock = nullptr;
+ SaveAndRestore<JumpTarget> save_break(BreakJumpTarget);
if (Stmt *Sub = CS->getSubStmt()) {
// For deeply nested chains of CaseStmts, instead of doing a recursion
@@ -3252,6 +3460,9 @@
// were processing (the "case XXX:" is the label).
CaseBlock->setLabel(CS);
+ if (BuildOpts.AddScopes && SwitchNeedsLocalScope)
+ BreakJumpTarget = JumpTarget(SwitchScopeEndBlock, ScopePos);
+
if (badCFG)
return nullptr;
@@ -3277,6 +3488,13 @@
}
CFGBlock *CFGBuilder::VisitDefaultStmt(DefaultStmt *Terminator) {
+ SaveAndRestore<JumpTarget> save_break(BreakJumpTarget);
+ if (BuildOpts.AddScopes && SwitchNeedsLocalScope) {
+ BreakJumpTarget = JumpTarget(SwitchScopeEndBlock, ScopePos);
+ Succ = SwitchScopeEndBlock;
+ Block = nullptr;
+ }
+
if (Terminator->getSubStmt())
addStmt(Terminator->getSubStmt());
@@ -3372,7 +3590,7 @@
// Store scope position. Add implicit destructor.
if (VarDecl *VD = CS->getExceptionDecl()) {
LocalScope::const_iterator BeginScopePos = ScopePos;
- addLocalScopeForVarDecl(VD);
+ addLocalScopeForVarDecl(VD, CS);
addAutomaticObjDtors(ScopePos, BeginScopePos, CS);
}
@@ -3422,11 +3640,11 @@
// Create local scopes and destructors for range, begin and end variables.
if (Stmt *Range = S->getRangeStmt())
- addLocalScopeForStmt(Range);
+ addLocalScopeForStmt(Range, S);
if (Stmt *Begin = S->getBeginStmt())
- addLocalScopeForStmt(Begin);
+ addLocalScopeForStmt(Begin, S);
if (Stmt *End = S->getEndStmt())
- addLocalScopeForStmt(End);
+ addLocalScopeForStmt(End, S);
addAutomaticObjDtors(ScopePos, save_scope_pos.get(), S);
LocalScope::const_iterator ContinueScopePos = ScopePos;
@@ -3498,7 +3716,7 @@
Block = nullptr;
// Add implicit scope and dtors for loop variable.
- addLocalScopeAndDtors(S->getLoopVarStmt());
+ addLocalScopeAndDtors(S->getLoopVarStmt(), S);
// Populate a new block to contain the loop body and loop variable.
addStmt(S->getBody());
@@ -3898,6 +4116,8 @@
case CFGElement::Statement:
case CFGElement::Initializer:
case CFGElement::NewAllocator:
+ case CFGElement::ScopeBegin:
+ case CFGElement::ScopeEnd:
llvm_unreachable("getDestructorDecl should only be used with "
"ImplicitDtors");
case CFGElement::AutomaticObjectDtor: {
@@ -4307,7 +4527,16 @@
OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()";
OS << " (Implicit destructor)\n";
-
+ } else if (Optional<CFGScopeBegin> SB = E.getAs<CFGScopeBegin>()) {
+ OS << "CFGScopeBegin(";
+ if (const Stmt *S = SB->getTriggerStmt())
+ OS << S->getStmtClassName();
+ OS << ")\n";
+ } else if (Optional<CFGScopeEnd> SE = E.getAs<CFGScopeEnd>()) {
+ OS << "CFGScopeEnd(";
+ if (const Stmt *S = SE->getTriggerStmt())
+ OS << S->getStmtClassName();
+ 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
@@ -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: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
===================================================================
--- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
+++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
@@ -211,6 +211,9 @@
/// \sa mayInlineCXXStandardLibrary
Optional<bool> InlineCXXStandardLibrary;
+ /// \sa includeScopesInCFG
+ Optional<bool> IncludeScopesInCFG;
+
/// \sa mayInlineTemplateFunctions
Optional<bool> InlineTemplateFunctions;
@@ -395,6 +398,12 @@
/// accepts the values "true" and "false".
bool includeTemporaryDtorsInCFG();
+ /// Returns whether or not scope information should be included in the CFG.
+ ///
+ /// This is controlled by the 'cfg-scope-info' config option, which accepts
+ /// the values "true" and "false".
+ bool includeScopesInCFG();
+
/// Returns whether or not C++ standard library functions may be considered
/// for inlining.
///
Index: include/clang/Analysis/CFG.h
===================================================================
--- include/clang/Analysis/CFG.h
+++ include/clang/Analysis/CFG.h
@@ -57,6 +57,8 @@
// main kind
Statement,
Initializer,
+ ScopeBegin,
+ ScopeEnd,
NewAllocator,
// dtor kind
AutomaticObjectDtor,
@@ -167,6 +169,48 @@
}
};
+/// Represents beginning of a scope implicitly generated
+/// by the compiler on encountering a CompoundStmt
+class CFGScopeBegin : public CFGElement {
+public:
+ CFGScopeBegin() {}
+ CFGScopeBegin(const Stmt *S)
+ : CFGElement(ScopeBegin, S) {}
+
+ // Get statement that triggered a new scope.
+ const Stmt *getTriggerStmt() const {
+ return static_cast<Stmt*>(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<Stmt*>(Data1.getPointer());
+ }
+
+private:
+ friend class CFGElement;
+ static bool isKind(const CFGElement &E) {
+ Kind kind = E.getKind();
+ return kind == ScopeEnd;
+ }
+};
+
/// CFGImplicitDtor - Represents C++ object destructor implicitly generated
/// by compiler on various occasions.
class CFGImplicitDtor : public CFGElement {
@@ -685,6 +729,18 @@
Elements.push_back(CFGNewAllocator(NE), C);
}
+ void appendScopeBegin(const Stmt *S, BumpVectorContext &C) {
+ Elements.push_back(CFGScopeBegin(S), C);
+ }
+
+ void appendScopeEnd(const Stmt *S, BumpVectorContext &C) {
+ Elements.push_back(CFGScopeEnd(S), C);
+ }
+
+ void prependScopeEnd(const Stmt *S, BumpVectorContext &C) {
+ Elements.insert(Elements.rbegin(), 1, CFGScopeEnd(S), C);
+ }
+
void appendBaseDtor(const CXXBaseSpecifier *BS, BumpVectorContext &C) {
Elements.push_back(CFGBaseDtor(BS), C);
}
@@ -705,6 +761,8 @@
Elements.push_back(CFGDeleteDtor(RD, DE), C);
}
+ void removeAllSuccessors() { Succs.clear(); }
+
// Destructors must be inserted in reversed order. So insertion is in two
// steps. First we prepare space for some number of elements, then we insert
// the elements beginning at the last position in prepared space.
@@ -754,6 +812,7 @@
bool AddInitializers;
bool AddImplicitDtors;
bool AddTemporaryDtors;
+ bool AddScopes;
bool AddStaticInitBranches;
bool AddCXXNewAllocator;
bool AddCXXDefaultInitExprInCtors;
@@ -776,8 +835,9 @@
: forcedBlkExprs(nullptr), Observer(nullptr),
PruneTriviallyFalseEdges(true), AddEHEdges(false),
AddInitializers(false), AddImplicitDtors(false),
- AddTemporaryDtors(false), AddStaticInitBranches(false),
- AddCXXNewAllocator(false), AddCXXDefaultInitExprInCtors(false) {}
+ AddTemporaryDtors(false), AddScopes(false),
+ AddStaticInitBranches(false), AddCXXNewAllocator(false),
+ AddCXXDefaultInitExprInCtors(false) {}
};
/// buildCFG - Builds a CFG from an AST.
Index: include/clang/Analysis/AnalysisContext.h
===================================================================
--- include/clang/Analysis/AnalysisContext.h
+++ include/clang/Analysis/AnalysisContext.h
@@ -426,6 +426,7 @@
bool addImplicitDtors = false,
bool addInitializers = false,
bool addTemporaryDtors = false,
+ bool addScopes = false,
bool synthesizeBodies = false,
bool addStaticInitBranches = false,
bool addCXXNewAllocator = true,
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits