Skip to content

Commit e08ee7a

Browse files
srawlinscommit-bot@chromium.org
authored and
commit-bot@chromium.org
committed
analyzer: resolve function type alias type instantiation
This code is a little weird because of how this type instantiation works: ``` typedef Fn<T> = void Function(T); var x = Fn<int>.foo; var y = (Fn<int>).foo; extension on Type { int get foo => 1; } ``` `x` is illegal under any circumstance, because calling a getter on a type instantiation can _only_ resolve to a constructor, but function types do not have constructors. But it's nice to resolve what we can, and what the user may have meant, and we have to represent `Fn<int>.foo` _somehow_. So it's a property access on a TypeLiteral. Add two new codes because it is not correct to say that `foo` is not a getter on 'Type' because that is beside the point. The issue is that there is no possible getter on a type-instantiated type literal of a function type alias (nor method, nor setter). Add lots of tests, for calling a method, a getter, and a setter on a function type alias literal. Add tests with prefixes, bounds, too many and too few args. Bug: #46020 Change-Id: Icdf17506a64b3382226c5e50786784130d9e3bf9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/213287 Commit-Queue: Samuel Rawlins <srawlins@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
1 parent 74fa14a commit e08ee7a

13 files changed

+538
-13
lines changed

pkg/analyzer/lib/error/error.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,13 +440,16 @@ const List<ErrorCode> errorCodeValues = [
440440
CompileTimeErrorCode.UNDEFINED_EXTENSION_SETTER,
441441
CompileTimeErrorCode.UNDEFINED_FUNCTION,
442442
CompileTimeErrorCode.UNDEFINED_GETTER,
443+
CompileTimeErrorCode.UNDEFINED_GETTER_ON_FUNCTION_TYPE,
443444
CompileTimeErrorCode.UNDEFINED_IDENTIFIER,
444445
CompileTimeErrorCode.UNDEFINED_IDENTIFIER_AWAIT,
445446
CompileTimeErrorCode.UNDEFINED_METHOD,
447+
CompileTimeErrorCode.UNDEFINED_METHOD_ON_FUNCTION_TYPE,
446448
CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER,
447449
CompileTimeErrorCode.UNDEFINED_OPERATOR,
448450
CompileTimeErrorCode.UNDEFINED_PREFIXED_NAME,
449451
CompileTimeErrorCode.UNDEFINED_SETTER,
452+
CompileTimeErrorCode.UNDEFINED_SETTER_ON_FUNCTION_TYPE,
450453
CompileTimeErrorCode.UNDEFINED_SUPER_GETTER,
451454
CompileTimeErrorCode.UNDEFINED_SUPER_METHOD,
452455
CompileTimeErrorCode.UNDEFINED_SUPER_OPERATOR,

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

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:analyzer/dart/ast/ast.dart';
66
import 'package:analyzer/dart/element/element.dart';
77
import 'package:analyzer/dart/element/scope.dart';
88
import 'package:analyzer/dart/element/type.dart';
9+
import 'package:analyzer/dart/element/type_provider.dart';
910
import 'package:analyzer/error/listener.dart';
1011
import 'package:analyzer/src/dart/ast/ast.dart';
1112
import 'package:analyzer/src/dart/ast/ast_factory.dart';
@@ -26,7 +27,9 @@ import 'package:analyzer/src/error/codes.dart';
2627
class AstRewriter {
2728
final ErrorReporter _errorReporter;
2829

29-
AstRewriter(this._errorReporter);
30+
final TypeProvider _typeProvider;
31+
32+
AstRewriter(this._errorReporter, this._typeProvider);
3033

3134
/// Possibly rewrites [node] as a [MethodInvocation] with a
3235
/// [FunctionReference] target.
@@ -45,9 +48,15 @@ class AstRewriter {
4548
var typeName = node.constructorName.type.name;
4649
if (typeName is SimpleIdentifier) {
4750
var element = nameScope.lookup(typeName.name).getter;
48-
if (element is FunctionElement || element is MethodElement) {
51+
if (element is FunctionElement ||
52+
element is MethodElement ||
53+
element is PropertyAccessorElement) {
4954
return _toMethodInvocationOfFunctionReference(
5055
node: node, function: typeName);
56+
} else if (element is TypeAliasElement &&
57+
element.aliasedElement is GenericFunctionTypeElement) {
58+
return _toMethodInvocationOfAliasedTypeLiteral(
59+
node: node, function: typeName, element: element);
5160
}
5261
} else if (typeName is PrefixedIdentifier) {
5362
var prefixElement = nameScope.lookup(typeName.prefix.name).getter;
@@ -57,6 +66,10 @@ class AstRewriter {
5766
if (element is FunctionElement) {
5867
return _toMethodInvocationOfFunctionReference(
5968
node: node, function: typeName);
69+
} else if (element is TypeAliasElement &&
70+
element.aliasedElement is GenericFunctionTypeElement) {
71+
return _toMethodInvocationOfAliasedTypeLiteral(
72+
node: node, function: typeName, element: element);
6073
}
6174

6275
// If `element` is a [ClassElement], or a [TypeAliasElement] aliasing
@@ -490,6 +503,28 @@ class AstRewriter {
490503
return instanceCreationExpression;
491504
}
492505

506+
MethodInvocation _toMethodInvocationOfAliasedTypeLiteral({
507+
required InstanceCreationExpression node,
508+
required Identifier function,
509+
required TypeAliasElement element,
510+
}) {
511+
var typeName = astFactory.typeName(node.constructorName.type.name,
512+
node.constructorName.type.typeArguments);
513+
typeName.type = element.aliasedType;
514+
typeName.name.staticType = element.aliasedType;
515+
var typeLiteral = astFactory.typeLiteral(typeName: typeName);
516+
typeLiteral.staticType = _typeProvider.typeType;
517+
var methodInvocation = astFactory.methodInvocation(
518+
typeLiteral,
519+
node.constructorName.period,
520+
node.constructorName.name!,
521+
null,
522+
node.argumentList,
523+
);
524+
NodeReplacer.replace(node, methodInvocation);
525+
return methodInvocation;
526+
}
527+
493528
MethodInvocation _toMethodInvocationOfFunctionReference({
494529
required InstanceCreationExpression node,
495530
required Identifier function,

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,13 @@ class FunctionReferenceResolver {
564564
// `prefix.C<int>.name` is initially represented as a [PropertyAccess]
565565
// with a [FunctionReference] target.
566566
if (node.parent is PropertyAccess) {
567-
_resolveConstructorReference(node);
567+
if (element is TypeAliasElement &&
568+
element.aliasedType is FunctionType) {
569+
function.staticElement = element;
570+
_resolveTypeAlias(node: node, element: element, typeAlias: function);
571+
} else {
572+
_resolveConstructorReference(node);
573+
}
568574
return;
569575
} else if (element is ClassElement) {
570576
function.staticElement = element;
@@ -649,6 +655,10 @@ class FunctionReferenceResolver {
649655
required DartType instantiatedType,
650656
required Identifier name,
651657
}) {
658+
// TODO(srawlins): set the static element of [typeName].
659+
// This involves a fair amount of resolution, as [name] may be a prefixed
660+
// identifier, etc. [TypeName]s should be resolved in [ResolutionVisitor],
661+
// and this could be done for nodes like this via [AstRewriter].
652662
var typeName = astFactory.typeName(name, node.typeArguments);
653663
typeName.type = instantiatedType;
654664
typeName.name.staticType = instantiatedType;

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,21 @@ class MethodInvocationResolver {
164164
return;
165165
}
166166

167+
if (receiver is TypeLiteralImpl &&
168+
receiver.typeName.typeArguments != null &&
169+
receiver.typeName.type is FunctionType) {
170+
// There is no possible resolution for a property access of a function
171+
// type literal (which can only be a type instantiation of a type alias
172+
// of a function type).
173+
_resolver.errorReporter.reportErrorForNode(
174+
CompileTimeErrorCode.UNDEFINED_METHOD_ON_FUNCTION_TYPE,
175+
nameNode,
176+
[name, receiver.typeName.name.name],
177+
);
178+
_setDynamicResolution(node, whyNotPromotedList: whyNotPromotedList);
179+
return;
180+
}
181+
167182
_resolveReceiverType(
168183
node: node,
169184
receiver: receiver,

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,26 @@ class PropertyElementResolver {
368368
targetType = _typeSystem.promoteToNonNull(targetType);
369369
}
370370

371+
if (target is TypeLiteral && target.typeName.type is FunctionType) {
372+
// There is no possible resolution for a property access of a function
373+
// type literal (which can only be a type instantiation of a type alias
374+
// of a function type).
375+
if (hasRead) {
376+
_errorReporter.reportErrorForNode(
377+
CompileTimeErrorCode.UNDEFINED_GETTER_ON_FUNCTION_TYPE,
378+
propertyName,
379+
[propertyName.name, target.typeName.name.name],
380+
);
381+
} else {
382+
_errorReporter.reportErrorForNode(
383+
CompileTimeErrorCode.UNDEFINED_SETTER_ON_FUNCTION_TYPE,
384+
propertyName,
385+
[propertyName.name, target.typeName.name.name],
386+
);
387+
}
388+
return PropertyElementResolverResult();
389+
}
390+
371391
var result = _resolver.typePropertyResolver.resolve(
372392
receiver: target,
373393
receiverType: targetType,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ class ResolutionVisitor extends RecursiveAstVisitor<void> {
116116
unitElement,
117117
isNonNullableByDefault,
118118
errorReporter,
119-
AstRewriter(errorReporter),
119+
AstRewriter(errorReporter, typeProvider),
120120
typeNameResolver,
121121
nameScope,
122122
elementWalker,

pkg/analyzer/lib/src/error/codes.dart

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13836,6 +13836,19 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
1383613836
"defining a getter or field named '{0}'.",
1383713837
hasPublishedDocs: true);
1383813838

13839+
/**
13840+
* Parameters:
13841+
* 0: the name of the getter
13842+
* 1: the name of the function type alias
13843+
*/
13844+
static const CompileTimeErrorCode UNDEFINED_GETTER_ON_FUNCTION_TYPE =
13845+
CompileTimeErrorCode('UNDEFINED_GETTER',
13846+
"The getter '{0}' isn't defined for the '{1}' function type.",
13847+
correction: "Try wrapping the function type alias in parentheses in "
13848+
"order to access '{0}' as an extension getter on 'Type'.",
13849+
hasPublishedDocs: true,
13850+
uniqueName: 'UNDEFINED_GETTER_ON_FUNCTION_TYPE');
13851+
1383913852
/**
1384013853
* Parameters:
1384113854
* 0: the name of the identifier
@@ -13945,6 +13958,19 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
1394513958
"defining a method named '{0}'.",
1394613959
hasPublishedDocs: true);
1394713960

13961+
/**
13962+
* Parameters:
13963+
* 0: the name of the method
13964+
* 1: the name of the function type alias
13965+
*/
13966+
static const CompileTimeErrorCode UNDEFINED_METHOD_ON_FUNCTION_TYPE =
13967+
CompileTimeErrorCode('UNDEFINED_METHOD',
13968+
"The method '{0}' isn't defined for the '{1}' function type.",
13969+
correction: "Try wrapping the function type alias in parentheses in "
13970+
"order to access '{0}' as an extension method on 'Type'.",
13971+
hasPublishedDocs: true,
13972+
uniqueName: 'UNDEFINED_METHOD_ON_FUNCTION_TYPE');
13973+
1394813974
/**
1394913975
* Parameters:
1395013976
* 0: the name of the requested named parameter
@@ -14148,6 +14174,19 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
1414814174
"defining a setter or field named '{0}'.",
1414914175
hasPublishedDocs: true);
1415014176

14177+
/**
14178+
* Parameters:
14179+
* 0: the name of the setter
14180+
* 1: the name of the function type alias
14181+
*/
14182+
static const CompileTimeErrorCode UNDEFINED_SETTER_ON_FUNCTION_TYPE =
14183+
CompileTimeErrorCode('UNDEFINED_SETTER',
14184+
"The setter '{0}' isn't defined for the '{1}' function type.",
14185+
correction: "Try wrapping the function type alias in parentheses in "
14186+
"order to access '{0}' as an extension getter on 'Type'.",
14187+
hasPublishedDocs: true,
14188+
uniqueName: 'UNDEFINED_SETTER_ON_FUNCTION_TYPE');
14189+
1415114190
/**
1415214191
* Parameters:
1415314192
* 0: the name of the getter

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

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,52 @@ var x = A.foo<int>;
6060
findElement.constructor('foo'), 'dynamic');
6161
}
6262

63-
test_explicitReceiver_dynamicTyped() async {
63+
test_dynamicTyped() async {
6464
await assertErrorsInCode('''
65-
dynamic f(dynamic x) => x;
65+
dynamic i = 1;
6666
67-
class C {
68-
T instanceMethod<T>(T t) => t;
67+
void bar() {
68+
i<int>;
6969
}
70+
''', [
71+
error(
72+
CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION, 31, 1),
73+
]);
7074

71-
main() {
72-
C c = new C();
73-
f(c).instanceMethod<int>;
75+
assertFunctionReference(findNode.functionReference('i<int>;'),
76+
findElement.topGet('i'), 'dynamic');
77+
}
78+
79+
test_dynamicTyped_targetOfMethodCall() async {
80+
await assertErrorsInCode('''
81+
dynamic i = 1;
82+
83+
void bar() {
84+
i<int>.foo();
85+
}
86+
''', [
87+
error(
88+
CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION, 31, 1),
89+
]);
90+
91+
assertFunctionReference(findNode.functionReference('i<int>.foo();'),
92+
findElement.topGet('i'), 'dynamic');
93+
}
94+
95+
test_explicitReceiver_dynamicTyped() async {
96+
await assertErrorsInCode('''
97+
dynamic f() => 1;
98+
99+
foo() {
100+
f().instanceMethod<int>;
74101
}
75102
''', [
76103
error(CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
77-
102, 24),
104+
29, 23),
78105
]);
79106

80107
assertFunctionReference(
81-
findNode.functionReference('f(c).instanceMethod<int>;'),
108+
findNode.functionReference('f().instanceMethod<int>;'),
82109
null,
83110
'dynamic');
84111
}
@@ -702,6 +729,22 @@ void bar<T>(T foo) {
702729
assertFunctionReference(reference, findElement.parameter('foo'), 'dynamic');
703730
}
704731

732+
test_neverTyped() async {
733+
await assertErrorsInCode('''
734+
external Never get i;
735+
736+
void bar() {
737+
i<int>;
738+
}
739+
''', [
740+
error(
741+
CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION, 38, 1),
742+
]);
743+
744+
assertFunctionReference(findNode.functionReference('i<int>;'),
745+
findElement.topGet('i'), 'dynamic');
746+
}
747+
705748
test_nonGenericFunction() async {
706749
await assertErrorsInCode('''
707750
class A {

0 commit comments

Comments
 (0)