Version 2.12.0-7.0.dev

Merge commit '77f2019f00858ac60dbca006ed93f11576b757b4' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 051ed07..041bc3f 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -229,12 +229,6 @@
       "languageVersion": "2.10"
     },
     {
-      "name": "dartfix",
-      "rootUri": "../pkg/dartfix",
-      "packageUri": "lib/",
-      "languageVersion": "2.8"
-    },
-    {
       "name": "dds",
       "rootUri": "../pkg/dds",
       "packageUri": "lib/",
diff --git a/.packages b/.packages
index 831d0f3..22dab41 100644
--- a/.packages
+++ b/.packages
@@ -35,7 +35,6 @@
 dart_style:third_party/pkg_tested/dart_style/lib
 dartdev:pkg/dartdev/lib
 dartdoc:third_party/pkg/dartdoc/lib
-dartfix:pkg/dartfix/lib
 dds:pkg/dds/lib
 dev_compiler:pkg/dev_compiler/lib
 diagnostic:pkg/diagnostic/lib
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index ea398ea..ce3c063 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -626,10 +626,6 @@
   /// generally used in specific SDKs (like the internal google3 one).
   SdkConfiguration configurationOverrides;
 
-  /// The list of the names of the experiments that should be enabled by
-  /// default, unless the analysis options file of a context overrides it.
-  List<String> enabledExperiments = const <String>[];
-
   /// Whether to use the Language Server Protocol.
   bool useLanguageServerProtocol = false;
 
@@ -659,9 +655,8 @@
       analysisServer.notificationManager;
 
   @override
-  nd.AnalysisDriver addAnalysisDriver(
-      Folder folder, ContextRoot contextRoot, AnalysisOptions options) {
-    var builder = createContextBuilder(folder, options);
+  nd.AnalysisDriver addAnalysisDriver(Folder folder, ContextRoot contextRoot) {
+    var builder = createContextBuilder(folder);
     var analysisDriver = builder.buildDriver(contextRoot);
     analysisDriver.results.listen((result) {
       var notificationManager = analysisServer.notificationManager;
@@ -814,9 +809,8 @@
   }
 
   @override
-  ContextBuilder createContextBuilder(Folder folder, AnalysisOptions options) {
+  ContextBuilder createContextBuilder(Folder folder) {
     var builderOptions = ContextBuilderOptions();
-    builderOptions.defaultOptions = options;
     var builder = ContextBuilder(
         resourceProvider, analysisServer.sdkManager, null,
         options: builderOptions);
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index bb545e4..f0db479 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -22,7 +22,6 @@
 import 'package:analysis_server/src/utilities/null_string_sink.dart';
 import 'package:analysis_server/src/utilities/request_statistics.dart';
 import 'package:analysis_server/src/utilities/tee_string_sink.dart';
-import 'package:analyzer/dart/analysis/features.dart' as analyzer_features;
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/dart/ast/ast.dart';
@@ -34,8 +33,6 @@
 import 'package:analyzer/instrumentation/instrumentation.dart';
 import 'package:analyzer/src/dart/analysis/byte_store.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart' as nd;
-import 'package:analyzer/src/dart/analysis/experiments.dart'
-    as analyzer_features;
 import 'package:analyzer/src/dart/analysis/file_byte_store.dart'
     show EvictingFileByteStore;
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
@@ -60,10 +57,6 @@
   /// context directories.
   ContextManager contextManager;
 
-  /// The default options used to create new analysis contexts. This object is
-  /// also referenced by the ContextManager.
-  final AnalysisOptionsImpl defaultContextOptions = AnalysisOptionsImpl();
-
   /// The object used to manage sending a subset of notifications to the client.
   /// The subset of notifications are those to which plugins may contribute.
   /// This field is `null` when the new plugin support is disabled.
@@ -155,12 +148,6 @@
         instrumentationService);
     var pluginWatcher = PluginWatcher(resourceProvider, pluginManager);
 
-    defaultContextOptions.contextFeatures =
-        analyzer_features.FeatureSet.fromEnableFlags2(
-      sdkLanguageVersion: analyzer_features.ExperimentStatus.currentVersion,
-      flags: options.enabledExperiments,
-    );
-
     {
       var name = options.newAnalysisDriverLog;
       StringSink sink = NullStringSink();
@@ -192,7 +179,7 @@
     }
 
     contextManager = ContextManagerImpl(resourceProvider, sdkManager,
-        analyzedFilesGlobs, instrumentationService, defaultContextOptions);
+        analyzedFilesGlobs, instrumentationService);
     searchEngine = SearchEngineImpl(driverMap.values);
   }
 
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index 19725d0..5c09be3 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -294,10 +294,8 @@
   /// Return the notification manager associated with the server.
   AbstractNotificationManager get notificationManager;
 
-  /// Create and return a new analysis driver rooted at the given [folder], with
-  /// the given analysis [options].
-  AnalysisDriver addAnalysisDriver(
-      Folder folder, ContextRoot contextRoot, AnalysisOptions options);
+  /// Create and return a new analysis driver rooted at the given [folder].
+  AnalysisDriver addAnalysisDriver(Folder folder, ContextRoot contextRoot);
 
   /// An [event] was processed, so analysis state might be different now.
   void afterWatchEvent(WatchEvent event);
@@ -318,9 +316,8 @@
   void broadcastWatchEvent(WatchEvent event);
 
   /// Create and return a context builder that can be used to create a context
-  /// for the files in the given [folder] when analyzed using the given
-  /// [options].
-  ContextBuilder createContextBuilder(Folder folder, AnalysisOptions options);
+  /// for the files in the given [folder].
+  ContextBuilder createContextBuilder(Folder folder);
 
   /// Remove the context associated with the given [folder].  [flushedFiles] is
   /// a list of the files which will be "orphaned" by removing this context
@@ -377,9 +374,6 @@
   /// A list of the globs used to determine which files should be analyzed.
   final List<Glob> analyzedFilesGlobs;
 
-  /// The default options used to create new analysis contexts.
-  final AnalysisOptionsImpl defaultContextOptions;
-
   /// The instrumentation service used to report instrumentation data.
   final InstrumentationService _instrumentationService;
 
@@ -400,11 +394,11 @@
       <Folder, StreamSubscription<WatchEvent>>{};
 
   ContextManagerImpl(
-      this.resourceProvider,
-      this.sdkManager,
-      this.analyzedFilesGlobs,
-      this._instrumentationService,
-      this.defaultContextOptions) {
+    this.resourceProvider,
+    this.sdkManager,
+    this.analyzedFilesGlobs,
+    this._instrumentationService,
+  ) {
     pathContext = resourceProvider.pathContext;
   }
 
@@ -1006,7 +1000,7 @@
     } catch (_) {
       // Parse errors are reported elsewhere.
     }
-    AnalysisOptions options = AnalysisOptionsImpl.from(defaultContextOptions);
+    var options = AnalysisOptionsImpl();
     applyToAnalysisOptions(options, optionMap);
 
     info.setDependencies(dependencies);
@@ -1021,8 +1015,7 @@
     if (optionsFile != null) {
       contextRoot.optionsFilePath = optionsFile.path;
     }
-    info.analysisDriver =
-        callbacks.addAnalysisDriver(folder, contextRoot, options);
+    info.analysisDriver = callbacks.addAnalysisDriver(folder, contextRoot);
     if (optionsFile != null) {
       _analyzeAnalysisOptionsFile(info.analysisDriver, optionsFile.path);
     }
@@ -1115,8 +1108,8 @@
 
   /// Set up a [SourceFactory] that resolves packages as appropriate for the
   /// given [folder].
-  SourceFactory _createSourceFactory(AnalysisOptions options, Folder folder) {
-    var builder = callbacks.createContextBuilder(folder, options);
+  SourceFactory _createSourceFactory(Folder folder) {
+    var builder = callbacks.createContextBuilder(folder);
     return builder.createSourceFactory(folder.path);
   }
 
@@ -1481,8 +1474,7 @@
   void _updateAnalysisOptions(ContextInfo info) {
     var driver = info.analysisDriver;
     var contextRoot = info.folder.path;
-    var builder =
-        callbacks.createContextBuilder(info.folder, defaultContextOptions);
+    var builder = callbacks.createContextBuilder(info.folder);
     var options = builder.getAnalysisOptions(contextRoot,
         contextRoot: driver.contextRoot);
     var factory = builder.createSourceFactory(contextRoot);
@@ -1493,8 +1485,7 @@
   void _updateContextPackageUriResolver(Folder contextFolder) {
     var info = getContextInfoFor(contextFolder);
     var driver = info.analysisDriver;
-    var sourceFactory =
-        _createSourceFactory(driver.analysisOptions, contextFolder);
+    var sourceFactory = _createSourceFactory(contextFolder);
     driver.configure(sourceFactory: sourceFactory);
   }
 
diff --git a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
index 4e53670..c8acbe8 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
@@ -43,7 +43,6 @@
 import 'package:analyzer/src/context/context_root.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart' as nd;
 import 'package:analyzer/src/dart/analysis/status.dart' as nd;
-import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/sdk.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
@@ -741,9 +740,8 @@
       analysisServer.notificationManager;
 
   @override
-  nd.AnalysisDriver addAnalysisDriver(
-      Folder folder, ContextRoot contextRoot, AnalysisOptions options) {
-    var builder = createContextBuilder(folder, options);
+  nd.AnalysisDriver addAnalysisDriver(Folder folder, ContextRoot contextRoot) {
+    var builder = createContextBuilder(folder);
     var analysisDriver = builder.buildDriver(contextRoot);
     final textDocumentCapabilities =
         analysisServer.clientCapabilities?.textDocument;
@@ -838,9 +836,8 @@
   }
 
   @override
-  ContextBuilder createContextBuilder(Folder folder, AnalysisOptions options) {
+  ContextBuilder createContextBuilder(Folder folder) {
     var builderOptions = ContextBuilderOptions();
-    builderOptions.defaultOptions = options;
     var builder = ContextBuilder(
         resourceProvider, analysisServer.sdkManager, null,
         options: builderOptions);
diff --git a/pkg/analysis_server/lib/src/server/driver.dart b/pkg/analysis_server/lib/src/server/driver.dart
index 298fd30..18b8438 100644
--- a/pkg/analysis_server/lib/src/server/driver.dart
+++ b/pkg/analysis_server/lib/src/server/driver.dart
@@ -234,9 +234,6 @@
   static const String DISABLE_SERVER_FEATURE_SEARCH =
       'disable-server-feature-search';
 
-  /// The name of the option used to enable experiments.
-  static const String ENABLE_EXPERIMENT_OPTION = 'enable-experiment';
-
   /// The name of the option used to enable instrumentation.
   static const String ENABLE_INSTRUMENTATION_OPTION = 'enable-instrumentation';
 
@@ -336,10 +333,6 @@
 
     analysisServerOptions.clientVersion = results[CLIENT_VERSION];
     analysisServerOptions.cacheFolder = results[CACHE_FOLDER];
-    if (results.wasParsed(ENABLE_EXPERIMENT_OPTION)) {
-      analysisServerOptions.enabledExperiments =
-          (results[ENABLE_EXPERIMENT_OPTION] as List).cast<String>().toList();
-    }
     analysisServerOptions.useNewRelevance = results[USE_NEW_RELEVANCE];
 
     // Read in any per-SDK overrides specified in <sdk>/config/settings.json.
@@ -769,11 +762,6 @@
         help: 'disable all completion features', defaultsTo: false, hide: true);
     parser.addFlag(DISABLE_SERVER_FEATURE_SEARCH,
         help: 'disable all search features', defaultsTo: false, hide: true);
-    parser.addMultiOption(ENABLE_EXPERIMENT_OPTION,
-        help: 'Enable one or more experimental features. If multiple features '
-            'are being added, they should be comma separated.',
-        hide: true,
-        splitCommas: true);
     parser.addFlag(ENABLE_INSTRUMENTATION_OPTION,
         help: 'enable sending instrumentation information to a server',
         defaultsTo: false,
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
index f00f631..e7b8a24 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
@@ -405,8 +405,9 @@
   Change _translateChange(YamlNode node, ErrorContext context) {
     assert(node != null);
     if (node is YamlMap) {
-      var kind = _translateString(node.valueAt(_kindKey),
-          ErrorContext(key: _kindKey, parentNode: node));
+      var kindNode = node.valueAt(_kindKey);
+      var kindContext = ErrorContext(key: _kindKey, parentNode: node);
+      var kind = _translateString(kindNode, kindContext);
       if (kind == null) {
         return null;
       } else if (kind == _addParameterKind) {
@@ -420,7 +421,7 @@
       } else if (kind == _renameKind) {
         return _translateRenameChange(node);
       }
-      return _reportInvalidValueOneOf(node, context, [
+      return _reportInvalidValueOneOf(kindNode, kindContext, [
         _addParameterKind,
         _addTypeParameterKind,
         _removeParameterKind,
@@ -879,8 +880,9 @@
   /// error.
   ValueGenerator _translateValueGenerator(YamlNode node, ErrorContext context) {
     if (node is YamlMap) {
-      var kind = _translateString(node.valueAt(_kindKey),
-          ErrorContext(key: _kindKey, parentNode: node));
+      var kindNode = node.valueAt(_kindKey);
+      var kindContext = ErrorContext(key: _kindKey, parentNode: node);
+      var kind = _translateString(kindNode, kindContext);
       if (kind == null) {
         return null;
       } else if (kind == _fragmentKind) {
@@ -888,7 +890,7 @@
       } else if (kind == _importKind) {
         return _translateImportValue(node);
       }
-      return _reportInvalidValueOneOf(node, context, [
+      return _reportInvalidValueOneOf(kindNode, kindContext, [
         _fragmentKind,
         _importKind,
       ]);
diff --git a/pkg/analysis_server/test/context_manager_test.dart b/pkg/analysis_server/test/context_manager_test.dart
index 0669678..28ba464 100644
--- a/pkg/analysis_server/test/context_manager_test.dart
+++ b/pkg/analysis_server/test/context_manager_test.dart
@@ -1591,11 +1591,11 @@
     MockSdk(resourceProvider: resourceProvider);
     var sdkManager = DartSdkManager(convertPath(sdkRoot));
     manager = ContextManagerImpl(
-        resourceProvider,
-        sdkManager,
-        analysisFilesGlobs,
-        InstrumentationService.NULL_SERVICE,
-        AnalysisOptionsImpl());
+      resourceProvider,
+      sdkManager,
+      analysisFilesGlobs,
+      InstrumentationService.NULL_SERVICE,
+    );
     var logger = PerformanceLog(NullStringSink());
     var scheduler = AnalysisDriverScheduler(logger);
     callbacks = TestContextManagerCallbacks(
@@ -2280,15 +2280,14 @@
   SourceFactory get sourceFactory => currentDriver?.sourceFactory;
 
   @override
-  AnalysisDriver addAnalysisDriver(
-      Folder folder, ContextRoot contextRoot, AnalysisOptions options) {
+  AnalysisDriver addAnalysisDriver(Folder folder, ContextRoot contextRoot) {
     var path = folder.path;
     expect(currentContextRoots, isNot(contains(path)));
     expect(contextRoot, isNotNull);
     expect(contextRoot.root, path);
     currentContextTimestamps[path] = now;
 
-    var builder = createContextBuilder(folder, options);
+    var builder = createContextBuilder(folder);
     builder.analysisDriverScheduler = scheduler;
     builder.byteStore = MemoryByteStore();
     builder.performanceLog = logger;
@@ -2345,9 +2344,8 @@
   }
 
   @override
-  ContextBuilder createContextBuilder(Folder folder, AnalysisOptions options) {
+  ContextBuilder createContextBuilder(Folder folder) {
     var builderOptions = ContextBuilderOptions();
-    builderOptions.defaultOptions = options;
     var builder = ContextBuilder(resourceProvider, sdkManager, null,
         options: builderOptions);
     return builder;
diff --git a/pkg/analysis_server/test/lsp/signature_help_test.dart b/pkg/analysis_server/test/lsp/signature_help_test.dart
index 64d902d..2e6094e 100644
--- a/pkg/analysis_server/test/lsp/signature_help_test.dart
+++ b/pkg/analysis_server/test/lsp/signature_help_test.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
-import 'package:analysis_server/src/analysis_server.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -449,10 +448,6 @@
 class SignatureHelpWithNullSafetyTest extends AbstractLspAnalysisServerTest
     with SignatureHelpMixin {
   @override
-  AnalysisServerOptions get serverOptions =>
-      AnalysisServerOptions()..enabledExperiments = ['non-nullable'];
-
-  @override
   String get testPackageLanguageVersion => latestLanguageVersion;
 
   Future<void> test_params_requiredNamed() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/relevance/completion_relevance.dart b/pkg/analysis_server/test/services/completion/dart/relevance/completion_relevance.dart
index 9a363e9..643fec4 100644
--- a/pkg/analysis_server/test/services/completion/dart/relevance/completion_relevance.dart
+++ b/pkg/analysis_server/test/services/completion/dart/relevance/completion_relevance.dart
@@ -9,12 +9,9 @@
 import '../../../../client/completion_driver_test.dart';
 
 class CompletionRelevanceTest extends AbstractCompletionDriverTest {
-  List<String> get enabledExperiments => [];
-
   @override
-  AnalysisServerOptions get serverOptions => AnalysisServerOptions()
-    ..enabledExperiments = enabledExperiments
-    ..useNewRelevance = true;
+  AnalysisServerOptions get serverOptions =>
+      AnalysisServerOptions()..useNewRelevance = true;
 
   @override
   bool get supportsAvailableSuggestions => true;
diff --git a/pkg/analysis_server/test/services/completion/dart/relevance/named_argument_relevance_test.dart b/pkg/analysis_server/test/services/completion/dart/relevance/named_argument_relevance_test.dart
index baaf538..ffe4112 100644
--- a/pkg/analysis_server/test/services/completion/dart/relevance/named_argument_relevance_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/relevance/named_argument_relevance_test.dart
@@ -46,9 +46,6 @@
 @reflectiveTest
 class NamedArgumentRelevanceWithNullSafetyTest
     extends NamedArgumentRelevanceTest {
-  @override
-  List<String> get enabledExperiments => ['non-nullable'];
-
   Future<void> test_required() async {
     await addTestFile('''
 void f({int a = 0, required int b}) {}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/invalid_value_one_of_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/invalid_value_one_of_test.dart
index 2e606bb..fa9beb5 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/invalid_value_one_of_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/invalid_value_one_of_test.dart
@@ -27,7 +27,7 @@
   changes:
     - kind: 'invalid'
 ''', [
-      error(TransformSetErrorCode.invalidValueOneOf, 129, 16),
+      error(TransformSetErrorCode.invalidValueOneOf, 135, 9),
     ]);
   }
 
@@ -50,7 +50,7 @@
           x:
             kind: 'invalid'
 ''', [
-      error(TransformSetErrorCode.invalidValueOneOf, 274, 16),
+      error(TransformSetErrorCode.invalidValueOneOf, 280, 9),
     ]);
   }
 }
diff --git a/pkg/analyzer/lib/dart/sdk/build_sdk_summary.dart b/pkg/analyzer/lib/dart/sdk/build_sdk_summary.dart
index 8705915..f3c9a79 100644
--- a/pkg/analyzer/lib/dart/sdk/build_sdk_summary.dart
+++ b/pkg/analyzer/lib/dart/sdk/build_sdk_summary.dart
@@ -12,7 +12,6 @@
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/context/context.dart';
-import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:analyzer/src/dart/analysis/session.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
 import 'package:analyzer/src/dart/sdk/sdk.dart';
@@ -217,7 +216,7 @@
 
     var unit = result.unit as CompilationUnitImpl;
     unit.languageVersion = LibraryLanguageVersion(
-      package: ExperimentStatus.currentVersion,
+      package: languageVersion,
       override: null,
     );
 
diff --git a/pkg/analyzer/lib/src/dart/analysis/feature_set_provider.dart b/pkg/analyzer/lib/src/dart/analysis/feature_set_provider.dart
index 16a57bd..32b5d0f 100644
--- a/pkg/analyzer/lib/src/dart/analysis/feature_set_provider.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/feature_set_provider.dart
@@ -6,7 +6,6 @@
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/context/packages.dart';
-import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:analyzer/src/generated/sdk.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/util/uri.dart';
@@ -85,7 +84,7 @@
   /// be either lower, or higher than the package language version.
   Version getLanguageVersion(String path, Uri uri) {
     if (uri.isScheme('dart')) {
-      return ExperimentStatus.currentVersion;
+      return _sdkLanguageVersion;
     }
     var package = _findPackage(uri, path);
     if (package != null) {
@@ -93,7 +92,7 @@
       if (languageVersion != null) {
         return languageVersion;
       }
-      return ExperimentStatus.currentVersion;
+      return _sdkLanguageVersion;
     }
 
     return _nonPackageDefaultLanguageVersion;
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
index 720c0e4..3688028 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
@@ -2030,17 +2030,17 @@
   /**
    * No parameters.
    */
-  /* // #### Description
+  // #### Description
   //
   // The analyzer produces this diagnostic when a reference to the class `Never`
   // is found in code that has an SDK constraint whose lower bound is less than
-  // 2.X.0. This class wasn't defined in earlier versions, so this code won't be
-  // able to run against earlier versions of the SDK.
+  // 2.12.0. This class wasn't defined in earlier versions, so this code won't
+  // be able to run against earlier versions of the SDK.
   //
   // #### Examples
   //
   // Here's an example of a pubspec that defines an SDK constraint with a lower
-  // bound of less than 2.X.0:
+  // bound of less than 2.12.0:
   //
   // ```yaml
   // %uri="pubspec.yaml"
@@ -2062,7 +2062,7 @@
   //
   // ```yaml
   // environment:
-  //   sdk: '>=2.X.0 <2.7.0'
+  //   sdk: '>=2.12.0 <2.13.0'
   // ```
   //
   // If you need to support older versions of the SDK, then rewrite the code to
@@ -2070,14 +2070,12 @@
   //
   // ```dart
   // dynamic x;
-  // ``` */
+  // ```
   static const HintCode SDK_VERSION_NEVER = HintCode(
-      // TODO(brianwilkerson) Replace the message with the following when we know
-      //  when this feature will ship:
-      //    The type 'Never' wasn't supported until version 2.X.0, but this code
-      //    is required to be able to run on earlier versions.
       'SDK_VERSION_NEVER',
-      "The type Never isn't yet supported.");
+      "The type 'Never' wasn't supported until version 2.X.0, but this code is "
+          "required to be able to run on earlier versions.",
+      correction: "Try updating the SDK constraints.");
 
   /**
    * No parameters.
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index 6489ae9..e901de2 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -11073,8 +11073,7 @@
   // `isEven` if `s` is `null`. In other words, if `s` is `null`, then neither
   // `length` nor `isEven` will be invoked, and if `s` is non-`null`, then
   // `length` can't return a `null` value. Either way, `isEven` can't be invoked
-  // on a `null` value, so the null-aware operator is neither necessary nor
-  // allowed. See
+  // on a `null` value, so the null-aware operator is not necessary. See
   // [Understanding null safety](/null-safety/understanding-null-safety#smarter-null-aware-methods)
   // for more details.
   //
@@ -11095,8 +11094,8 @@
   static const StaticWarningCode INVALID_NULL_AWARE_OPERATOR =
       StaticWarningCode(
           'INVALID_NULL_AWARE_OPERATOR',
-          "The receiver can't be null, so the null-aware operator '{0}' can't "
-              "be used.",
+          "The receiver can't be null, so the null-aware operator '{0}' is "
+              "unnecessary.",
           correction: "Try replacing the operator '{0}' with '{1}'.",
           hasPublishedDocs: true);
 
diff --git a/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart b/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart
index 25c7280..6bcabc9 100644
--- a/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart
+++ b/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart
@@ -279,21 +279,21 @@
   static bool _isLocalParameter(Element element, AstNode node) {
     if (element is ParameterElement) {
       ExecutableElement definingFunction = element.enclosingElement;
-      FunctionBody body = node.thisOrAncestorOfType<FunctionBody>();
-      while (body != null) {
-        ExecutableElement enclosingFunction;
-        AstNode parent = body.parent;
-        if (parent is ConstructorDeclaration) {
-          enclosingFunction = parent.declaredElement;
-        } else if (parent is FunctionExpression) {
-          enclosingFunction = parent.declaredElement;
-        } else if (parent is MethodDeclaration) {
-          enclosingFunction = parent.declaredElement;
+
+      for (; node != null; node = node.parent) {
+        if (node is ConstructorDeclaration) {
+          if (node.declaredElement == definingFunction) {
+            return true;
+          }
+        } else if (node is FunctionExpression) {
+          if (node.declaredElement == definingFunction) {
+            return true;
+          }
+        } else if (node is MethodDeclaration) {
+          if (node.declaredElement == definingFunction) {
+            return true;
+          }
         }
-        if (enclosingFunction == definingFunction) {
-          return true;
-        }
-        body = parent?.thisOrAncestorOfType<FunctionBody>();
       }
     }
     return false;
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index 4fd12b4..ccfec81 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -3677,7 +3677,7 @@
       handleRecoverableError(
         templateExperimentNotEnabled.withArguments(
           feature.enableString,
-          _versionAsString(ExperimentStatus.currentVersion),
+          _versionAsString(feature.releaseVersion),
         ),
         questionMark,
         questionMark,
@@ -3699,7 +3699,7 @@
       handleRecoverableError(
         templateExperimentNotEnabled.withArguments(
           feature.enableString,
-          _versionAsString(ExperimentStatus.currentVersion),
+          _versionAsString(feature.releaseVersion),
         ),
         modifierToken,
         modifierToken,
@@ -3712,7 +3712,7 @@
     handleRecoverableError(
       templateExperimentNotEnabled.withArguments(
         feature.enableString,
-        _versionAsString(ExperimentStatus.currentVersion),
+        _versionAsString(feature.releaseVersion),
       ),
       bang,
       bang,
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
index 6b23228..440043d 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
@@ -1103,7 +1103,7 @@
     _versionFile = resourceProvider
         .getFolder(resourceProvider.convertPath(sdkRoot))
         .getChildAssumingFile('version');
-    _versionFile.writeAsStringSync('2.10.0');
+    _versionFile.writeAsStringSync('2.12.0');
 
     for (MockSdkLibrary library in _LIBRARIES) {
       var convertedLibrary = library._toProvider(resourceProvider);
diff --git a/pkg/analyzer/test/src/dart/analysis/feature_set_provider_test.dart b/pkg/analyzer/test/src/dart/analysis/feature_set_provider_test.dart
index b304d11..37172b1 100644
--- a/pkg/analyzer/test/src/dart/analysis/feature_set_provider_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/feature_set_provider_test.dart
@@ -7,6 +7,7 @@
 import 'package:analyzer/src/context/packages.dart';
 import 'package:analyzer/src/context/source.dart';
 import 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer/src/dart/analysis/experiments_impl.dart';
 import 'package:analyzer/src/dart/analysis/feature_set_provider.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/generated/source_io.dart';
@@ -260,40 +261,65 @@
   }
 
   test_sdk_allowedExperiments_default() {
+    var feature_a = ExperimentalFeature(
+      index: 0,
+      enableString: 'a',
+      isEnabledByDefault: false,
+      isExpired: false,
+      documentation: 'a',
+      experimentalReleaseVersion: null,
+      releaseVersion: null,
+    );
+
     _newSdkExperimentsFile(r'''
 {
   "version": 1,
   "experimentSets": {
-    "nullSafety": ["non-nullable"]
+    "with_a": ["a"]
   },
   "sdk": {
     "default": {
-      "experimentSet": "nullSafety"
+      "experimentSet": "with_a"
     }
   }
 }
 ''');
 
-    provider = FeatureSetProvider.build(
-      sourceFactory: sourceFactory,
-      resourceProvider: resourceProvider,
-      packages: findPackagesFrom(resourceProvider, getFolder('/test')),
-      packageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
-      nonPackageDefaultLanguageVersion: ExperimentStatus.currentVersion,
-      nonPackageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
-    );
+    overrideKnownFeatures({'a': feature_a}, () {
+      provider = FeatureSetProvider.build(
+        sourceFactory: sourceFactory,
+        resourceProvider: resourceProvider,
+        packages: findPackagesFrom(resourceProvider, getFolder('/test')),
+        packageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
+        nonPackageDefaultLanguageVersion: ExperimentStatus.currentVersion,
+        nonPackageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
+      );
 
-    var featureSet = _getSdkFeatureSet('dart:math');
-    expect(featureSet.isEnabled(Feature.non_nullable), isTrue);
+      var core_featureSet = _getSdkFeatureSet('dart:core');
+      expect(core_featureSet.isEnabled(feature_a), isTrue);
+
+      var math_featureSet = _getSdkFeatureSet('dart:math');
+      expect(math_featureSet.isEnabled(feature_a), isTrue);
+    });
   }
 
   test_sdk_allowedExperiments_library() {
+    var feature_a = ExperimentalFeature(
+      index: 0,
+      enableString: 'a',
+      isEnabledByDefault: false,
+      isExpired: false,
+      documentation: 'a',
+      experimentalReleaseVersion: null,
+      releaseVersion: null,
+    );
+
     _newSdkExperimentsFile(r'''
 {
   "version": 1,
   "experimentSets": {
     "none": [],
-    "nullSafety": ["non-nullable"]
+    "with_a": ["a"]
   },
   "sdk": {
     "default": {
@@ -301,26 +327,29 @@
     },
     "libraries": {
       "math": {
-        "experimentSet": "nullSafety"
+        "experimentSet": "with_a"
       }
     }
   }
 }
 ''');
-    provider = FeatureSetProvider.build(
-      sourceFactory: sourceFactory,
-      resourceProvider: resourceProvider,
-      packages: findPackagesFrom(resourceProvider, getFolder('/test')),
-      packageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
-      nonPackageDefaultLanguageVersion: ExperimentStatus.currentVersion,
-      nonPackageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
-    );
 
-    var core_featureSet = _getSdkFeatureSet('dart:core');
-    expect(core_featureSet.isEnabled(Feature.non_nullable), isFalse);
+    overrideKnownFeatures({'a': feature_a}, () {
+      provider = FeatureSetProvider.build(
+        sourceFactory: sourceFactory,
+        resourceProvider: resourceProvider,
+        packages: findPackagesFrom(resourceProvider, getFolder('/test')),
+        packageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
+        nonPackageDefaultLanguageVersion: ExperimentStatus.currentVersion,
+        nonPackageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
+      );
 
-    var math_featureSet = _getSdkFeatureSet('dart:math');
-    expect(math_featureSet.isEnabled(Feature.non_nullable), isTrue);
+      var core_featureSet = _getSdkFeatureSet('dart:core');
+      expect(core_featureSet.isEnabled(feature_a), isFalse);
+
+      var math_featureSet = _getSdkFeatureSet('dart:math');
+      expect(math_featureSet.isEnabled(feature_a), isTrue);
+    });
   }
 
   test_sdk_allowedExperiments_mockDefault() {
diff --git a/pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart b/pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart
index 854de9d..a2d3d19 100644
--- a/pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart
@@ -864,6 +864,48 @@
     ]);
   }
 
+  test_parameter_named_inDefiningConstructor_asFieldFormalParameter() async {
+    await assertNoErrorsInCode(r'''
+class C {
+  int x;
+  C({@deprecated this.x});
+}
+''');
+  }
+
+  test_parameter_named_inDefiningConstructor_assertInitializer() async {
+    await assertNoErrorsInCode(r'''
+class C {
+  C({@deprecated int y}) : assert(y > 0);
+}
+''');
+  }
+
+  test_parameter_named_inDefiningConstructor_fieldInitializer() async {
+    await assertNoErrorsInCode(r'''
+class C {
+  int x;
+  C({@deprecated int y}) : x = y;
+}
+''');
+  }
+
+  test_parameter_named_inDefiningConstructor_inFieldFormalParameter_notName() async {
+    await assertErrorsInCode(r'''
+class A {}
+
+@deprecated
+class B extends A {}
+
+class C {
+  A a;
+  C({B this.a});
+}
+''', [
+      error(HintCode.DEPRECATED_MEMBER_USE_FROM_SAME_PACKAGE, 68, 1),
+    ]);
+  }
+
   test_parameter_named_inDefiningFunction() async {
     await assertNoErrorsInCode(r'''
 f({@deprecated int x}) => x;
diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md
index ef05f7e..7b12549 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -3884,7 +3884,7 @@
 _The receiver can't be null because of short-circuiting, so the null-aware
 operator '{0}' can't be used._
 
-_The receiver can't be null, so the null-aware operator '{0}' can't be used._
+_The receiver can't be null, so the null-aware operator '{0}' is unnecessary._
 
 #### Description
 
@@ -3923,8 +3923,7 @@
 `isEven` if `s` is `null`. In other words, if `s` is `null`, then neither
 `length` nor `isEven` will be invoked, and if `s` is non-`null`, then
 `length` can't return a `null` value. Either way, `isEven` can't be invoked
-on a `null` value, so the null-aware operator is neither necessary nor
-allowed. See
+on a `null` value, so the null-aware operator is not necessary. See
 [Understanding null safety](/null-safety/understanding-null-safety#smarter-null-aware-methods)
 for more details.
 
@@ -7122,6 +7121,52 @@
 var y = x is int ? 0 : 1;
 {% endprettify %}
 
+### sdk_version_never
+
+_The type 'Never' wasn't supported until version 2.X.0, but this code is
+required to be able to run on earlier versions._
+
+#### Description
+
+The analyzer produces this diagnostic when a reference to the class `Never`
+is found in code that has an SDK constraint whose lower bound is less than
+2.12.0. This class wasn't defined in earlier versions, so this code won't
+be able to run against earlier versions of the SDK.
+
+#### Examples
+
+Here's an example of a pubspec that defines an SDK constraint with a lower
+bound of less than 2.12.0:
+
+```yaml
+environment:
+  sdk: '>=2.5.0 <2.6.0'
+```
+
+In the package that has that pubspec, code like the following produces this
+diagnostic:
+
+{% prettify dart tag=pre+code %}
+[!Never!] n;
+{% endprettify %}
+
+#### Common fixes
+
+If you don't need to support older versions of the SDK, then you can
+increase the SDK constraint to allow the type to be used:
+
+```yaml
+environment:
+  sdk: '>=2.12.0 <2.13.0'
+```
+
+If you need to support older versions of the SDK, then rewrite the code to
+not reference this class:
+
+{% prettify dart tag=pre+code %}
+dynamic x;
+{% endprettify %}
+
 ### sdk_version_set_literal
 
 _Set literals weren't supported until version 2.2, but this code is required to
diff --git a/pkg/compiler/lib/src/ir/impact.dart b/pkg/compiler/lib/src/ir/impact.dart
index 0dfcc55..cedc8b2 100644
--- a/pkg/compiler/lib/src/ir/impact.dart
+++ b/pkg/compiler/lib/src/ir/impact.dart
@@ -201,9 +201,10 @@
   @override
   final ir.StaticTypeContext staticTypeContext;
 
-  ImpactBuilderBase(this.staticTypeContext, ir.ClassHierarchy classHierarchy,
-      this.variableScopeModel)
-      : super(staticTypeContext.typeEnvironment, classHierarchy);
+  ImpactBuilderBase(this.staticTypeContext, StaticTypeCacheImpl staticTypeCache,
+      ir.ClassHierarchy classHierarchy, this.variableScopeModel)
+      : super(
+            staticTypeContext.typeEnvironment, classHierarchy, staticTypeCache);
 
   @override
   void handleIntLiteral(ir.IntLiteral node) {
@@ -642,10 +643,15 @@
   @override
   final inferEffectivelyFinalVariableTypes;
 
-  ImpactBuilder(ir.StaticTypeContext staticTypeContext,
-      ir.ClassHierarchy classHierarchy, VariableScopeModel variableScopeModel,
-      {this.useAsserts: false, this.inferEffectivelyFinalVariableTypes: true})
-      : super(staticTypeContext, classHierarchy, variableScopeModel);
+  ImpactBuilder(
+      ir.StaticTypeContext staticTypeContext,
+      StaticTypeCacheImpl staticTypeCache,
+      ir.ClassHierarchy classHierarchy,
+      VariableScopeModel variableScopeModel,
+      {this.useAsserts: false,
+      this.inferEffectivelyFinalVariableTypes: true})
+      : super(staticTypeContext, staticTypeCache, classHierarchy,
+            variableScopeModel);
 
   ImpactBuilderData computeImpact(ir.Member node) {
     if (retainDataForTesting) {
diff --git a/pkg/compiler/lib/src/ir/static_type.dart b/pkg/compiler/lib/src/ir/static_type.dart
index 4472ff8..13b7967 100644
--- a/pkg/compiler/lib/src/ir/static_type.dart
+++ b/pkg/compiler/lib/src/ir/static_type.dart
@@ -39,6 +39,29 @@
   }
 }
 
+class StaticTypeCacheImpl implements ir.StaticTypeCache {
+  final Map<ir.Expression, ir.DartType> _expressionTypes = {};
+  final Map<ir.ForInStatement, ir.DartType> _forInIteratorTypes = {};
+
+  @override
+  ir.DartType getExpressionType(
+      ir.Expression node, ir.StaticTypeContext context) {
+    return _expressionTypes[node] ??= node.getStaticTypeInternal(context);
+  }
+
+  @override
+  ir.DartType getForInIteratorType(
+      ir.ForInStatement node, ir.StaticTypeContext context) {
+    return _forInIteratorTypes[node] ??= node.getElementTypeInternal(context);
+  }
+
+  @override
+  ir.DartType getForInElementType(
+      ir.ForInStatement node, ir.StaticTypeContext context) {
+    throw UnsupportedError('StaticTypeCacheImpl.getForInElementType');
+  }
+}
+
 /// Visitor that computes and caches the static type of expression while
 /// visiting the full tree at expression level.
 ///
@@ -47,8 +70,7 @@
 /// adds 'handleX' hooks for subclasses to handle individual expressions using
 /// the readily compute static types of subexpressions.
 abstract class StaticTypeVisitor extends StaticTypeBase {
-  final Map<ir.Expression, ir.DartType> _expressionTypeCache = {};
-  Map<ir.ForInStatement, ir.DartType> _forInIteratorTypeCache;
+  final StaticTypeCacheImpl _staticTypeCache;
   Map<ir.Expression, TypeMap> typeMapsForTesting;
   Map<ir.PropertyGet, RuntimeTypeUseData> _pendingRuntimeTypeUseData = {};
 
@@ -57,11 +79,13 @@
   ThisInterfaceType _thisType;
   ir.Library _currentLibrary;
 
-  StaticTypeVisitor(ir.TypeEnvironment typeEnvironment, this.hierarchy)
+  StaticTypeVisitor(
+      ir.TypeEnvironment typeEnvironment, this.hierarchy, this._staticTypeCache)
       : super(typeEnvironment);
 
   StaticTypeCache getStaticTypeCache() {
-    return new StaticTypeCache(_expressionTypeCache, _forInIteratorTypeCache);
+    return new StaticTypeCache(_staticTypeCache._expressionTypes,
+        _staticTypeCache._forInIteratorTypes);
   }
 
   /// If `true`, the effect of executing assert statements is taken into account
@@ -252,7 +276,7 @@
   @override
   ir.DartType visitPropertyGet(ir.PropertyGet node) {
     ir.DartType receiverType = visitNode(node.receiver);
-    ir.DartType resultType = _expressionTypeCache[node] =
+    ir.DartType resultType = _staticTypeCache._expressionTypes[node] =
         _computePropertyGetType(node, receiverType);
     receiverType = _narrowInstanceReceiver(node.interfaceTarget, receiverType);
     handlePropertyGet(node, receiverType, resultType);
@@ -490,7 +514,7 @@
       ir.VariableGet get = new ir.VariableGet(variable)..parent = parent;
       // Visit the newly created variable get.
       handleVariableGet(get, argumentType);
-      _expressionTypeCache[get] = argumentType;
+      _staticTypeCache._expressionTypes[get] = argumentType;
 
       if (checkedParameterType == null) {
         return get;
@@ -672,7 +696,7 @@
             .promote(right.variable, right.variable.type, isTrue: true);
       }
     }
-    _expressionTypeCache[node] = returnType;
+    _staticTypeCache._expressionTypes[node] = returnType;
     handleMethodInvocation(node, receiverType, argumentTypes, returnType);
     return returnType;
   }
@@ -693,7 +717,7 @@
                 ir.SubtypeCheckMode.ignoringNullabilities),
         "Unexpected promotion of ${node.variable} in ${node.parent}. "
         "Expected ${node.promotedType}, found $promotedType");
-    _expressionTypeCache[node] = promotedType;
+    _staticTypeCache._expressionTypes[node] = promotedType;
     handleVariableGet(node, promotedType);
     return promotedType;
   }
@@ -740,7 +764,7 @@
     ir.DartType returnType = ir.Substitution.fromPairs(
             node.target.function.typeParameters, node.arguments.types)
         .substituteType(node.target.function.returnType);
-    _expressionTypeCache[node] = returnType;
+    _staticTypeCache._expressionTypes[node] = returnType;
     handleStaticInvocation(node, argumentTypes, returnType);
     return returnType;
   }
@@ -756,7 +780,7 @@
             .nonNullableRawType(node.target.enclosingClass))
         : new ExactInterfaceType(node.target.enclosingClass,
             ir.Nullability.nonNullable, node.arguments.types);
-    _expressionTypeCache[node] = resultType;
+    _staticTypeCache._expressionTypes[node] = resultType;
     handleConstructorInvocation(node, argumentTypes, resultType);
     return resultType;
   }
@@ -781,7 +805,7 @@
             .substituteType(node.interfaceTarget.getterType);
       }
     }
-    _expressionTypeCache[node] = resultType;
+    _staticTypeCache._expressionTypes[node] = resultType;
     handleSuperPropertyGet(node, resultType);
     return resultType;
   }
@@ -817,7 +841,7 @@
               node.arguments.types)
           .substituteType(returnType);
     }
-    _expressionTypeCache[node] = returnType;
+    _staticTypeCache._expressionTypes[node] = returnType;
     handleSuperMethodInvocation(node, argumentTypes, returnType);
     return returnType;
   }
@@ -936,7 +960,7 @@
   ir.DartType visitInstantiation(ir.Instantiation node) {
     ir.FunctionType expressionType = visitNode(node.expression);
     ir.DartType resultType = _computeInstantiationType(node, expressionType);
-    _expressionTypeCache[node] = resultType;
+    _staticTypeCache._expressionTypes[node] = resultType;
     handleInstantiation(node, expressionType, resultType);
     return resultType;
   }
@@ -982,7 +1006,7 @@
     ir.DartType resultType = operandType == typeEnvironment.nullType
         ? const ir.NeverType(ir.Nullability.nonNullable)
         : operandType.withDeclaredNullability(ir.Nullability.nonNullable);
-    _expressionTypeCache[node] = resultType;
+    _staticTypeCache._expressionTypes[node] = resultType;
     return resultType;
   }
 
@@ -1221,8 +1245,7 @@
             .substituteType(member.getterType);
       }
     }
-    _forInIteratorTypeCache ??= {};
-    _forInIteratorTypeCache[node] = iteratorType;
+    _staticTypeCache._forInIteratorTypes[node] = iteratorType;
     TypeMap beforeLoop = typeMap =
         typeMap.remove(variableScopeModel.getScopeFor(node).assignedVariables);
     visitNode(node.variable);
diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart
index b176c21..05e5686 100644
--- a/pkg/compiler/lib/src/kernel/element_map_impl.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart
@@ -1429,12 +1429,15 @@
           new ir.StaticTypeContext(node, typeEnvironment));
       return converter.convert(impactData);
     } else {
+      StaticTypeCacheImpl staticTypeCache = new StaticTypeCacheImpl();
       KernelImpactBuilder builder = new KernelImpactBuilder(
           this,
           member,
           reporter,
           options,
-          new ir.StaticTypeContext(node, typeEnvironment),
+          new ir.StaticTypeContext(node, typeEnvironment,
+              cache: staticTypeCache),
+          staticTypeCache,
           variableScopeModel,
           annotations,
           _constantValuefier);
diff --git a/pkg/compiler/lib/src/kernel/kernel_impact.dart b/pkg/compiler/lib/src/kernel/kernel_impact.dart
index 94d96a6..ad10c22 100644
--- a/pkg/compiler/lib/src/kernel/kernel_impact.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_impact.dart
@@ -55,12 +55,14 @@
       this.reporter,
       this._options,
       ir.StaticTypeContext staticTypeContext,
+      StaticTypeCacheImpl staticTypeCache,
       VariableScopeModel variableScopeModel,
       this._annotations,
       this._constantValuefier)
       : this.impactBuilder = new ResolutionWorldImpactBuilder(
             elementMap.commonElements.dartTypes, currentMember),
-        super(staticTypeContext, elementMap.classHierarchy, variableScopeModel);
+        super(staticTypeContext, staticTypeCache, elementMap.classHierarchy,
+            variableScopeModel);
 
   @override
   CommonElements get commonElements => elementMap.commonElements;
diff --git a/pkg/compiler/lib/src/kernel/kernel_strategy.dart b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
index 2bc723d..eb961d7 100644
--- a/pkg/compiler/lib/src/kernel/kernel_strategy.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
@@ -25,6 +25,7 @@
 import '../ir/impact.dart';
 import '../ir/modular.dart';
 import '../ir/scope.dart' show ScopeModel;
+import '../ir/static_type.dart';
 import '../js_backend/annotations.dart';
 import '../js_backend/backend_impact.dart';
 import '../js_backend/backend_usage.dart';
@@ -439,8 +440,11 @@
       // depend on metadata, so these parts of the impact data need to be
       // computed during conversion to [ResolutionImpact].
       impactBuilderData = _compilerTask.measureSubtask('worldImpact', () {
+        StaticTypeCacheImpl staticTypeCache = StaticTypeCacheImpl();
         ImpactBuilder builder = ImpactBuilder(
-            ir.StaticTypeContext(node, _elementMap.typeEnvironment),
+            ir.StaticTypeContext(node, _elementMap.typeEnvironment,
+                cache: staticTypeCache),
+            staticTypeCache,
             _elementMap.classHierarchy,
             scopeModel.variableScopeModel,
             useAsserts: _elementMap.options.enableUserAssertions,
diff --git a/pkg/compiler/test/analyses/analysis_helper.dart b/pkg/compiler/test/analyses/analysis_helper.dart
index eee894d..3261133 100644
--- a/pkg/compiler/test/analyses/analysis_helper.dart
+++ b/pkg/compiler/test/analyses/analysis_helper.dart
@@ -86,7 +86,8 @@
       : assert(evaluationMode != null),
         super(
             new ir.TypeEnvironment(new ir.CoreTypes(component), classHierarchy),
-            classHierarchy) {
+            classHierarchy,
+            new StaticTypeCacheImpl()) {
     _constantEvaluator = new Dart2jsConstantEvaluator(
         typeEnvironment, const ir.SimpleErrorReporter().report,
         evaluationMode: evaluationMode);
diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart
index c31877f..275c2ad 100644
--- a/pkg/dartdev/lib/dartdev.dart
+++ b/pkg/dartdev/lib/dartdev.dart
@@ -89,14 +89,16 @@
       args = List.from(args)..remove('--disable-dartdev-analytics');
     }
 
-    // These flags have a format that can't be handled by package:args, so while
-    // they are valid flags we'll assume the VM has verified them by this point.
-    args = args
-        .where(
-          (element) => !(element.contains('--observe') ||
-              element.contains('--enable-vm-service')),
-        )
-        .toList();
+    if (args.contains('run')) {
+      // These flags have a format that can't be handled by package:args, so while
+      // they are valid flags we'll assume the VM has verified them by this point.
+      args = args
+          .where(
+            (element) => !(element.contains('--observe') ||
+                element.contains('--enable-vm-service')),
+          )
+          .toList();
+    }
 
     // If ... help pub ... is in the args list, remove 'help', and add '--help'
     // to the end of the list. This will make it possible to use the help
diff --git a/pkg/dartdev/test/commands/run_test.dart b/pkg/dartdev/test/commands/run_test.dart
index c380bcd..f33cb83 100644
--- a/pkg/dartdev/test/commands/run_test.dart
+++ b/pkg/dartdev/test/commands/run_test.dart
@@ -221,4 +221,18 @@
     expect(result.stderr, contains('Unhandled exception'));
     expect(result.exitCode, 255);
   });
+
+  test('does not interpret VM flags provided after script', () async {
+    p = project(mainSrc: 'void main() { assert(false); }');
+
+    // Any VM flags passed after the script shouldn't be interpreted by the VM.
+    ProcessResult result = p.runSync('run', [
+      p.relativeFilePath,
+      '--enable-asserts',
+    ]);
+
+    expect(result.stdout, isEmpty);
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+  });
 }
diff --git a/pkg/dartfix/.gitignore b/pkg/dartfix/.gitignore
deleted file mode 100644
index 113d783..0000000
--- a/pkg/dartfix/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-*.iml
-.dart_tool
-.packages
-pubspec.lock
diff --git a/pkg/dartfix/CHANGELOG.md b/pkg/dartfix/CHANGELOG.md
deleted file mode 100644
index fb292fa..0000000
--- a/pkg/dartfix/CHANGELOG.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# 0.1.8
-* The dartfix package has been deprecated. The functionality has been moved to
-  the new `dart fix` command that's included in the SDK.
-
-# 0.1.7
-* Improve experimental non-nullable migration support.
-* Extract some nnbd migration implementation components from the dartfix
-  package.
-
-# 0.1.6
-* Improve experimental non-nullable migration support.
-
-# 0.1.5
-* Add command line options for selecting/excluding fixes to apply (`--fix`,
-  `--excludeFix`, and `--required`). Call with `--help` for more details.
-* Add a `--color` option for printing messages with ANSI colors. This defaults
-  to true if the terminal supports ANSI colors.
-* Add a `--pedantic` option for specifying fixes relating to the [pedantic]
-  lints.
-* Add experimental non-nullable migration support.
-
-[pedantic]: https://pub.dev/packages/pedantic
-
-# 0.1.4
- * update protocol version constraints
-
-# 0.1.3
- * update SDK constraints
-
-# 0.1.2
- * update SDK constraints
- * add example.dart showing what can be "fixed"
-
-# 0.1.1
- * Remove reading dartfix version from pubspec
-
-# 0.1.0
- * Initial version
diff --git a/pkg/dartfix/LICENSE b/pkg/dartfix/LICENSE
deleted file mode 100644
index a9ab5f8..0000000
--- a/pkg/dartfix/LICENSE
+++ /dev/null
@@ -1,26 +0,0 @@
-Copyright 2018, the Dart project authors. All rights reserved.
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-    * Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above
-      copyright notice, this list of conditions and the following
-      disclaimer in the documentation and/or other materials provided
-      with the distribution.
-    * Neither the name of Google Inc. nor the names of its
-      contributors may be used to endorse or promote products derived
-      from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/pkg/dartfix/README.md b/pkg/dartfix/README.md
deleted file mode 100644
index a3f0493..0000000
--- a/pkg/dartfix/README.md
+++ /dev/null
@@ -1,82 +0,0 @@
-`dartfix` is a command-line tool for migrating your Dart code
-to use newer syntax styles.
-
-## Usage
-
-> **Important:**
-> Save a copy of your source code before making changes with `dartfix`.
-> Unlike [dartfmt][], which makes only safe changes (usually to whitespace),
-> `dartfix` can make changes that you might need to undo or modify.
-
-Before you can use the `dartfix` tool, you need to
-[install it](#installing-and-updating-dartfix), as described below.
-Then invoke it with the name of the directory that you want to update.
-When you're ready to make the suggested changes,
-add the `--overwrite` option.
-
-```terminal
-$ dartfix examples/misc
-... summary of recommended changes ...
-$ dartfix examples/misc --overwrite
-```
-
-## Features
-`dartfix` applies different types of "fixes" to migrate your Dart code.
-By default, all fixes are applied, but you can select only the specific fixes you
-want. See `dartfix --help` for more about the available command line options.
-
-Some of the fixes that you can apply are "required" in that the Dart language
-is changing and at some point the old syntax will no longer be supported.
-To only apply these changes, pass the `--required` option on the command line.
-The required fixes include:
-
-* Find classes used as mixins, and convert them to use the `mixin` keyword
-    instead of `class`.
-    Mixin support is one of the [features added to Dart in 2.1][].
-    At some point in the future, the Dart team plans
-    to disallow using classes as mixins.
-
-* Move named constructor type arguments from the name to the type. <br>
-  For example, given `class A<T> { A.from(Object obj) { } }`,
-  `dartfix` changes constructor invocations in the following way:
-
-  ```
-  Original code:
-  A.from<String>(anObject) // Invokes the `A.from` named constructor.
-
-  Code produced by dartfix:
-  A<String>.from(anObject) // Same, but the type is directly after `A`.
-  ```
-
-Other changes are recommended but not required. These include:
-
-* Find `double` literals that end in `.0`, and remove the `.0`.
-  Language support for this was [added in Dart in 2.1][].
-
-## Installing and updating dartfix
-
-The easiest way to use `dartfix` is to [globally install][] it,
-so that it can be [in your path][PATH]:
-
-```terminal
-$ pub global activate dartfix
-```
-
-Use the same command to update `dartfix`.
-We recommend updating `dartfix` whenever you update your Dart SDK
-or when a new feature is released.
-
-## Filing issues
-
-If you want a new fix, first look at [dartfix issues][]
-and star the fixes you want.
-If no issue exists for the fix, [create a GitHub issue.][new issue]
-
-[dartfix]: https://pub.dev/packages/dartfix
-[dartfmt]: https://dart.dev/tools/dartfmt
-[added in Dart in 2.1]: https://github.com/dart-lang/sdk/blob/master/CHANGELOG.md#210---2018-11-15
-[features added to Dart in 2.1]: https://github.com/dart-lang/sdk/blob/master/CHANGELOG.md#210---2018-11-15
-[globally install]: https://dart.dev/tools/pub/cmd/pub-global
-[new issue]: https://github.com/dart-lang/sdk/issues/new?title=dartfix%20request%3A%20%3CSUMMARIZE%20REQUEST%20HERE%3E
-[dartfix issues]: https://github.com/dart-lang/sdk/issues?q=is%3Aissue+is%3Aopen+label%3Aanalyzer-dartfix
-[PATH]: https://dart.dev/tools/pub/cmd/pub-global#running-a-script-from-your-path
diff --git a/pkg/dartfix/analysis_options.yaml b/pkg/dartfix/analysis_options.yaml
deleted file mode 100644
index 4be2194..0000000
--- a/pkg/dartfix/analysis_options.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-include: package:pedantic/analysis_options.1.8.0.yaml
-
-analyzer:
-  strong-mode:
-    implicit-casts: false
-  errors:
-    # Increase the severity of the unused_import hint.
-    unused_import: warning
-    unnecessary_brace_in_string_interps: warning
-    # There are many pre-existing violations; this lint may not work well with
-    # the Analyzer team's style.
-    omit_local_variable_types: ignore
-    # Turn off the prefer_is_empty lint so that it can be used in the example tests
-    # and not be reported during normal package analysis.
-    prefer_is_empty: ignore
-
-linter:
-  rules:
-    - await_only_futures
-    - directives_ordering
-    - empty_statements
-    - unnecessary_brace_in_string_interps
-    #
-    # Delta from pedantic 1.8.0 to 1.9.0
-    #
-    #- always_declare_return_types # 17
-    - always_require_non_null_named_parameters
-    - annotate_overrides
-    - avoid_null_checks_in_equality_operators
-    - camel_case_extensions
-    #- omit_local_variable_types # 44
-    - prefer_adjacent_string_concatenation
-    - prefer_collection_literals
-    - prefer_conditional_assignment
-    #- prefer_final_fields # 1
-    - prefer_for_elements_to_map_fromIterable
-    - prefer_generic_function_type_aliases
-    #- prefer_if_null_operators # 2
-    - prefer_single_quotes
-    - prefer_spread_collections
-    - unnecessary_this
-    - use_function_type_syntax_for_parameters
diff --git a/pkg/dartfix/bin/dartfix.dart b/pkg/dartfix/bin/dartfix.dart
deleted file mode 100644
index d611474..0000000
--- a/pkg/dartfix/bin/dartfix.dart
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env dart
-// Copyright (c) 2018, 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:dartfix/src/driver.dart';
-
-/// The entry point for dartfix.
-void main(List<String> args) async {
-  Driver starter = Driver();
-
-  // Wait for the starter to complete.
-  await starter.start(args);
-}
diff --git a/pkg/dartfix/example/example.dart b/pkg/dartfix/example/example.dart
deleted file mode 100644
index 6093104..0000000
--- a/pkg/dartfix/example/example.dart
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2019, 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.
-
-// This file contains code that is modified by running dartfix.
-// After running dartfix, this content matches a file in the "fixed" directory.
-
-// Dart will automatically convert int literals to doubles.
-// Running dartfix converts this double literal to an int
-// if --double-to-int is specified on the command line.
-const double myDouble = 4.0;
-
-// This class is used as a mixin but does not use the new mixin syntax.
-// Running dartfix converts this class to use the new syntax.
-class MyMixin {
-  final someValue = myDouble;
-}
-
-class MyClass with MyMixin {}
-
-void main(List<String> args) {
-  if (args.length == 0) {
-    print('myDouble = ${MyClass().someValue}');
-  }
-}
diff --git a/pkg/dartfix/fixed/example_default.dart b/pkg/dartfix/fixed/example_default.dart
deleted file mode 100644
index 10bd52c..0000000
--- a/pkg/dartfix/fixed/example_default.dart
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2019, 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.
-
-// This file contains code that has been modified by running dartfix.
-// See example.dart for the original unmodified code.
-
-// Dart will automatically convert int literals to doubles.
-// Running dartfix converts this double literal to an int
-// if --double-to-int is specified on the command line.
-const double myDouble = 4;
-
-// This class is used as a mixin but does not use the new mixin syntax.
-// Running dartfix converts this class to use the new syntax.
-mixin MyMixin {
-  final someValue = myDouble;
-}
-
-class MyClass with MyMixin {}
-
-void main(List<String> args) {
-  if (args.length == 0) {
-    print('myDouble = ${MyClass().someValue}');
-  }
-}
diff --git a/pkg/dartfix/fixed/example_pedantic.dart b/pkg/dartfix/fixed/example_pedantic.dart
deleted file mode 100644
index 5337a5b..0000000
--- a/pkg/dartfix/fixed/example_pedantic.dart
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2019, 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.
-
-// This file contains code that has been modified by running dartfix.
-// See example.dart for the original unmodified code.
-
-// Dart will automatically convert int literals to doubles.
-// Running dartfix converts this double literal to an int
-// if --double-to-int is specified on the command line.
-const double myDouble = 4.0;
-
-// This class is used as a mixin but does not use the new mixin syntax.
-// Running dartfix converts this class to use the new syntax.
-class MyMixin {
-  final someValue = myDouble;
-}
-
-class MyClass with MyMixin {}
-
-void main(List<String> args) {
-  if (args.isEmpty) {
-    print('myDouble = ${MyClass().someValue}');
-  }
-}
diff --git a/pkg/dartfix/fixed/example_prefer_is_empty.dart b/pkg/dartfix/fixed/example_prefer_is_empty.dart
deleted file mode 100644
index 5337a5b..0000000
--- a/pkg/dartfix/fixed/example_prefer_is_empty.dart
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2019, 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.
-
-// This file contains code that has been modified by running dartfix.
-// See example.dart for the original unmodified code.
-
-// Dart will automatically convert int literals to doubles.
-// Running dartfix converts this double literal to an int
-// if --double-to-int is specified on the command line.
-const double myDouble = 4.0;
-
-// This class is used as a mixin but does not use the new mixin syntax.
-// Running dartfix converts this class to use the new syntax.
-class MyMixin {
-  final someValue = myDouble;
-}
-
-class MyClass with MyMixin {}
-
-void main(List<String> args) {
-  if (args.isEmpty) {
-    print('myDouble = ${MyClass().someValue}');
-  }
-}
diff --git a/pkg/dartfix/fixed/example_required.dart b/pkg/dartfix/fixed/example_required.dart
deleted file mode 100644
index 9324f22..0000000
--- a/pkg/dartfix/fixed/example_required.dart
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2019, 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.
-
-// This file contains code that has been modified by running dartfix.
-// See example.dart for the original unmodified code.
-
-// Dart will automatically convert int literals to doubles.
-// Running dartfix converts this double literal to an int
-// if --double-to-int is specified on the command line.
-const double myDouble = 4.0;
-
-// This class is used as a mixin but does not use the new mixin syntax.
-// Running dartfix converts this class to use the new syntax.
-mixin MyMixin {
-  final someValue = myDouble;
-}
-
-class MyClass with MyMixin {}
-
-void main(List<String> args) {
-  if (args.length == 0) {
-    print('myDouble = ${MyClass().someValue}');
-  }
-}
diff --git a/pkg/dartfix/lib/handler/analysis_complete_handler.dart b/pkg/dartfix/lib/handler/analysis_complete_handler.dart
deleted file mode 100644
index e45b8a8..0000000
--- a/pkg/dartfix/lib/handler/analysis_complete_handler.dart
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) 2018, 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.
-//
-// This file has been automatically generated. Please do not edit it manually.
-// To regenerate the file, use the script
-// "pkg/analysis_server/tool/spec/generate_files".
-
-import 'dart:async';
-
-import 'package:analysis_server_client/handler/notification_handler.dart';
-import 'package:analysis_server_client/protocol.dart';
-
-/// [AnalysisCompleteHandler] listens to analysis server notifications
-/// and detects when analysis has finished.
-mixin AnalysisCompleteHandler on NotificationHandler {
-  Completer<void> _analysisComplete;
-
-  @override
-  void onServerStatus(ServerStatusParams params) {
-    if (params.analysis != null && !params.analysis.isAnalyzing) {
-      _analysisComplete?.complete();
-      _analysisComplete = null;
-    }
-  }
-
-  Future<void> analysisComplete() {
-    _analysisComplete ??= Completer<void>();
-    return _analysisComplete.future;
-  }
-}
diff --git a/pkg/dartfix/lib/listener/bad_message_listener.dart b/pkg/dartfix/lib/listener/bad_message_listener.dart
deleted file mode 100644
index 03cb102..0000000
--- a/pkg/dartfix/lib/listener/bad_message_listener.dart
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (c) 2018, 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_client/listener/server_listener.dart';
-
-/// [BadMessageListener] throws an exception if the [Client] receives bad data.
-mixin BadMessageListener on ServerListener {
-  /// True if we've received bad data from the server.
-  bool _receivedBadDataFromServer = false;
-
-  void throwDelayedException(String prefix, String details) {
-    if (!_receivedBadDataFromServer) {
-      _receivedBadDataFromServer = true;
-      // Give the server 1 second to continue outputting bad data
-      // such as outputting a stacktrace.
-      Future.delayed(Duration(seconds: 1), () {
-        throw '$prefix $details';
-      });
-    }
-  }
-
-  @override
-  void badMessage(String trimmedLine, exception) {
-    super.badMessage(trimmedLine, exception);
-    throwDelayedException('JSON decode failure', '$exception');
-  }
-
-  @override
-  void errorMessage(String line) {
-    super.errorMessage(line);
-    throwDelayedException('ERR:', line);
-  }
-
-  @override
-  void unexpectedMessage(Map<String, dynamic> message) {
-    super.unexpectedMessage(message);
-    throwDelayedException(
-        'BAD DATA FROM SERVER:', 'Unexpected message from server');
-  }
-
-  @override
-  void unexpectedResponse(Map<String, dynamic> message, id) {
-    super.unexpectedResponse(message, id);
-    throw 'Unexpected response from server: id=$id';
-  }
-
-  @override
-  void unexpectedStop(int exitCode) {
-    super.unexpectedStop(exitCode);
-    throwDelayedException('Server terminated with exit code', '$exitCode');
-  }
-}
diff --git a/pkg/dartfix/lib/listener/recording_listener.dart b/pkg/dartfix/lib/listener/recording_listener.dart
deleted file mode 100644
index a97120c..0000000
--- a/pkg/dartfix/lib/listener/recording_listener.dart
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) 2018, 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_client/listener/server_listener.dart';
-import 'package:analysis_server_client/server.dart';
-import 'package:dartfix/listener/bad_message_listener.dart';
-import 'package:dartfix/listener/timed_listener.dart';
-
-/// [RecordingListener] caches all messages exchanged with the server
-/// and print them if a problem occurs.
-///
-/// This is primarily used when testing and debugging the analysis server.
-class RecordingListener with ServerListener, BadMessageListener, TimedListener {
-  /// True if we are currently printing out messages exchanged with the server.
-  bool _echoMessages = false;
-
-  /// Messages which have been exchanged with the server; we buffer these
-  /// up until the test finishes, so that they can be examined in the debugger
-  /// or printed out in response to a call to [echoMessages].
-  final _messages = <String>[];
-
-  /// Print out any messages exchanged with the server.  If some messages have
-  /// already been exchanged with the server, they are printed out immediately.
-  void echoMessages() {
-    if (_echoMessages) {
-      return;
-    }
-    _echoMessages = true;
-    for (String line in _messages) {
-      print(line);
-    }
-  }
-
-  /// Called when the [Server] is terminating the server process
-  /// rather than requesting that the server stop itself.
-  @override
-  void killingServerProcess(String reason) {
-    echoMessages();
-    super.killingServerProcess(reason);
-  }
-
-  /// Log a timed message about interaction with the server.
-  @override
-  void logTimed(double elapseTime, String prefix, String details) {
-    String line = '$elapseTime: $prefix $details';
-    if (_echoMessages) {
-      print(line);
-    }
-    _messages.add(line);
-  }
-
-  @override
-  void throwDelayedException(String prefix, String details) {
-    echoMessages();
-    super.throwDelayedException(prefix, details);
-  }
-}
diff --git a/pkg/dartfix/lib/listener/timed_listener.dart b/pkg/dartfix/lib/listener/timed_listener.dart
deleted file mode 100644
index ab41744..0000000
--- a/pkg/dartfix/lib/listener/timed_listener.dart
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2018, 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_client/listener/server_listener.dart';
-
-/// [TimedListener] appends a timestamp (seconds since server startup)
-/// to each logged interaction with the server.
-mixin TimedListener on ServerListener {
-  /// Stopwatch that we use to generate timing information for debug output.
-  final Stopwatch _time = Stopwatch();
-
-  /// The [currentElapseTime] at which the last communication was received from
-  /// the server or `null` if no communication has been received.
-  double lastCommunicationTime;
-
-  /// The current elapse time (seconds) since the server was started.
-  double get currentElapseTime => _time.elapsedTicks / _time.frequency;
-
-  @override
-  void log(String prefix, String details) {
-    logTimed(currentElapseTime, prefix, details);
-  }
-
-  /// Log a timed message about interaction with the server.
-  void logTimed(double elapseTime, String prefix, String details);
-
-  @override
-  void messageReceived(String json) {
-    lastCommunicationTime = currentElapseTime;
-    super.messageReceived(json);
-  }
-
-  @override
-  void startingServer(String dartBinary, List<String> arguments) {
-    _time.start();
-    super.startingServer(dartBinary, arguments);
-  }
-}
diff --git a/pkg/dartfix/lib/src/context.dart b/pkg/dartfix/lib/src/context.dart
deleted file mode 100644
index dd28a47..0000000
--- a/pkg/dartfix/lib/src/context.dart
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (c) 2018, 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 'dart:io' as io;
-
-/// The context for dartfix.
-class Context {
-  String get workingDir => io.Directory.current.path;
-
-  bool exists(String filePath) {
-    return io.FileSystemEntity.typeSync(filePath) !=
-        io.FileSystemEntityType.notFound;
-  }
-
-  void exit(int code) {
-    io.exit(code);
-  }
-
-  bool isDirectory(String filePath) {
-    return io.FileSystemEntity.typeSync(filePath) ==
-        io.FileSystemEntityType.directory;
-  }
-}
diff --git a/pkg/dartfix/lib/src/driver.dart b/pkg/dartfix/lib/src/driver.dart
deleted file mode 100644
index 26f7ce93..0000000
--- a/pkg/dartfix/lib/src/driver.dart
+++ /dev/null
@@ -1,508 +0,0 @@
-// Copyright (c) 2018, 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 'dart:io' show File, Platform;
-
-import 'package:analysis_server_client/handler/connection_handler.dart';
-import 'package:analysis_server_client/handler/notification_handler.dart';
-import 'package:analysis_server_client/listener/server_listener.dart';
-import 'package:analysis_server_client/protocol.dart';
-import 'package:analysis_server_client/server.dart';
-import 'package:cli_util/cli_logging.dart';
-import 'package:dartfix/handler/analysis_complete_handler.dart';
-import 'package:dartfix/listener/bad_message_listener.dart';
-import 'package:dartfix/src/context.dart';
-import 'package:dartfix/src/options.dart';
-import 'package:dartfix/src/util.dart';
-import 'package:path/path.dart' as path;
-import 'package:pub_semver/pub_semver.dart';
-
-import 'migrate/display.dart';
-import 'util.dart';
-
-class Driver {
-  Context context;
-  _Handler handler;
-  Logger logger;
-  Server server;
-
-  bool force;
-  bool overwrite;
-  List<String> targets;
-  EditDartfixResult result;
-
-  Ansi get ansi => logger.ansi;
-
-  /// Apply the fixes that were computed.
-  void applyFixes() {
-    for (SourceFileEdit fileEdit in result.edits) {
-      final file = File(fileEdit.file);
-      String code = file.existsSync() ? file.readAsStringSync() : '';
-      code = SourceEdit.applySequence(code, fileEdit.edits);
-      file.writeAsStringSync(code);
-    }
-  }
-
-  bool checkIfChangesShouldBeApplied(EditDartfixResult result) {
-    logger.stdout('');
-    if (result.hasErrors) {
-      logger.stdout('WARNING: The analyzed source contains errors'
-          ' that might affect the accuracy of these changes.');
-      logger.stdout('');
-      if (!force) {
-        logger.stdout('Rerun with --$forceOption to apply these changes.');
-        return false;
-      }
-    } else if (!overwrite && !force) {
-      logger.stdout('Rerun with --$overwriteOption to apply these changes.');
-      return false;
-    }
-    return true;
-  }
-
-  /// Check if the specified options is supported by the version of analysis
-  /// server being run and return `true` if they are.
-  /// Display an error message and return `false` if not.
-  bool checkIfSelectedOptionsAreSupported(Options options) {
-    if (handler.serverProtocolVersion.compareTo(Version(1, 27, 2)) >= 0) {
-      return true;
-    }
-    if (options.pedanticFixes) {
-      _unsupportedOption(pedanticOption);
-      return false;
-    }
-    if (handler.serverProtocolVersion.compareTo(Version(1, 22, 2)) >= 0) {
-      return true;
-    }
-    if (options.excludeFixes.isNotEmpty) {
-      _unsupportedOption(excludeFixOption);
-      return false;
-    }
-    if (options.includeFixes.isNotEmpty) {
-      _unsupportedOption(includeFixOption);
-      return false;
-    }
-    if (options.showHelp) {
-      return false;
-    }
-    return true;
-  }
-
-  void printAndApplyFixes() {
-    showDescriptions('Recommended changes that cannot be automatically applied',
-        result.otherSuggestions);
-    showDetails(result.details);
-    if (result.edits.isEmpty) {
-      logger.stdout('');
-      logger.stdout(result.otherSuggestions.isNotEmpty
-          ? 'None of the recommended changes can be automatically applied.'
-          : 'No recommended changes.');
-      return;
-    }
-    logger.stdout('');
-    logger.stdout(ansi.emphasized('Files to be changed:'));
-    for (SourceFileEdit fileEdit in result.edits) {
-      logger.stdout('  ${_relativePath(fileEdit.file)}');
-    }
-    if (checkIfChangesShouldBeApplied(result)) {
-      applyFixes();
-      logger.stdout(ansi.emphasized('Changes have been applied.'));
-    }
-  }
-
-  Future<EditDartfixResult> requestFixes(
-    Options options, {
-    Progress progress,
-  }) async {
-    Future isAnalysisComplete = handler.analysisComplete();
-
-    final params = EditDartfixParams(options.targets);
-    if (options.excludeFixes.isNotEmpty) {
-      params.excludedFixes = options.excludeFixes;
-    }
-    if (options.includeFixes.isNotEmpty) {
-      params.includedFixes = options.includeFixes;
-    }
-    if (options.pedanticFixes) {
-      params.includePedanticFixes = true;
-    }
-    Map<String, dynamic> json =
-        await server.send(EDIT_REQUEST_DARTFIX, params.toJson());
-
-    // TODO(danrubel): This is imprecise signal for determining when all
-    // analysis error notifications have been received. Consider adding a new
-    // notification indicating that the server is idle (all requests processed,
-    // all analysis complete, all notifications sent).
-    await isAnalysisComplete;
-
-    progress.finish(showTiming: true);
-    ResponseDecoder decoder = ResponseDecoder(null);
-    return EditDartfixResult.fromJson(decoder, 'result', json);
-  }
-
-  /// Return `true` if the changes should be applied.
-  bool shouldApplyFixes(EditDartfixResult result) {
-    return overwrite || force;
-  }
-
-  void showDescriptions(String title, List<DartFixSuggestion> suggestions) {
-    if (suggestions.isNotEmpty) {
-      logger.stdout('');
-      logger.stdout(ansi.emphasized('$title:'));
-      List<DartFixSuggestion> sorted = List.from(suggestions)
-        ..sort(compareSuggestions);
-      for (DartFixSuggestion suggestion in sorted) {
-        final msg = StringBuffer();
-        msg.write('  ${toSentenceFragment(suggestion.description)}');
-        final loc = suggestion.location;
-        if (loc != null) {
-          msg.write(' • ${_relativePath(loc.file)}');
-          msg.write(' • ${loc.startLine}:${loc.startColumn}');
-        }
-        logger.stdout(msg.toString());
-      }
-    }
-  }
-
-  void showDetails(List<String> details) {
-    if (details == null || details.isEmpty) {
-      return;
-    }
-    logger.stdout('''
-
-Analysis Details:
-''');
-    for (String detail in details) {
-      logger.stdout('''
- • $detail
-''');
-    }
-  }
-
-  void showFix(DartFix fix) {
-    logger.stdout('''
-
-• ${ansi.emphasized(fix.name)}''');
-    if (fix.description != null) {
-      for (String line in _indentAndWrapDescription(fix.description)) {
-        logger.stdout(line);
-      }
-    }
-  }
-
-  Future<EditGetDartfixInfoResult> showFixes({Progress progress}) async {
-    Map<String, dynamic> json = await server.send(
-        EDIT_REQUEST_GET_DARTFIX_INFO, EditGetDartfixInfoParams().toJson());
-    progress?.finish(showTiming: true);
-    ResponseDecoder decoder = ResponseDecoder(null);
-    final result = EditGetDartfixInfoResult.fromJson(decoder, 'result', json);
-
-    final fixes = List<DartFix>.from(result.fixes)
-      ..sort((f1, f2) => f1.name.compareTo(f2.name));
-
-    logger.stdout('''
-
-These fixes can be enabled using --$includeFixOption:''');
-
-    fixes
-      ..sort(compareFixes)
-      ..forEach(showFix);
-
-    return result;
-  }
-
-  Future start(
-    List<String> args, {
-    Context testContext,
-    Logger testLogger,
-  }) async {
-    final Options options = Options.parse(args, testContext, testLogger);
-
-    force = options.force;
-    overwrite = options.overwrite;
-    targets = options.targets;
-    context = testContext ?? options.context;
-    logger = testLogger ?? options.logger;
-    server = Server(listener: _Listener(logger));
-    handler = _Handler(this, context);
-
-    // Start showing progress before we start the analysis server.
-    Progress progress;
-    if (options.showHelp) {
-      progress = logger.progress('${ansi.emphasized('Listing fixes')}');
-    } else {
-      progress = logger.progress('${ansi.emphasized('Calculating fixes')}');
-    }
-
-    if (!await startServer(options)) {
-      context.exit(16);
-    }
-
-    if (!checkIfSelectedOptionsAreSupported(options)) {
-      await server.stop();
-      context.exit(1);
-    }
-
-    if (options.showHelp) {
-      try {
-        await showFixes(progress: progress);
-      } finally {
-        await server.stop();
-      }
-      context.exit(0);
-    }
-
-    if (options.includeFixes.isEmpty && !options.pedanticFixes) {
-      logger.stdout('No fixes specified.');
-      context.exit(1);
-    }
-
-    Future serverStopped;
-    try {
-      await startServerAnalysis(options);
-      result = await requestFixes(options, progress: progress);
-      var fileEdits = result.edits;
-      var editCount = 0;
-      for (SourceFileEdit fileEdit in fileEdits) {
-        editCount += fileEdit.edits.length;
-      }
-      logger.stdout('Found $editCount changes in ${fileEdits.length} files.');
-
-      previewFixes(logger, result);
-
-      //
-      // Stop the server.
-      //
-      serverStopped = server.stop();
-
-      logger.stdout('');
-
-      // Check if we should apply fixes.
-      if (result.edits.isEmpty) {
-        logger.stdout(result.otherSuggestions.isNotEmpty
-            ? 'None of the recommended changes can be automatically applied.'
-            : 'There are no recommended changes.');
-      } else if (shouldApplyFixes(result)) {
-        applyFixes();
-        logger.stdout('Changes have been applied.');
-      } else {
-        logger.stdout('Re-run with --overwrite to apply the above changes.');
-      }
-      await serverStopped;
-    } finally {
-      // If we didn't already try to stop the server, then stop it now.
-      if (serverStopped == null) {
-        await server.stop();
-      }
-    }
-  }
-
-  Future<bool> startServer(Options options) async {
-    // Automatically run analysis server from source
-    // if this command line tool is being run from source within the SDK repo.
-    String serverPath = options.serverSnapshot ?? findServerPath();
-    if (options.verbose) {
-      logger.trace('''
-Dart SDK version ${Platform.version}
-  ${Platform.resolvedExecutable}
-dartfix
-  ${Platform.script.toFilePath()}
-analysis server
-  $serverPath
-''');
-    }
-    await server.start(
-      clientId: 'dartfix',
-      clientVersion: 'unspecified',
-      sdkPath: options.sdkPath,
-      serverPath: serverPath,
-    );
-    server.listenToOutput(notificationProcessor: handler.handleEvent);
-    return handler.serverConnected(timeLimit: const Duration(seconds: 30));
-  }
-
-  Future<Progress> startServerAnalysis(
-    Options options, {
-    Progress progress,
-  }) async {
-    logger.trace('');
-    logger.trace('Setup analysis');
-    await server.send(SERVER_REQUEST_SET_SUBSCRIPTIONS,
-        ServerSetSubscriptionsParams([ServerService.STATUS]).toJson());
-    await server.send(
-        ANALYSIS_REQUEST_SET_ANALYSIS_ROOTS,
-        AnalysisSetAnalysisRootsParams(
-          options.targets,
-          const [],
-        ).toJson());
-    return progress;
-  }
-
-  List<String> _indentAndWrapDescription(String description) =>
-      description.split('\n').map((line) => '    $line').toList();
-
-  String _relativePath(String filePath) {
-    for (String target in targets) {
-      if (filePath.startsWith(target)) {
-        return filePath.substring(target.length + 1);
-      }
-    }
-    return filePath;
-  }
-
-  void _unsupportedOption(String option) {
-    final version = handler.serverProtocolVersion.toString();
-    logger.stderr('''
-The --$option option is not supported by analysis server version $version.
-Please upgrade to a newer version of the Dart SDK to use this option.''');
-  }
-
-  void previewFixes(
-    Logger logger,
-    EditDartfixResult results,
-  ) {
-    final Ansi ansi = logger.ansi;
-
-    Map<String, List<DartFixSuggestion>> fileSuggestions = {};
-    for (DartFixSuggestion suggestion in results.suggestions) {
-      String file = suggestion.location.file;
-      fileSuggestions.putIfAbsent(file, () => <DartFixSuggestion>[]);
-      fileSuggestions[file].add(suggestion);
-    }
-
-    // present a diff-like view
-    for (SourceFileEdit sourceFileEdit in results.edits) {
-      String file = sourceFileEdit.file;
-      String relPath = path.relative(file);
-      int count = sourceFileEdit.edits.length;
-
-      logger.stdout('');
-      logger.stdout('${ansi.emphasized(relPath)} '
-          '($count ${pluralize('change', count)}):');
-
-      String source;
-      try {
-        source = File(file).readAsStringSync();
-      } catch (_) {}
-
-      if (source == null) {
-        logger.stdout('  (unable to retrieve source for file)');
-      } else {
-        SourcePrinter sourcePrinter = SourcePrinter(source);
-
-        List<SourceEdit> edits = sortEdits(sourceFileEdit);
-
-        // Apply edits.
-        sourcePrinter.applyEdits(edits);
-
-        // Render the changed lines.
-        sourcePrinter.processChangedLines((lineNumber, lineText) {
-          String prefix = '  line ${lineNumber.toString().padRight(3)} •';
-          logger.stdout('$prefix ${lineText.trim()}');
-        });
-      }
-    }
-  }
-}
-
-class _Handler
-    with NotificationHandler, ConnectionHandler, AnalysisCompleteHandler {
-  final Driver driver;
-  final Logger logger;
-  final Context context;
-
-  @override
-  final Server server;
-  Version serverProtocolVersion;
-
-  _Handler(this.driver, this.context)
-      : logger = driver.logger,
-        server = driver.server;
-
-  @override
-  bool checkServerProtocolVersion(Version version) {
-    serverProtocolVersion = version;
-    return super.checkServerProtocolVersion(version);
-  }
-
-  @override
-  void onAnalysisErrors(AnalysisErrorsParams params) {
-    List<AnalysisError> errors = params.errors;
-    bool foundAtLeastOneError = false;
-    for (AnalysisError error in errors) {
-      if (shouldShowError(error)) {
-        if (!foundAtLeastOneError) {
-          foundAtLeastOneError = true;
-          logger.stdout('${driver._relativePath(params.file)}:');
-        }
-        Location loc = error.location;
-        logger.stdout('  ${toSentenceFragment(error.message)}'
-            ' • ${loc.startLine}:${loc.startColumn}');
-      }
-    }
-    super.onAnalysisErrors(params);
-    // Analysis errors are non-fatal; no need to exit.
-  }
-
-  @override
-  void onFailedToConnect() {
-    logger.stderr('Failed to connect to server');
-    super.onFailedToConnect();
-    // Exiting on connection failure is already handled by [Driver.start].
-  }
-
-  @override
-  void onProtocolNotSupported(Version version) {
-    logger.stderr('Expected protocol version $PROTOCOL_VERSION,'
-        ' but found $version');
-    final expectedVersion = Version.parse(PROTOCOL_VERSION);
-    if (version > expectedVersion) {
-      logger.stdout('''
-This version of dartfix is incompatible with the current Dart SDK.
-Try installing a newer version of dartfix by running:
-
-    pub global activate dartfix
-''');
-    } else {
-      logger.stdout('''
-This version of dartfix is too new to be used with the current Dart SDK. Try
-upgrading the Dart SDK to a newer version or installing an older version of
-dartfix using:
-
-    pub global activate dartfix <version>
-''');
-    }
-    super.onProtocolNotSupported(version);
-    // This is handled by the connection failure case; no need to exit here.
-  }
-
-  @override
-  void onServerError(ServerErrorParams params) {
-    if (params.isFatal) {
-      logger.stderr('Fatal Server Error: ${params.message}');
-    } else {
-      logger.stderr('Server Error: ${params.message}');
-    }
-    if (params.stackTrace != null) {
-      logger.stderr(params.stackTrace);
-    }
-    super.onServerError(params);
-    // Server is stopped by super method, so we can safely exit here.
-    context.exit(16);
-  }
-}
-
-class _Listener with ServerListener, BadMessageListener {
-  final Logger logger;
-  final bool verbose;
-
-  _Listener(this.logger) : verbose = logger.isVerbose;
-
-  @override
-  void log(String prefix, String details) {
-    if (verbose) {
-      logger.trace('$prefix $details');
-    }
-  }
-}
diff --git a/pkg/dartfix/lib/src/migrate/apply.dart b/pkg/dartfix/lib/src/migrate/apply.dart
deleted file mode 100644
index b4c7ab2..0000000
--- a/pkg/dartfix/lib/src/migrate/apply.dart
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2020, 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_client/protocol.dart';
-
-import '../util.dart';
-
-// TODO(devoncarew): This is only referenced from tests.
-
-/// Perform the indicated source edits to the given source, returning the
-/// resulting transformed text.
-String applyEdits(SourceFileEdit sourceFileEdit, String source) {
-  List<SourceEdit> edits = sortEdits(sourceFileEdit);
-  return SourceEdit.applySequence(source, edits);
-}
diff --git a/pkg/dartfix/lib/src/migrate/display.dart b/pkg/dartfix/lib/src/migrate/display.dart
deleted file mode 100644
index 68561fb..0000000
--- a/pkg/dartfix/lib/src/migrate/display.dart
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (c) 2020, 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_client/protocol.dart';
-import 'package:cli_util/cli_logging.dart';
-import 'package:path/path.dart' as path;
-
-// TODO(devoncarew): This is only referenced from tests.
-
-/// Given a Logger and an analysis issue, render the issue to the logger.
-class IssueRenderer {
-  final Logger logger;
-  final String rootDirectory;
-
-  IssueRenderer(this.logger, this.rootDirectory);
-
-  void render(AnalysisError issue) {
-    // severity • Message ... at foo/bar.dart:6:1 • (error_code)
-
-    final Ansi ansi = logger.ansi;
-
-    logger.stdout(
-      '  ${ansi.error(issue.severity.name.toLowerCase())} • '
-      '${ansi.emphasized(_removePeriod(issue.message))} '
-      'at ${path.relative(issue.location.file, from: rootDirectory)}'
-      ':${issue.location.startLine}:'
-      '${issue.location.startColumn} '
-      '• (${issue.code})',
-    );
-  }
-}
-
-typedef LineProcessor = void Function(int lineNumber, String lineText);
-
-class SourcePrinter {
-  static final String red = '\u001b[31m';
-  static final String bold = '\u001b[1m';
-  static final String reversed = '\u001b[7m';
-  static final String none = '\u001b[0m';
-
-  String source;
-
-  SourcePrinter(this.source);
-
-  void applyEdits(List<SourceEdit> edits) {
-    for (SourceEdit edit in edits) {
-      if (edit.replacement.isNotEmpty) {
-        // an addition
-        insertText(edit.offset + edit.length, edit.replacement);
-      }
-
-      if (edit.length != 0) {
-        // a removal
-        deleteRange(edit.offset, edit.length);
-      }
-    }
-  }
-
-  void deleteRange(int offset, int length) {
-    source = source.substring(0, offset) +
-        red +
-        reversed +
-        source.substring(offset, offset + length) +
-        none +
-        source.substring(offset + length);
-  }
-
-  void insertText(int offset, String text) {
-    text = '$reversed$text$none';
-    source = source.substring(0, offset) + text + source.substring(offset);
-  }
-
-  void processChangedLines(LineProcessor callback) {
-    List<String> lines = source.split('\n');
-    for (int i = 0; i < lines.length; i++) {
-      String line = lines[i];
-
-      if (line.contains(none)) {
-        callback(i + 1, line);
-      }
-    }
-  }
-}
-
-String _removePeriod(String value) {
-  return value.endsWith('.') ? value.substring(0, value.length - 1) : value;
-}
diff --git a/pkg/dartfix/lib/src/migrate/options.dart b/pkg/dartfix/lib/src/migrate/options.dart
deleted file mode 100644
index e3c565e..0000000
--- a/pkg/dartfix/lib/src/migrate/options.dart
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (c) 2020, 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 'dart:io';
-
-import 'package:args/args.dart';
-import 'package:args/src/arg_parser.dart';
-import 'package:path/path.dart' as path;
-
-// TODO(devoncarew): This class is unused.
-
-class MigrateOptions {
-  static const applyChangesOption = 'apply-changes';
-  static const debugOption = 'debug';
-  static const ignoreErrorsOption = 'ignore-errors';
-  static const previewPortOption = 'preview-port';
-  static const sdkPathOption = 'sdk-path';
-  static const serverPathOption = 'server-path';
-  static const webPreviewOption = 'web-preview';
-
-  final bool applyChanges;
-  final bool debug;
-  final String directory;
-  final bool ignoreErrors;
-  final int previewPort;
-  final String serverPath;
-  final String sdkPath;
-  final bool webPreview;
-
-  MigrateOptions(ArgResults argResults, this.directory)
-      : applyChanges = argResults[applyChangesOption] as bool,
-        debug = argResults[debugOption] as bool,
-        ignoreErrors = argResults[ignoreErrorsOption] as bool,
-        previewPort =
-            int.tryParse(argResults[previewPortOption] as String) ?? 0,
-        sdkPath = argResults[sdkPathOption] as String,
-        serverPath = argResults[serverPathOption] as String,
-        webPreview = argResults['web-preview'] as bool;
-
-  String get directoryAbsolute => Directory(path.canonicalize(directory)).path;
-
-  @override
-  String toString() {
-    return '[$directory]';
-  }
-
-  static void defineOptions(ArgParser argParser) {
-    argParser.addFlag(
-      applyChangesOption,
-      defaultsTo: false,
-      negatable: false,
-      help: 'Apply the proposed null safety changes to the files on disk.',
-    );
-    argParser.addFlag(
-      debugOption,
-      defaultsTo: false,
-      hide: true,
-      negatable: true,
-      help: 'Show (very verbose) debugging information to stdout during '
-          'migration',
-    );
-    argParser.addFlag(
-      ignoreErrorsOption,
-      defaultsTo: false,
-      negatable: false,
-      help: 'Attempt to perform null safety analysis even if the package has '
-          'analysis errors.',
-    );
-    argParser.addOption(
-      sdkPathOption,
-      hide: true,
-      help: 'Override the SDK path used for migration.',
-    );
-    argParser.addOption(
-      previewPortOption,
-      defaultsTo: '0',
-      help: 'Run the preview server on the specified port.  If not specified '
-          'or invalid, dynamically allocate a port.',
-    );
-    argParser.addOption(
-      serverPathOption,
-      hide: true,
-      help: 'Override the analysis server path used for migration.',
-    );
-    argParser.addFlag(
-      webPreviewOption,
-      defaultsTo: true,
-      negatable: true,
-      help: 'Show an interactive preview of the proposed null safety changes '
-          'in a browser window.\n'
-          'With --no-web-preview, the proposed changes are instead printed to '
-          'the console.',
-    );
-  }
-}
diff --git a/pkg/dartfix/lib/src/options.dart b/pkg/dartfix/lib/src/options.dart
deleted file mode 100644
index b6d2cbb..0000000
--- a/pkg/dartfix/lib/src/options.dart
+++ /dev/null
@@ -1,210 +0,0 @@
-// Copyright (c) 2018, 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 'dart:io';
-
-import 'package:args/args.dart';
-import 'package:cli_util/cli_logging.dart';
-import 'package:dartfix/src/context.dart';
-import 'package:path/path.dart' as path;
-
-// TODO(brianwilkerson) Deprecate 'excludeFix' and replace it with 'exclude-fix'
-const excludeFixOption = 'excludeFix';
-const forceOption = 'force';
-const includeFixOption = 'fix';
-const overwriteOption = 'overwrite';
-const pedanticOption = 'pedantic';
-const previewDirOption = 'preview-dir';
-const previewPortOption = 'preview-port';
-const sdkOption = 'sdk';
-
-const _binaryName = 'dartfix';
-const _colorOption = 'color';
-
-// options only supported by server 1.22.2 and greater
-const _helpOption = 'help';
-const _serverSnapshot = 'server';
-
-// options not supported yet by any server
-const _verboseOption = 'verbose';
-
-/// Command line options for `dartfix`.
-class Options {
-  final Context context;
-  Logger logger;
-
-  List<String> targets;
-  final String sdkPath;
-  final String serverSnapshot;
-
-  final bool pedanticFixes;
-  final List<String> includeFixes;
-  final List<String> excludeFixes;
-
-  final bool force;
-  final bool showHelp;
-  bool overwrite;
-  final bool useColor;
-  final bool verbose;
-
-  Options._fromArgs(this.context, ArgResults results)
-      : force = results[forceOption] as bool,
-        includeFixes = (results[includeFixOption] as List ?? []).cast<String>(),
-        excludeFixes = (results[excludeFixOption] as List ?? []).cast<String>(),
-        overwrite = results[overwriteOption] as bool,
-        pedanticFixes = results[pedanticOption] as bool,
-        sdkPath = results[sdkOption] as String ?? _getSdkPath(),
-        serverSnapshot = results[_serverSnapshot] as String,
-        showHelp = results[_helpOption] as bool || results.arguments.isEmpty,
-        targets = results.rest,
-        useColor = results.wasParsed(_colorOption)
-            ? results[_colorOption] as bool
-            : null,
-        verbose = results[_verboseOption] as bool;
-
-  String makeAbsoluteAndNormalize(String target) {
-    if (!path.isAbsolute(target)) {
-      target = path.join(context.workingDir, target);
-    }
-    return path.normalize(target);
-  }
-
-  static Options parse(List<String> args, Context context, Logger logger) {
-    final parser = ArgParser(allowTrailingOptions: true)
-      ..addSeparator('Choosing fixes to be applied:')
-      ..addMultiOption(includeFixOption,
-          help: 'Include a specific fix.', valueHelp: 'name-of-fix')
-      ..addMultiOption(excludeFixOption,
-          help: 'Exclude a specific fix.', valueHelp: 'name-of-fix')
-      ..addFlag(pedanticOption,
-          help: 'Apply pedantic fixes.', defaultsTo: false, negatable: false)
-      ..addSeparator('Modifying files:')
-      ..addFlag(overwriteOption,
-          abbr: 'w',
-          help: 'Overwrite files with the changes.',
-          defaultsTo: false,
-          negatable: false)
-      ..addFlag(forceOption,
-          abbr: 'f',
-          help: 'Overwrite files even if there are errors.',
-          defaultsTo: false,
-          negatable: false)
-      ..addSeparator('Miscellaneous:')
-      ..addFlag(_helpOption,
-          abbr: 'h',
-          help: 'Display this help message.',
-          defaultsTo: false,
-          negatable: false)
-      ..addOption(sdkOption,
-          help: 'Path to the SDK to analyze against.',
-          valueHelp: 'path',
-          hide: true)
-      ..addOption(_serverSnapshot,
-          help: 'Path to the analysis server snapshot file.',
-          valueHelp: 'path',
-          hide: true)
-      ..addFlag(_verboseOption,
-          abbr: 'v',
-          help: 'Verbose output.',
-          defaultsTo: false,
-          negatable: false)
-      ..addFlag(_colorOption,
-          help: 'Use ansi colors when printing messages.',
-          defaultsTo: Ansi.terminalSupportsAnsi);
-
-    context ??= Context();
-
-    ArgResults results;
-    try {
-      results = parser.parse(args);
-    } on FormatException catch (e) {
-      logger ??= Logger.standard(ansi: Ansi(Ansi.terminalSupportsAnsi));
-      logger.stderr(e.message);
-      _showUsage(parser, logger);
-      context.exit(17);
-    }
-
-    Options options = Options._fromArgs(context, results);
-
-    if (logger == null) {
-      if (options.verbose) {
-        logger = Logger.verbose();
-      } else {
-        logger = Logger.standard(
-            ansi: Ansi(
-          options.useColor ?? Ansi.terminalSupportsAnsi,
-        ));
-      }
-    }
-    options.logger = logger;
-
-    // For '--help', we short circuit the logic to validate the sdk and project.
-    if (options.showHelp) {
-      _showUsage(parser, logger, showHelpHint: false);
-      return options;
-    }
-
-    // Validate the Dart SDK location
-    String sdkPath = options.sdkPath;
-    if (sdkPath == null) {
-      logger.stderr('No Dart SDK found.');
-      context.exit(18);
-    }
-
-    if (!context.exists(sdkPath)) {
-      logger.stderr('Invalid Dart SDK path: $sdkPath');
-      context.exit(19);
-    }
-
-    // Check for files and/or directories to analyze.
-    if (options.targets == null || options.targets.isEmpty) {
-      logger.stderr('Expected at least one file or directory to analyze.');
-      context.exit(20);
-    }
-
-    // Normalize and verify paths
-    options.targets =
-        options.targets.map<String>(options.makeAbsoluteAndNormalize).toList();
-    for (String target in options.targets) {
-      if (!context.isDirectory(target)) {
-        if (!context.exists(target)) {
-          logger.stderr('Target does not exist: $target');
-        } else {
-          logger.stderr('Expected directory, but found: $target');
-        }
-        context.exit(21);
-      }
-    }
-
-    if (options.verbose) {
-      logger.trace('Targets:');
-      for (String target in options.targets) {
-        logger.trace('  $target');
-      }
-    }
-
-    return options;
-  }
-
-  static String _getSdkPath() {
-    return Platform.environment['DART_SDK'] ??
-        path.dirname(path.dirname(Platform.resolvedExecutable));
-  }
-
-  static void _showUsage(ArgParser parser, Logger logger,
-      {bool showHelpHint = true}) {
-    Function(String message) out = showHelpHint ? logger.stderr : logger.stdout;
-    // show help on stdout when showHelp is true and showHelpHint is false
-    out('''
-Usage: $_binaryName [options...] <directory paths>
-''');
-    out(parser.usage);
-    out(showHelpHint
-        ? '''
-
-Use --$_helpOption to display the fixes that can be specified using either
---$includeFixOption or --$excludeFixOption.'''
-        : '');
-  }
-}
diff --git a/pkg/dartfix/lib/src/util.dart b/pkg/dartfix/lib/src/util.dart
deleted file mode 100644
index 07ad858..0000000
--- a/pkg/dartfix/lib/src/util.dart
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (c) 2018, 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 'dart:io' show File, Platform;
-
-import 'package:analysis_server_client/protocol.dart';
-import 'package:path/path.dart' as path;
-
-int compareSuggestions(DartFixSuggestion s1, DartFixSuggestion s2) {
-  int result = s1.description.compareTo(s2.description);
-  if (result != 0) {
-    return result;
-  }
-  return (s2.location?.offset ?? 0) - (s1.location?.offset ?? 0);
-}
-
-int compareFixes(DartFix s1, DartFix s2) {
-  return s1.name.compareTo(s2.name);
-}
-
-/// Return the analysis_server executable by proceeding upward until finding the
-/// Dart SDK repository root, then returning the analysis_server executable
-/// within the repository.
-///
-/// Return `null` if it cannot be found.
-String findServerPath() {
-  String pathname = Platform.script.toFilePath();
-  while (true) {
-    String parent = path.dirname(pathname);
-    if (parent.length >= pathname.length) {
-      return null;
-    }
-    String serverPath =
-        path.join(parent, 'pkg', 'analysis_server', 'bin', 'server.dart');
-    if (File(serverPath).existsSync()) {
-      return serverPath;
-    }
-    pathname = parent;
-  }
-}
-
-bool shouldShowError(AnalysisError error) {
-  // Only show diagnostics that will affect the fixes.
-  return error.type.name != 'HINT' &&
-      error.type.name != 'LINT' &&
-      error.type.name != 'TODO' &&
-      // TODO(danrubel): Rather than checking the error.code with
-      // specific strings, add something to the error indicating that
-      // it will be automatically fixed by edit.dartfix.
-      error.code != 'wrong_number_of_type_arguments_constructor';
-}
-
-String toSentenceFragment(String message) {
-  return message.endsWith('.')
-      ? message.substring(0, message.length - 1)
-      : message;
-}
-
-String pluralize(String word, int count) => count == 1 ? word : '${word}s';
-
-List<SourceEdit> sortEdits(SourceFileEdit sourceFileEdit) {
-  // Sort edits in reverse offset order.
-  List<SourceEdit> edits = sourceFileEdit.edits.toList();
-  edits.sort((a, b) {
-    return b.offset - a.offset;
-  });
-  return edits;
-}
diff --git a/pkg/dartfix/pubspec.yaml b/pkg/dartfix/pubspec.yaml
deleted file mode 100644
index 10dcae8..0000000
--- a/pkg/dartfix/pubspec.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-name: dartfix
-version: 0.1.8
-description:
-  A tool for migrating Dart source to newer versions of the Dart SDK
-  and fixing common issues.
-homepage: https://github.com/dart-lang/sdk/tree/master/pkg/dartfix
-environment:
-  sdk: '>=2.8.0 <3.0.0'
-
-# Add the bin/dartfix.dart script to the scripts pub installs.
-executables:
-  dartfix:
-
-dependencies:
-  # Pin to an exact version of analysis_server_client because the edit.dartfix
-  # protocol is experimental and will continue to evolve.
-  analysis_server_client: '>=1.1.3 <1.1.4'
-  args: ^1.4.0
-  cli_util: ^0.2.0
-  path: ^1.7.0
-  pub_semver: ^1.4.4
-
-dev_dependencies:
-  analyzer: ^0.40.0
-  pedantic: ^1.8.0
-  test: ^1.14.2
diff --git a/pkg/dartfix/test/src/driver_example_test.dart b/pkg/dartfix/test/src/driver_example_test.dart
deleted file mode 100644
index 4cbb06b..0000000
--- a/pkg/dartfix/test/src/driver_example_test.dart
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2019, 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 'dart:io';
-
-import 'package:test/test.dart';
-
-import 'test_context.dart';
-
-void main() {
-  test('run original example', () async {
-    File exampleFile = findFile('pkg/dartfix/example/example.dart');
-
-    print('--- launching original example');
-    final futureResult1 =
-        Process.run(Platform.resolvedExecutable, [exampleFile.path]);
-
-    print('--- waiting for original example');
-    final result = await futureResult1;
-
-    print('--- original example output');
-    var text = result.stdout as String;
-    print(text);
-
-    expect(text.trim(), 'myDouble = 4.0');
-  });
-}
diff --git a/pkg/dartfix/test/src/driver_exclude_test.dart b/pkg/dartfix/test/src/driver_exclude_test.dart
deleted file mode 100644
index 3236efa..0000000
--- a/pkg/dartfix/test/src/driver_exclude_test.dart
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (c) 2019, 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 'dart:io';
-
-import 'package:dartfix/src/driver.dart';
-import 'package:test/test.dart';
-
-import 'test_context.dart';
-
-void main() {
-  File exampleFile;
-  Directory exampleDir;
-
-  test('exclude fix', () async {
-    exampleFile = findFile('pkg/dartfix/example/example.dart');
-    exampleDir = exampleFile.parent;
-
-    final driver = Driver();
-    final testContext = TestContext();
-    final testLogger = TestLogger(debug: _debug);
-    String exampleSource = await exampleFile.readAsString();
-
-    try {
-      await driver.start([
-        if (_debug) '-v',
-        '--fix',
-        'convert_class_to_mixin',
-        '--excludeFix',
-        'convert_class_to_mixin',
-        exampleDir.path,
-      ], testContext: testContext, testLogger: testLogger);
-    } finally {
-      if (_debug) {
-        print(testLogger.stderrBuffer.toString());
-        print(testLogger.stdoutBuffer.toString());
-        print('--- original example');
-        print(exampleSource);
-      }
-    }
-
-    final suggestions = driver.result.suggestions;
-    expect(suggestions, hasLength(0));
-  }, timeout: const Timeout(Duration(minutes: 3)));
-}
-
-const _debug = true;
diff --git a/pkg/dartfix/test/src/driver_help_test.dart b/pkg/dartfix/test/src/driver_help_test.dart
deleted file mode 100644
index d8a6330..0000000
--- a/pkg/dartfix/test/src/driver_help_test.dart
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2019, 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:dartfix/src/driver.dart';
-import 'package:dartfix/src/options.dart';
-import 'package:test/test.dart';
-
-import 'test_context.dart';
-
-void main() {
-  test('help explicit', () async {
-    final driver = Driver();
-    final testContext = TestContext();
-    final testLogger = TestLogger();
-    try {
-      await driver.start(
-        ['--help'], // display help and list fixes
-        testContext: testContext,
-        testLogger: testLogger,
-      );
-      fail('expected exception');
-    } on TestExit catch (e) {
-      expect(e.code, 0);
-    }
-    final errText = testLogger.stderrBuffer.toString();
-    final outText = testLogger.stdoutBuffer.toString();
-    expect(errText, isEmpty);
-    expect(outText, contains('--$excludeFixOption'));
-    expect(outText, isNot(contains('Use --help to display the fixes')));
-  });
-
-  test('help implicit', () async {
-    final driver = Driver();
-    final testContext = TestContext();
-    final testLogger = TestLogger();
-    try {
-      await driver.start(
-        [], // no options or arguments should display help and list fixes
-        testContext: testContext,
-        testLogger: testLogger,
-      );
-      fail('expected exception');
-    } on TestExit catch (e) {
-      expect(e.code, 0);
-    }
-    final errText = testLogger.stderrBuffer.toString();
-    final outText = testLogger.stdoutBuffer.toString();
-    print(errText);
-    print(outText);
-    expect(errText, isEmpty);
-    expect(outText, contains('--$excludeFixOption'));
-    expect(outText, isNot(contains('Use --help to display the fixes')));
-  });
-}
diff --git a/pkg/dartfix/test/src/driver_include_test.dart b/pkg/dartfix/test/src/driver_include_test.dart
deleted file mode 100644
index 7079d5d..0000000
--- a/pkg/dartfix/test/src/driver_include_test.dart
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) 2019, 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 'dart:io';
-
-import 'package:dartfix/src/driver.dart';
-import 'package:test/test.dart';
-
-import 'test_context.dart';
-
-void main() {
-  File exampleFile;
-  Directory exampleDir;
-
-  test('include fix', () async {
-    exampleFile = findFile('pkg/dartfix/example/example.dart');
-    exampleDir = exampleFile.parent;
-
-    final driver = Driver();
-    final testContext = TestContext();
-    final testLogger = TestLogger(debug: _debug);
-    String exampleSource = await exampleFile.readAsString();
-
-    try {
-      await driver.start([
-        if (_debug) '-v',
-        '--fix',
-        'prefer_int_literals',
-        exampleDir.path,
-      ], testContext: testContext, testLogger: testLogger);
-    } finally {
-      if (_debug) {
-        print(testLogger.stderrBuffer.toString());
-        print(testLogger.stdoutBuffer.toString());
-        print('--- original example');
-        print(exampleSource);
-      }
-    }
-
-    final suggestions = driver.result.suggestions;
-    expect(suggestions, hasLength(1));
-    expectHasSuggestion(suggestions, 'Convert to an int literal');
-  }, timeout: const Timeout(Duration(minutes: 3)));
-}
-
-const _debug = true;
diff --git a/pkg/dartfix/test/src/driver_pedantic_test.dart b/pkg/dartfix/test/src/driver_pedantic_test.dart
deleted file mode 100644
index 6d4b99c6..0000000
--- a/pkg/dartfix/test/src/driver_pedantic_test.dart
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2019, 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 'driver_test.dart' show defineDriverTests;
-
-void main() {
-  defineDriverTests(
-    name: 'pedantic',
-    options: ['--pedantic'],
-    expectedSuggestions: ["Replace with 'isEmpty'"],
-  );
-}
diff --git a/pkg/dartfix/test/src/driver_prefer_is_empty_test.dart b/pkg/dartfix/test/src/driver_prefer_is_empty_test.dart
deleted file mode 100644
index db8b4ad..0000000
--- a/pkg/dartfix/test/src/driver_prefer_is_empty_test.dart
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2019, 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 'driver_test.dart' show defineDriverTests;
-
-void main() {
-  defineDriverTests(
-    name: 'prefer_is_empty',
-    options: ['--fix', 'prefer_is_empty'],
-    expectedSuggestions: ["Replace with 'isEmpty'"],
-  );
-}
diff --git a/pkg/dartfix/test/src/driver_test.dart b/pkg/dartfix/test/src/driver_test.dart
deleted file mode 100644
index 667b723..0000000
--- a/pkg/dartfix/test/src/driver_test.dart
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright (c) 2018, 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 'dart:io';
-
-import 'package:analysis_server_client/protocol.dart';
-import 'package:dartfix/src/driver.dart';
-import 'package:test/test.dart';
-
-import 'test_context.dart';
-
-void main() {
-  defineDriverTests(
-    name: 'default',
-    options: [
-      '--fix',
-      'prefer_int_literals',
-      '--fix',
-      'convert_class_to_mixin'
-    ],
-    expectedSuggestions: [
-      'Convert MyMixin to a mixin',
-      'Convert to an int literal',
-    ],
-  );
-}
-
-void defineDriverTests({
-  String name,
-  List<String> options,
-  List<String> expectedSuggestions,
-  bool debug = false,
-  bool updateExample = false,
-}) {
-  var fixFileName = 'example_$name.dart';
-
-  File exampleFile;
-  File exampleFixedFile;
-  Directory exampleDir;
-
-  setUp(() {
-    exampleFile = findFile('pkg/dartfix/example/example.dart');
-    exampleFixedFile = findFile('pkg/dartfix/fixed/$fixFileName');
-    exampleDir = exampleFile.parent;
-  });
-
-  test('fix example - $name', () async {
-    final driver = Driver();
-    final testContext = TestContext();
-    final testLogger = TestLogger(debug: debug);
-    String exampleSource = await exampleFile.readAsString();
-
-    await driver.start([if (debug) '-v', ...options, exampleDir.path],
-        testContext: testContext, testLogger: testLogger);
-    if (debug) {
-      print(testLogger.stderrBuffer.toString());
-      print(testLogger.stdoutBuffer.toString());
-      print('--- original example');
-      print(exampleSource);
-    }
-
-    expect(driver.result.edits, hasLength(1));
-    for (SourceEdit edit in driver.result.edits[0].edits) {
-      exampleSource = edit.apply(exampleSource);
-    }
-    if (debug) {
-      print('--- fixed example');
-      print(exampleSource);
-    }
-
-    final suggestions = driver.result.suggestions;
-    for (var expectedSuggestion in expectedSuggestions) {
-      expectHasSuggestion(suggestions, expectedSuggestion);
-    }
-    expect(suggestions, hasLength(expectedSuggestions.length));
-
-    exampleSource = replaceLeadingComment(exampleSource);
-    if (updateExample) {
-      await exampleFixedFile.writeAsString(exampleSource);
-    } else {
-      final expectedSource = await exampleFixedFile.readAsString();
-      expect(exampleSource, expectedSource);
-    }
-  }, timeout: const Timeout(Duration(minutes: 3)));
-
-  test('run example - $name', () async {
-    if (debug) print('--- launching original example');
-    final futureResult1 =
-        Process.run(Platform.resolvedExecutable, [exampleFile.path]);
-
-    if (debug) print('--- launching fixed example');
-    final futureResult2 =
-        Process.run(Platform.resolvedExecutable, [exampleFixedFile.path]);
-
-    if (debug) print('--- waiting for original example');
-    final result1 = await futureResult1;
-
-    if (debug) print('--- waiting for fixed example');
-    final result2 = await futureResult2;
-
-    final stdout1 = result1.stdout;
-    final stdout2 = result2.stdout;
-    if (debug) {
-      print('--- original example output');
-      print(stdout1);
-      print('--- fixed example output');
-      print(stdout2);
-    }
-    expect(stdout1, stdout2);
-  });
-}
-
-String replaceLeadingComment(String source) => source.replaceAll(
-    '''
-// This file contains code that is modified by running dartfix.
-// After running dartfix, this content matches a file in the "fixed" directory.
-'''
-        .trim(),
-    '''
-// This file contains code that has been modified by running dartfix.
-// See example.dart for the original unmodified code.
-  '''
-        .trim());
diff --git a/pkg/dartfix/test/src/migrate_command_test.dart b/pkg/dartfix/test/src/migrate_command_test.dart
deleted file mode 100644
index a2fcf89..0000000
--- a/pkg/dartfix/test/src/migrate_command_test.dart
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright (c) 2020, 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 'dart:io';
-
-import 'package:analysis_server_client/protocol.dart';
-import 'package:cli_util/cli_logging.dart';
-import 'package:dartfix/src/migrate/apply.dart';
-import 'package:dartfix/src/migrate/display.dart';
-import 'package:test/test.dart';
-
-void main() {
-  defineMigrateTests();
-}
-
-void defineMigrateTests() {
-  group('issue render', defineIssueRenderTests);
-  group('SourcePrinter', defineSourcePrinterTests);
-  group('applyEdits', defineApplyEditsTests);
-}
-
-void defineIssueRenderTests() {
-  IssueRenderer renderer;
-  TestLogger logger;
-
-  setUp(() {
-    logger = TestLogger();
-    renderer = IssueRenderer(logger, '.');
-  });
-
-  test('issue1', () {
-    AnalysisError issue = AnalysisError(
-      AnalysisErrorSeverity.ERROR,
-      AnalysisErrorType.COMPILE_TIME_ERROR,
-      Location('foo/bar/baz.dart', 1, 2, 3, 4),
-      'My message.',
-      'my_error_code',
-    );
-
-    renderer.render(issue);
-
-    expect(
-      logger.stdoutText.trim(),
-      contains(platformPath(
-          'error • My message at foo/bar/baz.dart:3:4 • (my_error_code)')),
-    );
-    expect(logger.stderrText, isEmpty);
-  });
-
-  test('issue2', () {
-    AnalysisError issue = AnalysisError(
-      AnalysisErrorSeverity.INFO,
-      AnalysisErrorType.TODO,
-      Location('foo/bar/qux.dart', 1, 2, 3, 4),
-      'todo: My message.',
-      'todo',
-    );
-
-    renderer.render(issue);
-
-    expect(
-        logger.stdoutText,
-        contains(platformPath(
-            'info • todo: My message at foo/bar/qux.dart:3:4 • (todo)')));
-
-    expect(logger.stderrText, isEmpty);
-  });
-}
-
-void defineSourcePrinterTests() {
-  SourcePrinter printer;
-
-  setUp(() {
-    printer = SourcePrinter('''
-void main() {
-  Cat one = Cat('Tabby');
-  print(one);
-}
-
-class Cat {
-  final String name;
-  String color;
-
-  Cat(this.name);
-
-  String toString() {
-    return name?.toString() + ' is ' + color.toString();
-  }
-}
-''');
-  });
-
-  test('add and remove', () {
-    printer.insertText(192, '?');
-    printer.deleteRange(164, 1);
-    printer.insertText(98, '?');
-
-    StringBuffer buf = StringBuffer();
-    printer.processChangedLines((lineNumber, lineText) {
-      buf.writeln('$lineNumber ${lineText.trim()}');
-    });
-
-    expect(buf.toString().trim(), '''
-8 String\x1B[7m?\x1B[0m color;
-13 return name?\x1B[31m\x1B[7m.\x1B[0mtoString() + \' is \' + color\x1B[7m?\x1B[0m.toString();''');
-  });
-}
-
-void defineApplyEditsTests() {
-  test('insert', () {
-    String source = 'one two\nthree four';
-    SourceFileEdit edit = SourceFileEdit('foo.dart', 0, edits: [
-      SourceEdit(0, 0, 'five '),
-    ]);
-
-    String result = applyEdits(edit, source);
-    expect(result, 'five one two\nthree four');
-  });
-
-  test('delete', () {
-    String source = 'one two\nthree four';
-    SourceFileEdit edit = SourceFileEdit('foo.dart', 0, edits: [
-      SourceEdit(0, 4, ''),
-      SourceEdit(8, 6, ''),
-    ]);
-
-    String result = applyEdits(edit, source);
-    expect(result, 'two\nfour');
-  });
-
-  test('insert and delete', () {
-    String source = 'one two\nthree four';
-    SourceFileEdit edit = SourceFileEdit('foo.dart', 0, edits: [
-      SourceEdit(13, 5, ''),
-      SourceEdit(8, 0, 'six '),
-      SourceEdit(7, 1, ' '),
-    ]);
-
-    String result = applyEdits(edit, source);
-    expect(result, 'one two six three');
-  });
-}
-
-class TestLogger implements Logger {
-  final bool debug;
-
-  @override
-  final Ansi ansi;
-  final stdoutBuffer = StringBuffer();
-  final stderrBuffer = StringBuffer();
-
-  TestLogger({this.debug = false}) : ansi = Ansi(false);
-
-  @override
-  void flush() {}
-
-  @override
-  bool get isVerbose => debug;
-
-  @override
-  Progress progress(String message) {
-    return SimpleProgress(this, message);
-  }
-
-  @override
-  void stdout(String message) {
-    stdoutBuffer.writeln(message);
-  }
-
-  @override
-  void write(String message) {
-    stdoutBuffer.write(message);
-  }
-
-  @override
-  void writeCharCode(int charCode) {
-    stdoutBuffer.writeCharCode(charCode);
-  }
-
-  @override
-  void stderr(String message) {
-    stderrBuffer.writeln(message);
-  }
-
-  @override
-  void trace(String message) {
-    if (debug) {
-      stdoutBuffer.writeln(message);
-    }
-  }
-
-  String get stdoutText => stdoutBuffer.toString();
-
-  String get stderrText => stderrBuffer.toString();
-}
-
-String platformPath(String path) {
-  return path.replaceAll('/', Platform.pathSeparator);
-}
diff --git a/pkg/dartfix/test/src/options_test.dart b/pkg/dartfix/test/src/options_test.dart
deleted file mode 100644
index 9843fab..0000000
--- a/pkg/dartfix/test/src/options_test.dart
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright (c) 2018, 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:dartfix/src/options.dart';
-import 'package:path/path.dart' as path;
-import 'package:test/test.dart';
-
-import 'test_context.dart';
-
-void main() {
-  TestContext context;
-  TestLogger logger;
-
-  setUp(() {
-    context = TestContext();
-    logger = TestLogger();
-  });
-
-  String p(String filePath) => context.convertPath(filePath);
-
-  Options parse(
-    List<String> args, {
-    String errorOut,
-    int exitCode,
-    bool force = false,
-    List<String> includeFixes = const <String>[],
-    List<String> excludeFixes = const <String>[],
-    bool showHelp = false,
-    String normalOut,
-    bool dependencies = false,
-    bool pedanticFixes = false,
-    bool requiredFixes = false,
-    bool overwrite = false,
-    String serverSnapshot,
-    List<String> targetSuffixes,
-    bool verbose = false,
-  }) {
-    Options options;
-    int actualExitCode;
-    try {
-      options = Options.parse(args, context, logger);
-    } on TestExit catch (e) {
-      actualExitCode = e.code;
-    }
-    expect(logger.stderrBuffer.toString(),
-        errorOut != null ? contains(errorOut) : isEmpty);
-    expect(logger.stdoutBuffer.toString(),
-        normalOut != null ? contains(normalOut) : isEmpty);
-    if (exitCode != null) {
-      expect(actualExitCode, exitCode, reason: 'exit code');
-      return null;
-    } else {
-      expect(actualExitCode, isNull, reason: 'exit code');
-    }
-    expect(options.force, force);
-    expect(options.pedanticFixes, pedanticFixes);
-    expect(options.overwrite, overwrite);
-    expect(options.serverSnapshot, serverSnapshot);
-    expect(options.showHelp, showHelp);
-    expect(options.includeFixes, includeFixes);
-    expect(options.excludeFixes, excludeFixes);
-    expect(options.verbose, verbose);
-    expect(path.isAbsolute(options.sdkPath), isTrue, reason: options.sdkPath);
-    for (String target in options.targets) {
-      expect(target, isNotNull);
-      expect(path.isAbsolute(target), isTrue, reason: '$target');
-    }
-    if (targetSuffixes != null) {
-      for (String suffix in targetSuffixes) {
-        expectContains(options.targets, suffix);
-      }
-    }
-    return options;
-  }
-
-  test('exclude fix', () {
-    parse(['--excludeFix', 'c', '--excludeFix', 'd', 'foo'],
-        excludeFixes: ['c', 'd'], targetSuffixes: ['foo']);
-  });
-
-  test('force', () {
-    parse(['--force', 'foo'], force: true, targetSuffixes: ['foo']);
-  });
-
-  test('help explicit', () {
-    parse(['--help'], normalOut: 'Display this help message', showHelp: true);
-  });
-
-  test('help implicit', () {
-    parse([], normalOut: 'Display this help message', showHelp: true);
-  });
-
-  test('include fix', () {
-    parse(['--fix', 'a', '--fix', 'b', 'foo'],
-        includeFixes: ['a', 'b'], targetSuffixes: ['foo']);
-  });
-
-  test('invalid option', () {
-    parse(['--foo'],
-        errorOut: 'Could not find an option named "foo"', exitCode: 17);
-  });
-
-  test('invalid option no logger', () {
-    try {
-      Options.parse(['--foo'], context, null);
-      fail('Expected exception');
-    } on TestExit catch (e) {
-      expect(e.code, 17, reason: 'exit code');
-    }
-  });
-
-  test('invalid target', () {
-    parse(['foo.dart'],
-        errorOut: 'Expected directory, but found', exitCode: 21);
-  });
-
-  test('overwrite', () {
-    parse(['--overwrite', 'foo'], overwrite: true, targetSuffixes: ['foo']);
-  });
-
-  test('pedantic fixes', () {
-    parse(['--pedantic', 'foo'], pedanticFixes: true);
-  });
-
-  test('server snapshot', () {
-    parse(['--server', 'some/path', 'foo'], serverSnapshot: 'some/path');
-  });
-
-  test('simple', () {
-    parse(['foo'], targetSuffixes: ['foo']);
-  });
-
-  test('two targets', () {
-    parse([p('one/foo'), p('two/bar')],
-        targetSuffixes: [p('one/foo'), p('two/bar')]);
-  });
-
-  test('verbose', () {
-    parse(['--verbose', 'foo'], verbose: true);
-  });
-}
-
-void expectContains(Iterable<String> collection, String suffix) {
-  for (String elem in collection) {
-    if (elem.endsWith(suffix)) {
-      return;
-    }
-  }
-  fail('Expected one of $collection\n  to end with "$suffix"');
-}
diff --git a/pkg/dartfix/test/src/test_context.dart b/pkg/dartfix/test/src/test_context.dart
deleted file mode 100644
index 9e55c3f..0000000
--- a/pkg/dartfix/test/src/test_context.dart
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (c) 2018, 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 'dart:io';
-
-import 'package:analysis_server_client/protocol.dart';
-import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
-import 'package:cli_util/cli_logging.dart';
-import 'package:dartfix/src/context.dart';
-import 'package:test/test.dart';
-
-class TestContext with ResourceProviderMixin implements Context {
-  @override
-  String get workingDir => convertPath('/usr/some/non/existing/directory');
-
-  @override
-  bool exists(String filePath) => true;
-
-  @override
-  void exit(int code) {
-    throw TestExit(code);
-  }
-
-  @override
-  bool isDirectory(String filePath) => !filePath.endsWith('.dart');
-}
-
-class TestExit {
-  final int code;
-
-  TestExit(this.code);
-
-  @override
-  String toString() => 'TestExit($code)';
-}
-
-class TestLogger implements Logger {
-  final bool debug;
-
-  @override
-  final Ansi ansi;
-  final stdoutBuffer = StringBuffer();
-  final stderrBuffer = StringBuffer();
-
-  TestLogger({this.debug = false}) : ansi = Ansi(false);
-
-  @override
-  void flush() {}
-
-  @override
-  bool get isVerbose => debug;
-
-  @override
-  Progress progress(String message) {
-    return SimpleProgress(this, message);
-  }
-
-  @override
-  void stderr(String message) {
-    stderrBuffer.writeln(message);
-  }
-
-  @override
-  void stdout(String message) {
-    stdoutBuffer.writeln(message);
-  }
-
-  @override
-  void write(String message) {
-    stdoutBuffer.write(message);
-  }
-
-  @override
-  void writeCharCode(int charCode) {
-    stdoutBuffer.writeCharCode(charCode);
-  }
-
-  @override
-  void trace(String message) {
-    if (debug) {
-      stdoutBuffer.writeln(message);
-    }
-  }
-}
-
-void expectHasSuggestion(
-    List<DartFixSuggestion> suggestions, String expectedText) {
-  for (DartFixSuggestion suggestion in suggestions) {
-    if (suggestion.description.contains(expectedText)) {
-      return;
-    }
-  }
-  fail('Failed to find suggestion containing: $expectedText\n'
-      'in ${suggestions.map((s) => s.description).toList()}');
-}
-
-void expectDoesNotHaveSuggestion(
-    List<DartFixSuggestion> suggestions, String expectedText) {
-  for (DartFixSuggestion suggestion in suggestions) {
-    if (suggestion.description.contains(expectedText)) {
-      fail('Did not expect to find suggestion containing: $expectedText');
-    }
-  }
-}
-
-File findFile(String relPath) {
-  Directory dir = Directory.current;
-  while (true) {
-    final file = File.fromUri(dir.uri.resolve(relPath));
-    if (file.existsSync()) {
-      return file;
-    }
-    final parent = dir.parent;
-    if (parent.path == dir.path) {
-      fail('Failed to find $relPath');
-    }
-    dir = parent;
-  }
-}
-
-String findValue(File pubspec, String key) {
-  List<String> lines = pubspec.readAsLinesSync();
-  for (String line in lines) {
-    if (line.trim().startsWith('$key:')) {
-      return line.split(':')[1].trim();
-    }
-  }
-  fail('Failed to find $key in ${pubspec.path}');
-}
diff --git a/pkg/dartfix/test/test_all.dart b/pkg/dartfix/test/test_all.dart
deleted file mode 100644
index 0b1908e..0000000
--- a/pkg/dartfix/test/test_all.dart
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2018, 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/test.dart';
-
-import 'src/driver_example_test.dart' as driver_example;
-import 'src/driver_exclude_test.dart' as driver_exclude;
-import 'src/driver_help_test.dart' as driver_help;
-import 'src/driver_include_test.dart' as driver_include;
-import 'src/driver_pedantic_test.dart' as driver_pedantic;
-import 'src/driver_prefer_is_empty_test.dart' as driver_prefer_is_empty;
-import 'src/driver_test.dart' as driver;
-import 'src/migrate_command_test.dart' as migrate_command_test;
-import 'src/options_test.dart' as options_test;
-
-void main() {
-  group('driver', driver_example.main);
-  group('driver', driver_exclude.main);
-  group('driver', driver_help.main);
-  group('driver', driver_include.main);
-  group('driver', driver_pedantic.main);
-  group('driver', driver_prefer_is_empty.main);
-  group('driver', driver.main);
-  group('migrate', migrate_command_test.main);
-  group('options', options_test.main);
-}
diff --git a/pkg/front_end/lib/src/fasta/kernel/collections.dart b/pkg/front_end/lib/src/fasta/kernel/collections.dart
index 0a26c3a..79b99c0 100644
--- a/pkg/front_end/lib/src/fasta/kernel/collections.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/collections.dart
@@ -53,6 +53,11 @@
   }
 
   @override
+  DartType getStaticTypeInternal(StaticTypeContext context) {
+    return unsupported("getStaticTypeInternal", fileOffset, getFileUri(this));
+  }
+
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.defaultExpression(this);
 
   @override
diff --git a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
index 5fdaeb3..dc9dfa1 100644
--- a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
@@ -24,6 +24,7 @@
 import 'package:kernel/text/ast_to_text.dart' show Precedence, Printer;
 import 'package:kernel/src/printer.dart';
 import 'package:kernel/core_types.dart';
+import 'package:kernel/type_environment.dart';
 
 import '../builder/type_alias_builder.dart';
 
@@ -439,7 +440,11 @@
       unsupported("${runtimeType}.accept1", -1, null);
 
   @override
-  DartType getStaticType(types) =>
+  DartType getStaticType(StaticTypeContext context) =>
+      unsupported("${runtimeType}.getStaticType", -1, null);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       unsupported("${runtimeType}.getStaticType", -1, null);
 
   ExpressionInferenceResult acceptInference(
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 09193dc..ec80b02 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -2964,7 +2964,19 @@
 
 abstract class Expression extends TreeNode {
   /// Returns the static type of the expression.
-  DartType getStaticType(StaticTypeContext context);
+  ///
+  /// This calls `StaticTypeContext.getExpressionType` which calls
+  /// [getStaticTypeInternal] to compute the type of not already cached in
+  /// [context].
+  DartType getStaticType(StaticTypeContext context) {
+    return context.getExpressionType(this);
+  }
+
+  /// Computes the static type of this expression.
+  ///
+  /// This is called by `StaticTypeContext.getExpressionType` if the static
+  /// type of this expression is not already cached in [context].
+  DartType getStaticTypeInternal(StaticTypeContext context);
 
   /// Returns the static type of the expression as an instantiation of
   /// [superclass].
@@ -3066,14 +3078,26 @@
 
   InvalidExpression(this.message);
 
-  DartType getStaticType(StaticTypeContext context) => const BottomType();
+  @override
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
 
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
+      const BottomType();
+
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitInvalidExpression(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitInvalidExpression(this, arg);
 
-  visitChildren(Visitor v) {}
-  transformChildren(Transformer v) {}
+  @override
+  void visitChildren(Visitor v) {}
+
+  @override
+  void transformChildren(Transformer v) {}
 
   @override
   String toString() {
@@ -3095,7 +3119,11 @@
 
   VariableGet(this.variable, [this.promotedType]) : assert(variable != null);
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     return promotedType ?? variable.type;
   }
 
@@ -3141,6 +3169,10 @@
   }
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       value.getStaticType(context);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitVariableSet(this);
@@ -3194,7 +3226,8 @@
     interfaceTargetReference = getMemberReference(member);
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     var interfaceTarget = this.interfaceTarget;
     if (interfaceTarget != null) {
       Class superclass = interfaceTarget.enclosingClass;
@@ -3274,6 +3307,10 @@
   }
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       value.getStaticType(context);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitPropertySet(this);
@@ -3333,7 +3370,7 @@
     interfaceTargetReference = getMemberReference(member);
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     if (interfaceTarget == null) {
       // TODO(johnniwinther): SuperPropertyGet without a target should be
       // replaced by invalid expressions.
@@ -3399,6 +3436,10 @@
   }
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       value.getStaticType(context);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitSuperPropertySet(this);
@@ -3447,7 +3488,12 @@
     targetReference = getMemberReference(target);
   }
 
-  DartType getStaticType(StaticTypeContext context) => target.getterType;
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
+      target.getterType;
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitStaticGet(this);
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
@@ -3492,6 +3538,10 @@
   }
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       value.getStaticType(context);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitStaticSet(this);
@@ -3686,7 +3736,7 @@
     interfaceTargetReference = getMemberReference(target);
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     var interfaceTarget = this.interfaceTarget;
     if (interfaceTarget != null) {
       if (interfaceTarget is Procedure &&
@@ -3816,7 +3866,7 @@
     interfaceTargetReference = getMemberReference(target);
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     if (interfaceTarget == null) return const DynamicType();
     Class superclass = interfaceTarget.enclosingClass;
     List<DartType> receiverTypeArguments = context.typeEnvironment
@@ -3887,7 +3937,7 @@
     targetReference = getMemberReference(target);
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     return Substitution.fromPairs(
             target.function.typeParameters, arguments.types)
         .substituteType(target.function.returnType);
@@ -3951,7 +4001,7 @@
     targetReference = getMemberReference(target);
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     return arguments.types.isEmpty
         ? context.typeEnvironment.coreTypes
             .rawType(target.enclosingClass, context.nonNullable)
@@ -4018,7 +4068,7 @@
     expression?.parent = this;
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     FunctionType type = expression.getStaticType(context);
     return Substitution.fromPairs(type.typeParameters, typeArguments)
         .substituteType(type.withoutTypeParameters);
@@ -4065,6 +4115,10 @@
   }
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.boolRawType(context.nonNullable);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitNot(this);
@@ -4118,6 +4172,10 @@
   }
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.boolRawType(context.nonNullable);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitLogicalExpression(this);
@@ -4170,7 +4228,11 @@
     otherwise?.parent = this;
   }
 
-  DartType getStaticType(StaticTypeContext context) => staticType;
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) => staticType;
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitConditionalExpression(this);
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
@@ -4238,6 +4300,10 @@
   }
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.stringRawType(context.nonNullable);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitStringConcatenation(this);
@@ -4288,7 +4354,11 @@
     setParents(lists, this);
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     return context.typeEnvironment.listType(typeArgument, context.nonNullable);
   }
 
@@ -4342,7 +4412,11 @@
     setParents(sets, this);
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     return context.typeEnvironment.setType(typeArgument, context.nonNullable);
   }
 
