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