Version 2.18.0-45.0.dev

Merge commit '9846fe2621b25e6c183dbc3a6536a0b1ae39fb86' into 'dev'
diff --git a/.github/ISSUE_TEMPLATE/1_cherry_pick.yml b/.github/ISSUE_TEMPLATE/1_cherry_pick.yml
new file mode 100644
index 0000000..a1c9afd
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/1_cherry_pick.yml
@@ -0,0 +1,69 @@
+name: Request a cherry-pick.
+description: You would like to request that a feature be cherry-picked into a release.
+title: '[CP] <title>'
+labels: ['cherry-pick-review']
+assignees:
+  - mit-mit
+  - whesse
+  - athomas
+  - vsmenon
+  - itsjustkevin
+body:
+  - type: input
+    id: commit_hash
+    attributes:
+      label: Commit(s) to merge
+      description: What are the shortened commit hash(es) that have been merged to main?
+    validations:
+      required: true
+  - type: input
+    id: target
+    attributes:
+      label: Target
+      description: Should this be cherry-picked to beta, stable or both??
+    validations:
+      required: true
+  - type: textarea
+    id: issue_description
+    attributes:
+      label: Issue Description
+      description: Brief description of the issue.  What is the issue? What platforms are the problems occuring on?
+    validations:
+      required: true
+  - type: textarea
+    id: fix
+    attributes:
+      label: What is the fix
+      description: Brief description of the fix.
+    validations:
+      required: true
+  - type: textarea
+    id: why
+    attributes:
+      label: Why cherry-pick
+      description: Describe the reasons, impacted users and functional issues to explain why this should be cherry-picked.
+    validations:
+      required: true
+  - type: dropdown
+    id: risk
+    attributes:
+      label: Risk
+      description: What is the risk level of this cherry-pick?
+      options:
+        - low
+        - medium
+        - high
+    validations:
+      required: true
+  - type: input
+    id: original_issue
+    attributes:
+      label: Issue link(s)
+      description: Add links to the original issues fixed by this cp
+    validations:
+      required: true
+  - type: textarea
+    id: extra_info
+    attributes:
+      label: Extra Info
+      description: Is there anything else we need to know about this cherry-pick?
diff --git a/DEPS b/DEPS
index f2884cf..7b77af9 100644
--- a/DEPS
+++ b/DEPS
@@ -77,7 +77,7 @@
 
   # Revisions of /third_party/* dependencies.
   "args_rev": "3b3f55766af13d895d2020ec001a28e8dc147f91",
-  "async_rev": "3f58c326bd4928fca9ddc10c72b19cb7ac659256",
+  "async_rev": "f3ed5f690e2ec9dbe1bfc5184705575b4f6480e5",
   "bazel_worker_rev": "ceeba0982d4ff40d32371c9d35f3d2dc1868de20",
   "benchmark_harness_rev": "c546dbd9f639f75cd2f75de8df2eb9f8ea15e8e7",
   "boolean_selector_rev": "437e7f06c7e416bed91e16ae1df453555897e945",
@@ -133,7 +133,6 @@
   "oauth2_rev": "7cd3284049fe5badbec9f2bea2afc41d14c01057",
   "package_config_rev": "8731bf10b5375542792a32a0f7c8a6f370583d96",
   "path_rev": "baedce9d2ca11ea2cdf54395a74eb038087777a4",
-  "pedantic_rev": "66f2f6c27581c7936482e83be80b27be2719901c",
   "platform_rev": "1ffad63428bbd1b3ecaa15926bacfb724023648c",
   "ply_rev": "604b32590ffad5cbb82e4afef1d305512d06ae93",
   "pool_rev": "7abe634002a1ba8a0928eded086062f1307ccfae",
@@ -160,7 +159,7 @@
   "test_descriptor_rev": "ead23c1e7df079ac0f6457a35f7a71432892e527",
   "test_process_rev": "7c73ec8a8a6e0e63d0ec27d70c21ca4323fb5e8f",
   "test_reflective_loader_rev": "fcfce37666672edac849d2af6dffc0f8df236a94",
-  "test_rev": "b6aba5544628730b7d6a38eae1aef9117a1bb235",
+  "test_rev": "d54846bc2b5cfa4e1445fda85c5e48a00940aa68",
   "typed_data_rev": "29ce5a92b03326d0b8035916ac04f528874994bd",
   "usage_rev": "e85d575d6decb921c57a43b9844bba3607479f56",
   "vector_math_rev": "0cbed0914d49a6a44555e6d5444c438a4a4c3fc1",
@@ -380,8 +379,6 @@
       "@" + Var("package_config_rev"),
   Var("dart_root") + "/third_party/pkg/path":
       Var("dart_git") + "path.git" + "@" + Var("path_rev"),
-  Var("dart_root") + "/third_party/pkg/pedantic":
-      Var("dart_git") + "pedantic.git" + "@" + Var("pedantic_rev"),
   Var("dart_root") + "/third_party/pkg/platform":
        Var("dart_git") + "platform.dart.git" + "@" + Var("platform_rev"),
   Var("dart_root") + "/third_party/pkg/pool":
diff --git a/benchmarks/Startup/dart/Startup.dart b/benchmarks/Startup/dart/Startup.dart
index 49889fb..7beaede 100644
--- a/benchmarks/Startup/dart/Startup.dart
+++ b/benchmarks/Startup/dart/Startup.dart
@@ -76,7 +76,7 @@
       print(ends.toList());
       throw '$name is missing or ambiguous';
     }
-    print('Startup.$name(RunTime): $micros us.');
+    print('Startup.$name(RunTimeRaw): $micros us.');
   }
 
   report('CreateIsolateGroupAndSetupHelper', null);
diff --git a/benchmarks/Startup/dart2/Startup.dart b/benchmarks/Startup/dart2/Startup.dart
index 26fb07b..3220928 100644
--- a/benchmarks/Startup/dart2/Startup.dart
+++ b/benchmarks/Startup/dart2/Startup.dart
@@ -78,7 +78,7 @@
       print(ends.toList());
       throw '$name is missing or ambiguous';
     }
-    print('Startup.$name(RunTime): $micros us.');
+    print('Startup.$name(RunTimeRaw): $micros us.');
   }
 
   report('CreateIsolateGroupAndSetupHelper', null);
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
index de81796..cd0889c 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
@@ -57,16 +57,21 @@
   FunctionBodyCode.fromParts(List<Object> parts) : super.fromParts(parts);
 }
 
-/// A piece of code identifying a syntactically valid function parameter.
+/// A piece of code identifying a syntactically valid function or function type
+/// parameter.
 ///
 /// There is no distinction here made between named and positional parameters.
 ///
+/// There is also no distinction between function type parameters and normal
+/// function parameters, so the [name] is nullable (it is not required for
+/// positional function type parameters).
+///
 /// It is the job of the user to construct and combine these together in a way
 /// that creates valid parameter lists.
 class ParameterCode implements Code {
   final Code? defaultValue;
   final List<String> keywords;
-  final String name;
+  final String? name;
   final TypeAnnotationCode? type;
 
   @override
@@ -82,7 +87,7 @@
           type!,
           ' ',
         ],
-        name,
+        if (name != null) name!,
         if (defaultValue != null) ...[
           ' = ',
           defaultValue!,
@@ -92,7 +97,7 @@
   ParameterCode({
     this.defaultValue,
     this.keywords = const [],
-    required this.name,
+    this.name,
     this.type,
   });
 }
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
index cc9ef15..131c21e 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
@@ -35,10 +35,10 @@
   TypeAnnotation get returnType;
 
   /// The positional parameters for this function.
-  Iterable<ParameterDeclaration> get positionalParameters;
+  Iterable<FunctionTypeParameter> get positionalParameters;
 
   /// The named parameters for this function.
-  Iterable<ParameterDeclaration> get namedParameters;
+  Iterable<FunctionTypeParameter> get namedParameters;
 
   /// The type parameters for this function.
   Iterable<TypeParameterDeclaration> get typeParameters;
@@ -197,8 +197,9 @@
 abstract class FieldDeclaration
     implements VariableDeclaration, ClassMemberDeclaration {}
 
-/// Parameter introspection information.
-abstract class ParameterDeclaration implements Declaration {
+/// General parameter introspection information, see the subtypes
+/// [FunctionTypeParameter] and [ParameterDeclaration].
+abstract class Parameter {
   /// The type of this parameter.
   TypeAnnotation get type;
 
@@ -216,6 +217,16 @@
   ParameterCode get code;
 }
 
+/// Parameters of normal functions/methods, which always have an identifier.
+abstract class ParameterDeclaration implements Parameter, Declaration {}
+
+/// Function type parameters don't always have names, and it is never useful to
+/// get an [Identifier] for them, so they do not implement [Declaration] and
+/// instead have an optional name.
+abstract class FunctionTypeParameter implements Parameter {
+  String? get name;
+}
+
 /// Type parameter introspection information.
 abstract class TypeParameterDeclaration implements Declaration {
   /// The bound for this type parameter, if it has any.
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/introspection_impls.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/introspection_impls.dart
index 29c443b..4aace85 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/introspection_impls.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/introspection_impls.dart
@@ -94,21 +94,21 @@
           typeParam.code,
       ],
       positionalParameters: [
-        for (ParameterDeclaration positional in positionalParameters)
+        for (FunctionTypeParameter positional in positionalParameters)
           positional.code,
       ],
       namedParameters: [
-        for (ParameterDeclaration named in namedParameters) named.code,
+        for (FunctionTypeParameter named in namedParameters) named.code,
       ],
     );
     return isNullable ? underlyingType.asNullable : underlyingType;
   }
 
   @override
-  final List<ParameterDeclarationImpl> namedParameters;
+  final List<FunctionTypeParameterImpl> namedParameters;
 
   @override
-  final List<ParameterDeclarationImpl> positionalParameters;
+  final List<FunctionTypeParameterImpl> positionalParameters;
 
   @override
   final TypeAnnotationImpl returnType;
@@ -137,13 +137,13 @@
     returnType.serialize(serializer);
 
     serializer.startList();
-    for (ParameterDeclarationImpl param in positionalParameters) {
+    for (FunctionTypeParameterImpl param in positionalParameters) {
       param.serialize(serializer);
     }
     serializer.endList();
 
     serializer.startList();
-    for (ParameterDeclarationImpl param in namedParameters) {
+    for (FunctionTypeParameterImpl param in namedParameters) {
       param.serialize(serializer);
     }
     serializer.endList();
@@ -223,6 +223,50 @@
       ]);
 }
 
+class FunctionTypeParameterImpl extends RemoteInstance
+    implements FunctionTypeParameter {
+  @override
+  final bool isNamed;
+
+  @override
+  final bool isRequired;
+
+  @override
+  final String? name;
+
+  @override
+  final TypeAnnotationImpl type;
+
+  @override
+  RemoteInstanceKind get kind => RemoteInstanceKind.functionTypeParameter;
+
+  FunctionTypeParameterImpl({
+    required int id,
+    required this.isNamed,
+    required this.isRequired,
+    required this.name,
+    required this.type,
+  }) : super(id);
+
+  @override
+  void serialize(Serializer serializer) {
+    super.serialize(serializer);
+    // Client side we don't encode anything but the ID.
+    if (serializationMode.isClient) return;
+
+    serializer.addBool(isNamed);
+    serializer.addBool(isRequired);
+    serializer.addNullableString(name);
+    type.serialize(serializer);
+  }
+
+  @override
+  ParameterCode get code =>
+      new ParameterCode(name: name, type: type.code, keywords: [
+        if (isNamed && isRequired) 'required',
+      ]);
+}
+
 class TypeParameterDeclarationImpl extends DeclarationImpl
     implements TypeParameterDeclaration {
   @override
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/remote_instance.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/remote_instance.dart
index 4ec67e4..666ff65 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/remote_instance.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/remote_instance.dart
@@ -95,6 +95,7 @@
   fieldDeclaration,
   functionDeclaration,
   functionTypeAnnotation,
+  functionTypeParameter,
   identifier,
   identifierResolver,
   namedStaticType,
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart
index c51175a..9ac4f12 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart
@@ -40,6 +40,9 @@
       case RemoteInstanceKind.functionTypeAnnotation:
         moveNext();
         return _expectFunctionTypeAnnotation(id) as T;
+      case RemoteInstanceKind.functionTypeParameter:
+        moveNext();
+        return _expectFunctionTypeParameter(id) as T;
       case RemoteInstanceKind.identifier:
         moveNext();
         return _expectIdentifier(id) as T;
@@ -103,6 +106,15 @@
         typeParameters: (this..moveNext())._expectRemoteInstanceList(),
       );
 
+  FunctionTypeParameter _expectFunctionTypeParameter(int id) =>
+      new FunctionTypeParameterImpl(
+        id: id,
+        isNamed: expectBool(),
+        isRequired: (this..moveNext()).expectBool(),
+        name: (this..moveNext()).expectNullableString(),
+        type: RemoteInstance.deserialize(this),
+      );
+
   Identifier _expectIdentifier(int id) => new IdentifierImpl(
         id: id,
         name: expectString(),
@@ -294,7 +306,7 @@
         return new ParameterCode(
             defaultValue: (this..moveNext()).expectNullableCode(),
             keywords: _readStringList(),
-            name: (this..moveNext()).expectString(),
+            name: (this..moveNext()).expectNullableString(),
             type: (this..moveNext()).expectNullableCode()) as T;
       case CodeKind.typeParameter:
         return new TypeParameterCode(
@@ -384,7 +396,7 @@
         }
         serializer
           ..endList()
-          ..addString(self.name);
+          ..addNullableString(self.name);
         self.type.serializeNullable(serializer);
         return;
       case CodeKind.typeParameter:
diff --git a/pkg/_fe_analyzer_shared/test/macros/executor/serialization_test.dart b/pkg/_fe_analyzer_shared/test/macros/executor/serialization_test.dart
index eb74a8e..b667364 100644
--- a/pkg/_fe_analyzer_shared/test/macros/executor/serialization_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/executor/serialization_test.dart
@@ -166,6 +166,12 @@
             identifier:
                 IdentifierImpl(id: RemoteInstance.uniqueId, name: 'foo'),
             type: fooType);
+        final fooNamedFunctionTypeParam = FunctionTypeParameterImpl(
+            id: RemoteInstance.uniqueId,
+            isNamed: true,
+            isRequired: true,
+            name: 'foo',
+            type: fooType);
 
         final barPositionalParam = ParameterDeclarationImpl(
             id: RemoteInstance.uniqueId,
@@ -174,6 +180,19 @@
             identifier:
                 IdentifierImpl(id: RemoteInstance.uniqueId, name: 'bar'),
             type: barType);
+        final barPositionalFunctionTypeParam = FunctionTypeParameterImpl(
+            id: RemoteInstance.uniqueId,
+            isNamed: true,
+            isRequired: true,
+            name: 'bar',
+            type: fooType);
+
+        final unnamedFunctionTypeParam = FunctionTypeParameterImpl(
+            id: RemoteInstance.uniqueId,
+            isNamed: true,
+            isRequired: true,
+            name: null,
+            type: fooType);
 
         final zapTypeParam = TypeParameterDeclarationImpl(
             id: RemoteInstance.uniqueId,
@@ -187,8 +206,11 @@
           var functionType = FunctionTypeAnnotationImpl(
             id: RemoteInstance.uniqueId,
             isNullable: true,
-            namedParameters: [fooNamedParam],
-            positionalParameters: [barPositionalParam],
+            namedParameters: [
+              fooNamedFunctionTypeParam,
+              unnamedFunctionTypeParam
+            ],
+            positionalParameters: [barPositionalFunctionTypeParam],
             returnType: fooType,
             typeParameters: [zapTypeParam],
           );
diff --git a/pkg/_js_interop_checks/lib/js_interop_checks.dart b/pkg/_js_interop_checks/lib/js_interop_checks.dart
index 351c410..34a6b09 100644
--- a/pkg/_js_interop_checks/lib/js_interop_checks.dart
+++ b/pkg/_js_interop_checks/lib/js_interop_checks.dart
@@ -41,6 +41,7 @@
   /// Libraries that use `external` to exclude from checks on external.
   static final Iterable<String> _pathsWithAllowedDartExternalUsage = <String>[
     '_foreign_helper', // for foreign helpers
+    '_late_helper', // for dart2js late variable utilities
     '_interceptors', // for ddc JS string
     '_native_typed_data',
     '_runtime', // for ddc types at runtime
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index 44c74aa..3411254 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -50,6 +50,7 @@
 import 'package:analysis_server/src/services/completion/completion_state.dart';
 import 'package:analysis_server/src/services/execution/execution_context.dart';
 import 'package:analysis_server/src/services/flutter/widget_descriptions.dart';
+import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analysis_server/src/utilities/process.dart';
 import 'package:analysis_server/src/utilities/request_statistics.dart';
 import 'package:analyzer/dart/analysis/results.dart';
@@ -144,6 +145,12 @@
   /// The state used by the completion domain handlers.
   final CompletionState completionState = CompletionState();
 
+  /// The workspace for rename refactorings.
+  late RefactoringWorkspace refactoringWorkspace;
+
+  /// The object used to manage uncompleted refactorings.
+  late RefactoringManager? _refactoringManager;
+
   /// The context used by the execution domain handlers.
   final ExecutionContext executionContext = ExecutionContext();
 
@@ -266,6 +273,8 @@
       CompletionDomainHandler(this),
       FlutterDomainHandler(this)
     ];
+    refactoringWorkspace = RefactoringWorkspace(driverMap.values, searchEngine);
+    _newRefactoringManager();
   }
 
   /// The analytics instance; note, this object can be `null`.
@@ -292,6 +301,18 @@
     return _onAnalysisStartedController.stream;
   }
 
+  RefactoringManager? get refactoringManager {
+    var refactoringManager = _refactoringManager;
+    if (refactoringManager == null) {
+      return null;
+    }
+    if (refactoringManager.hasPendingRequest) {
+      refactoringManager.cancel();
+      _newRefactoringManager();
+    }
+    return _refactoringManager;
+  }
+
   String get sdkPath {
     return sdkManager.defaultSdkDirectory;
   }
@@ -692,6 +713,11 @@
     return flutterServices[service]?.contains(file) ?? false;
   }
 