@@ -4399,7 +4473,11 @@
     setParents(maps, this);
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     return context.typeEnvironment
         .mapType(keyType, valueType, context.nonNullable);
   }
@@ -4460,7 +4538,11 @@
 
   Class get classNode => classReference.asClass;
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     return typeArguments.isEmpty
         ? context.typeEnvironment.coreTypes
             .rawType(classNode, context.nonNullable)
@@ -4558,6 +4640,10 @@
   }
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       expression.getStaticType(context);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitFileUriExpression(this);
@@ -4619,6 +4705,10 @@
   }
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.boolRawType(context.nonNullable);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitIsExpression(this);
@@ -4728,7 +4818,11 @@
         : (flags & ~FlagForNonNullableByDefault);
   }
 
-  DartType getStaticType(StaticTypeContext context) => type;
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) => type;
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitAsExpression(this);
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
@@ -4791,7 +4885,7 @@
     operand?.parent = this;
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     DartType operandType = operand.getStaticType(context);
     return operandType == context.typeEnvironment.nullType
         ? const NeverType(Nullability.nonNullable)
@@ -4840,6 +4934,10 @@
   StringLiteral(this.value);
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.stringRawType(context.nonNullable);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitStringLiteral(this);
@@ -4869,6 +4967,10 @@
   IntLiteral(this.value);
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.intRawType(context.nonNullable);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitIntLiteral(this);
@@ -4892,6 +4994,10 @@
   DoubleLiteral(this.value);
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.doubleRawType(context.nonNullable);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitDoubleLiteral(this);
@@ -4915,6 +5021,10 @@
   BoolLiteral(this.value);
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.boolRawType(context.nonNullable);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitBoolLiteral(this);
@@ -4936,6 +5046,10 @@
   Object get value => null;
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.nullType;
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitNullLiteral(this);
@@ -4959,6 +5073,10 @@
   SymbolLiteral(this.value);
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.symbolRawType(context.nonNullable);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitSymbolLiteral(this);
@@ -4986,6 +5104,10 @@
   TypeLiteral(this.type);
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.typeRawType(context.nonNullable);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitTypeLiteral(this);
@@ -5012,7 +5134,11 @@
 }
 
 class ThisExpression extends Expression {
-  DartType getStaticType(StaticTypeContext context) => context.thisType;
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) => context.thisType;
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitThisExpression(this);
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
@@ -5034,6 +5160,10 @@
 
 class Rethrow extends Expression {
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.isNonNullableByDefault
           ? const NeverType(Nullability.nonNullable)
           : const BottomType();
@@ -5064,6 +5194,10 @@
   }
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.isNonNullableByDefault
           ? const NeverType(Nullability.nonNullable)
           : const BottomType();
