Index: pkg/analyzer/lib/src/generated/element_resolver.dart |
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart |
index 57b8060e5a88a58076ae9e7d7b372c382969978a..368da2a31f59b68cec55e1f196e2926ec16f9cf9 100644 |
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart |
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart |
@@ -426,17 +426,26 @@ class ElementResolver extends SimpleAstVisitor<Object> { |
@override |
Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
- // TODO(brianwilkerson) Can we ever resolve the function being invoked? |
- Expression expression = node.function; |
- if (expression is FunctionExpression) { |
- FunctionExpression functionExpression = expression; |
- ExecutableElement functionElement = functionExpression.element; |
- ArgumentList argumentList = node.argumentList; |
- List<ParameterElement> parameters = |
- _resolveArgumentsToFunction(false, argumentList, functionElement); |
- if (parameters != null) { |
- argumentList.correspondingStaticParameters = parameters; |
- } |
+ Expression function = node.function; |
+ DartType staticInvokeType = |
+ _resolveGenericMethod(function.staticType, node.typeArguments, node); |
+ DartType propagatedInvokeType = _resolveGenericMethod( |
+ function.propagatedType, node.typeArguments, node); |
+ |
+ node.staticInvokeType = staticInvokeType; |
+ node.propagatedInvokeType = |
+ _propagatedInvokeTypeIfBetter(propagatedInvokeType, staticInvokeType); |
+ |
+ List<ParameterElement> parameters = |
+ _computeCorrespondingParameters(node.argumentList, staticInvokeType); |
+ if (parameters != null) { |
+ node.argumentList.correspondingStaticParameters = parameters; |
+ } |
+ |
+ parameters = _computeCorrespondingParameters( |
+ node.argumentList, propagatedInvokeType); |
+ if (parameters != null) { |
+ node.argumentList.correspondingPropagatedParameters = parameters; |
} |
return null; |
} |
@@ -652,17 +661,16 @@ class ElementResolver extends SimpleAstVisitor<Object> { |
methodName.propagatedElement = propagatedElement; |
node.staticInvokeType = staticInvokeType; |
- if (propagatedInvokeType != null && |
- (staticInvokeType == null || |
- propagatedInvokeType.isMoreSpecificThan(staticInvokeType))) { |
- // Don't store the propagated invoke type unless it's more specific than |
- // the static type. We still need to record the propagated parameter |
- // elements however, as that is used for the propagatedType downwards |
- // inference of lambda parameters. |
- node.propagatedInvokeType = propagatedInvokeType; |
- } else { |
- node.propagatedInvokeType = null; |
- } |
+ // |
+ // Store the propagated invoke type if it's more specific than the static |
+ // type. |
+ // |
+ // We still need to record the propagated parameter elements however, |
+ // as they are used in propagatedType downwards inference of lambda |
+ // parameters. So we don't want to clear the propagatedInvokeType variable. |
+ // |
+ node.propagatedInvokeType = |
+ _propagatedInvokeTypeIfBetter(propagatedInvokeType, staticInvokeType); |
ArgumentList argumentList = node.argumentList; |
if (staticInvokeType != null) { |
@@ -1328,6 +1336,30 @@ class ElementResolver extends SimpleAstVisitor<Object> { |
return null; |
} |
+ DartType _computeMethodInvokeType(MethodInvocation node, Element element) { |
+ if (element == null) { |
+ // TODO(jmesserly): should we return `dynamic` in this case? |
+ // Otherwise we have to guard against `null` every time we use |
+ // `staticInvokeType`. |
+ // If we do return `dynamic` we need to be careful that this doesn't |
+ // adversely affect propagatedType code path. But it shouldn't because |
+ // we'll discard `dynamic` anyway (see _propagatedInvokeTypeIfBetter). |
+ return null; |
+ } |
+ |
+ DartType invokeType; |
+ if (element is PropertyAccessorElement) { |
+ invokeType = element.returnType; |
+ } else if (element is ExecutableElement) { |
+ invokeType = element.type; |
+ } else if (element is VariableElement) { |
+ invokeType = _promoteManager.getStaticType(element); |
+ } |
+ |
+ return _resolveGenericMethod( |
+ invokeType, node.typeArguments, node.methodName); |
+ } |
+ |
/** |
* If the given [element] is a setter, return the getter associated with it. |
* Otherwise, return the element unchanged. |
@@ -1464,49 +1496,6 @@ class ElementResolver extends SimpleAstVisitor<Object> { |
return staticType; |
} |
- DartType _computeMethodInvokeType(MethodInvocation node, Element element) { |
- if (element == null) { |
- return null; |
- } |
- |
- DartType invokeType; |
- if (element is PropertyAccessorElement) { |
- invokeType = element.returnType; |
- } else if (element is ExecutableElement) { |
- invokeType = element.type; |
- } else if (element is VariableElement) { |
- invokeType = _promoteManager.getStaticType(element); |
- } |
- |
- // |
- // Check for a generic method & apply type arguments if any were passed. |
- // |
- // TODO(jmesserly): support generic "call" methods on InterfaceType. |
- if (invokeType is FunctionType) { |
- FunctionType type = invokeType; |
- List<TypeParameterElement> parameters = type.typeFormals; |
- |
- NodeList<TypeName> arguments = node.typeArguments?.arguments; |
- if (arguments != null && arguments.length != parameters.length) { |
- // Wrong number of type arguments. Ignore them |
- arguments = null; |
- _resolver.reportErrorForNode( |
- StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, |
- node.methodName, |
- [type, parameters.length, arguments?.length ?? 0]); |
- } |
- if (parameters.isNotEmpty) { |
- if (arguments == null) { |
- invokeType = _resolver.typeSystem.instantiateToBounds(type); |
- } else { |
- invokeType = type.instantiate(arguments.map((n) => n.type).toList()); |
- } |
- } |
- } |
- |
- return invokeType; |
- } |
- |
/** |
* Return `true` if the given [expression] is a prefix for a deferred import. |
*/ |
@@ -1751,6 +1740,24 @@ class ElementResolver extends SimpleAstVisitor<Object> { |
} |
/** |
+ * Determines if the [propagatedType] of the invoke is better (more specific) |
+ * than the [staticType]. If so it will be returned, otherwise returns null. |
+ */ |
+ // TODO(jmesserly): can we refactor Resolver.recordPropagatedTypeIfBetter to |
+ // get some code sharing? Right now, this method is to support |
+ // `staticInvokeType` and `propagatedInvokeType`, and the one in Resolver is |
+ // for `staticType` and `propagatedType` on Expression. |
+ DartType _propagatedInvokeTypeIfBetter( |
+ DartType propagatedType, DartType staticType) { |
+ if (propagatedType != null && |
+ (staticType == null || propagatedType.isMoreSpecificThan(staticType))) { |
+ return propagatedType; |
+ } else { |
+ return null; |
+ } |
+ } |
+ |
+ /** |
* Record that the given [node] is undefined, causing an error to be reported |
* if appropriate. The [declaringElement] is the element inside which no |
* declaration was found. If this element is a proxy, no error will be |
@@ -2074,6 +2081,36 @@ class ElementResolver extends SimpleAstVisitor<Object> { |
} |
/** |
+ * Check for a generic method & apply type arguments if any were passed. |
+ */ |
+ DartType _resolveGenericMethod( |
+ DartType invokeType, TypeArgumentList typeArguments, AstNode node) { |
+ // TODO(jmesserly): support generic "call" methods on InterfaceType. |
+ if (invokeType is FunctionType) { |
+ FunctionType type = invokeType; |
+ List<TypeParameterElement> parameters = type.typeFormals; |
+ |
+ NodeList<TypeName> arguments = typeArguments?.arguments; |
+ if (arguments != null && arguments.length != parameters.length) { |
+ // Wrong number of type arguments. Ignore them |
+ arguments = null; |
+ _resolver.reportErrorForNode( |
+ StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, |
+ node, |
+ [type, parameters.length, arguments?.length ?? 0]); |
+ } |
+ if (parameters.isNotEmpty) { |
+ if (arguments == null) { |
+ invokeType = _resolver.typeSystem.instantiateToBounds(type); |
+ } else { |
+ invokeType = type.instantiate(arguments.map((n) => n.type).toList()); |
+ } |
+ } |
+ } |
+ return invokeType; |
+ } |
+ |
+ /** |
* Given an invocation of the form 'm(a1, ..., an)', resolve 'm' to the |
* element being invoked. If the returned element is a method, then the method |
* will be invoked. If the returned element is a getter, the getter will be |