blob: 1e5f7a6e521c34edb1a2e32c3c9367bad57a64f7 [file] [log] [blame]
// Copyright (c) 2020, 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/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.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/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/migration.dart';
import 'package:analyzer/src/generated/resolver.dart';
/// Information about a constructor element to instantiate.
///
/// If the target is a [ClassElement], the [element] is a raw
/// [ConstructorElement] from the class, and [typeParameters] are the
/// type parameters of the class.
///
/// If the target is a [TypeAliasElement] with an [InterfaceType] as the
/// aliased type, the [element] is a [ConstructorMember] created from the
/// [ConstructorElement] of the corresponding class, and substituting
/// the class type parameters with the type arguments specified in the alias,
/// explicit types or the type parameters of the alias. The [typeParameters]
/// are the type parameters of the alias.
class ConstructorElementToInfer {
/// The type parameters used in [element].
final List<TypeParameterElement> typeParameters;
/// The element, might be [ConstructorMember].
final ConstructorElement element;
ConstructorElementToInfer(this.typeParameters, this.element);
/// Return the equivalent generic function type that we could use to
/// forward to the constructor, or for a non-generic type simply returns
/// the constructor type.
///
/// For example given the type `class C<T> { C(T arg); }`, the generic
/// function type is `<T>(T) -> C<T>`.
FunctionType get asType {
return FunctionTypeImpl(
typeFormals: typeParameters,
parameters: element.parameters,
returnType: element.returnType,
nullabilitySuffix: NullabilitySuffix.none,
);
}
}
class InvocationInferenceHelper {
final ResolverVisitor _resolver;
final ErrorReporter _errorReporter;
final TypeSystemImpl _typeSystem;
final MigrationResolutionHooks? _migrationResolutionHooks;
final bool _genericMetadataIsEnabled;
List<DartType>? _typeArgumentTypes;
FunctionType? _invokeType;
InvocationInferenceHelper({
required ResolverVisitor resolver,
required ErrorReporter errorReporter,
required TypeSystemImpl typeSystem,
required MigrationResolutionHooks? migrationResolutionHooks,
}) : _resolver = resolver,
_errorReporter = errorReporter,
_typeSystem = typeSystem,
_migrationResolutionHooks = migrationResolutionHooks,
_genericMetadataIsEnabled = resolver.definingLibrary.featureSet
.isEnabled(Feature.generic_metadata);
/// Compute the return type of the method or function represented by the given
/// type that is being invoked.
DartType computeInvokeReturnType(DartType? type) {
if (type is FunctionType) {
return type.returnType;
} else {
return DynamicTypeImpl.instance;
}
}
/// If the constructor referenced by the [constructorName] is generic,
/// and the [constructorName] does not have explicit type arguments,
/// return the element and type parameters to infer. Otherwise return `null`.
ConstructorElementToInfer? constructorElementToInfer({
required ConstructorName constructorName,
required LibraryElement definingLibrary,
}) {
List<TypeParameterElement>? typeParameters;
ConstructorElement? rawElement;
var typeName = constructorName.type;
var typeArguments = typeName.typeArguments;
var typeElement = typeName.name.staticElement;
if (typeElement is ClassElement) {
typeParameters = typeElement.typeParameters;
if (typeParameters.isNotEmpty && typeArguments == null) {
var constructorIdentifier = constructorName.name;
if (constructorIdentifier == null) {
rawElement = typeElement.unnamedConstructor;
} else {
var name = constructorIdentifier.name;
rawElement = typeElement.getNamedConstructor(name);
if (rawElement != null &&
!rawElement.isAccessibleIn(definingLibrary)) {
rawElement = null;
}
}
}
} else if (typeElement is TypeAliasElement) {
typeParameters = typeElement.typeParameters;
var aliasedType = typeElement.aliasedType;
if (aliasedType is InterfaceType) {
if (typeParameters.isNotEmpty && typeArguments == null) {
var constructorIdentifier = constructorName.name;
rawElement = aliasedType.lookUpConstructor(
constructorIdentifier?.name,
definingLibrary,
);
}
}
}
if (typeParameters == null || rawElement == null) {
return null;
}
rawElement = _resolver.toLegacyElement(rawElement);
return ConstructorElementToInfer(typeParameters, rawElement);
}
FunctionType? inferArgumentTypesForGeneric(AstNode inferenceNode,
DartType? uninstantiatedType, TypeArgumentList? typeArguments,
{AstNode? errorNode,
bool isConst = false,
required DartType? contextReturnType}) {
errorNode ??= inferenceNode;
uninstantiatedType = _getFreshType(uninstantiatedType);
if (typeArguments == null &&
uninstantiatedType is FunctionType &&
uninstantiatedType.typeFormals.isNotEmpty) {
var typeArguments = _typeSystem.inferGenericFunctionOrType(
typeParameters: uninstantiatedType.typeFormals,
parameters: const <ParameterElement>[],
declaredReturnType: uninstantiatedType.returnType,
argumentTypes: const <DartType>[],
contextReturnType: contextReturnType,
downwards: true,
isConst: isConst,
errorReporter: _errorReporter,
errorNode: errorNode,
genericMetadataIsEnabled: _genericMetadataIsEnabled,
);
if (typeArguments != null) {
return uninstantiatedType.instantiate(typeArguments);
}
}
return null;
}
DartType? inferArgumentTypesForInvocation(
InvocationExpression node, DartType? type,
{required DartType? contextType}) {
return inferArgumentTypesForGeneric(node, type, node.typeArguments,
contextReturnType: contextType) ??
node.staticInvokeType;
}
/// Given a possibly generic invocation like `o.m(args)` or `(f)(args)` try to
/// infer the instantiated generic function type.
///
/// This takes into account both the context type, as well as information from
/// the argument types.
void inferGenericInvocationExpression(
InvocationExpressionImpl node, DartType? type,
{required DartType? contextType}) {
var arguments = node.argumentList;
var freshType = _getFreshType(type);
var inferred = inferGenericInvoke(
node, freshType, node.typeArguments, arguments, node.function,
contextReturnType: contextType);
if (inferred != null && inferred != node.staticInvokeType) {
// Fix up the parameter elements based on inferred method.
arguments.correspondingStaticParameters =
ResolverVisitor.resolveArgumentsToParameters(
argumentList: arguments,
parameters: inferred.parameters,
);
node.staticInvokeType = inferred;
}
}
/// Given a possibly generic invocation or instance creation, such as
/// `o.m(args)` or `(f)(args)` or `new T(args)` try to infer the instantiated
/// generic function type.
///
/// This takes into account both the context type, as well as information from
/// the argument types.
FunctionType? inferGenericInvoke(
AstNode node,
DartType? fnType,
TypeArgumentList? typeArguments,
ArgumentList argumentList,
AstNode errorNode,
{bool isConst = false,
required DartType? contextReturnType}) {
if (typeArguments == null &&
fnType is FunctionType &&
fnType.typeFormals.isNotEmpty) {
// Get the parameters that correspond to the uninstantiated generic.
var typeArgs = _inferUpwards(
rawType: fnType,
argumentList: argumentList,
contextType: contextReturnType,
isConst: isConst,
errorNode: errorNode,
);
if (node is InvocationExpressionImpl) {
node.typeArgumentTypes = typeArgs;
}
if (typeArgs != null) {
return fnType.instantiate(typeArgs);
}
return fnType;
}
// There is currently no other place where we set type arguments
// for FunctionExpressionInvocation(s), so set it here, if not inferred.
if (node is FunctionExpressionInvocationImpl) {
if (typeArguments != null) {
var typeArgs =
typeArguments.arguments.map((n) => n.typeOrThrow).toList();
node.typeArgumentTypes = typeArgs;
} else {
node.typeArgumentTypes = const <DartType>[];
}
}
return null;
}
/// Given an uninstantiated generic function type, referenced by the
/// [identifier] in the tear-off [expression], try to infer the instantiated
/// generic function type from the surrounding context.
DartType inferTearOff(Expression expression, SimpleIdentifierImpl identifier,
DartType tearOffType,
{required DartType? contextType}) {
if (contextType is FunctionType && tearOffType is FunctionType) {
var typeArguments = _typeSystem.inferFunctionTypeInstantiation(
contextType,
tearOffType,
errorReporter: _errorReporter,
errorNode: expression,
genericMetadataIsEnabled: _genericMetadataIsEnabled,
)!;
identifier.tearOffTypeArgumentTypes = typeArguments;
if (typeArguments.isNotEmpty) {
return tearOffType.instantiate(typeArguments);
}
}
return tearOffType;
}
/// Record that the static type of the given node is the given type.
///
/// @param expression the node whose type is to be recorded
/// @param type the static type of the node
void recordStaticType(ExpressionImpl expression, DartType type,
{required DartType? contextType}) {
var hooks = _migrationResolutionHooks;
if (hooks != null) {
type = hooks.modifyExpressionType(expression, type, contextType);
}
expression.staticType = type;
if (_typeSystem.isBottom(type)) {
_resolver.flowAnalysis.flow?.handleExit();
}
}
/// Finish resolution of the [FunctionExpressionInvocation].
///
/// We have already found the invoked [ExecutableElement], and the [rawType]
/// is its not yet instantiated type. Here we perform downwards inference,
/// resolution of arguments, and upwards inference.
void resolveFunctionExpressionInvocation({
required FunctionExpressionInvocationImpl node,
required FunctionType rawType,
required List<WhyNotPromotedGetter> whyNotPromotedList,
required DartType? contextType,
}) {
_resolveInvocation(
rawType: rawType,
typeArgumentList: node.typeArguments,
argumentList: node.argumentList,
contextType: contextType,
isConst: false,
errorNode: node.function,
whyNotPromotedList: whyNotPromotedList,
);
node.typeArgumentTypes = _typeArgumentTypes;
node.staticInvokeType = _invokeType;
}
/// Finish resolution of the [MethodInvocation].
///
/// We have already found the invoked [ExecutableElement], and the [rawType]
/// is its not yet instantiated type. Here we perform downwards inference,
/// resolution of arguments, and upwards inference.
void resolveMethodInvocation({
required MethodInvocationImpl node,
required FunctionType rawType,
required List<WhyNotPromotedGetter> whyNotPromotedList,
required DartType? contextType,
}) {
_resolveInvocation(
rawType: rawType,
typeArgumentList: node.typeArguments,
argumentList: node.argumentList,
contextType: contextType,
isConst: false,
errorNode: node.function,
whyNotPromotedList: whyNotPromotedList,
);
node.typeArgumentTypes = _typeArgumentTypes;
node.staticInvokeType = _invokeType;
var returnType = computeInvokeReturnType(_invokeType);
var targetType = node.realTarget?.staticType;
if (targetType != null) {
returnType = _typeSystem.refineNumericInvocationType(
targetType,
node.methodName.staticElement,
[
for (var argument in node.argumentList.arguments) argument.typeOrThrow
],
returnType,
);
}
recordStaticType(node, returnType, contextType: contextType);
}
List<DartType>? _inferDownwards({
required FunctionType rawType,
required DartType? contextType,
required bool isConst,
required AstNode errorNode,
}) {
return _typeSystem.inferGenericFunctionOrType(
typeParameters: rawType.typeFormals,
parameters: const <ParameterElement>[],
declaredReturnType: rawType.returnType,
argumentTypes: const <DartType>[],
contextReturnType: contextType,
downwards: true,
isConst: isConst,
errorReporter: _errorReporter,
errorNode: errorNode,
genericMetadataIsEnabled: _genericMetadataIsEnabled,
);
}
/// TODO(scheglov) Instead of [isConst] sanitize [contextType] before calling.
List<DartType>? _inferUpwards({
required FunctionType rawType,
required DartType? contextType,
required ArgumentList argumentList,
required bool isConst,
required AstNode errorNode,
}) {
rawType = _getFreshType(rawType) as FunctionType;
// Get the parameters that correspond to the uninstantiated generic.
List<ParameterElement?> rawParameters =
ResolverVisitor.resolveArgumentsToParameters(
argumentList: argumentList,
parameters: rawType.parameters,
);
List<ParameterElement> params = <ParameterElement>[];
List<DartType> argTypes = <DartType>[];
for (int i = 0, length = rawParameters.length; i < length; i++) {
ParameterElement? parameter = rawParameters[i];
if (parameter != null) {
params.add(parameter);
argTypes.add(argumentList.arguments[i].typeOrThrow);
}
}
var typeArgs = _typeSystem.inferGenericFunctionOrType(
typeParameters: rawType.typeFormals,
parameters: params,
declaredReturnType: rawType.returnType,
argumentTypes: argTypes,
contextReturnType: contextType,
isConst: isConst,
errorReporter: _errorReporter,
errorNode: errorNode,
genericMetadataIsEnabled: _genericMetadataIsEnabled,
);
return typeArgs;
}
bool _isCallToIdentical(AstNode? invocation) {
if (invocation is MethodInvocation) {
var invokedMethod = invocation.methodName.staticElement;
return invokedMethod != null &&
invokedMethod.name == 'identical' &&
invokedMethod.library!.isDartCore;
}
return false;
}
void _resolveArguments(
ArgumentList argumentList,
List<WhyNotPromotedGetter> whyNotPromotedList,
List<ParameterElement> parameters,
{required DartType? methodInvocationContext}) {
_resolver.analyzeArgumentList(argumentList, parameters,
isIdentical: _isCallToIdentical(argumentList.parent),
whyNotPromotedList: whyNotPromotedList,
methodInvocationContext: methodInvocationContext);
}
void _resolveInvocation({
required FunctionType rawType,
required DartType? contextType,
required TypeArgumentList? typeArgumentList,
required ArgumentListImpl argumentList,
required bool isConst,
required AstNode errorNode,
required List<WhyNotPromotedGetter> whyNotPromotedList,
}) {
if (typeArgumentList != null) {
_resolveInvocationWithTypeArguments(
rawType: rawType,
typeArgumentList: typeArgumentList,
argumentList: argumentList,
whyNotPromotedList: whyNotPromotedList,
methodInvocationContext: contextType,
);
} else {
_resolveInvocationWithoutTypeArguments(
rawType: rawType,
contextType: contextType,
argumentList: argumentList,
isConst: isConst,
errorNode: errorNode,
whyNotPromotedList: whyNotPromotedList,
);
}
_setCorrespondingParameters(argumentList, _invokeType!);
}
void _resolveInvocationWithoutTypeArguments({
required FunctionType rawType,
required DartType? contextType,
required ArgumentList argumentList,
required bool isConst,
required AstNode errorNode,
required List<WhyNotPromotedGetter> whyNotPromotedList,
}) {
var typeParameters = rawType.typeFormals;
if (typeParameters.isEmpty) {
_resolveArguments(argumentList, whyNotPromotedList, rawType.parameters,
methodInvocationContext: contextType);
_typeArgumentTypes = const <DartType>[];
_invokeType = rawType;
} else {
rawType = _getFreshType(rawType) as FunctionType;
var downwardsTypeArguments = _inferDownwards(
rawType: rawType,
contextType: contextType,
isConst: isConst,
errorNode: errorNode,
)!;
var downwardsInvokeType = rawType.instantiate(downwardsTypeArguments);
_resolveArguments(
argumentList, whyNotPromotedList, downwardsInvokeType.parameters,
methodInvocationContext: contextType);
_typeArgumentTypes = _inferUpwards(
rawType: rawType,
argumentList: argumentList,
contextType: contextType,
isConst: isConst,
errorNode: errorNode,
);
_invokeType = rawType.instantiate(_typeArgumentTypes!);
}
}
void _resolveInvocationWithTypeArguments({
required FunctionType rawType,
required TypeArgumentList typeArgumentList,
required ArgumentList argumentList,
required List<WhyNotPromotedGetter> whyNotPromotedList,
required DartType? methodInvocationContext,
}) {
var typeParameters = rawType.typeFormals;
List<DartType> typeArguments;
if (typeArgumentList.arguments.length != typeParameters.length) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD,
typeArgumentList,
[
rawType,
typeParameters.length,
typeArgumentList.arguments.length,
],
);
typeArguments = List.filled(
typeParameters.length,
DynamicTypeImpl.instance,
);
} else {
typeArguments = typeArgumentList.arguments
.map((typeArgument) => typeArgument.typeOrThrow)
.toList(growable: true);
}
var invokeType = rawType.instantiate(typeArguments);
_resolveArguments(argumentList, whyNotPromotedList, invokeType.parameters,
methodInvocationContext: methodInvocationContext);
_typeArgumentTypes = typeArguments;
_invokeType = invokeType;
}
void _setCorrespondingParameters(
ArgumentListImpl argumentList,
FunctionType invokeType,
) {
var parameters = ResolverVisitor.resolveArgumentsToParameters(
argumentList: argumentList,
parameters: invokeType.parameters,
errorReporter: _errorReporter,
);
argumentList.correspondingStaticParameters = parameters;
}
static DartType? _getFreshType(DartType? type) {
if (type is FunctionType) {
var parameters = getFreshTypeParameters(type.typeFormals);
return parameters.applyToFunctionType(type);
} else {
return type;
}
}
}