@@ -5105,7 +5239,11 @@
     setParents(expressions, this);
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     return context.typeEnvironment.listType(typeArgument, context.nonNullable);
   }
 
@@ -5152,7 +5290,11 @@
     setParents(expressions, this);
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     return context.typeEnvironment.setType(typeArgument, context.nonNullable);
   }
 
@@ -5203,7 +5345,11 @@
     setParents(entries, this);
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     return context.typeEnvironment
         .mapType(keyType, valueType, context.nonNullable);
   }
@@ -5302,7 +5448,7 @@
     operand?.parent = this;
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     return context.typeEnvironment.flatten(operand.getStaticType(context));
   }
 
@@ -5348,7 +5494,7 @@
     function?.parent = this;
   }
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     return function.computeFunctionType(context.nonNullable);
   }
 
@@ -5386,7 +5532,11 @@
     assert(constant != null);
   }
 
-  DartType getStaticType(StaticTypeContext context) => type;
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) => type;
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitConstantExpression(this);
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
@@ -5424,6 +5574,10 @@
   }
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       body.getStaticType(context);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitLet(this);
@@ -5469,6 +5623,10 @@
   }
 
   DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
       value.getStaticType(context);
 
   R accept<R>(ExpressionVisitor<R> v) => v.visitBlockExpression(this);
