Version 2.13.0-197.0.dev

Merge commit '778c2361c38ddbbfd787ad9619f1380b18c5fe29' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
index 9bae526..e4c7261 100644
--- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
@@ -300,16 +300,16 @@
 /// Non-promotion reason describing the situation where a variable was not
 /// promoted due to an explicit write to the variable appearing somewhere in the
 /// source code.
-class DemoteViaExplicitWrite<Variable extends Object, Expression extends Object>
+class DemoteViaExplicitWrite<Variable extends Object>
     extends NonPromotionReason {
   /// The local variable that was not promoted.
   final Variable variable;
 
-  /// The expression that wrote to the variable; this corresponds to an
-  /// expression that was passed to [FlowAnalysis.write].
-  final Expression writeExpression;
+  /// The node that wrote to the variable; this corresponds to a node that was
+  /// passed to [FlowAnalysis.write].
+  final Object node;
 
-  DemoteViaExplicitWrite(this.variable, this.writeExpression);
+  DemoteViaExplicitWrite(this.variable, this.node);
 
   @override
   String get documentationLink => 'http://dart.dev/go/non-promo-write';
@@ -318,46 +318,14 @@
   String get shortName => 'explicitWrite';
 
   @override
-  R accept<R, Node extends Object, Expression extends Object,
-              Variable extends Object, Type extends Object>(
-          NonPromotionReasonVisitor<R, Node, Expression, Variable, Type>
-              visitor) =>
+  R accept<R, Node extends Object, Variable extends Object,
+              Type extends Object>(
+          NonPromotionReasonVisitor<R, Node, Variable, Type> visitor) =>
       visitor.visitDemoteViaExplicitWrite(
-          this as DemoteViaExplicitWrite<Variable, Expression>);
+          this as DemoteViaExplicitWrite<Variable>);
 
   @override
-  String toString() => 'DemoteViaExplicitWrite($writeExpression)';
-}
-
-/// Non-promotion reason describing the situation where a variable was not
-/// promoted due to the variable appearing before the word `in` in a "for each"
-/// statement or a "for each" collection element.
-class DemoteViaForEachVariableWrite<Variable extends Object,
-    Node extends Object> extends NonPromotionReason {
-  /// The local variable that was not promoted.
-  final Variable variable;
-
-  /// The "for each" statement or collection element that wrote to the variable.
-  final Node node;
-
-  DemoteViaForEachVariableWrite(this.variable, this.node);
-
-  @override
-  String get documentationLink => 'http://dart.dev/go/non-promo-write';
-
-  @override
-  String get shortName => 'explicitWrite';
-
-  @override
-  R accept<R, Node extends Object, Expression extends Object,
-              Variable extends Object, Type extends Object>(
-          NonPromotionReasonVisitor<R, Node, Expression, Variable, Type>
-              visitor) =>
-      visitor.visitDemoteViaForEachVariableWrite(
-          this as DemoteViaForEachVariableWrite<Variable, Node>);
-
-  @override
-  String toString() => 'DemoteViaForEachVariableWrite($node)';
+  String toString() => 'DemoteViaExplicitWrite($node)';
 }
 
 /// A collection of flow models representing the possible outcomes of evaluating
@@ -565,11 +533,8 @@
   /// - Call [forEach_end].
   ///
   /// [node] should be the same node that was passed to
-  /// [AssignedVariables.endNode] for the for statement.  [loopVariable] should
-  /// be the variable assigned to by the loop (if it is promotable, otherwise
-  /// null).  [writtenType] should be the type written to that variable (i.e.
-  /// if the loop iterates over `List<Foo>`, it should be `Foo`).
-  void forEach_bodyBegin(Node node, Variable? loopVariable, Type writtenType);
+  /// [AssignedVariables.endNode] for the for statement.
+  void forEach_bodyBegin(Node node);
 
   /// Call this method just before visiting the body of a "for-in" statement or
   /// collection element.  See [forEach_bodyBegin] for details.
@@ -982,7 +947,7 @@
 
   /// Register write of the given [variable] in the current state.
   /// [writtenType] should be the type of the value that was written.
-  /// [expression] should be the whole expression performing the write.
+  /// [node] should be the syntactic construct performing the write.
   /// [writtenExpression] should be the expression that was written, or `null`
   /// if the expression that was written is not directly represented in the
   /// source code (this happens, for example, with compound assignments and with
@@ -991,7 +956,7 @@
   /// This should also be used for the implicit write to a non-final variable in
   /// its initializer, to ensure that the type is promoted to non-nullable if
   /// necessary; in this case, [viaInitializer] should be `true`.
-  void write(Expression expression, Variable variable, Type writtenType,
+  void write(Node node, Variable variable, Type writtenType,
       Expression? writtenExpression);
 
   /// Prints out a summary of the current state of flow analysis, intended for
@@ -1162,9 +1127,9 @@
   }
 
   @override
