Version 2.17.0-241.0.dev

Merge commit 'ca99bde913e531e2f7717d1f491d1a5d58ba0649' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index f55d3ca..94f206f 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -40,6 +40,16 @@
     50,
     "Remove '{0}'",
   );
+  static const REPLACE_WITH_STRICT_CASTS = FixKind(
+    'analysisOptions.fix.replaceWithStrictCasts',
+    50,
+    'Replace with the strict-casts analysis mode',
+  );
+  static const REPLACE_WITH_STRICT_RAW_TYPES = FixKind(
+    'analysisOptions.fix.replaceWithStrictRawTypes',
+    50,
+    'Replace with the strict-raw-types analysis mode',
+  );
 }
 
 /// The implementation of [DartFixContext].
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/analysis_options/fix_generator.dart b/pkg/analysis_server/lib/src/services/correction/fix/analysis_options/fix_generator.dart
index 8c148db..5e36f57 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/analysis_options/fix_generator.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/analysis_options/fix_generator.dart
@@ -16,9 +16,12 @@
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/lint/options_rule_validator.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_yaml.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:collection/collection.dart';
 import 'package:yaml/yaml.dart';
+import 'package:yaml_edit/yaml_edit.dart';
 
 /// The generator used to generate fixes in analysis options files.
 class AnalysisOptionsFixGenerator {
@@ -67,7 +70,26 @@
 //        AnalysisOptionsHintCode.STRONG_MODE_SETTING_DEPRECATED) {
 //    } else
 
-    if (errorCode == DEPRECATED_LINT_HINT) {
+    if (errorCode ==
+        AnalysisOptionsWarningCode
+            .ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT) {
+      var analyzerMap = options['analyzer'];
+      if (analyzerMap is! YamlMap) {
+        return fixes;
+      }
+      var strongModeMap = analyzerMap['strong-mode'];
+
+      if (strongModeMap is! YamlMap) {
+        return fixes;
+      }
+      if (_isErrorAtMapKey(strongModeMap, 'implicit-casts')) {
+        await _addFix_replaceWithStrictCasts(
+            coveringNodePath, analyzerMap, strongModeMap);
+      } else if (_isErrorAtMapKey(strongModeMap, 'implicit-dynamic')) {
+        await _addFix_replaceWithStrictRawTypes(
+            coveringNodePath, analyzerMap, strongModeMap);
+      }
+    } else if (errorCode == DEPRECATED_LINT_HINT) {
       await _addFix_removeLint(coveringNodePath);
     } else if (errorCode ==
         AnalysisOptionsHintCode.SUPER_MIXINS_SETTING_DEPRECATED) {
@@ -109,6 +131,52 @@
     }
   }
 
+  /// Replaces `analyzer: strong-mode: implicit-casts: false` with
+  /// `analyzer: language: strict-casts: true`.
+  Future<void> _addFix_replaceWithStrictCasts(List<YamlNode> coveringNodePath,
+      YamlMap analyzerMap, YamlMap strongModeMap) async {
+    var builder =
+        ChangeBuilder(workspace: _NonDartChangeWorkspace(resourceProvider));
+    await builder.addYamlFileEdit(file, (builder) {
+      _replaceStrongModeEntryWithLanguageEntry(
+        builder,
+        coveringNodePath,
+        analyzerMap,
+        strongModeMap,
+        strongModeKey: 'implicit-casts',
+        languageKey: 'strict-casts',
+        languageValue: true,
+      );
+    });
+    _addFixFromBuilder(
+        builder, AnalysisOptionsFixKind.REPLACE_WITH_STRICT_CASTS,
+        args: [coveringNodePath[0].toString()]);
+  }
+
+  /// Replaces `analyzer: strong-mode: implicit-dynamic: false` with
+  /// `analyzer: language: strict-raw-types: true`.
+  Future<void> _addFix_replaceWithStrictRawTypes(
+      List<YamlNode> coveringNodePath,
+      YamlMap analyzerMap,
+      YamlMap strongModeMap) async {
+    var builder =
+        ChangeBuilder(workspace: _NonDartChangeWorkspace(resourceProvider));
+    await builder.addYamlFileEdit(file, (builder) {
+      _replaceStrongModeEntryWithLanguageEntry(
+        builder,
+        coveringNodePath,
+        analyzerMap,
+        strongModeMap,
+        strongModeKey: 'implicit-dynamic',
+        languageKey: 'strict-raw-types',
+        languageValue: true,
+      );
+    });
+    _addFixFromBuilder(
+        builder, AnalysisOptionsFixKind.REPLACE_WITH_STRICT_RAW_TYPES,
+        args: [coveringNodePath[0].toString()]);
+  }
+
   /// Add a fix whose edits were built by the [builder] that has the given
   /// [kind]. If [args] are provided, they will be used to fill in the message
   /// for the fix.
@@ -191,6 +259,20 @@
     return offset;
   }
 
+  /// Returns whether the error is located within [map], covering the
+  /// [YamlScalar] node for [key].
+  bool _isErrorAtMapKey(YamlMap map, String key) {
+    var keyNode = map.nodes.keys
+        .whereType<YamlScalar>()
+        .firstWhereOrNull((k) => k.value == key);
+    if (keyNode == null) {
+      return false;
+    }
+    var keyOffset = keyNode.span.start.offset;
+    var keyLength = keyNode.span.end.offset - keyOffset;
+    return keyOffset == errorOffset && keyLength == errorLength;
+  }
+
   SourceRange _lines(int start, int end) {
     var startLocation = lineInfo.getLocation(start);
     var startOffset = lineInfo.getOffsetOfLine(startLocation.lineNumber - 1);
@@ -199,6 +281,53 @@
         math.min(endLocation.lineNumber, lineInfo.lineCount - 1));
     return SourceRange(startOffset, endOffset - startOffset);
   }