@@ -5523,7 +5681,11 @@
 
   LoadLibrary(this.import);
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     return context.typeEnvironment
         .futureType(const DynamicType(), context.nonNullable);
   }
@@ -5554,7 +5716,11 @@
 
   CheckLibraryIsLoaded(this.import);
 
-  DartType getStaticType(StaticTypeContext context) {
+  DartType getStaticType(StaticTypeContext context) =>
+      getStaticTypeInternal(context);
+
+  @override
+  DartType getStaticTypeInternal(StaticTypeContext context) {
     return context.typeEnvironment.coreTypes.objectRawType(context.nonNullable);
   }
 
@@ -6069,6 +6235,76 @@
     }
   }
 
+  /// Returns the type of the iterator in this for-in statement.
+  ///
+  /// This calls `StaticTypeContext.getForInIteratorType` which calls
+  /// [getStaticTypeInternal] to compute the type of not already cached in
+  /// [context].
+  DartType getIteratorType(StaticTypeContext context) =>
+      context.getForInIteratorType(this);
+
+  /// Computes the type of the iterator in this for-in statement.
+  ///
+  /// This is called by `StaticTypeContext.getForInIteratorType` if the iterator
+  /// type of this for-in statement is not already cached in [context].
+  DartType getIteratorTypeInternal(StaticTypeContext context) {
+    DartType iteratorType;
+    if (isAsync) {
+      InterfaceType streamType = iterable.getStaticTypeAsInstanceOf(
+          context.typeEnvironment.coreTypes.streamClass, context);
+      if (streamType != null) {
+        iteratorType = new InterfaceType(
+            context.typeEnvironment.coreTypes.streamIteratorClass,
+            context.nonNullable,
+            streamType.typeArguments);
+      }
+    } else {
+      InterfaceType iterableType = iterable.getStaticTypeAsInstanceOf(
+          context.typeEnvironment.coreTypes.iterableClass, context);
+      Member member = context.typeEnvironment.hierarchy
+          .getInterfaceMember(iterableType.classNode, new Name('iterator'));
+      if (member != null) {
+        iteratorType = Substitution.fromInterfaceType(iterableType)
+            .substituteType(member.getterType);
+      }
+    }
+    return iteratorType ??= const DynamicType();
+  }
+
+  /// Returns the type of the element in this for-in statement.
+  ///
+  /// This calls `StaticTypeContext.getForInElementType` which calls
+  /// [getStaticTypeInternal] to compute the type of not already cached in
+  /// [context].
+  DartType getElementType(StaticTypeContext context) =>
+      context.getForInElementType(this);
+
+  /// Computes the type of the element in this for-in statement.
+  ///
+  /// This is called by `StaticTypeContext.getForInElementType` if the element
+  /// type of this for-in statement is not already cached in [context].
+  DartType getElementTypeInternal(StaticTypeContext context) {
+    DartType iterableType = iterable.getStaticType(context);
+    // TODO(johnniwinther): Update this to use the type of
+    //  `iterable.iterator.current` if inference is updated accordingly.
+    while (iterableType is TypeParameterType) {
+      TypeParameterType typeParameterType = iterableType;
+      iterableType =
+          typeParameterType.promotedBound ?? typeParameterType.parameter.bound;
+    }
+    if (isAsync) {
+      List<DartType> typeArguments = context.typeEnvironment
+          .getTypeArgumentsAsInstanceOf(
+              iterableType, context.typeEnvironment.coreTypes.streamClass);
+      return typeArguments.single;
+    } else {
+      List<DartType> typeArguments = context.typeEnvironment
+          .getTypeArgumentsAsInstanceOf(
+              iterableType, context.typeEnvironment.coreTypes.iterableClass);
+      return typeArguments.single;
+    }
+  }
+
   @override
   String toString() {
     return "ForInStatement(${toStringInternal()})";
diff --git a/pkg/kernel/lib/type_environment.dart b/pkg/kernel/lib/type_environment.dart
index 076f46e..8967df1 100644
--- a/pkg/kernel/lib/type_environment.dart
+++ b/pkg/kernel/lib/type_environment.dart
@@ -530,13 +530,89 @@
   ignoringNullabilities,
 }
 