-  void forEach_bodyBegin(Node node, Variable? loopVariable, Type writtenType) {
-    return _wrap('forEach_bodyBegin($node, $loopVariable, $writtenType)',
-        () => _wrapped.forEach_bodyBegin(node, loopVariable, writtenType));
+  void forEach_bodyBegin(Node node) {
+    return _wrap(
+        'forEach_bodyBegin($node)', () => _wrapped.forEach_bodyBegin(node));
   }
 
   @override
@@ -1513,12 +1478,10 @@
   }
 
   @override
-  void write(Expression expression, Variable variable, Type writtenType,
+  void write(Node node, Variable variable, Type writtenType,
       Expression? writtenExpression) {
-    _wrap(
-        'write($expression, $variable, $writtenType, $writtenExpression)',
-        () => _wrapped.write(
-            expression, variable, writtenType, writtenExpression));
+    _wrap('write($node, $variable, $writtenType, $writtenExpression)',
+        () => _wrapped.write(node, variable, writtenType, writtenExpression));
   }
 
   @override
@@ -2348,21 +2311,17 @@
   String get shortName;
 
   /// Implementation of the visitor pattern for non-promotion reasons.
-  R accept<R, Node extends Object, Expression extends Object,
-          Variable extends Object, Type extends Object>(
-      NonPromotionReasonVisitor<R, Node, Expression, Variable, Type> visitor);
+  R accept<R, Node extends Object, Variable extends Object,
+          Type extends Object>(
+      NonPromotionReasonVisitor<R, Node, Variable, Type> visitor);
 }
 
 /// Implementation of the visitor pattern for non-promotion reasons.
 abstract class NonPromotionReasonVisitor<R, Node extends Object,
-    Expression extends Object, Variable extends Object, Type extends Object> {
+    Variable extends Object, Type extends Object> {
   NonPromotionReasonVisitor._() : assert(false, 'Do not extend this class');
 
-  R visitDemoteViaExplicitWrite(
-      DemoteViaExplicitWrite<Variable, Expression> reason);
-
-  R visitDemoteViaForEachVariableWrite(
-      DemoteViaForEachVariableWrite<Variable, Node> reason);
+  R visitDemoteViaExplicitWrite(DemoteViaExplicitWrite<Variable> reason);
 
   R visitPropertyNotPromoted(PropertyNotPromoted<Type> reason);
 
@@ -2389,10 +2348,9 @@
   String get shortName => 'propertyNotPromoted';
 
   @override
-  R accept<R, Node extends Object, Expression extends Object,
-              Variable extends Object, Type extends Object>(
-          NonPromotionReasonVisitor<R, Node, Expression, Variable, Type>
-              visitor) =>
+  R accept<R, Node extends Object, Variable extends Object,
+              Type extends Object>(
+          NonPromotionReasonVisitor<R, Node, Variable, Type> visitor) =>
       visitor.visitPropertyNotPromoted(this as PropertyNotPromoted<Type>);
 }
 
@@ -2627,10 +2585,9 @@
   String get shortName => 'thisNotPromoted';
 
   @override
-  R accept<R, Node extends Object, Expression extends Object,
-              Variable extends Object, Type extends Object>(
-          NonPromotionReasonVisitor<R, Node, Expression, Variable, Type>
-              visitor) =>
+  R accept<R, Node extends Object, Variable extends Object,
+              Type extends Object>(
+          NonPromotionReasonVisitor<R, Node, Variable, Type> visitor) =>
       visitor.visitThisNotPromoted(this);
 }
 
@@ -3680,7 +3637,7 @@
   }
 
   @override
-  void forEach_bodyBegin(Node node, Variable? loopVariable, Type writtenType) {
+  void forEach_bodyBegin(Node node) {
     AssignedVariablesNodeInfo<Variable> info =
         _assignedVariables._getInfoForNode(node);
     _current = _current.conservativeJoin(info._written, info._captured).split();
@@ -3688,14 +3645,6 @@
         new _SimpleStatementContext<Variable, Type>(
             _current.reachable.parent!, _current);
     _stack.add(context);
-    if (loopVariable != null) {
-      _current = _current.write(
-          new DemoteViaForEachVariableWrite<Variable, Node>(loopVariable, node),
-          loopVariable,
-          writtenType,
-          new SsaNode<Variable, Type>(null),
-          typeOperations);
-    }
   }
 
   @override
@@ -4220,7 +4169,7 @@
   }
 
   @override
