Version 2.15.0-20.0.dev

Merge commit 'cfb057ddca6b90ed2b25296c295e73e5ce895076' into 'dev'
diff --git a/DEPS b/DEPS
index 0656dad..ee3a819 100644
--- a/DEPS
+++ b/DEPS
@@ -172,7 +172,7 @@
   "web_components_rev": "8f57dac273412a7172c8ade6f361b407e2e4ed02",
   "web_socket_channel_rev": "6448ce532445a8a458fa191d9346df071ae0acad",
   "WebCore_rev": "fb11e887f77919450e497344da570d780e078bc8",
-  "webdev_rev": "b0aae7b6944d484722e6af164abedd864a2a0afa",
+  "webdev_rev": "50fe70a3137d9665fbe94cd34af5277b65d95079",
   "webkit_inspection_protocol_rev": "dd6fb5d8b536e19cedb384d0bbf1f5631923f1e8",
   "yaml_rev": "b4c4411631bda556ce9a45af1ab0eecaf9f3ac53",
   "zlib_rev": "bf44340d1b6be1af8950bbdf664fec0cf5a831cc",
diff --git a/benchmarks/EventLoopLatencyRegexp/dart/EventLoopLatencyRegexp.dart b/benchmarks/EventLoopLatencyRegexp/dart/EventLoopLatencyRegexp.dart
new file mode 100644
index 0000000..5ffe53f
--- /dev/null
+++ b/benchmarks/EventLoopLatencyRegexp/dart/EventLoopLatencyRegexp.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:isolate';
+
+import 'regexp_benchmark.dart';
+import '../../EventLoopLatencyJson/dart/latency.dart';
+
+main() async {
+  final exitPort = ReceivePort();
+  final exitFuture = exitPort.first;
+  final isolate = await Isolate.spawn(run, null, onExit: exitPort.sendPort);
+
+  // Measure event loop latency.
+  const tickDuration = const Duration(milliseconds: 1);
+  const numberOfTicks = 8 * 1000; // min 8 seconds.
+  final EventLoopLatencyStats stats =
+      await measureEventLoopLatency(tickDuration, numberOfTicks);
+
+  // Kill isolate & wait until it's dead.
+  isolate.kill(priority: Isolate.immediate);
+  await exitFuture;
+
+  // Report event loop latency statistics.
+  stats.report('EventLoopLatencyRegexp');
+}
+
+void run(dynamic msg) {
+  while (true) {
+    RegexpBenchmark().run();
+  }
+}
diff --git a/benchmarks/EventLoopLatencyRegexp/dart/regexp_benchmark.dart b/benchmarks/EventLoopLatencyRegexp/dart/regexp_benchmark.dart
new file mode 100644
index 0000000..e3389a8
--- /dev/null
+++ b/benchmarks/EventLoopLatencyRegexp/dart/regexp_benchmark.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:math';
+import 'dart:convert';
+
+class RegexpBenchmark {
+  void run() {
+    final re = RegExp(r'(x+)*y');
+    final s = 'x' * 26 + '';
+    re.allMatches(s).iterator.moveNext();
+  }
+}
diff --git a/benchmarks/EventLoopLatencyRegexp/dart2/EventLoopLatencyRegexp.dart b/benchmarks/EventLoopLatencyRegexp/dart2/EventLoopLatencyRegexp.dart
new file mode 100644
index 0000000..dec8cb2
--- /dev/null
+++ b/benchmarks/EventLoopLatencyRegexp/dart2/EventLoopLatencyRegexp.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart=2.9
+
+import 'dart:isolate';
+
+import 'json_benchmark.dart';
+import '../../EventLoopLatencyJson/dart2/latency.dart';
+
+main() async {
+  final exitPort = ReceivePort();
+  final exitFuture = exitPort.first;
+  final isolate = await Isolate.spawn(run, null, onExit: exitPort.sendPort);
+
+  // Measure event loop latency.
+  const tickDuration = const Duration(milliseconds: 1);
+  const numberOfTicks = 8 * 1000; // min 8 seconds.
+  final EventLoopLatencyStats stats =
+      await measureEventLoopLatency(tickDuration, numberOfTicks);
+
+  // Kill isolate & wait until it's dead.
+  isolate.kill(priority: Isolate.immediate);
+  await exitFuture;
+
+  // Report event loop latency statistics.
+  stats.report('EventLoopLatencyRegexp');
+}
+
+void run(dynamic msg) {
+  while (true) {
+    RegexpBenchmark().run();
+  }
+}
diff --git a/benchmarks/EventLoopLatencyRegexp/dart2/regexp_benchmark.dart b/benchmarks/EventLoopLatencyRegexp/dart2/regexp_benchmark.dart
new file mode 100644
index 0000000..6047117
--- /dev/null
+++ b/benchmarks/EventLoopLatencyRegexp/dart2/regexp_benchmark.dart
@@ -0,0 +1,16 @@
+// 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.9
+
+import 'dart:math';
+import 'dart:convert';
+
+class RegexpBenchmark {
+  void run() {
+    final re = RegExp(r'(x+)*y');
+    final s = 'x' * 26 + '';
+    re.allMatches(s).iterator.moveNext();
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index d5fd17e..c5d1271 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -942,9 +942,6 @@
       AddNullCheck.newInstance,
       ReplaceWithNullAware.single,
     ],
-    CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE: [
-      AddNullCheck.newInstance,
-    ],
     CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_AS_CONDITION: [
       AddNullCheck.newInstance,
     ],
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index f96a7ee..d315c84 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -419,7 +419,6 @@
   CompileTimeErrorCode.UNCHECKED_METHOD_INVOCATION_OF_NULLABLE_VALUE,
   CompileTimeErrorCode.UNCHECKED_OPERATOR_INVOCATION_OF_NULLABLE_VALUE,
   CompileTimeErrorCode.UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE,
-  CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE,
   CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_AS_CONDITION,
   CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_AS_ITERATOR,
   CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_IN_SPREAD,
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 53fdfae..d8b80ca 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -80,7 +80,7 @@
 /// TODO(scheglov) Clean up the list of implicitly analyzed files.
 class AnalysisDriver implements AnalysisDriverGeneric {
   /// The version of data format, should be incremented on every format change.
-  static const int DATA_VERSION = 168;
+  static const int DATA_VERSION = 169;
 
   /// The number of exception contexts allowed to write. Once this field is
   /// zero, we stop writing any new exception contexts in this process.
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_context.dart b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
index 26339f7..f80a744 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_context.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
@@ -20,6 +20,7 @@
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/summary/package_bundle_reader.dart';
 import 'package:analyzer/src/summary2/bundle_reader.dart';
+import 'package:analyzer/src/summary2/informative_data.dart';
 import 'package:analyzer/src/summary2/link.dart' as link2;
 import 'package:analyzer/src/summary2/linked_element_factory.dart';
 import 'package:analyzer/src/summary2/reference.dart';
@@ -109,10 +110,13 @@
 
       cycle.directDependencies.forEach(loadBundle);
 
-      var unitsInformativeBytes = <Uri, Uint8List>{};
+      var unitsInformativeData = <Uri, InformativeUnitData>{};
       for (var library in cycle.libraries) {
         for (var file in library.libraryFiles) {
-          unitsInformativeBytes[file.uri] = file.getInformativeBytes();
+          unitsInformativeData[file.uri] = InformativeUnitData(
+            content: file.content,
+            bytes: file.getInformativeBytes(),
+          );
         }
       }
 
@@ -191,7 +195,7 @@
         elementFactory.addBundle(
           BundleReader(
             elementFactory: elementFactory,
-            unitsInformativeBytes: unitsInformativeBytes,
+            unitsInformativeData: unitsInformativeData,
             resolutionBytes: resolutionBytes,
           ),
         );
@@ -240,7 +244,7 @@
           BundleReader(
             elementFactory: elementFactory,
             resolutionBytes: bundle.resolutionBytes,
-            unitsInformativeBytes: {},
+            unitsInformativeData: {},
           ),
         );
       }
diff --git a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
index bbec62f..d8eaf00 100644
--- a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
@@ -18,178 +18,69 @@
   /// visited nodes to the given [sink].
   ToSourceVisitor(this.sink);
 
-  /// Visit the given function [body], printing the [prefix] before if the body
-  /// is not empty.
-  @protected
-  void safelyVisitFunctionWithPrefix(String prefix, FunctionBody body) {
-    if (body is! EmptyFunctionBody) {
-      sink.write(prefix);
-    }
-    safelyVisitNode(body);
-  }
-
-  /// Safely visit the given [node].
-  @protected
-  void safelyVisitNode(AstNode? node) {
-    if (node != null) {
-      node.accept(this);
-    }
-  }
-
-  /// Print a list of [nodes] without any separation.
-  @protected
-  void safelyVisitNodeList(NodeList<AstNode> nodes) {
-    safelyVisitNodeListWithSeparator(nodes, "");
-  }
-
-  /// Print a list of [nodes], separated by the given [separator].
-  @protected
-  void safelyVisitNodeListWithSeparator(
-      NodeList<AstNode> nodes, String separator) {
-    int size = nodes.length;
-    for (int i = 0; i < size; i++) {
-      if (i > 0) {
-        sink.write(separator);
-      }
-      nodes[i].accept(this);
-    }
-  }
-
-  /// Print a list of [nodes], prefixed by the given [prefix] if the list is not
-  /// empty, and separated by the given [separator].
-  @protected
-  void safelyVisitNodeListWithSeparatorAndPrefix(
-      String prefix, NodeList<AstNode> nodes, String separator) {
-    int size = nodes.length;
-    if (size > 0) {
-      sink.write(prefix);
-      for (int i = 0; i < size; i++) {
-        if (i > 0) {
-          sink.write(separator);
-        }
-        nodes[i].accept(this);
-      }
-    }
-  }
-
-  /// Print a list of [nodes], separated by the given [separator], followed by
-  /// the given [suffix] if the list is not empty.
-  @protected
-  void safelyVisitNodeListWithSeparatorAndSuffix(
-      NodeList<AstNode> nodes, String separator, String suffix) {
-    int size = nodes.length;
-    if (size > 0) {
-      for (int i = 0; i < size; i++) {
-        if (i > 0) {
-          sink.write(separator);
-        }
-        nodes[i].accept(this);
-      }
-      sink.write(suffix);
-    }
-  }
-
-  /// Safely visit the given [node], printing the [prefix] before the node if it
-  /// is non-`null`.
-  @protected
-  void safelyVisitNodeWithPrefix(String prefix, AstNode? node) {
-    if (node != null) {
-      sink.write(prefix);
-      node.accept(this);
-    }
-  }
-
-  /// Safely visit the given [node], printing the [suffix] after the node if it
-  /// is non-`null`.
-  @protected
-  void safelyVisitNodeWithSuffix(AstNode? node, String suffix) {
-    if (node != null) {
-      node.accept(this);
-      sink.write(suffix);
-    }
-  }
-
-  /// Safely visit the given [token].
-  @protected
-  void safelyVisitToken(Token? token) {
-    if (token != null) {
-      sink.write(token.lexeme);
-    }
-  }
-
-  /// Safely visit the given [token], printing the [suffix] after the token if
-  /// it is non-`null`.
-  @protected
-  void safelyVisitTokenWithSuffix(Token? token, String suffix) {
-    if (token != null) {
-      sink.write(token.lexeme);
-      sink.write(suffix);
-    }
-  }
-
   @override
   void visitAdjacentStrings(AdjacentStrings node) {
-    safelyVisitNodeListWithSeparator(node.strings, " ");
+    _visitNodeList(node.strings, separator: ' ');
   }
 
   @override
   void visitAnnotation(Annotation node) {
     sink.write('@');
-    safelyVisitNode(node.name);
-    safelyVisitNode(node.typeArguments);
-    safelyVisitNodeWithPrefix(".", node.constructorName);
-    safelyVisitNode(node.arguments);
+    _visitNode(node.name);
+    _visitNode(node.typeArguments);
+    _visitNode(node.constructorName, prefix: '.');
+    _visitNode(node.arguments);
   }
 
   @override
   void visitArgumentList(ArgumentList node) {
     sink.write('(');
-    safelyVisitNodeListWithSeparator(node.arguments, ", ");
+    _visitNodeList(node.arguments, separator: ', ');
     sink.write(')');
   }
 
   @override
   void visitAsExpression(AsExpression node) {
-    safelyVisitNode(node.expression);
-    sink.write(" as ");
-    safelyVisitNode(node.type);
+    _visitNode(node.expression);
+    sink.write(' as ');
+    _visitNode(node.type);
   }
 
   @override
   void visitAssertInitializer(AssertInitializer node) {
-    sink.write("assert (");
-    safelyVisitNode(node.condition);
+    sink.write('assert (');
+    _visitNode(node.condition);
     if (node.message != null) {
       sink.write(', ');
-      safelyVisitNode(node.message);
+      _visitNode(node.message);
     }
-    sink.write(");");
+    sink.write(');');
   }
 
   @override
   void visitAssertStatement(AssertStatement node) {
-    sink.write("assert (");
-    safelyVisitNode(node.condition);
+    sink.write('assert (');
+    _visitNode(node.condition);
     if (node.message != null) {
       sink.write(', ');
-      safelyVisitNode(node.message);
+      _visitNode(node.message);
     }
-    sink.write(");");
+    sink.write(');');
   }
 
   @override
   void visitAssignmentExpression(AssignmentExpression node) {
-    safelyVisitNode(node.leftHandSide);
+    _visitNode(node.leftHandSide);
     sink.write(' ');
     sink.write(node.operator.lexeme);
     sink.write(' ');
-    safelyVisitNode(node.rightHandSide);
+    _visitNode(node.rightHandSide);
   }
 
   @override
   void visitAwaitExpression(AwaitExpression node) {
-    sink.write("await ");
-    safelyVisitNode(node.expression);
+    sink.write('await ');
+    _visitNode(node.expression);
   }
 
   @override
@@ -204,7 +95,7 @@
   @override
   void visitBlock(Block node) {
     sink.write('{');
-    safelyVisitNodeListWithSeparator(node.statements, " ");
+    _visitNodeList(node.statements, separator: ' ');
     sink.write('}');
   }
 
@@ -218,7 +109,7 @@
       }
       sink.write(' ');
     }
-    safelyVisitNode(node.block);
+    _visitNode(node.block);
   }
 
   @override
@@ -228,63 +119,63 @@
 
   @override
   void visitBreakStatement(BreakStatement node) {
-    sink.write("break");
-    safelyVisitNodeWithPrefix(" ", node.label);
-    sink.write(";");
+    sink.write('break');
+    _visitNode(node.label, prefix: ' ');
+    sink.write(';');
   }
 
   @override
   void visitCascadeExpression(CascadeExpression node) {
-    safelyVisitNode(node.target);
-    safelyVisitNodeList(node.cascadeSections);
+    _visitNode(node.target);
+    _visitNodeList(node.cascadeSections);
   }
 
   @override
   void visitCatchClause(CatchClause node) {
-    safelyVisitNodeWithPrefix("on ", node.exceptionType);
+    _visitNode(node.exceptionType, prefix: 'on ');
     if (node.catchKeyword != null) {
       if (node.exceptionType != null) {
         sink.write(' ');
       }
-      sink.write("catch (");
-      safelyVisitNode(node.exceptionParameter);
-      safelyVisitNodeWithPrefix(", ", node.stackTraceParameter);
-      sink.write(") ");
+      sink.write('catch (');
+      _visitNode(node.exceptionParameter);
+      _visitNode(node.stackTraceParameter, prefix: ', ');
+      sink.write(') ');
     } else {
-      sink.write(" ");
+      sink.write(' ');
     }
-    safelyVisitNode(node.body);
+    _visitNode(node.body);
   }
 
   @override
   void visitClassDeclaration(ClassDeclaration node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    safelyVisitTokenWithSuffix(node.abstractKeyword, " ");
-    sink.write("class ");
-    safelyVisitNode(node.name);
-    safelyVisitNode(node.typeParameters);
-    safelyVisitNodeWithPrefix(" ", node.extendsClause);
-    safelyVisitNodeWithPrefix(" ", node.withClause);
-    safelyVisitNodeWithPrefix(" ", node.implementsClause);
-    sink.write(" {");
-    safelyVisitNodeListWithSeparator(node.members, " ");
-    sink.write("}");
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    _visitToken(node.abstractKeyword, suffix: ' ');
+    sink.write('class ');
+    _visitNode(node.name);
+    _visitNode(node.typeParameters);
+    _visitNode(node.extendsClause, prefix: ' ');
+    _visitNode(node.withClause, prefix: ' ');
+    _visitNode(node.implementsClause, prefix: ' ');
+    sink.write(' {');
+    _visitNodeList(node.members, separator: ' ');
+    sink.write('}');
   }
 
   @override
   void visitClassTypeAlias(ClassTypeAlias node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
     if (node.abstractKeyword != null) {
-      sink.write("abstract ");
+      sink.write('abstract ');
     }
-    sink.write("class ");
-    safelyVisitNode(node.name);
-    safelyVisitNode(node.typeParameters);
-    sink.write(" = ");
-    safelyVisitNode(node.superclass);
-    safelyVisitNodeWithPrefix(" ", node.withClause);
-    safelyVisitNodeWithPrefix(" ", node.implementsClause);
-    sink.write(";");
+    sink.write('class ');
+    _visitNode(node.name);
+    _visitNode(node.typeParameters);
+    sink.write(' = ');
+    _visitNode(node.superclass);
+    _visitNode(node.withClause, prefix: ' ');
+    _visitNode(node.implementsClause, prefix: ' ');
+    sink.write(';');
   }
 
   @override
@@ -297,103 +188,103 @@
   void visitCompilationUnit(CompilationUnit node) {
     var scriptTag = node.scriptTag;
     NodeList<Directive> directives = node.directives;
-    safelyVisitNode(scriptTag);
-    String prefix = scriptTag == null ? "" : " ";
-    safelyVisitNodeListWithSeparatorAndPrefix(prefix, directives, " ");
-    prefix = scriptTag == null && directives.isEmpty ? "" : " ";
-    safelyVisitNodeListWithSeparatorAndPrefix(prefix, node.declarations, " ");
+    _visitNode(scriptTag);
+    String prefix = scriptTag == null ? '' : ' ';
+    _visitNodeList(directives, prefix: prefix, separator: ' ');
+    prefix = scriptTag == null && directives.isEmpty ? '' : ' ';
+    _visitNodeList(node.declarations, prefix: prefix, separator: ' ');
   }
 
   @override
   void visitConditionalExpression(ConditionalExpression node) {
-    safelyVisitNode(node.condition);
-    sink.write(" ? ");
-    safelyVisitNode(node.thenExpression);
-    sink.write(" : ");
-    safelyVisitNode(node.elseExpression);
+    _visitNode(node.condition);
+    sink.write(' ? ');
+    _visitNode(node.thenExpression);
+    sink.write(' : ');
+    _visitNode(node.elseExpression);
   }
 
   @override
   void visitConfiguration(Configuration node) {
     sink.write('if (');
-    safelyVisitNode(node.name);
-    safelyVisitNodeWithPrefix(" == ", node.value);
+    _visitNode(node.name);
+    _visitNode(node.value, prefix: ' == ');
     sink.write(') ');
-    safelyVisitNode(node.uri);
+    _visitNode(node.uri);
   }
 
   @override
   void visitConstructorDeclaration(ConstructorDeclaration node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    safelyVisitTokenWithSuffix(node.externalKeyword, " ");
-    safelyVisitTokenWithSuffix(node.constKeyword, " ");
-    safelyVisitTokenWithSuffix(node.factoryKeyword, " ");
-    safelyVisitNode(node.returnType);
-    safelyVisitNodeWithPrefix(".", node.name);
-    safelyVisitNode(node.parameters);
-    safelyVisitNodeListWithSeparatorAndPrefix(" : ", node.initializers, ", ");
-    safelyVisitNodeWithPrefix(" = ", node.redirectedConstructor);
-    safelyVisitFunctionWithPrefix(" ", node.body);
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    _visitToken(node.externalKeyword, suffix: ' ');
+    _visitToken(node.constKeyword, suffix: ' ');
+    _visitToken(node.factoryKeyword, suffix: ' ');
+    _visitNode(node.returnType);
+    _visitNode(node.name, prefix: '.');
+    _visitNode(node.parameters);
+    _visitNodeList(node.initializers, prefix: ' : ', separator: ', ');
+    _visitNode(node.redirectedConstructor, prefix: ' = ');
+    _visitFunctionBody(node.body);
   }
 
   @override
   void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
