Improve method tearoff instantiation support for static methods.
* static method access with an explicit receiver
* the above, with a type alias of the receiver
* the two above, with a prefixed import
* many test cases for unsupported things
Bug: https://github.com/dart-lang/sdk/issues/46233
Change-Id: I982bc741aa8f57959d342b640921096170e94f0c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/205881
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
index 802fa11..84260a2 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/syntactic_entity.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
@@ -12,7 +13,6 @@
import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/element/type.dart';
-import 'package:analyzer/src/dart/resolver/resolution_result.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';
@@ -83,35 +83,46 @@
if (function is PrefixedIdentifierImpl) {
var prefixElement =
_resolver.nameScope.lookup(function.prefix.name).getter;
- if (prefixElement is PrefixElement) {
- _resolveReceiverPrefix(node, prefixElement, function);
- return;
- }
-
- ResolutionResult resolveTypeProperty(DartType prefixType) {
- return _resolver.typePropertyResolver.resolve(
- receiver: function.prefix,
- receiverType: prefixType,
- name: function.identifier.name,
- propertyErrorEntity: function.identifier,
- nameErrorEntity: function,
- );
- }
-
function.prefix.staticElement = prefixElement;
- ExecutableElement? methodElement;
- if (prefixElement is VariableElement) {
- var prefixType = prefixElement.type;
- function.prefix.staticType = prefixType;
- methodElement = resolveTypeProperty(prefixType).getter;
+ 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],
+ );
+ function.staticType = DynamicTypeImpl.instance;
+ node.staticType = DynamicTypeImpl.instance;
+ return;
+ } else {
+ functionElement = _resolver.toLegacyElement(functionElement);
+ _resolveReceiverPrefix(
+ node, prefixElement, function, functionElement);
+ return;
+ }
+ } else if (prefixElement is VariableElement) {
+ function.prefix.staticType = prefixElement.type;
} else if (prefixElement is PropertyAccessorElement) {
- var prefixType = prefixElement.returnType;
- function.prefix.staticType = prefixType;
- methodElement = resolveTypeProperty(prefixType).getter;
+ function.prefix.staticType = prefixElement.returnType;
}
+
+ var methodElement = _resolveTypeProperty(
+ receiver: function.prefix,
+ receiverElement: prefixElement,
+ name: function.identifier,
+ nameErrorEntity: function,
+ );
+
if (methodElement is MethodElement) {
- _resolveFunctionReferenceMethod(
- node: node, function: function, element: methodElement);
+ function.identifier.staticElement = methodElement;
+ function.staticType = methodElement.type;
+ _resolve(
+ node: node,
+ name: function.identifier.name,
+ rawType: methodElement.type,
+ );
return;
}
@@ -143,11 +154,39 @@
node.staticType = DynamicTypeImpl.instance;
return;
}
+ } else if (target is PrefixedIdentifierImpl) {
+ var prefixElement =
+ _resolver.nameScope.lookup(target.prefix.name).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) {
+ // TODO(srawlins): Can we get here?
+ node.staticType = DynamicTypeImpl.instance;
+ return;
+ } else {
+ _resolveReceiverPrefix(node, prefixElement, target, methodElement);
+ return;
+ }
+ } else {
+ // TODO(srawlins): Can we get here?
+ node.staticType = DynamicTypeImpl.instance;
+ return;
+ }
} else {
- // TODO(srawlins): Can we get here? PrefixedIdentifier?
+ // TODO(srawlins): Can we get here?
node.staticType = DynamicTypeImpl.instance;
return;
}
+
var propertyElement = _resolver.typePropertyResolver
.resolve(
receiver: function.realTarget,
@@ -238,55 +277,14 @@
_resolveTypeLiteral(node: node, instantiatedType: type, name: name);
}
- void _resolveFunctionReferenceMethod({
- required FunctionReferenceImpl node,
- required PrefixedIdentifier function,
- required MethodElement element,
- }) {
- function.accept(_resolver);
- var receiver = function.prefix;
- var receiverType = receiver.staticType;
- if (receiverType == null) {
- // TODO(srawlins): Handle this situation; see
- // `test_staticMethod_explicitReceiver` test case.
- node.staticType = DynamicTypeImpl.instance;
- return;
- }
- var nameNode = function.identifier;
- var name = nameNode.name;
- var result = _resolver.typePropertyResolver.resolve(
- receiver: receiver,
- receiverType: receiverType,
- name: name,
- propertyErrorEntity: nameNode,
- nameErrorEntity: nameNode,
- );
-
- var target = result.getter;
- if (target != null) {
- // TODO(srawlins): Set static type on `nameNode`?
-
- _resolve(node: node, name: name, rawType: element.type);
- return;
- }
-
- // TODO(srawlins): Report unresolved identifier.
- node.function.accept(_resolver);
- node.staticType = DynamicTypeImpl.instance;
- }
-
void _resolveReceiverPrefix(
FunctionReferenceImpl node,
PrefixElement prefixElement,
PrefixedIdentifier prefix,
+ Element element,
) {
// TODO(srawlins): Handle `loadLibrary`, as in `p.loadLibrary<int>;`.
- var nameNode = prefix.identifier;
- var name = nameNode.name;
- var element = prefixElement.scope.lookup(name).getter;
- element = _resolver.toLegacyElement(element);
-
if (element is MultiplyDefinedElement) {
MultiplyDefinedElement multiply = element;
element = multiply.conflictingElements[0];
@@ -310,7 +308,6 @@
return;
} else if (element is TypeAliasElement) {
prefix.accept(_resolver);
- (nameNode as SimpleIdentifierImpl).staticElement = element;
_resolveTypeAlias(node: node, element: element, typeAlias: prefix);
return;
}
@@ -326,10 +323,26 @@
// TODO(srawlins): Report undefined prefixed identifier.
- node.function.accept(_resolver);
node.staticType = DynamicTypeImpl.instance;
}
+ /// Returns the element that represents the property named [propertyName] on
+ /// [classElement].
+ ExecutableElement? _resolveStaticElement(
+ ClassElement classElement, SimpleIdentifier propertyName) {
+ String name = propertyName.name;
+ ExecutableElement? element;
+ if (propertyName.inSetterContext()) {
+ element = classElement.getSetter(name);
+ }
+ element ??= classElement.getGetter(name);
+ element ??= classElement.getMethod(name);
+ if (element != null && element.isAccessibleIn(_resolver.definingLibrary)) {
+ return element;
+ }
+ return null;
+ }
+
void _resolveTypeAlias({
required FunctionReferenceImpl node,
required TypeAliasElement element,
@@ -358,4 +371,53 @@
typeLiteral.staticType = _typeType;
NodeReplacer.replace(node, typeLiteral);
}
+
+ /// Resolves [name] as a property on [receiver] (with element
+ /// [receiverElement]).
+ ///
+ /// Returns `null` if [receiverElement] is `null`, a [TypeParameterElement],
+ /// or a [TypeAliasElement] for a non-interface type.
+ ExecutableElement? _resolveTypeProperty({
+ required Expression receiver,
+ required Element? receiverElement,
+ required SimpleIdentifier 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 TypeAliasElement) {
+ var aliasedType = receiverElement.aliasedType;
+ if (aliasedType is InterfaceType) {
+ return _resolveStaticElement(aliasedType.element, name);
+ } 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}');
+ return null;
+ }
+ return _resolver.typePropertyResolver
+ .resolve(
+ receiver: receiver,
+ receiverType: receiverType,
+ name: name.name,
+ propertyErrorEntity: name,
+ nameErrorEntity: nameErrorEntity,
+ )
+ .getter;
+ }
}
diff --git a/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart b/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
index 19e9a10..9922d73 100644
--- a/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
@@ -142,16 +142,69 @@
reference, findElement.method('foo'), 'void Function(int)');
}
+ test_instanceMethod_explicitReceiver_topLevelVariable() async {
+ await assertNoErrorsInCode('''
+class A {
+ void foo<T>(T a) {}
+}
+var a = A();
+
+void bar() {
+ a.foo<int>;
+}
+''');
+
+ assertIdentifierTopGetRef(findNode.simple('a.'), 'a');
+ var reference = findNode.functionReference('foo<int>;');
+ assertFunctionReference(
+ reference, findElement.method('foo'), 'void Function(int)');
+ }
+
+ test_instanceMethod_explicitReceiver_topLevelVariable_prefix() async {
+ newFile('$testPackageLibPath/a.dart', content: '''
+class A {
+ void foo<T>(T a) {}
+}
+var a = A();
+''');
+ await assertNoErrorsInCode('''
+import 'a.dart' as prefix;
+
+bar() {
+ prefix.a.foo<int>;
+}
+''');
+
+ assertImportPrefix(
+ findNode.simple('prefix.'), findElement.prefix('prefix'));
+ var reference = findNode.functionReference('foo<int>;');
+ assertFunctionReference(
+ reference,
+ findElement.importFind('package:test/a.dart').method('foo'),
+ 'void Function(int)');
+ }
+
+ test_instanceMethod_explicitReceiver_typeParameter() async {
+ await assertErrorsInCode('''
+bar<T>() {
+ T.foo<int>;
+}
+''', [
+ error(CompileTimeErrorCode.UNDEFINED_GETTER, 15, 3),
+ ]);
+
+ var reference = findNode.functionReference('foo<int>;');
+ assertType(reference, 'dynamic');
+ }
+
test_instanceMethod_explicitReceiver_variable() async {
await assertNoErrorsInCode('''
class A {
void foo<T>(T a) {}
}
-class B {
- bar(A a) {
- a.foo<int>;
- }
+bar(A a) {
+ a.foo<int>;
}
''');
@@ -221,24 +274,23 @@
reference, findElement.method('foo'), 'void Function(int)');
}
- @FailingTest(reason: 'Unresolved TODO in FunctionReferenceResolver')
test_staticMethod_explicitReceiver() async {
await assertNoErrorsInCode('''
class A {
static void foo<T>(T a) {}
+}
- bar() {
- A.foo<int>;
- }
+bar() {
+ A.foo<int>;
}
''');
+ assertClassRef(findNode.simple('A.'), findElement.class_('A'));
var reference = findNode.functionReference('foo<int>;');
assertElement(reference, findElement.method('foo'));
assertType(reference, 'void Function(int)');
}
- @FailingTest(reason: 'Unresolved TODO in FunctionReferenceResolver')
test_staticMethod_explicitReceiver_importPrefix() async {
newFile('$testPackageLibPath/a.dart', content: '''
class A {
@@ -253,9 +305,79 @@
}
''');
+ assertImportPrefix(findNode.simple('a.A'), findElement.prefix('a'));
+ assertClassRef(findNode.simple('A.'),
+ findElement.importFind('package:test/a.dart').class_('A'));
var reference = findNode.functionReference('foo<int>;');
- assertElement(reference, findElement.method('foo'));
- assertType(reference, 'void Function(int)');
+ assertFunctionReference(
+ reference,
+ findElement.importFind('package:test/a.dart').method('foo'),
+ 'void Function(int)');
+ }
+
+ test_staticMethod_explicitReceiver_prefix_typeAlias() async {
+ newFile('$testPackageLibPath/a.dart', content: '''
+class A {
+ static void foo<T>(T a) {}
+}
+typedef TA = A;
+''');
+ await assertNoErrorsInCode('''
+import 'a.dart' as prefix;
+
+bar() {
+ prefix.TA.foo<int>;
+}
+''');
+
+ assertImportPrefix(
+ findNode.simple('prefix.'), findElement.prefix('prefix'));
+ var reference = findNode.functionReference('foo<int>;');
+ assertFunctionReference(
+ reference,
+ findElement.importFind('package:test/a.dart').method('foo'),
+ 'void Function(int)');
+ }
+
+ test_staticMethod_explicitReceiver_typeAlias() async {
+ await assertNoErrorsInCode('''
+class A {
+ static void foo<T>(T a) {}
+}
+typedef TA = A;
+
+bar() {
+ TA.foo<int>;
+}
+''');
+
+ assertTypeAliasRef(findNode.simple('TA.'), findElement.typeAlias('TA'));
+ var reference = findNode.functionReference('foo<int>;');
+ assertFunctionReference(
+ reference, findElement.method('foo'), 'void Function(int)');
+ }
+
+ test_staticMethod_explicitReciver_prefix() async {
+ newFile('$testPackageLibPath/a.dart', content: '''
+class A {
+ static void foo<T>(T a) {}
+}
+''');
+ await assertNoErrorsInCode('''
+import 'a.dart' as prefix;
+
+bar() {
+ prefix.A.foo<int>;
+}
+''');
+
+ assertImportPrefix(
+ findNode.simple('prefix.'), findElement.prefix('prefix'));
+ var reference = findNode.functionReference('foo<int>;');
+ assertFunctionReference(
+ reference,
+ findElement.importFind('package:test/a.dart').method('foo'),
+ 'void Function(int)');
}
test_tooFewTypeArguments() async {
@@ -322,6 +444,7 @@
}
''');
+ assertImportPrefix(findNode.simple('a.f'), findElement.prefix('a'));
var reference = findNode.functionReference('foo<int>;');
assertFunctionReference(
reference,
@@ -330,6 +453,32 @@
);
}
+ test_typeAlias_function_unknownProperty() async {
+ await assertErrorsInCode('''
+typedef Cb = void Function();
+
+var a = Cb.foo<int>;
+''', [
+ error(CompileTimeErrorCode.UNDEFINED_GETTER, 42, 3),
+ ]);
+
+ var reference = findNode.functionReference('foo<int>;');
+ assertType(reference, 'dynamic');
+ }
+
+ test_typeAlias_typeVariable_unknownProperty() async {
+ await assertErrorsInCode('''
+typedef T<E> = E;
+
+var a = T.foo<int>;
+''', [
+ error(CompileTimeErrorCode.UNDEFINED_GETTER, 29, 3),
+ ]);
+
+ var reference = findNode.functionReference('foo<int>;');
+ assertType(reference, 'dynamic');
+ }
+
test_unknownIdentifier() async {
await assertErrorsInCode('''
void bar() {