blob: 0724712a0f9fe7d0dd85538efa4559db7753e5b2 [file] [log] [blame]
// Copyright (c) 2021, 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: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';
import 'package:analyzer/error/listener.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/type.dart';
import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';
/// A resolver for [FunctionReference] nodes.
///
/// This resolver is responsible for writing a given [FunctionReference] as a
/// [ConstructorReference] or as a [TypeLiteral], depending on how a function
/// reference's `function` resolves.
class FunctionReferenceResolver {
/// The resolver driving this participant.
final ResolverVisitor _resolver;
/// Helper for extension method resolution.
final ExtensionMemberResolver _extensionResolver;
/// The type representing the type 'type'.
final InterfaceType _typeType;
FunctionReferenceResolver(this._resolver)
: _extensionResolver = _resolver.extensionResolver,
_typeType = _resolver.typeProvider.typeType;
DiagnosticReporter get _diagnosticReporter => _resolver.diagnosticReporter;
void resolve(FunctionReferenceImpl node) {
var function = node.function;
node.typeArguments?.accept(_resolver);
if (function is SimpleIdentifierImpl) {
_resolveSimpleIdentifierFunction(node, function);
} else if (function is PrefixedIdentifierImpl) {
_resolvePrefixedIdentifierFunction(node, function);
} else if (function is PropertyAccessImpl) {
_resolvePropertyAccessFunction(node, function);
} else if (function is ConstructorReferenceImpl) {
var typeArguments = node.typeArguments;
if (typeArguments != null) {
// Something like `List.filled<int>`.
_resolver.analyzeExpression(function, _resolver.operations.unknownType);
_resolver.popRewrite();
// We can safely assume `function.constructorName.name` is non-null
// because if no name had been given, the construct would have been
// interpreted as a type literal (e.g. `List<int>`).
_diagnosticReporter.atNode(
typeArguments,
CompileTimeErrorCode.wrongNumberOfTypeArgumentsConstructor,
arguments: [
function.constructorName.type.qualifiedName,
function.constructorName.name!.name,
],
);
_resolve(node: node, rawType: function.staticType);
}
} else {
// TODO(srawlins): Handle `function` being a [SuperExpression].
_resolver.analyzeExpression(function, _resolver.operations.unknownType);
function = _resolver.popRewrite()!;
var functionType = function.staticType;
if (functionType == null) {
_resolveDisallowedExpression(node, functionType);
} else if (functionType is FunctionType) {
_resolve(node: node, rawType: functionType);
} else {
var callMethod = _getCallMethod(node, function.staticType);
if (callMethod is MethodElement) {
_resolveAsImplicitCallReference(node, callMethod);
return;
} else {
_resolveDisallowedExpression(node, functionType);
}
}
}
}
/// Checks for a type instantiation of a `dynamic`-typed expression.
///
/// Returns `true` if an error was reported, and resolution can stop.
bool _checkDynamicTypeInstantiation(
FunctionReferenceImpl node,
PrefixedIdentifierImpl function,
Element prefixElement,
) {
DartType? prefixType;
if (prefixElement is VariableElement) {
prefixType = prefixElement.type;
} else if (prefixElement is PropertyAccessorElement) {
prefixType = prefixElement.variable.type;
}
if (prefixType is DynamicType) {
_diagnosticReporter.atNode(
function,
CompileTimeErrorCode.genericMethodTypeInstantiationOnDynamic,
);
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
return true;
}
return false;
}
List<TypeImpl> _checkTypeArguments(
TypeArgumentList typeArgumentList,
String? name,
List<TypeParameterElement> typeParameters,
CompileTimeErrorCode errorCode,
) {
if (typeArgumentList.arguments.length != typeParameters.length) {
if (name == null &&
errorCode ==
CompileTimeErrorCode.wrongNumberOfTypeArgumentsFunction) {
errorCode =
CompileTimeErrorCode.wrongNumberOfTypeArgumentsAnonymousFunction;
_diagnosticReporter.atNode(
typeArgumentList,
errorCode,
arguments: [typeParameters.length, typeArgumentList.arguments.length],
);
} else {
assert(name != null);
_diagnosticReporter.atNode(
typeArgumentList,
errorCode,
arguments: [
name!,
typeParameters.length,
typeArgumentList.arguments.length,
],
);
}
return List.filled(typeParameters.length, DynamicTypeImpl.instance);
} else {
return typeArgumentList.arguments
.map((typeAnnotation) => typeAnnotation.typeOrThrow)
.toList();
}
}
ExecutableElement? _getCallMethod(
FunctionReferenceImpl node,
DartType? type,
) {
if (type is! InterfaceTypeImpl) {
return null;
}
var callMethodName = Name(
_resolver.definingLibrary.source.uri,
MethodElement.CALL_METHOD_NAME,
);
if (type.nullabilitySuffix == NullabilitySuffix.question) {
// If the interface type is nullable, only an applicable extension method
// applies.
return _extensionResolver
.findExtension(type, node, callMethodName)
.getter2;
}
// Otherwise, a 'call' method on the interface, or on an applicable
// extension method applies.
return type.lookUpMethod(
MethodElement.CALL_METHOD_NAME,
type.element.library,
) ??
_extensionResolver.findExtension(type, node, callMethodName).getter2;
}
void _reportInvalidAccessToStaticMember(
SimpleIdentifier nameNode,
ExecutableElement element, {
required bool implicitReceiver,
}) {
var enclosingElement = element.enclosingElement!;
if (implicitReceiver) {
if (_resolver.enclosingExtension != null) {
_resolver.diagnosticReporter.atNode(
nameNode,
CompileTimeErrorCode.unqualifiedReferenceToStaticMemberOfExtendedType,
arguments: [enclosingElement.displayName],
);
} else {
_resolver.diagnosticReporter.atNode(
nameNode,
CompileTimeErrorCode.unqualifiedReferenceToNonLocalStaticMember,
arguments: [enclosingElement.displayName],
);
}
} else if (enclosingElement is ExtensionElement &&
enclosingElement.name == null) {
_resolver.diagnosticReporter.atNode(
nameNode,
CompileTimeErrorCode.instanceAccessToStaticMemberOfUnnamedExtension,
arguments: [nameNode.name, element.kind.displayName],
);
} 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.
_resolver.diagnosticReporter.atNode(
nameNode,
CompileTimeErrorCode.instanceAccessToStaticMember,
arguments: [
nameNode.name,
element.kind.displayName,
enclosingElement.name!,
enclosingElement is MixinElement
? 'mixin'
: enclosingElement.kind.displayName,
],
);
}
}
/// Resolves [node]'s static type, as an instantiated function type, and type
/// argument types, using [rawType] as the uninstantiated function type.
void _resolve({
required FunctionReferenceImpl node,
required DartType? rawType,
String? name,
}) {
if (rawType == null) {
node.recordStaticType(DynamicTypeImpl.instance, resolver: _resolver);
}
if (rawType is InvalidType) {
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
return;
}
if (rawType is TypeParameterTypeImpl) {
// If the type of the function is a type parameter, the tearoff is
// disallowed, reported in [_resolveDisallowedExpression]. Use the type
// parameter's bound here in an attempt to assign the intended types.
rawType = rawType.element.bound;
}
if (rawType is FunctionType) {
// A FunctionReference with type arguments and with a
// ConstructorReference child is invalid. E.g. `List.filled<int>`.
// [CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR] is
// reported elsewhere; don't check type arguments here.
if (node.function is ConstructorReference) {
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
} else {
var typeArguments = node.typeArguments;
if (typeArguments == null) {
node.recordStaticType(rawType, resolver: _resolver);
} else {
var typeArgumentTypes = _checkTypeArguments(
typeArguments,
name,
rawType.typeParameters,
CompileTimeErrorCode.wrongNumberOfTypeArgumentsFunction,
);
var invokeType = rawType.instantiate(typeArgumentTypes);
node.typeArgumentTypes = typeArgumentTypes;
node.recordStaticType(invokeType, resolver: _resolver);
}
}
} else {
if (_resolver.isConstructorTearoffsEnabled) {
// Only report constructor tearoff-related errors if the constructor
// tearoff feature is enabled.
_diagnosticReporter.atNode(
node.function,
CompileTimeErrorCode.disallowedTypeInstantiationExpression,
);
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
} else if (rawType is DynamicType) {
node.recordStaticType(DynamicTypeImpl.instance, resolver: _resolver);
} else {
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
}
}
}
void _resolveAsImplicitCallReference(
FunctionReferenceImpl node,
MethodElement callMethod,
) {
// `node<...>` is to be treated as `node.call<...>`.
var callMethodType = callMethod.type;
var typeArgumentTypes = _checkTypeArguments(
// `node.typeArguments`, coming from the parser, is never null.
node.typeArguments!,
MethodElement.CALL_METHOD_NAME,
callMethodType.typeParameters,
CompileTimeErrorCode.wrongNumberOfTypeArgumentsFunction,
);
var callReference = ImplicitCallReferenceImpl(
expression: node.function,
element: callMethod,
typeArguments: node.typeArguments,
typeArgumentTypes: typeArgumentTypes,
);
_resolver.replaceExpression(node, callReference);
var instantiatedType = callMethodType.instantiate(typeArgumentTypes);
callReference.recordStaticType(instantiatedType, resolver: _resolver);
}
void _resolveConstructorReference(FunctionReferenceImpl node) {
// TODO(srawlins): Rewrite and resolve [node] as a constructor reference.
node.function.accept(_resolver);
node.setPseudoExpressionStaticType(DynamicTypeImpl.instance);
}
/// Resolves [node] as a [TypeLiteral] referencing an interface type directly
/// (not through a type alias).
void _resolveDirectTypeLiteral(
FunctionReferenceImpl node,
IdentifierImpl name,
InterfaceElementImpl element,
) {
var typeArguments = _checkTypeArguments(
// `node.typeArguments`, coming from the parser, is never null.
node.typeArguments!,
name.name,
element.typeParameters,
CompileTimeErrorCode.wrongNumberOfTypeArguments,
);
var type = element.instantiateImpl(
typeArguments: typeArguments,
nullabilitySuffix: NullabilitySuffix.none,
);
_resolveTypeLiteral(node: node, instantiatedType: type, name: name);
}
/// Resolves [node] as a type instantiation on an illegal expression.
///
/// This function attempts to give [node] a static type, to continue working
/// with what the user may be intending.
void _resolveDisallowedExpression(
FunctionReferenceImpl node,
DartType? rawType,
) {
if (_resolver.isConstructorTearoffsEnabled) {
// Only report constructor tearoff-related errors if the constructor
// tearoff feature is enabled.
_diagnosticReporter.atNode(
node.function,
CompileTimeErrorCode.disallowedTypeInstantiationExpression,
);
}
_resolve(node: node, rawType: rawType);
}
void _resolveExtensionOverride(
FunctionReferenceImpl node,
PropertyAccessImpl function,
ExtensionOverrideImpl override,
) {
var propertyName = function.propertyName;
var result = _extensionResolver.getOverrideMember(
override,
propertyName.name,
);
var member = result.getter2;
if (member == null) {
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
return;
}
if (member.isStatic) {
_resolver.diagnosticReporter.atNode(
function.propertyName,
CompileTimeErrorCode.extensionOverrideAccessToStaticMember,
);
// Continue to resolve type.
}
if (function.isCascaded) {
_resolver.diagnosticReporter.atToken(
override.name,
CompileTimeErrorCode.extensionOverrideWithCascade,
);
// Continue to resolve type.
}
if (member is PropertyAccessorElement) {
_resolve(node: node, rawType: member.returnType);
return;
}
_resolve(node: node, rawType: member.type, name: propertyName.name);
}
/// Resolve a possible function tearoff of a [FunctionTypedElement] receiver.
///
/// There are three possible valid cases: tearing off the `call` method of a
/// function element, tearing off an extension element declared on [Function],
/// and tearing off an extension element declared on a function type.
Element? _resolveFunctionTypeFunction(
ExpressionImpl receiver,
SimpleIdentifier methodName,
FunctionTypeImpl receiverType,
) {
var methodElement =
_resolver.typePropertyResolver
.resolve(
receiver: receiver,
receiverType: receiverType,
name: methodName.name,
hasRead: true,
hasWrite: false,
propertyErrorEntity: methodName,
nameErrorEntity: methodName,
)
.getter2;
if (methodElement != null && methodElement.isStatic) {
_reportInvalidAccessToStaticMember(
methodName,
methodElement,
implicitReceiver: false,
);
}
return methodElement;
}
void _resolvePrefixedIdentifierFunction(
FunctionReferenceImpl node,
PrefixedIdentifierImpl function,
) {
var prefixElement = function.prefix.scopeLookupResult!.getter;
if (prefixElement == null) {
_diagnosticReporter.atNode(
function.prefix,
CompileTimeErrorCode.undefinedIdentifier,
arguments: [function.name],
);
function.setPseudoExpressionStaticType(InvalidTypeImpl.instance);
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
return;
}
function.prefix.element = prefixElement;
function.prefix.setPseudoExpressionStaticType(
prefixElement is PromotableElementImpl
? _resolver.localVariableTypeProvider.getType(
function.prefix,
isRead: true,
)
: prefixElement.referenceType,
);
var functionName = function.identifier.name;
if (prefixElement is PrefixElement) {
var functionElement = prefixElement.scope.lookup(functionName).getter;
if (functionElement == null) {
_diagnosticReporter.atNode(
function.identifier,
CompileTimeErrorCode.undefinedPrefixedName,
arguments: [functionName, function.prefix.name],
);
function.setPseudoExpressionStaticType(InvalidTypeImpl.instance);
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
return;
} else {
_resolveReceiverPrefix(node, prefixElement, function, functionElement);
return;
}
}
if (_checkDynamicTypeInstantiation(node, function, prefixElement)) {
return;
}
if (prefixElement is TopLevelFunctionElement &&
functionName == MethodElement.CALL_METHOD_NAME) {
_resolve(node: node, rawType: prefixElement.type, name: functionName);
return;
}
var propertyType = _resolveTypeProperty(
receiver: function.prefix,
name: function.identifier,
nameErrorEntity: function,
);
var callMethod = _getCallMethod(node, propertyType);
if (callMethod is MethodElement) {
_resolveAsImplicitCallReference(node, callMethod);
return;
}
if (propertyType is FunctionType) {
function.setPseudoExpressionStaticType(propertyType);
_resolve(node: node, rawType: propertyType, name: functionName);
return;
}
if (propertyType != null) {
// If the property is unknown, [UNDEFINED_GETTER] is reported elsewhere.
// If it is known, we must report the bad type instantiation here.
_diagnosticReporter.atNode(
function.identifier,
CompileTimeErrorCode.disallowedTypeInstantiationExpression,
);
}
_resolver.analyzeExpression(function, _resolver.operations.unknownType);
_resolver.popRewrite();
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
}
void _resolvePropertyAccessFunction(
FunctionReferenceImpl node,
PropertyAccessImpl function,
) {
_resolver.analyzeExpression(function, _resolver.operations.unknownType);
_resolver.popRewrite();
var callMethod = _getCallMethod(node, function.staticType);
if (callMethod is MethodElement) {
_resolveAsImplicitCallReference(node, callMethod);
return;
}
var target = function.realTarget;
TypeImpl targetType;
if (target is SuperExpressionImpl) {
targetType = target.typeOrThrow;
} else if (target is ThisExpressionImpl) {
targetType = target.typeOrThrow;
} else if (target is SimpleIdentifierImpl) {
var targetElement = target.scopeLookupResult!.getter;
if (targetElement is InternalVariableElement) {
targetType = targetElement.type;
} else if (targetElement is InternalPropertyAccessorElement) {
targetType = targetElement.variable.type;
} else {
// TODO(srawlins): Can we get here?
node.setPseudoExpressionStaticType(DynamicTypeImpl.instance);
return;
}
} else if (target is ExtensionOverrideImpl) {
_resolveExtensionOverride(node, function, target);
return;
} else {
var targetType = target.staticType;
if (targetType is DynamicType) {
_diagnosticReporter.atNode(
node,
CompileTimeErrorCode.genericMethodTypeInstantiationOnDynamic,
);
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
return;
} else if (targetType is InvalidType) {
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
return;
}
var functionType = _resolveTypeProperty(
receiver: target,
name: function.propertyName,
nameErrorEntity: function,
);
if (functionType is FunctionType) {
function.setPseudoExpressionStaticType(functionType);
_resolve(
node: node,
rawType: functionType,
name: function.propertyName.name,
);
return;
} else if (functionType != null) {
// If the property is unknown, [UNDEFINED_GETTER] is reported elsewhere.
// If it is known, we must report the bad type instantiation here.
_diagnosticReporter.atNode(
function.propertyName,
CompileTimeErrorCode.disallowedTypeInstantiationExpression,
);
}
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
return;
}
var propertyElement =
_resolver.typePropertyResolver
.resolve(
receiver: function.realTarget,
receiverType: targetType,
name: function.propertyName.name,
hasRead: true,
hasWrite: false,
propertyErrorEntity: function.propertyName,
nameErrorEntity: function,
)
.getter2;
if (propertyElement is TypeParameterElement) {
_resolve(node: node, rawType: propertyElement!.type);
return;
}
_resolve(
node: node,
rawType: function.staticType,
name: propertyElement?.name,
);
}
void _resolveReceiverPrefix(
FunctionReferenceImpl node,
PrefixElement prefixElement,
PrefixedIdentifierImpl prefix,
Element element,
) {
if (element is MultiplyDefinedElement) {
MultiplyDefinedElement multiply = element;
element = multiply.conflictingElements[0];
// TODO(srawlins): Add a resolution test for this case.
}
// Classes and type aliases are checked first so as to include a
// PropertyAccess parent check, which does not need to be done for
// functions.
if (element is InterfaceElement || element is TypeAliasElement) {
// A type-instantiated constructor tearoff like `prefix.C<int>.name` is
// initially represented as a [PropertyAccess] with a
// [FunctionReference] 'target'.
if (node.parent is PropertyAccess) {
_resolveConstructorReference(node);
return;
} else if (element is InterfaceElementImpl) {
_resolver.analyzeExpression(
node.function,
_resolver.operations.unknownType,
);
_resolver.popRewrite();
_resolveDirectTypeLiteral(node, prefix, element);
return;
} else if (element is TypeAliasElementImpl) {
_resolver.analyzeExpression(prefix, _resolver.operations.unknownType);
_resolver.popRewrite();
_resolveTypeAlias(node: node, element: element, typeAlias: prefix);
return;
}
} else if (element is ExecutableElement) {
_resolver.analyzeExpression(
node.function,
_resolver.operations.unknownType,
);
_resolver.popRewrite();
var callMethod = _getCallMethod(node, node.function.staticType);
if (callMethod is MethodElement) {
_resolveAsImplicitCallReference(node, callMethod);
return;
}
_resolve(
node: node,
rawType: node.function.typeOrThrow as FunctionType,
name: element.name,
);
return;
} else if (element is ExtensionElement) {
prefix.identifier.element = element;
prefix.identifier.setPseudoExpressionStaticType(InvalidTypeImpl.instance);
prefix.setPseudoExpressionStaticType(InvalidTypeImpl.instance);
_resolveDisallowedExpression(node, InvalidTypeImpl.instance);
return;
}
assert(
false,
'Member of prefixed element, $prefixElement, is not a class, mixin, '
'type alias, or executable element: $element (${element.runtimeType})',
);
node.setPseudoExpressionStaticType(InvalidTypeImpl.instance);
}
void _resolveSimpleIdentifierFunction(
FunctionReferenceImpl node,
SimpleIdentifierImpl function,
) {
var element = function.scopeLookupResult!.getter;
if (element == null) {
TypeImpl receiverType;
var enclosingClass = _resolver.enclosingClass;
if (enclosingClass != null) {
receiverType = enclosingClass.thisType;
} else {
var enclosingExtension = _resolver.enclosingExtension;
if (enclosingExtension != null) {
receiverType = enclosingExtension.extendedType;
} else {
_diagnosticReporter.atNode(
function,
CompileTimeErrorCode.undefinedIdentifier,
arguments: [function.name],
);
function.setPseudoExpressionStaticType(InvalidTypeImpl.instance);
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
return;
}
}
var result = _resolver.typePropertyResolver.resolve(
receiver: null,
receiverType: receiverType,
name: function.name,
hasRead: true,
hasWrite: false,
propertyErrorEntity: function,
nameErrorEntity: function,
);
var method = result.getter2;
if (method != null) {
if (method.isStatic) {
_reportInvalidAccessToStaticMember(
function,
method,
implicitReceiver: true,
);
// Continue to assign types.
}
if (method is InternalPropertyAccessorElement) {
function.element = method;
function.setPseudoExpressionStaticType(method.returnType);
_resolve(node: node, rawType: method.variable.type);
return;
}
function.element = method;
function.setPseudoExpressionStaticType(method.type);
_resolve(node: node, rawType: method.type, name: function.name);
return;
} else {
_resolver.diagnosticReporter.atNode(
function,
CompileTimeErrorCode.undefinedMethod,
arguments: [function.name, receiverType],
);
function.setPseudoExpressionStaticType(InvalidTypeImpl.instance);
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
return;
}
}
// Classes and type aliases are checked first so as to include a
// PropertyAccess parent check, which does not need to be done for
// functions.
if (element is InterfaceElement || element is TypeAliasElement) {
// A type-instantiated constructor tearoff like `C<int>.name` or
// `prefix.C<int>.name` is initially represented as a [PropertyAccess]
// with a [FunctionReference] target.
if (node.parent is PropertyAccess) {
if (element is TypeAliasElementImpl &&
element.aliasedType is FunctionType) {
function.element = element;
_resolveTypeAlias(node: node, element: element, typeAlias: function);
} else {
_resolveConstructorReference(node);
}
return;
} else if (element is InterfaceElementImpl) {
function.element = element;
_resolveDirectTypeLiteral(node, function, element);
return;
} else if (element is TypeAliasElementImpl) {
function.element = element;
_resolveTypeAlias(node: node, element: element, typeAlias: function);
return;
}
} else if (element is MethodElement) {
function.element = element;
function.setPseudoExpressionStaticType(element.type);
_resolve(node: node, rawType: element.type, name: element.name);
return;
} else if (element is LocalFunctionElement) {
function.element = element;
function.setPseudoExpressionStaticType(element.type);
_resolve(node: node, rawType: element.type, name: element.name);
return;
} else if (element is TopLevelFunctionElement) {
function.element = element;
function.setPseudoExpressionStaticType(element.type);
_resolve(node: node, rawType: element.type, name: element.name);
return;
} else if (element is PropertyAccessorElement) {
function.element = element;
var variable = element.variable;
function.setPseudoExpressionStaticType(variable.type);
var callMethod = _getCallMethod(node, variable.type);
if (callMethod is MethodElement) {
_resolveAsImplicitCallReference(node, callMethod);
return;
}
_resolve(node: node, rawType: element.returnType);
return;
} else if (element is ExecutableElement) {
function.element = element;
function.setPseudoExpressionStaticType(element.type);
_resolve(node: node, rawType: element.type);
return;
} else if (element is VariableElement) {
function.element = element;
function.setPseudoExpressionStaticType(element.type);
var callMethod = _getCallMethod(node, element.type);
if (callMethod is MethodElement) {
_resolveAsImplicitCallReference(node, callMethod);
return;
}
_resolve(node: node, rawType: element.type);
return;
} else if (element is ExtensionElement) {
function.element = element;
function.setPseudoExpressionStaticType(InvalidTypeImpl.instance);
_resolveDisallowedExpression(node, InvalidTypeImpl.instance);
return;
} else {
_resolveDisallowedExpression(node, DynamicTypeImpl.instance);
return;
}
}
/// Returns the element that represents the property named [propertyName] on
/// [classElement].
ExecutableElement? _resolveStaticElement(
InterfaceElement 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 TypeAliasElementImpl element,
required IdentifierImpl typeAlias,
}) {
var typeArguments = _checkTypeArguments(
// `node.typeArguments`, coming from the parser, is never null.
node.typeArguments!,
element.name,
element.typeParameters,
CompileTimeErrorCode.wrongNumberOfTypeArguments,
);
var type = element.instantiateImpl(
typeArguments: typeArguments,
nullabilitySuffix: NullabilitySuffix.none,
);
_resolveTypeLiteral(node: node, instantiatedType: type, name: typeAlias);
}
void _resolveTypeLiteral({
required FunctionReferenceImpl node,
required TypeImpl instantiatedType,
required IdentifierImpl name,
}) {
// TODO(srawlins): set the static element of [typeName].
// This involves a fair amount of resolution, as [name] may be a prefixed
// identifier, etc. [NamedType]s should be resolved in [ResolutionVisitor],
// and this could be done for nodes like this via [AstRewriter].
var typeName = name.toNamedType(
typeArguments: node.typeArguments,
question: null,
);
typeName.type = instantiatedType;
var typeLiteral = TypeLiteralImpl(type: typeName);
_resolver.replaceExpression(node, typeLiteral);
typeLiteral.recordStaticType(_typeType, resolver: _resolver);
}
/// Resolves [name] as a property on [receiver].
///
/// Returns `null` if [receiver]'s type is `null`, a [TypeParameterType],
/// or a type alias for a non-interface type.
DartType? _resolveTypeProperty({
required ExpressionImpl receiver,
required SimpleIdentifierImpl name,
required SyntacticEntity nameErrorEntity,
}) {
if (receiver is IdentifierImpl) {
var receiverElement = receiver.element;
if (receiverElement is InterfaceElement) {
var element = _resolveStaticElement(receiverElement, name);
name.element = element;
return element?.referenceType;
} else if (receiverElement is TypeAliasElement) {
var aliasedType = receiverElement.aliasedType;
if (aliasedType is InterfaceType) {
var element = _resolveStaticElement(aliasedType.element, name);
name.element = element;
return element?.referenceType;
} else {
return null;
}
}
}
var receiverType = receiver.staticType;
if (receiverType == null) {
return null;
} else if (receiverType is TypeParameterTypeImpl) {
return null;
} else if (receiverType is FunctionTypeImpl) {
if (name.name == MethodElement.CALL_METHOD_NAME) {
return receiverType;
}
var element = _resolveFunctionTypeFunction(receiver, name, receiverType);
name.element = element;
return element?.referenceType;
}
var element =
_resolver.typePropertyResolver
.resolve(
receiver: receiver,
receiverType: receiverType,
name: name.name,
hasRead: true,
hasWrite: false,
propertyErrorEntity: name,
nameErrorEntity: nameErrorEntity,
)
.getter2;
name.element = element;
if (element != null && element.isStatic) {
_reportInvalidAccessToStaticMember(
name,
element,
implicitReceiver: false,
);
}
return element?.referenceType;
}
}
extension on Element {
/// Returns the 'type' of `this`, when accessed as a "reference", not
/// immediately followed by parentheses and arguments.
///
/// For all elements that don't have a type (for example, [LibraryElement]),
/// `null` is returned. For [PropertyAccessorElement], the return value is
/// returned. For all other elements, their `type` property is returned.
DartType? get referenceType {
if (this is ConstructorElement) {
return (this as ConstructorElement).type;
} else if (this is TopLevelFunctionElement) {
return (this as TopLevelFunctionElement).type;
} else if (this is PropertyAccessorElement) {
return (this as PropertyAccessorElement).returnType;
} else if (this is MethodElement) {
return (this as MethodElement).type;
} else if (this is VariableElement) {
return (this as VariableElement).type;
} else {
return null;
}
}
}