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>
This commit is contained in:
Konstantin Shcheglov 2019-01-03 16:57:22 +00:00 committed by commit-bot@chromium.org
parent c32737cc4c
commit d200089583
4 changed files with 107 additions and 24 deletions

View file

@ -7766,15 +7766,10 @@ class MethodInvocationImpl extends InvocationExpressionImpl
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 @@ class MethodInvocationImpl extends InvocationExpressionImpl
_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;

View file

@ -139,9 +139,14 @@ class MethodInvocationResolver {
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 @@ class MethodInvocationResolver {
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 @@ class MethodInvocationResolver {
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 @@ class MethodInvocationResolver {
if (targetType != null) {
nameNode.staticElement = targetType.element;
var calleeType = _getCalleeType(targetType);
var calleeType = _getCalleeType(node, targetType);
return _setResolution(node, calleeType);
}
@ -456,7 +459,7 @@ class MethodInvocationResolver {
}
if (element is ExecutableElement) {
var calleeType = _getCalleeType(element.type);
var calleeType = _getCalleeType(node, element.type);
return _setResolution(node, calleeType);
}
@ -479,7 +482,7 @@ class MethodInvocationResolver {
// 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 @@ class MethodInvocationResolver {
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 @@ class MethodInvocationResolver {
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);

View file

@ -282,7 +282,11 @@ main(C c) {
''');
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 @@ class B extends A {
''');
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 @@ class C {
''');
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 @@ class C<T extends MyFunction> {
findNode.methodInvocation('foo(0)'),
findElement.getter('foo'),
'(int) → double',
expectedMethodNameType: '() → T',
);
}
@ -366,6 +379,7 @@ main() {
_assertInvalidInvocation(
'foo()',
findElement.getter('foo'),
expectedMethodNameType: '() → int',
);
}
@ -386,6 +400,7 @@ class C {
_assertInvalidInvocation(
'foo()',
findElement.getter('foo'),
expectedMethodNameType: '() → int',
);
}
@ -408,6 +423,7 @@ class B extends A {
_assertInvalidInvocation(
'foo()',
findElement.getter('foo'),
expectedMethodNameType: '() → int',
);
}
@ -822,6 +838,7 @@ main(C<void> c) {
'c.foo()',
findElement.getter('foo'),
expectedNameType: 'void',
expectedMethodNameType: '() → void',
);
}
@ -878,6 +895,7 @@ main() {
'foo()',
findElement.topGet('foo'),
expectedNameType: 'void',
expectedMethodNameType: '() → void',
);
}
@ -996,6 +1014,7 @@ main() {
invocation,
findElement.getter('foo'),
'(int) → double',
expectedMethodNameType: '() → (int) → double',
);
assertClassRef(invocation.target, findElement.class_('C'));
}
@ -1113,6 +1132,7 @@ main() {
invocation,
import.topGetter('foo'),
'(int, int) → int',
expectedMethodNameType: '() → <T>(T, T) → T',
);
assertImportPrefix(invocation.target, import.prefix);
}
@ -1161,6 +1181,7 @@ main(C c) {
invocation,
findElement.getter('foo'),
'(int) → double',
expectedMethodNameType: '() → (int) → double',
);
}
@ -1182,6 +1203,31 @@ main(C c) {
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 @@ main() {
invocation,
import.class_('C').getGetter('foo'),
'(int) → double',
expectedMethodNameType: '() → (int) → double',
);
PrefixedIdentifier target = invocation.target;
@ -1323,6 +1370,7 @@ class B extends A {
invocation,
findElement.getter('foo', of: 'A'),
'(int) → double',
expectedMethodNameType: '() → (int) → double',
);
assertSuperExpression(invocation.target);
}
@ -1392,6 +1440,7 @@ class B extends A {
invocation,
findElement.getter('foo'),
'(int) → double',
expectedMethodNameType: '() → (int) → double',
);
}
@ -1413,6 +1462,7 @@ class C {
invocation,
findElement.getter('foo'),
'(int) → double',
expectedMethodNameType: '() → (int) → double',
);
}
@ -1570,6 +1620,7 @@ main() {
invocation,
findElement.topFunction('foo'),
'(int) → void',
expectedMethodNameType: '(int) → void',
);
}
@ -1589,6 +1640,7 @@ main() {
invocation,
findElement.topGet('foo'),
'(int) → double',
expectedMethodNameType: '() → (int) → double',
);
}
@ -1608,6 +1660,7 @@ main() {
invocation,
findElement.topGet('foo'),
'(int) → void',
expectedMethodNameType: '() → (int) → void',
);
}
@ -1643,7 +1696,9 @@ main() {
}
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 @@ main() {
invocation,
expectedElement,
'dynamic',
expectedMethodNameType: expectedMethodNameType,
expectedNameType: expectedNameType,
expectedType: 'dynamic',
);

View file

@ -8,6 +8,7 @@ import 'package:analyzer/dart/ast/ast.dart';
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 @@ mixin ResolutionTest implements ResourceProviderMixin {
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 @@ mixin ResolutionTest implements ResourceProviderMixin {
expectedType ??= _extractReturnType(expectedInvokeType);
assertType(invocation, expectedType);
expectedMethodNameType ??= expectedInvokeType;
assertElementTypeString(
invocationImpl.methodNameType, expectedMethodNameType);
}
void assertPropertyAccess(