| 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..98c4aa6a97dc640c90891e60bdefcfb186c215ad 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,105 @@ 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).
|
| + AstNode prevAncestor = node;
|
| + AstNode ancestor = node.parent;
|
| + while (ancestor != null && ancestor is! VariableDeclaration) {
|
| + 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 InstanceCreationExpression) {
|
| + // 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;
|
| + if (typeName.typeArguments != null) {
|
| + // Type arguments were explicitly specified. We are safe.
|
| + return;
|
| + }
|
| + DartType type = typeName.type;
|
| + if (!(type is ParameterizedType && type.typeParameters.isNotEmpty)) {
|
| + // Type is not generic. We are safe.
|
| + return;
|
| + }
|
| + }
|
| + if (ancestor is MethodInvocation) {
|
| + // node appears inside a method or function invocation; we may be safe
|
| + // if the type of the method or function requires no inference.
|
| + if (ancestor.typeArguments != null) {
|
| + // Type arguments were explicitly specified. We are safe.
|
| + return;
|
| + }
|
| + Element methodElement = ancestor.methodName.staticElement;
|
| + if (!(methodElement is ExecutableElement &&
|
| + methodElement.typeParameters.isNotEmpty)) {
|
| + // Method is not generic. We are safe.
|
| + return;
|
| + }
|
| + }
|
| + if (ancestor is FunctionExpressionInvocation &&
|
| + !identical(prevAncestor, ancestor.function)) {
|
| + // node appears inside an argument to a function expression invocation;
|
| + // we may be safe if the type of the function expression requires no
|
| + // inference.
|
| + if (ancestor.typeArguments != null) {
|
| + // Type arguments were explicitly specified. We are safe.
|
| + return;
|
| + }
|
| + DartType type = ancestor.function.staticType;
|
| + if (!(type is FunctionTypeImpl && type.typeFormals.isNotEmpty)) {
|
| + // Type is not generic or has had its type parameters instantiated.
|
| + // We are safe.
|
| + return;
|
| + }
|
| + }
|
| + if ((ancestor is ListLiteral && ancestor.typeArguments != null) ||
|
| + (ancestor is MapLiteral && ancestor.typeArguments != null)) {
|
| + // node appears inside a list or map literal with an explicit type. We
|
| + // are safe because no type inference is required.
|
| + return;
|
| + }
|
| + prevAncestor = ancestor;
|
| + ancestor = ancestor.parent;
|
| + }
|
| + if (ancestor == null) {
|
| + // node is not inside a variable declaration, so it is safe.
|
| + return;
|
| + }
|
| + VariableDeclaration decl = ancestor;
|
| + 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);
|
|
|