mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 14:32:24 +00:00
Resolve more cases of .call; correct many cases of PropertyAccess receivers
Fixes https://github.com/dart-lang/sdk/issues/47211 Much of this change is about decision-making based on the _type_ of the function in a FunctionReference, or the receiver of that function, in the case of prefixed identifiers or property access. Previously, there was a rule that only direct references to functions (top-level, local, and method) could be torn off and type-instantiated. That rule has been reversed (#1812), so a lot of unraveling has to be done here. `e.call<...>` is now legal for _any_ generic function-typed expression `e`, which certainly may be an expression without a staticElement. Additionally, `e<...>` is now legal for _any_ generic function-typed expression `e`. Because we no longer resolve PropertyAccess piecemeal, we no longer report UNDEFINED_IDENTIFIER or UNDEFINED_PREFIXED_ELEMENT etc before deciding to check if a tearoff is being made on a function-typed element. The unfortunate side-effect here is some redundant error-reporting. :( I've left TODOs. Change-Id: I62106332e39d528cbd7cdfa5ec831dc56b394b52 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/213800 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
parent
5c83d863ef
commit
f12333dfa0
2 changed files with 202 additions and 94 deletions
|
@ -89,7 +89,6 @@ class FunctionReferenceResolver {
|
|||
prefixType = prefixElement.returnType;
|
||||
}
|
||||
|
||||
function.prefix.staticType = prefixType;
|
||||
if (prefixType != null && prefixType.isDynamic) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
|
||||
|
@ -301,19 +300,15 @@ class FunctionReferenceResolver {
|
|||
/// There are three possible valid cases: tearing off the `call` method of a
|
||||
/// function element, tearing off an extension element declared on [Function],
|
||||
/// and tearing off an extension element declared on a function type.
|
||||
ExecutableElement? _resolveFunctionElementFunction(
|
||||
Element? _resolveFunctionTypeFunction(
|
||||
Expression receiver,
|
||||
SimpleIdentifier methodName,
|
||||
FunctionElement receiverElement,
|
||||
FunctionType receiverType,
|
||||
) {
|
||||
if (methodName.name == FunctionElement.CALL_METHOD_NAME) {
|
||||
return receiverElement;
|
||||
}
|
||||
|
||||
var methodElement = _resolver.typePropertyResolver
|
||||
.resolve(
|
||||
receiver: receiver,
|
||||
receiverType: receiverElement.type,
|
||||
receiverType: receiverType,
|
||||
name: methodName.name,
|
||||
propertyErrorEntity: methodName,
|
||||
nameErrorEntity: methodName,
|
||||
|
@ -342,6 +337,7 @@ class FunctionReferenceResolver {
|
|||
}
|
||||
|
||||
function.prefix.staticElement = prefixElement;
|
||||
function.prefix.staticType = prefixElement.referenceType;
|
||||
var functionName = function.identifier.name;
|
||||
|
||||
if (prefixElement is PrefixElement) {
|
||||
|
@ -376,28 +372,22 @@ class FunctionReferenceResolver {
|
|||
return;
|
||||
}
|
||||
|
||||
var methodElement = _resolveTypeProperty(
|
||||
var functionType = _resolveTypeProperty(
|
||||
receiver: function.prefix,
|
||||
receiverElement: prefixElement,
|
||||
name: function.identifier,
|
||||
nameErrorEntity: function,
|
||||
);
|
||||
|
||||
if (methodElement is MethodElement) {
|
||||
function.identifier.staticElement = methodElement;
|
||||
function.staticType = methodElement.type;
|
||||
_resolve(
|
||||
node: node,
|
||||
rawType: methodElement.type,
|
||||
name: functionName,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (methodElement is PropertyAccessorElement) {
|
||||
function.accept(_resolver);
|
||||
_resolveDisallowedExpression(node, methodElement.returnType);
|
||||
return;
|
||||
if (functionType != null) {
|
||||
if (functionType is FunctionType) {
|
||||
function.staticType = functionType;
|
||||
_resolve(
|
||||
node: node,
|
||||
rawType: functionType,
|
||||
name: functionName,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function.accept(_resolver);
|
||||
|
@ -425,39 +415,12 @@ class FunctionReferenceResolver {
|
|||
node.staticType = DynamicTypeImpl.instance;
|
||||
return;
|
||||
}
|
||||
} else if (target is PrefixedIdentifierImpl) {
|
||||
var prefixElement = target.prefix.scopeLookupResult!.getter;
|
||||
if (prefixElement is PrefixElement) {
|
||||
var prefixName = target.identifier.name;
|
||||
var targetElement = prefixElement.scope.lookup(prefixName).getter;
|
||||
|
||||
var methodElement = _resolveTypeProperty(
|
||||
receiver: target,
|
||||
receiverElement: targetElement,
|
||||
name: function.propertyName,
|
||||
nameErrorEntity: function,
|
||||
);
|
||||
|
||||
if (methodElement == null) {
|
||||
// The target is known, but the method is not; [UNDEFINED_GETTER] is
|
||||
// reported elsewhere.
|
||||
node.staticType = DynamicTypeImpl.instance;
|
||||
return;
|
||||
} else {
|
||||
_resolveReceiverPrefix(node, prefixElement, target, methodElement);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// The prefix is unknown; [UNDEFINED_IDENTIFIER] is reported elsewhere.
|
||||
node.staticType = DynamicTypeImpl.instance;
|
||||
return;
|
||||
}
|
||||
} else if (target is ExtensionOverrideImpl) {
|
||||
_resolveExtensionOverride(node, function, target);
|
||||
return;
|
||||
} else {
|
||||
targetType = target.typeOrThrow;
|
||||
if (targetType.isDynamic) {
|
||||
var targetType = target.staticType;
|
||||
if (targetType != null && targetType.isDynamic) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
|
||||
node,
|
||||
|
@ -466,6 +429,29 @@ class FunctionReferenceResolver {
|
|||
node.staticType = DynamicTypeImpl.instance;
|
||||
return;
|
||||
}
|
||||
var functionType = _resolveTypeProperty(
|
||||
receiver: target,
|
||||
name: function.propertyName,
|
||||
nameErrorEntity: function,
|
||||
);
|
||||
|
||||
if (functionType == null) {
|
||||
// The target is known, but the method is not; [UNDEFINED_GETTER] is
|
||||
// reported elsewhere.
|
||||
node.staticType = DynamicTypeImpl.instance;
|
||||
return;
|
||||
} else {
|
||||
if (functionType is FunctionType) {
|
||||
function.staticType = functionType;
|
||||
_resolve(
|
||||
node: node,
|
||||
rawType: functionType,
|
||||
name: function.propertyName.name,
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var propertyElement = _resolver.typePropertyResolver
|
||||
|
@ -719,47 +705,52 @@ class FunctionReferenceResolver {
|
|||
NodeReplacer.replace(node, typeLiteral);
|
||||
}
|
||||
|
||||
/// Resolves [name] as a property on [receiver] (with element
|
||||
/// [receiverElement]).
|
||||
/// Resolves [name] as a property on [receiver].
|
||||
///
|
||||
/// Returns `null` if [receiverElement] is `null`, a [TypeParameterElement],
|
||||
/// or a [TypeAliasElement] for a non-interface type.
|
||||
ExecutableElement? _resolveTypeProperty({
|
||||
/// Returns `null` if [receiver]'s type is `null`, a [TypeParameterType],
|
||||
/// or a type alias for a non-interface type.
|
||||
DartType? _resolveTypeProperty({
|
||||
required Expression receiver,
|
||||
required Element? receiverElement,
|
||||
required SimpleIdentifier name,
|
||||
required SimpleIdentifierImpl name,
|
||||
required SyntacticEntity nameErrorEntity,
|
||||
}) {
|
||||
if (receiverElement == null) {
|
||||
return null;
|
||||
}
|
||||
if (receiverElement is TypeParameterElement) {
|
||||
return null;
|
||||
}
|
||||
if (receiverElement is ClassElement) {
|
||||
return _resolveStaticElement(receiverElement, name);
|
||||
} else if (receiverElement is FunctionElement) {
|
||||
return _resolveFunctionElementFunction(receiver, name, receiverElement);
|
||||
} else if (receiverElement is TypeAliasElement) {
|
||||
var aliasedType = receiverElement.aliasedType;
|
||||
if (aliasedType is InterfaceType) {
|
||||
return _resolveStaticElement(aliasedType.element, name);
|
||||
} else {
|
||||
return null;
|
||||
if (receiver is Identifier) {
|
||||
var receiverElement = receiver.staticElement;
|
||||
if (receiverElement is ClassElement) {
|
||||
var element = _resolveStaticElement(receiverElement, name);
|
||||
name.staticElement = element;
|
||||
// TODO(srawlins): Should this use referenceType? E.g. if `element`
|
||||
// is a function-typed static getter.
|
||||
return element?.type;
|
||||
} else if (receiverElement is TypeAliasElement) {
|
||||
var aliasedType = receiverElement.aliasedType;
|
||||
if (aliasedType is InterfaceType) {
|
||||
var element = _resolveStaticElement(aliasedType.element, name);
|
||||
name.staticElement = element;
|
||||
// TODO(srawlins): Should this use referenceType? E.g. if `element`
|
||||
// is a function-typed static getter.
|
||||
return element?.type;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DartType receiverType;
|
||||
if (receiverElement is VariableElement) {
|
||||
receiverType = receiverElement.type;
|
||||
} else if (receiverElement is PropertyAccessorElement) {
|
||||
receiverType = receiverElement.returnType;
|
||||
} else {
|
||||
assert(false,
|
||||
'Unexpected receiverElement type: ${receiverElement.runtimeType}');
|
||||
var receiverType = receiver.staticType;
|
||||
if (receiverType == null) {
|
||||
return null;
|
||||
} else if (receiverType is TypeParameterType) {
|
||||
return null;
|
||||
} else if (receiverType is FunctionType) {
|
||||
if (name.name == FunctionElement.CALL_METHOD_NAME) {
|
||||
return receiverType;
|
||||
}
|
||||
var element = _resolveFunctionTypeFunction(receiver, name, receiverType);
|
||||
name.staticElement = element;
|
||||
return element?.referenceType;
|
||||
}
|
||||
var methodElement = _resolver.typePropertyResolver
|
||||
|
||||
var element = _resolver.typePropertyResolver
|
||||
.resolve(
|
||||
receiver: receiver,
|
||||
receiverType: receiverType,
|
||||
|
@ -768,10 +759,35 @@ class FunctionReferenceResolver {
|
|||
nameErrorEntity: nameErrorEntity,
|
||||
)
|
||||
.getter;
|
||||
if (methodElement != null && methodElement.isStatic) {
|
||||
_reportInvalidAccessToStaticMember(name, methodElement,
|
||||
name.staticElement = element;
|
||||
if (element != null && element.isStatic) {
|
||||
_reportInvalidAccessToStaticMember(name, element,
|
||||
implicitReceiver: false);
|
||||
}
|
||||
return methodElement;
|
||||
return element?.referenceType;
|
||||
}
|
||||
}
|
||||
|
||||
extension on Element {
|
||||
/// Returns the 'type' of `this`, when accessed as a "reference", not
|
||||
/// immediately followed by parentheses and arguments.
|
||||
///
|
||||
/// For all elements that don't have a type (for example, [ExportElement]),
|
||||
/// `null` is returned. For [PropertyAccessorElement], the return value is
|
||||
/// returned. For all other elements, their `type` property is returned.
|
||||
DartType? get referenceType {
|
||||
if (this is ConstructorElement) {
|
||||
return (this as ConstructorElement).type;
|
||||
} else if (this is FunctionElement) {
|
||||
return (this as FunctionElement).type;
|
||||
} else if (this is PropertyAccessorElement) {
|
||||
return (this as PropertyAccessorElement).returnType;
|
||||
} else if (this is MethodElement) {
|
||||
return (this as MethodElement).type;
|
||||
} else if (this is VariableElement) {
|
||||
return (this as VariableElement).type;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,6 +129,10 @@ bar() {
|
|||
a.b.foo<int>;
|
||||
}
|
||||
''', [
|
||||
// TODO(srawlins): Get the information to [FunctionReferenceResolve] that
|
||||
// [PropertyElementResolver] encountered an error, to avoid double reporting.
|
||||
error(CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
|
||||
10, 12),
|
||||
error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 10, 1),
|
||||
]);
|
||||
|
||||
|
@ -431,7 +435,7 @@ extension on Function {
|
|||
}
|
||||
|
||||
test_instanceGetter_explicitReceiver() async {
|
||||
await assertErrorsInCode('''
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
late void Function<T>(T) foo;
|
||||
}
|
||||
|
@ -439,10 +443,7 @@ class A {
|
|||
bar(A a) {
|
||||
a.foo<int>;
|
||||
}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION, 58, 5),
|
||||
]);
|
||||
''');
|
||||
|
||||
assertFunctionReference(findNode.functionReference('foo<int>;'),
|
||||
findElement.getter('foo'), 'void Function(int)');
|
||||
|
@ -480,6 +481,42 @@ class A {
|
|||
reference, findElement.method('foo'), 'void Function(int)');
|
||||
}
|
||||
|
||||
test_instanceMethod_call() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class C {
|
||||
void foo<T>(T a) {}
|
||||
|
||||
void bar() {
|
||||
foo.call<int>;
|
||||
}
|
||||
}
|
||||
''');
|
||||
|
||||
var reference = findNode.functionReference('foo.call<int>;');
|
||||
// TODO(srawlins): PropertyElementResolver does not return an element for
|
||||
// `.call`. If we want `findElement.method('foo')` here, we must change the
|
||||
// policy over there.
|
||||
assertFunctionReference(reference, null, 'void Function(int)');
|
||||
}
|
||||
|
||||
test_instanceMethod_explicitReceiver_call() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class C {
|
||||
void foo<T>(T a) {}
|
||||
}
|
||||
|
||||
void bar(C c) {
|
||||
c.foo.call<int>;
|
||||
}
|
||||
''');
|
||||
|
||||
var reference = findNode.functionReference('foo.call<int>;');
|
||||
// TODO(srawlins): PropertyElementResolver does not return an element for
|
||||
// `.call`. If we want `findElement.method('foo')` here, we must change the
|
||||
// policy over there.
|
||||
assertFunctionReference(reference, null, 'void Function(int)');
|
||||
}
|
||||
|
||||
test_instanceMethod_explicitReceiver_field() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
|
@ -515,6 +552,22 @@ void f(A? a, A b) {
|
|||
findElement.method('foo'), 'void Function(int)');
|
||||
}
|
||||
|
||||
test_instanceMethod_explicitReceiver_receiverIsNotIdentifier_call() async {
|
||||
await assertNoErrorsInCode('''
|
||||
extension on List<Object?> {
|
||||
void foo<T>(T a) {}
|
||||
}
|
||||
|
||||
var a = [].foo.call<int>;
|
||||
''');
|
||||
|
||||
var reference = findNode.functionReference('foo.call<int>;');
|
||||
// TODO(srawlins): PropertyElementResolver does not return an element for
|
||||
// `.call`. If we want `findElement.method('foo')` here, we must change the
|
||||
// policy over there.
|
||||
assertFunctionReference(reference, null, 'void Function(int)');
|
||||
}
|
||||
|
||||
test_instanceMethod_explicitReceiver_super() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
|
@ -785,6 +838,43 @@ void bar(void Function<T>(T a) foo) {
|
|||
reference, findElement.parameter('foo'), 'void Function(int)');
|
||||
}
|
||||
|
||||
test_localVariable_call() async {
|
||||
await assertNoErrorsInCode('''
|
||||
void foo<T>(T a) {}
|
||||
|
||||
void bar() {
|
||||
var fn = foo;
|
||||
fn.call<int>;
|
||||
}
|
||||
''');
|
||||
|
||||
var reference = findNode.functionReference('fn.call<int>;');
|
||||
// TODO(srawlins): PropertyElementResolver does not return an element for
|
||||
// `.call`. If we want `findElement.method('foo')` here, we must change the
|
||||
// policy over there.
|
||||
assertFunctionReference(reference, null, 'void Function(int)');
|
||||
}
|
||||
|
||||
test_localVariable_call_tooManyTypeArgs() async {
|
||||
await assertErrorsInCode('''
|
||||
void foo<T>(T a) {}
|
||||
|
||||
void bar() {
|
||||
void Function(int) fn = foo;
|
||||
fn.call<int>;
|
||||
}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION, 74, 5),
|
||||
]);
|
||||
|
||||
var reference = findNode.functionReference('fn.call<int>;');
|
||||
// TODO(srawlins): PropertyElementResolver does not return an element for
|
||||
// `.call`. If we want `findElement.method('fn')` here, we must change the
|
||||
// policy over there.
|
||||
assertFunctionReference(reference, null, 'void Function(int)');
|
||||
}
|
||||
|
||||
test_localVariable_typeVariable_boundToFunction() async {
|
||||
await assertErrorsInCode('''
|
||||
void bar<T extends Function>(T foo) {
|
||||
|
@ -1176,6 +1266,8 @@ bar() {
|
|||
prefix.a.foo<int>;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
|
||||
38, 17),
|
||||
error(CompileTimeErrorCode.UNDEFINED_PREFIXED_NAME, 45, 1),
|
||||
]);
|
||||
|
||||
|
|
Loading…
Reference in a new issue