-  void write(Expression expression, Variable variable, Type writtenType,
+  void write(Node node, Variable variable, Type writtenType,
       Expression? writtenExpression) {
     ExpressionInfo<Variable, Type>? expressionInfo = writtenExpression == null
         ? null
@@ -4228,7 +4177,7 @@
     SsaNode<Variable, Type> newSsaNode = new SsaNode<Variable, Type>(
         expressionInfo is _TrivialExpressionInfo ? null : expressionInfo);
     _current = _current.write(
-        new DemoteViaExplicitWrite<Variable, Expression>(variable, expression),
+        new DemoteViaExplicitWrite<Variable>(variable, node),
         variable,
         writtenType,
         newSsaNode,
@@ -4509,8 +4458,7 @@
   void for_updaterBegin() {}
 
   @override
-  void forEach_bodyBegin(
-      Node node, Variable? loopVariable, Type? writtenType) {}
+  void forEach_bodyBegin(Node node) {}
 
   @override
   void forEach_end() {}
@@ -4815,7 +4763,7 @@
   }
 
   @override
-  void write(Expression expression, Variable variable, Type writtenType,
+  void write(Node node, Variable variable, Type writtenType,
       Expression? writtenExpression) {
     assert(
         _assignedVariables._anywhere._written.contains(variable),
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 c48efaf..4e018e2 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
@@ -5500,8 +5500,8 @@
         x.expr.whyNotPromoted((reasons) {
           expect(reasons.keys, unorderedEquals([Type('int')]));
           var nonPromotionReason =
-              reasons.values.single as DemoteViaExplicitWrite<Var, Expression>;
-          expect(nonPromotionReason.writeExpression, same(writeExpression));
+              reasons.values.single as DemoteViaExplicitWrite<Var>;
+          expect(nonPromotionReason.node, same(writeExpression));
         }).stmt,
       ]);
     });
@@ -5523,13 +5523,9 @@
         checkNotPromoted(x),
         x.expr.whyNotPromoted((reasons) {
           expect(reasons.keys, unorderedEquals([Type('int'), Type('int?')]));
-          expect(
-              (reasons[Type('int')] as DemoteViaExplicitWrite<Var, Expression>)
-                  .writeExpression,
+          expect((reasons[Type('int')] as DemoteViaExplicitWrite<Var>).node,
               same(writeExpression));
-          expect(
-              (reasons[Type('int?')] as DemoteViaExplicitWrite<Var, Expression>)
-                  .writeExpression,
+          expect((reasons[Type('int?')] as DemoteViaExplicitWrite<Var>).node,
               same(writeExpression));
         }).stmt,
       ]);
@@ -5553,8 +5549,8 @@
         x.expr.whyNotPromoted((reasons) {
           expect(reasons.keys, unorderedEquals([Type('int')]));
           var nonPromotionReason =
-              reasons.values.single as DemoteViaExplicitWrite<Var, Expression>;
-          expect(nonPromotionReason.writeExpression, same(writeExpression));
+              reasons.values.single as DemoteViaExplicitWrite<Var>;
+          expect(nonPromotionReason.node, same(writeExpression));
         }).stmt,
       ]);
     });
@@ -5577,8 +5573,8 @@
         checkPromoted(x, 'num'),
         x.expr.whyNotPromoted((reasons) {
           var nonPromotionReason =
-              reasons[Type('int')] as DemoteViaExplicitWrite;
-          expect(nonPromotionReason.writeExpression, same(writeExpression));
+              reasons[Type('int')] as DemoteViaExplicitWrite<Var>;
+          expect(nonPromotionReason.node, same(writeExpression));
         }).stmt,
       ]);
     });
@@ -5759,10 +5755,9 @@
 
   String get shortName => fail('Unexpected call to shortName');
 
-  R accept<R, Node extends Object, Expression extends Object,
-              Variable extends Object, Type extends Object>(
-          NonPromotionReasonVisitor<R, Node, Expression, Variable, Type>
-              visitor) =>
+  R accept<R, Node extends Object, Variable extends Object,
+              Type extends Object>(
+          NonPromotionReasonVisitor<R, Node, Variable, Type> visitor) =>
       fail('Unexpected call to accept');
 }
 
diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart
index fb2769a..9e1ca0a 100644
--- a/pkg/_fe_analyzer_shared/test/mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart
@@ -37,7 +37,7 @@
     new _Break(branchTargetPlaceholder);
 
 SwitchCase case_(List<Statement> body, {bool hasLabel = false}) =>
-    SwitchCase._(hasLabel, body);
+    SwitchCase._(hasLabel, new _Block(body));
 
 /// Creates a pseudo-statement whose function is to verify that flow analysis
 /// considers [variable]'s assigned state to be [expectedAssignedState].
@@ -79,7 +79,7 @@
     new _Declare(variable, initializer, isFinal, isLate);
 
 Statement do_(List<Statement> body, Expression condition) =>
-    _Do(body, condition);
+    _Do(block(body), condition);
 
 /// Creates a pseudo-expression having type [typeStr] that otherwise has no
 /// effect on flow analysis.
@@ -92,7 +92,7 @@
 Statement for_(Statement? initializer, Expression? condition,
         Expression? updater, List<Statement> body,
         {bool forCollection = false}) =>
-    new _For(initializer, condition, updater, body, forCollection);
+    new _For(initializer, condition, updater, block(body), forCollection);
 
 /// Creates a "for each" statement where the identifier being assigned to by the
 /// iteration is not a local variable.