+abstract class StaticTypeCache {
+  DartType getExpressionType(Expression node, StaticTypeContext context);
+
+  DartType getForInIteratorType(ForInStatement node, StaticTypeContext context);
+
+  DartType getForInElementType(ForInStatement node, StaticTypeContext context);
+}
+
+class StaticTypeCacheImpl implements StaticTypeCache {
+  Map<Expression, DartType> _expressionTypes;
+  Map<ForInStatement, DartType> _forInIteratorTypes;
+  Map<ForInStatement, DartType> _forInElementTypes;
+
+  DartType getExpressionType(Expression node, StaticTypeContext context) {
+    _expressionTypes ??= <Expression, DartType>{};
+    return _expressionTypes[node] ??= node.getStaticTypeInternal(context);
+  }
+
+  DartType getForInIteratorType(
+      ForInStatement node, StaticTypeContext context) {
+    _forInIteratorTypes ??= <ForInStatement, DartType>{};
+    return _forInIteratorTypes[node] ??= node.getIteratorTypeInternal(context);
+  }
+
+  DartType getForInElementType(ForInStatement node, StaticTypeContext context) {
+    _forInElementTypes ??= <ForInStatement, DartType>{};
+    return _forInElementTypes[node] ??= node.getElementTypeInternal(context);
+  }
+}
+
 /// Context object needed for computing `Expression.getStaticType`.
 ///
 /// The [StaticTypeContext] provides access to the [TypeEnvironment] and the
 /// current 'this type' as well as determining the nullability state of the
 /// enclosing library.
