Index: pkg/analyzer/lib/src/generated/static_type_analyzer.dart |
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart |
index cc8e2f06e5ca9c4f409d14c684c20f332974cef1..41a5fdb4bc008d8639ecdfc19bf8dc99a34627a8 100644 |
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart |
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart |
@@ -461,37 +461,37 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> { |
// node. |
return null; |
} |
+ bool recordInference = false; |
ExecutableElementImpl functionElement = |
node.element as ExecutableElementImpl; |
- DartType computedType = _computeStaticReturnTypeOfFunctionExpression(node); |
- if (_strongMode) { |
- // In strong mode, we don't want to allow the function's return type to |
- // be bottom. If the surrounding context has a more precise type, we |
- // will push it down with inference, below. If not we want to use dynamic. |
- // TODO(jmesserly): should we do this for the `null` literal always in |
- // strong mode, instead of handling it here? |
- if (computedType.isBottom) { |
- computedType = DynamicTypeImpl.instance; |
- } |
- DartType functionType = InferenceContext.getType(node); |
- if (functionType is FunctionType) { |
- functionType = _resolver.matchFunctionTypeParameters( |
- node.typeParameters, functionType); |
- |
- if (functionType is FunctionType) { |
- DartType returnType = functionType.returnType; |
- if (computedType.isDynamic && |
- !(returnType.isDynamic || returnType.isBottom)) { |
- computedType = returnType; |
- _resolver.inferenceContext.recordInference(node, functionType); |
- } |
- } |
- } |
+ FunctionBody body = node.body; |
+ DartType computedType; |
+ if (body is ExpressionFunctionBody) { |
+ computedType = _getStaticType(body.expression); |
+ } else { |
+ computedType = _dynamicType; |
} |
+ |
+ // If we had a better type from the function body, use it. |
+ // |
+ // This helps in a few cases: |
+ // * ExpressionFunctionBody, when the surrounding context had a better type. |
+ // * BlockFunctionBody, if we inferred a type from yield/return. |
+ // * we also normalize bottom to dynamic here. |
+ if (_strongMode && (computedType.isBottom || computedType.isDynamic)) { |
+ computedType = InferenceContext.getType(body) ?? _dynamicType; |
+ recordInference = !computedType.isDynamic; |
+ } |
+ |
+ computedType = _computeReturnTypeOfFunction(body, computedType); |
+ |
functionElement.returnType = computedType; |
_recordPropagatedTypeOfFunction(functionElement, node.body); |
- _recordStaticType(node, node.element.type); |
+ _recordStaticType(node, functionElement.type); |
+ if (recordInference) { |
+ _resolver.inferenceContext.recordInference(node, functionElement.type); |
+ } |
return null; |
} |
@@ -941,6 +941,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> { |
*/ |
@override |
Object visitNullLiteral(NullLiteral node) { |
+ // TODO(jmesserly): in strong mode, should we just use the context type? |
_recordStaticType(node, _typeProvider.bottomType); |
return null; |
} |
@@ -1473,6 +1474,27 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> { |
} |
/** |
+ * Given a function body and its return type, compute the return type of |
+ * the entire function, taking into account whether the function body |
+ * is `sync*`, `async` or `async*`. |
+ * |
+ * See also [FunctionBody.isAsynchronous], [FunctionBody.isGenerator]. |
+ */ |
+ DartType _computeReturnTypeOfFunction(FunctionBody body, DartType type) { |
+ if (body.isGenerator) { |
+ InterfaceType genericType = body.isAsynchronous |
+ ? _typeProvider.streamType |
+ : _typeProvider.iterableType; |
+ return genericType.substitute4(<DartType>[type]); |
+ } else if (body.isAsynchronous) { |
+ return _typeProvider.futureType |
+ .substitute4(<DartType>[type.flattenFutures(_typeSystem)]); |
+ } else { |
+ return type; |
+ } |
+ } |
+ |
+ /** |
* Compute the static return type of the method or function represented by the given element. |
* |
* @param element the element representing the method or function invoked by the given node |
@@ -1514,38 +1536,6 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<Object> { |
return returnType.type; |
} |
- /** |
- * Given a function expression, compute the return type of the function. The return type of |
- * functions with a block body is `dynamicType`, with an expression body it is the type of |
- * the expression. |
- * |
- * @param node the function expression whose return type is to be computed |
- * @return the return type that was computed |
- */ |
- DartType _computeStaticReturnTypeOfFunctionExpression( |
- FunctionExpression node) { |
- FunctionBody body = node.body; |
- if (body.isGenerator) { |
- if (body.isAsynchronous) { |
- return _typeProvider.streamDynamicType; |
- } else { |
- return _typeProvider.iterableDynamicType; |
- } |
- } |
- DartType type; |
- if (body is ExpressionFunctionBody) { |
- type = _getStaticType(body.expression); |
- } else { |
- type = _dynamicType; |
- } |
- if (body.isAsynchronous) { |
- return _typeProvider.futureType |
- .substitute4(<DartType>[type.flattenFutures(_typeSystem)]); |
- } else { |
- return type; |
- } |
- } |
- |
DartType _findIteratedType(DartType type, DartType targetType) { |
// TODO(vsm): Use leafp's matchType here? |
// Set by _find if match is found |