[DAS] Fixes semantic token for `FunctionType.call`

Bug: https://github.com/dart-lang/sdk/issues/61319
Change-Id: I4b42c410ff8e901da546d71658e37a5da6a410aa
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/451620
Auto-Submit: Felipe Morschel <git@fmorschel.dev>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights.dart b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
index 5e2f93d..bd7bdcb 100644
--- a/pkg/analysis_server/lib/src/computer/computer_highlights.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
@@ -19,7 +19,6 @@
     show SemanticTokenInfo;
 import 'package:analysis_server/src/lsp/semantic_tokens/mapping.dart'
     show highlightRegionTokenModifiers, highlightRegionTokenTypes;
-import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/token.dart';
 import 'package:analyzer/dart/ast/visitor.dart';
 import 'package:analyzer/dart/element/element.dart';
@@ -30,6 +29,7 @@
 import 'package:analyzer/src/dart/element/extensions.dart';
 import 'package:analyzer/src/utilities/extensions/ast.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element;
+import 'package:collection/collection.dart';
 
 /// A computer for [HighlightRegion]s and LSP [SemanticTokenInfo] in a Dart [CompilationUnit].
 class DartUnitHighlightsComputer {
@@ -674,16 +674,42 @@
   /// Returns whether [nameToken] is a reference to the `call` method on
   /// a function.
   bool _isCallMethod(AstNode parent, Token nameToken) {
+    late bool enclosingInstanceFunction =
+        switch (parent.enclosingInstanceElement) {
+          ExtensionElement(:var extendedType) => extendedType.isFunction,
+          _ => false,
+        };
+    Expression? expression() => switch (parent) {
+      ExpressionFunctionBody(:var expression) ||
+      ExpressionStatement(:var expression) => expression,
+      ReturnStatement(:var expression) => expression,
+      ArgumentList(:var arguments) => arguments.firstWhereOrNull((argument) {
+        return argument is SimpleIdentifier && argument.token == nameToken;
+      }),
+      AssignmentExpression(:var rightHandSide) => rightHandSide,
+      VariableDeclaration(:var initializer) => initializer,
+      _ => null,
+    };
     return // Invocation
-    parent is MethodInvocation &&
+    (parent is MethodInvocation &&
             parent.methodName.token == nameToken &&
             parent.methodName.name == MethodElement.CALL_METHOD_NAME &&
-            parent.realTarget?.staticType is FunctionType ||
+            ((parent.realTarget?.staticType).isFunction ||
+                enclosingInstanceFunction)) ||
         // Tearoff
         (parent is PrefixedIdentifier &&
             parent.identifier.token == nameToken &&
             parent.identifier.name == MethodElement.CALL_METHOD_NAME &&
-            parent.prefix.staticType is FunctionType);
+            parent.prefix.staticType.isFunction) ||
+        // Property access
+        (parent is PropertyAccess &&
+            parent.propertyName.token == nameToken &&
+            parent.propertyName.name == MethodElement.CALL_METHOD_NAME &&
+            parent.realTarget.staticType.isFunction) ||
+        // Special cases for extension methods
+        (expression() is SimpleIdentifier &&
+            nameToken.lexeme == MethodElement.CALL_METHOD_NAME &&
+            enclosingInstanceFunction);
   }
 
   void _reset() {
@@ -2084,3 +2110,8 @@
     (false, false, false) => Quote.Double,
   };
 }
+
+extension on DartType? {
+  bool get isFunction =>
+      this is FunctionType || (this?.isDartCoreFunction ?? false);
+}
diff --git a/pkg/analysis_server/test/lsp/semantic_tokens_test.dart b/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
index 6aee1a5..943b097 100644
--- a/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
+++ b/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
@@ -1213,6 +1213,148 @@
     await _initializeAndVerifyTokensInRange(content, expected);
   }
 