-// TODO(johnniwinther): Support static type caching through [StaticTypeContext].
-class StaticTypeContext {
+abstract class StaticTypeContext {
+  /// The [TypeEnvironment] used for the static type computation.
+  ///
+  /// This provides access to the core types and the class hierarchy.
+  TypeEnvironment get typeEnvironment;
+
+  /// The static type of a `this` expression.
+  InterfaceType get thisType;
+
+  /// Creates a static type context for computing static types in the body
+  /// of [member].
+  factory StaticTypeContext(Member member, TypeEnvironment typeEnvironment,
+      {StaticTypeCache cache}) = StaticTypeContextImpl;
+
+  /// Creates a static type context for computing static types of annotations
+  /// in [library].
+  factory StaticTypeContext.forAnnotations(
+      Library library, TypeEnvironment typeEnvironment,
+      {StaticTypeCache cache}) = StaticTypeContextImpl.forAnnotations;
+
+  /// The [Nullability] used for non-nullable types.
+  ///
+  /// For opt out libraries this is [Nullability.legacy].
+  Nullability get nonNullable;
+
+  /// The [Nullability] used for nullable types.
+  ///
+  /// For opt out libraries this is [Nullability.legacy].
+  Nullability get nullable;
+
+  /// Return `true` if the current library is opted in to non-nullable by
+  /// default.
+  bool get isNonNullableByDefault;
+
+  /// Returns the mode under which the current library was compiled.
+  NonNullableByDefaultCompiledMode get nonNullableByDefaultCompiledMode;
+
+  /// Returns the static type of [node].
+  DartType getExpressionType(Expression node);
+
+  /// Returns the static type of the iterator in for-in statement [node].
+  DartType getForInIteratorType(ForInStatement node);
+
+  /// Returns the static type of the element in for-in statement [node].
+  DartType getForInElementType(ForInStatement node);
+}
+
+class StaticTypeContextImpl implements StaticTypeContext {
   /// The [TypeEnvironment] used for the static type computation.
   ///
   /// This provides access to the core types and the class hierarchy.
@@ -551,17 +627,23 @@
   /// The static type of a `this` expression.
   final InterfaceType thisType;
 
+  final StaticTypeCache _cache;
+
   /// Creates a static type context for computing static types in the body
   /// of [member].
-  StaticTypeContext(Member member, this.typeEnvironment)
+  StaticTypeContextImpl(Member member, this.typeEnvironment,
+      {StaticTypeCache cache})
       : _library = member.enclosingLibrary,
         thisType = member.enclosingClass?.getThisType(
-            typeEnvironment.coreTypes, member.enclosingLibrary.nonNullable);
+            typeEnvironment.coreTypes, member.enclosingLibrary.nonNullable),
+        _cache = cache;
 
   /// Creates a static type context for computing static types of annotations
   /// in [library].
-  StaticTypeContext.forAnnotations(this._library, this.typeEnvironment)
-      : thisType = null;
+  StaticTypeContextImpl.forAnnotations(this._library, this.typeEnvironment,
+      {StaticTypeCache cache})
+      : thisType = null,
+        _cache = cache;
 
   /// The [Nullability] used for non-nullable types.
   ///
@@ -580,6 +662,30 @@
   /// Returns the mode under which the current library was compiled.
   NonNullableByDefaultCompiledMode get nonNullableByDefaultCompiledMode =>
       _library.nonNullableByDefaultCompiledMode;
+
+  DartType getExpressionType(Expression node) {
+    if (_cache != null) {
+      return _cache.getExpressionType(node, this);
+    } else {
+      return node.getStaticTypeInternal(this);
+    }
+  }
+
+  DartType getForInIteratorType(ForInStatement node) {
+    if (_cache != null) {
+      return _cache.getForInIteratorType(node, this);
+    } else {
+      return node.getIteratorTypeInternal(this);
+    }
+  }
+
+  DartType getForInElementType(ForInStatement node) {
+    if (_cache != null) {
+      return _cache.getForInElementType(node, this);
+    } else {
+      return node.getElementTypeInternal(this);
+    }
+  }
 }
 
 /// Implementation of [StaticTypeContext] that update its state when entering
