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..40aa326056c381835ed655475c76565eb6597aa2 100644 |
--- a/pkg/analyzer/lib/src/generated/resolver.dart |
+++ b/pkg/analyzer/lib/src/generated/resolver.dart |
@@ -5415,6 +5415,7 @@ class InferenceContext { |
* A stack of return types for all of the enclosing |
* functions and methods. |
*/ |
+ // TODO(leafp) Handle the implicit union type for Futures |
List<DartType> _returnStack = <DartType>[]; |
InferenceContext._(this._errorListener, TypeProvider typeProvider, |
@@ -5423,6 +5424,12 @@ 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; |
Bob Nystrom
2015/12/30 01:03:39
Nit: the parentheses aren't needed here.
Leaf
2015/12/30 18:28:06
Done.
|
@@ -8360,7 +8367,8 @@ class ResolverVisitor extends ScopedVisitor { |
@override |
Object visitAwaitExpression(AwaitExpression node) { |
//TODO(leafp): Handle the implicit union type here |
- DartType contextType = InferenceContext.getType(node); |
+ DartType contextType = StaticTypeAnalyzer.flattenFutures( |
+ typeProvider, InferenceContext.getType(node)); |
if (contextType != null) { |
InterfaceType futureT = |
typeProvider.futureType.substitute4([contextType]); |
@@ -8894,7 +8902,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 +9111,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 +9344,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 +9393,35 @@ 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, isGenerator, isAsynchronous) { |
Bob Nystrom
2015/12/30 01:03:39
Type annotate isGenerator and isAsynchronous?
Leaf
2015/12/30 18:28:06
Done.
|
+ // Ordinary functions just return their declared types. |
+ if (!isGenerator && !isAsynchronous) { |
+ return declaredType; |
+ } |
+ if (isGenerator) { |
+ if (declaredType is InterfaceType) { |
Bob Nystrom
2015/12/30 01:03:39
You could avoid a little indentation by flipping t
Leaf
2015/12/30 18:28:06
Done.
|
+ // If it's synchronous, we expect Iterable<T>, otherwise Stream<T> |
+ InterfaceType wrapperD = isAsynchronous |
Bob Nystrom
2015/12/30 01:03:39
What does "D" mean here? "dynamic"? How about "raw
Leaf
2015/12/30 18:28:06
Done.
|
+ ? typeProvider.streamDynamicType |
+ : typeProvider.iterableDynamicType; |
+ // Match the types to instantiate the type arguments if possible |
+ List<DartType> targs = |
Bob Nystrom
2015/12/30 01:03:39
If "targs" isn't used much elsewhere, I'd use "typ
Leaf
2015/12/30 18:28:06
Done.
|
+ inferenceContext.matchTypes(wrapperD, declaredType); |
+ return (targs?.length == 1) ? targs[0] : null; |
+ } |
+ // Not declared as an interface type, so leave it alone. |
+ return 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 |