+  Future<void> test_function_callMethod_invocation_extension() async {
+    var content = r'''
+extension on void Function() {
+  m() => [!call()!];
+}
+''';
+
+    var expected = <_Token>[
+      _Token('call', SemanticTokenTypes.method, [
+        CustomSemanticTokenModifiers.instance,
+      ]),
+    ];
+
+    await _initializeAndVerifyTokensInRange(content, expected);
+  }
+
+  Future<void> test_function_callMethod_propertyAccess() async {
+    var content = r'''
+extension on void Function()? {
+  m() => [!this?.call!];
+}
+''';
+
+    var expected = <_Token>[
+      _Token('this', SemanticTokenTypes.keyword),
+      _Token('call', SemanticTokenTypes.method, [
+        CustomSemanticTokenModifiers.instance,
+      ]),
+    ];
+
+    await _initializeAndVerifyTokensInRange(content, expected);
+  }
+
+  Future<void> test_function_callMethod_simpleIdentifier() async {
+    var content = r'''
+extension on void Function() {
+  m() {
+    [!call!];
+  }
+}
+''';
+
+    var expected = <_Token>[
+      _Token('call', SemanticTokenTypes.method, [
+        CustomSemanticTokenModifiers.instance,
+      ]),
+    ];
+
+    await _initializeAndVerifyTokensInRange(content, expected);
+  }
+
+  Future<void> test_function_callMethod_simpleIdentifier_argument() async {
+    var content = r'''
+extension on void Function() {
+  m(void Function() f) {
+    m([!call!]);
+  }
+}
+''';
+
+    var expected = <_Token>[
+      _Token('call', SemanticTokenTypes.method, [
+        CustomSemanticTokenModifiers.instance,
+      ]),
+    ];
+
+    await _initializeAndVerifyTokensInRange(content, expected);
+  }
+
+  Future<void> test_function_callMethod_simpleIdentifier_assignment() async {
+    var content = r'''
+extension on void Function() {
+  m() {
+    var a;
+    a = [!call!];
+  }
+}
+''';
+
+    var expected = <_Token>[
+      _Token('call', SemanticTokenTypes.method, [
+        CustomSemanticTokenModifiers.instance,
+      ]),
+    ];
+
+    await _initializeAndVerifyTokensInRange(content, expected);
+  }
+
+  Future<void>
+  test_function_callMethod_simpleIdentifier_expressionFunctionBody() async {
+    var content = r'''
+extension on void Function() {
+  m() => [!call!];
+}
+''';
+
+    var expected = <_Token>[
+      _Token('call', SemanticTokenTypes.method, [
+        CustomSemanticTokenModifiers.instance,
+      ]),
+    ];
+
+    await _initializeAndVerifyTokensInRange(content, expected);
+  }
+
+  Future<void> test_function_callMethod_simpleIdentifier_return() async {
+    var content = r'''
+extension on void Function() {
+  m() {
+    return [!call!];
+  }
+}
+''';
+
+    var expected = <_Token>[
+      _Token('call', SemanticTokenTypes.method, [
+        CustomSemanticTokenModifiers.instance,
+      ]),
+    ];
+
+    await _initializeAndVerifyTokensInRange(content, expected);
+  }
+
+  Future<void>
+  test_function_callMethod_simpleIdentifier_variableDeclaration() async {
+    var content = r'''
+extension on void Function() {
+  m() {
+    var _ = [!call!];
+  }
+}
+''';
+
+    var expected = <_Token>[
+      _Token('call', SemanticTokenTypes.method, [
+        CustomSemanticTokenModifiers.instance,
+      ]),
+    ];
+
+    await _initializeAndVerifyTokensInRange(content, expected);
+  }
+
   Future<void> test_function_callMethod_tearOff() async {
     var content = r'''
 f(void Function(int) x) {
@@ -1230,6 +1372,39 @@
     await _initializeAndVerifyTokensInRange(content, expected);
   }
 
+  Future<void> test_functionType_callMethod_invocation_extension() async {
+    var content = r'''
+extension on Function {
+  m() => [!call()!];
+}
+''';
+
+    var expected = <_Token>[
+      _Token('call', SemanticTokenTypes.method, [
+        CustomSemanticTokenModifiers.instance,
+      ]),
+    ];
+
+    await _initializeAndVerifyTokensInRange(content, expected);
+  }
+
+  Future<void> test_functionType_callMethod_tearOff() async {
+    var content = r'''
+f(Function x) {
+  [!x.call!];
+}
+''';
+
+    var expected = [
+      _Token('x', SemanticTokenTypes.parameter),
+      _Token('call', SemanticTokenTypes.method, [
+        CustomSemanticTokenModifiers.instance,
+      ]),
+    ];
+
+    await _initializeAndVerifyTokensInRange(content, expected);
+  }
+
   /// Verify that sending a semantic token request immediately after an overlay
   /// update (with no delay) does not result in corrupt semantic tokens because
   /// the previous file content was used.