mirror of
https://github.com/dart-lang/sdk
synced 2024-10-04 19:59:42 +00:00
analyzer: fix various issues for static extension member resolution
* Unqualified, out-of-scope static extension members are not accessible. * Locally scoped extension members shadow others. * Static and instance extension members do not conflict. Fixes https://github.com/dart-lang/sdk/issues/41268 Fixes https://github.com/dart-lang/sdk/issues/55848 Cq-Include-Trybots: luci.dart.try:flutter-analyze-try,analyzer-win-release-try,pkg-win-release-try Change-Id: Icd618d044b3857efa24f365c6835d42c0022c176 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/370323 Reviewed-by: Paul Berry <paulberry@google.com> Commit-Queue: Sam Rawlins <srawlins@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
b4e30d1b45
commit
50e4c576b9
|
@ -22,7 +22,7 @@ class AddExtensionOverride extends MultiCorrectionProducer {
|
|||
if (target == null) return const [];
|
||||
|
||||
var extensions =
|
||||
libraryElement.accessibleExtensions.hasMemberWithBaseName(node.name);
|
||||
libraryElement.accessibleExtensions.havingMemberWithBaseName(node.name);
|
||||
var producers = <ResolvedCorrectionProducer>[];
|
||||
for (var extension in extensions) {
|
||||
var name = extension.extension.name;
|
||||
|
|
|
@ -186,7 +186,7 @@ class ImportLibrary extends MultiCorrectionProducer {
|
|||
}
|
||||
foundImport = true;
|
||||
var instantiatedExtensions = importedLibrary.exportedExtensions
|
||||
.hasMemberWithBaseName(memberName)
|
||||
.havingMemberWithBaseName(memberName)
|
||||
.applicableTo(targetLibrary: libraryElement, targetType: targetType);
|
||||
for (var instantiatedExtension in instantiatedExtensions) {
|
||||
// If the import has a combinator that needs to be updated, then offer
|
||||
|
@ -482,7 +482,7 @@ class _ImportLibraryContainingExtension extends ResolvedCorrectionProducer {
|
|||
@override
|
||||
Future<void> compute(ChangeBuilder builder) async {
|
||||
var instantiatedExtensions = library.exportedExtensions
|
||||
.hasMemberWithBaseName(memberName)
|
||||
.havingMemberWithBaseName(memberName)
|
||||
.applicableTo(targetLibrary: libraryElement, targetType: targetType);
|
||||
if (instantiatedExtensions.isNotEmpty) {
|
||||
await builder.addDartFileEdit(file, (builder) {
|
||||
|
|
|
@ -38,25 +38,6 @@ void f(A a) {
|
|||
''');
|
||||
}
|
||||
|
||||
Future<void> test_method_extension() async {
|
||||
await resolveTestCode('''
|
||||
extension E on int {
|
||||
static void foo() {}
|
||||
}
|
||||
void f() {
|
||||
0.foo();
|
||||
}
|
||||
''');
|
||||
await assertHasFix('''
|
||||
extension E on int {
|
||||
static void foo() {}
|
||||
}
|
||||
void f() {
|
||||
E.foo();
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_method_importType() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
class A {
|
||||
|
|
|
@ -114,7 +114,9 @@ extension ExtensionsExtensions on Iterable<ExtensionElement> {
|
|||
.applicableTo(targetLibrary: targetLibrary, targetType: targetType);
|
||||
}
|
||||
|
||||
List<_NotInstantiatedExtensionWithMember> hasMemberWithBaseName(
|
||||
/// Returns the sublist of [ExtensionElement]s that have an instance member
|
||||
/// named [baseName].
|
||||
List<_NotInstantiatedExtensionWithMember> havingMemberWithBaseName(
|
||||
String baseName,
|
||||
) {
|
||||
var result = <_NotInstantiatedExtensionWithMember>[];
|
||||
|
@ -140,6 +142,9 @@ extension ExtensionsExtensions on Iterable<ExtensionElement> {
|
|||
}
|
||||
} else {
|
||||
for (var field in extension.augmented.fields) {
|
||||
if (field.isStatic) {
|
||||
continue;
|
||||
}
|
||||
if (field.name == baseName) {
|
||||
result.add(
|
||||
_NotInstantiatedExtensionWithMember(
|
||||
|
@ -152,6 +157,9 @@ extension ExtensionsExtensions on Iterable<ExtensionElement> {
|
|||
}
|
||||
}
|
||||
for (var method in extension.augmented.methods) {
|
||||
if (method.isStatic) {
|
||||
continue;
|
||||
}
|
||||
if (method.name == baseName) {
|
||||
result.add(
|
||||
_NotInstantiatedExtensionWithMember(
|
||||
|
|
|
@ -20,7 +20,6 @@ import 'package:analyzer/src/dart/resolver/applicable_extensions.dart';
|
|||
import 'package:analyzer/src/dart/resolver/resolution_result.dart';
|
||||
import 'package:analyzer/src/error/codes.dart';
|
||||
import 'package:analyzer/src/generated/resolver.dart';
|
||||
import 'package:analyzer/src/util/either.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/string.dart';
|
||||
|
||||
class ExtensionMemberResolver {
|
||||
|
@ -74,20 +73,17 @@ class ExtensionMemberResolver {
|
|||
).substituteType(element.extendedType);
|
||||
}
|
||||
|
||||
/// Return the most specific extension in the current scope for this [type],
|
||||
/// Returns the most specific accessible extension, applicable to [type],
|
||||
/// that defines the member with the given [name].
|
||||
///
|
||||
/// If no applicable extensions, return [ResolutionResult.none].
|
||||
/// If no applicable extensions are found, returns [ResolutionResult.none].
|
||||
///
|
||||
/// If the match is ambiguous, report an error on the [nameEntity], and
|
||||
/// return [ResolutionResult.ambiguous].
|
||||
/// If the match is ambiguous, reports an error on the [nameEntity], and
|
||||
/// returns [ResolutionResult.ambiguous].
|
||||
ResolutionResult findExtension(
|
||||
DartType type,
|
||||
SyntacticEntity nameEntity,
|
||||
String name,
|
||||
) {
|
||||
DartType type, SyntacticEntity nameEntity, String name) {
|
||||
var extensions = _resolver.definingLibrary.accessibleExtensions
|
||||
.hasMemberWithBaseName(name)
|
||||
.havingMemberWithBaseName(name)
|
||||
.applicableTo(
|
||||
targetLibrary: _resolver.definingLibrary,
|
||||
targetType: type,
|
||||
|
@ -102,29 +98,27 @@ class ExtensionMemberResolver {
|
|||
}
|
||||
|
||||
var mostSpecific = _chooseMostSpecific(extensions);
|
||||
return mostSpecific.map(
|
||||
(extension) {
|
||||
return extension.asResolutionResult;
|
||||
},
|
||||
(noneMoreSpecific) {
|
||||
_errorReporter.atEntity(
|
||||
nameEntity,
|
||||
CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS,
|
||||
arguments: [
|
||||
name,
|
||||
noneMoreSpecific.map((e) {
|
||||
var name = e.extension.name;
|
||||
if (name != null) {
|
||||
return "extension '$name'";
|
||||
}
|
||||
var type = e.extension.extendedType.getDisplayString();
|
||||
return "unnamed extension on '$type'";
|
||||
}).commaSeparatedWithAnd,
|
||||
],
|
||||
);
|
||||
return ResolutionResult.ambiguous;
|
||||
},
|
||||
if (mostSpecific.length == 1) {
|
||||
return mostSpecific.first.asResolutionResult;
|
||||
}
|
||||
|
||||
// The most specific extension is ambiguous.
|
||||
_errorReporter.atEntity(
|
||||
nameEntity,
|
||||
CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS,
|
||||
arguments: [
|
||||
name,
|
||||
mostSpecific.map((e) {
|
||||
var name = e.extension.name;
|
||||
if (name != null) {
|
||||
return "extension '$name'";
|
||||
}
|
||||
var type = e.extension.extendedType.getDisplayString();
|
||||
return "unnamed extension on '$type'";
|
||||
}).commaSeparatedWithAnd,
|
||||
],
|
||||
);
|
||||
return ResolutionResult.ambiguous;
|
||||
}
|
||||
|
||||
/// Resolve the [name] (without `=`) to the corresponding getter and setter
|
||||
|
@ -260,11 +254,10 @@ class ExtensionMemberResolver {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return either the most specific extension, or a list of the extensions
|
||||
/// that are ambiguous.
|
||||
Either2<InstantiatedExtensionWithMember,
|
||||
List<InstantiatedExtensionWithMember>>
|
||||
_chooseMostSpecific(List<InstantiatedExtensionWithMember> extensions) {
|
||||
/// Returns a list with either the most specific extension, or, if the most
|
||||
/// specific is ambiguous, then the extensions that are ambiguous.
|
||||
List<InstantiatedExtensionWithMember> _chooseMostSpecific(
|
||||
List<InstantiatedExtensionWithMember> extensions) {
|
||||
InstantiatedExtensionWithMember? bestSoFar;
|
||||
var noneMoreSpecific = <InstantiatedExtensionWithMember>[];
|
||||
for (var candidate in extensions) {
|
||||
|
@ -299,18 +292,17 @@ class ExtensionMemberResolver {
|
|||
}
|
||||
|
||||
if (bestSoFar != null) {
|
||||
return Either2.t1(bestSoFar);
|
||||
} else {
|
||||
return Either2.t2(noneMoreSpecific);
|
||||
return [bestSoFar];
|
||||
}
|
||||
|
||||
return noneMoreSpecific;
|
||||
}
|
||||
|
||||
/// Given the generic [element] element, either return types specified
|
||||
/// explicitly in [typeArguments], or infer type arguments from the given
|
||||
/// [receiverType].
|
||||
/// Given the generic [node], either returns types specified explicitly in its
|
||||
/// type arguments, or infer type arguments from the given [receiverType].
|
||||
///
|
||||
/// If the number of explicit type arguments is different than the number
|
||||
/// of extension's type parameters, or inference fails, return `dynamic`
|
||||
/// of extension's type parameters, or inference fails, returns `dynamic`
|
||||
/// for all type parameters.
|
||||
List<DartType>? _inferTypeArguments(
|
||||
ExtensionOverride node, DartType receiverType,
|
||||
|
|
|
@ -574,6 +574,39 @@ class MethodInvocationResolver with ScopeHelpers {
|
|||
return null;
|
||||
}
|
||||
|
||||
element = scopeLookupResult.setter;
|
||||
if (element != null) {
|
||||
// If the scope lookup reveals a setter, but no getter, then we may still
|
||||
// find the getter by looking up the inheritence chain (via
|
||||
// TypePropertyResolver, via `_resolveReceiverType`). However, if the
|
||||
// setter that was found is either top-level, or declared in an extension,
|
||||
// or is static, then we do not keep searching for the getter; this
|
||||
// setter represents the property being accessed (erroneously).
|
||||
var noGetterIsPossible =
|
||||
element.enclosingElement is CompilationUnitElement ||
|
||||
element.enclosingElement is ExtensionElement ||
|
||||
(element is ExecutableElement && element.isStatic);
|
||||
if (noGetterIsPossible) {
|
||||
nameNode.staticElement = element;
|
||||
|
||||
_setInvalidTypeResolution(node,
|
||||
setNameTypeToDynamic: false,
|
||||
whyNotPromotedList: whyNotPromotedList,
|
||||
contextType: contextType);
|
||||
var receiverTypeName = switch (receiverType) {
|
||||
InterfaceType() => receiverType.element.name,
|
||||
FunctionType() => 'Function',
|
||||
_ => '<unknown>',
|
||||
};
|
||||
_resolver.errorReporter.atNode(
|
||||
nameNode,
|
||||
CompileTimeErrorCode.UNDEFINED_METHOD,
|
||||
arguments: [name, receiverTypeName],
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return _resolveReceiverType(
|
||||
node: node,
|
||||
receiver: null,
|
||||
|
|
|
@ -2203,8 +2203,8 @@ extension E on int {
|
|||
}
|
||||
|
||||
void f() {
|
||||
0.foo;
|
||||
0.foo = 0;
|
||||
E.foo;
|
||||
E.foo = 0;
|
||||
}
|
||||
''');
|
||||
var getter = findElement.getter('foo');
|
||||
|
|
|
@ -484,6 +484,36 @@ FunctionExpressionInvocation
|
|||
''');
|
||||
}
|
||||
|
||||
test_getter_functionTyped_withSetterDeclaredLocally() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
Function get foo => () {};
|
||||
}
|
||||
class B extends A {
|
||||
set foo(Function _) {}
|
||||
|
||||
void f() {
|
||||
foo();
|
||||
}
|
||||
}
|
||||
''');
|
||||
|
||||
var node = findNode.singleFunctionExpressionInvocation;
|
||||
assertResolvedNodeText(node, r'''
|
||||
FunctionExpressionInvocation
|
||||
function: SimpleIdentifier
|
||||
token: foo
|
||||
staticElement: self::@class::A::@getter::foo
|
||||
staticType: Function
|
||||
argumentList: ArgumentList
|
||||
leftParenthesis: (
|
||||
rightParenthesis: )
|
||||
staticElement: <null>
|
||||
staticInvokeType: dynamic
|
||||
staticType: dynamic
|
||||
''');
|
||||
}
|
||||
|
||||
test_invalidConst_topLevelVariable() async {
|
||||
await assertErrorsInCode(r'''
|
||||
const id = identical;
|
||||
|
|
|
@ -1030,15 +1030,11 @@ void bar() {
|
|||
foo.m<int>;
|
||||
}
|
||||
|
||||
extension on Function {
|
||||
extension E on Function {
|
||||
static void m<T>(T t) {}
|
||||
}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode
|
||||
.INSTANCE_ACCESS_TO_STATIC_MEMBER_OF_UNNAMED_EXTENSION,
|
||||
40,
|
||||
1),
|
||||
error(CompileTimeErrorCode.UNDEFINED_GETTER, 40, 1),
|
||||
]);
|
||||
|
||||
assertResolvedNodeText(findNode.functionReference('foo.m<int>;'), r'''
|
||||
|
@ -1051,10 +1047,10 @@ FunctionReference
|
|||
period: .
|
||||
identifier: SimpleIdentifier
|
||||
token: m
|
||||
staticElement: self::@extension::0::@method::m
|
||||
staticType: null
|
||||
staticElement: self::@extension::0::@method::m
|
||||
staticType: void Function<T>(T)
|
||||
staticElement: <null>
|
||||
staticType: InvalidType
|
||||
staticElement: <null>
|
||||
staticType: InvalidType
|
||||
typeArguments: TypeArgumentList
|
||||
leftBracket: <
|
||||
arguments
|
||||
|
@ -1063,9 +1059,7 @@ FunctionReference
|
|||
element: dart:core::@class::int
|
||||
type: int
|
||||
rightBracket: >
|
||||
staticType: void Function(int)
|
||||
typeArgumentTypes
|
||||
int
|
||||
staticType: InvalidType
|
||||
''');
|
||||
}
|
||||
|
||||
|
|
|
@ -347,6 +347,29 @@ int Function() foo(A? a) {
|
|||
assertType(identifier, 'A?');
|
||||
}
|
||||
|
||||
test_inClass_getterInherited_setterDeclaredLocally() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
int get foo => 7;
|
||||
}
|
||||
class B extends A {
|
||||
set foo(int _) {}
|
||||
|
||||
void f() {
|
||||
foo;
|
||||
}
|
||||
}
|
||||
''');
|
||||
|
||||
var node = findNode.simple('foo;');
|
||||
assertResolvedNodeText(node, r'''
|
||||
SimpleIdentifier
|
||||
token: foo
|
||||
staticElement: self::@class::A::@getter::foo
|
||||
staticType: int
|
||||
''');
|
||||
}
|
||||
|
||||
test_inClass_inDeclaration_augmentationDeclares() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
augment library 'test.dart'
|
||||
|
|
|
@ -65,6 +65,36 @@ PropertyAccess
|
|||
''');
|
||||
}
|
||||
|
||||
test_getter_getterStatic() async {
|
||||
await assertNoErrorsInCode('''
|
||||
extension E1 on int {
|
||||
void get a => 1;
|
||||
}
|
||||
|
||||
extension E2 on int {
|
||||
static void get a => 2;
|
||||
}
|
||||
|
||||
f() {
|
||||
0.a;
|
||||
}
|
||||
''');
|
||||
|
||||
var node = findNode.propertyAccess('0.a');
|
||||
assertResolvedNodeText(node, r'''
|
||||
PropertyAccess
|
||||
target: IntegerLiteral
|
||||
literal: 0
|
||||
staticType: int
|
||||
operator: .
|
||||
propertyName: SimpleIdentifier
|
||||
token: a
|
||||
staticElement: self::@extension::E1::@getter::a
|
||||
staticType: void
|
||||
staticType: void
|
||||
''');
|
||||
}
|
||||
|
||||
test_getter_method() async {
|
||||
await assertErrorsInCode('''
|
||||
extension E on int {
|
||||
|
|
|
@ -34,98 +34,6 @@ f(C c) {
|
|||
);
|
||||
}
|
||||
|
||||
test_extension_getter() async {
|
||||
await assertErrorsInCode('''
|
||||
class C {}
|
||||
|
||||
extension E on C {
|
||||
static int get a => 0;
|
||||
}
|
||||
|
||||
C g(C c) => C();
|
||||
f(C c) {
|
||||
g(c).a;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 92, 1,
|
||||
correctionContains: "extension 'E'"),
|
||||
]);
|
||||
assertElement(
|
||||
findNode.simple('a;'),
|
||||
findElement.getter('a'),
|
||||
);
|
||||
}
|
||||
|
||||
test_extension_getter_unnamed() async {
|
||||
await assertErrorsInCode('''
|
||||
class C {}
|
||||
|
||||
extension on C {
|
||||
static int get a => 0;
|
||||
}
|
||||
|
||||
C g(C c) => C();
|
||||
f(C c) {
|
||||
g(c).a;
|
||||
}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode
|
||||
.INSTANCE_ACCESS_TO_STATIC_MEMBER_OF_UNNAMED_EXTENSION,
|
||||
90,
|
||||
1),
|
||||
]);
|
||||
assertElement(
|
||||
findNode.simple('a;'),
|
||||
findElement.getter('a'),
|
||||
);
|
||||
}
|
||||
|
||||
test_extension_method() async {
|
||||
await assertErrorsInCode('''
|
||||
class C {}
|
||||
|
||||
extension E on C {
|
||||
static void a() {}
|
||||
}
|
||||
|
||||
f(C c) {
|
||||
c.a();
|
||||
}
|
||||
''', [
|
||||
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_OF_UNNAMED_EXTENSION,
|
||||
66,
|
||||
1),
|
||||
]);
|
||||
assertElement(
|
||||
findNode.methodInvocation('a();'),
|
||||
findElement.method('a'),
|
||||
);
|
||||
}
|
||||
|
||||
test_extension_referring_to_class_member() async {
|
||||
await assertErrorsInCode('''
|
||||
class C {
|
||||
|
@ -145,49 +53,6 @@ test(int i) {
|
|||
]);
|
||||
}
|
||||
|
||||
test_extension_setter() async {
|
||||
await assertErrorsInCode('''
|
||||
class C {}
|
||||
|
||||
extension E on C {
|
||||
static set a(int v) {}
|
||||
}
|
||||
|
||||
f(C c) {
|
||||
c.a = 2;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 72, 1),
|
||||
]);
|
||||
|
||||
assertResolvedNodeText(findNode.assignment('a ='), r'''
|
||||
AssignmentExpression
|
||||
leftHandSide: PrefixedIdentifier
|
||||
prefix: SimpleIdentifier
|
||||
token: c
|
||||
staticElement: self::@function::f::@parameter::c
|
||||
staticType: C
|
||||
period: .
|
||||
identifier: SimpleIdentifier
|
||||
token: a
|
||||
staticElement: <null>
|
||||
staticType: null
|
||||
staticElement: <null>
|
||||
staticType: null
|
||||
operator: =
|
||||
rightHandSide: IntegerLiteral
|
||||
literal: 2
|
||||
parameter: self::@extension::E::@setter::a::@parameter::v
|
||||
staticType: int
|
||||
readElement: <null>
|
||||
readType: null
|
||||
writeElement: self::@extension::E::@setter::a
|
||||
writeType: int
|
||||
staticElement: <null>
|
||||
staticType: int
|
||||
''');
|
||||
}
|
||||
|
||||
test_method_reference() async {
|
||||
await assertErrorsInCode(r'''
|
||||
class A {
|
||||
|
@ -211,25 +76,7 @@ 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_OF_UNNAMED_EXTENSION,
|
||||
55,
|
||||
1),
|
||||
error(CompileTimeErrorCode.UNDEFINED_GETTER, 57, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -481,6 +481,23 @@ f(var p) {
|
|||
]);
|
||||
}
|
||||
|
||||
test_static_extension_InstanceAccess() async {
|
||||
await assertErrorsInCode('''
|
||||
class C {}
|
||||
|
||||
extension E on C {
|
||||
static int get a => 0;
|
||||
}
|
||||
|
||||
C g(C c) => C();
|
||||
f(C c) {
|
||||
g(c).a;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.UNDEFINED_GETTER, 92, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_static_undefined() async {
|
||||
await assertErrorsInCode('''
|
||||
class C {}
|
||||
|
|
|
@ -75,6 +75,42 @@ f(C c) {
|
|||
]);
|
||||
}
|
||||
|
||||
test_extensionMethodHiddenByStaticSetter() async {
|
||||
await assertErrorsInCode('''
|
||||
class C {
|
||||
void f() {
|
||||
foo();
|
||||
}
|
||||
static set foo(int x) {}
|
||||
}
|
||||
|
||||
extension E on C {
|
||||
int foo() => 1;
|
||||
}
|
||||
|
||||
''', [
|
||||
error(CompileTimeErrorCode.UNDEFINED_METHOD, 27, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_extensionMethodShadowingTopLevelSetter() async {
|
||||
await assertErrorsInCode('''
|
||||
class C {
|
||||
void f() {
|
||||
foo();
|
||||
}
|
||||
}
|
||||
|
||||
extension E on C {
|
||||
int foo() => 1;
|
||||
}
|
||||
|
||||
set foo(int x) {}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.UNDEFINED_METHOD, 27, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_functionAlias_notInstantiated() async {
|
||||
await assertNoErrorsInCode('''
|
||||
typedef Fn<T> = void Function(T);
|
||||
|
@ -152,6 +188,26 @@ class C {
|
|||
]);
|
||||
}
|
||||
|
||||
test_localSetterShadowingExtensionMethod() async {
|
||||
await assertErrorsInCode('''
|
||||
class C {}
|
||||
|
||||
extension E1 on C {
|
||||
int foo(int x) => 1;
|
||||
}
|
||||
|
||||
extension E2 on C {
|
||||
static set foo(int x) {}
|
||||
|
||||
void f() {
|
||||
foo();
|
||||
}
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.UNDEFINED_METHOD, 123, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_method_undefined() async {
|
||||
await assertErrorsInCode(r'''
|
||||
class C {
|
||||
|
@ -217,6 +273,22 @@ f() { A?.m(); }
|
|||
]);
|
||||
}
|
||||
|
||||
test_static_extension_instanceAccess() async {
|
||||
await assertErrorsInCode('''
|
||||
class C {}
|
||||
|
||||
extension E on C {
|
||||
static void a() {}
|
||||
}
|
||||
|
||||
f(C c) {
|
||||
c.a();
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.UNDEFINED_METHOD, 68, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_static_mixinApplication_superConstructorIsFactory() async {
|
||||
await assertErrorsInCode(r'''
|
||||
mixin M {}
|
||||
|
|
|
@ -232,6 +232,49 @@ f(var p) {
|
|||
]);
|
||||
}
|
||||
|
||||
test_static_extension_instanceAccess() async {
|
||||
await assertErrorsInCode('''
|
||||
class C {}
|
||||
|
||||
extension E on C {
|
||||
static set a(int v) {}
|
||||
}
|
||||
|
||||
f(C c) {
|
||||
c.a = 2;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.UNDEFINED_SETTER, 72, 1),
|
||||
]);
|
||||
|
||||
assertResolvedNodeText(findNode.assignment('a ='), r'''
|
||||
AssignmentExpression
|
||||
leftHandSide: PrefixedIdentifier
|
||||
prefix: SimpleIdentifier
|
||||
token: c
|
||||
staticElement: self::@function::f::@parameter::c
|
||||
staticType: C
|
||||
period: .
|
||||
identifier: SimpleIdentifier
|
||||
token: a
|
||||
staticElement: <null>
|
||||
staticType: null
|
||||
staticElement: <null>
|
||||
staticType: null
|
||||
operator: =
|
||||
rightHandSide: IntegerLiteral
|
||||
literal: 2
|
||||
parameter: <null>
|
||||
staticType: int
|
||||
readElement: <null>
|
||||
readType: null
|
||||
writeElement: <null>
|
||||
writeType: InvalidType
|
||||
staticElement: <null>
|
||||
staticType: int
|
||||
''');
|
||||
}
|
||||
|
||||
test_static_undefined() async {
|
||||
await assertErrorsInCode(r'''
|
||||
class A {}
|
||||
|
|
|
@ -68,37 +68,37 @@ extension E2 on A2 {
|
|||
void test() {
|
||||
// The instance setter shadows the global getter
|
||||
topLevelGetter = topLevelGetter + 1;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'topLevelGetter'.
|
||||
topLevelGetter++;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'topLevelGetter'.
|
||||
topLevelGetter;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'topLevelGetter'.
|
||||
topLevelGetter = 3;
|
||||
|
||||
// The instance setter shadows the global field getter
|
||||
topLevelField = topLevelField + 1;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'topLevelField'.
|
||||
topLevelField++;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'topLevelField'.
|
||||
topLevelField;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'topLevelField'.
|
||||
|
||||
// The instance setter shadows the global method
|
||||
topLevelMethod(4);
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_METHOD
|
||||
// [cfe] Getter not found: 'topLevelMethod'.
|
||||
}
|
||||
}
|
||||
|
@ -160,36 +160,36 @@ extension E4 on A4 {
|
|||
void test() {
|
||||
// The static setter shadows the global getter
|
||||
topLevelGetter = topLevelGetter + 1;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'topLevelGetter'.
|
||||
topLevelGetter++;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'topLevelGetter'.
|
||||
topLevelGetter;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'topLevelGetter'.
|
||||
|
||||
// The static setter shadows the global field getter
|
||||
topLevelField = topLevelField + 1;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'topLevelField'.
|
||||
topLevelField++;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'topLevelField'.
|
||||
topLevelField;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'topLevelField'.
|
||||
|
||||
// The static setter shadows the global method
|
||||
topLevelMethod(4);
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_METHOD
|
||||
// [cfe] Getter not found: 'topLevelMethod'.
|
||||
}
|
||||
}
|
||||
|
@ -276,22 +276,22 @@ extension E8 on A8 {
|
|||
void test() {
|
||||
// The instance setter shadows the other extension's getter
|
||||
extensionGetter = extensionGetter + 1;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'extensionGetter'.
|
||||
extensionGetter++;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'extensionGetter'.
|
||||
extensionGetter;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'extensionGetter'.
|
||||
|
||||
// The instance setter shadows the other extension's method.
|
||||
extensionMethod(4);
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_METHOD
|
||||
// [cfe] Getter not found: 'extensionMethod'.
|
||||
}
|
||||
}
|
||||
|
@ -306,22 +306,22 @@ class A9 extends A8 {
|
|||
void test() {
|
||||
// The instance setter shadows the other extension's getter
|
||||
extensionGetter = extensionGetter + 1;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] The getter 'extensionGetter' isn't defined for the class 'A9'.
|
||||
extensionGetter++;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] The getter 'extensionGetter' isn't defined for the class 'A9'.
|
||||
extensionGetter;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] The getter 'extensionGetter' isn't defined for the class 'A9'.
|
||||
|
||||
// The instance setter shadows the other extension's method.
|
||||
extensionMethod(4);
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_METHOD
|
||||
// [cfe] The method 'extensionMethod' isn't defined for the class 'A9'.
|
||||
}
|
||||
}
|
||||
|
@ -356,16 +356,16 @@ extension E10 on A10 {
|
|||
|
||||
// The static field shadows the other extension's setter
|
||||
extensionFieldSetter = extensionFieldSetter + 1;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL
|
||||
// [cfe] Setter not found: 'extensionFieldSetter'.
|
||||
extensionFieldSetter++;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL
|
||||
// [cfe] Setter not found: 'extensionFieldSetter'.
|
||||
extensionFieldSetter = 0;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL
|
||||
// [cfe] Setter not found: 'extensionFieldSetter'.
|
||||
|
||||
// The static getter shadows the other extensions method
|
||||
|
@ -402,16 +402,16 @@ class A11 extends A10 {
|
|||
|
||||
// The static field shadows the other extension's setter
|
||||
extensionFieldSetter = extensionFieldSetter + 1;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL
|
||||
// [cfe] Setter not found: 'extensionFieldSetter'.
|
||||
extensionFieldSetter++;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL
|
||||
// [cfe] Setter not found: 'extensionFieldSetter'.
|
||||
extensionFieldSetter = 0;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL
|
||||
// [cfe] Setter not found: 'extensionFieldSetter'.
|
||||
|
||||
// The static getter shadows the other extensions method
|
||||
|
@ -437,22 +437,22 @@ extension E12 on A12 {
|
|||
void test() {
|
||||
// The static setter shadows the other extension's getter
|
||||
extensionGetter = extensionGetter + 1;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'extensionGetter'.
|
||||
extensionGetter++;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'extensionGetter'.
|
||||
extensionGetter;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'extensionGetter'.
|
||||
|
||||
// The static setter shadows the other extension's method.
|
||||
extensionMethod(4);
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_METHOD
|
||||
// [cfe] Getter not found: 'extensionMethod'.
|
||||
}
|
||||
}
|
||||
|
@ -467,22 +467,22 @@ class A13 extends A12 {
|
|||
void test() {
|
||||
// The static setter shadows the other extension's getter
|
||||
extensionGetter = extensionGetter + 1;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'extensionGetter'.
|
||||
extensionGetter++;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'extensionGetter'.
|
||||
extensionGetter;
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
// [cfe] Getter not found: 'extensionGetter'.
|
||||
|
||||
// The static setter shadows the other extension's method.
|
||||
extensionMethod(4);
|
||||
// ^^
|
||||
// [analyzer] unspecified
|
||||
// ^^^^^^^^^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_METHOD
|
||||
// [cfe] Getter not found: 'extensionMethod'.
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue