Fix #33194, handle type parameters on implicit call tearoffs.

Bug: 33194
Change-Id: Iecf3d2bdf1dcd1a05ef128f10cd2fa374f9b47ca
Reviewed-on: https://dart-review.googlesource.com/64822
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Jenny Messerly <jmesserly@google.com>
Commit-Queue: Mike Fairhurst <mfairhurst@google.com>
diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart
index ac71287..56213b8 100644
--- a/pkg/analyzer/lib/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/dart/ast/ast.dart
@@ -520,6 +520,11 @@
   E accept<E>(AstVisitor<E> visitor);
 
   /**
+   * Return the token before [target] or `null` if it cannot be found.
+   */
+  Token findPrevious(Token target);
+
+  /**
    * Return the most immediate ancestor of this node for which the [predicate]
    * returns `true`, or `null` if there is no such ancestor. Note that this node
    * will never be returned.
@@ -533,11 +538,6 @@
   E getProperty<E>(String name);
 
   /**
-   * Return the token before [target] or `null` if it cannot be found.
-   */
-  Token findPrevious(Token target);
-
-  /**
    * Set the value of the property with the given [name] to the given [value].
    * If the value is `null`, the property will effectively be removed.
    */
@@ -4469,9 +4469,9 @@
    * information, or `null` if the AST structure has not been resolved, or if
    * the invoke could not be resolved.
    *
-   * This will usually be a [FunctionType], but it can also be an
-   * [InterfaceType] with a `call` method, `dynamic`, `Function`, or a `@proxy`
-   * interface type that implements `Function`.
+   * This will usually be a [FunctionType], but it can also be `dynamic` or
+   * `Function`. In the case of interface types that have a `call` method, we
+   * store the type of that `call` method here as parameterized.
    */
   DartType get staticInvokeType;
 
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index 94c6855..a782664 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -1622,33 +1622,45 @@
    */
   DartType _instantiateGenericMethod(
       DartType invokeType, TypeArgumentList typeArguments, AstNode node) {
-    // TODO(jmesserly): support generic "call" methods on InterfaceType.
+    DartType parameterizableType;
+    List<TypeParameterElement> parameters;
     if (invokeType is FunctionType) {
-      List<TypeParameterElement> parameters = invokeType.typeFormals;
+      parameterizableType = invokeType;
+      parameters = invokeType.typeFormals;
+    } else if (invokeType is InterfaceType) {
+      MethodElement callMethod = invokeType.lookUpMethod(
+          FunctionElement.CALL_METHOD_NAME, _resolver.definingLibrary);
+      parameterizableType = callMethod?.type;
+      parameters = (parameterizableType as FunctionType)?.typeFormals;
+    }
 
+    if (parameterizableType is ParameterizedType) {
       NodeList<TypeAnnotation> arguments = typeArguments?.arguments;
       if (arguments != null && arguments.length != parameters.length) {
         if (_resolver.strongMode) {
           _resolver.errorReporter.reportErrorForNode(
               StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD,
               node,
-              [invokeType, parameters.length, arguments?.length ?? 0]);
+              [parameterizableType, parameters.length, arguments?.length ?? 0]);
         } else {
           _resolver.errorReporter.reportErrorForNode(
               HintCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD,
               node,
-              [invokeType, parameters.length, arguments?.length ?? 0]);
+              [parameterizableType, parameters.length, arguments?.length ?? 0]);
         }
         // Wrong number of type arguments. Ignore them.
         arguments = null;
       }
       if (parameters.isNotEmpty) {
         if (arguments == null) {
-          return _resolver.typeSystem.instantiateToBounds(invokeType);
+          return _resolver.typeSystem.instantiateToBounds(parameterizableType);
         } else {
-          return invokeType.instantiate(arguments.map((n) => n.type).toList());
+          return parameterizableType
+              .instantiate(arguments.map((n) => n.type).toList());
         }
       }
+
+      return parameterizableType;
     }
     return invokeType;
   }
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 1371020..25343f2 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -467,10 +467,10 @@
     _checkForAbstractSuperMemberReference(realTarget, node.methodName);
     _checkForNullAwareHints(node, node.operator);
     DartType staticInvokeType = node.staticInvokeType;
-    if (staticInvokeType is InterfaceType) {
-      MethodElement methodElement = staticInvokeType.lookUpMethod(
-          FunctionElement.CALL_METHOD_NAME, _currentLibrary);
-      _checkForDeprecatedMemberUse(methodElement, node);
+    Element callElement = staticInvokeType?.element;
+    if (callElement is MethodElement &&
+        callElement.name == FunctionElement.CALL_METHOD_NAME) {
+      _checkForDeprecatedMemberUse(callElement, node);
     }
     return super.visitMethodInvocation(node);
   }
