mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 02:27:39 +00:00
Resolve / report errors for extension overrides and index expressions.
R=brianwilkerson@google.com Bug: https://github.com/dart-lang/sdk/issues/38484 Change-Id: Ie02c24c7929f736279d93e06f329b226a39835ea Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/118462 Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
d53d355c6c
commit
e6092d54ae
|
@ -69,34 +69,38 @@ class ExtensionMemberResolver {
|
|||
return ResolutionResult.ambiguous;
|
||||
}
|
||||
|
||||
/// Return the member with the [name] (without `=`).
|
||||
/// Resolve the [name] (without `=`) to the corresponding getter and setter
|
||||
/// members of the extension [node].
|
||||
///
|
||||
/// The [node] is fully resolved, and its type arguments are set.
|
||||
ExecutableElement getOverrideMember(
|
||||
ExtensionOverride node,
|
||||
String name, {
|
||||
bool setter = false,
|
||||
}) {
|
||||
ResolutionResult getOverrideMember(ExtensionOverride node, String name) {
|
||||
ExtensionElement element = node.extensionName.staticElement;
|
||||
|
||||
ExecutableElement member;
|
||||
if (setter) {
|
||||
member = element.getSetter(name);
|
||||
ExecutableElement getter;
|
||||
ExecutableElement setter;
|
||||
if (name == '[]') {
|
||||
getter = element.getMethod('[]');
|
||||
setter = element.getMethod('[]=');
|
||||
} else {
|
||||
member = element.getGetter(name) ?? element.getMethod(name);
|
||||
getter = element.getGetter(name) ?? element.getMethod(name);
|
||||
setter = element.getSetter(name);
|
||||
}
|
||||
|
||||
if (member == null) {
|
||||
return null;
|
||||
if (getter == null && setter == null) {
|
||||
return ResolutionResult.none;
|
||||
}
|
||||
|
||||
return ExecutableMember.from2(
|
||||
member,
|
||||
Substitution.fromPairs(
|
||||
element.typeParameters,
|
||||
node.typeArgumentTypes,
|
||||
),
|
||||
var substitution = Substitution.fromPairs(
|
||||
element.typeParameters,
|
||||
node.typeArgumentTypes,
|
||||
);
|
||||
|
||||
var getterMember =
|
||||
getter != null ? ExecutableMember.from2(getter, substitution) : null;
|
||||
var setterMember =
|
||||
setter != null ? ExecutableMember.from2(setter, substitution) : null;
|
||||
|
||||
return ResolutionResult(getter: getterMember, setter: setterMember);
|
||||
}
|
||||
|
||||
/// Perform upward inference for the override.
|
||||
|
|
|
@ -390,7 +390,8 @@ class MethodInvocationResolver {
|
|||
|
||||
void _resolveExtensionOverride(MethodInvocation node,
|
||||
ExtensionOverride override, SimpleIdentifier nameNode, String name) {
|
||||
var member = _extensionResolver.getOverrideMember(override, name);
|
||||
var result = _extensionResolver.getOverrideMember(override, name);
|
||||
var member = result.getter;
|
||||
|
||||
if (member == null) {
|
||||
_setDynamicResolution(node);
|
||||
|
|
|
@ -396,7 +396,8 @@ class ElementResolver extends SimpleAstVisitor<void> {
|
|||
Expression function = node.function;
|
||||
DartType functionType;
|
||||
if (function is ExtensionOverride) {
|
||||
var member = _extensionResolver.getOverrideMember(function, 'call');
|
||||
var result = _extensionResolver.getOverrideMember(function, 'call');
|
||||
var member = result.getter;
|
||||
if (member == null) {
|
||||
_resolver.errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.INVOCATION_OF_EXTENSION_WITHOUT_CALL,
|
||||
|
@ -478,8 +479,13 @@ class ElementResolver extends SimpleAstVisitor<void> {
|
|||
String getterMethodName = TokenType.INDEX.lexeme;
|
||||
String setterMethodName = TokenType.INDEX_EQ.lexeme;
|
||||
|
||||
var result = _newPropertyResolver()
|
||||
.resolve(target, staticType, getterMethodName, target);
|
||||
ResolutionResult result;
|
||||
if (target is ExtensionOverride) {
|
||||
result = _extensionResolver.getOverrideMember(target, getterMethodName);
|
||||
} else {
|
||||
result = _newPropertyResolver()
|
||||
.resolve(target, staticType, getterMethodName, target);
|
||||
}
|
||||
|
||||
bool isInGetterContext = node.inGetterContext();
|
||||
bool isInSetterContext = node.inSetterContext();
|
||||
|
@ -694,12 +700,9 @@ class ElementResolver extends SimpleAstVisitor<void> {
|
|||
SimpleIdentifier propertyName = node.propertyName;
|
||||
String memberName = propertyName.name;
|
||||
ExecutableElement member;
|
||||
var result = _extensionResolver.getOverrideMember(target, memberName);
|
||||
if (propertyName.inSetterContext()) {
|
||||
member = _extensionResolver.getOverrideMember(
|
||||
target,
|
||||
memberName,
|
||||
setter: true,
|
||||
);
|
||||
member = result.setter;
|
||||
if (member == null) {
|
||||
_resolver.errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.UNDEFINED_EXTENSION_SETTER,
|
||||
|
@ -707,8 +710,7 @@ class ElementResolver extends SimpleAstVisitor<void> {
|
|||
[memberName, element.name]);
|
||||
}
|
||||
if (propertyName.inGetterContext()) {
|
||||
PropertyAccessorElement getter =
|
||||
_extensionResolver.getOverrideMember(target, memberName);
|
||||
PropertyAccessorElement getter = result.getter;
|
||||
if (getter == null) {
|
||||
_resolver.errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.UNDEFINED_EXTENSION_GETTER,
|
||||
|
@ -718,7 +720,7 @@ class ElementResolver extends SimpleAstVisitor<void> {
|
|||
propertyName.auxiliaryElements = AuxiliaryElements(getter, null);
|
||||
}
|
||||
} else if (propertyName.inGetterContext()) {
|
||||
member = _extensionResolver.getOverrideMember(target, memberName);
|
||||
member = result.getter;
|
||||
if (member == null) {
|
||||
_resolver.errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.UNDEFINED_EXTENSION_GETTER,
|
||||
|
@ -984,30 +986,43 @@ class ElementResolver extends SimpleAstVisitor<void> {
|
|||
if (element != null) {
|
||||
return;
|
||||
}
|
||||
if (staticType == null || staticType.isDynamic) {
|
||||
return;
|
||||
if (target is! ExtensionOverride) {
|
||||
if (staticType == null || staticType.isDynamic) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Token leftBracket = expression.leftBracket;
|
||||
Token rightBracket = expression.rightBracket;
|
||||
ErrorCode errorCode;
|
||||
var errorArguments = [methodName, staticType.displayName];
|
||||
if (target is SuperExpression) {
|
||||
errorCode = StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR;
|
||||
} else if (staticType != null && staticType.isVoid) {
|
||||
errorCode = StaticWarningCode.USE_OF_VOID_RESULT;
|
||||
errorArguments = [];
|
||||
var leftBracket = expression.leftBracket;
|
||||
var rightBracket = expression.rightBracket;
|
||||
var offset = leftBracket.offset;
|
||||
var length = rightBracket.end - offset;
|
||||
if (target is ExtensionOverride) {
|
||||
_resolver.errorReporter.reportErrorForOffset(
|
||||
CompileTimeErrorCode.UNDEFINED_EXTENSION_OPERATOR,
|
||||
offset,
|
||||
length,
|
||||
[methodName, target.staticElement.name],
|
||||
);
|
||||
} else if (target is SuperExpression) {
|
||||
_resolver.errorReporter.reportErrorForOffset(
|
||||
StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR,
|
||||
offset,
|
||||
length,
|
||||
[methodName, staticType.displayName],
|
||||
);
|
||||
} else if (staticType.isVoid) {
|
||||
_resolver.errorReporter.reportErrorForOffset(
|
||||
StaticWarningCode.USE_OF_VOID_RESULT,
|
||||
offset,
|
||||
length,
|
||||
);
|
||||
} else {
|
||||
errorCode = StaticTypeWarningCode.UNDEFINED_OPERATOR;
|
||||
}
|
||||
if (leftBracket == null || rightBracket == null) {
|
||||
_recordUndefinedNode(
|
||||
staticType.element, errorCode, expression, errorArguments);
|
||||
} else {
|
||||
int offset = leftBracket.offset;
|
||||
int length = rightBracket.offset - offset + 1;
|
||||
_recordUndefinedOffset(
|
||||
staticType.element, errorCode, offset, length, errorArguments);
|
||||
_resolver.errorReporter.reportErrorForOffset(
|
||||
StaticTypeWarningCode.UNDEFINED_OPERATOR,
|
||||
offset,
|
||||
length,
|
||||
[methodName, staticType.displayName],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1225,20 +1240,6 @@ class ElementResolver extends SimpleAstVisitor<void> {
|
|||
_resolver.errorReporter.reportErrorForNode(errorCode, node, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record that the given [offset]/[length] is undefined, causing an error to
|
||||
* be reported if appropriate. The [declaringElement] is the element inside
|
||||
* which no declaration was found. If this element is a proxy, no error will
|
||||
* be reported. If null, then an error will always be reported. The
|
||||
* [errorCode] is the error code to report. The [arguments] are arguments to
|
||||
* the error message.
|
||||
*/
|
||||
void _recordUndefinedOffset(Element declaringElement, ErrorCode errorCode,
|
||||
int offset, int length, List<Object> arguments) {
|
||||
_resolver.errorReporter
|
||||
.reportErrorForOffset(errorCode, offset, length, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record that the given [token] is undefined, causing an error to be reported
|
||||
* if appropriate. The [declaringElement] is the element inside which no
|
||||
|
|
|
@ -47,4 +47,154 @@ f() {
|
|||
assertInvokeTypeNull(binaryExpression);
|
||||
assertTypeDynamic(binaryExpression);
|
||||
}
|
||||
|
||||
test_index_get_hasGetter() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
class A {}
|
||||
|
||||
extension E on A {
|
||||
int operator[](int index) => 0;
|
||||
}
|
||||
|
||||
f(A a) {
|
||||
E(a)[0];
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_index_get_hasNone() async {
|
||||
await assertErrorsInCode(r'''
|
||||
class A {}
|
||||
|
||||
extension E on A {}
|
||||
|
||||
f(A a) {
|
||||
E(a)[0];
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.UNDEFINED_EXTENSION_OPERATOR, 48, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_index_get_hasSetter() async {
|
||||
await assertErrorsInCode(r'''
|
||||
class A {}
|
||||
|
||||
extension E on A {
|
||||
void operator[]=(int index, int value) {}
|
||||
}
|
||||
|
||||
f(A a) {
|
||||
E(a)[0];
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.UNDEFINED_EXTENSION_OPERATOR, 93, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_index_getSet_hasBoth() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
class A {}
|
||||
|
||||
extension E on A {
|
||||
int operator[](int index) => 0;
|
||||
void operator[]=(int index, int value) {}
|
||||
}
|
||||
|
||||
f(A a) {
|
||||
E(a)[0] += 1;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_index_getSet_hasGetter() async {
|
||||
await assertErrorsInCode(r'''
|
||||
class A {}
|
||||
|
||||
extension E on A {
|
||||
int operator[](int index) => 0;
|
||||
}
|
||||
|
||||
f(A a) {
|
||||
E(a)[0] += 1;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.UNDEFINED_EXTENSION_OPERATOR, 83, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_index_getSet_hasNone() async {
|
||||
await assertErrorsInCode(r'''
|
||||
class A {}
|
||||
|
||||
extension E on A {}
|
||||
|
||||
f(A a) {
|
||||
E(a)[0] += 1;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.UNDEFINED_EXTENSION_OPERATOR, 48, 3),
|
||||
error(CompileTimeErrorCode.UNDEFINED_EXTENSION_OPERATOR, 48, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_index_getSet_hasSetter() async {
|
||||
await assertErrorsInCode(r'''
|
||||
class A {}
|
||||
|
||||
extension E on A {
|
||||
void operator[]=(int index, int value) {}
|
||||
}
|
||||
|
||||
f(A a) {
|
||||
E(a)[0] += 1;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.UNDEFINED_EXTENSION_OPERATOR, 93, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_index_set_hasGetter() async {
|
||||
await assertErrorsInCode(r'''
|
||||
class A {}
|
||||
|
||||
extension E on A {
|
||||
int operator[](int index) => 0;
|
||||
}
|
||||
|
||||
f(A a) {
|
||||
E(a)[0] = 1;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.UNDEFINED_EXTENSION_OPERATOR, 83, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_index_set_hasNone() async {
|
||||
await assertErrorsInCode(r'''
|
||||
class A {}
|
||||
|
||||
extension E on A {}
|
||||
|
||||
f(A a) {
|
||||
E(a)[0] = 1;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.UNDEFINED_EXTENSION_OPERATOR, 48, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_index_set_hasSetter() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
class A {}
|
||||
|
||||
extension E on A {
|
||||
void operator[]=(int index, int value) {}
|
||||
}
|
||||
|
||||
f(A a) {
|
||||
E(a)[0] = 1;
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue