Version 2.17.0-242.0.dev

Merge commit '2a1dba465c4e38106702e0108c223fb15190bb77' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_cascade.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_cascade.dart
new file mode 100644
index 0000000..818a11c
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_cascade.dart
@@ -0,0 +1,72 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ConvertToCascade extends CorrectionProducer {
+  @override
+  FixKind get fixKind => DartFixKind.CONVERT_TO_CASCADE;
+
+  @override
+  Future<void> compute(ChangeBuilder builder) async {
+    var node = this.node;
+    if (node is! ExpressionStatement) return;
+
+    var block = node.parent;
+    if (block is! Block) return;
+
+    var previous = _getPrevious(block, node);
+    if (previous is! ExpressionStatement) return;
+    var previousOperator = _getTargetAndOperator(previous.expression)?.operator;
+
+    var expression = node.expression;
+    var target = _getTargetAndOperator(expression)?.target;
+    if (target == null) return;
+
+    var targetReplacement = expression is CascadeExpression ? '' : '.';
+
+    await builder.addDartFileEdit(file, (builder) {
+      if (previousOperator != null) {
+        builder.addSimpleInsertion(previousOperator.offset, '.');
+      }
+      builder.addDeletion(range.token(previous.semicolon!));
+      builder.addSimpleReplacement(range.node(target), targetReplacement);
+    });
+  }
+
+  Statement? _getPrevious(Block block, Statement statement) {
+    var statements = block.statements;
+    var index = statements.indexOf(statement);
+    return index > 0 ? statements[index - 1] : null;
+  }
+
+  _TargetAndOperator? _getTargetAndOperator(Expression expression) {
+    if (expression is AssignmentExpression) {
+      var lhs = expression.leftHandSide;
+      if (lhs is PrefixedIdentifier) {
+        return _TargetAndOperator(lhs.prefix, lhs.period);
+      }
+    } else if (expression is MethodInvocation) {
+      return _TargetAndOperator(expression.target, expression.operator);
+    } else if (expression is CascadeExpression) {
+      return _TargetAndOperator(expression.target, null);
+    }
+    return null;
+  }
+
+  /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
+  static ConvertToCascade newInstance() => ConvertToCascade();
+}
+
+class _TargetAndOperator {
+  AstNode? target;
+  Token? operator;
+  _TargetAndOperator(this.target, this.operator);
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_super.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_super.dart
index c571123..3fd0c99 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_super.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_super.dart
@@ -5,6 +5,7 @@
 import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
 import 'package:analysis_server/src/services/correction/util.dart';
+import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
@@ -61,12 +62,22 @@
   List<Object> get fixArguments {
     var buffer = StringBuffer();
     buffer.write('super');
-    var constructorName = _constructor.displayName;
-    if (constructorName.isNotEmpty) {
-      buffer.write('.');
-      buffer.write(constructorName);
+    var constructorName = _constructor.name;
+    if (libraryElement.featureSet.isEnabled(Feature.super_parameters)) {
+      if (constructorName.isNotEmpty) {
+        buffer.write('.');
+        buffer.write(constructorName);
+        buffer.write('()');
+      } else {
+        buffer.write('.');
+      }
+    } else {
+      if (constructorName.isNotEmpty) {
+        buffer.write('.');
+        buffer.write(constructorName);
+      }
+      buffer.write('(...)');
     }
-    buffer.write('(...)');
     return [buffer.toString()];
   }
 
@@ -75,6 +86,14 @@
 
   @override
   Future<void> compute(ChangeBuilder builder) async {
+    if (libraryElement.featureSet.isEnabled(Feature.super_parameters)) {
+      await _computeWithSuperParameters(builder);
+    } else {
+      await _computeWithoutSuperParameters(builder);
+    }
+  }
+
+  Future<void> _computeWithoutSuperParameters(ChangeBuilder builder) async {
     var constructorName = _constructor.name;
     var requiredPositionalParameters = _constructor.parameters
         .where((parameter) => parameter.isRequiredPositional);
@@ -154,4 +173,70 @@
       });
     });
   }
+
+  Future<void> _computeWithSuperParameters(ChangeBuilder builder) async {
+    var constructorName = _constructor.name;
+    var requiredPositionalParameters = _constructor.parameters
+        .where((parameter) => parameter.isRequiredPositional);
+    var requiredNamedParameters =
+        _constructor.parameters.where((parameter) => parameter.isRequiredNamed);
+    await builder.addDartFileEdit(file, (builder) {
+      builder.addInsertion(_targetLocation.offset, (builder) {
+        void writeParameter(ParameterElement parameter) {
+          var parameterName = parameter.displayName;
+
+          if (parameterName.length > 1 && parameterName.startsWith('_')) {
+            parameterName = parameterName.substring(1);
+          }
+
+          if (parameter.isRequiredNamed) {
+            builder.write('required ');
+          }
+
+          builder.write('super.');
+          builder.write(parameterName);
+        }
+
+        builder.write(_targetLocation.prefix);
+        builder.write(_targetClassName);
+        if (constructorName.isNotEmpty) {
+          builder.write('.');
+          builder.addSimpleLinkedEdit('NAME', constructorName);
+        }
+        builder.write('(');
+
+        var firstParameter = true;
+        void writeComma() {
+          if (firstParameter) {
+            firstParameter = false;
+          } else {
+            builder.write(', ');
+          }
+        }
+
+        for (var parameter in requiredPositionalParameters) {
+          writeComma();
+          writeParameter(parameter);
+        }
+        if (requiredNamedParameters.isNotEmpty) {
+          writeComma();
+          firstParameter = true; // Reset since we just included a comma.
+          builder.write('{');
+          for (var parameter in requiredNamedParameters) {
+            writeComma();
+            writeParameter(parameter);
+          }
+          builder.write('}');
+        }
+
+        if (constructorName.isNotEmpty) {
+          builder.write(') : super.');
+          builder.addSimpleLinkedEdit('NAME', constructorName);
+          builder.write('(');
+        }
+        builder.write(');');
+        builder.write(_targetLocation.suffix);
+      });
+    });
+  }
 }
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index 1ea1e2e..1be33f8 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -1587,7 +1587,7 @@
 LintCode.cancel_subscriptions:
   status: needsEvaluation
 LintCode.cascade_invocations:
-  status: needsEvaluation
+  status: hasFix
 LintCode.cast_nullable_to_non_nullable:
   status: needsEvaluation
 LintCode.close_sinks:
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index 94f206f..a9b52c3 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -373,6 +373,11 @@
     DartFixKindPriority.IN_FILE,
     'Convert the quotes and remove escapes everywhere in file',
   );
+  static const CONVERT_TO_CASCADE = FixKind(
+    'dart.fix.convert.toCascade',
+    DartFixKindPriority.DEFAULT,
+    'Convert to cascade notation',
+  );
   static const CONVERT_TO_CONTAINS = FixKind(
     'dart.fix.convert.toContains',
     DartFixKindPriority.DEFAULT,
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 9fb3607..7239a67 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -48,6 +48,7 @@
 import 'package:analysis_server/src/services/correction/dart/convert_into_is_not.dart';
 import 'package:analysis_server/src/services/correction/dart/convert_map_from_iterable_to_for_literal.dart';
 import 'package:analysis_server/src/services/correction/dart/convert_quotes.dart';
+import 'package:analysis_server/src/services/correction/dart/convert_to_cascade.dart';
 import 'package:analysis_server/src/services/correction/dart/convert_to_contains.dart';
 import 'package:analysis_server/src/services/correction/dart/convert_to_expression_function_body.dart';
 import 'package:analysis_server/src/services/correction/dart/convert_to_for_loop.dart';
@@ -424,6 +425,9 @@
     LintNames.await_only_futures: [
       RemoveAwait.newInstance,
     ],
+    LintNames.cascade_invocations: [
+      ConvertToCascade.newInstance,
+    ],
     LintNames.curly_braces_in_flow_control_structures: [
       UseCurlyBraces.newInstance,
     ],
diff --git a/pkg/analysis_server/lib/src/services/linter/lint_names.dart b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
index 60448e7..fe9d319 100644
--- a/pkg/analysis_server/lib/src/services/linter/lint_names.dart
+++ b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
@@ -45,6 +45,7 @@
       'avoid_unnecessary_containers';
   static const String avoid_void_async = 'avoid_void_async';
   static const String await_only_futures = 'await_only_futures';
+  static const String cascade_invocations = 'cascade_invocations';
   static const String curly_braces_in_flow_control_structures =
       'curly_braces_in_flow_control_structures';
   static const String diagnostic_describe_all_properties =
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_cascade_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_cascade_test.dart
new file mode 100644
index 0000000..66c1a2a
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_cascade_test.dart
@@ -0,0 +1,169 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/linter/lint_names.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'fix_processor.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ConvertToCascadeTest);
+  });
+}
+
+@reflectiveTest
+class ConvertToCascadeTest extends FixProcessorLintTest {
+  @override
+  FixKind get kind => DartFixKind.CONVERT_TO_CASCADE;
+
+  @override
+  String get lintCode => LintNames.cascade_invocations;
+
+  Future<void> test_cascade_method() async {
+    await resolveTestCode('''
+class A {
+  void m() {}
+  int? x;
+}
+
+void f(A a) {
+  a..x = 1
+  ..x = 2;
+  a..m();
+}
+''');
+    await assertHasFix('''
+class A {
+  void m() {}
+  int? x;
+}
+
+void f(A a) {
+  a..x = 1
+  ..x = 2
+  ..m();
+}
+''');
+  }
+
+  Future<void> test_method_method() async {
+    await resolveTestCode('''
+class A {
+  void m() {}
+  int? x;
+}
+void f(A a) {
+  a.m();
+  a.m();
+}
+''');
+    await assertHasFix('''
+class A {
+  void m() {}
+  int? x;
+}
+void f(A a) {
+  a..m()
+  ..m();
+}
+''');
+  }
+
+  Future<void> test_method_property() async {
+    await resolveTestCode('''
+class A {
+  void m() {}
+  int? x;
+}
+void f(A a) {
+  a.m();
+  a.x = 1;
+}
+''');
+    await assertHasFix('''
+class A {
+  void m() {}
+  int? x;
+}
+void f(A a) {
+  a..m()
+  ..x = 1;
+}
+''');
+  }
+
+  Future<void> test_property_cascade() async {
+    await resolveTestCode('''
+class A {
+  void m() {}
+  int? x;
+}
+
+void f(A a) {
+  a.x = 1;
+  a..m();
+}
+''');
+    await assertHasFix('''
+class A {
+  void m() {}
+  int? x;
+}
+
+void f(A a) {
+  a..x = 1
+  ..m();
+}
+''');
+  }
+
+  Future<void> test_property_method() async {
+    await resolveTestCode('''
+class A {
+  void m() {}
+  int? x;
+}
+void f(A a) {
+  a.x = 1;
+  a.m();
+}
+''');
+    await assertHasFix('''
+class A {
+  void m() {}
+  int? x;
+}
+void f(A a) {
+  a..x = 1
+  ..m();
+}
+''');
+  }
+
+  Future<void> test_property_property() async {
+    await resolveTestCode('''
+class A {
+  void m() {}
+  int? x;
+}
+void f(A a) {
+  a.x = 1;
+  a.x = 2;
+}
+''');
+    await assertHasFix('''
+class A {
+  void m() {}
+  int? x;
+}
+void f(A a) {
+  a..x = 1
+  ..x = 2;
+}
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_constructor_super_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_constructor_super_test.dart
index 6095520..d5a3275 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_constructor_super_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_constructor_super_test.dart
@@ -12,6 +12,7 @@
 void main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(CreateConstructorSuperTest);
+    defineReflectiveTests(CreateConstructorSuperWithoutSuperParametersTest);
   });
 }
 
@@ -42,18 +43,271 @@
 class B extends A {
   int existingField = 0;
 
-  B(int field) : super(field);
+  B(super.field);
+
+  void existingMethod() {}
+}
+''', matchFixMessage: 'Create constructor to call super.');
+  }
+
+  Future<void> test_importType() async {
+    addSource('$testPackageLibPath/a.dart', '''
+class A {}
+''');
+    addSource('$testPackageLibPath/b.dart', '''
+import 'package:test/a.dart';
+
+class B {
+  B(A a);
+}
+''');
+    await resolveTestCode('''
+import 'package:test/b.dart';
+
+class C extends B {
+}
+''');
+    await assertHasFix('''
+import 'package:test/b.dart';
+
+class C extends B {
+  C(super.a);
+}
+''');
+  }
+
+  Future<void> test_lint_sortConstructorsFirst() async {
+    createAnalysisOptionsFile(lints: [LintNames.sort_constructors_first]);
+    await resolveTestCode('''
+class A {
+  A(this.field);
+
+  int field;
+}
+class B extends A {
+  int existingField = 0;
+  void existingMethod() {}
+}
+''');
+    await assertHasFix('''
+class A {
+  A(this.field);
+
+  int field;
+}
+class B extends A {
+  B(super.field);
+
+  int existingField = 0;
+  void existingMethod() {}
+}
+''');
+  }
+
+  Future<void> test_namedConstructor() async {
+    await resolveTestCode('''
+class A {
+  A.named(p1, int p2);
+}
+class B extends A {
+  int existingField = 0;
+
+  void existingMethod() {}
+}
+''');
+    await assertHasFix('''
+class A {
+  A.named(p1, int p2);
+}
+class B extends A {
+  int existingField = 0;
+
+  B.named(super.p1, super.p2) : super.named();
+
+  void existingMethod() {}
+}
+''', matchFixMessage: 'Create constructor to call super.named()');
+  }
+
+  Future<void> test_namedOptionalParams() async {
+    await resolveTestCode('''
+class A {
+  A(p1, int p2, List<String> p3, {int? p4});
+}
+class B extends A {
+  int existingField = 0;
+
+  void existingMethod() {}
+}
+''');
+    await assertHasFix('''
+class A {
+  A(p1, int p2, List<String> p3, {int? p4});
+}
+class B extends A {
+  int existingField = 0;
+
+  B(super.p1, super.p2, super.p3);
 
   void existingMethod() {}
 }
 ''');
   }
 
+  Future<void> test_namedRequiredParams() async {
+    await resolveTestCode('''
+class A {
+  A(p1, int p2, List<String> p3, {required int p4, required int p5});
+}
+class B extends A {
+  int existingField = 0;
+
+  void existingMethod() {}
+}
+''');
+    await assertHasFix('''
+class A {
+  A(p1, int p2, List<String> p3, {required int p4, required int p5});
+}
+class B extends A {
+  int existingField = 0;
+
+  B(super.p1, super.p2, super.p3, {required super.p4, required super.p5});
+
+  void existingMethod() {}
+}
+''');
+  }
+
+  Future<void> test_optional() async {
+    await resolveTestCode('''
+class A {
+  A(p1, int p2, List<String> p3, [int p4 = 0]);
+}
+class B extends A {
+  int existingField = 0;
+
+  void existingMethod() {}
+}
+''');
+    await assertHasFix('''
+class A {
+  A(p1, int p2, List<String> p3, [int p4 = 0]);
+}
+class B extends A {
+  int existingField = 0;
+
+  B(super.p1, super.p2, super.p3);
+
+  void existingMethod() {}
+}
+''');
+  }
+
+  Future<void> test_private() async {
+    await resolveTestCode('''
+class A {
+  A._named(p);
+}
+class B extends A {
+}
+''');
+    await assertNoFix();
+  }
+
+  Future<void> test_typeArgument() async {
+    await resolveTestCode('''
+class C<T> {
+  final T x;
+  C(this.x);
+}
+class D extends C<int> {
+}
+''');
+    await assertHasFix('''
+class C<T> {
+  final T x;
+  C(this.x);
+}
+class D extends C<int> {
+  D(super.x);
+}
+''');
+  }
+
+  Future<void> test_underscore() async {
+    await resolveTestCode('''
+class A {
+  int _;
+  A(this._);
+  int get field => _;
+}
+class B extends A {
+  int existingField = 0;
+
+  void existingMethod() {}
+}
+''');
+    await assertHasFix('''
+class A {
+  int _;
+  A(this._);
+  int get field => _;
+}
+class B extends A {
+  int existingField = 0;
+
+  B(super._);
+
+  void existingMethod() {}
+}
+''');
+  }
+}
+
+@reflectiveTest
+class CreateConstructorSuperWithoutSuperParametersTest
+    extends FixProcessorTest {
+  @override
+  FixKind get kind => DartFixKind.CREATE_CONSTRUCTOR_SUPER;
+
+  @override
+  String get testPackageLanguageVersion => '2.16';
+
+  Future<void> test_fieldInitializer() async {
+    await resolveTestCode('''
+class A {
+  int _field;
+  A(this._field);
+  int get field => _field;
+}
+class B extends A {
+  int existingField = 0;
+
+  void existingMethod() {}
+}
+''');
+    await assertHasFix('''
+class A {
+  int _field;
+  A(this._field);
+  int get field => _field;
+}
+class B extends A {
+  int existingField = 0;
+
+  B(int field) : super(field);
+
+  void existingMethod() {}
+}
+''', matchFixMessage: 'Create constructor to call super(...)');
+  }
+
   Future<void> test_importType() async {
-    addSource('$testPackageLibPath/a.dart', r'''
+    addSource('$testPackageLibPath/a.dart', '''
 class A {}
 ''');
-    addSource('$testPackageLibPath/b.dart', r'''
+    addSource('$testPackageLibPath/b.dart', '''
 import 'package:test/a.dart';
 
 class B {
@@ -126,7 +380,7 @@
 
   void existingMethod() {}
 }
-''');
+''', matchFixMessage: 'Create constructor to call super.named(...)');
   }
 
   Future<void> test_namedOptionalParams() async {
@@ -222,7 +476,8 @@
   C(this.x);
 }
 class D extends C<int> {
-}''');
+}
+''');
     await assertHasFix('''
 class C<T> {
   final T x;
@@ -230,6 +485,36 @@
 }
 class D extends C<int> {
   D(int x) : super(x);
-}''');
+}
+''');
+  }
+
+  Future<void> test_underscore() async {
+    await resolveTestCode('''
+class A {
+  int _;
+  A(this._);
+  int get field => _;
+}
+class B extends A {
+  int existingField = 0;
+
+  void existingMethod() {}
+}
+''');
+    await assertHasFix('''
+class A {
+  int _;
+  A(this._);
+  int get field => _;
+}
+class B extends A {
+  int existingField = 0;
+
+  B(int _) : super(_);
+
+  void existingMethod() {}
+}
+''');
   }
 }
diff --git a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
index 8bd5f64..4f1543a 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
@@ -58,6 +58,7 @@
 import 'convert_into_expression_body_test.dart' as convert_into_expression_body;
 import 'convert_into_is_not_test.dart' as convert_into_is_not;
 import 'convert_quotes_test.dart' as convert_quotes;
+import 'convert_to_cascade_test.dart' as convert_to_cascade;
 import 'convert_to_contains_test.dart' as convert_to_contains;
 import 'convert_to_double_quoted_string_test.dart'
     as convert_to_double_quoted_string;
@@ -274,6 +275,7 @@
     convert_into_expression_body.main();
     convert_into_is_not.main();
     convert_quotes.main();
+    convert_to_cascade.main();
     convert_to_contains.main();
     convert_to_double_quoted_string.main();
     convert_to_for_element.main();
diff --git a/pkg/analyzer/lib/dart/analysis/features.dart b/pkg/analyzer/lib/dart/analysis/features.dart
index bb407f0..211cc17 100644
--- a/pkg/analyzer/lib/dart/analysis/features.dart
+++ b/pkg/analyzer/lib/dart/analysis/features.dart
@@ -37,6 +37,9 @@
   /// Feature information for generic metadata.
   static final generic_metadata = ExperimentalFeatures.generic_metadata;
 
+  /// Feature information for inference-update-1.
+  static final inference_update_1 = ExperimentalFeatures.inference_update_1;
+
   /// Feature information for macros.
   static final macros = ExperimentalFeatures.macros;
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
index 6867d3b..52fb27d 100644
--- a/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
@@ -163,7 +163,7 @@
 
     List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo =
         _isIdentical(node) ? [] : null;
-    _visitArguments(
+    var deferredClosures = _visitArguments(
         resolver: resolver,
         node: node,
         argumentList: argumentList,
@@ -173,6 +173,18 @@
         identicalInfo: identicalInfo,
         substitution: substitution,
         inferrer: inferrer);
+    if (deferredClosures != null) {
+      _resolveDeferredClosures(
+          resolver: resolver,
+          node: node,
+          argumentList: argumentList,
+          rawType: rawType,
+          contextType: contextType,
+          deferredClosures: deferredClosures,
+          identicalInfo: identicalInfo,
+          substitution: substitution,
+          inferrer: inferrer);
+    }
 
     if (inferrer != null) {
       typeArgumentTypes = inferrer.upwardsInfer();
@@ -335,13 +347,22 @@
     required DartType? contextType,
     required List<WhyNotPromotedGetter> whyNotPromotedList,
   }) {
-    _visitArguments(
+    var deferredClosures = _visitArguments(
         resolver: resolver,
         node: node,
         argumentList: argumentList,
         rawType: rawType,
         contextType: contextType,
         whyNotPromotedList: whyNotPromotedList);
+    if (deferredClosures != null) {
+      _resolveDeferredClosures(
+          resolver: resolver,
+          node: node,
+          argumentList: argumentList,
+          rawType: rawType,
+          contextType: contextType,
+          deferredClosures: deferredClosures);
+    }
   }
 
   /// Computes the type context that should be used when evaluating a particular
@@ -371,10 +392,49 @@
     }
   }
 
+  /// Resolves any closures that were deferred by [_visitArguments].
+  void _resolveDeferredClosures(
+      {required ResolverVisitor resolver,
+      required Node node,
+      required ArgumentListImpl argumentList,
+      required FunctionType? rawType,
+      required DartType? contextType,
+      required List<_DeferredClosure> deferredClosures,
+      List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo,
+      Substitution? substitution,
+      GenericInferrer? inferrer}) {
+    var flow = resolver.flowAnalysis.flow;
+    var arguments = argumentList.arguments;
+    for (var deferredArgument in deferredClosures) {
+      var parameter = deferredArgument.parameter;
+      DartType? parameterContextType;
+      if (parameter != null) {
+        var parameterType = parameter.type;
+        if (substitution != null) {
+          parameterType = substitution.substituteType(parameterType);
+        }
+        parameterContextType = _computeContextForArgument(
+            resolver, node, parameterType, contextType);
+      }
+      var argument = arguments[deferredArgument.index];
+      resolver.analyzeExpression(argument, parameterContextType);
+      // In case of rewrites, we need to grab the argument again.
+      argument = arguments[deferredArgument.index];
+      if (flow != null) {
+        identicalInfo?[deferredArgument.index] =
+            flow.equalityOperand_end(argument, argument.typeOrThrow);
+      }
+      if (parameter != null) {
+        inferrer?.constrainArgument(
+            argument.typeOrThrow, parameter.type, parameter.name);
+      }
+    }
+  }
+
   /// Visits [argumentList], resolving each argument.  If any arguments need to
   /// be deferred due to the `inference-update-1` feature, a list of them is
   /// returned.
