Version 2.15.0-258.0.dev

Merge commit 'c46baff9c9a7d10b55d9039f0c75c11ecc41c32a' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 9c19e78..981de4b 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
     "constraint, update this by running tools/generate_package_config.dart."
   ],
   "configVersion": 2,
-  "generated": "2021-10-15T13:46:59.969883",
+  "generated": "2021-10-26T10:20:01.277340",
   "generator": "tools/generate_package_config.dart",
   "packages": [
     {
@@ -515,7 +515,7 @@
     {
       "name": "package_deps",
       "rootUri": "../tools/package_deps",
-      "languageVersion": "2.8"
+      "languageVersion": "2.12"
     },
     {
       "name": "path",
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index e798ddb..cfb6da5 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -15,6 +15,7 @@
 import 'package:analysis_server/src/provisional/completion/completion_core.dart';
 import 'package:analysis_server/src/services/completion/completion_performance.dart';
 import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
+import 'package:analysis_server/src/services/completion/dart/fuzzy_filter_sort.dart';
 import 'package:analysis_server/src/services/completion/yaml/analysis_options_generator.dart';
 import 'package:analysis_server/src/services/completion/yaml/fix_data_generator.dart';
 import 'package:analysis_server/src/services/completion/yaml/pubspec_generator.dart';
@@ -64,13 +65,13 @@
   /// automatically called when a client listens to the stream returned by
   /// [results]. Subclasses should override this method, append at least one
   /// result to the [controller], and close the controller stream once complete.
-  Future<List<CompletionSuggestion>> computeSuggestions(
-    OperationPerformanceImpl performance,
-    DartCompletionRequest request,
+  Future<List<CompletionSuggestion>> computeSuggestions({
+    required OperationPerformanceImpl performance,
+    required DartCompletionRequest request,
     Set<ElementKind>? includedElementKinds,
     Set<String>? includedElementNames,
     List<IncludedSuggestionRelevanceTag>? includedSuggestionRelevanceTags,
-  ) async {
+  }) async {
     //
     // Allow plugins to start computing fixes.
     //
@@ -204,6 +205,84 @@
     );
   }
 
+  /// Implement the 'completion.getSuggestions2' request.
+  void getSuggestions2(Request request) async {
+    var params = CompletionGetSuggestions2Params.fromRequest(request);
+    var file = params.file;
+    var offset = params.offset;
+
+    var provider = server.resourceProvider;
+    var pathContext = provider.pathContext;
+
+    // TODO(scheglov) Support non-Dart files.
+    if (!file_paths.isDart(pathContext, file)) {
+      server.sendResponse(
+        CompletionGetSuggestions2Result(offset, 0, [], [], false)
+            .toResponse(request.id),
+      );
+      return;
+    }
+
+    var resolvedUnit = await server.getResolvedUnit(file);
+    if (resolvedUnit == null) {
+      server.sendResponse(Response.fileNotAnalyzed(request, file));
+      return;
+    }
+
+    server.requestStatistics?.addItemTimeNow(request, 'resolvedUnit');
+
+    if (offset < 0 || offset > resolvedUnit.content.length) {
+      server.sendResponse(Response.invalidParameter(
+          request,
+          'params.offset',
+          'Expected offset between 0 and source length inclusive,'
+              ' but found $offset'));
+      return;
+    }
+
+    final completionPerformance = CompletionPerformance();
+    recordRequest(completionPerformance, file, resolvedUnit.content, offset);
+
+    await completionPerformance.runRequestOperation((performance) async {
+      var completionRequest = DartCompletionRequest.from(
+        resolvedUnit: resolvedUnit,
+        offset: offset,
+        dartdocDirectiveInfo: server.getDartdocDirectiveInfoFor(
+          resolvedUnit,
+        ),
+        documentationCache: server.getDocumentationCacheFor(resolvedUnit),
+      );
+
+      var suggestions = await computeSuggestions(
+        performance: performance,
+        request: completionRequest,
+      );
+
+      performance.run('filter', (performance) {
+        performance.getDataInt('count').add(suggestions.length);
+        suggestions = fuzzyFilterSort(
+          pattern: completionRequest.targetPrefix,
+          suggestions: suggestions,
+        );
+        performance.getDataInt('matchCount').add(suggestions.length);
+      });
+
+      var lengthRestricted = suggestions.take(params.maxResults).toList();
+      var isIncomplete = lengthRestricted.length < suggestions.length;
+      completionPerformance.suggestionCount = lengthRestricted.length;
+
+      server.sendResponse(
+        CompletionGetSuggestions2Result(
+          completionRequest.replacementOffset,
+          completionRequest.replacementLength,
+          lengthRestricted,
+          [], // TODO(scheglov)
+          isIncomplete,
+        ).toResponse(request.id),
+      );
+    });
+  }
+
   @override
   Response? handleRequest(Request request) {
     if (!server.options.featureSet.completion) {
@@ -223,6 +302,9 @@
       } else if (requestName == COMPLETION_REQUEST_GET_SUGGESTIONS) {
         processRequest(request);
         return Response.DELAYED_RESPONSE;
+      } else if (requestName == COMPLETION_REQUEST_GET_SUGGESTIONS2) {
+        getSuggestions2(request);
+        return Response.DELAYED_RESPONSE;
       } else if (requestName == COMPLETION_REQUEST_SET_SUBSCRIPTIONS) {
         return setSubscriptions(request);
       }
@@ -341,11 +423,11 @@
       // Compute suggestions in the background
       try {
         var suggestions = await computeSuggestions(
-          perf,
-          completionRequest,
-          includedElementKinds,
-          includedElementNames,
-          includedSuggestionRelevanceTags,
+          performance: perf,
+          request: completionRequest,
+          includedElementKinds: includedElementKinds,
+          includedElementNames: includedElementNames,
+          includedSuggestionRelevanceTags: includedSuggestionRelevanceTags,
         );
         String? libraryFile;
         var includedSuggestionSets = <IncludedSuggestionSet>[];
diff --git a/pkg/analysis_server/lib/src/lsp/source_edits.dart b/pkg/analysis_server/lib/src/lsp/source_edits.dart
index e8b0baf..2a5834e 100644
--- a/pkg/analysis_server/lib/src/lsp/source_edits.dart
+++ b/pkg/analysis_server/lib/src/lsp/source_edits.dart
@@ -167,7 +167,48 @@
     int formattedStart,
     int formattedEnd,
   ) {
+    final unformattedWhitespace =
+        unformatted.substring(unformattedStart, unformattedEnd);
+    final formattedWhitespace =
+        formatted.substring(formattedStart, formattedEnd);
+
     if (rangeStart != null && rangeEnd != null) {
+      // If this change crosses over the start of the requested range, discarding
+      // the change may result in leading whitespace of the next line not being
+      // formatted correctly.
+      //
+      // To handle this, if both unformatted/formatted contain at least one
+      // newline, split this change into two around the last newline so that the
+      // final part (likely leading whitespace) can be included without
+      // including the whole change.
+      //
+      // Without this, functionality like VS Code's "format modified lines"
+      // (which uses Git status to know which lines are edited) may appear to
+      // fail to format the first newly added line in a range.
+      if (unformattedStart < rangeStart.result &&
+          unformattedEnd > rangeStart.result &&
+          unformattedWhitespace.contains('\n') &&
+          formattedWhitespace.contains('\n')) {
+        // Find the offsets of the character after the last newlines.
+        final unformattedOffset = unformattedWhitespace.lastIndexOf('\n') + 1;
+        final formattedOffset = formattedWhitespace.lastIndexOf('\n') + 1;
+        // Call us again for the leading part
+        addEditFor(
+          unformattedStart,
+          unformattedStart + unformattedOffset,
+          formattedStart,
+          formattedStart + formattedOffset,
+        );
+        // Call us again for the trailing part
+        addEditFor(
+          unformattedStart + unformattedOffset,
+          unformattedEnd,
+          formattedStart + formattedOffset,
+          formattedEnd,
+        );
+        return;
+      }
+
       // If we're formatting only a range, skip over any segments that don't fall
       // entirely within that range.
       if (unformattedStart < rangeStart.result ||
@@ -176,11 +217,6 @@
       }
     }
 
-    final unformattedWhitespace =
-        unformatted.substring(unformattedStart, unformattedEnd);
-    final formattedWhitespace =
-        formatted.substring(formattedStart, formattedEnd);
-
     if (unformattedWhitespace == formattedWhitespace) {
       return;
     }
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_expression_function_body.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_expression_function_body.dart
index ef03f62..c66c747 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_expression_function_body.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_expression_function_body.dart
@@ -16,6 +16,12 @@
   AssistKind get assistKind => DartAssistKind.CONVERT_INTO_EXPRESSION_BODY;
 
   @override
+  bool get canBeAppliedInBulk => true;
+
+  @override
+  bool get canBeAppliedToFile => true;
+
+  @override
   FixKind get fixKind => DartFixKind.CONVERT_INTO_EXPRESSION_BODY;
 
   @override
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_deprecated_new_in_comment_reference.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_deprecated_new_in_comment_reference.dart
new file mode 100644
index 0000000..9c902e7
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_deprecated_new_in_comment_reference.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class RemoveDeprecatedNewInCommentReference extends CorrectionProducer {
+  @override
+  bool get canBeAppliedInBulk => true;
+
+  @override
+  bool get canBeAppliedToFile => true;
+
+  @override
+  FixKind get fixKind => DartFixKind.REMOVE_DEPRECATED_NEW_IN_COMMENT_REFERENCE;
+
+  @override
+  FixKind get multiFixKind =>
+      DartFixKind.REMOVE_DEPRECATED_NEW_IN_COMMENT_REFERENCE_MULTI;
+
+  @override
+  Future<void> compute(ChangeBuilder builder) async {
+    final comment = node;
+    if (comment is! CommentReference) {
+      return;
+    }
+
+    final newToken = comment.newKeyword;
+    if (newToken == null) {
+      return;
+    }
+
+    await builder.addDartFileEdit(file, (builder) {
+      builder.addDeletion(range.startStart(newToken, newToken.next!));
+    });
+
+    final identifier = comment.identifier;
+    final classElement = identifier.staticElement;
+    if (identifier is SimpleIdentifier && classElement is ConstructorElement) {
+      await builder.addDartFileEdit(file, (builder) {
+        builder.addSimpleInsertion(identifier.end, '.new');
+      });
+    } else {
+      if (classElement is ClassElement) {
+        if (classElement.unnamedConstructor != null) {
+          await builder.addDartFileEdit(file, (builder) {
+            builder.addSimpleInsertion(identifier.end, '.new');
+          });
+        }
+      }
+    }
+  }
+
+  /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
+  static RemoveDeprecatedNewInCommentReference newInstance() =>
+      RemoveDeprecatedNewInCommentReference();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/replace_new_with_const.dart b/pkg/analysis_server/lib/src/services/correction/dart/replace_new_with_const.dart
index 2828c1c..5e4b9b6 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/replace_new_with_const.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/replace_new_with_const.dart
@@ -11,13 +11,10 @@
 
 class ReplaceNewWithConst extends CorrectionProducer {
   @override
-  // TODO(brianwilkerson) This fix can produce changes that are inconsistent
-  //  with the `unnecessary_const` lint. Fix it and then enable it for both
-  //  uses.
-  bool get canBeAppliedInBulk => false;
+  bool get canBeAppliedInBulk => true;
 
   @override
-  bool get canBeAppliedToFile => false;
+  bool get canBeAppliedToFile => true;
 
   @override
   FixKind get fixKind => DartFixKind.REPLACE_NEW_WITH_CONST;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/replace_with_interpolation.dart b/pkg/analysis_server/lib/src/services/correction/dart/replace_with_interpolation.dart
index 48ddf53..71adedb 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/replace_with_interpolation.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/replace_with_interpolation.dart
@@ -13,9 +13,18 @@
 
 class ReplaceWithInterpolation extends CorrectionProducer {
   @override
+  bool get canBeAppliedInBulk => true;
+
+  @override
+  bool get canBeAppliedToFile => true;
+
+  @override
   FixKind get fixKind => DartFixKind.REPLACE_WITH_INTERPOLATION;
 
   @override
+  FixKind? get multiFixKind => DartFixKind.REPLACE_WITH_INTERPOLATION_MULTI;
+
+  @override
   Future<void> compute(ChangeBuilder builder) async {
     //
     // Validate the fix.
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index e344b12..84a648c 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -825,6 +825,16 @@
     DartFixKindPriority.DEFAULT,
     'Remove dead code',
   );
+  static const REMOVE_DEPRECATED_NEW_IN_COMMENT_REFERENCE = FixKind(
+    'dart.fix.remove.deprecatedNewInCommentReference',
+    DartFixKindPriority.DEFAULT,
+    'Remove deprecated new keyword',
+  );
+  static const REMOVE_DEPRECATED_NEW_IN_COMMENT_REFERENCE_MULTI = FixKind(
+    'dart.fix.remove.deprecatedNewInCommentReference.multi',
+    DartFixKindPriority.IN_FILE,
+    'Remove deprecated new keyword in file',
+  );
   static const REMOVE_DUPLICATE_CASE = FixKind(
     'dart.fix.remove.duplicateCase',
     DartFixKindPriority.DEFAULT,
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 04f4725..67f769b 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -105,6 +105,7 @@
 import 'package:analysis_server/src/services/correction/dart/remove_constructor_name.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_dead_code.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_dead_if_null.dart';
+import 'package:analysis_server/src/services/correction/dart/remove_deprecated_new_in_comment_reference.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_duplicate_case.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_empty_catch.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_empty_constructor_body.dart';
@@ -474,7 +475,6 @@
       ReplaceColonWithEquals.newInstance,
     ],
     LintNames.prefer_expression_function_bodies: [
-      // TODO(brianwilkerson) Consider applying in bulk.
       ConvertToExpressionFunctionBody.newInstance,
     ],
     LintNames.prefer_final_fields: [
@@ -518,7 +518,6 @@
       ConvertToIntLiteral.newInstance,
     ],
     LintNames.prefer_interpolation_to_compose_strings: [
-      // TODO(brianwilkerson) Consider applying in bulk.
       ReplaceWithInterpolation.newInstance,
     ],
     LintNames.prefer_is_not_operator: [
@@ -1097,6 +1096,9 @@
       //  a place where it can be reached (when possible).
       RemoveDeadCode.newInstance,
     ],
+    HintCode.DEPRECATED_NEW_IN_COMMENT_REFERENCE: [
+      RemoveDeprecatedNewInCommentReference.newInstance,
+    ],
     HintCode.DIVISION_OPTIMIZATION: [
       UseEffectiveIntegerDivision.newInstance,
     ],
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index d9c733a..b0adedf 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -2,12 +2,19 @@
 // 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/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/domain_analysis.dart';
+import 'package:analysis_server/src/domain_completion.dart';
 import 'package:analysis_server/src/plugin/plugin_manager.dart';
+import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
+import 'package:analysis_server/src/server/crash_reporting_attachments.dart';
+import 'package:analysis_server/src/utilities/mocks.dart';
 import 'package:analyzer/instrumentation/service.dart';
+import 'package:analyzer/src/generated/sdk.dart';
+import 'package:analyzer/src/test_utilities/mock_sdk.dart';
+import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
 import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
-import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -18,11 +25,496 @@
 
 void main() {
   defineReflectiveSuite(() {
+    defineReflectiveTests(CompletionDomainHandlerGetSuggestions2Test);
     defineReflectiveTests(CompletionDomainHandlerGetSuggestionsTest);
   });
 }
 
 @reflectiveTest
+class CompletionDomainHandlerGetSuggestions2Test with ResourceProviderMixin {
+  late final MockServerChannel serverChannel;
+  late final AnalysisServer server;
+
+  AnalysisDomainHandler get analysisDomain {
+    return server.handlers.whereType<AnalysisDomainHandler>().single;
+  }
+
+  CompletionDomainHandler get completionDomain {
+    return server.handlers.whereType<CompletionDomainHandler>().single;
+  }
+
+  String get testFilePath => '$testPackageLibPath/test.dart';
+
+  String get testPackageLibPath => '$testPackageRootPath/lib';
+
+  String get testPackageRootPath => '$workspaceRootPath/test';
+
+  String get workspaceRootPath => '/home';
+
+  Future<void> setRoots({
+    required List<String> included,
+    required List<String> excluded,
+  }) async {
+    var includedConverted = included.map(convertPath).toList();
+    var excludedConverted = excluded.map(convertPath).toList();
+    await _handleSuccessfulRequest(
+      AnalysisSetAnalysisRootsParams(
+        includedConverted,
+        excludedConverted,
+        packageRoots: {},
+      ).toRequest('0'),
+    );
+  }
+
+  void setUp() {
+    serverChannel = MockServerChannel();
+
+    var sdkRoot = newFolder('/sdk');
+    createMockSdk(
+      resourceProvider: resourceProvider,
+      root: sdkRoot,
+    );
+
+    server = AnalysisServer(
+      serverChannel,
+      resourceProvider,
+      AnalysisServerOptions(),
+      DartSdkManager(sdkRoot.path),
+      CrashReportingAttachmentsBuilder.empty,
+      InstrumentationService.NULL_SERVICE,
+    );
+  }
+
+  Future<void> test_numResults_class_methods() async {
+    await _configureWithWorkspaceRoot();
+
+    var responseValidator = await _getTestCodeSuggestions('''
+class A {
+  void foo01() {}
+  void foo02() {}
+  void foo03() {}
+}
+
+void f(A a) {
+  a.foo0^
+}
+''', maxResults: 2);
+
+    responseValidator
+      ..assertIncomplete()
+      ..assertReplacementBack(4);
+
+    var suggestionsValidator = responseValidator.suggestions;
+    suggestionsValidator.assertCompletions(['foo01', 'foo02']);
+  }
+
+  Future<void> test_numResults_topLevelVariables() async {
+    await _configureWithWorkspaceRoot();
+
+    var responseValidator = await _getTestCodeSuggestions('''
+var foo01 = 0;
+var foo02 = 0;
+var foo03 = 0;
+
+void f() {
+  foo0^
+}
+''', maxResults: 2);
+
+    responseValidator
+      ..assertIncomplete()
+      ..assertReplacementBack(4);
+
+    var suggestionsValidator = responseValidator.suggestions;
+    suggestionsValidator.assertCompletions(['foo01', 'foo02']);
+    suggestionsValidator
+        .withCompletion('foo01')
+        .assertSingle()
+        .assertTopLevelVariable();
+    suggestionsValidator
+        .withCompletion('foo02')
+        .assertSingle()
+        .assertTopLevelVariable();
+  }
+
+  Future<void> test_numResults_topLevelVariables_imported_withPrefix() async {
+    await _configureWithWorkspaceRoot();
+
+    newFile('$testPackageLibPath/a.dart', content: '''
+var foo01 = 0;
+var foo02 = 0;
+var foo03 = 0;
+''');
+
+    var responseValidator = await _getTestCodeSuggestions('''
+import 'a.dart' as prefix;
+
+void f() {
+  prefix.^
+}
+''', maxResults: 2);
+
+    responseValidator
+      ..assertIncomplete()
+      ..assertEmptyReplacement();
+
+    var suggestionsValidator = responseValidator.suggestions;
+    suggestionsValidator.assertCompletions(['foo01', 'foo02']);
+  }
+
+  Future<void> test_prefixed_class_constructors() async {
+    await _configureWithWorkspaceRoot();
+
+    var responseValidator = await _getTestCodeSuggestions('''
+class A {
+  A.foo01();
+  A.foo02();
+}
+
+void f() {
+  A.foo0^
+}
+''');
+
+    responseValidator
+      ..assertComplete()
+      ..assertReplacementBack(4);
+
+    var suggestions = responseValidator.suggestions;
+    suggestions.assertCompletions(['foo01', 'foo02']);
+    suggestions.withCompletion('foo01').assertSingle().assertConstructor();
+    suggestions.withCompletion('foo02').assertSingle().assertConstructor();
+  }
+
+  Future<void> test_prefixed_class_getters() async {
+    await _configureWithWorkspaceRoot();
+
+    var responseValidator = await _getTestCodeSuggestions('''
+class A {
+  int get foo01 => 0;
+  int get foo02 => 0;
+}
+
+void f(A a) {
+  a.foo0^
+}
+''');
+
+    responseValidator
+      ..assertComplete()
+      ..assertReplacementBack(4);
+
+    var suggestions = responseValidator.suggestions;
+    suggestions.assertCompletions(['foo01', 'foo02']);
+    suggestions.withCompletion('foo01').assertSingle().assertGetter();
+    suggestions.withCompletion('foo02').assertSingle().assertGetter();
+  }
+
+  Future<void> test_prefixed_class_methods_instance() async {
+    await _configureWithWorkspaceRoot();
+
+    var responseValidator = await _getTestCodeSuggestions('''
+class A {
+  void foo01() {}
+  void foo02() {}
+}
+
+void f(A a) {
+  a.foo0^
+}
+''');
+
+    responseValidator
+      ..assertComplete()
+      ..assertReplacementBack(4);
+
+    var suggestions = responseValidator.suggestions;
+    suggestions.assertCompletions(['foo01', 'foo02']);
+    suggestions.withCompletion('foo01').assertSingle().assertMethod();
+    suggestions.withCompletion('foo02').assertSingle().assertMethod();
+  }
+
+  Future<void> test_prefixed_class_methods_static() async {
+    await _configureWithWorkspaceRoot();
+
+    var responseValidator = await _getTestCodeSuggestions('''
+class A {
+  static void foo01() {}
+  static void foo02() {}
+}
+
+void f() {
+  A.foo0^
+}
+''');
+
+    responseValidator
+      ..assertComplete()
+      ..assertReplacementBack(4);
+
+    var suggestions = responseValidator.suggestions;
+    suggestions.assertCompletions(['foo01', 'foo02']);
+    suggestions.withCompletion('foo01').assertSingle().assertMethod();
+    suggestions.withCompletion('foo02').assertSingle().assertMethod();
+  }
+
+  Future<void> test_prefixed_extensionGetters() async {
+    await _configureWithWorkspaceRoot();
+
+    var responseValidator = await _getTestCodeSuggestions(r'''
+extension E1 on int {
+  int get foo01 => 0;
+  int get foo02 => 0;
+  int get bar => 0;
+}
+
+extension E2 on double {
+  int get foo03 => 0;
+}
+
+void f() {
+  0.foo0^
+}
+''');
+
+    responseValidator
+      ..assertComplete()
+      ..assertReplacementBack(4);
+
+    var suggestionsValidator = responseValidator.suggestions;
+    suggestionsValidator.assertCompletions(['foo01', 'foo02']);
+
+    suggestionsValidator.withCompletion('foo01').assertSingle().assertGetter();
+    suggestionsValidator.withCompletion('foo02').assertSingle().assertGetter();
+  }
+
+  /// TODO(scheglov) Also not imported, with type checks.
+  Future<void> test_prefixed_extensionGetters_imported() async {
+    await _configureWithWorkspaceRoot();
+
+    newFile('$testPackageLibPath/a.dart', content: '''
+extension E1 on int {
+  int get foo01 => 0;
+  int get foo02 => 0;
+  int get bar => 0;
+}
+
+extension E2 on double {
+  int get foo03 => 0;
+}
+''');
+
+    var responseValidator = await _getTestCodeSuggestions(r'''
+import 'a.dart';
+
+void f() {
+  0.foo0^
+}
+''');
+
+    responseValidator
+      ..assertComplete()
+      ..assertReplacementBack(4);
+
+    var suggestionsValidator = responseValidator.suggestions;
+    suggestionsValidator.assertCompletions(['foo01', 'foo02']);
+
+    suggestionsValidator.withCompletion('foo01').assertSingle().assertGetter();
+    suggestionsValidator.withCompletion('foo02').assertSingle().assertGetter();
+  }
+
+  Future<void> test_unprefixed_filters() async {
+    await _configureWithWorkspaceRoot();
+
+    var responseValidator = await _getTestCodeSuggestions(r'''
+var foo01 = 0;
+var foo02 = 0;
+var bar01 = 0;
+var bar02 = 0;
+
+void f() {
+  foo0^
+}
+''');
+
+    responseValidator
+      ..assertComplete()
+      ..assertReplacementBack(4);
+
+    var suggestionsValidator = responseValidator.suggestions;
+    suggestionsValidator.assertCompletions(['foo01', 'foo02']);
+
+    suggestionsValidator
+        .withCompletion('foo01')
+        .assertSingle()
+        .assertTopLevelVariable();
+    suggestionsValidator
+        .withCompletion('foo02')
+        .assertSingle()
+        .assertTopLevelVariable();
+    suggestionsValidator.withCompletion('bar01').assertEmpty();
+    suggestionsValidator.withCompletion('bar02').assertEmpty();
+  }
+
+  Future<void> test_unprefixed_imported_class() async {
+    await _configureWithWorkspaceRoot();
+
+    newFile('$testPackageLibPath/a.dart', content: '''
+class A01 {}
+''');
+
+    newFile('$testPackageLibPath/b.dart', content: '''
+class A02 {}
+''');
+
+    var responseValidator = await _getTestCodeSuggestions('''
+import 'a.dart';
+import 'b.dart';
+
+void f() {
+  A0^
+}
+''');
+
+    responseValidator
+      ..assertComplete()
+      ..assertReplacementBack(2);
+
+    var classes = responseValidator.suggestions.withElementClass();
+    classes.assertCompletions(['A01', 'A02']);
+    classes.withCompletion('A01').assertSingle().assertClass();
+    classes.withCompletion('A02').assertSingle().assertClass();
+  }
+
+  Future<void> test_unprefixed_imported_topLevelVariable() async {
+    await _configureWithWorkspaceRoot();
+
+    newFile('$testPackageLibPath/a.dart', content: '''
+var foo01 = 0;
+''');
+
+    newFile('$testPackageLibPath/b.dart', content: '''
+var foo02 = 0;
+''');
+
+    var responseValidator = await _getTestCodeSuggestions('''
+import 'a.dart';
+import 'b.dart';
+
+void f() {
+  foo0^
+}
+''');
+
+    responseValidator
+      ..assertComplete()
+      ..assertReplacementBack(4);
+
+    var suggestionsValidator = responseValidator.suggestions;
+    suggestionsValidator.assertCompletions(['foo01', 'foo02']);
+    suggestionsValidator
+        .withCompletion('foo01')
+        .assertSingle()
+        .assertTopLevelVariable();
+    suggestionsValidator
+        .withCompletion('foo02')
+        .assertSingle()
+        .assertTopLevelVariable();
+  }
+
+  Future<void> test_unprefixed_sorts_byScore() async {
+    await _configureWithWorkspaceRoot();
+
+    var responseValidator = await _getTestCodeSuggestions(r'''
+var fooAB = 0;
+var fooBB = 0;
+
+void f() {
+  fooB^
+}
+''');
+
+    responseValidator
+      ..assertComplete()
+      ..assertReplacementBack(4);
+
+    var suggestionsValidator = responseValidator.suggestions;
+    // `fooBB` has better score than `fooAB` - prefix match
+    suggestionsValidator.assertCompletions(['fooBB', 'fooAB']);
+  }
+
+  Future<void> test_unprefixed_sorts_byType() async {
+    await _configureWithWorkspaceRoot();
+
+    var responseValidator = await _getTestCodeSuggestions(r'''
+var foo01 = 0.0;
+var foo02 = 0;
+
+void f() {
+  int v = foo0^
+}
+''');
+
+    responseValidator
+      ..assertComplete()
+      ..assertReplacementBack(4);
+
+    var suggestionsValidator = responseValidator.suggestions;
+    // `foo02` has better relevance, its type matches the context type
+    suggestionsValidator.assertCompletions(['foo02', 'foo01']);
+  }
+
+  Future<void> _configureWithWorkspaceRoot() async {
+    await setRoots(included: [workspaceRootPath], excluded: []);
+    await server.onAnalysisComplete;
+  }
+
+  Future<CompletionGetSuggestions2ResponseValidator> _getSuggestions({
+    required String path,
+    required int completionOffset,
+    required int maxResults,
+  }) async {
+    var request = CompletionGetSuggestions2Params(
+      path,
+      completionOffset,
+      maxResults,
+    ).toRequest('0');
+
+    var response = await _handleSuccessfulRequest(request);
+    var result = CompletionGetSuggestions2Result.fromResponse(response);
+    return CompletionGetSuggestions2ResponseValidator(completionOffset, result);
+  }
+
+  Future<CompletionGetSuggestions2ResponseValidator> _getTestCodeSuggestions(
+    String content, {
+    int maxResults = 1 << 10,
+  }) async {
+    var completionOffset = content.indexOf('^');
+    expect(completionOffset, isNot(equals(-1)), reason: 'missing ^');
+
+    var nextOffset = content.indexOf('^', completionOffset + 1);
+    expect(nextOffset, equals(-1), reason: 'too many ^');
+
+    var testFile = newFile(testFilePath,
+        content: content.substring(0, completionOffset) +
+            content.substring(completionOffset + 1));
+
+    return await _getSuggestions(
+      path: testFile.path,
+      completionOffset: completionOffset,
+      maxResults: maxResults,
+    );
+  }
+
+  /// Validates that the given [request] is handled successfully.
+  Future<Response> _handleSuccessfulRequest(Request request) async {
+    var response = await serverChannel.sendRequest(request);
+    expect(response, isResponseSuccess(request.id));
+    return response;
+  }
+}
+
+@reflectiveTest
 class CompletionDomainHandlerGetSuggestionsTest
     extends AbstractCompletionDomainTest {
   Future<void> test_ArgumentList_constructor_named_fieldFormalParam() async {
@@ -910,3 +1402,123 @@
     });
   }
 }
+
+class CompletionGetSuggestions2ResponseValidator {
+  final int completionOffset;
+  final CompletionGetSuggestions2Result result;
+
+  CompletionGetSuggestions2ResponseValidator(
+    this.completionOffset,
+    this.result,
+  );
+
+  SuggestionsValidator get suggestions {
+    return SuggestionsValidator(result.suggestions);
+  }
+
+  void assertComplete() {
+    expect(result.isIncomplete, isFalse);
+  }
+
+  void assertEmptyReplacement() {
+    assertReplacement(completionOffset, 0);
+  }
+
+  void assertIncomplete() {
+    expect(result.isIncomplete, isTrue);
+  }
+
+  void assertReplacement(int offset, int length) {
+    expect(result.replacementOffset, offset);
+    expect(result.replacementLength, length);
+  }
+
+  void assertReplacementBack(int length) {
+    assertReplacement(completionOffset - length, length);
+  }
+}
+
+class SingleSuggestionValidator {
+  final CompletionSuggestion suggestion;
+
+  SingleSuggestionValidator(this.suggestion);
+
+  void assertClass() {
+    expect(suggestion.kind, CompletionSuggestionKind.INVOCATION);
+    expect(suggestion.element?.kind, ElementKind.CLASS);
+  }
+
+  void assertConstructor() {
+    expect(suggestion.kind, CompletionSuggestionKind.INVOCATION);
+    expect(suggestion.element?.kind, ElementKind.CONSTRUCTOR);
+  }
+
+  void assertGetter() {
+    expect(suggestion.kind, CompletionSuggestionKind.INVOCATION);
+    expect(suggestion.element?.kind, ElementKind.GETTER);
+  }
+
+  void assertMethod() {
+    expect(suggestion.kind, CompletionSuggestionKind.INVOCATION);
+    expect(suggestion.element?.kind, ElementKind.METHOD);
+  }
+
+  void assertTopLevelVariable() {
+    expect(suggestion.kind, CompletionSuggestionKind.INVOCATION);
+    expect(suggestion.element?.kind, ElementKind.TOP_LEVEL_VARIABLE);
+  }
+}
+
+class SuggestionsValidator {
+  final List<CompletionSuggestion> suggestions;
+
+  SuggestionsValidator(this.suggestions);
+
+  int get length => suggestions.length;
+
+  /// Assert that this has suggestions with exactly the given completions,
+  /// with the exact order.
+  ///
+  /// Does not check suggestion kinds, elements, etc.
+  void assertCompletions(Iterable<String> completions) {
+    var actual = suggestions.map((e) => e.completion).toList();
+    expect(actual, completions);
+  }
+
+  void assertEmpty() {
+    expect(suggestions, isEmpty);
+  }
+
+  void assertLength(Object matcher) {
+    expect(suggestions, hasLength(matcher));
+  }
+
+  SingleSuggestionValidator assertSingle() {
+    assertLength(1);
+    return SingleSuggestionValidator(suggestions.single);
+  }
+
+  SuggestionsValidator withCompletion(String completion) {
+    return SuggestionsValidator(
+      suggestions.where((suggestion) {
+        return suggestion.completion == completion;
+      }).toList(),
+    );
+  }
+
+  SuggestionsValidator withElementClass() {
+    return withElementKind(ElementKind.CLASS);
+  }
+
+  SuggestionsValidator withElementConstructor() {
+    return withElementKind(ElementKind.CONSTRUCTOR);
+  }
+
+  SuggestionsValidator withElementKind(ElementKind kind) {
+    return SuggestionsValidator(
+      suggestions.where((suggestion) {
+        return suggestion.element?.kind == kind;
+      }).toList(),
+    );
+  }
+}
diff --git a/pkg/analysis_server/test/lsp/format_test.dart b/pkg/analysis_server/test/lsp/format_test.dart
index 5ba380f..61bac2e 100644
--- a/pkg/analysis_server/test/lsp/format_test.dart
+++ b/pkg/analysis_server/test/lsp/format_test.dart
@@ -210,6 +210,30 @@
     await expectRangeFormattedContents(mainFileUri, contents, expected);
   }
 
+  Future<void> test_formatRange_expandsLeadingWhitespaceToNearestLine() async {
+    const contents = '''
+void main()
+{
+
+[[        print('test'); // line 2
+        print('test'); // line 3
+        print('test'); // line 4]]
+}
+''';
+    const expected = '''
+void main()
+{
+
+  print('test'); // line 2
+  print('test'); // line 3
+  print('test'); // line 4
+}
+''';
+    await initialize();
+    await openFile(mainFileUri, withoutMarkers(contents));
+    await expectRangeFormattedContents(mainFileUri, contents, expected);
+  }
+
   Future<void> test_formatRange_invalidRange() async {
     const contents = '''
 void f()
diff --git a/pkg/analysis_server/test/services/refactoring/rename_constructor_test.dart b/pkg/analysis_server/test/services/refactoring/rename_constructor_test.dart
index c688ac2..79d6fe7 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_constructor_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_constructor_test.dart
@@ -91,7 +91,8 @@
 
   Future<void> test_createChange_add() async {
     await indexTestUnit('''
-/// Documentation for [new A]
+// ignore: deprecated_new_in_comment_reference
+/// Documentation for [new A] and [A.new]
 class A {
   A() {} // marker
   factory A._() = A;
@@ -112,7 +113,8 @@
     // validate change
     refactoring.newName = 'newName';
     return assertSuccessfulRefactoring('''
-/// Documentation for [new A.newName]
+// ignore: deprecated_new_in_comment_reference
+/// Documentation for [new A.newName] and [A.newName]
 class A {
   A.newName() {} // marker
   factory A._() = A.newName;
@@ -129,7 +131,8 @@
 
   Future<void> test_createChange_add_toSynthetic() async {
     await indexTestUnit('''
-/// Documentation for [new A]
+// ignore: deprecated_new_in_comment_reference
+/// Documentation for [new A] and [A.new]
 class A {
   int field = 0;
 }
@@ -149,7 +152,8 @@
     // validate change
     refactoring.newName = 'newName';
     return assertSuccessfulRefactoring('''
-/// Documentation for [new A.newName]
+// ignore: deprecated_new_in_comment_reference
+/// Documentation for [new A.newName] and [A.newName]
 class A {
   int field = 0;
 
@@ -167,6 +171,7 @@
 
   Future<void> test_createChange_change() async {
     await indexTestUnit('''
+// ignore: deprecated_new_in_comment_reference
 /// Documentation for [A.test] and [new A.test]
 class A {
   A.test() {} // marker
@@ -188,6 +193,7 @@
     // validate change
     refactoring.newName = 'newName';
     return assertSuccessfulRefactoring('''
+// ignore: deprecated_new_in_comment_reference
 /// Documentation for [A.newName] and [new A.newName]
 class A {
   A.newName() {} // marker
@@ -234,6 +240,7 @@
 
   Future<void> test_createChange_remove() async {
     await indexTestUnit('''
+// ignore: deprecated_new_in_comment_reference
 /// Documentation for [A.test] and [new A.test]
 class A {
   A.test() {} // marker
@@ -255,6 +262,7 @@
     // validate change
     refactoring.newName = '';
     return assertSuccessfulRefactoring('''
+// ignore: deprecated_new_in_comment_reference
 /// Documentation for [A] and [new A]
 class A {
   A() {} // marker
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_into_expression_body_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_into_expression_body_test.dart
index 33d2b33..638ecab 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/convert_into_expression_body_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_into_expression_body_test.dart
@@ -12,10 +12,49 @@
 void main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(ConvertIntoExpressionBodyTest);
+    defineReflectiveTests(ConvertIntoExpressionBodyBulkTest);
   });
 }
 
 @reflectiveTest
+class ConvertIntoExpressionBodyBulkTest extends BulkFixProcessorTest {
+  @override
+  String get lintCode => LintNames.prefer_expression_function_bodies;
+
+  Future<void> test_singleFile() async {
+    // See the discussion in https://dart-review.googlesource.com/c/sdk/+/217521
+    // for the nested closure case (var f = () ...).
+    // Note this is another place where multiple passes will improve results.
+    await resolveTestCode('''
+class A {
+  mmm() async {
+    return 42;
+  }
+  int nnn() {
+    return mmm() + 1;
+  }
+}
+
+var f = () {
+  return () {
+    return 3;
+  };
+};
+''');
+    await assertHasFix('''
+class A {
+  mmm() async => 42;
+  int nnn() => mmm() + 1;
+}
+
+var f = () => () {
+    return 3;
+  };
+''');
+  }
+}
+
+@reflectiveTest
 class ConvertIntoExpressionBodyTest extends FixProcessorLintTest {
   @override
   FixKind get kind => DartFixKind.CONVERT_INTO_EXPRESSION_BODY;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/fix_processor_map_test.dart b/pkg/analysis_server/test/src/services/correction/fix/fix_processor_map_test.dart
index 83e66a2b..763bc38 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/fix_processor_map_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/fix_processor_map_test.dart
@@ -19,6 +19,7 @@
     'avoid_types_on_closure_parameters',
     'empty_statements',
     'prefer_collection_literals',
+    'prefer_const_constructors',
     'prefer_inlined_adds',
   ];
 
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_deprecated_new_in_comment_reference_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_deprecated_new_in_comment_reference_test.dart
new file mode 100644
index 0000000..58cc478
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_deprecated_new_in_comment_reference_test.dart
@@ -0,0 +1,80 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'fix_processor.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(RemoveDeprecatedNewInCommentReferenceBulkTest);
+    defineReflectiveTests(RemoveDeprecatedNewInCommentReferenceTest);
+  });
+}
+
+@reflectiveTest
+class RemoveDeprecatedNewInCommentReferenceBulkTest
+    extends BulkFixProcessorTest {
+  Future<void> test_named() async {
+    await resolveTestCode('''
+/// See [new A.named].
+class A {
+  A.named();
+}
+''');
+    await assertHasFix('''
+/// See [A.named].
+class A {
+  A.named();
+}
+''');
+  }
+}
+
+@reflectiveTest
+class RemoveDeprecatedNewInCommentReferenceTest extends FixProcessorTest {
+  @override
+  FixKind get kind => DartFixKind.REMOVE_DEPRECATED_NEW_IN_COMMENT_REFERENCE;
+
+  Future<void> test_named() async {
+    await resolveTestCode('''
+/// See [new A.named].
+class A {
+  A.named();
+}
+''');
+    await assertHasFix('''
+/// See [A.named].
+class A {
+  A.named();
+}
+''');
+  }
+
+  Future<void> test_prefixedUnnamed() async {
+    await resolveTestCode('''
+/// See [new self.A].
+import '' as self;
+class A {}
+''');
+    await assertHasFix('''
+/// See [self.A.new].
+import '' as self;
+class A {}
+''');
+  }
+
+  Future<void> test_unnamed() async {
+    await resolveTestCode('''
+/// See [new A].
+class A {}
+''');
+    await assertHasFix('''
+/// See [A.new].
+class A {}
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/replace_new_with_const_test.dart b/pkg/analysis_server/test/src/services/correction/fix/replace_new_with_const_test.dart
index 6960bb5..4fca1eb 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/replace_new_with_const_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/replace_new_with_const_test.dart
@@ -21,23 +21,75 @@
   @override
   String get lintCode => LintNames.prefer_const_constructors;
 
-  /// Disabled in BulkFixProcessor.
-  @failingTest
   Future<void> test_singleFile() async {
     await resolveTestCode(r'''
-class C {
-  const C();
+class A {
+  const A();
 }
 main() {
-  print('${new C()} ${new C()}');
+  var a = new A();
+  var b = new A();
 }
 ''');
     await assertHasFix(r'''
-class C {
-  const C();
+class A {
+  const A();
 }
 main() {
-  print('${const C()} ${const C()}');
+  var a = const A();
+  var b = const A();
+}
+''');
+  }
+
+  Future<void> test_singleFile_incomplete_promotableNew() async {
+    await resolveTestCode(r'''
+class A {
+  const A({A? parent});
+  const A.a();
+}
+main() {
+  var a = new A(
+    parent: new A(),
+  );
+}
+''');
+    // The outer new should get promoted to const on a future fix pass.
+    await assertHasFix(r'''
+class A {
+  const A({A? parent});
+  const A.a();
+}
+main() {
+  var a = new A(
+    parent: const A(),
+  );
+}
+''');
+  }
+
+  Future<void> test_singleFile_incomplete_unnecessaryConst() async {
+    await resolveTestCode(r'''
+class A {
+  const A({A? parent});
+  const A.a();
+}
+main() {
+  var b = new A(
+    parent: const A.a(),
+  );
+}
+''');
+    // The inner const is unnecessary and should get removed on a future fix pass.
+    await assertHasFix(r'''
+class A {
+  const A({A? parent});
+  const A.a();
+}
+main() {
+  var b = const A(
+    parent: const A.a(),
+  );
 }
 ''');
   }
diff --git a/pkg/analysis_server/test/src/services/correction/fix/replace_with_interpolation_test.dart b/pkg/analysis_server/test/src/services/correction/fix/replace_with_interpolation_test.dart
index 5dc54a1..64726ee 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/replace_with_interpolation_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/replace_with_interpolation_test.dart
@@ -12,10 +12,34 @@
 void main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(ReplaceWithInterpolationTest);
+    defineReflectiveTests(ReplaceWithInterpolationBulkTest);
   });
 }
 
 @reflectiveTest
+class ReplaceWithInterpolationBulkTest extends BulkFixProcessorTest {
+  @override
+  String get lintCode => LintNames.prefer_interpolation_to_compose_strings;
+
+  Future<void> test_singleFile() async {
+    await resolveTestCode(r'''
+String f() {
+  var a = 'a';
+  var c = a + 'b';
+  return c + 'and $s';
+}
+''');
+    await assertHasFix(r'''
+String f() {
+  var a = 'a';
+  var c = '${a}b';
+  return '${c}and $s';
+}
+''');
+  }
+}
+
+@reflectiveTest
 class ReplaceWithInterpolationTest extends FixProcessorLintTest {
   @override
   FixKind get kind => DartFixKind.REPLACE_WITH_INTERPOLATION;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
index ac07949..3753bfb 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
@@ -128,6 +128,8 @@
 import 'remove_const_test.dart' as remove_const;
 import 'remove_constructor_name_test.dart' as remove_constructor_name;
 import 'remove_dead_code_test.dart' as remove_dead_code;
+import 'remove_deprecated_new_in_comment_reference_test.dart'
+    as remove_deprecated_new_in_comment_reference;
 import 'remove_duplicate_case_test.dart' as remove_duplicate_case;
 import 'remove_empty_catch_test.dart' as remove_empty_catch;
 import 'remove_empty_constructor_body_test.dart'
@@ -321,6 +323,7 @@
     remove_const.main();
     remove_constructor_name.main();
     remove_dead_code.main();
+    remove_deprecated_new_in_comment_reference.main();
     remove_duplicate_case.main();
     remove_empty_catch.main();
     remove_empty_constructor_body.main();
diff --git a/pkg/analysis_server/tool/bulk_fix/supported_nonlints.dart b/pkg/analysis_server/tool/bulk_fix/supported_diagnostics.dart
similarity index 80%
rename from pkg/analysis_server/tool/bulk_fix/supported_nonlints.dart
rename to pkg/analysis_server/tool/bulk_fix/supported_diagnostics.dart
index e9dcbbc..2603f84 100644
--- a/pkg/analysis_server/tool/bulk_fix/supported_nonlints.dart
+++ b/pkg/analysis_server/tool/bulk_fix/supported_diagnostics.dart
@@ -7,19 +7,22 @@
 
 import 'parse_utils.dart';
 
-/// Print hint bulk-fix info.
+/// Print diagnostic bulk-fix info.
 Future<void> main() async {
   var overrideDetails = await BulkFixDetails().collectOverrides();
 
-  print('hints w/ correction producers:\n');
+  print('diagnostics w/ correction producers:\n');
 
   var hintEntries = FixProcessor.nonLintProducerMap.entries.where((e) =>
       e.key.type == ErrorType.HINT || e.key.type == ErrorType.STATIC_WARNING);
-  for (var hint in hintEntries) {
+
+  var diagnostics = List.from(hintEntries)
+    ..addAll(FixProcessor.lintProducerMap.entries);
+  for (var diagnostic in diagnostics) {
     var canBeAppliedInBulk = false;
     var missingExplanations = <String>[];
     var hasOverride = false;
-    for (var generator in hint.value) {
+    for (var generator in diagnostic.value) {
       var producer = generator();
       if (!producer.canBeAppliedInBulk) {
         var producerName = producer.runtimeType.toString();
@@ -36,7 +39,7 @@
       }
     }
 
-    print('${hint.key} bulk fixable: $canBeAppliedInBulk');
+    print('${diagnostic.key} bulk fixable: $canBeAppliedInBulk');
     if (!canBeAppliedInBulk && !hasOverride) {
       print('  => override missing');
     }
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 6e1954d..1c98a3a 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -530,6 +530,7 @@
   HintCode.DEPRECATED_MEMBER_USE_FROM_SAME_PACKAGE_WITH_MESSAGE,
   HintCode.DEPRECATED_MEMBER_USE_WITH_MESSAGE,
   HintCode.DEPRECATED_MIXIN_FUNCTION,
+  HintCode.DEPRECATED_NEW_IN_COMMENT_REFERENCE,
   HintCode.DIVISION_OPTIMIZATION,
   HintCode.DUPLICATE_HIDDEN_NAME,
   HintCode.DUPLICATE_IGNORE,
diff --git a/pkg/analyzer/lib/src/dart/constant/value.dart b/pkg/analyzer/lib/src/dart/constant/value.dart
index 9e95612..227abea 100644
--- a/pkg/analyzer/lib/src/dart/constant/value.dart
+++ b/pkg/analyzer/lib/src/dart/constant/value.dart
@@ -487,15 +487,7 @@
     _assertType(testedType);
     var typeType = (testedType._state as TypeState)._type;
     BoolState state;
-    if (isNull) {
-      if (typeType == typeSystem.typeProvider.objectType ||
-          typeType == typeSystem.typeProvider.dynamicType ||
-          typeType == typeSystem.typeProvider.nullType) {
-        state = BoolState.TRUE_STATE;
-      } else {
-        state = BoolState.FALSE_STATE;
-      }
-    } else if (typeType == null) {
+    if (typeType == null) {
       state = BoolState.TRUE_STATE;
     } else {
       state = BoolState.from(typeSystem.isSubtypeOf(type, typeType));
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.g.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.g.dart
index e0f5728..8dc8f2a 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.g.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.g.dart
@@ -439,6 +439,15 @@
   );
 
   /**
+   * No parameters.
+   */
+  static const HintCode DEPRECATED_NEW_IN_COMMENT_REFERENCE = HintCode(
+    'DEPRECATED_NEW_IN_COMMENT_REFERENCE',
+    "Using the 'new' keyword in a comment reference is deprecated.",
+    correctionMessage: "Try referring to a constructor by its name.",
+  );
+
+  /**
    * Hint to use the ~/ operator.
    */
   static const HintCode DIVISION_OPTIMIZATION = HintCode(
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index cdd12a2..85441dc 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -4,6 +4,7 @@
 
 import 'dart:collection';
 
+import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/syntactic_entity.dart';
 import 'package:analyzer/dart/ast/token.dart';
@@ -393,6 +394,17 @@
   }
 
   @override
+  void visitCommentReference(CommentReference node) {
+    var newKeyword = node.newKeyword;
+    if (newKeyword != null &&
+        _currentLibrary.featureSet.isEnabled(Feature.constructor_tearoffs)) {
+      _errorReporter.reportErrorForToken(
+          HintCode.DEPRECATED_NEW_IN_COMMENT_REFERENCE, newKeyword, []);
+    }
+    super.visitCommentReference(node);
+  }
+
+  @override
   void visitConstructorDeclaration(ConstructorDeclaration node) {
     var element = node.declaredElement as ConstructorElementImpl;
     if (!_isNonNullableByDefault && node.declaredElement!.isFactory) {
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index 24287cf..3637c8d 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -14293,6 +14293,10 @@
       Parameters:
       0: the name of the member
       1: message details
+  DEPRECATED_NEW_IN_COMMENT_REFERENCE:
+    problemMessage: "Using the 'new' keyword in a comment reference is deprecated."
+    correctionMessage: Try referring to a constructor by its name.
+    comment: No parameters.
   DEPRECATED_MIXIN_FUNCTION:
     sharedName: DEPRECATED_SUBTYPE_OF_FUNCTION
     problemMessage: "Mixing in 'Function' is deprecated."
diff --git a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
index 24b6f96..dddec74 100644
--- a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
+++ b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
@@ -557,6 +557,49 @@
     _assertTypeArguments(result, null);
   }
 
+  test_visitIsExpression_is_null() async {
+    await resolveTestCode('''
+const a = null;
+const b = a is A;
+class A {}
+''');
+    DartObjectImpl result = _evaluateConstant('b');
+    expect(result.type, typeProvider.boolType);
+    expect(result.toBoolValue(), false);
+  }
+
+  test_visitIsExpression_is_null_nullable() async {
+    await resolveTestCode('''
+const a = null;
+const b = a is A?;
+class A {}
+''');
+    DartObjectImpl result = _evaluateConstant('b');
+    expect(result.type, typeProvider.boolType);
+    expect(result.toBoolValue(), true);
+  }
+
+  test_visitIsExpression_is_null_object() async {
+    await resolveTestCode('''
+const a = null;
+const b = a is Object;
+''');
+    DartObjectImpl result = _evaluateConstant('b');
+    expect(result.type, typeProvider.boolType);
+    expect(result.toBoolValue(), false);
+  }
+
+  test_visitIsExpression_isNot_null() async {
+    await resolveTestCode('''
+const a = null;
+const b = a is! A;
+class A {}
+''');
+    DartObjectImpl result = _evaluateConstant('b');
+    expect(result.type, typeProvider.boolType);
+    expect(result.toBoolValue(), true);
+  }
+
   test_visitPrefixedIdentifier_genericFunction_instantiated() async {
     await resolveTestCode('''
 import '' as self;
@@ -1520,17 +1563,6 @@
     expect(result.toBoolValue(), false);
   }
 
-  test_visitIsExpression_is_null() async {
-    await resolveTestCode('''
-const a = null;
-const b = a is A;
-class A {}
-''');
-    DartObjectImpl result = _evaluateConstant('b');
-    expect(result.type, typeProvider.boolType);
-    expect(result.toBoolValue(), false);
-  }
-
   test_visitIsExpression_is_null_dynamic() async {
     await resolveTestCode('''
 const a = null;
@@ -1553,17 +1585,6 @@
     expect(result.toBoolValue(), true);
   }
 
-  test_visitIsExpression_is_null_object() async {
-    await resolveTestCode('''
-const a = null;
-const b = a is Object;
-class A {}
-''');
-    DartObjectImpl result = _evaluateConstant('b');
-    expect(result.type, typeProvider.boolType);
-    expect(result.toBoolValue(), true);
-  }
-
   test_visitIsExpression_isNot_instanceOfSameClass() async {
     await resolveTestCode('''
 const a = const A();
@@ -1625,17 +1646,6 @@
     expect(result.toBoolValue(), true);
   }
 
-  test_visitIsExpression_isNot_null() async {
-    await resolveTestCode('''
-const a = null;
-const b = a is! A;
-class A {}
-''');
-    DartObjectImpl result = _evaluateConstant('b');
-    expect(result.type, typeProvider.boolType);
-    expect(result.toBoolValue(), true);
-  }
-
   test_visitPrefixedIdentifier_function() async {
     await resolveTestCode('''
 import '' as self;
@@ -1892,11 +1902,58 @@
     DartObjectImpl result = _evaluateConstant('b');
     expect(result.type, typeProvider.nullType);
   }
+
+  test_visitIsExpression_is_null() async {
+    await resolveTestCode('''
+const a = null;
+const b = a is A;
+class A {}
+''');
+    DartObjectImpl result = _evaluateConstant('b');
+    expect(result.type, typeProvider.boolType);
+    expect(result.toBoolValue(), true);
+  }
+
+  test_visitIsExpression_is_null_object() async {
+    await resolveTestCode('''
+const a = null;
+const b = a is Object;
+''');
+    DartObjectImpl result = _evaluateConstant('b');
+    expect(result.type, typeProvider.boolType);
+    expect(result.toBoolValue(), true);
+  }
+
+  test_visitIsExpression_isNot_null() async {
+    await resolveTestCode('''
+const a = null;
+const b = a is! A;
+class A {}
+''');
+    DartObjectImpl result = _evaluateConstant('b');
+    expect(result.type, typeProvider.boolType);
+    expect(result.toBoolValue(), false);
+  }
 }
 
 @reflectiveTest
 class InstanceCreationEvaluatorTest extends ConstantVisitorTestSupport
     with InstanceCreationEvaluatorTestCases {
+  test_assertInitializer_assertIsNot_null_nullableType() async {
+    await resolveTestCode('''
+class A<T> {
+  const A() : assert(null is! T);
+}
+
+const a = const A<int?>();
+''');
+
+    _evaluateConstantOrNull(
+      'a',
+      errorCodes: [CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION],
+    );
+  }
+
   test_fieldInitializer_typeParameter() async {
     await resolveTestCode('''
 class A<T> {
@@ -2031,6 +2088,31 @@
 
 @reflectiveTest
 mixin InstanceCreationEvaluatorTestCases on ConstantVisitorTestSupport {
+  test_assertInitializer_assertIsNot_false() async {
+    await resolveTestCode('''
+class A {
+  const A() : assert(0 is! int);
+}
+
+const a = const A(null);
+''');
+    _evaluateConstantOrNull(
+      'a',
+      errorCodes: [CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION],
+    );
+  }
+
+  test_assertInitializer_assertIsNot_true() async {
+    await resolveTestCode('''
+class A {
+  const A() : assert(0 is! String);
+}
+
+const a = const A(null);
+''');
+    _assertValidConstant('a');
+  }
+
   test_assertInitializer_intInDoubleContext_assertIsDouble_true() async {
     await resolveTestCode('''
 class A {
diff --git a/pkg/analyzer/test/src/dart/resolution/comment_test.dart b/pkg/analyzer/test/src/dart/resolution/comment_test.dart
index d5d1255..3d386b9 100644
--- a/pkg/analyzer/test/src/dart/resolution/comment_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/comment_test.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'package:analyzer/src/dart/error/hint_codes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'context_collection_resolution.dart';
@@ -367,7 +368,7 @@
   }
 
   test_new() async {
-    await assertNoErrorsInCode(r'''
+    await assertErrorsInCode('''
 class A {
   A();
   A.named();
@@ -375,7 +376,10 @@
 
 /// [new A] or [new A.named]
 main() {}
-''');
+''', [
+      error(HintCode.DEPRECATED_NEW_IN_COMMENT_REFERENCE, 38, 3),
+      error(HintCode.DEPRECATED_NEW_IN_COMMENT_REFERENCE, 49, 3),
+    ]);
 
     assertElement(
       findNode.simple('A]'),
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 5a67b84..b899fbe 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -44,6 +44,7 @@
 import 'js_model/js_strategy.dart';
 import 'js_model/js_world.dart';
 import 'js_model/locals.dart';
+import 'kernel/front_end_adapter.dart' show CompilerFileSystem;
 import 'kernel/kernel_strategy.dart';
 import 'kernel/loader.dart' show KernelLoaderTask, KernelResult;
 import 'null_compiler_output.dart' show NullCompilerOutput;
@@ -90,6 +91,7 @@
 
   final List<CodeLocation> _userCodeLocations = <CodeLocation>[];
 
+  ir.Component componentForTesting;
   JClosedWorld backendClosedWorldForTesting;
   DataSourceIndices closedWorldIndicesForTesting;
 
@@ -252,9 +254,12 @@
     reporter.log('Compiling $compilationTarget (${options.buildId})');
 
     if (options.readProgramSplit != null) {
+      var constraintUri = options.readProgramSplit;
       var constraintParser = psc.Parser();
-      programSplitConstraintsData =
-          await constraintParser.read(provider, options.readProgramSplit);
+      var programSplitJson = await CompilerFileSystem(provider)
+          .entityForUri(constraintUri)
+          .readAsString();
+      programSplitConstraintsData = constraintParser.read(programSplitJson);
     }
 
     if (onlyPerformGlobalTypeInference) {
@@ -309,6 +314,9 @@
       if (compilationFailed) {
         return;
       }
+      if (retainDataForTesting) {
+        componentForTesting = result.component;
+      }
       if (options.cfeOnly) return;
 
       frontendStrategy.registerLoadedLibraries(result);
diff --git a/pkg/compiler/lib/src/deferred_load/program_split_constraints/nodes.dart b/pkg/compiler/lib/src/deferred_load/program_split_constraints/nodes.dart
index 33f7b58..9f10c18 100644
--- a/pkg/compiler/lib/src/deferred_load/program_split_constraints/nodes.dart
+++ b/pkg/compiler/lib/src/deferred_load/program_split_constraints/nodes.dart
@@ -4,7 +4,9 @@
 
 /// A [Node] is an abstract base class for all [Node]s parsed from json
 /// constraints.
-abstract class Node {}
+abstract class Node {
+  Map<String, dynamic> toJson();
+}
 
 /// A [NamedNode] is an abstract base class for all [Node]s that have a name.
 abstract class NamedNode extends Node {
@@ -13,12 +15,54 @@
   NamedNode(this.name);
 }
 
-/// A [ReferenceNode] is a [NamedNode] with a uri and prefix.
-class ReferenceNode extends NamedNode {
+/// A [UriAndPrefix] is a simple POD data type wrapping a uri and prefix.
+class UriAndPrefix {
   final Uri uri;
   final String prefix;
 
-  ReferenceNode(name, this.uri, this.prefix) : super(name);
+  UriAndPrefix(this.uri, this.prefix);
+
+  @override
+  String toString() {
+    return '$uri#$prefix';
+  }
+
+  static UriAndPrefix fromJson(String json) {
+    var uriAndPrefix = json.split('#');
+    if (uriAndPrefix.length != 2) {
+      throw 'Invalid "import" "uri#prefix" value in $json';
+    }
+    var uri = Uri.parse(uriAndPrefix[0]);
+    var prefix = uriAndPrefix[1];
+    return UriAndPrefix(uri, prefix);
+  }
+}
+
+/// A [ReferenceNode] is a [NamedNode] with a uri and prefix.
+class ReferenceNode extends NamedNode {
+  final UriAndPrefix _uriAndPrefix;
+  Uri get uri => _uriAndPrefix.uri;
+  String get prefix => _uriAndPrefix.prefix;
+
+  ReferenceNode(this._uriAndPrefix, {name})
+      : super(name ?? _uriAndPrefix.prefix);
+
+  @override
+  Map<String, dynamic> toJson() {
+    return {
+      'type': 'reference',
+      'name': name,
+      'import': _uriAndPrefix.toString()
+    };
+  }
+
+  static ReferenceNode fromJson(Map<String, dynamic> nodeJson) {
+    if (nodeJson['type'] != 'reference') {
+      throw 'Unrecognized type for reference node: ${nodeJson['type']}.';
+    }
+    return ReferenceNode(UriAndPrefix.fromJson(nodeJson['import']),
+        name: nodeJson['name']);
+  }
 
   @override
   String toString() {
@@ -30,13 +74,76 @@
 /// single step.
 enum CombinerType { fuse, and, or }
 
+CombinerType parseCombinerType(Map<String, dynamic> nodeJson) {
+  String type = nodeJson['type'];
+  switch (type) {
+    case 'fuse':
+      return CombinerType.fuse;
+    case 'and':
+      return CombinerType.and;
+    case 'or':
+      return CombinerType.or;
+    default:
+      throw 'Unrecognized Combiner $nodeJson';
+  }
+}
+
+String combinerTypeToString(CombinerType type) {
+  switch (type) {
+    case CombinerType.fuse:
+      return 'fuse';
+    case CombinerType.and:
+      return 'and';
+    case CombinerType.or:
+      return 'or';
+  }
+  throw 'Unreachable';
+}
+
+T _jsonLookup<T>(Map<String, dynamic> nodeJson, String key) {
+  var value = nodeJson[key];
+  if (value == null) {
+    throw 'Missing "$key" key in $nodeJson';
+  }
+  return value;
+}
+
+NamedNode _jsonLookupNode(
+    Map<String, dynamic> nodeJson, String key, Map<String, NamedNode> nameMap) {
+  var node = nameMap[_jsonLookup(nodeJson, key)];
+  if (node == null) {
+    throw 'Invalid "$key" name in $nodeJson';
+  }
+  return node;
+}
+
 /// A [CombinerNode] is a [NamedNode] with a list of [ReferenceNode] children
 /// and a [CombinerType] for combining them.
 class CombinerNode extends NamedNode {
   final CombinerType type;
   final Set<ReferenceNode> nodes;
 
-  CombinerNode(name, this.type, this.nodes) : super(name);
+  CombinerNode(String name, this.type, this.nodes) : super(name);
+
+  @override
+  Map<String, dynamic> toJson() {
+    return {
+      'type': combinerTypeToString(type),
+      'name': name,
+      'nodes': nodes.map((node) => node.name).toList()
+    };
+  }
+
+  static CombinerNode fromJson(
+      Map<String, dynamic> nodeJson, Map<String, NamedNode> nameMap) {
+    String name = _jsonLookup(nodeJson, 'name');
+    List<dynamic> referencesJson = _jsonLookup(nodeJson, 'nodes');
+    Set<ReferenceNode> references = {};
+    for (String reference in referencesJson) {
+      references.add(nameMap[reference]);
+    }
+    return CombinerNode(name, parseCombinerType(nodeJson), references);
+  }
 
   @override
   String toString() {
@@ -57,12 +164,95 @@
   }
 
   @override
+  Map<String, dynamic> toJson() {
+    return {
+      'type': 'order',
+      'predecessor': predecessor.name,
+      'successor': successor.name
+    };
+  }
+
+  static RelativeOrderNode fromJson(
+      Map<String, dynamic> nodeJson, Map<String, NamedNode> nameMap) {
+    var predecessor = _jsonLookupNode(nodeJson, 'predecessor', nameMap);
+    var successor = _jsonLookupNode(nodeJson, 'successor', nameMap);
+    return RelativeOrderNode(predecessor: predecessor, successor: successor);
+  }
+
+  @override
   String toString() {
     return 'RelativeOrderNode(predecessor=${predecessor.name}, '
         'successor=${successor.name})';
   }
 }
 
+/// A builder class for constructing constraint nodes.
+typedef ReferenceNodeNamer = String Function(UriAndPrefix);
+
+class ProgramSplitBuilder {
+  final Map<String, NamedNode> namedNodes = {};
+  ReferenceNodeNamer _referenceNodeNamer;
+
+  /// The prefix in the 'uri#prefix' string will become a key to reference this
+  /// node in other builder calls.
+  String _prefixNamer(UriAndPrefix uriAndPrefix) => uriAndPrefix.prefix;
+
+  /// Override the default reference node namer.
+  set referenceNodeNamer(ReferenceNodeNamer namer) =>
+      _referenceNodeNamer = namer;
+
+  /// Returns the [ReferenceNodeNamer] to use for naming.
+  ReferenceNodeNamer get referenceNodeNamer =>
+      _referenceNodeNamer ?? _prefixNamer;
+
+  /// Returns a [ReferenceNode] referencing [importUriAndPrefix].
+  /// [ReferenceNode]s are typically created in bulk, by mapping over a list of
+  /// strings of imports in the form 'uri#prefix'. In further builder calls,
+  /// created nodes can be referenced by their namers, derived from calling
+  /// [referenceNodeNamer] per [ReferenceNode].
+  ReferenceNode referenceNode(String importUriAndPrefix) {
+    var uriAndPrefix = UriAndPrefix.fromJson(importUriAndPrefix);
+    var referenceNode = ReferenceNode(uriAndPrefix);
+    var name = referenceNodeNamer(uriAndPrefix);
+    namedNodes[name] = referenceNode;
+    return referenceNode;
+  }
+
+  /// Creates an unnamed [RelativeOrderNode] referencing two [NamedNode]s.
+  RelativeOrderNode orderNode(String predecessor, String successor) {
+    return RelativeOrderNode(
+        predecessor: namedNodes[predecessor], successor: namedNodes[successor]);
+  }
+
+  /// Creates a [CombinerNode] which can be referenced by [name] in further
+  /// calls to the builder.
+  CombinerNode combinerNode(
+      String name, List<String> nodes, CombinerType type) {
+    var combinerNode = CombinerNode(name, type,
+        nodes.map((name) => namedNodes[name] as ReferenceNode).toSet());
+    namedNodes[name] = combinerNode;
+    return combinerNode;
+  }
+
+  /// Creates an 'and' [CombinerNode] which can be referenced by [name] in
+  /// further calls to the builder.
+  CombinerNode andNode(String name, List<String> nodes) {
+    return combinerNode(name, nodes, CombinerType.and);
+  }
+
+  /// Creates a 'fuse' [CombinerNode] which can be referenced by [name] in
+  /// further calls to the builder.
+  CombinerNode fuseNode(String name, List<String> nodes) {
+    return combinerNode(name, nodes, CombinerType.fuse);
+  }
+
+  /// Creates an 'or' [CombinerNode] which can be referenced by [name] in
+  /// further calls to the builder.
+  CombinerNode orNode(String name, List<String> nodes) {
+    return combinerNode(name, nodes, CombinerType.or);
+  }
+}
+
 /// [ConstraintData] is a data object which contains the results of parsing json
 /// program split constraints.
 class ConstraintData {
diff --git a/pkg/compiler/lib/src/deferred_load/program_split_constraints/parser.dart b/pkg/compiler/lib/src/deferred_load/program_split_constraints/parser.dart
index a758569..968b07b 100644
--- a/pkg/compiler/lib/src/deferred_load/program_split_constraints/parser.dart
+++ b/pkg/compiler/lib/src/deferred_load/program_split_constraints/parser.dart
@@ -6,83 +6,29 @@
 
 import 'nodes.dart';
 
-import '../../../compiler_new.dart' as api;
-import '../../kernel/front_end_adapter.dart' show CompilerFileSystem;
-
 /// [Parser] parsers a program split constraints json file and returns a
 /// [ConstraintData] object.
 class Parser {
   final Map<String, NamedNode> nameMap = {};
   final List<RelativeOrderNode> orderedNodes = [];
 
-  T _lookup<T>(Map<String, dynamic> nodeJson, String key) {
-    var value = nodeJson[key];
-    if (value == null) {
-      throw 'Missing "$key" key in $nodeJson';
-    }
-    return value;
-  }
-
   void parseReference(Map<String, dynamic> nodeJson) {
-    String name = _lookup(nodeJson, 'name');
-    String uriAndPrefixString = _lookup(nodeJson, 'import');
-    var uriAndPrefix = uriAndPrefixString.split('#');
-    if (uriAndPrefix.length != 2) {
-      throw 'Invalid "import" "uri#prefix" value in $nodeJson';
-    }
-    var uri = Uri.parse(uriAndPrefix[0]);
-    var prefix = uriAndPrefix[1];
-    var referenceNode = ReferenceNode(name, uri, prefix);
-    nameMap[name] = referenceNode;
-  }
-
-  CombinerType parseCombinerType(Map<String, dynamic> nodeJson) {
-    String type = nodeJson['type'];
-    switch (type) {
-      case 'fuse':
-        return CombinerType.fuse;
-      case 'and':
-        return CombinerType.and;
-      case 'or':
-        return CombinerType.or;
-      default:
-        throw 'Unrecognized Combiner $nodeJson';
-    }
+    var reference = ReferenceNode.fromJson(nodeJson);
+    nameMap[reference.name] = reference;
   }
 
   void parseCombiner(Map<String, dynamic> nodeJson) {
-    String name = _lookup(nodeJson, 'name');
-    List<dynamic> referencesJson = _lookup(nodeJson, 'nodes');
-    Set<ReferenceNode> references = {};
-    for (String reference in referencesJson) {
-      references.add(nameMap[reference]);
-    }
-    var combinerNode =
-        CombinerNode(name, parseCombinerType(nodeJson), references);
-    nameMap[name] = combinerNode;
-  }
-
-  NamedNode _lookupNode(Map<String, dynamic> nodeJson, String key) {
-    var node = nameMap[_lookup(nodeJson, key)];
-    if (node == null) {
-      throw 'Invalid "$key" name in $nodeJson';
-    }
-    return node;
+    var combinerNode = CombinerNode.fromJson(nodeJson, nameMap);
+    nameMap[combinerNode.name] = combinerNode;
   }
 
   void parseOrder(Map<String, dynamic> nodeJson) {
-    var predecessor = _lookupNode(nodeJson, 'predecessor');
-    var successor = _lookupNode(nodeJson, 'successor');
-    var orderNode =
-        RelativeOrderNode(predecessor: predecessor, successor: successor);
-    orderedNodes.add(orderNode);
+    orderedNodes.add(RelativeOrderNode.fromJson(nodeJson, nameMap));
   }
 
-  /// Reads a program split constraints json file and returns a [Nodes] object
-  /// reflecting the parsed constraints.
-  Future<ConstraintData> read(api.CompilerInput provider, Uri path) async {
-    String programSplitJson =
-        await CompilerFileSystem(provider).entityForUri(path).readAsString();
+  /// Reads a program split constraints json file string and returns a [Nodes]
+  /// object reflecting the parsed constraints.
+  ConstraintData read(String programSplitJson) {
     List<dynamic> doc = json.decode(programSplitJson);
     List<Map<String, dynamic>> referenceConstraints = [];
     List<Map<String, dynamic>> combinerConstraints = [];
diff --git a/pkg/compiler/test/custom_split/constraint_harness.dart b/pkg/compiler/test/custom_split/constraint_harness.dart
new file mode 100644
index 0000000..24c890e
--- /dev/null
+++ b/pkg/compiler/test/custom_split/constraint_harness.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.7
+
+import 'dart:convert';
+import 'dart:isolate';
+
+import 'package:compiler/src/deferred_load/program_split_constraints/nodes.dart';
+
+typedef ImportsProcessor = List<Node> Function(List<String>);
+
+/// A helper function which waits for a list of deferred imports, and then
+/// invokes the supplied [processFunc]. The list of nodes returned from
+/// [processFunc] will then be serialized as json and sent back over the
+/// supplied [sendPort].
+void waitForImportsAndInvoke(
+    SendPort sendPort, ImportsProcessor importsProcessor) async {
+  ReceivePort receivePort = ReceivePort();
+  sendPort.send(receivePort.sendPort);
+
+  var msg = await receivePort.first;
+  assert(msg is List<String>);
+  var constraints = importsProcessor(msg);
+  sendPort.send(JsonEncoder.withIndent('  ').convert(constraints));
+  receivePort.close();
+}
diff --git a/pkg/compiler/test/custom_split/custom_split_test.dart b/pkg/compiler/test/custom_split/custom_split_test.dart
index 5203eb3..fb0968a 100644
--- a/pkg/compiler/test/custom_split/custom_split_test.dart
+++ b/pkg/compiler/test/custom_split/custom_split_test.dart
@@ -5,7 +5,13 @@
 // @dart = 2.7
 
 import 'dart:io' hide Link;
+import 'dart:isolate';
+
 import 'package:async_helper/async_helper.dart';
+import 'package:compiler/src/compiler.dart';
+import 'package:expect/expect.dart';
+import 'package:kernel/ast.dart' as ir;
+
 import '../equivalence/id_equivalence_helper.dart';
 import '../deferred_loading/deferred_loading_test_helper.dart';
 
@@ -25,12 +31,71 @@
 Map<String, List<String>> createPerTestOptions() {
   Map<String, List<String>> perTestOptions = {};
   for (var test in tests) {
-    Uri dir = Platform.script.resolve('data/$test/constraints.json');
-    perTestOptions['$test'] = ['--read-program-split=$dir'];
+    Uri constraints = Platform.script.resolve('data/$test/constraints.json');
+    perTestOptions['$test'] = ['--read-program-split=$constraints'];
   }
   return perTestOptions;
 }
 
+/// Returns a list of the deferred imports in a component where each import
+/// becomes a string of 'uri#prefix'.
+List<String> getDeferredImports(ir.Component component) {
+  List<String> imports = [];
+  for (var library in component.libraries) {
+    for (var import in library.dependencies) {
+      if (import.isDeferred) {
+        imports.add('${library.importUri}#${import.name}');
+      }
+    }
+  }
+  imports.sort();
+  return imports;
+}
+
+/// A helper function which performs the following steps:
+/// 1) Get deferred imports from a given [component]
+/// 2) Spawns the supplied [constraintsUri] in its own isolate
+/// 3) Passes deferred imports via a port to the spawned isolate
+/// 4) Listens for a json string from the spawned isolated and returns the
+///    results as a a [Future<String>].
+Future<String> constraintsToJson(
+    ir.Component component, Uri constraintsUri) async {
+  var imports = getDeferredImports(component);
+  SendPort sendPort;
+  var receivePort = ReceivePort();
+  var isolate = await Isolate.spawnUri(constraintsUri, [], receivePort.sendPort,
+      paused: true);
+  isolate.addOnExitListener(receivePort.sendPort);
+  isolate.resume(isolate.pauseCapability);
+  String json;
+  await for (var msg in receivePort) {
+    if (msg == null) {
+      receivePort.close();
+    } else if (sendPort == null) {
+      sendPort = msg;
+      sendPort.send(imports);
+    } else if (json == null) {
+      json = msg;
+    } else {
+      throw 'Unexpected message $msg';
+    }
+  }
+  return json;
+}
+
+/// Verifies the programmatic API produces the expected JSON.
+Future<void> verifyCompiler(String test, Compiler compiler) async {
+  var constraints = Platform.script.resolve('data/$test/constraints.dart');
+  var constraintsJsonUri =
+      Platform.script.resolve('data/$test/constraints.json');
+  var component = compiler.componentForTesting;
+  var json = await constraintsToJson(component, constraints);
+  var constraintsJson =
+      File(constraintsJsonUri.toFilePath()).readAsStringSync();
+  constraintsJson = constraintsJson.substring(0, constraintsJson.length - 1);
+  Expect.equals(json, constraintsJson);
+}
+
 /// Compute the [OutputUnit]s for all source files involved in the test, and
 /// ensure that the compiler is correctly calculating what is used and what is
 /// not. We expect all test entry points to be in the `data` directory and any
@@ -44,6 +109,6 @@
         perTestOptions: createPerTestOptions(),
         args: args, setUpFunction: () {
       importPrefixes.clear();
-    }, testedConfigs: allSpecConfigs);
+    }, testedConfigs: allSpecConfigs, verifyCompiler: verifyCompiler);
   });
 }
diff --git a/pkg/compiler/test/custom_split/data/diamond/constraints.dart b/pkg/compiler/test/custom_split/data/diamond/constraints.dart
new file mode 100644
index 0000000..92a7665
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/diamond/constraints.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2021, 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:isolate';
+
+import 'package:compiler/src/deferred_load/program_split_constraints/nodes.dart';
+import '../../constraint_harness.dart';
+
+void main(List<String> args, SendPort sendPort) {
+  waitForImportsAndInvoke(sendPort, processDeferredImports);
+}
+
+List<Node> processDeferredImports(List<String> imports) {
+  var builder = ProgramSplitBuilder();
+  return [
+    ...imports.map(builder.referenceNode),
+    builder.orderNode('step1', 'step2a'),
+    builder.orderNode('step1', 'step2b'),
+    builder.orderNode('step2a', 'step3'),
+    builder.orderNode('step2b', 'step3'),
+  ];
+}
diff --git a/pkg/compiler/test/custom_split/data/diamond/constraints.json b/pkg/compiler/test/custom_split/data/diamond/constraints.json
index 19bc46e..83dfe0a 100644
--- a/pkg/compiler/test/custom_split/data/diamond/constraints.json
+++ b/pkg/compiler/test/custom_split/data/diamond/constraints.json
@@ -1,42 +1,42 @@
 [
   {
     "type": "reference",
-    "name": "s1",
+    "name": "step1",
     "import": "memory:sdk/tests/web/native/main.dart#step1"
   },
   {
     "type": "reference",
-    "name": "s2a",
+    "name": "step2a",
     "import": "memory:sdk/tests/web/native/main.dart#step2a"
   },
   {
     "type": "reference",
-    "name": "s2b",
+    "name": "step2b",
     "import": "memory:sdk/tests/web/native/main.dart#step2b"
   },
   {
     "type": "reference",
-    "name": "s3",
+    "name": "step3",
     "import": "memory:sdk/tests/web/native/main.dart#step3"
   },
   {
     "type": "order",
-    "predecessor": "s1",
-    "successor": "s2a"
+    "predecessor": "step1",
+    "successor": "step2a"
   },
   {
     "type": "order",
-    "predecessor": "s1",
-    "successor": "s2b"
+    "predecessor": "step1",
+    "successor": "step2b"
   },
   {
     "type": "order",
-    "predecessor": "s2a",
-    "successor": "s3"
+    "predecessor": "step2a",
+    "successor": "step3"
   },
   {
     "type": "order",
-    "predecessor": "s2b",
-    "successor": "s3"
+    "predecessor": "step2b",
+    "successor": "step3"
   }
 ]
diff --git a/pkg/compiler/test/custom_split/data/diamond_and/constraints.dart b/pkg/compiler/test/custom_split/data/diamond_and/constraints.dart
new file mode 100644
index 0000000..b3a40ca
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/diamond_and/constraints.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2021, 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:isolate';
+
+import 'package:compiler/src/deferred_load/program_split_constraints/nodes.dart';
+import '../../constraint_harness.dart';
+
+void main(List<String> args, SendPort sendPort) {
+  waitForImportsAndInvoke(sendPort, processDeferredImports);
+}
+
+List<Node> processDeferredImports(List<String> imports) {
+  var builder = ProgramSplitBuilder();
+  return [
+    ...imports.map(builder.referenceNode),
+    builder.andNode('step2', ['step2a', 'step2b']),
+    builder.orderNode('step1', 'step2'),
+    builder.orderNode('step2', 'step3'),
+  ];
+}
diff --git a/pkg/compiler/test/custom_split/data/diamond_and/constraints.json b/pkg/compiler/test/custom_split/data/diamond_and/constraints.json
index 72d7e25..931a910 100644
--- a/pkg/compiler/test/custom_split/data/diamond_and/constraints.json
+++ b/pkg/compiler/test/custom_split/data/diamond_and/constraints.json
@@ -1,40 +1,40 @@
 [
   {
     "type": "reference",
-    "name": "s1",
+    "name": "step1",
     "import": "memory:sdk/tests/web/native/main.dart#step1"
   },
   {
     "type": "reference",
-    "name": "s2a",
+    "name": "step2a",
     "import": "memory:sdk/tests/web/native/main.dart#step2a"
   },
   {
     "type": "reference",
-    "name": "s2b",
+    "name": "step2b",
     "import": "memory:sdk/tests/web/native/main.dart#step2b"
   },
   {
     "type": "reference",
-    "name": "s3",
+    "name": "step3",
     "import": "memory:sdk/tests/web/native/main.dart#step3"
   },
   {
     "type": "and",
-    "name": "s2",
+    "name": "step2",
     "nodes": [
-      "s2a",
-      "s2b"
+      "step2a",
+      "step2b"
     ]
   },
   {
     "type": "order",
-    "predecessor": "s1",
-    "successor": "s2"
+    "predecessor": "step1",
+    "successor": "step2"
   },
   {
     "type": "order",
-    "predecessor": "s2",
-    "successor": "s3"
+    "predecessor": "step2",
+    "successor": "step3"
   }
 ]
diff --git a/pkg/compiler/test/custom_split/data/diamond_fuse/constraints.dart b/pkg/compiler/test/custom_split/data/diamond_fuse/constraints.dart
new file mode 100644
index 0000000..6fdf62e
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/diamond_fuse/constraints.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2021, 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:isolate';
+
+import 'package:compiler/src/deferred_load/program_split_constraints/nodes.dart';
+import '../../constraint_harness.dart';
+
+void main(List<String> args, SendPort sendPort) {
+  waitForImportsAndInvoke(sendPort, processDeferredImports);
+}
+
+List<Node> processDeferredImports(List<String> imports) {
+  var builder = ProgramSplitBuilder();
+  return [
+    ...imports.map(builder.referenceNode),
+    builder.fuseNode('step2', ['step2a', 'step2b']),
+    builder.orderNode('step1', 'step2'),
+    builder.orderNode('step2', 'step3'),
+  ];
+}
diff --git a/pkg/compiler/test/custom_split/data/diamond_fuse/constraints.json b/pkg/compiler/test/custom_split/data/diamond_fuse/constraints.json
index 9fbb3cd..f170d18 100644
--- a/pkg/compiler/test/custom_split/data/diamond_fuse/constraints.json
+++ b/pkg/compiler/test/custom_split/data/diamond_fuse/constraints.json
@@ -1,40 +1,40 @@
 [
   {
     "type": "reference",
-    "name": "s1",
+    "name": "step1",
     "import": "memory:sdk/tests/web/native/main.dart#step1"
   },
   {
     "type": "reference",
-    "name": "s2a",
+    "name": "step2a",
     "import": "memory:sdk/tests/web/native/main.dart#step2a"
   },
   {
     "type": "reference",
-    "name": "s2b",
+    "name": "step2b",
     "import": "memory:sdk/tests/web/native/main.dart#step2b"
   },
   {
     "type": "reference",
-    "name": "s3",
+    "name": "step3",
     "import": "memory:sdk/tests/web/native/main.dart#step3"
   },
   {
     "type": "fuse",
-    "name": "s2",
+    "name": "step2",
     "nodes": [
-      "s2a",
-      "s2b"
+      "step2a",
+      "step2b"
     ]
   },
   {
     "type": "order",
-    "predecessor": "s1",
-    "successor": "s2"
+    "predecessor": "step1",
+    "successor": "step2"
   },
   {
     "type": "order",
-    "predecessor": "s2",
-    "successor": "s3"
+    "predecessor": "step2",
+    "successor": "step3"
   }
 ]
diff --git a/pkg/compiler/test/custom_split/data/diamond_or/constraints.dart b/pkg/compiler/test/custom_split/data/diamond_or/constraints.dart
new file mode 100644
index 0000000..659bf73
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/diamond_or/constraints.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2021, 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:isolate';
+
+import 'package:compiler/src/deferred_load/program_split_constraints/nodes.dart';
+import '../../constraint_harness.dart';
+
+void main(List<String> args, SendPort sendPort) {
+  waitForImportsAndInvoke(sendPort, processDeferredImports);
+}
+
+List<Node> processDeferredImports(List<String> imports) {
+  var builder = ProgramSplitBuilder();
+  return [
+    ...imports.map(builder.referenceNode),
+    builder.orNode('step2', ['step2a', 'step2b']),
+    builder.orderNode('step1', 'step2'),
+    builder.orderNode('step2', 'step3'),
+  ];
+}
diff --git a/pkg/compiler/test/custom_split/data/diamond_or/constraints.json b/pkg/compiler/test/custom_split/data/diamond_or/constraints.json
index 310309a..5fcc300 100644
--- a/pkg/compiler/test/custom_split/data/diamond_or/constraints.json
+++ b/pkg/compiler/test/custom_split/data/diamond_or/constraints.json
@@ -1,40 +1,40 @@
 [
   {
     "type": "reference",
-    "name": "s1",
+    "name": "step1",
     "import": "memory:sdk/tests/web/native/main.dart#step1"
   },
   {
     "type": "reference",
-    "name": "s2a",
+    "name": "step2a",
     "import": "memory:sdk/tests/web/native/main.dart#step2a"
   },
   {
     "type": "reference",
-    "name": "s2b",
+    "name": "step2b",
     "import": "memory:sdk/tests/web/native/main.dart#step2b"
   },
   {
     "type": "reference",
-    "name": "s3",
+    "name": "step3",
     "import": "memory:sdk/tests/web/native/main.dart#step3"
   },
   {
     "type": "or",
-    "name": "s2",
+    "name": "step2",
     "nodes": [
-      "s2a",
-      "s2b"
+      "step2a",
+      "step2b"
     ]
   },
   {
     "type": "order",
-    "predecessor": "s1",
-    "successor": "s2"
+    "predecessor": "step1",
+    "successor": "step2"
   },
   {
     "type": "order",
-    "predecessor": "s2",
-    "successor": "s3"
+    "predecessor": "step2",
+    "successor": "step3"
   }
 ]
diff --git a/pkg/compiler/test/custom_split/data/two_branch/constraints.dart b/pkg/compiler/test/custom_split/data/two_branch/constraints.dart
new file mode 100644
index 0000000..1e6ad05
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/two_branch/constraints.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2021, 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:isolate';
+
+import 'package:compiler/src/deferred_load/program_split_constraints/nodes.dart';
+import '../../constraint_harness.dart';
+
+void main(List<String> args, SendPort sendPort) {
+  waitForImportsAndInvoke(sendPort, processDeferredImports);
+}
+
+List<Node> processDeferredImports(List<String> imports) {
+  var builder = ProgramSplitBuilder();
+  return [
+    ...imports.map(builder.referenceNode),
+    builder.orderNode('step1', 'step2a'),
+    builder.orderNode('step1', 'step2b'),
+  ];
+}
diff --git a/pkg/compiler/test/custom_split/data/two_branch/constraints.json b/pkg/compiler/test/custom_split/data/two_branch/constraints.json
index 542cf85..8b86071 100644
--- a/pkg/compiler/test/custom_split/data/two_branch/constraints.json
+++ b/pkg/compiler/test/custom_split/data/two_branch/constraints.json
@@ -1,27 +1,27 @@
 [
   {
     "type": "reference",
-    "name": "s1",
+    "name": "step1",
     "import": "memory:sdk/tests/web/native/main.dart#step1"
   },
   {
     "type": "reference",
-    "name": "s2a",
+    "name": "step2a",
     "import": "memory:sdk/tests/web/native/main.dart#step2a"
   },
   {
     "type": "reference",
-    "name": "s2b",
+    "name": "step2b",
     "import": "memory:sdk/tests/web/native/main.dart#step2b"
   },
   {
     "type": "order",
-    "predecessor": "s1",
-    "successor": "s2a"
+    "predecessor": "step1",
+    "successor": "step2a"
   },
   {
     "type": "order",
-    "predecessor": "s1",
-    "successor": "s2b"
+    "predecessor": "step1",
+    "successor": "step2b"
   }
 ]
diff --git a/pkg/compiler/test/custom_split/data/two_step/constraints.dart b/pkg/compiler/test/custom_split/data/two_step/constraints.dart
new file mode 100644
index 0000000..9089c5c
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/two_step/constraints.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2021, 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:isolate';
+
+import 'package:compiler/src/deferred_load/program_split_constraints/nodes.dart';
+import '../../constraint_harness.dart';
+
+void main(List<String> args, SendPort sendPort) {
+  waitForImportsAndInvoke(sendPort, processDeferredImports);
+}
+
+List<Node> processDeferredImports(List<String> imports) {
+  var builder = ProgramSplitBuilder();
+  return [
+    ...imports.map(builder.referenceNode),
+    builder.orderNode('step1', 'step2'),
+    builder.orderNode('step2', 'step3'),
+  ];
+}
diff --git a/pkg/compiler/test/custom_split/data/two_step/constraints.json b/pkg/compiler/test/custom_split/data/two_step/constraints.json
index 31c29a1..b72db44 100644
--- a/pkg/compiler/test/custom_split/data/two_step/constraints.json
+++ b/pkg/compiler/test/custom_split/data/two_step/constraints.json
@@ -1,27 +1,27 @@
 [
   {
     "type": "reference",
-    "name": "s1",
+    "name": "step1",
     "import": "memory:sdk/tests/web/native/main.dart#step1"
   },
   {
     "type": "reference",
-    "name": "s2",
+    "name": "step2",
     "import": "memory:sdk/tests/web/native/main.dart#step2"
   },
   {
     "type": "reference",
-    "name": "s3",
+    "name": "step3",
     "import": "memory:sdk/tests/web/native/main.dart#step3"
   },
   {
     "type": "order",
-    "predecessor": "s1",
-    "successor": "s2"
+    "predecessor": "step1",
+    "successor": "step2"
   },
   {
     "type": "order",
-    "predecessor": "s2",
-    "successor": "s3"
+    "predecessor": "step2",
+    "successor": "step3"
   }
 ]
diff --git a/pkg/compiler/test/equivalence/id_equivalence_helper.dart b/pkg/compiler/test/equivalence/id_equivalence_helper.dart
index 20993eb..d0288ee 100644
--- a/pkg/compiler/test/equivalence/id_equivalence_helper.dart
+++ b/pkg/compiler/test/equivalence/id_equivalence_helper.dart
@@ -121,7 +121,7 @@
 /// [entryPoint] and [memorySourceFiles].
 ///
 /// Actual data is computed using [computeMemberData].
-Future<CompiledData<T>> computeData<T>(Uri entryPoint,
+Future<CompiledData<T>> computeData<T>(String name, Uri entryPoint,
     Map<String, String> memorySourceFiles, DataComputer<T> dataComputer,
     {List<String> options: const <String>[],
     bool verbose: false,
@@ -130,7 +130,8 @@
     bool forUserLibrariesOnly: true,
     bool skipUnprocessedMembers: false,
     bool skipFailedCompilations: false,
-    Iterable<Id> globalIds: const <Id>[]}) async {
+    Iterable<Id> globalIds: const <Id>[],
+    Future<void> verifyCompiler(String test, Compiler compiler)}) async {
   OutputCollector outputCollector = new OutputCollector();
   DiagnosticCollector diagnosticCollector = new DiagnosticCollector();
   Uri packageConfig;
@@ -166,6 +167,9 @@
     print('------------------------------------------------------------------');
   }
   Compiler compiler = result.compiler;
+  if (verifyCompiler != null) {
+    await verifyCompiler(name, compiler);
+  }
 
   Map<Uri, Map<Id, ActualData<T>>> actualMaps = <Uri, Map<Id, ActualData<T>>>{};
   Map<Id, ActualData<T>> globalData = <Id, ActualData<T>>{};
@@ -403,7 +407,8 @@
     int shardIndex: 0,
     void onTest(Uri uri),
     List<TestConfig> testedConfigs = const [],
-    Map<String, List<String>> perTestOptions = const {}}) async {
+    Map<String, List<String>> perTestOptions = const {},
+    Future<void> verifyCompiler(String test, Compiler compiler)}) async {
   if (testedConfigs.isEmpty) testedConfigs = defaultInternalConfigs;
   Set<String> testedMarkers =
       testedConfigs.map((config) => config.marker).toSet();
@@ -455,7 +460,8 @@
             succinct: succinct,
             testAfterFailures: testAfterFailures,
             forUserLibrariesOnly: forUserLibrariesOnly,
-            printCode: printCode);
+            printCode: printCode,
+            verifyCompiler: verifyCompiler);
       }
     }
     return results;
@@ -484,17 +490,19 @@
     bool succinct: false,
     bool printCode: false,
     bool forUserLibrariesOnly: true,
-    bool testAfterFailures: false}) async {
+    bool testAfterFailures: false,
+    Future<void> verifyCompiler(String test, Compiler compiler)}) async {
   MemberAnnotations<IdValue> annotations =
       testData.expectedMaps[testConfiguration.marker];
-  CompiledData<T> compiledData = await computeData(
+  CompiledData<T> compiledData = await computeData(testData.name,
       testData.entryPoint, testData.memorySourceFiles, dataComputer,
       options: [...options, ...testConfiguration.options],
       verbose: verbose,
       printCode: printCode,
       testFrontend: dataComputer.testFrontend,
       forUserLibrariesOnly: forUserLibrariesOnly,
-      globalIds: annotations.globalData.keys);
+      globalIds: annotations.globalData.keys,
+      verifyCompiler: verifyCompiler);
   return await checkCode(testConfiguration.name, testData.testFileUri,
       testData.code, annotations, compiledData, dataComputer.dataValidator,
       filterActualData: filterActualData,
diff --git a/pkg/compiler/test/equivalence/show_helper.dart b/pkg/compiler/test/equivalence/show_helper.dart
index 754f2e2..ecc6257 100644
--- a/pkg/compiler/test/equivalence/show_helper.dart
+++ b/pkg/compiler/test/equivalence/show_helper.dart
@@ -52,7 +52,7 @@
     options.add(Flags.omitImplicitChecks);
   }
   Dart2jsCompiledData<T> data = await computeData<T>(
-      entryPoint, const {}, dataComputer,
+      file, entryPoint, const {}, dataComputer,
       options: options,
       testFrontend: dataComputer.testFrontend,
       forUserLibrariesOnly: false,
diff --git a/pkg/nnbd_migration/pubspec.yaml b/pkg/nnbd_migration/pubspec.yaml
index 6d8ac9d..19a70c2 100644
--- a/pkg/nnbd_migration/pubspec.yaml
+++ b/pkg/nnbd_migration/pubspec.yaml
@@ -7,28 +7,34 @@
   sdk: '>=2.14.0 <3.0.0'
 
 dependencies:
-  _fe_analyzer_shared:
-    path: ../_fe_analyzer_shared
-  analyzer:
-    path: ../analyzer
-  analyzer_plugin:
-    path: ../analyzer_plugin
-  args: ^1.4.4
+  _fe_analyzer_shared: any
+  analyzer: any
+  analyzer_plugin: any
+  args: ^2.3.0
   charcode: ^1.1.0
-  cli_util: ^0.2.0
-  collection: ^1.15.0-nullsafety.4
-  crypto: ^2.0.6
-  meta:
-    path: ../meta
+  cli_util: ^0.3.5
+  collection: ^1.15.0
+  crypto: ^3.0.1
+  meta: any
   path: ^1.6.2
-  pub_semver: ^1.4.2
+  pub_semver: ^2.1.0
   source_span: ^1.4.1
   yaml: any
 
 dev_dependencies:
   analyzer_utilities:
     path: ../analyzer_utilities
-  http: '>=0.11.3+17 <0.13.0'
+  http: ^0.13.4
   pedantic: ^1.9.0
   test: ^1.6.4
-  test_reflective_loader: ^0.1.8
+  test_reflective_loader: ^0.2.0
+
+dependency_overrides:
+  _fe_analyzer_shared:
+    path: ../_fe_analyzer_shared
+  analyzer:
+    path: ../analyzer
+  analyzer_plugin:
+    path: ../analyzer_plugin
+  meta:
+    path: ../meta
diff --git a/pkg/vm/lib/transformations/type_flow/analysis.dart b/pkg/vm/lib/transformations/type_flow/analysis.dart
index 089a4c9..61870d6 100644
--- a/pkg/vm/lib/transformations/type_flow/analysis.dart
+++ b/pkg/vm/lib/transformations/type_flow/analysis.dart
@@ -104,6 +104,9 @@
 
   _Invocation(this.selector, this.args);
 
+  /// Initialize invocation before it is cached and processed.
+  void init() {}
+
   Type process(TypeFlowAnalysis typeFlowAnalysis);
 
   /// Returns result of this invocation if its available without
@@ -186,21 +189,29 @@
 
 class _DirectInvocation extends _Invocation {
   _DirectInvocation(DirectSelector selector, Args<Type> args)
-      : super(selector, args) {
+      : super(selector, args);
+
+  @override
+  void init() {
     // We don't emit [TypeCheck] statements for bounds checks of type
     // parameters, so if there are any type parameters, we must assume
     // they could fail bounds checks.
     //
     // TODO(sjindel): Use [TypeCheck] to avoid bounds checks.
-    final function = selector.member.function;
+    final function = selector.member!.function;
     if (function != null) {
-      typeChecksNeeded =
-          function.typeParameters.any((t) => t.isCovariantByClass);
+      for (TypeParameter tp in function.typeParameters) {
+        if (tp.isCovariantByClass) {
+          typeChecksNeeded = true;
+        }
+      }
     } else {
       Field field = selector.member as Field;
       if (selector.callKind == CallKind.PropertySet) {
         // TODO(dartbug.com/40615): Use TFA results to improve this criterion.
-        typeChecksNeeded = field.isCovariantByClass;
+        if (field.isCovariantByClass) {
+          typeChecksNeeded = true;
+        }
       }
     }
   }
@@ -791,6 +802,7 @@
               _typeFlowAnalysis.summaryCollector.rawArguments(selector);
           sa.approximation =
               approximation = _DispatchableInvocation(selector, rawArgs);
+          approximation.init();
           Statistics.approximateInvocationsCreated++;
         }
         Statistics.approximateInvocationsUsed++;
@@ -802,6 +814,7 @@
           max(Statistics.maxInvocationsCachedPerSelector, sa.count);
     }
 
+    invocation.init();
     bool added = _invocations.add(invocation);
     assert(added);
     ++Statistics.invocationsAddedToCache;
diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
index 952eefe..a72ad85 100644
--- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
@@ -1669,23 +1669,35 @@
 
   @override
   TypeExpr visitEqualsCall(EqualsCall node) {
-    final left = _visit(node.left);
-    final right = _visit(node.right);
+    _addUse(_visit(node.left));
+    _addUse(_visit(node.right));
     final target = node.interfaceTarget;
+    // 'operator==' is a very popular method which can be called
+    // with a huge number of combinations of argument types.
+    // These invocations can be sensitive to changes in the set of allocated
+    // classes, causing a large number of invalidated invocations.
+    // In order to speed up the analysis, arguments of 'operator=='
+    // are approximated eagerly to static types during summary construction.
     return _makeCall(
         node,
         (node.left is ThisExpression)
             ? new VirtualSelector(target)
             : new InterfaceSelector(target),
-        Args<TypeExpr>([left, right]));
+        Args<TypeExpr>([_staticType(node.left), _staticType(node.right)]));
   }
 
   @override
   TypeExpr visitEqualsNull(EqualsNull node) {
     final arg = _visit(node.expression);
     _makeNarrowNotNull(node, arg);
+    // 'operator==' is a very popular method which can be called
+    // with a huge number of combinations of argument types.
+    // These invocations can be sensitive to changes in the set of allocated
+    // classes, causing a large number of invalidated invocations.
+    // In order to speed up the analysis, arguments of 'operator=='
+    // are approximated eagerly to static types during summary construction.
     _makeCall(node, DirectSelector(_environment.coreTypes.objectEquals),
-        Args<TypeExpr>([arg, _nullType]));
+        Args<TypeExpr>([_staticType(node.expression), _nullType]));
     return _boolType;
   }
 
@@ -1838,6 +1850,18 @@
         passTypeArguments: node.target.isFactory);
     final target = node.target;
     assert((target is! Field) && !target.isGetter && !target.isSetter);
+    if (target == _environment.coreTypes.identicalProcedure) {
+      assert(args.values.length == 2 && args.names.isEmpty);
+      // 'identical' is a very popular method which can be called
+      // with a huge number of combinations of argument types.
+      // Those invocations can be sensitive to changes in the set of allocated
+      // classes, causing a large number of invalidated invocations.
+      // In order to speed up the analysis, invocations of 'identical'
+      // are approximated eagerly during summary construction.
+      _makeCall(node, new DirectSelector(target),
+          Args<TypeExpr>([Type.nullableAny(), Type.nullableAny()]));
+      return _boolType;
+    }
     TypeExpr result = _makeCall(node, new DirectSelector(target), args);
     if (target == unsafeCast) {
       // Async transformation inserts unsafeCasts to make sure
diff --git a/pkg/vm/lib/transformations/type_flow/types.dart b/pkg/vm/lib/transformations/type_flow/types.dart
index 3ed7b9b..6db75a7 100644
--- a/pkg/vm/lib/transformations/type_flow/types.dart
+++ b/pkg/vm/lib/transformations/type_flow/types.dart
@@ -166,7 +166,7 @@
   factory Type.nullable(Type t) => new NullableType(t);
 
   /// Create a type representing arbitrary nullable object (`dynamic`).
-  factory Type.nullableAny() => new NullableType(const AnyType());
+  factory Type.nullableAny() => const NullableType(const AnyType());
 
   Class? getConcreteClass(TypeHierarchy typeHierarchy) => null;
 
@@ -253,9 +253,7 @@
 class NullableType extends Type {
   final Type baseType;
 
-  NullableType(this.baseType) {
-    assert(baseType is! NullableType);
-  }
+  const NullableType(this.baseType) : assert(baseType is! NullableType);
 
   @override
   int get hashCode => (baseType.hashCode + 31) & kHashMask;
diff --git a/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart.expect b/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart.expect
index 23f3ef4..4f6ab2a 100644
--- a/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/summary_collector/control_flow.dart.expect
@@ -112,8 +112,8 @@
 %x = _Parameter #0 [_T (dart.core::int)+?]
 %y = _Parameter #1 [_T (dart.core::String)+?]
 %z = _Parameter #2 [_T ANY?]
-t3* = _Call [dart.core::num.==] (%x, _T (dart.core::_Smi, 5))
-t4* = _Call [dart.core::String.==] (%y, _T (dart.core::_OneByteString, "hi"))
+t3* = _Call [dart.core::num.==] (_T (dart.core::int)+?, _T (dart.core::int)+?)
+t4* = _Call [dart.core::String.==] (_T (dart.core::String)+?, _T (dart.core::String)+?)
 t5 = _Call direct [dart.core::Object.==] (%z, _T {}?)
 t6 = _Narrow (%z to _T ANY)
 t7 = _Call direct [#lib::foo] (_T (dart.core::_Smi, 5))
@@ -127,7 +127,7 @@
 RESULT: _T {}?
 ------------ if9 ------------
 %x = _Parameter #0 [_T (#lib::TestEnum)+?]
-t1* = _Call [dart.core::Object.==] (%x, _T (#lib::TestEnum, const #lib::TestEnum{dart.core::_Enum.index: 0, dart.core::_Enum._name: "v1"}))
+t1* = _Call [dart.core::Object.==] (_T (#lib::TestEnum)+?, _T (#lib::TestEnum)+?)
 t2 = _Call direct [#lib::foo] (_T (#lib::TestEnum, const #lib::TestEnum{dart.core::_Enum.index: 0, dart.core::_Enum._name: "v1"}))
 RESULT: _T {}?
 ------------ conditional1 ------------
diff --git a/pkg/vm/testcases/transformations/type_flow/summary_collector/vars.dart.expect b/pkg/vm/testcases/transformations/type_flow/summary_collector/vars.dart.expect
index b559883..e5f2883 100644
--- a/pkg/vm/testcases/transformations/type_flow/summary_collector/vars.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/summary_collector/vars.dart.expect
@@ -14,7 +14,7 @@
 a1_0 = _Join [dart.core::Object] (t3, %a1)
 t5 = _Call direct [#lib::bar] (a1_0, _T (dart.core::_Smi, 42))
 t6* = _Call direct [#lib::B.] (_T (#lib::B))
-t7* = _Call [dart.core::Object.==] (t6, %a2)
+t7* = _Call [dart.core::Object.==] (_T (dart.core::Object)+?, _T (dart.core::Object)+?)
 t8 = _Join [dart.core::Object?] (t6, %a2)
 t9 = _Narrow (t8 to _T (dart.core::Object)+?)
 RESULT: t9
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/bench_is_prime.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/bench_is_prime.dart.expect
index 9ff69a2..1878d03 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/bench_is_prime.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/bench_is_prime.dart.expect
@@ -7,7 +7,7 @@
   if(_in::unsafeCast<core::bool>([@vm.direct-call.metadata=dart.core::_IntegerImplementation.<] [@vm.inferred-type.metadata=dart.core::bool] n{dynamic}.<(2)))
     return false;
   for (core::int i = 2; [@vm.direct-call.metadata=dart.core::_IntegerImplementation.<=] [@vm.inferred-type.metadata=dart.core::bool (skip check)] [@vm.direct-call.metadata=dart.core::_IntegerImplementation.*] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::*}(i){(core::num) → core::int}.{core::num::<=}(_in::unsafeCast<core::num>(n)){(core::num) → core::bool}; i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}) {
-    if([@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] [@vm.direct-call.metadata=dart.core::_IntegerImplementation.%] [@vm.inferred-type.metadata=int] n{dynamic}.%(i) =={core::Object::==}{(core::Object) → core::bool} 0)
+    if([@vm.inferred-type.metadata=dart.core::bool?] [@vm.direct-call.metadata=dart.core::_IntegerImplementation.%] [@vm.inferred-type.metadata=int] n{dynamic}.%(i) =={core::Object::==}{(core::Object) → core::bool} 0)
       return false;
   }
   return true;
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_cycle.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_cycle.dart.expect
index 4771de1..b822802 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_cycle.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_cycle.dart.expect
@@ -100,7 +100,7 @@
   self::ByteStream x = new self::ByteStream::•(new self::_ControllerStream::•());
   self::Stream y = [@vm.direct-call.metadata=#lib::ByteStream.super_stream] [@vm.inferred-type.metadata=!] x.{self::ByteStream::super_stream}{self::Stream};
   self::Stream z = [@vm.direct-call.metadata=#lib::StreamView._stream] [@vm.inferred-type.metadata=!] x.{self::StreamView::_stream}{self::Stream};
-  if([@vm.direct-call.metadata=dart.core::Object.==] [@vm.inferred-type.metadata=dart.core::bool (skip check) (receiver not int)] y =={core::Object::==}{(core::Object) → core::bool} z) {
+  if([@vm.inferred-type.metadata=dart.core::bool (skip check) (receiver not int)] y =={core::Object::==}{(core::Object) → core::bool} z) {
     [@vm.direct-call.metadata=#lib::ByteStream.super_foobar2] [@vm.inferred-type.metadata=!? (skip check)] x.{self::ByteStream::super_foobar2}(){((dynamic) →? void) → dynamic};
   }
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/encode_all_fields.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/encode_all_fields.dart.expect
index 73b0430..423420c 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/encode_all_fields.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/lib/encode_all_fields.dart.expect
@@ -29,7 +29,7 @@
   core::print("List<int> buffer = <int>[");
   for (core::int i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation.<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::<}([@vm.direct-call.metadata=dart.typed_data::_TypedListBase.length] [@vm.inferred-type.metadata=dart.core::_Smi] buffer.{core::List::length}{core::int}){(core::num) → core::bool}; i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(5){(core::num) → core::int}) {
     final core::String numbers = [@vm.direct-call.metadata=dart.typed_data::_TypedListBase.join] [@vm.inferred-type.metadata=!? (skip check)] [@vm.direct-call.metadata=dart.typed_data::__Uint8List&_TypedList&_IntListMixin&_TypedIntListMixin.sublist] [@vm.inferred-type.metadata=dart.typed_data::_Uint8List (skip check)] buffer.{typ::Uint8List::sublist}(i, [@vm.inferred-type.metadata=int] math::min<core::int>([@vm.direct-call.metadata=dart.typed_data::_TypedListBase.length] [@vm.inferred-type.metadata=dart.core::_Smi] buffer.{core::List::length}{core::int}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(5){(core::num) → core::int})){(core::int, [core::int?]) → typ::Uint8List}.{core::Iterable::join}(", "){([core::String]) → core::String};
-    core::print("  ${numbers},${[@vm.direct-call.metadata=dart.core::_IntegerImplementation.==] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i =={core::num::==}{(core::Object) → core::bool} 0 ?{core::String} " //" : ""}");
+    core::print("  ${numbers},${[@vm.inferred-type.metadata=dart.core::bool] i =={core::num::==}{(core::Object) → core::bool} 0 ?{core::String} " //" : ""}");
   }
   core::print("];");
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field2.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field2.dart.expect
index 3310998..4203e7a 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field2.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field2.dart.expect
@@ -61,7 +61,7 @@
   });
   self::E e = new self::F::•();
   let final core::int #t3 = [@vm.direct-call.metadata=#lib::F.bar] [@vm.inferred-type.metadata=dart.core::_Smi (value: 42)] e.{self::E::bar}{core::int} in exp::Expect::equals();
-  exp::Expect::isTrue(![@vm.inferred-type.metadata=dart.core::bool] core::identical(#C2, #C4));
+  exp::Expect::isTrue(!core::identical(#C2, #C4));
   [@vm.direct-call.metadata=#lib::I.foo] [@vm.inferred-type.metadata=!? (skip check)] new self::I::•().{self::I::foo}(){() → dynamic};
   5;
 }
diff --git a/runtime/bin/process_fuchsia.cc b/runtime/bin/process_fuchsia.cc
index de4433c..4b8f59d 100644
--- a/runtime/bin/process_fuchsia.cc
+++ b/runtime/bin/process_fuchsia.cc
@@ -756,12 +756,13 @@
         actions[fixed_actions_cnt + i] = {
           .action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY,
           .ns = {
-            .prefix = flat_ns->path[i],
+            .prefix = DartUtils::ScopedCopyCString(flat_ns->path[i]),
             .handle = flat_ns->handle[i],
           },
         };
+        flat_ns->handle[i] = ZX_HANDLE_INVALID;
       }
-      free(flat_ns);
+      fdio_ns_free_flat_ns(flat_ns);
       flat_ns = nullptr;
     }
 
diff --git a/tools/VERSION b/tools/VERSION
index 772fd4d..dd9fa8f 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 257
+PRERELEASE 258
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/package_deps/bin/package_deps.dart b/tools/package_deps/bin/package_deps.dart
index eab4201..d0a8abc 100644
--- a/tools/package_deps/bin/package_deps.dart
+++ b/tools/package_deps/bin/package_deps.dart
@@ -1,6 +1,7 @@
 import 'dart:io';
 
 import 'package:cli_util/cli_logging.dart';
+import 'package:collection/collection.dart';
 import 'package:path/path.dart' as path;
 import 'package:yaml/yaml.dart' as yaml;
 
@@ -85,34 +86,59 @@
   }
 
   if (validateFailure) {
-    exit(1);
+    exitCode = 1;
   }
 }
 
 class Package implements Comparable<Package> {
   final String dir;
+  final _regularDependencies = <String>{};
+  final _devDependencies = <String>{};
+  final _declaredPubDeps = <PubDep>[];
+  final _declaredDevPubDeps = <PubDep>[];
+  final _declaredOverridePubDeps = <PubDep>[];
+
+  late final String _packageName;
+  late final Set<String> _declaredDependencies;
+  late final Set<String> _declaredDevDependencies;
+  late final Set<String> _declaredOverrideDependencies;
+  late final bool _publishToNone;
 
   Package(this.dir) {
-    _parsePubspec();
+    var pubspec = File(path.join(dir, 'pubspec.yaml'));
+    var doc = yaml.loadYamlDocument(pubspec.readAsStringSync());
+    dynamic docContents = doc.contents.value;
+    _packageName = docContents['name'];
+    _publishToNone = docContents['publish_to'] == 'none';
+
+    Set<String> process(String section, List<PubDep> target) {
+      if (docContents[section] != null) {
+        final value = Set<String>.from(docContents[section].keys);
+
+        var deps = docContents[section];
+        for (var package in deps.keys) {
+          target.add(PubDep.parse(package, deps[package]));
+        }
+
+        return value;
+      } else {
+        return {};
+      }
+    }
+
+    _declaredDependencies = process('dependencies', _declaredPubDeps);
+    _declaredDevDependencies = process('dev_dependencies', _declaredDevPubDeps);
+    _declaredOverrideDependencies =
+        process('dependency_overrides', _declaredOverridePubDeps);
   }
 
   String get dirName => path.basename(dir);
-  final Set<String> _regularDependencies = {};
-  final Set<String> _devDependencies = {};
-  String _packageName;
-
   String get packageName => _packageName;
-  Set<String> _declaredDependencies;
-  List<PubDep> _declaredPubDeps;
-  Set<String> _declaredDevDependencies;
-  List<PubDep> _declaredDevPubDeps;
 
   List<String> get regularDependencies => _regularDependencies.toList()..sort();
 
   List<String> get devDependencies => _devDependencies.toList()..sort();
 
-  bool _publishToNone;
-
   bool get publishable => !_publishToNone;
 
   @override
@@ -161,40 +187,6 @@
     }
   }
 
-  void _parsePubspec() {
-    var pubspec = File(path.join(dir, 'pubspec.yaml'));
-    var doc = yaml.loadYamlDocument(pubspec.readAsStringSync());
-    dynamic docContents = doc.contents.value;
-    _packageName = docContents['name'];
-    _publishToNone = docContents['publish_to'] == 'none';
-
-    _declaredPubDeps = [];
-    if (docContents['dependencies'] != null) {
-      _declaredDependencies =
-          Set<String>.from(docContents['dependencies'].keys);
-
-      var deps = docContents['dependencies'];
-      for (var package in deps.keys) {
-        _declaredPubDeps.add(PubDep.parse(package, deps[package]));
-      }
-    } else {
-      _declaredDependencies = {};
-    }
-
-    _declaredDevPubDeps = [];
-    if (docContents['dev_dependencies'] != null) {
-      _declaredDevDependencies =
-          Set<String>.from(docContents['dev_dependencies'].keys);
-
-      var deps = docContents['dev_dependencies'];
-      for (var package in deps.keys) {
-        _declaredDevPubDeps.add(PubDep.parse(package, deps[package]));
-      }
-    } else {
-      _declaredDevDependencies = {};
-    }
-  }
-
   bool _validatePubspecDeps(Logger logger, List<String> pkgPackages) {
     var fail = false;
 
@@ -300,6 +292,13 @@
     if (!publishable) {
       for (PubDep dep in [..._declaredPubDeps, ..._declaredDevPubDeps]) {
         if (pkgPackages.contains(dep.name) && dep is! PathPubDep) {
+          // check to see if there is a dependency_override to a path dependency
+          final override = _declaredOverridePubDeps
+              .singleWhereOrNull((element) => element.name == dep.name);
+          if (override != null && override is PathPubDep) {
+            continue;
+          }
+
           out('  Prefer a relative path dep for pkg/ packages:');
           out('    $dep');
           fail = true;
@@ -368,13 +367,13 @@
 
       var match = importRegex1.firstMatch(line);
       if (match != null) {
-        results.add(match.group(2));
+        results.add(match.group(2)!);
         continue;
       }
 
       match = importRegex2.firstMatch(line);
       if (match != null) {
-        results.add(match.group(2));
+        results.add(match.group(2)!);
         continue;
       }
     }
@@ -418,9 +417,9 @@
       var testedPkgDep = testedPkgRegExp.firstMatch(line);
 
       if (pkgDep != null) {
-        pkgs.add(pkgDep.group(1));
+        pkgs.add(pkgDep.group(1)!);
       } else if (testedPkgDep != null) {
-        testedPkgs.add(testedPkgDep.group(1));
+        testedPkgs.add(testedPkgDep.group(1)!);
       }
     }
 
diff --git a/tools/package_deps/pubspec.yaml b/tools/package_deps/pubspec.yaml
index ef104c9..184795e 100644
--- a/tools/package_deps/pubspec.yaml
+++ b/tools/package_deps/pubspec.yaml
@@ -5,12 +5,10 @@
 publish_to: none
 
 environment:
-  sdk: '>=2.8.1 <3.0.0'
+  sdk: '>=2.12.0 <3.0.0'
 
 dependencies:
   cli_util: any
+  collection: any
   path: any
   yaml: any
-
-dev_dependencies:
-  pedantic: ^1.9.0