blob: ea681e1cdc89919dac3bd61b951c041901ddfab4 [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/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';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/ast_factory.dart';
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/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;
final bool _isNonNullableByDefault;
/// The type representing the type 'type'.
final InterfaceType _typeType;
FunctionReferenceResolver(this._resolver, this._isNonNullableByDefault)
: _extensionResolver = _resolver.extensionResolver,
_typeType = _resolver.typeProvider.typeType;
ErrorReporter get _errorReporter => _resolver.errorReporter;
NullabilitySuffix get _nullabilitySuffix =>
_isNonNullableByDefault ? NullabilitySuffix.none : NullabilitySuffix.star;
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>`.
function.accept(_resolver);
// 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>`).
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR,
typeArguments,
[
function.constructorName.type.name.toSource(),
function.constructorName.name!.name
],
);
_resolve(node: node, rawType: function.staticType);
}
} else {
// TODO(srawlins): Handle `function` being a [SuperExpression].
function.accept(_resolver);
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 != null && prefixType.isDynamic) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
function,
[],
);
node.staticType = DynamicTypeImpl.instance;
return true;
}
return false;
}
List<DartType> _checkTypeArguments(
TypeArgumentList typeArgumentList,
String? name,
List<TypeParameterElement> typeParameters,
CompileTimeErrorCode errorCode,
) {
if (typeArgumentList.arguments.length != typeParameters.length) {
if (name == null &&
errorCode ==
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION) {
errorCode = CompileTimeErrorCode
.WRONG_NUMBER_OF_TYPE_ARGUMENTS_ANONYMOUS_FUNCTION;
_errorReporter.reportErrorForNode(
errorCode,
typeArgumentList,
[typeParameters.length, typeArgumentList.arguments.length],
);
} else {
assert(name != null);
_errorReporter.reportErrorForNode(
errorCode,
typeArgumentList,
[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! InterfaceType) {
return null;
}
if (type.nullabilitySuffix == NullabilitySuffix.question) {
// If the interface type is nullable, only an applicable extension method
// applies.
return _extensionResolver
.findExtension(type, node, FunctionElement.CALL_METHOD_NAME)
.getter;
}
// Otherwise, a 'call' method on the interface, or on an applicable
// extension method applies.
return type.lookUpMethod2(
FunctionElement.CALL_METHOD_NAME, type.element.library) ??
_extensionResolver
.findExtension(type, node, FunctionElement.CALL_METHOD_NAME)
.getter;
}
void _reportInvalidAccessToStaticMember(
SimpleIdentifier nameNode,
ExecutableElement element, {
required bool implicitReceiver,
}) {
var enclosingElement = element.enclosingElement;
if (implicitReceiver) {
if (_resolver.enclosingExtension != null) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode
.UNQUALIFIED_REFERENCE_TO_STATIC_MEMBER_OF_EXTENDED_TYPE,
nameNode,
[enclosingElement.displayName],
);
} else {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER,
nameNode,
[enclosingElement.displayName],
);
}
} else if (enclosingElement is ExtensionElement &&
enclosingElement.name == null) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode
.INSTANCE_ACCESS_TO_STATIC_MEMBER_OF_UNNAMED_EXTENSION,
nameNode,
[
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.errorReporter.reportErrorForNode(
CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER,
nameNode,
[
nameNode.name,
element.kind.displayName,
enclosingElement.name!,
enclosingElement is ClassElement && enclosingElement.isMixin
? '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.staticType = DynamicTypeImpl.instance;
}
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.staticType = DynamicTypeImpl.instance;
} else {
var typeArguments = node.typeArguments;
if (typeArguments == null) {
node.staticType = rawType;
} else {
var typeArgumentTypes = _checkTypeArguments(
typeArguments,
name,
rawType.typeFormals,
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION,
);
var invokeType = rawType.instantiate(typeArgumentTypes);
node.typeArgumentTypes = typeArgumentTypes;
node.staticType = invokeType;
}
}
} else {
if (_resolver.isConstructorTearoffsEnabled) {
// Only report constructor tearoff-related errors if the constructor
// tearoff feature is enabled.
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION,
node.function,
[],
);
}
node.staticType = DynamicTypeImpl.instance;
}
}
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!, FunctionElement.CALL_METHOD_NAME,
callMethodType.typeFormals,
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION,
);
var callReference = astFactory.implicitCallReference(
expression: node.function,
staticElement: callMethod,
typeArguments: node.typeArguments,
typeArgumentTypes: typeArgumentTypes,
);
NodeReplacer.replace(node, callReference);
var instantiatedType = callMethodType.instantiate(typeArgumentTypes);
callReference.staticType = instantiatedType;
}
void _resolveConstructorReference(FunctionReferenceImpl node) {
// TODO(srawlins): Rewrite and resolve [node] as a constructor reference.
node.function.accept(_resolver);
node.staticType = DynamicTypeImpl.instance;
}
/// Resolves [node] as a [TypeLiteral] referencing an interface type directly
/// (not through a type alias).
void _resolveDirectTypeLiteral(
FunctionReferenceImpl node, Identifier name, ClassElement element) {
var typeArguments = _checkTypeArguments(
// `node.typeArguments`, coming from the parser, is never null.
node.typeArguments!, name.name, element.typeParameters,
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS,
);
var type = element.instantiate(
typeArguments: typeArguments,
nullabilitySuffix: _nullabilitySuffix,
);
_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.
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION,
node.function,
[],
);
}
_resolve(node: node, rawType: rawType);
}
void _resolveExtensionOverride(
FunctionReferenceImpl node,
PropertyAccessImpl function,
ExtensionOverride override,
) {
var propertyName = function.propertyName;
var result =
_extensionResolver.getOverrideMember(override, propertyName.name);
var member = _resolver.toLegacyElement(result.getter);
if (member == null) {
node.staticType = DynamicTypeImpl.instance;
return;
}
if (member.isStatic) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.EXTENSION_OVERRIDE_ACCESS_TO_STATIC_MEMBER,
function.propertyName,
);
// Continue to resolve type.
}
if (function.isCascaded) {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.EXTENSION_OVERRIDE_WITH_CASCADE,
override.extensionName,
);
// Continue to resolve type.
}
if (member is PropertyAccessorElement) {
function.accept(_resolver);
_resolve(node: node, rawType: member.returnType);
return;
}
_resolve(node: node, rawType: member.type, name: propertyName.name);
}
/// Resolve a possible function tearoff of a [FunctionElement] 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(
Expression receiver,
SimpleIdentifier methodName,
FunctionType receiverType,
) {
var methodElement = _resolver.typePropertyResolver
.resolve(
receiver: receiver,
receiverType: receiverType,
name: methodName.name,
propertyErrorEntity: methodName,
nameErrorEntity: methodName,
)
.getter;
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) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_IDENTIFIER,
function.prefix,
[function.name],
);
function.staticType = DynamicTypeImpl.instance;
node.staticType = DynamicTypeImpl.instance;
return;
}
function.prefix.staticElement = prefixElement;
function.prefix.staticType = prefixElement.referenceType;
var functionName = function.identifier.name;
if (prefixElement is PrefixElement) {
var functionElement = prefixElement.scope.lookup(functionName).getter;
if (functionElement == null) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_PREFIXED_NAME,
function.identifier,
[functionName, function.prefix.name],
);
function.staticType = DynamicTypeImpl.instance;
node.staticType = DynamicTypeImpl.instance;
return;
} else {
functionElement = _resolver.toLegacyElement(functionElement);
_resolveReceiverPrefix(node, prefixElement, function, functionElement);
return;
}
}
if (_checkDynamicTypeInstantiation(node, function, prefixElement)) {
return;
}
if (prefixElement is FunctionElement &&
functionName == FunctionElement.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.staticType = 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.
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION,
function.identifier,
[],
);
}
function.accept(_resolver);
node.staticType = DynamicTypeImpl.instance;
}
void _resolvePropertyAccessFunction(
FunctionReferenceImpl node, PropertyAccessImpl function) {
function.accept(_resolver);
var callMethod = _getCallMethod(node, function.staticType);
if (callMethod is MethodElement) {
_resolveAsImplicitCallReference(node, callMethod);
return;
}
var target = function.realTarget;
DartType 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 VariableElement) {
targetType = targetElement.type;
} else if (targetElement is PropertyAccessorElement) {
targetType = targetElement.variable.type;
} else {
// TODO(srawlins): Can we get here?
node.staticType = DynamicTypeImpl.instance;
return;
}
} else if (target is ExtensionOverrideImpl) {
_resolveExtensionOverride(node, function, target);
return;
} else {
var targetType = target.staticType;
if (targetType != null && targetType.isDynamic) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
node,
[],
);
node.staticType = DynamicTypeImpl.instance;
return;
}
var functionType = _resolveTypeProperty(
receiver: target,
name: function.propertyName,
nameErrorEntity: function,
);
if (functionType is FunctionType) {
function.staticType = 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.
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION,
function.propertyName,
[],
);
}
node.staticType = DynamicTypeImpl.instance;
return;
}
var propertyElement = _resolver.typePropertyResolver
.resolve(
receiver: function.realTarget,
receiverType: targetType,
name: function.propertyName.name,
propertyErrorEntity: function.propertyName,
nameErrorEntity: function,
)
.getter;
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 ClassElement || 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 ClassElement) {
node.function.accept(_resolver);
_resolveDirectTypeLiteral(node, prefix, element);
return;
} else if (element is TypeAliasElement) {
prefix.accept(_resolver);
_resolveTypeAlias(node: node, element: element, typeAlias: prefix);
return;
}
} else if (element is ExecutableElement) {
node.function.accept(_resolver);
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.staticElement = element;
prefix.identifier.staticType = DynamicTypeImpl.instance;
prefix.staticType = DynamicTypeImpl.instance;
_resolveDisallowedExpression(node, DynamicTypeImpl.instance);
return;
}
assert(
false,
'Member of prefixed element, $prefixElement, is not a class, mixin, '
'type alias, or executable element: $element (${element.runtimeType})',
);
node.staticType = DynamicTypeImpl.instance;
}
void _resolveSimpleIdentifierFunction(
FunctionReferenceImpl node, SimpleIdentifierImpl function) {
var element = function.scopeLookupResult!.getter;
if (element == null) {
DartType receiverType;
var enclosingClass = _resolver.enclosingClass;
if (enclosingClass != null) {
receiverType = enclosingClass.thisType;
} else {
var enclosingExtension = _resolver.enclosingExtension;
if (enclosingExtension != null) {
receiverType = enclosingExtension.extendedType;
} else {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_IDENTIFIER,
function,
[function.name],
);
function.staticType = DynamicTypeImpl.instance;
node.staticType = DynamicTypeImpl.instance;
return;
}
}
var result = _resolver.typePropertyResolver.resolve(
receiver: null,
receiverType: receiverType,
name: function.name,
propertyErrorEntity: function,
nameErrorEntity: function,
);
var method = result.getter;
if (method != null) {
if (method.isStatic) {
_reportInvalidAccessToStaticMember(function, method,
implicitReceiver: true);
// Continue to assign types.
}
if (method is PropertyAccessorElement) {
function.staticElement = method;
function.staticType = method.returnType;
_resolve(node: node, rawType: method.variable.type);
return;
}
function.staticElement = method;
function.staticType = method.type;
_resolve(node: node, rawType: method.type, name: function.name);
return;
} else {
_resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_METHOD,
function,
[function.name, receiverType],
);
function.staticType = DynamicTypeImpl.instance;
node.staticType = DynamicTypeImpl.instance;
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 ClassElement || 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 TypeAliasElement &&
element.aliasedType is FunctionType) {
function.staticElement = element;
_resolveTypeAlias(node: node, element: element, typeAlias: function);
} else {
_resolveConstructorReference(node);
}
return;
} else if (element is ClassElement) {
function.staticElement = element;
_resolveDirectTypeLiteral(node, function, element);
return;
} else if (element is TypeAliasElement) {
function.staticElement = element;
_resolveTypeAlias(node: node, element: element, typeAlias: function);
return;
}
} else if (element is MethodElement) {
function.staticElement = element;
function.staticType = element.type;
_resolve(node: node, rawType: element.type, name: element.name);
return;
} else if (element is FunctionElement) {
function.staticElement = element;
function.staticType = element.type;
_resolve(node: node, rawType: element.type, name: element.name);
return;
} else if (element is PropertyAccessorElement) {
function.staticElement = element;
function.staticType = element.variable.type;
var callMethod = _getCallMethod(node, element.variable.type);
if (callMethod is MethodElement) {
_resolveAsImplicitCallReference(node, callMethod);
return;
}
_resolve(node: node, rawType: element.returnType);
return;
} else if (element is ExecutableElement) {
function.staticElement = element;
function.staticType = element.type;
_resolve(node: node, rawType: element.type);
return;
} else if (element is VariableElement) {
function.staticElement = element;
function.staticType = 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.staticElement = element;
function.staticType = DynamicTypeImpl.instance;
_resolveDisallowedExpression(node, DynamicTypeImpl.instance);
return;
} else {
_resolveDisallowedExpression(node, DynamicTypeImpl.instance);
return;
}
}
/// 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.isAccessibleIn2(_resolver.definingLibrary)) {
return element;
}
return null;
}
void _resolveTypeAlias({
required FunctionReferenceImpl node,
required TypeAliasElement element,
required Identifier typeAlias,
}) {
var typeArguments = _checkTypeArguments(
// `node.typeArguments`, coming from the parser, is never null.
node.typeArguments!, element.name, element.typeParameters,
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS,
);
var type = element.instantiate(
typeArguments: typeArguments,
nullabilitySuffix: _nullabilitySuffix,
);
_resolveTypeLiteral(node: node, instantiatedType: type, name: typeAlias);
}
void _resolveTypeLiteral({
required FunctionReferenceImpl node,
required DartType instantiatedType,
required Identifier name,
}) {
// TODO(srawlins): set the static element of [typeName].
// This involves a fair amount of resolution, as [name] may be a prefixed
// identifier, etc. [TypeName]s should be resolved in [ResolutionVisitor],
// and this could be done for nodes like this via [AstRewriter].
var typeName = astFactory.namedType(
name: name,
typeArguments: node.typeArguments,
);
typeName.type = instantiatedType;
typeName.name.staticType = instantiatedType;
var typeLiteral = astFactory.typeLiteral(typeName: typeName);
typeLiteral.staticType = _typeType;
NodeReplacer.replace(node, typeLiteral);
}
/// 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 Expression receiver,
required SimpleIdentifierImpl name,
required SyntacticEntity nameErrorEntity,
}) {
if (receiver is Identifier) {
var receiverElement = receiver.staticElement;
if (receiverElement is ClassElement) {
var element = _resolveStaticElement(receiverElement, name);
name.staticElement = element;
return element?.referenceType;
} else if (receiverElement is TypeAliasElement) {
var aliasedType = receiverElement.aliasedType;
if (aliasedType is InterfaceType) {
var element = _resolveStaticElement(aliasedType.element, name);
name.staticElement = element;
return element?.referenceType;
} else {
return null;
}
}
}
var receiverType = receiver.staticType;
if (receiverType == null) {
return null;
} else if (receiverType is TypeParameterType) {
return null;
} else if (receiverType is FunctionType) {
if (name.name == FunctionElement.CALL_METHOD_NAME) {
return receiverType;
}
var element = _resolveFunctionTypeFunction(receiver, name, receiverType);
name.staticElement = element;
return element?.referenceType;
}
var element = _resolver.typePropertyResolver
.resolve(
receiver: receiver,
receiverType: receiverType,
name: name.name,
propertyErrorEntity: name,
nameErrorEntity: nameErrorEntity,
)
.getter;
name.staticElement = 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, [ExportElement2]),
/// `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 FunctionElement) {
return (this as FunctionElement).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;
}
}
}