Index: pkg/analyzer/lib/src/generated/resolver.dart |
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart |
index a52736d015cea04e0182ceda91145fac082f106a..0aa090c14b3f954d3e613fbd6895768fc741f4e9 100644 |
--- a/pkg/analyzer/lib/src/generated/resolver.dart |
+++ b/pkg/analyzer/lib/src/generated/resolver.dart |
@@ -5415,6 +5415,8 @@ class InferenceContext { |
* A stack of return types for all of the enclosing |
* functions and methods. |
*/ |
+ // TODO(leafp) Handle the implicit union type for Futures |
+ // https://github.com/dart-lang/sdk/issues/25322 |
List<DartType> _returnStack = <DartType>[]; |
InferenceContext._(this._errorListener, TypeProvider typeProvider, |
@@ -5423,9 +5425,15 @@ class InferenceContext { |
/** |
* Get the return type of the current enclosing function, if any. |
+ * |
+ * The type returned for a function is the type that is expected |
+ * to be used in a return or yield context. For ordinary functions |
+ * this is the same as the return type of the function. For async |
+ * functions returning Future<T> and for generator functions |
+ * returning Stream<T> or Iterable<T>, this is T. |
*/ |
DartType get returnContext => |
- (_returnStack.isNotEmpty) ? _returnStack.last : null; |
+ _returnStack.isNotEmpty ? _returnStack.last : null; |
/** |
* Match type [t1] against type [t2] as follows. |
@@ -8359,8 +8367,10 @@ class ResolverVisitor extends ScopedVisitor { |
@override |
Object visitAwaitExpression(AwaitExpression node) { |
- //TODO(leafp): Handle the implicit union type here |
- DartType contextType = InferenceContext.getType(node); |
+ // TODO(leafp): Handle the implicit union type here |
+ // https://github.com/dart-lang/sdk/issues/25322 |
+ DartType contextType = StaticTypeAnalyzer.flattenFutures( |
+ typeProvider, InferenceContext.getType(node)); |
if (contextType != null) { |
InterfaceType futureT = |
typeProvider.futureType.substitute4([contextType]); |
@@ -8894,7 +8904,11 @@ class ResolverVisitor extends ScopedVisitor { |
DartType functionType = InferenceContext.getType(node); |
if (functionType is FunctionType) { |
_inferFormalParameterList(node.parameters, functionType); |
- InferenceContext.setType(node.body, functionType.returnType); |
+ DartType returnType = _computeReturnOrYieldType( |
+ functionType.returnType, |
+ _enclosingFunction.isGenerator, |
+ _enclosingFunction.isAsynchronous); |
+ InferenceContext.setType(node.body, returnType); |
} |
super.visitFunctionExpression(node); |
} finally { |
@@ -9099,7 +9113,11 @@ class ResolverVisitor extends ScopedVisitor { |
ExecutableElement outerFunction = _enclosingFunction; |
try { |
_enclosingFunction = node.element; |
- InferenceContext.setType(node.body, node.element.type?.returnType); |
+ DartType returnType = _computeReturnOrYieldType( |
+ _enclosingFunction.type?.returnType, |
+ _enclosingFunction.isGenerator, |
+ _enclosingFunction.isAsynchronous); |
+ InferenceContext.setType(node.body, returnType); |
super.visitMethodDeclaration(node); |
} finally { |
_enclosingFunction = outerFunction; |
@@ -9328,18 +9346,15 @@ class ResolverVisitor extends ScopedVisitor { |
// If we're not in a generator ([a]sync*, then we shouldn't have a yield. |
// so don't infer |
if (_enclosingFunction.isGenerator) { |
- // If this is a yield*, then we just propagate the return type downwards |
+ // If this just a yield, then we just pass on the element type |
DartType type = returnType; |
- // If this just a yield, then we need to get the element type |
- if (node.star == null) { |
+ if (node.star != null) { |
+ // If this is a yield*, then we wrap the element return type |
// If it's synchronous, we expect Iterable<T>, otherwise Stream<T> |
- InterfaceType wrapperD = _enclosingFunction.isSynchronous |
- ? typeProvider.iterableDynamicType |
- : typeProvider.streamDynamicType; |
- // Match the types to instantiate the type arguments if possible |
- List<DartType> targs = |
- inferenceContext.matchTypes(wrapperD, returnType); |
- type = (targs?.length == 1) ? targs[0] : null; |
+ InterfaceType wrapperType = _enclosingFunction.isSynchronous |
+ ? typeProvider.iterableType |
+ : typeProvider.streamType; |
+ type = wrapperType.substitute4(<DartType>[type]); |
} |
InferenceContext.setType(node.expression, type); |
} |
@@ -9380,6 +9395,34 @@ class ResolverVisitor extends ScopedVisitor { |
} |
/** |
+ * Given the declared return type of a function, compute the type of the |
+ * values which should be returned or yielded as appropriate. If a type |
+ * cannot be computed from the declared return type, return null. |
+ */ |
+ DartType _computeReturnOrYieldType( |
+ DartType declaredType, bool isGenerator, bool isAsynchronous) { |
+ // Ordinary functions just return their declared types. |
+ if (!isGenerator && !isAsynchronous) { |
+ return declaredType; |
+ } |
+ if (isGenerator) { |
+ if (declaredType is! InterfaceType) { |
+ return null; |
+ } |
+ // If it's synchronous, we expect Iterable<T>, otherwise Stream<T> |
+ InterfaceType rawType = isAsynchronous |
+ ? typeProvider.streamDynamicType |
+ : typeProvider.iterableDynamicType; |
+ // Match the types to instantiate the type arguments if possible |
+ List<DartType> typeArgs = |
+ inferenceContext.matchTypes(rawType, declaredType); |
+ return (typeArgs?.length == 1) ? typeArgs[0] : null; |
+ } |
+ // Must be asynchronous to reach here, so strip off any layers of Future |
+ return StaticTypeAnalyzer.flattenFutures(typeProvider, declaredType); |
+ } |
+ |
+ /** |
* The given expression is the expression used to compute the iterator for a |
* for-each statement. Attempt to compute the type of objects that will be |
* assigned to the loop variable and return that type. Return `null` if the |