| // 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/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/element/type.dart'; |
| import 'package:analyzer/src/dart/element/type_algebra.dart'; |
| import 'package:analyzer/src/dart/element/type_provider.dart'; |
| import 'package:analyzer/src/dart/element/type_system.dart'; |
| import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart'; |
| import 'package:analyzer/src/error/codes.dart'; |
| import 'package:analyzer/src/generated/migration.dart'; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| import 'package:meta/meta.dart'; |
| |
| class InvocationInferenceHelper { |
| final ResolverVisitor _resolver; |
| final ErrorReporter _errorReporter; |
| final FlowAnalysisHelper _flowAnalysis; |
| final TypeSystemImpl _typeSystem; |
| final TypeProviderImpl _typeProvider; |
| final MigrationResolutionHooks _migrationResolutionHooks; |
| |
| List<DartType> _typeArgumentTypes; |
| FunctionType _invokeType; |
| |
| InvocationInferenceHelper( |
| {@required ResolverVisitor resolver, |
| @required ErrorReporter errorReporter, |
| @required FlowAnalysisHelper flowAnalysis, |
| @required TypeSystemImpl typeSystem, |
| @required MigrationResolutionHooks migrationResolutionHooks}) |
| : _resolver = resolver, |
| _errorReporter = errorReporter, |
| _typeSystem = typeSystem, |
| _typeProvider = typeSystem.typeProvider, |
| _flowAnalysis = flowAnalysis, |
| _migrationResolutionHooks = migrationResolutionHooks; |
| |
| /// 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, |
| ); |
| if (typeArguments != null) { |
| return uninstantiatedType.instantiate(typeArguments); |
| } |
| } |
| return null; |
| } |
| |
| void inferArgumentTypesForInvocation( |
| InvocationExpression node, |
| DartType type, |
| ) { |
| DartType 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( |
| InvocationExpression node, |
| DartType type, |
| ) { |
| ArgumentList arguments = node.argumentList; |
| var freshType = _getFreshType(type); |
| |
| FunctionType 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. |
| List<DartType> 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.type).toList(); |
| node.typeArgumentTypes = typeArgs; |
| } else { |
| node.typeArgumentTypes = const <DartType>[]; |
| } |
| } |
| |
| return null; |
| } |
| |
| /// Given a method invocation [node], attempt to infer a better |
| /// type for the result if the target is dynamic and the method |
| /// being called is one of the object methods. |
| bool inferMethodInvocationObject(MethodInvocation node) { |
| // If we have a call like `toString()` or `libraryPrefix.toString()`, don't |
| // infer it. |
| Expression target = node.realTarget; |
| if (target == null || |
| target is SimpleIdentifier && target.staticElement is PrefixElement) { |
| return false; |
| } |
| DartType nodeType = node.staticInvokeType; |
| if (nodeType == null || |
| !nodeType.isDynamic || |
| node.argumentList.arguments.isNotEmpty) { |
| return false; |
| } |
| // Object methods called on dynamic targets can have their types improved. |
| String name = node.methodName.name; |
| MethodElement inferredElement = |
| _typeProvider.objectType.element.getMethod(name); |
| if (inferredElement == null || inferredElement.isStatic) { |
| return false; |
| } |
| inferredElement = _resolver.toLegacyElement(inferredElement); |
| DartType inferredType = inferredElement.type; |
| if (inferredType is FunctionType) { |
| DartType returnType = inferredType.returnType; |
| if (inferredType.parameters.isEmpty && |
| returnType is InterfaceType && |
| _typeProvider.nonSubtypableClasses.contains(returnType.element)) { |
| node.staticInvokeType = inferredType; |
| recordStaticType(node, inferredType.returnType); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /// 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(Expression expression, DartType type) { |
| if (_migrationResolutionHooks != null) { |
| // TODO(scheglov) type cannot be null |
| type = _migrationResolutionHooks.modifyExpressionType( |
| expression, |
| type ?? DynamicTypeImpl.instance, |
| ); |
| } |
| |
| // TODO(scheglov) type cannot be null |
| if (type == null) { |
| expression.staticType = DynamicTypeImpl.instance; |
| } else { |
| expression.staticType = type; |
| if (_typeSystem.isBottom(type)) { |
| _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); |
| 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, |
| ); |
| } |
| |
| /// 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); |
| |
| // 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].staticType); |
| } |
| } |
| var typeArgs = _typeSystem.inferGenericFunctionOrType( |
| typeParameters: rawType.typeFormals, |
| parameters: params, |
| declaredReturnType: rawType.returnType, |
| argumentTypes: argTypes, |
| contextReturnType: contextType, |
| isConst: isConst, |
| errorReporter: _errorReporter, |
| errorNode: errorNode, |
| ); |
| return typeArgs; |
| } |
| |
| void _resolveArguments(ArgumentList argumentList) { |
| argumentList.accept(_resolver); |
| } |
| |
| void _resolveInvocation({ |
| @required FunctionType rawType, |
| @required DartType contextType, |
| @required TypeArgumentList typeArgumentList, |
| @required ArgumentList 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); |
| |
| List<DartType> 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({ |
| FunctionType rawType, |
| TypeArgumentList typeArgumentList, |
| 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.type) |
| .toList(growable: true); |
| } |
| |
| var invokeType = rawType.instantiate(typeArguments); |
| InferenceContext.setType(argumentList, invokeType); |
| |
| _resolveArguments(argumentList); |
| |
| _typeArgumentTypes = typeArguments; |
| _invokeType = invokeType; |
| } |
| |
| void _setCorrespondingParameters( |
| ArgumentList 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; |
| } |
| } |
| } |