blob: 8239ccd23b9d49b8346b9dd97e21b535f5e1d2d6 [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/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/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';
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;
}
}
FunctionType? inferArgumentTypesForGeneric(AstNode inferenceNode,
DartType? uninstantiatedType, TypeArgumentList? typeArguments,
{AstNode? errorNode, bool isConst = false}) {
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: InferenceContext.getContext(inferenceNode),
downwards: true,
isConst: isConst,
errorReporter: _errorReporter,
errorNode: errorNode,
genericMetadataIsEnabled: _genericMetadataIsEnabled,
);
if (typeArguments != null) {
return uninstantiatedType.instantiate(typeArguments);
}
}
return null;
}
void inferArgumentTypesForInvocation(
InvocationExpression node,
DartType? type,
) {
var inferred = inferArgumentTypesForGeneric(node, type, node.typeArguments);
InferenceContext.setType(
node.argumentList, inferred ?? 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,
) {
var arguments = node.argumentList;
var freshType = _getFreshType(type);
var inferred = inferGenericInvoke(
node, freshType, node.typeArguments, arguments, node.function);
if (inferred != null && inferred != node.staticInvokeType) {
// Fix up the parameter elements based on inferred method.
arguments.correspondingStaticParameters =
ResolverVisitor.resolveArgumentsToParameters(
arguments, inferred.parameters, null);
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(
Expression node,
DartType? fnType,
TypeArgumentList? typeArguments,
ArgumentList argumentList,
AstNode errorNode,
{bool isConst = false}) {
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: InferenceContext.getContext(node),
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,
SimpleIdentifier identifier,
DartType tearOffType,
) {
var context = InferenceContext.getContext(expression);
if (context is FunctionType && tearOffType is FunctionType) {
var typeArguments = _typeSystem.inferFunctionTypeInstantiation(
context,
tearOffType,
errorReporter: _resolver.errorReporter,
errorNode: expression,
genericMetadataIsEnabled: _genericMetadataIsEnabled,
)!;
(identifier as SimpleIdentifierImpl).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
///
/// TODO(scheglov) this is duplication
void recordStaticType(ExpressionImpl expression, DartType type) {
var hooks = _migrationResolutionHooks;
if (hooks != null) {
type = hooks.modifyExpressionType(expression, type);
}
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,
}) {
_resolveInvocation(
rawType: rawType,
typeArgumentList: node.typeArguments,
argumentList: node.argumentList,
contextType: InferenceContext.getContext(node),
isConst: false,
errorNode: node.function,
);
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,
}) {
_resolveInvocation(
rawType: rawType,
typeArgumentList: node.typeArguments,
argumentList: node.argumentList,
contextType: InferenceContext.getContext(node),
isConst: false,
errorNode: node.function,
);
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);
}
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, rawType.parameters, null);
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) {
_resolver.visitArgumentList(argumentList,
isIdentical: _isCallToIdentical(argumentList.parent));
}
void _resolveInvocation({
required FunctionType rawType,
required DartType? contextType,
required TypeArgumentList? typeArgumentList,
required ArgumentListImpl argumentList,
required bool isConst,
required AstNode errorNode,
}) {
if (typeArgumentList != null) {
_resolveInvocationWithTypeArguments(
rawType: rawType,
typeArgumentList: typeArgumentList,
argumentList: argumentList,
);
} else {
_resolveInvocationWithoutTypeArguments(
rawType: rawType,
contextType: contextType,
argumentList: argumentList,
isConst: isConst,
errorNode: errorNode,
);
}
_setCorrespondingParameters(argumentList, _invokeType!);
}
void _resolveInvocationWithoutTypeArguments({
required FunctionType rawType,
required DartType? contextType,
required ArgumentList argumentList,
required bool isConst,
required AstNode errorNode,
}) {
var typeParameters = rawType.typeFormals;
if (typeParameters.isEmpty) {
InferenceContext.setType(argumentList, rawType);
_resolveArguments(argumentList);
_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);
InferenceContext.setType(argumentList, downwardsInvokeType);
_resolveArguments(argumentList);
_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,
}) {
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);
InferenceContext.setType(argumentList, invokeType);
_resolveArguments(argumentList);
_typeArgumentTypes = typeArguments;
_invokeType = invokeType;
}
void _setCorrespondingParameters(
ArgumentListImpl argumentList,
FunctionType invokeType,
) {
var parameters = ResolverVisitor.resolveArgumentsToParameters(
argumentList,
invokeType.parameters,
_errorReporter.reportErrorForNode,
);
argumentList.correspondingStaticParameters = parameters;
}
static DartType? _getFreshType(DartType? type) {
if (type is FunctionType) {
var parameters = getFreshTypeParameters(type.typeFormals);
return parameters.applyToFunctionType(type);
} else {
return type;
}
}
}