blob: c40a4ff086b5cdba956f2ab36d29bc9dcada11a9 [file] [log] [blame]
// Copyright (c) 2022, 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:_fe_analyzer_shared/src/base/errors.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/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/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';
/// Specialization of [InvocationInferrer] for performing type inference on AST
/// nodes of type [Annotation] that resolve to a constructor invocation.
class AnnotationInferrer extends FullInvocationInferrer<AnnotationImpl> {
/// The identifier pointing to the constructor that's being invoked, or `null`
/// if a constructor name couldn't be found (should only happen when
/// recovering from errors). If the constructor is generic, this identifier's
/// static element will be updated to point to a [ConstructorMember] with type
/// arguments filled in.
final SimpleIdentifierImpl? constructorName;
AnnotationInferrer({required this.constructorName}) : super._();
@override
bool get _needsTypeArgumentBoundsCheck => true;
@override
ErrorCode get _wrongNumberOfTypeArgumentsErrorCode =>
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS;
@override
ArgumentListImpl _getArgumentList(AnnotationImpl node) => node.arguments!;
@override
bool _getIsConst(AnnotationImpl node) => true;
@override
TypeArgumentListImpl? _getTypeArguments(AnnotationImpl node) =>
node.typeArguments;
@override
bool _isGenericInferenceDisabled(ResolverVisitor resolver) =>
!resolver.genericMetadataIsEnabled;
@override
List<ParameterElement>? _storeResult(AnnotationImpl node,
List<DartType>? typeArgumentTypes, FunctionType? invokeType) {
if (invokeType != null) {
var constructorElement = ConstructorMember.from(
node.element as ConstructorElement,
invokeType.returnType as InterfaceType,
);
constructorName?.staticElement = constructorElement;
node.element = constructorElement;
return constructorElement.parameters;
}
return null;
}
}
/// Specialization of [InvocationInferrer] for performing type inference on AST
/// nodes of type [ExtensionOverride].
class ExtensionOverrideInferrer
extends InvocationInferrer<ExtensionOverrideImpl> {
const ExtensionOverrideInferrer() : super._();
@override
ArgumentListImpl _getArgumentList(ExtensionOverrideImpl node) =>
node.argumentList;
}
/// Specialization of [InvocationInferrer] for performing type inference on AST
/// nodes that require full downward and upward inference.
abstract class FullInvocationInferrer<Node extends AstNodeImpl>
extends InvocationInferrer<Node> {
const FullInvocationInferrer._() : super._();
bool get _needsTypeArgumentBoundsCheck => false;
ErrorCode get _wrongNumberOfTypeArgumentsErrorCode =>
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD;
@override
DartType resolveInvocation({
required ResolverVisitor resolver,
required Node node,
required FunctionType? rawType,
required DartType? contextType,
required List<WhyNotPromotedGetter> whyNotPromotedList,
}) {
var typeArgumentList = _getTypeArguments(node);
List<DartType>? typeArgumentTypes;
FunctionType? invokeType;
if (_isGenericInferenceDisabled(resolver)) {
if (rawType != null && rawType.typeFormals.isNotEmpty) {
typeArgumentTypes = List.filled(
rawType.typeFormals.length,
DynamicTypeImpl.instance,
);
} else {
typeArgumentTypes = const <DartType>[];
invokeType = rawType;
}
invokeType = rawType?.instantiate(typeArgumentTypes);
} else if (typeArgumentList != null) {
if (rawType != null &&
typeArgumentList.arguments.length != rawType.typeFormals.length) {
var typeParameters = rawType.typeFormals;
_reportWrongNumberOfTypeArguments(
resolver, typeArgumentList, rawType, typeParameters);
typeArgumentTypes = List.filled(
typeParameters.length,
DynamicTypeImpl.instance,
);
} else {
typeArgumentTypes = typeArgumentList.arguments
.map((typeArgument) => typeArgument.typeOrThrow)
.toList(growable: true);
if (rawType != null && _needsTypeArgumentBoundsCheck) {
var typeParameters = rawType.typeFormals;
var substitution = Substitution.fromPairs(
typeParameters,
typeArgumentTypes,
);
for (var i = 0; i < typeParameters.length; i++) {
var typeParameter = typeParameters[i];
var bound = typeParameter.bound;
if (bound != null) {
bound = resolver.definingLibrary.toLegacyTypeIfOptOut(bound);
bound = substitution.substituteType(bound);
var typeArgument = typeArgumentTypes[i];
if (!resolver.typeSystem.isSubtypeOf(typeArgument, bound)) {
resolver.errorReporter.reportErrorForNode(
CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS,
typeArgumentList.arguments[i],
[typeArgument, typeParameter.name, bound],
);
}
}
}
}
}
invokeType = rawType?.instantiate(typeArgumentTypes);
} else if (rawType == null || rawType.typeFormals.isEmpty) {
typeArgumentTypes = const <DartType>[];
invokeType = rawType;
} else {
rawType = getFreshTypeParameters(rawType.typeFormals)
.applyToFunctionType(rawType);
var downwardsTypeArguments =
resolver.typeSystem.inferGenericFunctionOrType(
typeParameters: rawType.typeFormals,
parameters: const <ParameterElement>[],
declaredReturnType: rawType.returnType,
argumentTypes: const <DartType>[],
contextReturnType: contextType,
downwards: true,
isConst: _getIsConst(node),
errorReporter: resolver.errorReporter,
errorNode: _getErrorNode(node),
genericMetadataIsEnabled: resolver.genericMetadataIsEnabled,
)!;
invokeType = rawType.instantiate(downwardsTypeArguments);
}
super.resolveInvocation(
resolver: resolver,
node: node,
rawType: invokeType,
contextType: contextType,
whyNotPromotedList: whyNotPromotedList);
var argumentList = _getArgumentList(node);
if (typeArgumentTypes == null) {
if (rawType != null) {
// 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);
}
}
typeArgumentTypes = resolver.typeSystem.inferGenericFunctionOrType(
typeParameters: rawType.typeFormals,
parameters: params,
declaredReturnType: rawType.returnType,
argumentTypes: argTypes,
contextReturnType: contextType,
isConst: _getIsConst(node),
errorReporter: resolver.errorReporter,
errorNode: _getErrorNode(node),
genericMetadataIsEnabled: resolver.genericMetadataIsEnabled,
)!;
invokeType = rawType.instantiate(typeArgumentTypes);
} else {
typeArgumentTypes = const [];
}
}
var parameters = _storeResult(node, typeArgumentTypes, invokeType);
if (parameters != null) {
argumentList.correspondingStaticParameters =
ResolverVisitor.resolveArgumentsToParameters(
argumentList: argumentList,
parameters: parameters,
errorReporter: resolver.errorReporter,
);
}
var returnType = InvocationInferrer.computeInvokeReturnType(invokeType);
return _refineReturnType(resolver, node, returnType);
}
AstNode _getErrorNode(Node node) => node;
bool _getIsConst(Node node) => false;
TypeArgumentListImpl? _getTypeArguments(Node node);
bool _isGenericInferenceDisabled(ResolverVisitor resolver) => false;
DartType _refineReturnType(
ResolverVisitor resolver, Node node, DartType returnType) =>
returnType;
void _reportWrongNumberOfTypeArguments(
ResolverVisitor resolver,
TypeArgumentList typeArgumentList,
FunctionType rawType,
List<TypeParameterElement> typeParameters) {
resolver.errorReporter.reportErrorForNode(
_wrongNumberOfTypeArgumentsErrorCode,
typeArgumentList,
[
rawType,
typeParameters.length,
typeArgumentList.arguments.length,
],
);
}
List<ParameterElement>? _storeResult(
Node node, List<DartType>? typeArgumentTypes, FunctionType? invokeType) {
return invokeType?.parameters;
}
}
/// Specialization of [InvocationInferrer] for performing type inference on AST
/// nodes of type [FunctionExpressionInvocation].
class FunctionExpressionInvocationInferrer
extends InvocationExpressionInferrer<FunctionExpressionInvocationImpl> {
const FunctionExpressionInvocationInferrer() : super._();
@override
ExpressionImpl _getErrorNode(FunctionExpressionInvocationImpl node) =>
node.function;
}
/// Specialization of [InvocationInferrer] for performing type inference on AST
/// nodes of type [InstanceCreationExpression].
class InstanceCreationInferrer
extends FullInvocationInferrer<InstanceCreationExpressionImpl> {
const InstanceCreationInferrer() : super._();
@override
bool get _needsTypeArgumentBoundsCheck => true;
@override
ArgumentListImpl _getArgumentList(InstanceCreationExpressionImpl node) =>
node.argumentList;
@override
ConstructorNameImpl _getErrorNode(InstanceCreationExpressionImpl node) =>
node.constructorName;
@override
bool _getIsConst(InstanceCreationExpressionImpl node) => node.isConst;
@override
TypeArgumentListImpl? _getTypeArguments(InstanceCreationExpressionImpl node) {
// For an instance creation expression the type arguments are on the
// constructor name.
return node.constructorName.type.typeArguments;
}
@override
void _reportWrongNumberOfTypeArguments(
ResolverVisitor resolver,
TypeArgumentList typeArgumentList,
FunctionType rawType,
List<TypeParameterElement> typeParameters) {
// Error reporting for instance creations is done elsewhere.
}
@override
List<ParameterElement>? _storeResult(InstanceCreationExpressionImpl node,
List<DartType>? typeArgumentTypes, FunctionType? invokeType) {
if (invokeType != null) {
var constructedType = invokeType.returnType;
node.constructorName.type.type = constructedType;
var constructorElement = ConstructorMember.from(
node.constructorName.staticElement!,
constructedType as InterfaceType,
);
node.constructorName.staticElement = constructorElement;
return constructorElement.parameters;
}
return null;
}
}
/// Specialization of [InvocationInferrer] for performing type inference on AST
/// nodes derived from [InvocationExpression].
abstract class InvocationExpressionInferrer<
Node extends InvocationExpressionImpl>
extends FullInvocationInferrer<Node> {
const InvocationExpressionInferrer._() : super._();
@override
ArgumentListImpl _getArgumentList(Node node) => node.argumentList;
@override
Expression _getErrorNode(Node node) => node.function;
@override
TypeArgumentListImpl? _getTypeArguments(Node node) => node.typeArguments;
@override
List<ParameterElement>? _storeResult(
Node node, List<DartType>? typeArgumentTypes, FunctionType? invokeType) {
node.typeArgumentTypes = typeArgumentTypes;
node.staticInvokeType = invokeType ?? DynamicTypeImpl.instance;
return super._storeResult(node, typeArgumentTypes, invokeType);
}
}
/// Base class containing functionality for performing type inference on AST
/// nodes that invoke a method, function, or constructor.
abstract class InvocationInferrer<Node extends AstNodeImpl> {
const InvocationInferrer._();
/// Performs type inference on an invocation expression of type [Node].
/// [rawType] should be the type of the function the invocation is resolved to
/// (with type arguments not applied yet).
void resolveInvocation({
required ResolverVisitor resolver,
required Node node,
required FunctionType? rawType,
required DartType? contextType,
required List<WhyNotPromotedGetter> whyNotPromotedList,
}) {
var parameters = rawType?.parameters;
var namedParameters = <String, ParameterElement>{};
if (parameters != null) {
for (var i = 0; i < parameters.length; i++) {
var parameter = parameters[i];
if (parameter.isNamed) {
namedParameters[parameter.name] = parameter;
}
}
}
var argumentList = _getArgumentList(node);
resolver.checkUnreachableNode(argumentList);
var flow = resolver.flowAnalysis.flow;
var positionalParameterIndex = 0;
for (var argument in _iterateArguments(resolver, argumentList)) {
ParameterElement? parameter;
if (argument is NamedExpression) {
parameter = namedParameters[argument.name.label.name];
} else if (parameters != null) {
while (positionalParameterIndex < parameters.length) {
parameter = parameters[positionalParameterIndex++];
if (!parameter.isNamed) {
break;
}
}
}
DartType? parameterContextType;
if (parameter != null) {
var parameterType = parameter.type;
parameterContextType = _computeContextForArgument(
resolver, node, parameterType, contextType);
}
resolver.analyzeExpression(argument, parameterContextType);
if (flow != null) {
whyNotPromotedList.add(flow.whyNotPromoted(argument));
}
}
}
/// Computes the type context that should be used when evaluating a particular
/// argument of the invocation. Usually this is just the type of the
/// corresponding parameter, but it can be different for certain primitive
/// numeric operations.
DartType? _computeContextForArgument(ResolverVisitor resolver, Node node,
DartType parameterType, DartType? methodInvocationContext) =>
parameterType;
/// Gets the argument list for the invocation. TODO(paulberry): remove?
ArgumentListImpl _getArgumentList(Node node);
/// Iterates through the argument list for the invocation. Usually this is
/// just a simple iteration through the arguments, but in certain cases, some
/// flow analysis methods need to be called in between visiting the various
/// arguments.
Iterable<Expression> _iterateArguments(
ResolverVisitor resolver, ArgumentList argumentList) =>
argumentList.arguments;
/// Computes the return type of the method or function represented by the
/// given type that is being invoked.
static DartType computeInvokeReturnType(DartType? type) {
if (type is FunctionType) {
return type.returnType;
} else {
return DynamicTypeImpl.instance;
}
}
}
/// Specialization of [InvocationInferrer] for performing type inference on AST
/// nodes of type [MethodInvocation].
class MethodInvocationInferrer
extends InvocationExpressionInferrer<MethodInvocationImpl> {
/// Gets the appropriate instance of [MethodInvocation] for the given [node].
///
/// This factory takes care of the fact that invocations of `identical` need
/// to have special integration with flow analysis.
factory MethodInvocationInferrer.forNode(MethodInvocationImpl node) {
var invokedMethod = node.methodName.staticElement;
if (invokedMethod != null &&
invokedMethod.name == 'identical' &&
invokedMethod.library!.isDartCore &&
node.argumentList.arguments.length == 2) {
return const _IdenticalInvocationInferrer._();
} else {
return const MethodInvocationInferrer._();
}
}
const MethodInvocationInferrer._() : super._();
@override
DartType? _computeContextForArgument(
ResolverVisitor resolver,
MethodInvocationImpl node,
DartType parameterType,
DartType? methodInvocationContext) {
var contextType = super._computeContextForArgument(
resolver, node, parameterType, methodInvocationContext);
var targetType = node.realTarget?.staticType;
if (targetType != null) {
contextType = resolver.typeSystem.refineNumericInvocationContext(
targetType,
node.methodName.staticElement,
methodInvocationContext,
parameterType);
}
return contextType;
}
@override
DartType _refineReturnType(ResolverVisitor resolver,
MethodInvocationImpl node, DartType returnType) {
var targetType = node.realTarget?.staticType;
if (targetType != null) {
returnType = resolver.typeSystem.refineNumericInvocationType(
targetType,
node.methodName.staticElement,
[
for (var argument in node.argumentList.arguments) argument.typeOrThrow
],
returnType,
);
}
return returnType;
}
}
/// Specialization of [InvocationInferrer] for performing type inference on AST
/// nodes of type [RedirectingConstructorInvocation].
class RedirectingConstructorInvocationInferrer
extends InvocationInferrer<RedirectingConstructorInvocationImpl> {
const RedirectingConstructorInvocationInferrer() : super._();
@override
ArgumentListImpl _getArgumentList(
RedirectingConstructorInvocationImpl node) =>
node.argumentList;
}
/// Specialization of [InvocationInferrer] for performing type inference on AST
/// nodes of type [SuperConstructorInvocation].
class SuperConstructorInvocationInferrer
extends InvocationInferrer<SuperConstructorInvocationImpl> {
const SuperConstructorInvocationInferrer() : super._();
@override
ArgumentListImpl _getArgumentList(SuperConstructorInvocationImpl node) =>
node.argumentList;
}
/// Specialization of [InvocationInferrer] for performing type inference on AST
/// nodes of type [MethodInvocation] that resolve to the core function
/// `identical`. (Such nodes need to be handled in a special way due to the
/// interaction between `identical` and flow analysis).
class _IdenticalInvocationInferrer extends MethodInvocationInferrer {
const _IdenticalInvocationInferrer._() : super._();
@override
Iterable<Expression> _iterateArguments(
ResolverVisitor resolver, ArgumentList argumentList) sync* {
var flow = resolver.flowAnalysis.flow;
var arguments = argumentList.arguments;
assert(arguments.length == 2);
var firstArg = arguments[0];
yield firstArg;
firstArg = arguments[0]; // In case it was rewritten
flow?.equalityOp_rightBegin(firstArg, firstArg.typeOrThrow);
var secondArg = arguments[1];
yield secondArg;
secondArg = arguments[1]; // In case it was rewritten
flow?.equalityOp_end(
argumentList.parent as Expression, secondArg, secondArg.typeOrThrow);
}
}