-    safelyVisitTokenWithSuffix(node.thisKeyword, ".");
-    safelyVisitNode(node.fieldName);
-    sink.write(" = ");
-    safelyVisitNode(node.expression);
+    _visitToken(node.thisKeyword, suffix: '.');
+    _visitNode(node.fieldName);
+    sink.write(' = ');
+    _visitNode(node.expression);
   }
 
   @override
   void visitConstructorName(ConstructorName node) {
-    safelyVisitNode(node.type);
-    safelyVisitNodeWithPrefix(".", node.name);
+    _visitNode(node.type);
+    _visitNode(node.name, prefix: '.');
   }
 
   @override
   void visitConstructorReference(ConstructorReference node) {
-    safelyVisitNode(node.constructorName);
+    _visitNode(node.constructorName);
   }
 
   @override
   void visitContinueStatement(ContinueStatement node) {
-    sink.write("continue");
-    safelyVisitNodeWithPrefix(" ", node.label);
-    sink.write(";");
+    sink.write('continue');
+    _visitNode(node.label, prefix: ' ');
+    sink.write(';');
   }
 
   @override
   void visitDeclaredIdentifier(DeclaredIdentifier node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    safelyVisitTokenWithSuffix(node.keyword, " ");
-    safelyVisitNodeWithSuffix(node.type, " ");
-    safelyVisitNode(node.identifier);
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    _visitToken(node.keyword, suffix: ' ');
+    _visitNode(node.type, suffix: ' ');
+    _visitNode(node.identifier);
   }
 
   @override
   void visitDefaultFormalParameter(DefaultFormalParameter node) {
-    safelyVisitNode(node.parameter);
+    _visitNode(node.parameter);
     if (node.separator != null) {
-      if (node.separator!.lexeme != ":") {
-        sink.write(" ");
+      if (node.separator!.lexeme != ':') {
+        sink.write(' ');
       }
       sink.write(node.separator!.lexeme);
-      safelyVisitNodeWithPrefix(" ", node.defaultValue);
+      _visitNode(node.defaultValue, prefix: ' ');
     }
   }
 
   @override
   void visitDoStatement(DoStatement node) {
-    sink.write("do ");
-    safelyVisitNode(node.body);
-    sink.write(" while (");
-    safelyVisitNode(node.condition);
-    sink.write(");");
+    sink.write('do ');
+    _visitNode(node.body);
+    sink.write(' while (');
+    _visitNode(node.condition);
+    sink.write(');');
   }
 
   @override
   void visitDottedName(DottedName node) {
-    safelyVisitNodeListWithSeparator(node.components, ".");
+    _visitNodeList(node.components, separator: '.');
   }
 
   @override
@@ -413,26 +304,27 @@
 
   @override
   void visitEnumConstantDeclaration(EnumConstantDeclaration node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    safelyVisitNode(node.name);
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    _visitNode(node.name);
   }
 
   @override
   void visitEnumDeclaration(EnumDeclaration node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    sink.write("enum ");
-    safelyVisitNode(node.name);
-    sink.write(" {");
-    safelyVisitNodeListWithSeparator(node.constants, ", ");
-    sink.write("}");
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    sink.write('enum ');
+    _visitNode(node.name);
+    sink.write(' {');
+    _visitNodeList(node.constants, separator: ', ');
+    sink.write('}');
   }
 
   @override
   void visitExportDirective(ExportDirective node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    sink.write("export ");
-    safelyVisitNode(node.uri);
-    safelyVisitNodeListWithSeparatorAndPrefix(" ", node.combinators, " ");
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    sink.write('export ');
+    _visitNode(node.uri);
+    _visitNodeList(node.configurations, prefix: ' ', separator: ' ');
+    _visitNodeList(node.combinators, prefix: ' ', separator: ' ');
     sink.write(';');
   }
 
@@ -444,7 +336,7 @@
       sink.write(' ');
     }
     sink.write('${node.functionDefinition.lexeme} ');
-    safelyVisitNode(node.expression);
+    _visitNode(node.expression);
     if (node.semicolon != null) {
       sink.write(';');
     }
@@ -452,83 +344,83 @@
 
   @override
   void visitExpressionStatement(ExpressionStatement node) {
-    safelyVisitNode(node.expression);
+    _visitNode(node.expression);
     sink.write(';');
   }
 
   @override
   void visitExtendsClause(ExtendsClause node) {
-    sink.write("extends ");
-    safelyVisitNode(node.superclass);
+    sink.write('extends ');
+    _visitNode(node.superclass);
   }
 
   @override
   void visitExtensionDeclaration(ExtensionDeclaration node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, ' ', ' ');
-    safelyVisitTokenWithSuffix(node.extensionKeyword, ' ');
-    safelyVisitTokenWithSuffix(node.typeKeyword, ' ');
-    safelyVisitNode(node.name);
-    safelyVisitNode(node.typeParameters);
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    _visitToken(node.extensionKeyword, suffix: ' ');
+    _visitToken(node.typeKeyword, suffix: ' ');
+    _visitNode(node.name);
+    _visitNode(node.typeParameters);
     sink.write(' ');
-    safelyVisitToken(node.onKeyword);
+    _visitToken(node.onKeyword);
     sink.write(' ');
-    safelyVisitNodeWithSuffix(node.extendedType, ' ');
-    safelyVisitToken(node.leftBracket);
-    safelyVisitNodeListWithSeparator(node.members, ' ');
-    safelyVisitToken(node.rightBracket);
+    _visitNode(node.extendedType, suffix: ' ');
+    _visitToken(node.leftBracket);
+    _visitNodeList(node.members, separator: ' ');
+    _visitToken(node.rightBracket);
   }
 
   @override
   void visitExtensionOverride(ExtensionOverride node) {
-    safelyVisitNode(node.extensionName);
-    safelyVisitNode(node.typeArguments);
-    safelyVisitNode(node.argumentList);
+    _visitNode(node.extensionName);
+    _visitNode(node.typeArguments);
+    _visitNode(node.argumentList);
   }
 
   @override
   void visitFieldDeclaration(FieldDeclaration node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    safelyVisitTokenWithSuffix(node.abstractKeyword, " ");
-    safelyVisitTokenWithSuffix(node.externalKeyword, " ");
-    safelyVisitTokenWithSuffix(node.staticKeyword, " ");
-    safelyVisitNode(node.fields);
-    sink.write(";");
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    _visitToken(node.abstractKeyword, suffix: ' ');
+    _visitToken(node.externalKeyword, suffix: ' ');
+    _visitToken(node.staticKeyword, suffix: ' ');
+    _visitNode(node.fields);
+    sink.write(';');
   }
 
   @override
   void visitFieldFormalParameter(FieldFormalParameter node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, ' ', ' ');
-    safelyVisitTokenWithSuffix(node.requiredKeyword, " ");
-    safelyVisitTokenWithSuffix(node.covariantKeyword, ' ');
-    safelyVisitTokenWithSuffix(node.keyword, " ");
-    safelyVisitNodeWithSuffix(node.type, " ");
-    sink.write("this.");
-    safelyVisitNode(node.identifier);
-    safelyVisitNode(node.typeParameters);
-    safelyVisitNode(node.parameters);
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    _visitToken(node.requiredKeyword, suffix: ' ');
+    _visitToken(node.covariantKeyword, suffix: ' ');
+    _visitToken(node.keyword, suffix: ' ');
+    _visitNode(node.type, suffix: ' ');
+    sink.write('this.');
+    _visitNode(node.identifier);
+    _visitNode(node.typeParameters);
+    _visitNode(node.parameters);
   }
 
   @override
   void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) {
-    safelyVisitNode(node.loopVariable);
+    _visitNode(node.loopVariable);
     sink.write(' in ');
-    safelyVisitNode(node.iterable);
+    _visitNode(node.iterable);
   }
 
   @override
   void visitForEachPartsWithIdentifier(ForEachPartsWithIdentifier node) {
-    safelyVisitNode(node.identifier);
+    _visitNode(node.identifier);
     sink.write(' in ');
-    safelyVisitNode(node.iterable);
+    _visitNode(node.iterable);
   }
 
   @override
   void visitForElement(ForElement node) {
-    safelyVisitTokenWithSuffix(node.awaitKeyword, ' ');
+    _visitToken(node.awaitKeyword, suffix: ' ');
     sink.write('for (');
-    safelyVisitNode(node.forLoopParts);
+    _visitNode(node.forLoopParts);
     sink.write(') ');
-    safelyVisitNode(node.body);
+    _visitNode(node.body);
   }
 
   @override
@@ -540,14 +432,14 @@
     for (int i = 0; i < size; i++) {
       FormalParameter parameter = parameters[i];
       if (i > 0) {
-        sink.write(", ");
+        sink.write(', ');
       }
       if (groupEnd == null && parameter is DefaultFormalParameter) {
         if (parameter.isNamed) {
-          groupEnd = "}";
+          groupEnd = '}';
           sink.write('{');
         } else {
-          groupEnd = "]";
+          groupEnd = ']';
           sink.write('[');
         }
       }
@@ -561,20 +453,20 @@
 
   @override
   void visitForPartsWithDeclarations(ForPartsWithDeclarations node) {
-    safelyVisitNode(node.variables);
+    _visitNode(node.variables);
     sink.write(';');
-    safelyVisitNodeWithPrefix(' ', node.condition);
+    _visitNode(node.condition, prefix: ' ');
     sink.write(';');
-    safelyVisitNodeListWithSeparatorAndPrefix(' ', node.updaters, ', ');
+    _visitNodeList(node.updaters, prefix: ' ', separator: ', ');
   }
 
   @override
   void visitForPartsWithExpression(ForPartsWithExpression node) {
-    safelyVisitNode(node.initialization);
+    _visitNode(node.initialization);
     sink.write(';');
-    safelyVisitNodeWithPrefix(' ', node.condition);
+    _visitNode(node.condition, prefix: ' ');
     sink.write(';');
-    safelyVisitNodeListWithSeparatorAndPrefix(" ", node.updaters, ', ');
+    _visitNodeList(node.updaters, prefix: ' ', separator: ', ');
   }
 
   @override
@@ -583,69 +475,66 @@
       sink.write('await ');
     }
     sink.write('for (');
-    safelyVisitNode(node.forLoopParts);
+    _visitNode(node.forLoopParts);
     sink.write(') ');
-    safelyVisitNode(node.body);
+    _visitNode(node.body);
   }
 
   @override
   void visitFunctionDeclaration(FunctionDeclaration node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    safelyVisitTokenWithSuffix(node.externalKeyword, " ");
-    safelyVisitNodeWithSuffix(node.returnType, " ");
-    safelyVisitTokenWithSuffix(node.propertyKeyword, " ");
-    safelyVisitNode(node.name);
-    safelyVisitNode(node.functionExpression);
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    _visitToken(node.externalKeyword, suffix: ' ');
+    _visitNode(node.returnType, suffix: ' ');
+    _visitToken(node.propertyKeyword, suffix: ' ');
+    _visitNode(node.name);
+    _visitNode(node.functionExpression);
   }
 
   @override
   void visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
-    safelyVisitNode(node.functionDeclaration);
+    _visitNode(node.functionDeclaration);
   }
 
   @override
   void visitFunctionExpression(FunctionExpression node) {
-    safelyVisitNode(node.typeParameters);
-    safelyVisitNode(node.parameters);
-    if (node.body is! EmptyFunctionBody) {
-      sink.write(' ');
-    }
-    safelyVisitNode(node.body);
+    _visitNode(node.typeParameters);
+    _visitNode(node.parameters);
+    _visitFunctionBody(node.body);
   }
 
   @override
   void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
-    safelyVisitNode(node.function);
-    safelyVisitNode(node.typeArguments);
-    safelyVisitNode(node.argumentList);
+    _visitNode(node.function);
+    _visitNode(node.typeArguments);
+    _visitNode(node.argumentList);
   }
 
   @override
   void visitFunctionReference(FunctionReference node) {
-    safelyVisitNode(node.function);
-    safelyVisitNode(node.typeArguments);
+    _visitNode(node.function);
+    _visitNode(node.typeArguments);
   }
 
   @override
   void visitFunctionTypeAlias(FunctionTypeAlias node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    sink.write("typedef ");
-    safelyVisitNodeWithSuffix(node.returnType, " ");
-    safelyVisitNode(node.name);
-    safelyVisitNode(node.typeParameters);
-    safelyVisitNode(node.parameters);
-    sink.write(";");
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    sink.write('typedef ');
+    _visitNode(node.returnType, suffix: ' ');
+    _visitNode(node.name);
+    _visitNode(node.typeParameters);
+    _visitNode(node.parameters);
+    sink.write(';');
   }
 
   @override
   void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, ' ', ' ');
-    safelyVisitTokenWithSuffix(node.requiredKeyword, ' ');
-    safelyVisitTokenWithSuffix(node.covariantKeyword, ' ');
-    safelyVisitNodeWithSuffix(node.returnType, " ");
-    safelyVisitNode(node.identifier);
-    safelyVisitNode(node.typeParameters);
-    safelyVisitNode(node.parameters);
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    _visitToken(node.requiredKeyword, suffix: ' ');
+    _visitToken(node.covariantKeyword, suffix: ' ');
+    _visitNode(node.returnType, suffix: ' ');
+    _visitNode(node.identifier);
+    _visitNode(node.typeParameters);
+    _visitNode(node.parameters);
     if (node.question != null) {
       sink.write('?');
     }
@@ -653,10 +542,10 @@
 
   @override
   void visitGenericFunctionType(GenericFunctionType node) {
-    safelyVisitNode(node.returnType);
+    _visitNode(node.returnType);
     sink.write(' Function');
-    safelyVisitNode(node.typeParameters);
-    safelyVisitNode(node.parameters);
+    _visitNode(node.typeParameters);
+    _visitNode(node.parameters);
     if (node.question != null) {
       sink.write('?');
     }
@@ -664,75 +553,76 @@
 
   @override
   void visitGenericTypeAlias(GenericTypeAlias node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    sink.write("typedef ");
-    safelyVisitNode(node.name);
-    safelyVisitNode(node.typeParameters);
-    sink.write(" = ");
-    safelyVisitNode(node.type);
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    sink.write('typedef ');
+    _visitNode(node.name);
+    _visitNode(node.typeParameters);
+    sink.write(' = ');
+    _visitNode(node.type);
   }
 
   @override
   void visitHideCombinator(HideCombinator node) {
-    sink.write("hide ");
-    safelyVisitNodeListWithSeparator(node.hiddenNames, ", ");
+    sink.write('hide ');
+    _visitNodeList(node.hiddenNames, separator: ', ');
   }
 
   @override
   void visitIfElement(IfElement node) {
     sink.write('if (');
-    safelyVisitNode(node.condition);
+    _visitNode(node.condition);
     sink.write(') ');
-    safelyVisitNode(node.thenElement);
-    safelyVisitNodeWithPrefix(' else ', node.elseElement);
+    _visitNode(node.thenElement);
+    _visitNode(node.elseElement, prefix: ' else ');
   }
 
   @override
   void visitIfStatement(IfStatement node) {
-    sink.write("if (");
-    safelyVisitNode(node.condition);
-    sink.write(") ");
-    safelyVisitNode(node.thenStatement);
-    safelyVisitNodeWithPrefix(" else ", node.elseStatement);
+    sink.write('if (');
+    _visitNode(node.condition);
+    sink.write(') ');
+    _visitNode(node.thenStatement);
+    _visitNode(node.elseStatement, prefix: ' else ');
   }
 
   @override
   void visitImplementsClause(ImplementsClause node) {
-    sink.write("implements ");
-    safelyVisitNodeListWithSeparator(node.interfaces, ", ");
+    sink.write('implements ');
+    _visitNodeList(node.interfaces, separator: ', ');
   }
 
   @override
   void visitImportDirective(ImportDirective node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    sink.write("import ");
-    safelyVisitNode(node.uri);
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    sink.write('import ');
+    _visitNode(node.uri);
+    _visitNodeList(node.configurations, prefix: ' ', separator: ' ');
     if (node.deferredKeyword != null) {
-      sink.write(" deferred");
+      sink.write(' deferred');
     }
-    safelyVisitNodeWithPrefix(" as ", node.prefix);
-    safelyVisitNodeListWithSeparatorAndPrefix(" ", node.combinators, " ");
+    _visitNode(node.prefix, prefix: ' as ');
+    _visitNodeList(node.combinators, prefix: ' ', separator: ' ');
     sink.write(';');
   }
 
   @override
   void visitIndexExpression(IndexExpression node) {
     if (node.isCascaded) {
-      safelyVisitToken(node.period);
+      _visitToken(node.period);
     } else {
-      safelyVisitNode(node.target);
+      _visitNode(node.target);
     }
-    safelyVisitToken(node.question);
-    safelyVisitToken(node.leftBracket);
-    safelyVisitNode(node.index);
-    safelyVisitToken(node.rightBracket);
+    _visitToken(node.question);
+    _visitToken(node.leftBracket);
+    _visitNode(node.index);
+    _visitToken(node.rightBracket);
   }
 
   @override
   void visitInstanceCreationExpression(InstanceCreationExpression node) {
-    safelyVisitTokenWithSuffix(node.keyword, " ");
-    safelyVisitNode(node.constructorName);
-    safelyVisitNode(node.argumentList);
+    _visitToken(node.keyword, suffix: ' ');
+    _visitNode(node.constructorName);
+    _visitNode(node.argumentList);
   }
 
   @override
@@ -743,12 +633,12 @@
   @override
   void visitInterpolationExpression(InterpolationExpression node) {
     if (node.rightBracket != null) {
-      sink.write("\${");
-      safelyVisitNode(node.expression);
-      sink.write("}");
+      sink.write('\${');
+      _visitNode(node.expression);
+      sink.write('}');
     } else {
-      sink.write("\$");
-      safelyVisitNode(node.expression);
+      sink.write('\$');
+      _visitNode(node.expression);
     }
   }
 
@@ -759,32 +649,32 @@
 
   @override
   void visitIsExpression(IsExpression node) {
-    safelyVisitNode(node.expression);
+    _visitNode(node.expression);
     if (node.notOperator == null) {
-      sink.write(" is ");
+      sink.write(' is ');
     } else {
-      sink.write(" is! ");
+      sink.write(' is! ');
     }
-    safelyVisitNode(node.type);
+    _visitNode(node.type);
   }
 
   @override
   void visitLabel(Label node) {
-    safelyVisitNode(node.label);
-    sink.write(":");
+    _visitNode(node.label);
+    sink.write(':');
   }
 
   @override
   void visitLabeledStatement(LabeledStatement node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.labels, " ", " ");
-    safelyVisitNode(node.statement);
+    _visitNodeList(node.labels, separator: ' ', suffix: ' ');
+    _visitNode(node.statement);
   }
 
   @override
   void visitLibraryDirective(LibraryDirective node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    sink.write("library ");
-    safelyVisitNode(node.name);
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    sink.write('library ');
+    _visitNode(node.name);
     sink.write(';');
   }
 
@@ -795,34 +685,34 @@
 
   @override
   void visitListLiteral(ListLiteral node) {
-    safelyVisitTokenWithSuffix(node.constKeyword, ' ');
-    safelyVisitNode(node.typeArguments);
+    _visitToken(node.constKeyword, suffix: ' ');
+    _visitNode(node.typeArguments);
     sink.write('[');
-    safelyVisitNodeListWithSeparator(node.elements, ', ');
+    _visitNodeList(node.elements, separator: ', ');
     sink.write(']');
   }
 
   @override
   void visitMapLiteralEntry(MapLiteralEntry node) {
-    safelyVisitNode(node.key);
-    sink.write(" : ");
-    safelyVisitNode(node.value);
+    _visitNode(node.key);
+    sink.write(' : ');
+    _visitNode(node.value);
   }
 
   @override
   void visitMethodDeclaration(MethodDeclaration node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    safelyVisitTokenWithSuffix(node.externalKeyword, " ");
-    safelyVisitTokenWithSuffix(node.modifierKeyword, " ");
-    safelyVisitNodeWithSuffix(node.returnType, " ");
-    safelyVisitTokenWithSuffix(node.propertyKeyword, " ");
-    safelyVisitTokenWithSuffix(node.operatorKeyword, " ");
-    safelyVisitNode(node.name);
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    _visitToken(node.externalKeyword, suffix: ' ');
+    _visitToken(node.modifierKeyword, suffix: ' ');
+    _visitNode(node.returnType, suffix: ' ');
+    _visitToken(node.propertyKeyword, suffix: ' ');
+    _visitToken(node.operatorKeyword, suffix: ' ');
+    _visitNode(node.name);
     if (!node.isGetter) {
-      safelyVisitNode(node.typeParameters);
-      safelyVisitNode(node.parameters);
+      _visitNode(node.typeParameters);
+      _visitNode(node.parameters);
     }
-    safelyVisitFunctionWithPrefix(" ", node.body);
+    _visitFunctionBody(node.body);
   }
 
   @override
@@ -835,75 +725,75 @@
         sink.write(node.operator!.lexeme);
       }
     }
-    safelyVisitNode(node.methodName);
-    safelyVisitNode(node.typeArguments);
-    safelyVisitNode(node.argumentList);
+    _visitNode(node.methodName);
+    _visitNode(node.typeArguments);
+    _visitNode(node.argumentList);
   }
 
   @override
   void visitMixinDeclaration(MixinDeclaration node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    sink.write("mixin ");
-    safelyVisitNode(node.name);
-    safelyVisitNode(node.typeParameters);
-    safelyVisitNodeWithPrefix(" ", node.onClause);
-    safelyVisitNodeWithPrefix(" ", node.implementsClause);
-    sink.write(" {");
-    safelyVisitNodeListWithSeparator(node.members, " ");
-    sink.write("}");
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    sink.write('mixin ');
+    _visitNode(node.name);
+    _visitNode(node.typeParameters);
+    _visitNode(node.onClause, prefix: ' ');
+    _visitNode(node.implementsClause, prefix: ' ');
+    sink.write(' {');
+    _visitNodeList(node.members, separator: ' ');
+    sink.write('}');
   }
 
   @override
   void visitNamedExpression(NamedExpression node) {
-    safelyVisitNode(node.name);
-    safelyVisitNodeWithPrefix(" ", node.expression);
+    _visitNode(node.name);
+    _visitNode(node.expression, prefix: ' ');
   }
 
   @override
   void visitNativeClause(NativeClause node) {
-    sink.write("native ");
-    safelyVisitNode(node.name);
+    sink.write('native ');
+    _visitNode(node.name);
   }
 
   @override
   void visitNativeFunctionBody(NativeFunctionBody node) {
-    sink.write("native ");
-    safelyVisitNode(node.stringLiteral);
+    sink.write('native ');
+    _visitNode(node.stringLiteral);
     sink.write(';');
   }
 
   @override
   void visitNullLiteral(NullLiteral node) {
-    sink.write("null");
+    sink.write('null');
   }
 
   @override
   void visitOnClause(OnClause node) {
     sink.write('on ');
-    safelyVisitNodeListWithSeparator(node.superclassConstraints, ", ");
+    _visitNodeList(node.superclassConstraints, separator: ', ');
   }
 
   @override
   void visitParenthesizedExpression(ParenthesizedExpression node) {
     sink.write('(');
-    safelyVisitNode(node.expression);
+    _visitNode(node.expression);
     sink.write(')');
   }
 
   @override
   void visitPartDirective(PartDirective node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    sink.write("part ");
-    safelyVisitNode(node.uri);
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    sink.write('part ');
+    _visitNode(node.uri);
     sink.write(';');
   }
 
   @override
   void visitPartOfDirective(PartOfDirective node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    sink.write("part of ");
-    safelyVisitNode(node.libraryName);
-    safelyVisitNode(node.uri);
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    sink.write('part of ');
+    _visitNode(node.libraryName);
+    _visitNode(node.uri);
     sink.write(';');
   }
 
