Skip to content

Commit 9181df9

Browse files
DanTupcommit-bot@chromium.org
authored andcommitted
[Analyzer] Fix error in LSP go-to-definition when target is in a "part"
Plus unrelated navigation regions (for example in comments, references outside of the required range would be returned). Fixes dart-lang/sdk#28799. Change-Id: I2fce33a3bd2678abfb40e5e9b3e3c5662e4f5608 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/161948 Commit-Queue: Danny Tuppeny <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 743a1d0 commit 9181df9

File tree

4 files changed

+114
-21
lines changed

4 files changed

+114
-21
lines changed

pkg/analysis_server/test/analysis/get_navigation_test.dart

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,29 @@ main() {
4343
assertHasTarget('test = 0');
4444
}
4545

46+
Future<void> test_comment_outsideReference() async {
47+
addTestFile('''
48+
/// Returns a [String].
49+
String main() {
50+
}''');
51+
await waitForTasksFinished();
52+
var search = 'Returns';
53+
await _getNavigation(testFile, testCode.indexOf(search), 1);
54+
expect(regions, hasLength(0));
55+
}
56+
57+
Future<void> test_comment_reference() async {
58+
addTestFile('''
59+
/// Returns a [String].
60+
String main() {
61+
}''');
62+
await waitForTasksFinished();
63+
var search = '[String';
64+
await _getNavigation(testFile, testCode.indexOf(search), 1);
65+
expect(regions, hasLength(1));
66+
assertHasRegion('String]');
67+
}
68+
4669
Future<void> test_fieldType() async {
4770
// This test mirrors test_navigation() from
4871
// test/integration/analysis/get_navigation_test.dart
@@ -101,20 +124,6 @@ main() {
101124
expect(testTargets[0].kind, ElementKind.LIBRARY);
102125
}
103126

104-
Future<void> test_importKeyword() async {
105-
addTestFile('''
106-
import 'dart:math';
107-
108-
main() {
109-
}''');
110-
await waitForTasksFinished();
111-
await _getNavigation(testFile, 0, 1);
112-
expect(regions, hasLength(1));
113-
assertHasRegionString("'dart:math'");
114-
expect(testTargets, hasLength(1));
115-
expect(testTargets[0].kind, ElementKind.LIBRARY);
116-
}
117-
118127
Future<void> test_importUri() async {
119128
addTestFile('''
120129
import 'dart:math';

pkg/analysis_server/test/integration/analysis/get_navigation_test.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,7 @@ class Bar {
4141
expect(target.startColumn, 7);
4242
}
4343

44-
@failingTest
4544
Future<void> test_navigation_no_result() async {
46-
// This fails - it returns navigation results for a whitespace area (#28799).
4745
var pathname = sourcePath('test.dart');
4846
var text = r'''
4947
//

pkg/analysis_server/test/lsp/definition_test.dart

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,26 @@ class DefinitionTest extends AbstractLspAnalysisServerTest {
5555
expect(loc.uri, equals(referencedFileUri.toString()));
5656
}
5757

58+
Future<void> test_comment_adjacentReference() async {
59+
/// Computing Dart navigation locates a node at the provided offset then
60+
/// returns all navigation regions inside it. This test ensures we filter
61+
/// out any regions that are in the same target node (the comment) but do
62+
/// not span the requested offset.
63+
final contents = '''
64+
/// Te^st
65+
///
66+
/// References [String].
67+
main() {}
68+
''';
69+
70+
await initialize();
71+
await openFile(mainFileUri, withoutMarkers(contents));
72+
final res = await getDefinitionAsLocation(
73+
mainFileUri, positionFromMarker(contents));
74+
75+
expect(res, hasLength(0));
76+
}
77+
5878
Future<void> test_fromPlugins() async {
5979
final pluginAnalyzedFilePath = join(projectFolderPath, 'lib', 'foo.foo');
6080
final pluginAnalyzedFileUri = Uri.file(pluginAnalyzedFilePath);
@@ -176,6 +196,59 @@ class DefinitionTest extends AbstractLspAnalysisServerTest {
176196
expect(res, isEmpty);
177197
}
178198

199+
/// Failing due to incorrect range because _DartNavigationCollector._getCodeLocation
200+
/// does not handle parts.
201+
@failingTest
202+
Future<void> test_part() async {
203+
final mainContents = '''
204+
import 'lib.dart';
205+
206+
main() {
207+
Icons.[[ad^d]]();
208+
}
209+
''';
210+
211+
final libContents = '''
212+
part 'part.dart';
213+
''';
214+
215+
final partContents = '''
216+
part of 'lib.dart';
217+
218+
void unrelatedFunction() {}
219+
220+
class Icons {
221+
/// `targetRange` should not include the dartDoc but should include the full
222+
/// function body. `targetSelectionRange` will be just the name.
223+
[[String add = "Test"]];
224+
}
225+
226+
void otherUnrelatedFunction() {}
227+
''';
228+
229+
final libFileUri = Uri.file(join(projectFolderPath, 'lib', 'lib.dart'));
230+
final partFileUri = Uri.file(join(projectFolderPath, 'lib', 'part.dart'));
231+
232+
await initialize(
233+
textDocumentCapabilities:
234+
withLocationLinkSupport(emptyTextDocumentClientCapabilities));
235+
await openFile(mainFileUri, withoutMarkers(mainContents));
236+
await openFile(libFileUri, withoutMarkers(libContents));
237+
await openFile(partFileUri, withoutMarkers(partContents));
238+
final res = await getDefinitionAsLocationLinks(
239+
mainFileUri, positionFromMarker(mainContents));
240+
241+
expect(res, hasLength(1));
242+
var loc = res.single;
243+
expect(loc.originSelectionRange, equals(rangeFromMarkers(mainContents)));
244+
expect(loc.targetUri, equals(partFileUri.toString()));
245+
expect(loc.targetRange, equals(rangeFromMarkers(partContents)));
246+
expect(
247+
loc.targetSelectionRange,
248+
equals(rangeOfString(partContents, 'add')),
249+
);
250+
}
251+
179252
Future<void> test_sameLine() async {
180253
final contents = '''
181254
int plusOne(int [[value]]) => 1 + val^ue;

pkg/analyzer_plugin/lib/utilities/navigation/navigation_dart.dart

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ NavigationCollector computeDartNavigation(
1919
CompilationUnit unit,
2020
int offset,
2121
int length) {
22-
var dartCollector = _DartNavigationCollector(collector);
22+
var dartCollector = _DartNavigationCollector(collector, offset, length);
2323
var visitor = _DartNavigationComputerVisitor(resourceProvider, dartCollector);
2424
if (offset == null || length == null) {
2525
unit.accept(visitor);
@@ -43,8 +43,11 @@ AstNode _getNodeForRange(CompilationUnit unit, int offset, int length) {
4343
/// A Dart specific wrapper around [NavigationCollector].
4444
class _DartNavigationCollector {
4545
final NavigationCollector collector;
46+
final int requestedOffset;
47+
final int requestedLength;
4648

47-
_DartNavigationCollector(this.collector);
49+
_DartNavigationCollector(
50+
this.collector, this.requestedOffset, this.requestedLength);
4851

4952
void _addRegion(int offset, int length, Element element) {
5053
if (element is FieldFormalParameterElement) {
@@ -56,6 +59,12 @@ class _DartNavigationCollector {
5659
if (element.location == null) {
5760
return;
5861
}
62+
// Discard elements that don't span the offset/range given (if provided).
63+
if (requestedOffset != null &&
64+
(offset > requestedOffset + (requestedLength ?? 0) ||
65+
offset + length < requestedOffset)) {
66+
return;
67+
}
5968
var converter = AnalyzerConverter();
6069
var kind = converter.convertElementKind(element.kind);
6170
var location = converter.locationFromElement(element);
@@ -118,9 +127,13 @@ class _DartNavigationCollector {
118127
}
119128

120129
// Read the declaration so we can get the offset after the doc comments.
121-
final declaration = codeElement.session
122-
.getParsedLibrary(location.file)
123-
.getElementDeclaration(codeElement);
130+
// TODO(dantup): Skip this for parts (getParsedLibrary will throw), but find
131+
// a better solution.
132+
final declaration = !codeElement.session.getFile(location.file).isPart
133+
? codeElement.session
134+
.getParsedLibrary(location.file)
135+
.getElementDeclaration(codeElement)
136+
: null;
124137
var node = declaration?.node;
125138
if (node is VariableDeclaration) {
126139
node = node.parent;

0 commit comments

Comments
 (0)