@@ -636,7 +742,6 @@
   _FlatStatefulStaticTypeContext(TypeEnvironment typeEnvironment)
       : super._internal(typeEnvironment);
 
-  @override
   Library get _library {
     Library library = _currentLibrary ?? _currentMember?.enclosingLibrary;
     assert(library != null,
@@ -718,6 +823,18 @@
         "Trying to leave $node but current is ${_currentLibrary}.");
     _currentLibrary = null;
   }
+
+  @override
+  DartType getExpressionType(Expression node) =>
+      node.getStaticTypeInternal(this);
+
+  @override
+  DartType getForInIteratorType(ForInStatement node) =>
+      node.getIteratorTypeInternal(this);
+
+  @override
+  DartType getForInElementType(ForInStatement node) =>
+      node.getElementTypeInternal(this);
 }
 
 /// Implementation of [StatefulStaticTypeContext] that use a stack to change
@@ -729,7 +846,6 @@
   _StackedStatefulStaticTypeContext(TypeEnvironment typeEnvironment)
       : super._internal(typeEnvironment);
 
-  @override
   Library get _library {
     assert(_contextStack.isNotEmpty,
         "No library currently associated with StaticTypeContext.");
@@ -805,6 +921,18 @@
         "Inconsistent static type context stack: "
         "Trying to leave $node but current is ${state._node}.");
   }
+
+  @override
+  DartType getExpressionType(Expression node) =>
+      node.getStaticTypeInternal(this);
+
+  @override
+  DartType getForInIteratorType(ForInStatement node) =>
+      node.getIteratorTypeInternal(this);
+
+  @override
+  DartType getForInElementType(ForInStatement node) =>
+      node.getElementTypeInternal(this);
 }
 
 class _StaticTypeContextState {
diff --git a/runtime/bin/main_options.cc b/runtime/bin/main_options.cc
index 16d0fb1..e644202 100644
--- a/runtime/bin/main_options.cc
+++ b/runtime/bin/main_options.cc
@@ -599,7 +599,18 @@
   // dartdev command (e.g., --enable-vm-service, --observe, etc).
   if (!run_script) {
     int tmp_i = i;
+    // We only run the CLI implicitly if the service is enabled and the user
+    // didn't run with the 'run' command. If they did provide a command, we need
+    // to skip it here to continue parsing VM flags.
+    if (!implicitly_use_dart_dev) {
+      tmp_i++;
+    }
     while (tmp_i < argc) {
+      // Check if this flag is a potentially valid VM flag. If not, we've likely
+      // hit a script name and are done parsing VM flags.
+      if (!OptionProcessor::IsValidFlag(argv[tmp_i], kPrefix, kPrefixLen)) {
+        break;
+      }
       OptionProcessor::TryProcess(argv[tmp_i], vm_options);
       tmp_i++;
     }
diff --git a/runtime/tests/vm/dart/il_round_trip_deserialization_test.dart b/runtime/tests/vm/dart/il_round_trip_deserialization_test.dart
deleted file mode 100644
index 0a6f667..0000000
--- a/runtime/tests/vm/dart/il_round_trip_deserialization_test.dart
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2019, 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.
-
-// VMOptions=
-// VMOptions=--early-round-trip-serialization
-// VMOptions=--late-round-trip-serialization
-// VMOptions=--early-round-trip-serialization --late-round-trip-serialization
-// VMOptions=--deterministic
-// VMOptions=--deterministic --early-round-trip-serialization
-// VMOptions=--deterministic --late-round-trip-serialization
-// VMOptions=--deterministic --early-round-trip-serialization --late-round-trip-serialization
-
-// Just use the existing hello world test for now.
-// TODO(36882): Add more interesting code as the deserializer grows.
-import 'hello_world_test.dart' as test;
-
-main(args) {
-  test.main();
-}
diff --git a/runtime/tests/vm/dart_2/il_round_trip_deserialization_test.dart b/runtime/tests/vm/dart_2/il_round_trip_deserialization_test.dart
deleted file mode 100644
index 0a6f667..0000000
--- a/runtime/tests/vm/dart_2/il_round_trip_deserialization_test.dart
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2019, 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.
-
-// VMOptions=
-// VMOptions=--early-round-trip-serialization
-// VMOptions=--late-round-trip-serialization
-// VMOptions=--early-round-trip-serialization --late-round-trip-serialization
-// VMOptions=--deterministic
-// VMOptions=--deterministic --early-round-trip-serialization
-// VMOptions=--deterministic --late-round-trip-serialization
-// VMOptions=--deterministic --early-round-trip-serialization --late-round-trip-serialization
-
-// Just use the existing hello world test for now.
-// TODO(36882): Add more interesting code as the deserializer grows.
-import 'hello_world_test.dart' as test;
-
-main(args) {
-  test.main();
-}
diff --git a/runtime/vm/heap/compactor.cc b/runtime/vm/heap/compactor.cc
index a4c5370..cd6e83f 100644
--- a/runtime/vm/heap/compactor.cc
+++ b/runtime/vm/heap/compactor.cc
@@ -296,6 +296,8 @@
     ForwardStackPointers();
   }
 
+  heap_->old_space()->VisitRoots(this);
+
   {
     MutexLocker ml(pages_lock);
 
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index d7f1455..a1d5866 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -58,9 +58,11 @@
 };
 
 Heap::Heap(IsolateGroup* isolate_group,
+           bool is_vm_isolate,
            intptr_t max_new_gen_semi_words,
            intptr_t max_old_gen_words)
     : isolate_group_(isolate_group),
+      is_vm_isolate_(is_vm_isolate),
       new_space_(this, max_new_gen_semi_words),
       old_space_(this, max_old_gen_words),
       barrier_(),
@@ -150,6 +152,9 @@
   if (addr != 0) {
     return addr;
   }
+
+  old_space_.TryReleaseReservation();
+
   // Give up allocating this object.
   OS::PrintErr("Exhausted heap space, trying to allocate %" Pd " bytes.\n",
                size);
@@ -678,11 +683,12 @@
 }
 
 void Heap::Init(IsolateGroup* isolate_group,
+                bool is_vm_isolate,
                 intptr_t max_new_gen_words,
                 intptr_t max_old_gen_words) {
   ASSERT(isolate_group->heap() == nullptr);
-  std::unique_ptr<Heap> heap(
-      new Heap(isolate_group, max_new_gen_words, max_old_gen_words));
+  std::unique_ptr<Heap> heap(new Heap(isolate_group, is_vm_isolate,
+                                      max_new_gen_words, max_old_gen_words));
   isolate_group->set_heap(std::move(heap));
 }
 
diff --git a/runtime/vm/heap/heap.h b/runtime/vm/heap/heap.h
index 7e42b75..026d793 100644
--- a/runtime/vm/heap/heap.h
+++ b/runtime/vm/heap/heap.h
@@ -167,6 +167,7 @@
 
   // Initialize the heap and register it with the isolate.
   static void Init(IsolateGroup* isolate_group,
+                   bool is_vm_isolate,
                    intptr_t max_new_gen_words,
                    intptr_t max_old_gen_words);
 
@@ -305,6 +306,7 @@
 #endif  // PRODUCT
 
   IsolateGroup* isolate_group() const { return isolate_group_; }
+  bool is_vm_isolate() const { return is_vm_isolate_; }
 
   Monitor* barrier() const { return &barrier_; }
   Monitor* barrier_done() const { return &barrier_done_; }
@@ -354,6 +356,7 @@
   };
 
   Heap(IsolateGroup* isolate_group,
+       bool is_vm_isolate,
        intptr_t max_new_gen_semi_words,  // Max capacity of new semi-space.
        intptr_t max_old_gen_words);
 
@@ -391,6 +394,7 @@
   void CollectForDebugging();
 
   IsolateGroup* isolate_group_;
+  bool is_vm_isolate_;
 
   // The different spaces used for allocation.
   Scavenger new_space_;
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index 3c73952..d3b62fa 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -249,6 +249,8 @@
   for (intptr_t i = 0; i < num_freelists_; i++) {
     freelists_[i].Reset();
   }
+
+  TryReserveForOOM();
 }
 
 PageSpace::~PageSpace() {
@@ -366,7 +368,7 @@
 
   page->set_object_end(page->memory_->end());
   if ((type != OldPage::kExecutable) && (heap_ != nullptr) &&
-      (heap_->isolate_group() != Dart::vm_isolate()->group())) {
+      (!heap_->is_vm_isolate())) {
     page->AllocateForwardingPage();
   }
   return page;
@@ -1020,6 +1022,47 @@
   return estimated_mark_compact_completion <= deadline;
 }
 
+void PageSpace::TryReleaseReservation() {
+  if (oom_reservation_ == nullptr) return;
+  uword addr = reinterpret_cast<uword>(oom_reservation_);
+  intptr_t size = oom_reservation_->HeapSize();
+  oom_reservation_ = nullptr;
+  freelists_[OldPage::kData].Free(addr, size);
+}
+
+bool PageSpace::MarkReservation() {
+  if (oom_reservation_ == nullptr) {
+    return false;
+  }
+  ObjectLayout* ptr = reinterpret_cast<ObjectLayout*>(oom_reservation_);
+  if (!ptr->IsMarked()) {
+    ptr->SetMarkBit();
+  }
+  return true;
+}
+
+void PageSpace::TryReserveForOOM() {
+  if (oom_reservation_ == nullptr) {
+    uword addr = TryAllocate(kOOMReservationSize, OldPage::kData,
+                             kForceGrowth /* Don't re-enter GC */);
+    if (addr != 0) {
+      oom_reservation_ = FreeListElement::AsElement(addr, kOOMReservationSize);
+    }
+  }
+}
+
+void PageSpace::VisitRoots(ObjectPointerVisitor* visitor) {
+  if (oom_reservation_ != nullptr) {
+    // FreeListElements are generally held untagged, but ObjectPointerVisitors
+    // expect tagged pointers.
+    ObjectPtr ptr =
+        ObjectLayout::FromAddr(reinterpret_cast<uword>(oom_reservation_));
+    visitor->VisitPointer(&ptr);
+    oom_reservation_ =
+        reinterpret_cast<FreeListElement*>(ObjectLayout::ToAddr(ptr));
+  }
+}
+
 void PageSpace::CollectGarbage(bool compact, bool finalize) {
   ASSERT(GrowthControlState());
 
@@ -1186,11 +1229,13 @@
     mid3 = OS::GetCurrentMonotonicMicros();
   }
 
+  bool has_reservation = MarkReservation();
+
   if (compact) {
     SweepLarge();
     Compact(thread);
     set_phase(kDone);
-  } else if (FLAG_concurrent_sweep) {
+  } else if (FLAG_concurrent_sweep && has_reservation) {
     ConcurrentSweep(isolate_group);
   } else {
     SweepLarge();
@@ -1198,6 +1243,8 @@
     set_phase(kDone);
   }
 
+  TryReserveForOOM();
+
   // Make code pages read-only.
   if (finalize) WriteProtectCode(true);
 
@@ -1551,9 +1598,10 @@
       before.CombinedUsedInWords() - last_usage_.CombinedUsedInWords();
   intptr_t grow_heap;
   if (allocated_since_previous_gc > 0) {
-    const intptr_t garbage =
+    intptr_t garbage =
         before.CombinedUsedInWords() - after.CombinedUsedInWords();
-    ASSERT(garbage >= 0);
+    // Garbage may be negative if when the OOM reservation is refilled.
+    garbage = Utils::Maximum(static_cast<intptr_t>(0), garbage);
     // It makes no sense to expect that each kb allocated will cause more than
     // one kb of garbage, so we clamp k at 1.0.
     const double k = Utils::Minimum(
diff --git a/runtime/vm/heap/pages.h b/runtime/vm/heap/pages.h
index 83c5488..a786f78 100644
--- a/runtime/vm/heap/pages.h
+++ b/runtime/vm/heap/pages.h
@@ -307,6 +307,11 @@
                                is_protected, is_locked);
   }
 
+  void TryReleaseReservation();
+  bool MarkReservation();
+  void TryReserveForOOM();
+  void VisitRoots(ObjectPointerVisitor* visitor);
+
   bool ReachedHardThreshold() const {
     return page_space_controller_.ReachedHardThreshold(usage_);
   }
@@ -569,6 +574,8 @@
   // page freelists without locking.
   const intptr_t num_freelists_;
   FreeList* freelists_;