@@ -915,9 +805,9 @@
 
   @override
   void visitPrefixedIdentifier(PrefixedIdentifier node) {
-    safelyVisitNode(node.prefix);
+    _visitNode(node.prefix);
     sink.write('.');
-    safelyVisitNode(node.identifier);
+    _visitNode(node.identifier);
   }
 
   @override
@@ -931,34 +821,34 @@
     if (node.isCascaded) {
       sink.write(node.operator.lexeme);
     } else {
-      safelyVisitNode(node.target);
+      _visitNode(node.target);
       sink.write(node.operator.lexeme);
     }
-    safelyVisitNode(node.propertyName);
+    _visitNode(node.propertyName);
   }
 
   @override
   void visitRedirectingConstructorInvocation(
       RedirectingConstructorInvocation node) {
-    sink.write("this");
-    safelyVisitNodeWithPrefix(".", node.constructorName);
-    safelyVisitNode(node.argumentList);
+    sink.write('this');
+    _visitNode(node.constructorName, prefix: '.');
+    _visitNode(node.argumentList);
   }
 
   @override
   void visitRethrowExpression(RethrowExpression node) {
-    sink.write("rethrow");
+    sink.write('rethrow');
   }
 
   @override
   void visitReturnStatement(ReturnStatement node) {
     var expression = node.expression;
     if (expression == null) {
-      sink.write("return;");
+      sink.write('return;');
     } else {
-      sink.write("return ");
+      sink.write('return ');
       expression.accept(this);
-      sink.write(";");
+      sink.write(';');
     }
   }
 
@@ -969,30 +859,30 @@
 
   @override
   void visitSetOrMapLiteral(SetOrMapLiteral node) {
-    safelyVisitTokenWithSuffix(node.constKeyword, ' ');
-    safelyVisitNode(node.typeArguments);
+    _visitToken(node.constKeyword, suffix: ' ');
+    _visitNode(node.typeArguments);
     sink.write('{');
-    safelyVisitNodeListWithSeparator(node.elements, ', ');
+    _visitNodeList(node.elements, separator: ', ');
     sink.write('}');
   }
 
   @override
   void visitShowCombinator(ShowCombinator node) {
-    sink.write("show ");
-    safelyVisitNodeListWithSeparator(node.shownNames, ", ");
+    sink.write('show ');
+    _visitNodeList(node.shownNames, separator: ', ');
   }
 
   @override
   void visitSimpleFormalParameter(SimpleFormalParameter node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, ' ', ' ');
-    safelyVisitTokenWithSuffix(node.requiredKeyword, ' ');
-    safelyVisitTokenWithSuffix(node.covariantKeyword, ' ');
-    safelyVisitTokenWithSuffix(node.keyword, " ");
-    safelyVisitNode(node.type);
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    _visitToken(node.requiredKeyword, suffix: ' ');
+    _visitToken(node.covariantKeyword, suffix: ' ');
+    _visitToken(node.keyword, suffix: ' ');
+    _visitNode(node.type);
     if (node.type != null && node.identifier != null) {
       sink.write(' ');
     }
-    safelyVisitNode(node.identifier);
+    _visitNode(node.identifier);
   }
 
   @override
@@ -1008,58 +898,58 @@
   @override
   void visitSpreadElement(SpreadElement node) {
     sink.write(node.spreadOperator.lexeme);
-    safelyVisitNode(node.expression);
+    _visitNode(node.expression);
   }
 
   @override
   void visitStringInterpolation(StringInterpolation node) {
-    safelyVisitNodeList(node.elements);
+    _visitNodeList(node.elements);
   }
 
   @override
   void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
-    sink.write("super");
-    safelyVisitNodeWithPrefix(".", node.constructorName);
-    safelyVisitNode(node.argumentList);
+    sink.write('super');
+    _visitNode(node.constructorName, prefix: '.');
+    _visitNode(node.argumentList);
   }
 
   @override
   void visitSuperExpression(SuperExpression node) {
-    sink.write("super");
+    sink.write('super');
   }
 
   @override
   void visitSwitchCase(SwitchCase node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.labels, " ", " ");
-    sink.write("case ");
-    safelyVisitNode(node.expression);
-    sink.write(": ");
-    safelyVisitNodeListWithSeparator(node.statements, " ");
+    _visitNodeList(node.labels, separator: ' ', suffix: ' ');
+    sink.write('case ');
+    _visitNode(node.expression);
+    sink.write(': ');
+    _visitNodeList(node.statements, separator: ' ');
   }
 
   @override
   void visitSwitchDefault(SwitchDefault node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.labels, " ", " ");
-    sink.write("default: ");
-    safelyVisitNodeListWithSeparator(node.statements, " ");
+    _visitNodeList(node.labels, separator: ' ', suffix: ' ');
+    sink.write('default: ');
+    _visitNodeList(node.statements, separator: ' ');
   }
 
   @override
   void visitSwitchStatement(SwitchStatement node) {
-    sink.write("switch (");
-    safelyVisitNode(node.expression);
-    sink.write(") {");
-    safelyVisitNodeListWithSeparator(node.members, " ");
-    sink.write("}");
+    sink.write('switch (');
+    _visitNode(node.expression);
+    sink.write(') {');
+    _visitNodeList(node.members, separator: ' ');
+    sink.write('}');
   }
 
   @override
   void visitSymbolLiteral(SymbolLiteral node) {
-    sink.write("#");
+    sink.write('#');
     List<Token> components = node.components;
     for (int i = 0; i < components.length; i++) {
       if (i > 0) {
-        sink.write(".");
+        sink.write('.');
       }
       sink.write(components[i].lexeme);
     }
@@ -1067,45 +957,45 @@
 
   @override
   void visitThisExpression(ThisExpression node) {
-    sink.write("this");
+    sink.write('this');
   }
 
   @override
   void visitThrowExpression(ThrowExpression node) {
-    sink.write("throw ");
-    safelyVisitNode(node.expression);
+    sink.write('throw ');
+    _visitNode(node.expression);
   }
 
   @override
   void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
-    safelyVisitTokenWithSuffix(node.externalKeyword, " ");
-    safelyVisitNodeWithSuffix(node.variables, ";");
+    _visitToken(node.externalKeyword, suffix: ' ');
+    _visitNode(node.variables, suffix: ';');
   }
 
   @override
   void visitTryStatement(TryStatement node) {
-    sink.write("try ");
-    safelyVisitNode(node.body);
-    safelyVisitNodeListWithSeparatorAndPrefix(" ", node.catchClauses, " ");
-    safelyVisitNodeWithPrefix(" finally ", node.finallyBlock);
+    sink.write('try ');
+    _visitNode(node.body);
+    _visitNodeList(node.catchClauses, prefix: ' ', separator: ' ');
+    _visitNode(node.finallyBlock, prefix: ' finally ');
   }
 
   @override
   void visitTypeArgumentList(TypeArgumentList node) {
     sink.write('<');
-    safelyVisitNodeListWithSeparator(node.arguments, ", ");
+    _visitNodeList(node.arguments, separator: ', ');
     sink.write('>');
   }
 
   @override
   void visitTypeLiteral(TypeLiteral node) {
-    safelyVisitNode(node.typeName);
+    _visitNode(node.typeName);
   }
 
   @override
   void visitTypeName(TypeName node) {
-    safelyVisitNode(node.name);
-    safelyVisitNode(node.typeArguments);
+    _visitNode(node.name);
+    _visitNode(node.typeArguments);
     if (node.question != null) {
       sink.write('?');
     }
@@ -1113,69 +1003,114 @@
 
   @override
   void visitTypeParameter(TypeParameter node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
     // TODO (kallentu) : Clean up TypeParameterImpl casting once variance is
     // added to the interface.
     var varianceKeyword = (node as TypeParameterImpl).varianceKeyword;
     if (varianceKeyword != null) {
       sink.write(varianceKeyword.lexeme + ' ');
     }
-    safelyVisitNode(node.name);
-    safelyVisitNodeWithPrefix(" extends ", node.bound);
+    _visitNode(node.name);
+    _visitNode(node.bound, prefix: ' extends ');
   }
 
   @override
   void visitTypeParameterList(TypeParameterList node) {
     sink.write('<');
-    safelyVisitNodeListWithSeparator(node.typeParameters, ", ");
+    _visitNodeList(node.typeParameters, separator: ', ');
     sink.write('>');
   }
 
   @override
   void visitVariableDeclaration(VariableDeclaration node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    safelyVisitNode(node.name);
-    safelyVisitNodeWithPrefix(" = ", node.initializer);
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    _visitNode(node.name);
+    _visitNode(node.initializer, prefix: ' = ');
   }
 
   @override
   void visitVariableDeclarationList(VariableDeclarationList node) {
-    safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
-    safelyVisitTokenWithSuffix(node.lateKeyword, " ");
-    safelyVisitTokenWithSuffix(node.keyword, " ");
-    safelyVisitNodeWithSuffix(node.type, " ");
-    safelyVisitNodeListWithSeparator(node.variables, ", ");
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    _visitToken(node.lateKeyword, suffix: ' ');
+    _visitToken(node.keyword, suffix: ' ');
+    _visitNode(node.type, suffix: ' ');
+    _visitNodeList(node.variables, separator: ', ');
   }
 
   @override
   void visitVariableDeclarationStatement(VariableDeclarationStatement node) {
-    safelyVisitNode(node.variables);
-    sink.write(";");
+    _visitNode(node.variables);
+    sink.write(';');
   }
 
   @override
   void visitWhileStatement(WhileStatement node) {
-    sink.write("while (");
-    safelyVisitNode(node.condition);
-    sink.write(") ");
-    safelyVisitNode(node.body);
+    sink.write('while (');
+    _visitNode(node.condition);
+    sink.write(') ');
+    _visitNode(node.body);
   }
 
   @override
   void visitWithClause(WithClause node) {
-    sink.write("with ");
-    safelyVisitNodeListWithSeparator(node.mixinTypes, ", ");
+    sink.write('with ');
+    _visitNodeList(node.mixinTypes, separator: ', ');
   }
 
   @override
   void visitYieldStatement(YieldStatement node) {
     if (node.star != null) {
-      sink.write("yield* ");
+      sink.write('yield* ');
     } else {
-      sink.write("yield ");
+      sink.write('yield ');
     }
-    safelyVisitNode(node.expression);
-    sink.write(";");
+    _visitNode(node.expression);
+    sink.write(';');
+  }
+
+  /// Visit the given function [body], printing a prefix before if the body
+  /// is not empty.
+  void _visitFunctionBody(FunctionBody body) {
+    if (body is! EmptyFunctionBody) {
+      sink.write(' ');
+    }
+    _visitNode(body);
+  }
+
+  /// Print the given [node], printing the [prefix] before the node,
+  /// and [suffix] after the node, if it is non-`null`.
+  void _visitNode(AstNode? node, {String prefix = '', String suffix = ''}) {
+    if (node != null) {
+      sink.write(prefix);
+      node.accept(this);
+      sink.write(suffix);
+    }
+  }
+
+  /// Print a list of [nodes], separated by the given [separator]; if the list
+  /// is not empty print [prefix] before the first node, and [suffix] after
+  /// the last node.
+  void _visitNodeList(List<AstNode> nodes,
+      {String prefix = '', String separator = '', String suffix = ''}) {
+    var length = nodes.length;
+    if (length > 0) {
+      sink.write(prefix);
+      for (int i = 0; i < length; i++) {
+        if (i > 0) {
+          sink.write(separator);
+        }
+        nodes[i].accept(this);
+      }
+      sink.write(suffix);
+    }
+  }
+
+  /// Print the given [token].
+  void _visitToken(Token? token, {String suffix = ''}) {
+    if (token != null) {
+      sink.write(token.lexeme);
+      sink.write(suffix);
+    }
   }
 
   void _writeOperand(Expression node, Expression operand) {
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 531e95f..99c960c 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -983,6 +983,22 @@
   @override
   late Source librarySource;
 
+  /// If this unit has macro-generated elements, and is created in the
+  /// environment where the file content is available (e.g. interactive
+  /// analysis, and not batch analysis), then this is a combination of the
+  /// user-written code and macro-generated declarations.
+  ///
+  /// For elements created from the user-written code [Element.nameOffset]s
+  /// are offsets in the user-written code.
+  ///
+  /// For macro-generated elements [Element.nameOffset]s are offsets in the
+  /// combined file containing both user-written and generated code.
+  String? macroGeneratedContent;
+
+  /// If this unit has macro-generated elements, information about each one
+  /// is stored here, so that it can be combined to [macroGeneratedContent].
+  List<MacroGenerationData>? macroGenerationDataList;
+
   /// A list containing all of the top-level accessors (getters and setters)
   /// contained in this compilation unit.
   List<PropertyAccessorElement> _accessors = const [];
@@ -4175,18 +4191,30 @@
 
   /// The code that was produced by the macro. It is used to compose full
   /// code of a unit to display to the user, so that new declarations are
-  /// added to the unit or existing classes.
+  /// added to the end of the unit or existing classes.
   ///
   /// When a class is generated, its code might have some members, or might
   /// be empty, and new elements might be macro-generated into it.
   final String code;
 
-  /// When we build elements from macro-produced code, we remember informative
-  /// data, such as offsets - to store it into bytes. This field is set to
-  /// an empty list when reading from bytes.
+  /// Informative data derived from the [code], such as offsets.
   final Uint8List informative;
 
-  MacroGenerationData(this.id, this.code, this.informative);
+  /// If this element is macro-generated into a class declaration, this is
+  /// the index of this class declaration in the unit.
+  final int? classDeclarationIndex;
+
+  /// The offset in [CompilationUnitElementImpl.macroGeneratedContent],
+  /// where the [code] is located. This offset depends on the informative
+  /// data, as any other offset.
+  int offset = 0;
+
+  MacroGenerationData({
+    required this.id,
+    required this.code,
+    required this.informative,
+    required this.classDeclarationIndex,
+  });
 }
 
 /// A concrete implementation of a [MethodElement].
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index d52742b..26f8a6d 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -31,6 +31,7 @@
 import 'package:analyzer/src/summary/format.dart';
 import 'package:analyzer/src/summary/idl.dart';
 import 'package:analyzer/src/summary2/bundle_reader.dart';
+import 'package:analyzer/src/summary2/informative_data.dart';
 import 'package:analyzer/src/summary2/link.dart' as link2;
 import 'package:analyzer/src/summary2/linked_element_factory.dart';
 import 'package:analyzer/src/summary2/reference.dart';
@@ -834,12 +835,15 @@
       var resolutionData = byteStore.get(resolutionKey, cycle.signature);
       var resolutionBytes = resolutionData?.bytes;
 
-      var unitsInformativeBytes = <Uri, Uint8List>{};
+      var unitsInformativeData = <Uri, InformativeUnitData>{};
       for (var library in cycle.libraries) {
         for (var file in library.libraryFiles) {
           var informativeBytes = file.informativeBytes;
           if (informativeBytes != null) {
-            unitsInformativeBytes[file.uri] = informativeBytes;
+            unitsInformativeData[file.uri] = InformativeUnitData(
+              content: file.getContentWithSameDigest(),
+              bytes: informativeBytes,
+            );
           }
         }
       }
@@ -909,7 +913,7 @@
         elementFactory.addBundle(
           BundleReader(
             elementFactory: elementFactory,
-            unitsInformativeBytes: unitsInformativeBytes,
+            unitsInformativeData: unitsInformativeData,
             resolutionBytes: resolutionBytes as Uint8List,
           ),
         );
diff --git a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
index 840b7b8..7882635 100644
--- a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
@@ -78,7 +78,7 @@
       _setRhsContext(node, leftType!, operator, right);
     }
 
-    var flow = _resolver.flowAnalysis?.flow;
+    var flow = _resolver.flowAnalysis.flow;
     if (flow != null && isIfNull) {
       flow.ifNullExpression_rightBegin(left, node.readType!);
     }