@@ -793,9 +793,8 @@
       } else if (displayName == FunctionElement.CALL_METHOD_NAME &&
           node is MethodInvocation &&
           node.staticInvokeType is InterfaceType) {
-        displayName = "${resolutionMap
-            .staticInvokeTypeForInvocationExpression(node)
-            .displayName}.${element.displayName}";
+        displayName =
+            "${resolutionMap.staticInvokeTypeForInvocationExpression(node).displayName}.${element.displayName}";
       }
       _errorReporter.reportErrorForNode(
           HintCode.DEPRECATED_MEMBER_USE, node, [displayName]);
@@ -4706,8 +4705,7 @@
       typeAnalyzer.thisType = enclosingClass?.type;
       if (enclosingClass == null) {
         AnalysisEngine.instance.logger.logInformation(
-            "Missing element for class declaration ${node.name
-                .name} in ${definingLibrary.source.fullName}",
+            "Missing element for class declaration ${node.name.name} in ${definingLibrary.source.fullName}",
             new CaughtException(new AnalysisException(), null));
         // Don't try to re-resolve the initializers if we cannot set up the
         // right name scope for resolution.
@@ -7443,8 +7441,7 @@
     try {
       if (classElement == null) {
         AnalysisEngine.instance.logger.logInformation(
-            "Missing element for class declaration ${node.name
-                .name} in ${definingLibrary.source.fullName}",
+            "Missing element for class declaration ${node.name.name} in ${definingLibrary.source.fullName}",
             new CaughtException(new AnalysisException(), null));
         super.visitClassDeclaration(node);
       } else {
@@ -7572,8 +7569,7 @@
     try {
       if (classElement == null) {
         AnalysisEngine.instance.logger.logInformation(
-            "Missing element for enum declaration ${node.name
-                .name} in ${definingLibrary.source.fullName}",
+            "Missing element for enum declaration ${node.name.name} in ${definingLibrary.source.fullName}",
             new CaughtException(new AnalysisException(), null));
         super.visitEnumDeclaration(node);
       } else {
@@ -7689,8 +7685,7 @@
     try {
       if (functionElement == null) {
         AnalysisEngine.instance.logger.logInformation(
-            "Missing element for top-level function ${node.name
-                .name} in ${definingLibrary.source.fullName}",
+            "Missing element for top-level function ${node.name.name} in ${definingLibrary.source.fullName}",
             new CaughtException(new AnalysisException(), null));
       } else {
         nameScope = new FunctionScope(nameScope, functionElement);
@@ -7766,8 +7761,7 @@
       ParameterElement parameterElement = node.element;
       if (parameterElement == null) {
         AnalysisEngine.instance.logger.logInformation(
-            "Missing element for function typed formal parameter ${node
-                .identifier.name} in ${definingLibrary.source.fullName}",
+            "Missing element for function typed formal parameter ${node.identifier.name} in ${definingLibrary.source.fullName}",
             new CaughtException(new AnalysisException(), null));
       } else {
         nameScope = new EnclosedScope(nameScope);
@@ -7868,8 +7862,7 @@
       ExecutableElement methodElement = node.element;
       if (methodElement == null) {
         AnalysisEngine.instance.logger.logInformation(
-            "Missing element for method ${node.name.name} in ${definingLibrary
-                .source.fullName}",
+            "Missing element for method ${node.name.name} in ${definingLibrary.source.fullName}",
             new CaughtException(new AnalysisException(), null));
       } else {
         nameScope = new FunctionScope(nameScope, methodElement);
diff --git a/pkg/analyzer/test/generated/non_error_resolver_test.dart b/pkg/analyzer/test/generated/non_error_resolver_test.dart
index 4992e56..c47e992 100644
--- a/pkg/analyzer/test/generated/non_error_resolver_test.dart
+++ b/pkg/analyzer/test/generated/non_error_resolver_test.dart
@@ -4966,6 +4966,21 @@
     verify([source]);
   }
 
+  test_parametricCallFunction() async {
+    Source source = addSource(r'''
+f() {
+  var c = new C();
+  c<String>().codeUnits;
+}
+
+class C {
+  T call<T>() => null;
+}''');
+    await computeAnalysisResult(source);
+    assertNoErrors(source);
+    verify([source]);
+  }
+
   test_prefixCollidesWithTopLevelMembers() async {
     addNamedSource("/lib.dart", r'''
 library lib;