Make MethodInvocationImpl.methodNameType a synonym of staticInvokeType if the target element is not a getter.
So, that type inference that updates staticInvokeType later, in
StaticTypeAnalyzer._inferGenericInvocationExpression() also implicitly
updates methodNameType. This fixes the new set of exceptions that we
saw right before holidays. I'm running them now, most are green,
no reds yet.
R=brianwilkerson@google.com, paulberry@google.com
Change-Id: I53030a1a62a2a669549a47680c6c0c98b4031383
Reviewed-on: https://dart-review.googlesource.com/c/88227
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index ef9cbb2..23a69ac 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -7766,15 +7766,10 @@
SimpleIdentifierImpl _methodName;
/**
- * The type invoke of the [methodName].
- *
- * If the target element is a [MethodElement], this is the same as the
- * [staticInvokeType]. If the target element is a [PropertyAccessorElement],
- * presumably returning an [ExecutableElement] so that it can be invoked
- * in this [MethodInvocation], then this type is the type of the accessor,
- * and the [staticInvokeType] is the invoked type of the returned element.
+ * The invoke type of the [methodName] if the target element is a getter,
+ * or `null` otherwise.
*/
- DartType methodNameType;
+ DartType _methodNameType;
/**
* Initialize a newly created method invocation. The [target] and [operator]
@@ -7826,6 +7821,26 @@
_methodName = _becomeParentOf(identifier as SimpleIdentifierImpl);
}
+ /**
+ * The invoke type of the [methodName].
+ *
+ * If the target element is a [MethodElement], this is the same as the
+ * [staticInvokeType]. If the target element is a getter, presumably
+ * returning an [ExecutableElement] so that it can be invoked in this
+ * [MethodInvocation], then this type is the type of the getter, and the
+ * [staticInvokeType] is the invoked type of the returned element.
+ */
+ DartType get methodNameType => _methodNameType ?? staticInvokeType;
+
+ /**
+ * Set the [methodName] invoke type, only if the target element is a getter.
+ * Otherwise, the target element itself is invoked, [_methodNameType] is
+ * `null`, and the getter will return [staticInvokeType].
+ */
+ set methodNameType(DartType methodNameType) {
+ _methodNameType = methodNameType;
+ }
+
@override
int get precedence => 15;
diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
index 2674291..10e01f6 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -139,9 +139,14 @@
return null;
}
- DartType _getCalleeType(FunctionType targetType) {
+ /// If the element of the invoked [targetType] is a getter, then actually
+ /// the return type of the [targetType] is invoked. So, remember the
+ /// [targetType] into [MethodInvocationImpl.methodNameType] and return the
+ /// actual invoked type.
+ DartType _getCalleeType(MethodInvocation node, FunctionType targetType) {
if (targetType.element.kind == ElementKind.GETTER) {
- var calleeType = (targetType as FunctionTypeImpl).returnType;
+ (node as MethodInvocationImpl).methodNameType = targetType;
+ var calleeType = targetType.returnType;
calleeType = _resolveTypeParameter(calleeType);
return calleeType;
}
@@ -328,9 +333,7 @@
var targetType = _inheritance.getMember(receiverType, _currentName);
if (targetType != null) {
- (node as MethodInvocationImpl).methodNameType = targetType;
-
- var calleeType = _getCalleeType(targetType);
+ var calleeType = _getCalleeType(node, targetType);
// TODO(scheglov) This is bad, we have to create members here.
// Find a way to avoid this.
@@ -382,7 +385,7 @@
element = multiply.conflictingElements[0];
}
if (element is ExecutableElement) {
- var calleeType = _getCalleeType(element.type);
+ var calleeType = _getCalleeType(node, element.type);
return _setResolution(node, calleeType);
}
if (element is VariableElement) {
@@ -407,7 +410,7 @@
if (targetType != null) {
nameNode.staticElement = targetType.element;
- var calleeType = _getCalleeType(targetType);
+ var calleeType = _getCalleeType(node, targetType);
return _setResolution(node, calleeType);
}
@@ -456,7 +459,7 @@
}
if (element is ExecutableElement) {
- var calleeType = _getCalleeType(element.type);
+ var calleeType = _getCalleeType(node, element.type);
return _setResolution(node, calleeType);
}
@@ -479,7 +482,7 @@
// If there is that concrete dispatch target, then we are done.
if (targetType != null) {
nameNode.staticElement = targetType.element;
- var calleeType = _getCalleeType(targetType);
+ var calleeType = _getCalleeType(node, targetType);
_setResolution(node, calleeType);
return;
}
@@ -490,7 +493,7 @@
targetType = _inheritance.getInherited(receiverType, _currentName);
if (targetType != null) {
nameNode.staticElement = targetType.element;
- var calleeType = _getCalleeType(targetType);
+ var calleeType = _getCalleeType(node, targetType);
_setResolution(node, calleeType);
ClassElementImpl receiverSuperClass = AbstractClassElementImpl.getImpl(
@@ -525,7 +528,7 @@
if (element != null) {
if (element is ExecutableElement) {
nameNode.staticElement = element;
- var calleeType = _getCalleeType(element.type);
+ var calleeType = _getCalleeType(node, element.type);
_setResolution(node, calleeType);
} else {
_reportInvocationOfNonFunction(node);
diff --git a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
index 811228a..a10b0e1 100644
--- a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
@@ -282,7 +282,11 @@
''');
await resolveTestFile();
assertNoTestErrors();
- _assertInvalidInvocation('c.foo();', findElement.getter('foo'));
+ _assertInvalidInvocation(
+ 'c.foo();',
+ findElement.getter('foo'),
+ expectedMethodNameType: '() → dynamic',
+ );
}
test_error_invocationOfNonFunction_OK_dynamicGetter_superClass() async {
@@ -299,7 +303,11 @@
''');
await resolveTestFile();
assertNoTestErrors();
- _assertInvalidInvocation('foo();', findElement.getter('foo'));
+ _assertInvalidInvocation(
+ 'foo();',
+ findElement.getter('foo'),
+ expectedMethodNameType: '() → dynamic',
+ );
}
test_error_invocationOfNonFunction_OK_dynamicGetter_thisClass() async {
@@ -314,7 +322,11 @@
''');
await resolveTestFile();
assertNoTestErrors();
- _assertInvalidInvocation('foo();', findElement.getter('foo'));
+ _assertInvalidInvocation(
+ 'foo();',
+ findElement.getter('foo'),
+ expectedMethodNameType: '() → dynamic',
+ );
}
test_error_invocationOfNonFunction_OK_Function() async {
@@ -346,6 +358,7 @@
findNode.methodInvocation('foo(0)'),
findElement.getter('foo'),
'(int) → double',
+ expectedMethodNameType: '() → T',
);
}
@@ -366,6 +379,7 @@
_assertInvalidInvocation(
'foo()',
findElement.getter('foo'),
+ expectedMethodNameType: '() → int',
);
}
@@ -386,6 +400,7 @@
_assertInvalidInvocation(
'foo()',
findElement.getter('foo'),
+ expectedMethodNameType: '() → int',
);
}
@@ -408,6 +423,7 @@
_assertInvalidInvocation(
'foo()',
findElement.getter('foo'),
+ expectedMethodNameType: '() → int',
);
}
@@ -822,6 +838,7 @@
'c.foo()',
findElement.getter('foo'),
expectedNameType: 'void',
+ expectedMethodNameType: '() → void',
);
}
@@ -878,6 +895,7 @@
'foo()',
findElement.topGet('foo'),
expectedNameType: 'void',
+ expectedMethodNameType: '() → void',
);
}
@@ -996,6 +1014,7 @@
invocation,
findElement.getter('foo'),
'(int) → double',
+ expectedMethodNameType: '() → (int) → double',
);
assertClassRef(invocation.target, findElement.class_('C'));
}
@@ -1113,6 +1132,7 @@
invocation,
import.topGetter('foo'),
'(int, int) → int',
+ expectedMethodNameType: '() → <T>(T, T) → T',
);
assertImportPrefix(invocation.target, import.prefix);
}
@@ -1161,6 +1181,7 @@
invocation,
findElement.getter('foo'),
'(int) → double',
+ expectedMethodNameType: '() → (int) → double',
);
}
@@ -1182,6 +1203,31 @@
invocation,
findElement.method('foo'),
'(int) → void',
+ expectedMethodNameType: '(int) → void',
+ );
+ }
+
+ test_hasReceiver_instance_method_generic() async {
+ addTestFile(r'''
+class C {
+ T foo<T>(T a) {
+ return a;
+ }
+}
+
+main(C c) {
+ c.foo(0);
+}
+''');
+ await resolveTestFile();
+ assertNoTestErrors();
+
+ var invocation = findNode.methodInvocation('foo(0);');
+ assertMethodInvocation(
+ invocation,
+ findElement.method('foo'),
+ '(int) → int',
+ expectedMethodNameType: '(int) → int',
);
}
@@ -1265,6 +1311,7 @@
invocation,
import.class_('C').getGetter('foo'),
'(int) → double',
+ expectedMethodNameType: '() → (int) → double',
);
PrefixedIdentifier target = invocation.target;
@@ -1323,6 +1370,7 @@
invocation,
findElement.getter('foo', of: 'A'),
'(int) → double',
+ expectedMethodNameType: '() → (int) → double',
);
assertSuperExpression(invocation.target);
}
@@ -1392,6 +1440,7 @@
invocation,
findElement.getter('foo'),
'(int) → double',
+ expectedMethodNameType: '() → (int) → double',
);
}
@@ -1413,6 +1462,7 @@
invocation,
findElement.getter('foo'),
'(int) → double',
+ expectedMethodNameType: '() → (int) → double',
);
}
@@ -1570,6 +1620,7 @@
invocation,
findElement.topFunction('foo'),
'(int) → void',
+ expectedMethodNameType: '(int) → void',
);
}
@@ -1589,6 +1640,7 @@
invocation,
findElement.topGet('foo'),
'(int) → double',
+ expectedMethodNameType: '() → (int) → double',
);
}
@@ -1608,6 +1660,7 @@
invocation,
findElement.topGet('foo'),
'(int) → void',
+ expectedMethodNameType: '() → (int) → void',
);
}
@@ -1643,7 +1696,9 @@
}
void _assertInvalidInvocation(String search, Element expectedElement,
- {String expectedNameType, bool dynamicNameType: false}) {
+ {String expectedMethodNameType,
+ String expectedNameType,
+ bool dynamicNameType: false}) {
var invocation = findNode.methodInvocation(search);
if (dynamicNameType) {
assertTypeDynamic(invocation.methodName);
@@ -1653,6 +1708,7 @@
invocation,
expectedElement,
'dynamic',
+ expectedMethodNameType: expectedMethodNameType,
expectedNameType: expectedNameType,
expectedType: 'dynamic',
);
diff --git a/pkg/analyzer/test/src/dart/resolution/resolution.dart b/pkg/analyzer/test/src/dart/resolution/resolution.dart
index a762eb6..52e8428 100644
--- a/pkg/analyzer/test/src/dart/resolution/resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/resolution.dart
@@ -8,6 +8,7 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/error.dart';
+import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/handle.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
@@ -249,7 +250,11 @@
void assertMethodInvocation(MethodInvocation invocation,
Element expectedElement, String expectedInvokeType,
- {String expectedNameType, String expectedType}) {
+ {String expectedMethodNameType,
+ String expectedNameType,
+ String expectedType}) {
+ MethodInvocationImpl invocationImpl = invocation;
+
// TODO(scheglov) Check for Member.
var element = invocation.methodName.staticElement;
if (element is Member) {
@@ -273,6 +278,10 @@
expectedType ??= _extractReturnType(expectedInvokeType);
assertType(invocation, expectedType);
+
+ expectedMethodNameType ??= expectedInvokeType;
+ assertElementTypeString(
+ invocationImpl.methodNameType, expectedMethodNameType);
}
void assertPropertyAccess(