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 |