-  void _visitArguments(
+  List<_DeferredClosure>? _visitArguments(
       {required ResolverVisitor resolver,
       required Node node,
       required ArgumentListImpl argumentList,
@@ -385,6 +445,7 @@
       Substitution? substitution,
       GenericInferrer? inferrer}) {
     assert(whyNotPromotedList.isEmpty);
+    List<_DeferredClosure>? deferredClosures;
     var parameters = rawType?.parameters;
     var namedParameters = <String, ParameterElement>{};
     if (parameters != null) {
@@ -401,40 +462,56 @@
     var arguments = argumentList.arguments;
     for (int i = 0; i < arguments.length; i++) {
       var argument = arguments[i];
+      Expression value;
       ParameterElement? parameter;
       if (argument is NamedExpression) {
+        value = argument.expression;
         parameter = namedParameters[argument.name.label.name];
-      } else if (parameters != null) {
-        while (positionalParameterIndex < parameters.length) {
-          var candidate = parameters[positionalParameterIndex++];
-          if (!candidate.isNamed) {
-            parameter = candidate;
-            break;
+      } else {
+        value = argument;
+        if (parameters != null) {
+          while (positionalParameterIndex < parameters.length) {
+            var candidate = parameters[positionalParameterIndex++];
+            if (!candidate.isNamed) {
+              parameter = candidate;
+              break;
+            }
           }
         }
       }
-      DartType? parameterContextType;
-      if (parameter != null) {
-        var parameterType = parameter.type;
-        if (substitution != null) {
-          parameterType = substitution.substituteType(parameterType);
+      if (resolver.isInferenceUpdate1Enabled &&
+          value is FunctionExpressionImpl) {
+        (deferredClosures ??= []).add(_DeferredClosure(parameter, value, i));
+        identicalInfo?.add(null);
+        // The "why not promoted" list isn't really relevant for closures
+        // because promoting a closure doesn't even make sense.  So we store an
+        // innocuous value in the list.
+        whyNotPromotedList.add(() => const {});
+      } else {
+        DartType? parameterContextType;
+        if (parameter != null) {
+          var parameterType = parameter.type;
+          if (substitution != null) {
+            parameterType = substitution.substituteType(parameterType);
+          }
+          parameterContextType = _computeContextForArgument(
+              resolver, node, parameterType, contextType);
         }
-        parameterContextType = _computeContextForArgument(
-            resolver, node, parameterType, contextType);
-      }
-      resolver.analyzeExpression(argument, parameterContextType);
-      // In case of rewrites, we need to grab the argument again.
-      argument = arguments[i];
-      if (flow != null) {
-        identicalInfo
-            ?.add(flow.equalityOperand_end(argument, argument.typeOrThrow));
-        whyNotPromotedList.add(flow.whyNotPromoted(argument));
-      }
-      if (parameter != null) {
-        inferrer?.constrainArgument(
-            argument.typeOrThrow, parameter.type, parameter.name);
+        resolver.analyzeExpression(argument, parameterContextType);
+        // In case of rewrites, we need to grab the argument again.
+        argument = arguments[i];
+        if (flow != null) {
+          identicalInfo
+              ?.add(flow.equalityOperand_end(argument, argument.typeOrThrow));
+          whyNotPromotedList.add(flow.whyNotPromoted(argument));
+        }
+        if (parameter != null) {
+          inferrer?.constrainArgument(
+              argument.typeOrThrow, parameter.type, parameter.name);
+        }
       }
     }
+    return deferredClosures;
   }
 
   /// Computes the return type of the method or function represented by the
@@ -498,3 +575,19 @@
     return returnType;
   }
 }
+
+/// Information about an invocation argument that needs to be resolved later due
+/// to the fact that it's a closure and the `inference-update-1` feature is
+/// enabled.
+class _DeferredClosure {
+  /// The [ParameterElement] the closure is being passed to.
+  final ParameterElement? parameter;
+
+  /// The closure expression.
+  final FunctionExpression value;
+
+  /// The index into the argument list of the closure expression.
+  final int index;
+
+  _DeferredClosure(this.parameter, this.value, this.index);
+}
diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart
index 94c717d..64e21e7 100644
--- a/pkg/analyzer/lib/src/error/codes.g.dart
+++ b/pkg/analyzer/lib/src/error/codes.g.dart
@@ -1657,6 +1657,7 @@
     "Concrete classes can't have 'Enum' as a superinterface.",
     correctionMessage:
         "Try specifying a different interface, or remove it from the list.",
+    hasPublishedDocs: true,
   );
 
   /**
@@ -3850,6 +3851,7 @@
     'ENUM_CONSTANT_WITH_NON_CONST_CONSTRUCTOR',
     "The invoked constructor isn't a 'const' constructor.",
     correctionMessage: "Try invoking a 'const' generative constructor.",
+    hasPublishedDocs: true,
   );
 
   static const CompileTimeErrorCode
@@ -3908,6 +3910,7 @@
     'ENUM_MIXIN_WITH_INSTANCE_VARIABLE',
     "Mixins applied to enums can't have instance variables.",
     correctionMessage: "Try replacing the instance variables with getters.",
+    hasPublishedDocs: true,
   );
 
   /**
@@ -3950,6 +3953,7 @@
     'ENUM_WITH_ABSTRACT_MEMBER',
     "'{0}' must have a method body because '{1}' is an enum.",
     correctionMessage: "Try adding a body to '{0}'.",
+    hasPublishedDocs: true,
   );
 
   // #### Description
@@ -3977,6 +3981,7 @@
     'ENUM_WITH_NAME_VALUES',
     "The name 'values' is not a valid name for an enum.",
     correctionMessage: "Try using a different name.",
+    hasPublishedDocs: true,
   );
 
   /**
@@ -6182,6 +6187,7 @@
     "A concrete instance member named '{0}' can't be declared in a class that "
         "implements 'Enum'.",
     correctionMessage: "Try using a different name.",
+    hasPublishedDocs: true,
     uniqueName: 'ILLEGAL_CONCRETE_ENUM_MEMBER_DECLARATION',
   );
 
@@ -6196,6 +6202,7 @@
     "A concrete instance member named '{0}' can't be inherited from '{1}' in a "
         "class that implements 'Enum'.",
     correctionMessage: "Try using a different name.",
+    hasPublishedDocs: true,
     uniqueName: 'ILLEGAL_CONCRETE_ENUM_MEMBER_INHERITANCE',
   );
 
@@ -6242,6 +6249,7 @@
     "An instance member named 'values' can't be declared in a class that "
         "implements 'Enum'.",
     correctionMessage: "Try using a different name.",
+    hasPublishedDocs: true,
     uniqueName: 'ILLEGAL_ENUM_VALUES_DECLARATION',
   );
 
@@ -6255,6 +6263,7 @@
     "An instance member named 'values' can't be inherited from '{0}' in a "
         "class that implements 'Enum'.",
     correctionMessage: "Try using a different name.",
+    hasPublishedDocs: true,
     uniqueName: 'ILLEGAL_ENUM_VALUES_INHERITANCE',
   );
 
@@ -6565,6 +6574,7 @@
         "parameters.",
     correctionMessage:
         "Try adding an explicit super parameter with the required arguments.",
+    hasPublishedDocs: true,
   );
 
   /**
@@ -8265,6 +8275,7 @@
     'INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR',
     "Generative enum constructors can only be used as targets of redirection.",
     correctionMessage: "Try using an enum constant, or a factory constructor.",
+    hasPublishedDocs: true,
   );
 
   /**
@@ -8396,6 +8407,7 @@
     correctionMessage:
         "Try removing the 'super' modifier, or changing the constructor to be "
         "non-redirecting and generative.",
+    hasPublishedDocs: true,
   );
 
   /**
@@ -11195,6 +11207,7 @@
     'NON_CONST_GENERATIVE_ENUM_CONSTRUCTOR',
     "Generative enum constructors must be 'const'.",
     correctionMessage: "Try adding the keyword 'const'.",
+    hasPublishedDocs: true,
   );
 
   /**
@@ -11255,6 +11268,7 @@
     'NON_FINAL_FIELD_IN_ENUM',
     "Enums can only declare final fields.",
     correctionMessage: "Try making the field final.",
+    hasPublishedDocs: true,
   );
 
   /**
@@ -12778,6 +12792,7 @@
     correctionMessage:
         "Try making all the positional parameters passed to the super "
         "constructor be either all super parameters or all normal parameters.",
+    hasPublishedDocs: true,
   );
 
   /**
@@ -14507,6 +14522,7 @@
         "associated super constructor parameter.",
     correctionMessage:
         "Try removing the explicit type annotation from the parameter.",
+    hasPublishedDocs: true,
   );
 
   /**
@@ -14584,6 +14600,7 @@
     correctionMessage:
         "Try changing the name to the name of an existing named super "
         "constructor parameter, or creating such named parameter.",
+    hasPublishedDocs: true,
   );
 
   /**
@@ -14667,6 +14684,7 @@
     correctionMessage:
         "Try using a normal parameter, or adding more positional parameters to "
         "the super constructor.",
+    hasPublishedDocs: true,
   );
 
   /**
@@ -14763,6 +14781,7 @@
     'SUPER_IN_ENUM_CONSTRUCTOR',
     "The enum constructor can't have a 'super' initializer.",
     correctionMessage: "Try removing the 'super' invocation.",
+    hasPublishedDocs: true,
   );
 
   /**
@@ -15915,6 +15934,7 @@
     correctionMessage:
         "Try correcting the name to the name of an existing constructor, or "
         "defining constructor with the name '{0}'.",
+    hasPublishedDocs: true,
     uniqueName: 'UNDEFINED_ENUM_CONSTRUCTOR_NAMED',
   );
 
@@ -15925,6 +15945,7 @@
     correctionMessage:
         "Try adding the name of an existing constructor, or defining an "
         "unnamed constructor.",
+    hasPublishedDocs: true,
     uniqueName: 'UNDEFINED_ENUM_CONSTRUCTOR_UNNAMED',
   );
 
@@ -17194,6 +17215,7 @@
     'VALUES_DECLARATION_IN_ENUM',
     "A member named 'values' can't be declared in an enum.",
     correctionMessage: "Try using a different name.",
+    hasPublishedDocs: true,
   );
 
   /**
@@ -17555,6 +17577,7 @@
     "The enum is declared with {0} type parameters, but {1} type arguments "
         "were given.",
     correctionMessage: "Try adjusting the number of type arguments.",
+    hasPublishedDocs: true,
   );
 
   /**
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 6b2ba66..3c61b28 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -382,6 +382,9 @@
   bool get isConstructorTearoffsEnabled =>
       _featureSet.isEnabled(Feature.constructor_tearoffs);
 
+  bool get isInferenceUpdate1Enabled =>
+      _featureSet.isEnabled(Feature.inference_update_1);
+
   /// Return the object providing promoted or declared types of variables.
   LocalVariableTypeProvider get localVariableTypeProvider {
     return flowAnalysis.localVariableTypeProvider;
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index 67a45e2..c4921ef 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -1559,6 +1559,7 @@
   NON_CONST_GENERATIVE_ENUM_CONSTRUCTOR:
     problemMessage: Generative enum constructors must be 'const'.
     correctionMessage: Try adding the keyword 'const'.
+    hasPublishedDocs: true
     documentation: |-
       #### Description
       
@@ -1673,6 +1674,7 @@
   CONCRETE_CLASS_HAS_ENUM_SUPERINTERFACE:
     problemMessage: "Concrete classes can't have 'Enum' as a superinterface."
     correctionMessage: Try specifying a different interface, or remove it from the list.
+    hasPublishedDocs: true
     documentation: |-
       #### Description
       
@@ -3535,6 +3537,7 @@
   ENUM_CONSTANT_WITH_NON_CONST_CONSTRUCTOR:
     problemMessage: The invoked constructor isn't a 'const' constructor.
     correctionMessage: Try invoking a 'const' generative constructor.
+    hasPublishedDocs: true
     documentation: |-
       #### Description
       
@@ -3574,6 +3577,7 @@
   ENUM_MIXIN_WITH_INSTANCE_VARIABLE:
     problemMessage: Mixins applied to enums can't have instance variables.
     correctionMessage: Try replacing the instance variables with getters.
+    hasPublishedDocs: true
     documentation: |-
       #### Description
       
@@ -3622,6 +3626,7 @@
   ENUM_WITH_ABSTRACT_MEMBER:
     problemMessage: "'{0}' must have a method body because '{1}' is an enum."
     correctionMessage: "Try adding a body to '{0}'."
+    hasPublishedDocs: true
     comment: |-
       Parameters:
       0: the name of the abstract method
@@ -3660,6 +3665,7 @@
   ENUM_WITH_NAME_VALUES:
     problemMessage: The name 'values' is not a valid name for an enum.
     correctionMessage: Try using a different name.
+    hasPublishedDocs: true
     documentation: |-
       #### Description
       
@@ -5422,6 +5428,7 @@
     sharedName: ILLEGAL_ENUM_VALUES
     problemMessage: An instance member named 'values' can't be declared in a class that implements 'Enum'.
     correctionMessage: Try using a different name.
+    hasPublishedDocs: true
     documentation: |-
       #### Description
       
@@ -5464,6 +5471,7 @@
     sharedName: ILLEGAL_ENUM_VALUES
     problemMessage: An instance member named 'values' can't be inherited from '{0}' in a class that implements 'Enum'.
     correctionMessage: Try using a different name.
+    hasPublishedDocs: true
     comment: |-
       Parameters:
       0: the name of the class that declares 'values'
@@ -5474,6 +5482,7 @@
     sharedName: ILLEGAL_CONCRETE_ENUM_MEMBER
     problemMessage: A concrete instance member named '{0}' can't be declared in a class that implements 'Enum'.
     correctionMessage: Try using a different name.
+    hasPublishedDocs: true
     comment: |-
       Parameters:
       0: the name of member that cannot be declared
@@ -5543,6 +5552,7 @@
     sharedName: ILLEGAL_CONCRETE_ENUM_MEMBER
     problemMessage: A concrete instance member named '{0}' can't be inherited from '{1}' in a class that implements 'Enum'.
     correctionMessage: Try using a different name.
+    hasPublishedDocs: true
     comment: |-
       Parameters:
       0: the name of member that cannot be inherited
@@ -5753,6 +5763,7 @@
   IMPLICIT_SUPER_INITIALIZER_MISSING_ARGUMENTS:
     problemMessage: The implicitly invoked unnamed constructor from '{0}' has required parameters.
     correctionMessage: Try adding an explicit super parameter with the required arguments.
+    hasPublishedDocs: true
     comment: |-
       Parameters:
       0: the name of the superclass
@@ -7208,6 +7219,7 @@
   INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR:
     problemMessage: Generative enum constructors can only be used as targets of redirection.
     correctionMessage: Try using an enum constant, or a factory constructor.
+    hasPublishedDocs: true
     documentation: |-
       #### Description
       
@@ -7296,6 +7308,7 @@
   INVALID_SUPER_FORMAL_PARAMETER_LOCATION:
     problemMessage: Super parameters can only be used in non-redirecting generative constructors.
     correctionMessage: Try removing the 'super' modifier, or changing the constructor to be non-redirecting and generative.
+    hasPublishedDocs: true
     comment: |-
       Parameters:
       0: the super modifier
@@ -9600,6 +9613,7 @@
   NON_FINAL_FIELD_IN_ENUM:
     problemMessage: Enums can only declare final fields.
     correctionMessage: Try making the field final.
+    hasPublishedDocs: true
     comment: No parameters.
     documentation: |-
       #### Description
@@ -10913,6 +10927,7 @@
   POSITIONAL_SUPER_FORMAL_PARAMETER_WITH_POSITIONAL_ARGUMENT:
     problemMessage: Positional super parameters can't be used when the super constructor invocation has a positional argument.
     correctionMessage: Try making all the positional parameters passed to the super constructor be either all super parameters or all normal parameters.
+    hasPublishedDocs: true
     comment: No parameters.
     documentation: |-
       #### Description
@@ -12414,6 +12429,7 @@
   SUPER_FORMAL_PARAMETER_TYPE_IS_NOT_SUBTYPE_OF_ASSOCIATED:
     problemMessage: The type '{0}' of this parameter isn't a subtype of the type '{1}' of the associated super constructor parameter.
     correctionMessage: Try removing the explicit type annotation from the parameter.
+    hasPublishedDocs: true
     comment: |-
       Parameters:
       0: the type of super-parameter
@@ -12485,6 +12501,7 @@
   SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_NAMED:
     problemMessage: No associated named super constructor parameter.
     correctionMessage: Try changing the name to the name of an existing named super constructor parameter, or creating such named parameter.
+    hasPublishedDocs: true
     comment: No parameters.
     documentation: |-
       #### Description
@@ -12555,6 +12572,7 @@
   SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_POSITIONAL:
     problemMessage: No associated positional super constructor parameter.
     correctionMessage: Try using a normal parameter, or adding more positional parameters to the super constructor.
+    hasPublishedDocs: true
     comment: No parameters.
     documentation: |-
       #### Description
@@ -12872,6 +12890,7 @@
   SUPER_IN_ENUM_CONSTRUCTOR:
     problemMessage: The enum constructor can't have a 'super' initializer.
     correctionMessage: Try removing the 'super' invocation.
+    hasPublishedDocs: true
     comment: No parameters.
     documentation: |-
       #### Description
@@ -13801,6 +13820,7 @@
     sharedName: UNDEFINED_ENUM_CONSTRUCTOR
     problemMessage: The enum doesn't have a constructor named '{0}'.
     correctionMessage: Try correcting the name to the name of an existing constructor, or defining constructor with the name '{0}'.
+    hasPublishedDocs: true
     comment: |-
       Parameters:
       0: the name of the constructor that is undefined
@@ -13891,6 +13911,7 @@
     sharedName: UNDEFINED_ENUM_CONSTRUCTOR
     problemMessage: The enum doesn't have an unnamed constructor.
     correctionMessage: Try adding the name of an existing constructor, or defining an unnamed constructor.
+    hasPublishedDocs: true
   UNDEFINED_EXTENSION_GETTER:
     problemMessage: "The getter '{0}' isn't defined for the extension '{1}'."
     correctionMessage: "Try correcting the name to the name of an existing getter, or defining a getter named '{0}'."
@@ -14967,6 +14988,7 @@
   VALUES_DECLARATION_IN_ENUM:
     problemMessage: A member named 'values' can't be declared in an enum.
     correctionMessage: Try using a different name.
+    hasPublishedDocs: true
     documentation: |-
       #### Description
       
@@ -15258,6 +15280,7 @@
   WRONG_NUMBER_OF_TYPE_ARGUMENTS_ENUM:
     problemMessage: The enum is declared with {0} type parameters, but {1} type arguments were given.
     correctionMessage: Try adjusting the number of type arguments.
+    hasPublishedDocs: true
     comment: |-
       Parameters:
       0: the number of type parameters that were declared
diff --git a/pkg/analyzer/test/src/dart/resolution/type_inference/inference_update_1_test.dart b/pkg/analyzer/test/src/dart/resolution/type_inference/inference_update_1_test.dart
new file mode 100644
index 0000000..209ee0f
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/type_inference/inference_update_1_test.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(HorizontalInferenceEnabledTest);
+    defineReflectiveTests(HorizontalInferenceDisabledTest);
+  });
+}
+
+@reflectiveTest
+class HorizontalInferenceDisabledTest extends PubPackageResolutionTest
+    with HorizontalInferenceTestCases {
+  @override
+  String get testPackageLanguageVersion => '2.17';
+}
+
+@reflectiveTest
+class HorizontalInferenceEnabledTest extends PubPackageResolutionTest
+    with HorizontalInferenceTestCases {
+  @override
+  List<String> get experiments =>
+      [...super.experiments, EnableString.inference_update_1];
+}
+
+mixin HorizontalInferenceTestCases on PubPackageResolutionTest {
+  bool get _isEnabled => experiments.contains(EnableString.inference_update_1);
+
+  test_closure_passed_to_identical() async {
+    await assertNoErrorsInCode('''
+test() => identical(() {}, () {});
+''');
+    // No further assertions; we just want to make sure the interaction between
+    // flow analysis for `identical` and deferred analysis of closures doesn't
+    // lead to a crash.
+  }
+
+  test_write_capture_deferred() async {
+    await assertNoErrorsInCode('''
+test(int? i) {
+  if (i != null) {
+    f(() { i = null; }, i); // (1)
+    i; // (2)
+  }
+}
+void f(void Function() g, Object? x) {}
+''');
+    // With the feature enabled, analysis of the closure is deferred until after
+    // all the other arguments to `f`, so the `i` at (1) is not yet write
+    // captured and retains its promoted value.  With the experiment disabled,
+    // it is write captured immediately.
+    assertType(findNode.simple('i); // (1)'), _isEnabled ? 'int' : 'int?');
+    // At (2), after the call to `f`, the write capture has taken place
+    // regardless of whether the experiment is enabled.
+    assertType(findNode.simple('i; // (2)'), 'int?');
+  }
+
+  test_write_capture_deferred_named() async {
+    await assertNoErrorsInCode('''
+test(int? i) {
+  if (i != null) {
+    f(g: () { i = null; }, x: i); // (1)
+    i; // (2)
+  }
+}
+void f({required void Function() g, Object? x}) {}
+''');
+    // With the feature enabled, analysis of the closure is deferred until after
+    // all the other arguments to `f`, so the `i` at (1) is not yet write
+    // captured and retains its promoted value.  With the experiment disabled,
+    // it is write captured immediately.
+    assertType(findNode.simple('i); // (1)'), _isEnabled ? 'int' : 'int?');
+    // At (2), after the call to `f`, the write capture has taken place
+    // regardless of whether the experiment is enabled.
+    assertType(findNode.simple('i; // (2)'), 'int?');
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/type_inference/test_all.dart b/pkg/analyzer/test/src/dart/resolution/type_inference/test_all.dart
index 6c94826..ade0cef 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_inference/test_all.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_inference/test_all.dart
@@ -11,6 +11,7 @@
 import 'extension_methods_test.dart' as extension_methods;
 import 'function_expression_test.dart' as function_expression;
 import 'function_test.dart' as function;
+import 'inference_update_1_test.dart' as inference_update_1;
 import 'list_literal_test.dart' as list_literal;
 import 'local_variable_test.dart' as local_variable;
 import 'logical_boolean_expressions_test.dart' as logical_boolean_expressions;
@@ -29,6 +30,7 @@
     extension_methods.main();
     function.main();
     function_expression.main();
+    inference_update_1.main();
     list_literal.main();
     local_variable.main();
     logical_boolean_expressions.main();
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 7abb1f8..54d8b99 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -24,7 +24,7 @@
     show ConstraintData;
 import 'deferred_load/program_split_constraints/parser.dart' as psc show Parser;
 import 'diagnostics/code_location.dart';
-import 'diagnostics/messages.dart' show Message, MessageTemplate;
+import 'diagnostics/messages.dart' show Message;
 import 'dump_info.dart' show DumpInfoTask;
 import 'elements/entities.dart';
 import 'enqueue.dart' show Enqueuer, ResolutionEnqueuer;
@@ -36,7 +36,6 @@
 import 'inferrer/typemasks/masks.dart' show TypeMaskStrategy;
 import 'inferrer/types.dart'
     show GlobalTypeInferenceResults, GlobalTypeInferenceTask;
-import 'io/source_information.dart' show SourceInformation;
 import 'ir/modular.dart';
 import 'js_backend/backend.dart' show CodegenInputs;
 import 'js_backend/enqueuer.dart';
@@ -54,16 +53,12 @@
 import 'serialization/task.dart';
 import 'serialization/serialization.dart';
 import 'serialization/strategies.dart';
-import 'ssa/nodes.dart' show HInstruction;
 import 'universe/selector.dart' show Selector;
 import 'universe/codegen_world_builder.dart';
 import 'universe/resolution_world_builder.dart';
 import 'universe/world_impact.dart' show WorldImpact, WorldImpactBuilderImpl;
 import 'world.dart' show JClosedWorld;
 
-typedef MakeReporterFunction = CompilerDiagnosticReporter Function(
-    Compiler compiler, CompilerOptions options);
-
 /// Implementation of the compiler using  a [api.CompilerInput] for supplying
 /// the sources.
 class Compiler {
@@ -73,7 +68,7 @@
 
   KernelFrontendStrategy frontendStrategy;
   JsBackendStrategy backendStrategy;
-  CompilerDiagnosticReporter _reporter;
+  DiagnosticReporter _reporter;
   Map<Entity, WorldImpact> _impactCache;
   GenericTask userHandlerTask;
   GenericTask userProviderTask;
@@ -122,8 +117,6 @@
   DumpInfoTask dumpInfoTask;
   SerializationTask serializationTask;
 
-  bool get hasCrashed => _reporter.hasCrashed;
-
   Progress progress = const Progress();
 
   static const int PHASE_SCANNING = 0;
@@ -142,8 +135,7 @@
   // Callback function used for testing codegen enqueuing.
   void Function() onCodegenQueueEmptyForTesting;
 
-  Compiler(this.provider, this._outputProvider, this.handler, this.options,
-      {MakeReporterFunction makeReporter})
+  Compiler(this.provider, this._outputProvider, this.handler, this.options)
       // NOTE: allocating measurer is done upfront to ensure the wallclock is
       // started before other computations.
       : measurer = Measurer(enableTaskMeasurements: options.verbose),
@@ -164,11 +156,7 @@
     CompilerTask kernelFrontEndTask;
     selfTask = GenericTask('self', measurer);
     _outputProvider = _CompilerOutput(this, outputProvider);
-    if (makeReporter != null) {
-      _reporter = makeReporter(this, options);
-    } else {
-      _reporter = CompilerDiagnosticReporter(this);
-    }
+    _reporter = DiagnosticReporter(this);
     kernelFrontEndTask = GenericTask('Front end', measurer);
     frontendStrategy = KernelFrontendStrategy(
         kernelFrontEndTask, options, reporter, environment);
@@ -950,293 +938,6 @@
   }
 }
 
-/// Information about suppressed warnings and hints for a given library.
-class SuppressionInfo {
-  int warnings = 0;
-  int hints = 0;
-}
-
-class CompilerDiagnosticReporter extends DiagnosticReporter {
-  final Compiler compiler;
-  @override
-  CompilerOptions get options => compiler.options;
-
-  Entity _currentElement;
-  bool hasCrashed = false;
-
-  /// `true` if the last diagnostic was filtered, in which case the
-  /// accompanying info message should be filtered as well.
-  bool lastDiagnosticWasFiltered = false;
-
-  /// Map containing information about the warnings and hints that have been
-  /// suppressed for each library.
-  Map<Uri, SuppressionInfo> suppressedWarnings = <Uri, SuppressionInfo>{};
-
-  CompilerDiagnosticReporter(this.compiler);
-
-  Entity get currentElement => _currentElement;
-
-  @override
-  DiagnosticMessage createMessage(Spannable spannable, MessageKind messageKind,
-      [Map<String, String> arguments = const {}]) {
-    SourceSpan span = spanFromSpannable(spannable);
-    MessageTemplate template = MessageTemplate.TEMPLATES[messageKind];
-    Message message = template.message(arguments, options);
-    return DiagnosticMessage(span, spannable, message);
-  }
-
-  @override
-  void reportError(DiagnosticMessage message,
-      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
-    reportDiagnosticInternal(message, infos, api.Diagnostic.ERROR);
-  }
-
-  @override
-  void reportWarning(DiagnosticMessage message,
-      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
-    reportDiagnosticInternal(message, infos, api.Diagnostic.WARNING);
-  }
-
-  @override
-  void reportHint(DiagnosticMessage message,
-      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
-    reportDiagnosticInternal(message, infos, api.Diagnostic.HINT);
-  }
-
-  @override
-  void reportInfo(DiagnosticMessage message,
-      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
-    reportDiagnosticInternal(message, infos, api.Diagnostic.INFO);
-  }
-
-  void reportDiagnosticInternal(DiagnosticMessage message,
-      List<DiagnosticMessage> infos, api.Diagnostic kind) {
-    if (!options.showAllPackageWarnings &&
-        message.spannable != NO_LOCATION_SPANNABLE) {
-      switch (kind) {
-        case api.Diagnostic.WARNING:
-        case api.Diagnostic.HINT:
-          Entity element = elementFromSpannable(message.spannable);
-          if (!compiler.inUserCode(element, assumeInUserCode: true)) {
-            Uri uri = compiler.getCanonicalUri(element);
-            if (options.showPackageWarningsFor(uri)) {
-              reportDiagnostic(message, infos, kind);
-              return;
-            }
-            SuppressionInfo info =
-                suppressedWarnings.putIfAbsent(uri, () => SuppressionInfo());
-            if (kind == api.Diagnostic.WARNING) {
-              info.warnings++;
-            } else {
-              info.hints++;
-            }
-            lastDiagnosticWasFiltered = true;
-            return;
-          }
-          break;
-        case api.Diagnostic.INFO:
-          if (lastDiagnosticWasFiltered) {
-            return;
-          }
-          break;
-      }
-    }
-    lastDiagnosticWasFiltered = false;
-    reportDiagnostic(message, infos, kind);
-  }
-
-  void reportDiagnostic(DiagnosticMessage message,
-      List<DiagnosticMessage> infos, api.Diagnostic kind) {
-    compiler.reportDiagnostic(message, infos, kind);
-    if (kind == api.Diagnostic.ERROR ||
-        kind == api.Diagnostic.CRASH ||
-        (options.fatalWarnings && kind == api.Diagnostic.WARNING)) {
-      compiler.fatalDiagnosticReported(message, infos, kind);
-    }
-  }
-
-  @override
-  bool get hasReportedError => compiler.compilationFailed;
-
-  /// Perform an operation, [f], returning the return value from [f].  If an
-  /// error occurs then report it as having occurred during compilation of
-  /// [element].  Can be nested.
-  @override
-  withCurrentElement(Entity element, f()) {
-    Entity old = currentElement;
-    _currentElement = element;
-    try {
-      return f();
-    } on SpannableAssertionFailure catch (ex) {
-      if (!hasCrashed) {
-        reportAssertionFailure(ex);
-        pleaseReportCrash();
-      }
-      hasCrashed = true;
-      rethrow;
-    } on StackOverflowError {
-      // We cannot report anything useful in this case, because we
-      // do not have enough stack space.
-      rethrow;
-    } catch (ex) {
-      if (hasCrashed) rethrow;
-      try {
-        unhandledExceptionOnElement(element);
-      } catch (doubleFault) {
-        // Ignoring exceptions in exception handling.
-      }
-      rethrow;
-    } finally {
-      _currentElement = old;
-    }
-  }
-
-  void reportAssertionFailure(SpannableAssertionFailure ex) {
-    String message =
-        (ex.message != null) ? tryToString(ex.message) : tryToString(ex);
-    reportDiagnosticInternal(
-        createMessage(ex.node, MessageKind.GENERIC, {'text': message}),
-        const <DiagnosticMessage>[],
-        api.Diagnostic.CRASH);
-  }
-
-  /// Using [frontendStrategy] to compute a [SourceSpan] from spannable using
-  /// the [currentElement] as context.
-  SourceSpan _spanFromStrategy(Spannable spannable) {
-    SourceSpan span;
-    if (compiler.phase == Compiler.PHASE_COMPILING) {
-      span =
-          compiler.backendStrategy.spanFromSpannable(spannable, currentElement);
-    } else {
-      span = compiler.frontendStrategy
-          .spanFromSpannable(spannable, currentElement);
-    }
-    if (span != null) return span;
-    throw 'No error location.';
-  }
-
-  @override
-  SourceSpan spanFromSpannable(Spannable spannable) {
-    if (spannable == CURRENT_ELEMENT_SPANNABLE) {
-      spannable = currentElement;
-    } else if (spannable == NO_LOCATION_SPANNABLE) {
-      if (currentElement == null) return null;
-      spannable = currentElement;
-    }
-    if (spannable is SourceSpan) {
-      return spannable;
-    } else if (spannable is HInstruction) {
-      Entity element = spannable.sourceElement;
-      if (element == null) element = currentElement;
-      SourceInformation position = spannable.sourceInformation;
-      if (position != null) return position.sourceSpan;
-      return _spanFromStrategy(element);
-    } else {
-      return _spanFromStrategy(spannable);
-    }
-  }
-
-  @override
-  internalError(Spannable spannable, reason) {
-    String message = tryToString(reason);
-    reportDiagnosticInternal(
-        createMessage(spannable, MessageKind.GENERIC, {'text': message}),
-        const <DiagnosticMessage>[],
-        api.Diagnostic.CRASH);
-    throw 'Internal Error: $message';
-  }
-
-  void unhandledExceptionOnElement(Entity element) {
-    if (hasCrashed) return;
-    hasCrashed = true;
-    reportDiagnostic(createMessage(element, MessageKind.COMPILER_CRASHED),
-        const <DiagnosticMessage>[], api.Diagnostic.CRASH);
-    pleaseReportCrash();
-  }
-
-  void pleaseReportCrash() {
-    print(MessageTemplate.TEMPLATES[MessageKind.PLEASE_REPORT_THE_CRASH]
-        .message({'buildId': compiler.options.buildId}, options));
-  }
-
-  /// Finds the approximate [Element] for [node]. [currentElement] is used as
-  /// the default value.
-  Entity elementFromSpannable(Spannable node) {
-    Entity element;
-    if (node is Entity) {
-      element = node;
-    } else if (node is HInstruction) {
-      element = node.sourceElement;
-    }
-    return element ?? currentElement;
-  }
-
-  @override
-  void log(message) {
-    Message msg = MessageTemplate.TEMPLATES[MessageKind.GENERIC]
-        .message({'text': '$message'}, options);
-    reportDiagnostic(DiagnosticMessage(null, null, msg),
-        const <DiagnosticMessage>[], api.Diagnostic.VERBOSE_INFO);
-  }
-
-  String tryToString(object) {
-    try {
-      return object.toString();
-    } catch (_) {
-      return '<exception in toString()>';
-    }
-  }
-
-  onError(Uri uri, error, StackTrace stackTrace) {
-    try {
-      if (!hasCrashed) {
-        hasCrashed = true;
-        if (error is SpannableAssertionFailure) {
-          reportAssertionFailure(error);
-        } else {
-          reportDiagnostic(
-              createMessage(
-                  SourceSpan(uri, 0, 0), MessageKind.COMPILER_CRASHED),
-              const <DiagnosticMessage>[],
-              api.Diagnostic.CRASH);
-        }
-        pleaseReportCrash();
-      }
-    } catch (doubleFault) {
-      // Ignoring exceptions in exception handling.
-    }
-    return Future.error(error, stackTrace);
-  }
-
-  @override
-  void onCrashInUserCode(String message, exception, stackTrace) {
-    hasCrashed = true;
-    print('$message: ${tryToString(exception)}');
-    print(tryToString(stackTrace));
-  }
-
-  void reportSuppressedMessagesSummary() {
-    if (!options.showAllPackageWarnings && !options.suppressWarnings) {
-      suppressedWarnings.forEach((Uri uri, SuppressionInfo info) {
-        MessageKind kind = MessageKind.HIDDEN_WARNINGS_HINTS;
-        if (info.warnings == 0) {
-          kind = MessageKind.HIDDEN_HINTS;
-        } else if (info.hints == 0) {
-          kind = MessageKind.HIDDEN_WARNINGS;
-        }
-        MessageTemplate template = MessageTemplate.TEMPLATES[kind];
-        Message message = template.message({
-          'warnings': info.warnings.toString(),
-          'hints': info.hints.toString(),
-          'uri': uri.toString(),
-        }, options);
-        reportDiagnostic(DiagnosticMessage(null, null, message),
-            const <DiagnosticMessage>[], api.Diagnostic.HINT);
-      });
-    }
-  }
-}
-
 class _TimingData {
   final String description;
   final int milliseconds;
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index 11c2d38..b78f0fc 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -1527,6 +1527,7 @@
   fromCodegenAndData,
   fromCodegenAndClosedWorldAndData,
 }
+
 enum WriteStrategy {
   toKernel,
   toModularAnalysis,
diff --git a/pkg/compiler/lib/src/diagnostics/diagnostic_listener.dart b/pkg/compiler/lib/src/diagnostics/diagnostic_listener.dart
index c425106..4b895a4 100644
--- a/pkg/compiler/lib/src/diagnostics/diagnostic_listener.dart
+++ b/pkg/compiler/lib/src/diagnostics/diagnostic_listener.dart
@@ -4,72 +4,313 @@
 
 library dart2js.diagnostic_listener;
 
+import '../../compiler.dart' as api;
+import '../compiler.dart' show Compiler;
 import '../elements/entities.dart';
-import '../options.dart' show DiagnosticOptions;
+import '../io/source_information.dart';
+import '../options.dart';
+import '../ssa/nodes.dart' show HInstruction;
 import 'messages.dart';
 import 'source_span.dart' show SourceSpan;
-import 'spannable.dart' show Spannable;
+import 'spannable.dart';
 
-// TODO(johnniwinther): Rename and cleanup this interface. Add severity enum.
-abstract class DiagnosticReporter {
-  DiagnosticOptions get options;
+class DiagnosticReporter {
+  final Compiler _compiler;
 
-  // TODO(karlklose): rename log to something like reportInfo.
-  void log(message);
+  CompilerOptions get options => _compiler.options;
 
-  internalError(Spannable spannable, message);
+  Entity _currentElement;
+  bool _hasCrashed = false;
 
-  /// Creates a [SourceSpan] for [node] in scope of the current element.
-  ///
-  /// If [node] is a [Node] we assert in checked mode that the corresponding
-  /// tokens can be found within the tokens of the current element.
-  SourceSpan spanFromSpannable(Spannable node);
+  /// `true` if the last diagnostic was filtered, in which case the
+  /// accompanying info message should be filtered as well.
+  bool _lastDiagnosticWasFiltered = false;
+
+  /// Map containing information about the warnings and hints that have been
+  /// suppressed for each library.
+  final Map<Uri, SuppressionInfo> _suppressedWarnings = {};
+
+  DiagnosticReporter(this._compiler);
+
+  Entity get currentElement => _currentElement;
+
+  DiagnosticMessage createMessage(Spannable spannable, MessageKind messageKind,
+      [Map<String, String> arguments = const {}]) {
+    SourceSpan span = spanFromSpannable(spannable);
+    MessageTemplate template = MessageTemplate.TEMPLATES[messageKind];
+    Message message = template.message(arguments, options);
+    return DiagnosticMessage(span, spannable, message);
+  }
+
+  void reportError(DiagnosticMessage message,
+      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
+    _reportDiagnosticInternal(message, infos, api.Diagnostic.ERROR);
+  }
 
   void reportErrorMessage(Spannable spannable, MessageKind messageKind,
       [Map<String, String> arguments = const {}]) {
     reportError(createMessage(spannable, messageKind, arguments));
   }
 
-  void reportError(DiagnosticMessage message,
-      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]);
+  void reportWarning(DiagnosticMessage message,
+      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
+    _reportDiagnosticInternal(message, infos, api.Diagnostic.WARNING);
+  }
 
   void reportWarningMessage(Spannable spannable, MessageKind messageKind,
       [Map<String, String> arguments = const {}]) {
     reportWarning(createMessage(spannable, messageKind, arguments));
   }
 
-  void reportWarning(DiagnosticMessage message,
-      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]);
+  void reportHint(DiagnosticMessage message,
+      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
+    _reportDiagnosticInternal(message, infos, api.Diagnostic.HINT);
+  }
 
   void reportHintMessage(Spannable spannable, MessageKind messageKind,
       [Map<String, String> arguments = const {}]) {
     reportHint(createMessage(spannable, messageKind, arguments));
   }
 
-  void reportHint(DiagnosticMessage message,
-      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]);
+  void reportInfo(DiagnosticMessage message,
+      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
+    _reportDiagnosticInternal(message, infos, api.Diagnostic.INFO);
+  }
 
   void reportInfoMessage(Spannable node, MessageKind errorCode,
       [Map<String, String> arguments = const {}]) {
     reportInfo(createMessage(node, errorCode, arguments));
   }
 
-  void reportInfo(DiagnosticMessage message,
-      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]);
+  void _reportDiagnosticInternal(DiagnosticMessage message,
+      List<DiagnosticMessage> infos, api.Diagnostic kind) {
+    if (!options.showAllPackageWarnings &&
+        message.spannable != NO_LOCATION_SPANNABLE) {
+      switch (kind) {
+        case api.Diagnostic.WARNING:
+        case api.Diagnostic.HINT:
+          Entity element = _elementFromSpannable(message.spannable);
+          if (!_compiler.inUserCode(element, assumeInUserCode: true)) {
+            Uri uri = _compiler.getCanonicalUri(element);
+            if (options.showPackageWarningsFor(uri)) {
+              _reportDiagnostic(message, infos, kind);
+              return;
+            }
+            SuppressionInfo info =
+                _suppressedWarnings.putIfAbsent(uri, () => SuppressionInfo());
+            if (kind == api.Diagnostic.WARNING) {
+              info.warnings++;
+            } else {
+              info.hints++;
+            }
+            _lastDiagnosticWasFiltered = true;
+            return;
+          }
+          break;
+        case api.Diagnostic.INFO:
+          if (_lastDiagnosticWasFiltered) {
+            return;
+          }
+          break;
+      }
+    }
+    _lastDiagnosticWasFiltered = false;
+    _reportDiagnostic(message, infos, kind);
+  }
 
-  /// Set current element of this reporter to [element]. This is used for
-  /// creating [SourceSpan] in [spanFromSpannable].
-  withCurrentElement(Entity element, f());
-
-  DiagnosticMessage createMessage(Spannable spannable, MessageKind messageKind,
-      [Map<String, String> arguments = const {}]);
+  void _reportDiagnostic(DiagnosticMessage message,
+      List<DiagnosticMessage> infos, api.Diagnostic kind) {
+    _compiler.reportDiagnostic(message, infos, kind);
+    if (kind == api.Diagnostic.ERROR ||
+        kind == api.Diagnostic.CRASH ||
+        (options.fatalWarnings && kind == api.Diagnostic.WARNING)) {
+      _compiler.fatalDiagnosticReported(message, infos, kind);
+    }
+  }
 
   /// Returns `true` if a crash, an error or a fatal warning has been reported.
-  bool get hasReportedError;
+  bool get hasReportedError => _compiler.compilationFailed;
+
+  /// Set current element of this reporter to [element]. This is used for
+  /// creating [SourceSpan] in [spanFromSpannable]. That is,
+  /// [withCurrentElement] performs an operation, [f], returning the return
+  /// value from [f].  If an error occurs then report it as having occurred
+  /// during compilation of [element].  Can be nested.
+  dynamic withCurrentElement(Entity element, dynamic f()) {
+    Entity old = currentElement;
+    _currentElement = element;
+    try {
+      return f();
+    } on SpannableAssertionFailure catch (ex) {
+      if (!_hasCrashed) {
+        _reportAssertionFailure(ex);
+        _pleaseReportCrash();
+      }
+      _hasCrashed = true;
+      rethrow;
+    } on StackOverflowError {
+      // We cannot report anything useful in this case, because we
+      // do not have enough stack space.
+      rethrow;
+    } catch (ex) {
+      if (_hasCrashed) rethrow;
+      try {
+        _unhandledExceptionOnElement(element);
+      } catch (doubleFault) {
+        // Ignoring exceptions in exception handling.
+      }
+      rethrow;
+    } finally {
+      _currentElement = old;
+    }
+  }
+
+  void _reportAssertionFailure(SpannableAssertionFailure ex) {
+    String message =
+        (ex.message != null) ? tryToString(ex.message) : tryToString(ex);
+    _reportDiagnosticInternal(
+        createMessage(ex.node, MessageKind.GENERIC, {'text': message}),
+        const <DiagnosticMessage>[],
+        api.Diagnostic.CRASH);
+  }
+
+  /// Using [frontendStrategy] to compute a [SourceSpan] from spannable using
+  /// the [currentElement] as context.
+  SourceSpan _spanFromStrategy(Spannable spannable) {
+    SourceSpan span;
+    if (_compiler.phase == Compiler.PHASE_COMPILING) {
+      span = _compiler.backendStrategy
+          .spanFromSpannable(spannable, currentElement);
+    } else {
+      span = _compiler.frontendStrategy
+          .spanFromSpannable(spannable, currentElement);
+    }
+    if (span != null) return span;
+    throw 'No error location.';
+  }
+
+  /// Creates a [SourceSpan] for [node] in scope of the current element.
+  ///
+  /// If [node] is a [Node] we assert in checked mode that the corresponding
+  /// tokens can be found within the tokens of the current element.
+  SourceSpan spanFromSpannable(Spannable spannable) {
+    if (spannable == CURRENT_ELEMENT_SPANNABLE) {
+      spannable = currentElement;
+    } else if (spannable == NO_LOCATION_SPANNABLE) {
+      if (currentElement == null) return null;
+      spannable = currentElement;
+    }
+    if (spannable is SourceSpan) {
+      return spannable;
+    } else if (spannable is HInstruction) {
+      Entity element = spannable.sourceElement;
+      if (element == null) element = currentElement;
+      SourceInformation position = spannable.sourceInformation;
+      if (position != null) return position.sourceSpan;
+      return _spanFromStrategy(element);
+    } else {
+      return _spanFromStrategy(spannable);
+    }
+  }
+
+  dynamic internalError(Spannable spannable, reason) {
+    String message = tryToString(reason);
+    _reportDiagnosticInternal(
+        createMessage(spannable, MessageKind.GENERIC, {'text': message}),
+        const <DiagnosticMessage>[],
+        api.Diagnostic.CRASH);
+    throw 'Internal Error: $message';
+  }
+
+  void _unhandledExceptionOnElement(Entity element) {
+    if (_hasCrashed) return;
+    _hasCrashed = true;
+    _reportDiagnostic(createMessage(element, MessageKind.COMPILER_CRASHED),
+        const <DiagnosticMessage>[], api.Diagnostic.CRASH);
+    _pleaseReportCrash();
+  }
+
+  void _pleaseReportCrash() {
+    print(MessageTemplate.TEMPLATES[MessageKind.PLEASE_REPORT_THE_CRASH]
+        .message({'buildId': _compiler.options.buildId}, options));
+  }
+
+  /// Finds the approximate [Element] for [node]. [currentElement] is used as
+  /// the default value.
+  Entity _elementFromSpannable(Spannable node) {
+    Entity element;
+    if (node is Entity) {
+      element = node;
+    } else if (node is HInstruction) {
+      element = node.sourceElement;
+    }
+    return element ?? currentElement;
+  }
+
+  void log(message) {
+    Message msg = MessageTemplate.TEMPLATES[MessageKind.GENERIC]
+        .message({'text': '$message'}, options);
+    _reportDiagnostic(DiagnosticMessage(null, null, msg),
+        const <DiagnosticMessage>[], api.Diagnostic.VERBOSE_INFO);
+  }
+
+  String tryToString(object) {
+    try {
+      return object.toString();
+    } catch (_) {
+      return '<exception in toString()>';
+    }
+  }
+
+  Future onError(Uri uri, error, StackTrace stackTrace) {
+    try {
+      if (!_hasCrashed) {
+        _hasCrashed = true;
+        if (error is SpannableAssertionFailure) {
+          _reportAssertionFailure(error);
+        } else {
+          _reportDiagnostic(
+              createMessage(
+                  SourceSpan(uri, 0, 0), MessageKind.COMPILER_CRASHED),
+              const <DiagnosticMessage>[],
+              api.Diagnostic.CRASH);
+        }
+        _pleaseReportCrash();
+      }
+    } catch (doubleFault) {
+      // Ignoring exceptions in exception handling.
+    }
+    return Future.error(error, stackTrace);
+  }
 
   /// Called when an [exception] is thrown from user-provided code, like from
   /// the input provider or diagnostics handler.
-  void onCrashInUserCode(String message, exception, stackTrace) {}
+  void onCrashInUserCode(String message, exception, stackTrace) {
+    _hasCrashed = true;
+    print('$message: ${tryToString(exception)}');
+    print(tryToString(stackTrace));
+  }
+
+  void reportSuppressedMessagesSummary() {
+    if (!options.showAllPackageWarnings && !options.suppressWarnings) {
+      _suppressedWarnings.forEach((Uri uri, SuppressionInfo info) {
+        MessageKind kind = MessageKind.HIDDEN_WARNINGS_HINTS;
+        if (info.warnings == 0) {
+          kind = MessageKind.HIDDEN_HINTS;
+        } else if (info.hints == 0) {
+          kind = MessageKind.HIDDEN_WARNINGS;
+        }
+        MessageTemplate template = MessageTemplate.TEMPLATES[kind];
+        Message message = template.message({
+          'warnings': info.warnings.toString(),
+          'hints': info.hints.toString(),
+          'uri': uri.toString(),
+        }, options);
+        _reportDiagnostic(DiagnosticMessage(null, null, message),
+            const <DiagnosticMessage>[], api.Diagnostic.HINT);
+      });
+    }
+  }
 }
 
 class DiagnosticMessage {
@@ -79,3 +320,9 @@
 
   DiagnosticMessage(this.sourceSpan, this.spannable, this.message);
 }
+
+/// Information about suppressed warnings and hints for a given library.
+class SuppressionInfo {
+  int warnings = 0;
+  int hints = 0;
+}
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index 18d2044..b24e672 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -81,6 +81,10 @@
   /// Whether to emit JavaScript encoded as UTF-8.
   FeatureOption writeUtf8 = FeatureOption('utf8');
 
+  /// Experimental instrumentation to add tree shaking information to
+  /// dump-info's output.
+  FeatureOption newDumpInfo = FeatureOption('new-dump-info');
+
   /// [FeatureOption]s which are shipped and cannot be toggled.
   late final List<FeatureOption> shipped = [
     newHolders,
@@ -93,9 +97,7 @@
   ];
 
   /// [FeatureOption]s which default to disabled.
-  late final List<FeatureOption> canary = [
-    writeUtf8,
-  ];
+  late final List<FeatureOption> canary = [writeUtf8, newDumpInfo];
 
   /// Forces canary feature on. This must run after [Option].parse.
   void forceCanary() {
diff --git a/pkg/compiler/test/deferred_loading/deferred_loading_test_helper.dart b/pkg/compiler/test/deferred_loading/deferred_loading_test_helper.dart
index 435002d..92c1cb6 100644
--- a/pkg/compiler/test/deferred_loading/deferred_loading_test_helper.dart
+++ b/pkg/compiler/test/deferred_loading/deferred_loading_test_helper.dart
@@ -341,7 +341,7 @@
 /// corresponding to [object] at location [sourceSpan]. We also perform error
 /// checking to ensure that the same [id] isn't added twice.
 void _registerValue<T>(Id id, T value, Object object, SourceSpan sourceSpan,
-    Map<Id, ActualData<T>> actualMap, CompilerDiagnosticReporter reporter) {
+    Map<Id, ActualData<T>> actualMap, DiagnosticReporter reporter) {
   if (actualMap.containsKey(id)) {
     ActualData<T> existingData = actualMap[id];
     reportHere(reporter, sourceSpan,
diff --git a/pkg/compiler/test/end_to_end/diagnostic_reporter_helper.dart b/pkg/compiler/test/end_to_end/diagnostic_reporter_helper.dart
deleted file mode 100644
index ba061e2..0000000
--- a/pkg/compiler/test/end_to_end/diagnostic_reporter_helper.dart
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// @dart = 2.7
-
-library dart2js.diagnostic_reporter.helper;
-
-import 'package:compiler/src/diagnostics/diagnostic_listener.dart';
-import 'package:compiler/src/diagnostics/messages.dart';
-import 'package:compiler/src/diagnostics/source_span.dart';
-import 'package:compiler/src/diagnostics/spannable.dart';
-import 'package:compiler/src/elements/entities.dart';
-import 'package:compiler/src/options.dart';
-
-abstract class DiagnosticReporterWrapper extends DiagnosticReporter {
-  DiagnosticReporter get reporter;
-
-  @override
-  DiagnosticMessage createMessage(Spannable spannable, MessageKind messageKind,
-      [Map<String, String> arguments = const {}]) {
-    return reporter.createMessage(spannable, messageKind, arguments);
-  }
-
-  @override
-  internalError(Spannable spannable, message) {
-    return reporter.internalError(spannable, message);
-  }
-
-  @override
-  void log(message) {
-    return reporter.log(message);
-  }
-
-  @override
-  DiagnosticOptions get options => reporter.options;
-
-  @override
-  void reportError(DiagnosticMessage message,
-      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
-    reporter.reportError(message, infos);
-  }
-
-  @override
-  void reportHint(DiagnosticMessage message,
-      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
-    reporter.reportHint(message, infos);
-  }
-
-  @override
-  void reportInfo(DiagnosticMessage message,
-      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
-    reporter.reportInfo(message, infos);
-  }
-
-  @override
-  void reportInfoMessage(Spannable node, MessageKind errorCode,
-      [Map<String, String> arguments = const {}]) {
-    // ignore: deprecated_member_use_from_same_package
-    reporter.reportInfoMessage(node, errorCode, arguments);
-  }
-
-  @override
-  void reportWarning(DiagnosticMessage message,
-      [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
-    reporter.reportWarning(message, infos);
-  }
-
-  @override
-  SourceSpan spanFromSpannable(Spannable node) {
-    return reporter.spanFromSpannable(node);
-  }
-
-  @override
-  withCurrentElement(Entity element, f()) {
-    return reporter.withCurrentElement(element, f);
-  }
-
-  @override
-  bool get hasReportedError => reporter.hasReportedError;
-}
diff --git a/pkg/compiler/test/end_to_end/exit_code_test.dart b/pkg/compiler/test/end_to_end/exit_code_test.dart
index be0974f..b42b5fd 100644
--- a/pkg/compiler/test/end_to_end/exit_code_test.dart
+++ b/pkg/compiler/test/end_to_end/exit_code_test.dart
@@ -28,7 +28,6 @@
 import 'package:compiler/src/options.dart' show CompilerOptions;
 import 'package:compiler/src/universe/world_impact.dart';
 import 'package:compiler/src/world.dart';
-import 'diagnostic_reporter_helper.dart';
 import '../helpers/memory_compiler.dart';
 
 class TestCompiler extends Compiler {
@@ -46,10 +45,8 @@
       String this.testMarker,
       String this.testType,
       Function this.onTest)
-      : reporter = new TestDiagnosticReporter(),
-        super(inputProvider, outputProvider, handler, options) {
-    reporter.compiler = this;
-    reporter.reporter = super.reporter;
+      : super(inputProvider, outputProvider, handler, options) {
+    reporter = new TestDiagnosticReporter(this);
     test('Compiler');
   }
 
@@ -122,10 +119,10 @@
   }
 }
 
-class TestDiagnosticReporter extends DiagnosticReporterWrapper {
+class TestDiagnosticReporter extends DiagnosticReporter {
   TestCompiler compiler;
-  @override
-  DiagnosticReporter reporter;
+
+  TestDiagnosticReporter(this.compiler) : super(compiler);
 
   @override
   withCurrentElement(Entity element, f()) {
diff --git a/pkg/compiler/test/js/js_spec_string_test.dart b/pkg/compiler/test/js/js_spec_string_test.dart
index ad274e5..8ae300c 100644
--- a/pkg/compiler/test/js/js_spec_string_test.dart
+++ b/pkg/compiler/test/js/js_spec_string_test.dart
@@ -18,6 +18,8 @@
 const NULL = 'Null';
 
 class Listener extends DiagnosticReporter {
+  Listener() : super(null);
+
   String errorMessage;
   @override
   internalError(spannable, message) {
diff --git a/pkg/dds/test/devtools_server/devtools_server_driver.dart b/pkg/dds/test/devtools_server/devtools_server_driver.dart
index 3a287ca..a5d44b8 100644
--- a/pkg/dds/test/devtools_server/devtools_server_driver.dart
+++ b/pkg/dds/test/devtools_server/devtools_server_driver.dart
@@ -210,6 +210,7 @@
         .where((e) => e.kind == EventKind.kServiceRegistered)
         .listen((e) => registeredServices[e.service!] = e.method!);
     await appFixture.serviceConnection.streamListen(EventStreams.kService);
+    await appFixture.onAppStarted;
   }
 
   int nextId = 0;
diff --git a/pkg/js_ast/lib/src/characters.dart b/pkg/js_ast/lib/src/characters.dart
index 5fd1196..aaa068d 100644
--- a/pkg/js_ast/lib/src/characters.dart
+++ b/pkg/js_ast/lib/src/characters.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// @dart=2.15
+
 library js_character_codes;
 
 const int $EOF = 0;
diff --git a/pkg/js_ast/lib/src/equivalence_visitor.dart b/pkg/js_ast/lib/src/equivalence_visitor.dart
index ac23703..5f67e72 100644
--- a/pkg/js_ast/lib/src/equivalence_visitor.dart
+++ b/pkg/js_ast/lib/src/equivalence_visitor.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// @dart=2.15
+
 library js_ast.equivalence_visitor;
 
 import 'nodes.dart';
@@ -13,17 +15,17 @@
   /// Called when [node1] and [node2] are not equivalent.
   ///
   /// Override this to collect or report information on the inequivalent nodes.
-  bool failAt(Node node1, Node node2) => false;
+  bool failAt(Node? node1, Node? node2) => false;
 
   /// Returns whether the non-node values [value1] and [value2] are equivalent.
-  bool testValues(Node node1, Object value1, Node node2, Object value2) =>
+  bool testValues(Node node1, Object? value1, Node node2, Object? value2) =>
       value1 == value2;
 
   /// Returns whether the labels [label1] and [label2] are equivalent.
-  bool testLabels(Node node1, String label1, Node node2, String label2) =>
+  bool testLabels(Node node1, String? label1, Node node2, String? label2) =>
       label1 == label2;
 
-  bool testNodes(Node node1, Node node2) {
+  bool testNodes(Node? node1, Node? node2) {
     if (identical(node1, node2)) return true;
     if (node1 == null || node2 == null) return failAt(node1, node2);
     return node1.accept1(this, node2);
diff --git a/pkg/js_ast/lib/src/nodes.dart b/pkg/js_ast/lib/src/nodes.dart
index b8fe27a..6d5d5ac 100644
--- a/pkg/js_ast/lib/src/nodes.dart
+++ b/pkg/js_ast/lib/src/nodes.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// @dart=2.15
+
 library js_ast.nodes;
 
 import 'precedence.dart';
@@ -561,9 +563,9 @@
 }
 
 abstract class Node {
-  JavaScriptNodeSourceInformation get sourceInformation => _sourceInformation;
+  JavaScriptNodeSourceInformation? get sourceInformation => _sourceInformation;
 
-  JavaScriptNodeSourceInformation _sourceInformation;
+  JavaScriptNodeSourceInformation? _sourceInformation;
 
   T accept<T>(NodeVisitor<T> visitor);
   void visitChildren<T>(NodeVisitor<T> visitor);
@@ -696,9 +698,7 @@
 class ExpressionStatement extends Statement {
   final Expression expression;
 
-  ExpressionStatement(this.expression) {
-    assert(this.expression != null);
-  }
+  ExpressionStatement(this.expression);
 
   @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitExpressionStatement(this);
@@ -779,14 +779,13 @@
 
 abstract class Loop extends Statement {
   final Statement body;
-
   Loop(this.body);
 }
 
 class For extends Loop {
-  final Expression init;
-  final Expression condition;
-  final Expression update;
+  final Expression? init;
+  final Expression? condition;
+  final Expression? update;
 
   For(this.init, this.condition, this.update, Statement body) : super(body);
 
@@ -851,7 +850,7 @@
 }
 
 class While extends Loop {
-  final Node condition;
+  final Expression condition;
 
   While(this.condition, Statement body) : super(body);
 
@@ -907,7 +906,8 @@
 }
 
 class Continue extends Statement {
-  final String targetLabel; // Can be null.
+  /// Name of the label L for `continue L;` or `null` for `continue;`.
+  final String? targetLabel;
 
   Continue(this.targetLabel);
 
@@ -929,7 +929,8 @@
 }
 
 class Break extends Statement {
-  final String targetLabel; // Can be null.
+  /// Name of the label L for `break L;` or `null` for `break;`.
+  final String? targetLabel;
 
   Break(this.targetLabel);
 
@@ -951,7 +952,8 @@
 }
 
 class Return extends Statement {
-  final Expression value; // Can be null.
+  /// The expression for `return expression;`, or `null` for `return;`.
+  final Expression? value;
 
   Return([this.value = null]);
 
@@ -1004,8 +1006,8 @@
 
 class Try extends Statement {
   final Block body;
-  final Catch catchPart; // Can be null if [finallyPart] is non-null.
-  final Block finallyPart; // Can be null if [catchPart] is non-null.
+  final Catch? catchPart; // Can be null if [finallyPart] is non-null.
+  final Block? finallyPart; // Can be null if [catchPart] is non-null.
 
   Try(this.body, this.catchPart, this.finallyPart) {
     assert(catchPart != null || finallyPart != null);
@@ -1310,7 +1312,7 @@
 class LiteralStringFromName extends LiteralString {
   final Name name;
 
-  LiteralStringFromName(this.name) : super(null) {
+  LiteralStringFromName(this.name) : super('') {
     ArgumentError.checkNotNull(name, 'name');
   }
 
@@ -1430,14 +1432,13 @@
 
 class Assignment extends Expression {
   final Expression leftHandSide;
-  final String op; // Null, if the assignment is not compound.
+  final String? op; // `null` if the assignment is not compound.
   final Expression value;
 
-  Assignment(leftHandSide, value) : this.compound(leftHandSide, null, value);
+  Assignment(this.leftHandSide, this.value) : op = null;
 
   // If `this.op == null` this will be a non-compound assignment.
-  Assignment.compound(this.leftHandSide, this.op, this.value)
-      : assert(value != null);
+  Assignment.compound(this.leftHandSide, this.op, this.value);
 
   @override
   int get precedenceLevel => ASSIGNMENT;
@@ -1471,7 +1472,8 @@
   // TODO(sra): Can [VariableInitialization] be a non-expression?
 
   final Declaration declaration;
-  final Expression value; // [value] may be null.
+  // The initializing value can be missing, e.g. for `a` in `var a, b=1;`.
+  final Expression? value;
 
   VariableInitialization(this.declaration, this.value);
 
@@ -1542,7 +1544,7 @@
   List<Expression> arguments;
 
   Call(this.target, this.arguments,
-      {JavaScriptNodeSourceInformation sourceInformation}) {
+      {JavaScriptNodeSourceInformation? sourceInformation}) {
     this._sourceInformation = sourceInformation;
   }
 
@@ -1843,9 +1845,9 @@
 }
 
 abstract class FunctionExpression extends Expression {
-  Node body;
-  List<Parameter> params;
-  AsyncModifier asyncModifier;
+  Node get body;
+  List<Parameter> get params;
+  AsyncModifier get asyncModifier;
 }
 
 class Fun extends FunctionExpression {
@@ -1935,7 +1937,7 @@
   final String description;
 
   const AsyncModifier(this.index, this.description,
-      {this.isAsync, this.isYielding});
+      {required this.isAsync, required this.isYielding});
 
   static const AsyncModifier sync =
       AsyncModifier(0, "sync", isAsync: false, isYielding: false);
diff --git a/pkg/js_ast/lib/src/precedence.dart b/pkg/js_ast/lib/src/precedence.dart
index 222f4a8..19c5c9f 100644
--- a/pkg/js_ast/lib/src/precedence.dart
+++ b/pkg/js_ast/lib/src/precedence.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// @dart=2.15
+
 library precedence;
 
 const EXPRESSION = 0;
diff --git a/pkg/js_ast/lib/src/printer.dart b/pkg/js_ast/lib/src/printer.dart
index bcfe5a8..67d0f9b 100644
--- a/pkg/js_ast/lib/src/printer.dart
+++ b/pkg/js_ast/lib/src/printer.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// @dart=2.15
+
 library js_ast.printer;
 
 import 'characters.dart' as charCodes;
@@ -49,7 +51,7 @@
   ///
   /// [enterNode] is called in post-traversal order.
   void exitNode(
-      Node node, int startPosition, int endPosition, int closingPosition) {}
+      Node node, int startPosition, int endPosition, int? closingPosition) {}
 
   /// Should return `true` if the printing tolerates unfinalized deferred AST
   /// nodes.
@@ -150,11 +152,10 @@
     if (!shouldCompressOutput) out(" ", isWhitespace: true);
   }
 
-  String lastAddedString = null;
+  String lastAddedString = '\u0000';
 
   int get lastCharCode {
-    if (lastAddedString == null) return 0;
-    assert(lastAddedString.length != 0);
+    assert(lastAddedString.isNotEmpty);
     return lastAddedString.codeUnitAt(lastAddedString.length - 1);
   }
 
@@ -227,7 +228,7 @@
     }
   }
 
-  EnterExitNode currentNode;
+  EnterExitNode? currentNode;
 
   void _emit(String text) {
     context.emit(text);
@@ -244,7 +245,7 @@
   }
 
   void enterNode() {
-    currentNode.addToNode(context, _charCount);
+    currentNode!.addToNode(context, _charCount);
   }
 
   void endNode(Node node) {
@@ -253,8 +254,8 @@
         endNode(node.value);
       }
     }
-    assert(currentNode.node == node);
-    currentNode = currentNode.exitNode(context, _charCount);
+    assert(currentNode!.node == node);
+    currentNode = currentNode!.exitNode(context, _charCount);
   }
 
   void visit(Node node) {
@@ -263,8 +264,8 @@
     endNode(node);
   }
 
-  void visitCommaSeparated(List<Node> nodes, int hasRequiredType,
-      {bool newInForInit, bool newAtStatementBegin}) {
+  void visitCommaSeparated(List<Expression> nodes, int hasRequiredType,
+      {required bool newInForInit, required bool newAtStatementBegin}) {
     for (int i = 0; i < nodes.length; i++) {
       if (i != 0) {
         atStatementBegin = false;
@@ -280,7 +281,7 @@
     nodes.forEach(visit);
   }
 
-  Node _undefer(Node node) {
+  Expression _undefer(Expression node) {
     if (isDebugContext && !node.isFinalized) return node;
     if (node is DeferredExpression) return _undefer(node.value);
     return node;
@@ -303,7 +304,8 @@
     return result;
   }
 
-  bool blockBody(Statement body, {bool needsSeparation, bool needsNewline}) {
+  bool blockBody(Statement body,
+      {required bool needsSeparation, required bool needsNewline}) {
     if (body is Block) {
       spaceOut();
       blockOut(body, shouldIndent: false, needsNewline: needsNewline);
@@ -338,7 +340,8 @@
     }
   }
 
-  int blockOut(Block node, {bool shouldIndent, bool needsNewline}) {
+  int blockOut(Block node,
+      {required bool shouldIndent, required bool needsNewline}) {
     if (shouldIndent) indent();
     startNode(node);
     out("{");
@@ -425,19 +428,19 @@
     spaceOut();
     out("(");
     if (loop.init != null) {
-      visitNestedExpression(loop.init, EXPRESSION,
+      visitNestedExpression(loop.init!, EXPRESSION,
           newInForInit: true, newAtStatementBegin: false);
     }
     out(";");
     if (loop.condition != null) {
       spaceOut();
-      visitNestedExpression(loop.condition, EXPRESSION,
+      visitNestedExpression(loop.condition!, EXPRESSION,
           newInForInit: false, newAtStatementBegin: false);
     }
     out(";");
     if (loop.update != null) {
       spaceOut();
-      visitNestedExpression(loop.update, EXPRESSION,
+      visitNestedExpression(loop.update!, EXPRESSION,
           newInForInit: false, newAtStatementBegin: false);
     }
     out(")");
@@ -513,16 +516,17 @@
 
   @override
   void visitReturn(Return node) {
-    if (node.value == null) {
+    final value = node.value;
+    if (value == null) {
       outIndent("return");
     } else {
       outIndent("return");
       pendingSpace = true;
-      visitNestedExpression(node.value, EXPRESSION,
+      visitNestedExpression(value, EXPRESSION,
           newInForInit: false, newAtStatementBegin: false);
     }
     // Set the closing position to be before the optional semicolon.
-    currentNode.closingPosition = _charCount;
+    currentNode!.closingPosition = _charCount;
     outSemicolonLn();
   }
 
@@ -553,12 +557,12 @@
     outIndent("try");
     blockBody(node.body, needsSeparation: true, needsNewline: false);
     if (node.catchPart != null) {
-      visit(node.catchPart);
+      visit(node.catchPart!);
     }
     if (node.finallyPart != null) {
       spaceOut();
       out("finally");
-      blockBody(node.finallyPart, needsSeparation: true, needsNewline: true);
+      blockBody(node.finallyPart!, needsSeparation: true, needsNewline: true);
     } else {
       lineOut();
     }
@@ -632,7 +636,7 @@
     blockBody(body, needsSeparation: false, needsNewline: true);
   }
 
-  int functionOut(Fun fun, Node name, VarCollector vars) {
+  int functionOut(Fun fun, Expression? name, VarCollector vars) {
     out("function");
     if (name != null) {
       out(" ");
@@ -642,10 +646,8 @@
     }
     localNamer.enterScope(vars);
     out("(");
-    if (fun.params != null) {
-      visitCommaSeparated(fun.params, PRIMARY,
-          newInForInit: false, newAtStatementBegin: false);
-    }
+    visitCommaSeparated(fun.params, PRIMARY,
+        newInForInit: false, newAtStatementBegin: false);
     out(")");
     switch (fun.asyncModifier) {
       case AsyncModifier.sync:
@@ -676,14 +678,14 @@
     vars.visitFunctionDeclaration(declaration);
     indent();
     startNode(declaration.function);
-    currentNode.closingPosition =
+    currentNode!.closingPosition =
         functionOut(declaration.function, declaration.name, vars);
     endNode(declaration.function);
     lineOut();
   }
 
   visitNestedExpression(Expression node, int requiredPrecedence,
-      {bool newInForInit, bool newAtStatementBegin}) {
+      {required bool newInForInit, required bool newAtStatementBegin}) {
     int precedenceLevel =
         (isDebugContext && !node.isFinalized) ? CALL : node.precedenceLevel;
     bool needsParentheses =
@@ -714,7 +716,7 @@
   @override
   visitVariableDeclarationList(VariableDeclarationList list) {
     out("var ");
-    List<Node> nodes = list.declarations;
+    final nodes = list.declarations;
     if (inForInit) {
       visitCommaSeparated(nodes, ASSIGNMENT,
           newInForInit: inForInit, newAtStatementBegin: false);
@@ -727,7 +729,7 @@
       }
       bool lastWasBig = false;
       for (int i = 0; i < nodes.length; i++) {
-        Node node = nodes[i];
+        Expression node = nodes[i];
         bool thisIsBig = !_isSmallInitialization(node);
         if (i > 0) {
           atStatementBegin = false;
@@ -752,8 +754,7 @@
   bool _isSmallInitialization(Node node) {
     if (node is VariableInitialization) {
       if (node.value == null) return true;
-      Node value = _undefer(node.value);
-      if (value == null) return true;
+      Node value = _undefer(node.value!);
       if (value is This) return true;
       if (value is LiteralNull) return true;
       if (value is LiteralNumber) return true;
@@ -765,7 +766,7 @@
     return false;
   }
 
-  void _outputIncDec(String op, Expression variable, [Expression alias]) {
+  void _outputIncDec(String op, Expression variable, [Expression? alias]) {
     if (op == '+') {
       if (lastCharCode == charCodes.$PLUS) out(" ", isWhitespace: true);
       out('++');
@@ -784,24 +785,23 @@
     /// To print assignments like `a = a + 1` and `a = a + b` compactly as
     /// `++a` and `a += b` in the face of [DeferredExpression]s we detect the
     /// pattern of the undeferred assignment.
-    String op = assignment.op;
+    String? op = assignment.op;
     Node leftHandSide = _undefer(assignment.leftHandSide);
-    Node value = assignment.value;
-    Node rightHandSide = value == null ? value : _undefer(value);
+    Node rightHandSide = _undefer(assignment.value);
     if ((op == '+' || op == '-') &&
         leftHandSide is VariableUse &&
         rightHandSide is LiteralNumber &&
         rightHandSide.value == "1") {
       // Output 'a += 1' as '++a' and 'a -= 1' as '--a'.
-      _outputIncDec(op, assignment.leftHandSide);
+      _outputIncDec(op!, assignment.leftHandSide);
       return;
     }
     if (!assignment.isCompound &&
         leftHandSide is VariableUse &&
         rightHandSide is Binary) {
-      Node rLeft = _undefer(rightHandSide.left);
-      Node rRight = _undefer(rightHandSide.right);
-      String op = rightHandSide.op;
+      Expression rLeft = _undefer(rightHandSide.left);
+      Expression rRight = _undefer(rightHandSide.right);
+      String? op = rightHandSide.op;
       if (op == '+' ||
           op == '-' ||
           op == '/' ||
@@ -835,14 +835,13 @@
     }
     visitNestedExpression(assignment.leftHandSide, CALL,
         newInForInit: inForInit, newAtStatementBegin: atStatementBegin);
-    if (value != null) {
-      spaceOut();
-      if (op != null) out(op);
-      out("=");
-      spaceOut();
-      visitNestedExpression(value, ASSIGNMENT,
-          newInForInit: inForInit, newAtStatementBegin: false);
-    }
+
+    spaceOut();
+    if (op != null) out(op);
+    out("=");
+    spaceOut();
+    visitNestedExpression(assignment.value, ASSIGNMENT,
+        newInForInit: inForInit, newAtStatementBegin: false);
   }
 
   @override
@@ -853,7 +852,7 @@
       spaceOut();
       out("=");
       spaceOut();
-      visitNestedExpression(initialization.value, ASSIGNMENT,
+      visitNestedExpression(initialization.value!, ASSIGNMENT,
           newInForInit: inForInit, newAtStatementBegin: false);
     }
   }
@@ -976,6 +975,8 @@
         rightPrecedenceRequirement = UNARY;
         break;
       default:
+        leftPrecedenceRequirement = EXPRESSION;
+        rightPrecedenceRequirement = EXPRESSION;
         context.error("Forgot operator: $op");
     }
 
@@ -1111,7 +1112,7 @@
     out(']');
   }
 
-  void _dotString(Node selector, Node receiver, String selectorValue,
+  void _dotString(Node selector, Expression receiver, String selectorValue,
       {bool assumeValid = false}) {
     if (assumeValid || isValidJavaScriptId(selectorValue)) {
       if (_undefer(receiver) is LiteralNumber &&
@@ -1134,41 +1135,41 @@
     VarCollector vars = VarCollector();
     vars.visitNamedFunction(namedFunction);
     startNode(namedFunction.function);
-    int closingPosition = currentNode.closingPosition =
+    int closingPosition = currentNode!.closingPosition =
         functionOut(namedFunction.function, namedFunction.name, vars);
     endNode(namedFunction.function);
     // Use closing position of `namedFunction.function` as the closing position
     // of the named function itself.
-    currentNode.closingPosition = closingPosition;
+    currentNode!.closingPosition = closingPosition;
   }
 
   @override
   void visitFun(Fun fun) {
     VarCollector vars = VarCollector();
     vars.visitFun(fun);
-    currentNode.closingPosition = functionOut(fun, null, vars);
+    currentNode!.closingPosition = functionOut(fun, null, vars);
   }
 
   @override
   void visitArrowFunction(ArrowFunction fun) {
     VarCollector vars = VarCollector();
     vars.visitArrowFunction(fun);
-    currentNode.closingPosition = arrowFunctionOut(fun, vars);
+    currentNode!.closingPosition = arrowFunctionOut(fun, vars);
   }
 
+  static bool _isIdentifierParameter(Node node) => node is VariableReference;
+
   int arrowFunctionOut(ArrowFunction fun, VarCollector vars) {
     // TODO: support static, get/set, async, and generators.
     localNamer.enterScope(vars);
     final List<Parameter> params = fun.params;
-    if (params.length == 1 && params.first is VariableReference) {
+    if (params.length == 1 && _isIdentifierParameter(params.first)) {
       visitNestedExpression(params.single, ASSIGNMENT,
           newInForInit: false, newAtStatementBegin: false);
     } else {
       out("(");
-      if (params != null) {
-        visitCommaSeparated(fun.params, PRIMARY,
-            newInForInit: false, newAtStatementBegin: false);
-      }
+      visitCommaSeparated(fun.params, PRIMARY,
+          newInForInit: false, newAtStatementBegin: false);
       out(")");
     }
     spaceOut();
@@ -1182,7 +1183,7 @@
     if (fun.implicitReturnAllowed && body is Block) {
       final statement = unwrapBlockIfSingleStatement(body);
       if (statement is Return) {
-        body = statement.value;
+        body = statement.value!;
       }
     }
     if (body is Block) {
@@ -1194,7 +1195,7 @@
       // https://tc39.github.io/ecma262/#sec-arrow-function-definitions
       bool needsParens = body is ObjectInitializer;
       if (needsParens) out("(");
-      visitNestedExpression(body, ASSIGNMENT,
+      visitNestedExpression(body as Expression, ASSIGNMENT,
           newInForInit: false, newAtStatementBegin: false);
       if (needsParens) out(")");
       closingPosition = _charCount;
@@ -1344,7 +1345,7 @@
     out("{");
     indentMore();
     for (int i = 0; i < properties.length; i++) {
-      Node value = properties[i].value;
+      Expression value = properties[i].value;
       if (isOneLiner && exitOneLinerMode(value)) isOneLiner = false;
       if (i != 0) {
         out(",");
@@ -1379,7 +1380,7 @@
     VarCollector vars = VarCollector();
     vars.visitMethodDefinition(node);
     startNode(node.function);
-    currentNode.closingPosition = methodOut(node, vars);
+    currentNode!.closingPosition = methodOut(node, vars);
     endNode(node.function);
   }
 
@@ -1388,10 +1389,8 @@
     Fun fun = node.function;
     localNamer.enterScope(vars);
     out("(");
-    if (fun.params != null) {
-      visitCommaSeparated(fun.params, PRIMARY,
-          newInForInit: false, newAtStatementBegin: false);
-    }
+    visitCommaSeparated(fun.params, PRIMARY,
+        newInForInit: false, newAtStatementBegin: false);
     out(")");
     spaceOut();
     int closingPosition =
@@ -1586,10 +1585,8 @@
   void collectVarsInFunction(FunctionExpression fun) {
     if (!nested) {
       nested = true;
-      if (fun.params != null) {
-        for (int i = 0; i < fun.params.length; i++) {
-          params.add(fun.params[i].name);
-        }
+      for (int i = 0; i < fun.params.length; i++) {
+        params.add(fun.params[i].name);
       }
       fun.body.accept(this);
       nested = false;
@@ -1697,9 +1694,9 @@
   @override
   bool visitTry(Try node) {
     if (node.finallyPart != null) {
-      return node.finallyPart.accept(this);
+      return node.finallyPart!.accept(this);
     } else {
-      return node.catchPart.accept(this);
+      return node.catchPart!.accept(this);
     }
   }
 
@@ -1874,30 +1871,28 @@
 
 /// Information pertaining the enter and exit callbacks for [node].
 class EnterExitNode {
-  final EnterExitNode parent;
+  final EnterExitNode? parent;
   final Node node;
 
-  int startPosition;
-  int closingPosition;
+  int? startPosition;
+  int? closingPosition;
 
   EnterExitNode(this.parent, this.node);
 
   void addToNode(JavaScriptPrintingContext context, int position) {
     if (startPosition == null) {
       // [position] is the start position of [node].
-      if (parent != null) {
-        // This might be the start position of the parent as well.
-        parent.addToNode(context, position);
-      }
+      // This might be the start position of the parent as well.
+      parent?.addToNode(context, position);
       startPosition = position;
       context.enterNode(node, position);
     }
   }
 
-  EnterExitNode exitNode(JavaScriptPrintingContext context, int position) {
+  EnterExitNode? exitNode(JavaScriptPrintingContext context, int position) {
     // Enter must happen before exit.
     addToNode(context, position);
-    context.exitNode(node, startPosition, position, closingPosition);
+    context.exitNode(node, startPosition!, position, closingPosition);
     return parent;
   }
 }
diff --git a/pkg/js_ast/lib/src/strings.dart b/pkg/js_ast/lib/src/strings.dart
index 91c2bae..871eea7 100644
--- a/pkg/js_ast/lib/src/strings.dart
+++ b/pkg/js_ast/lib/src/strings.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// @dart=2.15
+
 // Utilities for converting between JavaScript source-code Strings and the
 // String value they represent.
 
@@ -15,7 +17,7 @@
   /// [true] if contents require no escaping with the preferred quoting.
   final bool simple;
 
-  const StringToSourceKind({this.doubleQuotes, this.simple});
+  const StringToSourceKind({required this.doubleQuotes, required this.simple});
 
   String get quote => doubleQuotes ? '"' : "'";
 }
@@ -23,7 +25,7 @@
 class StringToSource {
   const StringToSource();
 
-  static StringToSourceKind analyze(String value, {/*required*/ bool utf8}) {
+  static StringToSourceKind analyze(String value, {required bool utf8}) {
     final ascii = !utf8;
     int singleQuotes = 0;
     int doubleQuotes = 0;
@@ -73,9 +75,9 @@
 
   static void writeString(
       StringBuffer sb, String string, StringToSourceKind kind,
-      {/*required*/ bool utf8}) {
+      {required bool utf8}) {
     for (int rune in string.runes) {
-      String escape = _irregularEscape(rune, kind.doubleQuotes);
+      String? escape = _irregularEscape(rune, kind.doubleQuotes);
       if (escape != null) {
         sb.write(escape);
         continue;
@@ -109,7 +111,7 @@
 
   static bool _isUnpairedSurrogate(int code) => (code & 0xFFFFF800) == 0xD800;
 
-  static String _irregularEscape(int code, bool useDoubleQuotes) {
+  static String? _irregularEscape(int code, bool useDoubleQuotes) {
     switch (code) {
       case charCodes.$SQ:
         return useDoubleQuotes ? r"'" : r"\'";
diff --git a/pkg/test_runner/lib/src/test_file.dart b/pkg/test_runner/lib/src/test_file.dart
index 3c71e5b..b39ba90 100644
--- a/pkg/test_runner/lib/src/test_file.dart
+++ b/pkg/test_runner/lib/src/test_file.dart
@@ -12,7 +12,7 @@
 final _vmOptionsRegExp = RegExp(r"// VMOptions=(.*)");
 final _environmentRegExp = RegExp(r"// Environment=(.*)");
 final _packagesRegExp = RegExp(r"// Packages=(.*)");
-final _experimentRegExp = RegExp(r"^--enable-experiment=([a-z,-]+)$");
+final _experimentRegExp = RegExp(r"^--enable-experiment=([a-z0-9,-]+)$");
 
 List<String> _splitWords(String s) =>
     s.split(' ').where((e) => e != '').toList();
diff --git a/tests/language/inference_update_1/write_capture_deferral_disabled_test.dart b/tests/language/inference_update_1/write_capture_deferral_disabled_test.dart
new file mode 100644
index 0000000..1083524
--- /dev/null
+++ b/tests/language/inference_update_1/write_capture_deferral_disabled_test.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Tests that when the feature is disabled, if an invocation argument is a
+// closure, write captures made by that closure take effect immediately after
+// the closure is visited
+
+// @dart=2.17
+
+import '../static_type_helper.dart';
+
+withUnnamedArguments(
+    int? i, void Function(Object?, void Function(), Object?) f) {
+  if (i != null) {
+    f(i..expectStaticType<Exactly<int>>(), () {
+      i = null;
+    }, i..expectStaticType<Exactly<int?>>());
+    i..expectStaticType<Exactly<int?>>();
+  }
+}
+
+withNamedArguments(int? i,
+    void Function({Object? x, required void Function() g, Object? y}) f) {
+  if (i != null) {
+    f(
+        x: i..expectStaticType<Exactly<int>>(),
+        g: () {
+          i = null;
+        },
+        y: i..expectStaticType<Exactly<int?>>());
+    i..expectStaticType<Exactly<int?>>();
+  }
+}
+
+withIdentical_lhs(int? i) {
+  if (i != null) {
+    i..expectStaticType<Exactly<int>>();
+    identical(() {
+      i = null;
+    }, i..expectStaticType<Exactly<int?>>());
+    i..expectStaticType<Exactly<int?>>();
+  }
+}
+
+withIdentical_rhs(int? i) {
+  if (i != null) {
+    identical(i..expectStaticType<Exactly<int>>(), () {
+      i = null;
+    });
+    i..expectStaticType<Exactly<int?>>();
+  }
+}
+
+main() {}
diff --git a/tests/language/inference_update_1/write_capture_deferral_enabled_test.dart b/tests/language/inference_update_1/write_capture_deferral_enabled_test.dart
new file mode 100644
index 0000000..3a93caa
--- /dev/null
+++ b/tests/language/inference_update_1/write_capture_deferral_enabled_test.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Tests that when the feature is enabled, if an invocation argument is a
+// closure, write captures made by that closure do not take effect until after
+// the invocation.  This is a minor improvement to flow analysis that falls
+// naturally out of the fact that closures are analyzed last (so that their
+// types can depend on the types of other arguments).
+
+// SharedOptions=--enable-experiment=inference-update-1
+
+import '../static_type_helper.dart';
+
+withUnnamedArguments(int? i, void Function(void Function(), Object?) f) {
+  if (i != null) {
+    f(() {
+      i = null;
+    }, i..expectStaticType<Exactly<int>>());
+    i..expectStaticType<Exactly<int?>>();
+  }
+}
+
+withNamedArguments(
+    int? i, void Function({required void Function() g, Object? x}) f) {
+  if (i != null) {
+    f(
+        g: () {
+          i = null;
+        },
+        x: i..expectStaticType<Exactly<int>>());
+    i..expectStaticType<Exactly<int?>>();
+  }
+}
+
+withIdentical_lhs(int? i) {
+  if (i != null) {
+    i..expectStaticType<Exactly<int>>();
+    identical(() {
+      i = null;
+    }, i..expectStaticType<Exactly<int>>());
+    i..expectStaticType<Exactly<int?>>();
+  }
+}
+
+withIdentical_rhs(int? i) {
+  if (i != null) {
+    identical(i..expectStaticType<Exactly<int>>(), () {
+      i = null;
+    });
+    i..expectStaticType<Exactly<int?>>();
+  }
+}
+
+main() {}
diff --git a/tools/VERSION b/tools/VERSION
index 78b37fe..f0cd312 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 241
+PRERELEASE 242
 PRERELEASE_PATCH 0
\ No newline at end of file