blob: 97dad9b97b24d2b356af79b48ac1e1a56a02233d [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:_fe_analyzer_shared/src/deferred_closure_heuristic.dart';
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.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/generic_inferrer.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/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 ResolverVisitor resolver,
required AnnotationImpl node,
required ArgumentListImpl argumentList,
required FunctionType? rawType,
required DartType? contextType,
required List<WhyNotPromotedGetter> whyNotPromotedList,
required this.constructorName})
: super._(
resolver: resolver,
node: node,
argumentList: argumentList,
rawType: rawType,
contextType: contextType,
whyNotPromotedList: whyNotPromotedList);
@override
bool get _isConst => true;
@override
bool get _isGenericInferenceDisabled => !resolver.genericMetadataIsEnabled;
@override
bool get _needsTypeArgumentBoundsCheck => true;
@override
TypeArgumentListImpl? get _typeArguments => node.typeArguments;
@override
ErrorCode get _wrongNumberOfTypeArgumentsErrorCode =>
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS;
@override
List<ParameterElement>? _storeResult(
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 that require full downward and upward inference.
abstract class FullInvocationInferrer<Node extends AstNodeImpl>
extends InvocationInferrer<Node> {
FullInvocationInferrer._(
{required ResolverVisitor resolver,
required Node node,
required ArgumentListImpl argumentList,
required FunctionType? rawType,
required DartType? contextType,
required List<WhyNotPromotedGetter> whyNotPromotedList})
: super(
resolver: resolver,
node: node,
argumentList: argumentList,
rawType: rawType,
contextType: contextType,
whyNotPromotedList: whyNotPromotedList);
AstNode get _errorNode => node;
bool get _isConst => false;
bool get _isGenericInferenceDisabled => false;
bool get _needsTypeArgumentBoundsCheck => false;
TypeArgumentListImpl? get _typeArguments;
ErrorCode get _wrongNumberOfTypeArgumentsErrorCode =>
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD;
@override
DartType resolveInvocation() {
var typeArgumentList = _typeArguments;
var rawType = this.rawType;
List<DartType>? typeArgumentTypes;
GenericInferrer? inferrer;
Substitution? substitution;
if (_isGenericInferenceDisabled) {
if (rawType != null && rawType.typeFormals.isNotEmpty) {
typeArgumentTypes = List.filled(
rawType.typeFormals.length,
DynamicTypeImpl.instance,
);
substitution =
Substitution.fromPairs(rawType.typeFormals, typeArgumentTypes);
} else {
typeArgumentTypes = const <DartType>[];
}
} else if (typeArgumentList != null) {
if (rawType != null &&
typeArgumentList.arguments.length != rawType.typeFormals.length) {
var typeParameters = rawType.typeFormals;
_reportWrongNumberOfTypeArguments(
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],
);
}
}
}
}
}
if (rawType != null) {
substitution =
Substitution.fromPairs(rawType.typeFormals, typeArgumentTypes);
}
} else if (rawType == null || rawType.typeFormals.isEmpty) {
typeArgumentTypes = const <DartType>[];
} else {
this.rawType = rawType = getFreshTypeParameters(rawType.typeFormals)
.applyToFunctionType(rawType);
inferrer = resolver.typeSystem.setupGenericTypeInference(
typeParameters: rawType.typeFormals,
declaredReturnType: rawType.returnType,
contextReturnType: contextType,
isConst: _isConst,
errorReporter: resolver.errorReporter,
errorNode: _errorNode,
genericMetadataIsEnabled: resolver.genericMetadataIsEnabled,
);
substitution =
Substitution.fromPairs(rawType.typeFormals, inferrer.partialInfer());
}
List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo =
_isIdentical ? [] : null;
var deferredClosures = _visitArguments(
identicalInfo: identicalInfo,
substitution: substitution,
inferrer: inferrer);
if (deferredClosures != null) {
for (var stage in _ClosureDependencies(resolver.typeSystem,
deferredClosures, rawType?.typeFormals.toSet() ?? const {})
.planClosureReconciliationStages()) {
if (inferrer != null) {
substitution = Substitution.fromPairs(
rawType!.typeFormals, inferrer.partialInfer());
}
_resolveDeferredClosures(
deferredClosures: stage,
identicalInfo: identicalInfo,
substitution: substitution,
inferrer: inferrer);
}
}
if (inferrer != null) {
typeArgumentTypes = inferrer.upwardsInfer();
}
FunctionType? invokeType = typeArgumentTypes != null
? rawType?.instantiate(typeArgumentTypes)
: rawType;
var parameters = _storeResult(typeArgumentTypes, invokeType);
if (parameters != null) {
argumentList.correspondingStaticParameters =
ResolverVisitor.resolveArgumentsToParameters(
argumentList: argumentList,
parameters: parameters,
errorReporter: resolver.errorReporter,
);
}
var returnType = _refineReturnType(
InvocationInferrer.computeInvokeReturnType(invokeType));
_recordIdenticalInfo(identicalInfo);
return returnType;
}
DartType _refineReturnType(DartType returnType) => returnType;
void _reportWrongNumberOfTypeArguments(TypeArgumentList typeArgumentList,
FunctionType rawType, List<TypeParameterElement> typeParameters) {
resolver.errorReporter.reportErrorForNode(
_wrongNumberOfTypeArgumentsErrorCode,
typeArgumentList,
[
rawType,
typeParameters.length,
typeArgumentList.arguments.length,
],
);
}
List<ParameterElement>? _storeResult(
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> {
FunctionExpressionInvocationInferrer(
{required ResolverVisitor resolver,
required FunctionExpressionInvocationImpl node,
required ArgumentListImpl argumentList,
required FunctionType? rawType,
required DartType? contextType,
required List<WhyNotPromotedGetter> whyNotPromotedList})
: super._(
resolver: resolver,
node: node,
argumentList: argumentList,
rawType: rawType,
contextType: contextType,
whyNotPromotedList: whyNotPromotedList);
@override
ExpressionImpl get _errorNode => node.function;
}
/// Specialization of [InvocationInferrer] for performing type inference on AST
/// nodes of type [InstanceCreationExpression].
class InstanceCreationInferrer
extends FullInvocationInferrer<InstanceCreationExpressionImpl> {
InstanceCreationInferrer(
{required ResolverVisitor resolver,
required InstanceCreationExpressionImpl node,
required ArgumentListImpl argumentList,
required FunctionType? rawType,
required DartType? contextType,
required List<WhyNotPromotedGetter> whyNotPromotedList})
: super._(
resolver: resolver,
node: node,
argumentList: argumentList,
rawType: rawType,
contextType: contextType,
whyNotPromotedList: whyNotPromotedList);
@override
ConstructorNameImpl get _errorNode => node.constructorName;
@override
bool get _isConst => node.isConst;
@override
bool get _needsTypeArgumentBoundsCheck => true;
@override
TypeArgumentListImpl? get _typeArguments {
// For an instance creation expression the type arguments are on the
// constructor name.
return node.constructorName.type.typeArguments;
}
@override
void _reportWrongNumberOfTypeArguments(TypeArgumentList typeArgumentList,
FunctionType rawType, List<TypeParameterElement> typeParameters) {
// Error reporting for instance creations is done elsewhere.
}
@override
List<ParameterElement>? _storeResult(
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> {
InvocationExpressionInferrer._(
{required ResolverVisitor resolver,
required Node node,
required ArgumentListImpl argumentList,
required FunctionType? rawType,
required DartType? contextType,
required List<WhyNotPromotedGetter> whyNotPromotedList})
: super._(
resolver: resolver,
node: node,
argumentList: argumentList,
rawType: rawType,
contextType: contextType,
whyNotPromotedList: whyNotPromotedList);
@override
Expression get _errorNode => node.function;
@override
TypeArgumentListImpl? get _typeArguments => node.typeArguments;
@override
List<ParameterElement>? _storeResult(
List<DartType>? typeArgumentTypes, FunctionType? invokeType) {
node.typeArgumentTypes = typeArgumentTypes;
node.staticInvokeType = invokeType ?? DynamicTypeImpl.instance;
return super._storeResult(typeArgumentTypes, invokeType);
}
}
/// Base class containing functionality for performing type inference on AST
/// nodes that invoke a method, function, or constructor.
///
/// This class may be used directly for inference of [ExtensionOverride],
/// [RedirectingConstructorInvocation], or [SuperConstructorInvocation].
class InvocationInferrer<Node extends AstNodeImpl> {
final ResolverVisitor resolver;
final Node node;
final ArgumentListImpl argumentList;
FunctionType? rawType;
final DartType? contextType;
final List<WhyNotPromotedGetter> whyNotPromotedList;
/// Prepares to perform 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).
InvocationInferrer(
{required this.resolver,
required this.node,
required this.argumentList,
required this.rawType,
required this.contextType,
required this.whyNotPromotedList});
/// Determines whether [node] is an invocation of the core function
/// `identical` (which needs special flow analysis treatment).
bool get _isIdentical => false;
/// Performs type inference on the invocation expression.
void resolveInvocation() {
var deferredClosures = _visitArguments();
if (deferredClosures != null) {
_resolveDeferredClosures(deferredClosures: deferredClosures);
}
}
/// 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(DartType parameterType) => parameterType;
/// If the invocation being processed is a call to `identical`, informs flow
/// analysis about it, so that it can do appropriate promotions.
void _recordIdenticalInfo(
List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo) {
var flow = resolver.flowAnalysis.flow;
if (identicalInfo != null) {
flow?.equalityOperation_end(argumentList.parent as Expression,
identicalInfo[0], identicalInfo[1]);
}
}
/// Resolves any closures that were deferred by [_visitArguments].
void _resolveDeferredClosures(
{required Iterable<_DeferredClosure> deferredClosures,
List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo,
Substitution? substitution,
GenericInferrer? inferrer}) {
var flow = resolver.flowAnalysis.flow;
var arguments = argumentList.arguments;
for (var deferredArgument in deferredClosures) {
var parameter = deferredArgument.parameter;
DartType? parameterContextType;
if (parameter != null) {
var parameterType = parameter.type;
if (substitution != null) {
parameterType = substitution.substituteType(parameterType);
}
parameterContextType = _computeContextForArgument(parameterType);
}
var argument = arguments[deferredArgument.index];
resolver.analyzeExpression(argument, parameterContextType);
// In case of rewrites, we need to grab the argument again.
argument = arguments[deferredArgument.index];
if (flow != null) {
identicalInfo?[deferredArgument.index] =
flow.equalityOperand_end(argument, argument.typeOrThrow);
}
if (parameter != null) {
inferrer?.constrainArgument(
argument.typeOrThrow, parameter.type, parameter.name);
}
}
}
/// Visits [argumentList], resolving each argument. If any arguments need to
/// be deferred due to the `inference-update-1` feature, a list of them is
/// returned.
List<_DeferredClosure>? _visitArguments(
{List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo,
Substitution? substitution,
GenericInferrer? inferrer}) {
assert(whyNotPromotedList.isEmpty);
List<_DeferredClosure>? deferredClosures;
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;
}
}
}
resolver.checkUnreachableNode(argumentList);
var flow = resolver.flowAnalysis.flow;
var positionalParameterIndex = 0;
var arguments = argumentList.arguments;
for (int i = 0; i < arguments.length; i++) {
var argument = arguments[i];
Expression value;
ParameterElement? parameter;
if (argument is NamedExpression) {
value = argument.expression;
parameter = namedParameters[argument.name.label.name];
} else {
value = argument;
if (parameters != null) {
while (positionalParameterIndex < parameters.length) {
var candidate = parameters[positionalParameterIndex++];
if (!candidate.isNamed) {
parameter = candidate;
break;
}
}
}
}
if (resolver.isInferenceUpdate1Enabled &&
value is FunctionExpressionImpl) {
(deferredClosures ??= []).add(_DeferredClosure(parameter, value, i));
identicalInfo?.add(null);
// The "why not promoted" list isn't really relevant for closures
// because promoting a closure doesn't even make sense. So we store an
// innocuous value in the list.
whyNotPromotedList.add(() => const {});
} else {
DartType? parameterContextType;
if (parameter != null) {
var parameterType = parameter.type;
if (substitution != null) {
parameterType = substitution.substituteType(parameterType);
}
parameterContextType = _computeContextForArgument(parameterType);
}
resolver.analyzeExpression(argument, parameterContextType);
// In case of rewrites, we need to grab the argument again.
argument = arguments[i];
if (flow != null) {
identicalInfo
?.add(flow.equalityOperand_end(argument, argument.typeOrThrow));
whyNotPromotedList.add(flow.whyNotPromoted(argument));
}
if (parameter != null) {
inferrer?.constrainArgument(
argument.typeOrThrow, parameter.type, parameter.name);
}
}
}
return deferredClosures;
}
/// 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> {
MethodInvocationInferrer(
{required ResolverVisitor resolver,
required MethodInvocationImpl node,
required ArgumentListImpl argumentList,
required FunctionType? rawType,
required DartType? contextType,
required List<WhyNotPromotedGetter> whyNotPromotedList})
: super._(
resolver: resolver,
node: node,
argumentList: argumentList,
rawType: rawType,
contextType: contextType,
whyNotPromotedList: whyNotPromotedList);
@override
bool get _isIdentical {
var invokedMethod = node.methodName.staticElement;
return invokedMethod is FunctionElement &&
invokedMethod.isDartCoreIdentical &&
node.argumentList.arguments.length == 2;
}
@override
DartType? _computeContextForArgument(DartType parameterType) {
var argumentContextType = super._computeContextForArgument(parameterType);
var targetType = node.realTarget?.staticType;
if (targetType != null) {
argumentContextType = resolver.typeSystem.refineNumericInvocationContext(
targetType,
node.methodName.staticElement,
contextType,
parameterType);
}
return argumentContextType;
}
@override
DartType _refineReturnType(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;
}
}
class _ClosureDependencies
extends ClosureDependencies<TypeParameterElement, _DeferredClosure> {
final TypeSystemImpl _typeSystem;
final Set<TypeParameterElement> _typeVariables;
_ClosureDependencies(this._typeSystem, Iterable<_DeferredClosure> closures,
this._typeVariables)
: super(closures, _typeVariables);
@override
Iterable<TypeParameterElement> typeVarsFreeInClosureArguments(
_DeferredClosure closure) {
var type = closure.parameter?.type;
if (type is FunctionType) {
Set<TypeParameterElement> result = {};
for (var parameter in type.parameters) {
result.addAll(_typeSystem.getFreeParameters(parameter.type,
candidates: _typeVariables) ??
const []);
}
return result;
} else {
return const [];
}
}
@override
Iterable<TypeParameterElement> typeVarsFreeInClosureReturns(
_DeferredClosure closure) {
var type = closure.parameter?.type;
if (type is FunctionType) {
return _typeSystem.getFreeParameters(type.returnType,
candidates: _typeVariables) ??
const [];
} else if (type != null) {
return _typeSystem.getFreeParameters(type, candidates: _typeVariables) ??
const [];
} else {
return const [];
}
}
}
/// Information about an invocation argument that needs to be resolved later due
/// to the fact that it's a closure and the `inference-update-1` feature is
/// enabled.
class _DeferredClosure {
/// The [ParameterElement] the closure is being passed to.
final ParameterElement? parameter;
/// The closure expression.
final FunctionExpression value;
/// The index into the argument list of the closure expression.
final int index;
_DeferredClosure(this.parameter, this.value, this.index);
}