@@ -294,7 +294,6 @@
 
   void checkFinalAlreadyAssigned(Expression left) {
     var flowAnalysis = _resolver.flowAnalysis;
-    if (flowAnalysis == null) return;
 
     var flow = flowAnalysis.flow;
     if (flow == null) return;
diff --git a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
index abb22ed..d569e12 100644
--- a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
@@ -104,7 +104,7 @@
     left.accept(_resolver);
     left = node.leftOperand;
 
-    var flow = _resolver.flowAnalysis?.flow;
+    var flow = _resolver.flowAnalysis.flow;
     var leftExtensionOverride = left is ExtensionOverride;
     if (!leftExtensionOverride) {
       flow?.equalityOp_rightBegin(left, left.typeOrThrow);
@@ -113,7 +113,7 @@
     var right = node.rightOperand;
     right.accept(_resolver);
     right = node.rightOperand;
-    var whyNotPromoted = _resolver.flowAnalysis?.flow?.whyNotPromoted(right);
+    var whyNotPromoted = _resolver.flowAnalysis.flow?.whyNotPromoted(right);
 
     if (!leftExtensionOverride) {
       flow?.equalityOp_end(node, right, right.typeOrThrow, notEqual: notEqual);
@@ -132,7 +132,7 @@
   void _resolveIfNull(BinaryExpressionImpl node) {
     var left = node.leftOperand;
     var right = node.rightOperand;
-    var flow = _resolver.flowAnalysis?.flow;
+    var flow = _resolver.flowAnalysis.flow;
 
     var leftContextType = InferenceContext.getContext(node);
     if (leftContextType != null && _isNonNullableByDefault) {
@@ -168,7 +168,7 @@
   void _resolveLogicalAnd(BinaryExpressionImpl node) {
     var left = node.leftOperand;
     var right = node.rightOperand;
-    var flow = _resolver.flowAnalysis?.flow;
+    var flow = _resolver.flowAnalysis.flow;
 
     InferenceContext.setType(left, _typeProvider.boolType);
     InferenceContext.setType(right, _typeProvider.boolType);
@@ -176,7 +176,7 @@
     flow?.logicalBinaryOp_begin();
     left.accept(_resolver);
     left = node.leftOperand;
-    var leftWhyNotPromoted = _resolver.flowAnalysis?.flow?.whyNotPromoted(left);
+    var leftWhyNotPromoted = _resolver.flowAnalysis.flow?.whyNotPromoted(left);
 
     flow?.logicalBinaryOp_rightBegin(left, node, isAnd: true);
     _resolver.checkUnreachableNode(right);
@@ -184,7 +184,7 @@
     right.accept(_resolver);
     right = node.rightOperand;
     var rightWhyNotPromoted =
-        _resolver.flowAnalysis!.flow?.whyNotPromoted(right);
+        _resolver.flowAnalysis.flow?.whyNotPromoted(right);
 
     _resolver.nullSafetyDeadCodeVerifier.flowEnd(right);
     flow?.logicalBinaryOp_end(node, right, isAnd: true);
@@ -198,7 +198,7 @@
   void _resolveLogicalOr(BinaryExpressionImpl node) {
     var left = node.leftOperand;
     var right = node.rightOperand;
-    var flow = _resolver.flowAnalysis?.flow;
+    var flow = _resolver.flowAnalysis.flow;
 
     InferenceContext.setType(left, _typeProvider.boolType);
     InferenceContext.setType(right, _typeProvider.boolType);
@@ -206,7 +206,7 @@
     flow?.logicalBinaryOp_begin();
     left.accept(_resolver);
     left = node.leftOperand;
-    var leftWhyNotPromoted = _resolver.flowAnalysis?.flow?.whyNotPromoted(left);
+    var leftWhyNotPromoted = _resolver.flowAnalysis.flow?.whyNotPromoted(left);
 
     flow?.logicalBinaryOp_rightBegin(left, node, isAnd: false);
     _resolver.checkUnreachableNode(right);
@@ -214,7 +214,7 @@
     right.accept(_resolver);
     right = node.rightOperand;
     var rightWhyNotPromoted =
-        _resolver.flowAnalysis?.flow?.whyNotPromoted(right);
+        _resolver.flowAnalysis.flow?.whyNotPromoted(right);
 
     _resolver.nullSafetyDeadCodeVerifier.flowEnd(right);
     flow?.logicalBinaryOp_end(node, right, isAnd: false);
@@ -264,7 +264,7 @@
 
     right.accept(_resolver);
     right = node.rightOperand;
-    var whyNotPromoted = _resolver.flowAnalysis?.flow?.whyNotPromoted(right);
+    var whyNotPromoted = _resolver.flowAnalysis.flow?.whyNotPromoted(right);
 
     _resolveUserDefinableType(node);
     _resolver.checkForArgumentTypeNotAssignableForArgument(right,
diff --git a/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
index 04403ce..beb8e4f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
@@ -130,20 +130,20 @@
     }
 
     if (loopVariable != null) {
-      _resolver.flowAnalysis?.flow
+      _resolver.flowAnalysis.flow
           ?.declare(loopVariable.declaredElement!, true);
     }
 
-    _resolver.flowAnalysis?.flow?.forEach_bodyBegin(node);
+    _resolver.flowAnalysis.flow?.forEach_bodyBegin(node);
     if (identifierElement is PromotableElement &&
         forEachParts is ForEachPartsWithIdentifier) {
-      _resolver.flowAnalysis?.flow?.write(forEachParts, identifierElement,
+      _resolver.flowAnalysis.flow?.write(forEachParts, identifierElement,
           elementType ?? DynamicTypeImpl.instance, null);
     }
 
     body.accept(_resolver);
 
-    _resolver.flowAnalysis?.flow?.forEach_end();
+    _resolver.flowAnalysis.flow?.forEach_end();
   }
 
   void _forParts(AstNode node, ForParts forParts, AstNode body) {
@@ -153,7 +153,7 @@
       forParts.initialization?.accept(_resolver);
     }
 
-    _resolver.flowAnalysis?.for_conditionBegin(node);
+    _resolver.flowAnalysis.for_conditionBegin(node);
 
     var condition = forParts.condition;
     if (condition != null) {
@@ -161,17 +161,17 @@
       condition.accept(_resolver);
       condition = forParts.condition!;
       var whyNotPromoted =
-          _resolver.flowAnalysis?.flow?.whyNotPromoted(condition);
+          _resolver.flowAnalysis.flow?.whyNotPromoted(condition);
       _resolver.boolExpressionVerifier
           .checkForNonBoolCondition(condition, whyNotPromoted: whyNotPromoted);
     }
 
-    _resolver.flowAnalysis?.for_bodyBegin(node, condition);
+    _resolver.flowAnalysis.for_bodyBegin(node, condition);
     body.accept(_resolver);
 
-    _resolver.flowAnalysis?.flow?.for_updaterBegin();
+    _resolver.flowAnalysis.flow?.for_updaterBegin();
     forParts.updaters.accept(_resolver);
 
-    _resolver.flowAnalysis?.flow?.for_end();
+    _resolver.flowAnalysis.flow?.for_end();
   }
 }
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
index 2ea42d8..599eda3 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
@@ -38,8 +38,8 @@
     bool isFunctionDeclaration = parent is FunctionDeclaration;
     var body = node.body;
 
-    if (_resolver.flowAnalysis!.flow != null && !isFunctionDeclaration) {
-      _resolver.flowAnalysis!
+    if (_resolver.flowAnalysis.flow != null && !isFunctionDeclaration) {
+      _resolver.flowAnalysis
           .executableDeclaration_enter(node, node.parameters, true);
     }
 
@@ -63,14 +63,14 @@
     }
     _resolve2(node);
 
-    if (_resolver.flowAnalysis!.flow != null && !isFunctionDeclaration) {
+    if (_resolver.flowAnalysis.flow != null && !isFunctionDeclaration) {
       var bodyContext = BodyInferenceContext.of(node.body);
       _resolver.checkForBodyMayCompleteNormally(
         returnType: bodyContext?.contextType,
         body: body,
         errorNode: body,
       );
-      _resolver.flowAnalysis!.flow?.functionExpression_end();
+      _resolver.flowAnalysis.flow?.functionExpression_end();
       _resolver.nullSafetyDeadCodeVerifier.flowEnd(node);
     }
   }
diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
index a73cf65..f665fe8f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
@@ -282,7 +282,7 @@
 
     expression.staticType = type;
     if (_typeSystem.isBottom(type)) {
-      _resolver.flowAnalysis?.flow?.handleExit();
+      _resolver.flowAnalysis.flow?.handleExit();
     }
   }
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
index d6267db..d5d7b50 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -194,7 +194,7 @@
 
     expression.staticType = type;
     if (_resolver.typeSystem.isBottom(type)) {
-      _resolver.flowAnalysis?.flow?.handleExit();
+      _resolver.flowAnalysis.flow?.handleExit();
     }
   }
 
@@ -826,7 +826,7 @@
           node.methodName,
         );
       }
-      _resolver.flowAnalysis?.flow?.propertyGet(
+      _resolver.flowAnalysis.flow?.propertyGet(
           functionExpression,
           target,
           node.methodName.name,
@@ -843,7 +843,7 @@
     NodeReplacer.replace(node, invocation);
     node.setProperty(_rewriteResultKey, invocation);
     InferenceContext.setTypeFromNode(invocation, node);
-    _resolver.flowAnalysis?.transferTestData(node, invocation);
+    _resolver.flowAnalysis.transferTestData(node, invocation);
   }
 
   void _setDynamicResolution(MethodInvocationImpl node,
diff --git a/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart
index c09803d..2927a58 100644
--- a/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart
@@ -173,7 +173,7 @@
       if (operand is SimpleIdentifier) {
         var element = operand.staticElement;
         if (element is PromotableElement) {
-          _resolver.flowAnalysis?.flow
+          _resolver.flowAnalysis.flow
               ?.write(node, element, operatorReturnType, null);
         }
       }
@@ -213,6 +213,6 @@
     _inferenceHelper.recordStaticType(node, type);
 
     _resolver.nullShortingTermination(node);
-    _resolver.flowAnalysis?.flow?.nonNullAssert_end(operand);
+    _resolver.flowAnalysis.flow?.nonNullAssert_end(operand);
   }
 }
diff --git a/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart
index 6e787e6..4f5a2d4 100644
--- a/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart
@@ -210,7 +210,7 @@
         if (operand is SimpleIdentifier) {
           var element = operand.staticElement;
           if (element is PromotableElement) {
-            _resolver.flowAnalysis?.flow
+            _resolver.flowAnalysis.flow
                 ?.write(node, element, staticType, null);
           }
         }
@@ -226,13 +226,13 @@
 
     operand.accept(_resolver);
     operand = node.operand;
-    var whyNotPromoted = _resolver.flowAnalysis?.flow?.whyNotPromoted(operand);
+    var whyNotPromoted = _resolver.flowAnalysis.flow?.whyNotPromoted(operand);
 
     _resolver.boolExpressionVerifier.checkForNonBoolNegationExpression(operand,
         whyNotPromoted: whyNotPromoted);
 
     _recordStaticType(node, _typeProvider.boolType);
 
-    _resolver.flowAnalysis?.flow?.logicalNot_end(node, operand);
+    _resolver.flowAnalysis.flow?.logicalNot_end(node, operand);
   }
 }
diff --git a/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
index b8dae44..84c1ec2 100644
--- a/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
@@ -205,7 +205,7 @@
       readElementRequested = _resolver.toLegacyElement(readLookup?.requested);
       if (readElementRequested is PropertyAccessorElement &&
           !readElementRequested.isStatic) {
-        _resolver.flowAnalysis?.flow?.thisOrSuperPropertyGet(node, node.name,
+        _resolver.flowAnalysis.flow?.thisOrSuperPropertyGet(node, node.name,
             readElementRequested, readElementRequested.returnType);
       }
       _resolver.checkReadOfNotAssignedLocalVariable(node, readElementRequested);
@@ -376,7 +376,7 @@
       nameErrorEntity: propertyName,
     );
 
-    _resolver.flowAnalysis?.flow?.propertyGet(
+    _resolver.flowAnalysis.flow?.propertyGet(
         node,
         target,
         propertyName.name,
@@ -656,7 +656,7 @@
             );
           }
         }
