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..d66d60eff9e8d6fa3d27db4562ecc93d908509b1 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) |
@@ -4638,19 +4646,49 @@ 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 [addReturnType]. |
*/ |
- 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); |
+ } |
+ |
+ /** |
+ * 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 addReturnType(DartType type) { |
+ if (type == null || _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; |
+ } |
} |
/** |
@@ -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; |
} |
@@ -8451,8 +8489,13 @@ 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); |
+ if (e != null) { |
+ inferenceContext.addReturnType(e.staticType); |
Leaf
2016/02/23 02:17:47
As it stands, I think that this will do the right
Jennifer Messerly
2016/02/23 17:41:46
Nice catch. Yeah, it seems like we should interpre
Jennifer Messerly
2016/02/24 00:20:40
Done.
|
+ } |
+ return null; |
} |
@override |