Version 2.15.0-185.0.dev

Merge commit 'a242b4d6cc56de2175a8b344e6495ae523fcd4ee' into 'dev'
diff --git a/pkg/analysis_server/lib/src/search/type_hierarchy.dart b/pkg/analysis_server/lib/src/search/type_hierarchy.dart
index 988f96a..4877ec5 100644
--- a/pkg/analysis_server/lib/src/search/type_hierarchy.dart
+++ b/pkg/analysis_server/lib/src/search/type_hierarchy.dart
@@ -52,7 +52,19 @@
     var pivotClass = _pivotClass;
     if (pivotClass != null) {
       _createSuperItem(pivotClass, null);
+      var superLength = _items.length;
       await _createSubclasses(_items[0], 0, pivotClass);
+
+      // sort subclasses only
+      if (_items.length > superLength + 1) {
+        var subList = _items.sublist(superLength);
+        subList
+            .sort((a, b) => a.classElement.name.compareTo(b.classElement.name));
+        for (var i = 0; i < subList.length; i++) {
+          _items[i + superLength] = subList[i];
+        }
+      }
+
       return _items;
     }
     return null;
diff --git a/pkg/analysis_server/test/search/type_hierarchy_test.dart b/pkg/analysis_server/test/search/type_hierarchy_test.dart
index 2cc6f9f..e0fef5d 100644
--- a/pkg/analysis_server/test/search/type_hierarchy_test.dart
+++ b/pkg/analysis_server/test/search/type_hierarchy_test.dart
@@ -431,6 +431,116 @@
     ]);
   }
 
+  Future<void> test_class_order() async {
+    addTestFile('''
+class A {}
+class D extends A {}
+class C extends A {}
+class B extends A {}
+class G extends B {}
+class F extends B {}
+class E extends A {}
+''');
+    var items = await _getTypeHierarchy('A {}');
+    expect(_toJson(items), [
+      {
+        'classElement': {
+          'kind': 'CLASS',
+          'name': 'A',
+          'location': anything,
+          'flags': 0
+        },
+        'superclass': 1,
+        'interfaces': [],
+        'mixins': [],
+        'subclasses': [2, 3, 4, 5]
+      },
+      {
+        'classElement': {
+          'kind': 'CLASS',
+          'name': 'Object',
+          'location': anything,
+          'flags': 0
+        },
+        'interfaces': [],
+        'mixins': [],
+        'subclasses': []
+      },
+      {
+        'classElement': {
+          'kind': 'CLASS',
+          'name': 'B',
+          'location': anything,
+          'flags': 0
+        },
+        'superclass': 0,
+        'interfaces': [],
+        'mixins': [],
+        'subclasses': [6, 7]
+      },
+      {
+        'classElement': {
+          'kind': 'CLASS',
+          'name': 'C',
+          'location': anything,
+          'flags': 0
+        },
+        'superclass': 0,
+        'interfaces': [],
+        'mixins': [],
+        'subclasses': []
+      },
+      {
+        'classElement': {
+          'kind': 'CLASS',
+          'name': 'D',
+          'location': anything,
+          'flags': 0
+        },
+        'superclass': 0,
+        'interfaces': [],
+        'mixins': [],
+        'subclasses': []
+      },
+      {
+        'classElement': {
+          'kind': 'CLASS',
+          'name': 'E',
+          'location': anything,
+          'flags': 0
+        },
+        'superclass': 0,
+        'interfaces': [],
+        'mixins': [],
+        'subclasses': []
+      },
+      {
+        'classElement': {
+          'kind': 'CLASS',
+          'name': 'F',
+          'location': anything,
+          'flags': 0
+        },
+        'superclass': 4,
+        'interfaces': [],
+        'mixins': [],
+        'subclasses': []
+      },
+      {
+        'classElement': {
+          'kind': 'CLASS',
+          'name': 'G',
+          'location': anything,
+          'flags': 0
+        },
+        'superclass': 4,
+        'interfaces': [],
+        'mixins': [],
+        'subclasses': []
+      }
+    ]);
+  }
+
   Future<void> test_class_withTypes() async {
     addTestFile('''
 class MA {}
diff --git a/pkg/analyzer/lib/src/dart/element/extensions.dart b/pkg/analyzer/lib/src/dart/element/extensions.dart
index 66ba586..2b9767d 100644
--- a/pkg/analyzer/lib/src/dart/element/extensions.dart
+++ b/pkg/analyzer/lib/src/dart/element/extensions.dart
@@ -8,7 +8,7 @@
 import 'package:analyzer/src/generated/utilities_dart.dart';
 
 extension ElementExtension on Element {
-  /// Return `true` if this element is an instance member.
+  /// Return `true` if this element is an instance member of a class or mixin.
   ///
   /// Only [MethodElement]s and [PropertyAccessorElement]s are supported.
   /// We intentionally exclude [ConstructorElement]s - they can only be
@@ -18,7 +18,7 @@
   bool get isInstanceMember {
     var this_ = this;
     var enclosing = this_.enclosingElement;
-    if (enclosing is ClassElement || enclosing is ExtensionElement) {
+    if (enclosing is ClassElement) {
       return this_ is MethodElement && !this_.isStatic ||
           this_ is PropertyAccessorElement && !this_.isStatic;
     }
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index 62bf4a9..66844bc 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -271,8 +271,7 @@
         } else if (parent.declaredElement != null) {
           final declaredElement = parent.declaredElement!;
           if (element.isVisibleForOverriding &&
-              (!declaredElement.isInstanceMember ||
-                  declaredElement.enclosingElement is ExtensionElement)) {
+              !declaredElement.isInstanceMember) {
             reportInvalidVisibleForOverriding(declaredElement);
           }
 
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
index a05f61d..006dde9 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
@@ -48,7 +48,8 @@
 
   Future<T> whenComplete(action());
 
-  static Future<List<T>> wait<T>(Iterable<Future<T>> futures) => throw 0;
+  static Future<List<T>> wait<T>(Iterable<Future<T>> futures, 
+    {void cleanUp(T successValue)?}) => throw 0;
 }
 
 abstract class FutureOr<T> {}
@@ -162,6 +163,8 @@
   }
 }
 
+abstract class IterableMixin<E> implements Iterable<E> { }
+
 abstract class LinkedHashMap<K, V> implements Map<K, V> {
   external factory LinkedHashMap(
       {bool Function(K, K)? equals,
@@ -208,6 +211,12 @@
     throw 0;
   }
 }
+
+abstract class ListMixin<E> implements List<E> { }
+
+abstract class MapMixin<K, V> implements Map<K, V> { }
+
+abstract class SetMixin<E> implements Set<E> { }
 ''',
   )
 ]);