+
+  /// Replaces a 'strong-mode' entry keyed to [strongModeKey] with a 'language'
+  /// entry with [languageKey] and [languageValue].
+  ///
+  /// 'strong-mode' and 'language' are each maps which can be found under the
+  /// top-level 'analyzer' map. 'strong-mode' (given as [strongModeMap]) must
+  /// already be present under the 'analyzer' map (given as [analyzerMap]).
+  void _replaceStrongModeEntryWithLanguageEntry(
+    YamlFileEditBuilder builder,
+    List<YamlNode> coveringNodePath,
+    YamlMap analyzerMap,
+    YamlMap strongModeMap, {
+    required String strongModeKey,
+    required String languageKey,
+    required Object? languageValue,
+  }) {
+    var yamlEditor = YamlEditor(content);
+    // If 'language' does not exist yet under 'analyzer', create it.
+    if (analyzerMap['language'] == null) {
+      yamlEditor.update(['analyzer', 'language'], {languageKey: languageValue});
+    } else {
+      yamlEditor.update(['analyzer', 'language', languageKey], languageValue);
+    }
+    var languageEdit = yamlEditor.edits.single;
+    builder.addSimpleReplacement(
+        SourceRange(languageEdit.offset, languageEdit.length),
+        languageEdit.replacement);
+
+    // If `strongModeKey` is the only entry under 'strong-mode', then remove
+    // the entire 'strong-mode' entry.
+    if (strongModeMap.length == 1) {
+      yamlEditor.remove(['analyzer', 'strong-mode']);
+    } else {
+      yamlEditor.remove(['analyzer', 'strong-mode', strongModeKey]);
+    }
+    var strongModeEdit = yamlEditor.edits[1];
+    int strongModeEditOffset;
+    if (strongModeEdit.offset > languageEdit.offset) {
+      strongModeEditOffset = strongModeEdit.offset -
+          (languageEdit.replacement.length - languageEdit.length);
+    } else {
+      strongModeEditOffset = strongModeEdit.offset;
+    }
+    builder.addSimpleReplacement(
+        SourceRange(strongModeEditOffset, strongModeEdit.length),
+        strongModeEdit.replacement);
+  }
 }
 
 class _NonDartChangeWorkspace implements ChangeWorkspace {
diff --git a/pkg/analysis_server/pubspec.yaml b/pkg/analysis_server/pubspec.yaml
index 5f62d61..98487cb 100644
--- a/pkg/analysis_server/pubspec.yaml
+++ b/pkg/analysis_server/pubspec.yaml
@@ -29,6 +29,7 @@
   path: any
   watcher: any
   yaml: any
+  yaml_edit: any
 
 dev_dependencies:
   analyzer_utilities:
diff --git a/pkg/analysis_server/test/abstract_context.dart b/pkg/analysis_server/test/abstract_context.dart
index ab5be7e..3d99a77 100644
--- a/pkg/analysis_server/test/abstract_context.dart
+++ b/pkg/analysis_server/test/abstract_context.dart
@@ -66,6 +66,10 @@
 
   Future<AnalysisSession> get session => sessionFor(testPackageRootPath);
 
+  /// The path for `analysis_options.yaml` in [testPackageRootPath].
+  String get testAnalysisOptionsPath =>
+      convertPath('$testPackageRootPath/analysis_options.yaml');
+
   String? get testPackageLanguageVersion => latestLanguageVersion;
 
   String get testPackageLibPath => '$testPackageRootPath/lib';
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index 816c714..f4d93622 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -43,8 +43,6 @@
 /// TODO(scheglov) this is duplicate
 class AnalysisOptionsFileConfig {
   final List<String> experiments;
-  final bool implicitCasts;
-  final bool implicitDynamic;
   final List<String> lints;
   final bool strictCasts;
   final bool strictInference;
@@ -52,8 +50,6 @@
 
   AnalysisOptionsFileConfig({
     this.experiments = const [],
-    this.implicitCasts = true,
-    this.implicitDynamic = true,
     this.lints = const [],
     this.strictCasts = false,
     this.strictInference = false,
@@ -72,9 +68,6 @@
     buffer.writeln('    strict-casts: $strictCasts');
     buffer.writeln('    strict-inference: $strictInference');
     buffer.writeln('    strict-raw-types: $strictRawTypes');
-    buffer.writeln('  strong-mode:');
-    buffer.writeln('    implicit-casts: $implicitCasts');
-    buffer.writeln('    implicit-dynamic: $implicitDynamic');
 
     buffer.writeln('linter:');
     buffer.writeln('  rules:');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/analysis_options/replace_with_strict_casts_test.dart b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/replace_with_strict_casts_test.dart
new file mode 100644
index 0000000..c8be60d
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/replace_with_strict_casts_test.dart
@@ -0,0 +1,163 @@
+// 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:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ReplaceWithStrictCastsTest);
+  });
+}
+
+@reflectiveTest
+class ReplaceWithStrictCastsTest extends AnalysisOptionsFixTest {
+  Future<void> test_hasLanguage() async {
+    await assertHasFix('''
+analyzer:
+  language:
+    strict-inference: true
+  strong-mode:
+    implicit-casts: false
+''', '''
+analyzer:
+  language:
+    strict-casts: true
+    strict-inference: true
+''');
+  }
+
+  Future<void> test_hasLanguage_isAfter() async {
+    await assertHasFix('''
+analyzer:
+  strong-mode:
+    implicit-casts: false
+  language:
+    strict-inference: true
+''', '''
+analyzer:
+  language:
+    strict-casts: true
+    strict-inference: true
+''');
+  }
+
+  Future<void> test_hasStrictCastsFalse() async {
+    await assertHasFix('''
+analyzer:
+  language:
+    strict-casts: false
+  strong-mode:
+    implicit-casts: false
+''', '''
+analyzer:
+  language:
+    strict-casts: true
+''');
+  }
+
+  Future<void> test_hasStrictCastsTrue() async {
+    await assertHasFix('''
+analyzer:
+  language:
+    strict-casts: true
+  strong-mode:
+    implicit-casts: false
+''', '''
+analyzer:
+  language:
+    strict-casts: true
+''');
+  }
+
+  Future<void> test_noLanguage() async {
+    await assertHasFix('''
+analyzer:
+  strong-mode:
+    implicit-casts: false
+''', '''
+analyzer:
+  language:
+    strict-casts: true
+''');
+  }
+
+  Future<void> test_noLanguage_analyzerHasOtherEntries() async {
+    await assertHasFix('''
+analyzer:
+  errors:
+    unused_import: ignore
+  strong-mode:
+    implicit-casts: false
+''', '''
+analyzer:
+  errors:
+    unused_import: ignore
+  language:
+    strict-casts: true
+''');
+  }
+
+  Future<void> test_noLanguage_analyzerHasOtherEntriesAfter() async {
+    await assertHasFix('''
+analyzer:
+  strong-mode:
+    implicit-casts: false
+  errors:
+    unused_import: ignore
+''', '''
+analyzer:
+  errors:
+    unused_import: ignore
+  language:
+    strict-casts: true
+''');
+  }
+
+  Future<void> test_noLanguage_hasOtherStrongModeEntry() async {
+    await assertHasFix('''
+analyzer:
+  strong-mode:
+    implicit-casts: false
+    implicit-dynamic: false
+''', '''
+analyzer:
+  language:
+    strict-casts: true
+  strong-mode:
+    implicit-dynamic: false
+''', errorFilter: (error) => error.message.contains('implicit-casts'));
+  }
+
+  Future<void> test_noLanguage_implicitCastsHasComment() async {
+    await assertHasFix('''
+analyzer:
+  strong-mode:
+    # No implicit casts
+    implicit-casts: false
+''', '''
+analyzer:
+  language:
+    strict-casts: true
+''');
+  }
+
+  Future<void> test_noLanguage_strongModeHasComment() async {
+    // TODO(srawlins): This is unfortunate; it would be better to remove the
+    // comment. But we leave this assertion as is to show at least the file is
+    // not corrupted.
+    await assertHasFix('''
+analyzer:
+  # Strong mode
+  strong-mode:
+    implicit-casts: false
+''', '''
+analyzer:
+  # Strong mode
+  language:
+    strict-casts: true
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/analysis_options/replace_with_strict_raw_types_test.dart b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/replace_with_strict_raw_types_test.dart
new file mode 100644
index 0000000..eb8012d
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/replace_with_strict_raw_types_test.dart
@@ -0,0 +1,163 @@
+// 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:test_reflective_loader/test_reflective_loader.dart';
+
+import 'test_support.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ReplaceWithStrictRawTypesTest);
+  });
+}
+
+@reflectiveTest
+class ReplaceWithStrictRawTypesTest extends AnalysisOptionsFixTest {
+  Future<void> test_hasLanguage() async {
+    await assertHasFix('''
+analyzer:
+  language:
+    strict-inference: true
+  strong-mode:
+    implicit-dynamic: false
+''', '''
+analyzer:
+  language:
+    strict-inference: true
+    strict-raw-types: true
+''');
+  }
+
+  Future<void> test_hasLanguage_isAfter() async {
+    await assertHasFix('''
+analyzer:
+  strong-mode:
+    implicit-dynamic: false
+  language:
+    strict-inference: true
+''', '''
+analyzer:
+  language:
+    strict-inference: true
+    strict-raw-types: true
+''');
+  }
+
+  Future<void> test_hasStrictRawTypesFalse() async {
+    await assertHasFix('''
+analyzer:
+  language:
+    strict-raw-types: false
+  strong-mode:
+    implicit-dynamic: false
+''', '''
+analyzer:
+  language:
+    strict-raw-types: true
+''');
+  }
+
+  Future<void> test_hasStrictRawTypesTrue() async {
+    await assertHasFix('''
+analyzer:
+  language:
+    strict-raw-types: true
+  strong-mode:
+    implicit-dynamic: false
+''', '''
+analyzer:
+  language:
+    strict-raw-types: true
+''');
+  }
+
+  Future<void> test_noLanguage() async {
+    await assertHasFix('''
+analyzer:
+  strong-mode:
+    implicit-dynamic: false
+''', '''
+analyzer:
+  language:
+    strict-raw-types: true
+''');
+  }
+
+  Future<void> test_noLanguage_analyzerHasOtherEntries() async {
+    await assertHasFix('''
+analyzer:
+  errors:
+    unused_import: ignore
+  strong-mode:
+    implicit-dynamic: false
+''', '''
+analyzer:
+  errors:
+    unused_import: ignore
+  language:
+    strict-raw-types: true
+''');
+  }
+
+  Future<void> test_noLanguage_analyzerHasOtherEntriesAfter() async {
+    await assertHasFix('''
+analyzer:
+  strong-mode:
+    implicit-dynamic: false
+  errors:
+    unused_import: ignore
+''', '''
+analyzer:
+  errors:
+    unused_import: ignore
+  language:
+    strict-raw-types: true
+''');
+  }
+
+  Future<void> test_noLanguage_hasOtherStrongModeEntry() async {
+    await assertHasFix('''
+analyzer:
+  strong-mode:
+    implicit-casts: false
+    implicit-dynamic: false
+''', '''
+analyzer:
+  language:
+    strict-raw-types: true
+  strong-mode:
+    implicit-casts: false
+''', errorFilter: (error) => error.message.contains('implicit-dynamic'));
+  }
+
+  Future<void> test_noLanguage_implicitDynamicHasComment() async {
+    await assertHasFix('''
+analyzer:
+  strong-mode:
+    # No implicit dynamic
+    implicit-dynamic: false
+''', '''
+analyzer:
+  language:
+    strict-raw-types: true
+''');
+  }
+
+  Future<void> test_noLanguage_strongModeHasComment() async {
+    // TODO(srawlins): This is unfortunate; it would be better to remove the
+    // comment. But we leave this assertion as is to show at least the file is
+    // not corrupted.
+    await assertHasFix('''
+analyzer:
+  # Strong mode
+  strong-mode:
+    implicit-dynamic: false
+''', '''
+analyzer:
+  # Strong mode
+  language:
+    strict-raw-types: true
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_all.dart
index 6d61c19..c4d940e 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_all.dart
@@ -6,10 +6,15 @@
 
 import 'remove_lint_test.dart' as remove_lint;
 import 'remove_setting_test.dart' as remove_setting;
+import 'replace_with_strict_casts_test.dart' as replace_with_strict_casts;
+import 'replace_with_strict_raw_types_test.dart'
+    as replace_with_strict_raw_types;
 
 void main() {
   defineReflectiveSuite(() {
     remove_lint.main();
     remove_setting.main();
+    replace_with_strict_casts.main();
+    replace_with_strict_raw_types.main();
   });
 }
diff --git a/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_support.dart b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_support.dart
index 058fc31..d5cc2c4 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_support.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/analysis_options/test_support.dart
@@ -3,12 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
-import 'package:analysis_server/src/protocol_server.dart' show SourceEdit;
 import 'package:analysis_server/src/services/correction/fix/analysis_options/fix_generator.dart';
+import 'package:analyzer/error/error.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/task/options.dart';
 import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
-import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart'
+    hide AnalysisError;
 import 'package:test/test.dart';
 import 'package:yaml/yaml.dart';
 
@@ -16,8 +17,11 @@
 /// errors in Dart files.
 class AnalysisOptionsFixTest with ResourceProviderMixin {
   Future<void> assertHasFix(
-      String initialContent, String expectedContent) async {
-    var fixes = await _getFixes(initialContent);
+    String initialContent,
+    String expectedContent, {
+    bool Function(AnalysisError)? errorFilter,
+  }) async {
+    var fixes = await _getFixes(initialContent, errorFilter: errorFilter);
     expect(fixes, hasLength(1));
     var fileEdits = fixes[0].change.edits;
     expect(fileEdits, hasLength(1));
@@ -32,8 +36,11 @@
     expect(fixes, hasLength(0));
   }
 
-  Future<List<Fix>> _getFixes(String content) {
-    var optionsFile = getFile('/analysis_options.yaml');
+  Future<List<Fix>> _getFixes(
+    String content, {
+    bool Function(AnalysisError)? errorFilter,
+  }) {
+    var optionsFile = newFile2('/analysis_options.yaml', content);
     var sourceFactory = SourceFactory([]);
     var errors = analyzeAnalysisOptions(
       optionsFile.createSource(),
@@ -41,6 +48,12 @@
       sourceFactory,
       '/',
     );
+    if (errorFilter != null) {
+      if (errors.length == 1) {
+        fail('Unnecessary error filter');
+      }
+      errors = errors.where(errorFilter).toList();
+    }
     expect(errors, hasLength(1));
     var error = errors[0];
     var options = _parseYaml(content);
diff --git a/pkg/analyzer/lib/src/analysis_options/error/option_codes.g.dart b/pkg/analyzer/lib/src/analysis_options/error/option_codes.g.dart
index cbe95e2..962d243 100644
--- a/pkg/analyzer/lib/src/analysis_options/error/option_codes.g.dart
+++ b/pkg/analyzer/lib/src/analysis_options/error/option_codes.g.dart
@@ -135,6 +135,17 @@
   );
 
   /**
+   * An error code indicating that the given option is deprecated.
+   */
+  static const AnalysisOptionsWarningCode
+      ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT = AnalysisOptionsWarningCode(
+    'ANALYSIS_OPTION_DEPRECATED',
+    "The option '{0}' is no longer supported.",
+    correctionMessage: "Try using the new '{1}' option.",
+    uniqueName: 'ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT',
+  );
+
+  /**
    * An error code indicating a specified include file has a warning.
    *
    * Parameters:
diff --git a/pkg/analyzer/lib/src/task/options.dart b/pkg/analyzer/lib/src/task/options.dart
index ea2f3f4..3c67e40 100644
--- a/pkg/analyzer/lib/src/task/options.dart
+++ b/pkg/analyzer/lib/src/task/options.dart
@@ -635,43 +635,48 @@
   void validate(ErrorReporter reporter, YamlMap options) {
     var analyzer = options.valueAt(AnalyzerOptions.analyzer);
     if (analyzer is YamlMap) {
-      var v = analyzer.valueAt(AnalyzerOptions.strong_mode);
-      if (v is YamlScalar) {
-        var value = toLowerCase(v.value);
-        if (!AnalyzerOptions.trueOrFalse.contains(value)) {
+      var strongModeNode = analyzer.valueAt(AnalyzerOptions.strong_mode);
+      if (strongModeNode is YamlScalar) {
+        return _validateStrongModeAsScalar(reporter, strongModeNode);
+      } else if (strongModeNode is YamlMap) {
+        return _validateStrongModeAsMap(reporter, strongModeNode);
+      } else if (strongModeNode != null) {
+        reporter.reportErrorForSpan(
+            AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT,
+            strongModeNode.span,
+            [AnalyzerOptions.enableExperiment]);
+      }
+    }
+  }
+
+  void _validateStrongModeAsMap(
+      ErrorReporter reporter, YamlMap strongModeNode) {
+    strongModeNode.nodes.forEach((k, v) {
+      if (k is YamlScalar) {
+        var key = k.value?.toString();
+        if (!AnalyzerOptions.strongModeOptions.contains(key)) {
+          _builder.reportError(reporter, AnalyzerOptions.strong_mode, k);
+        } else if (key == AnalyzerOptions.declarationCasts) {
           reporter.reportErrorForSpan(
-              AnalysisOptionsWarningCode.UNSUPPORTED_VALUE, v.span, [
-            AnalyzerOptions.strong_mode,
-            v.value,
-            AnalyzerOptions.trueOrFalseProposal
-          ]);
-        } else if (value == 'false') {
+              AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED,
+              k.span,
+              [AnalyzerOptions.declarationCasts]);
+        } else if (key == AnalyzerOptions.implicitCasts) {
           reporter.reportErrorForSpan(
-              AnalysisOptionsWarningCode.SPEC_MODE_REMOVED, v.span);
-        } else if (value == 'true') {
+              AnalysisOptionsWarningCode
+                  .ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT,
+              k.span,
+              [AnalyzerOptions.implicitCasts, 'strict-casts']);
+        } else if (key == AnalyzerOptions.implicitDynamic) {
           reporter.reportErrorForSpan(
-              AnalysisOptionsHintCode.STRONG_MODE_SETTING_DEPRECATED, v.span);
-        }
-      } else if (v is YamlMap) {
-        v.nodes.forEach((k, v) {
-          String? key, value;
-          bool validKey = false;
-          if (k is YamlScalar) {
-            key = k.value?.toString();
-            if (!AnalyzerOptions.strongModeOptions.contains(key)) {
-              _builder.reportError(reporter, AnalyzerOptions.strong_mode, k);
-            } else if (key == AnalyzerOptions.declarationCasts) {
-              reporter.reportErrorForSpan(
-                  AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED,
-                  k.span,
-                  [AnalyzerOptions.declarationCasts]);
-            } else {
-              // If we have a valid key, go on and check the value.
-              validKey = true;
-            }
-          }
-          if (validKey && v is YamlScalar) {
-            value = toLowerCase(v.value);
+              AnalysisOptionsWarningCode
+                  .ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT,
+              k.span,
+              [AnalyzerOptions.implicitDynamic, 'strict-raw-types']);
+        } else {
+          // The key is valid.
+          if (v is YamlScalar) {
+            var value = toLowerCase(v.value);
             if (!AnalyzerOptions.trueOrFalse.contains(value)) {
               reporter.reportErrorForSpan(
                   AnalysisOptionsWarningCode.UNSUPPORTED_VALUE,
@@ -679,13 +684,28 @@
                   [key!, v.value, AnalyzerOptions.trueOrFalseProposal]);
             }
           }
-        });
-      } else if (v != null) {
-        reporter.reportErrorForSpan(
-            AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT,
-            v.span,
-            [AnalyzerOptions.enableExperiment]);
+        }
       }
+    });
+  }
+
+  void _validateStrongModeAsScalar(
+      ErrorReporter reporter, YamlScalar strongModeNode) {
+    var stringValue = toLowerCase(strongModeNode.value);
+    if (!AnalyzerOptions.trueOrFalse.contains(stringValue)) {
+      reporter.reportErrorForSpan(
+          AnalysisOptionsWarningCode.UNSUPPORTED_VALUE, strongModeNode.span, [
+        AnalyzerOptions.strong_mode,
+        strongModeNode.value,
+        AnalyzerOptions.trueOrFalseProposal
+      ]);
+    } else if (stringValue == 'false') {
+      reporter.reportErrorForSpan(
+          AnalysisOptionsWarningCode.SPEC_MODE_REMOVED, strongModeNode.span);
+    } else if (stringValue == 'true') {
+      reporter.reportErrorForSpan(
+          AnalysisOptionsHintCode.STRONG_MODE_SETTING_DEPRECATED,
+          strongModeNode.span);
     }
   }
 }
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index 2401083..67a45e2 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -74,6 +74,11 @@
   ANALYSIS_OPTION_DEPRECATED:
     problemMessage: "The option '{0}' is no longer supported."
     comment: An error code indicating that the given option is deprecated.
+  ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT:
+    sharedName: ANALYSIS_OPTION_DEPRECATED
+    problemMessage: "The option '{0}' is no longer supported."
+    correctionMessage: "Try using the new '{1}' option."
+    comment: An error code indicating that the given option is deprecated.
   INCLUDED_FILE_WARNING:
     problemMessage: "Warning in the included options file {0}({1}..{2}): {3}"
     comment: |-
diff --git a/pkg/analyzer/test/src/task/options_test.dart b/pkg/analyzer/test/src/task/options_test.dart
index bf13737..0afa6ef 100644
--- a/pkg/analyzer/test/src/task/options_test.dart
+++ b/pkg/analyzer/test/src/task/options_test.dart
@@ -460,6 +460,22 @@
 ''', [AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED]);
   }
 
+  test_analyzer_strong_mode_deprecated_key_implicit_casts() {
+    validate('''
+analyzer:
+  strong-mode:
+    implicit-casts: false
+''', [AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT]);
+  }
+
+  test_analyzer_strong_mode_deprecated_key_implicit_dynamic() {
+    validate('''
+analyzer:
+  strong-mode:
+    implicit-dynamic: false
+''', [AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT]);
+  }
+
   test_analyzer_strong_mode_error_code_supported() {
     validate('''
 analyzer:
@@ -491,14 +507,6 @@
 ''', [AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES]);
   }
 
-  test_analyzer_strong_mode_unsupported_value() {
-    validate('''
-analyzer:
-  strong-mode:
-    implicit-dynamic: foo
-''', [AnalysisOptionsWarningCode.UNSUPPORTED_VALUE]);
-  }
-
   test_analyzer_supported_exclude() {
     validate('''
 analyzer:
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
index dca43e9..5ea1a4a 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
@@ -181,11 +181,7 @@
           this,
           path,
           loadYamlDocument(
-              workspace
-                  .getSession(path)!
-                  .resourceProvider
-                  .getFile(path)
-                  .readAsStringSync(),
+              workspace.resourceProvider.getFile(path).readAsStringSync(),
               recover: true),
           0);
       _yamlFileEditBuilders[path] = builder;
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index 06b64b7..c472ba5 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -26,7 +26,6 @@
 import 'js/js.dart' as jsAst;
 import 'js_model/js_strategy.dart';
 import 'js_backend/field_analysis.dart';
-import 'universe/codegen_world_builder.dart';
 import 'universe/world_impact.dart' show WorldImpact, WorldImpactVisitorImpl;
 import 'util/sink_adapter.dart';
 import 'world.dart' show JClosedWorld;
@@ -38,7 +37,6 @@
   final DumpInfoTask dumpInfoTask;
 
   JElementEnvironment get environment => closedWorld.elementEnvironment;
-  CodegenWorldBuilder get codegenWorldBuilder => compiler.codegenWorldBuilder;
 
   final AllInfo result = AllInfo();
   final Map<Entity, Info> _entityToInfo = <Entity, Info>{};
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index 3c01756..ce19532 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -256,31 +256,14 @@
   }
 }
 
-/// Interface for resources only used during code generation.
-abstract class CodegenInputs {
-  CheckedModeHelpers get checkedModeHelpers;
-  RuntimeTypesSubstitutions get rtiSubstitutions;
-  RecipeEncoder get rtiRecipeEncoder;
-  Tracer get tracer;
-  FixedNames get fixedNames;
-}
-
-class CodegenInputsImpl implements CodegenInputs {
-  @override
+/// Holds resources only used during code generation.
+class CodegenInputs {
   final CheckedModeHelpers checkedModeHelpers = CheckedModeHelpers();
-
-  @override
   final RuntimeTypesSubstitutions rtiSubstitutions;
-
-  @override
   final RecipeEncoder rtiRecipeEncoder;
-
-  @override
   final Tracer tracer;
-
-  @override
   final FixedNames fixedNames;
 
-  CodegenInputsImpl(this.rtiSubstitutions, this.rtiRecipeEncoder, this.tracer,
+  CodegenInputs(this.rtiSubstitutions, this.rtiRecipeEncoder, this.tracer,
       this.fixedNames);
 }
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index 61dfd89..534f422 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -213,8 +213,8 @@
     RecipeEncoder rtiRecipeEncoder = RecipeEncoderImpl(closedWorld,
         rtiSubstitutions, closedWorld.nativeData, closedWorld.commonElements);
 
-    CodegenInputs codegen = CodegenInputsImpl(
-        rtiSubstitutions, rtiRecipeEncoder, tracer, fixedNames);
+    CodegenInputs codegen =
+        CodegenInputs(rtiSubstitutions, rtiRecipeEncoder, tracer, fixedNames);
 
     functionCompiler.initialize(globalTypeInferenceResults, codegen);
     return codegen;
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 8a647cb..888041a 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -7167,6 +7167,23 @@
 #undef EXPECT_NOT_SUBTYPE
 #undef EXPECT_SUBTYPE
 
+static void ExpectTypesEquivalent(const Expect& expect,
+                                  const AbstractType& expected,
+                                  const AbstractType& got,
+                                  TypeEquality kind) {
+  if (got.IsEquivalent(expected, kind)) return;
+  TextBuffer buffer(128);
+  buffer.AddString("Expected type ");
+  expected.PrintName(Object::kScrubbedName, &buffer);
+  buffer.AddString(", got ");
+  got.PrintName(Object::kScrubbedName, &buffer);
+  expect.Fail("%s", buffer.buffer());
+}
+
+#define EXPECT_TYPES_EQUAL(expected, got)                                      \
+  ExpectTypesEquivalent(Expect(__FILE__, __LINE__), expected, got,             \
+                        TypeEquality::kCanonical);
+
 TEST_CASE(Class_GetInstantiationOf) {
   const char* kScript = R"(
     class B<T> {}
@@ -7188,17 +7205,6 @@
   const auto& core_lib = Library::Handle(zone, Library::CoreLibrary());
   const auto& class_list = Class::Handle(zone, GetClass(core_lib, "List"));
 
-  auto expect_type_equal = [](const AbstractType& expected,
-                              const AbstractType& got) {
-    if (got.Equals(expected)) return;
-    TextBuffer buffer(128);
-    buffer.AddString("Expected type ");
-    expected.PrintName(Object::kScrubbedName, &buffer);
-    buffer.AddString(", got ");
-    got.PrintName(Object::kScrubbedName, &buffer);
-    dart::Expect(__FILE__, __LINE__).Fail("%s", buffer.buffer());
-  };
-
   const auto& decl_type_b = Type::Handle(zone, class_b.DeclarationType());
   const auto& decl_type_list = Type::Handle(zone, class_list.DeclarationType());
   const auto& null_tav = Object::null_type_arguments();
@@ -7228,7 +7234,7 @@
     const auto& inst_b_a1 =
         Type::Handle(zone, class_a1.GetInstantiationOf(zone, class_b));
     EXPECT(!inst_b_a1.IsNull());
-    expect_type_equal(type_b_list_a1_y, inst_b_a1);
+    EXPECT_TYPES_EQUAL(type_b_list_a1_y, inst_b_a1);
   }
 
   // Test that A2.GetInstantiationOf(B) returns B<List<A2::X>>.
@@ -7256,8 +7262,342 @@
     const auto& inst_b_a2 =
         Type::Handle(zone, class_a2.GetInstantiationOf(zone, class_b));
     EXPECT(!inst_b_a2.IsNull());
-    expect_type_equal(type_b_list_a2_x, inst_b_a2);
+    EXPECT_TYPES_EQUAL(type_b_list_a2_x, inst_b_a2);
   }
 }
 
+#undef EXPECT_TYPES_EQUAL
+
+#define EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got)                   \
+  ExpectTypesEquivalent(Expect(__FILE__, __LINE__), expected, got,             \
+                        TypeEquality::kSyntactical);
+
+static TypePtr CreateFutureOrType(const AbstractType& param,
+                                  Nullability nullability) {
+  const auto& async_lib = Library::Handle(Library::AsyncLibrary());
+  const auto& future_or_class =
+      Class::Handle(async_lib.LookupClass(Symbols::FutureOr()));
+  const auto& tav = TypeArguments::Handle(TypeArguments::New(1));
+  tav.SetTypeAt(0, param);
+  const auto& type =
+      AbstractType::Handle(Type::New(future_or_class, tav, nullability));
+  return Type::RawCast(
+      ClassFinalizer::FinalizeType(type, ClassFinalizer::kFinalize));
+}
+
+static TypePtr CreateFutureType(const AbstractType& param,
+                                Nullability nullability) {
+  ObjectStore* const object_store = IsolateGroup::Current()->object_store();
+  const auto& future_class = Class::Handle(object_store->future_class());
+  const auto& tav = TypeArguments::Handle(TypeArguments::New(1));
+  tav.SetTypeAt(0, param);
+  const auto& type = Type::Handle(Type::New(future_class, tav, nullability));
+  return Type::RawCast(
+      ClassFinalizer::FinalizeType(type, ClassFinalizer::kFinalize));
+}
+
+ISOLATE_UNIT_TEST_CASE(AbstractType_NormalizeFutureOrType) {
+  // This should be kept up to date with any changes in
+  // https://github.com/dart-lang/language/blob/master/resources/type-system/normalization.md
+
+  ObjectStore* const object_store = IsolateGroup::Current()->object_store();
+
+  auto normalized_future_or = [&](const AbstractType& param,
+                                  Nullability nullability) -> AbstractTypePtr {
+    const auto& type = Type::Handle(CreateFutureOrType(param, nullability));
+    return type.NormalizeFutureOrType(Heap::kNew);
+  };
+
+  // NORM(FutureOr<T>) =
+  //   let S be NORM(T)
+  //   if S is a top type then S
+  {
+    const auto& type = AbstractType::Handle(normalized_future_or(
+        Object::dynamic_type(), Nullability::kNonNullable));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(Object::dynamic_type(), type);
+  }
+
+  {
+    const auto& type = AbstractType::Handle(
+        normalized_future_or(Object::void_type(), Nullability::kNonNullable));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(Object::void_type(), type);
+  }
+
+  {
+    const auto& type_nullable_object =
+        Type::Handle(object_store->nullable_object_type());
+    const auto& type = AbstractType::Handle(
+        normalized_future_or(type_nullable_object, Nullability::kNonNullable));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_nullable_object, type);
+  }
+
+  //   if S is Object then S
+
+  {
+    const auto& type_non_nullable_object =
+        Type::Handle(object_store->non_nullable_object_type());
+    const auto& type = AbstractType::Handle(normalized_future_or(
+        type_non_nullable_object, Nullability::kNonNullable));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_non_nullable_object, type);
+  }
+
+  //   if S is Object* then S
+
+  {
+    const auto& type_legacy_object =
+        Type::Handle(object_store->legacy_object_type());
+    const auto& type = AbstractType::Handle(
+        normalized_future_or(type_legacy_object, Nullability::kNonNullable));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_legacy_object, type);
+  }
+
+  //   if S is Never then Future<Never>
+
+  {
+    const auto& type_never = Type::Handle(object_store->never_type());
+    const auto& expected =
+        Type::Handle(CreateFutureType(type_never, Nullability::kNonNullable));
+    const auto& got = AbstractType::Handle(
+        normalized_future_or(type_never, Nullability::kNonNullable));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+  }
+
+  //   if S is Null then Future<Null>?
+
+  {
+    const auto& type_null = Type::Handle(object_store->null_type());
+    const auto& expected =
+        Type::Handle(CreateFutureType(type_null, Nullability::kNullable));
+    const auto& got = AbstractType::Handle(
+        normalized_future_or(type_null, Nullability::kNonNullable));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+  }
+
+  //   else FutureOr<S>
+
+  // NORM(T?) =
+  //   let S be NORM(T)
+  //   ...
+  //   if S is FutureOr<R> and R is nullable then S
+
+  {
+    const auto& type_nullable_int =
+        Type::Handle(object_store->nullable_int_type());
+    const auto& expected = Type::Handle(
+        CreateFutureOrType(type_nullable_int, Nullability::kNonNullable));
+    const auto& got = AbstractType::Handle(
+        normalized_future_or(type_nullable_int, Nullability::kNullable));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+  }
+}
+
+TEST_CASE(AbstractType_InstantiatedFutureOrIsNormalized) {
+  const char* kScript = R"(
+import 'dart:async';
+
+FutureOr<T>? foo<T>() { return null; }
+FutureOr<T?> bar<T>() { return null; }
+)";
+
+  Dart_Handle api_lib = TestCase::LoadTestScript(kScript, nullptr);
+  EXPECT_VALID(api_lib);
+  TransitionNativeToVM transition(thread);
+  Zone* const zone = thread->zone();
+  ObjectStore* const object_store = IsolateGroup::Current()->object_store();
+
+  const auto& null_tav = Object::null_type_arguments();
+  auto instantiate_future_or =
+      [&](const AbstractType& generic,
+          const AbstractType& param) -> AbstractTypePtr {
+    const auto& tav = TypeArguments::Handle(TypeArguments::New(1));
+    tav.SetTypeAt(0, param);
+    return generic.InstantiateFrom(null_tav, tav, kCurrentAndEnclosingFree,
+                                   Heap::kNew);
+  };
+
+  const auto& root_lib =
+      Library::CheckedHandle(zone, Api::UnwrapHandle(api_lib));
+  EXPECT(!root_lib.IsNull());
+  const auto& foo = Function::Handle(zone, GetFunction(root_lib, "foo"));
+  const auto& bar = Function::Handle(zone, GetFunction(root_lib, "bar"));
+  const auto& foo_sig = FunctionType::Handle(zone, foo.signature());
+  const auto& bar_sig = FunctionType::Handle(zone, bar.signature());
+
+  const auto& nullable_future_or_T =
+      AbstractType::Handle(zone, foo_sig.result_type());
+  const auto& future_or_nullable_T =
+      AbstractType::Handle(zone, bar_sig.result_type());
+
+  const auto& type_nullable_object =
+      Type::Handle(object_store->nullable_object_type());
+  const auto& type_non_nullable_object =
+      Type::Handle(object_store->non_nullable_object_type());
+  const auto& type_legacy_object =
+      Type::Handle(object_store->legacy_object_type());
+
+  // Testing same cases as AbstractType_NormalizeFutureOrType.
+
+  // FutureOr<T>?[top type] = top type
+
+  {
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(nullable_future_or_T, Object::dynamic_type()));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(Object::dynamic_type(), got);
+  }
+
+  {
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(nullable_future_or_T, Object::void_type()));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(Object::void_type(), got);
+  }
+
+  {
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(nullable_future_or_T, type_nullable_object));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_nullable_object, got);
+  }
+
+  // FutureOr<T?>[top type] = top type
+
+  {
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(future_or_nullable_T, Object::dynamic_type()));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(Object::dynamic_type(), got);
+  }
+
+  {
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(future_or_nullable_T, Object::void_type()));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(Object::void_type(), got);
+  }
+
+  {
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(future_or_nullable_T, type_nullable_object));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_nullable_object, got);
+  }
+
+  // FutureOr<T?>[Object] = Object?
+
+  {
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(future_or_nullable_T, type_non_nullable_object));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_nullable_object, got);
+  }
+
+  // FutureOr<T?>[Object*] = Object?
+
+  {
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(future_or_nullable_T, type_legacy_object));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_nullable_object, got);
+  }
+
+  // FutureOr<T>?[Object] = Object?
+
+  {
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(nullable_future_or_T, type_non_nullable_object));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_nullable_object, got);
+  }
+
+  // FutureOr<T>?[Object*] = Object?
+
+  {
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(nullable_future_or_T, type_legacy_object));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(type_nullable_object, got);
+  }
+
+  const auto& type_never = Type::Handle(object_store->never_type());
+  const auto& type_null = Type::Handle(object_store->null_type());
+
+  // FutureOr<T?>[Never] = Future<Null>?
+
+  {
+    const auto& expected =
+        Type::Handle(CreateFutureType(type_null, Nullability::kNullable));
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(future_or_nullable_T, type_never));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+  }
+
+  // FutureOr<T>?[Never] = Future<Never>?
+
+  {
+    const auto& expected =
+        Type::Handle(CreateFutureType(type_never, Nullability::kNullable));
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(nullable_future_or_T, type_never));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+  }
+
+  // FutureOr<T?>[Null] = Future<Null>?
+
+  {
+    const auto& expected =
+        Type::Handle(CreateFutureType(type_null, Nullability::kNullable));
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(future_or_nullable_T, type_null));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+  }
+
+  // FutureOr<T>?[Null] = Future<Null>?
+
+  {
+    const auto& expected =
+        Type::Handle(CreateFutureType(type_null, Nullability::kNullable));
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(nullable_future_or_T, type_null));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+  }
+
+  const auto& type_nullable_int =
+      Type::Handle(object_store->nullable_int_type());
+  const auto& type_non_nullable_int =
+      Type::Handle(object_store->non_nullable_int_type());
+
+  // FutureOr<T?>[int] = FutureOr<int?>
+
+  {
+    const auto& expected = Type::Handle(
+        CreateFutureOrType(type_nullable_int, Nullability::kNonNullable));
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(future_or_nullable_T, type_non_nullable_int));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+  }
+
+  // FutureOr<T?>[int?] = FutureOr<int?>
+
+  {
+    const auto& expected = Type::Handle(
+        CreateFutureOrType(type_nullable_int, Nullability::kNonNullable));
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(future_or_nullable_T, type_nullable_int));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+  }
+
+  // FutureOr<T>?[int?] = FutureOr<int?>
+
+  {
+    const auto& expected = Type::Handle(
+        CreateFutureOrType(type_nullable_int, Nullability::kNonNullable));
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(nullable_future_or_T, type_nullable_int));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+  }
+
+  // FutureOr<T>?[int] = FutureOr<int>?
+
+  {
+    const auto& expected = Type::Handle(
+        CreateFutureOrType(type_non_nullable_int, Nullability::kNullable));
+    const auto& got = AbstractType::Handle(
+        instantiate_future_or(nullable_future_or_T, type_non_nullable_int));
+    EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT(expected, got);
+  }
+}
+
+#undef EXPECT_TYPES_SYNTACTICALLY_EQUIVALENT
+
 }  // namespace dart
diff --git a/tools/VERSION b/tools/VERSION
index 4113a05..78b37fe 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 240
+PRERELEASE 241
 PRERELEASE_PATCH 0
\ No newline at end of file