Version 2.15.0-224.0.dev

Merge commit 'b70a134933d7678dbc91513d6e4691750961e111' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_argument.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_argument.dart
index 3f3c939..7e370f9 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/remove_argument.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_argument.dart
@@ -5,6 +5,7 @@
 import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
 import 'package:analysis_server/src/services/correction/util.dart';
+import 'package:analysis_server/src/utilities/extensions/range_factory.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
@@ -38,7 +39,8 @@
     }
 
     await builder.addDartFileEdit(file, (builder) {
-      final sourceRange = range.nodeInList(argumentList.arguments, arg);
+      final sourceRange = range.nodeInListWithComments(
+          resolvedResult.lineInfo, argumentList.arguments, arg);
       builder.addDeletion(sourceRange);
     });
   }
diff --git a/pkg/analysis_server/lib/src/utilities/extensions/range_factory.dart b/pkg/analysis_server/lib/src/utilities/extensions/range_factory.dart
new file mode 100644
index 0000000..4b8451a
--- /dev/null
+++ b/pkg/analysis_server/lib/src/utilities/extensions/range_factory.dart
@@ -0,0 +1,108 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:_fe_analyzer_shared/src/scanner/token.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/source/line_info.dart';
+import 'package:analyzer/source/source_range.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+extension RangeFactoryExtensions on RangeFactory {
+  /// Return a source range that covers the given [item] in the containing
+  /// [list]. This includes a leading or trailing comma, as appropriate, and any
+  /// leading or trailing comments. The [lineInfo] is used to differentiate
+  /// trailing comments (on the same line as the end of the item) from leading
+  /// comments (on lines between the start of the item and the preceding comma).
+  ///
+  /// Throws an `ArgumentError` if the [item] is not an element of the [list].
+  SourceRange nodeInListWithComments<T extends AstNode>(
+      LineInfo lineInfo, NodeList<T> list, T item) {
+    // TODO(brianwilkerson) Improve the name and signature of this method and
+    //  make it part of the API of either `RangeFactory` or
+    //  `DartFileEditBuilder`. The implementation currently assumes that the
+    //  list is an argument list, and we might want to generalize that.
+    // TODO(brianwilkerson) Consider adding parameters to allow us to access the
+    //  left and right parentheses in cases where the only element of the list
+    //  is being removed.
+    // TODO(brianwilkerson) Consider adding a `separator` parameter so that we
+    //  can handle things like statements in a block.
+    if (list.length == 1) {
+      if (list[0] != item) {
+        throw ArgumentError('The item must be in the list.');
+      }
+      // If there's only one item in the list, then delete everything including
+      // any leading or trailing comments, including any trailing comma.
+      var leadingComment = _leadingComment(lineInfo, item.beginToken);
+      var trailingComment = _trailingComment(lineInfo, item.endToken, true);
+      return startEnd(leadingComment, trailingComment);
+    }
+    final index = list.indexOf(item);
+    if (index < 0) {
+      throw ArgumentError('The item must be in the list.');
+    }
+    if (index == 0) {
+      // If this is the first item in the list, then delete everything from the
+      // leading comment for this item to the leading comment for the next item.
+      // This will include the comment after this item.
+      var thisLeadingComment = _leadingComment(lineInfo, item.beginToken);
+      var nextLeadingComment = _leadingComment(lineInfo, list[1].beginToken);
+      return startStart(thisLeadingComment, nextLeadingComment);
+    } else {
+      // If this isn't the first item in the list, then delete everything from
+      // the end of the previous item, after the comma and any trailing comment,
+      // to the end of this item, also after the comma and any trailing comment.
+      var previousTrailingComment =
+          _trailingComment(lineInfo, list[index - 1].endToken, false);
+      var previousHasTrailingComment = previousTrailingComment is CommentToken;
+      var thisTrailingComment =
+          _trailingComment(lineInfo, item.endToken, previousHasTrailingComment);
+      if (!previousHasTrailingComment && thisTrailingComment is CommentToken) {
+        // But if this item has a trailing comment and the previous didn't, then
+        // we'd be deleting both commas, which would leave invalid code. We
+        // can't leave the comment, so instead we leave the preceding comma.
+        previousTrailingComment = previousTrailingComment.next!;
+      }
+      return endEnd(previousTrailingComment, thisTrailingComment);
+    }
+  }
+
+  /// Return the comment token immediately following the [token] if it is on the
+  /// same line as the [token], or the [token] if there is no comment after the
+  /// [token] or if the comment is on a different line than the [token]. If
+  /// [returnComma] is `true` and there is a comma after the [token], then the
+  /// comma will be returned when the [token] would have been.
+  Token _trailingComment(LineInfo lineInfo, Token token, bool returnComma) {
+    var lastToken = token;
+    var nextToken = lastToken.next!;
+    if (nextToken.type == TokenType.COMMA) {
+      lastToken = nextToken;
+      nextToken = lastToken.next!;
+    }
+    var tokenLine = lineInfo.getLocation(lastToken.offset).lineNumber;
+    Token? comment = nextToken.precedingComments;
+    if (comment != null &&
+        lineInfo.getLocation(comment.offset).lineNumber == tokenLine) {
+      // This doesn't account for the possibility of multiple trailing block
+      // comments.
+      return comment;
+    }
+    return returnComma ? lastToken : token;
+  }
+
+  /// Return the left-most comment immediately before the [token] that is not on
+  /// the same line as the first non-comment token before the [token]. Return
+  /// the [token] if there is no such comment.
+  Token _leadingComment(LineInfo lineInfo, Token token) {
+    var previousLine = lineInfo.getLocation(token.previous!.offset).lineNumber;
+    Token? comment = token.precedingComments;
+    while (comment != null) {
+      var commentLine = lineInfo.getLocation(comment.offset).lineNumber;
+      if (commentLine != previousLine) {
+        break;
+      }
+      comment = comment.next;
+    }
+    return comment ?? token;
+  }
+}
diff --git a/pkg/analysis_server/test/src/utilities/extensions/range_factory_test.dart b/pkg/analysis_server/test/src/utilities/extensions/range_factory_test.dart
new file mode 100644
index 0000000..206d8fe
--- /dev/null
+++ b/pkg/analysis_server/test/src/utilities/extensions/range_factory_test.dart
@@ -0,0 +1,348 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/utilities/extensions/range_factory.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/source/source_range.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../../abstract_single_unit.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(RangeFactory_NodeInListTest);
+    defineReflectiveTests(RangeFactory_NodeInListWithCommentsTest);
+  });
+}
+
+abstract class BaseRangeFactoryTest extends AbstractSingleUnitTest {
+  /// Assuming that the test code starts with a function whose block body starts
+  /// with a method invocation, return the list of arguments in that invocation.
+  NodeList<Expression> get _argumentList {
+    var f = testUnit.declarations[0] as FunctionDeclaration;
+    var body = f.functionExpression.body as BlockFunctionBody;
+    var statement = body.block.statements[0] as ExpressionStatement;
+    var invocation = statement.expression as MethodInvocation;
+    return invocation.argumentList.arguments;
+  }
+
+  void _assertRange(int index, SourceRange expectedRange) {
+    var list = _argumentList;
+    expect(range.nodeInListWithComments(testUnit.lineInfo!, list, list[index]),
+        expectedRange);
+  }
+}
+
+/// Copied from `analyzer_plugin/test/utilities/range_factory_test.dart` in
+/// order to ensure backward compatibility.
+@reflectiveTest
+class RangeFactory_NodeInListTest extends BaseRangeFactoryTest {
+  // TODO(brianwilkerson) When the tested method becomes public API then these
+  //  two classes should be merged.
+  Future<void> test_argumentList_first_named() async {
+    await resolveTestCode('''
+void f() {
+  g(a: 1, b: 2);
+}
+void g({int? a, int? b}) {}
+''');
+    _assertRange(0, SourceRange(15, 6));
+  }
+
+  Future<void> test_argumentList_first_positional() async {
+    await resolveTestCode('''
+void f() {
+  g(1, 2);
+}
+void g(int a, int b) {}
+''');
+    _assertRange(0, SourceRange(15, 3));
+  }
+
+  Future<void> test_argumentList_last_named() async {
+    await resolveTestCode('''
+void f() {
+  g(a: 1, b: 2);
+}
+void g({int? a, int? b}) {}
+''');
+    _assertRange(1, SourceRange(19, 6));
+  }
+
+  Future<void> test_argumentList_last_positional() async {
+    await resolveTestCode('''
+void f() {
+  g(1, 2);
+}
+void g(int a, int b) {}
+''');
+    _assertRange(1, SourceRange(16, 3));
+  }
+
+  Future<void> test_argumentList_middle_named() async {
+    await resolveTestCode('''
+void f() {
+  g(a: 1, b: 2, c: 3);
+}
+void g({int? a, int? b, int? c}) {}
+''');
+    _assertRange(1, SourceRange(19, 6));
+  }
+
+  Future<void> test_argumentList_middle_positional() async {
+    await resolveTestCode('''
+void f() {
+  g(1, 2, 3);
+}
+void g(int a, int b, int c) {}
+''');
+    _assertRange(1, SourceRange(16, 3));
+  }
+
+  Future<void> test_argumentList_only_named() async {
+    await resolveTestCode('''
+void f() {
+  g(a: 1);
+}
+void g({int? a}) {}
+''');
+    _assertRange(0, SourceRange(15, 4));
+  }
+
+  Future<void> test_argumentList_only_named_trailingComma() async {
+    await resolveTestCode('''
+void f() {
+  g(a: 1,);
+}
+void g({int? a}) {}
+''');
+    _assertRange(0, SourceRange(15, 5));
+  }
+
+  Future<void> test_argumentList_only_positional() async {
+    await resolveTestCode('''
+void f() {
+  g(1);
+}
+void g(int a) {}
+''');
+    _assertRange(0, SourceRange(15, 1));
+  }
+
+  Future<void> test_argumentList_only_positional_trailingComma() async {
+    await resolveTestCode('''
+void f() {
+  g(1,);
+}
+void g(int a) {}
+''');
+    _assertRange(0, SourceRange(15, 2));
+  }
+}
+
+@reflectiveTest
+class RangeFactory_NodeInListWithCommentsTest extends BaseRangeFactoryTest {
+  Future<void> test_argumentList_first_named_leadingAndTrailingComment() async {
+    await resolveTestCode('''
+void f() {
+  g(
+    // comment
+    a: 1, // comment
+    // comment
+    b: 2,
+  );
+}
+void g({int? a, int? b}) {}
+''');
+    _assertRange(0, SourceRange(20, 36));
+  }
+
+  Future<void> test_argumentList_first_named_leadingComment() async {
+    await resolveTestCode('''
+void f() {
+  g(
+    // comment
+    a: 1,
+    // comment
+    b: 2,
+  );
+}
+void g({int? a, int? b}) {}
+''');
+    _assertRange(0, SourceRange(20, 25));
+  }
+
+  Future<void> test_argumentList_first_named_trailingComment() async {
+    await resolveTestCode('''
+void f() {
+  g(
+    a: 1, // comment
+    // comment
+    b: 2,
+  );
+}
+void g({int? a, int? b}) {}
+''');
+    _assertRange(0, SourceRange(20, 21));
+  }
+
+  Future<void> test_argumentList_last_named_leadingAndTrailingComment() async {
+    await resolveTestCode('''
+void f() {
+  g(
+    a: 1, // comment
+    // comment
+    b: 2, // comment
+  );
+}
+void g({int? a, int? b}) {}
+''');
+    _assertRange(1, SourceRange(36, 36));
+  }
+
+  Future<void> test_argumentList_last_named_leadingComment() async {
+    await resolveTestCode('''
+void f() {
+  g(
+    a: 1, // comment
+    // comment
+    b: 2,
+  );
+}
+void g({int? a, int? b}) {}
+''');
+    _assertRange(1, SourceRange(36, 25));
+  }
+
+  Future<void> test_argumentList_last_named_trailingComment() async {
+    await resolveTestCode('''
+void f() {
+  g(
+    a: 1, // comment
+    b: 2, // comment
+  );
+}
+void g({int? a, int? b}) {}
+''');
+    _assertRange(1, SourceRange(36, 21));
+  }
+
+  Future<void>
+      test_argumentList_last_named_trailingComment_commentAfterTrailing() async {
+    await resolveTestCode('''
+void f() {
+  g(
+    a: 1, // comment
+    b: 2, // comment
+    // final comment
+  );
+}
+void g({int? a, int? b}) {}
+''');
+    _assertRange(1, SourceRange(36, 21));
+  }
+
+  Future<void>
+      test_argumentList_middle_named_leadingAndTrailingComment() async {
+    await resolveTestCode('''
+void f() {
+  g(
+    a: 1, // comment
+    // comment
+    b: 2, // comment
+    // comment
+    c: 3,
+  );
+}
+void g({int? a, int? b, int? c}) {}
+''');
+    _assertRange(1, SourceRange(36, 36));
+  }
+
+  Future<void> test_argumentList_middle_named_leadingComment() async {
+    await resolveTestCode('''
+void f() {
+  g(
+    a: 1, // comment
+    // comment
+    b: 2,
+    // comment
+    c: 3,
+  );
+}
+void g({int? a, int? b, int? c}) {}
+''');
+    _assertRange(1, SourceRange(36, 25));
+  }
+
+  Future<void> test_argumentList_middle_named_trailingComment() async {
+    await resolveTestCode('''
+void f() {
+  g(
+    a: 1, // comment
+    b: 2, // comment
+    // comment
+    c: 3,
+  );
+}
+void g({int? a, int? b, int? c}) {}
+''');
+    _assertRange(1, SourceRange(36, 21));
+  }
+
+  Future<void>
+      test_argumentList_middle_named_trailingComment_noTrailingOnPrevious() async {
+    await resolveTestCode('''
+void f() {
+  g(
+    a: 1,
+    b: 2, // comment
+    c: 3,
+  );
+}
+void g({int? a, int? b, int? c}) {}
+''');
+    _assertRange(1, SourceRange(25, 21));
+  }
+
+  Future<void> test_argumentList_only_named_leadingAndTrailingComment() async {
+    await resolveTestCode('''
+void f() {
+  g(
+    // comment
+    a: 1, // comment
+  );
+}
+void g({int? a}) {}
+''');
+    _assertRange(0, SourceRange(20, 31));
+  }
+
+  Future<void> test_argumentList_only_named_leadingComment() async {
+    await resolveTestCode('''
+void f() {
+  g(
+    // comment
+    a: 1,
+  );
+}
+void g({int? a}) {}
+''');
+    _assertRange(0, SourceRange(20, 20));
+  }
+
+  Future<void> test_argumentList_only_named_trailingComment() async {
+    await resolveTestCode('''
+void f() {
+  g(
+    a: 1, // comment
+  );
+}
+void g({int? a}) {}
+''');
+    _assertRange(0, SourceRange(20, 16));
+  }
+}
diff --git a/pkg/analysis_server/test/src/utilities/extensions/test_all.dart b/pkg/analysis_server/test/src/utilities/extensions/test_all.dart
new file mode 100644
index 0000000..d4620d4
--- /dev/null
+++ b/pkg/analysis_server/test/src/utilities/extensions/test_all.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'range_factory_test.dart' as range_factory;
+
+void main() {
+  defineReflectiveSuite(() {
+    range_factory.main();
+  });
+}
diff --git a/pkg/analysis_server/test/src/utilities/test_all.dart b/pkg/analysis_server/test/src/utilities/test_all.dart
index a9cd96e..6c36075 100644
--- a/pkg/analysis_server/test/src/utilities/test_all.dart
+++ b/pkg/analysis_server/test/src/utilities/test_all.dart
@@ -4,12 +4,14 @@
 
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
+import 'extensions/test_all.dart' as extensions;
 import 'flutter_test.dart' as flutter_test;
 import 'profiling_test.dart' as profiling_test;
 import 'strings_test.dart' as strings_test;
 
 void main() {
   defineReflectiveSuite(() {
+    extensions.main();
     flutter_test.main();
     profiling_test.main();
     strings_test.main();
diff --git a/pkg/analyzer/lib/src/dart/constant/value.dart b/pkg/analyzer/lib/src/dart/constant/value.dart
index 7f85d1e..121484e 100644
--- a/pkg/analyzer/lib/src/dart/constant/value.dart
+++ b/pkg/analyzer/lib/src/dart/constant/value.dart
@@ -537,13 +537,12 @@
   DartObjectImpl isIdentical2(
       TypeSystemImpl typeSystem, DartObjectImpl rightOperand) {
     // Workaround for Flutter `const kIsWeb = identical(0, 0.0)`.
-    if (type.isDartCoreInt && rightOperand.type.isDartCoreDouble) {
+    if (type.isDartCoreInt && rightOperand.type.isDartCoreDouble ||
+        type.isDartCoreDouble && rightOperand.type.isDartCoreInt) {
       return DartObjectImpl(
         typeSystem,
         typeSystem.typeProvider.boolType,
-        BoolState(
-          toIntValue() == 0 && rightOperand.toDoubleValue() == 0.0,
-        ),
+        BoolState.UNKNOWN_VALUE,
       );
     }
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
index 52c0c39..6ec3f7e 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
@@ -148,18 +148,19 @@
     ExecutableElement element, {
     required bool implicitReceiver,
   }) {
+    var enclosingElement = element.enclosingElement;
     if (_resolver.enclosingExtension != null) {
       _resolver.errorReporter.reportErrorForNode(
         CompileTimeErrorCode
             .UNQUALIFIED_REFERENCE_TO_STATIC_MEMBER_OF_EXTENDED_TYPE,
         nameNode,
-        [element.enclosingElement.displayName],
+        [enclosingElement.displayName],
       );
     } else if (implicitReceiver) {
       _resolver.errorReporter.reportErrorForNode(
         CompileTimeErrorCode.UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER,
         nameNode,
-        [element.enclosingElement.displayName],
+        [enclosingElement.displayName],
       );
     } else {
       _resolver.errorReporter.reportErrorForNode(
@@ -168,7 +169,10 @@
         [
           nameNode.name,
           element.kind.displayName,
-          element.enclosingElement.displayName,
+          enclosingElement.name ?? '<unnamed>',
+          enclosingElement is ClassElement && enclosingElement.isMixin
+              ? 'mixin'
+              : enclosingElement.kind.displayName,
         ],
       );
     }
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 6833dd3..3d1d725 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -218,18 +218,19 @@
     ExecutableElement element,
     bool nullReceiver,
   ) {
+    var enclosingElement = element.enclosingElement;
     if (_resolver.enclosingExtension != null) {
       _resolver.errorReporter.reportErrorForNode(
         CompileTimeErrorCode
             .UNQUALIFIED_REFERENCE_TO_STATIC_MEMBER_OF_EXTENDED_TYPE,
         nameNode,
-        [element.enclosingElement.displayName],
+        [enclosingElement.displayName],
       );
     } else if (nullReceiver) {
       _resolver.errorReporter.reportErrorForNode(
         CompileTimeErrorCode.UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER,
         nameNode,
-        [element.enclosingElement.displayName],
+        [enclosingElement.displayName],
       );
     } else {
       _resolver.errorReporter.reportErrorForNode(
@@ -238,7 +239,10 @@
         [
           nameNode.name,
           element.kind.displayName,
-          element.enclosingElement.displayName,
+          enclosingElement.name ?? '<unnamed>',
+          enclosingElement is ClassElement && enclosingElement.isMixin
+              ? 'mixin'
+              : enclosingElement.kind.displayName,
         ],
       );
     }
diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart
index f9d2a42..2e29e47 100644
--- a/pkg/analyzer/lib/src/error/codes.g.dart
+++ b/pkg/analyzer/lib/src/error/codes.g.dart
@@ -6209,7 +6209,8 @@
    * Parameters:
    * 0: the name of the static member
    * 1: the kind of the static member (field, getter, setter, or method)
-   * 2: the name of the defining class
+   * 2: the name of the static member's enclosing element
+   * 3: the kind of the static member's enclosing element (class, mixin, or extension)
    */
   // #### Description
   //
@@ -6247,8 +6248,8 @@
   static const CompileTimeErrorCode INSTANCE_ACCESS_TO_STATIC_MEMBER =
       CompileTimeErrorCode(
     'INSTANCE_ACCESS_TO_STATIC_MEMBER',
-    "Static {1} '{0}' can't be accessed through an instance.",
-    correctionMessage: "Try using the class '{2}' to access the {1}.",
+    "The static {1} '{0}' can't be accessed through an instance.",
+    correctionMessage: "Try using the {3} '{2}' to access the {1}.",
     hasPublishedDocs: true,
   );
 
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 7f01644..a4f8ff7 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -2659,9 +2659,14 @@
         }
       }
       errorReporter.reportErrorForNode(
-          CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER,
-          name,
-          [name.name, _getKind(element), element.enclosingElement.name]);
+          CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, name, [
+        name.name,
+        _getKind(element),
+        enclosingElement.name ?? '<unnamed>',
+        enclosingElement is ClassElement && enclosingElement.isMixin
+            ? 'mixin'
+            : enclosingElement.kind.displayName
+      ]);
     }
   }
 
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index ef1c2be..079cc69 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -5500,14 +5500,15 @@
       }
       ```
   INSTANCE_ACCESS_TO_STATIC_MEMBER:
-    problemMessage: "Static {1} '{0}' can't be accessed through an instance."
-    correctionMessage: "Try using the class '{2}' to access the {1}."
+    problemMessage: "The static {1} '{0}' can't be accessed through an instance."
+    correctionMessage: "Try using the {3} '{2}' to access the {1}."
     hasPublishedDocs: true
     comment: |-
       Parameters:
       0: the name of the static member
       1: the kind of the static member (field, getter, setter, or method)
-      2: the name of the defining class
+      2: the name of the static member's enclosing element
+      3: the kind of the static member's enclosing element (class, mixin, or extension)
     documentation: |-
       #### Description
 
diff --git a/pkg/analyzer/test/generated/test_support.dart b/pkg/analyzer/test/generated/test_support.dart
index c8a5797..c039514 100644
--- a/pkg/analyzer/test/generated/test_support.dart
+++ b/pkg/analyzer/test/generated/test_support.dart
@@ -2,6 +2,8 @@
 // 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/diagnostic/diagnostic.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/error/listener.dart';
@@ -44,6 +46,10 @@
   /// The error code associated with the error.
   final ErrorCode code;
 
+  // A pattern that should be contained in the error's correction message, or
+  // `null` if the correction message contents should not be checked.
+  final Pattern? correctionContains;
+
   /// The offset of the beginning of the error's region.
   final int offset;
 
@@ -63,7 +69,8 @@
 
   /// Initialize a newly created error description.
   ExpectedError(this.code, this.offset, this.length,
-      {this.message,
+      {this.correctionContains,
+      this.message,
       this.messageContains,
       this.expectedContextMessages = const <ExpectedContextMessage>[]});
 
@@ -82,6 +89,10 @@
         error.message.contains(messageContains!) != true) {
       return false;
     }
+    if (correctionContains != null &&
+        !(error.correctionMessage ?? '').contains(correctionContains!)) {
+      return false;
+    }
     List<DiagnosticMessage> contextMessages = error.contextMessages.toList();
     contextMessages.sort((first, second) {
       int result = first.filePath.compareTo(second.filePath);
@@ -196,7 +207,9 @@
         buffer.write(', ');
         buffer.write(actual.length);
         buffer.write(', ');
-        buffer.write(actual.message);
+        buffer.write(json.encode(actual.message));
+        buffer.write(', ');
+        buffer.write(json.encode(actual.correctionMessage));
         buffer.writeln(']');
       }
     }
diff --git a/pkg/analyzer/test/src/dart/constant/value_test.dart b/pkg/analyzer/test/src/dart/constant/value_test.dart
index 232d0f1..11a22c5 100644
--- a/pkg/analyzer/test/src/dart/constant/value_test.dart
+++ b/pkg/analyzer/test/src/dart/constant/value_test.dart
@@ -795,7 +795,8 @@
   void test_identical_intZero_doubleZero() {
     // Used in Flutter:
     // const bool kIsWeb = identical(0, 0.0);
-    _assertIdentical(_boolValue(true), _intValue(0), _doubleValue(0.0));
+    _assertIdentical(_boolValue(null), _intValue(0), _doubleValue(0.0));
+    _assertIdentical(_boolValue(null), _doubleValue(0.0), _intValue(0));
   }
 
   void test_identical_list_empty() {
diff --git a/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart b/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
index 7f61cf5..0238785 100644
--- a/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
@@ -444,7 +444,8 @@
   static void m<T>(T t) {}
 }
 ''', [
-      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 40, 1),
+      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 40, 1,
+          correctionContains: "extension '<unnamed>'"),
     ]);
 
     assertFunctionReference(findNode.functionReference('foo.m<int>;'),
diff --git a/pkg/analyzer/test/src/dart/resolution/resolution.dart b/pkg/analyzer/test/src/dart/resolution/resolution.dart
index 45c4114..5290a73 100644
--- a/pkg/analyzer/test/src/dart/resolution/resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/resolution.dart
@@ -850,11 +850,13 @@
   }
 
   ExpectedError error(ErrorCode code, int offset, int length,
-          {String? text,
+          {Pattern? correctionContains,
+          String? text,
           Pattern? messageContains,
           List<ExpectedContextMessage> contextMessages =
               const <ExpectedContextMessage>[]}) =>
       ExpectedError(code, offset, length,
+          correctionContains: correctionContains,
           message: text,
           messageContains: messageContains,
           expectedContextMessages: contextMessages);
diff --git a/pkg/analyzer/test/src/diagnostics/analysis_options/analysis_options_test_support.dart b/pkg/analyzer/test/src/diagnostics/analysis_options/analysis_options_test_support.dart
index e0432e0..30552bf 100644
--- a/pkg/analyzer/test/src/diagnostics/analysis_options/analysis_options_test_support.dart
+++ b/pkg/analyzer/test/src/diagnostics/analysis_options/analysis_options_test_support.dart
@@ -19,11 +19,13 @@
   }
 
   ExpectedError error(ErrorCode code, int offset, int length,
-          {String? text,
+          {Pattern? correctionContains,
+          String? text,
           Pattern? messageContains,
           List<ExpectedContextMessage> contextMessages =
               const <ExpectedContextMessage>[]}) =>
       ExpectedError(code, offset, length,
+          correctionContains: correctionContains,
           message: text,
           messageContains: messageContains,
           expectedContextMessages: contextMessages);
diff --git a/pkg/analyzer/test/src/diagnostics/instance_access_to_static_member_test.dart b/pkg/analyzer/test/src/diagnostics/instance_access_to_static_member_test.dart
index ee423c8..470dbc7 100644
--- a/pkg/analyzer/test/src/diagnostics/instance_access_to_static_member_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/instance_access_to_static_member_test.dart
@@ -15,6 +15,25 @@
 
 @reflectiveTest
 class InstanceAccessToStaticMemberTest extends PubPackageResolutionTest {
+  test_class_method() async {
+    await assertErrorsInCode('''
+class C {
+  static void a() {}
+}
+
+f(C c) {
+  c.a();
+}
+''', [
+      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 47, 1,
+          correctionContains: "class 'C'"),
+    ]);
+    assertElement(
+      findNode.methodInvocation('a();'),
+      findElement.method('a'),
+    );
+  }
+
   test_extension_getter() async {
     await assertErrorsInCode('''
 class C {}
@@ -28,7 +47,8 @@
   g(c).a;
 }
 ''', [
-      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 92, 1),
+      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 92, 1,
+          correctionContains: "extension 'E'"),
     ]);
     assertElement(
       findNode.simple('a;'),
@@ -48,7 +68,29 @@
   c.a();
 }
 ''', [
-      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 68, 1),
+      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 68, 1,
+          correctionContains: "extension 'E'"),
+    ]);
+    assertElement(
+      findNode.methodInvocation('a();'),
+      findElement.method('a'),
+    );
+  }
+
+  test_extension_method_unnamed() async {
+    await assertErrorsInCode('''
+class C {}
+
+extension on C {
+  static void a() {}
+}
+
+f(C c) {
+  c.a();
+}
+''', [
+      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 66, 1,
+          correctionContains: "extension '<unnamed>'"),
     ]);
     assertElement(
       findNode.methodInvocation('a();'),
@@ -98,7 +140,50 @@
   a.m;
 }
 ''', [
-      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 41, 1),
+      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 41, 1,
+          correctionContains: "class 'A'"),
+    ]);
+  }
+
+  test_method_reference_extension() async {
+    await assertErrorsInCode(r'''
+extension E on int {
+  static m<T>() {}
+}
+f(int a) {
+  a.m<int>;
+}
+''', [
+      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 57, 1,
+          correctionContains: "extension 'E'"),
+    ]);
+  }
+
+  test_method_reference_extension_unnamed() async {
+    await assertErrorsInCode(r'''
+extension on int {
+  static m<T>() {}
+}
+f(int a) {
+  a.m<int>;
+}
+''', [
+      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 55, 1,
+          correctionContains: "extension '<unnamed>'"),
+    ]);
+  }
+
+  test_method_reference_mixin() async {
+    await assertErrorsInCode(r'''
+mixin A {
+  static m() {}
+}
+f(A a) {
+  a.m;
+}
+''', [
+      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 41, 1,
+          correctionContains: "mixin 'A'"),
     ]);
   }
 
@@ -111,10 +196,44 @@
   a.m<int>;
 }
 ''', [
-      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 44, 1),
+      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 44, 1,
+          correctionContains: "class 'A'"),
     ]);
   }
 
+  test_method_reference_typeInstantiation_mixin() async {
+    await assertErrorsInCode(r'''
+mixin A {
+  static m<T>() {}
+}
+f(A a) {
+  a.m<int>;
+}
+''', [
+      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 44, 1,
+          correctionContains: "mixin 'A'"),
+    ]);
+  }
+
+  test_mixin_method() async {
+    await assertErrorsInCode('''
+mixin A {
+  static void a() {}
+}
+
+f(A a) {
+  a.a();
+}
+''', [
+      error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 47, 1,
+          correctionContains: "mixin 'A'"),
+    ]);
+    assertElement(
+      findNode.methodInvocation('a();'),
+      findElement.method('a'),
+    );
+  }
+
   test_propertyAccess_field() async {
     await assertErrorsInCode(r'''
 class A {
diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md
index 1878547..7aefd9d 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -6000,7 +6000,7 @@
 
 ### instance_access_to_static_member
 
-_Static {1} '{0}' can't be accessed through an instance._
+_The static {1} '{0}' can't be accessed through an instance._
 
 #### Description
 
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index 3f0ddf0..91c66d3 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -1679,12 +1679,20 @@
               ? locals.find('_lazyFinal', 'hunkHelpers.lazyFinal')
               : locals.find('_lazy', 'hunkHelpers.lazy')
           : locals.find('_lazyOld', 'hunkHelpers.lazyOld');
+      js.Expression staticFieldCode = field.code;
+      if (!_options.features.legacyJavaScript.isEnabled &&
+          staticFieldCode is js.Fun) {
+        js.Fun fun = staticFieldCode;
+        staticFieldCode = js.ArrowFunction(fun.params, fun.body,
+                asyncModifier: fun.asyncModifier)
+            .withSourceInformation(fun.sourceInformation);
+      }
       js.Statement statement = js.js.statement("#(#, #, #, #);", [
         helper,
         _namer.globalObjectForStaticState(),
         js.quoteName(field.name),
         js.quoteName(field.getterName),
-        field.code,
+        staticFieldCode,
       ]);
 
       registerEntityAst(field.element, statement,
diff --git a/pkg/compiler/test/sourcemaps/stacktrace/throw_in_lazy_field.dart b/pkg/compiler/test/sourcemaps/stacktrace/throw_in_lazy_field.dart
new file mode 100644
index 0000000..161a5d2
--- /dev/null
+++ b/pkg/compiler/test/sourcemaps/stacktrace/throw_in_lazy_field.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.7
+
+main() {
+  Class. /*1:main*/ field;
+}
+
+class Class {
+  static dynamic field = /*2:Class.field*/ throw '>ExceptionMarker<';
+}
diff --git a/pkg/compiler/test/sourcemaps/stacktrace/throw_in_lazy_field_indirect.dart b/pkg/compiler/test/sourcemaps/stacktrace/throw_in_lazy_field_indirect.dart
new file mode 100644
index 0000000..bad3bbb
--- /dev/null
+++ b/pkg/compiler/test/sourcemaps/stacktrace/throw_in_lazy_field_indirect.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.7
+
+main() {
+  Class. /*1:main*/ field;
+}
+
+class Class {
+  static dynamic field = /*2:Class.field*/ test();
+  @pragma('dart2js:noInline')
+  static test() {
+    /*3:Class.test*/ throw '>ExceptionMarker<';
+  }
+}
diff --git a/pkg/compiler/test/sourcemaps/stacktrace/throw_in_lazy_final_field.dart b/pkg/compiler/test/sourcemaps/stacktrace/throw_in_lazy_final_field.dart
new file mode 100644
index 0000000..a7f0152
--- /dev/null
+++ b/pkg/compiler/test/sourcemaps/stacktrace/throw_in_lazy_final_field.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.7
+
+main() {
+  Class. /*1:main*/ field;
+}
+
+class Class {
+  static final field = /*2:Class.field*/ throw '>ExceptionMarker<';
+}
diff --git a/pkg/compiler/test/sourcemaps/stacktrace/throw_in_lazy_final_field_indirect.dart b/pkg/compiler/test/sourcemaps/stacktrace/throw_in_lazy_final_field_indirect.dart
new file mode 100644
index 0000000..7adc1cc
--- /dev/null
+++ b/pkg/compiler/test/sourcemaps/stacktrace/throw_in_lazy_final_field_indirect.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.7
+
+main() {
+  Class. /*1:main*/ field;
+}
+
+class Class {
+  static final field = /*2:Class.field*/ test();
+  @pragma('dart2js:noInline')
+  static test() {
+    /*3:Class.test*/ throw '>ExceptionMarker<';
+  }
+}
diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc
index c2c8b3d..3cc94ca 100644
--- a/runtime/vm/app_snapshot.cc
+++ b/runtime/vm/app_snapshot.cc
@@ -2381,6 +2381,7 @@
   void ReadFill(Deserializer* d, bool primary) {
     ASSERT(!is_canonical());  // Never canonical.
     fill_position_ = d->position();
+#if defined(DART_PRECOMPILED_RUNTIME)
     const uint8_t immediate_bits =
         ObjectPool::EncodeBits(ObjectPool::EntryType::kImmediate,
                                ObjectPool::Patchability::kPatchable);
@@ -2392,6 +2393,7 @@
       megamorphic_call_entry_point =
           StubCode::MegamorphicCall().MonomorphicEntryPoint();
     }
+#endif  // defined(DART_PRECOMPILED_RUNTIME)
 
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       const intptr_t length = d->ReadUnsigned();
@@ -2416,6 +2418,7 @@
             entry.raw_value_ = static_cast<intptr_t>(new_entry);
             break;
           }
+#if defined(DART_PRECOMPILED_RUNTIME)
           case ObjectPool::EntryType::kSwitchableCallMissEntryPoint:
             ASSERT(FLAG_use_bare_instructions);
             pool->untag()->entry_bits()[j] = immediate_bits;
@@ -2428,6 +2431,7 @@
             entry.raw_value_ =
                 static_cast<intptr_t>(megamorphic_call_entry_point);
             break;
+#endif  // defined(DART_PRECOMPILED_RUNTIME)
           default:
             UNREACHABLE();
         }
diff --git a/tests/lib/isolate/spawn_uri_nested_vm_test.dart b/tests/lib/isolate/spawn_uri_nested_vm_test.dart
index 087b5fc..0847cf6 100644
--- a/tests/lib/isolate/spawn_uri_nested_vm_test.dart
+++ b/tests/lib/isolate/spawn_uri_nested_vm_test.dart
@@ -12,12 +12,19 @@
 import 'package:async_helper/async_minitest.dart';
 
 main() {
-  test('isolate fromUri - nested send and reply', () {
-    ReceivePort port = new ReceivePort();
-    Isolate.spawnUri(Uri.parse('spawn_uri_nested_child1_vm_isolate.dart'), [], [
-      [1, 2],
-      port.sendPort
-    ]);
+  test('isolate fromUri - nested send and reply', () async {
+    final port = ReceivePort();
+    final exitPort = ReceivePort();
+    Isolate.spawnUri(
+        Uri.parse('spawn_uri_nested_child1_vm_isolate.dart'),
+        [],
+        [
+          [1, 2],
+          port.sendPort
+        ],
+        onExit: exitPort.sendPort);
     port.first.then(expectAsync((result) => print(result)));
+    // ensure main isolate doesn't exit before child isolate exits
+    await exitPort.first;
   });
 }
diff --git a/tests/lib_2/isolate/spawn_uri_nested_vm_test.dart b/tests/lib_2/isolate/spawn_uri_nested_vm_test.dart
index 423821d..81e5882 100644
--- a/tests/lib_2/isolate/spawn_uri_nested_vm_test.dart
+++ b/tests/lib_2/isolate/spawn_uri_nested_vm_test.dart
@@ -14,12 +14,19 @@
 import 'package:async_helper/async_minitest.dart';
 
 main() {
-  test('isolate fromUri - nested send and reply', () {
-    ReceivePort port = new ReceivePort();
-    Isolate.spawnUri(Uri.parse('spawn_uri_nested_child1_vm_isolate.dart'), [], [
-      [1, 2],
-      port.sendPort
-    ]);
+  test('isolate fromUri - nested send and reply', () async {
+    final port = ReceivePort();
+    final exitPort = ReceivePort();
+    Isolate.spawnUri(
+        Uri.parse('spawn_uri_nested_child1_vm_isolate.dart'),
+        [],
+        [
+          [1, 2],
+          port.sendPort
+        ],
+        onExit: exitPort.sendPort);
     port.first.then(expectAsync((result) => print(result)));
+    // ensure main isolate doesn't exit before child isolate exits
+    await exitPort.first;
   });
 }
diff --git a/tools/VERSION b/tools/VERSION
index e386a00..a34cafe 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 223
+PRERELEASE 224
 PRERELEASE_PATCH 0
\ No newline at end of file