+  static constexpr intptr_t kOOMReservationSize = 32 * KB;
+  FreeListElement* oom_reservation_ = nullptr;
 
   // Use ExclusivePageIterator for safe access to these.
   mutable Mutex pages_lock_;
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index 41f1f99..e45ebd5 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -1512,6 +1512,10 @@
   if (abort_) {
     ReverseScavenge(&from);
     bytes_promoted = 0;
+  } else if ((CapacityInWords() - UsedInWords()) < KBInWords) {
+    // Don't scavenge again until the next old-space GC has occurred. Prevents
+    // performing one scavenge per allocation as the heap limit is approached.
+    heap_->assume_scavenge_will_fail_ = true;
   }
   ASSERT(promotion_stack_.IsEmpty());
   MournWeakHandles();
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 7a1c69e..0667421 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -448,7 +448,7 @@
 
 void IsolateGroup::CreateHeap(bool is_vm_isolate,
                               bool is_service_or_kernel_isolate) {
-  Heap::Init(this,
+  Heap::Init(this, is_vm_isolate,
              is_vm_isolate
                  ? 0  // New gen size 0; VM isolate should only allocate in old.
                  : FLAG_new_gen_semi_max_size * MBInWords,
diff --git a/tests/language/deferred/split_constants_canonicalization_a.dart b/tests/language/deferred/split_constants_canonicalization_a.dart
new file mode 100644
index 0000000..3e3ada7
--- /dev/null
+++ b/tests/language/deferred/split_constants_canonicalization_a.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, 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 "split_constants_canonicalization_a_1.dart" deferred as a_1;
+import "split_constants_canonicalization_a_2.dart" deferred as a_2;
+
+loadChildren() async {
+  await a_1.loadLibrary();
+  await a_2.loadLibrary();
+}
+
+a_1_mint() => a_1.mint();
+a_1_string() => a_1.string();
+a_1_list() => a_1.list();
+a_1_map() => a_1.map();
+a_1_box() => a_1.box();
+a_1_enum() => a_1.enumm();
+a_1_type() => a_1.type();
+a_1_closure() => a_1.closure();
+
+a_2_mint() => a_2.mint();
+a_2_string() => a_2.string();
+a_2_list() => a_2.list();
+a_2_map() => a_2.map();
+a_2_box() => a_2.box();
+a_2_enum() => a_2.enumm();
+a_2_type() => a_2.type();
+a_2_closure() => a_2.closure();
diff --git a/tests/language/deferred/split_constants_canonicalization_a_1.dart b/tests/language/deferred/split_constants_canonicalization_a_1.dart
new file mode 100644
index 0000000..ea054f5
--- /dev/null
+++ b/tests/language/deferred/split_constants_canonicalization_a_1.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, 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 "split_constants_canonicalization_test.dart";
+
+@pragma("vm:never-inline")
+mint() => 0x7FFFFFFFFFFFFFFF;
+
+@pragma("vm:never-inline")
+string() => "We all have identical strings";
+
+@pragma("vm:never-inline")
+list() => const <String>["We all have identical lists"];
+
+@pragma("vm:never-inline")
+map() => const <String, String>{"We all have": "identical maps"};
+
+@pragma("vm:never-inline")
+box() => const Box("We all have identical boxes");
+
+@pragma("vm:never-inline")
+enumm() => Enum.GREEN;
+
+@pragma("vm:never-inline")
+type() => Box;
+
+@pragma("vm:never-inline")
+closure() => commonClosure;
diff --git a/tests/language/deferred/split_constants_canonicalization_a_2.dart b/tests/language/deferred/split_constants_canonicalization_a_2.dart
new file mode 100644
index 0000000..ea054f5
--- /dev/null
+++ b/tests/language/deferred/split_constants_canonicalization_a_2.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, 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 "split_constants_canonicalization_test.dart";
+
+@pragma("vm:never-inline")
+mint() => 0x7FFFFFFFFFFFFFFF;
+
+@pragma("vm:never-inline")
+string() => "We all have identical strings";
+
+@pragma("vm:never-inline")
+list() => const <String>["We all have identical lists"];
+
+@pragma("vm:never-inline")
+map() => const <String, String>{"We all have": "identical maps"};
+
+@pragma("vm:never-inline")
+box() => const Box("We all have identical boxes");
+
+@pragma("vm:never-inline")
+enumm() => Enum.GREEN;
+
+@pragma("vm:never-inline")
+type() => Box;
+
+@pragma("vm:never-inline")
+closure() => commonClosure;
diff --git a/tests/language/deferred/split_constants_canonicalization_b.dart b/tests/language/deferred/split_constants_canonicalization_b.dart
new file mode 100644
index 0000000..f27900d
--- /dev/null
+++ b/tests/language/deferred/split_constants_canonicalization_b.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, 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 "split_constants_canonicalization_b_1.dart" deferred as b_1;
+import "split_constants_canonicalization_b_2.dart" deferred as b_2;
+
+loadChildren() async {
+  await b_1.loadLibrary();
+  await b_2.loadLibrary();
+}
+
+b_1_mint() => b_1.mint();
+b_1_string() => b_1.string();
+b_1_list() => b_1.list();
+b_1_map() => b_1.map();
+b_1_box() => b_1.box();
+b_1_enum() => b_1.enumm();
+b_1_type() => b_1.type();
+b_1_closure() => b_1.closure();
+
+b_2_mint() => b_2.mint();
+b_2_string() => b_2.string();
+b_2_list() => b_2.list();
+b_2_map() => b_2.map();
+b_2_box() => b_2.box();
+b_2_enum() => b_2.enumm();
+b_2_type() => b_2.type();
+b_2_closure() => b_2.closure();
diff --git a/tests/language/deferred/split_constants_canonicalization_b_1.dart b/tests/language/deferred/split_constants_canonicalization_b_1.dart
new file mode 100644
index 0000000..ea054f5
--- /dev/null
+++ b/tests/language/deferred/split_constants_canonicalization_b_1.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, 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 "split_constants_canonicalization_test.dart";
+
+@pragma("vm:never-inline")
+mint() => 0x7FFFFFFFFFFFFFFF;
+
+@pragma("vm:never-inline")
+string() => "We all have identical strings";
+
+@pragma("vm:never-inline")
+list() => const <String>["We all have identical lists"];
+
+@pragma("vm:never-inline")
+map() => const <String, String>{"We all have": "identical maps"};
+
+@pragma("vm:never-inline")
+box() => const Box("We all have identical boxes");
+
+@pragma("vm:never-inline")
+enumm() => Enum.GREEN;
+
+@pragma("vm:never-inline")
+type() => Box;
+
+@pragma("vm:never-inline")
+closure() => commonClosure;
diff --git a/tests/language/deferred/split_constants_canonicalization_b_2.dart b/tests/language/deferred/split_constants_canonicalization_b_2.dart
new file mode 100644
index 0000000..ea054f5
--- /dev/null
+++ b/tests/language/deferred/split_constants_canonicalization_b_2.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, 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 "split_constants_canonicalization_test.dart";
+
+@pragma("vm:never-inline")
+mint() => 0x7FFFFFFFFFFFFFFF;
+
+@pragma("vm:never-inline")
+string() => "We all have identical strings";
+
+@pragma("vm:never-inline")
+list() => const <String>["We all have identical lists"];
+
+@pragma("vm:never-inline")
+map() => const <String, String>{"We all have": "identical maps"};
+
+@pragma("vm:never-inline")
+box() => const Box("We all have identical boxes");
+
+@pragma("vm:never-inline")
+enumm() => Enum.GREEN;
+
+@pragma("vm:never-inline")
+type() => Box;
+
+@pragma("vm:never-inline")
+closure() => commonClosure;
diff --git a/tests/language/deferred/split_constants_canonicalization_test.dart b/tests/language/deferred/split_constants_canonicalization_test.dart
new file mode 100644
index 0000000..02d5c6e
--- /dev/null
+++ b/tests/language/deferred/split_constants_canonicalization_test.dart
@@ -0,0 +1,94 @@
+// Copyright (c) 2020, 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.
+
+// VMOptions=--use_bare_instructions=false
+// VMOptions=--use_bare_instructions=true
+
+import "package:expect/expect.dart";
+import "split_constants_canonicalization_a.dart" deferred as a;
+import "split_constants_canonicalization_b.dart" deferred as b;
+
+class Box {
+  final contents;
+  const Box(this.contents);
+}
+
+enum Enum {
+  RED,
+  GREEN,
+  BLUE,
+}
+
+commonClosure() {}
+
+main() async {
+  await a.loadLibrary();
+  await a.loadChildren();
+  await b.loadLibrary();
+  await b.loadChildren();
+
+  var a_1_mint = await a.a_1_mint();
+  var a_2_mint = await a.a_2_mint();
+  var b_1_mint = await b.b_1_mint();
+  var b_2_mint = await b.b_2_mint();
+  Expect.isTrue(identical(a_1_mint, a_2_mint));
+  Expect.isTrue(identical(a_1_mint, b_1_mint));
+  Expect.isTrue(identical(a_1_mint, b_2_mint));
+
+  var a_1_string = await a.a_1_string();
+  var a_2_string = await a.a_2_string();
+  var b_1_string = await b.b_1_string();
+  var b_2_string = await b.b_2_string();
+  Expect.isTrue(identical(a_1_string, a_2_string));
+  Expect.isTrue(identical(a_1_string, b_1_string));
+  Expect.isTrue(identical(a_1_string, b_2_string));
+
+  var a_1_list = await a.a_1_list();
+  var a_2_list = await a.a_2_list();
+  var b_1_list = await b.b_1_list();
+  var b_2_list = await b.b_2_list();
+  Expect.isTrue(identical(a_1_list, a_2_list));
+  Expect.isTrue(identical(a_1_list, b_1_list));
+  Expect.isTrue(identical(a_1_list, b_2_list));
+
+  var a_1_map = await a.a_1_map();
+  var a_2_map = await a.a_2_map();
+  var b_1_map = await b.b_1_map();
+  var b_2_map = await b.b_2_map();
+  Expect.isTrue(identical(a_1_map, a_2_map));
+  Expect.isTrue(identical(a_1_map, b_1_map));
+  Expect.isTrue(identical(a_1_map, b_2_map));
+
+  var a_1_box = await a.a_1_box();
+  var a_2_box = await a.a_2_box();
+  var b_1_box = await b.b_1_box();
+  var b_2_box = await b.b_2_box();
+  Expect.isTrue(identical(a_1_box, a_2_box));
+  Expect.isTrue(identical(a_1_box, b_1_box));
+  Expect.isTrue(identical(a_1_box, b_2_box));
+
+  var a_1_enum = await a.a_1_enum();
+  var a_2_enum = await a.a_2_enum();
+  var b_1_enum = await b.b_1_enum();
+  var b_2_enum = await b.b_2_enum();
+  Expect.isTrue(identical(a_1_enum, a_2_enum));
+  Expect.isTrue(identical(a_1_enum, b_1_enum));
+  Expect.isTrue(identical(a_1_enum, b_2_enum));
+
+  var a_1_type = await a.a_1_type();
+  var a_2_type = await a.a_2_type();
+  var b_1_type = await b.b_1_type();
+  var b_2_type = await b.b_2_type();
+  Expect.isTrue(identical(a_1_type, a_2_type));
+  Expect.isTrue(identical(a_1_type, b_1_type));
+  Expect.isTrue(identical(a_1_type, b_2_type));
+
+  var a_1_closure = await a.a_1_closure();
+  var a_2_closure = await a.a_2_closure();
+  var b_1_closure = await b.b_1_closure();
+  var b_2_closure = await b.b_2_closure();
+  Expect.isTrue(identical(a_1_closure, a_2_closure));
+  Expect.isTrue(identical(a_1_closure, b_1_closure));
+  Expect.isTrue(identical(a_1_closure, b_2_closure));
+}
diff --git a/tests/language_2/deferred/split_constants_canonicalization_a.dart b/tests/language_2/deferred/split_constants_canonicalization_a.dart
new file mode 100644
index 0000000..3e3ada7
--- /dev/null
+++ b/tests/language_2/deferred/split_constants_canonicalization_a.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, 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 "split_constants_canonicalization_a_1.dart" deferred as a_1;
+import "split_constants_canonicalization_a_2.dart" deferred as a_2;
+
+loadChildren() async {
+  await a_1.loadLibrary();
+  await a_2.loadLibrary();
+}
+
+a_1_mint() => a_1.mint();
+a_1_string() => a_1.string();
+a_1_list() => a_1.list();
+a_1_map() => a_1.map();
+a_1_box() => a_1.box();
+a_1_enum() => a_1.enumm();
+a_1_type() => a_1.type();
+a_1_closure() => a_1.closure();
+
+a_2_mint() => a_2.mint();
+a_2_string() => a_2.string();
+a_2_list() => a_2.list();
+a_2_map() => a_2.map();
+a_2_box() => a_2.box();
+a_2_enum() => a_2.enumm();
+a_2_type() => a_2.type();
+a_2_closure() => a_2.closure();
diff --git a/tests/language_2/deferred/split_constants_canonicalization_a_1.dart b/tests/language_2/deferred/split_constants_canonicalization_a_1.dart
new file mode 100644
index 0000000..ea054f5
--- /dev/null
+++ b/tests/language_2/deferred/split_constants_canonicalization_a_1.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, 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 "split_constants_canonicalization_test.dart";
+
+@pragma("vm:never-inline")
+mint() => 0x7FFFFFFFFFFFFFFF;
+
+@pragma("vm:never-inline")
+string() => "We all have identical strings";
+
+@pragma("vm:never-inline")
+list() => const <String>["We all have identical lists"];
+
+@pragma("vm:never-inline")
+map() => const <String, String>{"We all have": "identical maps"};
+
+@pragma("vm:never-inline")
+box() => const Box("We all have identical boxes");
+
+@pragma("vm:never-inline")
+enumm() => Enum.GREEN;
+
+@pragma("vm:never-inline")
+type() => Box;
+
+@pragma("vm:never-inline")
+closure() => commonClosure;
diff --git a/tests/language_2/deferred/split_constants_canonicalization_a_2.dart b/tests/language_2/deferred/split_constants_canonicalization_a_2.dart
new file mode 100644
index 0000000..ea054f5
--- /dev/null
+++ b/tests/language_2/deferred/split_constants_canonicalization_a_2.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, 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 "split_constants_canonicalization_test.dart";
+
+@pragma("vm:never-inline")
+mint() => 0x7FFFFFFFFFFFFFFF;
+
+@pragma("vm:never-inline")
+string() => "We all have identical strings";
+
+@pragma("vm:never-inline")
+list() => const <String>["We all have identical lists"];
+
+@pragma("vm:never-inline")
+map() => const <String, String>{"We all have": "identical maps"};
+
+@pragma("vm:never-inline")
+box() => const Box("We all have identical boxes");
+
+@pragma("vm:never-inline")
+enumm() => Enum.GREEN;
+
+@pragma("vm:never-inline")
+type() => Box;
+
+@pragma("vm:never-inline")
+closure() => commonClosure;
diff --git a/tests/language_2/deferred/split_constants_canonicalization_b.dart b/tests/language_2/deferred/split_constants_canonicalization_b.dart
new file mode 100644
index 0000000..f27900d
--- /dev/null
+++ b/tests/language_2/deferred/split_constants_canonicalization_b.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, 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 "split_constants_canonicalization_b_1.dart" deferred as b_1;
+import "split_constants_canonicalization_b_2.dart" deferred as b_2;
+
+loadChildren() async {
+  await b_1.loadLibrary();
+  await b_2.loadLibrary();
+}
+
+b_1_mint() => b_1.mint();
+b_1_string() => b_1.string();
+b_1_list() => b_1.list();
+b_1_map() => b_1.map();
+b_1_box() => b_1.box();
+b_1_enum() => b_1.enumm();
+b_1_type() => b_1.type();
+b_1_closure() => b_1.closure();
+
+b_2_mint() => b_2.mint();
+b_2_string() => b_2.string();
+b_2_list() => b_2.list();
+b_2_map() => b_2.map();
+b_2_box() => b_2.box();
+b_2_enum() => b_2.enumm();
+b_2_type() => b_2.type();
+b_2_closure() => b_2.closure();
diff --git a/tests/language_2/deferred/split_constants_canonicalization_b_1.dart b/tests/language_2/deferred/split_constants_canonicalization_b_1.dart
new file mode 100644
index 0000000..ea054f5
--- /dev/null
+++ b/tests/language_2/deferred/split_constants_canonicalization_b_1.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, 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 "split_constants_canonicalization_test.dart";
+
+@pragma("vm:never-inline")
+mint() => 0x7FFFFFFFFFFFFFFF;
+
+@pragma("vm:never-inline")
+string() => "We all have identical strings";
+
+@pragma("vm:never-inline")
+list() => const <String>["We all have identical lists"];
+
+@pragma("vm:never-inline")
+map() => const <String, String>{"We all have": "identical maps"};
+
+@pragma("vm:never-inline")
+box() => const Box("We all have identical boxes");
+
+@pragma("vm:never-inline")
+enumm() => Enum.GREEN;
+
+@pragma("vm:never-inline")
+type() => Box;
+
+@pragma("vm:never-inline")
+closure() => commonClosure;
diff --git a/tests/language_2/deferred/split_constants_canonicalization_b_2.dart b/tests/language_2/deferred/split_constants_canonicalization_b_2.dart
new file mode 100644
index 0000000..ea054f5
--- /dev/null
+++ b/tests/language_2/deferred/split_constants_canonicalization_b_2.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, 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 "split_constants_canonicalization_test.dart";
+
+@pragma("vm:never-inline")
+mint() => 0x7FFFFFFFFFFFFFFF;
+
+@pragma("vm:never-inline")
+string() => "We all have identical strings";
+
+@pragma("vm:never-inline")
+list() => const <String>["We all have identical lists"];
+
+@pragma("vm:never-inline")
+map() => const <String, String>{"We all have": "identical maps"};
+
+@pragma("vm:never-inline")
+box() => const Box("We all have identical boxes");
+
+@pragma("vm:never-inline")
+enumm() => Enum.GREEN;
+
+@pragma("vm:never-inline")
+type() => Box;
+
+@pragma("vm:never-inline")
+closure() => commonClosure;
diff --git a/tests/language_2/deferred/split_constants_canonicalization_test.dart b/tests/language_2/deferred/split_constants_canonicalization_test.dart
new file mode 100644
index 0000000..02d5c6e
--- /dev/null
+++ b/tests/language_2/deferred/split_constants_canonicalization_test.dart
@@ -0,0 +1,94 @@
+// Copyright (c) 2020, 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.
+
+// VMOptions=--use_bare_instructions=false
+// VMOptions=--use_bare_instructions=true
+
+import "package:expect/expect.dart";
+import "split_constants_canonicalization_a.dart" deferred as a;
+import "split_constants_canonicalization_b.dart" deferred as b;
+
+class Box {
+  final contents;
+  const Box(this.contents);
+}
+
+enum Enum {
+  RED,
+  GREEN,
+  BLUE,
+}
+
+commonClosure() {}
+
+main() async {
+  await a.loadLibrary();
+  await a.loadChildren();
+  await b.loadLibrary();
+  await b.loadChildren();
+
+  var a_1_mint = await a.a_1_mint();
+  var a_2_mint = await a.a_2_mint();
+  var b_1_mint = await b.b_1_mint();
+  var b_2_mint = await b.b_2_mint();
+  Expect.isTrue(identical(a_1_mint, a_2_mint));
+  Expect.isTrue(identical(a_1_mint, b_1_mint));
+  Expect.isTrue(identical(a_1_mint, b_2_mint));
+
+  var a_1_string = await a.a_1_string();
+  var a_2_string = await a.a_2_string();
+  var b_1_string = await b.b_1_string();
+  var b_2_string = await b.b_2_string();
+  Expect.isTrue(identical(a_1_string, a_2_string));
+  Expect.isTrue(identical(a_1_string, b_1_string));
+  Expect.isTrue(identical(a_1_string, b_2_string));
+
+  var a_1_list = await a.a_1_list();
+  var a_2_list = await a.a_2_list();
+  var b_1_list = await b.b_1_list();
+  var b_2_list = await b.b_2_list();
+  Expect.isTrue(identical(a_1_list, a_2_list));
+  Expect.isTrue(identical(a_1_list, b_1_list));
+  Expect.isTrue(identical(a_1_list, b_2_list));
+
+  var a_1_map = await a.a_1_map();
+  var a_2_map = await a.a_2_map();
+  var b_1_map = await b.b_1_map();
+  var b_2_map = await b.b_2_map();
+  Expect.isTrue(identical(a_1_map, a_2_map));
+  Expect.isTrue(identical(a_1_map, b_1_map));
+  Expect.isTrue(identical(a_1_map, b_2_map));
+
+  var a_1_box = await a.a_1_box();
+  var a_2_box = await a.a_2_box();
+  var b_1_box = await b.b_1_box();
+  var b_2_box = await b.b_2_box();
+  Expect.isTrue(identical(a_1_box, a_2_box));
+  Expect.isTrue(identical(a_1_box, b_1_box));
+  Expect.isTrue(identical(a_1_box, b_2_box));
+
+  var a_1_enum = await a.a_1_enum();
+  var a_2_enum = await a.a_2_enum();
+  var b_1_enum = await b.b_1_enum();
+  var b_2_enum = await b.b_2_enum();
+  Expect.isTrue(identical(a_1_enum, a_2_enum));
+  Expect.isTrue(identical(a_1_enum, b_1_enum));
+  Expect.isTrue(identical(a_1_enum, b_2_enum));
+
+  var a_1_type = await a.a_1_type();
+  var a_2_type = await a.a_2_type();
+  var b_1_type = await b.b_1_type();
+  var b_2_type = await b.b_2_type();
+  Expect.isTrue(identical(a_1_type, a_2_type));
+  Expect.isTrue(identical(a_1_type, b_1_type));
+  Expect.isTrue(identical(a_1_type, b_2_type));
+
+  var a_1_closure = await a.a_1_closure();
+  var a_2_closure = await a.a_2_closure();
+  var b_1_closure = await b.b_1_closure();
+  var b_2_closure = await b.b_2_closure();
+  Expect.isTrue(identical(a_1_closure, a_2_closure));
+  Expect.isTrue(identical(a_1_closure, b_1_closure));
+  Expect.isTrue(identical(a_1_closure, b_2_closure));
+}
diff --git a/tests/standalone/out_of_memory_recovery_synchronous_test.dart b/tests/standalone/out_of_memory_recovery_synchronous_test.dart
new file mode 100644
index 0000000..2157fa7
--- /dev/null
+++ b/tests/standalone/out_of_memory_recovery_synchronous_test.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2020, 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.
+
+// VMOptions=--old_gen_heap_size=20
+
+import "package:expect/expect.dart";
+
+main() {
+  var leak;
+  var exceptionThrown = false;
+  try {
+    leak = [];
+    while (true) {
+      leak = [leak];
+    }
+  } on OutOfMemoryError catch (exception) {
+    leak = null;
+    exceptionThrown = true;
+    print("Okay");
+  }
+  Expect.isTrue(exceptionThrown);
+
+  exceptionThrown = false;
+  try {
+    leak = [];
+    while (true) {
+      leak = [leak];
+    }
+  } on OutOfMemoryError catch (exception) {
+    leak = null;
+    exceptionThrown = true;
+    print("Okay");
+  }
+  Expect.isTrue(exceptionThrown);
+
+  exceptionThrown = false;
+  try {
+    leak = [];
+    while (true) {
+      leak = [leak];
+    }
+  } on OutOfMemoryError catch (exception) {
+    leak = null;
+    exceptionThrown = true;
+    print("Okay");
+  }
+  Expect.isTrue(exceptionThrown);
+}
diff --git a/tests/standalone/out_of_memory_recovery_test.dart b/tests/standalone/out_of_memory_recovery_test.dart
new file mode 100644
index 0000000..05aa4db
--- /dev/null
+++ b/tests/standalone/out_of_memory_recovery_test.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2020, 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.
+
+// VMOptions=--old_gen_heap_size=20
+
+import "dart:io";
+import "dart:isolate";
+
+import "package:expect/expect.dart";
+
+handleRequest(request) {
+  if (request % 2 == 0) {
+    var leak = [];
+    while (true) {
+      leak = [leak];
+    }
+  }
+  return "Okay";
+}
+
+handleMessage(message) {
+  print(">> $message");
+  var responsePort = message[0];
+  var request = message[1];
+  try {
+    responsePort.send(<dynamic>[request, handleRequest(request)]);
+  } catch (e, st) {
+    responsePort.send(<dynamic>[request, "Failed: $e\n$st"]);
+  }
+}
+
+main(args) async {
+  var child = new RawReceivePort(handleMessage);
+
+  var parent;
+  parent = new RawReceivePort((message) {
+    print("<< $message");
+    var request = message[0];
+    var response = message[1];
+    if (request % 2 == 0) {
+      Expect.isTrue(response.contains("Out of Memory"));
+    } else {
+      Expect.equals("Okay", response);
+    }
+    if (request == 5) {
+      child.close();
+      parent.close();
+    } else {
+      child.sendPort.send(<dynamic>[parent.sendPort, request + 1]);
+    }
+  });
+
+  child.sendPort.send(<dynamic>[parent.sendPort, 1]);
+}
diff --git a/tests/standalone/standalone_kernel.status b/tests/standalone/standalone_kernel.status
index 1da02b7..63813b2 100644
--- a/tests/standalone/standalone_kernel.status
+++ b/tests/standalone/standalone_kernel.status
@@ -6,7 +6,6 @@
 
 fragmentation_typed_data_test: Pass, Slow # GC heavy
 io/process_sync_test: Pass, Slow # Spawns synchronously subprocesses in sequence.
-out_of_memory_unhandled_exception_test: Pass, Slow
 
 [ $system == android ]
 entrypoints_verification_test: Skip # Requires shared objects which the test script doesn't "adb push".
diff --git a/tests/standalone_2/out_of_memory_recovery_synchronous_test.dart b/tests/standalone_2/out_of_memory_recovery_synchronous_test.dart
new file mode 100644
index 0000000..2157fa7
--- /dev/null
+++ b/tests/standalone_2/out_of_memory_recovery_synchronous_test.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2020, 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.
+
+// VMOptions=--old_gen_heap_size=20
+
+import "package:expect/expect.dart";
+
+main() {
+  var leak;
+  var exceptionThrown = false;
+  try {
+    leak = [];
+    while (true) {
+      leak = [leak];
+    }
+  } on OutOfMemoryError catch (exception) {
+    leak = null;
+    exceptionThrown = true;
+    print("Okay");
+  }
+  Expect.isTrue(exceptionThrown);
+
+  exceptionThrown = false;
+  try {
+    leak = [];
+    while (true) {
+      leak = [leak];
+    }
+  } on OutOfMemoryError catch (exception) {
+    leak = null;
+    exceptionThrown = true;
+    print("Okay");
+  }
+  Expect.isTrue(exceptionThrown);
+
+  exceptionThrown = false;
+  try {
+    leak = [];
+    while (true) {
+      leak = [leak];
+    }
+  } on OutOfMemoryError catch (exception) {
+    leak = null;
+    exceptionThrown = true;
+    print("Okay");
+  }
+  Expect.isTrue(exceptionThrown);
+}
diff --git a/tests/standalone_2/out_of_memory_recovery_test.dart b/tests/standalone_2/out_of_memory_recovery_test.dart
new file mode 100644
index 0000000..05aa4db
--- /dev/null
+++ b/tests/standalone_2/out_of_memory_recovery_test.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2020, 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.
+
+// VMOptions=--old_gen_heap_size=20
+
+import "dart:io";
+import "dart:isolate";
+
+import "package:expect/expect.dart";
+
+handleRequest(request) {
+  if (request % 2 == 0) {
+    var leak = [];
+    while (true) {
+      leak = [leak];
+    }
+  }
+  return "Okay";
+}
+
+handleMessage(message) {
+  print(">> $message");
+  var responsePort = message[0];
+  var request = message[1];
+  try {
+    responsePort.send(<dynamic>[request, handleRequest(request)]);
+  } catch (e, st) {
+    responsePort.send(<dynamic>[request, "Failed: $e\n$st"]);
+  }
+}
+
+main(args) async {
+  var child = new RawReceivePort(handleMessage);
+
+  var parent;
+  parent = new RawReceivePort((message) {
+    print("<< $message");
+    var request = message[0];
+    var response = message[1];
+    if (request % 2 == 0) {
+      Expect.isTrue(response.contains("Out of Memory"));
+    } else {
+      Expect.equals("Okay", response);
+    }
+    if (request == 5) {
+      child.close();
+      parent.close();
+    } else {
+      child.sendPort.send(<dynamic>[parent.sendPort, request + 1]);
+    }
+  });
+
+  child.sendPort.send(<dynamic>[parent.sendPort, 1]);
+}
diff --git a/tests/standalone_2/standalone_2_kernel.status b/tests/standalone_2/standalone_2_kernel.status
index 939b834..52e78e2 100644
--- a/tests/standalone_2/standalone_2_kernel.status
+++ b/tests/standalone_2/standalone_2_kernel.status
@@ -7,7 +7,6 @@
 fragmentation_test: Pass, Slow # GC heavy
 fragmentation_typed_data_test: Pass, Slow # GC heavy
 io/process_sync_test: Pass, Slow # Spawns synchronously subprocesses in sequence.
-out_of_memory_unhandled_exception_test: Pass, Slow
 
 [ $system == android ]
 entrypoints_verification_test: Skip # Requires shared objects which the test script doesn't "adb push".
diff --git a/tools/VERSION b/tools/VERSION
index 2ba0f39..dcc999f 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 6
+PRERELEASE 7
 PRERELEASE_PATCH 0
\ No newline at end of file