m.ostapenko updated this revision to Diff 108644.
m.ostapenko added a comment.
Updated some comments. Could someone take a look please?
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
lib/StaticAnalyzer/Core/ExprEngine.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,1099 @@
+// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -analyze -analyzer-checker=debug.DumpCFG -analyzer-config cfg-scopes=true %s > %t 2>&1
+// RUN: FileCheck --input-file=%t %s
+
+class A {
+public:
+// CHECK: [B1 (ENTRY)]
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+ A() {}
+
+// CHECK: [B1 (ENTRY)]
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+ ~A() {}
+
+// CHECK: [B2 (ENTRY)]
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: 1
+// CHECK-NEXT: 3: return [B1.2];
+// CHECK-NEXT: 4: CFGScopeEnd(ReturnStmt)
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+ operator int() const { return 1; }
+};
+
+int getX();
+extern const bool UV;
+
+// CHECK: [B2 (ENTRY)]
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: 4: a
+// CHECK-NEXT: 5: [B1.4] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 6: const A &b = a;
+// CHECK-NEXT: 7: A() (CXXConstructExpr, class A)
+// CHECK-NEXT: 8: [B1.7] (BindTemporary)
+// CHECK-NEXT: 9: [B1.8] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 10: [B1.9]
+// CHECK-NEXT: 11: const A &c = A();
+// CHECK-NEXT: 12: [B1.11].~A() (Implicit destructor)
+// CHECK-NEXT: 13: [B1.3].~A() (Implicit destructor)
+// CHECK-NEXT: 14: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+void test_const_ref() {
+ A a;
+ const A& b = a;
+ const A& c = A();
+}
+
+// CHECK: [B2 (ENTRY)]
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A [2])
+// CHECK-NEXT: 3: A a[2];
+// CHECK-NEXT: 4: (CXXConstructExpr, class A [0])
+// CHECK-NEXT: 5: A b[0];
+// CHECK-NEXT: 6: [B1.3].~A() (Implicit destructor)
+// CHECK-NEXT: 7: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+void test_array() {
+ A a[2];
+ A b[0];
+}
+
+// CHECK: [B2 (ENTRY)]
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: 4: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 5: (CXXConstructExpr, class A)
+// CHECK-NEXT: 6: A c;
+// CHECK-NEXT: 7: (CXXConstructExpr, class A)
+// CHECK-NEXT: 8: A d;
+// CHECK-NEXT: 9: [B1.8].~A() (Implicit destructor)
+// CHECK-NEXT: 10: [B1.6].~A() (Implicit destructor)
+// CHECK-NEXT: 11: (CXXConstructExpr, class A)
+// CHECK-NEXT: 12: A b;
+// CHECK-NEXT: 13: [B1.12].~A() (Implicit destructor)
+// CHECK-NEXT: 14: [B1.3].~A() (Implicit destructor)
+// CHECK-NEXT: 15: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 16: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+void test_scope() {
+ A a;
+ { A c;
+ A d;
+ }
+ A b;
+}
+
+// CHECK: [B4 (ENTRY)]
+// CHECK-NEXT: Succs (1): B3
+// CHECK: [B1]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A c;
+// CHECK-NEXT: 3: [B1.2].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B3.5].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B3.3].~A() (Implicit destructor)
+// CHECK-NEXT: 6: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: return;
+// CHECK-NEXT: 3: [B3.5].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B3.3].~A() (Implicit destructor)
+// CHECK-NEXT: 5: CFGScopeEnd(ReturnStmt)
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: 4: (CXXConstructExpr, class A)
+// CHECK-NEXT: 5: A b;
+// CHECK-NEXT: 6: UV
+// CHECK-NEXT: 7: [B3.6] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B3.7]
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (2): B2 B1
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (2): B1 B2
+void test_return() {
+ A a;
+ A b;
+ if (UV) return;
+ A c;
+}
+
+// CHECK: [B5 (ENTRY)]
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: 2: [B4.8].~A() (Implicit destructor)
+// CHECK-NEXT: 3: [B4.3].~A() (Implicit destructor)
+// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (2): B2 B3
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A c;
+// CHECK-NEXT: 4: [B2.3].~A() (Implicit destructor)
+// CHECK-NEXT: 5: CFGScopeEnd(IfStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A c;
+// CHECK-NEXT: 4: [B3.3].~A() (Implicit destructor)
+// CHECK-NEXT: 5: CFGScopeEnd(IfStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: 4: CFGScopeBegin(DeclStmt)
+// CHECK-NEXT: 5: a
+// CHECK-NEXT: 6: [B4.5] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 7: [B4.6] (CXXConstructExpr, class A)
+// CHECK-NEXT: 8: A b = a;
+// CHECK-NEXT: 9: b
+// CHECK-NEXT: 10: [B4.9] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 11: [B4.10].operator int
+// CHECK-NEXT: 12: [B4.10]
+// CHECK-NEXT: 13: [B4.12] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT: 14: [B4.13] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: if [B4.14]
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (2): B3 B2
+// CHECK: [B0 (EXIT)]
+void test_if_implicit_scope() {
+ A a;
+ if (A b = a)
+ A c;
+ else A c;
+}
+
+// CHECK: [B8 (ENTRY)]
+// CHECK-NEXT: Succs (1): B7
+// CHECK: [B1]
+// CHECK-NEXT: l1:
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A c;
+// CHECK-NEXT: 3: [B1.2].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B6.2].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B7.2].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (2): B2 B3
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A b;
+// CHECK-NEXT: 3: [B2.2].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B6.4].~A() (Implicit destructor)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B3]
+// CHECK-NEXT: 1: [B6.4].~A() (Implicit destructor)
+// CHECK-NEXT: T: goto l1;
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B4]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B4.2]
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (2): B3 B2
+// CHECK: [B5]
+// CHECK-NEXT: 1: [B6.4].~A() (Implicit destructor)
+// CHECK-NEXT: 2: [B6.2].~A() (Implicit destructor)
+// CHECK-NEXT: T: goto l0;
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B6
+// CHECK: [B6]
+// CHECK-NEXT: l0:
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A b;
+// CHECK-NEXT: 3: (CXXConstructExpr, class A)
+// CHECK-NEXT: 4: A a;
+// CHECK-NEXT: 5: UV
+// CHECK-NEXT: 6: [B6.5] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B6.6]
+// CHECK-NEXT: Preds (2): B7 B5
+// CHECK-NEXT: Succs (2): B5 B4
+// CHECK: [B7]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A a;
+// CHECK-NEXT: Preds (1): B8
+// CHECK-NEXT: Succs (1): B6
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+void test_goto() {
+ A a;
+l0:
+ A b;
+ {
+ A a;
+ if (UV)
+ goto l0;
+ if (UV)
+ goto l1;
+ A b;
+ }
+l1:
+ A c;
+}
+
+// CHECK: [B9 (ENTRY)]
+// CHECK-NEXT: Succs (1): B8
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: 2: [B8.8].~A() (Implicit destructor)
+// CHECK-NEXT: 3: (CXXConstructExpr, class A)
+// CHECK-NEXT: 4: A e;
+// CHECK-NEXT: 5: [B1.4].~A() (Implicit destructor)
+// CHECK-NEXT: 6: [B8.3].~A() (Implicit destructor)
+// CHECK-NEXT: 7: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (2): B2 B5
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A d;
+// CHECK-NEXT: 3: [B2.2].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B4.3].~A() (Implicit destructor)
+// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: return;
+// CHECK-NEXT: 3: [B4.3].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B8.8].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B8.3].~A() (Implicit destructor)
+// CHECK-NEXT: 6: CFGScopeEnd(ReturnStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A c;
+// CHECK-NEXT: 4: UV
+// CHECK-NEXT: 5: [B4.4] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B4.5]
+// CHECK-NEXT: Preds (1): B8
+// CHECK-NEXT: Succs (2): B3 B2
+// CHECK: [B5]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A d;
+// CHECK-NEXT: 3: [B5.2].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B7.3].~A() (Implicit destructor)
+// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B6]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: return;
+// CHECK-NEXT: 3: [B7.3].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B8.8].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B8.3].~A() (Implicit destructor)
+// CHECK-NEXT: 6: CFGScopeEnd(ReturnStmt)
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B7]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A c;
+// CHECK-NEXT: 4: UV
+// CHECK-NEXT: 5: [B7.4] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B7.5]
+// CHECK-NEXT: Preds (1): B8
+// CHECK-NEXT: Succs (2): B6 B5
+// CHECK: [B8]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: 4: CFGScopeBegin(DeclStmt)
+// CHECK-NEXT: 5: a
+// CHECK-NEXT: 6: [B8.5] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 7: [B8.6] (CXXConstructExpr, class A)
+// CHECK-NEXT: 8: A b = a;
+// CHECK-NEXT: 9: b
+// CHECK-NEXT: 10: [B8.9] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 11: [B8.10].operator int
+// CHECK-NEXT: 12: [B8.10]
+// CHECK-NEXT: 13: [B8.12] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT: 14: [B8.13] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: if [B8.14]
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (2): B7 B4
+// CHECK: [B0 (EXIT)]
+void test_if_jumps() {
+ A a;
+ if (A b = a) {
+ A c;
+ if (UV) return;
+ A d;
+ } else {
+ A c;
+ if (UV) return;
+ A d;
+ }
+ A e;
+}
+
+// CHECK: [B12 (ENTRY)]
+// CHECK-NEXT: Succs (1): B11
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (2): B2 B9
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: CFGScopeEnd(IfStmt)
+// CHECK-NEXT: Preds (2): B3 B6
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeEnd(IfStmt)
+// CHECK-NEXT: Preds (2): B4 B5
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: 'a'
+// CHECK-NEXT: 3: char c = 'a';
+// CHECK-NEXT: 4: CFGScopeEnd(IfStmt)
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B3
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: a
+// CHECK-NEXT: 3: [B5.2] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 4: [B5.3] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: 5: ![B5.4]
+// CHECK-NEXT: T: if [B5.5]
+// CHECK-NEXT: Preds (2): B7 B8
+// CHECK-NEXT: Succs (2): B4 B3
+// CHECK: [B6]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: 2
+// CHECK-NEXT: 3: a
+// CHECK-NEXT: 4: [B6.3] = [B6.2]
+// CHECK-NEXT: 5: 3
+// CHECK-NEXT: 6: b
+// CHECK-NEXT: 7: [B6.6] = [B6.5]
+// CHECK-NEXT: 8: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B7]
+// CHECK-NEXT: 1: b
+// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 3: [B7.2] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: 4: ![B7.3]
+// CHECK-NEXT: T: if [B8.4] && [B7.4]
+// CHECK-NEXT: Preds (1): B8
+// CHECK-NEXT: Succs (2): B6 B5
+// CHECK: [B8]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: a
+// CHECK-NEXT: 3: [B8.2] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 4: [B8.3] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: [B8.4] && ...
+// CHECK-NEXT: Preds (2): B10 B11
+// CHECK-NEXT: Succs (2): B7 B5
+// CHECK: [B9]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: 0
+// CHECK-NEXT: 3: a
+// CHECK-NEXT: 4: [B9.3] = [B9.2]
+// CHECK-NEXT: 5: 1
+// CHECK-NEXT: 6: b
+// CHECK-NEXT: 7: [B9.6] = [B9.5]
+// CHECK-NEXT: 8: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B10
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B10]
+// CHECK-NEXT: 1: b
+// CHECK-NEXT: 2: [B10.1] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 3: [B10.2] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: if [B11.4] && [B10.3]
+// CHECK-NEXT: Preds (1): B11
+// CHECK-NEXT: Succs (2): B9 B8
+// CHECK: [B11]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: a
+// CHECK-NEXT: 3: [B11.2] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 4: [B11.3] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: [B11.4] && ...
+// CHECK-NEXT: Preds (1): B12
+// CHECK-NEXT: Succs (2): B10 B8
+// CHECK: [B0 (EXIT)]
+int a, b;
+void text_if_with_and() {
+ if (a && b) {
+ a = 0;
+ b = 1;
+ } else if (a && !b) {
+ a = 2;
+ b = 3;
+ } else if (!a)
+ char c = 'a';
+}
+
+// CHECK: [B6 (ENTRY)]
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(CXXConstructExpr)
+// CHECK-NEXT: 2: [B4.5].~A() (Implicit destructor)
+// CHECK-NEXT: 3: [B5.3].~A() (Implicit destructor)
+// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: CFGScopeEnd(CXXConstructExpr)
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeBegin(WhileStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A c;
+// CHECK-NEXT: 4: [B3.3].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B4.5].~A() (Implicit destructor)
+// CHECK-NEXT: 6: CFGScopeEnd(WhileStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(CXXConstructExpr)
+// CHECK-NEXT: 2: a
+// CHECK-NEXT: 3: [B4.2] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 4: [B4.3] (CXXConstructExpr, class A)
+// CHECK-NEXT: 5: A b = a;
+// CHECK-NEXT: 6: b
+// CHECK-NEXT: 7: [B4.6] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 8: [B4.7].operator int
+// CHECK-NEXT: 9: [B4.7]
+// CHECK-NEXT: 10: [B4.9] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT: 11: [B4.10] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: while [B4.11]
+// CHECK-NEXT: Preds (2): B2 B5
+// CHECK-NEXT: Succs (2): B3 B1
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B0 (EXIT)]
+void test_while_implicit_scope() {
+ A a;
+ while (A b = a)
+ A c;
+}
+
+// CHECK: [B12 (ENTRY)]
+// CHECK-NEXT: Succs (1): B11
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(CXXConstructExpr)
+// CHECK-NEXT: 2: [B10.5].~A() (Implicit destructor)
+// CHECK-NEXT: 3: (CXXConstructExpr, class A)
+// CHECK-NEXT: 4: A e;
+// CHECK-NEXT: 5: [B1.4].~A() (Implicit destructor)
+// CHECK-NEXT: 6: [B11.3].~A() (Implicit destructor)
+// CHECK-NEXT: 7: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (2): B8 B10
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: CFGScopeEnd(CXXConstructExpr)
+// CHECK-NEXT: Preds (2): B3 B6
+// CHECK-NEXT: Succs (1): B10
+// CHECK: [B3]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A d;
+// CHECK-NEXT: 3: [B3.2].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B9.3].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B10.5].~A() (Implicit destructor)
+// CHECK-NEXT: 6: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: return;
+// CHECK-NEXT: 3: [B9.3].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B10.5].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B11.3].~A() (Implicit destructor)
+// CHECK-NEXT: 6: CFGScopeEnd(ReturnStmt)
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B5]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B5.2]
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (2): B4 B3
+// CHECK: [B6]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: [B9.3].~A() (Implicit destructor)
+// CHECK-NEXT: 3: [B10.5].~A() (Implicit destructor)
+// CHECK-NEXT: 4: CFGScopeEnd(ContinueStmt)
+// CHECK-NEXT: T: continue;
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B7]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B7.2]
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (2): B6 B5
+// CHECK: [B8]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: [B9.3].~A() (Implicit destructor)
+// CHECK-NEXT: 3: CFGScopeEnd(BreakStmt)
+// CHECK-NEXT: T: break;
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B9]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A c;
+// CHECK-NEXT: 4: UV
+// CHECK-NEXT: 5: [B9.4] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B9.5]
+// CHECK-NEXT: Preds (1): B10
+// CHECK-NEXT: Succs (2): B8 B7
+// CHECK: [B10]
+// CHECK-NEXT: 1: CFGScopeBegin(CXXConstructExpr)
+// CHECK-NEXT: 2: a
+// CHECK-NEXT: 3: [B10.2] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 4: [B10.3] (CXXConstructExpr, class A)
+// CHECK-NEXT: 5: A b = a;
+// CHECK-NEXT: 6: b
+// CHECK-NEXT: 7: [B10.6] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 8: [B10.7].operator int
+// CHECK-NEXT: 9: [B10.7]
+// CHECK-NEXT: 10: [B10.9] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT: 11: [B10.10] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: while [B10.11]
+// CHECK-NEXT: Preds (2): B2 B11
+// CHECK-NEXT: Succs (2): B9 B1
+// CHECK: [B11]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: Preds (1): B12
+// CHECK-NEXT: Succs (1): B10
+// CHECK: [B0 (EXIT)]
+void test_while_jumps() {
+ A a;
+ while (A b = a) {
+ A c;
+ if (UV) break;
+ if (UV) continue;
+ if (UV) return;
+ A d;
+ }
+ A e;
+}
+
+// CHECK: [B12 (ENTRY)]
+// CHECK-NEXT: Succs (1): B11
+// CHECK: [B1]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A d;
+// CHECK-NEXT: 3: [B1.2].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B11.3].~A() (Implicit destructor)
+// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (2): B8 B2
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: do ... while [B2.2]
+// CHECK-NEXT: Preds (2): B3 B6
+// CHECK-NEXT: Succs (2): B10 B1
+// CHECK: [B3]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A c;
+// CHECK-NEXT: 3: [B3.2].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B9.3].~A() (Implicit destructor)
+// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: return;
+// CHECK-NEXT: 3: [B9.3].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B11.3].~A() (Implicit destructor)
+// CHECK-NEXT: 5: CFGScopeEnd(ReturnStmt)
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B5]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B5.2]
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (2): B4 B3
+// CHECK: [B6]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: [B9.3].~A() (Implicit destructor)
+// CHECK-NEXT: 3: CFGScopeEnd(ContinueStmt)
+// CHECK-NEXT: T: continue;
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B7]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B7.2]
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (2): B6 B5
+// CHECK: [B8]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: [B9.3].~A() (Implicit destructor)
+// CHECK-NEXT: 3: CFGScopeEnd(BreakStmt)
+// CHECK-NEXT: T: break;
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B9]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A b;
+// CHECK-NEXT: 4: UV
+// CHECK-NEXT: 5: [B9.4] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B9.5]
+// CHECK-NEXT: Preds (2): B10 B11
+// CHECK-NEXT: Succs (2): B8 B7
+// CHECK: [B10]
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B9
+// CHECK: [B11]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: Preds (1): B12
+// CHECK-NEXT: Succs (1): B9
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (2): B1 B4
+void test_do_jumps() {
+ A a;
+ do {
+ A b;
+ if (UV) break;
+ if (UV) continue;
+ if (UV) return;
+ A c;
+ } while (UV);
+ A d;
+}
+
+// CHECK: [B6 (ENTRY)]
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: 2: [B4.5].~A() (Implicit destructor)
+// CHECK-NEXT: 3: [B5.3].~A() (Implicit destructor)
+// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeBegin(ForStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A c;
+// CHECK-NEXT: 4: [B3.3].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B4.5].~A() (Implicit destructor)
+// CHECK-NEXT: 6: CFGScopeEnd(ForStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(DeclStmt)
+// CHECK-NEXT: 2: a
+// CHECK-NEXT: 3: [B4.2] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 4: [B4.3] (CXXConstructExpr, class A)
+// CHECK-NEXT: 5: A b = a;
+// CHECK-NEXT: 6: b
+// CHECK-NEXT: 7: [B4.6] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 8: [B4.7].operator int
+// CHECK-NEXT: 9: [B4.7]
+// CHECK-NEXT: 10: [B4.9] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT: 11: [B4.10] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: for (...; [B4.11]; )
+// CHECK-NEXT: Preds (2): B2 B5
+// CHECK-NEXT: Succs (2): B3 B1
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B4
+// CHECK: [B0 (EXIT)]
+void test_for_implicit_scope() {
+ for (A a; A b = a; )
+ A c;
+}
+
+// CHECK: [B12 (ENTRY)]
+// CHECK-NEXT: Succs (1): B11
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: 2: [B10.5].~A() (Implicit destructor)
+// CHECK-NEXT: 3: [B11.5].~A() (Implicit destructor)
+// CHECK-NEXT: 4: (CXXConstructExpr, class A)
+// CHECK-NEXT: 5: A f;
+// CHECK-NEXT: 6: [B1.5].~A() (Implicit destructor)
+// CHECK-NEXT: 7: [B11.3].~A() (Implicit destructor)
+// CHECK-NEXT: 8: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (2): B8 B10
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: Preds (2): B3 B6
+// CHECK-NEXT: Succs (1): B10
+// CHECK: [B3]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: A e;
+// CHECK-NEXT: 3: [B3.2].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B9.3].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B10.5].~A() (Implicit destructor)
+// CHECK-NEXT: 6: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: return;
+// CHECK-NEXT: 3: [B9.3].~A() (Implicit destructor)
+// CHECK-NEXT: 4: [B10.5].~A() (Implicit destructor)
+// CHECK-NEXT: 5: [B11.5].~A() (Implicit destructor)
+// CHECK-NEXT: 6: [B11.3].~A() (Implicit destructor)
+// CHECK-NEXT: 7: CFGScopeEnd(ReturnStmt)
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B5]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B5.2]
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (2): B4 B3
+// CHECK: [B6]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: [B9.3].~A() (Implicit destructor)
+// CHECK-NEXT: 3: CFGScopeEnd(ContinueStmt)
+// CHECK-NEXT: T: continue;
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B7]
+// CHECK-NEXT: 1: UV
+// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B7.2]
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (2): B6 B5
+// CHECK: [B8]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: [B9.3].~A() (Implicit destructor)
+// CHECK-NEXT: 3: CFGScopeEnd(BreakStmt)
+// CHECK-NEXT: T: break;
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B9]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A d;
+// CHECK-NEXT: 4: UV
+// CHECK-NEXT: 5: [B9.4] (ImplicitCastExpr, LValueToRValue, _Bool)
+// CHECK-NEXT: T: if [B9.5]
+// CHECK-NEXT: Preds (1): B10
+// CHECK-NEXT: Succs (2): B8 B7
+// CHECK: [B10]
+// CHECK-NEXT: 1: CFGScopeBegin(DeclStmt)
+// CHECK-NEXT: 2: b
+// CHECK-NEXT: 3: [B10.2] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 4: [B10.3] (CXXConstructExpr, class A)
+// CHECK-NEXT: 5: A c = b;
+// CHECK-NEXT: 6: c
+// CHECK-NEXT: 7: [B10.6] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 8: [B10.7].operator int
+// CHECK-NEXT: 9: [B10.7]
+// CHECK-NEXT: 10: [B10.9] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT: 11: [B10.10] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: for (...; [B10.11]; )
+// CHECK-NEXT: Preds (2): B2 B11
+// CHECK-NEXT: Succs (2): B9 B1
+// CHECK: [B11]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: 4: (CXXConstructExpr, class A)
+// CHECK-NEXT: 5: A b;
+// CHECK-NEXT: Preds (1): B12
+// CHECK-NEXT: Succs (1): B10
+// CHECK: [B0 (EXIT)]
+void test_for_jumps() {
+ A a;
+ for (A b; A c = b; ) {
+ A d;
+ if (UV) break;
+ if (UV) continue;
+ if (UV) return;
+ A e;
+ }
+ A f;
+}
+
+// CHECK: [B7 (ENTRY)]
+// CHECK-NEXT: Succs (1): B6
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: 2: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 3: int unused2;
+// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: 5: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (2): B4 B5
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: i
+// CHECK-NEXT: 2: ++[B2.1]
+// CHECK-NEXT: 3: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: Preds (1): B3
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B3]
+// CHECK-NEXT: 1: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 3: int unused1;
+// CHECK-NEXT: 4: CFGScopeEnd(BreakStmt)
+// CHECK-NEXT: T: break;
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeBegin(DeclStmt)
+// CHECK-NEXT: 2: i
+// CHECK-NEXT: 3: [B5.2] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 4: 3
+// CHECK-NEXT: 5: [B5.3] < [B5.4]
+// CHECK-NEXT: T: for (...; [B5.5]; ...)
+// CHECK-NEXT: Preds (2): B2 B6
+// CHECK-NEXT: Succs (2): B4 B1
+// CHECK: [B6]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: 0
+// CHECK-NEXT: 3: int i = 0;
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B0 (EXIT)]
+void test_for_compound_and_break() {
+ for (int i = 0; i < 3; ++i) {
+ {
+ int unused1;
+ break;
+ }
+ }
+ {
+ int unused2;
+ }
+}
+
+// CHECK: [B6 (ENTRY)]
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B1]
+// CHECK-NEXT: 1: [B5.3].~A() (Implicit destructor)
+// CHECK-NEXT: 2: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: __begin
+// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, LValueToRValue, class A *)
+// CHECK-NEXT: 3: __end
+// CHECK-NEXT: 4: [B2.3] (ImplicitCastExpr, LValueToRValue, class A *)
+// CHECK-NEXT: 5: [B2.2] != [B2.4]
+// CHECK-NEXT: T: for (auto &i : [B5.4]) {
+// CHECK: [B4.12];
+// CHECK-NEXT:}
+// CHECK-NEXT: Preds (2): B3 B5
+// CHECK-NEXT: Succs (2): B4 B1
+// CHECK: [B3]
+// CHECK-NEXT: 1: __begin
+// CHECK-NEXT: 2: ++[B3.1]
+// CHECK-NEXT: 3: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: Preds (1): B4
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(DeclStmt)
+// CHECK-NEXT: 2: CFGScopeBegin(CXXForRangeStmt)
+// CHECK-NEXT: 3: __begin
+// CHECK-NEXT: 4: [B4.3] (ImplicitCastExpr, LValueToRValue, class A *)
+// CHECK-NEXT: 5: *[B4.4]
+// CHECK-NEXT: 6: auto &i = *__begin;
+// CHECK-NEXT: 7: operator=
+// CHECK-NEXT: 8: [B4.7] (ImplicitCastExpr, FunctionToPointerDecay, class A &(*)(const class A &) throw())
+// CHECK-NEXT: 9: i
+// CHECK-NEXT: 10: b
+// CHECK-NEXT: 11: [B4.10] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 12: [B4.9] = [B4.11] (OperatorCall)
+// CHECK-NEXT: 13: CFGScopeEnd(CXXForRangeStmt)
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B3
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A [10])
+// CHECK-NEXT: 3: A a[10];
+// CHECK-NEXT: 4: a
+// CHECK-NEXT: 5: auto &&__range = a;
+// CHECK-NEXT: 6: __range
+// CHECK-NEXT: 7: [B5.6] (ImplicitCastExpr, ArrayToPointerDecay, class A *)
+// CHECK-NEXT: 8: 10L
+// CHECK-NEXT: 9: [B5.7] + [B5.8]
+// CHECK-NEXT: 10: auto __end = __range + 10L;
+// CHECK-NEXT: 11: __range
+// CHECK-NEXT: 12: [B5.11] (ImplicitCastExpr, ArrayToPointerDecay, class A *)
+// CHECK-NEXT: 13: auto __begin = __range;
+// CHECK-NEXT: Preds (1): B6
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B0 (EXIT)]
+void test_range_for(A &b) {
+ A a[10];
+ for (auto &i : a)
+ i = b;
+}
+
+// CHECK: [B14 (ENTRY)]
+// CHECK-NEXT: Succs (1): B13
+// CHECK: [B1]
+// CHECK-NEXT: 1: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: 2: [B13.3].~A() (Implicit destructor)
+// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B12
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B2]
+// CHECK-NEXT: 1: i
+// CHECK-NEXT: 2: ++[B2.1]
+// CHECK-NEXT: 3: CFGScopeEnd(DeclStmt)
+// CHECK-NEXT: Preds (2): B3 B4
+// CHECK-NEXT: Succs (1): B12
+// CHECK: [B3]
+// CHECK-NEXT: 1: j
+// CHECK-NEXT: 2: ++[B3.1]
+// CHECK-NEXT: 3: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B4]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: CFGScopeEnd(ContinueStmt)
+// CHECK-NEXT: T: continue;
+// CHECK-NEXT: Preds (1): B5
+// CHECK-NEXT: Succs (1): B2
+// CHECK: [B5]
+// CHECK-NEXT: 1: CFGScopeEnd(CXXConstructExpr)
+// CHECK-NEXT: 2: [B10.5].~A() (Implicit destructor)
+// CHECK-NEXT: 3: i
+// CHECK-NEXT: 4: [B5.3] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 5: 3
+// CHECK-NEXT: 6: [B5.4] > [B5.5]
+// CHECK-NEXT: T: if [B5.6]
+// CHECK-NEXT: Preds (2): B8 B10
+// CHECK-NEXT: Succs (2): B4 B3
+// CHECK: [B6]
+// CHECK-NEXT: 1: CFGScopeEnd(CXXConstructExpr)
+// CHECK-NEXT: Preds (1): B7
+// CHECK-NEXT: Succs (1): B10
+// CHECK: [B7]
+// CHECK-NEXT: 1: j
+// CHECK-NEXT: 2: ++[B7.1]
+// CHECK-NEXT: 3: [B10.5].~A() (Implicit destructor)
+// CHECK-NEXT: 4: CFGScopeEnd(CompoundStmt)
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (1): B6
+// CHECK: [B8]
+// CHECK-NEXT: 1: CFGScopeBegin(IfStmt)
+// CHECK-NEXT: 2: CFGScopeEnd(BreakStmt)
+// CHECK-NEXT: T: break;
+// CHECK-NEXT: Preds (1): B9
+// CHECK-NEXT: Succs (1): B5
+// CHECK: [B9]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: int unused2;
+// CHECK-NEXT: 3: j
+// CHECK-NEXT: 4: [B9.3] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 5: 13
+// CHECK-NEXT: 6: [B9.4] > [B9.5]
+// CHECK-NEXT: T: if [B9.6]
+// CHECK-NEXT: Preds (1): B10
+// CHECK-NEXT: Succs (2): B8 B7
+// CHECK: [B10]
+// CHECK-NEXT: 1: CFGScopeBegin(CXXConstructExpr)
+// CHECK-NEXT: 2: a
+// CHECK-NEXT: 3: [B10.2] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 4: [B10.3] (CXXConstructExpr, class A)
+// CHECK-NEXT: 5: A b = a;
+// CHECK-NEXT: 6: b
+// CHECK-NEXT: 7: [B10.6] (ImplicitCastExpr, NoOp, const class A)
+// CHECK-NEXT: 8: [B10.7].operator int
+// CHECK-NEXT: 9: [B10.7]
+// CHECK-NEXT: 10: [B10.9] (ImplicitCastExpr, UserDefinedConversion, int)
+// CHECK-NEXT: 11: [B10.10] (ImplicitCastExpr, IntegralToBoolean, _Bool)
+// CHECK-NEXT: T: while [B10.11]
+// CHECK-NEXT: Preds (2): B6 B11
+// CHECK-NEXT: Succs (2): B9 B5
+// CHECK: [B11]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: int unused1;
+// CHECK-NEXT: 3: i
+// CHECK-NEXT: 4: [B11.3] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 5: int j = i;
+// CHECK-NEXT: Preds (1): B12
+// CHECK-NEXT: Succs (1): B10
+// CHECK: [B12]
+// CHECK-NEXT: 1: CFGScopeBegin(DeclStmt)
+// CHECK-NEXT: 2: i
+// CHECK-NEXT: 3: [B12.2] (ImplicitCastExpr, LValueToRValue, int)
+// CHECK-NEXT: 4: 10
+// CHECK-NEXT: 5: [B12.3] < [B12.4]
+// CHECK-NEXT: T: for (...; [B12.5]; ...)
+// CHECK-NEXT: Preds (2): B2 B13
+// CHECK-NEXT: Succs (2): B11 B1
+// CHECK: [B13]
+// CHECK-NEXT: 1: CFGScopeBegin(CompoundStmt)
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: A a;
+// CHECK-NEXT: 4: 0
+// CHECK-NEXT: 5: int i = 0;
+// CHECK-NEXT: Preds (1): B14
+// CHECK-NEXT: Succs (1): B12
+// CHECK: [B0 (EXIT)
+void test_nested_loops() {
+ A a;
+ for (int i = 0; i < 10; ++i) {
+ int unused1;
+ int j = i;
+ while (A b = a) {
+ int unused2;
+ if (j > 13)
+ break; // Break from while loop
+ ++j;
+ }
+ if (i > 3)
+ continue; // Continue for loop
+ ++j;
+ }
+}
Index: test/Analysis/analyzer-config.cpp
===================================================================
--- test/Analysis/analyzer-config.cpp
+++ test/Analysis/analyzer-config.cpp
@@ -25,6 +25,7 @@
// CHECK-NEXT: cfg-conditional-static-initializers = true
// CHECK-NEXT: cfg-implicit-dtors = true
// CHECK-NEXT: cfg-lifetime = false
+// CHECK-NEXT: cfg-scopes = false
// CHECK-NEXT: cfg-temporary-dtors = false
// CHECK-NEXT: faux-bodies = true
// CHECK-NEXT: graph-trim-interval = 1000
@@ -41,4 +42,4 @@
// CHECK-NEXT: unroll-loops = false
// CHECK-NEXT: widen-loops = false
// CHECK-NEXT: [stats]
-// CHECK-NEXT: num-entries = 23
+// CHECK-NEXT: num-entries = 24
Index: test/Analysis/analyzer-config.c
===================================================================
--- test/Analysis/analyzer-config.c
+++ test/Analysis/analyzer-config.c
@@ -14,6 +14,7 @@
// CHECK-NEXT: cfg-conditional-static-initializers = true
// CHECK-NEXT: cfg-implicit-dtors = true
// CHECK-NEXT: cfg-lifetime = false
+// CHECK-NEXT: cfg-scopes = false
// CHECK-NEXT: cfg-temporary-dtors = false
// CHECK-NEXT: faux-bodies = true
// CHECK-NEXT: graph-trim-interval = 1000
@@ -30,4 +31,4 @@
// CHECK-NEXT: unroll-loops = false
// CHECK-NEXT: widen-loops = false
// CHECK-NEXT: [stats]
-// CHECK-NEXT: num-entries = 18
+// CHECK-NEXT: num-entries = 19
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -365,6 +365,8 @@
ProcessImplicitDtor(E.castAs<CFGImplicitDtor>(), Pred);
return;
case CFGElement::LifetimeEnds:
+ case CFGElement::ScopeBegin:
+ case CFGElement::ScopeEnd:
return;
}
}
Index: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
===================================================================
--- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -183,6 +183,12 @@
/* Default = */ false);
}
+bool AnalyzerOptions::includeScopesInCFG() {
+ return getBooleanOption(IncludeScopesInCFG,
+ "cfg-scopes",
+ /* Default = */ false);
+}
+
bool AnalyzerOptions::mayInlineCXXStandardLibrary() {
return getBooleanOption(InlineCXXStandardLibrary,
"c++-stdlib-inlining",
Index: lib/StaticAnalyzer/Core/AnalysisManager.cpp
===================================================================
--- lib/StaticAnalyzer/Core/AnalysisManager.cpp
+++ lib/StaticAnalyzer/Core/AnalysisManager.cpp
@@ -27,6 +27,7 @@
/*AddInitializers=*/true,
Options.includeTemporaryDtorsInCFG(),
Options.includeLifetimeInCFG(),
+ Options.includeScopesInCFG(),
Options.shouldSynthesizeBodies(),
Options.shouldConditionalizeStaticInitializers(),
/*addCXXNewAllocator=*/true,
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -402,6 +402,8 @@
CFGBlock *SwitchTerminatedBlock;
CFGBlock *DefaultCaseBlock;
CFGBlock *TryTerminatedBlock;
+ Stmt *TerminatedLoop;
+ bool InSwitchNest;
// Current position in local scope.
LocalScope::const_iterator ScopePos;
@@ -440,9 +442,10 @@
: Context(astContext), cfg(new CFG()), // crew a new CFG
Block(nullptr), Succ(nullptr),
SwitchTerminatedBlock(nullptr), DefaultCaseBlock(nullptr),
- TryTerminatedBlock(nullptr), badCFG(false), BuildOpts(buildOpts),
- switchExclusivelyCovered(false), switchCond(nullptr),
- cachedEntry(nullptr), lastLookup(nullptr) {}
+ TryTerminatedBlock(nullptr), TerminatedLoop(nullptr), InSwitchNest(false),
+ badCFG(false), BuildOpts(buildOpts),
+ switchExclusivelyCovered(false),
+ switchCond(0), cachedEntry(nullptr), lastLookup(nullptr) {}
// buildCFG - Used by external clients to construct the CFG.
std::unique_ptr<CFG> buildCFG(const Decl *D, Stmt *Statement);
@@ -513,6 +516,8 @@
CFGBlock *VisitChildren(Stmt *S);
CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc);
+ void CreateScopeEndBlockForIfStmt(IfStmt *I);
+
/// When creating the CFG for temporary destructors, we want to mirror the
/// branch structure of the corresponding constructor calls.
/// Thus, while visiting a statement for temporary destructors, we keep a
@@ -635,6 +640,26 @@
void appendNewAllocator(CFGBlock *B, CXXNewExpr *NE) {
B->appendNewAllocator(NE, cfg->getBumpVectorContext());
}
+ bool needAddScopes() {
+ // FIXME: Support SwitchStmt
+ return !InSwitchNest && BuildOpts.AddScopes;
+ }
+ void appendScopeBegin(CFGBlock *B, const Stmt *S) {
+ if (needAddScopes())
+ B->appendScopeBegin(S, cfg->getBumpVectorContext());
+ }
+ void appendScopeEnd(CFGBlock *B, const Stmt *TriggerStmt,
+ const Stmt *TerminatedStmt) {
+ if (needAddScopes())
+ B->appendScopeEnd(TriggerStmt, TerminatedStmt,
+ cfg->getBumpVectorContext());
+ }
+ void prependScopeEnd(CFGBlock *B, const Stmt *TriggerStmt,
+ const Stmt *TerminatedStmt) {
+ if (needAddScopes())
+ B->prependScopeEnd(TriggerStmt, TerminatedStmt,
+ cfg->getBumpVectorContext());
+ }
void appendBaseDtor(CFGBlock *B, const CXXBaseSpecifier *BS) {
B->appendBaseDtor(BS, cfg->getBumpVectorContext());
}
@@ -1404,7 +1429,7 @@
}
/// addLocalScopeForStmt - Add LocalScope to local scopes tree for statement
-/// that should create implicit scope (e.g. if/else substatements).
+/// that should create implicit scope (e.g. if/else substatements).
void CFGBuilder::addLocalScopeForStmt(Stmt *S) {
if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime)
return;
@@ -1944,6 +1969,7 @@
// AST. This means that the CFG cannot be constructed.
if (BreakJumpTarget.block) {
addAutomaticObjHandling(ScopePos, BreakJumpTarget.scopePosition, B);
+ prependScopeEnd(Block, B, TerminatedLoop);
addSuccessor(Block, BreakJumpTarget.block);
} else
badCFG = true;
@@ -2072,15 +2098,26 @@
return addStmt(C->getCond());
}
+// If some block is terminated by break, continue or return don't need to emit
+// ScopeEnd right away and we leave this to corresponding visitor.
+static bool shouldDeferScopeEnd(Stmt *S) {
+ return isa<BreakStmt>(S) || isa<ContinueStmt>(S) || isa<ReturnStmt>(S);
+}
CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) {
+ SaveAndRestore<JumpTarget> save_break(BreakJumpTarget);
LocalScope::const_iterator scopeBeginPos = ScopePos;
addLocalScopeForStmt(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.
addAutomaticObjHandling(ScopePos, scopeBeginPos, C);
+
+ if (!C->body_empty() && needAddScopes() &&
+ !shouldDeferScopeEnd(*C->body_rbegin())) {
+ autoCreateBlock();
+ prependScopeEnd(Block, C, C);
}
CFGBlock *LastBlock = Block;
@@ -2096,6 +2133,12 @@
return nullptr;
}
+ if (needAddScopes() && !C->body_empty()) {
+ if (!LastBlock)
+ LastBlock = createBlock();
+ appendScopeBegin(LastBlock, C);
+ }
+
return LastBlock;
}
@@ -2289,6 +2332,12 @@
return B;
}
+void CFGBuilder::CreateScopeEndBlockForIfStmt(IfStmt *I) {
+ autoCreateBlock();
+ prependScopeEnd(Block, I, I);
+ Succ = Block;
+}
+
CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) {
// We may see an if statement in the middle of a basic block, or it may be the
// first statement we are processing. In either case, we create a new basic
@@ -2320,6 +2369,10 @@
return nullptr;
}
+ // If the IfStmt contains a condition variable, its scope to the CFG.
+ if (const DeclStmt *DS = I->getConditionVariableDeclStmt())
+ appendScopeEnd(Succ, DS, DS);
+
// Process the false branch.
CFGBlock *ElseBlock = Succ;
@@ -2332,17 +2385,22 @@
// If branch is not a compound statement create implicit scope
// and add destructors.
- if (!isa<CompoundStmt>(Else))
+ if (!isa<CompoundStmt>(Else)) {
addLocalScopeAndDtors(Else);
-
+ if (needAddScopes() && !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.
@@ -2355,8 +2413,11 @@
// If branch is not a compound statement create implicit scope
// and add destructors.
- if (!isa<CompoundStmt>(Then))
+ if (!isa<CompoundStmt>(Then)) {
addLocalScopeAndDtors(Then);
+ if (needAddScopes() && !shouldDeferScopeEnd(Then))
+ CreateScopeEndBlockForIfStmt(I);
+ }
ThenBlock = addStmt(Then);
@@ -2370,6 +2431,9 @@
if (badCFG)
return nullptr;
}
+
+ if (!isa<CompoundStmt>(Then))
+ appendScopeBegin(ThenBlock, I);
}
// Specially handle "if (expr1 || ...)" and "if (expr1 && ...)" by
@@ -2411,6 +2475,7 @@
if (const DeclStmt* DS = I->getConditionVariableDeclStmt()) {
autoCreateBlock();
LastBlock = addStmt(const_cast<DeclStmt *>(DS));
+ appendScopeBegin(LastBlock, DS);
}
}
@@ -2439,8 +2504,10 @@
// If the one of the destructors does not return, we already have the Exit
// block as a successor.
- if (!Block->hasNoReturnElement())
+ if (!Block->hasNoReturnElement()) {
+ prependScopeEnd(Block, R, R);
addSuccessor(Block, &cfg->getExit());
+ }
// Add the return statement to the block. This may create new blocks if R
// contains control-flow (short-circuit operations).
@@ -2502,6 +2569,11 @@
}
CFGBlock *CFGBuilder::VisitGotoStmt(GotoStmt *G) {
+ // FIXME: support scopes for GotoStmt
+ if (needAddScopes()) {
+ badCFG = true;
+ return Block;
+ }
// Goto is a control-flow statement. Thus we stop processing the current
// block and create a new one.
@@ -2600,9 +2672,14 @@
if (!isa<CompoundStmt>(F->getBody()))
addLocalScopeAndDtors(F->getBody());
- // Now populate the body block, and in the process create new blocks as we
- // walk the body of the loop.
- BodyBlock = addStmt(F->getBody());
+ {
+ SaveAndRestore<Stmt *> save_TerminatedLoop(TerminatedLoop);
+ TerminatedLoop = F;
+
+ // Now populate the body block, and in the process create new blocks as we
+ // walk the body of the loop.
+ BodyBlock = addStmt(F->getBody());
+ }
if (!BodyBlock) {
// In the case of "for (...;...;...);" we can have a null BodyBlock.
@@ -2613,6 +2690,11 @@
return nullptr;
}
+ if (!isa<CompoundStmt>(F->getBody())) {
+ appendScopeBegin(BodyBlock, F);
+ if (!shouldDeferScopeEnd(F->getBody()))
+ prependScopeEnd(BodyBlock, F, F);
+ }
// Because of short-circuit evaluation, the condition of the loop can span
// multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that
// evaluate the condition.
@@ -2681,7 +2763,11 @@
// statements. This block can also contain statements that precede the loop.
if (Stmt *I = F->getInit()) {
Block = createBlock();
- return addStmt(I);
+ CFGBlock *InitBlock = addStmt(I);
+ appendScopeBegin(EntryConditionBlock, I);
+ prependScopeEnd(TransitionBlock, I, I);
+ appendScopeEnd(LoopSuccessor, I, I);
+ return InitBlock;
}
// There is no loop initialization. We are thus basically a while loop.
@@ -2896,6 +2982,11 @@
CFGBlock *BodyBlock = nullptr, *TransitionBlock = nullptr;
+ // If this block contains a condition variable, add scope end to it now.
+ if (VarDecl *VD = W->getConditionVariable())
+ if (Expr *Init = VD->getInit())
+ appendScopeEnd(LoopSuccessor, Init, Init);
+
// Process the loop body.
{
assert(W->getBody());
@@ -2922,13 +3013,24 @@
if (!isa<CompoundStmt>(W->getBody()))
addLocalScopeAndDtors(W->getBody());
- // Create the body. The returned block is the entry to the loop body.
- BodyBlock = addStmt(W->getBody());
+ {
+ SaveAndRestore<Stmt *> save_TerminatedLoop(TerminatedLoop);
+ TerminatedLoop = 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, W);
+ }
}
// Because of short-circuit evaluation, the condition of the loop can span
@@ -2965,6 +3067,8 @@
autoCreateBlock();
appendStmt(Block, W->getConditionVariableDeclStmt());
EntryConditionBlock = addStmt(Init);
+ appendScopeBegin(EntryConditionBlock, Init);
+ appendScopeEnd(TransitionBlock, Init, Init);
assert(Block == EntryConditionBlock);
}
}
@@ -3104,16 +3208,26 @@
if (!isa<CompoundStmt>(D->getBody()))
addLocalScopeAndDtors(D->getBody());
- // Create the body. The returned block is the entry to the loop body.
- BodyBlock = addStmt(D->getBody());
+ {
+ SaveAndRestore<Stmt *> save_TerminatedLoop(TerminatedLoop);
+ TerminatedLoop = D;
+
+ // Create the body. The returned block is the entry to the loop body.
+ BodyBlock = addStmt(D->getBody());
+ }
if (!BodyBlock)
BodyBlock = EntryConditionBlock; // can happen for "do ; while(...)"
else if (Block) {
if (badCFG)
return nullptr;
}
+ if (!isa<CompoundStmt>(D->getBody())) {
+ appendScopeBegin(BodyBlock, D);
+ if (!shouldDeferScopeEnd(D->getBody()))
+ prependScopeEnd(BodyBlock, D, D);
+ }
// Add an intermediate block between the BodyBlock and the
// ExitConditionBlock to represent the "loop back" transition. Create an
// empty block to represent the transition block for looping back to the
@@ -3158,6 +3272,7 @@
// incomplete AST. This means the CFG cannot be constructed.
if (ContinueJumpTarget.block) {
addAutomaticObjHandling(ScopePos, ContinueJumpTarget.scopePosition, C);
+ prependScopeEnd(Block, C, TerminatedLoop);
addSuccessor(Block, ContinueJumpTarget.block);
} else
badCFG = true;
@@ -3261,24 +3376,34 @@
if (!isa<CompoundStmt>(Terminator->getBody()))
addLocalScopeAndDtors(Terminator->getBody());
- addStmt(Terminator->getBody());
- if (Block) {
- if (badCFG)
- return nullptr;
- }
+ // If the SwitchStmt contains a condition variable, end its scope after switch.
+ if (VarDecl *VD = Terminator->getConditionVariable())
+ if (Expr *Init = VD->getInit())
+ appendScopeEnd(Succ, Terminator->getConditionVariableDeclStmt(), Init);
- // If we have no "default:" case, the default transition is to the code
- // following the switch body. Moreover, take into account if all the
- // cases of a switch are covered (e.g., switching on an enum value).
- //
- // Note: We add a successor to a switch that is considered covered yet has no
- // case statements if the enumeration has no enumerators.
- bool SwitchAlwaysHasSuccessor = false;
- SwitchAlwaysHasSuccessor |= switchExclusivelyCovered;
- SwitchAlwaysHasSuccessor |= Terminator->isAllEnumCasesCovered() &&
- Terminator->getSwitchCaseList();
- addSuccessor(SwitchTerminatedBlock, DefaultCaseBlock,
- !SwitchAlwaysHasSuccessor);
+ {
+ SaveAndRestore<bool> save_InSwitchNest(InSwitchNest);
+ InSwitchNest = true;
+
+ addStmt(Terminator->getBody());
+ if (Block) {
+ if (badCFG)
+ return nullptr;
+ }
+
+ // If we have no "default:" case, the default transition is to the code
+ // following the switch body. Moreover, take into account if all the
+ // cases of a switch are covered (e.g., switching on an enum value).
+ //
+ // Note: We add a successor to a switch that is considered covered yet has
+ // no case statements if the enumeration has no enumerators.
+ bool SwitchAlwaysHasSuccessor = false;
+ SwitchAlwaysHasSuccessor |= switchExclusivelyCovered;
+ SwitchAlwaysHasSuccessor |=
+ Terminator->isAllEnumCasesCovered() && Terminator->getSwitchCaseList();
+ addSuccessor(SwitchTerminatedBlock, DefaultCaseBlock,
+ !SwitchAlwaysHasSuccessor);
+ }
// Add the terminator and condition in the switch block.
SwitchTerminatedBlock->setTerminator(Terminator);
@@ -3290,8 +3415,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);
}
}
@@ -3558,6 +3685,8 @@
LocalScope::const_iterator ContinueScopePos = ScopePos;
+ Stmt *LoopVarStmt = S->getLoopVarStmt();
+
// "for" is a control-flow statement. Thus we stop processing the current
// block.
CFGBlock *LoopSuccessor = nullptr;
@@ -3610,6 +3739,7 @@
// continue statements.
Block = nullptr;
Succ = addStmt(S->getInc());
+ prependScopeEnd(Succ, LoopVarStmt, LoopVarStmt);
if (badCFG)
return nullptr;
ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos);
@@ -3627,14 +3757,27 @@
// Add implicit scope and dtors for loop variable.
addLocalScopeAndDtors(S->getLoopVarStmt());
- // Populate a new block to contain the loop body and loop variable.
- addStmt(S->getBody());
+ CFGBlock *BodyBlock = nullptr;
+ {
+ SaveAndRestore<Stmt *> save_TerminatedLoop(TerminatedLoop);
+ TerminatedLoop = S;
+ // Populate a new block to contain the loop body and loop variable.
+ BodyBlock = addStmt(S->getBody());
+ }
if (badCFG)
return nullptr;
CFGBlock *LoopVarStmtBlock = addStmt(S->getLoopVarStmt());
if (badCFG)
return nullptr;
+ if (!isa<CompoundStmt>(S->getBody())) {
+ appendScopeBegin(BodyBlock, S);
+ if (!shouldDeferScopeEnd(S->getBody()))
+ prependScopeEnd(BodyBlock, S, S);
+ }
+
+ appendScopeBegin(BodyBlock, LoopVarStmt);
+
// This new body block is a successor to our condition block.
addSuccessor(ConditionBlock,
KnownVal.isFalse() ? nullptr : LoopVarStmtBlock);
@@ -4026,6 +4169,8 @@
case CFGElement::Initializer:
case CFGElement::NewAllocator:
case CFGElement::LifetimeEnds:
+ case CFGElement::ScopeBegin:
+ case CFGElement::ScopeEnd:
llvm_unreachable("getDestructorDecl should only be used with "
"ImplicitDtors");
case CFGElement::AutomaticObjectDtor: {
@@ -4442,6 +4587,16 @@
OS << " (Lifetime ends)\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
@@ -68,6 +68,7 @@
bool addInitializers,
bool addTemporaryDtors,
bool addLifetime,
+ bool addScopes,
bool synthesizeBodies,
bool addStaticInitBranch,
bool addCXXNewAllocator,
@@ -79,6 +80,7 @@
cfgBuildOptions.AddInitializers = addInitializers;
cfgBuildOptions.AddTemporaryDtors = addTemporaryDtors;
cfgBuildOptions.AddLifetime = addLifetime;
+ cfgBuildOptions.AddScopes = addScopes;
cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch;
cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator;
}
@@ -219,6 +221,14 @@
if (!builtCFG) {
cfg = CFG::buildCFG(D, getBody(), &D->getASTContext(), cfgBuildOptions);
+ // If we tried to generate scopes in CFG and failed for some reason
+ // (e.g. faced GotoStmt), retry with scopes disabled.
+ if (!cfg && cfgBuildOptions.AddScopes) {
+ SaveAndRestore<bool> SavedAddScopes(cfgBuildOptions.AddScopes);
+ cfgBuildOptions.AddScopes = false;
+ cfg = CFG::buildCFG(D, getBody(), &D->getASTContext(), cfgBuildOptions);
+ }
+
// Even when the cfg is not successfully built, we don't
// want to try building it again.
builtCFG = true;
Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
===================================================================
--- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
+++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
@@ -217,6 +217,9 @@
/// \sa mayInlineCXXStandardLibrary
Optional<bool> InlineCXXStandardLibrary;
+ /// \sa includeScopesInCFG
+ Optional<bool> IncludeScopesInCFG;
+
/// \sa mayInlineTemplateFunctions
Optional<bool> InlineTemplateFunctions;
@@ -418,6 +421,12 @@
/// the values "true" and "false".
bool includeLifetimeInCFG();
+ /// Returns whether or not scope information should be included in the CFG.
+ ///
+ /// This is controlled by the 'cfg-scope-info' config option, which accepts
+ /// the values "true" and "false".
+ bool includeScopesInCFG();
+
/// Returns whether or not C++ standard library functions may be considered
/// for inlining.
///
Index: include/clang/Analysis/CFG.h
===================================================================
--- include/clang/Analysis/CFG.h
+++ include/clang/Analysis/CFG.h
@@ -57,6 +57,8 @@
// main kind
Statement,
Initializer,
+ ScopeBegin,
+ ScopeEnd,
NewAllocator,
LifetimeEnds,
// dtor kind
@@ -190,6 +192,56 @@
}
};
+/// Represents beginning of a scope implicitly generated by the compiler on
+/// encountering a CompoundStmt, IfStmt, ForStmt, WhileStmt and DoStmt.
+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, IfStmt, ForStmt, WhileStmt, DoStmt body or
+/// after encountering a ReturnStmt, BreakStmt or ContinueStmt.
+class CFGScopeEnd : public CFGElement {
+public:
+ CFGScopeEnd() {}
+ CFGScopeEnd(const Stmt *TriggerStmt, const Stmt *TerminatedStmt)
+ : CFGElement(ScopeEnd, TriggerStmt), TerminatedStmt(TerminatedStmt) {}
+
+ // Get statement that triggered end of previously created scope.
+ const Stmt *getTriggerStmt() const {
+ return static_cast<Stmt*>(Data1.getPointer());
+ }
+ // Get statement that was terminated by this CFGScopeEnd. For CompoundStmt,
+ // IfStmt, ForStmt, WhileStmt and DoStmt this would be just a TriggerStmt,
+ // while for BreakStmt and ContinueStmt that would be a terminated loop stmt.
+ const Stmt *getTerminatedStmt() const {
+ return TerminatedStmt;
+ }
+
+private:
+ friend class CFGElement;
+ static bool isKind(const CFGElement &E) {
+ Kind kind = E.getKind();
+ return kind == ScopeEnd;
+ }
+ const Stmt *TerminatedStmt;
+};
+
/// CFGImplicitDtor - Represents C++ object destructor implicitly generated
/// by compiler on various occasions.
class CFGImplicitDtor : public CFGElement {
@@ -708,6 +760,21 @@
Elements.push_back(CFGNewAllocator(NE), C);
}
+ void appendScopeBegin(const Stmt *S, BumpVectorContext &C) {
+ Elements.push_back(CFGScopeBegin(S), C);
+ }
+
+ void appendScopeEnd(const Stmt *TriggerStmt, const Stmt *TerminatedStmt,
+ BumpVectorContext &C) {
+ Elements.push_back(CFGScopeEnd(TriggerStmt, TerminatedStmt), C);
+ }
+
+ void prependScopeEnd(const Stmt *TriggerStmt, const Stmt *TerminatedStmt,
+ BumpVectorContext &C) {
+ Elements.insert(Elements.rbegin(), 1,
+ CFGScopeEnd(TriggerStmt, TerminatedStmt), C);
+ }
+
void appendBaseDtor(const CXXBaseSpecifier *BS, BumpVectorContext &C) {
Elements.push_back(CFGBaseDtor(BS), C);
}
@@ -795,6 +862,7 @@
bool AddImplicitDtors;
bool AddLifetime;
bool AddTemporaryDtors;
+ bool AddScopes;
bool AddStaticInitBranches;
bool AddCXXNewAllocator;
bool AddCXXDefaultInitExprInCtors;
@@ -819,7 +887,8 @@
AddEHEdges(false),
AddInitializers(false), AddImplicitDtors(false),
AddLifetime(false),
- AddTemporaryDtors(false), AddStaticInitBranches(false),
+ AddTemporaryDtors(false), AddScopes(false),
+ AddStaticInitBranches(false),
AddCXXNewAllocator(false), AddCXXDefaultInitExprInCtors(false) {}
};
Index: include/clang/Analysis/AnalysisContext.h
===================================================================
--- include/clang/Analysis/AnalysisContext.h
+++ include/clang/Analysis/AnalysisContext.h
@@ -427,6 +427,7 @@
bool addInitializers = false,
bool addTemporaryDtors = false,
bool addLifetime = false,
+ bool addScopes = false,
bool synthesizeBodies = false,
bool addStaticInitBranches = false,
bool addCXXNewAllocator = true,
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits