Version 2.13.0-203.0.dev

Merge commit '0873d4eb824506b5e7bac157b103b2f697b075b6' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
index 4e018e2..76c3ebc 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
@@ -443,11 +443,11 @@
         x.expr.as_('int').stmt,
         checkPromoted(x, 'int'),
         getSsaNodes((nodes) => ssaBeforeLoop = nodes[x]!),
-        branchTarget((t) => do_([
-              getSsaNodes((nodes) => expect(nodes[x], isNot(ssaBeforeLoop))),
-              checkNotPromoted(x),
-              x.write(expr('Null')).stmt,
-            ], expr('bool'))),
+        do_([
+          getSsaNodes((nodes) => expect(nodes[x], isNot(ssaBeforeLoop))),
+          checkNotPromoted(x),
+          x.write(expr('Null')).stmt,
+        ], expr('bool')),
       ]);
     });
 
@@ -473,19 +473,19 @@
       var x = Var('x', 'int?');
       h.run([
         declare(x, initialized: true),
-        branchTarget((t) => do_(
-                [
-                  if_(x.expr.notEq(nullLiteral), [
-                    continue_(t),
-                  ]),
-                  return_(),
-                  checkReachable(false),
-                  checkNotPromoted(x),
-                ],
-                block([
-                  checkReachable(true),
-                  checkPromoted(x, 'int'),
-                ]).thenExpr(expr('bool')))),
+        do_(
+            [
+              if_(x.expr.notEq(nullLiteral), [
+                continue_(),
+              ]),
+              return_(),
+              checkReachable(false),
+              checkNotPromoted(x),
+            ],
+            block([
+              checkReachable(true),
+              checkPromoted(x, 'int'),
+            ]).thenExpr(expr('bool'))),
       ]);
     });
 
@@ -494,8 +494,7 @@
       var x = Var('x', 'int?');
       h.run([
         declare(x, initialized: true),
-        branchTarget((t) =>
-            do_([], checkNotPromoted(x).thenExpr(x.expr.eq(nullLiteral)))),
+        do_([], checkNotPromoted(x).thenExpr(x.expr.eq(nullLiteral))),
         checkPromoted(x, 'int'),
       ]);
     });
@@ -697,23 +696,23 @@
         declare(x, initialized: true),
         declare(y, initialized: true),
         declare(z, initialized: true),
-        branchTarget((t) => for_(
-                null,
-                expr('bool'),
-                block([
-                  checkPromoted(x, 'int'),
-                  checkNotPromoted(y),
-                  checkNotPromoted(z),
-                ]).thenExpr(expr('Null')),
-                [
-                  if_(expr('bool'), [
-                    x.expr.as_('int').stmt,
-                    y.expr.as_('int').stmt,
-                    continue_(t),
-                  ]),
-                  x.expr.as_('int').stmt,
-                  z.expr.as_('int').stmt,
-                ])),
+        for_(
+            null,
+            expr('bool'),
+            block([
+              checkPromoted(x, 'int'),
+              checkNotPromoted(y),
+              checkNotPromoted(z),
+            ]).thenExpr(expr('Null')),
+            [
+              if_(expr('bool'), [
+                x.expr.as_('int').stmt,
+                y.expr.as_('int').stmt,
+                continue_(),
+              ]),
+              x.expr.as_('int').stmt,
+              z.expr.as_('int').stmt,
+            ]),
       ]);
     });
 
@@ -729,14 +728,13 @@
         declare(x, initialized: true),
         declare(y, initialized: true),
         declare(z, initialized: true),
-        branchTarget((t) => for_(
-                null, x.expr.eq(nullLiteral).or(z.expr.eq(nullLiteral)), null, [
-              if_(expr('bool'), [
-                x.expr.as_('int').stmt,
-                y.expr.as_('int').stmt,
-                break_(t),
-              ]),
-            ])),
+        for_(null, x.expr.eq(nullLiteral).or(z.expr.eq(nullLiteral)), null, [
+          if_(expr('bool'), [
+            x.expr.as_('int').stmt,
+            y.expr.as_('int').stmt,
+            break_(),
+          ]),
+        ]),
         checkPromoted(x, 'int'),
         checkNotPromoted(y),
         checkNotPromoted(z),
