Index: pkg/analyzer/lib/src/task/strong/checker.dart |
diff --git a/pkg/analyzer/lib/src/task/strong/checker.dart b/pkg/analyzer/lib/src/task/strong/checker.dart |
index 568bf3069595ddaff612925f49439cd640106c25..86969f58d80dc6bb95991fdd6f593f61cdba3d8e 100644 |
--- a/pkg/analyzer/lib/src/task/strong/checker.dart |
+++ b/pkg/analyzer/lib/src/task/strong/checker.dart |
@@ -380,6 +380,12 @@ class CodeChecker extends RecursiveAstVisitor { |
} |
@override |
+ void visitFunctionExpression(FunctionExpression node) { |
+ _checkForUnsafeBlockClosureInference(node); |
+ super.visitFunctionExpression(node); |
+ } |
+ |
+ @override |
void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
checkFunctionApplication(node, node.function, node.argumentList); |
node.visitChildren(this); |
@@ -713,6 +719,71 @@ class CodeChecker extends RecursiveAstVisitor { |
node.visitChildren(this); |
} |
+ /** |
+ * Check if the closure [node] is unsafe due to dartbug.com/26947. If so, |
+ * issue a warning. |
+ * |
+ * TODO(paulberry): eliminate this once dartbug.com/26947 is fixed. |
+ */ |
+ void _checkForUnsafeBlockClosureInference(FunctionExpression node) { |
+ if (node.body is! BlockFunctionBody) { |
+ return; |
+ } |
+ if (node.element.returnType.isDynamic) { |
+ return; |
+ } |
+ // Find the enclosing variable declaration whose inferred type might depend |
+ // on the inferred return type of the block closure (if any). |
+ VariableDeclaration decl = null; |
+ AstNode ancestor = node.parent; |
+ while (ancestor != null) { |
+ if (ancestor is BlockFunctionBody) { |
+ // node is inside another block function body; if that block |
+ // function body is unsafe, we've already warned about it. |
+ return; |
+ } |
+ if (ancestor is VariableDeclaration) { |
Leaf
2016/07/22 23:34:41
Might be a bit clearer just to make this part of t
Paul Berry
2016/07/23 16:18:18
Done.
|
+ decl = ancestor; |
+ break; |
+ } |
+ if (ancestor is InstanceCreationExpression) { |
Leaf
2016/07/22 23:34:41
Presumably this same logic could be applied to Lis
Paul Berry
2016/07/23 16:18:18
Done. PTAL.
|
+ // node appears inside an instance creation expression; we may be safe |
+ // if the type of the instance creation expression requires no |
+ // inference. |
+ TypeName typeName = ancestor.constructorName.type; |
+ DartType type = typeName.type; |
+ if (typeName.typeArguments != null) { |
+ // Type arguments were explicitly specified. We are safe. |
+ return; |
+ } |
+ if (!(type is ParameterizedType && type.typeParameters.isNotEmpty)) { |
+ // Type is not generic. We are safe. |
+ return; |
+ } |
+ } |
+ ancestor = ancestor.parent; |
+ } |
+ if (decl == null) { |
+ // node is not inside a variable declaration, so it is safe. |
+ return; |
+ } |
+ VariableElement declElement = decl.element; |
+ if (!declElement.hasImplicitType) { |
+ // Variable declaration has an explicit type, so it's safe. |
+ return; |
+ } |
+ if (declElement.type.isDynamic) { |
+ // No type was successfully inferred for this variable, so it's safe. |
+ return; |
+ } |
+ if (declElement.enclosingElement is ExecutableElement) { |
+ // Variable declaration is inside a function or method, so it's safe. |
+ return; |
+ } |
+ _recordMessage(node, StrongModeCode.UNSAFE_BLOCK_CLOSURE_INFERENCE, |
+ [declElement.name]); |
+ } |
+ |
void _checkReturnOrYield(Expression expression, AstNode node, |
{bool yieldStar: false}) { |
FunctionBody body = node.getAncestor((n) => n is FunctionBody); |