Index: lib/src/checker/checker.dart |
diff --git a/lib/src/checker/checker.dart b/lib/src/checker/checker.dart |
index 3c44e81204eee129dcaa8d7bce8b9a05bfb72128..9c0c279b9861cdc423a9d2e09628a17d9a0a5c8f 100644 |
--- a/lib/src/checker/checker.dart |
+++ b/lib/src/checker/checker.dart |
@@ -545,15 +545,35 @@ class CodeChecker extends RecursiveAstVisitor { |
if (_rules.isDynamicCall(f)) { |
// If f is Function and this is a method invocation, we should have |
// gotten an analyzer error, so no need to issue another error. |
- _recordDynamicInvoke(node); |
+ _recordDynamicInvoke(node, f); |
} else { |
checkArgumentList(list, _rules.getTypeAsCaller(f)); |
} |
} |
@override |
- void visitMethodInvocation(MethodInvocation node) { |
- checkFunctionApplication(node, node.methodName, node.argumentList); |
+ visitMethodInvocation(MethodInvocation node) { |
+ var target = node.realTarget; |
+ if (_rules.isDynamicTarget(target)) { |
+ _recordDynamicInvoke(node, target); |
+ |
+ // Mark the tear-off as being dynamic, too. This lets us distinguish |
+ // cases like: |
+ // |
+ // dynamic d; |
+ // d.someMethod(...); // the whole method call must be a dynamic send. |
+ // |
+ // ... from case like: |
+ // |
+ // SomeType s; |
+ // s.someDynamicField(...); // static get, followed by dynamic call. |
+ // |
+ // The first case is handled here, the second case is handled below when |
+ // we call [checkFunctionApplication]. |
+ DynamicInvoke.set(node.methodName, true); |
+ } else { |
+ checkFunctionApplication(node, node.methodName, node.argumentList); |
+ } |
node.visitChildren(this); |
} |
@@ -622,8 +642,9 @@ class CodeChecker extends RecursiveAstVisitor { |
@override |
void visitPropertyAccess(PropertyAccess node) { |
- if (node.staticType.isDynamic && _rules.isDynamicTarget(node.realTarget)) { |
- _recordDynamicInvoke(node); |
+ var target = node.realTarget; |
+ if (_rules.isDynamicTarget(target)) { |
+ _recordDynamicInvoke(node, target); |
} |
node.visitChildren(this); |
} |
@@ -632,7 +653,7 @@ class CodeChecker extends RecursiveAstVisitor { |
void visitPrefixedIdentifier(PrefixedIdentifier node) { |
final target = node.prefix; |
if (_rules.isDynamicTarget(target)) { |
- _recordDynamicInvoke(node); |
+ _recordDynamicInvoke(node, target); |
} |
node.visitChildren(this); |
} |
@@ -774,7 +795,7 @@ class CodeChecker extends RecursiveAstVisitor { |
op.type == TokenType.PLUS_PLUS || |
op.type == TokenType.MINUS_MINUS) { |
if (_rules.isDynamicTarget(node.operand)) { |
- _recordDynamicInvoke(node); |
+ _recordDynamicInvoke(node, node.operand); |
} |
// For ++ and --, even if it is not dynamic, we still need to check |
// that the user defined method accepts an `int` as the RHS. |
@@ -790,7 +811,7 @@ class CodeChecker extends RecursiveAstVisitor { |
// Dynamic invocation |
// TODO(vsm): Move this logic to the resolver? |
if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) { |
- _recordDynamicInvoke(node); |
+ _recordDynamicInvoke(node, node.leftOperand); |
} |
} else { |
var element = node.staticElement; |
@@ -831,8 +852,9 @@ class CodeChecker extends RecursiveAstVisitor { |
@override |
void visitIndexExpression(IndexExpression node) { |
- if (_rules.isDynamicTarget(node.target)) { |
- _recordDynamicInvoke(node); |
+ var target = node.realTarget; |
+ if (_rules.isDynamicTarget(target)) { |
+ _recordDynamicInvoke(node, target); |
} else { |
var element = node.staticElement; |
if (element is MethodElement) { |
@@ -897,7 +919,7 @@ class CodeChecker extends RecursiveAstVisitor { |
var methodElement = expr.staticElement; |
if (methodElement == null) { |
// Dynamic invocation |
- _recordDynamicInvoke(expr); |
+ _recordDynamicInvoke(expr, expr.leftHandSide); |
} else { |
// Sanity check the operator |
assert(methodElement.isOperator); |
@@ -941,8 +963,13 @@ class CodeChecker extends RecursiveAstVisitor { |
} |
} |
- void _recordDynamicInvoke(AstNode node) { |
- _reporter.log(new DynamicInvoke(_rules, node)); |
+ void _recordDynamicInvoke(AstNode node, AstNode target) { |
+ var dinvoke = new DynamicInvoke(_rules, node); |
+ _reporter.log(dinvoke); |
+ // TODO(jmesserly): we may eventually want to record if the whole operation |
+ // (node) was dynamic, rather than the target, but this is an easier fit |
+ // with what we used to do. |
+ DynamicInvoke.set(target, true); |
} |
void _recordMessage(StaticInfo info) { |