@@ -234,6 +243,10 @@
   const JsonCodec();
   String encode(Object? value, {Object? toEncodable(dynamic object)?}) => '';
 }
+
+abstract class StringConversionSink { }
+
+abstract class StringConversionSinkMixin implements StringConversionSink { }
 ''',
     )
   ],
@@ -290,7 +303,12 @@
 
 typedef Comparator<T> = int Function(T a, T b);
 
-class DateTime extends Object {}
+class DateTime extends Object {
+  external DateTime._now();
+  DateTime.now() : this._now();
+  external bool isBefore(DateTime other);
+  external int get millisecondsSinceEpoch;
+}
 
 class Deprecated extends Object {
   final String expires;
@@ -360,6 +378,8 @@
 
   bool get isEven => false;
   bool get isNegative;
+  bool get isOdd;
+  int get sign;
 
   int operator &(int other);
   int operator -();
@@ -371,8 +391,10 @@
   int operator ~();
 
   int abs();
+  int ceil();
   int gcd(int other);
   String toString();
+  int truncate();
 
   external static int parse(String source,
       {int? radix, @deprecated int onError(String source)?});
@@ -389,7 +411,11 @@
   Iterator<E> get iterator;
   int get length;
 
-  bool contains(Object element);
+  const Iterable();
+
+  const factory Iterable.empty() => EmptyIterable<E>();
+
+  bool contains(Object? element);
 
   Iterable<T> expand<T>(Iterable<T> f(E element));
 
@@ -410,6 +436,7 @@
   Set<E> toSet();
 
   Iterable<E> where(bool test(E element));
+  Iterable<T> whereType<T>();
 }
 
 abstract class Iterator<E> {
@@ -436,6 +463,7 @@
   Map<int, E> asMap() {}
   void clear() {}
   int indexOf(Object element);
+  bool remove(Object? value);
   E removeLast() {}
 
   noSuchMethod(Invocation invocation) => null;
@@ -463,8 +491,11 @@
   V? operator [](Object? key);
   void operator []=(K key, V value);
 
+  void addAll(Map<K, V> other);
   Map<RK, RV> cast<RK, RV>();
   bool containsKey(Object? key);
+  void forEach(void action(K key, V value));
+  V putIfAbsent(K key, V ifAbsent());
 }
 
 class Null extends Object {
@@ -541,7 +572,7 @@
 }
 
 abstract class RegExp implements Pattern {
-  external factory RegExp(String source);
+  external factory RegExp(String source, {bool unicode = false});
 }
 
 abstract class Set<E> implements Iterable<E> {
@@ -895,6 +926,7 @@
       "iframe");
 
   String src;
+  set srcdoc(String? value) native;
 }
 
 class OptionElement extends HtmlElement {
@@ -997,6 +1029,10 @@
 
 class Symbol {}
 
+class EmptyIterable<E> implements Iterable<E> {
+  const EmptyIterable();
+}
+
 class ExternalName {
   final String name;
   const ExternalName(this.name);
diff --git a/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart b/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart
index 902e2e7..73f39f6 100644
--- a/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart
@@ -1773,6 +1773,32 @@
     assertType(invocation, 'int');
   }
 
+  test_instance_getter_asSetter() async {
+    await assertErrorsInCode('''
+extension E1 on int {
+  int get foo => 0;
+}
+
+extension E2 on int {
+  int get foo => 0;
+  void f() {
+    foo = 0;
+  }
+}
+''', [
+      error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, 104, 3),
+    ]);
+    assertAssignment(
+      findNode.assignment('foo = 0'),
+      readElement: null,
+      readType: null,
+      writeElement: findElement.getter('foo', of: 'E2'),
+      writeType: 'dynamic',
+      operatorElement: null,
+      type: 'int',
+    );
+  }
+
   test_instance_getter_fromInstance() async {
     await assertNoErrorsInCode('''
 class C {
@@ -1978,6 +2004,28 @@
     assertElement(prefix, findElement.method('unary-', of: 'E'));
   }
 