@@ -752,14 +750,14 @@
       h.run([
         declare(x, initialized: true),
         declare(y, initialized: true),
-        branchTarget((t) => for_(null, expr('bool'), null, [
-              x.write(expr('int?')).stmt,
-              if_(expr('bool'), [break_(t)]),
-              getSsaNodes((nodes) {
-                xSsaInsideLoop = nodes[x]!;
-                ySsaInsideLoop = nodes[y]!;
-              }),
-            ])),
+        for_(null, expr('bool'), null, [
+          x.write(expr('int?')).stmt,
+          if_(expr('bool'), [break_()]),
+          getSsaNodes((nodes) {
+            xSsaInsideLoop = nodes[x]!;
+            ySsaInsideLoop = nodes[y]!;
+          }),
+        ]),
         getSsaNodes((nodes) {
           // x's Ssa should have been changed because of the join at the end of
           // of the loop.  y's should not, since it retains the value it had
@@ -781,15 +779,15 @@
       h.run([
         declare(x, initialized: true),
         declare(y, initialized: true),
-        branchTarget((t) => for_(null, expr('bool'), null, [
-              x.write(expr('int?')).stmt,
-              if_(expr('bool'), [break_(t)]),
-              if_(x.expr.is_('int'), []),
-              getSsaNodes((nodes) {
-                xSsaInsideLoop = nodes[x]!;
-                ySsaInsideLoop = nodes[y]!;
-              }),
-            ])),
+        for_(null, expr('bool'), null, [
+          x.write(expr('int?')).stmt,
+          if_(expr('bool'), [break_()]),
+          if_(x.expr.is_('int'), []),
+          getSsaNodes((nodes) {
+            xSsaInsideLoop = nodes[x]!;
+            ySsaInsideLoop = nodes[y]!;
+          }),
+        ]),
         getSsaNodes((nodes) {
           // x's Ssa should have been changed because of the join at the end of
           // the loop.  y's should not, since it retains the value it had prior
@@ -867,12 +865,12 @@
       h.run([
         declare(x, initialized: false),
         checkUnassigned(x, true),
-        branchTarget((t) => forEachWithNonVariable(expr('List<int>'), [
-              // Since a write to x occurs somewhere in the loop, x should no
-              // longer be considered unassigned.
-              checkUnassigned(x, false),
-              break_(t), x.write(expr('int')).stmt,
-            ])),
+        forEachWithNonVariable(expr('List<int>'), [
+          // Since a write to x occurs somewhere in the loop, x should no
+          // longer be considered unassigned.
+          checkUnassigned(x, false),
+          break_(), x.write(expr('int')).stmt,
+        ]),
         // Even though the write to x is unreachable (since it occurs after a
         // break), x should still be considered "possibly assigned" because of
         // the conservative join done at the top of the loop.
@@ -1050,15 +1048,15 @@
     test('handleBreak handles deep nesting', () {
       var h = Harness();
       h.run([
-        branchTarget((t) => while_(booleanLiteral(true), [
-              if_(expr('bool'), [
-                if_(expr('bool'), [
-                  break_(t),
-                ]),
-              ]),
-              return_(),
-              checkReachable(false),
-            ])),
+        while_(booleanLiteral(true), [
+          if_(expr('bool'), [
+            if_(expr('bool'), [
+              break_(),
+            ]),
+          ]),
+          return_(),
+          checkReachable(false),
+        ]),
         checkReachable(true),
       ]);
     });
@@ -1066,16 +1064,16 @@
     test('handleBreak handles mixed nesting', () {
       var h = Harness();
       h.run([
-        branchTarget((t) => while_(booleanLiteral(true), [
-              if_(expr('bool'), [
-                if_(expr('bool'), [
-                  break_(t),
-                ]),
-                break_(t),
-              ]),
-              break_(t),
-              checkReachable(false),
-            ])),
+        while_(booleanLiteral(true), [
+          if_(expr('bool'), [
+            if_(expr('bool'), [
+              break_(),
+            ]),
+            break_(),
+          ]),
+          break_(),
+          checkReachable(false),
+        ]),
         checkReachable(true),
       ]);
     });
@@ -1083,15 +1081,15 @@
     test('handleContinue handles deep nesting', () {
       var h = Harness();
       h.run([
-        branchTarget((t) => do_([
-              if_(expr('bool'), [
-                if_(expr('bool'), [
-                  continue_(t),
-                ]),
-              ]),
-              return_(),
-              checkReachable(false),
-            ], checkReachable(true).thenExpr(booleanLiteral(true)))),
+        do_([
+          if_(expr('bool'), [
+            if_(expr('bool'), [
+              continue_(),
+            ]),
+          ]),
+          return_(),
+          checkReachable(false),
+        ], checkReachable(true).thenExpr(booleanLiteral(true))),
         checkReachable(false),
       ]);
     });
@@ -1099,16 +1097,16 @@
     test('handleContinue handles mixed nesting', () {
       var h = Harness();
       h.run([
-        branchTarget((t) => do_([
-              if_(expr('bool'), [
-                if_(expr('bool'), [
-                  continue_(t),
-                ]),
-                continue_(t),
-              ]),
-              continue_(t),
-              checkReachable(false),
-            ], checkReachable(true).thenExpr(booleanLiteral(true)))),
+        do_([
+          if_(expr('bool'), [
+            if_(expr('bool'), [
+              continue_(),
+            ]),
+            continue_(),
+          ]),
+          continue_(),
+          checkReachable(false),
+        ], checkReachable(true).thenExpr(booleanLiteral(true))),
         checkReachable(false),
       ]);
     });
@@ -1530,7 +1528,7 @@
       h.run([
         declare(x, initialized: true),
         if_(x.expr.isNot('int'), [
-          labeled(return_()),
+          labeled((_) => return_()),
         ]),
         checkPromoted(x, 'int'),
       ]);
@@ -1542,12 +1540,12 @@
       h.run([
         declare(x, initialized: true),
         if_(x.expr.isNot('int'), [
-          branchTarget((t) => labeled(block([
+          labeled((t) => block([
                 if_(expr('bool'), [
                   break_(t),
                 ]),
                 return_(),
-              ]))),
+              ])),
         ]),
         checkNotPromoted(x),
       ]);
@@ -1926,16 +1924,16 @@
         declare(z, initialized: true),
         y.expr.as_('int').stmt,
         z.expr.as_('int').stmt,
-        branchTarget((t) => switch_(
+        switch_(
             expr('Null'),
             [
               case_([
                 x.expr.as_('int').stmt,
                 y.write(expr('int?')).stmt,
-                break_(t),
+                break_(),
               ]),
             ],
-            isExhaustive: false)),
+            isExhaustive: false),
         checkNotPromoted(x),
         checkNotPromoted(y),
         checkPromoted(z, 'int'),
@@ -1956,23 +1954,23 @@
         x.expr.as_('int').stmt,
         y.expr.as_('int').stmt,
         z.expr.as_('int').stmt,
-        branchTarget((t) => switch_(
+        switch_(
             expr('Null'),
             [
               case_([
                 w.expr.as_('int').stmt,
                 y.expr.as_('int').stmt,
                 x.write(expr('int?')).stmt,
-                break_(t),
+                break_(),
               ]),
               case_([
                 w.expr.as_('int').stmt,
                 x.expr.as_('int').stmt,
                 y.write(expr('int?')).stmt,
-                break_(t),
+                break_(),
               ]),
             ],
-            isExhaustive: true)),
+            isExhaustive: true),
         checkPromoted(w, 'int'),
         checkNotPromoted(x),
         checkNotPromoted(y),
@@ -1985,16 +1983,16 @@
       var x = Var('x', 'int?');
       h.run([
         declare(x, initialized: true),
-        branchTarget((t) => switch_(
+        switch_(
             expr('Null'),
             [
               case_([
                 x.expr.as_('int').stmt,
-                break_(t),
+                break_(),
               ]),
               case_([]),
             ],
-            isExhaustive: true)),
+            isExhaustive: true),
         checkNotPromoted(x),
       ]);
     });
@@ -2825,14 +2823,13 @@
         declare(x, initialized: true),
         declare(y, initialized: true),
         declare(z, initialized: true),
-        branchTarget(
-            (t) => while_(x.expr.eq(nullLiteral).or(z.expr.eq(nullLiteral)), [
-                  if_(expr('bool'), [
-                    x.expr.as_('int').stmt,
-                    y.expr.as_('int').stmt,
-                    break_(t),
-                  ]),
-                ])),
+        while_(x.expr.eq(nullLiteral).or(z.expr.eq(nullLiteral)), [
+          if_(expr('bool'), [
+            x.expr.as_('int').stmt,
+            y.expr.as_('int').stmt,
+            break_(),
+          ]),
+        ]),
         checkPromoted(x, 'int'),
         checkNotPromoted(y),
         checkNotPromoted(z),
@@ -2848,14 +2845,14 @@
       h.run([
         declare(x, initialized: true),
         declare(y, initialized: true),
-        branchTarget((t) => while_(expr('bool'), [
-              x.write(expr('int?')).stmt,
-              if_(expr('bool'), [break_(t)]),
-              getSsaNodes((nodes) {
-                xSsaInsideLoop = nodes[x]!;
-                ySsaInsideLoop = nodes[y]!;
-              }),
-            ])),
+        while_(expr('bool'), [
+          x.write(expr('int?')).stmt,
+          if_(expr('bool'), [break_()]),
+          getSsaNodes((nodes) {
+            xSsaInsideLoop = nodes[x]!;
+            ySsaInsideLoop = nodes[y]!;
+          }),
+        ]),
         getSsaNodes((nodes) {
           // x's Ssa should have been changed because of the join at the end of
           // the loop.  y's should not, since it retains the value it had prior
@@ -2877,15 +2874,15 @@
       h.run([
         declare(x, initialized: true),
         declare(y, initialized: true),
-        branchTarget((t) => while_(expr('bool'), [
-              x.write(expr('int?')).stmt,
-              if_(expr('bool'), [break_(t)]),
-              if_(x.expr.is_('int'), []),
-              getSsaNodes((nodes) {
-                xSsaInsideLoop = nodes[x]!;
-                ySsaInsideLoop = nodes[y]!;
-              }),
-            ])),
+        while_(expr('bool'), [
+          x.write(expr('int?')).stmt,
+          if_(expr('bool'), [break_()]),
+          if_(x.expr.is_('int'), []),
+          getSsaNodes((nodes) {
+            xSsaInsideLoop = nodes[x]!;
+            ySsaInsideLoop = nodes[y]!;
+          }),
+        ]),
         getSsaNodes((nodes) {
           // x's Ssa should have been changed because of the join at the end of
           // the loop.  y's should not, since it retains the value it had prior
diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart
index 9e1ca0a..5e41049 100644
--- a/pkg/_fe_analyzer_shared/test/mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart
@@ -22,19 +22,7 @@
 
 Expression booleanLiteral(bool value) => _BooleanLiteral(value);
 
-/// Wrapper allowing creation of a statement that can be used as the target of
-/// `break` or `continue` statements.  [callback] will be invoked to create the
-/// statement, and it will be passed a [BranchTargetPlaceholder] that can be
-/// passed to [break_] or [continue_].
-Statement branchTarget(Statement Function(BranchTargetPlaceholder) callback) {
-  var branchTargetPlaceholder = BranchTargetPlaceholder._();
-  var stmt = callback(branchTargetPlaceholder);
-  branchTargetPlaceholder._target = stmt;
-  return stmt;
-}
-
-Statement break_(BranchTargetPlaceholder branchTargetPlaceholder) =>
-    new _Break(branchTargetPlaceholder);
+Statement break_([LabeledStatement? target]) => new _Break(target);
 
 SwitchCase case_(List<Statement> body, {bool hasLabel = false}) =>
     SwitchCase._(hasLabel, new _Block(body));
@@ -64,8 +52,7 @@
 Statement checkUnassigned(Var variable, bool expectedUnassignedState) =>
     new _CheckUnassigned(variable, expectedUnassignedState);
 
-Statement continue_(BranchTargetPlaceholder branchTargetPlaceholder) =>
-    new _Continue(branchTargetPlaceholder);
+Statement continue_() => new _Continue();
 
 Statement declare(Var variable,
         {required bool initialized,
@@ -148,7 +135,11 @@
         void Function(Map<Type, NonPromotionReason>) callback) =>
     new _WhyNotPromoted_ImplicitThis(Type(staticType), callback);
 
-Statement labeled(Statement body) => new _LabeledStatement(body);
+Statement labeled(Statement Function(LabeledStatement) callback) {
+  var labeledStatement = LabeledStatement._();
+  labeledStatement._body = callback(labeledStatement);
+  return labeledStatement;
+}
 
 Statement localFunction(List<Statement> body) => _LocalFunction(block(body));
 
@@ -169,14 +160,6 @@
 Statement while_(Expression condition, List<Statement> body) =>
     new _While(condition, block(body));
 
-/// Placeholder used by [branchTarget] to tie `break` and `continue` statements
-/// to their branch targets.
-class BranchTargetPlaceholder {
-  late Statement _target;
-
-  BranchTargetPlaceholder._();
-}
-
 /// Representation of an expression in the pseudo-Dart language used for flow
 /// analysis testing.  Methods in this class may be used to create more complex
 /// expressions based on this one.
@@ -398,6 +381,10 @@
 
   Map<String, Map<String, String>> _promotionExceptions = {};
 
+  Statement? _currentBreakTarget;
+
+  Statement? _currentContinueTarget;
+
   Harness({this.legacy = false, String? thisType})
       : thisType = thisType == null ? null : Type(thisType);
 
@@ -544,6 +531,39 @@
           'TODO(paulberry): least upper bound of $type1 and $type2');
     }
   }
+
+  void _visitLoopBody(Statement loop, Statement body,
+      FlowAnalysis<Node, Statement, Expression, Var, Type> flow) {
+    var previousBreakTarget = _currentBreakTarget;
+    var previousContinueTarget = _currentContinueTarget;
+    _currentBreakTarget = loop;
+    _currentContinueTarget = loop;
+    body._visit(this, flow);
+    _currentBreakTarget = previousBreakTarget;
+    _currentContinueTarget = previousContinueTarget;
+  }
+}
+
+class LabeledStatement extends Statement {
+  late final Statement _body;
+
+  LabeledStatement._() : super._();
+
+  @override
+  String toString() => 'labeled: $_body';
+
+  @override
+  void _preVisit(AssignedVariables<Node, Var> assignedVariables) {
+    _body._preVisit(assignedVariables);
+  }
+
+  @override
+  void _visit(
+      Harness h, FlowAnalysis<Node, Statement, Expression, Var, Type> flow) {
+    flow.labeledStatement_begin(this);
+    _body._visit(h, flow);
+    flow.labeledStatement_end();
+  }
 }
 
 /// Representation of an expression that can appear on the left hand side of an
@@ -752,9 +772,9 @@
 }
 
 class _Break extends Statement {
-  final BranchTargetPlaceholder branchTargetPlaceholder;
+  final LabeledStatement? target;
 
-  _Break(this.branchTargetPlaceholder) : super._();
+  _Break(this.target) : super._();
 
   @override
   String toString() => 'break;';
@@ -765,9 +785,7 @@
   @override
   void _visit(
       Harness h, FlowAnalysis<Node, Statement, Expression, Var, Type> flow) {
-    // ignore: unnecessary_null_comparison
-    assert(branchTargetPlaceholder._target != null);
-    flow.handleBreak(branchTargetPlaceholder._target);
+    flow.handleBreak(target ?? h._currentBreakTarget!);
   }
 }
 
@@ -925,9 +943,7 @@
 }
 
 class _Continue extends Statement {
-  final BranchTargetPlaceholder branchTargetPlaceholder;
-
-  _Continue(this.branchTargetPlaceholder) : super._();
+  _Continue() : super._();
 
   @override
   String toString() => 'continue;';
@@ -938,9 +954,7 @@
   @override
   void _visit(
       Harness h, FlowAnalysis<Node, Statement, Expression, Var, Type> flow) {
-    // ignore: unnecessary_null_comparison
-    assert(branchTargetPlaceholder._target != null);
-    flow.handleContinue(branchTargetPlaceholder._target);
+    flow.handleContinue(h._currentContinueTarget!);
   }
 }
 
@@ -1002,7 +1016,7 @@
   void _visit(
       Harness h, FlowAnalysis<Node, Statement, Expression, Var, Type> flow) {
     flow.doStatement_bodyBegin(this);
-    body._visit(h, flow);
+    h._visitLoopBody(this, body, flow);
     flow.doStatement_conditionBegin();
     condition._visit(h, flow);
     flow.doStatement_end(condition);
@@ -1104,7 +1118,7 @@
     flow.for_conditionBegin(this);
     condition?._visit(h, flow);
     flow.for_bodyBegin(forCollection ? null : this, condition);
-    body._visit(h, flow);
+    h._visitLoopBody(this, body, flow);
     flow.for_updaterBegin();
     updater?._visit(h, flow);
     flow.for_end();
@@ -1157,7 +1171,7 @@
     if (variable != null && !declaresVariable) {
       flow.write(this, variable, iteratedType, null);
     }
-    body._visit(h, flow);
+    h._visitLoopBody(this, body, flow);
     flow.forEach_end();
   }
 }
@@ -1284,28 +1298,6 @@
   }
 }
 
-class _LabeledStatement extends Statement {
-  final Statement body;
-
-  _LabeledStatement(this.body) : super._();
-
-  @override
-  String toString() => 'labeled: $body';
-
-  @override
-  void _preVisit(AssignedVariables<Node, Var> assignedVariables) {
-    body._preVisit(assignedVariables);
-  }
-
-  @override
-  void _visit(
-      Harness h, FlowAnalysis<Node, Statement, Expression, Var, Type> flow) {
-    flow.labeledStatement_begin(this);
-    body._visit(h, flow);
-    flow.labeledStatement_end();
-  }
-}
-
 class _LocalFunction extends Statement {
   final Statement body;
 
@@ -1581,11 +1573,14 @@
     expression._visit(h, flow);
     flow.switchStatement_expressionEnd(this);
     var oldSwitch = h._currentSwitch;
+    var previousBreakTarget = h._currentBreakTarget;
     h._currentSwitch = this;
+    h._currentBreakTarget = this;
     for (var case_ in cases) {
       case_._visit(h, flow);
     }
     h._currentSwitch = oldSwitch;
+    h._currentBreakTarget = previousBreakTarget;
     flow.switchStatement_end(isExhaustive);
   }
 }
@@ -1781,7 +1776,7 @@
     flow.whileStatement_conditionBegin(this);
     condition._visit(h, flow);
     flow.whileStatement_bodyBegin(this, condition);
-    body._visit(h, flow);
+    h._visitLoopBody(this, body, flow);
     flow.whileStatement_end();
   }
 }
diff --git a/tools/VERSION b/tools/VERSION
index df1501b..5d714ab 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 202
+PRERELEASE 203
 PRERELEASE_PATCH 0
\ No newline at end of file