@@ -103,7 +103,7 @@
 ///       for (x in iterable) { ... }
 ///     }
 Statement forEachWithNonVariable(Expression iterable, List<Statement> body) =>
-    new _ForEach(null, iterable, body, false);
+    new _ForEach(null, iterable, block(body), false);
 
 /// Creates a "for each" statement where the identifier being assigned to by the
 /// iteration is a variable that is being declared by the "for each" statement.
@@ -116,7 +116,7 @@
     Var variable, Expression iterable, List<Statement> body) {
   // ignore: unnecessary_null_comparison
   assert(variable != null);
-  return new _ForEach(variable, iterable, body, true);
+  return new _ForEach(variable, iterable, block(body), true);
 }
 
 /// Creates a "for each" statement where the identifier being assigned to by the
@@ -131,7 +131,7 @@
     Var variable, Expression iterable, List<Statement> body) {
   // ignore: unnecessary_null_comparison
   assert(variable != null);
-  return new _ForEach(variable, iterable, body, false);
+  return new _ForEach(variable, iterable, block(body), false);
 }
 
 /// Creates a [Statement] that, when analyzed, will cause [callback] to be
@@ -142,7 +142,7 @@
 
 Statement if_(Expression condition, List<Statement> ifTrue,
         [List<Statement>? ifFalse]) =>
-    new _If(condition, ifTrue, ifFalse);
+    new _If(condition, block(ifTrue), ifFalse == null ? null : block(ifFalse));
 
 Statement implicitThis_whyNotPromoted(String staticType,
         void Function(Map<Type, NonPromotionReason>) callback) =>
@@ -150,7 +150,7 @@
 
 Statement labeled(Statement body) => new _LabeledStatement(body);
 
-Statement localFunction(List<Statement> body) => _LocalFunction(body);
+Statement localFunction(List<Statement> body) => _LocalFunction(block(body));
 
 Statement return_() => new _Return();
 
@@ -163,10 +163,11 @@
 
 Expression throw_(Expression operand) => new _Throw(operand);
 
-TryBuilder try_(List<Statement> body) => new _TryStatement(body, [], null);
+TryBuilder try_(List<Statement> body) =>
+    new _TryStatement(block(body), [], null);
 
 Statement while_(Expression condition, List<Statement> body) =>
-    new _While(condition, body);
+    new _While(condition, block(body));
 
 /// Placeholder used by [branchTarget] to tie `break` and `continue` statements
 /// to their branch targets.
@@ -481,13 +482,14 @@
   /// they contain.
   void run(List<Statement> statements) {
     var assignedVariables = AssignedVariables<Node, Var>();
-    statements._preVisit(assignedVariables);
+    var b = block(statements);
+    b._preVisit(assignedVariables);
     var flow = legacy
         ? FlowAnalysis<Node, Statement, Expression, Var, Type>.legacy(
             this, assignedVariables)
         : FlowAnalysis<Node, Statement, Expression, Var, Type>(
             this, assignedVariables);
-    statements._visit(this, flow);
+    b._visit(this, flow);
     flow.finish();
   }
 
