| // 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) { |
| var candidate = parameters[positionalParameterIndex++]; |
| if (!candidate.isNamed) { |
| parameter = candidate; |
| 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); |
| } |
| } |