-        _resolver.flowAnalysis?.flow?.propertyGet(
+        _resolver.flowAnalysis.flow?.propertyGet(
             node,
             target,
             propertyName.name,
diff --git a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
index 5fa2a91..2344d90 100644
--- a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
@@ -129,7 +129,7 @@
       }
 
       List<DiagnosticMessage> messages = [];
-      var flow = _resolver.flowAnalysis?.flow;
+      var flow = _resolver.flowAnalysis.flow;
       if (flow != null) {
         if (receiver != null) {
           messages = _resolver.computeWhyNotPromotedMessages(
diff --git a/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
index 63d610e..1e41759 100644
--- a/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
@@ -45,24 +45,24 @@
 
     InferenceContext.setTypeFromNode(initializer, node);
     if (isTopLevel) {
-      _resolver.flowAnalysis?.topLevelDeclaration_enter(node, null);
+      _resolver.flowAnalysis.topLevelDeclaration_enter(node, null);
     } else if (element.isLate) {
-      _resolver.flowAnalysis?.flow?.lateInitializer_begin(node);
+      _resolver.flowAnalysis.flow?.lateInitializer_begin(node);
     }
 
     initializer.accept(_resolver);
     initializer = node.initializer!;
     var whyNotPromoted =
-        _resolver.flowAnalysis?.flow?.whyNotPromoted(initializer);
+        _resolver.flowAnalysis.flow?.whyNotPromoted(initializer);
 
     if (parent.type == null) {
       _setInferredType(element, initializer.typeOrThrow);
     }
 
     if (isTopLevel) {
-      _resolver.flowAnalysis?.topLevelDeclaration_exit();
+      _resolver.flowAnalysis.topLevelDeclaration_exit();
     } else if (element.isLate) {
-      _resolver.flowAnalysis?.flow?.lateInitializer_end();
+      _resolver.flowAnalysis.flow?.lateInitializer_end();
     }
 
     // Initializers of top-level variables and fields are already included
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index fd77bb3..dce95dc 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -13014,9 +13014,6 @@
           hasPublishedDocs: true,
           uniqueName: 'UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE');
 
-  /**
-   * No parameters.
-   */
   // #### Description
   //
   // The analyzer produces this diagnostic when an expression whose type is
@@ -13072,16 +13069,6 @@
   //   }
   // }
   // ```
-  static const CompileTimeErrorCode UNCHECKED_USE_OF_NULLABLE_VALUE =
-      CompileTimeErrorCode(
-          'UNCHECKED_USE_OF_NULLABLE_VALUE',
-          "An expression whose value can be 'null' must be null-checked before "
-              "it can be dereferenced.",
-          correction:
-              "Try checking that the value isn't 'null' before dereferencing "
-              "it.",
-          hasPublishedDocs: true);
-
   static const CompileTimeErrorCode
       UNCHECKED_USE_OF_NULLABLE_VALUE_AS_CONDITION = CompileTimeErrorCode(
           'UNCHECKED_USE_OF_NULLABLE_VALUE',
diff --git a/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart b/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart
index 3c5caba..35834b5 100644
--- a/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart
+++ b/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart
@@ -78,7 +78,7 @@
     List<DiagnosticMessage>? messages;
     if (errorNode is Expression) {
       messages = _resolver.computeWhyNotPromotedMessages(
-          errorNode, _resolver.flowAnalysis?.flow?.whyNotPromoted(errorNode)());
+          errorNode, _resolver.flowAnalysis.flow?.whyNotPromoted(errorNode)());
     }
     report(errorCode, errorNode, receiverType, messages: messages);
     return true;
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index d776e90..9fad340 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -105,7 +105,7 @@
   void popFunctionBodyContext(FunctionBody node) {
     var context = _bodyContexts.removeLast();
 
-    var flow = _resolver.flowAnalysis?.flow;
+    var flow = _resolver.flowAnalysis.flow;
 
     var resultType = context.computeInferredReturnType(
       endOfBlockIsReachable: flow == null || flow.isReachable,
@@ -261,7 +261,7 @@
   /// Otherwise `null`.
   DartType? _thisType;
 
-  final FlowAnalysisHelper? flowAnalysis;
+  final FlowAnalysisHelper flowAnalysis;
 
   /// A comment before a function should be resolved in the context of the
   /// function. But when we incrementally resolve a comment, we don't want to
@@ -316,7 +316,7 @@
       TypeProvider typeProvider,
       AnalysisErrorListener errorListener,
       {FeatureSet? featureSet,
-      FlowAnalysisHelper? flowAnalysisHelper})
+      required FlowAnalysisHelper flowAnalysisHelper})
       : this._(
             inheritanceManager,
             definingLibrary,
@@ -424,11 +424,7 @@
 
   /// Return the object providing promoted or declared types of variables.
   LocalVariableTypeProvider get localVariableTypeProvider {
-    if (flowAnalysis != null) {
-      return flowAnalysis!.localVariableTypeProvider;
-    } else {
-      return const NonPromotingLocalVariableTypeProvider();
-    }
+    return flowAnalysis.localVariableTypeProvider;
   }
 
   NullabilitySuffix get noneOrStarSuffix {
@@ -464,7 +460,7 @@
     for (int i = 0; i < arguments.length; i++) {
       checkForArgumentTypeNotAssignableForArgument(arguments[i],
           whyNotPromoted:
-              flowAnalysis?.flow == null ? null : whyNotPromotedList[i]);
+              flowAnalysis.flow == null ? null : whyNotPromotedList[i]);
     }
   }
 
@@ -474,7 +470,7 @@
     required AstNode errorNode,
   }) {
     if (!_isNonNullableByDefault) return;
-    if (!flowAnalysis!.flow!.isReachable) {
+    if (!flowAnalysis.flow!.isReachable) {
       return;
     }
 
@@ -512,7 +508,7 @@
     SimpleIdentifier node,
     Element? element,
   ) {
-    if (flowAnalysis?.flow == null) {
+    if (flowAnalysis.flow == null) {
       return;
     }
 
@@ -521,9 +517,9 @@
     }
 
     if (element is VariableElement) {
-      var assigned = flowAnalysis!
-          .isDefinitelyAssigned(node, element as PromotableElement);
-      var unassigned = flowAnalysis!.isDefinitelyUnassigned(node, element);
+      var assigned =
+          flowAnalysis.isDefinitelyAssigned(node, element as PromotableElement);
+      var unassigned = flowAnalysis.isDefinitelyUnassigned(node, element);
 
       if (element.isLate) {
         if (unassigned) {
@@ -571,11 +567,11 @@
     if (whyNotPromoted != null) {
       for (var entry in whyNotPromoted.entries) {
         var whyNotPromotedVisitor = _WhyNotPromotedVisitor(
-            source, errorEntity, flowAnalysis!.dataForTesting);
+            source, errorEntity, flowAnalysis.dataForTesting);
         if (typeSystem.isPotentiallyNullable(entry.key)) continue;
         var message = entry.value.accept(whyNotPromotedVisitor);
         if (message != null) {
-          if (flowAnalysis!.dataForTesting != null) {
+          if (flowAnalysis.dataForTesting != null) {
             var nonPromotionReasonText = entry.value.shortName;
             var args = <String>[];
             if (whyNotPromotedVisitor.propertyReference != null) {
@@ -589,7 +585,7 @@
             if (args.isNotEmpty) {
               nonPromotionReasonText += '(${args.join(', ')})';
             }
-            flowAnalysis!.dataForTesting!.nonPromotionReasons[errorEntity] =
+            flowAnalysis.dataForTesting!.nonPromotionReasons[errorEntity] =
                 nonPromotionReasonText;
           }
           messages = [message];
@@ -630,7 +626,7 @@
     if (identical(_unfinishedNullShorts.last, node)) {
       do {
         _unfinishedNullShorts.removeLast();
-        flowAnalysis!.flow!.nullAwareAccess_end();
+        flowAnalysis.flow!.nullAwareAccess_end();
       } while (identical(_unfinishedNullShorts.last, node));
       if (node is! CascadeExpression && !discardType) {
         node.staticType = typeSystem.makeNullable(node.staticType as TypeImpl);
@@ -713,7 +709,7 @@
 
       InferenceContext.setType(node.index, result.indexContextType);
       node.index.accept(this);
-      var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(node.index);
+      var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(node.index);
       checkIndexExpressionIndex(
         node.index,
         readElement: result.readElement as ExecutableElement?,
@@ -844,7 +840,7 @@
 
   void startNullAwareIndexExpression(IndexExpression node) {
     if (_migratableAstInfoProvider.isIndexExpressionNullAware(node)) {
-      var flow = flowAnalysis?.flow;
+      var flow = flowAnalysis.flow;
       if (flow != null) {
         flow.nullAwareAccess_rightBegin(node.target,
             node.realTarget.staticType ?? typeProvider.dynamicType);
@@ -855,7 +851,7 @@
 
   void startNullAwarePropertyAccess(PropertyAccess node) {
     if (_migratableAstInfoProvider.isPropertyAccessNullAware(node)) {
-      var flow = flowAnalysis?.flow;
+      var flow = flowAnalysis.flow;
       if (flow != null) {
         var target = node.target;
         if (target is SimpleIdentifier &&
@@ -970,7 +966,7 @@
     }
     checkUnreachableNode(node);
     int length = arguments.length;
-    var flow = flowAnalysis?.flow;
+    var flow = flowAnalysis.flow;
     for (var i = 0; i < length; i++) {
       if (isIdentical && length > 1 && i == 1) {
         var firstArg = arguments[0];
@@ -993,37 +989,37 @@
   @override
   void visitAsExpression(AsExpression node) {
     super.visitAsExpression(node);
-    flowAnalysis?.asExpression(node);
+    flowAnalysis.asExpression(node);
   }
 
   @override
   void visitAssertInitializer(AssertInitializer node) {
     InferenceContext.setType(node.condition, typeProvider.boolType);
-    flowAnalysis?.flow?.assert_begin();
+    flowAnalysis.flow?.assert_begin();
     node.condition.accept(this);
     boolExpressionVerifier.checkForNonBoolExpression(
       node.condition,
       errorCode: CompileTimeErrorCode.NON_BOOL_EXPRESSION,
-      whyNotPromoted: flowAnalysis?.flow?.whyNotPromoted(node.condition),
+      whyNotPromoted: flowAnalysis.flow?.whyNotPromoted(node.condition),
     );
-    flowAnalysis?.flow?.assert_afterCondition(node.condition);
+    flowAnalysis.flow?.assert_afterCondition(node.condition);
     node.message?.accept(this);
-    flowAnalysis?.flow?.assert_end();
+    flowAnalysis.flow?.assert_end();
   }
 
   @override
   void visitAssertStatement(AssertStatement node) {
     InferenceContext.setType(node.condition, typeProvider.boolType);
-    flowAnalysis?.flow?.assert_begin();
+    flowAnalysis.flow?.assert_begin();
     node.condition.accept(this);
     boolExpressionVerifier.checkForNonBoolExpression(
       node.condition,
       errorCode: CompileTimeErrorCode.NON_BOOL_EXPRESSION,
-      whyNotPromoted: flowAnalysis?.flow?.whyNotPromoted(node.condition),
+      whyNotPromoted: flowAnalysis.flow?.whyNotPromoted(node.condition),
     );
-    flowAnalysis?.flow?.assert_afterCondition(node.condition);
+    flowAnalysis.flow?.assert_afterCondition(node.condition);
     node.message?.accept(this);
-    flowAnalysis?.flow?.assert_end();
+    flowAnalysis.flow?.assert_end();
   }
 
   @override
@@ -1060,7 +1056,7 @@
 
   @override
   void visitBooleanLiteral(BooleanLiteral node) {
-    flowAnalysis?.flow?.booleanLiteral(node, node.value);
+    flowAnalysis.flow?.booleanLiteral(node, node.value);
     super.visitBooleanLiteral(node);
   }
 
@@ -1073,7 +1069,7 @@
     checkUnreachableNode(node);
     node.accept(elementResolver);
     node.accept(typeAnalyzer);
-    flowAnalysis?.breakStatement(node);
+    flowAnalysis.breakStatement(node);
   }
 
   @override
@@ -1081,8 +1077,8 @@
     InferenceContext.setTypeFromNode(node.target, node);
     node.target.accept(this);
 
-    if (node.isNullAware && flowAnalysis != null) {
-      flowAnalysis!.flow!.nullAwareAccess_rightBegin(
+    if (node.isNullAware) {
+      flowAnalysis.flow!.nullAwareAccess_rightBegin(
           node.target, node.target.staticType ?? typeProvider.dynamicType);
       _unfinishedNullShorts.add(node.nullShortingTermination);
     }
@@ -1148,13 +1144,13 @@
   @override
   void visitConditionalExpression(ConditionalExpression node) {
     Expression condition = node.condition;
-    var flow = flowAnalysis?.flow;
+    var flow = flowAnalysis.flow;
     flow?.conditional_conditionBegin();
 
     // TODO(scheglov) Do we need these checks for null?
     condition.accept(this);
     condition = node.condition;
-    var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(condition);
+    var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(condition);
     boolExpressionVerifier.checkForNonBoolCondition(condition,
         whyNotPromoted: whyNotPromoted);
 
@@ -1193,8 +1189,8 @@
 
   @override
   void visitConstructorDeclaration(ConstructorDeclaration node) {
-    flowAnalysis!.topLevelDeclaration_enter(node, node.parameters);
-    flowAnalysis!.executableDeclaration_enter(node, node.parameters, false);
+    flowAnalysis.topLevelDeclaration_enter(node, node.parameters);
+    flowAnalysis.executableDeclaration_enter(node, node.parameters, false);
 
     var returnType = node.declaredElement!.type.returnType;
     InferenceContext.setType(node.body, returnType);
@@ -1218,8 +1214,8 @@
         errorNode: node,
       );
     }
-    flowAnalysis!.executableDeclaration_exit(node.body, false);
-    flowAnalysis!.topLevelDeclaration_exit();
+    flowAnalysis.executableDeclaration_exit(node.body, false);
+    flowAnalysis.topLevelDeclaration_exit();
     nullSafetyDeadCodeVerifier.flowEnd(node);
   }
 
@@ -1232,7 +1228,7 @@
     var fieldElement = enclosingClass!.getField(node.fieldName.name);
     InferenceContext.setType(node.expression, fieldElement?.type);
     node.expression.accept(this);
-    var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(node.expression);
+    var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(node.expression);
     node.accept(elementResolver);
     node.accept(typeAnalyzer);
     var enclosingConstructor = enclosingFunction as ConstructorElement;
@@ -1268,7 +1264,7 @@
     checkUnreachableNode(node);
     node.accept(elementResolver);
     node.accept(typeAnalyzer);
-    flowAnalysis?.continueStatement(node);
+    flowAnalysis.continueStatement(node);
   }
 
   @override
@@ -1289,18 +1285,18 @@
     var body = node.body;
     var condition = node.condition;
 
-    flowAnalysis?.flow?.doStatement_bodyBegin(node);
+    flowAnalysis.flow?.doStatement_bodyBegin(node);
     body.accept(this);
 
-    flowAnalysis?.flow?.doStatement_conditionBegin();
+    flowAnalysis.flow?.doStatement_conditionBegin();
     InferenceContext.setType(condition, typeProvider.boolType);
     condition.accept(this);
     condition = node.condition;
-    var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(condition);
+    var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(condition);
     boolExpressionVerifier.checkForNonBoolCondition(condition,
         whyNotPromoted: whyNotPromoted);
 
-    flowAnalysis?.flow?.doStatement_end(condition);
+    flowAnalysis.flow?.doStatement_end(condition);
   }
 
   @override
@@ -1349,7 +1345,7 @@
 
       super.visitExpressionFunctionBody(node);
 
-      flowAnalysis?.flow?.handleExit();
+      flowAnalysis.flow?.handleExit();
 
       inferenceContext.bodyContext!.addReturnExpression(node.expression);
     } finally {
@@ -1414,12 +1410,12 @@
     bool isLocal = node.parent is FunctionDeclarationStatement;
 
     if (isLocal) {
-      flowAnalysis!.flow!.functionExpression_begin(node);
+      flowAnalysis.flow!.functionExpression_begin(node);
     } else {
-      flowAnalysis!
-          .topLevelDeclaration_enter(node, node.functionExpression.parameters);
+      flowAnalysis.topLevelDeclaration_enter(
+          node, node.functionExpression.parameters);
     }
-    flowAnalysis!.executableDeclaration_enter(
+    flowAnalysis.executableDeclaration_enter(
       node,
       node.functionExpression.parameters,
       isLocal,
@@ -1445,14 +1441,14 @@
       body: node.functionExpression.body,
       errorNode: node.name,
     );
-    flowAnalysis!.executableDeclaration_exit(
+    flowAnalysis.executableDeclaration_exit(
       node.functionExpression.body,
       isLocal,
     );
     if (isLocal) {
-      flowAnalysis!.flow!.functionExpression_end();
+      flowAnalysis.flow!.functionExpression_end();
     } else {
-      flowAnalysis!.topLevelDeclaration_exit();
+      flowAnalysis.topLevelDeclaration_exit();
     }
     nullSafetyDeadCodeVerifier.flowEnd(node);
 
@@ -1516,27 +1512,27 @@
 
   @override
   void visitIfElement(IfElement node) {
-    flowAnalysis?.flow?.ifStatement_conditionBegin();
+    flowAnalysis.flow?.ifStatement_conditionBegin();
     Expression condition = node.condition;
     InferenceContext.setType(condition, typeProvider.boolType);
     condition.accept(this);
     condition = node.condition;
-    var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(condition);
+    var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(condition);
 
     boolExpressionVerifier.checkForNonBoolCondition(condition,
         whyNotPromoted: whyNotPromoted);
 
     CollectionElement thenElement = node.thenElement;
-    flowAnalysis!.flow?.ifStatement_thenBegin(condition, node);
+    flowAnalysis.flow?.ifStatement_thenBegin(condition, node);
     thenElement.accept(this);
 
     var elseElement = node.elseElement;
     if (elseElement != null) {
-      flowAnalysis?.flow?.ifStatement_elseBegin();
+      flowAnalysis.flow?.ifStatement_elseBegin();
       elseElement.accept(this);
     }
 
-    flowAnalysis?.flow?.ifStatement_end(elseElement != null);
+    flowAnalysis.flow?.ifStatement_end(elseElement != null);
 
     node.accept(elementResolver);
     node.accept(typeAnalyzer);
@@ -1545,31 +1541,31 @@
   @override
   void visitIfStatement(IfStatement node) {
     checkUnreachableNode(node);
-    flowAnalysis?.flow?.ifStatement_conditionBegin();
+    flowAnalysis.flow?.ifStatement_conditionBegin();
 
     Expression condition = node.condition;
 
     InferenceContext.setType(condition, typeProvider.boolType);
     condition.accept(this);
     condition = node.condition;
-    var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(condition);
+    var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(condition);
 
     boolExpressionVerifier.checkForNonBoolCondition(condition,
         whyNotPromoted: whyNotPromoted);
 
     Statement thenStatement = node.thenStatement;
-    flowAnalysis!.flow?.ifStatement_thenBegin(condition, node);
+    flowAnalysis.flow?.ifStatement_thenBegin(condition, node);
     thenStatement.accept(this);
     nullSafetyDeadCodeVerifier.flowEnd(thenStatement);
 
     var elseStatement = node.elseStatement;
     if (elseStatement != null) {
-      flowAnalysis?.flow?.ifStatement_elseBegin();
+      flowAnalysis.flow?.ifStatement_elseBegin();
       elseStatement.accept(this);
       nullSafetyDeadCodeVerifier.flowEnd(elseStatement);
     }
 
-    flowAnalysis?.flow?.ifStatement_end(elseStatement != null);
+    flowAnalysis.flow?.ifStatement_end(elseStatement != null);
 
     node.accept(elementResolver);
     node.accept(typeAnalyzer);
@@ -1592,7 +1588,7 @@
 
     InferenceContext.setType(node.index, result.indexContextType);
     node.index.accept(this);
-    var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(node.index);
+    var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(node.index);
     checkIndexExpressionIndex(
       node.index,
       readElement: result.readElement as ExecutableElement?,
@@ -1622,7 +1618,7 @@
   @override
   void visitIsExpression(IsExpression node) {
     super.visitIsExpression(node);
-    flowAnalysis?.isExpression(node);
+    flowAnalysis.isExpression(node);
   }
 
   @override
@@ -1630,9 +1626,9 @@
 
   @override
   void visitLabeledStatement(LabeledStatement node) {
-    flowAnalysis?.labeledStatement_enter(node);
+    flowAnalysis.labeledStatement_enter(node);
     super.visitLabeledStatement(node);
-    flowAnalysis?.labeledStatement_exit(node);
+    flowAnalysis.labeledStatement_exit(node);
   }
 
   @override
@@ -1646,8 +1642,8 @@
 
   @override
   void visitMethodDeclaration(MethodDeclaration node) {
-    flowAnalysis!.topLevelDeclaration_enter(node, node.parameters);
-    flowAnalysis!.executableDeclaration_enter(node, node.parameters, false);
+    flowAnalysis.topLevelDeclaration_enter(node, node.parameters);
+    flowAnalysis.executableDeclaration_enter(node, node.parameters, false);
 
     DartType returnType = node.declaredElement!.returnType;
     InferenceContext.setType(node.body, returnType);
@@ -1670,8 +1666,8 @@
       body: node.body,
       errorNode: node.name,
     );
-    flowAnalysis!.executableDeclaration_exit(node.body, false);
-    flowAnalysis!.topLevelDeclaration_exit();
+    flowAnalysis.executableDeclaration_exit(node.body, false);
+    flowAnalysis.topLevelDeclaration_exit();
     nullSafetyDeadCodeVerifier.flowEnd(node);
 
     node.accept(elementResolver);
@@ -1686,7 +1682,7 @@
     target?.accept(this);
 
     if (_migratableAstInfoProvider.isMethodInvocationNullAware(node)) {
-      var flow = flowAnalysis?.flow;
+      var flow = flowAnalysis.flow;
       if (flow != null) {
         if (target is SimpleIdentifierImpl &&
             target.staticElement is ClassElement) {
@@ -1738,7 +1734,7 @@
     // Any "why not promoted" information that flow analysis had associated with
     // `node.expression` now needs to be forwarded to `node`, so that when
     // `visitArgumentList` iterates through the arguments, it will find it.
-    flowAnalysis?.flow?.forwardExpression(node, node.expression);
+    flowAnalysis.flow?.forwardExpression(node, node.expression);
   }
 
   @override
@@ -1751,7 +1747,7 @@
 
   @override
   void visitNullLiteral(NullLiteral node) {
-    flowAnalysis?.flow?.nullLiteral(node);
+    flowAnalysis.flow?.nullLiteral(node);
     super.visitNullLiteral(node);
   }
 
@@ -1759,7 +1755,7 @@
   void visitParenthesizedExpression(ParenthesizedExpression node) {
     InferenceContext.setTypeFromNode(node.expression, node);
     super.visitParenthesizedExpression(node);
-    flowAnalysis?.flow?.parenthesizedExpression(node, node.expression);
+    flowAnalysis.flow?.parenthesizedExpression(node, node.expression);
   }
 
   @override
@@ -1834,7 +1830,7 @@
   @override
   void visitRethrowExpression(RethrowExpression node) {
     super.visitRethrowExpression(node);
-    flowAnalysis?.flow?.handleExit();
+    flowAnalysis.flow?.handleExit();
   }
 
   @override
@@ -1847,7 +1843,7 @@
     super.visitReturnStatement(node);
 
     inferenceContext.bodyContext?.addReturnExpression(node.expression);
-    flowAnalysis?.flow?.handleExit();
+    flowAnalysis.flow?.handleExit();
   }
 
   @override
@@ -1901,7 +1897,7 @@
         node.expression, _enclosingSwitchStatementExpressionType);
     super.visitSwitchCase(node);
 
-    var flow = flowAnalysis?.flow;
+    var flow = flowAnalysis.flow;
     if (flow != null && flow.isReachable && _isNonNullableByDefault) {
       var switchStatement = node.parent as SwitchStatement;
       if (switchStatement.members.last != node && node.statements.isNotEmpty) {
@@ -1933,27 +1929,23 @@
 
       _enclosingSwitchStatementExpressionType = expression.typeOrThrow;
 
-      if (flowAnalysis != null) {
-        var flow = flowAnalysis!.flow!;
+      var flow = flowAnalysis.flow!;
 
-        flow.switchStatement_expressionEnd(node);
+      flow.switchStatement_expressionEnd(node);
 
-        var exhaustiveness = _SwitchExhaustiveness(
-          _enclosingSwitchStatementExpressionType!,
-        );
+      var exhaustiveness = _SwitchExhaustiveness(
+        _enclosingSwitchStatementExpressionType!,
+      );
 
-        var members = node.members;
-        for (var member in members) {
-          flow.switchStatement_beginCase(member.labels.isNotEmpty, node);
-          member.accept(this);
+      var members = node.members;
+      for (var member in members) {
+        flow.switchStatement_beginCase(member.labels.isNotEmpty, node);
+        member.accept(this);
 
-          exhaustiveness.visitSwitchMember(member);
-        }
-
-        flow.switchStatement_end(exhaustiveness.isExhaustive);
-      } else {
-        node.members.accept(this);
+        exhaustiveness.visitSwitchMember(member);
       }
+
+      flow.switchStatement_end(exhaustiveness.isExhaustive);
     } finally {
       _enclosingSwitchStatementExpressionType = previousExpressionType;
     }
@@ -1962,17 +1954,13 @@
   @override
   void visitThrowExpression(ThrowExpression node) {
     super.visitThrowExpression(node);
-    flowAnalysis?.flow?.handleExit();
+    flowAnalysis.flow?.handleExit();
   }
 
   @override
   void visitTryStatement(TryStatement node) {
-    if (flowAnalysis == null) {
-      return super.visitTryStatement(node);
-    }
-
     checkUnreachableNode(node);
-    var flow = flowAnalysis!.flow!;
+    var flow = flowAnalysis.flow!;
 
     var body = node.body;
     var catchClauses = node.catchClauses;
@@ -2044,11 +2032,11 @@
       if (declaredType == null) {
         if (_isNonNullableByDefault &&
             initializerStaticType is TypeParameterType) {
-          flowAnalysis?.flow?.promote(
+          flowAnalysis.flow?.promote(
               declaredElement as PromotableElement, initializerStaticType);
         }
       } else {
-        flowAnalysis?.flow?.initialize(declaredElement as PromotableElement,
+        flowAnalysis.flow?.initialize(declaredElement as PromotableElement,
             initializerStaticType, initializer,
             isFinal: parent.isFinal, isLate: parent.isLate);
       }
@@ -2057,7 +2045,7 @@
 
   @override
   void visitVariableDeclarationList(VariableDeclarationList node) {
-    flowAnalysis?.variableDeclarationList(node);
+    flowAnalysis.variableDeclarationList(node);
     for (VariableDeclaration decl in node.variables) {
       VariableElement variableElement = decl.declaredElement!;
       InferenceContext.setType(decl, variableElement.type);
@@ -2072,18 +2060,18 @@
     Expression condition = node.condition;
     InferenceContext.setType(condition, typeProvider.boolType);
 
-    flowAnalysis?.flow?.whileStatement_conditionBegin(node);
+    flowAnalysis.flow?.whileStatement_conditionBegin(node);
     condition.accept(this);
     condition = node.condition;
-    var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(condition);
+    var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(condition);
 
     boolExpressionVerifier.checkForNonBoolCondition(node.condition,
         whyNotPromoted: whyNotPromoted);
 
     Statement body = node.body;
-    flowAnalysis?.flow?.whileStatement_bodyBegin(node, condition);
+    flowAnalysis.flow?.whileStatement_bodyBegin(node, condition);
     body.accept(this);
-    flowAnalysis?.flow?.whileStatement_end();
+    flowAnalysis.flow?.whileStatement_end();
     nullSafetyDeadCodeVerifier.flowEnd(node.body);
     // TODO(brianwilkerson) If the loop can only be exited because the condition
     // is false, then propagateFalseState(condition);
@@ -2125,7 +2113,7 @@
       if (target is SimpleIdentifier && target.staticElement is ClassElement) {
         // `?.` to access static methods is equivalent to `.`, so do nothing.
       } else {
-        flowAnalysis!.flow!.nullAwareAccess_rightBegin(function,
+        flowAnalysis.flow!.nullAwareAccess_rightBegin(function,
             function.realTarget.staticType ?? typeProvider.dynamicType);
         _unfinishedNullShorts.add(node.nullShortingTermination);
       }
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index 43750b2..4c44ba5 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -59,7 +59,7 @@
 
     expression.staticType = type;
     if (_typeSystem.isBottom(type)) {
-      _resolver.flowAnalysis?.flow?.handleExit();
+      _resolver.flowAnalysis.flow?.handleExit();
     }
   }
 
@@ -270,7 +270,7 @@
   @override
   void visitSuperExpression(covariant SuperExpressionImpl node) {
     var thisType = _resolver.thisType;
-    _resolver.flowAnalysis?.flow?.thisOrSuper(node, thisType ?? _dynamicType);
+    _resolver.flowAnalysis.flow?.thisOrSuper(node, thisType ?? _dynamicType);
     if (thisType == null ||
         node.thisOrAncestorOfType<ExtensionDeclaration>() != null) {
       // TODO(brianwilkerson) Report this error if it hasn't already been
@@ -291,7 +291,7 @@
   @override
   void visitThisExpression(covariant ThisExpressionImpl node) {
     var thisType = _resolver.thisType;
-    _resolver.flowAnalysis?.flow?.thisOrSuper(node, thisType ?? _dynamicType);
+    _resolver.flowAnalysis.flow?.thisOrSuper(node, thisType ?? _dynamicType);
     if (thisType == null) {
       // TODO(brianwilkerson) Report this error if it hasn't already been
       // reported.
diff --git a/pkg/analyzer/lib/src/generated/variable_type_provider.dart b/pkg/analyzer/lib/src/generated/variable_type_provider.dart
index 4258443..35cf432 100644
--- a/pkg/analyzer/lib/src/generated/variable_type_provider.dart
+++ b/pkg/analyzer/lib/src/generated/variable_type_provider.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
 
 /// Provider of types for local variables and formal parameters.
@@ -12,15 +11,3 @@
   /// return the type of the variable at the node - declared or promoted.
   DartType getType(SimpleIdentifier node, {required bool isRead});
 }
-
-/// Implementation of [LocalVariableTypeProvider] that does not promote, for use
-/// in situations where no flow analysis is being done.  This happens in some
-/// analyzer internal unit tests.
-class NonPromotingLocalVariableTypeProvider
-    implements LocalVariableTypeProvider {
-  const NonPromotingLocalVariableTypeProvider();
-
-  @override
-  DartType getType(SimpleIdentifier node, {required bool isRead}) =>
-      (node.staticElement as VariableElement).type;
-}
diff --git a/pkg/analyzer/lib/src/macro/impl/macro.dart b/pkg/analyzer/lib/src/macro/impl/macro.dart
index 73e10f7..989bdb4 100644
--- a/pkg/analyzer/lib/src/macro/impl/macro.dart
+++ b/pkg/analyzer/lib/src/macro/impl/macro.dart
@@ -2,23 +2,33 @@
 // 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:typed_data';
-
 import 'package:analyzer/dart/analysis/utilities.dart';
 import 'package:analyzer/dart/ast/ast.dart' as ast;
 import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/visitor.dart';
 import 'package:analyzer/src/dart/ast/ast.dart' as ast;
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/macro/api/code.dart';
 import 'package:analyzer/src/macro/api/macro.dart';
 import 'package:analyzer/src/summary2/informative_data.dart';
+import 'package:analyzer/src/summary2/library_builder.dart';
 
 class ClassDeclarationBuilderImpl extends DeclarationBuilderImpl
     implements ClassDeclarationBuilder {
-  final DeclarationCollector _collector;
+  final LinkingUnit linkingUnit;
+
+  /// The index of [node] among other [ast.ClassDeclarationImpl]s.
+  final int nodeIndex;
+
   final ast.ClassDeclarationImpl node;
 
-  ClassDeclarationBuilderImpl(this._collector, this.node);
+  ClassDeclarationBuilderImpl(
+    this.linkingUnit,
+    this.nodeIndex,
+    this.node,
+  );
 
   @override
   void addToClass(Declaration declaration) {
@@ -35,7 +45,18 @@
     _rebaseOffsets(parsedMember);
 
     node.members.add(parsedMember);
-    _collector._add(parsedMember, declaration);
+
+    var macroGeneratedContent = linkingUnit.macroGeneratedContent;
+    var collected = _Declaration(
+      data: MacroGenerationData(
+        id: macroGeneratedContent.nextId++,
+        code: declarationCode,
+        informative: writeDeclarationInformative(parsedMember),
+        classDeclarationIndex: nodeIndex,
+      ),
+      node: parsedMember,
+    );
+    macroGeneratedContent._declarations.add(collected);
   }
 
   /// We parsed [node] in the context of some synthetic code string, its
@@ -63,46 +84,136 @@
   }
 }
 
-class DeclarationCollector {
-  final Map<ast.AstNode, _CollectedDeclaration> _declarations = {};
-  int _nextId = 0;
+class MacroGeneratedContent {
+  final LinkingUnit linkingUnit;
+  final List<_Declaration> _declarations = [];
+  int nextId = 0;
 
-  /// Elements for nodes in [_declarations] were built.
-  /// Move information from [_CollectedDeclaration] into elements.
-  void updateElements() {
-    for (var entry in _declarations.entries) {
-      var node = entry.key;
+  MacroGeneratedContent(this.linkingUnit);
+
+  /// Finish building of elements: combine the source code of the unit with
+  /// macro-generated pieces of code; update offsets of macro-generated
+  /// elements to use offsets inside this combined code.
+  void finish() {
+    // Don't set empty values, keep it null.
+    if (_declarations.isEmpty) {
+      return;
+    }
+
+    // Sort declarations by:
+    // 1. The top-level declaration location.
+    // 2. The ordering in the top-level declaration.
+    // We need (2) because `sort` is not stable.
+    _declarations.sort((a, b) {
+      const indexForUnit = 1 << 24;
+      var aIndex = a.data.classDeclarationIndex ?? indexForUnit;
+      var bIndex = b.data.classDeclarationIndex ?? indexForUnit;
+      if (aIndex != bIndex) {
+        return aIndex - bIndex;
+      }
+      return a.data.id - b.data.id;
+    });
+
+    const classMemberCodePrefix = '\n';
+    const classMemberCodeSuffix = '\n';
+    // TODO(scheglov) make it required?
+    var generatedContent = linkingUnit.input.sourceContent!;
+    var shift = 0;
+    var classDeclarations = linkingUnit.input.unit.declarations
+        .whereType<ast.ClassDeclaration>()
+        .toList();
+    for (var declaration in _declarations) {
+      var classIndex = declaration.data.classDeclarationIndex;
+      if (classIndex != null) {
+        var targetClass = classDeclarations[classIndex];
+        var code = classMemberCodePrefix +
+            declaration.data.code +
+            classMemberCodeSuffix;
+        var insertOffset = shift + targetClass.rightBracket.offset;
+        declaration.data.offset = insertOffset + classMemberCodePrefix.length;
+        generatedContent = generatedContent.substring(0, insertOffset) +
+            code +
+            generatedContent.substring(insertOffset);
+        shift += code.length;
+      } else {
+        throw UnimplementedError();
+      }
+
+      var node = declaration.node;
       if (node is ast.Declaration) {
-        var element = node.declaredElement;
+        var element = node.declaredElement as ElementImpl;
+        element.accept(
+          _ShiftOffsetsElementVisitor(declaration.data.offset),
+        );
         if (element is HasMacroGenerationData) {
-          var collectedDeclaration = entry.value;
-          (element as HasMacroGenerationData).macro = MacroGenerationData(
-            collectedDeclaration.id,
-            collectedDeclaration.declaration.code,
-            collectedDeclaration.informative,
-          );
+          (element as HasMacroGenerationData).macro = declaration.data;
         }
       }
     }
-  }
 
-  void _add(ast.AstNode node, Declaration declaration) {
-    _declarations[node] = _CollectedDeclaration(
-      _nextId++,
-      declaration,
-      writeDeclarationInformative(node),
-    );
+    linkingUnit.element.macroGeneratedContent = generatedContent;
+    linkingUnit.element.macroGenerationDataList =
+        _declarations.map((e) => e.data).toList();
   }
 }
 
-class _CollectedDeclaration {
-  final int id;
-  final Declaration declaration;
-  final Uint8List informative;
+/// [MacroGenerationData] plus its linking [node] (to get the element).
+class _Declaration {
+  final MacroGenerationData data;
+  final ast.AstNode node;
 
-  _CollectedDeclaration(
-    this.id,
-    this.declaration,
-    this.informative,
-  );
+  _Declaration({
+    required this.data,
+    required this.node,
+  });
+}
+
+/// TODO(scheglov) Enhance to support more nodes.
+/// For now only nodes that are currently used in tests are supported.
+/// Which is probably enough for experiments, but should be improved if this
+/// is something we are going to do for real.
+class _ShiftOffsetsAstVisitor extends RecursiveAstVisitor<void> {
+  final int shift;
+
+  _ShiftOffsetsAstVisitor(this.shift);
+
+  @override
+  void visitAnnotation(ast.Annotation node) {
+    _token(node.atSign);
+    super.visitAnnotation(node);
+  }
+
+  @override
+  void visitSimpleIdentifier(ast.SimpleIdentifier node) {
+    _token(node.token);
+  }
+
+  void _token(Token token) {
+    token.offset += shift;
+  }
+}
+
+/// Macro-generated elements are created from pieces of code that are rebased
+/// to start at zero-th offset. When we later know that actual offset in the
+/// combined (source + generated) code of the unit, we shift the offsets.
+class _ShiftOffsetsElementVisitor extends GeneralizingElementVisitor<void> {
+  final int shift;
+
+  _ShiftOffsetsElementVisitor(this.shift);
+
+  @override
+  void visitElement(covariant ElementImpl element) {
+    element.nameOffset += shift;
+    _metadata(element.metadata);
+    super.visitElement(element);
+  }
+
+  void _metadata(List<ElementAnnotation> metadata) {
+    for (var annotation in metadata) {
+      annotation as ElementAnnotationImpl;
+      annotation.annotationAst.accept(
+        _ShiftOffsetsAstVisitor(shift),
+      );
+    }
+  }
 }
diff --git a/pkg/analyzer/lib/src/summary2/bundle_reader.dart b/pkg/analyzer/lib/src/summary2/bundle_reader.dart
index 2c9d02c..c6ae30c 100644
--- a/pkg/analyzer/lib/src/summary2/bundle_reader.dart
+++ b/pkg/analyzer/lib/src/summary2/bundle_reader.dart
@@ -30,16 +30,16 @@
 
 class BundleReader {
   final SummaryDataReader _reader;
-  final Map<Uri, Uint8List> _unitsInformativeBytes;
+  final Map<Uri, InformativeUnitData> _unitsInformativeData;
 
   final Map<String, LibraryReader> libraryMap = {};
 
   BundleReader({
     required LinkedElementFactory elementFactory,
     required Uint8List resolutionBytes,
-    Map<Uri, Uint8List> unitsInformativeBytes = const {},
+    Map<Uri, InformativeUnitData> unitsInformativeData = const {},
   })  : _reader = SummaryDataReader(resolutionBytes),
-        _unitsInformativeBytes = unitsInformativeBytes {
+        _unitsInformativeData = unitsInformativeData {
     _reader.offset = _reader.bytes.length - 4 * 4;
     var baseResolutionOffset = _reader.readUInt32();
     var librariesOffset = _reader.readUInt32();
@@ -69,7 +69,7 @@
       libraryMap[uriStr] = LibraryReader._(
         elementFactory: elementFactory,
         reader: _reader,
-        unitsInformativeBytes: _unitsInformativeBytes,
+        unitsInformativeData: _unitsInformativeData,
         baseResolutionOffset: baseResolutionOffset,
         referenceReader: referenceReader,
         reference: reference,
@@ -412,11 +412,12 @@
 class LibraryReader {
   final LinkedElementFactory _elementFactory;
   final SummaryDataReader _reader;
-  final Map<Uri, Uint8List> _unitsInformativeBytes;
+  final Map<Uri, InformativeUnitData> _unitsInformativeData;
   final int _baseResolutionOffset;
   final _ReferenceReader _referenceReader;
   final Reference _reference;
   final int _offset;
+  final Map<int, MacroGenerationData> _macroDeclarations = {};
 
   final Uint32List _classMembersLengths;
   int _classMembersLengthsIndex = 0;
@@ -426,7 +427,7 @@
   LibraryReader._({
     required LinkedElementFactory elementFactory,
     required SummaryDataReader reader,
-    required Map<Uri, Uint8List> unitsInformativeBytes,
+    required Map<Uri, InformativeUnitData> unitsInformativeData,
     required int baseResolutionOffset,
     required _ReferenceReader referenceReader,
     required Reference reference,
@@ -434,7 +435,7 @@
     required Uint32List classMembersLengths,
   })  : _elementFactory = elementFactory,
         _reader = reader,
-        _unitsInformativeBytes = unitsInformativeBytes,
+        _unitsInformativeData = unitsInformativeData,
         _baseResolutionOffset = baseResolutionOffset,
         _referenceReader = referenceReader,
         _reference = reference,
@@ -493,7 +494,7 @@
     _declareDartCoreDynamicNever();
 
     InformativeDataApplier(_elementFactory).applyTo(
-      _unitsInformativeBytes,
+      _unitsInformativeData,
       libraryElement,
     );
 
@@ -851,15 +852,16 @@
   }
 
   void _readMacro(Element element, HasMacroGenerationData hasMacro) {
-    if (_reader.readBool()) {
-      hasMacro.macro = MacroGenerationData(
-        _reader.readUInt30(),
-        _reader.readStringUtf8(),
-        Uint8List(0),
-      );
-      InformativeDataApplier(_elementFactory).applyToDeclaration(
+    var id = _reader.readOptionalUInt30();
+    if (id != null) {
+      var data = _macroDeclarations[id]!;
+      hasMacro.macro = data;
+      InformativeDataApplier(
+        _elementFactory,
+        baseOffset: data.offset,
+      ).applyToDeclaration(
         element,
-        _reader.readUint8List(),
+        data.informative,
       );
     }
   }
@@ -1235,6 +1237,7 @@
     unitElement.isSynthetic = _reader.readBool();
     unitElement.sourceContent = _reader.readOptionalStringUtf8();
 
+    _readUnitMacroGenerationDataList(unitElement);
     _readClasses(unitElement, unitReference);
     _readEnums(unitElement, unitReference);
     _readExtensions(unitElement, unitReference);
@@ -1252,6 +1255,28 @@
     return unitElement;
   }
 
+  void _readUnitMacroGenerationDataList(
+    CompilationUnitElementImpl unitElement,
+  ) {
+    var length = _reader.readUInt30();
+    if (length == 0) {
+      return;
+    }
+
+    var dataList = List.generate(length, (index) {
+      return MacroGenerationData(
+        id: _reader.readUInt30(),
+        code: _reader.readStringUtf8(),
+        informative: _reader.readUint8List(),
+        classDeclarationIndex: _reader.readOptionalUInt30(),
+      );
+    });
+    unitElement.macroGenerationDataList = dataList;
+    for (var data in dataList) {
+      _macroDeclarations[data.id] = data;
+    }
+  }
+
   static Variance? _decodeVariance(int index) {
     var tag = TypeParameterVarianceTag.values[index];
     switch (tag) {
diff --git a/pkg/analyzer/lib/src/summary2/bundle_writer.dart b/pkg/analyzer/lib/src/summary2/bundle_writer.dart
index 4b67169..8a3993f 100644
--- a/pkg/analyzer/lib/src/summary2/bundle_writer.dart
+++ b/pkg/analyzer/lib/src/summary2/bundle_writer.dart
@@ -280,12 +280,7 @@
   }
 
   void _writeMacro(MacroGenerationData? macro) {
-    _sink.writeBool(macro != null);
-    if (macro != null) {
-      _sink.writeUInt30(macro.id);
-      _sink.writeStringUtf8(macro.code);
-      _sink.writeUint8List(macro.informative);
-    }
+    _sink.writeOptionalUInt30(macro?.id);
   }
 
   void _writeMethodElement(MethodElement element) {
@@ -454,6 +449,7 @@
     _sink.writeBool(unitElement.isSynthetic);
     _sink.writeOptionalStringUtf8(unitElement.sourceContent);
     _resolutionSink._writeAnnotationList(unitElement.metadata);
+    _writeUnitElementMacroGenerationDataList(unitElement);
     _writeList(unitElement.classes, _writeClassElement);
     _writeList(unitElement.enums, _writeEnumElement);
     _writeList(unitElement.extensions, _writeExtensionElement);
@@ -473,6 +469,18 @@
     );
   }
 
+  void _writeUnitElementMacroGenerationDataList(
+    CompilationUnitElementImpl unitElement,
+  ) {
+    var dataList = unitElement.macroGenerationDataList ?? [];
+    _writeList<MacroGenerationData>(dataList, (data) {
+      _sink.writeUInt30(data.id);
+      _sink.writeStringUtf8(data.code);
+      _sink.writeUint8List(data.informative);
+      _sink.writeOptionalUInt30(data.classDeclarationIndex);
+    });
+  }
+
   static TypeParameterVarianceTag _encodeVariance(
       TypeParameterElementImpl element) {
     if (element.isLegacyCovariant) {
diff --git a/pkg/analyzer/lib/src/summary2/informative_data.dart b/pkg/analyzer/lib/src/summary2/informative_data.dart
index f14a0f2..f513e75 100644
--- a/pkg/analyzer/lib/src/summary2/informative_data.dart
+++ b/pkg/analyzer/lib/src/summary2/informative_data.dart
@@ -41,10 +41,15 @@
 /// offsets are different from `nameOffset` for example, which are applied
 /// directly after creating corresponding elements during a library loading.
 class ApplyConstantOffsets {
+  final int _baseOffset;
   Uint32List? _offsets;
   void Function(_OffsetsApplier)? _function;
 
-  ApplyConstantOffsets(this._offsets, this._function);
+  ApplyConstantOffsets(
+    this._offsets,
+    this._function, {
+    int baseOffset = 0,
+  }) : _baseOffset = baseOffset;
 
   void perform() {
     var offsets = _offsets;
@@ -52,6 +57,7 @@
     if (offsets != null && function != null) {
       var applier = _OffsetsApplier(
         _SafeListIterator(offsets),
+        baseOffset: _baseOffset,
       );
       function.call(applier);
       // Clear the references to possible closure data.
@@ -64,11 +70,15 @@
 
 class InformativeDataApplier {
   final LinkedElementFactory _elementFactory;
+  final int _baseOffset;
 
-  InformativeDataApplier(this._elementFactory);
+  InformativeDataApplier(
+    this._elementFactory, {
+    int baseOffset = 0,
+  }) : _baseOffset = baseOffset;
 
   void applyTo(
-    Map<Uri, Uint8List> unitsInformativeBytes,
+    Map<Uri, InformativeUnitData> unitsInformativeData,
     LibraryElementImpl libraryElement,
   ) {
     if (_elementFactory.isApplyingInformativeData) {
@@ -80,9 +90,9 @@
     for (var i = 0; i < unitElements.length; i++) {
       var unitElement = unitElements[i] as CompilationUnitElementImpl;
       var unitUri = unitElement.source.uri;
-      var unitInfoBytes = unitsInformativeBytes[unitUri];
-      if (unitInfoBytes != null) {
-        var unitReader = SummaryDataReader(unitInfoBytes);
+      var unitInfoData = unitsInformativeData[unitUri];
+      if (unitInfoData != null) {
+        var unitReader = SummaryDataReader(unitInfoData.bytes);
         var unitInfo = _InfoUnit(unitReader);
 
         if (i == 0) {
@@ -91,6 +101,7 @@
 
         unitElement.setCodeRange(unitInfo.codeOffset, unitInfo.codeLength);
         unitElement.lineInfo = LineInfo(unitInfo.lineStarts);
+        _setUnitMacroGeneratedContent(unitElement, unitInfoData, unitInfo);
 
         _applyToAccessors(unitElement.accessors, unitInfo.accessors);
 
@@ -269,7 +280,7 @@
     element as ConstructorElementImpl;
     element.setCodeRange(info.codeOffset, info.codeLength);
     element.periodOffset = info.periodOffset;
-    element.nameOffset = info.nameOffset;
+    element.nameOffset = _baseOffset + info.nameOffset;
     element.nameEnd = info.nameEnd;
     element.documentationComment = info.documentationComment;
     _applyToFormalParameters(
@@ -390,7 +401,7 @@
       (element, info) {
         element as ParameterElementImpl;
         element.setCodeRange(info.codeOffset, info.codeLength);
-        element.nameOffset = info.nameOffset;
+        element.nameOffset = _baseOffset + info.nameOffset;
         _applyToTypeParameters(element.typeParameters, info.typeParameters);
         _applyToFormalParameters(element.parameters, info.parameters);
       },
@@ -529,7 +540,7 @@
   void _applyToMethod(MethodElement element, _InfoMethodDeclaration info) {
     element as MethodElementImpl;
     element.setCodeRange(info.codeOffset, info.codeLength);
-    element.nameOffset = info.nameOffset;
+    element.nameOffset = _baseOffset + info.nameOffset;
     element.documentationComment = info.documentationComment;
     _applyToTypeParameters(
       element.typeParameters_unresolved,
@@ -548,6 +559,7 @@
         applier.applyToTypeParameters(element.typeParameters);
         applier.applyToFormalParameters(element.parameters);
       },
+      baseOffset: _baseOffset,
     );
   }
 
@@ -595,7 +607,7 @@
   ) {
     element as PropertyAccessorElementImpl;
     element.setCodeRange(info.codeOffset, info.codeLength);
-    element.nameOffset = info.nameOffset;
+    element.nameOffset = _baseOffset + info.nameOffset;
     element.documentationComment = info.documentationComment;
     _applyToFormalParameters(
       element.parameters_unresolved,
@@ -611,6 +623,7 @@
           applier.applyToTypeParameters(element.typeParameters);
           applier.applyToFormalParameters(element.parameters);
         },
+        baseOffset: _baseOffset,
       );
     }
   }
@@ -649,6 +662,36 @@
     );
   }
 
+  void _setUnitMacroGeneratedContent(
+    CompilationUnitElementImpl unitElement,
+    InformativeUnitData unitInfoData,
+    _InfoUnit unitInfo,
+  ) {
+    var macroGenerationDataList = unitElement.macroGenerationDataList;
+    if (macroGenerationDataList != null) {
+      const classMemberCodePrefix = '\n';
+      const classMemberCodeSuffix = '\n';
+      var generatedContent = unitInfoData.content;
+      var shift = 0;
+      for (var data in macroGenerationDataList) {
+        var classIndex = data.classDeclarationIndex;
+        if (classIndex != null) {
+          var targetClass = unitInfo.classDeclarations[classIndex];
+          var code = classMemberCodePrefix + data.code + classMemberCodeSuffix;
+          var insertOffset = shift + targetClass.rightBracketOffset;
+          data.offset = insertOffset + classMemberCodePrefix.length;
+          generatedContent = generatedContent.substring(0, insertOffset) +
+              code +
+              generatedContent.substring(insertOffset);
+          shift += code.length;
+        } else {
+          throw UnimplementedError();
+        }
+      }
+      unitElement.macroGeneratedContent = generatedContent;
+    }
+  }
+
   void _setupApplyConstantOffsetsForTypeAlias(
     TypeAliasElementImpl element,
     Uint32List constantOffsets, {
@@ -684,6 +727,20 @@
   }
 }
 
+/// Informative data about a source file.
+class InformativeUnitData {
+  /// The content of the file.
+  final String content;
+
+  /// Informative data derived from the [content], such as offsets.
+  final Uint8List bytes;
+
+  InformativeUnitData({
+    required this.content,
+    required this.bytes,
+  });
+}
+
 enum _DeclarationKind {
   constructorDeclaration,
   methodDeclaration,
@@ -693,6 +750,7 @@
   final int codeOffset;
   final int codeLength;
   final int nameOffset;
+  final int rightBracketOffset;
   final String? documentationComment;
   final List<_InfoTypeParameter> typeParameters;
   final List<_InfoConstructorDeclaration> constructors;
@@ -707,6 +765,7 @@
       codeOffset: reader.readUInt30(),
       codeLength: reader.readUInt30(),
       nameOffset: reader.readUInt30() - nameOffsetDelta,
+      rightBracketOffset: reader.readUInt30(),
       documentationComment: reader.readStringUtf8().nullIfEmpty,
       typeParameters: reader.readTypedList(
         () => _InfoTypeParameter(reader),
@@ -731,6 +790,7 @@
     required this.codeOffset,
     required this.codeLength,
     required this.nameOffset,
+    required this.rightBracketOffset,
     required this.documentationComment,
     required this.typeParameters,
     required this.constructors,
@@ -1194,6 +1254,7 @@
       sink.writeUInt30(node.offset);
       sink.writeUInt30(node.length);
       sink.writeUInt30(node.name.offset);
+      sink.writeUInt30(node.rightBracket.offset);
       _writeDocumentationComment(node);
       _writeTypeParameters(node.typeParameters);
       _writeConstructors(node.members);
@@ -1239,6 +1300,7 @@
       sink.writeUInt30(node.offset);
       sink.writeUInt30(node.length);
       sink.writeUInt30(1 + (node.name?.offset ?? -1));
+      sink.writeUInt30(node.rightBracket.offset);
       _writeDocumentationComment(node);
       _writeTypeParameters(node.typeParameters);
       _writeConstructors(node.members);
@@ -1328,6 +1390,7 @@
       sink.writeUInt30(node.offset);
       sink.writeUInt30(node.length);
       sink.writeUInt30(node.name.offset);
+      sink.writeUInt30(node.rightBracket.offset);
       _writeDocumentationComment(node);
       _writeTypeParameters(node.typeParameters);
       _writeConstructors(node.members);
@@ -1738,8 +1801,12 @@
 
 class _OffsetsApplier extends _OffsetsAstVisitor {
   final _SafeListIterator<int> _iterator;
+  final int _baseOffset;
 
-  _OffsetsApplier(this._iterator);
+  _OffsetsApplier(
+    this._iterator, {
+    int baseOffset = 0,
+  }) : _baseOffset = baseOffset;
 
   void applyToConstantInitializer(Element element) {
     if (element is ConstVariableElement) {
@@ -1793,7 +1860,7 @@
   void handleToken(Token token) {
     var offset = _iterator.take();
     if (offset != null) {
-      token.offset = offset;
+      token.offset = _baseOffset + offset;
     }
   }
 
diff --git a/pkg/analyzer/lib/src/summary2/library_builder.dart b/pkg/analyzer/lib/src/summary2/library_builder.dart
index c1eb205..661ba7d 100644
--- a/pkg/analyzer/lib/src/summary2/library_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/library_builder.dart
@@ -231,13 +231,15 @@
       TypesBuilder(linker).build(nodesToBuildType);
     }
 
-    var collector = macro.DeclarationCollector();
     for (var linkingUnit in units) {
+      var classDeclarationIndex = -1;
       for (var declaration in linkingUnit.node.declarations) {
         if (declaration is ast.ClassDeclarationImpl) {
+          classDeclarationIndex++;
           var members = declaration.members.toList();
           var classBuilder = macro.ClassDeclarationBuilderImpl(
-            collector,
+            linkingUnit,
+            classDeclarationIndex,
             declaration,
           );
           if (hasMacroAnnotation(declaration, 'autoConstructor')) {
@@ -283,7 +285,10 @@
         }
       }
     }
-    collector.updateElements();
+
+    for (var linkingUnit in units) {
+      linkingUnit.macroGeneratedContent.finish();
+    }
   }
 
   void storeExportScope() {
@@ -413,6 +418,8 @@
   final Reference reference;
   final ast.CompilationUnitImpl node;
   final CompilationUnitElementImpl element;
+  late final macro.MacroGeneratedContent macroGeneratedContent =
+      macro.MacroGeneratedContent(this);
 
   LinkingUnit({
     required this.input,
diff --git a/pkg/analyzer/test/generated/element_resolver_test.dart b/pkg/analyzer/test/generated/element_resolver_test.dart
index ad23ac1..a79005d 100644
--- a/pkg/analyzer/test/generated/element_resolver_test.dart
+++ b/pkg/analyzer/test/generated/element_resolver_test.dart
@@ -13,6 +13,7 @@
 import 'package:analyzer/src/dart/ast/ast_factory.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
+import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
 import 'package:analyzer/src/generated/element_resolver.dart';
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/source.dart';
@@ -725,7 +726,9 @@
 
     _visitor = ResolverVisitor(
         inheritance, _definingLibrary, source, _typeProvider, _listener,
-        featureSet: FeatureSet.forTesting());
+        featureSet: FeatureSet.forTesting(),
+        flowAnalysisHelper:
+            FlowAnalysisHelper(context.typeSystemLegacy, false, false));
     _resolver = _visitor.elementResolver;
   }
 
diff --git a/pkg/analyzer/test/generated/static_type_analyzer_test.dart b/pkg/analyzer/test/generated/static_type_analyzer_test.dart
index 696241c..18d6b1a 100644
--- a/pkg/analyzer/test/generated/static_type_analyzer_test.dart
+++ b/pkg/analyzer/test/generated/static_type_analyzer_test.dart
@@ -13,6 +13,7 @@
 import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
 import 'package:analyzer/src/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/type_system.dart';
+import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
 import 'package:analyzer/src/generated/resolver.dart' show ResolverVisitor;
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/generated/static_type_analyzer.dart';
@@ -514,7 +515,9 @@
 
     _visitor = ResolverVisitor(
         inheritance, _definingLibrary, source, _typeProvider, _listener,
-        featureSet: featureSet);
+        featureSet: featureSet,
+        flowAnalysisHelper:
+            FlowAnalysisHelper(context.typeSystemLegacy, false, false));
     _analyzer = _visitor.typeAnalyzer;
   }
 
diff --git a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
index 9a41bd0..6119dbf 100644
--- a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
+++ b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
@@ -792,6 +792,21 @@
         ]));
   }
 
+  void test_visitExportDirective_configurations() {
+    var unit = parseString(content: r'''
+export 'foo.dart'
+  if (dart.library.io) 'foo_io.dart'
+  if (dart.library.html) 'foo_html.dart';
+''').unit;
+    var directive = unit.directives[0] as ExportDirective;
+    _assertSource(
+      "export 'foo.dart'"
+      " if (dart.library.io) 'foo_io.dart'"
+      " if (dart.library.html) 'foo_html.dart';",
+      directive,
+    );
+  }
+
   void test_visitExportDirective_minimal() {
     _assertSource(
         "export 'a.dart';", AstTestFactory.exportDirective2("a.dart"));
@@ -1788,6 +1803,21 @@
         ]));
   }
 
+  void test_visitImportDirective_configurations() {
+    var unit = parseString(content: r'''
+import 'foo.dart'
+  if (dart.library.io) 'foo_io.dart'
+  if (dart.library.html) 'foo_html.dart';
+''').unit;
+    var directive = unit.directives[0] as ImportDirective;
+    _assertSource(
+      "import 'foo.dart'"
+      " if (dart.library.io) 'foo_io.dart'"
+      " if (dart.library.html) 'foo_html.dart';",
+      directive,
+    );
+  }
+
   void test_visitImportDirective_deferred() {
     _assertSource("import 'a.dart' deferred as p;",
         AstTestFactory.importDirective2("a.dart", true, "p"));
diff --git a/pkg/analyzer/test/src/summary/element_text.dart b/pkg/analyzer/test/src/summary/element_text.dart
index f8366c5..a4ad397 100644
--- a/pkg/analyzer/test/src/summary/element_text.dart
+++ b/pkg/analyzer/test/src/summary/element_text.dart
@@ -361,7 +361,6 @@
     });
 
     _withIndent(() {
-      _writeMacro(e);
       _writeDocumentation(e);
       _writeMetadata(e);
       _writeCodeRange(e);
@@ -530,19 +529,6 @@
     buffer.writeln(line);
   }
 
-  void _writeMacro(Element e) {
-    if (e is HasMacroGenerationData) {
-      var macro = (e as HasMacroGenerationData).macro;
-      if (macro != null) {
-        _writelnWithIndent('macro');
-        _withIndent(() {
-          _writelnWithIndent('id: ${macro.id}');
-          _writelnMultiLineWithIndent('code: ${macro.code}');
-        });
-      }
-    }
-  }
-
   void _writeMetadata(Element element) {
     var annotations = element.metadata;
     if (annotations.isNotEmpty) {
@@ -568,7 +554,6 @@
     });
 
     _withIndent(() {
-      _writeMacro(e);
       _writeDocumentation(e);
       _writeMetadata(e);
       _writeCodeRange(e);
@@ -711,7 +696,6 @@
     });
 
     _withIndent(() {
-      _writeMacro(e);
       _writeDocumentation(e);
       _writeMetadata(e);
       _writeCodeRange(e);
@@ -875,6 +859,7 @@
   }
 
   void _writeUnitElement(CompilationUnitElement e) {
+    e as CompilationUnitElementImpl;
     _writeElements('classes', e.classes, _writeClassElement);
     _writeElements('enums', e.enums, _writeClassElement);
     _writeElements('extensions', e.extensions, _writeExtensionElement);
@@ -891,6 +876,12 @@
       _writePropertyAccessorElement,
     );
     _writeElements('functions', e.functions, _writeFunctionElement);
+
+    var macroGeneratedContent = e.macroGeneratedContent;
+    if (macroGeneratedContent != null) {
+      _writelnWithIndent('macroGeneratedContent');
+      buffer.write(macroGeneratedContent);
+    }
   }
 
   void _writeUri(Source? source) {
diff --git a/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart b/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart
index 3a95b46..c18ab0cf 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart
@@ -59,11 +59,9 @@
     var inputLibraries = <LinkInputLibrary>[];
     for (var sdkLibrary in sdk.sdkLibraries) {
       var source = sourceFactory.resolveUri(null, sdkLibrary.shortName)!;
-      var text = getFile(source.fullName).readAsStringSync();
-      var unit = parseText(text, featureSet);
 
       var inputUnits = <LinkInputUnit>[];
-      _addLibraryUnits(source, unit, inputUnits, featureSet);
+      _addLibraryUnits(source, inputUnits, featureSet);
       inputLibraries.add(
         LinkInputLibrary(
           source: source,
@@ -99,11 +97,17 @@
     var inputLibraries = <LinkInputLibrary>[];
     _addNonDartLibraries({}, inputLibraries, source);
 
-    var unitsInformativeBytes = <Uri, Uint8List>{};
+    var unitsInformativeData = <Uri, InformativeUnitData>{};
     for (var inputLibrary in inputLibraries) {
       for (var inputUnit in inputLibrary.units) {
-        var informativeBytes = writeUnitInformative(inputUnit.unit);
-        unitsInformativeBytes[inputUnit.uri] = informativeBytes;
+        var content = inputUnit.sourceContent;
+        if (content != null) {
+          var informativeBytes = writeUnitInformative(inputUnit.unit);
+          unitsInformativeData[inputUnit.uri] = InformativeUnitData(
+            content: content,
+            bytes: informativeBytes,
+          );
+        }
       }
     }
 
@@ -123,7 +127,7 @@
     elementFactory.addBundle(
       BundleReader(
         elementFactory: elementFactory,
-        unitsInformativeBytes: {},
+        unitsInformativeData: {},
         resolutionBytes: sdkBundle.resolutionBytes,
       ),
     );
@@ -137,7 +141,7 @@
       elementFactory.addBundle(
         BundleReader(
           elementFactory: elementFactory,
-          unitsInformativeBytes: unitsInformativeBytes,
+          unitsInformativeData: unitsInformativeData,
           resolutionBytes: linkResult.resolutionBytes,
         ),
       );
@@ -152,14 +156,16 @@
 
   void _addLibraryUnits(
     Source definingSource,
-    CompilationUnit definingUnit,
     List<LinkInputUnit> units,
     FeatureSet featureSet,
   ) {
+    var definingContent = _readSafely(definingSource.fullName);
+    var definingUnit = parseText(definingContent, featureSet);
     units.add(
       LinkInputUnit(
         partDirectiveIndex: null,
         source: definingSource,
+        sourceContent: definingContent,
         isSynthetic: false,
         unit: definingUnit,
       ),
@@ -184,6 +190,7 @@
               partDirectiveIndex: partDirectiveIndex,
               partUriStr: relativeUriStr,
               source: partSource,
+              sourceContent: text,
               isSynthetic: false,
               unit: unit,
             ),
@@ -208,7 +215,7 @@
     var unit = parseText(text, featureSet);
 
     var units = <LinkInputUnit>[];
-    _addLibraryUnits(source, unit, units, featureSet);
+    _addLibraryUnits(source, units, featureSet);
     libraries.add(
       LinkInputLibrary(
         source: source,
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index 9adf69a..74fe439 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -21282,37 +21282,31 @@
           final b @88
             type: int?
         constructors
-          @0
-            macro
-              id: 0
-              code: A({required this.a, this.b});
+          @92
             parameters
-              requiredName final this.a @17
+              requiredName final this.a @109
                 type: int
-              optionalNamed final this.b @25
+              optionalNamed final this.b @117
                 type: int?
         accessors
           synthetic get a @-1
             returnType: int
           synthetic get b @-1
             returnType: int?
+    macroGeneratedContent
+import 'macro_annotations.dart';
+@autoConstructor
+class A {
+  final int a;
+  final int? b;
+
+A({required this.a, this.b});
+}
 ''');
   }
 
   test_macro_hashCode() async {
     addLibrarySource('/macro_annotations.dart', r'''
-library analyzer.macro.annotations;
-const hashCode = 0;
-''');
-    var library = await checkLibrary(r'''
-import 'macro_annotations.dart';
-@hashCode
-class A {
-  final int a;
-  final int b;
-}
-''');
-    checkElementText(library, r'''
 library
   imports
     macro_annotations.dart
@@ -21341,10 +21335,7 @@
             returnType: int
           synthetic get b @-1
             returnType: int
-          get hashCode @18
-            macro
-              id: 0
-              code: @override\nint get hashCode => a.hashCode ^ b.hashCode;
+          get hashCode @102
             metadata
               Annotation
                 atSign: @ @0
@@ -21354,6 +21345,16 @@
                   staticType: null
                   token: override @1
             returnType: int
+    macroGeneratedContent
+import 'macro_annotations.dart';
+@hashCode
+class A {
+  final int a;
+  final int b;
+
+@override
+int get hashCode => a.hashCode ^ b.hashCode;
+}
 ''');
   }
 
@@ -21409,19 +21410,30 @@
         accessors
           synthetic get b @-1
             returnType: int
-          get hashCode @18
-            macro
-              id: 0
-              code: @override\nint get hashCode => b.hashCode ^ a.hashCode;
+          get hashCode @126
             metadata
               Annotation
-                atSign: @ @0
+                atSign: @ @108
                 element: dart:core::@getter::override
                 name: SimpleIdentifier
                   staticElement: dart:core::@getter::override
                   staticType: null
-                  token: override @1
+                  token: override @109
             returnType: int
+    macroGeneratedContent
+import 'macro_annotations.dart';
+
+class A {
+  final int a;
+}
+
+@hashCode
+class B extends A {
+  final int b;
+
+@override
+int get hashCode => b.hashCode ^ a.hashCode;
+}
 ''');
   }
 
@@ -21467,19 +21479,26 @@
               requiredPositional __f @-1
                 type: int
             returnType: void
-          get f @8
-            macro
-              id: 0
-              code: int get f => _f;
+          get f @80
             returnType: int
-          set f @4
-            macro
-              id: 1
-              code: set f(int val) {\n  print('Setting f to ${val}');\n  _f = val;\n}
+          set f @94
             parameters
-              requiredPositional val @10
+              requiredPositional val @100
                 type: int
             returnType: void
+    macroGeneratedContent
+import 'macro_annotations.dart';
+class A {
+  @observable
+  int _f = 0;
+
+int get f => _f;
+
+set f(int val) {
+  print('Setting f to ${val}');
+  _f = val;
+}
+}
 ''');
   }
 
@@ -21528,19 +21547,26 @@
               requiredPositional __f @-1
                 type: T
             returnType: void
-          get f @6
-            macro
-              id: 0
-              code: T get f => _f;
+          get f @75
             returnType: T
-          set f @4
-            macro
-              id: 1
-              code: set f(T val) {\n  print('Setting f to ${val}');\n  _f = val;\n}
+          set f @89
             parameters
-              requiredPositional val @8
+              requiredPositional val @93
                 type: T
             returnType: void
+    macroGeneratedContent
+import 'macro_annotations.dart';
+class A<T> {
+  @observable
+  T _f;
+
+T get f => _f;
+
+set f(T val) {
+  print('Setting f to ${val}');
+  _f = val;
+}
+}
 ''');
   }
 
@@ -21585,19 +21611,26 @@
           synthetic get b @-1
             returnType: int
         methods
-          toString @17
-            macro
-              id: 0
-              code: @override\nString toString() => 'A(a: $a, b: $b)';
+          toString @101
             metadata
               Annotation
-                atSign: @ @0
+                atSign: @ @84
                 element: dart:core::@getter::override
                 name: SimpleIdentifier
                   staticElement: dart:core::@getter::override
                   staticType: null
-                  token: override @1
+                  token: override @85
             returnType: String
+    macroGeneratedContent
+import 'macro_annotations.dart';
+@toString
+class A {
+  final int a;
+  final int b;
+
+@override
+String toString() => 'A(a: $a, b: $b)';
+}
 ''');
   }
 
diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md
index 3d568e4..dbd9c55 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -12928,9 +12928,6 @@
 
 _A nullable expression can't be used in a yield-each statement._
 
-_An expression whose value can be 'null' must be null-checked before it can be
-dereferenced._
-
 _The function can't be unconditionally invoked because it can be 'null'._
 
 _The method '{0}' can't be unconditionally invoked because the receiver can be
diff --git a/pkg/compiler/lib/src/common_elements.dart b/pkg/compiler/lib/src/common_elements.dart
index 64a6537..6f2a3bd 100644
--- a/pkg/compiler/lib/src/common_elements.dart
+++ b/pkg/compiler/lib/src/common_elements.dart
@@ -570,10 +570,6 @@
   FieldEntity get pragmaClassOptionsField;
 
   bool isCreateInvocationMirrorHelper(MemberEntity member);
-
-  ClassEntity get metaNoInlineClass;
-
-  ClassEntity get metaTryInlineClass;
 }
 
 abstract class JCommonElements implements CommonElements {
@@ -2112,38 +2108,6 @@
       _env.lookupLibrary(Uris.dart__js_embedded_names, required: true),
       'JsBuiltin');
 
-  bool _metaAnnotationChecked = false;
-  ClassEntity _metaNoInlineClass;
-  ClassEntity _metaTryInlineClass;
-
-  void _ensureMetaAnnotations() {
-    if (!_metaAnnotationChecked) {
-      _metaAnnotationChecked = true;
-      LibraryEntity library = _env.lookupLibrary(Uris.package_meta_dart2js);
-      if (library != null) {
-        _metaNoInlineClass = _env.lookupClass(library, '_NoInline');
-        _metaTryInlineClass = _env.lookupClass(library, '_TryInline');
-        if (_metaNoInlineClass == null || _metaTryInlineClass == null) {
-          // This is not the package you're looking for.
-          _metaNoInlineClass = null;
-          _metaTryInlineClass = null;
-        }
-      }
-    }
-  }
-
-  @override
-  ClassEntity get metaNoInlineClass {
-    _ensureMetaAnnotations();
-    return _metaNoInlineClass;
-  }
-
-  @override
-  ClassEntity get metaTryInlineClass {
-    _ensureMetaAnnotations();
-    return _metaTryInlineClass;
-  }
-
   @override
   bool isForeign(MemberEntity element) => element.library == foreignLibrary;
 
diff --git a/pkg/meta/CHANGELOG.md b/pkg/meta/CHANGELOG.md
index 81fd808..ea032dd 100644
--- a/pkg/meta/CHANGELOG.md
+++ b/pkg/meta/CHANGELOG.md
@@ -1,6 +1,8 @@
 ## master
 
 * Add `@UseResult.unless`.
+* The mechanism behind `noInline` and `tryInline` from `dart2js.dart` has been
+  changed. This should not affect the use of these annotations in practice.
 
 ## 1.7.0
 
diff --git a/pkg/meta/lib/dart2js.dart b/pkg/meta/lib/dart2js.dart
index 483faeb..29944c8 100644
--- a/pkg/meta/lib/dart2js.dart
+++ b/pkg/meta/lib/dart2js.dart
@@ -17,7 +17,7 @@
 ///
 ///     @dart2js.noInline
 ///     String text() => 'A String of unusual size';
-const _NoInline noInline = _NoInline();
+const noInline = pragma('dart2js:noInline');
 
 /// An annotation for methods to request that dart2js always inline the
 /// method.
@@ -34,12 +34,4 @@
 ///     }
 ///
 /// It is an error to use both `@noInline` and `@tryInline` on the same method.
-const _TryInline tryInline = _TryInline();
-
-class _NoInline {
-  const _NoInline();
-}
-
-class _TryInline {
-  const _TryInline();
-}
+const tryInline = pragma('dart2js:tryInline');
diff --git a/pkg/meta/pubspec.yaml b/pkg/meta/pubspec.yaml
index b7e00f8..d4d36da 100644
--- a/pkg/meta/pubspec.yaml
+++ b/pkg/meta/pubspec.yaml
@@ -1,6 +1,6 @@
 name: meta
 # Note, because version `2.0.0` was mistakenly released, the next major version must be `3.x.y`.
-version: 1.7.0
+version: 1.7.1-dev
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/meta
 description: >-
  Annotations that developers can use to express the intentions that otherwise
diff --git a/runtime/lib/object.cc b/runtime/lib/object.cc
index fb33534..02f2592 100644
--- a/runtime/lib/object.cc
+++ b/runtime/lib/object.cc
@@ -88,7 +88,18 @@
     return Type::Double();
   } else if (instance.IsType() || instance.IsFunctionType()) {
     return Type::DartTypeType();
+  } else if (IsArrayClassId(instance.GetClassId())) {
+    const auto& cls = Class::Handle(
+        zone, thread->isolate_group()->object_store()->list_class());
+    const auto& type_arguments =
+        TypeArguments::Handle(zone, instance.GetTypeArguments());
+    const auto& type = Type::Handle(
+        zone,
+        Type::New(cls, type_arguments, Nullability::kNonNullable, Heap::kNew));
+    type.SetIsFinalized();
+    return type.Canonicalize(thread, nullptr);
   }
+
   return instance.GetType(Heap::kNew);
 }
 
@@ -101,14 +112,18 @@
   if (left_cid != right_cid) {
     if (IsIntegerClassId(left_cid)) {
       return IsIntegerClassId(right_cid);
-    }
-    if (IsStringClassId(left_cid)) {
+    } else if (IsStringClassId(left_cid)) {
       return IsStringClassId(right_cid);
-    }
-    if (IsTypeClassId(left_cid)) {
+    } else if (IsTypeClassId(left_cid)) {
       return IsTypeClassId(right_cid);
+    } else if (IsArrayClassId(left_cid)) {
+      if (!IsArrayClassId(right_cid)) {
+        return false;
+      }
+      // Still need to check type arguments.
+    } else {
+      return false;
     }
-    return false;
   }
 
   if (left_cid == kClosureCid) {
diff --git a/runtime/tests/vm/dart/have_same_runtime_type_test.dart b/runtime/tests/vm/dart/have_same_runtime_type_test.dart
new file mode 100644
index 0000000..b7ade2a
--- /dev/null
+++ b/runtime/tests/vm/dart/have_same_runtime_type_test.dart
@@ -0,0 +1,32 @@
+// 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.
+
+// Test for corner cases of 'a.runtimeType == b.runtimeType' pattern
+// which is recognized and optimized in AOT mode.
+
+import "package:expect/expect.dart";
+
+@pragma('vm:never-inline')
+Object getType(Object obj) => obj.runtimeType;
+
+@pragma('vm:never-inline')
+void test(bool expected, Object a, Object b) {
+  bool result1 = getType(a) == getType(b);
+  bool result2 = a.runtimeType == b.runtimeType;
+  Expect.equals(expected, result1);
+  Expect.equals(expected, result2);
+}
+
+typedef Func = void Function();
+
+void main() {
+  test(true, 0x7fffffffffffffff, int.parse('42'));
+  test(true, 'hi', String.fromCharCode(1114111));
+  test(false, 'hi', 1);
+  test(true, List, Func);
+  test(true, <int>[1], const <int>[2]);
+  test(true, const <String>[], List<String>.filled(1, ''));
+  test(true, <String>[]..add('hi'), List<String>.filled(2, ''));
+  test(false, <int>[], <String>[]);
+}
diff --git a/runtime/tests/vm/dart_2/have_same_runtime_type_test.dart b/runtime/tests/vm/dart_2/have_same_runtime_type_test.dart
new file mode 100644
index 0000000..119e40f
--- /dev/null
+++ b/runtime/tests/vm/dart_2/have_same_runtime_type_test.dart
@@ -0,0 +1,34 @@
+// 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.
+
+// Test for corner cases of 'a.runtimeType == b.runtimeType' pattern
+// which is recognized and optimized in AOT mode.
+
+// @dart = 2.9
+
+import "package:expect/expect.dart";
+
+@pragma('vm:never-inline')
+Object getType(Object obj) => obj.runtimeType;
+
+@pragma('vm:never-inline')
+void test(bool expected, Object a, Object b) {
+  bool result1 = getType(a) == getType(b);
+  bool result2 = a.runtimeType == b.runtimeType;
+  Expect.equals(expected, result1);
+  Expect.equals(expected, result2);
+}
+
+typedef Func = void Function();
+
+void main() {
+  test(true, 0x7fffffffffffffff, int.parse('42'));
+  test(true, 'hi', String.fromCharCode(1114111));
+  test(false, 'hi', 1);
+  test(true, List, Func);
+  test(true, <int>[1], const <int>[2]);
+  test(true, const <String>[], List<String>.filled(1, ''));
+  test(true, <String>[]..add('hi'), List<String>.filled(2, ''));
+  test(false, <int>[], <String>[]);
+}
diff --git a/runtime/vm/class_id.h b/runtime/vm/class_id.h
index ac06355..5ca709a 100644
--- a/runtime/vm/class_id.h
+++ b/runtime/vm/class_id.h
@@ -78,7 +78,6 @@
   V(Mint)                                                                      \
   V(Double)                                                                    \
   V(Bool)                                                                      \
-  V(GrowableObjectArray)                                                       \
   V(Float32x4)                                                                 \
   V(Int32x4)                                                                   \
   V(Float64x2)                                                                 \
@@ -108,10 +107,14 @@
 // TODO(http://dartbug.com/45908): Add ImmutableLinkedHashSet.
 #define CLASS_LIST_SETS(V) V(LinkedHashSet)
 
-#define CLASS_LIST_ARRAYS(V)                                                   \
+#define CLASS_LIST_FIXED_LENGTH_ARRAYS(V)                                      \
   V(Array)                                                                     \
   V(ImmutableArray)
 
+#define CLASS_LIST_ARRAYS(V)                                                   \
+  CLASS_LIST_FIXED_LENGTH_ARRAYS(V)                                            \
+  V(GrowableObjectArray)
+
 #define CLASS_LIST_STRINGS(V)                                                  \
   V(String)                                                                    \
   V(OneByteString)                                                             \
@@ -182,6 +185,7 @@
   V(LinkedHashMap)                                                             \
   V(LinkedHashSet)                                                             \
   V(Array)                                                                     \
+  V(GrowableObjectArray)                                                       \
   V(String)
 
 #define CLASS_LIST_NO_OBJECT(V)                                                \
@@ -327,11 +331,15 @@
           index == kExternalTwoByteStringCid);
 }
 
+inline bool IsArrayClassId(intptr_t index) {
+  COMPILE_ASSERT(kImmutableArrayCid == kArrayCid + 1);
+  COMPILE_ASSERT(kGrowableObjectArrayCid == kArrayCid + 2);
+  return (index >= kArrayCid && index <= kGrowableObjectArrayCid);
+}
+
 inline bool IsBuiltinListClassId(intptr_t index) {
   // Make sure this function is updated when new builtin List types are added.
-  COMPILE_ASSERT(kImmutableArrayCid == kArrayCid + 1);
-  return ((index >= kArrayCid && index <= kImmutableArrayCid) ||
-          (index == kGrowableObjectArrayCid) || IsTypedDataBaseClassId(index) ||
+  return (IsArrayClassId(index) || IsTypedDataBaseClassId(index) ||
           (index == kByteBufferCid));
 }
 
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm.cc b/runtime/vm/compiler/asm_intrinsifier_arm.cc
index 375c454..e7f2ee1 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm.cc
@@ -1198,6 +1198,14 @@
              kIfNotInRange, target);
 }
 
+static void JumpIfNotList(Assembler* assembler,
+                          Register cid,
+                          Register tmp,
+                          Label* target) {
+  RangeCheck(assembler, cid, tmp, kArrayCid, kGrowableObjectArrayCid,
+             kIfNotInRange, target);
+}
+
 static void JumpIfType(Assembler* assembler,
                        Register cid,
                        Register tmp,
@@ -1284,7 +1292,7 @@
                                Register scratch,
                                bool testing_instance_cids) {
   Label different_cids, equal_cids_but_generic, not_integer,
-      not_integer_or_string;
+      not_integer_or_string, not_integer_or_string_or_list;
 
   // Check if left hand side is a closure. Closures are handled in the runtime.
   __ CompareImmediate(cid1, kClosureCid);
@@ -1310,7 +1318,8 @@
   __ b(equal);
 
   // Class ids are different. Check if we are comparing two string types (with
-  // different representations) or two integer types or two type types.
+  // different representations), two integer types, two list types or two type
+  // types.
   __ Bind(&different_cids);
   __ CompareImmediate(cid1, kNumPredefinedCids);
   __ b(not_equal, HI);
@@ -1335,9 +1344,20 @@
 
   if (testing_instance_cids) {
     __ Bind(&not_integer_or_string);
+    // Check if both are List types.
+    JumpIfNotList(assembler, cid1, scratch, &not_integer_or_string_or_list);
+
+    // First type is a List. Check if the second is a List too.
+    JumpIfNotList(assembler, cid2, scratch, not_equal);
+    ASSERT(compiler::target::Array::type_arguments_offset() ==
+           compiler::target::GrowableObjectArray::type_arguments_offset());
+    __ LoadImmediate(scratch, compiler::target::Array::type_arguments_offset());
+    __ b(&equal_cids_but_generic);
+
+    __ Bind(&not_integer_or_string_or_list);
     // Check if the first type is a Type. If it is not then types are not
     // equivalent because they have different class ids and they are not String
-    // or integer or Type.
+    // or integer or List or Type.
     JumpIfNotType(assembler, cid1, scratch, not_equal);
 
     // First type is a Type. Check if the second is a Type too.
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm64.cc b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
index 36cb205..1e0134a 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
@@ -1342,6 +1342,14 @@
              kIfNotInRange, target);
 }
 
+static void JumpIfNotList(Assembler* assembler,
+                          Register cid,
+                          Register tmp,
+                          Label* target) {
+  RangeCheck(assembler, cid, tmp, kArrayCid, kGrowableObjectArrayCid,
+             kIfNotInRange, target);
+}
+
 static void JumpIfType(Assembler* assembler,
                        Register cid,
                        Register tmp,
@@ -1432,7 +1440,7 @@
                                Register scratch,
                                bool testing_instance_cids) {
   Label different_cids, equal_cids_but_generic, not_integer,
-      not_integer_or_string;
+      not_integer_or_string, not_integer_or_string_or_list;
 
   // Check if left hand side is a closure. Closures are handled in the runtime.
   __ CompareImmediate(cid1, kClosureCid);
@@ -1458,7 +1466,8 @@
   __ b(equal);
 
   // Class ids are different. Check if we are comparing two string types (with
-  // different representations) or two integer types or two type types.
+  // different representations), two integer types, two list types or two type
+  // types.
   __ Bind(&different_cids);
   __ CompareImmediate(cid1, kNumPredefinedCids);
   __ b(not_equal, HI);
@@ -1483,9 +1492,20 @@
 
   if (testing_instance_cids) {
     __ Bind(&not_integer_or_string);
+    // Check if both are List types.
+    JumpIfNotList(assembler, cid1, scratch, &not_integer_or_string_or_list);
+
+    // First type is a List. Check if the second is a List too.
+    JumpIfNotList(assembler, cid2, scratch, not_equal);
+    ASSERT(compiler::target::Array::type_arguments_offset() ==
+           compiler::target::GrowableObjectArray::type_arguments_offset());
+    __ LoadImmediate(scratch, compiler::target::Array::type_arguments_offset());
+    __ b(&equal_cids_but_generic);
+
+    __ Bind(&not_integer_or_string_or_list);
     // Check if the first type is a Type. If it is not then types are not
     // equivalent because they have different class ids and they are not String
-    // or integer or Type.
+    // or integer or List or Type.
     JumpIfNotType(assembler, cid1, scratch, not_equal);
 
     // First type is a Type. Check if the second is a Type too.
diff --git a/runtime/vm/compiler/asm_intrinsifier_ia32.cc b/runtime/vm/compiler/asm_intrinsifier_ia32.cc
index a17f7a8..a6f1c9d 100644
--- a/runtime/vm/compiler/asm_intrinsifier_ia32.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_ia32.cc
@@ -1281,6 +1281,11 @@
              kIfNotInRange, target);
 }
 
+static void JumpIfNotList(Assembler* assembler, Register cid, Label* target) {
+  RangeCheck(assembler, cid, kArrayCid, kGrowableObjectArrayCid, kIfNotInRange,
+             target);
+}
+
 static void JumpIfType(Assembler* assembler, Register cid, Label* target) {
   RangeCheck(assembler, cid, kTypeCid, kFunctionTypeCid, kIfInRange, target);
 }
@@ -1370,7 +1375,7 @@
                                Register scratch,
                                bool testing_instance_cids) {
   Label different_cids, equal_cids_but_generic, not_integer,
-      not_integer_or_string;
+      not_integer_or_string, not_integer_or_string_or_list;
 
   // Check if left hand side is a closure. Closures are handled in the runtime.
   __ cmpl(cid1, Immediate(kClosureCid));
@@ -1392,11 +1397,12 @@
           scratch,
           target::Class::host_type_arguments_field_offset_in_words_offset()));
   __ cmpl(scratch, Immediate(target::Class::kNoTypeArguments));
-  __ j(NOT_EQUAL, &equal_cids_but_generic, Assembler::kNearJump);
+  __ j(NOT_EQUAL, &equal_cids_but_generic);
   __ jmp(equal);
 
   // Class ids are different. Check if we are comparing two string types (with
-  // different representations) or two integer types or two type types.
+  // different representations), two integer types, two list types or two type
+  // types.
   __ Bind(&different_cids);
   __ cmpl(cid1, Immediate(kNumPredefinedCids));
   __ j(ABOVE_EQUAL, not_equal);
@@ -1406,25 +1412,42 @@
   JumpIfNotInteger(assembler, scratch, &not_integer);
 
   // First type is an integer. Check if the second is an integer too.
-  JumpIfInteger(assembler, cid2, equal);
+  __ movl(scratch, cid2);
+  JumpIfInteger(assembler, scratch, equal);
   // Integer types are only equivalent to other integer types.
   __ jmp(not_equal);
 
   __ Bind(&not_integer);
   // Check if both are String types.
-  JumpIfNotString(assembler, cid1,
+  __ movl(scratch, cid1);
+  JumpIfNotString(assembler, scratch,
                   testing_instance_cids ? &not_integer_or_string : not_equal);
 
   // First type is a String. Check if the second is a String too.
-  JumpIfString(assembler, cid2, equal);
+  __ movl(scratch, cid2);
+  JumpIfString(assembler, scratch, equal);
   // String types are only equivalent to other String types.
   __ jmp(not_equal);
 
   if (testing_instance_cids) {
     __ Bind(&not_integer_or_string);
+    // Check if both are List types.
+    __ movl(scratch, cid1);
+    JumpIfNotList(assembler, scratch, &not_integer_or_string_or_list);
+
+    // First type is a List. Check if the second is a List too.
+    __ movl(scratch, cid2);
+    JumpIfNotList(assembler, scratch, not_equal);
+    ASSERT(compiler::target::Array::type_arguments_offset() ==
+           compiler::target::GrowableObjectArray::type_arguments_offset());
+    __ movl(scratch,
+            Immediate(compiler::target::Array::type_arguments_offset()));
+    __ jmp(&equal_cids_but_generic, Assembler::kNearJump);
+
+    __ Bind(&not_integer_or_string_or_list);
     // Check if the first type is a Type. If it is not then types are not
     // equivalent because they have different class ids and they are not String
-    // or integer or Type.
+    // or integer or List or Type.
     JumpIfNotType(assembler, cid1, not_equal);
 
     // First type is a Type. Check if the second is a Type too.
diff --git a/runtime/vm/compiler/asm_intrinsifier_x64.cc b/runtime/vm/compiler/asm_intrinsifier_x64.cc
index b7109e8..3196c49 100644
--- a/runtime/vm/compiler/asm_intrinsifier_x64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_x64.cc
@@ -1184,6 +1184,11 @@
              kIfNotInRange, target);
 }
 
+static void JumpIfNotList(Assembler* assembler, Register cid, Label* target) {
+  RangeCheck(assembler, cid, kArrayCid, kGrowableObjectArrayCid, kIfNotInRange,
+             target);
+}
+
 static void JumpIfType(Assembler* assembler, Register cid, Label* target) {
   RangeCheck(assembler, cid, kTypeCid, kFunctionTypeCid, kIfInRange, target);
 }
@@ -1275,7 +1280,7 @@
                                Register scratch,
                                bool testing_instance_cids) {
   Label different_cids, equal_cids_but_generic, not_integer,
-      not_integer_or_string;
+      not_integer_or_string, not_integer_or_string_or_list;
 
   // Check if left hand side is a closure. Closures are handled in the runtime.
   __ cmpq(cid1, Immediate(kClosureCid));
@@ -1297,11 +1302,12 @@
           scratch,
           target::Class::host_type_arguments_field_offset_in_words_offset()));
   __ cmpl(scratch, Immediate(target::Class::kNoTypeArguments));
-  __ j(NOT_EQUAL, &equal_cids_but_generic, Assembler::kNearJump);
+  __ j(NOT_EQUAL, &equal_cids_but_generic);
   __ jmp(equal);
 
   // Class ids are different. Check if we are comparing two string types (with
-  // different representations) or two integer types or two type types.
+  // different representations), two integer types, two list types or two type
+  // types.
   __ Bind(&different_cids);
   __ cmpq(cid1, Immediate(kNumPredefinedCids));
   __ j(ABOVE_EQUAL, not_equal);
@@ -1311,25 +1317,42 @@
   JumpIfNotInteger(assembler, scratch, &not_integer);
 
   // First type is an integer. Check if the second is an integer too.
-  JumpIfInteger(assembler, cid2, equal);
+  __ movq(scratch, cid2);
+  JumpIfInteger(assembler, scratch, equal);
   // Integer types are only equivalent to other integer types.
   __ jmp(not_equal);
 
   __ Bind(&not_integer);
   // Check if both are String types.
-  JumpIfNotString(assembler, cid1,
+  __ movq(scratch, cid1);
+  JumpIfNotString(assembler, scratch,
                   testing_instance_cids ? &not_integer_or_string : not_equal);
 
   // First type is a String. Check if the second is a String too.
-  JumpIfString(assembler, cid2, equal);
+  __ movq(scratch, cid2);
+  JumpIfString(assembler, scratch, equal);
   // String types are only equivalent to other String types.
   __ jmp(not_equal);
 
   if (testing_instance_cids) {
     __ Bind(&not_integer_or_string);
+    // Check if both are List types.
+    __ movq(scratch, cid1);
+    JumpIfNotList(assembler, scratch, &not_integer_or_string_or_list);
+
+    // First type is a List. Check if the second is a List too.
+    __ movq(scratch, cid2);
+    JumpIfNotList(assembler, scratch, not_equal);
+    ASSERT(compiler::target::Array::type_arguments_offset() ==
+           compiler::target::GrowableObjectArray::type_arguments_offset());
+    __ movq(scratch,
+            Immediate(compiler::target::Array::type_arguments_offset()));
+    __ jmp(&equal_cids_but_generic, Assembler::kNearJump);
+
+    __ Bind(&not_integer_or_string_or_list);
     // Check if the first type is a Type. If it is not then types are not
     // equivalent because they have different class ids and they are not String
-    // or integer or Type.
+    // or integer or List or Type.
     JumpIfNotType(assembler, cid1, not_equal);
 
     // First type is a Type. Check if the second is a Type too.
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index 4473e81..5ce5395 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -1531,13 +1531,11 @@
     Register class_id_reg,
     compiler::Label* is_instance_lbl) {
   assembler()->Comment("ListTypeCheck");
-  compiler::Label unknown;
-  GrowableArray<intptr_t> args;
-  args.Add(kArrayCid);
-  args.Add(kGrowableObjectArrayCid);
-  args.Add(kImmutableArrayCid);
-  CheckClassIds(class_id_reg, args, is_instance_lbl, &unknown);
-  assembler()->Bind(&unknown);
+  COMPILE_ASSERT((kImmutableArrayCid == kArrayCid + 1) &&
+                 (kGrowableObjectArrayCid == kArrayCid + 2));
+  CidRangeVector ranges;
+  ranges.Add({kArrayCid, kGrowableObjectArrayCid});
+  GenerateCidRangesCheck(assembler(), class_id_reg, ranges, is_instance_lbl);
 }
 
 void FlowGraphCompiler::EmitComment(Instruction* instr) {
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index cf52ef4..cfb05b7 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -291,7 +291,7 @@
     if (!obj->IsNewObject() || visiting_old_object_->untag()->IsRemembered()) {
       return;
     }
-    visiting_old_object_->untag()->SetRememberedBit();
+    visiting_old_object_->untag()->SetRememberedBitUnsynchronized();
     thread_->StoreBufferAddObjectGC(visiting_old_object_);
   }
 
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 5c3adc4..9b7ff28 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -607,6 +607,7 @@
     builtin_vtables_[k##clazz##Cid] = fake_handle.vtable();                    \
   }
   CLASS_LIST_NO_OBJECT_NOR_STRING_NOR_ARRAY_NOR_MAP(INIT_VTABLE)
+  INIT_VTABLE(GrowableObjectArray)
 #undef INIT_VTABLE
 
 #define INIT_VTABLE(clazz)                                                     \
@@ -630,7 +631,7 @@
     Array fake_handle;                                                         \
     builtin_vtables_[k##clazz##Cid] = fake_handle.vtable();                    \
   }
-  CLASS_LIST_ARRAYS(INIT_VTABLE)
+  CLASS_LIST_FIXED_LENGTH_ARRAYS(INIT_VTABLE)
 #undef INIT_VTABLE
 
 #define INIT_VTABLE(clazz)                                                     \
diff --git a/runtime/vm/object_graph_copy.cc b/runtime/vm/object_graph_copy.cc
index 76d65b6..1a54883 100644
--- a/runtime/vm/object_graph_copy.cc
+++ b/runtime/vm/object_graph_copy.cc
@@ -1009,6 +1009,7 @@
 
       CLASS_LIST_NO_OBJECT_NOR_STRING_NOR_ARRAY_NOR_MAP(COPY_TO)
       COPY_TO(Array)
+      COPY_TO(GrowableObjectArray)
       COPY_TO(LinkedHashMap)
       COPY_TO(LinkedHashSet)
 #undef COPY_TO
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index f99afbe..5680c3a 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -317,10 +317,14 @@
     ASSERT(IsOldObject());
     return !tags_.Read<OldAndNotRememberedBit>();
   }
-  void SetRememberedBit() {
+  bool TryAcquireRememberedBit() {
+    ASSERT(!IsCardRemembered());
+    return tags_.TryClear<OldAndNotRememberedBit>();
+  }
+  void SetRememberedBitUnsynchronized() {
     ASSERT(!IsRemembered());
     ASSERT(!IsCardRemembered());
-    tags_.UpdateBool<OldAndNotRememberedBit>(false);
+    tags_.UpdateUnsynchronized<OldAndNotRememberedBit>(false);
   }
   void ClearRememberedBit() {
     ASSERT(IsOldObject());
@@ -328,10 +332,10 @@
   }
 
   DART_FORCE_INLINE
-  void AddToRememberedSet(Thread* thread) {
-    ASSERT(!this->IsRemembered());
-    this->SetRememberedBit();
-    thread->StoreBufferAddObject(ObjectPtr(this));
+  void EnsureInRememberedSet(Thread* thread) {
+    if (TryAcquireRememberedBit()) {
+      thread->StoreBufferAddObject(ObjectPtr(this));
+    }
   }
 
   bool IsCardRemembered() const { return tags_.Read<CardRememberedBit>(); }
@@ -665,7 +669,7 @@
       if (value->IsNewObject()) {
         // Generational barrier: record when a store creates an
         // old-and-not-remembered -> new reference.
-        AddToRememberedSet(thread);
+        EnsureInRememberedSet(thread);
       } else {
         // Incremental barrier: record when a store creates an
         // old -> old-and-not-marked reference.
@@ -695,11 +699,9 @@
       if (value->IsNewObject()) {
         // Generational barrier: record when a store creates an
         // old-and-not-remembered -> new reference.
-        ASSERT(!this->IsRemembered());
         if (this->IsCardRemembered()) {
           RememberCard(addr);
-        } else {
-          this->SetRememberedBit();
+        } else if (this->TryAcquireRememberedBit()) {
           thread->StoreBufferAddObject(static_cast<ObjectPtr>(this));
         }
       } else {
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index a545c0e..d6c3ad6 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -483,7 +483,7 @@
   }
 
   if (add_to_remembered_set) {
-    object->untag()->AddToRememberedSet(thread);
+    object->untag()->EnsureInRememberedSet(thread);
   }
 
   // For incremental write barrier elimination, we need to ensure that the
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 6112fa7..4985582 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -5208,7 +5208,6 @@
   {
     JSONArray internals(&map, "List");
     CLASS_LIST_ARRAYS(DEFINE_ADD_VALUE_F_CID)
-    DEFINE_ADD_VALUE_F_CID(GrowableObjectArray)
     DEFINE_ADD_VALUE_F_CID(ByteBuffer)
   }
   {
diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc
index 09a3b25..a9e319e 100644
--- a/runtime/vm/thread.cc
+++ b/runtime/vm/thread.cc
@@ -688,9 +688,7 @@
 
       switch (op_) {
         case Thread::RestoreWriteBarrierInvariantOp::kAddToRememberedSet:
-          if (!obj->untag()->IsRemembered()) {
-            obj->untag()->AddToRememberedSet(current_);
-          }
+          obj->untag()->EnsureInRememberedSet(current_);
           if (current_->is_marking()) {
             current_->DeferredMarkingStackAddObject(obj);
           }
diff --git a/tests/lib/mirrors/regress_b196606044_test.dart b/tests/lib/mirrors/regress_b196606044_test.dart
new file mode 100644
index 0000000..e656519
--- /dev/null
+++ b/tests/lib/mirrors/regress_b196606044_test.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Regression test for b/196606044.
+//
+// Verifies that instance.type.instanceMembers contains 'isNotEmpty'
+// member for a List literal.
+
+import 'package:expect/expect.dart';
+import 'dart:mirrors';
+
+dynamic object = <int>[1, 2, 3];
+String name = 'isNotEmpty';
+
+main() {
+  var instance = reflect(object);
+  var member = instance.type.instanceMembers[new Symbol(name)];
+  Expect.isNotNull(member);
+  var invocation = instance.getField(member!.simpleName);
+  Expect.isNotNull(invocation);
+  Expect.equals(true, invocation.reflectee);
+}
diff --git a/tests/lib_2/mirrors/regress_b196606044_test.dart b/tests/lib_2/mirrors/regress_b196606044_test.dart
new file mode 100644
index 0000000..12df1a2
--- /dev/null
+++ b/tests/lib_2/mirrors/regress_b196606044_test.dart
@@ -0,0 +1,25 @@
+// 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.9
+
+// Regression test for b/196606044.
+//
+// Verifies that instance.type.instanceMembers contains 'isNotEmpty'
+// member for a List literal.
+
+import 'package:expect/expect.dart';
+import 'dart:mirrors';
+
+dynamic object = <int>[1, 2, 3];
+String name = 'isNotEmpty';
+
+main() {
+  var instance = reflect(object);
+  var member = instance.type.instanceMembers[new Symbol(name)];
+  Expect.isNotNull(member);
+  var invocation = instance.getField(member.simpleName);
+  Expect.isNotNull(invocation);
+  Expect.equals(true, invocation.reflectee);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 848e6fb..2155d95 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 19
+PRERELEASE 20
 PRERELEASE_PATCH 0
\ No newline at end of file