Fix reporting of INSTANCE_ACCESS_TO_STATIC_MEMBER from within an extension.

Previously, if a piece of code inside an extension tried erroneously
to refer to a static class member using instance access, the error
CompileTimeError.UNQUALIFIED_ACCESS_TO_STATIC_MEMBER_OF_EXTENDED_TYPE
would be reported.  This change corrects the error to
CompileTimeError.INSTANCE_ACCESS_TO_STATIC_MEMBER.

In addition, the responsibility to report the error
CompileTimeError.INSTANCE_ACCESS_TO_STATIC_MEMBER is shifted fully to
the resolver (previously, it was split between the resolver and the
error verifier).

Change-Id: Idcd1a3b8a1e226fed692900838c3d2d3c0585d4f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/217020
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
Paul Berry 2021-10-17 06:22:30 +00:00 committed by commit-bot@chromium.org
parent 0a8091f908
commit 189aeead47
7 changed files with 99 additions and 92 deletions

View file

@ -149,19 +149,21 @@ class FunctionReferenceResolver {
required bool implicitReceiver,
}) {
var enclosingElement = element.enclosingElement;
if (_resolver.enclosingExtension != null) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode
.UNQUALIFIED_REFERENCE_TO_STATIC_MEMBER_OF_EXTENDED_TYPE,
nameNode,
[enclosingElement.displayName],
);
} else if (implicitReceiver) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER,
nameNode,
[enclosingElement.displayName],
);
if (implicitReceiver) {
if (_resolver.enclosingExtension != null) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode
.UNQUALIFIED_REFERENCE_TO_STATIC_MEMBER_OF_EXTENDED_TYPE,
nameNode,
[enclosingElement.displayName],
);
} else {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER,
nameNode,
[enclosingElement.displayName],
);
}
} else {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER,

View file

@ -219,19 +219,21 @@ class MethodInvocationResolver {
bool nullReceiver,
) {
var enclosingElement = element.enclosingElement;
if (_resolver.enclosingExtension != null) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode
.UNQUALIFIED_REFERENCE_TO_STATIC_MEMBER_OF_EXTENDED_TYPE,
nameNode,
[enclosingElement.displayName],
);
} else if (nullReceiver) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER,
nameNode,
[enclosingElement.displayName],
);
if (nullReceiver) {
if (_resolver.enclosingExtension != null) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode
.UNQUALIFIED_REFERENCE_TO_STATIC_MEMBER_OF_EXTENDED_TYPE,
nameNode,
[enclosingElement.displayName],
);
} else {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER,
nameNode,
[enclosingElement.displayName],
);
}
} else {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER,

View file

@ -248,18 +248,6 @@ class PropertyElementResolver {
);
}
void _checkExtensionOverrideStaticMember(
SimpleIdentifier propertyName,
ExecutableElement? element,
) {
if (element != null && element.isStatic) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.EXTENSION_OVERRIDE_ACCESS_TO_STATIC_MEMBER,
propertyName,
);
}
}
/// If the [element] is not static, report the error on the [identifier].
void _checkForStaticAccessToInstanceMember(
SimpleIdentifier identifier,
@ -274,6 +262,33 @@ class PropertyElementResolver {
);
}
void _checkForStaticMember(
Expression target,
SimpleIdentifier propertyName,
ExecutableElement? element,
) {
if (element != null && element.isStatic) {
if (target is ExtensionOverride) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.EXTENSION_OVERRIDE_ACCESS_TO_STATIC_MEMBER,
propertyName,
);
} else {
var enclosingElement = element.enclosingElement;
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER,
propertyName, [
propertyName,
element.kind.displayName,
enclosingElement.name ?? '<unnamed>',
enclosingElement is ClassElement && enclosingElement.isMixin
? 'mixin'
: enclosingElement.kind.displayName,
]);
}
}
}
DartType? _computeIndexContextType({
required ExecutableElement? readElement,
required ExecutableElement? writeElement,
@ -417,21 +432,27 @@ class PropertyElementResolver {
result.getter,
result.getter?.returnType ?? _typeSystem.typeProvider.dynamicType);
if (hasRead && result.needsGetterError) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_GETTER,
propertyName,
[propertyName.name, targetType],
);
if (hasRead) {
_checkForStaticMember(target, propertyName, result.getter);
if (result.needsGetterError) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_GETTER,
propertyName,
[propertyName.name, targetType],
);
}
}
if (hasWrite && result.needsSetterError) {
AssignmentVerifier(_definingLibrary, _errorReporter).verify(
node: propertyName,
requested: null,
recovery: result.getter,
receiverTypeObject: targetType,
);
if (hasWrite) {
_checkForStaticMember(target, propertyName, result.setter);
if (result.needsSetterError) {
AssignmentVerifier(_definingLibrary, _errorReporter).verify(
node: propertyName,
requested: null,
recovery: result.getter,
receiverTypeObject: targetType,
);
}
}
return PropertyElementResolverResult(
@ -591,7 +612,7 @@ class PropertyElementResolver {
[memberName, element.name],
);
}
_checkExtensionOverrideStaticMember(propertyName, readElement);
_checkForStaticMember(target, propertyName, readElement);
}
ExecutableElement? writeElement;
@ -604,7 +625,7 @@ class PropertyElementResolver {
[memberName, element.name],
);
}
_checkExtensionOverrideStaticMember(propertyName, writeElement);
_checkForStaticMember(target, propertyName, writeElement);
}
return PropertyElementResolverResult(
@ -670,6 +691,7 @@ class PropertyElementResolver {
if (readElement != null) {
readElement = _resolver.toLegacyElement(readElement);
_checkForStaticMember(target, propertyName, readElement);
} else {
// We were not able to find the concrete dispatch target.
// But we would like to give the user at least some resolution.
@ -708,6 +730,7 @@ class PropertyElementResolver {
if (writeElement != null) {
writeElement = _resolver.toLegacyElement(writeElement);
_checkForStaticMember(target, propertyName, writeElement);
} else {
// We were not able to find the concrete dispatch target.
// But we would like to give the user at least some resolution.

View file

@ -2658,15 +2658,6 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
return;
}
}
errorReporter.reportErrorForNode(
CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, name, [
name.name,
_getKind(element),
enclosingElement.name ?? '<unnamed>',
enclosingElement is ClassElement && enclosingElement.isMixin
? 'mixin'
: enclosingElement.kind.displayName
]);
}
}
@ -4856,30 +4847,6 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
return null;
}
/// Return a human-readable representation of the kind of the [element].
String _getKind(ExecutableElement element) {
if (element is MethodElement) {
return 'method';
} else if (element is PropertyAccessorElement) {
if (element.isSynthetic) {
PropertyInducingElement variable = element.variable;
if (variable is FieldElement) {
return 'field';
}
return 'variable';
} else if (element.isGetter) {
return 'getter';
} else {
return 'setter';
}
} else if (element is ConstructorElement) {
return 'constructor';
} else if (element is FunctionElement) {
return 'function';
}
return 'member';
}
/// Return the name of the library that defines given [element].
String _getLibraryName(Element? element) {
if (element == null) {

View file

@ -98,6 +98,25 @@ f(C c) {
);
}
test_extension_referring_to_class_member() async {
await assertErrorsInCode('''
class C {
static void m() {}
}
extension on int {
foo(C c) {
c.m(); // ERROR
}
}
test(int i) {
i.foo(C());
}
''', [
error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 71, 1,
correctionContains: "class 'C'"),
]);
}
test_extension_setter() async {
await assertErrorsInCode('''
class C {}

View file

@ -15,7 +15,6 @@ class Example {
// [cfe] Setter not found: 'nextVar'.
this.nextVar = 1;
// ^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.INSTANCE_ACCESS_TO_STATIC_MEMBER
// [cfe] The setter 'nextVar' isn't defined for the class 'Example'.
// ^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL_NO_SETTER
@ -30,8 +29,6 @@ class Example {
// [analyzer] COMPILE_TIME_ERROR.INVALID_REFERENCE_TO_THIS
// [cfe] Expected identifier, but got 'this'.
// ^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.INSTANCE_ACCESS_TO_STATIC_MEMBER
// ^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL_NO_SETTER
}
}

View file

@ -17,7 +17,6 @@ class Example {
// [cfe] Setter not found: 'nextVar'.
this.nextVar = 1;
// ^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.INSTANCE_ACCESS_TO_STATIC_MEMBER
// [cfe] The setter 'nextVar' isn't defined for the class 'Example'.
// ^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL_NO_SETTER
@ -32,8 +31,6 @@ class Example {
// [analyzer] COMPILE_TIME_ERROR.INVALID_REFERENCE_TO_THIS
// [cfe] Expected identifier, but got 'this'.
// ^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.INSTANCE_ACCESS_TO_STATIC_MEMBER
// ^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL_NO_SETTER
}
}