| // Copyright (c) 2018, 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/ast/token.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/element/inheritance_manager3.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/resolution_result.dart'; |
| import 'package:analyzer/src/dart/resolver/scope.dart'; |
| import 'package:analyzer/src/error/codes.dart'; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| import 'package:analyzer/src/generated/super_context.dart'; |
| import 'package:analyzer/src/generated/variable_type_provider.dart'; |
| |
| class MethodInvocationResolver { |
| static final _nameCall = new Name(null, 'call'); |
| |
| /// The resolver driving this participant. |
| final ResolverVisitor _resolver; |
| |
| /// The type representing the type 'dynamic'. |
| final DynamicTypeImpl _dynamicType = DynamicTypeImpl.instance; |
| |
| /// The type representing the type 'type'. |
| final InterfaceType _typeType; |
| |
| /// The manager for the inheritance mappings. |
| final InheritanceManager3 _inheritance; |
| |
| /// The element for the library containing the compilation unit being visited. |
| final LibraryElement _definingLibrary; |
| |
| /// The URI of [_definingLibrary]. |
| final Uri _definingLibraryUri; |
| |
| /// The object providing promoted or declared types of variables. |
| final LocalVariableTypeProvider _localVariableTypeProvider; |
| |
| /// Helper for extension method resolution. |
| final ExtensionMemberResolver _extensionResolver; |
| |
| /// The invocation being resolved. |
| MethodInvocationImpl _invocation; |
| |
| /// The [Name] object of the invocation being resolved by [resolve]. |
| Name _currentName; |
| |
| MethodInvocationResolver(this._resolver) |
| : _typeType = _resolver.typeProvider.typeType, |
| _inheritance = _resolver.inheritance, |
| _definingLibrary = _resolver.definingLibrary, |
| _definingLibraryUri = _resolver.definingLibrary.source.uri, |
| _localVariableTypeProvider = _resolver.localVariableTypeProvider, |
| _extensionResolver = _resolver.extensionResolver; |
| |
| /// The scope used to resolve identifiers. |
| Scope get nameScope => _resolver.nameScope; |
| |
| void resolve(MethodInvocation node) { |
| _invocation = node; |
| |
| SimpleIdentifier nameNode = node.methodName; |
| String name = nameNode.name; |
| _currentName = Name(_definingLibraryUri, name); |
| |
| // |
| // Synthetic identifiers have been already reported during parsing. |
| // |
| if (nameNode.isSynthetic) { |
| return; |
| } |
| |
| Expression receiver = node.realTarget; |
| |
| if (receiver == null) { |
| _resolveReceiverNull(node, nameNode, name); |
| return; |
| } |
| |
| if (receiver is NullLiteral) { |
| _setDynamicResolution(node); |
| return; |
| } |
| |
| if (receiver is SimpleIdentifier) { |
| var receiverElement = receiver.staticElement; |
| if (receiverElement is PrefixElement) { |
| _resolveReceiverPrefix(node, receiver, receiverElement, nameNode, name); |
| return; |
| } |
| } |
| |
| if (receiver is Identifier) { |
| var receiverElement = receiver.staticElement; |
| if (receiverElement is ExtensionElement) { |
| _resolveExtensionMember( |
| node, receiver, receiverElement, nameNode, name); |
| return; |
| } |
| } |
| |
| if (receiver is SuperExpression) { |
| _resolveReceiverSuper(node, receiver, nameNode, name); |
| return; |
| } |
| |
| if (receiver is ExtensionOverride) { |
| _resolveExtensionOverride(node, receiver, nameNode, name); |
| return; |
| } |
| |
| ClassElement typeReference = getTypeReference(receiver); |
| if (typeReference != null) { |
| _resolveReceiverTypeLiteral(node, typeReference, nameNode, name); |
| return; |
| } |
| |
| DartType receiverType = receiver.staticType; |
| receiverType = _resolveTypeParameter(receiverType); |
| |
| if (receiverType is InterfaceType) { |
| _resolveReceiverInterfaceType(node, receiverType, nameNode, name); |
| return; |
| } |
| |
| if (receiverType is DynamicTypeImpl) { |
| _resolveReceiverDynamic(node, name); |
| return; |
| } |
| |
| if (receiverType is FunctionType) { |
| _resolveReceiverFunctionType( |
| node, receiver, receiverType, nameNode, name); |
| return; |
| } |
| |
| if (receiverType is VoidType) { |
| _reportUseOfVoidType(node, receiver); |
| return; |
| } |
| |
| if (receiverType == BottomTypeImpl.instance) { |
| _reportUseOfNeverType(node, receiver); |
| return; |
| } |
| } |
| |
| /// Given an [argumentList] and the executable [element] that will be invoked |
| /// using those arguments, compute the list of parameters that correspond to |
| /// the list of arguments. Return the parameters that correspond to the |
| /// arguments, or `null` if no correspondence could be computed. |
| List<ParameterElement> _computeCorrespondingParameters( |
| ArgumentList argumentList, DartType type) { |
| if (type is InterfaceType) { |
| MethodElement callMethod = |
| type.lookUpMethod(FunctionElement.CALL_METHOD_NAME, _definingLibrary); |
| if (callMethod != null) { |
| return _resolveArgumentsToFunction(false, argumentList, callMethod); |
| } |
| } else if (type is FunctionType) { |
| return _resolveArgumentsToParameters( |
| false, argumentList, type.parameters); |
| } |
| return null; |
| } |
| |
| /// If the invoked [target] is a getter, then actually the return type of |
| /// the [target] is invoked. So, remember the [target] into |
| /// [MethodInvocationImpl.methodNameType] and return the actual invoked type. |
| DartType _getCalleeType(MethodInvocation node, ExecutableElement target) { |
| if (target.kind == ElementKind.GETTER) { |
| (node as MethodInvocationImpl).methodNameType = target.type; |
| var calleeType = target.returnType; |
| calleeType = _resolveTypeParameter(calleeType); |
| return calleeType; |
| } |
| return target.type; |
| } |
| |
| /// Check for a generic type, and apply type arguments. |
| FunctionType _instantiateFunctionType( |
| FunctionType invokeType, TypeArgumentList typeArguments, AstNode node) { |
| var typeFormals = invokeType.typeFormals; |
| var arguments = typeArguments?.arguments; |
| if (arguments != null && arguments.length != typeFormals.length) { |
| _resolver.errorReporter.reportErrorForNode( |
| StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD, |
| node, |
| [invokeType, typeFormals.length, arguments?.length ?? 0]); |
| arguments = null; |
| } |
| |
| if (typeFormals.isNotEmpty) { |
| if (arguments == null) { |
| var typeArguments = |
| _resolver.typeSystem.instantiateTypeFormalsToBounds(typeFormals); |
| _invocation.typeArgumentTypes = typeArguments; |
| return invokeType.instantiate(typeArguments); |
| } else { |
| var typeArguments = arguments.map((n) => n.type).toList(); |
| _invocation.typeArgumentTypes = typeArguments; |
| return invokeType.instantiate(typeArguments); |
| } |
| } else { |
| _invocation.typeArgumentTypes = const <DartType>[]; |
| } |
| |
| return invokeType; |
| } |
| |
| bool _isCoreFunction(DartType type) { |
| // TODO(scheglov) Can we optimize this? |
| return type is InterfaceType && type.isDartCoreFunction; |
| } |
| |
| ExecutableElement _lookUpClassMember(ClassElement element, String name) { |
| // TODO(scheglov) Use class hierarchy. |
| return element.lookUpMethod(name, _definingLibrary); |
| } |
| |
| void _reportInvocationOfNonFunction(MethodInvocation node) { |
| _setDynamicResolution(node, setNameTypeToDynamic: false); |
| _resolver.errorReporter.reportErrorForNode( |
| StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION, |
| node.methodName, |
| [node.methodName.name], |
| ); |
| } |
| |
| void _reportPrefixIdentifierNotFollowedByDot(SimpleIdentifier target) { |
| _resolver.errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT, |
| target, |
| [target.name], |
| ); |
| } |
| |
| void _reportUndefinedFunction( |
| MethodInvocation node, Identifier ignorableIdentifier) { |
| _setDynamicResolution(node); |
| |
| // TODO(scheglov) This is duplication. |
| if (nameScope.shouldIgnoreUndefined(ignorableIdentifier)) { |
| return; |
| } |
| |
| _resolver.errorReporter.reportErrorForNode( |
| StaticTypeWarningCode.UNDEFINED_FUNCTION, |
| node.methodName, |
| [node.methodName.name], |
| ); |
| } |
| |
| void _reportUndefinedMethod( |
| MethodInvocation node, String name, ClassElement typeReference) { |
| _setDynamicResolution(node); |
| _resolver.errorReporter.reportErrorForNode( |
| StaticTypeWarningCode.UNDEFINED_METHOD, |
| node.methodName, |
| [name, typeReference.displayName], |
| ); |
| } |
| |
| void _reportUseOfNeverType(MethodInvocation node, AstNode errorNode) { |
| _setDynamicResolution(node); |
| _resolver.errorReporter.reportErrorForNode( |
| StaticWarningCode.INVALID_USE_OF_NEVER_VALUE, |
| errorNode, |
| ); |
| } |
| |
| void _reportUseOfVoidType(MethodInvocation node, AstNode errorNode) { |
| _setDynamicResolution(node); |
| _resolver.errorReporter.reportErrorForNode( |
| StaticWarningCode.USE_OF_VOID_RESULT, |
| errorNode, |
| ); |
| } |
| |
| /// Given an [argumentList] and the [executableElement] that will be invoked |
| /// using those argument, compute the list of parameters that correspond to the |
| /// list of arguments. An error will be reported if any of the arguments cannot |
| /// be matched to a parameter. The flag [reportAsError] should be `true` if a |
| /// compile-time error should be reported; or `false` if a compile-time warning |
| /// should be reported. Return the parameters that correspond to the arguments, |
| /// or `null` if no correspondence could be computed. |
| List<ParameterElement> _resolveArgumentsToFunction(bool reportAsError, |
| ArgumentList argumentList, ExecutableElement executableElement) { |
| if (executableElement == null) { |
| return null; |
| } |
| List<ParameterElement> parameters = executableElement.parameters; |
| return _resolveArgumentsToParameters( |
| reportAsError, argumentList, parameters); |
| } |
| |
| /// Given an [argumentList] and the [parameters] related to the element that |
| /// will be invoked using those arguments, compute the list of parameters that |
| /// correspond to the list of arguments. An error will be reported if any of |
| /// the arguments cannot be matched to a parameter. The flag [reportAsError] |
| /// should be `true` if a compile-time error should be reported; or `false` if |
| /// a compile-time warning should be reported. Return the parameters that |
| /// correspond to the arguments. |
| List<ParameterElement> _resolveArgumentsToParameters(bool reportAsError, |
| ArgumentList argumentList, List<ParameterElement> parameters) { |
| return ResolverVisitor.resolveArgumentsToParameters( |
| argumentList, parameters, _resolver.errorReporter.reportErrorForNode, |
| reportAsError: reportAsError); |
| } |
| |
| /// Given that we are accessing a property of the given [classElement] with the |
| /// given [propertyName], return the element that represents the property. |
| Element _resolveElement( |
| ClassElement classElement, SimpleIdentifier propertyName) { |
| // TODO(scheglov) Replace with class hierarchy. |
| String name = propertyName.name; |
| Element element = null; |
| if (propertyName.inSetterContext()) { |
| element = classElement.getSetter(name); |
| } |
| if (element == null) { |
| element = classElement.getGetter(name); |
| } |
| if (element == null) { |
| element = classElement.getMethod(name); |
| } |
| if (element != null && element.isAccessibleIn(_definingLibrary)) { |
| return element; |
| } |
| return null; |
| } |
| |
| /// If there is an extension matching the [receiverType] and defining a |
| /// member with the given [name], resolve to the corresponding extension |
| /// method. Return a result indicating whether the [node] was resolved and if |
| /// not why. |
| ResolutionResult _resolveExtension( |
| MethodInvocation node, |
| DartType receiverType, |
| SimpleIdentifier nameNode, |
| String name, |
| ) { |
| var result = _extensionResolver.findExtension( |
| receiverType, |
| name, |
| nameNode, |
| ElementKind.METHOD, |
| ); |
| |
| if (!result.isSingle) { |
| _setDynamicResolution(node); |
| return result; |
| } |
| |
| ExecutableElement member = result.element; |
| nameNode.staticElement = member; |
| |
| if (member.isStatic) { |
| _setDynamicResolution(node); |
| _resolver.errorReporter.reportErrorForNode( |
| StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, |
| nameNode, |
| [name, member.kind.displayName, member.enclosingElement.name]); |
| return result; |
| } |
| |
| var calleeType = _getCalleeType(node, member); |
| _setResolution(node, calleeType); |
| return result; |
| } |
| |
| void _resolveExtensionMember(MethodInvocation node, Identifier receiver, |
| ExtensionElement extension, SimpleIdentifier nameNode, String name) { |
| ExecutableElement element = |
| extension.getMethod(name) ?? extension.getGetter(name); |
| if (element is ExecutableElement) { |
| if (!element.isStatic) { |
| _resolver.errorReporter.reportErrorForNode( |
| StaticWarningCode.STATIC_ACCESS_TO_INSTANCE_MEMBER, |
| nameNode, |
| [name]); |
| } |
| nameNode.staticElement = element; |
| _setResolution(node, _getCalleeType(node, element)); |
| } else { |
| _reportUndefinedFunction(node, receiver); |
| } |
| } |
| |
| void _resolveExtensionOverride(MethodInvocation node, |
| ExtensionOverride override, SimpleIdentifier nameNode, String name) { |
| var member = _extensionResolver.getOverrideMember( |
| override, name, ElementKind.METHOD) ?? |
| _extensionResolver.getOverrideMember( |
| override, name, ElementKind.GETTER); |
| |
| if (member == null) { |
| _setDynamicResolution(node); |
| _resolver.errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.UNDEFINED_EXTENSION_METHOD, |
| nameNode, |
| [name, override.staticElement.name], |
| ); |
| return; |
| } |
| |
| if (member.isStatic) { |
| _resolver.errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.EXTENSION_OVERRIDE_ACCESS_TO_STATIC_MEMBER, |
| nameNode, |
| ); |
| } |
| |
| if (node.isCascaded) { |
| // TODO(brianwilkerson) Report this error and decide how to recover. |
| throw new UnsupportedError('cascaded extension override'); |
| } |
| |
| nameNode.staticElement = member; |
| var calleeType = _getCalleeType(node, member); |
| _setResolution(node, calleeType); |
| } |
| |
| void _resolveReceiverDynamic(MethodInvocation node, String name) { |
| _setDynamicResolution(node); |
| } |
| |
| void _resolveReceiverFunctionType(MethodInvocation node, Expression receiver, |
| FunctionType receiverType, SimpleIdentifier nameNode, String name) { |
| if (name == FunctionElement.CALL_METHOD_NAME) { |
| _setResolution(node, receiverType); |
| // TODO(scheglov) Replace this with using FunctionType directly. |
| // Here was erase resolution that _setResolution() sets. |
| nameNode.staticElement = null; |
| nameNode.staticType = _dynamicType; |
| return; |
| } |
| |
| ResolutionResult result = _extensionResolver.findExtension( |
| receiverType, name, nameNode, ElementKind.METHOD); |
| if (result.isSingle) { |
| nameNode.staticElement = result.element; |
| var calleeType = _getCalleeType(node, result.element); |
| return _setResolution(node, calleeType); |
| } else if (result.isAmbiguous) { |
| return; |
| } |
| // We can invoke Object methods on Function. |
| var member = _inheritance.getMember( |
| _resolver.typeProvider.functionType, |
| new Name(null, name), |
| ); |
| if (member != null) { |
| nameNode.staticElement = member; |
| return _setResolution(node, member.type); |
| } |
| |
| _reportUndefinedMethod( |
| node, |
| name, |
| _resolver.typeProvider.functionType.element, |
| ); |
| } |
| |
| void _resolveReceiverInterfaceType(MethodInvocation node, |
| InterfaceType receiverType, SimpleIdentifier nameNode, String name) { |
| if (_isCoreFunction(receiverType) && |
| name == FunctionElement.CALL_METHOD_NAME) { |
| _setDynamicResolution(node); |
| return; |
| } |
| |
| var target = _inheritance.getMember(receiverType, _currentName); |
| if (target != null) { |
| nameNode.staticElement = target; |
| var calleeType = _getCalleeType(node, target); |
| return _setResolution(node, calleeType); |
| } |
| |
| // Look for an applicable extension. |
| var result = _resolveExtension(node, receiverType, nameNode, name); |
| if (result.isSingle) { |
| return; |
| } |
| |
| // The interface of the receiver does not have an instance member. |
| // Try to recover and find a member in the class. |
| var targetElement = _lookUpClassMember(receiverType.element, name); |
| if (targetElement != null && targetElement.isStatic) { |
| nameNode.staticElement = targetElement; |
| _setDynamicResolution(node); |
| _resolver.errorReporter.reportErrorForNode( |
| StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, |
| nameNode, |
| [ |
| name, |
| targetElement.kind.displayName, |
| targetElement.enclosingElement.displayName, |
| ], |
| ); |
| return; |
| } |
| |
| _setDynamicResolution(node); |
| if (result.isNone) { |
| _resolver.errorReporter.reportErrorForNode( |
| StaticTypeWarningCode.UNDEFINED_METHOD, |
| nameNode, |
| [name, receiverType.element.displayName], |
| ); |
| } |
| } |
| |
| void _resolveReceiverNull( |
| MethodInvocation node, SimpleIdentifier nameNode, String name) { |
| var element = nameScope.lookup(nameNode, _definingLibrary); |
| if (element != null) { |
| nameNode.staticElement = element; |
| if (element is MultiplyDefinedElement) { |
| MultiplyDefinedElement multiply = element; |
| element = multiply.conflictingElements[0]; |
| } |
| if (element is ExecutableElement) { |
| var calleeType = _getCalleeType(node, element); |
| return _setResolution(node, calleeType); |
| } |
| if (element is VariableElement) { |
| var targetType = _localVariableTypeProvider.getType(nameNode); |
| return _setResolution(node, targetType); |
| } |
| // TODO(scheglov) This is a questionable distinction. |
| if (element is PrefixElement) { |
| _setDynamicResolution(node); |
| return _reportPrefixIdentifierNotFollowedByDot(nameNode); |
| } |
| return _reportInvocationOfNonFunction(node); |
| } |
| |
| InterfaceType receiverType; |
| ClassElement enclosingClass = _resolver.enclosingClass; |
| if (enclosingClass == null) { |
| if (_resolver.enclosingExtension == null) { |
| return _reportUndefinedFunction(node, node.methodName); |
| } |
| var extendedType = |
| _resolveTypeParameter(_resolver.enclosingExtension.extendedType); |
| if (extendedType is InterfaceType) { |
| receiverType = extendedType; |
| } else if (extendedType is FunctionType) { |
| receiverType = _resolver.typeProvider.functionType; |
| } else { |
| return _reportUndefinedFunction(node, node.methodName); |
| } |
| enclosingClass = receiverType.element; |
| } else { |
| receiverType = enclosingClass.type; |
| } |
| var target = _inheritance.getMember(receiverType, _currentName); |
| |
| if (target != null) { |
| nameNode.staticElement = target; |
| var calleeType = _getCalleeType(node, target); |
| return _setResolution(node, calleeType); |
| } |
| |
| var targetElement = _lookUpClassMember(enclosingClass, name); |
| if (targetElement != null && targetElement.isStatic) { |
| nameNode.staticElement = targetElement; |
| _setDynamicResolution(node); |
| _resolver.errorReporter.reportErrorForNode( |
| StaticTypeWarningCode.UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER, |
| nameNode, |
| [receiverType.displayName], |
| ); |
| return; |
| } |
| |
| var result = _extensionResolver.findExtension( |
| receiverType, name, nameNode, ElementKind.METHOD); |
| if (result.isSingle) { |
| var target = result.element; |
| if (target != null) { |
| nameNode.staticElement = target; |
| var calleeType = _getCalleeType(node, target); |
| _setResolution(node, calleeType); |
| return; |
| } |
| } |
| |
| return _reportUndefinedMethod(node, name, enclosingClass); |
| } |
| |
| void _resolveReceiverPrefix(MethodInvocation node, SimpleIdentifier receiver, |
| PrefixElement prefix, SimpleIdentifier nameNode, String name) { |
| if (node.operator.type == TokenType.QUESTION_PERIOD) { |
| _reportPrefixIdentifierNotFollowedByDot(receiver); |
| } |
| |
| if (name == FunctionElement.LOAD_LIBRARY_NAME) { |
| var imports = _definingLibrary.getImportsWithPrefix(prefix); |
| if (imports.length == 1 && imports[0].isDeferred) { |
| var importedLibrary = imports[0].importedLibrary; |
| var loadLibraryFunction = importedLibrary?.loadLibraryFunction; |
| nameNode.staticElement = loadLibraryFunction; |
| node.staticInvokeType = loadLibraryFunction?.type; |
| node.staticType = loadLibraryFunction?.returnType; |
| _setExplicitTypeArgumentTypes(); |
| return; |
| } |
| } |
| |
| // TODO(scheglov) I don't like how we resolve prefixed names. |
| // But maybe this is the only one solution. |
| var prefixedName = new PrefixedIdentifierImpl.temp(receiver, nameNode); |
| var element = nameScope.lookup(prefixedName, _definingLibrary); |
| nameNode.staticElement = element; |
| |
| if (element is MultiplyDefinedElement) { |
| MultiplyDefinedElement multiply = element; |
| element = multiply.conflictingElements[0]; |
| } |
| |
| if (element is ExecutableElement) { |
| var calleeType = _getCalleeType(node, element); |
| return _setResolution(node, calleeType); |
| } |
| |
| _reportUndefinedFunction(node, prefixedName); |
| } |
| |
| void _resolveReceiverSuper(MethodInvocation node, SuperExpression receiver, |
| SimpleIdentifier nameNode, String name) { |
| var enclosingClass = _resolver.enclosingClass; |
| if (SuperContext.of(receiver) != SuperContext.valid) { |
| return; |
| } |
| |
| var receiverType = enclosingClass.type; |
| var target = _inheritance.getMember( |
| receiverType, |
| _currentName, |
| forSuper: true, |
| ); |
| |
| // If there is that concrete dispatch target, then we are done. |
| if (target != null) { |
| nameNode.staticElement = target; |
| var calleeType = _getCalleeType(node, target); |
| _setResolution(node, calleeType); |
| return; |
| } |
| |
| // Otherwise, this is an error. |
| // But we would like to give the user at least some resolution. |
| // So, we try to find the interface target. |
| target = _inheritance.getInherited(receiverType, _currentName); |
| if (target != null) { |
| nameNode.staticElement = target; |
| var calleeType = _getCalleeType(node, target); |
| _setResolution(node, calleeType); |
| |
| _resolver.errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.ABSTRACT_SUPER_MEMBER_REFERENCE, |
| nameNode, |
| [target.kind.displayName, name]); |
| return; |
| } |
| |
| // Nothing help, there is no target at all. |
| _setDynamicResolution(node); |
| _resolver.errorReporter.reportErrorForNode( |
| StaticTypeWarningCode.UNDEFINED_SUPER_METHOD, |
| nameNode, |
| [name, enclosingClass.displayName]); |
| } |
| |
| void _resolveReceiverTypeLiteral(MethodInvocation node, ClassElement receiver, |
| SimpleIdentifier nameNode, String name) { |
| if (node.isCascaded) { |
| receiver = _typeType.element; |
| } |
| |
| var element = _resolveElement(receiver, nameNode); |
| if (element != null) { |
| if (element is ExecutableElement) { |
| nameNode.staticElement = element; |
| var calleeType = _getCalleeType(node, element); |
| _setResolution(node, calleeType); |
| } else { |
| _reportInvocationOfNonFunction(node); |
| } |
| return; |
| } |
| |
| _reportUndefinedMethod(node, name, receiver); |
| } |
| |
| /// If the given [type] is a type parameter, replace with its bound. |
| /// Otherwise, return the original type. |
| DartType _resolveTypeParameter(DartType type) { |
| if (type is TypeParameterType) { |
| return type.resolveToBound(_resolver.typeProvider.objectType); |
| } |
| return type; |
| } |
| |
| void _setDynamicResolution(MethodInvocation node, |
| {bool setNameTypeToDynamic: true}) { |
| if (setNameTypeToDynamic) { |
| node.methodName.staticType = _dynamicType; |
| } |
| node.staticInvokeType = _dynamicType; |
| node.staticType = _dynamicType; |
| _setExplicitTypeArgumentTypes(); |
| } |
| |
| /// Set explicitly specified type argument types, or empty if not specified. |
| /// Inference is done in type analyzer, so inferred type arguments might be |
| /// set later. |
| void _setExplicitTypeArgumentTypes() { |
| var typeArgumentList = _invocation.typeArguments; |
| if (typeArgumentList != null) { |
| var arguments = typeArgumentList.arguments; |
| _invocation.typeArgumentTypes = arguments.map((n) => n.type).toList(); |
| } else { |
| _invocation.typeArgumentTypes = []; |
| } |
| } |
| |
| void _setResolution(MethodInvocation node, DartType type) { |
| // TODO(scheglov) We need this for StaticTypeAnalyzer to run inference. |
| // But it seems weird. Do we need to know the raw type of a function?! |
| node.methodName.staticType = type; |
| |
| if (type == _dynamicType || _isCoreFunction(type)) { |
| _setDynamicResolution(node, setNameTypeToDynamic: false); |
| return; |
| } |
| |
| if (type is InterfaceType) { |
| var call = _inheritance.getMember(type, _nameCall); |
| if (call == null) { |
| var result = _extensionResolver.findExtension( |
| type, _nameCall.name, node.methodName, ElementKind.METHOD); |
| if (result.isSingle) { |
| call = result.element; |
| } else if (result.isAmbiguous) { |
| return; |
| } |
| } |
| if (call != null && call.kind == ElementKind.METHOD) { |
| type = call.type; |
| } |
| } |
| |
| if (type is FunctionType) { |
| // TODO(scheglov) Extract this when receiver is already FunctionType? |
| var instantiatedType = _instantiateFunctionType( |
| type, |
| node.typeArguments, |
| node.methodName, |
| ); |
| instantiatedType = _toSyntheticFunctionType(instantiatedType); |
| node.staticInvokeType = instantiatedType; |
| node.staticType = instantiatedType.returnType; |
| // TODO(scheglov) too much magic |
| node.argumentList.correspondingStaticParameters = |
| _computeCorrespondingParameters( |
| node.argumentList, |
| instantiatedType, |
| ); |
| return; |
| } |
| |
| if (type is VoidType) { |
| return _reportUseOfVoidType(node, node.methodName); |
| } |
| |
| if (type == BottomTypeImpl.instance) { |
| return _reportUseOfNeverType(node, node.methodName); |
| } |
| |
| _reportInvocationOfNonFunction(node); |
| } |
| |
| /// Checks whether the given [expression] is a reference to a class. If it is |
| /// then the element representing the class is returned, otherwise `null` is |
| /// returned. |
| static ClassElement getTypeReference(Expression expression) { |
| if (expression is Identifier) { |
| Element staticElement = expression.staticElement; |
| if (staticElement is ClassElement) { |
| return staticElement; |
| } |
| } |
| return null; |
| } |
| |
| /// As an experiment for using synthetic [FunctionType]s, we replace some |
| /// function types with the equivalent synthetic function type instance. |
| /// The assumption that we try to prove is that only the set of parameters, |
| /// with their names, types and kinds is important, but the element that |
| /// encloses them is not (`null` for synthetic function types). |
| static FunctionType _toSyntheticFunctionType(FunctionType type) { |
| // if (type.element is GenericFunctionTypeElement) { |
| // var synthetic = FunctionTypeImpl.synthetic( |
| // type.returnType, |
| // type.typeFormals.map((e) { |
| // return TypeParameterElementImpl.synthetic(e.name)..bound = e.bound; |
| // }).toList(), |
| // type.parameters.map((p) { |
| // return ParameterElementImpl.synthetic( |
| // p.name, |
| // p.type, |
| // // ignore: deprecated_member_use_from_same_package |
| // p.parameterKind, |
| // ); |
| // }).toList(), |
| // ); |
| // return synthetic; |
| // } |
| return type; |
| } |
| } |