Skip to content

Commit a0eb8c5

Browse files
DanTupCommit Queue
authored andcommitted
[analysis_server] Fix Call Hierarchy target selection for tear-offs
This fixes `_findTargetNode` on function tear-offs. Previously it would take the parent node of many `SimpleIdentifiers` which in the case of a tear-off could be some random expression. Now we only return something other than the `node` in some very specific cases where we're trying to handle constructors (so that the type name is not considered a call for named constructors, but is considered the constructor for unnamed constructors). Fixes #61736 Change-Id: I815d4dc8e1f1eaadd5ee033abf9731666944e45e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/457061 Reviewed-by: Brian Wilkerson <[email protected]> Reviewed-by: Samuel Rawlins <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent ff30536 commit a0eb8c5

File tree

3 files changed

+123
-23
lines changed

3 files changed

+123
-23
lines changed

pkg/analysis_server/lib/src/computer/computer_call_hierarchy.dart

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'package:analyzer/dart/ast/ast.dart';
99
import 'package:analyzer/dart/ast/visitor.dart';
1010
import 'package:analyzer/dart/element/element.dart';
1111
import 'package:analyzer/source/source_range.dart';
12+
import 'package:analyzer/src/dart/ast/ast.dart';
1213
import 'package:analyzer/src/dart/ast/element_locator.dart';
1314
import 'package:analyzer/src/dart/element/element.dart';
1415

@@ -342,32 +343,23 @@ class DartCallHierarchyComputer {
342343
/// Finds a target node for call hierarchy navigation at [offset].
343344
AstNode? _findTargetNode(int offset) {
344345
var node = _result.unit.nodeCovering(offset: offset);
345-
if (node is SimpleIdentifier &&
346-
node.parent != null &&
347-
node.parent is! VariableDeclaration &&
348-
node.parent is! AssignmentExpression) {
349-
node = node.parent;
350-
}
351346

352347
// For consistency with other places, we only treat the type name as the
353348
// constructor for unnamed constructor (since we use the constructors name
354-
// as the target).
355-
if (node is NamedType) {
356-
var parent = node.parent;
357-
if (parent is ConstructorName) {
358-
var name = parent.name;
359-
if (name != null && offset < name.offset) {
360-
return null;
361-
}
362-
}
363-
} else if (node is ConstructorDeclaration) {
364-
var name = node.name;
365-
if (name != null && offset < name.offset) {
366-
return null;
367-
}
368-
}
369-
370-
return node;
349+
// as the target otherwise).
350+
return switch (node) {
351+
// Type name in a named constructor reference, not considered a call.
352+
NamedType(parent: ConstructorName(:var name?))
353+
when offset < name.offset =>
354+
null,
355+
// Type name in a named constructor declaration, not considered a call.
356+
Identifier(parent: ConstructorDeclaration(:var name?))
357+
when offset < name.offset =>
358+
null,
359+
// Type name in an unnamed constructor declaration, use the constructor.
360+
Identifier(parent: ConstructorDeclaration(name: null)) => node.parent,
361+
_ => node,
362+
};
371363
}
372364

373365
/// Return the [Element] of the given [node], or `null` if [node] is `null`,

pkg/analysis_server/test/lsp/call_hierarchy_test.dart

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,6 +1500,60 @@ void myFunction() {}
15001500
);
15011501
}
15021502

1503+
Future<void> test_functionTearOff() async {
1504+
var mainCode = TestCode.parse('''
1505+
import 'other.dart';
1506+
1507+
void main() {
1508+
myFunct^ion;
1509+
}
1510+
''');
1511+
1512+
var otherCode = TestCode.parse('''
1513+
void myFunction() {}
1514+
''');
1515+
1516+
await expectResults(
1517+
mainCode: mainCode,
1518+
otherCode: otherCode,
1519+
expectedResult: CallHierarchyItem(
1520+
name: 'myFunction',
1521+
detail: 'other.dart', // Containing file name
1522+
kind: SymbolKind.Function,
1523+
uri: otherFileUri,
1524+
range: rangeOfString(otherCode, 'void myFunction() {}'),
1525+
selectionRange: rangeOfString(otherCode, 'myFunction'),
1526+
),
1527+
);
1528+
}
1529+
1530+
Future<void> test_functionTearOff_prefixed() async {
1531+
var mainCode = TestCode.parse('''
1532+
import 'other.dart' as other;
1533+
1534+
void main() {
1535+
other.myFunct^ion;
1536+
}
1537+
''');
1538+
1539+
var otherCode = TestCode.parse('''
1540+
void myFunction() {}
1541+
''');
1542+
1543+
await expectResults(
1544+
mainCode: mainCode,
1545+
otherCode: otherCode,
1546+
expectedResult: CallHierarchyItem(
1547+
name: 'myFunction',
1548+
detail: 'other.dart', // Containing file name
1549+
kind: SymbolKind.Function,
1550+
uri: otherFileUri,
1551+
range: rangeOfString(otherCode, 'void myFunction() {}'),
1552+
selectionRange: rangeOfString(otherCode, 'myFunction'),
1553+
),
1554+
);
1555+
}
1556+
15031557
Future<void> test_implicitConstructorCall() async {
15041558
// Even if a constructor is implicit, we might want to be able to get the
15051559
// incoming calls, so invoking it here should still return an element

pkg/analysis_server/test/src/computer/call_hierarchy_computer_test.dart

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,60 @@ void f() {
596596
);
597597
}
598598

599+
Future<void> test_functionTearOff() async {
600+
var code = parseCode('''
601+
import 'other.dart';
602+
603+
void f() {
604+
myFun^ction;
605+
}
606+
''');
607+
608+
var otherCode = parseCode('''
609+
[!void myFunction() {}!]
610+
''');
611+
612+
newFile(otherFile, otherCode.code);
613+
await expectTarget(
614+
code,
615+
_isItem(
616+
CallHierarchyKind.function,
617+
'myFunction',
618+
otherFile,
619+
containerName: 'other.dart',
620+
nameRange: rangeAtSearch('myFunction', otherCode),
621+
codeRange: otherCode.range.sourceRange,
622+
),
623+
);
624+
}
625+
626+
Future<void> test_functionTearOff_prefixed() async {
627+
var code = parseCode('''
628+
import 'other.dart' as other;
629+
630+
void f() {
631+
other.myFun^ction;
632+
}
633+
''');
634+
635+
var otherCode = parseCode('''
636+
[!void myFunction() {}!]
637+
''');
638+
639+
newFile(otherFile, otherCode.code);
640+
await expectTarget(
641+
code,
642+
_isItem(
643+
CallHierarchyKind.function,
644+
'myFunction',
645+
otherFile,
646+
containerName: 'other.dart',
647+
nameRange: rangeAtSearch('myFunction', otherCode),
648+
codeRange: otherCode.range.sourceRange,
649+
),
650+
);
651+
}
652+
599653
Future<void> test_getter() async {
600654
var code = parseCode('''
601655
class Foo {

0 commit comments

Comments
 (0)