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:
Konstantin Shcheglov 2019-09-24 01:51:10 +00:00 committed by commit-bot@chromium.org
parent d53d355c6c
commit e6092d54ae
4 changed files with 221 additions and 65 deletions

View file

@ -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.

View file

@ -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);

View file

@ -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

View file

@ -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;
}
''');
}
}