mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 10:48:25 +00:00
analyzer: Support type instantiation of 'call' tearoff of function
Fixes https://github.com/dart-lang/sdk/issues/47212 Change-Id: Id4d5fc47e2e3ae1ef9949a698ba22c697f9e4b14 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/213539 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
parent
d30a43d464
commit
797bfe5bb0
|
@ -77,6 +77,31 @@ class FunctionReferenceResolver {
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks for a type instantiation of a `dynamic`-typed expression.
|
||||
///
|
||||
/// Returns `true` if an error was reported, and resolution can stop.
|
||||
bool _checkDynamicTypeInstantiation(FunctionReferenceImpl node,
|
||||
PrefixedIdentifierImpl function, Element prefixElement) {
|
||||
DartType? prefixType;
|
||||
if (prefixElement is VariableElement) {
|
||||
prefixType = prefixElement.type;
|
||||
} else if (prefixElement is PropertyAccessorElement) {
|
||||
prefixType = prefixElement.returnType;
|
||||
}
|
||||
|
||||
function.prefix.staticType = prefixType;
|
||||
if (prefixType != null && prefixType.isDynamic) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
|
||||
function,
|
||||
[],
|
||||
);
|
||||
node.staticType = DynamicTypeImpl.instance;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
List<DartType> _checkTypeArguments(
|
||||
TypeArgumentList typeArgumentList,
|
||||
String? name,
|
||||
|
@ -271,6 +296,36 @@ class FunctionReferenceResolver {
|
|||
_resolve(node: node, rawType: member.type, name: propertyName.name);
|
||||
}
|
||||
|
||||
/// Resolve a possible function tearoff of a [FunctionElement] receiver.
|
||||
///
|
||||
/// 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(
|
||||
Expression receiver,
|
||||
SimpleIdentifier methodName,
|
||||
FunctionElement receiverElement,
|
||||
) {
|
||||
if (methodName.name == FunctionElement.CALL_METHOD_NAME) {
|
||||
return receiverElement;
|
||||
}
|
||||
|
||||
var methodElement = _resolver.typePropertyResolver
|
||||
.resolve(
|
||||
receiver: receiver,
|
||||
receiverType: receiverElement.type,
|
||||
name: methodName.name,
|
||||
propertyErrorEntity: methodName,
|
||||
nameErrorEntity: methodName,
|
||||
)
|
||||
.getter;
|
||||
if (methodElement != null && methodElement.isStatic) {
|
||||
_reportInvalidAccessToStaticMember(methodName, methodElement,
|
||||
implicitReceiver: false);
|
||||
}
|
||||
return methodElement;
|
||||
}
|
||||
|
||||
void _resolvePrefixedIdentifierFunction(
|
||||
FunctionReferenceImpl node, PrefixedIdentifierImpl function) {
|
||||
var prefixElement = function.prefix.scopeLookupResult!.getter;
|
||||
|
@ -287,14 +342,15 @@ class FunctionReferenceResolver {
|
|||
}
|
||||
|
||||
function.prefix.staticElement = prefixElement;
|
||||
var functionName = function.identifier.name;
|
||||
|
||||
if (prefixElement is PrefixElement) {
|
||||
var functionName = function.identifier.name;
|
||||
var functionElement = prefixElement.scope.lookup(functionName).getter;
|
||||
if (functionElement == null) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.UNDEFINED_PREFIXED_NAME,
|
||||
function.identifier,
|
||||
[function.identifier.name, function.prefix.name],
|
||||
[functionName, function.prefix.name],
|
||||
);
|
||||
function.staticType = DynamicTypeImpl.instance;
|
||||
node.staticType = DynamicTypeImpl.instance;
|
||||
|
@ -306,21 +362,17 @@ class FunctionReferenceResolver {
|
|||
}
|
||||
}
|
||||
|
||||
DartType? prefixType;
|
||||
if (prefixElement is VariableElement) {
|
||||
prefixType = prefixElement.type;
|
||||
} else if (prefixElement is PropertyAccessorElement) {
|
||||
prefixType = prefixElement.returnType;
|
||||
if (_checkDynamicTypeInstantiation(node, function, prefixElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
function.prefix.staticType = prefixType;
|
||||
if (prefixType != null && prefixType.isDynamic) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
|
||||
function,
|
||||
[],
|
||||
if (prefixElement is FunctionElement &&
|
||||
functionName == FunctionElement.CALL_METHOD_NAME) {
|
||||
_resolve(
|
||||
node: node,
|
||||
rawType: prefixElement.type,
|
||||
name: functionName,
|
||||
);
|
||||
node.staticType = DynamicTypeImpl.instance;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -337,7 +389,7 @@ class FunctionReferenceResolver {
|
|||
_resolve(
|
||||
node: node,
|
||||
rawType: methodElement.type,
|
||||
name: function.identifier.name,
|
||||
name: functionName,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -686,6 +738,8 @@ class FunctionReferenceResolver {
|
|||
}
|
||||
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) {
|
||||
|
|
|
@ -334,6 +334,102 @@ extension E on A {
|
|||
reference, findElement.method('foo'), 'void Function(int)');
|
||||
}
|
||||
|
||||
test_function_call() async {
|
||||
await assertNoErrorsInCode('''
|
||||
void foo<T>(T a) {}
|
||||
|
||||
void bar() {
|
||||
foo.call<int>;
|
||||
}
|
||||
''');
|
||||
|
||||
assertFunctionReference(findNode.functionReference('foo.call<int>;'), null,
|
||||
'void Function(int)');
|
||||
}
|
||||
|
||||
test_function_call_tooFewTypeArgs() async {
|
||||
await assertErrorsInCode('''
|
||||
void foo<T, U>(T a, U b) {}
|
||||
|
||||
void bar() {
|
||||
foo.call<int>;
|
||||
}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION, 52, 5),
|
||||
]);
|
||||
|
||||
assertFunctionReference(findNode.functionReference('foo.call<int>;'), null,
|
||||
'void Function(dynamic, dynamic)');
|
||||
}
|
||||
|
||||
test_function_call_tooManyTypeArgs() async {
|
||||
await assertErrorsInCode('''
|
||||
void foo(String a) {}
|
||||
|
||||
void bar() {
|
||||
foo.call<int>;
|
||||
}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION, 46, 5),
|
||||
]);
|
||||
|
||||
assertFunctionReference(findNode.functionReference('foo.call<int>;'), null,
|
||||
'void Function(String)');
|
||||
}
|
||||
|
||||
test_function_call_typeArgNotMatchingBound() async {
|
||||
await assertNoErrorsInCode('''
|
||||
void foo<T extends num>(T a) {}
|
||||
|
||||
void bar() {
|
||||
foo.call<String>;
|
||||
}
|
||||
''');
|
||||
|
||||
assertFunctionReference(findNode.functionReference('foo.call<String>;'),
|
||||
null, 'void Function(String)');
|
||||
}
|
||||
|
||||
test_function_extensionOnFunction() async {
|
||||
// TODO(srawlins): Test extension on function type, like
|
||||
// `extension on void Function<T>(T)`.
|
||||
await assertNoErrorsInCode('''
|
||||
void foo<T>(T a) {}
|
||||
|
||||
void bar() {
|
||||
foo.m<int>;
|
||||
}
|
||||
|
||||
extension on Function {
|
||||
void m<T>(T t) {}
|
||||
}
|
||||
''');
|
||||
|
||||
assertFunctionReference(findNode.functionReference('foo.m<int>;'),
|
||||
findElement.method('m'), 'void Function(int)');
|
||||
}
|
||||
|
||||
test_function_extensionOnFunction_static() async {
|
||||
await assertErrorsInCode('''
|
||||
void foo<T>(T a) {}
|
||||
|
||||
void bar() {
|
||||
foo.m<int>;
|
||||
}
|
||||
|
||||
extension on Function {
|
||||
static void m<T>(T t) {}
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 40, 1),
|
||||
]);
|
||||
|
||||
assertFunctionReference(findNode.functionReference('foo.m<int>;'),
|
||||
findElement.method('m'), 'void Function(int)');
|
||||
}
|
||||
|
||||
test_instanceGetter_explicitReceiver() async {
|
||||
await assertErrorsInCode('''
|
||||
class A {
|
||||
|
@ -1040,6 +1136,21 @@ bar() {
|
|||
findNode.functionReference('foo<int>;'), null, 'dynamic');
|
||||
}
|
||||
|
||||
test_topLevelFunction_targetOfCall() async {
|
||||
await assertNoErrorsInCode('''
|
||||
void foo<T>(T a) {}
|
||||
|
||||
void bar() {
|
||||
foo<int>.call;
|
||||
}
|
||||
''');
|
||||
|
||||
assertFunctionReference(findNode.functionReference('foo<int>.call;'),
|
||||
findElement.topFunction('foo'), 'void Function(int)');
|
||||
assertSimpleIdentifier(findNode.simple('call;'),
|
||||
element: null, type: 'void Function(int)');
|
||||
}
|
||||
|
||||
test_topLevelFunction_targetOfFunctionCall() async {
|
||||
await assertNoErrorsInCode('''
|
||||
void foo<T>(T arg) {}
|
||||
|
|
Loading…
Reference in a new issue