m.ostapenko updated this revision to Diff 103719.
m.ostapenko set the repository for this revision to rL LLVM.
m.ostapenko added a project: clang.
m.ostapenko added a comment.

So, updating the diff. This is still a very experimental version and any 
feedback would be greatly appreciated.
Current patch should support basic {If, While, For, Compound, Switch}Stmts as 
well as their interactions with {Break, Continue, Return}Stmts.
GotoStmt and CXXForRangeStmt are not supported at this moment.


Repository:
  rL LLVM

https://reviews.llvm.org/D16403

Files:
  include/clang/Analysis/AnalysisContext.h
  include/clang/Analysis/CFG.h
  include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
  lib/Analysis/AnalysisDeclContext.cpp
  lib/Analysis/CFG.cpp
  lib/StaticAnalyzer/Core/AnalysisManager.cpp
  lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
  test/Analysis/analyzer-config.c
  test/Analysis/analyzer-config.cpp
  test/Analysis/scopes-cfg-output.cpp

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

Reply via email to