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
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to