@@ -601,12 +603,15 @@
 /// to create instances of this class.
 class SwitchCase implements _Visitable<void> {
   final bool _hasLabel;
-  final List<Statement> _body;
+  final _Block _body;
 
   SwitchCase._(this._hasLabel, this._body);
 
-  String toString() =>
-      [if (_hasLabel) '<label>:', 'case <value>:', ..._body].join(' ');
+  String toString() => [
+        if (_hasLabel) '<label>:',
+        'case <value>:',
+        ..._body.statements
+      ].join(' ');
 
   void _preVisit(AssignedVariables<Node, Var> assignedVariables) {
     _body._preVisit(assignedVariables);
@@ -713,13 +718,17 @@
 
   @override
   void _preVisit(AssignedVariables<Node, Var> assignedVariables) {
-    statements._preVisit(assignedVariables);
+    for (var statement in statements) {
+      statement._preVisit(assignedVariables);
+    }
   }
 
   @override
   void _visit(
       Harness h, FlowAnalysis<Node, Statement, Expression, Var, Type> flow) {
-    statements._visit(h, flow);
+    for (var statement in statements) {
+      statement._visit(h, flow);
+    }
   }
 }
 
@@ -765,7 +774,7 @@
 /// Representation of a single catch clause in a try/catch statement.  Use
 /// [catch_] to create instances of this class.
 class _CatchClause implements _Visitable<void> {
-  final List<Statement> _body;
+  final Statement _body;
   final Var? _exception;
   final Var? _stackTrace;
 
@@ -780,7 +789,7 @@
     } else {
       initialPart = 'on ...';
     }
-    return '$initialPart ${block(_body)}';
+    return '$initialPart $_body';
   }
 
   void _preVisit(AssignedVariables<Node, Var> assignedVariables) {
@@ -973,13 +982,13 @@
 }
 
 class _Do extends Statement {
-  final List<Statement> body;
+  final Statement body;
   final Expression condition;
 
   _Do(this.body, this.condition) : super._();
 
   @override
-  String toString() => 'do ${block(body)} while ($condition);';
+  String toString() => 'do $body while ($condition);';
 
   @override
   void _preVisit(AssignedVariables<Node, Var> assignedVariables) {
@@ -1051,7 +1060,7 @@
   final Statement? initializer;
   final Expression? condition;
   final Expression? updater;
-  final List<Statement> body;
+  final Statement body;
   final bool forCollection;
 
   _For(this.initializer, this.condition, this.updater, this.body,
@@ -1074,7 +1083,7 @@
     if (updater != null) {
       buffer.write(' $updater');
     }
-    buffer.write(') ${block(body)}');
+    buffer.write(') $body');
     return buffer.toString();
   }
 
@@ -1105,7 +1114,7 @@
 class _ForEach extends Statement {
   final Var? variable;
   final Expression iterable;
-  final List<Statement> body;
+  final Statement body;
   final bool declaresVariable;
 
   _ForEach(this.variable, this.iterable, this.body, this.declaresVariable)
@@ -1121,7 +1130,7 @@
     } else {
       declarationPart = variable!.name;
     }
-    return 'for ($declarationPart in $iterable) ${block(body)}';
+    return 'for ($declarationPart in $iterable) $body';
   }
 
   @override
@@ -1143,7 +1152,11 @@
   void _visit(
       Harness h, FlowAnalysis<Node, Statement, Expression, Var, Type> flow) {
     var iteratedType = h._getIteratedType(iterable._visit(h, flow));
-    flow.forEach_bodyBegin(this, variable, iteratedType);
+    flow.forEach_bodyBegin(this);
+    var variable = this.variable;
+    if (variable != null && !declaresVariable) {
+      flow.write(this, variable, iteratedType, null);
+    }
     body._visit(h, flow);
     flow.forEach_end();
   }
@@ -1188,15 +1201,14 @@
 
 class _If extends Statement {
   final Expression condition;
-  final List<Statement> ifTrue;
-  final List<Statement>? ifFalse;
+  final Statement ifTrue;
+  final Statement? ifFalse;
 
   _If(this.condition, this.ifTrue, this.ifFalse) : super._();
 
   @override
   String toString() =>
-      'if ($condition) ${block(ifTrue)}' +
-      (ifFalse == null ? '' : 'else ${block(ifFalse!)}');
+      'if ($condition) $ifTrue' + (ifFalse == null ? '' : 'else $ifFalse');
 
   @override
   void _preVisit(AssignedVariables<Node, Var> assignedVariables) {
@@ -1295,12 +1307,12 @@
 }
 
 class _LocalFunction extends Statement {
-  final List<Statement> body;
+  final Statement body;
 
   _LocalFunction(this.body) : super._();
 
   @override
-  String toString() => '() ${block(body)}';
+  String toString() => '() $body';
 
   @override
   void _preVisit(AssignedVariables<Node, Var> assignedVariables) {
@@ -1557,7 +1569,9 @@
   void _preVisit(AssignedVariables<Node, Var> assignedVariables) {
     expression._preVisit(assignedVariables);
     assignedVariables.beginNode();
-    cases._preVisit(assignedVariables);
+    for (var case_ in cases) {
+      case_._preVisit(assignedVariables);
+    }
     assignedVariables.endNode(this);
   }
 
@@ -1568,7 +1582,9 @@
     flow.switchStatement_expressionEnd(this);
     var oldSwitch = h._currentSwitch;
     h._currentSwitch = this;
-    cases._visit(h, flow);
+    for (var case_ in cases) {
+      case_._visit(h, flow);
+    }
     h._currentSwitch = oldSwitch;
     flow.switchStatement_end(isExhaustive);
   }
@@ -1631,9 +1647,9 @@
 }
 
 class _TryStatement extends TryStatement {
-  final List<Statement> _body;
+  final Statement _body;
   final List<_CatchClause> _catches;
-  final List<Statement>? _finally;
+  final Statement? _finally;
   final _bodyNode = Node._();
 
   _TryStatement(this._body, this._catches, this._finally) : super._();
@@ -1642,14 +1658,14 @@
   TryStatement catch_(
       {Var? exception, Var? stackTrace, required List<Statement> body}) {
     assert(_finally == null, 'catch after finally');
-    return _TryStatement(
-        _body, [..._catches, _CatchClause(body, exception, stackTrace)], null);
+    return _TryStatement(_body,
+        [..._catches, _CatchClause(block(body), exception, stackTrace)], null);
   }
 
   @override
   Statement finally_(List<Statement> statements) {
     assert(_finally == null, 'multiple finally clauses');
-    return _TryStatement(_body, _catches, statements);
+    return _TryStatement(_body, _catches, block(statements));
   }
 
   @override
@@ -1662,7 +1678,9 @@
     }
     _body._preVisit(assignedVariables);
     assignedVariables.endNode(_bodyNode);
-    _catches._preVisit(assignedVariables);
+    for (var catch_ in _catches) {
+      catch_._preVisit(assignedVariables);
+    }
     if (_finally != null) {
       if (_catches.isNotEmpty) {
         assignedVariables.endNode(this);
@@ -1683,7 +1701,9 @@
     _body._visit(h, flow);
     if (_catches.isNotEmpty) {
       flow.tryCatchStatement_bodyEnd(_bodyNode);
-      _catches._visit(h, flow);
+      for (var catch_ in _catches) {
+        catch_._visit(h, flow);
+      }
       flow.tryCatchStatement_end();
     }
     if (_finally != null) {
@@ -1740,12 +1760,12 @@
 
 class _While extends Statement {
   final Expression condition;
-  final List<Statement> body;
+  final Statement body;
 
   _While(this.condition, this.body) : super._();
 
   @override
-  String toString() => 'while ($condition) ${block(body)}';
+  String toString() => 'while ($condition) $body';
 
   @override
   void _preVisit(AssignedVariables<Node, Var> assignedVariables) {
@@ -1888,18 +1908,3 @@
     return type;
   }
 }
-
-extension on List<_Visitable<void>> {
-  void _preVisit(AssignedVariables<Node, Var> assignedVariables) {
-    for (var element in this) {
-      element._preVisit(assignedVariables);
-    }
-  }
-
-  void _visit(
-      Harness h, FlowAnalysis<Node, Statement, Expression, Var, Type> flow) {
-    for (var element in this) {
-      element._visit(h, flow);
-    }
-  }
-}
diff --git a/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
index c82e332..efa3e86c 100644
--- a/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
@@ -14,6 +14,7 @@
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/member.dart';
 import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/dart/element/type_algebra.dart';
 import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/resolver.dart';
@@ -65,6 +66,7 @@
 
     _constructorInvocation(
       node,
+      classElement.name,
       constructorName,
       classElement.typeParameters,
       constructorElement,
@@ -113,6 +115,7 @@
 
   void _constructorInvocation(
     AnnotationImpl node,
+    String typeDisplayName,
     SimpleIdentifierImpl? constructorName,
     List<TypeParameterElement> typeParameters,
     ConstructorElement? constructorElement,
@@ -136,6 +139,18 @@
 
     // If no type parameters, the elements are correct.
     if (typeParameters.isEmpty) {
+      var typeArgumentList = node.typeArguments;
+      if (typeArgumentList != null) {
+        _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS,
+          typeArgumentList,
+          [
+            typeDisplayName,
+            typeParameters.length,
+            typeArgumentList.arguments.length,
+          ],
+        );
+      }
       _resolveConstructorInvocationArguments(node);
       InferenceContext.setType(argumentList, constructorElement.type);
       _resolver.visitArgumentList(argumentList,
@@ -174,7 +189,35 @@
         typeArguments = typeArgumentList.arguments
             .map((element) => element.typeOrThrow)
             .toList();
+        var substitution = Substitution.fromPairs(
+          typeParameters,
+          typeArguments,
+        );
+        for (var i = 0; i < typeParameters.length; i++) {
+          var typeParameter = typeParameters[i];
+          var bound = typeParameter.bound;
+          if (bound != null) {
+            bound = substitution.substituteType(bound);
+            var typeArgument = typeArguments[i];
+            if (!_resolver.typeSystem.isSubtypeOf(typeArgument, bound)) {
+              _errorReporter.reportErrorForNode(
+                CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS,
+                typeArgumentList.arguments[i],
+                [typeArgument, typeParameter.name, bound],
+              );
+            }
+          }
+        }
       } else {
+        _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS,
+          typeArgumentList,
+          [
+            typeDisplayName,
+            typeParameters.length,
+            typeArgumentList.arguments.length,
+          ],
+        );
         typeArguments = List.filled(
           typeParameters.length,
           DynamicTypeImpl.instance,
@@ -458,6 +501,7 @@
 
     _constructorInvocation(
       node,
+      typeAliasElement.name,
       constructorName,
       typeAliasElement.typeParameters,
       constructorElement,
diff --git a/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
index 3c44519..1797cfc 100644
--- a/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
@@ -132,13 +132,12 @@
           ?.declare(loopVariable.declaredElement!, true);
     }
 
-    _resolver.flowAnalysis?.flow?.forEach_bodyBegin(
-      node,
-      identifierElement is PromotableElement
-          ? identifierElement
-          : loopVariable?.declaredElement,
-      elementType ?? DynamicTypeImpl.instance,
-    );
+    _resolver.flowAnalysis?.flow?.forEach_bodyBegin(node);
+    if (identifierElement is PromotableElement &&
+        forEachParts is ForEachPartsWithIdentifier) {
+      _resolver.flowAnalysis?.flow?.write(forEachParts, identifierElement,
+          elementType ?? DynamicTypeImpl.instance, null);
+    }
 
     _resolveBody(body);
 
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index bb494fd..1a1918d 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -3443,7 +3443,7 @@
 
 class _WhyNotPromotedVisitor
     implements
-        NonPromotionReasonVisitor<DiagnosticMessage?, AstNode, Expression,
+        NonPromotionReasonVisitor<DiagnosticMessage?, AstNode,
             PromotableElement, DartType> {
   final Source source;
 
@@ -3464,43 +3464,17 @@
 
   @override
   DiagnosticMessage? visitDemoteViaExplicitWrite(
-      DemoteViaExplicitWrite<PromotableElement, Expression> reason) {
-    var writeExpression = reason.writeExpression;
+      DemoteViaExplicitWrite<PromotableElement> reason) {
+    var node = reason.node as AstNode;
+    if (node is ForEachPartsWithIdentifier) {
+      node = node.identifier;
+    }
     if (_dataForTesting != null) {
-      _dataForTesting!.nonPromotionReasonTargets[writeExpression] =
-          reason.shortName;
+      _dataForTesting!.nonPromotionReasonTargets[node] = reason.shortName;
     }
     var variableName = reason.variable.name;
     if (variableName == null) return null;
-    return _contextMessageForWrite(variableName, writeExpression, reason);
-  }
-
-  @override
-  DiagnosticMessage? visitDemoteViaForEachVariableWrite(
-      DemoteViaForEachVariableWrite<PromotableElement, AstNode> reason) {
-    var node = reason.node;
-    var variableName = reason.variable.name;
-    if (variableName == null) return null;
-    ForLoopParts parts;
-    if (node is ForStatement) {
-      parts = node.forLoopParts;
-    } else if (node is ForElement) {
-      parts = node.forLoopParts;
-    } else {
-      assert(false, 'Unexpected node type');
-      return null;
-    }
-    if (parts is ForEachPartsWithIdentifier) {
-      var identifier = parts.identifier;
-      if (_dataForTesting != null) {
-        _dataForTesting!.nonPromotionReasonTargets[identifier] =
-            reason.shortName;
-      }
-      return _contextMessageForWrite(variableName, identifier, reason);
-    } else {
-      assert(false, 'Unexpected parts type');
-      return null;
-    }
+    return _contextMessageForWrite(variableName, node, reason);
   }
 
   @override
@@ -3551,13 +3525,13 @@
         length: property.nameLength);
   }
 
-  DiagnosticMessageImpl _contextMessageForWrite(String variableName,
-      Expression writeExpression, NonPromotionReason reason) {
+  DiagnosticMessageImpl _contextMessageForWrite(
+      String variableName, AstNode node, NonPromotionReason reason) {
     return DiagnosticMessageImpl(
         filePath: source.fullName,
         message: "Variable '$variableName' could not be promoted due to an "
             "assignment.  See ${reason.documentationLink}",
-        offset: writeExpression.offset,
-        length: writeExpression.length);
+        offset: node.offset,
+        length: node.length);
   }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart b/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
index 9d407aa..25184c2 100644
--- a/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
@@ -471,6 +471,45 @@
 ''');
   }
 
+  test_metadata_matching() async {
+    await assertNoErrorsInCode(r'''
+class A<T extends num> {
+  const A();
+}
+
+@A<int>()
+void f() {}
+''');
+  }
+
+  test_metadata_notMatching() async {
+    await assertErrorsInCode(r'''
+class A<T extends num> {
+  const A();
+}
+
+@A<String>()
+void f() {}
+''', [
+      error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 44, 6),
+    ]);
+  }
+
+  test_metadata_notMatching_viaTypeAlias() async {
+    await assertErrorsInCode(r'''
+class A<T> {
+  const A();
+}
+
+typedef B<T extends num> = A<T>;
+
+@B<String>()
+void f() {}
+''', [
+      error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 66, 6),
+    ]);
+  }
+
   test_methodInvocation_genericFunctionTypeArgument_match() async {
     await assertNoErrorsInCode(r'''
 typedef F = void Function<T extends num>();
diff --git a/pkg/analyzer/test/src/diagnostics/wrong_number_of_type_arguments_test.dart b/pkg/analyzer/test/src/diagnostics/wrong_number_of_type_arguments_test.dart
index 29e3eee..6754881 100644
--- a/pkg/analyzer/test/src/diagnostics/wrong_number_of_type_arguments_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/wrong_number_of_type_arguments_test.dart
@@ -93,6 +93,90 @@
     ]);
   }
 
+  test_metadata_1of0() async {
+    await assertErrorsInCode(r'''
+class A {
+  const A();
+}
+
+@A<int>()
+void f() {}
+''', [
+      error(CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 28, 5),
+    ]);
+  }
+
+  test_metadata_1of0_viaTypeAlias() async {
+    await assertErrorsInCode(r'''
+class A {
+  const A();
+}
+
+typedef B = A;
+
+@B<int>()
+void f() {}
+''', [
+      error(CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 44, 5),
+    ]);
+  }
+
+  test_metadata_1of2() async {
+    await assertErrorsInCode(r'''
+class A<T, U> {
+  const A();
+}
+
+@A<int>()
+void f() {}
+''', [
+      error(CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 34, 5),
+    ]);
+  }
+
+  test_metadata_1of2_viaTypeAlias() async {
+    await assertErrorsInCode(r'''
+class A {
+  const A();
+}
+
+typedef B<T, U> = A;
+
+@B<int>()
+void f() {}
+''', [
+      error(CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 50, 5),
+    ]);
+  }
+
+  test_metadata_2of1() async {
+    await assertErrorsInCode(r'''
+class A<T> {
+  const A();
+}
+
+@A<int, String>()
+void f() {}
+''', [
+      error(CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 31, 13),
+    ]);
+  }
+
+  test_metadata_2of1_viaTypeAlias() async {
+    await assertErrorsInCode(r'''
+class A {
+  const A();
+}
+
+typedef B<T> = A;
+
+@B<int, String>()
+void f() {}
+''', [
+      error(CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 47, 13),
+    ]);
+  }
+
   test_new_nonGeneric() async {
     await assertErrorsInCode('''
 class C {}
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 13c72b9..89fdd5e 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -901,7 +901,7 @@
     // This is matched by the call to [forEach_end] in
     // [inferElement], [inferMapEntry] or [inferForInStatement].
     inferrer.flowAnalysis.declare(variable, true);
-    inferrer.flowAnalysis.forEach_bodyBegin(node, variable, variable.type);
+    inferrer.flowAnalysis.forEach_bodyBegin(node);
 
     VariableDeclaration tempVariable =
         new VariableDeclaration(null, type: inferredType, isFinal: true);
@@ -1022,8 +1022,11 @@
     }
     // This is matched by the call to [forEach_end] in
     // [inferElement], [inferMapEntry] or [inferForInStatement].
-    inferrer.flowAnalysis.forEach_bodyBegin(node, variable, inferredType);
+    inferrer.flowAnalysis.forEach_bodyBegin(node);
     syntheticAssignment = forInVariable.inferAssignment(inferrer, inferredType);
+    if (syntheticAssignment is VariableSet) {
+      inferrer.flowAnalysis.write(node, variable, inferredType, null);
+    }
     if (expressionEffects != null) {
       StatementInferenceResult result =
           inferrer.inferStatement(expressionEffects);
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index ff29dc3..15606b8 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -5130,8 +5130,8 @@
 
 class _WhyNotPromotedVisitor
     implements
-        NonPromotionReasonVisitor<LocatedMessage, Node, Expression,
-            VariableDeclaration, DartType> {
+        NonPromotionReasonVisitor<LocatedMessage, Node, VariableDeclaration,
+            DartType> {
   final TypeInferrerImpl inferrer;
 
   final Expression receiver;
@@ -5144,21 +5144,13 @@
 
   @override
   LocatedMessage visitDemoteViaExplicitWrite(
-      DemoteViaExplicitWrite<VariableDeclaration, Expression> reason) {
+      DemoteViaExplicitWrite<VariableDeclaration> reason) {
+    TreeNode node = reason.node as TreeNode;
     if (inferrer.dataForTesting != null) {
       inferrer.dataForTesting.flowAnalysisResult
-          .nonPromotionReasonTargets[reason.writeExpression] = reason.shortName;
+          .nonPromotionReasonTargets[node] = reason.shortName;
     }
-    int offset = reason.writeExpression.fileOffset;
-    return templateVariableCouldBeNullDueToWrite
-        .withArguments(reason.variable.name, reason.documentationLink)
-        .withLocation(inferrer.helper.uri, offset, noLength);
-  }
-
-  @override
-  LocatedMessage visitDemoteViaForEachVariableWrite(
-      DemoteViaForEachVariableWrite<VariableDeclaration, Node> reason) {
-    int offset = (reason.node as TreeNode).fileOffset;
+    int offset = node.fileOffset;
     return templateVariableCouldBeNullDueToWrite
         .withArguments(reason.variable.name, reason.documentationLink)
         .withLocation(inferrer.helper.uri, offset, noLength);
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index 5b97ce5..26ad6fe 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -2762,10 +2762,11 @@
               source: elementType, destination: lhsType, hard: false);
         }
       }
-      _flowAnalysis.forEach_bodyBegin(
-          node,
-          lhsElement is PromotableElement ? lhsElement : null,
-          elementType ?? _makeNullableDynamicType(node));
+      _flowAnalysis.forEach_bodyBegin(node);
+      if (lhsElement is PromotableElement) {
+        _flowAnalysis.write(node, lhsElement,
+            elementType ?? _makeNullableDynamicType(node), null);
+      }
     }
 
     // The condition may fail/iterable may be empty, so the body gets a new
diff --git a/tools/VERSION b/tools/VERSION
index b182322..275cd60 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 196
+PRERELEASE 197
 PRERELEASE_PATCH 0
\ No newline at end of file