blob: 84260a22285d359e05c778f20a7e5a3432aaa991 [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/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;
final bool _isNonNullableByDefault;
/// The type representing the type 'type'.
final InterfaceType _typeType;
FunctionReferenceResolver(this._resolver, this._isNonNullableByDefault)
: _typeType = _resolver.typeProvider.typeType;
ErrorReporter get _errorReporter => _resolver.errorReporter;
NullabilitySuffix get _nullabilitySuffixForTypeNames =>
_isNonNullableByDefault ? NullabilitySuffix.none : NullabilitySuffix.star;
void resolve(FunctionReferenceImpl node) {
var function = node.function;
node.typeArguments?.accept(_resolver);
if (function is SimpleIdentifierImpl) {
var element = _resolver.nameScope.lookup(function.name).getter;
// 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) {
_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 ExecutableElement) {
function.staticElement = element;
_resolve(node: node, name: element.name, rawType: element.type);
return;
} else if (element is VariableElement) {
var functionType = element.type;
if (functionType is FunctionType) {
function.accept(_resolver);
_resolve(node: node, name: element.name ?? '', rawType: functionType);
return;
}
}
}
// TODO(srawlins): Handle `function` being a [SuperExpression].
if (function is PrefixedIdentifierImpl) {
var prefixElement =
_resolver.nameScope.lookup(function.prefix.name).getter;
function.prefix.staticElement = prefixElement;
if (prefixElement is PrefixElement) {
var functionName = function.identifier.name;
var functionElement = prefixElement.scope.lookup(functionName).getter;
if (functionElement == null) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_PREFIXED_NAME,
function.identifier,
[function.identifier.name, function.prefix.name],
);
function.staticType = DynamicTypeImpl.instance;
node.staticType = DynamicTypeImpl.instance;
return;
} else {
functionElement = _resolver.toLegacyElement(functionElement);
_resolveReceiverPrefix(
node, prefixElement, function, functionElement);
return;
}
} else if (prefixElement is VariableElement) {
function.prefix.staticType = prefixElement.type;
} else if (prefixElement is PropertyAccessorElement) {
function.prefix.staticType = prefixElement.returnType;
}
var methodElement = _resolveTypeProperty(
receiver: function.prefix,
receiverElement: prefixElement,
name: function.identifier,
nameErrorEntity: function,
);
if (methodElement is MethodElement) {
function.identifier.staticElement = methodElement;
function.staticType = methodElement.type;
_resolve(
node: node,
name: function.identifier.name,
rawType: methodElement.type,
);
return;
}
// TODO(srawlins): Need to report cases where [methodElement] is not
// generic. The 'test_instanceGetter_explicitReceiver' test case needs to
// be updated to handle this.
function.accept(_resolver);
node.staticType = DynamicTypeImpl.instance;
return;
}
if (function is PropertyAccessImpl) {
function.accept(_resolver);
var target = function.target;
DartType targetType;
if (target is SuperExpressionImpl) {
targetType = target.typeOrThrow;
} else if (target is ThisExpressionImpl) {
targetType = target.typeOrThrow;
} else if (target is SimpleIdentifierImpl) {
var targetElement = _resolver.nameScope.lookup(target.name).getter;
if (targetElement is VariableElement) {
targetType = targetElement.type;
} else if (targetElement is PropertyAccessorElement) {
targetType = targetElement.returnType;
} else {
// TODO(srawlins): Can we get here?
node.staticType = DynamicTypeImpl.instance;
return;
}
} else if (target is PrefixedIdentifierImpl) {
var prefixElement =
_resolver.nameScope.lookup(target.prefix.name).getter;
if (prefixElement is PrefixElement) {
var prefixName = target.identifier.name;
var targetElement = prefixElement.scope.lookup(prefixName).getter;
var methodElement = _resolveTypeProperty(
receiver: target,
receiverElement: targetElement,
name: function.propertyName,
nameErrorEntity: function,
);
if (methodElement == null) {
// TODO(srawlins): Can we get here?
node.staticType = DynamicTypeImpl.instance;
return;
} else {
_resolveReceiverPrefix(node, prefixElement, target, methodElement);
return;
}
} else {
// TODO(srawlins): Can we get here?
node.staticType = DynamicTypeImpl.instance;
return;
}
} else {
// TODO(srawlins): Can we get here?
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;
var functionType = function.typeOrThrow;
if (functionType is FunctionType && propertyElement != null) {
_resolve(
node: node,
name: propertyElement.name,
rawType: functionType,
);
return;
}
// TODO(srawlins): Handle type variables bound to function type, like
// `T extends void Function<U>(U)`.
}
// TODO(srawlins): Enumerate and handle all cases that fall through to
// here; ultimately it should just be a case of "unknown identifier."
function.accept(_resolver);
node.staticType = DynamicTypeImpl.instance;
}
List<DartType> _checkTypeArguments(
TypeArgumentList typeArgumentList,
String name,
List<TypeParameterElement> typeParameters,
CompileTimeErrorCode errorCode,
) {
if (typeArgumentList.arguments.length != typeParameters.length) {
_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();
}
}
/// 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 String name,
required FunctionType rawType,
}) {
var typeArguments = _checkTypeArguments(
// `node.typeArguments`, coming from the parser, is never null.
node.typeArguments!, name, rawType.typeFormals,
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_FUNCTION,
);
var invokeType = rawType.instantiate(typeArguments);
node.typeArgumentTypes = typeArguments;
node.staticType = invokeType;
}
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: _nullabilitySuffixForTypeNames,
);
_resolveTypeLiteral(node: node, instantiatedType: type, name: name);
}
void _resolveReceiverPrefix(
FunctionReferenceImpl node,
PrefixElement prefixElement,
PrefixedIdentifier prefix,
Element element,
) {
// TODO(srawlins): Handle `loadLibrary`, as in `p.loadLibrary<int>;`.
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);
_resolve(
node: node,
name: element.name,
rawType: node.function.typeOrThrow as FunctionType,
);
return;
}
// TODO(srawlins): Report undefined prefixed identifier.
node.staticType = DynamicTypeImpl.instance;
}
/// Returns the element that represents the property named [propertyName] on
/// [classElement].
ExecutableElement? _resolveStaticElement(
ClassElement classElement, SimpleIdentifier propertyName) {
String name = propertyName.name;
ExecutableElement? element;
if (propertyName.inSetterContext()) {
element = classElement.getSetter(name);
}
element ??= classElement.getGetter(name);
element ??= classElement.getMethod(name);
if (element != null && element.isAccessibleIn(_resolver.definingLibrary)) {
return element;
}
return null;
}
void _resolveTypeAlias({
required FunctionReferenceImpl node,
required TypeAliasElement element,
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: _nullabilitySuffixForTypeNames);
_resolveTypeLiteral(node: node, instantiatedType: type, name: typeAlias);
}
void _resolveTypeLiteral({
required FunctionReferenceImpl node,
required DartType instantiatedType,
required Identifier name,
}) {
var typeName = astFactory.typeName(name, 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] (with element
/// [receiverElement]).
///
/// Returns `null` if [receiverElement] is `null`, a [TypeParameterElement],
/// or a [TypeAliasElement] for a non-interface type.
ExecutableElement? _resolveTypeProperty({
required Expression receiver,
required Element? receiverElement,
required SimpleIdentifier name,
required SyntacticEntity nameErrorEntity,
}) {
if (receiverElement == null) {
return null;
}
if (receiverElement is TypeParameterElement) {
return null;
}
if (receiverElement is ClassElement) {
return _resolveStaticElement(receiverElement, name);
} else if (receiverElement is TypeAliasElement) {
var aliasedType = receiverElement.aliasedType;
if (aliasedType is InterfaceType) {
return _resolveStaticElement(aliasedType.element, name);
} else {
return null;
}
}
DartType receiverType;
if (receiverElement is VariableElement) {
receiverType = receiverElement.type;
} else if (receiverElement is PropertyAccessorElement) {
receiverType = receiverElement.returnType;
} else {
assert(false,
'Unexpected receiverElement type: ${receiverElement.runtimeType}');
return null;
}
return _resolver.typePropertyResolver
.resolve(
receiver: receiver,
receiverType: receiverType,
name: name.name,
propertyErrorEntity: name,
nameErrorEntity: nameErrorEntity,
)
.getter;
}
}