blob: e85711e7ff074d7f01a197459d75f918d640e907 [file]
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
import 'package:_fe_analyzer_shared/src/types/shared_type.dart';
import 'package:analyzer/dart/ast/token.dart' show Token, TokenType;
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/scope.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart';
import 'package:analyzer/src/dart/resolver/invocation_inferrer.dart';
import 'package:analyzer/src/dart/resolver/resolution_result.dart';
import 'package:analyzer/src/dart/resolver/type_analyzer.dart';
import 'package:analyzer/src/dart/type_instantiation_target.dart';
import 'package:analyzer/src/diagnostic/diagnostic.dart' as diag;
import 'package:analyzer/src/error/listener.dart';
import 'package:analyzer/src/error/lookup_failure_reporter.dart';
import 'package:analyzer/src/generated/inference_log.dart';
import 'package:analyzer/src/generated/scope_helpers.dart';
import 'package:analyzer/src/generated/super_context.dart';
class MethodInvocationResolver with ScopeHelpers {
/// The type analyzer driving this participant.
final TypeAnalyzer _typeAnalyzer;
/// The manager for the inheritance mappings.
final InheritanceManager3 _inheritance;
/// The element for the library containing the compilation unit being visited.
final LibraryElementImpl _definingLibrary;
/// The URI of [_definingLibrary].
final Uri _definingLibraryUri;
/// Helper for extension method resolution.
final ExtensionMemberResolver _extensionResolver;
MethodInvocationResolver(this._typeAnalyzer)
: _inheritance = _typeAnalyzer.inheritance,
_definingLibrary = _typeAnalyzer.definingLibrary,
_definingLibraryUri = _typeAnalyzer.definingLibrary.uri,
_extensionResolver = _typeAnalyzer.extensionResolver;
@override
DiagnosticReporter get diagnosticReporter => _typeAnalyzer.diagnosticReporter;
LookupFailureReporter get _lookupFailureReporter =>
_typeAnalyzer.lookupFailureReporter;
TypeSystemImpl get _typeSystem => _typeAnalyzer.typeSystem;
void resolveCascade(
ParsedValueArgumentsImpl node,
CascadeExpressionImpl cascade,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
required TypeImpl contextType,
}) {
var receiver = _typeAnalyzer.cascadeReceiver(cascade);
if (receiver is ExpressionImpl &&
cascade.isNullAware &&
_typeSystem.isNull(_typeSystem.resolveToBound(receiver.typeOrThrow))) {
_resolveReceiverWithoutTarget(
_createNamedInvocation(node, receiver),
whyNotPromotedArguments,
contextType: contextType,
type: NeverTypeImpl.instance,
hasInvocation: false,
);
return;
}
_resolveInstanceInvocation(
node,
receiver,
whyNotPromotedArguments,
isNullAware: cascade.isNullAware,
contextType: contextType,
);
}
/// Resolves a call through an import namespace without constructing an
/// expression receiver for the prefix.
void resolveImportPrefixed(
ImportPrefixedFunctionInvocationImpl node,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
required TypeImpl contextType,
}) {
var prefix = node.importPrefix.element as PrefixElementImpl;
var name = node.name;
Element? candidate;
if (name.lexeme == TopLevelFunctionElement.LOAD_LIBRARY_NAME) {
var imports = prefix.imports;
if (imports.length == 1 && imports.single.prefix?.isDeferred == true) {
candidate = imports.single.importedLibrary?.loadLibraryFunction;
}
}
ScopeLookupResult? lookup;
if (candidate == null) {
lookup = prefix.scope.lookup(name.lexeme);
reportDeprecatedExportUseGetter(
scopeLookupResult: lookup,
nameToken: name,
);
candidate = lookup.getter;
}
var element = candidate;
if (element is MultiplyDefinedElement) {
element = element.conflictingElements.first;
}
if (element is InternalPropertyAccessorElement) {
var type = element.returnType;
var receiver =
ImportPrefixedNameExpressionImpl(
importPrefix: node.importPrefix,
name: name,
)
..resolution = candidate is MultiplyDefinedElement
? InvalidNamedReadResolutionImpl(recoveryElement: candidate)
: element is InternalGetterElement
? GetterInvocationResolutionImpl(element: element, type: type)
: InvalidNamedReadResolutionImpl(recoveryElement: element);
inferenceLogWriter?.enterFunctionExpressionInvocationTarget(receiver);
receiver.recordStaticType(type, typeAnalyzer: _typeAnalyzer);
if (type.isBottom) {
_typeAnalyzer.flowAnalysis.flow?.handleExit(offset: name.end);
}
inferenceLogWriter?.exitExpression(receiver);
var invocation = CallInvocationImpl(
receiver: receiver,
typeArguments: node.typeArguments,
argumentList: node.argumentList,
);
_typeAnalyzer.replaceExpression(node, invocation);
_typeAnalyzer.flowAnalysis.transferTestData(node, invocation);
_typeAnalyzer.callInvocationResolver.resolve(
invocation,
whyNotPromotedArguments,
contextType: contextType,
);
return;
}
if (element == null) {
if (name.isSynthetic) {
// The parser has already reported the missing name. But the prefix
// is still used, so its imports must not be reported as unused.
prefix.scope.notifyPrefixUsedWithoutName();
}
_lookupFailureReporter.reportReadFailure(
domain: PrefixedLookupDomain(prefix),
name: name,
foundInstead:
lookup?.setter ??
prefix.scope.getPrivateDeclarationOfImportedLibrary(name.lexeme),
);
} else if (element is! InternalExecutableElement) {
// Such as a type alias of a function type; other types are invoked as
// instance creations.
diagnosticReporter.report(
diag.invocationOfNonFunction.withArguments(name: name.lexeme).at(name),
);
}
_resolveNamedInvocation(
node,
whyNotPromotedArguments,
contextType: contextType,
candidate: candidate,
element: element is InternalExecutableElement ? element : null,
);
}
/// Resolves the final named operation after its receiver has been analyzed.
/// Qualifiers select a static namespace without evaluating a type literal.
void resolveReceiver(
ParsedValueArgumentsImpl node,
NamedReceiverImpl receiver,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
required TypeImpl contextType,
}) {
switch (receiver) {
case StaticQualifierImpl():
_resolveQualifiedInvocation(
node,
receiver,
whyNotPromotedArguments,
contextType: contextType,
);
case SuperReferenceImpl():
_resolveSuperInvocation(
node,
receiver,
whyNotPromotedArguments,
contextType: contextType,
);
case ExpressionImpl() && InstanceReceiverImpl receiver:
case ExtensionOverride2Impl() && InstanceReceiverImpl receiver:
var selector = node.namedInvocationParts!.selector;
_resolveInstanceInvocation(
node,
receiver,
whyNotPromotedArguments,
isNullAware: selector.operator.type == TokenType.QUESTION_PERIOD,
contextType: contextType,
);
}
}
/// Resolves a named call whose lexical scope has already been recorded.
/// Callable values become reads followed by [CallInvocation]; executable
/// members remain direct invocations, without an intervening tear-off.
void resolveUnqualified(
UnqualifiedFunctionInvocationImpl node,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
required TypeImpl contextType,
}) {
var name = node.name;
var lookup = node.scopeLookupResult!;
reportDeprecatedExportUseGetter(scopeLookupResult: lookup, nameToken: name);
var candidate = lookup.getter;
Element? element = candidate;
FunctionTypeImpl? callFunctionType;
TypeImpl? recordFieldType;
var isFunctionInterfaceCall = false;
if (element is MultiplyDefinedElement) {
element = element.conflictingElements.first;
}
if (element == null) {
var receiverType = _typeAnalyzer.thisType;
if (receiverType == null) {
_lookupFailureReporter.reportReadFailure(
domain: UnqualifiedLookupDomain(),
name: name,
foundInstead: lookup.setter,
);
} else {
var setter = lookup.setter;
// An instance setter can hide an inherited getter. A top-level,
// extension, or static setter instead ends lexical lookup.
var stopsLookup =
setter != null &&
(setter.enclosingElement is LibraryElement ||
setter.enclosingElement is ExtensionElement ||
setter is ExecutableElement && setter.isStatic);
var needsError = true;
Element? setterInstead;
if (stopsLookup) {
candidate = setterInstead = setter;
} else {
var result = _typeAnalyzer.typePropertyResolver.resolve(
receiver: null,
receiverType: receiverType,
name: name.lexeme,
hasRead: true,
hasWrite: false,
propertyErrorEntity: name,
nameErrorEntity: name,
parentNode: node,
);
candidate = element = result.getter2;
setterInstead = result.setter2;
callFunctionType = result.callFunctionType;
recordFieldType = result.recordField?.type;
isFunctionInterfaceCall =
receiverType.isDartCoreFunction &&
name.lexeme == MethodElement.CALL_METHOD_NAME;
needsError = result.getterOutcome == LookupOutcome.notFound;
if (element is InternalExecutableElement && element.isStatic) {
_reportInstanceAccessToStaticMember(name, element, true);
}
}
if (element == null &&
callFunctionType == null &&
recordFieldType == null &&
!isFunctionInterfaceCall &&
needsError &&
!(receiverType is InterfaceTypeImpl &&
receiverType.element.name == null)) {
_lookupFailureReporter.reportReadFailure(
domain: UnqualifiedLookupDomain(),
name: name,
foundInstead: setterInstead,
);
}
}
} else if (element is PrefixElement) {
diagnosticReporter.report(
diag.prefixIdentifierNotFollowedByDot
.withArguments(name: name.lexeme)
.at(name),
);
} else if (element is! InternalExecutableElement &&
element is! InternalVariableElement) {
diagnosticReporter.report(
diag.invocationOfNonFunction.withArguments(name: name.lexeme).at(name),
);
}
if (element is InternalVariableElement ||
element is InternalPropertyAccessorElement ||
recordFieldType != null) {
var receiver = UnqualifiedNameExpressionImpl(name: name)
..scopeLookupResult = lookup;
TypeImpl type;
var flow = _typeAnalyzer.flowAnalysis.flow;
if (recordFieldType != null) {
type = recordFieldType;
receiver.resolution = RecordFieldReadResolutionImpl(type: type);
} else if (element is InternalVariableElement) {
_typeAnalyzer.checkReadOfNotAssignedLocalVariable2(
receiver,
name: name.lexeme,
element: element,
);
type = element.type;
if (element is PromotableElementImpl && flow != null) {
var (:promotedType, :expressionInfo) = flow.variableRead(
element,
offset: name.offset,
);
type = promotedType?.unwrapTypeView<TypeImpl>() ?? type;
_typeAnalyzer.flowAnalysis.storeExpressionInfo(
receiver,
expressionInfo,
);
}
receiver.resolution = VariableReadResolutionImpl(
element: element,
type: type,
);
} else {
element as InternalPropertyAccessorElement;
type = element.returnType;
if (!element.isStatic && flow != null) {
var (:promotedType, :expressionInfo) = flow.propertyGet(
ThisPropertyTarget.singleton,
name.lexeme,
element,
SharedTypeView(type),
);
type = promotedType?.unwrapTypeView<TypeImpl>() ?? type;
_typeAnalyzer.flowAnalysis.storeExpressionInfo(
receiver,
expressionInfo,
);
}
receiver.resolution = element is InternalGetterElement
? GetterInvocationResolutionImpl(element: element, type: type)
: InvalidNamedReadResolutionImpl(recoveryElement: element);
}
if (candidate is MultiplyDefinedElement) {
receiver.resolution = InvalidNamedReadResolutionImpl(
recoveryElement: candidate,
);
}
inferenceLogWriter?.enterFunctionExpressionInvocationTarget(receiver);
receiver.recordStaticType(type, typeAnalyzer: _typeAnalyzer);
if (type.isBottom) {
flow?.handleExit(offset: name.end);
}
inferenceLogWriter?.exitExpression(receiver);
var invocation = CallInvocationImpl(
receiver: receiver,
typeArguments: node.typeArguments,
argumentList: node.argumentList,
);
_typeAnalyzer.replaceExpression(node, invocation);
_typeAnalyzer.flowAnalysis.transferTestData(node, invocation);
_typeAnalyzer.callInvocationResolver.resolve(
invocation,
whyNotPromotedArguments,
contextType: contextType,
);
return;
}
_resolveNamedInvocation(
node,
whyNotPromotedArguments,
contextType: contextType,
candidate: candidate,
element: element,
callFunctionType: callFunctionType,
isFunctionInterfaceCall: isFunctionInterfaceCall,
);
}
NamedFunctionInvocationImpl _createNamedInvocation(
ParsedValueArgumentsImpl node,
NamedReceiverImpl receiver,
) {
NamedFunctionInvocationImpl invocation;
if (node.cascadeInvocationParts case var parts?) {
invocation = CascadeMethodInvocationImpl(
name: parts.head.name,
typeArguments: parts.typeArguments,
argumentList: node.argumentList,
);
} else {
var (:selector, :typeArguments) = node.namedInvocationParts!;
invocation = ReceiverMethodInvocationImpl(
receiver: receiver,
operator: selector.operator,
name: selector.name,
typeArguments: typeArguments,
argumentList: node.argumentList,
);
}
_typeAnalyzer.replaceExpression(node, invocation);
_typeAnalyzer.flowAnalysis.transferTestData(node, invocation);
return invocation;
}
bool _hasMatchingObjectMethod(
MethodElement target,
NodeListImpl<ArgumentImpl> arguments,
) {
return arguments.length == target.formalParameters.length &&
!arguments.any((e) => e is NamedArgument);
}
void _reportInstanceAccessToStaticMember(
Token nameNode,
ExecutableElement element,
bool nullReceiver,
) {
var enclosingElement = element.enclosingElement!;
if (nullReceiver) {
if (_typeAnalyzer.enclosingInstanceElement is ExtensionElementImpl) {
_typeAnalyzer.diagnosticReporter.report(
diag.unqualifiedReferenceToStaticMemberOfExtendedType
.withArguments(name: enclosingElement.displayName)
.at(nameNode),
);
} else {
_typeAnalyzer.diagnosticReporter.report(
diag.unqualifiedReferenceToNonLocalStaticMember
.withArguments(name: enclosingElement.displayName)
.at(nameNode),
);
}
} else if (enclosingElement is ExtensionElement &&
enclosingElement.name == null) {
_typeAnalyzer.diagnosticReporter.report(
diag.instanceAccessToStaticMemberOfUnnamedExtension
.withArguments(
name: nameNode.lexeme,
kind: element.kind.displayName,
)
.at(nameNode),
);
} else {
// It is safe to assume that `enclosingElement.name` is non-`null` because
// it can only be `null` for extensions, and we handle that case above.
_typeAnalyzer.diagnosticReporter.report(
diag.instanceAccessToStaticMember
.withArguments(
memberName: nameNode.lexeme,
memberKind: element.kind.displayName,
enclosingElementName: enclosingElement.name!,
enclosingElementKind: enclosingElement is MixinElement
? 'mixin'
: enclosingElement.kind.displayName,
)
.at(nameNode),
);
}
}
void _reportUseOfVoidType(AstNode errorNode) {
_typeAnalyzer.diagnosticReporter.report(diag.useOfVoidResult.at(errorNode));
}
void _resolveCallableProperty(
ParsedValueArgumentsImpl node,
NamedReceiverImpl receiver,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
required TypeImpl contextType,
required InternalExecutableElement? element,
required TypeImpl type,
}) {
if (node.cascadeInvocationParts case var parts?) {
var read = CascadePropertyExtractionImpl(name: parts.head.name);
var result = _typeAnalyzer.resolveCascadeProperty(
read,
parts.head.name,
hasRead: true,
hasWrite: false,
);
read.resolution = result?.read;
inferenceLogWriter?.enterFunctionExpressionInvocationTarget(read);
read.recordStaticType(
result?.read?.type ?? NeverTypeImpl.instance,
typeAnalyzer: _typeAnalyzer,
);
if (read.typeOrThrow.isBottom) {
_typeAnalyzer.flowAnalysis.flow?.handleExit(
offset: parts.head.name.end,
);
}
inferenceLogWriter?.exitExpression(read);
if (result?.readExpressionInfo case var expressionInfo?) {
_typeAnalyzer.flowAnalysis.storeExpressionInfo(read, expressionInfo);
}
var invocation = CallInvocationImpl(
receiver: read,
typeArguments: parts.typeArguments,
argumentList: node.argumentList,
);
_typeAnalyzer.replaceExpression(node, invocation);
_typeAnalyzer.flowAnalysis.transferTestData(node, invocation);
_typeAnalyzer.callInvocationResolver.resolve(
invocation,
whyNotPromotedArguments,
contextType: contextType,
);
return;
}
var (:selector, :typeArguments) = node.namedInvocationParts!;
var read = ReceiverPropertyExtractionImpl(
receiver: receiver,
operator: selector.operator,
name: selector.name,
);
if ((receiver, _typeAnalyzer.flowAnalysis.flow) case (
InstanceReceiverImpl receiver,
var flow?,
)) {
var (:promotedType, :expressionInfo) = flow.propertyGet(
receiver is SuperReferenceImpl
? SuperPropertyTarget.singleton
: ExpressionPropertyTarget(
_typeAnalyzer.flowAnalysis.getExpressionInfo(
receiver is ExpressionImpl ? receiver : null,
),
),
selector.name.lexeme,
element,
SharedTypeView(type),
);
type = promotedType?.unwrapTypeView<TypeImpl>() ?? type;
_typeAnalyzer.flowAnalysis.storeExpressionInfo(read, expressionInfo);
}
read.resolution = switch (element) {
InternalGetterElement() => GetterInvocationResolutionImpl(
element: element,
type: type,
),
null => RecordFieldReadResolutionImpl(type: type),
_ => InvalidNamedReadResolutionImpl(recoveryElement: element),
};
inferenceLogWriter?.enterFunctionExpressionInvocationTarget(read);
read.recordStaticType(type, typeAnalyzer: _typeAnalyzer);
if (type.isBottom) {
_typeAnalyzer.flowAnalysis.flow?.handleExit(offset: selector.name.end);
}
inferenceLogWriter?.exitExpression(read);
var invocation = CallInvocationImpl(
receiver: read,
typeArguments: typeArguments,
argumentList: node.argumentList,
);
_typeAnalyzer.replaceExpression(node, invocation);
_typeAnalyzer.flowAnalysis.transferTestData(node, invocation);
_typeAnalyzer.callInvocationResolver.resolve(
invocation,
whyNotPromotedArguments,
contextType: contextType,
);
}
/// Looks up the member before inferring arguments, so callable properties
/// use call inference and methods use their executable signatures.
void _resolveInstanceInvocation(
ParsedValueArgumentsImpl node,
InstanceReceiverImpl receiver,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
required bool isNullAware,
required TypeImpl contextType,
}) {
var name =
node.cascadeInvocationParts?.head.name ??
node.namedInvocationParts!.selector.name;
if (receiver is ExtensionOverride2Impl) {
var member = _extensionResolver
.getOverrideMember(receiver, name.lexeme)
.getter2;
if (member == null) {
_lookupFailureReporter.reportReadFailure(
domain: ExtensionOverrideLookupDomain(receiver.element),
name: name,
foundInstead: receiver.element.getMemberForFailedLookup(
Name(_definingLibraryUri, name.lexeme),
),
);
} else if (member.isStatic) {
diagnosticReporter.report(
diag.extensionOverrideAccessToStaticMember.at(name),
);
}
if (member != null && node.cascadeInvocationParts != null) {
diagnosticReporter.report(
diag.extensionOverrideWithCascade.at(receiver.name),
);
}
if (member is InternalPropertyAccessorElement) {
_resolveCallableProperty(
node,
receiver,
whyNotPromotedArguments,
contextType: contextType,
element: member,
type: member.returnType,
);
} else {
_resolveNamedInvocation(
_createNamedInvocation(node, receiver),
whyNotPromotedArguments,
contextType: contextType,
candidate: member,
element: member,
);
}
return;
}
var receiverType = _typeAnalyzer.instanceReceiverType(receiver);
if (_typeSystem.isDynamicBounded(receiverType)) {
var method = _typeAnalyzer.typeProvider.objectElement.getMethod(
name.lexeme,
);
if (receiverType is! InvalidType &&
method != null &&
!method.isStatic &&
_hasMatchingObjectMethod(method, node.argumentList.arguments2)) {
_resolveNamedInvocation(
_createNamedInvocation(node, receiver),
whyNotPromotedArguments,
contextType: contextType,
candidate: method,
element: method,
);
} else {
_resolveReceiverWithoutTarget(
_createNamedInvocation(node, receiver),
whyNotPromotedArguments,
contextType: contextType,
type: receiverType is InvalidType
? InvalidTypeImpl.instance
: DynamicTypeImpl.instance,
);
}
return;
}
if (receiverType is NeverTypeImpl) {
var method = _typeAnalyzer.typeProvider.objectElement.getMethod(
name.lexeme,
);
if (receiverType.nullabilitySuffix == NullabilitySuffix.question &&
method != null) {
_resolveNamedInvocation(
_createNamedInvocation(node, receiver),
whyNotPromotedArguments,
contextType: contextType,
candidate: method,
element: method,
);
return;
}
if (receiverType.nullabilitySuffix == NullabilitySuffix.none ||
isNullAware) {
if (receiverType.nullabilitySuffix == NullabilitySuffix.none) {
diagnosticReporter.report(diag.receiverOfTypeNever.at(receiver));
}
_resolveReceiverWithoutTarget(
_createNamedInvocation(node, receiver),
whyNotPromotedArguments,
contextType: contextType,
type: receiverType,
hasInvocation: false,
);
return;
}
}
if (receiverType is VoidType) {
_reportUseOfVoidType(receiver);
_resolveReceiverWithoutTarget(
_createNamedInvocation(node, receiver),
whyNotPromotedArguments,
contextType: contextType,
type: InvalidTypeImpl.instance,
);
return;
}
if (isNullAware) {
receiverType = _typeSystem.promoteToNonNull(receiverType);
}
var result = _typeAnalyzer.typePropertyResolver.resolve(
receiver: receiver,
receiverType: receiverType,
name: name.lexeme,
hasRead: true,
hasWrite: false,
propertyErrorEntity: name,
nameErrorEntity: name,
parentNode: node,
);
var element = result.getter2;
if (element != null && element.isStatic) {
_reportInstanceAccessToStaticMember(name, element, false);
}
if (element is InternalPropertyAccessorElement ||
result.recordField != null) {
_resolveCallableProperty(
node,
receiver,
whyNotPromotedArguments,
contextType: contextType,
element: element,
type:
result.recordField?.type ??
(element as InternalPropertyAccessorElement).returnType,
);
return;
}
var isFunctionInterfaceCall =
receiverType.isDartCoreFunction &&
name.lexeme == MethodElement.CALL_METHOD_NAME;
if (element == null &&
result.callFunctionType == null &&
!isFunctionInterfaceCall &&
result.getterOutcome == LookupOutcome.notFound &&
!(receiverType is InterfaceTypeImpl &&
receiverType.element.name == null)) {
_lookupFailureReporter.reportReadFailure(
domain: InstanceLookupDomain(receiverType),
name: name,
foundInstead: _typeAnalyzer.typePropertyResolver.getMemberForFailedRead(
result: result,
receiverType: receiverType,
name: name.lexeme,
),
);
}
var invocation = _createNamedInvocation(node, receiver);
_resolveNamedInvocation(
invocation,
whyNotPromotedArguments,
contextType: contextType,
candidate: element,
element: element,
callFunctionType: result.callFunctionType,
isFunctionInterfaceCall: isFunctionInterfaceCall,
);
if (isFunctionInterfaceCall) {
invocation.resolution = FunctionInterfaceInvocationResolutionImpl(
type: invocation.typeOrThrow,
);
}
}
void _resolveNamedInvocation(
NamedFunctionInvocationImpl node,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
required TypeImpl contextType,
required Element? candidate,
required Element? element,
FunctionTypeImpl? callFunctionType,
bool isFunctionInterfaceCall = false,
}) {
InvocationTarget? target;
if (element is InternalExecutableElement) {
target = InvocationTargetExecutableElement(element);
node.resolution = ExecutableInvocationResolutionImpl(
element: element,
invokeType: element.type,
type: element.returnType,
);
if (candidate is MultiplyDefinedElement) {
node.resolution = InvalidInvocationResolutionImpl(
candidates: [candidate],
recovery: FunctionCallInvocationResolutionImpl(
invokeType: element.type,
type: element.returnType,
),
type: element.returnType,
);
}
} else if (callFunctionType != null) {
target = InvocationTargetFunctionTypedExpression(callFunctionType);
}
var type =
NamedFunctionInvocationInferrer(
typeAnalyzer: _typeAnalyzer,
node: node,
argumentList: node.argumentList,
whyNotPromotedArguments: whyNotPromotedArguments,
contextType: contextType,
target: target,
).resolveInvocation()
as TypeImpl;
var invokeType = node.staticInvokeType;
ValidInvocationResolutionImpl? resolution;
if (invokeType is FunctionTypeImpl) {
resolution = candidate is InternalExecutableElement
? ExecutableInvocationResolutionImpl(
element: candidate,
invokeType: invokeType,
type: type,
)
: FunctionCallInvocationResolutionImpl(
invokeType: invokeType,
type: type,
);
}
if (target == null && !isFunctionInterfaceCall) {
type = InvalidTypeImpl.instance;
node.staticInvokeType = type;
}
node.resolution =
candidate is MultiplyDefinedElement || type is InvalidTypeImpl
? InvalidInvocationResolutionImpl(
candidates: [?candidate],
recovery: resolution,
type: type,
)
: resolution ?? DynamicInvocationResolutionImpl(type: type);
node.recordStaticType(type, typeAnalyzer: _typeAnalyzer);
}
void _resolveQualifiedInvocation(
ParsedValueArgumentsImpl node,
StaticQualifierImpl receiver,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
required TypeImpl contextType,
}) {
if (receiver.scopeLookupResult case var lookup?) {
reportDeprecatedExportUseGetter(
scopeLookupResult: lookup,
nameToken: receiver.name,
);
}
var name = node.namedInvocationParts!.selector.name;
var namespace = receiver.element;
var interface = switch (namespace) {
InterfaceElementImpl element => element,
TypeAliasElementImpl(aliasedType: InterfaceTypeImpl(:var element)) =>
element,
_ => null,
};
InternalExecutableElement? element;
if (interface != null) {
element =
(interface.getGetter(name.lexeme) ?? interface.getMethod(name.lexeme))
as InternalExecutableElement?;
} else if (namespace is ExtensionElementImpl) {
element =
namespace.getGetter(name.lexeme) ?? namespace.getMethod(name.lexeme);
}
if (element != null && !element.isAccessibleIn(_definingLibrary)) {
element = null;
}
if (element != null && !element.isStatic) {
// Preserve argument checking against the recovery declaration without
// exposing its enclosing type parameters as a valid invocation result.
diagnosticReporter.report(
diag.staticAccessToInstanceMember
.withArguments(name: name.lexeme)
.at(name),
);
var invocation = _createNamedInvocation(node, receiver);
_resolveNamedInvocation(
invocation,
whyNotPromotedArguments,
contextType: contextType,
candidate: element,
element: element,
);
var recovery = invocation.resolution;
invocation.resolution = InvalidInvocationResolutionImpl(
candidates: [element],
recovery: recovery is ValidInvocationResolutionImpl ? recovery : null,
type: InvalidTypeImpl.instance,
);
invocation.staticInvokeType = InvalidTypeImpl.instance;
invocation.setPseudoExpressionStaticType(InvalidTypeImpl.instance);
return;
}
if (element is InternalPropertyAccessorElement) {
_resolveCallableProperty(
node,
receiver,
whyNotPromotedArguments,
contextType: contextType,
element: element,
type: element.returnType,
);
return;
}
if (element == null) {
// Function-type aliases are converted to expression receivers before
// qualified invocation resolution.
var declaration = switch (namespace) {
ExtensionElementImpl() => namespace,
_ => interface,
};
if (declaration == null) {
throw StateError('Unexpected static invocation qualifier: $namespace');
}
_lookupFailureReporter.reportReadFailure(
domain: StaticLookupDomain(declaration),
name: name,
foundInstead: declaration.getMemberForFailedLookup(
Name(_definingLibraryUri, name.lexeme),
),
);
}
_resolveNamedInvocation(
_createNamedInvocation(node, receiver),
whyNotPromotedArguments,
contextType: contextType,
candidate: element,
element: element,
);
}
void _resolveReceiverWithoutTarget(
NamedFunctionInvocationImpl node,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
required TypeImpl contextType,
required TypeImpl type,
bool hasInvocation = true,
}) {
NamedFunctionInvocationInferrer(
typeAnalyzer: _typeAnalyzer,
node: node,
argumentList: node.argumentList,
whyNotPromotedArguments: whyNotPromotedArguments,
contextType: contextType,
target: null,
).resolveInvocation();
node.staticInvokeType = type is InvalidType
? InvalidTypeImpl.instance
: DynamicTypeImpl.instance;
node.resolution = !hasInvocation
? null
: type is InvalidType
? InvalidInvocationResolutionImpl(
candidates: [],
recovery: null,
type: type,
)
: DynamicInvocationResolutionImpl(type: type);
node.recordStaticType(type, typeAnalyzer: _typeAnalyzer);
}
void _resolveSuperInvocation(
ParsedValueArgumentsImpl node,
SuperReferenceImpl receiver,
List<WhyNotPromotedGetter> whyNotPromotedArguments, {
required TypeImpl contextType,
}) {
var enclosingInterface = _typeAnalyzer.enclosingInstanceElement;
if (enclosingInterface is! InterfaceElementImpl ||
SuperContext.of(receiver) != SuperContext.valid) {
_resolveReceiverWithoutTarget(
_createNamedInvocation(node, receiver),
whyNotPromotedArguments,
contextType: contextType,
type: InvalidTypeImpl.instance,
);
return;
}
var name = node.namedInvocationParts!.selector.name;
var memberName = Name(_definingLibraryUri, name.lexeme);
var member = _inheritance.getMember(
enclosingInterface,
memberName,
forSuper: true,
);
if (member is InternalPropertyAccessorElement) {
_resolveCallableProperty(
node,
receiver,
whyNotPromotedArguments,
contextType: contextType,
element: member,
type: member.returnType,
);
return;
}
if (member == null) {
switch (_inheritance.recoverFailedSuperLookup(
enclosingInterface,
memberName,
)) {
case AbstractMemberSuperLookupRecovery(member: var abstractMember):
// Keep the abstract member for argument checking.
member = abstractMember;
diagnosticReporter.report(
diag.abstractSuperMemberReference
.withArguments(
memberKind: abstractMember.kind.displayName,
name: name.lexeme,
)
.at(name),
);
case MissingMemberSuperLookupRecovery(:var foundInstead):
_lookupFailureReporter.reportReadFailure(
domain: SuperLookupDomain(enclosingInterface.thisType),
name: name,
foundInstead: foundInstead,
);
}
}
_resolveNamedInvocation(
_createNamedInvocation(node, receiver),
whyNotPromotedArguments,
contextType: contextType,
candidate: member,
element: member,
);
}
}