+  /// Initializes [_refactoringManager] with a new instance.
+  void _newRefactoringManager() {
+    _refactoringManager = RefactoringManager(this, refactoringWorkspace);
+  }
+
   Future<void> _scheduleAnalysisImplementedNotification() async {
     var files = analysisServices[AnalysisService.IMPLEMENTED];
     if (files != null) {
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index a7ea8fd..dc3afe6 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -183,10 +183,8 @@
   final Map<Folder, AnalysisDriver> driverMap =
       HashMap<Folder, AnalysisDriver>();
 
-  /// Stream subscription we are using to watch each analysis root directory for
-  /// changes.
-  final Map<Folder, StreamSubscription<WatchEvent>> changeSubscriptions =
-      <Folder, StreamSubscription<WatchEvent>>{};
+  /// Subscriptions to watch included resources for changes.
+  final List<StreamSubscription<WatchEvent>> watcherSubscriptions = [];
 
   /// For each folder, stores the subscription to the Bazel workspace so that we
   /// can establish watches for the generated files.
@@ -468,10 +466,16 @@
           var rootFolder = analysisContext.contextRoot.root;
           driverMap[rootFolder] = driver;
 
-          var watcher = rootFolder.watch();
-          watchers.add(watcher);
-          changeSubscriptions[rootFolder] = watcher.changes
-              .listen(_handleWatchEvent, onError: _handleWatchInterruption);
+          for (final included in analysisContext.contextRoot.included) {
+            final watcher = included.watch();
+            watchers.add(watcher);
+            watcherSubscriptions.add(
+              watcher.changes.listen(
+                _handleWatchEvent,
+                onError: _handleWatchInterruption,
+              ),
+            );
+          }
 
           _watchBazelFilesIfNeeded(rootFolder, driver);
 
@@ -582,7 +586,6 @@
   void _destroyAnalysisContext(DriverBasedAnalysisContext context) {
     context.driver.dispose();
     var rootFolder = context.contextRoot.root;
-    changeSubscriptions.remove(rootFolder)?.cancel();
     var watched = bazelWatchedPathsPerFolder.remove(rootFolder);
     if (watched != null) {
       for (var path in watched.paths) {
@@ -597,6 +600,9 @@
   void _destroyAnalysisContexts() {
     var collection = _collection;
     if (collection != null) {
+      for (final subscription in watcherSubscriptions) {
+        subscription.cancel();
+      }
       for (var analysisContext in collection.contexts) {
         _destroyAnalysisContext(analysisContext);
       }
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index dd858f1..87660ab 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -12,8 +12,10 @@
 import 'package:analysis_server/src/handler/legacy/edit_format.dart';
 import 'package:analysis_server/src/handler/legacy/edit_format_if_enabled.dart';
 import 'package:analysis_server/src/handler/legacy/edit_get_assists.dart';
+import 'package:analysis_server/src/handler/legacy/edit_get_available_refactorings.dart';
 import 'package:analysis_server/src/handler/legacy/edit_get_fixes.dart';
 import 'package:analysis_server/src/handler/legacy/edit_get_postfix_completion.dart';
+import 'package:analysis_server/src/handler/legacy/edit_get_refactoring.dart';
 import 'package:analysis_server/src/handler/legacy/edit_get_statement_completion.dart';
 import 'package:analysis_server/src/handler/legacy/edit_import_elements.dart';
 import 'package:analysis_server/src/handler/legacy/edit_is_postfix_completion_applicable.dart';
@@ -43,19 +45,9 @@
 /// Instances of the class [EditDomainHandler] implement a [RequestHandler]
 /// that handles requests in the edit domain.
 class EditDomainHandler extends AbstractRequestHandler {
-  /// The workspace for rename refactorings.
-  RefactoringWorkspace? refactoringWorkspace;
-
-  /// The object used to manage uncompleted refactorings.
-  _RefactoringManager? refactoringManager;
-
   /// Initialize a newly created handler to handle requests for the given
   /// [server].
-  EditDomainHandler(AnalysisServer server) : super(server) {
-    refactoringWorkspace =
-        RefactoringWorkspace(server.driverMap.values, server.searchEngine);
-    _newRefactoringManager();
-  }
+  EditDomainHandler(super.server);
 
   @override
   Response? handleRequest(
@@ -72,7 +64,8 @@
         EditGetAssistsHandler(server, request, cancellationToken).handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == EDIT_REQUEST_GET_AVAILABLE_REFACTORINGS) {
-        _getAvailableRefactorings(request);
+        EditGetAvailableRefactoringsHandler(server, request, cancellationToken)
+            .handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == EDIT_REQUEST_BULK_FIXES) {
         EditBulkFixes(server, request, cancellationToken).handle();
@@ -81,7 +74,8 @@
         EditGetFixesHandler(server, request, cancellationToken).handle();
         return Response.DELAYED_RESPONSE;
       } else if (requestName == EDIT_REQUEST_GET_REFACTORING) {
-        return _getRefactoring(request, cancellationToken);
+        EditGetRefactoringHandler(server, request, cancellationToken).handle();
+        return Response.DELAYED_RESPONSE;
       } else if (requestName == EDIT_REQUEST_IMPORT_ELEMENTS) {
         EditImportElementsHandler(server, request, cancellationToken).handle();
         return Response.DELAYED_RESPONSE;
@@ -117,95 +111,6 @@
     }
     return null;
   }
-
-  Future<void> _getAvailableRefactorings(Request request) async {
-    var params = EditGetAvailableRefactoringsParams.fromRequest(request);
-    var file = params.file;
-    var offset = params.offset;
-    var length = params.length;
-
-    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
-      return;
-    }
-
-    // add refactoring kinds
-    var kinds = <RefactoringKind>[];
-    // Check nodes.
-    final searchEngine = server.searchEngine;
-    {
-      var resolvedUnit = await server.getResolvedUnit(file);
-      if (resolvedUnit != null) {
-        // Try EXTRACT_LOCAL_VARIABLE.
-        if (ExtractLocalRefactoring(resolvedUnit, offset, length)
-            .isAvailable()) {
-          kinds.add(RefactoringKind.EXTRACT_LOCAL_VARIABLE);
-        }
-        // Try EXTRACT_METHOD.
-        if (ExtractMethodRefactoring(searchEngine, resolvedUnit, offset, length)
-            .isAvailable()) {
-          kinds.add(RefactoringKind.EXTRACT_METHOD);
-        }
-        // Try EXTRACT_WIDGETS.
-        if (ExtractWidgetRefactoring(searchEngine, resolvedUnit, offset, length)
-            .isAvailable()) {
-          kinds.add(RefactoringKind.EXTRACT_WIDGET);
-        }
-      }
-    }
-    // check elements
-    {
-      var resolvedUnit = await server.getResolvedUnit(file);
-      if (resolvedUnit != null) {
-        var node = NodeLocator(offset).searchWithin(resolvedUnit.unit);
-        var element = server.getElementOfNode(node);
-        if (element != null) {
-          // try CONVERT_METHOD_TO_GETTER
-          if (element is ExecutableElement) {
-            Refactoring refactoring = ConvertMethodToGetterRefactoring(
-                searchEngine, resolvedUnit.session, element);
-            var status = await refactoring.checkInitialConditions();
-            if (!status.hasFatalError) {
-              kinds.add(RefactoringKind.CONVERT_METHOD_TO_GETTER);
-            }
-          }
-          // try RENAME
-          final refactoringWorkspace = this.refactoringWorkspace;
-          if (refactoringWorkspace != null) {
-            var renameRefactoring = RenameRefactoring.create(
-                refactoringWorkspace, resolvedUnit, element);
-            if (renameRefactoring != null) {
-              kinds.add(RefactoringKind.RENAME);
-            }
-          }
-        }
-      }
-    }
-    // respond
-    var result = EditGetAvailableRefactoringsResult(kinds);
-    server.sendResponse(result.toResponse(request.id));
-  }
-
-  Response _getRefactoring(
-      Request request, CancellationToken cancellationToken) {
-    final refactoringManager = this.refactoringManager;
-    if (refactoringManager == null) {
-      return Response.unsupportedFeature(request.id, 'Search is not enabled.');
-    }
-    if (refactoringManager.hasPendingRequest) {
-      refactoringManager.cancel();
-      _newRefactoringManager();
-    }
-    refactoringManager.getRefactoring(request, cancellationToken);
-    return Response.DELAYED_RESPONSE;
-  }
-
-  /// Initializes [refactoringManager] with a new instance.
-  void _newRefactoringManager() {
-    final refactoringWorkspace = this.refactoringWorkspace;
-    if (refactoringWorkspace != null) {
-      refactoringManager = _RefactoringManager(server, refactoringWorkspace);
-    }
-  }
 }
 
 /// An object managing a single [Refactoring] instance.
@@ -216,7 +121,7 @@
 ///
 /// Once new set of parameters is received, the previous [Refactoring] instance
 /// is invalidated and a new one is created and initialized.
-class _RefactoringManager {
+class RefactoringManager {
   static const List<RefactoringProblem> EMPTY_PROBLEM_LIST =
       <RefactoringProblem>[];
 
@@ -238,7 +143,7 @@
   Request? request;
   EditGetRefactoringResult? result;
 
-  _RefactoringManager(this.server, this.refactoringWorkspace)
+  RefactoringManager(this.server, this.refactoringWorkspace)
       : searchEngine = refactoringWorkspace.searchEngine {
     _reset();
   }
@@ -615,6 +520,6 @@
   }
 }
 
-/// [_RefactoringManager] throws instances of this class internally to stop
+/// [RefactoringManager] throws instances of this class internally to stop
 /// processing in a manager that was reset.
 class _ResetError {}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_get_available_refactorings.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_get_available_refactorings.dart
new file mode 100644
index 0000000..04b8ac1
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_get_available_refactorings.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/services/refactoring/refactoring.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+
+/// The handler for the `edit.getAvailableRefactorings` request.
+class EditGetAvailableRefactoringsHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  EditGetAvailableRefactoringsHandler(
+      super.server, super.request, super.cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    var params = EditGetAvailableRefactoringsParams.fromRequest(request);
+    var file = params.file;
+    var offset = params.offset;
+    var length = params.length;
+
+    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+      return;
+    }
+
+    // add refactoring kinds
+    var kinds = <RefactoringKind>[];
+    // Check nodes.
+    final searchEngine = server.searchEngine;
+    {
+      var resolvedUnit = await server.getResolvedUnit(file);
+      if (resolvedUnit != null) {
+        // Try EXTRACT_LOCAL_VARIABLE.
+        if (ExtractLocalRefactoring(resolvedUnit, offset, length)
+            .isAvailable()) {
+          kinds.add(RefactoringKind.EXTRACT_LOCAL_VARIABLE);
+        }
+        // Try EXTRACT_METHOD.
+        if (ExtractMethodRefactoring(searchEngine, resolvedUnit, offset, length)
+            .isAvailable()) {
+          kinds.add(RefactoringKind.EXTRACT_METHOD);
+        }
+        // Try EXTRACT_WIDGETS.
+        if (ExtractWidgetRefactoring(searchEngine, resolvedUnit, offset, length)
+            .isAvailable()) {
+          kinds.add(RefactoringKind.EXTRACT_WIDGET);
+        }
+      }
+    }
+    // check elements
+    var resolvedUnit = await server.getResolvedUnit(file);
+    if (resolvedUnit != null) {
+      var node = NodeLocator(offset).searchWithin(resolvedUnit.unit);
+      var element = server.getElementOfNode(node);
+      if (element != null) {
+        // try CONVERT_METHOD_TO_GETTER
+        if (element is ExecutableElement) {
+          Refactoring refactoring = ConvertMethodToGetterRefactoring(
+              searchEngine, resolvedUnit.session, element);
+          var status = await refactoring.checkInitialConditions();
+          if (!status.hasFatalError) {
+            kinds.add(RefactoringKind.CONVERT_METHOD_TO_GETTER);
+          }
+        }
+        // try RENAME
+        final refactoringWorkspace = server.refactoringWorkspace;
+        var renameRefactoring = RenameRefactoring.create(
+            refactoringWorkspace, resolvedUnit, element);
+        if (renameRefactoring != null) {
+          kinds.add(RefactoringKind.RENAME);
+        }
+      }
+    }
+    // respond
+    sendResult(EditGetAvailableRefactoringsResult(kinds));
+  }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_get_refactoring.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_get_refactoring.dart
new file mode 100644
index 0000000..f4793ce
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_get_refactoring.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+
+/// The handler for the `edit.getRefactoring` request.
+class EditGetRefactoringHandler extends LegacyHandler {
+  /// Initialize a newly created handler to be able to service requests for the
+  /// [server].
+  EditGetRefactoringHandler(
+      super.server, super.request, super.cancellationToken);
+
+  @override
+  Future<void> handle() async {
+    final refactoringManager = server.refactoringManager;
+    if (refactoringManager == null) {
+      sendResponse(
+          Response.unsupportedFeature(request.id, 'Search is not enabled.'));
+      return;
+    }
+    refactoringManager.getRefactoring(request, cancellationToken);
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index c0147af..ae9517c 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -608,6 +608,8 @@
   status: needsEvaluation
 CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE:
   status: needsEvaluation
+CompileTimeErrorCode.MACRO_EXECUTION_EXCEPTION:
+  status: needsEvaluation
 CompileTimeErrorCode.MAIN_FIRST_POSITIONAL_PARAMETER_TYPE:
   status: needsEvaluation
 CompileTimeErrorCode.MAIN_HAS_REQUIRED_NAMED_PARAMETERS:
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics.dart b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
index a8f6982..f2d73e1 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
@@ -52,6 +52,7 @@
 import 'package:analyzer_plugin/src/utilities/completion/optype.dart';
 import 'package:args/args.dart';
 import 'package:cli_util/cli_logging.dart';
+import 'package:collection/collection.dart';
 
 import 'metrics_util.dart';
 import 'output_utilities.dart';
@@ -151,8 +152,9 @@
 
 /// Create a parser that can be used to parse the command-line arguments.
 ArgParser createArgParser() {
-  return ArgParser()
-    ..addOption(
+  return ArgParser(
+      usageLineLength: stdout.hasTerminal ? stdout.terminalColumns : 80)
+    ..addFlag(
       'help',
       abbr: 'h',
       help: 'Print this help message.',
@@ -169,6 +171,11 @@
             'token can be removed, or the rest of the file can be removed to '
             'test code completion with diverse methods. The default mode is to '
             'complete at the start of the token without modifying the file.')
+    ..addOption(CompletionMetricsOptions.PREFIX_LENGTH,
+        defaultsTo: '0',
+        help: 'The number of characters to include in the prefix. Each '
+            'completion will be requested this many characters in from the '
+            'start of the token being completed.')
     ..addFlag(CompletionMetricsOptions.PRINT_MISSED_COMPLETION_DETAILS,
         defaultsTo: false,
         help:
@@ -299,7 +306,7 @@
 /// objects for a run of [CompletionMetricsComputer].
 class CompletionMetrics {
   /// The maximum number of slowest results to collect.
-  static const maxSlowestResults = 5;
+  static const maxSlowestResults = 100;
 
   /// The maximum number of worst results to collect.
   static const maxWorstResults = 5;
@@ -382,7 +389,10 @@
   /// where a shadowed element was suggested rather than the visible one.
   Map<String, List<ShadowedCompletion>> shadowedCompletions = {};
 
-  final Map<CompletionGroup, List<CompletionResult>> slowestResults = {};
+  /// A list of the slowest results, sorted from slowest to fastest.
+  ///
+  /// This list contains at most [maxSlowestResults] results.
+  final List<CompletionResult> slowestResults = [];
 
   final Map<CompletionGroup, List<CompletionResult>> worstResults = {};
 
@@ -441,13 +451,10 @@
         in map['missingCompletionLocationTables'] as List<dynamic>) {
       metrics.missingCompletionLocationTables.add(element as String);
     }
-    for (var entry in (map['slowestResults'] as Map<String, dynamic>).entries) {
-      var group = CompletionGroup.values[int.parse(entry.key)];
-      var results = (entry.value as List<dynamic>)
-          .map((map) => CompletionResult.fromJson(map as Map<String, dynamic>))
-          .toList();
-      metrics.slowestResults[group] = results;
-    }
+    metrics.slowestResults.addAll([
+      for (var result in map['slowestResults'] as List<dynamic>)
+        CompletionResult.fromJson(result as Map<String, dynamic>),
+    ]);
     for (var entry in (map['worstResults'] as Map<String, dynamic>).entries) {
       var group = CompletionGroup.values[int.parse(entry.key)];
       var results = (entry.value as List<dynamic>)
@@ -487,10 +494,8 @@
     missingCompletionLocations.addAll(metrics.missingCompletionLocations);
     missingCompletionLocationTables
         .addAll(metrics.missingCompletionLocationTables);
-    for (var resultList in metrics.slowestResults.values) {
-      for (var result in resultList) {
-        _recordSlowestResult(result);
-      }
+    for (var result in metrics.slowestResults) {
+      _recordSlowestResult(result);
     }
     for (var resultList in metrics.worstResults.values) {
       for (var result in resultList) {
@@ -572,9 +577,8 @@
       'missingCompletionLocations': missingCompletionLocations.toList(),
       'missingCompletionLocationTables':
           missingCompletionLocationTables.toList(),
-      'slowestResults': slowestResults.map((key, value) => MapEntry(
-          key.index.toString(),
-          value.map((result) => result.toJson()).toList())),
+      'slowestResults':
+          slowestResults.map((result) => result.toJson()).toList(),
       'worstResults': worstResults.map((key, value) => MapEntry(
           key.index.toString(),
           value.map((result) => result.toJson()).toList())),
@@ -614,18 +618,17 @@
     }
   }
 
-  /// If the [result] is took longer than any previously recorded results,
+  /// If the [result] took longer than any previously recorded results,
   /// record it.
   void _recordSlowestResult(CompletionResult result) {
-    var results = slowestResults.putIfAbsent(result.group, () => []);
-    if (results.length >= maxSlowestResults) {
-      if (result.elapsedMS <= results.last.elapsedMS) {
+    if (slowestResults.length >= maxSlowestResults) {
+      if (result.elapsedMS <= slowestResults.last.elapsedMS) {
         return;
       }
-      results.removeLast();
+      slowestResults.removeLast();
     }
-    results.add(result);
-    results.sort((first, second) => second.elapsedMS - first.elapsedMS);
+    slowestResults.add(result);
+    slowestResults.sort((first, second) => second.elapsedMS - first.elapsedMS);
   }
 
   /// Record this elapsed ms count for the average ms count.
@@ -1199,15 +1202,36 @@
     }
   }
 
+  /// Prints the results which took the longest amounts of time to compute.
+  ///
+  /// Specifically, only results which are above the 90th percentile, and which
+  /// are in the top [maxSlowestResults] slowest results, are included.
   void printSlowestResults(CompletionMetrics metrics) {
-    var slowestResults = metrics.slowestResults;
-    var entries = slowestResults.entries.toList();
-    entries.sort((first, second) => first.key.name.compareTo(second.key.name));
+    var p90ElapsedMs = metrics.percentileCompletionMS.p90;
+    var slowestResults = metrics.slowestResults
+        .where((element) => element.elapsedMS >= p90ElapsedMs)
+        .toList();
     print('');
     printHeading(2, 'The slowest completion results to compute');
-    for (var entry in entries) {
-      _printSlowestResults('In ${entry.key.name}', entry.value);
+    for (var result in slowestResults) {
+      var expected = result.expectedCompletion;
+      print('');
+      print('* Elapsed ms: ${result.elapsedMS}');
+      print('* Group: ${result.group.name}');
+      print("* Completion: '${expected.completion}'");
+      print('* Completion kind: ${expected.kind}');
+      print('* Element kind: ${expected.elementKind}');
+      print('* Location: ${expected.location}');
     }
+
+    print('');
+    var slowestResultCountByGroup = slowestResults.groupFoldBy(
+        (result) => result.group.name,
+        (int? previous, result) => (previous ?? 0) + 1);
+    slowestResultCountByGroup.forEach((groupName, count) {
+      var countString = count.toString().padLeft(2);
+      print('${groupName.padRight(20)}: $countString result(s)');
+    });
   }
 
   void printWorstResults(CompletionMetrics metrics) {
@@ -1240,6 +1264,8 @@
     return expected.length;
   }
 
+  /// Computes completion suggestions for [dartRequest], and returns the
+  /// suggestions, sorted by rank and then by completion text.
   Future<List<protocol.CompletionSuggestion>> _computeCompletionSuggestions(
       MetricsSuggestionListener listener,
       OperationPerformanceImpl performance,
@@ -1406,7 +1432,8 @@
       var filePath = result.path;
       // Use the ExpectedCompletionsVisitor to compute the set of expected
       // completions for this CompilationUnit.
-      final visitor = ExpectedCompletionsVisitor(result);
+      final visitor =
+          ExpectedCompletionsVisitor(result, caretOffset: options.prefixLength);
       _resolvedUnitResult.unit.accept(visitor);
 
       for (var expectedCompletion in visitor.expectedCompletions) {
@@ -1508,9 +1535,14 @@
     var length = expectedCompletion.syntacticEntity.length;
     assert(offset >= 0);
     assert(length > 0);
+    var tokenEndOffset = offset + length;
+    if (length >= options.prefixLength) {
+      // Rather than removing the whole token, remove the characters after
+      // the given prefix length.
+      offset += options.prefixLength;
+    }
     if (options.overlay == CompletionMetricsOptions.OVERLAY_REMOVE_TOKEN) {
-      return contents.substring(0, offset) +
-          contents.substring(offset + length);
+      return contents.substring(0, offset) + contents.substring(tokenEndOffset);
     } else if (options.overlay ==
         CompletionMetricsOptions.OVERLAY_REMOVE_REST_OF_FILE) {
       return contents.substring(0, offset);
@@ -1522,26 +1554,6 @@
     }
   }
 
-  void _printSlowestResults(
-      String title, List<CompletionResult> slowestResults) {
-    printHeading(3, title);
-    var needsBlankLine = false;
-    for (var result in slowestResults) {
-      var elapsedMS = result.elapsedMS;
-      var expected = result.expectedCompletion;
-      if (needsBlankLine) {
-        print('');
-      } else {
-        needsBlankLine = true;
-      }
-      print('  Elapsed ms: $elapsedMS');
-      print('  Completion: ${expected.completion}');
-      print('  Completion kind: ${expected.kind}');
-      print('  Element kind: ${expected.elementKind}');
-      print('  Location: ${expected.location}');
-    }
-  }
-
   void _printWorstResults(String title, List<CompletionResult> worstResults) {
     List<String> suggestionRow(int rank, SuggestionData data) {
       var suggestion = data.suggestion;
@@ -1636,15 +1648,17 @@
     return null;
   }
 
+  /// Returns a [Place] indicating the position of [expectedCompletion] in
+  /// [suggestions].
+  ///
+  /// If [expectedCompletion] is not found, `Place.none()` is returned.
   static Place placementInSuggestionList(
       List<protocol.CompletionSuggestion> suggestions,
       ExpectedCompletion expectedCompletion) {
-    var placeCounter = 1;
-    for (var completionSuggestion in suggestions) {
-      if (expectedCompletion.matches(completionSuggestion)) {
-        return Place(placeCounter, suggestions.length);
+    for (var i = 0; i < suggestions.length; i++) {
+      if (expectedCompletion.matches(suggestions[i])) {
+        return Place(i + 1, suggestions.length);
       }
-      placeCounter++;
     }
     return Place.none();
   }
@@ -1666,6 +1680,12 @@
   /// completion offset should be removed.
   static const String OVERLAY_REMOVE_TOKEN = 'remove-token';
 
+  /// An option controlling how long of a prefix should be used.
+  ///
+  /// This affects the offset of the completion request, and how much content is
+  /// removed in each of the overlay modes.
+  static const String PREFIX_LENGTH = 'prefix-length';
+
   /// A flag that causes detailed information to be printed every time a
   /// completion request fails to produce a suggestions matching the expected
   /// suggestion.
@@ -1705,6 +1725,8 @@
   /// The overlay mode that should be used.
   final String overlay;
 
+  final int prefixLength;
+
   /// A flag indicating whether information should be printed every time a
   /// completion request fails to produce a suggestions matching the expected
   /// suggestion.
@@ -1740,6 +1762,7 @@
   factory CompletionMetricsOptions(results) {
     return CompletionMetricsOptions._(
         overlay: results[OVERLAY] as String,
+        prefixLength: int.parse(results[PREFIX_LENGTH] as String),
         printMissedCompletionDetails:
             results[PRINT_MISSED_COMPLETION_DETAILS] as bool,
         printMissedCompletionSummary:
@@ -1754,6 +1777,7 @@
 
   CompletionMetricsOptions._(
       {required this.overlay,
+      required this.prefixLength,
       required this.printMissedCompletionDetails,
       required this.printMissedCompletionSummary,
       required this.printMissingInformation,
diff --git a/pkg/analysis_server/tool/code_completion/visitors.dart b/pkg/analysis_server/tool/code_completion/visitors.dart
index 5783758..0e36d73 100644
--- a/pkg/analysis_server/tool/code_completion/visitors.dart
+++ b/pkg/analysis_server/tool/code_completion/visitors.dart
@@ -127,6 +127,11 @@
   /// The result of resolving the file being visited.
   final ResolvedUnitResult result;
 
+  /// The offset from the location of each entity passed to
+  /// [safelyRecordEntity], to be used as the column for each
+  /// [ExpectedCompletion] created.
+  final int _caretOffset;
+
   /// The completions that are expected to be produced in the file being
   /// visited.
   final List<ExpectedCompletion> expectedCompletions = [];
@@ -144,7 +149,8 @@
   /// comment don't yield an error like Dart syntax mistakes would yield.
   final bool _doExpectCommentRefs = false;
 
-  ExpectedCompletionsVisitor(this.result);
+  ExpectedCompletionsVisitor(this.result, {required int caretOffset})
+      : _caretOffset = caretOffset;
 
   /// Return the path of the file that is being visited.
   String get filePath => result.path;
@@ -160,6 +166,9 @@
       var location = lineInfo.getLocation(entity.offset);
       var lineNumber = location.lineNumber;
       var columnNumber = location.columnNumber;
+      if (entity.length >= _caretOffset) {
+        columnNumber += _caretOffset;
+      }
 
       bool isKeyword() => kind == protocol.CompletionSuggestionKind.KEYWORD;
 
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 15c5a05..46810d4 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -285,6 +285,7 @@
   CompileTimeErrorCode.LATE_FINAL_FIELD_WITH_CONST_CONSTRUCTOR,
   CompileTimeErrorCode.LATE_FINAL_LOCAL_ALREADY_ASSIGNED,
   CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE,
+  CompileTimeErrorCode.MACRO_EXECUTION_EXCEPTION,
   CompileTimeErrorCode.MAIN_FIRST_POSITIONAL_PARAMETER_TYPE,
   CompileTimeErrorCode.MAIN_HAS_REQUIRED_NAMED_PARAMETERS,
   CompileTimeErrorCode.MAIN_HAS_TOO_MANY_REQUIRED_POSITIONAL_PARAMETERS,
diff --git a/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart b/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
index e7ff31d..4b4946f 100644
--- a/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
@@ -16,6 +16,7 @@
 import 'package:analyzer/src/dart/analysis/file_content_cache.dart';
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
+import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
 import 'package:analyzer/src/summary2/macro.dart';
 import 'package:analyzer/src/util/sdk.dart';
 
@@ -25,7 +26,10 @@
   final ResourceProvider resourceProvider;
 
   /// The instance of macro executor that is used for all macros.
-  final macro.MultiMacroExecutor? macroExecutor = macro.MultiMacroExecutor();
+  final macro.MultiMacroExecutor macroExecutor = macro.MultiMacroExecutor();
+
+  /// The instance of the macro kernel builder.
+  final MacroKernelBuilder macroKernelBuilder = MacroKernelBuilder();
 
   /// The list of analysis contexts.
   @override
@@ -48,7 +52,6 @@
     AnalysisDriverScheduler? scheduler,
     FileContentCache? fileContentCache,
     void Function(AnalysisOptionsImpl)? updateAnalysisOptions,
-    MacroKernelBuilder? macroKernelBuilder,
   }) : resourceProvider =
             resourceProvider ?? PhysicalResourceProvider.INSTANCE {
     sdkPath ??= getSdkPath();
@@ -119,7 +122,9 @@
     for (var analysisContext in contexts) {
       analysisContext.driver.dispose();
     }
-    macroExecutor?.close();
+    macroExecutor.close();
+    // If there are other collections, they will have to start it again.
+    KernelCompilationService.dispose();
   }
 
   /// Check every element with [_throwIfNotAbsoluteNormalizedPath].
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index e2a6383..618627c 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -331,7 +331,7 @@
   String get transitiveSignature {
     var librarySignatureBuilder = ApiSignature()
       ..addString(uriStr)
-      ..addString(libraryCycle.transitiveSignature);
+      ..addString(libraryCycle.apiSignature);
     return librarySignatureBuilder.toHex();
   }
 
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_context.dart b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
index f19c23c..3d44aa2 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_context.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
@@ -160,7 +160,7 @@
         }
       }
 
-      var resolutionKey = '${cycle.transitiveSignature}.linked_bundle';
+      var resolutionKey = '${cycle.apiSignature}.linked_bundle';
       var resolutionBytes = byteStore.get(resolutionKey);
 
       if (resolutionBytes == null) {
@@ -244,7 +244,7 @@
 
       final macroKernelBuilder = this.macroKernelBuilder;
       if (macroKernelBuilder != null && macroLibraries.isNotEmpty) {
-        var macroKernelKey = '${cycle.transitiveSignature}.macro_kernel';
+        var macroKernelKey = '${cycle.implSignature}.macro_kernel';
         var macroKernelBytes = byteStore.get(macroKernelKey);
         if (macroKernelBytes == null) {
           macroKernelBytes = await macroKernelBuilder.build(
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
index 1a4ed51..1457c6b 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
@@ -28,18 +28,46 @@
   /// The cycles that use this cycle, used to [invalidate] transitively.
   final List<LibraryCycle> _directUsers = [];
 
-  /// The transitive signature of this cycle.
+  /// The transitive API signature of this cycle.
   ///
   /// It is based on the API signatures of all files of the [libraries], and
-  /// transitive signatures of the cycles that the [libraries] reference
-  /// directly.  So, indirectly it is based on the transitive closure of all
-  /// files that [libraries] reference (but we don't compute these files).
-  String transitiveSignature;
+  /// API signatures of the cycles that the [libraries] reference directly.
+  /// So, indirectly it is based on API signatures of the transitive closure
+  /// of all files that [libraries] reference.
+  String apiSignature;
+
+  /// The transitive implementation signature of this cycle.
+  ///
+  /// It is based on the full code signatures of all files of the [libraries],
+  /// and full code signatures of the cycles that the [libraries] reference
+  /// directly. So, indirectly it is based on full code signatures of the
+  /// transitive closure of all files that [libraries] reference.
+  ///
+  /// Usually, when a library is imported we need its [apiSignature], because
+  /// its API is all we can see from outside. But if the library contains
+  /// a macro, and we use it, we run full code of the macro defining library,
+  /// potentially executing every method body of the transitive closure of
+  /// the libraries imported by the macro defining library. So, the resulting
+  /// library (that imports a macro defining library) API signature must
+  /// include [implSignature] of the macro defining library.
+  String implSignature;
+
+  late final bool hasMacroClass = () {
+    for (final library in libraries) {
+      for (final file in library.libraryFiles) {
+        if (file.unlinked2.macroClasses.isNotEmpty) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }();
 
   LibraryCycle({
     required this.libraries,
     required this.directDependencies,
-    required this.transitiveSignature,
+    required this.apiSignature,
+    required this.implSignature,
   }) {
     for (var directDependency in directDependencies) {
       directDependency._directUsers.add(this);
@@ -99,8 +127,10 @@
 
   @override
   void evaluateScc(List<_LibraryNode> scc) {
-    var signature = ApiSignature();
-    signature.addUint32List(_salt);
+    var apiSignature = ApiSignature();
+    var implSignature = ApiSignature();
+    apiSignature.addUint32List(_salt);
+    implSignature.addUint32List(_salt);
 
     // Sort libraries to produce stable signatures.
     scc.sort((first, second) {
@@ -115,7 +145,8 @@
       var file = node.file;
       _appendDirectlyReferenced(
         directDependencies,
-        signature,
+        apiSignature,
+        implSignature,
         file.directReferencedLibraries.whereNotNull().toList(),
       );
     }
@@ -125,13 +156,22 @@
     for (var node in scc) {
       libraries.add(node.file);
 
-      signature.addLanguageVersion(node.file.packageLanguageVersion);
-      signature.addString(node.file.uriStr);
+      apiSignature.addLanguageVersion(node.file.packageLanguageVersion);
+      apiSignature.addString(node.file.uriStr);
 
-      signature.addInt(node.file.libraryFiles.length);
+      implSignature.addLanguageVersion(node.file.packageLanguageVersion);
+      implSignature.addString(node.file.uriStr);
+
+      apiSignature.addInt(node.file.libraryFiles.length);
       for (var file in node.file.libraryFiles) {
-        signature.addBool(file.exists);
-        signature.addBytes(file.apiSignature);
+        apiSignature.addBool(file.exists);
+        apiSignature.addBytes(file.apiSignature);
+      }
+
+      implSignature.addInt(node.file.libraryFiles.length);
+      for (var file in node.file.libraryFiles) {
+        implSignature.addBool(file.exists);
+        implSignature.addString(file.contentHash);
       }
     }
 
@@ -139,7 +179,8 @@
     var cycle = LibraryCycle(
       libraries: libraries,
       directDependencies: directDependencies,
-      transitiveSignature: signature.toHex(),
+      apiSignature: apiSignature.toHex(),
+      implSignature: implSignature.toHex(),
     );
 
     // Set the instance into the libraries.
@@ -154,10 +195,12 @@
 
   void _appendDirectlyReferenced(
     Set<LibraryCycle> directDependencies,
-    ApiSignature signature,
+    ApiSignature apiSignature,
+    ApiSignature implSignature,
     List<FileState> directlyReferenced,
   ) {
-    signature.addInt(directlyReferenced.length);
+    apiSignature.addInt(directlyReferenced.length);
+    implSignature.addInt(directlyReferenced.length);
     for (var referencedLibrary in directlyReferenced) {
       var referencedCycle = referencedLibrary.internal_libraryCycle;
 
@@ -165,7 +208,12 @@
       if (referencedCycle == null) continue;
 
       if (directDependencies.add(referencedCycle)) {
-        signature.addString(referencedCycle.transitiveSignature);
+        apiSignature.addString(
+          referencedCycle.hasMacroClass
+              ? referencedCycle.implSignature
+              : referencedCycle.apiSignature,
+        );
+        implSignature.addString(referencedCycle.implSignature);
       }
     }
   }
diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart
index 34789a9..a230ed1 100644
--- a/pkg/analyzer/lib/src/error/codes.g.dart
+++ b/pkg/analyzer/lib/src/error/codes.g.dart
@@ -9018,6 +9018,18 @@
   );
 
   /**
+   * Parameters:
+   * 0: the message of the exception
+   * 1: the stack trace
+   */
+  static const CompileTimeErrorCode MACRO_EXECUTION_EXCEPTION =
+      CompileTimeErrorCode(
+    'MACRO_EXECUTION_EXCEPTION',
+    "Exception during macro execution: {0}\n{1}",
+    correctionMessage: "Re-install the Dart or Flutter SDK.",
+  );
+
+  /**
    * No parameters.
    */
   // #### Description
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index b93727e..d5a1aac 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -46,6 +46,7 @@
 import 'package:analyzer/src/generated/error_detection_helpers.dart';
 import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode;
 import 'package:analyzer/src/generated/this_access_tracker.dart';
+import 'package:analyzer/src/summary2/macro_application_error.dart';
 import 'package:analyzer/src/utilities/extensions/string.dart';
 import 'package:collection/collection.dart';
 
@@ -445,6 +446,10 @@
       _checkForBadFunctionUse(node);
       _checkForWrongTypeParameterVarianceInSuperinterfaces();
       _checkForMainFunction(node.name);
+      _reportMacroApplicationErrors(
+        annotations: node.metadata,
+        macroErrors: element.macroApplicationErrors,
+      );
 
       GetterSetterTypesVerifier(
         typeSystem: typeSystem,
@@ -5173,6 +5178,30 @@
     return null;
   }
 
+  void _reportMacroApplicationErrors({
+    required List<Annotation> annotations,
+    required List<MacroApplicationError> macroErrors,
+  }) {
+    for (final macroError in macroErrors) {
+      if (macroError.annotationIndex < annotations.length) {
+        final applicationNode = annotations[macroError.annotationIndex];
+        if (macroError is UnknownMacroApplicationError) {
+          errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.MACRO_EXECUTION_EXCEPTION,
+            applicationNode,
+            [
+              macroError.message,
+              macroError.stackTrace,
+            ],
+          );
+        } else {
+          // TODO(scheglov) Other implementations.
+          throw UnimplementedError('(${macroError.runtimeType}) $macroError');
+        }
+      }
+    }
+  }
+
   void _withEnclosingExecutable(
     ExecutableElement element,
     void Function() operation,
diff --git a/pkg/analyzer/lib/src/summary2/macro.dart b/pkg/analyzer/lib/src/summary2/macro.dart
index 27130c3..bf4756d 100644
--- a/pkg/analyzer/lib/src/summary2/macro.dart
+++ b/pkg/analyzer/lib/src/summary2/macro.dart
@@ -68,34 +68,6 @@
   }
 }
 
-/// Implementation of [MacroKernelBuilder] using `frontend_server`.
-class FrontEndServerMacroKernelBuilder implements MacroKernelBuilder {
-  @override
-  Future<Uint8List> build({
-    required MacroFileSystem fileSystem,
-    required List<MacroLibrary> libraries,
-  }) async {
-    final macroMainContent = macro.bootstrapMacroIsolate(
-      {
-        for (final library in libraries)
-          library.uri.toString(): {
-            for (final c in library.classes) c.name: c.constructors
-          },
-      },
-      macro.SerializationMode.byteDataClient,
-    );
-
-    final macroMainPath = '${libraries.first.path}.macro';
-    final overlayFileSystem = _OverlayMacroFileSystem(fileSystem);
-    overlayFileSystem.overlays[macroMainPath] = macroMainContent;
-
-    return KernelCompilationService.compile(
-      fileSystem: overlayFileSystem,
-      path: macroMainPath,
-    );
-  }
-}
-
 class MacroClass {
   final String name;
   final List<String> constructors;
@@ -140,11 +112,32 @@
   MacroFileEntry getFile(String path);
 }
 
-abstract class MacroKernelBuilder {
+class MacroKernelBuilder {
+  const MacroKernelBuilder();
+
   Future<Uint8List> build({
     required MacroFileSystem fileSystem,
     required List<MacroLibrary> libraries,
-  });
+  }) async {
+    final macroMainContent = macro.bootstrapMacroIsolate(
+      {
+        for (final library in libraries)
+          library.uri.toString(): {
+            for (final c in library.classes) c.name: c.constructors
+          },
+      },
+      macro.SerializationMode.byteDataClient,
+    );
+
+    final macroMainPath = '${libraries.first.path}.macro';
+    final overlayFileSystem = _OverlayMacroFileSystem(fileSystem);
+    overlayFileSystem.overlays[macroMainPath] = macroMainContent;
+
+    return KernelCompilationService.compile(
+      fileSystem: overlayFileSystem,
+      path: macroMainPath,
+    );
+  }
 }
 
 class MacroLibrary {
diff --git a/pkg/analyzer/lib/src/summary2/macro_application.dart b/pkg/analyzer/lib/src/summary2/macro_application.dart
index 1931e3b..d9719b2 100644
--- a/pkg/analyzer/lib/src/summary2/macro_application.dart
+++ b/pkg/analyzer/lib/src/summary2/macro_application.dart
@@ -6,6 +6,7 @@
 import 'package:_fe_analyzer_shared/src/macros/executor.dart' as macro;
 import 'package:_fe_analyzer_shared/src/macros/executor/introspection_impls.dart'
     as macro;
+import 'package:_fe_analyzer_shared/src/macros/executor/protocol.dart' as macro;
 import 'package:_fe_analyzer_shared/src/macros/executor/remote_instance.dart'
     as macro;
 import 'package:analyzer/dart/ast/ast.dart';
@@ -69,12 +70,20 @@
                           }
                         } on MacroApplicationError catch (e) {
                           classElement.macroApplicationErrors.add(e);
+                        } on macro.RemoteException catch (e) {
+                          classElement.macroApplicationErrors.add(
+                            UnknownMacroApplicationError(
+                              annotationIndex: i,
+                              message: e.error,
+                              stackTrace: e.stackTrace ?? '<null>',
+                            ),
+                          );
                         } catch (e, stackTrace) {
                           classElement.macroApplicationErrors.add(
                             UnknownMacroApplicationError(
                               annotationIndex: i,
-                              stackTrace: stackTrace.toString(),
                               message: e.toString(),
+                              stackTrace: stackTrace.toString(),
                             ),
                           );
                         }
@@ -172,7 +181,7 @@
     );
   }
 
-  static macro.ParameterDeclarationImpl _buildFormalParameter(
+  static macro.FunctionTypeParameterImpl _buildFormalParameter(
     FormalParameter node,
   ) {
     if (node is DefaultFormalParameter) {
@@ -186,12 +195,11 @@
       throw UnimplementedError('(${node.runtimeType}) $node');
     }
 
-    return macro.ParameterDeclarationImpl(
+    return macro.FunctionTypeParameterImpl(
       id: macro.RemoteInstance.uniqueId,
-      identifier:
-          _buildIdentifier(node.identifier!), // TODO(scheglov) might be null
       isNamed: node.isNamed,
       isRequired: node.isRequired,
+      name: node.identifier?.name,
       type: typeAnnotation,
     );
   }
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index f38485c..4d58c07 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -7917,6 +7917,13 @@
       ```dart
       List<num> x = [1, 2.5, 3];
       ```
+  MACRO_EXECUTION_EXCEPTION:
+    problemMessage: "Exception during macro execution: {0}\n{1}"
+    correctionMessage: Re-install the Dart or Flutter SDK.
+    comment: |-
+      Parameters:
+      0: the message of the exception
+      1: the stack trace
   MAIN_FIRST_POSITIONAL_PARAMETER_TYPE:
     problemMessage: "The type of the first positional parameter of the 'main' function must be a supertype of 'List<String>'."
     correctionMessage: Try changing the type of the parameter.
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart
index 28a1626..de7d1bc 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart
@@ -5,11 +5,13 @@
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/error/error.dart';
+import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
+import '../../summary/macros_environment.dart';
 import '../resolution/context_collection_resolution.dart';
 
 main() {
@@ -27,6 +29,16 @@
     return driver.test.libraryContextTestView.linkedCycles;
   }
 
+  @override
+  void setUp() {
+    super.setUp();
+
+    writeTestPackageConfig(
+      PackageConfigFileBuilder(),
+      macrosEnvironment: MacrosEnvironment.instance,
+    );
+  }
+
   test_analysisOptions_strictCasts() async {
     useEmptyByteStore();
 
@@ -340,6 +352,180 @@
     _assertNoLinkedCycles();
   }
 
+  test_macro_libraryElement_changeMacroCode() async {
+    File newFileWithFixedNameMacro(String className) {
+      return newFile('$testPackageLibPath/my_macro.dart', '''
+import 'dart:async';
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+  const MyMacro();
+
+  FutureOr<void> buildTypesForClass(clazz, builder) {
+    builder.declareType(
+      '$className',
+      DeclarationCode.fromString('class $className {}'),
+    );
+  }
+}
+''');
+    }
+
+    final macroFile = newFileWithFixedNameMacro('MacroA');
+
+    var a = newFile('$testPackageLibPath/a.dart', r'''
+import 'my_macro.dart';
+
+@MyMacro()
+class A {}
+''');
+
+    var b = newFile('$testPackageLibPath/b.dart', r'''
+export 'a.dart';
+''');
+
+    final analysisContext = contextFor(macroFile.path);
+
+    Future<LibraryElement> getLibrary(String uriStr) async {
+      final result = await analysisContext.currentSession
+          .getLibraryByUri(uriStr) as LibraryElementResult;
+      return result.element;
+    }
+
+    // This macro generates `MacroA`, but not `MacroB`.
+    {
+      final libraryA = await getLibrary('package:test/a.dart');
+      expect(libraryA.getType('MacroA'), isNotNull);
+      expect(libraryA.getType('MacroB'), isNull);
+      // This propagates transitively.
+      final libraryB = await getLibrary('package:test/b.dart');
+      expect(libraryB.exportNamespace.get('MacroA'), isNotNull);
+      expect(libraryB.exportNamespace.get('MacroB'), isNull);
+    }
+
+    _assertContainsLinkedCycle({a.path});
+    _assertContainsLinkedCycle({b.path}, andClear: true);
+
+    // The macro will generate `MacroB`.
+    newFileWithFixedNameMacro('MacroB');
+
+    // Notify about changes.
+    analysisContext.changeFile(macroFile.path);
+    await analysisContext.applyPendingFileChanges();
+
+    // This macro generates `MacroB`, but not `MacroA`.
+    {
+      final libraryA = await getLibrary('package:test/a.dart');
+      expect(libraryA.getType('MacroA'), isNull);
+      expect(libraryA.getType('MacroB'), isNotNull);
+      // This propagates transitively.
+      final libraryB = await getLibrary('package:test/b.dart');
+      expect(libraryB.exportNamespace.get('MacroA'), isNull);
+      expect(libraryB.exportNamespace.get('MacroB'), isNotNull);
+    }
+
+    _assertContainsLinkedCycle({a.path});
+    _assertContainsLinkedCycle({b.path}, andClear: true);
+  }
+
+  test_macro_resolvedUnit_changeCodeUsedByMacro() async {
+    final a = newFile('$testPackageLibPath/a.dart', r'''
+String getClassName() => 'MacroA';
+''');
+
+    newFile('$testPackageLibPath/my_macro.dart', r'''
+import 'dart:async';
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+import 'a.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+  const MyMacro();
+
+  FutureOr<void> buildTypesForClass(clazz, builder) {
+    final className = getClassName();
+    builder.declareType(
+      '$className',
+      DeclarationCode.fromString('class $className {}'),
+    );
+  }
+}
+''');
+
+    // The macro will generate `MacroA`, so no errors.
+    await assertNoErrorsInCode('''
+import 'my_macro.dart';
+
+@MyMacro()
+class A {}
+
+void f(MacroA a) {}
+''');
+
+    // The macro will generate `MacroB`.
+    newFile(a.path, r'''
+String getClassName() => 'MacroB';
+''');
+
+    // Notify about changes.
+    var analysisContext = contextFor(a.path);
+    analysisContext.changeFile(a.path);
+    await analysisContext.applyPendingFileChanges();
+
+    // Resolve the test file, it still references `MacroA`, but the macro
+    // generates `MacroB` now, so we have an error.
+    await resolveTestFile();
+    assertErrorsInResult([
+      error(CompileTimeErrorCode.UNDEFINED_CLASS, 55, 6),
+    ]);
+  }
+
+  test_macro_resolvedUnit_changeMacroCode() async {
+    File newFileWithFixedNameMacro(String className) {
+      return newFile('$testPackageLibPath/my_macro.dart', '''
+import 'dart:async';
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+  const MyMacro();
+
+  FutureOr<void> buildTypesForClass(clazz, builder) {
+    builder.declareType(
+      '$className',
+      DeclarationCode.fromString('class $className {}'),
+    );
+  }
+}
+''');
+    }
+
+    var macroFile = newFileWithFixedNameMacro('MacroA');
+
+    // The macro will generate `MacroA`, so no errors.
+    await assertNoErrorsInCode('''
+import 'my_macro.dart';
+
+@MyMacro()
+class A {}
+
+void f(MacroA a) {}
+''');
+
+    // The macro will generate `MacroB`.
+    newFileWithFixedNameMacro('MacroB');
+
+    // Notify about changes.
+    var analysisContext = contextFor(macroFile.path);
+    analysisContext.changeFile(macroFile.path);
+    await analysisContext.applyPendingFileChanges();
+
+    // Resolve the test file, it still references `MacroA`, but the updated
+    // macro generates `MacroB` now, so we have an error.
+    await resolveTestFile();
+    assertErrorsInResult([
+      error(CompileTimeErrorCode.UNDEFINED_CLASS, 55, 6),
+    ]);
+  }
+
   void _assertContainsLinkedCycle(Set<String> expectedPosix,
       {bool andClear = false}) {
     var expected = expectedPosix.map(convertPath).toSet();
diff --git a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
index d3fffbd..40822da 100644
--- a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
@@ -12,7 +12,6 @@
 import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
 import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
-import 'package:analyzer/src/summary2/macro.dart';
 import 'package:analyzer/src/test_utilities/mock_packages.dart';
 import 'package:analyzer/src/test_utilities/mock_sdk.dart';
 import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
@@ -141,8 +140,6 @@
     _declaredVariables = map;
   }
 
-  MacroKernelBuilder? get macroKernelBuilder => null;
-
   bool get retainDataForTesting => false;
 
   Folder get sdkRoot => newFolder('/sdk');
@@ -257,7 +254,6 @@
       retainDataForTesting: retainDataForTesting,
       sdkPath: sdkRoot.path,
       updateAnalysisOptions: updateAnalysisOptions,
-      macroKernelBuilder: macroKernelBuilder,
     );
 
     verifyCreatedCollection();
diff --git a/pkg/analyzer/test/src/dart/resolution/macro_test.dart b/pkg/analyzer/test/src/dart/resolution/macro_test.dart
index 0178ec0..1ed52c5 100644
--- a/pkg/analyzer/test/src/dart/resolution/macro_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/macro_test.dart
@@ -2,8 +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/summary2/kernel_compilation_service.dart';
-import 'package:analyzer/src/summary2/macro.dart';
+import 'package:analyzer/src/error/codes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import '../../summary/macros_environment.dart';
@@ -25,31 +24,15 @@
 @reflectiveTest
 class MacroResolutionTest extends PubPackageResolutionTest {
   @override
-  MacroKernelBuilder? get macroKernelBuilder {
-    return FrontEndServerMacroKernelBuilder();
-  }
-
-  @override
   void setUp() {
     super.setUp();
 
-    // TODO(scheglov) Dependency tracking for macros is not right yet.
-    useEmptyByteStore();
-
     writeTestPackageConfig(
       PackageConfigFileBuilder(),
       macrosEnvironment: MacrosEnvironment.instance,
     );
   }
 
-  @override
-  Future<void> tearDown() async {
-    await super.tearDown();
-    KernelCompilationService.disposeDelayed(
-      const Duration(milliseconds: 100),
-    );
-  }
-
   test_0() async {
     newFile('$testPackageLibPath/a.dart', r'''
 import 'dart:async';
@@ -77,4 +60,46 @@
 void f(A_Macro a) {}
 ''');
   }
+
+  test_macroExecutionException_compileTimeError() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+  const MyMacro();
+
+  buildTypesForClass(clazz, builder) {
+    unresolved;
+  }
+}
+''');
+
+    await assertErrorsInCode('''
+import 'a.dart';
+
+@MyMacro()
+class A {}
+''', [error(CompileTimeErrorCode.MACRO_EXECUTION_EXCEPTION, 18, 10)]);
+  }
+
+  test_macroExecutionException_throwsException() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+  const MyMacro();
+
+  buildTypesForClass(clazz, builder) {
+    throw 42;
+  }
+}
+''');
+
+    await assertErrorsInCode('''
+import 'a.dart';
+
+@MyMacro()
+class A {}
+''', [error(CompileTimeErrorCode.MACRO_EXECUTION_EXCEPTION, 18, 10)]);
+  }
 }
diff --git a/pkg/analyzer/test/src/summary/elements_base.dart b/pkg/analyzer/test/src/summary/elements_base.dart
index 0c30670..0254431 100644
--- a/pkg/analyzer/test/src/summary/elements_base.dart
+++ b/pkg/analyzer/test/src/summary/elements_base.dart
@@ -23,6 +23,7 @@
 import 'package:analyzer/src/source/package_map_resolver.dart';
 import 'package:analyzer/src/summary2/bundle_reader.dart';
 import 'package:analyzer/src/summary2/informative_data.dart';
+import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
 import 'package:analyzer/src/summary2/link.dart';
 import 'package:analyzer/src/summary2/linked_element_factory.dart';
 import 'package:analyzer/src/summary2/macro.dart';
@@ -30,6 +31,7 @@
 import 'package:analyzer/src/test_utilities/mock_sdk.dart';
 import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
 import 'package:analyzer/src/util/uri.dart';
+import 'package:meta/meta.dart';
 import 'package:path/path.dart' as package_path;
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -42,8 +44,8 @@
   /// The shared SDK bundle, computed once and shared among test invocations.
   static _SdkBundle? _sdkBundle;
 
-  MacroKernelBuilder? macroKernelBuilder;
-  macro.MultiMacroExecutor? macroExecutor;
+  /// The instance of macro executor that is used for all macros.
+  final macro.MultiMacroExecutor _macroExecutor = macro.MultiMacroExecutor();
 
   /// The set of features enabled in this test.
   FeatureSet featureSet = FeatureSets.latestWithExperiments;
@@ -180,7 +182,7 @@
     var linkResult = await link(
       elementFactory,
       inputLibraries,
-      macroExecutor: macroExecutor,
+      macroExecutor: _macroExecutor,
     );
 
     for (var macroUnit in linkResult.macroGeneratedUnits) {
@@ -204,6 +206,14 @@
     return elementFactory.libraryOfUri2('$testUri');
   }
 
+  @mustCallSuper
+  Future<void> tearDown() async {
+    await _macroExecutor.close();
+    KernelCompilationService.disposeDelayed(
+      const Duration(milliseconds: 100),
+    );
+  }
+
   void _addLibraryUnits(
     Source definingSource,
     CompilationUnit definingUnit,
@@ -315,23 +325,14 @@
       return;
     }
 
-    final macroKernelBuilder = this.macroKernelBuilder;
-    if (macroKernelBuilder == null) {
-      return;
-    }
-
-    final macroExecutor = this.macroExecutor;
-    if (macroExecutor == null) {
-      return;
-    }
-
+    final macroKernelBuilder = const MacroKernelBuilder();
     var macroKernelBytes = await macroKernelBuilder.build(
       fileSystem: _MacroFileSystem(resourceProvider),
       libraries: macroLibraries,
     );
 
     var bundleMacroExecutor = BundleMacroExecutor(
-      macroExecutor: macroExecutor,
+      macroExecutor: _macroExecutor,
       kernelBytes: macroKernelBytes,
       libraries: macroLibraries.map((e) => e.uri).toSet(),
     );
@@ -368,7 +369,7 @@
       await link(
         elementFactory,
         cycleInputLibraries,
-        macroExecutor: macroExecutor,
+        macroExecutor: _macroExecutor,
       );
 
       await _buildMacroLibraries(elementFactory, macroLibraries);
diff --git a/pkg/analyzer/test/src/summary/macro/declaration_text.dart b/pkg/analyzer/test/src/summary/macro/declaration_text.dart
index 85ec1ed..f438eec 100644
--- a/pkg/analyzer/test/src/summary/macro/declaration_text.dart
+++ b/pkg/analyzer/test/src/summary/macro/declaration_text.dart
@@ -145,7 +145,7 @@
     }
   }
 
-  void _writeFormalParameter(ParameterDeclaration node) {
+  void _writeFormalParameter(FunctionTypeParameter node) {
     final String closeSeparator;
     if (node.isNamed) {
       _sink.write('{');
@@ -161,8 +161,10 @@
     }
 
     write(node.type);
-    _sink.write(' ');
-    _sink.write(node.identifier.name);
+    if (node.name != null) {
+      _sink.write(' ');
+      _sink.write(node.name);
+    }
 
     _sink.write(closeSeparator);
   }
diff --git a/pkg/analyzer/test/src/summary/macro_test.dart b/pkg/analyzer/test/src/summary/macro_test.dart
index f21ac8a..e2dd9f2 100644
--- a/pkg/analyzer/test/src/summary/macro_test.dart
+++ b/pkg/analyzer/test/src/summary/macro_test.dart
@@ -2,13 +2,10 @@
 // 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:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
-    as macro;
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/element.dart';
-import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
-import 'package:analyzer/src/summary2/macro.dart';
+import 'package:analyzer/src/summary2/macro_application_error.dart';
 import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -64,16 +61,6 @@
       PackageConfigFileBuilder(),
       macrosEnvironment: MacrosEnvironment.instance,
     );
-
-    macroKernelBuilder = FrontEndServerMacroKernelBuilder();
-    macroExecutor = macro.MultiMacroExecutor();
-  }
-
-  Future<void> tearDown() async {
-    await macroExecutor?.close();
-    KernelCompilationService.disposeDelayed(
-      const Duration(milliseconds: 100),
-    );
   }
 
   test_arguments_error() async {
@@ -482,6 +469,64 @@
 ''');
   }
 
+  test_macroApplicationErrors_compileTimeError() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+  buildTypesForClass(clazz, builder) {
+    unresolved;
+  }
+}
+''');
+
+    final library = await buildLibrary(r'''
+import 'a.dart';
+
+@MyMacro()
+class A {}
+''', preBuildSequence: [
+      {'package:test/a.dart'}
+    ]);
+
+    final A = library.getType('A') as ClassElementImpl;
+    final error = A.macroApplicationErrors.single;
+    error as UnknownMacroApplicationError;
+
+    expect(error.annotationIndex, 0);
+    expect(error.message, contains('unresolved'));
+    expect(error.stackTrace, contains('executeTypesMacro'));
+  }
+
+  test_macroApplicationErrors_throwsException() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+  buildTypesForClass(clazz, builder) {
+    throw 'foo bar';
+  }
+}
+''');
+
+    final library = await buildLibrary(r'''
+import 'a.dart';
+
+@MyMacro()
+class A {}
+''', preBuildSequence: [
+      {'package:test/a.dart'}
+    ]);
+
+    final A = library.getType('A') as ClassElementImpl;
+    final error = A.macroApplicationErrors.single;
+    error as UnknownMacroApplicationError;
+
+    expect(error.annotationIndex, 0);
+    expect(error.message, 'foo bar');
+    expect(error.stackTrace, contains('MyMacro.buildTypesForClass'));
+  }
+
   test_macroFlag_class() async {
     var library = await buildLibrary(r'''
 macro class A {}
@@ -577,7 +622,7 @@
 import 'package:_fe_analyzer_shared/src/macros/api.dart';
 
 macro class ArgumentsTextMacro implements ClassTypesMacro {
-${fields.entries.map((e) => '  final${e.value} ${e.key}').join('\n')}
+${fields.entries.map((e) => '  final ${e.value} ${e.key};').join('\n')}
 
   const ArgumentsTextMacro${constructorParametersCode.trim()};
 
diff --git a/pkg/compiler/lib/src/common/elements.dart b/pkg/compiler/lib/src/common/elements.dart
index 2919892..c190bfc 100644
--- a/pkg/compiler/lib/src/common/elements.dart
+++ b/pkg/compiler/lib/src/common/elements.dart
@@ -247,7 +247,7 @@
   LibraryEntity get foreignLibrary =>
       _foreignLibrary ??= _env.lookupLibrary(Uris.dart__foreign_helper);
 
-  /// The dart:_internal library.
+  /// The dart:_rti library.
   LibraryEntity get rtiLibrary =>
       _rtiLibrary ??= _env.lookupLibrary(Uris.dart__rti, required: true);
 
@@ -926,9 +926,24 @@
 
   FunctionEntity get defineProperty => _findHelperFunction('defineProperty');
 
+  FunctionEntity get throwLateFieldNI =>
+      _findLateHelperFunction('throwLateFieldNI');
+
+  FunctionEntity get throwLateFieldAI =>
+      _findLateHelperFunction('throwLateFieldAI');
+
   FunctionEntity get throwLateFieldADI =>
       _findLateHelperFunction('throwLateFieldADI');
 
+  FunctionEntity get throwUnnamedLateFieldNI =>
+      _findLateHelperFunction('throwUnnamedLateFieldNI');
+
+  FunctionEntity get throwUnnamedLateFieldAI =>
+      _findLateHelperFunction('throwUnnamedLateFieldAI');
+
+  FunctionEntity get throwUnnamedLateFieldADI =>
+      _findLateHelperFunction('throwUnnamedLateFieldADI');
+
   bool isExtractTypeArguments(FunctionEntity member) {
     return member.name == 'extractTypeArguments' &&
         member.library == internalLibrary;
@@ -1120,6 +1135,9 @@
   /// Most foreign helpers are located in the `dart:_foreign_helper` library.
   bool isForeignHelper(MemberEntity member) {
     return member.library == foreignLibrary ||
+        isLateReadCheck(member) ||
+        isLateWriteOnceCheck(member) ||
+        isLateInitializeOnceCheck(member) ||
         isCreateInvocationMirrorHelper(member);
   }
 
@@ -1139,17 +1157,35 @@
       _isTopLevelFunctionNamed('isJsSentinel', member);
 
   /// Returns `true` if [member] is the `_lateReadCheck` function defined in
-  /// dart:_internal.
+  /// dart:_late_helper.
   bool isLateReadCheck(MemberEntity member) =>
       member.library == lateHelperLibrary &&
       _isTopLevelFunctionNamed('_lateReadCheck', member);
 
+  /// Returns `true` if [member] is the `_lateWriteOnceCheck` function defined
+  /// in dart:_late_helper.
+  bool isLateWriteOnceCheck(MemberEntity member) =>
+      member.library == lateHelperLibrary &&
+      _isTopLevelFunctionNamed('_lateWriteOnceCheck', member);
+
+  /// Returns `true` if [member] is the `_lateInitializeOnceCheck` function
+  /// defined in dart:_late_helper.
+  bool isLateInitializeOnceCheck(MemberEntity member) =>
+      member.library == lateHelperLibrary &&
+      _isTopLevelFunctionNamed('_lateInitializeOnceCheck', member);
+
   /// Returns `true` if [member] is the `createSentinel` function defined in
   /// dart:_internal.
   bool isCreateSentinel(MemberEntity member) =>
       member.library == internalLibrary &&
       _isTopLevelFunctionNamed('createSentinel', member);
 
+  /// Returns `true` if [member] is the `isSentinel` function defined in
+  /// dart:_internal.
+  bool isIsSentinel(MemberEntity member) =>
+      member.library == internalLibrary &&
+      _isTopLevelFunctionNamed('isSentinel', member);
+
   ClassEntity getDefaultSuperclass(
       ClassEntity cls, NativeBasicData nativeBasicData) {
     if (nativeBasicData.isJsInteropClass(cls)) {
diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
index d1f6982..a1c9da3 100644
--- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart
+++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
@@ -279,7 +279,7 @@
     Local local = _localsMap.getLocalVariable(node);
     DartType type = _localsMap.getLocalType(_elementMap, local);
     _state.updateLocal(_inferrer, _capturedAndBoxed, local,
-        _inferrer.typeOfParameter(local), node, type);
+        _inferrer.typeOfParameter(local), type);
     if (isOptional) {
       TypeInformation type;
       if (node.initializer != null) {
@@ -816,10 +816,10 @@
     DartType type = _localsMap.getLocalType(_elementMap, local);
     if (node.initializer == null) {
       _state.updateLocal(
-          _inferrer, _capturedAndBoxed, local, _types.nullType, node, type);
+          _inferrer, _capturedAndBoxed, local, _types.nullType, type);
     } else {
-      _state.updateLocal(_inferrer, _capturedAndBoxed, local,
-          visit(node.initializer), node, type);
+      _state.updateLocal(
+          _inferrer, _capturedAndBoxed, local, visit(node.initializer), type);
     }
     if (node.initializer is ir.ThisExpression) {
       _state.markThisAsExposed();
@@ -844,8 +844,7 @@
     }
     Local local = _localsMap.getLocalVariable(node.variable);
     DartType type = _localsMap.getLocalType(_elementMap, local);
-    _state.updateLocal(
-        _inferrer, _capturedAndBoxed, local, rhsType, node, type);
+    _state.updateLocal(_inferrer, _capturedAndBoxed, local, rhsType, type);
     return rhsType;
   }
 
@@ -938,7 +937,7 @@
     // receiver of the call to `==`, which doesn't happen in this case. Remove
     // this when the ssa builder recognizes `== null` directly.
     _typeOfReceiver(node, node.expression);
-    _potentiallyAddNullCheck(node, node.expression);
+    _potentiallyAddNullCheck(node.expression);
     return _types.boolType;
   }
 
@@ -996,11 +995,11 @@
     bool rightIsNull = _types.isNull(rightType);
     if (leftIsNull) {
       // [right] is `null` if [node] evaluates to `true`.
-      _potentiallyAddNullCheck(node, right);
+      _potentiallyAddNullCheck(right);
     }
     if (rightIsNull) {
       // [left] is `null` if [node] evaluates to `true`.
-      _potentiallyAddNullCheck(node, left);
+      _potentiallyAddNullCheck(left);
     }
     if (leftIsNull || rightIsNull) {
       // `left == right` where `left` and/or `right` is known to have type
@@ -1088,7 +1087,7 @@
         // Receiver strengthening to non-null.
         DartType type = _localsMap.getLocalType(_elementMap, local);
         _state.updateLocal(
-            _inferrer, _capturedAndBoxed, local, receiverType, node, type,
+            _inferrer, _capturedAndBoxed, local, receiverType, type,
             excludeNull: !selector.appliesToNullWithoutThrow());
       }
     }
@@ -1191,8 +1190,8 @@
 
     Local variable = _localsMap.getLocalVariable(node.variable);
     DartType variableType = _localsMap.getLocalType(_elementMap, variable);
-    _state.updateLocal(_inferrer, _capturedAndBoxed, variable, currentType,
-        node.variable, variableType);
+    _state.updateLocal(
+        _inferrer, _capturedAndBoxed, variable, currentType, variableType);
 
     JumpTarget target = _localsMap.getJumpTargetForForIn(node);
     return handleLoop(node, target, () {
@@ -1550,6 +1549,32 @@
     } else if (_closedWorld.commonElements.isCreateSentinel(member)) {
       handleStaticInvoke(node, selector, member, arguments);
       return _types.lateSentinelType;
+    } else if (_closedWorld.commonElements.isIsSentinel(member)) {
+      handleStaticInvoke(node, selector, member, arguments);
+
+      // Calls to `isSentinel` can only come from the late lowering kernel
+      // transformation.
+      final value = node.arguments.positional.single as ir.VariableGet;
+
+      Local local = _localsMap.getLocalVariable(value.variable);
+      DartType localType = _localsMap.getLocalType(_elementMap, local);
+      LocalState stateWhenSentinel = LocalState.childPath(_state);
+      LocalState stateWhenNotSentinel = LocalState.childPath(_state);
+
+      // Narrow tested variable to late sentinel on true branch.
+      stateWhenSentinel.updateLocal(_inferrer, _capturedAndBoxed, local,
+          _types.lateSentinelType, localType);
+
+      // Narrow tested variable to not late sentinel on false branch.
+      TypeInformation currentTypeInformation =
+          stateWhenNotSentinel.readLocal(_inferrer, _capturedAndBoxed, local);
+      stateWhenNotSentinel.updateLocal(_inferrer, _capturedAndBoxed, local,
+          currentTypeInformation, localType,
+          excludeLateSentinel: true);
+
+      _setStateAfter(_state, stateWhenSentinel, stateWhenNotSentinel);
+
+      return _types.boolType;
     } else if (member.isConstructor) {
       return handleConstructorInvoke(
           node, node.arguments, selector, member, arguments);
@@ -1721,13 +1746,13 @@
       TypeInformation currentTypeInformation = stateAfterCheckWhenTrue
           .readLocal(_inferrer, _capturedAndBoxed, local);
       stateAfterCheckWhenTrue.updateLocal(_inferrer, _capturedAndBoxed, local,
-          currentTypeInformation, node, localType,
+          currentTypeInformation, localType,
           isCast: false);
       _setStateAfter(_state, stateAfterCheckWhenTrue, stateAfterCheckWhenFalse);
     }
   }
 
-  void _potentiallyAddNullCheck(ir.Expression node, ir.Expression receiver) {
+  void _potentiallyAddNullCheck(ir.Expression receiver) {
     if (!_accumulateIsChecks) return;
     if (receiver is ir.VariableGet) {
       Local local = _localsMap.getLocalVariable(receiver.variable);
@@ -1736,19 +1761,14 @@
       LocalState stateAfterCheckWhenNotNull = LocalState.childPath(_state);
 
       // Narrow tested variable to 'Null' on true branch.
-      stateAfterCheckWhenNull.updateLocal(_inferrer, _capturedAndBoxed, local,
-          _types.nullType, node, localType);
+      stateAfterCheckWhenNull.updateLocal(
+          _inferrer, _capturedAndBoxed, local, _types.nullType, localType);
 
       // Narrow tested variable to 'not null' on false branch.
       TypeInformation currentTypeInformation = stateAfterCheckWhenNotNull
           .readLocal(_inferrer, _capturedAndBoxed, local);
-      stateAfterCheckWhenNotNull.updateLocal(
-          _inferrer,
-          _capturedAndBoxed,
-          local,
-          currentTypeInformation,
-          node,
-          _closedWorld.commonElements.objectType,
+      stateAfterCheckWhenNotNull.updateLocal(_inferrer, _capturedAndBoxed,
+          local, currentTypeInformation, _closedWorld.commonElements.objectType,
           excludeNull: true);
       _setStateAfter(
           _state, stateAfterCheckWhenNull, stateAfterCheckWhenNotNull);
@@ -1904,7 +1924,7 @@
       Local local = _localsMap.getLocalVariable(variable);
       DartType type = _localsMap.getLocalType(_elementMap, local);
       _state.updateLocal(
-          _inferrer, _capturedAndBoxed, local, localFunctionType, node, type,
+          _inferrer, _capturedAndBoxed, local, localFunctionType, type,
           excludeNull: true);
     }
 
@@ -2059,8 +2079,8 @@
         mask = _types.dynamicType;
       }
       Local local = _localsMap.getLocalVariable(exception);
-      _state.updateLocal(_inferrer, _capturedAndBoxed, local, mask, node,
-          _dartTypes.dynamicType(),
+      _state.updateLocal(
+          _inferrer, _capturedAndBoxed, local, mask, _dartTypes.dynamicType(),
           excludeNull: true /* `throw null` produces a NullThrownError */);
     }
     ir.VariableDeclaration stackTrace = node.stackTrace;
@@ -2070,7 +2090,7 @@
       // Note: stack trace may be null if users omit a stack in
       // `completer.completeError`.
       _state.updateLocal(_inferrer, _capturedAndBoxed, local,
-          _types.dynamicType, node, _dartTypes.dynamicType());
+          _types.dynamicType, _dartTypes.dynamicType());
     }
     visit(node.body);
     return null;
@@ -2408,7 +2428,7 @@
     if (field != null) {
       return inferrer.typeOfMember(field);
     } else {
-      return _locals.use(inferrer, local);
+      return _locals.use(local);
     }
   }
 
@@ -2417,19 +2437,21 @@
       Map<Local, FieldEntity> capturedAndBoxed,
       Local local,
       TypeInformation type,
-      ir.Node node,
       DartType staticType,
       {isCast = true,
-      excludeNull = false}) {
+      excludeNull = false,
+      excludeLateSentinel = false}) {
     assert(type != null);
     type = inferrer.types.narrowType(type, staticType,
-        isCast: isCast, excludeNull: excludeNull, excludeLateSentinel: true);
+        isCast: isCast,
+        excludeNull: excludeNull,
+        excludeLateSentinel: excludeLateSentinel);
 
     FieldEntity field = capturedAndBoxed[local];
     if (field != null) {
       inferrer.recordTypeOfField(field, type);
     } else {
-      _locals.update(inferrer, local, type, node, staticType, _tryBlock);
+      _locals.update(inferrer, local, type, _tryBlock);
     }
   }
 
diff --git a/pkg/compiler/lib/src/inferrer/locals_handler.dart b/pkg/compiler/lib/src/inferrer/locals_handler.dart
index 2545b2b..d32de95 100644
--- a/pkg/compiler/lib/src/inferrer/locals_handler.dart
+++ b/pkg/compiler/lib/src/inferrer/locals_handler.dart
@@ -9,7 +9,6 @@
 import 'dart:collection' show IterableMixin;
 import 'package:kernel/ast.dart' as ir;
 import '../elements/entities.dart';
-import '../elements/types.dart';
 import '../ir/util.dart';
 import '../util/util.dart';
 import 'inferrer_engine.dart';
@@ -342,12 +341,12 @@
   LocalsHandler.deepCopyOf(LocalsHandler other)
       : _locals = VariableScope.deepCopyOf(other._locals);
 
-  TypeInformation use(InferrerEngine inferrer, Local local) {
+  TypeInformation use(Local local) {
     return _locals[local];
   }
 
   void update(InferrerEngine inferrer, Local local, TypeInformation type,
-      ir.Node node, DartType staticType, LocalsHandler tryBlock) {
+      LocalsHandler tryBlock) {
     if (tryBlock != null) {
       // We don't know if an assignment in a try block
       // will be executed, so all assignments in that block are
diff --git a/pkg/compiler/lib/src/inferrer/type_system.dart b/pkg/compiler/lib/src/inferrer/type_system.dart
index d28d06f..9a90a88 100644
--- a/pkg/compiler/lib/src/inferrer/type_system.dart
+++ b/pkg/compiler/lib/src/inferrer/type_system.dart
@@ -9,7 +9,6 @@
 import '../constants/values.dart' show BoolConstantValue;
 import '../elements/entities.dart';
 import '../elements/types.dart';
-import '../ir/static_type.dart' show ClassRelation;
 import '../world.dart';
 import 'abstract_value_domain.dart';
 import 'type_graph_nodes.dart';
@@ -350,34 +349,49 @@
       {bool isCast = true,
       bool excludeNull = false,
       bool excludeLateSentinel = false}) {
-    // Avoid refining an input with an exact type. It we are almost always
+    AbstractValue inferredType = type.type;
+
+    TypeInformation _excludeLateSentinel() {
+      if (!excludeLateSentinel) return type;
+      final newType = NarrowTypeInformation(
+          _abstractValueDomain, type, _abstractValueDomain.dynamicType);
+      allocatedTypes.add(newType);
+      return newType;
+    }
+
+    // Avoid refining an input with an exact type, since we are almost always
     // adding a narrowing to a subtype of the same class or a superclass.
-    if (_abstractValueDomain.isExact(type.type).isDefinitelyTrue) return type;
+    if (_abstractValueDomain.isExact(inferredType).isDefinitelyTrue) {
+      return _excludeLateSentinel();
+    }
 
-    AbstractValueWithPrecision narrowing =
-        _abstractValueDomain.createFromStaticType(annotation,
-            classRelation: ClassRelation.subtype, nullable: isCast);
+    AbstractValue narrowing = _abstractValueDomain
+        .createFromStaticType(annotation, nullable: isCast)
+        .abstractValue;
 
-    AbstractValue abstractValue = narrowing.abstractValue;
     if (excludeNull) {
-      abstractValue = _abstractValueDomain.excludeNull(abstractValue);
-    }
-    if (!excludeLateSentinel) {
-      abstractValue = _abstractValueDomain.includeLateSentinel(abstractValue);
+      narrowing = _abstractValueDomain.excludeNull(narrowing);
     }
 
-    if (_abstractValueDomain.containsAll(abstractValue).isPotentiallyTrue) {
+    if (!excludeLateSentinel) {
+      // Narrowing is an intersection of [AbstractValue]s. Unless
+      // [excludeLateSentinel] is `true`, we include the late sentinel here so
+      // that it is preserved by the intersection.
+      narrowing = _abstractValueDomain.includeLateSentinel(narrowing);
+    }
+
+    if (_abstractValueDomain.containsAll(narrowing).isPotentiallyTrue) {
       // Top, or non-nullable Top.
-      if (_abstractValueDomain.isNull(abstractValue).isPotentiallyTrue) {
-        return type;
+      if (_abstractValueDomain.isNull(narrowing).isPotentiallyTrue) {
+        return _excludeLateSentinel();
       }
-      // If the input is already narrowed to be not-null, there is no value
-      // in adding another narrowing node.
-      if (_isNonNullNarrow(type)) return type;
+      // If the input is already narrowed to be non-null, there is no value in
+      // adding another non-null narrowing node.
+      if (_isNonNullNarrow(type)) return _excludeLateSentinel();
     }
 
     TypeInformation newType =
-        NarrowTypeInformation(_abstractValueDomain, type, abstractValue);
+        NarrowTypeInformation(_abstractValueDomain, type, narrowing);
     allocatedTypes.add(newType);
     return newType;
   }
diff --git a/pkg/compiler/lib/src/js_backend/backend_impact.dart b/pkg/compiler/lib/src/js_backend/backend_impact.dart
index 37855c0..f6dfa17 100644
--- a/pkg/compiler/lib/src/js_backend/backend_impact.dart
+++ b/pkg/compiler/lib/src/js_backend/backend_impact.dart
@@ -779,4 +779,28 @@
       ], otherImpacts: [
         _needsString('Needed to encode the new RTI ruleset.')
       ]);
+
+  BackendImpact _lateFieldReadCheck;
+
+  BackendImpact get lateFieldReadCheck =>
+      _lateFieldReadCheck ??= BackendImpact(globalUses: [
+        _commonElements.throwUnnamedLateFieldNI,
+        _commonElements.throwLateFieldNI,
+      ]);
+
+  BackendImpact _lateFieldWriteOnceCheck;
+
+  BackendImpact get lateFieldWriteOnceCheck =>
+      _lateFieldWriteOnceCheck ??= BackendImpact(globalUses: [
+        _commonElements.throwUnnamedLateFieldAI,
+        _commonElements.throwLateFieldAI,
+      ]);
+
+  BackendImpact _lateFieldInitializeOnceCheck;
+
+  BackendImpact get lateFieldInitializeOnceCheck =>
+      _lateFieldInitializeOnceCheck ??= BackendImpact(globalUses: [
+        _commonElements.throwUnnamedLateFieldADI,
+        _commonElements.throwLateFieldADI,
+      ]);
 }
diff --git a/pkg/compiler/lib/src/js_backend/resolution_listener.dart b/pkg/compiler/lib/src/js_backend/resolution_listener.dart
index 88e96d3..c6b8b41 100644
--- a/pkg/compiler/lib/src/js_backend/resolution_listener.dart
+++ b/pkg/compiler/lib/src/js_backend/resolution_listener.dart
@@ -292,6 +292,19 @@
       _registerBackendImpact(worldImpact, _impacts.noSuchMethodSupport);
     }
 
+    if (_commonElements.isLateReadCheck(member)) {
+      _registerBackendImpact(worldImpact, _impacts.lateFieldReadCheck);
+    }
+
+    if (_commonElements.isLateWriteOnceCheck(member)) {
+      _registerBackendImpact(worldImpact, _impacts.lateFieldWriteOnceCheck);
+    }
+
+    if (_commonElements.isLateInitializeOnceCheck(member)) {
+      _registerBackendImpact(
+          worldImpact, _impacts.lateFieldInitializeOnceCheck);
+    }
+
     if (member.isGetter && member.name == Identifiers.runtimeType_) {
       // Enable runtime type support if we discover a getter called
       // runtimeType. We have to enable runtime type before hitting the
diff --git a/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart b/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
index bdec099..7711128 100644
--- a/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
+++ b/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
@@ -462,6 +462,16 @@
         //   }
         //   return value;
         // }
+        //
+        // The following lowering is also possible but currently worse:
+        //
+        // T get field {
+        //   var value = this._#field;
+        //   return isSentinel(value) ? this._#field = e : value;
+        // }
+        //
+        // This lowering avoids generating an extra narrowing node in inference,
+        // but the generated code is worse due to poor register allocation.
         VariableDeclaration value =
             VariableDeclaration('value', initializer: fieldRead(), type: type)
               ..fileOffset = fileOffset;
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index c34f02f..02d707c 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -4212,6 +4212,12 @@
       _handleForeignCreateJsSentinel(invocation);
     } else if (name == 'isJsSentinel') {
       _handleForeignIsJsSentinel(invocation);
+    } else if (name == '_lateReadCheck') {
+      _handleLateReadCheck(invocation);
+    } else if (name == '_lateWriteOnceCheck') {
+      _handleLateWriteOnceCheck(invocation);
+    } else if (name == '_lateInitializeOnceCheck') {
+      _handleLateInitializeOnceCheck(invocation);
     } else {
       reporter.internalError(
           _elementMap.getSpannable(targetElement, invocation),
@@ -4901,6 +4907,14 @@
   }
 
   void _handleForeignCreateJsSentinel(ir.StaticInvocation invocation) {
+    if (_unexpectedForeignArguments(invocation,
+        minPositional: 0, maxPositional: 0, typeArgumentCount: 1)) {
+      stack.add(
+          // Result expected on stack.
+          graph.addConstantNull(closedWorld));
+      return;
+    }
+
     SourceInformation sourceInformation =
         _sourceInformationBuilder.buildCall(invocation, invocation);
     stack.add(graph.addConstantLateSentinel(closedWorld,
@@ -4908,6 +4922,14 @@
   }
 
   void _handleForeignIsJsSentinel(ir.StaticInvocation invocation) {
+    if (_unexpectedForeignArguments(invocation,
+        minPositional: 1, maxPositional: 1)) {
+      stack.add(
+          // Result expected on stack.
+          graph.addConstantNull(closedWorld));
+      return;
+    }
+
     SourceInformation sourceInformation =
         _sourceInformationBuilder.buildCall(invocation, invocation);
     HInstruction checkedExpression =
@@ -4916,6 +4938,72 @@
       ..sourceInformation = sourceInformation);
   }
 
+  // TODO(fishythefish): Support specialization of late sentinels based on type.
+
+  void _handleLateReadCheck(ir.StaticInvocation invocation) {
+    if (_unexpectedForeignArguments(invocation,
+        minPositional: 2, maxPositional: 2, typeArgumentCount: 1)) {
+      stack.add(
+          // Result expected on stack.
+          graph.addConstantNull(closedWorld));
+      return;
+    }
+
+    SourceInformation sourceInformation =
+        _sourceInformationBuilder.buildCall(invocation, invocation);
+
+    List<HInstruction> arguments =
+        _visitPositionalArguments(invocation.arguments);
+    HInstruction value = arguments[0];
+    HInstruction name = options.omitLateNames ? null : arguments[1];
+
+    push(HLateReadCheck(value, name,
+        _abstractValueDomain.excludeLateSentinel(value.instructionType))
+      ..sourceInformation = sourceInformation);
+  }
+
+  void _handleLateWriteOnceCheck(ir.StaticInvocation invocation) {
+    if (_unexpectedForeignArguments(invocation,
+        minPositional: 2, maxPositional: 2)) {
+      stack.add(
+          // Result expected on stack.
+          graph.addConstantNull(closedWorld));
+      return;
+    }
+
+    SourceInformation sourceInformation =
+        _sourceInformationBuilder.buildCall(invocation, invocation);
+
+    List<HInstruction> arguments =
+        _visitPositionalArguments(invocation.arguments);
+    HInstruction value = arguments[0];
+    HInstruction name = options.omitLateNames ? null : arguments[1];
+
+    push(HLateWriteOnceCheck(value, name, _abstractValueDomain.dynamicType)
+      ..sourceInformation = sourceInformation);
+  }
+
+  void _handleLateInitializeOnceCheck(ir.StaticInvocation invocation) {
+    if (_unexpectedForeignArguments(invocation,
+        minPositional: 2, maxPositional: 2)) {
+      stack.add(
+          // Result expected on stack.
+          graph.addConstantNull(closedWorld));
+      return;
+    }
+
+    SourceInformation sourceInformation =
+        _sourceInformationBuilder.buildCall(invocation, invocation);
+
+    List<HInstruction> arguments =
+        _visitPositionalArguments(invocation.arguments);
+    HInstruction value = arguments[0];
+    HInstruction name = options.omitLateNames ? null : arguments[1];
+
+    push(HLateInitializeOnceCheck(value, name, _abstractValueDomain.dynamicType)
+      ..sourceInformation = sourceInformation);
+  }
+
   void _pushStaticInvocation(MemberEntity target, List<HInstruction> arguments,
       AbstractValue typeMask, List<DartType> typeArguments,
       {SourceInformation sourceInformation, InterfaceType instanceType}) {
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index dbdf302..c394f07 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -729,7 +729,8 @@
           instruction is HAsCheck ||
           instruction is HAsCheckSimple ||
           instruction is HBoolConversion ||
-          instruction is HNullCheck) {
+          instruction is HNullCheck ||
+          instruction is HLateReadCheck) {
         String inputName = variableNames.getName(instruction.checkedInput);
         if (variableNames.getName(instruction) == inputName) {
           needsAssignment = false;
@@ -3017,6 +3018,94 @@
   }
 
   @override
+  void visitLateReadCheck(HLateReadCheck node) {
+    // We generate code roughly equivalent to invoking:
+    //
+    // T _lateReadCheck<T>(T value, String name) {
+    //   if (isSentinel(value)) throw LateError.fieldNI(name);
+    //   return value;
+    // }
+
+    assert(!node.isRedundant(_closedWorld));
+
+    final sourceInformation = node.sourceInformation;
+
+    _emitIsLateSentinel(node.checkedInput, sourceInformation);
+    final condition = pop();
+
+    if (node.hasName) {
+      use(node.name);
+      _pushCallStatic(
+          _commonElements.throwLateFieldNI, [pop()], sourceInformation);
+    } else {
+      _pushCallStatic(
+          _commonElements.throwUnnamedLateFieldNI, const [], sourceInformation);
+    }
+
+    final lateError =
+        pop().toStatement().withSourceInformation(sourceInformation);
+    pushStatement(js.If.noElse(condition, lateError)
+        .withSourceInformation(sourceInformation));
+  }
+
+  @override
+  void visitLateWriteOnceCheck(HLateWriteOnceCheck node) {
+    // We generate code roughly equivalent to invoking:
+    //
+    // void _lateWriteOnceCheck(Object? value, String name) {
+    //   if (!isSentinel(value)) throw LateError.fieldAI(name);
+    // }
+
+    assert(!node.isRedundant(_closedWorld));
+
+    final sourceInformation = node.sourceInformation;
+    _emitIsLateSentinel(node.checkedInput, sourceInformation, inverse: true);
+    final condition = pop();
+
+    if (node.hasName) {
+      use(node.name);
+      _pushCallStatic(
+          _commonElements.throwLateFieldAI, [pop()], sourceInformation);
+    } else {
+      _pushCallStatic(
+          _commonElements.throwUnnamedLateFieldAI, [], sourceInformation);
+    }
+    final lateError =
+        pop().toStatement().withSourceInformation(sourceInformation);
+    pushStatement(js.If.noElse(condition, lateError)
+        .withSourceInformation(sourceInformation));
+  }
+
+  @override
+  void visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) {
+    // We generate code roughly equivalent to invoking:
+    //
+    // void _lateInitializeOnceCheck(Object? value, String name) {
+    //   if (!isSentinel(value)) throw LateError.fieldADI(name);
+    // }
+
+    assert(!node.isRedundant(_closedWorld));
+
+    final sourceInformation = node.sourceInformation;
+    _emitIsLateSentinel(node.checkedInput, sourceInformation, inverse: true);
+    final condition = pop();
+
+    if (node.hasName) {
+      use(node.name);
+      _pushCallStatic(
+          _commonElements.throwLateFieldADI, [pop()], sourceInformation);
+    } else {
+      _pushCallStatic(
+          _commonElements.throwUnnamedLateFieldADI, [], sourceInformation);
+    }
+
+    final lateError =
+        pop().toStatement().withSourceInformation(sourceInformation);
+    pushStatement(js.If.noElse(condition, lateError)
+        .withSourceInformation(sourceInformation));
+  }
+
+  @override
   void visitTypeKnown(HTypeKnown node) {
     // [HTypeKnown] instructions are removed before generating code.
     assert(false);
@@ -3299,9 +3388,9 @@
         StaticUse.directInvoke(method, selector.callStructure, null));
   }
 
-  _emitIsLateSentinel(HIsLateSentinel node, SourceInformation sourceInformation,
+  _emitIsLateSentinel(HInstruction input, SourceInformation sourceInformation,
       {inverse = false}) {
-    use(node.inputs[0]);
+    use(input);
     js.Expression value = pop();
     js.Expression sentinel =
         _emitter.constantReference(LateSentinelConstantValue());
@@ -3311,5 +3400,5 @@
 
   @override
   visitIsLateSentinel(HIsLateSentinel node) =>
-      _emitIsLateSentinel(node, node.sourceInformation);
+      _emitIsLateSentinel(node.inputs.single, node.sourceInformation);
 }
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index 600cbac..ca162ab 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -778,6 +778,19 @@
   }
 
   @override
+  void visitLateReadCheck(HLateReadCheck instruction) {
+    // If the checked value is used, the input might still have one use
+    // (i.e. this HLateReadCheck), but it cannot be generated at use, since we
+    // will rely on non-generate-at-use to assign the value to a variable.
+    //
+    // However, if the checked value is unused then the input may be generated
+    // at use in the check.
+    if (instruction.usedBy.isEmpty) {
+      visitInstruction(instruction);
+    }
+  }
+
+  @override
   void visitTypeKnown(HTypeKnown instruction) {
     // [HTypeKnown] instructions are removed before code generation.
     assert(false);
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 16ac75c..6e8e464 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -105,6 +105,9 @@
   R visitPrimitiveCheck(HPrimitiveCheck node);
   R visitBoolConversion(HBoolConversion node);
   R visitNullCheck(HNullCheck node);
+  R visitLateReadCheck(HLateReadCheck node);
+  R visitLateWriteOnceCheck(HLateWriteOnceCheck node);
+  R visitLateInitializeOnceCheck(HLateInitializeOnceCheck node);
   R visitTypeKnown(HTypeKnown node);
   R visitYield(HYield node);
 
@@ -591,6 +594,13 @@
   @override
   visitNullCheck(HNullCheck node) => visitCheck(node);
   @override
+  visitLateReadCheck(HLateReadCheck node) => visitCheck(node);
+  @override
+  visitLateWriteOnceCheck(HLateWriteOnceCheck node) => visitCheck(node);
+  @override
+  visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) =>
+      visitCheck(node);
+  @override
   visitPrimitiveCheck(HPrimitiveCheck node) => visitCheck(node);
   @override
   visitTypeKnown(HTypeKnown node) => visitCheck(node);
@@ -1101,6 +1111,10 @@
   static const int STRING_CONCAT_TYPECODE = 59;
   static const int STRINGIFY_TYPECODE = 60;
 
+  static const int LATE_READ_CHECK_TYPECODE = 61;
+  static const int LATE_WRITE_ONCE_CHECK_TYPECODE = 62;
+  static const int LATE_INITIALIZE_ONCE_CHECK_TYPECODE = 63;
+
   HInstruction(this.inputs, this.instructionType) {
     assert(inputs.every((e) => e != null), "inputs: $inputs");
   }
@@ -3677,6 +3691,121 @@
   }
 }
 
+/// A check for a late sentinel to determine if a late field may be read from or
+/// written to.
+abstract class HLateCheck extends HCheck {
+  final HInstruction name;
+
+  HLateCheck(HInstruction input, this.name, AbstractValue type)
+      : super([input, if (name != null) name], type);
+
+  bool get hasName => name != null;
+
+  @override
+  bool isControlFlow() => true;
+
+  @override
+  bool isCodeMotionInvariant() => false;
+}
+
+/// A check that a late field has been initialized and can therefore be read.
+class HLateReadCheck extends HLateCheck {
+  HLateReadCheck(HInstruction input, HInstruction name, AbstractValue type)
+      : super(input, name, type);
+
+  @override
+  accept(HVisitor visitor) => visitor.visitLateReadCheck(this);
+
+  @override
+  int typeCode() => HInstruction.LATE_READ_CHECK_TYPECODE;
+
+  @override
+  bool typeEquals(HInstruction other) => other is HLateReadCheck;
+
+  @override
+  bool dataEquals(HLateReadCheck other) => true;
+
+  bool isRedundant(JClosedWorld closedWorld) {
+    AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
+    AbstractValue inputType = checkedInput.instructionType;
+    return abstractValueDomain.isLateSentinel(inputType).isDefinitelyFalse;
+  }
+
+  @override
+  String toString() {
+    return 'HLateReadCheck($checkedInput)';
+  }
+}
+
+/// A check that a late final field has not been initialized yet and can
+/// therefore be written to.
+///
+/// The difference between [HLateWriteOnceCheck] and [HLateInitializeOnceCheck]
+/// is that the latter occurs on writes performed as part of the initializer
+/// expression.
+class HLateWriteOnceCheck extends HLateCheck {
+  HLateWriteOnceCheck(HInstruction input, HInstruction name, AbstractValue type)
+      : super(input, name, type);
+
+  @override
+  accept(HVisitor visitor) => visitor.visitLateWriteOnceCheck(this);
+
+  @override
+  int typeCode() => HInstruction.LATE_WRITE_ONCE_CHECK_TYPECODE;
+
+  @override
+  bool typeEquals(HInstruction other) => other is HLateWriteOnceCheck;
+
+  @override
+  bool dataEquals(HLateWriteOnceCheck other) => true;
+
+  bool isRedundant(JClosedWorld closedWorld) {
+    AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
+    AbstractValue inputType = checkedInput.instructionType;
+    return abstractValueDomain.isLateSentinel(inputType).isDefinitelyTrue;
+  }
+
+  @override
+  String toString() {
+    return 'HLateWriteOnceCheck($checkedInput)';
+  }
+}
+
+/// A check that a late final field has not been initialized yet and can
+/// therefore be initialized.
+///
+/// The difference between [HLateWriteOnceCheck] and [HLateInitializeOnceCheck]
+/// is that the latter occurs on writes performed as part of the initializer
+/// expression.
+class HLateInitializeOnceCheck extends HLateCheck {
+  HLateInitializeOnceCheck(
+      HInstruction input, HInstruction name, AbstractValue type)
+      : super(input, name, type);
+
+  @override
+  accept(HVisitor visitor) => visitor.visitLateInitializeOnceCheck(this);
+
+  @override
+  int typeCode() => HInstruction.LATE_INITIALIZE_ONCE_CHECK_TYPECODE;
+
+  @override
+  bool typeEquals(HInstruction other) => other is HLateInitializeOnceCheck;
+
+  @override
+  bool dataEquals(HLateInitializeOnceCheck other) => true;
+
+  bool isRedundant(JClosedWorld closedWorld) {
+    AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
+    AbstractValue inputType = checkedInput.instructionType;
+    return abstractValueDomain.isLateSentinel(inputType).isDefinitelyTrue;
+  }
+
+  @override
+  String toString() {
+    return 'HLateInitializeOnceCheck($checkedInput)';
+  }
+}
+
 /// The [HTypeKnown] instruction marks a value with a refined type.
 class HTypeKnown extends HCheck {
   AbstractValue knownType;
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 81af4d3..75f9512 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -1399,6 +1399,24 @@
   }
 
   @override
+  HInstruction visitLateReadCheck(HLateReadCheck node) {
+    if (node.isRedundant(_closedWorld)) return node.checkedInput;
+    return node;
+  }
+
+  @override
+  HInstruction visitLateWriteOnceCheck(HLateWriteOnceCheck node) {
+    if (node.isRedundant(_closedWorld)) return node.checkedInput;
+    return node;
+  }
+
+  @override
+  HInstruction visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) {
+    if (node.isRedundant(_closedWorld)) return node.checkedInput;
+    return node;
+  }
+
+  @override
   HInstruction visitTypeKnown(HTypeKnown node) {
     return node.isRedundant(_closedWorld) ? node.checkedInput : node;
   }
@@ -3771,6 +3789,8 @@
   @override
   void visitNullCheck(HNullCheck instruction) {}
   @override
+  void visitLateReadCheck(HLateReadCheck instruction) {}
+  @override
   void visitParameterValue(HParameterValue instruction) {}
   @override
   void visitRelational(HRelational instruction) {}
diff --git a/pkg/compiler/lib/src/ssa/ssa_tracer.dart b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
index 0db158a..f04fe16 100644
--- a/pkg/compiler/lib/src/ssa/ssa_tracer.dart
+++ b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
@@ -668,6 +668,27 @@
   }
 
   @override
+  String visitLateReadCheck(HLateReadCheck node) {
+    String checkedInput = temporaryId(node.checkedInput);
+    String comment = node.hasName ? "(${temporaryId(node.name)})" : "";
+    return "LateReadCheck$comment: $checkedInput";
+  }
+
+  @override
+  String visitLateWriteOnceCheck(HLateWriteOnceCheck node) {
+    String checkedInput = temporaryId(node.checkedInput);
+    String comment = node.hasName ? "(${temporaryId(node.name)})" : "";
+    return "LateWriteOnceCheck$comment: $checkedInput";
+  }
+
+  @override
+  String visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) {
+    String checkedInput = temporaryId(node.checkedInput);
+    String comment = node.hasName ? "(${temporaryId(node.name)})" : "";
+    return "LateInitializeOnceCheck$comment: $checkedInput";
+  }
+
+  @override
   String visitTypeKnown(HTypeKnown node) {
     assert(node.inputs.length <= 2);
     String result =
diff --git a/pkg/compiler/lib/src/ssa/types_propagation.dart b/pkg/compiler/lib/src/ssa/types_propagation.dart
index 082183b..1ddc6f2 100644
--- a/pkg/compiler/lib/src/ssa/types_propagation.dart
+++ b/pkg/compiler/lib/src/ssa/types_propagation.dart
@@ -449,6 +449,20 @@
   }
 
   @override
+  AbstractValue visitLateReadCheck(HLateReadCheck instruction) {
+    HInstruction input = instruction.checkedInput;
+    AbstractValue inputType = input.instructionType;
+    AbstractValue outputType =
+        abstractValueDomain.excludeLateSentinel(inputType);
+    if (inputType != outputType) {
+      // Replace dominated uses of input with uses of this check so the uses
+      // benefit from the stronger type.
+      input.replaceAllUsersDominatedBy(instruction.next, instruction);
+    }
+    return outputType;
+  }
+
+  @override
   AbstractValue visitAsCheck(HAsCheck instruction) {
     return _narrowAsCheck(instruction, instruction.checkedInput,
         instruction.checkedType.abstractValue);
diff --git a/pkg/compiler/test/codegen/late_field_redundancy_test.dart b/pkg/compiler/test/codegen/late_field_redundancy_test.dart
new file mode 100644
index 0000000..a0ebf05
--- /dev/null
+++ b/pkg/compiler/test/codegen/late_field_redundancy_test.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.10
+
+import 'package:async_helper/async_helper.dart';
+import 'package:expect/expect.dart';
+import '../helpers/compiler_helper.dart';
+
+const String TEST = r"""
+class Foo {
+  late int x;
+}
+
+void entry() {
+  final foo = Foo();
+  foo.x = 42;
+  test(foo);
+}
+
+@pragma('dart2js:noInline')
+void test(Foo foo) {
+  final a = foo.x;
+  final b = foo.x;
+  print([a, b]);
+}
+""";
+
+void main() {
+  asyncTest(() async {
+    await compile(TEST,
+        entry: 'entry',
+        methodName: 'test',
+        disableTypeInference: false,
+        disableInlining: false,
+        soundNullSafety: true, check: (String generated) {
+      RegExp regexp = new RegExp(r'=== \$');
+      Expect.equals(1, regexp.allMatches(generated).length);
+    });
+  });
+}
diff --git a/pkg/compiler/test/codegen/late_field_test.dart b/pkg/compiler/test/codegen/late_field_test.dart
new file mode 100644
index 0000000..fa34ffe
--- /dev/null
+++ b/pkg/compiler/test/codegen/late_field_test.dart
@@ -0,0 +1,60 @@
+// 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.10
+
+import 'dart:async';
+import 'package:async_helper/async_helper.dart';
+import '../helpers/compiler_helper.dart';
+
+const String TEST_DIRECT = r"""
+class Foo {
+  late int x;
+}
+
+int test() {
+  final foo = Foo();
+  foo.x = 40;
+  return foo.x + 2;
+  // present: '42'
+  // absent: '+ 2'
+  // absent: 'add'
+}
+""";
+
+const String TEST_INDIRECT = r"""
+class Foo {
+  late int x;
+}
+
+int entry() {
+  final foo = Foo();
+  foo.x = 40;
+  return test(foo);
+}
+
+@pragma('dart2js:noInline')
+int test(Foo foo) {
+  return foo.x + 2;
+  // present: '+ 2'
+  // absent: 'add'
+}
+""";
+
+Future check(String test, {String entry: 'test'}) {
+  return compile(test,
+      entry: entry,
+      methodName: 'test',
+      check: checkerForAbsentPresent(test),
+      disableTypeInference: false,
+      disableInlining: false,
+      soundNullSafety: true);
+}
+
+void main() {
+  asyncTest(() async {
+    await check(TEST_DIRECT);
+    await check(TEST_INDIRECT, entry: 'entry');
+  });
+}
diff --git a/pkg/compiler/test/codegen/late_test.dart b/pkg/compiler/test/codegen/late_test.dart
deleted file mode 100644
index af820fb..0000000
--- a/pkg/compiler/test/codegen/late_test.dart
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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.10
-
-import 'dart:async';
-import 'package:async_helper/async_helper.dart';
-import '../helpers/compiler_helper.dart';
-
-const String TEST = r"""
-class Foo {
-  late int x;
-}
-
-int foo() {
-  final foo = Foo();
-  foo.x = 40;
-  return foo.x + 2;
-  // present: '+ 2'
-  // absent: 'add'
-}
-""";
-
-Future check(String test) {
-  return compile(test,
-      entry: 'foo',
-      check: checkerForAbsentPresent(test),
-      disableTypeInference: false,
-      disableInlining: false,
-      soundNullSafety: true);
-}
-
-void main() {
-  asyncTest(() async {
-    await check(TEST);
-  });
-}
diff --git a/pkg/dartdev/test/core_test.dart b/pkg/dartdev/test/core_test.dart
index e06feef..dc74efa 100644
--- a/pkg/dartdev/test/core_test.dart
+++ b/pkg/dartdev/test/core_test.dart
@@ -105,7 +105,7 @@
   test('hasDependency', () {
     PackageConfig packageConfig = PackageConfig(jsonDecode(_packageData));
     expect(packageConfig.hasDependency('test'), isFalse);
-    expect(packageConfig.hasDependency('pedantic'), isTrue);
+    expect(packageConfig.hasDependency('lints'), isTrue);
   });
 }
 
@@ -149,8 +149,8 @@
   "configVersion": 2,
   "packages": [
     {
-      "name": "pedantic",
-      "rootUri": "file:///Users/.../.pub-cache/hosted/pub.dartlang.org/pedantic-1.9.0",
+      "name": "lints",
+      "rootUri": "file:///Users/.../.pub-cache/hosted/pub.dartlang.org/lints-1.0.1",
       "packageUri": "lib/",
       "languageVersion": "2.1"
     },
diff --git a/pkg/dartdev/test/utils_test.dart b/pkg/dartdev/test/utils_test.dart
index 642adbe..a25734d 100644
--- a/pkg/dartdev/test/utils_test.dart
+++ b/pkg/dartdev/test/utils_test.dart
@@ -163,8 +163,8 @@
   "configVersion": 2,
   "packages": [
     {
-      "name": "pedantic",
-      "rootUri": "file:///Users/.../.pub-cache/hosted/pub.dartlang.org/pedantic-1.9.0",
+      "name": "lints",
+      "rootUri": "file:///Users/.../.pub-cache/hosted/pub.dartlang.org/lints-1.0.1",
       "packageUri": "lib/",
       "languageVersion": "2.1"
     },
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index b646c44..e193567 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -319,7 +319,8 @@
       _benchmarker
           ?.enterPhase(BenchmarkPhases.incremental_experimentalInvalidation);
       ExperimentalInvalidation? experimentalInvalidation =
-          await _initializeExperimentalInvalidation(reusedResult, c);
+          await _initializeExperimentalInvalidation(
+              reusedResult, c, uriTranslator);
       recorderForTesting?.recordRebuildBodiesCount(
           experimentalInvalidation?.missingSources.length ?? 0);
 
@@ -1165,7 +1166,9 @@
   /// Note that - when doing experimental invalidation - [reusedResult] is
   /// updated.
   Future<ExperimentalInvalidation?> _initializeExperimentalInvalidation(
-      ReusageResult reusedResult, CompilerContext c) async {
+      ReusageResult reusedResult,
+      CompilerContext c,
+      UriTranslator uriTranslator) async {
     Set<LibraryBuilder>? rebuildBodies;
     Set<LibraryBuilder> originalNotReusedLibraries;
     Set<Uri>? missingSources;
@@ -1208,37 +1211,46 @@
         }
       }
 
-      List<int>? previousSource =
-          CompilerContext.current.uriToSource[builder.fileUri]!.source;
-      // ignore: unnecessary_null_comparison
-      if (previousSource == null || previousSource.isEmpty) {
-        return null;
+      List<Uri> builderUris = [builder.fileUri];
+      for (LibraryPart part in builder.library.parts) {
+        Uri? fileUri =
+            uriTranslator.getPartFileUri(builder.library.fileUri, part);
+        if (fileUri != null) builderUris.add(fileUri);
       }
-      ScannerConfiguration scannerConfiguration = new ScannerConfiguration(
-          enableExtensionMethods: true /* can't be disabled */,
-          enableNonNullable: builder
-              .isNonNullableByDefault /* depends on language version etc */,
-          enableTripleShift:
-              /* should this be on the library? */
-              /* this is effectively what the constant evaluator does */
-              context.options.globalFeatures.tripleShift.isEnabled);
-      String? before = textualOutline(previousSource, scannerConfiguration,
-          performModelling: true);
-      if (before == null) {
-        return null;
-      }
-      String? now;
-      FileSystemEntity entity =
-          c.options.fileSystem.entityForUri(builder.fileUri);
-      if (await entity.exists()) {
-        now = textualOutline(await entity.readAsBytes(), scannerConfiguration,
+
+      for (Uri uri in builderUris) {
+        List<int>? previousSource =
+            CompilerContext.current.uriToSource[uri]!.source;
+        // ignore: unnecessary_null_comparison
+        if (previousSource == null || previousSource.isEmpty) {
+          return null;
+        }
+        ScannerConfiguration scannerConfiguration = new ScannerConfiguration(
+            enableExtensionMethods: true /* can't be disabled */,
+            enableNonNullable: builder
+                .isNonNullableByDefault /* depends on language version etc */,
+            enableTripleShift:
+                /* should this be on the library? */
+                /* this is effectively what the constant evaluator does */
+                context.options.globalFeatures.tripleShift.isEnabled);
+        String? before = textualOutline(previousSource, scannerConfiguration,
             performModelling: true);
+        if (before == null) {
+          return null;
+        }
+        String? now;
+        FileSystemEntity entity = c.options.fileSystem.entityForUri(uri);
+        if (await entity.exists()) {
+          now = textualOutline(await entity.readAsBytes(), scannerConfiguration,
+              performModelling: true);
+        }
+        if (before != now) {
+          return null;
+        }
+        missingSources ??= new Set<Uri>();
+        missingSources.add(uri);
       }
-      if (before != now) {
-        return null;
-      }
-      missingSources ??= new Set<Uri>();
-      missingSources.add(builder.fileUri);
+
       LibraryBuilder? partOfLibrary = builder.partOfLibrary;
       rebuildBodies ??= new Set<LibraryBuilder>();
       if (partOfLibrary != null) {
@@ -1255,6 +1267,8 @@
     if (!skipExperimentalInvalidationChecksForTesting) {
       for (LibraryBuilder builder in reusedResult.notReusedLibraries) {
         if (missingSources!.contains(builder.fileUri)) {
+          // Missing sources will be rebuild, so mixin usage there doesn't
+          // matter.
           continue;
         }
         Library lib = builder.library;
diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart
index 498d457..3d62028 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -1576,6 +1576,7 @@
     }
 
     List<Uri> originalUris = List<Uri>.of(fs.data.keys);
+    uriLoop:
     for (Uri uri in originalUris) {
       print("Work on $uri");
       LibraryBuilder? builder = builders[uri];
@@ -1599,14 +1600,21 @@
 
       // Put each chunk into its own file.
       StringBuffer headerSb = new StringBuffer();
+      StringBuffer orgFileOnlyHeaderSb = new StringBuffer();
       List<FuzzAstVisitorSorterChunk> nonHeaderChunks = [];
 
+      print("Found ${fuzzAstVisitorSorter.chunks.length} chunks...");
+
       for (FuzzAstVisitorSorterChunk chunk in fuzzAstVisitorSorter.chunks) {
-        if (chunk.originalType == FuzzOriginalType.Import ||
+        if (chunk.originalType == FuzzOriginalType.PartOf) {
+          print("Skipping part...");
+          continue uriLoop;
+        } else if (chunk.originalType == FuzzOriginalType.Part) {
+          // The part declaration should only be in the "main" file.
+          orgFileOnlyHeaderSb.writeln(chunk.getSource());
+        } else if (chunk.originalType == FuzzOriginalType.Import ||
             chunk.originalType == FuzzOriginalType.Export ||
             chunk.originalType == FuzzOriginalType.LibraryName ||
-            chunk.originalType == FuzzOriginalType.Part ||
-            chunk.originalType == FuzzOriginalType.PartOf ||
             chunk.originalType == FuzzOriginalType.LanguageVersion) {
           headerSb.writeln(chunk.getSource());
         } else {
@@ -1625,14 +1633,12 @@
         // exports, etc.
         StringBuffer sb = new StringBuffer();
         sb.writeln(headerSb.toString());
-        for (int i = 0; i < totalSubFiles; i++) {
-          if (i == currentSubFile) continue;
-          sb.writeln("import '${getUriForChunk(i)}';");
-          sb.writeln("export '${getUriForChunk(i)}';");
-        }
+        sb.writeln("import '${uri.pathSegments.last}';");
         sb.writeln(chunk.getSource());
         fs.data[getUriForChunk(currentSubFile)] =
             utf8.encode(sb.toString()) as Uint8List;
+        print(" => Split into ${getUriForChunk(currentSubFile)}:\n"
+            "${sb.toString()}\n-------------\n");
         currentSubFile++;
       }
 
@@ -1640,9 +1646,11 @@
       StringBuffer sb = new StringBuffer();
       sb.writeln(headerSb.toString());
       for (int i = 0; i < totalSubFiles; i++) {
-        sb.writeln("import '${getUriForChunk(i)}';");
-        sb.writeln("export '${getUriForChunk(i)}';");
+        sb.writeln("import '${getUriForChunk(i).pathSegments.last}';");
+        sb.writeln("export '${getUriForChunk(i).pathSegments.last}';");
       }
+      sb.writeln(orgFileOnlyHeaderSb.toString());
+      print(" => Main file becomes:\n${sb.toString()}\n-------------\n");
       fs.data[uri] = utf8.encode(sb.toString()) as Uint8List;
     }
 
@@ -1715,6 +1723,13 @@
         enableExtensionMethods: true,
         enableNonNullable: nnbd);
     accept(ast);
+
+    if (metadataStart == null &&
+        ast.token.precedingComments != null &&
+        chunks.isEmpty) {
+      _chunkOutLanguageVersionComment(ast.token);
+    }
+
     if (metadataStart != null) {
       String metadata = asString.substring(
           metadataStart!.charOffset, metadataEndInclusive!.charEnd);
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_51.yaml b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml
new file mode 100644
index 0000000..d953359
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml
@@ -0,0 +1,59 @@
+# Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE.md file.
+
+# Change a part in a non-outline changing way, then a outline-changing way.
+
+type: newworld
+worlds:
+  - entry: main.dart
+    experiments: alternative-invalidation-strategy
+    sources:
+      main.dart: |
+        import 'lib.dart';
+        part 'part.dart';
+        main() {
+          print(baz());
+        }
+      part.dart: |
+        part of 'main.dart';
+        String foo() {
+          return "foo";
+        }
+      lib.dart: |
+        import 'main.dart';
+        String baz() {
+          return foo();
+        }
+    expectedLibraryCount: 2
+
+  - entry: main.dart
+    experiments: alternative-invalidation-strategy
+    worldType: updated
+    expectInitializeFromDill: false
+    invalidate:
+      - part.dart
+    sources:
+      part.dart: |
+        part of 'main.dart';
+        String foo() {
+          return "foo!";
+        }
+    expectedLibraryCount: 2
+    expectsRebuildBodiesOnly: true
+
+  - entry: main.dart
+    experiments: alternative-invalidation-strategy
+    worldType: updated
+    errors: true # lib.dart calls foo, but foo no longer exists.
+    expectInitializeFromDill: false
+    invalidate:
+      - part.dart
+    sources:
+      part.dart: |
+        part of 'main.dart';
+        String bar() {
+          return "bar";
+        }
+    expectedLibraryCount: 2
+    expectsRebuildBodiesOnly: false # part changed its outline.
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.1.expect b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.1.expect
new file mode 100644
index 0000000..047b356
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.1.expect
@@ -0,0 +1,21 @@
+main = main::main;
+library from "org-dartlang-test:///lib.dart" as lib {
+
+  import "org-dartlang-test:///main.dart";
+
+  static method baz() → dart.core::String {
+    return main::foo();
+  }
+}
+library from "org-dartlang-test:///main.dart" as main {
+
+  import "org-dartlang-test:///lib.dart";
+
+  part part.dart;
+  static method main() → dynamic {
+    dart.core::print(lib::baz());
+  }
+  static method /* from org-dartlang-test:///part.dart */ foo() → dart.core::String {
+    return "foo";
+  }
+}
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.2.expect b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.2.expect
new file mode 100644
index 0000000..2a362a4
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.2.expect
@@ -0,0 +1,21 @@
+main = main::main;
+library from "org-dartlang-test:///lib.dart" as lib {
+
+  import "org-dartlang-test:///main.dart";
+
+  static method baz() → dart.core::String {
+    return main::foo();
+  }
+}
+library from "org-dartlang-test:///main.dart" as main {
+
+  import "org-dartlang-test:///lib.dart";
+
+  part part.dart;
+  static method main() → dynamic {
+    dart.core::print(lib::baz());
+  }
+  static method /* from org-dartlang-test:///part.dart */ foo() → dart.core::String {
+    return "foo!";
+  }
+}
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.3.expect b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.3.expect
new file mode 100644
index 0000000..aa9d1f5
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.3.expect
@@ -0,0 +1,28 @@
+main = main::main;
+library from "org-dartlang-test:///lib.dart" as lib {
+//
+// Problems in library:
+//
+// org-dartlang-test:///lib.dart:3:10: Error: Method not found: 'foo'.
+//   return foo();
+//          ^^^
+//
+
+  import "org-dartlang-test:///main.dart";
+
+  static method baz() → dart.core::String {
+    return invalid-expression "org-dartlang-test:///lib.dart:3:10: Error: Method not found: 'foo'.\n  return foo();\n         ^^^";
+  }
+}
+library from "org-dartlang-test:///main.dart" as main {
+
+  import "org-dartlang-test:///lib.dart";
+
+  part part.dart;
+  static method main() → dynamic {
+    dart.core::print(lib::baz());
+  }
+  static method /* from org-dartlang-test:///part.dart */ bar() → dart.core::String {
+    return "bar";
+  }
+}
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_52.yaml b/pkg/front_end/testcases/incremental/no_outline_change_52.yaml
new file mode 100644
index 0000000..2297147
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/no_outline_change_52.yaml
@@ -0,0 +1,48 @@
+# Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE.md file.
+
+# Compile an application, change a file, but don't change the outline.
+# Use it as a mixin, though, so we cannot neccesarily rebuild the bodies only.
+# Have the thing being mixed in be in a part.
+
+type: newworld
+worlds:
+  - entry: main.dart
+    experiments: alternative-invalidation-strategy
+    sources:
+      main.dart: |
+        import 'lib.dart';
+        part 'part.dart';
+      part.dart: |
+        part of 'main.dart';
+        class Foo {
+          method() {
+            print("A");
+          }
+        }
+      lib.dart: |
+        import 'main.dart';
+
+        class Bar extends Object with Foo {
+          method2() {
+            method();
+          }
+        }
+    expectedLibraryCount: 2
+  - entry: main.dart
+    experiments: alternative-invalidation-strategy
+    worldType: updated
+    expectInitializeFromDill: false
+    invalidate:
+      - part.dart
+    sources:
+      part.dart: |
+        part of 'main.dart';
+        class Foo {
+          method() {
+            print("B");
+          }
+        }
+    expectedLibraryCount: 2
+    expectsRebuildBodiesOnly: false
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_52.yaml.world.1.expect b/pkg/front_end/testcases/incremental/no_outline_change_52.yaml.world.1.expect
new file mode 100644
index 0000000..3d7bb8a
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/no_outline_change_52.yaml.world.1.expect
@@ -0,0 +1,36 @@
+main = <No Member>;
+library from "org-dartlang-test:///lib.dart" as lib {
+
+  import "org-dartlang-test:///main.dart";
+
+  abstract class _Bar&Object&Foo extends dart.core::Object implements main::Foo /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
+    const synthetic constructor •() → lib::_Bar&Object&Foo
+      : super dart.core::Object::•()
+      ;
+    method /* from org-dartlang-test:///part.dart */ method() → dynamic {
+      dart.core::print("A");
+    }
+  }
+  class Bar extends lib::_Bar&Object&Foo {
+    synthetic constructor •() → lib::Bar
+      : super lib::_Bar&Object&Foo::•()
+      ;
+    method method2() → dynamic {
+      this.{lib::_Bar&Object&Foo::method}(){() → dynamic};
+    }
+  }
+}
+library from "org-dartlang-test:///main.dart" as main {
+
+  import "org-dartlang-test:///lib.dart";
+
+  part part.dart;
+  class Foo extends dart.core::Object { // from org-dartlang-test:///part.dart
+    synthetic constructor •() → main::Foo
+      : super dart.core::Object::•()
+      ;
+    method method() → dynamic {
+      dart.core::print("A");
+    }
+  }
+}
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_52.yaml.world.2.expect b/pkg/front_end/testcases/incremental/no_outline_change_52.yaml.world.2.expect
new file mode 100644
index 0000000..c9aba5f
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/no_outline_change_52.yaml.world.2.expect
@@ -0,0 +1,36 @@
+main = <No Member>;
+library from "org-dartlang-test:///lib.dart" as lib {
+
+  import "org-dartlang-test:///main.dart";
+
+  abstract class _Bar&Object&Foo extends dart.core::Object implements main::Foo /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
+    const synthetic constructor •() → lib::_Bar&Object&Foo
+      : super dart.core::Object::•()
+      ;
+    method /* from org-dartlang-test:///part.dart */ method() → dynamic {
+      dart.core::print("B");
+    }
+  }
+  class Bar extends lib::_Bar&Object&Foo {
+    synthetic constructor •() → lib::Bar
+      : super lib::_Bar&Object&Foo::•()
+      ;
+    method method2() → dynamic {
+      this.{lib::_Bar&Object&Foo::method}(){() → dynamic};
+    }
+  }
+}
+library from "org-dartlang-test:///main.dart" as main {
+
+  import "org-dartlang-test:///lib.dart";
+
+  part part.dart;
+  class Foo extends dart.core::Object { // from org-dartlang-test:///part.dart
+    synthetic constructor •() → main::Foo
+      : super dart.core::Object::•()
+      ;
+    method method() → dynamic {
+      dart.core::print("B");
+    }
+  }
+}
diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status
index cf5467d..762a17d 100644
--- a/pkg/front_end/testcases/strong.status
+++ b/pkg/front_end/testcases/strong.status
@@ -17,14 +17,13 @@
 generic_metadata/typedef_generic_types_in_arguments_and_bounds: SemiFuzzCrash
 
 # These tests have "privacy issues" and isn't compatiable with splitting files (fuzzing):
-constructor_tearoffs/lowering/typedef_from: semiFuzzFailureOnForceRebuildBodies # parts not supported
 dart2js/mixin_default_values/main: semiFuzzFailureOnForceRebuildBodies # private method
 dartdevc/factory_patch/main: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
 dartdevc/private_covariant: semiFuzzFailureOnForceRebuildBodies # private method in class
 late_lowering/injected_late_field_checks/main: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
 late_lowering/issue41436/issue41436: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
 late_lowering/issue41922: semiFuzzFailureOnForceRebuildBodies # private field
-late_lowering/private_members: semiFuzzFailureOnForceRebuildBodies # parts not supported
+late_lowering/private_members: semiFuzzFailureOnForceRebuildBodies # private class
 macros/inject_constructor: semiFuzzFailureOnForceRebuildBodies # Macro injection --- think it might introduce a class in a file?
 nnbd/injected_late_field_checks/main: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
 nnbd/issue42504: semiFuzzFailureOnForceRebuildBodies # private class
diff --git a/pkg/front_end/testcases/weak.status b/pkg/front_end/testcases/weak.status
index def324c..6097d2c 100644
--- a/pkg/front_end/testcases/weak.status
+++ b/pkg/front_end/testcases/weak.status
@@ -28,7 +28,6 @@
 static_field_lowering/opt_in: SemiFuzzFailure
 
 # These tests have "privacy issues" and isn't compatiable with splitting files (fuzzing):
-constructor_tearoffs/lowering/typedef_from: semiFuzzFailureOnForceRebuildBodies # parts not supported
 dart2js/mixin_default_values/main: semiFuzzFailureOnForceRebuildBodies # private method
 dartdevc/factory_patch/main: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
 dartdevc/private_covariant: semiFuzzFailureOnForceRebuildBodies # private method in class
@@ -47,20 +46,17 @@
 general/issue47036: semiFuzzFailureOnForceRebuildBodies # private class
 general/issue48548: semiFuzzFailureOnForceRebuildBodies # private field
 general/nested_lib_spec/main: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
-general/part_as_entry_point: semiFuzzFailureOnForceRebuildBodies # parts not supported
-general/private_members: semiFuzzFailureOnForceRebuildBodies # parts not supported
+general/private_members: semiFuzzFailureOnForceRebuildBodies # private class (etc).
 general/private_method_tearoff: semiFuzzFailureOnForceRebuildBodies # private method in class
 general/redirecting_factory_const_inference: semiFuzzFailureOnForceRebuildBodies # private class
-general/redirecting_factory_invocation_metadata: semiFuzzFailureOnForceRebuildBodies # parts not supported
 general/store_load: semiFuzzFailureOnForceRebuildBodies # private field in class
-general/top_level_accessors: semiFuzzFailureOnForceRebuildBodies # parts not supported
 general/typedef: semiFuzzFailureOnForceRebuildBodies # private typedef
 inference/downwards_inference_on_list_literals_infer_if_value_types_match_context: semiFuzzFailureOnForceRebuildBodies # private field
 late_lowering/injected_late_field_checks/main: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
 
 late_lowering/issue41436/issue41436: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
 late_lowering/issue41922: semiFuzzFailureOnForceRebuildBodies # private field
-late_lowering/private_members: semiFuzzFailureOnForceRebuildBodies # parts not supported
+late_lowering/private_members: semiFuzzFailureOnForceRebuildBodies # private class (etc)
 macros/inject_constructor: semiFuzzFailureOnForceRebuildBodies # Macro injection --- think it might introduce a class in a file?
 nnbd_mixed/issue46518: semiFuzzFailureOnForceRebuildBodies # private method
 nnbd_mixed/no_null_shorting_explicit_extension: semiFuzzFailureOnForceRebuildBodies # private field in class
diff --git a/pkg/modular_test/lib/src/create_package_config.dart b/pkg/modular_test/lib/src/create_package_config.dart
index c25ef69..db463ad 100644
--- a/pkg/modular_test/lib/src/create_package_config.dart
+++ b/pkg/modular_test/lib/src/create_package_config.dart
@@ -60,7 +60,7 @@
 }
 
 String _packageConfigEntry(String name, Uri root,
-    {Uri packageRoot, LanguageVersion version}) {
+    {Uri? packageRoot, LanguageVersion? version}) {
   var fields = [
     '"name": "${name}"',
     '"rootUri": "$root"',
diff --git a/pkg/modular_test/lib/src/find_sdk_root.dart b/pkg/modular_test/lib/src/find_sdk_root.dart
index 2b36550..8988b83 100644
--- a/pkg/modular_test/lib/src/find_sdk_root.dart
+++ b/pkg/modular_test/lib/src/find_sdk_root.dart
@@ -15,9 +15,8 @@
     var segments = current.pathSegments;
     var index = segments.lastIndexOf('sdk');
     if (index == -1) {
-      print("error: cannot find the root of the Dart SDK");
       exitCode = 1;
-      return null;
+      throw "error: cannot find the root of the Dart SDK";
     }
     current = current.resolve("../" * (segments.length - index - 1));
     if (await File.fromUri(current.resolve("sdk/DEPS")).exists()) {
diff --git a/pkg/modular_test/lib/src/generic_runner.dart b/pkg/modular_test/lib/src/generic_runner.dart
index e7c3030..fe2a85f 100644
--- a/pkg/modular_test/lib/src/generic_runner.dart
+++ b/pkg/modular_test/lib/src/generic_runner.dart
@@ -24,29 +24,29 @@
 
 class RunnerOptions {
   /// Name of the test suite being run.
-  String suiteName;
+  final String suiteName;
 
   /// Configuration name to use when writing result logs.
-  String configurationName;
+  final String? configurationName;
 
   /// Filter used to only run tests that match the filter name.
-  String filter;
+  final String? filter;
 
   /// Where log files are emitted.
   ///
   /// Note that all shards currently emit the same filenames, so two shards
   /// shouldn't be given the same [logDir] otherwise they will overwrite each
   /// other's log files.
-  Uri logDir;
+  final Uri? logDir;
 
   /// Of [shards], which shard is currently being executed.
-  int shard;
+  final int shard;
 
   /// How many shards will be used to run a suite.
-  int shards;
+  final int shards;
 
   /// Whether to print verbose information.
-  bool verbose;
+  final bool verbose;
 
   /// Template used to help developers reproduce the issue.
   ///
@@ -54,23 +54,34 @@
   ///   * %executable is replaced with `Platform.executable`
   ///   * %script is replaced with the current `Platform.script`
   ///   * %name is replaced with the test name.
-  String reproTemplate;
+  final String reproTemplate;
+
+  RunnerOptions(
+      {required this.suiteName,
+      this.configurationName,
+      this.filter,
+      this.logDir,
+      required this.shard,
+      required this.shards,
+      required this.verbose,
+      required this.reproTemplate});
 }
 
 class _TestOutcome {
   /// Unique test name.
-  String name;
+  final String name;
 
-  /// Whether, after running the test, the test matches its expectations. Null
-  /// before the test is executed.
-  bool matchedExpectations;
+  /// Whether, after running the test, the test matches its expectations.
+  late bool matchedExpectations;
 
   /// Additional output emitted by the test, only used when expectations don't
   /// match and more details need to be provided.
-  String output;
+  String? output;
 
   /// Time used to run the test.
-  Duration elapsedTime;
+  late Duration elapsedTime;
+
+  _TestOutcome(this.name);
 }
 
 Future<void> runSuite<T>(List<Test> tests, RunnerOptions options) async {
@@ -91,13 +102,13 @@
     var test = sortedTests[i];
     var name = test.name;
     if (options.verbose) stdout.write('$name: ');
-    if (options.filter != null && !name.contains(options.filter)) {
+    if (options.filter != null && !name.contains(options.filter!)) {
       if (options.verbose) stdout.write('skipped\n');
       continue;
     }
 
     var watch = new Stopwatch()..start();
-    var outcome = new _TestOutcome()..name = test.name;
+    var outcome = new _TestOutcome(test.name);
     try {
       await test.run();
       if (options.verbose) stdout.write('pass\n');
@@ -153,7 +164,7 @@
   }
 
   // Ensure the directory URI ends with a path separator.
-  var logDir = Directory.fromUri(options.logDir).uri;
+  var logDir = Directory.fromUri(options.logDir!).uri;
   var resultJsonUri = logDir.resolve('results.json');
   var logsJsonUri = logDir.resolve('logs.json');
   File.fromUri(resultJsonUri)
diff --git a/pkg/modular_test/lib/src/io_pipeline.dart b/pkg/modular_test/lib/src/io_pipeline.dart
index 21c57b6..5a1066e 100644
--- a/pkg/modular_test/lib/src/io_pipeline.dart
+++ b/pkg/modular_test/lib/src/io_pipeline.dart
@@ -41,17 +41,17 @@
   /// libraries), and not modules that are test specific. File names will be
   /// specific enough so that we can keep separate the artifacts created from
   /// running tools under different configurations (with different flags).
-  Uri _resultsFolderUri;
-  Uri get resultFolderUriForTesting => _resultsFolderUri;
+  Uri? _resultsFolderUri;
+  Uri? get resultFolderUriForTesting => _resultsFolderUri;
 
   /// A unique number to denote the current modular test configuration.
   ///
   /// When using [cacheSharedModules], a test can resuse the output of a
   /// previous run of this pipeline if that output was generated with the same
   /// configuration.
-  int _currentConfiguration;
+  int? _currentConfiguration;
 
-  final ConfigurationRegistry _registry;
+  final ConfigurationRegistry? _registry;
 
   /// Whether to keep alive the temporary folder used to store intermediate
   /// results in order to inspect it later in test.
@@ -65,13 +65,13 @@
 
   @override
   Future<void> run(ModularTest test) async {
-    var resultsDir = null;
+    Directory? resultsDir;
     if (_resultsFolderUri == null) {
       resultsDir = await Directory.systemTemp.createTemp('modular_test_res-');
       _resultsFolderUri = resultsDir.uri;
     }
     if (cacheSharedModules) {
-      _currentConfiguration = _registry.computeConfigurationId(test);
+      _currentConfiguration = _registry!.computeConfigurationId(test);
     }
     await super.run(test);
     if (resultsDir != null &&
@@ -90,7 +90,7 @@
   Future<void> cleanup() async {
     if (_resultsFolderUri == null) return;
     if (saveIntermediateResultsForTesting || cacheSharedModules) {
-      await Directory.fromUri(_resultsFolderUri).delete(recursive: true);
+      await Directory.fromUri(_resultsFolderUri!).delete(recursive: true);
       _resultsFolderUri = null;
     }
   }
@@ -98,11 +98,12 @@
   @override
   Future<void> runStep(IOModularStep step, Module module,
       Map<Module, Set<DataId>> visibleData, List<String> flags) async {
+    final resultsFolderUri = _resultsFolderUri!;
     if (cacheSharedModules && module.isShared) {
       // If all expected outputs are already available, skip the step.
       bool allCachedResultsFound = true;
       for (var dataId in step.resultData) {
-        var cachedFile = File.fromUri(_resultsFolderUri
+        var cachedFile = File.fromUri(resultsFolderUri
             .resolve(_toFileName(module, dataId, configSpecific: true)));
         if (!await cachedFile.exists()) {
           allCachedResultsFound = false;
@@ -121,8 +122,8 @@
     var stepFolder =
         await Directory.systemTemp.createTemp('modular_test_${stepId}-');
     for (var module in visibleData.keys) {
-      for (var dataId in visibleData[module]) {
-        var assetUri = _resultsFolderUri
+      for (var dataId in visibleData[module]!) {
+        var assetUri = resultsFolderUri
             .resolve(_toFileName(module, dataId, configSpecific: true));
         await File.fromUri(assetUri).copy(
             stepFolder.uri.resolve(_toFileName(module, dataId)).toFilePath());
@@ -147,7 +148,7 @@
         throw StateError(
             "Step '${step.runtimeType}' didn't produce an output file");
       }
-      await outputFile.copy(_resultsFolderUri
+      await outputFile.copy(resultsFolderUri
           .resolve(_toFileName(module, dataId, configSpecific: true))
           .toFilePath());
     }
diff --git a/pkg/modular_test/lib/src/loader.dart b/pkg/modular_test/lib/src/loader.dart
index b67f011..f846a41 100644
--- a/pkg/modular_test/lib/src/loader.dart
+++ b/pkg/modular_test/lib/src/loader.dart
@@ -32,8 +32,8 @@
   Set<String> defaultPackages = defaultTestSpecification.packages.keys.toSet();
   Module sdkModule = await _createSdkModule(root);
   Map<String, Module> modules = {'sdk': sdkModule};
-  String specString;
-  Module mainModule;
+  String? specString;
+  Module? mainModule;
   var entries = folder.listSync(recursive: false).toList()
     // Sort to avoid dependency on file system order.
     ..sort(_compareFileSystemEntity);
@@ -54,7 +54,7 @@
               "that is provided by default.");
         }
         if (modules.containsKey(moduleName)) {
-          return _moduleConflict(fileName, modules[moduleName], testUri);
+          return _moduleConflict(fileName, modules[moduleName]!, testUri);
         }
         var relativeUri = Uri.parse(fileName);
         var isMain = moduleName == 'main';
@@ -82,7 +82,7 @@
             "that is provided by default.");
       }
       if (modules.containsKey(moduleName)) {
-        return _moduleConflict(moduleName, modules[moduleName], testUri);
+        return _moduleConflict(moduleName, modules[moduleName]!, testUri);
       }
       var sources = await _listModuleSources(entryUri);
       modules[moduleName] = Module(moduleName, [], testUri, sources,
@@ -133,7 +133,7 @@
 void _attachDependencies(
     Map<String, List<String>> dependencies, Map<String, Module> modules) {
   dependencies.forEach((name, moduleDependencies) {
-    var module = modules[name];
+    final module = modules[name];
     if (module == null) {
       _invalidTest(
           "declared dependencies for a non existing module named '$name'");
@@ -142,7 +142,7 @@
       _invalidTest("Module dependencies have already been declared on $name.");
     }
     moduleDependencies.forEach((dependencyName) {
-      var moduleDependency = modules[dependencyName];
+      final moduleDependency = modules[dependencyName];
       if (moduleDependency == null) {
         _invalidTest("'$name' declares a dependency on a non existing module "
             "named '$dependencyName'");
@@ -169,7 +169,7 @@
     if (module != null) {
       module.isPackage = true;
     } else {
-      var packageLibUri = configRoot.resolve(packages[packageName]);
+      var packageLibUri = configRoot.resolve(packages[packageName]!);
       var packageRootUri = Directory.fromUri(packageLibUri).parent.uri;
       var sources = await _listModuleSources(packageLibUri);
       // TODO(sigmund): validate that we don't use a different alias for a
@@ -254,7 +254,7 @@
   var entryType = isFile ? 'file' : 'folder';
 
   var existingIsFile =
-      existing.packageBase.path == './' && existing.sources.length == 1;
+      existing.packageBase!.path == './' && existing.sources.length == 1;
   var existingEntryType = existingIsFile ? 'file' : 'folder';
 
   var existingName = existingIsFile
@@ -266,7 +266,7 @@
       "'$existingName'.");
 }
 
-_invalidTest(String message) {
+Never _invalidTest(String message) {
   throw new InvalidTestError(message);
 }
 
diff --git a/pkg/modular_test/lib/src/memory_pipeline.dart b/pkg/modular_test/lib/src/memory_pipeline.dart
index d97586c..13025aa 100644
--- a/pkg/modular_test/lib/src/memory_pipeline.dart
+++ b/pkg/modular_test/lib/src/memory_pipeline.dart
@@ -9,8 +9,8 @@
 import 'suite.dart';
 
 /// A hook to fetch data previously computed for a dependency.
-typedef ModuleDataProvider = Object Function(Module, DataId);
-typedef SourceProvider = String Function(Uri);
+typedef ModuleDataProvider = Object? Function(Module, DataId);
+typedef SourceProvider = String? Function(Uri);
 
 abstract class MemoryModularStep extends ModularStep {
   Future<Map<DataId, Object>> execute(
@@ -25,12 +25,12 @@
 
   /// Internal state to hold the current results as they are computed by the
   /// pipeline. Expected to be null before and after the pipeline runs.
-  Map<Module, Map<DataId, Object>> _results;
+  Map<Module, Map<DataId, Object>>? _results;
 
   /// A copy of [_result] at the time the pipeline last finished running.
-  Map<Module, Map<DataId, Object>> resultsForTesting;
+  Map<Module, Map<DataId, Object>>? resultsForTesting;
 
-  final ConfigurationRegistry _registry;
+  final ConfigurationRegistry? _registry;
 
   /// Cache of results when [cacheSharedModules] is true
   final List<Map<Module, Map<DataId, Object>>> _resultCache;
@@ -38,29 +38,29 @@
   MemoryPipeline(this._sources, List<MemoryModularStep> steps,
       {bool cacheSharedModules: false})
       : _registry = cacheSharedModules ? new ConfigurationRegistry() : null,
-        _resultCache = cacheSharedModules ? [] : null,
+        _resultCache = cacheSharedModules ? [] : const [],
         super(steps, cacheSharedModules);
 
   @override
   Future<void> run(ModularTest test) async {
-    _results = {};
-    Map<Module, Map<DataId, Object>> cache = null;
+    var results = _results = {};
+    Map<Module, Map<DataId, Object>>? cache = null;
     if (cacheSharedModules) {
-      int id = _registry.computeConfigurationId(test);
+      int id = _registry!.computeConfigurationId(test);
       if (id < _resultCache.length) {
         cache = _resultCache[id];
       } else {
         assert(id == _resultCache.length);
         _resultCache.add(cache = {});
       }
-      _results.addAll(cache);
+      results.addAll(cache);
     }
     await super.run(test);
-    resultsForTesting = _results;
+    resultsForTesting = results;
     if (cacheSharedModules) {
-      for (var module in _results.keys) {
+      for (var module in results.keys) {
         if (module.isShared) {
-          cache[module] = _results[module];
+          cache![module] = results[module]!;
         }
       }
     }
@@ -70,10 +70,11 @@
   @override
   Future<void> runStep(MemoryModularStep step, Module module,
       Map<Module, Set<DataId>> visibleData, List<String> flags) async {
+    final results = _results!;
     if (cacheSharedModules && module.isShared) {
       bool allCachedResultsFound = true;
       for (var dataId in step.resultData) {
-        if (_results[module] == null || _results[module][dataId] == null) {
+        if (results[module] == null || results[module]![dataId] == null) {
           allCachedResultsFound = false;
           break;
         }
@@ -88,23 +89,23 @@
     visibleData.forEach((module, dataIdSet) {
       inputData[module] = {};
       for (var dataId in dataIdSet) {
-        inputData[module][dataId] = _results[module][dataId];
+        inputData[module]![dataId] = results[module]![dataId]!;
       }
     });
     Map<Uri, String> inputSources = {};
     if (step.needsSources) {
       module.sources.forEach((relativeUri) {
         var uri = module.rootUri.resolveUri(relativeUri);
-        inputSources[uri] = _sources[uri];
+        inputSources[uri] = _sources[uri]!;
       });
     }
     Map<DataId, Object> result = await step.execute(
         module,
         (Uri uri) => inputSources[uri],
-        (Module m, DataId id) => inputData[m][id],
+        (Module m, DataId id) => inputData[m]![id],
         flags);
     for (var dataId in step.resultData) {
-      (_results[module] ??= {})[dataId] = result[dataId];
+      (results[module] ??= {})[dataId] = result[dataId]!;
     }
   }
 }
diff --git a/pkg/modular_test/lib/src/pipeline.dart b/pkg/modular_test/lib/src/pipeline.dart
index 7e10bac..16377b8 100644
--- a/pkg/modular_test/lib/src/pipeline.dart
+++ b/pkg/modular_test/lib/src/pipeline.dart
@@ -52,7 +52,7 @@
       {this.needsSources: true,
       this.dependencyDataNeeded: const [],
       this.moduleDataNeeded: const [],
-      this.resultData,
+      this.resultData: const [],
       this.onlyOnMain: false,
       this.onlyOnSdk: false,
       this.notOnSdk: false});
@@ -94,13 +94,13 @@
     // or by the same step on a dependency.
     Map<DataId, S> previousKinds = {};
     for (var step in steps) {
-      if (step.resultData == null || step.resultData.isEmpty) {
+      if (step.resultData.isEmpty) {
         _validationError(
             "'${step.runtimeType}' needs to declare what data it produces.");
       }
       for (var resultKind in step.resultData) {
         if (previousKinds.containsKey(resultKind) &&
-            !areMutuallyExclusive(step, previousKinds[resultKind])) {
+            !areMutuallyExclusive(step, previousKinds[resultKind]!)) {
           _validationError("Cannot produce the same data on two modular steps."
               " '$resultKind' was previously produced by "
               "'${previousKinds[resultKind].runtimeType}' but "
@@ -151,7 +151,7 @@
       await _recursiveRun(
           step, dependency, computedData, transitiveDependencies, flags);
       deps.add(dependency);
-      deps.addAll(transitiveDependencies[dependency]);
+      deps.addAll(transitiveDependencies[dependency]!);
     }
 
     if ((step.onlyOnMain && !module.isMain) ||
@@ -163,15 +163,15 @@
     deps.forEach((dep) {
       visibleData[dep] = {};
       for (var dataId in step.dependencyDataNeeded) {
-        if (computedData[dep].contains(dataId)) {
-          visibleData[dep].add(dataId);
+        if (computedData[dep]!.contains(dataId)) {
+          visibleData[dep]!.add(dataId);
         }
       }
     });
     visibleData[module] = {};
     for (var dataId in step.moduleDataNeeded) {
-      if (computedData[module].contains(dataId)) {
-        visibleData[module].add(dataId);
+      if (computedData[module]!.contains(dataId)) {
+        visibleData[module]!.add(dataId);
       }
     }
     await runStep(step, module, visibleData, flags);
diff --git a/pkg/modular_test/lib/src/runner.dart b/pkg/modular_test/lib/src/runner.dart
index 13fb65a..916f002 100644
--- a/pkg/modular_test/lib/src/runner.dart
+++ b/pkg/modular_test/lib/src/runner.dart
@@ -26,15 +26,15 @@
 
   await generic.runSuite(
       entries,
-      new generic.RunnerOptions()
-        ..suiteName = suiteName
-        ..configurationName = options.configurationName
-        ..filter = options.filter
-        ..logDir = options.outputDirectory
-        ..shard = options.shard
-        ..shards = options.shards
-        ..verbose = options.verbose
-        ..reproTemplate = '%executable %script --verbose --filter %name');
+      new generic.RunnerOptions(
+          suiteName: suiteName,
+          configurationName: options.configurationName,
+          filter: options.filter,
+          logDir: options.outputDirectory,
+          shard: options.shard,
+          shards: options.shards,
+          verbose: options.verbose,
+          reproTemplate: '%executable %script --verbose --filter %name'));
   await pipeline.cleanup();
 }
 
@@ -59,11 +59,11 @@
 class Options {
   bool showSkipped = false;
   bool verbose = false;
-  String filter = null;
+  String? filter;
   int shards = 1;
   int shard = 1;
-  String configurationName;
-  Uri outputDirectory;
+  String? configurationName;
+  Uri? outputDirectory;
   bool useSdk = false;
 
   static Options parse(List<String> args) {
@@ -92,7 +92,7 @@
           help: 'configuration name to use for emitting jsonl result files.');
     ArgResults argResults = parser.parse(args);
     int shards = int.tryParse(argResults['shards']) ?? 1;
-    int shard;
+    int shard = 1;
     if (shards > 1) {
       shard = int.tryParse(argResults['shard']) ?? 1;
       if (shard <= 0 || shard >= shards) {
@@ -101,7 +101,7 @@
         exit(1);
       }
     }
-    Uri toUri(s) => s == null ? null : Uri.base.resolveUri(Uri.file(s));
+    Uri? toUri(s) => s == null ? null : Uri.base.resolveUri(Uri.file(s));
     return Options()
       ..showSkipped = argResults['show-skipped']
       ..verbose = argResults['verbose']
diff --git a/pkg/modular_test/lib/src/suite.dart b/pkg/modular_test/lib/src/suite.dart
index 281c381..2821b97 100644
--- a/pkg/modular_test/lib/src/suite.dart
+++ b/pkg/modular_test/lib/src/suite.dart
@@ -17,15 +17,7 @@
   final List<String> flags;
 
   ModularTest(this.modules, this.mainModule, this.flags) {
-    if (mainModule == null) {
-      throw ArgumentError("main module was null");
-    }
-    if (flags == null) {
-      throw ArgumentError("flags was null");
-    }
-    if (modules == null || modules.length == 0) {
-      throw ArgumentError("modules cannot be null or empty");
-    }
+    if (modules.isEmpty) throw ArgumentError("modules cannot be empty");
     for (var module in modules) {
       module._validate();
     }
@@ -52,7 +44,7 @@
 
   /// The file containing the main entry method, if any. Stored as a relative
   /// [Uri] from [rootUri].
-  final Uri mainSource;
+  final Uri? mainSource;
 
   /// Whether this module is also available as a package import, where the
   /// package name matches the module name.
@@ -63,7 +55,7 @@
 
   /// When [isPackage], the base where all package URIs are resolved against.
   /// Stored as a relative [Uri] from [rootUri].
-  final Uri packageBase;
+  final Uri? packageBase;
 
   /// Whether this is the main entry module of a test.
   bool isMain;
diff --git a/pkg/modular_test/lib/src/test_specification_parser.dart b/pkg/modular_test/lib/src/test_specification_parser.dart
index 85272b0..2253df8 100644
--- a/pkg/modular_test/lib/src/test_specification_parser.dart
+++ b/pkg/modular_test/lib/src/test_specification_parser.dart
@@ -72,15 +72,15 @@
     if (key is! String) {
       _invalidSpecification("key: '$key' is not a string");
     }
-    normalizedMap[key] = [];
+    final values = normalizedMap[key] = [];
     if (value is String) {
-      normalizedMap[key].add(value);
+      values.add(value);
     } else if (value is List) {
       value.forEach((entry) {
         if (entry is! String) {
           _invalidSpecification("entry: '$entry' is not a string");
         }
-        normalizedMap[key].add(entry);
+        values.add(entry);
       });
     } else {
       _invalidSpecification(
diff --git a/pkg/modular_test/pubspec.yaml b/pkg/modular_test/pubspec.yaml
index c1507d6..d6fcca8 100644
--- a/pkg/modular_test/pubspec.yaml
+++ b/pkg/modular_test/pubspec.yaml
@@ -6,7 +6,7 @@
  This is used within the Dart SDK to define and validate modular tests, and to
  execute them using the modular pipeline of different SDK tools.
 environment:
-  sdk: ">=2.10.0 <3.0.0"
+  sdk: '>=2.16.0 <3.0.0'
 
 dependencies:
   args: any
diff --git a/pkg/modular_test/test/io_pipeline_test.dart b/pkg/modular_test/test/io_pipeline_test.dart
index 8176201..46cbaf9 100644
--- a/pkg/modular_test/test/io_pipeline_test.dart
+++ b/pkg/modular_test/test/io_pipeline_test.dart
@@ -35,7 +35,7 @@
     for (var uri in sources.keys) {
       var file = new File.fromUri(uri);
       await file.create(recursive: true);
-      await file.writeAsStringSync(sources[uri]);
+      file.writeAsStringSync(sources[uri]!);
     }
     return new IOPipeline(steps,
         saveIntermediateResultsForTesting: true,
@@ -44,49 +44,49 @@
 
   @override
   IOModularStep createSourceOnlyStep(
-          {String Function(Map<Uri, String>) action,
-          DataId resultId,
+          {required String Function(Map<Uri, String?>) action,
+          required DataId resultId,
           bool requestSources: true}) =>
       SourceOnlyStep(action, resultId, requestSources);
 
   @override
   IOModularStep createModuleDataStep(
-          {String Function(String) action,
-          DataId inputId,
-          DataId resultId,
+          {required String Function(String) action,
+          required DataId inputId,
+          required DataId resultId,
           bool requestModuleData: true}) =>
       ModuleDataStep(action, inputId, resultId, requestModuleData);
 
   @override
   IOModularStep createLinkStep(
-          {String Function(String, List<String>) action,
-          DataId inputId,
-          DataId depId,
-          DataId resultId,
+          {required String Function(String, List<String?>) action,
+          required DataId inputId,
+          required DataId depId,
+          required DataId resultId,
           bool requestDependenciesData: true}) =>
       LinkStep(action, inputId, depId, resultId, requestDependenciesData);
 
   @override
   IOModularStep createMainOnlyStep(
-          {String Function(String, List<String>) action,
-          DataId inputId,
-          DataId depId,
-          DataId resultId,
+          {required String Function(String, List<String?>) action,
+          required DataId inputId,
+          required DataId depId,
+          required DataId resultId,
           bool requestDependenciesData: true}) =>
       MainOnlyStep(action, inputId, depId, resultId, requestDependenciesData);
 
   @override
   IOModularStep createTwoOutputStep(
-          {String Function(String) action1,
-          String Function(String) action2,
-          DataId inputId,
-          DataId result1Id,
-          DataId result2Id}) =>
+          {required String Function(String) action1,
+          required String Function(String) action2,
+          required DataId inputId,
+          required DataId result1Id,
+          required DataId result2Id}) =>
       TwoOutputStep(action1, action2, inputId, result1Id, result2Id);
 
   @override
-  String getResult(covariant IOPipeline pipeline, Module m, DataId dataId) {
-    var folderUri = pipeline.resultFolderUriForTesting;
+  String? getResult(covariant IOPipeline pipeline, Module m, DataId dataId) {
+    var folderUri = pipeline.resultFolderUriForTesting!;
     var file = File.fromUri(folderUri
         .resolve(pipeline.configSpecificResultFileNameForTesting(m, dataId)));
     return file.existsSync() ? file.readAsStringSync() : null;
@@ -100,7 +100,7 @@
 }
 
 class SourceOnlyStep implements IOModularStep {
-  final String Function(Map<Uri, String>) action;
+  final String Function(Map<Uri, String?>) action;
   final DataId resultId;
   final bool needsSources;
   List<DataId> get dependencyDataNeeded => const [];
@@ -115,12 +115,11 @@
   @override
   Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
       List<String> flags) async {
-    Map<Uri, String> sources = {};
+    Map<Uri, String?> sources = {};
 
     for (var uri in module.sources) {
       var file = File.fromUri(root.resolveUri(uri));
-      String data = await file.exists() ? await file.readAsString() : null;
-      sources[uri] = data;
+      sources[uri] = await file.exists() ? await file.readAsString() : null;
     }
     await File.fromUri(root.resolveUri(toUri(module, resultId)))
         .writeAsString(action(sources));
@@ -199,7 +198,7 @@
   final List<DataId> dependencyDataNeeded;
   List<DataId> get moduleDataNeeded => [inputId];
   List<DataId> get resultData => [resultId];
-  final String Function(String, List<String>) action;
+  final String Function(String, List<String?>) action;
   final DataId inputId;
   final DataId depId;
   final DataId resultId;
@@ -214,14 +213,14 @@
   @override
   Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
       List<String> flags) async {
-    List<String> depsData = [];
+    List<String?> depsData = [];
     for (var dependency in module.dependencies) {
       var depData = await _readHelper(dependency, root, depId, toUri);
       depsData.add(depData);
     }
     var inputData = await _readHelper(module, root, inputId, toUri);
     await File.fromUri(root.resolveUri(toUri(module, resultId)))
-        .writeAsString(action(inputData, depsData));
+        .writeAsString(action(inputData!, depsData));
   }
 
   @override
@@ -233,7 +232,7 @@
   final List<DataId> dependencyDataNeeded;
   List<DataId> get moduleDataNeeded => [inputId];
   List<DataId> get resultData => [resultId];
-  final String Function(String, List<String>) action;
+  final String Function(String, List<String?>) action;
   final DataId inputId;
   final DataId depId;
   final DataId resultId;
@@ -248,21 +247,21 @@
   @override
   Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
       List<String> flags) async {
-    List<String> depsData = [];
+    List<String?> depsData = [];
     for (var dependency in computeTransitiveDependencies(module)) {
       var depData = await _readHelper(dependency, root, depId, toUri);
       depsData.add(depData);
     }
     var inputData = await _readHelper(module, root, inputId, toUri);
     await File.fromUri(root.resolveUri(toUri(module, resultId)))
-        .writeAsString(action(inputData, depsData));
+        .writeAsString(action(inputData!, depsData));
   }
 
   @override
   void notifyCached(Module module) {}
 }
 
-Future<String> _readHelper(Module module, Uri root, DataId dataId,
+Future<String?> _readHelper(Module module, Uri root, DataId dataId,
     ModuleDataToRelativeUri toUri) async {
   var file = File.fromUri(root.resolveUri(toUri(module, dataId)));
   if (await file.exists()) {
diff --git a/pkg/modular_test/test/loader/loader_test.dart b/pkg/modular_test/test/loader/loader_test.dart
index 39b3081..8fadfef 100644
--- a/pkg/modular_test/test/loader/loader_test.dart
+++ b/pkg/modular_test/test/loader/loader_test.dart
@@ -19,7 +19,7 @@
     if (entry is Directory) {
       var dirName = entry.uri.path.substring(baseDir.path.length);
       test(dirName, () => _runTest(entry.uri, dirName, options),
-          skip: options.filter != null && !dirName.contains(options.filter));
+          skip: options.filter != null && !dirName.contains(options.filter!));
     }
   }
 }
@@ -46,7 +46,7 @@
           "   ${Platform.executable} ${Platform.script} "
           "--update --show-update --filter $dirName");
     }
-    expect(expectation, result);
+    expect(result, expectation);
   } else if (await file.exists() && (await file.readAsString() == result)) {
     print("  expectation matches result and was up to date.");
   } else {
@@ -100,7 +100,7 @@
 class _Options {
   bool updateExpectations = false;
   bool showResultOnUpdate = false;
-  String filter = null;
+  String? filter;
 
   static _Options parse(List<String> args) {
     var parser = new ArgParser()
diff --git a/pkg/modular_test/test/memory_pipeline_test.dart b/pkg/modular_test/test/memory_pipeline_test.dart
index 95ba01d..894ee21 100644
--- a/pkg/modular_test/test/memory_pipeline_test.dart
+++ b/pkg/modular_test/test/memory_pipeline_test.dart
@@ -30,56 +30,57 @@
 
   @override
   MemoryModularStep createSourceOnlyStep(
-          {String Function(Map<Uri, String>) action,
-          DataId resultId,
+          {required String Function(Map<Uri, String?>) action,
+          required DataId resultId,
           bool requestSources: true}) =>
       SourceOnlyStep(action, resultId, requestSources);
 
   @override
   MemoryModularStep createModuleDataStep(
-          {String Function(String) action,
-          DataId inputId,
-          DataId resultId,
+          {required String Function(String) action,
+          required DataId inputId,
+          required DataId resultId,
           bool requestModuleData: true}) =>
       ModuleDataStep(action, inputId, resultId, requestModuleData);
 
   @override
   MemoryModularStep createLinkStep(
-          {String Function(String, List<String>) action,
-          DataId inputId,
-          DataId depId,
-          DataId resultId,
+          {required String Function(String, List<String?>) action,
+          required DataId inputId,
+          required DataId depId,
+          required DataId resultId,
           bool requestDependenciesData: true}) =>
       LinkStep(action, inputId, depId, resultId, requestDependenciesData);
 
   @override
   MemoryModularStep createMainOnlyStep(
-          {String Function(String, List<String>) action,
-          DataId inputId,
-          DataId depId,
-          DataId resultId,
+          {required String Function(String, List<String?>) action,
+          required DataId inputId,
+          required DataId depId,
+          required DataId resultId,
           bool requestDependenciesData: true}) =>
       MainOnlyStep(action, inputId, depId, resultId, requestDependenciesData);
 
   @override
   MemoryModularStep createTwoOutputStep(
-          {String Function(String) action1,
-          String Function(String) action2,
-          DataId inputId,
-          DataId result1Id,
-          DataId result2Id}) =>
+          {required String Function(String) action1,
+          required String Function(String) action2,
+          required DataId inputId,
+          required DataId result1Id,
+          required DataId result2Id}) =>
       TwoOutputStep(action1, action2, inputId, result1Id, result2Id);
 
   @override
-  String getResult(covariant MemoryPipeline pipeline, Module m, DataId dataId) {
-    return pipeline.resultsForTesting[m][dataId];
+  String? getResult(
+      covariant MemoryPipeline pipeline, Module m, DataId dataId) {
+    return pipeline.resultsForTesting![m]![dataId] as String?;
   }
 
   FutureOr<void> cleanup(Pipeline<MemoryModularStep> pipeline) => null;
 }
 
 class SourceOnlyStep implements MemoryModularStep {
-  final String Function(Map<Uri, String>) action;
+  final String Function(Map<Uri, String?>) action;
   final DataId resultId;
   final bool needsSources;
   List<DataId> get dependencyDataNeeded => const [];
@@ -96,7 +97,7 @@
       SourceProvider sourceProvider,
       ModuleDataProvider dataProvider,
       List<String> flags) {
-    Map<Uri, String> sources = {};
+    Map<Uri, String?> sources = {};
     for (var uri in module.sources) {
       sources[uri] = sourceProvider(module.rootUri.resolveUri(uri));
     }
@@ -127,9 +128,10 @@
       SourceProvider sourceProvider,
       ModuleDataProvider dataProvider,
       List<String> flags) {
-    var inputData = dataProvider(module, inputId) as String;
-    if (inputData == null)
+    var inputData = dataProvider(module, inputId) as String?;
+    if (inputData == null) {
       return Future.value({resultId: "data for $module was null"});
+    }
     return Future.value({resultId: action(inputData)});
   }
 
@@ -159,12 +161,13 @@
       SourceProvider sourceProvider,
       ModuleDataProvider dataProvider,
       List<String> flags) {
-    var inputData = dataProvider(module, inputId) as String;
-    if (inputData == null)
+    var inputData = dataProvider(module, inputId) as String?;
+    if (inputData == null) {
       return Future.value({
-        result1Id: "data for $module was null",
-        result2Id: "data for $module was null",
+        result1Id: "result for $module was null",
+        result2Id: "result for $module was null",
       });
+    }
     return Future.value(
         {result1Id: action1(inputData), result2Id: action2(inputData)});
   }
@@ -177,7 +180,7 @@
   bool get needsSources => false;
   final List<DataId> dependencyDataNeeded;
   List<DataId> get moduleDataNeeded => [inputId];
-  final String Function(String, List<String>) action;
+  final String Function(String, List<String?>) action;
   final DataId inputId;
   final DataId depId;
   final DataId resultId;
@@ -196,9 +199,9 @@
       ModuleDataProvider dataProvider,
       List<String> flags) {
     List<String> depsData = module.dependencies
-        .map((d) => dataProvider(d, depId) as String)
+        .map((d) => dataProvider(d, depId).toString())
         .toList();
-    var inputData = dataProvider(module, inputId) as String;
+    var inputData = dataProvider(module, inputId).toString();
     return Future.value({resultId: action(inputData, depsData)});
   }
 
@@ -210,7 +213,7 @@
   bool get needsSources => false;
   final List<DataId> dependencyDataNeeded;
   List<DataId> get moduleDataNeeded => [inputId];
-  final String Function(String, List<String>) action;
+  final String Function(String, List<String?>) action;
   final DataId inputId;
   final DataId depId;
   final DataId resultId;
@@ -228,11 +231,11 @@
       SourceProvider sourceProvider,
       ModuleDataProvider dataProvider,
       List<String> flags) {
-    List<String> depsData = computeTransitiveDependencies(module)
-        .map((d) => dataProvider(d, depId) as String)
+    List<String?> depsData = computeTransitiveDependencies(module)
+        .map((d) => dataProvider(d, depId) as String?)
         .toList();
-    var inputData = dataProvider(module, inputId) as String;
-    return Future.value({resultId: action(inputData, depsData)});
+    var inputData = dataProvider(module, inputId) as String?;
+    return Future.value({resultId: action(inputData!, depsData)});
   }
 
   @override
diff --git a/pkg/modular_test/test/pipeline_common.dart b/pkg/modular_test/test/pipeline_common.dart
index 254db8c..688d1e4 100644
--- a/pkg/modular_test/test/pipeline_common.dart
+++ b/pkg/modular_test/test/pipeline_common.dart
@@ -34,16 +34,16 @@
   /// Create a step that applies [action] on all input files of the module, and
   /// emits a result with the given [id]
   S createSourceOnlyStep(
-      {String Function(Map<Uri, String>) action,
-      DataId resultId,
+      {required String Function(Map<Uri, String?>) action,
+      required DataId resultId,
       bool requestSources: true});
 
   /// Create a step that applies [action] on the module [inputId] data, and
   /// emits a result with the given [resultId].
   S createModuleDataStep(
-      {String Function(String) action,
-      DataId inputId,
-      DataId resultId,
+      {required String Function(String) action,
+      required DataId inputId,
+      required DataId resultId,
       bool requestModuleData: true});
 
   /// Create a step that applies [action] on the module [inputId] data and the
@@ -52,10 +52,10 @@
   ///
   /// [depId] may be the same as [resultId] or [inputId].
   S createLinkStep(
-      {String Function(String, List<String>) action,
-      DataId inputId,
-      DataId depId,
-      DataId resultId,
+      {required String Function(String, List<String?>) action,
+      required DataId inputId,
+      required DataId depId,
+      required DataId resultId,
       bool requestDependenciesData: true});
 
   /// Create a step that applies [action] only on the main module [inputId] data
@@ -65,23 +65,23 @@
   /// [depId] may be the same as [inputId] but not [resultId] since this action
   /// is only applied on the main module.
   S createMainOnlyStep(
-      {String Function(String, List<String>) action,
-      DataId inputId,
-      DataId depId,
-      DataId resultId,
+      {required String Function(String, List<String?>) action,
+      required DataId inputId,
+      required DataId depId,
+      required DataId resultId,
       bool requestDependenciesData: true});
 
   /// Create a step that applies [action1] and [action2] on the module [inputId]
   /// data, and emits two results with the given [result1Id] and [result2Id].
   S createTwoOutputStep(
-      {String Function(String) action1,
-      String Function(String) action2,
-      DataId inputId,
-      DataId result1Id,
-      DataId result2Id});
+      {required String Function(String) action1,
+      required String Function(String) action2,
+      required DataId inputId,
+      required DataId result1Id,
+      required DataId result2Id});
 
   /// Return the result data produced by a modular step.
-  String getResult(Pipeline<S> pipeline, Module m, DataId dataId);
+  String? getResult(Pipeline<S> pipeline, Module m, DataId dataId);
 
   /// Do any cleanup work needed after pipeline is completed. Needed because
   /// some implementations retain data around to be able to answer [getResult]
@@ -322,7 +322,7 @@
     var counterStep = testStrategy.createSourceOnlyStep(
         action: (_) => '${i++}', resultId: counterId);
     var linkStep = testStrategy.createLinkStep(
-        action: (String m, List<String> deps) => "${deps.join(',')},$m",
+        action: (String m, List<String?> deps) => "${deps.join(',')},$m",
         inputId: counterId,
         depId: counterId,
         resultId: linkId,
@@ -352,7 +352,7 @@
     var counterStep = testStrategy.createSourceOnlyStep(
         action: (_) => '${i++}', resultId: counterId);
     var linkStep = testStrategy.createLinkStep(
-        action: (String m, List<String> deps) => "${deps.join(',')},$m",
+        action: (String m, List<String?> deps) => "${deps.join(',')},$m",
         inputId: counterId,
         depId: counterId,
         resultId: linkId,
@@ -384,7 +384,7 @@
     var counterStep = testStrategy.createSourceOnlyStep(
         action: (_) => '${i++}', resultId: counterId);
     var linkStep = testStrategy.createLinkStep(
-        action: (String m, List<String> deps) => "${deps.join(',')},$m",
+        action: (String m, List<String?> deps) => "${deps.join(',')},$m",
         inputId: counterId,
         depId: counterId,
         resultId: linkId,
@@ -419,7 +419,7 @@
 DataId _uppercaseId = const DataId("uppercase");
 DataId _joinId = const DataId("join");
 
-String _concat(Map<Uri, String> sources) {
+String _concat(Map<Uri, String?> sources) {
   var buffer = new StringBuffer();
   sources.forEach((uri, contents) {
     buffer.write("$uri: $contents\n");
@@ -430,7 +430,7 @@
 String _lowercase(String contents) => contents.toLowerCase();
 String _uppercase(String contents) => contents.toUpperCase();
 
-String _replaceAndJoin(String moduleData, List<String> depContents) {
+String _replaceAndJoin(String moduleData, List<String?> depContents) {
   var buffer = new StringBuffer();
   depContents.forEach(buffer.writeln);
   buffer.write(moduleData.replaceAll(".dart:", ""));
diff --git a/pkg/modular_test/test/validate_pipeline_test.dart b/pkg/modular_test/test/validate_pipeline_test.dart
index a0b5759..431d91a 100644
--- a/pkg/modular_test/test/validate_pipeline_test.dart
+++ b/pkg/modular_test/test/validate_pipeline_test.dart
@@ -83,5 +83,5 @@
   @override
   Future<void> runStep(ModularStep step, Module module,
           Map<Module, Set<DataId>> visibleData, List<String> flags) =>
-      null;
+      Future.value(null);
 }
diff --git a/pkg/modular_test/test/validate_suite_test.dart b/pkg/modular_test/test/validate_suite_test.dart
index f335567..5093444 100644
--- a/pkg/modular_test/test/validate_suite_test.dart
+++ b/pkg/modular_test/test/validate_suite_test.dart
@@ -8,19 +8,10 @@
 
 main() {
   test('module test is not empty', () {
-    expect(
-        () => ModularTest([], null, []), throwsA(TypeMatcher<ArgumentError>()));
-
     var m = Module("a", [], Uri.parse("app:/"), []);
     expect(() => ModularTest([], m, []), throwsA(TypeMatcher<ArgumentError>()));
   });
 
-  test('module test must have a main module', () {
-    var m = Module("a", [], Uri.parse("app:/"), []);
-    expect(() => ModularTest([m], null, []),
-        throwsA(TypeMatcher<ArgumentError>()));
-  });
-
   test('package must depend on package', () {
     var m1a = Module("a", const [], Uri.parse("app:/"),
         [Uri.parse("a1.dart"), Uri.parse("a2.dart")],
diff --git a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
index 9686fae..0b1972c 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
@@ -1301,7 +1301,7 @@
     Regress216834909_hang_at_exit = false;
   }
 }
-#endif  // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ||     \
+#endif  // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ||
         // defined(DART_HOST_OS_MACOS)
 
 }  // namespace dart
diff --git a/runtime/tests/vm/dart_2/isolates/reload_many_isolates_live_and_die_test.dart b/runtime/tests/vm/dart_2/isolates/reload_many_isolates_live_and_die_test.dart
index 7a0f2e6..74ecd60 100644
--- a/runtime/tests/vm/dart_2/isolates/reload_many_isolates_live_and_die_test.dart
+++ b/runtime/tests/vm/dart_2/isolates/reload_many_isolates_live_and_die_test.dart
@@ -10,7 +10,7 @@
 
 import 'reload_utils.dart';
 
-const N = 20;
+final N = runningInSimulator ? 2 : 20;
 
 main() async {
   if (!currentVmSupportsReload) return;
diff --git a/runtime/tests/vm/dart_2/isolates/reload_utils.dart b/runtime/tests/vm/dart_2/isolates/reload_utils.dart
index c1c9e96..f41c52f 100644
--- a/runtime/tests/vm/dart_2/isolates/reload_utils.dart
+++ b/runtime/tests/vm/dart_2/isolates/reload_utils.dart
@@ -20,6 +20,11 @@
       !executable.contains('dart_precompiled_runtime');
 }
 
+bool get runningInSimulator {
+  final executable = Platform.executable;
+  return executable.contains('SIM');
+}
+
 final includeIn = RegExp(r'//\s+@include-in-reload-([0-9]+)([+]?)\s*$');
 
 Future<List<String>> generateDills(String tempDir, String testDartFile) async {
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index e9b73af..7c963b6 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -37,6 +37,7 @@
 dart_2/isolates/*: Pass, Slow # Tests use many isolates and take a longer time.
 dart_2/isolates/concurrency_stress_sanity_test: Pass, Slow # Spawns subprocesses
 dart_2/isolates/fast_object_copy_test: Pass, Slow # Slow due to doing a lot of transitive object copies.
+dart_2/isolates/reload_many_isolates_live_and_die_test: Pass, Slow # Launches subprocesses, makes 2 kernel compilations and use reload.
 dart_2/minimal_kernel_test: Pass, Slow # Spawns several subprocesses
 dart_2/null_safety_autodetection_in_kernel_compiler_test: Pass, Slow # Spawns several subprocesses
 dart_2/print_object_layout_test: Pass, Slow # Spawns several subprocesses
@@ -280,6 +281,7 @@
 dart/data_uri_spawn_test: Skip # Timeout
 dart/kernel_determinism_test: SkipSlow
 dart_2/data_uri_spawn_test: Skip # Timeout
+dart_2/isolates/reload_many_isolates_live_and_die_test: SkipSlow # The test itself does reloading of subprocesses.
 dart_2/kernel_determinism_test: SkipSlow
 
 [ $compiler != dartk && $compiler != none ]
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.cc b/runtime/vm/compiler/assembler/assembler_arm64.cc
index e2b39f7..8f0e2ee 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.cc
+++ b/runtime/vm/compiler/assembler/assembler_arm64.cc
@@ -1673,15 +1673,13 @@
   }
 }
 
-void Assembler::LeaveDartFrame(RestorePP restore_pp) {
+void Assembler::LeaveDartFrame() {
   if (!FLAG_precompiled_mode) {
-    if (restore_pp == kRestoreCallerPP) {
-      // Restore and untag PP.
-      LoadFromOffset(
-          PP, FP,
-          target::frame_layout.saved_caller_pp_from_fp * target::kWordSize);
-      sub(PP, PP, Operand(kHeapObjectTag));
-    }
+    // Restore and untag PP.
+    LoadFromOffset(
+        PP, FP,
+        target::frame_layout.saved_caller_pp_from_fp * target::kWordSize);
+    sub(PP, PP, Operand(kHeapObjectTag));
   }
   set_constant_pool_allowed(false);
   LeaveFrame();
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h
index 33fa8bb..4690df6e 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.h
+++ b/runtime/vm/compiler/assembler/assembler_arm64.h
@@ -2100,7 +2100,7 @@
 
   void EnterDartFrame(intptr_t frame_size, Register new_pp = kNoRegister);
   void EnterOsrFrame(intptr_t extra_size, Register new_pp = kNoRegister);
-  void LeaveDartFrame(RestorePP restore_pp = kRestoreCallerPP);
+  void LeaveDartFrame();
 
   // For non-leaf runtime calls. For leaf runtime calls, use LeafRuntimeScope,
   void CallRuntime(const RuntimeEntry& entry, intptr_t argument_count);
diff --git a/runtime/vm/compiler/assembler/assembler_base.h b/runtime/vm/compiler/assembler/assembler_base.h
index 7faf6fd..f30cfc9 100644
--- a/runtime/vm/compiler/assembler/assembler_base.h
+++ b/runtime/vm/compiler/assembler/assembler_base.h
@@ -591,8 +591,6 @@
   friend class AssemblerFixup;
 };
 
-enum RestorePP { kRestoreCallerPP, kKeepCalleePP };
-
 class AssemblerBase : public StackResource {
  public:
   explicit AssemblerBase(ObjectPoolBuilder* object_pool_builder)
diff --git a/runtime/vm/compiler/assembler/assembler_riscv.cc b/runtime/vm/compiler/assembler/assembler_riscv.cc
index 335f0ea..fd70709 100644
--- a/runtime/vm/compiler/assembler/assembler_riscv.cc
+++ b/runtime/vm/compiler/assembler/assembler_riscv.cc
@@ -3691,15 +3691,13 @@
   }
 }
 
-void Assembler::LeaveDartFrame(RestorePP restore_pp) {
+void Assembler::LeaveDartFrame() {
   // N.B. The ordering here is important. We must never read beyond SP or
   // it may have already been clobbered by a signal handler.
   if (!FLAG_precompiled_mode) {
-    if (restore_pp == kRestoreCallerPP) {
-      lx(PP, Address(FP, target::frame_layout.saved_caller_pp_from_fp *
-                             target::kWordSize));
-      subi(PP, PP, kHeapObjectTag);
-    }
+    lx(PP, Address(FP, target::frame_layout.saved_caller_pp_from_fp *
+                           target::kWordSize));
+    subi(PP, PP, kHeapObjectTag);
   }
   set_constant_pool_allowed(false);
   subi(SP, FP, 2 * target::kWordSize);
diff --git a/runtime/vm/compiler/assembler/assembler_riscv.h b/runtime/vm/compiler/assembler/assembler_riscv.h
index d24ea42..44424ae 100644
--- a/runtime/vm/compiler/assembler/assembler_riscv.h
+++ b/runtime/vm/compiler/assembler/assembler_riscv.h
@@ -1232,7 +1232,7 @@
 
   void EnterDartFrame(intptr_t frame_size, Register new_pp = kNoRegister);
   void EnterOsrFrame(intptr_t extra_size, Register new_pp = kNoRegister);
-  void LeaveDartFrame(RestorePP restore_pp = kRestoreCallerPP);
+  void LeaveDartFrame();
 
   // For non-leaf runtime calls. For leaf runtime calls, use LeafRuntimeScope,
   void CallRuntime(const RuntimeEntry& entry, intptr_t argument_count);
diff --git a/runtime/vm/compiler/assembler/assembler_x64.cc b/runtime/vm/compiler/assembler/assembler_x64.cc
index bfc1a69..9a65cda 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.cc
+++ b/runtime/vm/compiler/assembler/assembler_x64.cc
@@ -2017,13 +2017,11 @@
   }
 }
 
-void Assembler::LeaveDartFrame(RestorePP restore_pp) {
+void Assembler::LeaveDartFrame() {
   // Restore caller's PP register that was pushed in EnterDartFrame.
   if (!FLAG_precompiled_mode) {
-    if (restore_pp == kRestoreCallerPP) {
-      movq(PP, Address(RBP, (target::frame_layout.saved_caller_pp_from_fp *
-                             target::kWordSize)));
-    }
+    movq(PP, Address(RBP, (target::frame_layout.saved_caller_pp_from_fp *
+                           target::kWordSize)));
   }
   set_constant_pool_allowed(false);
   LeaveFrame();
diff --git a/runtime/vm/compiler/assembler/assembler_x64.h b/runtime/vm/compiler/assembler/assembler_x64.h
index 653e97f..395a372 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.h
+++ b/runtime/vm/compiler/assembler/assembler_x64.h
@@ -1142,7 +1142,7 @@
   //   pushq r15
   //   .....
   void EnterDartFrame(intptr_t frame_size, Register new_pp = kNoRegister);
-  void LeaveDartFrame(RestorePP restore_pp = kRestoreCallerPP);
+  void LeaveDartFrame();
 
   // Set up a Dart frame for a function compiled for on-stack replacement.
   // The frame layout is a normal Dart frame, but the frame is partially set
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index 88f0e9a..d1a9fe0 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -1360,8 +1360,7 @@
     // Restore the pre-aligned SP.
     __ mov(SPREG, saved_fp_or_sp);
   } else {
-    // Although PP is a callee-saved register, it may have been moved by the GC.
-    __ LeaveDartFrame(compiler::kRestoreCallerPP);
+    __ LeaveDartFrame();
 
     // Restore the global object pool after returning from runtime (old space is
     // moving, so the GOP could have been relocated).
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index ae2531a..b30b025 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -1301,8 +1301,7 @@
     // Restore the pre-aligned SP.
     __ movq(SPREG, saved_sp);
   } else {
-    // Although PP is a callee-saved register, it may have been moved by the GC.
-    __ LeaveDartFrame(compiler::kRestoreCallerPP);
+    __ LeaveDartFrame();
     // Restore the global object pool after returning from runtime (old space is
     // moving, so the GOP could have been relocated).
     if (FLAG_precompiled_mode) {
diff --git a/runtime/vm/compiler/ffi/call.cc b/runtime/vm/compiler/ffi/call.cc
index d4cb983..be76d75 100644
--- a/runtime/vm/compiler/ffi/call.cc
+++ b/runtime/vm/compiler/ffi/call.cc
@@ -16,10 +16,13 @@
 // TODO(dartbug.com/36607): Cache the trampolines.
 FunctionPtr TrampolineFunction(const FunctionType& dart_signature,
                                const FunctionType& c_signature,
-                               bool is_leaf) {
+                               bool is_leaf,
+                               const String& function_name) {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  String& name = String::Handle(zone, Symbols::New(thread, "FfiTrampoline"));
+  String& name =
+      String::Handle(zone, Symbols::NewFormatted(thread, "FfiTrampoline_%s",
+                                                 function_name.ToCString()));
   const Library& lib = Library::Handle(zone, Library::FfiLibrary());
   const Class& owner_class = Class::Handle(zone, lib.toplevel_class());
   FunctionType& signature = FunctionType::Handle(zone, FunctionType::New());
diff --git a/runtime/vm/compiler/ffi/call.h b/runtime/vm/compiler/ffi/call.h
index 06bccde..aecd128 100644
--- a/runtime/vm/compiler/ffi/call.h
+++ b/runtime/vm/compiler/ffi/call.h
@@ -21,7 +21,8 @@
 
 FunctionPtr TrampolineFunction(const FunctionType& dart_signature,
                                const FunctionType& c_signature,
-                               bool is_leaf);
+                               bool is_leaf,
+                               const String& function_name);
 
 }  // namespace ffi
 
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
index f510129..e8fe9a6 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
@@ -1028,8 +1028,10 @@
     Report::LongJump(language_error);
   }
 
+  const auto& name =
+      String::Handle(parsed_function_->function().UserVisibleName());
   const Function& target = Function::ZoneHandle(
-      compiler::ffi::TrampolineFunction(dart_type, native_type, is_leaf));
+      compiler::ffi::TrampolineFunction(dart_type, native_type, is_leaf, name));
 
   Fragment code;
   // Store the pointer in the context, we cannot load the untagged address
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index fc87889..9162e8b 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -15811,7 +15811,7 @@
     ASSERT(array.At(sentinel_start + entry_length - 1) ==
            smi_illegal_cid().ptr());
     ASSERT(ICData::CachedEmptyICDataArray(num_args, is_tracking_exactness()) ==
-           entries());
+           array.ptr());
   } else {
     ASSERT(array.At(sentinel_start + entry_length - 1) == ptr());
   }
diff --git a/sdk/lib/_internal/js_runtime/lib/late_helper.dart b/sdk/lib/_internal/js_runtime/lib/late_helper.dart
index ceabcd7..1643555 100644
--- a/sdk/lib/_internal/js_runtime/lib/late_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/late_helper.dart
@@ -6,8 +6,20 @@
 
 import 'dart:_internal' show LateError, createSentinel, isSentinel;
 
+@pragma('dart2js:tryInline')
+void throwLateFieldNI(String fieldName) => throw LateError.fieldNI(fieldName);
+@pragma('dart2js:tryInline')
+void throwLateFieldAI(String fieldName) => throw LateError.fieldAI(fieldName);
+@pragma('dart2js:tryInline')
 void throwLateFieldADI(String fieldName) => throw LateError.fieldADI(fieldName);
 
+@pragma('dart2js:tryInline')
+void throwUnnamedLateFieldNI() => throw LateError.fieldNI('');
+@pragma('dart2js:tryInline')
+void throwUnnamedLateFieldAI() => throw LateError.fieldAI('');
+@pragma('dart2js:tryInline')
+void throwUnnamedLateFieldADI() => throw LateError.fieldADI('');
+
 /// A boxed variable used for lowering uninitialized `late` variables when they
 /// are locals or statics.
 class _Cell {
@@ -113,19 +125,8 @@
 // Helpers for lowering late instance fields:
 // TODO(fishythefish): Support specialization of sentinels based on type.
 
-@pragma('dart2js:noInline')
-@pragma('dart2js:as:trust')
-T _lateReadCheck<T>(Object? value, String name) {
-  if (isSentinel(value)) throw LateError.fieldNI(name);
-  return value as T;
-}
+external T _lateReadCheck<T>(Object? value, String name);
 
-@pragma('dart2js:noInline')
-void _lateWriteOnceCheck(Object? value, String name) {
-  if (!isSentinel(value)) throw LateError.fieldAI(name);
-}
+external void _lateWriteOnceCheck(Object? value, String name);
 
-@pragma('dart2js:noInline')
-void _lateInitializeOnceCheck(Object? value, String name) {
-  if (!isSentinel(value)) throw LateError.fieldADI(name);
-}
+external void _lateInitializeOnceCheck(Object? value, String name);
diff --git a/tools/VERSION b/tools/VERSION
index 5c4a258..bbee9a8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 44
+PRERELEASE 45
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/git_cl_try.sh b/tools/git_cl_try.sh
index 12f56b9..4b76be9 100644
--- a/tools/git_cl_try.sh
+++ b/tools/git_cl_try.sh
@@ -42,19 +42,22 @@
   echo "git-cl-try-vm-ffi"
   git cl try -B dart/try                                     \
      -b vm-ffi-android-debug-arm-try                         \
-     -b vm-ffi-android-debug-arm64-try                       \
+     -b vm-ffi-android-debug-arm64c-try                      \
      -b vm-ffi-android-product-arm-try                       \
-     -b vm-ffi-android-product-arm64-try                     \
+     -b vm-ffi-android-product-arm64c-try                    \
      -b vm-ffi-android-release-arm-try                       \
-     -b vm-ffi-android-release-arm64-try
+     -b vm-ffi-android-release-arm64c-try                    \
 }
 function git-cl-try-vm-precomp {
   echo "git-cl-try-vm-precomp"
   git cl try -B dart/try                                     \
-     -b vm-kernel-precomp-android-release-arm64-try          \
+     -b vm-kernel-precomp-android-release-arm64c-try          \
      -b vm-kernel-precomp-android-release-arm_x64-try        \
+     -b vm-kernel-precomp-dwarf-linux-product-x64-try        \
      -b vm-kernel-precomp-linux-debug-simarm_x64-try         \
+     -b vm-kernel-precomp-linux-debug-simriscv64-try         \
      -b vm-kernel-precomp-linux-debug-x64-try                \
+     -b vm-kernel-precomp-linux-debug-x64c-try               \
      -b vm-kernel-precomp-linux-product-x64-try              \
      -b vm-kernel-precomp-linux-release-simarm-try           \
      -b vm-kernel-precomp-linux-release-simarm64-try         \
@@ -74,6 +77,7 @@
    -b vm-kernel-precomp-asan-linux-release-x64-try           \
    -b vm-kernel-precomp-msan-linux-release-x64-try           \
    -b vm-kernel-precomp-tsan-linux-release-x64-try           \
+   -b vm-kernel-ubsan-linux-release-x64-try
 }
 function git-cl-try-vm-all {
   echo "git-cl-try-vm-all"
diff --git a/tools/package_deps/bin/package_deps.dart b/tools/package_deps/bin/package_deps.dart
index 43f2bc1..ce47f51 100644
--- a/tools/package_deps/bin/package_deps.dart
+++ b/tools/package_deps/bin/package_deps.dart
@@ -233,9 +233,9 @@
 
     var extraDevDeclarations = Set<String>.from(_declaredDevDependencies)
       ..removeAll(devdeps);
-    // Remove package:pedantic and package:lints as they are often declared as
-    // dev dependencies in order to bring in their analysis_options.yaml files.
-    extraDevDeclarations.removeAll(['lints', 'pedantic']);
+    // Remove package:lints - it's often declared as a dev dependency in order
+    // to bring in analysis_options configuration files.
+    extraDevDeclarations.removeAll(['lints']);
     if (extraDevDeclarations.isNotEmpty) {
       out('  ${_printSet(extraDevDeclarations)} declared in '
           "'dev_dependencies:' but not used in dev dirs.");