Skip to content

Commit f12333d

Browse files
srawlinscommit-bot@chromium.org
authored andcommitted
Resolve more cases of .call; correct many cases of PropertyAccess receivers
Fixes #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 <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent 5c83d86 commit f12333d

File tree

2 files changed

+202
-94
lines changed

2 files changed

+202
-94
lines changed

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

Lines changed: 105 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ class FunctionReferenceResolver {
8989
prefixType = prefixElement.returnType;
9090
}
9191

92-
function.prefix.staticType = prefixType;
9392
if (prefixType != null && prefixType.isDynamic) {
9493
_errorReporter.reportErrorForNode(
9594
CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
@@ -301,19 +300,15 @@ class FunctionReferenceResolver {
301300
/// There are three possible valid cases: tearing off the `call` method of a
302301
/// function element, tearing off an extension element declared on [Function],
303302
/// and tearing off an extension element declared on a function type.
304-
ExecutableElement? _resolveFunctionElementFunction(
303+
Element? _resolveFunctionTypeFunction(
305304
Expression receiver,
306305
SimpleIdentifier methodName,
307-
FunctionElement receiverElement,
306+
FunctionType receiverType,
308307
) {
309-
if (methodName.name == FunctionElement.CALL_METHOD_NAME) {
310-
return receiverElement;
311-
}
312-
313308
var methodElement = _resolver.typePropertyResolver
314309
.resolve(
315310
receiver: receiver,
316-
receiverType: receiverElement.type,
311+
receiverType: receiverType,
317312
name: methodName.name,
318313
propertyErrorEntity: methodName,
319314
nameErrorEntity: methodName,
@@ -342,6 +337,7 @@ class FunctionReferenceResolver {
342337
}
343338

344339
function.prefix.staticElement = prefixElement;
340+
function.prefix.staticType = prefixElement.referenceType;
345341
var functionName = function.identifier.name;
346342

347343
if (prefixElement is PrefixElement) {
@@ -376,28 +372,22 @@ class FunctionReferenceResolver {
376372
return;
377373
}
378374

379-
var methodElement = _resolveTypeProperty(
375+
var functionType = _resolveTypeProperty(
380376
receiver: function.prefix,
381-
receiverElement: prefixElement,
382377
name: function.identifier,
383378
nameErrorEntity: function,
384379
);
385380

386-
if (methodElement is MethodElement) {
387-
function.identifier.staticElement = methodElement;
388-
function.staticType = methodElement.type;
389-
_resolve(
390-
node: node,
391-
rawType: methodElement.type,
392-
name: functionName,
393-
);
394-
return;
395-
}
396-
397-
if (methodElement is PropertyAccessorElement) {
398-
function.accept(_resolver);
399-
_resolveDisallowedExpression(node, methodElement.returnType);
400-
return;
381+
if (functionType != null) {
382+
if (functionType is FunctionType) {
383+
function.staticType = functionType;
384+
_resolve(
385+
node: node,
386+
rawType: functionType,
387+
name: functionName,
388+
);
389+
return;
390+
}
401391
}
402392

403393
function.accept(_resolver);
@@ -425,39 +415,12 @@ class FunctionReferenceResolver {
425415
node.staticType = DynamicTypeImpl.instance;
426416
return;
427417
}
428-
} else if (target is PrefixedIdentifierImpl) {
429-
var prefixElement = target.prefix.scopeLookupResult!.getter;
430-
if (prefixElement is PrefixElement) {
431-
var prefixName = target.identifier.name;
432-
var targetElement = prefixElement.scope.lookup(prefixName).getter;
433-
434-
var methodElement = _resolveTypeProperty(
435-
receiver: target,
436-
receiverElement: targetElement,
437-
name: function.propertyName,
438-
nameErrorEntity: function,
439-
);
440-
441-
if (methodElement == null) {
442-
// The target is known, but the method is not; [UNDEFINED_GETTER] is
443-
// reported elsewhere.
444-
node.staticType = DynamicTypeImpl.instance;
445-
return;
446-
} else {
447-
_resolveReceiverPrefix(node, prefixElement, target, methodElement);
448-
return;
449-
}
450-
} else {
451-
// The prefix is unknown; [UNDEFINED_IDENTIFIER] is reported elsewhere.
452-
node.staticType = DynamicTypeImpl.instance;
453-
return;
454-
}
455418
} else if (target is ExtensionOverrideImpl) {
456419
_resolveExtensionOverride(node, function, target);
457420
return;
458421
} else {
459-
targetType = target.typeOrThrow;
460-
if (targetType.isDynamic) {
422+
var targetType = target.staticType;
423+
if (targetType != null && targetType.isDynamic) {
461424
_errorReporter.reportErrorForNode(
462425
CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
463426
node,
@@ -466,6 +429,29 @@ class FunctionReferenceResolver {
466429
node.staticType = DynamicTypeImpl.instance;
467430
return;
468431
}
432+
var functionType = _resolveTypeProperty(
433+
receiver: target,
434+
name: function.propertyName,
435+
nameErrorEntity: function,
436+
);
437+
438+
if (functionType == null) {
439+
// The target is known, but the method is not; [UNDEFINED_GETTER] is
440+
// reported elsewhere.
441+
node.staticType = DynamicTypeImpl.instance;
442+
return;
443+
} else {
444+
if (functionType is FunctionType) {
445+
function.staticType = functionType;
446+
_resolve(
447+
node: node,
448+
rawType: functionType,
449+
name: function.propertyName.name,
450+
);
451+
}
452+
453+
return;
454+
}
469455
}
470456

471457
var propertyElement = _resolver.typePropertyResolver
@@ -719,47 +705,52 @@ class FunctionReferenceResolver {
719705
NodeReplacer.replace(node, typeLiteral);
720706
}
721707

722-
/// Resolves [name] as a property on [receiver] (with element
723-
/// [receiverElement]).
708+
/// Resolves [name] as a property on [receiver].
724709
///
725-
/// Returns `null` if [receiverElement] is `null`, a [TypeParameterElement],
726-
/// or a [TypeAliasElement] for a non-interface type.
727-
ExecutableElement? _resolveTypeProperty({
710+
/// Returns `null` if [receiver]'s type is `null`, a [TypeParameterType],
711+
/// or a type alias for a non-interface type.
712+
DartType? _resolveTypeProperty({
728713
required Expression receiver,
729-
required Element? receiverElement,
730-
required SimpleIdentifier name,
714+
required SimpleIdentifierImpl name,
731715
required SyntacticEntity nameErrorEntity,
732716
}) {
733-
if (receiverElement == null) {
734-
return null;
735-
}
736-
if (receiverElement is TypeParameterElement) {
737-
return null;
738-
}
739-
if (receiverElement is ClassElement) {
740-
return _resolveStaticElement(receiverElement, name);
741-
} else if (receiverElement is FunctionElement) {
742-
return _resolveFunctionElementFunction(receiver, name, receiverElement);
743-
} else if (receiverElement is TypeAliasElement) {
744-
var aliasedType = receiverElement.aliasedType;
745-
if (aliasedType is InterfaceType) {
746-
return _resolveStaticElement(aliasedType.element, name);
747-
} else {
748-
return null;
717+
if (receiver is Identifier) {
718+
var receiverElement = receiver.staticElement;
719+
if (receiverElement is ClassElement) {
720+
var element = _resolveStaticElement(receiverElement, name);
721+
name.staticElement = element;
722+
// TODO(srawlins): Should this use referenceType? E.g. if `element`
723+
// is a function-typed static getter.
724+
return element?.type;
725+
} else if (receiverElement is TypeAliasElement) {
726+
var aliasedType = receiverElement.aliasedType;
727+
if (aliasedType is InterfaceType) {
728+
var element = _resolveStaticElement(aliasedType.element, name);
729+
name.staticElement = element;
730+
// TODO(srawlins): Should this use referenceType? E.g. if `element`
731+
// is a function-typed static getter.
732+
return element?.type;
733+
} else {
734+
return null;
735+
}
749736
}
750737
}
751738

752-
DartType receiverType;
753-
if (receiverElement is VariableElement) {
754-
receiverType = receiverElement.type;
755-
} else if (receiverElement is PropertyAccessorElement) {
756-
receiverType = receiverElement.returnType;
757-
} else {
758-
assert(false,
759-
'Unexpected receiverElement type: ${receiverElement.runtimeType}');
739+
var receiverType = receiver.staticType;
740+
if (receiverType == null) {
760741
return null;
742+
} else if (receiverType is TypeParameterType) {
743+
return null;
744+
} else if (receiverType is FunctionType) {
745+
if (name.name == FunctionElement.CALL_METHOD_NAME) {
746+
return receiverType;
747+
}
748+
var element = _resolveFunctionTypeFunction(receiver, name, receiverType);
749+
name.staticElement = element;
750+
return element?.referenceType;
761751
}
762-
var methodElement = _resolver.typePropertyResolver
752+
753+
var element = _resolver.typePropertyResolver
763754
.resolve(
764755
receiver: receiver,
765756
receiverType: receiverType,
@@ -768,10 +759,35 @@ class FunctionReferenceResolver {
768759
nameErrorEntity: nameErrorEntity,
769760
)
770761
.getter;
771-
if (methodElement != null && methodElement.isStatic) {
772-
_reportInvalidAccessToStaticMember(name, methodElement,
762+
name.staticElement = element;
763+
if (element != null && element.isStatic) {
764+
_reportInvalidAccessToStaticMember(name, element,
773765
implicitReceiver: false);
774766
}
775-
return methodElement;
767+
return element?.referenceType;
768+
}
769+
}
770+
771+
extension on Element {
772+
/// Returns the 'type' of `this`, when accessed as a "reference", not
773+
/// immediately followed by parentheses and arguments.
774+
///
775+
/// For all elements that don't have a type (for example, [ExportElement]),
776+
/// `null` is returned. For [PropertyAccessorElement], the return value is
777+
/// returned. For all other elements, their `type` property is returned.
778+
DartType? get referenceType {
779+
if (this is ConstructorElement) {
780+
return (this as ConstructorElement).type;
781+
} else if (this is FunctionElement) {
782+
return (this as FunctionElement).type;
783+
} else if (this is PropertyAccessorElement) {
784+
return (this as PropertyAccessorElement).returnType;
785+
} else if (this is MethodElement) {
786+
return (this as MethodElement).type;
787+
} else if (this is VariableElement) {
788+
return (this as VariableElement).type;
789+
} else {
790+
return null;
791+
}
776792
}
777793
}

0 commit comments

Comments
 (0)