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(