+  test_instance_setter_asGetter() async {
+    await assertErrorsInCode('''
+extension E1 on int {
+  set foo(int _) {}
+}
+
+extension E2 on int {
+  set foo(int _) {}
+  void f() {
+    foo;
+  }
+}
+''', [
+      error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 104, 3),
+    ]);
+    assertSimpleIdentifier(
+      findNode.simple('foo;'),
+      element: null,
+      type: 'dynamic',
+    );
+  }
+
   test_instance_setter_fromInstance() async {
     await assertNoErrorsInCode('''
 class C {
diff --git a/pkg/analyzer/test/verify_diagnostics_test.dart b/pkg/analyzer/test/verify_diagnostics_test.dart
index d8d7076..f1554cf 100644
--- a/pkg/analyzer/test/verify_diagnostics_test.dart
+++ b/pkg/analyzer/test/verify_diagnostics_test.dart
@@ -8,12 +8,12 @@
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
-import 'package:analyzer/src/dart/ast/token.dart';
 import 'package:path/path.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import '../tool/diagnostics/generate.dart';
+import '../tool/messages/error_code_documentation_info.dart';
 import 'src/dart/resolution/context_collection_resolution.dart';
 
 main() {
@@ -111,18 +111,6 @@
     'PubspecWarningCode.UNNECESSARY_DEV_DEPENDENCY',
   ];
 
-  /// The prefix used on directive lines to specify the experiments that should
-  /// be enabled for a snippet.
-  static const String experimentsPrefix = '%experiments=';
-
-  /// The prefix used on directive lines to specify the language version for
-  /// the snippet.
-  static const String languagePrefix = '%language=';
-
-  /// The prefix used on directive lines to indicate the uri of an auxiliary
-  /// file that is needed for testing purposes.
-  static const String uriDirectivePrefix = '%uri="';
-
   /// The absolute paths of the files containing the declarations of the error
   /// codes.
   final List<CodePath> codePaths;
@@ -174,30 +162,6 @@
     return variable.name.name;
   }
 
-  /// Extract documentation from the given [field] declaration.
-  List<String>? _extractDoc(FieldDeclaration field) {
-    var comments = field.firstTokenAfterCommentAndMetadata.precedingComments;
-    if (comments == null) {
-      return null;
-    }
-    List<String> docs = [];
-    while (comments != null) {
-      String lexeme = comments.lexeme;
-      if (lexeme.startsWith('// TODO')) {
-        break;
-      } else if (lexeme.startsWith('// ')) {
-        docs.add(lexeme.substring(3));
-      } else if (lexeme == '//') {
-        docs.add('');
-      }
-      comments = comments.next as CommentToken?;
-    }
-    if (docs.isEmpty) {
-      return null;
-    }
-    return docs;
-  }
-
   _SnippetData _extractSnippetData(
     String snippet,
     bool errorRequired,
@@ -232,52 +196,32 @@
         languageVersion);
   }
 
-  /// Extract the snippets of Dart code between the start (inclusive) and end
-  /// (exclusive) indexes.
+  /// Extract the snippets of Dart code from [documentationParts] that are
+  /// tagged as belonging to the given [blockSection].
   List<_SnippetData> _extractSnippets(
-      List<String> lines, int start, int end, bool errorRequired) {
+      List<ErrorCodeDocumentationPart> documentationParts,
+      BlockSection blockSection) {
     var snippets = <_SnippetData>[];
     var auxiliaryFiles = <String, String>{};
-    List<String>? experiments;
-    String? languageVersion;
-    var currentStart = -1;
-    for (var i = start; i < end; i++) {
-      var line = lines[i];
-      if (line == '```') {
-        if (currentStart < 0) {
-          _reportProblem('Snippet without file type on line $i.');
-          return snippets;
+    for (var documentationPart in documentationParts) {
+      if (documentationPart is ErrorCodeDocumentationBlock) {
+        if (documentationPart.containingSection != blockSection) {
+          continue;
         }
-        var secondLine = lines[currentStart + 1];
-        if (secondLine.startsWith(uriDirectivePrefix)) {
-          var name = secondLine.substring(
-              uriDirectivePrefix.length, secondLine.length - 1);
-          var content = lines.sublist(currentStart + 2, i).join('\n');
-          auxiliaryFiles[name] = content;
-        } else if (lines[currentStart] == '```dart') {
-          if (secondLine.startsWith(experimentsPrefix)) {
-            experiments = secondLine
-                .substring(experimentsPrefix.length)
-                .split(',')
-                .map((e) => e.trim())
-                .toList();
-            currentStart++;
-          } else if (secondLine.startsWith(languagePrefix)) {
-            languageVersion = secondLine.substring(languagePrefix.length);
-            currentStart++;
+        var uri = documentationPart.uri;
+        if (uri != null) {
+          auxiliaryFiles[uri] = documentationPart.text;
+        } else {
+          if (documentationPart.fileType == 'dart') {
+            snippets.add(_extractSnippetData(
+                documentationPart.text,
+                blockSection == BlockSection.examples,
+                auxiliaryFiles,
+                documentationPart.experiments,
+                documentationPart.languageVersion));
           }
-          var content = lines.sublist(currentStart + 1, i).join('\n');
-          snippets.add(_extractSnippetData(content, errorRequired,
-              auxiliaryFiles, experiments ?? [], languageVersion));
           auxiliaryFiles = <String, String>{};
         }
-        currentStart = -1;
-      } else if (line.startsWith('```')) {
-        if (currentStart >= 0) {
-          _reportProblem('Snippet before line $i was not closed.');
-          return snippets;
-        }
-        currentStart = i;
       }
     }
     return snippets;
@@ -329,7 +273,7 @@
         String className = declaration.name.name;
         for (ClassMember member in declaration.members) {
           if (member is FieldDeclaration) {
-            var docs = _extractDoc(member);
+            var docs = parseErrorCodeDocumentation(member);
             if (docs != null) {
               VariableDeclaration variable = member.fields.variables[0];
               codeName = _extractCodeName(variable);
@@ -342,11 +286,8 @@
               }
               hasWrittenVariableName = false;
 
-              int exampleStart = docs.indexOf('#### Examples');
-              int fixesStart = docs.indexOf('#### Common fixes');
-
               List<_SnippetData> exampleSnippets =
-                  _extractSnippets(docs, exampleStart + 1, fixesStart, true);
+                  _extractSnippets(docs, BlockSection.examples);
               _SnippetData? firstExample;
               if (exampleSnippets.isEmpty) {
                 _reportProblem('No example.');
@@ -358,7 +299,7 @@
               }
 
               List<_SnippetData> fixesSnippets =
-                  _extractSnippets(docs, fixesStart + 1, docs.length, false);
+                  _extractSnippets(docs, BlockSection.commonFixes);
               for (int i = 0; i < fixesSnippets.length; i++) {
                 _SnippetData snippet = fixesSnippets[i];
                 if (firstExample != null) {
diff --git a/pkg/analyzer/tool/diagnostics/generate.dart b/pkg/analyzer/tool/diagnostics/generate.dart
index 9ed18ff..7d0b2f9 100644
--- a/pkg/analyzer/tool/diagnostics/generate.dart
+++ b/pkg/analyzer/tool/diagnostics/generate.dart
@@ -9,10 +9,11 @@
 import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
-import 'package:analyzer/src/dart/ast/token.dart';
 import 'package:analyzer_utilities/package_root.dart' as package_root;
 import 'package:path/src/context.dart';
 
+import '../messages/error_code_documentation_info.dart';
+
 /// Generate the file `diagnostics.md` based on the documentation associated
 /// with the declarations of the error codes.
 void main() async {
@@ -95,8 +96,8 @@
   /// The messages associated with the diagnostic.
   List<String> messages;
 
-  /// The lines of documentation associated with the diagnostic.
-  List<String>? documentation;
+  /// The documentation text associated with the diagnostic.
+  String? documentation;
 
   /// Initialize a newly created information holder with the given [name] and
   /// [message].
@@ -123,9 +124,7 @@
       }
     }
     sink.writeln();
-    for (String line in documentation!) {
-      sink.writeln(line);
-    }
+    sink.writeln(documentation!);
   }
 
   /// Return a version of the [text] in which characters that have special
@@ -244,43 +243,15 @@
   }
 
   /// Extract documentation from the given [field] declaration.
-  List<String>? _extractDoc(FieldDeclaration field) {
-    var comments = field.firstTokenAfterCommentAndMetadata.precedingComments;
-    if (comments == null) {
-      return null;
+  String _extractDoc(FieldDeclaration field) {
+    var parsedComment = parseErrorCodeDocumentation(field);
+    if (parsedComment == null) {
+      return '';
     }
-    List<String> docs = [];
-    bool inDartCodeBlock = false;
-    while (comments != null) {
-      String lexeme = comments.lexeme;
-      if (lexeme.startsWith('// TODO')) {
-        break;
-      } else if (lexeme.startsWith('// %')) {
-        // Ignore lines containing directives for testing support.
-      } else if (lexeme.startsWith('// ')) {
-        String trimmedLine = lexeme.substring(3);
-        if (trimmedLine == '```dart') {
-          inDartCodeBlock = true;
-          docs.add('{% prettify dart tag=pre+code %}');
-        } else if (trimmedLine == '```') {
-          if (inDartCodeBlock) {
-            docs.add('{% endprettify %}');
-            inDartCodeBlock = false;
-          } else {
-            docs.add(trimmedLine);
-          }
-        } else {
-          docs.add(trimmedLine);
-        }
-      } else if (lexeme == '//') {
-        docs.add('');
-      }
-      comments = comments.next as CommentToken?;
-    }
-    if (docs.isEmpty) {
-      return null;
-    }
-    return docs;
+    return [
+      for (var documentationPart in parsedComment)
+        documentationPart.formatForDocumentation()
+    ].join('\n');
   }
 
   /// Extract documentation from the file that was parsed to produce the given
@@ -301,7 +272,7 @@
                 variable.initializer!, generatedResult);
             if (info != null) {
               var docs = _extractDoc(member);
-              if (docs != null) {
+              if (docs.isNotEmpty) {
                 if (info.documentation != null) {
                   throw StateError(
                       'Documentation defined multiple times for ${info.name}');
diff --git a/pkg/analyzer/tool/messages/error_code_documentation_info.dart b/pkg/analyzer/tool/messages/error_code_documentation_info.dart
new file mode 100644
index 0000000..37a4a76
--- /dev/null
+++ b/pkg/analyzer/tool/messages/error_code_documentation_info.dart
@@ -0,0 +1,247 @@
+// 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:convert';
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/token.dart';
+
+/// Extracts comments from the declaration of [field] and interprets them as a
+/// list of [ErrorCodeDocumentationPart] objects.  These objects represent
+/// user-publishable documentation about the error code, along with code blocks
+/// illustrating when the error occurs and how to fix it.
+List<ErrorCodeDocumentationPart>? parseErrorCodeDocumentation(
+    FieldDeclaration field) {
+  var comments = field.firstTokenAfterCommentAndMetadata.precedingComments;
+  if (comments == null) {
+    return null;
+  }
+  var className = (field.parent as ClassDeclaration).name.name;
+  var errorName = field.fields.variables.single.name.name;
+  var commentLines = <String>[];
+  while (comments != null) {
+    String lexeme = comments.lexeme;
+    if (lexeme.startsWith('// ')) {
+      commentLines.add(lexeme.substring(3));
+    } else if (lexeme == '//') {
+      commentLines.add('');
+    }
+    comments = comments.next as CommentToken?;
+  }
+  if (commentLines.isEmpty) {
+    return null;
+  }
+  var parser =
+      _ErrorCodeDocumentationParser('$className.$errorName', commentLines);
+  parser.parse();
+  return parser.result;
+}
+
+/// Enum representing the different documentation sections in which an
+/// [ErrorCodeDocumentationBlock] might appear.
+enum BlockSection {
+  // The "Examples" section, where we give examples of code that generates the
+  // error.
+  examples,
+
+  // The "Common fixes" section, where we give examples of code that doesn't
+  // generate the error.
+  commonFixes,
+}
+
+/// An [ErrorCodeDocumentationPart] containing a block of code.
+class ErrorCodeDocumentationBlock extends ErrorCodeDocumentationPart {
+  /// The code itself.
+  final String text;
+
+  /// The section this block is contained in.
+  final BlockSection containingSection;
+
+  /// A list of the experiments that need to be enabled for this code to behave
+  /// as expected (if any).
+  final List<String> experiments;
+
+  /// The file type of this code block (e.g. `dart` or `yaml`).
+  final String fileType;
+
+  /// The language version that must be active for this code to behave as
+  /// expected (if any).
+  final String? languageVersion;
+
+  /// If this code is an auxiliary file that supports other blocks, the URI of
+  /// the file.
+  final String? uri;
+
+  ErrorCodeDocumentationBlock(this.text,
+      {required this.containingSection,
+      this.experiments = const [],
+      required this.fileType,
+      this.languageVersion,
+      this.uri});
+
+  @override
+  String formatForDocumentation() => fileType == 'dart'
+      ? ['{% prettify dart tag=pre+code %}', text, '{% endprettify %}']
+          .join('\n')
+      : ['```$fileType', text, '```'].join('\n');
+}
+
+/// A portion of an error code's documentation.  This could be free form
+/// markdown text ([ErrorCodeDocumentationText]) or a code block
+/// ([ErrorCodeDocumentationBlock]).
+abstract class ErrorCodeDocumentationPart {
+  /// Formats this documentation part as text suitable for inclusion in the
+  /// analyzer's `diagnostics.md` file.
+  String formatForDocumentation();
+}
+
+/// An [ErrorCodeDocumentationPart] containing free form markdown text.
+class ErrorCodeDocumentationText extends ErrorCodeDocumentationPart {
+  /// The text, in markdown format.
+  final String text;
+
+  ErrorCodeDocumentationText(this.text);
+
+  @override
+  String formatForDocumentation() => text;
+}
+
+class _ErrorCodeDocumentationParser {
+  /// The prefix used on directive lines to specify the experiments that should
+  /// be enabled for a snippet.
+  static const String experimentsPrefix = '%experiments=';
+
+  /// The prefix used on directive lines to specify the language version for
+  /// the snippet.
+  static const String languagePrefix = '%language=';
+
+  /// The prefix used on directive lines to indicate the uri of an auxiliary
+  /// file that is needed for testing purposes.
+  static const String uriDirectivePrefix = '%uri="';
+
+  final String errorCode;
+
+  final List<String> commentLines;
+
+  final List<ErrorCodeDocumentationPart> result = [];
+
+  int currentLineNumber = 0;
+
+  String? currentSection;
+
+  _ErrorCodeDocumentationParser(this.errorCode, this.commentLines);
+
+  bool get done => currentLineNumber >= commentLines.length;
+
+  String get line => commentLines[currentLineNumber];
+
+  BlockSection computeCurrentBlockSection() {
+    switch (currentSection) {
+      case '#### Example':
+      case '#### Examples':
+        return BlockSection.examples;
+      case '#### Common fixes':
+        return BlockSection.commonFixes;
+      case null:
+        problem('Code block before section header');
+      default:
+        problem('Code block in invalid section ${json.encode(currentSection)}');
+    }
+  }
+
+  void parse() {
+    var textLines = <String>[];
+
+    void flushText() {
+      if (textLines.isNotEmpty) {
+        result.add(ErrorCodeDocumentationText(textLines.join('\n')));
+        textLines = [];
+      }
+    }
+
+    while (!done) {
+      if (line.startsWith('TODO')) {
+        // Everything after the "TODO" is ignored.
+        break;
+      } else if (line.startsWith('%')) {
+        problem('% directive outside code block');
+      } else if (line.startsWith('```')) {
+        flushText();
+        processCodeBlock();
+      } else {
+        if (line.startsWith('#') && !line.startsWith('#####')) {
+          currentSection = line;
+        }
+        textLines.add(line);
+        currentLineNumber++;
+      }
+    }
+    flushText();
+  }
+
+  Never problem(String explanation) {
+    throw 'In documentation for $errorCode, at line ${currentLineNumber + 1}, '
+        '$explanation';
+  }
+
+  void processCodeBlock() {
+    var containingSection = computeCurrentBlockSection();
+    var codeLines = <String>[];
+    String? languageVersion;
+    String? uri;
+    List<String>? experiments;
+    assert(line.startsWith('```'));
+    var fileType = line.substring(3);
+    if (fileType.isEmpty) {
+      problem('Code blocks should have a file type, e.g. "```dart"');
+    }
+    ++currentLineNumber;
+    while (true) {
+      if (done) {
+        problem('Unterminated code block');
+      } else if (line.startsWith('```')) {
+        if (line != '```') {
+          problem('Code blocks should end with "```"');
+        }
+        ++currentLineNumber;
+        result.add(ErrorCodeDocumentationBlock(codeLines.join('\n'),
+            containingSection: containingSection,
+            experiments: experiments ?? const [],
+            fileType: fileType,
+            languageVersion: languageVersion,
+            uri: uri));
+        return;
+      } else if (line.startsWith('%')) {
+        if (line.startsWith(languagePrefix)) {
+          if (languageVersion != null) {
+            problem('Multiple language version directives');
+          }
+          languageVersion = line.substring(languagePrefix.length);
+        } else if (line.startsWith(uriDirectivePrefix)) {
+          if (uri != null) {
+            problem('Multiple URI directives');
+          }
+          if (!line.endsWith('"')) {
+            problem('URI directive should be surrounded by double quotes');
+          }
+          uri = line.substring(uriDirectivePrefix.length, line.length - 1);
+        } else if (line.startsWith(experimentsPrefix)) {
+          if (experiments != null) {
+            problem('Multiple experiments directives');
+          }
+          experiments = line
+              .substring(experimentsPrefix.length)
+              .split(',')
+              .map((e) => e.trim())
+              .toList();
+        } else {
+          problem('Unrecognized directive ${json.encode(line)}');
+        }
+      } else {
+        codeLines.add(line);
+      }
+      ++currentLineNumber;
+    }
+  }
+}
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 09b4aea..bc87f29 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -267,17 +267,19 @@
       }
       GlobalTypeInferenceResults globalTypeInferenceResults =
           performGlobalTypeInference(closedWorldAndIndices.closedWorld);
+      var indices = closedWorldAndIndices.indices;
       if (options.writeDataUri != null) {
         if (options.noClosedWorldInData) {
           serializationTask.serializeGlobalTypeInference(
-              globalTypeInferenceResults, closedWorldAndIndices.indices);
+              globalTypeInferenceResults, indices);
         } else {
           serializationTask
               .serializeGlobalTypeInferenceLegacy(globalTypeInferenceResults);
         }
         return;
       }
-      await generateJavaScriptCode(globalTypeInferenceResults);
+      await generateJavaScriptCode(globalTypeInferenceResults,
+          indices: indices);
     } else if (onlyPerformCodegen) {
       GlobalTypeInferenceResults globalTypeInferenceResults;
       ir.Component component =
@@ -291,7 +293,8 @@
               abstractValueStrategy,
               component,
               closedWorldAndIndices);
-      await generateJavaScriptCode(globalTypeInferenceResults);
+      await generateJavaScriptCode(globalTypeInferenceResults,
+          indices: closedWorldAndIndices.indices);
     } else if (options.readDataUri != null) {
       // TODO(joshualitt) delete and clean up after google3 roll
       var globalTypeInferenceResults =
@@ -324,7 +327,8 @@
   }
 
   void generateJavaScriptCode(
-      GlobalTypeInferenceResults globalTypeInferenceResults) async {
+      GlobalTypeInferenceResults globalTypeInferenceResults,
+      {DataSourceIndices indices}) async {
     JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
     backendStrategy.registerJClosedWorld(closedWorld);
     phase = PHASE_COMPILING;
@@ -333,8 +337,8 @@
 
     if (options.readCodegenUri != null) {
       CodegenResults codegenResults =
-          await serializationTask.deserializeCodegen(
-              backendStrategy, globalTypeInferenceResults, codegenInputs);
+          await serializationTask.deserializeCodegen(backendStrategy,
+              globalTypeInferenceResults, codegenInputs, indices);
       reporter.log('Compiling methods');
       runCodegenEnqueuer(codegenResults);
     } else {
@@ -344,7 +348,8 @@
           codegenInputs,
           backendStrategy.functionCompiler);
       if (options.writeCodegenUri != null) {
-        serializationTask.serializeCodegen(backendStrategy, codegenResults);
+        serializationTask.serializeCodegen(
+            backendStrategy, codegenResults, indices);
       } else {
         runCodegenEnqueuer(codegenResults);
       }
diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart
index b4943f1..071727d 100644
--- a/pkg/compiler/lib/src/serialization/task.dart
+++ b/pkg/compiler/lib/src/serialization/task.dart
@@ -353,8 +353,8 @@
 
   // TODO(joshualitt): Investigate whether closed world indices can be shared
   // with codegen.
-  void serializeCodegen(
-      BackendStrategy backendStrategy, CodegenResults codegenResults) {
+  void serializeCodegen(BackendStrategy backendStrategy,
+      CodegenResults codegenResults, DataSourceIndices indices) {
     GlobalTypeInferenceResults globalTypeInferenceResults =
         codegenResults.globalTypeInferenceResults;
     JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
@@ -373,7 +373,8 @@
     measureSubtask('serialize codegen', () {
       Uri uri = Uri.parse('${_options.writeCodegenUri}$shard');
       api.BinaryOutputSink dataOutput = _outputProvider.createBinarySink(uri);
-      DataSink sink = BinarySink(BinaryOutputSinkAdapter(dataOutput));
+      DataSink sink = BinarySink(BinaryOutputSinkAdapter(dataOutput),
+          importedIndices: indices);
       _reporter.log('Writing data to ${uri}');
       sink.registerEntityWriter(entityWriter);
       sink.registerCodegenWriter(CodegenWriterImpl(closedWorld));
@@ -388,7 +389,8 @@
   Future<CodegenResults> deserializeCodegen(
       BackendStrategy backendStrategy,
       GlobalTypeInferenceResults globalTypeInferenceResults,
-      CodegenInputs codegenInputs) async {
+      CodegenInputs codegenInputs,
+      DataSourceIndices indices) async {
     int shards = _options.codegenShards;
     JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
     Map<MemberEntity, CodegenResult> results = {};
@@ -401,7 +403,7 @@
         // TODO(36983): This code is extracted because there appeared to be a
         // memory leak for large buffer held by `source`.
         _deserializeCodegenInput(
-            backendStrategy, closedWorld, uri, dataInput, results);
+            backendStrategy, closedWorld, uri, dataInput, indices, results);
         dataInput.release();
       });
     }
@@ -414,9 +416,10 @@
       JClosedWorld closedWorld,
       Uri uri,
       api.Input<List<int>> dataInput,
+      DataSourceIndices importedIndices,
       Map<MemberEntity, CodegenResult> results) {
-    DataSource source =
-        BinarySourceImpl(dataInput.data, stringInterner: _stringInterner);
+    DataSource source = BinarySourceImpl(dataInput.data,
+        stringInterner: _stringInterner, importedIndices: importedIndices);
     backendStrategy.prepareCodegenReader(source);
     Map<MemberEntity, CodegenResult> codegenResults =
         source.readMemberMap((MemberEntity member) {
diff --git a/pkg/compiler/tool/modular_test_suite.dart b/pkg/compiler/tool/modular_test_suite.dart
index 4d42ff3..c045cc1 100644
--- a/pkg/compiler/tool/modular_test_suite.dart
+++ b/pkg/compiler/tool/modular_test_suite.dart
@@ -336,6 +336,7 @@
       if (useModularAnalysis)
         '${Flags.readModularAnalysis}=${dataDependencies.join(',')}',
       '${Flags.writeClosedWorld}=${toUri(module, closedWorldId)}',
+      Flags.noClosedWorldInData,
       '--out=${toUri(module, globalUpdatedDillId)}',
     ];
     var result =
diff --git a/runtime/tests/concurrency/run_stress_test_shards.dart b/runtime/tests/concurrency/run_stress_test_shards.dart
index 87eb9ab..d1d1d80 100644
--- a/runtime/tests/concurrency/run_stress_test_shards.dart
+++ b/runtime/tests/concurrency/run_stress_test_shards.dart
@@ -39,10 +39,14 @@
   forwardStream(process.stderr, stderr);
   final int exitCode = await process.exitCode;
   if (exitCode != 0) {
-    final crashNr = crashCounter++;
-    print('=> Running "$executable ${args.join(' ')}" failed with $exitCode');
-    print('=> Possible crash $crashNr (pid: ${process.pid})');
-    crashes.add(PotentialCrash('crash-$crashNr', process.pid, [executable]));
+    // Ignore normal exceptions and compile-time errors for the purpose of
+    // crashdump reporting.
+    if (exitCode != 255 && exitCode != 254) {
+      final crashNr = crashCounter++;
+      print('=> Running "$executable ${args.join(' ')}" failed with $exitCode');
+      print('=> Possible crash $crashNr (pid: ${process.pid})');
+      crashes.add(PotentialCrash('crash-$crashNr', process.pid, [executable]));
+    }
     io.exitCode = 255; // Make this shard fail.
     return false;
   }
diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc
index 1d74f0a..85e1bcb 100644
--- a/runtime/vm/app_snapshot.cc
+++ b/runtime/vm/app_snapshot.cc
@@ -2290,9 +2290,35 @@
       s->WriteUnsigned(length);
       uint8_t* entry_bits = pool->untag()->entry_bits();
       for (intptr_t j = 0; j < length; j++) {
-        s->Write<uint8_t>(entry_bits[j]);
         UntaggedObjectPool::Entry& entry = pool->untag()->data()[j];
-        switch (ObjectPool::TypeBits::decode(entry_bits[j])) {
+        uint8_t bits = entry_bits[j];
+        ObjectPool::EntryType type = ObjectPool::TypeBits::decode(bits);
+        if (weak && (type == ObjectPool::EntryType::kTaggedObject)) {
+          // By default, every switchable call site will put (ic_data, code)
+          // into the object pool. The [code] is initialized (at AOT
+          // compile-time) to be [StubCode::SwitchableCallMiss] or
+          // [StubCode::MegamorphicCall].
+          //
+          // In --use-bare-instruction we reduce the extra indirection via
+          // the [code] object and store instead (ic_data, entrypoint) in
+          // the object pool.
+          //
+          // Since the actual [entrypoint] is only known at AOT runtime we
+          // switch all existing entries for these stubs to entrypoints
+          // encoded as EntryType::kSwitchableCallMissEntryPoint and
+          // EntryType::kMegamorphicCallEntryPoint.
+          if (entry.raw_obj_ == StubCode::SwitchableCallMiss().ptr()) {
+            type = ObjectPool::EntryType::kSwitchableCallMissEntryPoint;
+            bits = ObjectPool::EncodeBits(type,
+                                          ObjectPool::Patchability::kPatchable);
+          } else if (entry.raw_obj_ == StubCode::MegamorphicCall().ptr()) {
+            type = ObjectPool::EntryType::kMegamorphicCallEntryPoint;
+            bits = ObjectPool::EncodeBits(type,
+                                          ObjectPool::Patchability::kPatchable);
+          }
+        }
+        s->Write<uint8_t>(bits);
+        switch (type) {
           case ObjectPool::EntryType::kTaggedObject: {
             if ((entry.raw_obj_ == StubCode::CallNoScopeNative().ptr()) ||
                 (entry.raw_obj_ == StubCode::CallAutoScopeNative().ptr())) {
@@ -2319,6 +2345,11 @@
             // Write nothing. Will initialize with the lazy link entry.
             break;
           }
+          case ObjectPool::EntryType::kSwitchableCallMissEntryPoint:
+          case ObjectPool::EntryType::kMegamorphicCallEntryPoint:
+            // Write nothing. Entry point is initialized during
+            // snapshot deserialization.
+            break;
           default:
             UNREACHABLE();
         }
@@ -2351,6 +2382,17 @@
   void ReadFill(Deserializer* d, bool primary) {
     ASSERT(!is_canonical());  // Never canonical.
     fill_position_ = d->position();
+    const uint8_t immediate_bits =
+        ObjectPool::EncodeBits(ObjectPool::EntryType::kImmediate,
+                               ObjectPool::Patchability::kPatchable);
+    uword switchable_call_miss_entry_point = 0;
+    uword megamorphic_call_entry_point = 0;
+    if (FLAG_use_bare_instructions) {
+      switchable_call_miss_entry_point =
+          StubCode::SwitchableCallMiss().MonomorphicEntryPoint();
+      megamorphic_call_entry_point =
+          StubCode::MegamorphicCall().MonomorphicEntryPoint();
+    }
 
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       const intptr_t length = d->ReadUnsigned();
@@ -2375,6 +2417,18 @@
             entry.raw_value_ = static_cast<intptr_t>(new_entry);
             break;
           }
+          case ObjectPool::EntryType::kSwitchableCallMissEntryPoint:
+            ASSERT(FLAG_use_bare_instructions);
+            pool->untag()->entry_bits()[j] = immediate_bits;
+            entry.raw_value_ =
+                static_cast<intptr_t>(switchable_call_miss_entry_point);
+            break;
+          case ObjectPool::EntryType::kMegamorphicCallEntryPoint:
+            ASSERT(FLAG_use_bare_instructions);
+            pool->untag()->entry_bits()[j] = immediate_bits;
+            entry.raw_value_ =
+                static_cast<intptr_t>(megamorphic_call_entry_point);
+            break;
           default:
             UNREACHABLE();
         }
@@ -6894,6 +6948,8 @@
   // Code cluster should be deserialized before Function as
   // FunctionDeserializationCluster::ReadFill uses instructions table
   // which is filled in CodeDeserializationCluster::ReadFill.
+  // Code cluster should also precede ObjectPool as its ReadFill uses
+  // entry points of stubs.
   ADD_NON_CANONICAL_NEXT(kCodeCid)
   // The function cluster should be deserialized before any closures, as
   // PostLoad for closures caches the entry point found in the function.
@@ -8305,7 +8361,6 @@
   ProgramDeserializationRoots roots(thread_->isolate_group()->object_store());
   deserializer.Deserialize(&roots);
 
-  PatchGlobalObjectPool();
   InitializeBSS();
 
   return ApiError::null();
@@ -8351,52 +8406,11 @@
   UnitDeserializationRoots roots(unit);
   deserializer.Deserialize(&roots);
 
-  PatchGlobalObjectPool();
   InitializeBSS();
 
   return ApiError::null();
 }
 
-void FullSnapshotReader::PatchGlobalObjectPool() {
-#if defined(DART_PRECOMPILED_RUNTIME)
-  if (FLAG_use_bare_instructions) {
-    // By default, every switchable call site will put (ic_data, code) into the
-    // object pool.  The [code] is initialized (at AOT compile-time) to be a
-    // [StubCode::SwitchableCallMiss].
-    //
-    // In --use-bare-instruction we reduce the extra indirection via the [code]
-    // object and store instead (ic_data, entrypoint) in the object pool.
-    //
-    // Since the actual [entrypoint] is only known at AOT runtime we switch all
-    // existing UnlinkedCall entries in the object pool to be it's entrypoint.
-    auto zone = thread_->zone();
-    const auto& pool = ObjectPool::Handle(
-        zone, ObjectPool::RawCast(
-                  isolate_group()->object_store()->global_object_pool()));
-    auto& entry = Object::Handle(zone);
-    auto& smi = Smi::Handle(zone);
-    for (intptr_t i = 0; i < pool.Length(); i++) {
-      if (pool.TypeAt(i) == ObjectPool::EntryType::kTaggedObject) {
-        entry = pool.ObjectAt(i);
-        if (entry.ptr() == StubCode::SwitchableCallMiss().ptr()) {
-          smi = Smi::FromAlignedAddress(
-              StubCode::SwitchableCallMiss().MonomorphicEntryPoint());
-          pool.SetTypeAt(i, ObjectPool::EntryType::kImmediate,
-                         ObjectPool::Patchability::kPatchable);
-          pool.SetObjectAt(i, smi);
-        } else if (entry.ptr() == StubCode::MegamorphicCall().ptr()) {
-          smi = Smi::FromAlignedAddress(
-              StubCode::MegamorphicCall().MonomorphicEntryPoint());
-          pool.SetTypeAt(i, ObjectPool::EntryType::kImmediate,
-                         ObjectPool::Patchability::kPatchable);
-          pool.SetObjectAt(i, smi);
-        }
-      }
-    }
-  }
-#endif  // defined(DART_PRECOMPILED_RUNTIME)
-}
-
 void FullSnapshotReader::InitializeBSS() {
 #if defined(DART_PRECOMPILED_RUNTIME)
   // Initialize entries in the isolate portion of the BSS segment.
diff --git a/runtime/vm/app_snapshot.h b/runtime/vm/app_snapshot.h
index da96ab4..7bc3032 100644
--- a/runtime/vm/app_snapshot.h
+++ b/runtime/vm/app_snapshot.h
@@ -805,7 +805,6 @@
   IsolateGroup* isolate_group() const { return thread_->isolate_group(); }
 
   ApiErrorPtr ConvertToApiError(char* message);
-  void PatchGlobalObjectPool();
   void InitializeBSS();
 
   Snapshot::Kind kind_;
diff --git a/runtime/vm/compiler/assembler/object_pool_builder.h b/runtime/vm/compiler/assembler/object_pool_builder.h
index db77eb8..e33ded6 100644
--- a/runtime/vm/compiler/assembler/object_pool_builder.h
+++ b/runtime/vm/compiler/assembler/object_pool_builder.h
@@ -30,6 +30,14 @@
     kImmediate,
     kNativeFunction,
     kNativeFunctionWrapper,
+
+    // Used only during AOT snapshot serialization/deserialization.
+    // Denotes kImmediate entry with
+    //  - StubCode::SwitchableCallMiss().MonomorphicEntryPoint()
+    //  - StubCode::MegamorphicCall().MonomorphicEntryPoint()
+    // values which become known only at run time.
+    kSwitchableCallMissEntryPoint,
+    kMegamorphicCallEntryPoint,
   };
 
   using TypeBits = BitField<uint8_t, EntryType, 0, 7>;
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index c76665a..7959652 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -5254,10 +5254,13 @@
     return PatchableBit::decode(untag()->entry_bits()[index]);
   }
 
+  static uint8_t EncodeBits(EntryType type, Patchability patchable) {
+    return PatchableBit::encode(patchable) | TypeBits::encode(type);
+  }
+
   void SetTypeAt(intptr_t index, EntryType type, Patchability patchable) const {
     ASSERT(index >= 0 && index <= Length());
-    const uint8_t bits =
-        PatchableBit::encode(patchable) | TypeBits::encode(type);
+    const uint8_t bits = EncodeBits(type, patchable);
     StoreNonPointer(&untag()->entry_bits()[index], bits);
   }
 
diff --git a/runtime/vm/timer.cc b/runtime/vm/timer.cc
index 4757a5e..894966f 100644
--- a/runtime/vm/timer.cc
+++ b/runtime/vm/timer.cc
@@ -5,5 +5,14 @@
 #include "vm/timer.h"
 #include "platform/globals.h"
 #include "vm/json_stream.h"
+#include "vm/thread.h"
 
-namespace dart {}  // namespace dart
+namespace dart {
+
+PrintTimeScope::~PrintTimeScope() {
+  timer_.Stop();
+  OS::PrintErr("%s %s\n", name_,
+               timer_.FormatElapsedHumanReadable(Thread::Current()->zone()));
+}
+
+}  // namespace dart
diff --git a/runtime/vm/timer.h b/runtime/vm/timer.h
index 8f478d5..19c11a7 100644
--- a/runtime/vm/timer.h
+++ b/runtime/vm/timer.h
@@ -198,6 +198,16 @@
   Timer* const timer_;
 };
 
+class PrintTimeScope : public ValueObject {
+ public:
+  explicit PrintTimeScope(const char* name) : name_(name) { timer_.Start(); }
+  ~PrintTimeScope();
+
+ private:
+  Timer timer_;
+  const char* name_;
+};
+
 }  // namespace dart
 
 #endif  // RUNTIME_VM_TIMER_H_
diff --git a/tools/VERSION b/tools/VERSION
index 3ada223..8f9bd6b 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 184
+PRERELEASE 185
 PRERELEASE_PATCH 0
\ No newline at end of file