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 496b821de8f7b985808eea72ca1e4ce9c9071f30..60610e95fe7357e02c2cf48085d2ca5609228c69 100644 |
--- a/pkg/analyzer/lib/src/generated/resolver.dart |
+++ b/pkg/analyzer/lib/src/generated/resolver.dart |
@@ -4602,12 +4602,20 @@ class InferenceContext { |
final TypeSystem _typeSystem; |
/** |
+ * When no context type is available, this will track the least upper bound |
+ * of all return statements in a lambda. |
+ * |
+ * This will always be kept in sync with [_returnStack]. |
+ */ |
+ final List<DartType> _inferredReturn = <DartType>[]; |
+ |
+ /** |
* 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>[]; |
+ final List<DartType> _returnStack = <DartType>[]; |
InferenceContext._(this._errorListener, TypeProvider typeProvider, |
this._typeSystem, this._inferenceHints) |
@@ -4626,6 +4634,24 @@ class InferenceContext { |
_returnStack.isNotEmpty ? _returnStack.last : null; |
/** |
+ * Records the type of the expression of a return statement. |
+ * |
+ * This will be used for inferring a block bodied lambda, if no context |
+ * type was available. |
+ */ |
+ void addReturnOrYieldType(DartType type) { |
+ if (_returnStack.isEmpty) { |
+ return; |
+ } |
+ DartType context = _returnStack.last; |
+ if (context == null || context.isDynamic) { |
+ DartType inferred = _inferredReturn.last; |
+ inferred = _typeSystem.getLeastUpperBound(_typeProvider, type, inferred); |
+ _inferredReturn[_inferredReturn.length - 1] = inferred; |
+ } |
+ } |
+ |
+ /** |
* Match type [t1] against type [t2] as follows. |
* If `t1 = I<dynamic, ..., dynamic>`, then look for a supertype |
* of t1 of the form `K<S0, ..., Sm>` where `t2 = K<S0', ..., Sm'>` |
@@ -4638,19 +4664,31 @@ class InferenceContext { |
/** |
* Pop a return type off of the return stack. |
+ * |
+ * Also record any inferred return type using [setType], unless this node |
+ * already has a context type. This recorded type will be the least upper |
+ * bound of all types added with [addReturnOrYieldType]. |
*/ |
- void popReturnContext() { |
- assert(_returnStack.isNotEmpty); |
+ void popReturnContext(BlockFunctionBody node) { |
+ assert(_returnStack.isNotEmpty && _inferredReturn.isNotEmpty); |
if (_returnStack.isNotEmpty) { |
_returnStack.removeLast(); |
} |
+ if (_inferredReturn.isNotEmpty) { |
+ DartType inferred = _inferredReturn.removeLast(); |
+ if (!inferred.isBottom) { |
+ setType(node, inferred); |
+ } |
+ } |
} |
/** |
- * Push a [returnType] onto the return stack. |
+ * Push a block function body's return type onto the return stack. |
*/ |
- void pushReturnContext(DartType returnType) { |
+ void pushReturnContext(BlockFunctionBody node) { |
+ DartType returnType = getType(node); |
_returnStack.add(returnType); |
+ _inferredReturn.add(BottomTypeImpl.instance); |
} |
/** |
@@ -7657,11 +7695,11 @@ class ResolverVisitor extends ScopedVisitor { |
Object visitBlockFunctionBody(BlockFunctionBody node) { |
_overrideManager.enterScope(); |
try { |
- inferenceContext.pushReturnContext(InferenceContext.getType(node)); |
+ inferenceContext.pushReturnContext(node); |
super.visitBlockFunctionBody(node); |
} finally { |
_overrideManager.exitScope(); |
- inferenceContext.popReturnContext(); |
+ inferenceContext.popReturnContext(node); |
} |
return null; |
} |
@@ -8139,10 +8177,8 @@ class ResolverVisitor extends ScopedVisitor { |
matchFunctionTypeParameters(node.typeParameters, functionType); |
if (functionType is FunctionType) { |
_inferFormalParameterList(node.parameters, functionType); |
- DartType returnType = _computeReturnOrYieldType( |
- functionType.returnType, |
- _enclosingFunction.isGenerator, |
- _enclosingFunction.isAsynchronous); |
+ DartType returnType = |
+ _computeReturnOrYieldType(functionType.returnType); |
InferenceContext.setType(node.body, returnType); |
} |
} |
@@ -8352,10 +8388,8 @@ class ResolverVisitor extends ScopedVisitor { |
try { |
_currentFunctionBody = node.body; |
_enclosingFunction = node.element; |
- DartType returnType = _computeReturnOrYieldType( |
- _enclosingFunction.type?.returnType, |
- _enclosingFunction.isGenerator, |
- _enclosingFunction.isAsynchronous); |
+ DartType returnType = |
+ _computeReturnOrYieldType(_enclosingFunction.type?.returnType); |
InferenceContext.setType(node.body, returnType); |
super.visitMethodDeclaration(node); |
} finally { |
@@ -8451,8 +8485,19 @@ class ResolverVisitor extends ScopedVisitor { |
@override |
Object visitReturnStatement(ReturnStatement node) { |
- InferenceContext.setType(node.expression, inferenceContext.returnContext); |
- return super.visitReturnStatement(node); |
+ Expression e = node.expression; |
+ InferenceContext.setType(e, inferenceContext.returnContext); |
+ super.visitReturnStatement(node); |
+ DartType type = e?.staticType; |
+ // Generators cannot return values, so don't try to do any inference if |
+ // we're processing erroneous code. |
+ if (type != null && _enclosingFunction?.isGenerator == false) { |
+ if (_enclosingFunction.isAsynchronous) { |
+ type = type.flattenFutures(typeSystem); |
+ } |
+ inferenceContext.addReturnOrYieldType(type); |
+ } |
+ return null; |
} |
@override |
@@ -8574,25 +8619,44 @@ class ResolverVisitor extends ScopedVisitor { |
@override |
Object visitYieldStatement(YieldStatement node) { |
+ Expression e = node.expression; |
DartType returnType = inferenceContext.returnContext; |
- if (returnType != null && _enclosingFunction != null) { |
+ bool isGenerator = _enclosingFunction?.isGenerator ?? false; |
+ if (returnType != null && isGenerator) { |
// 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 just a yield, then we just pass on the element type |
- DartType type = returnType; |
- 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 wrapperType = _enclosingFunction.isSynchronous |
- ? typeProvider.iterableType |
- : typeProvider.streamType; |
- type = wrapperType.substitute4(<DartType>[type]); |
- } |
- InferenceContext.setType(node.expression, type); |
+ |
+ // If this just a yield, then we just pass on the element type |
+ DartType type = returnType; |
+ 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 wrapperType = _enclosingFunction.isSynchronous |
+ ? typeProvider.iterableType |
+ : typeProvider.streamType; |
+ type = wrapperType.substitute4(<DartType>[type]); |
+ } |
+ InferenceContext.setType(e, type); |
+ } |
+ super.visitYieldStatement(node); |
+ DartType type = e?.staticType; |
+ if (type != null && isGenerator) { |
+ // If this just a yield, then we just pass on the element type |
+ if (node.star != null) { |
+ // If this is a yield*, then we unwrap the element return type |
+ // If it's synchronous, we expect Iterable<T>, otherwise Stream<T> |
+ InterfaceType wrapperType = _enclosingFunction.isSynchronous |
+ ? typeProvider.iterableType |
+ : typeProvider.streamType; |
+ List<DartType> candidates = |
+ _findImplementedTypeArgument(type, wrapperType); |
+ type = InterfaceTypeImpl.findMostSpecificType(candidates, typeSystem); |
+ } |
+ if (type != null) { |
+ inferenceContext.addReturnOrYieldType(type); |
} |
} |
- return super.visitYieldStatement(node); |
+ return null; |
} |
/** |
@@ -8632,8 +8696,10 @@ class ResolverVisitor extends ScopedVisitor { |
* 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) { |
+ DartType _computeReturnOrYieldType(DartType declaredType) { |
+ bool isGenerator = _enclosingFunction.isGenerator; |
+ bool isAsynchronous = _enclosingFunction.isAsynchronous; |
+ |
// Ordinary functions just return their declared types. |
if (!isGenerator && !isAsynchronous) { |
return declaredType; |
@@ -8656,6 +8722,39 @@ class ResolverVisitor extends ScopedVisitor { |
} |
/** |
+ * Starting from t1, search its class hierarchy for types of the form |
+ * `t2<R>`, and return a list of the resulting R's. |
+ * |
+ * For example, given t1 = `List<int>` and t2 = `Iterable<T>`, this will |
+ * return [int]. |
+ */ |
+ // TODO(jmesserly): this is very similar to code used for flattening futures. |
+ // The only difference is, because of a lack of TypeProvider, the other method |
+ // has to match the Future type by its name and library. Here was are passed |
+ // in the correct type. |
+ List<DartType> _findImplementedTypeArgument(DartType t1, InterfaceType t2) { |
+ List<DartType> result = <DartType>[]; |
+ HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>(); |
+ void recurse(InterfaceTypeImpl type) { |
+ if (type.element == t2.element && type.typeArguments.isNotEmpty) { |
+ result.add(type.typeArguments[0]); |
+ } |
+ if (visitedClasses.add(type.element)) { |
+ if (type.superclass != null) { |
+ recurse(type.superclass); |
+ } |
+ type.mixins.forEach(recurse); |
+ type.interfaces.forEach(recurse); |
+ visitedClasses.remove(type.element); |
+ } |
+ } |
+ if (t1 is InterfaceType) { |
+ recurse(t1); |
+ } |
+ return result; |
+ } |
+ |
+ /** |
* 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 |