Chromium Code Reviews| 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 |