Skip to content

Commit 797bfe5

Browse files
srawlinscommit-bot@chromium.org
authored andcommitted
analyzer: Support type instantiation of 'call' tearoff of function
Fixes #47212 Change-Id: Id4d5fc47e2e3ae1ef9949a698ba22c697f9e4b14 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/213539 Reviewed-by: Konstantin Shcheglov <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent d30a43d commit 797bfe5

File tree

2 files changed

+180
-15
lines changed

2 files changed

+180
-15
lines changed

pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart

Lines changed: 69 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,31 @@ class FunctionReferenceResolver {
7777
}
7878
}
7979

80+
/// Checks for a type instantiation of a `dynamic`-typed expression.
81+
///
82+
/// Returns `true` if an error was reported, and resolution can stop.
83+
bool _checkDynamicTypeInstantiation(FunctionReferenceImpl node,
84+
PrefixedIdentifierImpl function, Element prefixElement) {
85+
DartType? prefixType;
86+
if (prefixElement is VariableElement) {
87+
prefixType = prefixElement.type;
88+
} else if (prefixElement is PropertyAccessorElement) {
89+
prefixType = prefixElement.returnType;
90+
}
91+
92+
function.prefix.staticType = prefixType;
93+
if (prefixType != null && prefixType.isDynamic) {
94+
_errorReporter.reportErrorForNode(
95+
CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
96+
function,
97+
[],
98+
);
99+
node.staticType = DynamicTypeImpl.instance;
100+
return true;
101+
}
102+
return false;
103+
}
104+
80105
List<DartType> _checkTypeArguments(
81106
TypeArgumentList typeArgumentList,
82107
String? name,
@@ -271,6 +296,36 @@ class FunctionReferenceResolver {
271296
_resolve(node: node, rawType: member.type, name: propertyName.name);
272297
}
273298

299+
/// Resolve a possible function tearoff of a [FunctionElement] receiver.
300+
///
301+
/// There are three possible valid cases: tearing off the `call` method of a
302+
/// function element, tearing off an extension element declared on [Function],
303+
/// and tearing off an extension element declared on a function type.
304+
ExecutableElement? _resolveFunctionElementFunction(
305+
Expression receiver,
306+
SimpleIdentifier methodName,
307+
FunctionElement receiverElement,
308+
) {
309+
if (methodName.name == FunctionElement.CALL_METHOD_NAME) {
310+
return receiverElement;
311+
}
312+
313+
var methodElement = _resolver.typePropertyResolver
314+
.resolve(
315+
receiver: receiver,
316+
receiverType: receiverElement.type,
317+
name: methodName.name,
318+
propertyErrorEntity: methodName,
319+
nameErrorEntity: methodName,
320+
)
321+
.getter;
322+
if (methodElement != null && methodElement.isStatic) {
323+
_reportInvalidAccessToStaticMember(methodName, methodElement,
324+
implicitReceiver: false);
325+
}
326+
return methodElement;
327+
}
328+
274329
void _resolvePrefixedIdentifierFunction(
275330
FunctionReferenceImpl node, PrefixedIdentifierImpl function) {
276331
var prefixElement = function.prefix.scopeLookupResult!.getter;
@@ -287,14 +342,15 @@ class FunctionReferenceResolver {
287342
}
288343

289344
function.prefix.staticElement = prefixElement;
345+
var functionName = function.identifier.name;
346+
290347
if (prefixElement is PrefixElement) {
291-
var functionName = function.identifier.name;
292348
var functionElement = prefixElement.scope.lookup(functionName).getter;
293349
if (functionElement == null) {
294350
_errorReporter.reportErrorForNode(
295351
CompileTimeErrorCode.UNDEFINED_PREFIXED_NAME,
296352
function.identifier,
297-
[function.identifier.name, function.prefix.name],
353+
[functionName, function.prefix.name],
298354
);
299355
function.staticType = DynamicTypeImpl.instance;
300356
node.staticType = DynamicTypeImpl.instance;
@@ -306,21 +362,17 @@ class FunctionReferenceResolver {
306362
}
307363
}
308364

309-
DartType? prefixType;
310-
if (prefixElement is VariableElement) {
311-
prefixType = prefixElement.type;
312-
} else if (prefixElement is PropertyAccessorElement) {
313-
prefixType = prefixElement.returnType;
365+
if (_checkDynamicTypeInstantiation(node, function, prefixElement)) {
366+
return;
314367
}
315368

316-
function.prefix.staticType = prefixType;
317-
if (prefixType != null && prefixType.isDynamic) {
318-
_errorReporter.reportErrorForNode(
319-
CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
320-
function,
321-
[],
369+
if (prefixElement is FunctionElement &&
370+
functionName == FunctionElement.CALL_METHOD_NAME) {
371+
_resolve(
372+
node: node,
373+
rawType: prefixElement.type,
374+
name: functionName,
322375
);
323-
node.staticType = DynamicTypeImpl.instance;
324376
return;
325377
}
326378

@@ -337,7 +389,7 @@ class FunctionReferenceResolver {
337389
_resolve(
338390
node: node,
339391
rawType: methodElement.type,
340-
name: function.identifier.name,
392+
name: functionName,
341393
);
342394
return;
343395
}
@@ -686,6 +738,8 @@ class FunctionReferenceResolver {
686738
}
687739
if (receiverElement is ClassElement) {
688740
return _resolveStaticElement(receiverElement, name);
741+
} else if (receiverElement is FunctionElement) {
742+
return _resolveFunctionElementFunction(receiver, name, receiverElement);
689743
} else if (receiverElement is TypeAliasElement) {
690744
var aliasedType = receiverElement.aliasedType;
691745
if (aliasedType is InterfaceType) {

pkg/analyzer/test/src/dart/resolution/function_reference_test.dart

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,102 @@ extension E on A {
334334
reference, findElement.method('foo'), 'void Function(int)');
335335
}
336336

337+
test_function_call() async {
338+
await assertNoErrorsInCode('''
339+
void foo<T>(T a) {}
340+
341+
void bar() {
342+
foo.call<int>;
343+
}
344+
''');
345+
346+
assertFunctionReference(findNode.functionReference('foo.call<int>;'), null,
347+
'void Function(int)');
348+
}
349+
350+
test_function_call_tooFewTypeArgs() async {
351+
await assertErrorsInCode('''
352+
void foo<T, U>(T a, U b) {}
353+
354+
void bar() {
355+
foo.call<int>;
356+
}
357+
''', [
358+
error(
359+
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION, 52, 5),
360+
]);
361+
362+
assertFunctionReference(findNode.functionReference('foo.call<int>;'), null,
363+
'void Function(dynamic, dynamic)');
364+
}
365+
366+
test_function_call_tooManyTypeArgs() async {
367+
await assertErrorsInCode('''
368+
void foo(String a) {}
369+
370+
void bar() {
371+
foo.call<int>;
372+
}
373+
''', [
374+
error(
375+
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION, 46, 5),
376+
]);
377+
378+
assertFunctionReference(findNode.functionReference('foo.call<int>;'), null,
379+
'void Function(String)');
380+
}
381+
382+
test_function_call_typeArgNotMatchingBound() async {
383+
await assertNoErrorsInCode('''
384+
void foo<T extends num>(T a) {}
385+
386+
void bar() {
387+
foo.call<String>;
388+
}
389+
''');
390+
391+
assertFunctionReference(findNode.functionReference('foo.call<String>;'),
392+
null, 'void Function(String)');
393+
}
394+
395+
test_function_extensionOnFunction() async {
396+
// TODO(srawlins): Test extension on function type, like
397+
// `extension on void Function<T>(T)`.
398+
await assertNoErrorsInCode('''
399+
void foo<T>(T a) {}
400+
401+
void bar() {
402+
foo.m<int>;
403+
}
404+
405+
extension on Function {
406+
void m<T>(T t) {}
407+
}
408+
''');
409+
410+
assertFunctionReference(findNode.functionReference('foo.m<int>;'),
411+
findElement.method('m'), 'void Function(int)');
412+
}
413+
414+
test_function_extensionOnFunction_static() async {
415+
await assertErrorsInCode('''
416+
void foo<T>(T a) {}
417+
418+
void bar() {
419+
foo.m<int>;
420+
}
421+
422+
extension on Function {
423+
static void m<T>(T t) {}
424+
}
425+
''', [
426+
error(CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, 40, 1),
427+
]);
428+
429+
assertFunctionReference(findNode.functionReference('foo.m<int>;'),
430+
findElement.method('m'), 'void Function(int)');
431+
}
432+
337433
test_instanceGetter_explicitReceiver() async {
338434
await assertErrorsInCode('''
339435
class A {
@@ -1040,6 +1136,21 @@ bar() {
10401136
findNode.functionReference('foo<int>;'), null, 'dynamic');
10411137
}
10421138

1139+
test_topLevelFunction_targetOfCall() async {
1140+
await assertNoErrorsInCode('''
1141+
void foo<T>(T a) {}
1142+
1143+
void bar() {
1144+
foo<int>.call;
1145+
}
1146+
''');
1147+
1148+
assertFunctionReference(findNode.functionReference('foo<int>.call;'),
1149+
findElement.topFunction('foo'), 'void Function(int)');
1150+
assertSimpleIdentifier(findNode.simple('call;'),
1151+
element: null, type: 'void Function(int)');
1152+
}
1153+
10431154
test_topLevelFunction_targetOfFunctionCall() async {
10441155
await assertNoErrorsInCode('''
10451156
void foo<T>(T arg) {}

0 commit comments

Comments
 (0)