Skip to content

Commit 8513b16

Browse files
srawlinscommit-bot@chromium.org
authored andcommitted
NNBD i13n: Fix some navigation targets and text
Change-Id: Iba6befb86453353823c1702405057f83fff64456 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/120723 Commit-Queue: Samuel Rawlins <srawlins@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
1 parent 8bf424e commit 8513b16

File tree

3 files changed

+117
-1
lines changed

3 files changed

+117
-1
lines changed

pkg/analysis_server/lib/src/edit/nnbd_migration/info_builder.dart

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ class InfoBuilder {
143143
return "This variable is initialized to null";
144144
}
145145
return "This variable is initialized to a nullable value";
146+
} else if (parent is AsExpression) {
147+
return "The value of the expression is nullable";
146148
}
147149
if (node is NullLiteral) {
148150
return "An explicit 'null' is assigned";
@@ -321,7 +323,25 @@ class InfoBuilder {
321323
/// Return the navigation target corresponding to the given [node] in the file
322324
/// with the given [filePath].
323325
NavigationTarget _targetForNode(String filePath, AstNode node) {
324-
return _targetFor(filePath, node.offset, node.length);
326+
AstNode parent = node.parent;
327+
if (node is MethodDeclaration) {
328+
// Rather than create a NavigationTarget for an entire method declaration
329+
// (starting at its doc comment, ending at `}`, return a target pointing
330+
// to the method's name.
331+
return _targetFor(filePath, node.name.offset, node.name.length);
332+
} else if (parent is ReturnStatement) {
333+
// Rather than create a NavigationTarget for an entire expression, return
334+
// a target pointing to the `return` token.
335+
return _targetFor(
336+
filePath, parent.returnKeyword.offset, parent.returnKeyword.length);
337+
} else if (parent is ExpressionFunctionBody) {
338+
// Rather than create a NavigationTarget for an entire expression function
339+
// body, return a target pointing to the `=>` token.
340+
return _targetFor(filePath, parent.functionDefinition.offset,
341+
parent.functionDefinition.length);
342+
} else {
343+
return _targetFor(filePath, node.offset, node.length);
344+
}
325345
}
326346

327347
/// Return the unit info for the file at the given [path].

pkg/analysis_server/lib/src/edit/nnbd_migration/migration_info.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ class NavigationTarget extends NavigationRegion {
5555
other.offset == offset &&
5656
other.length == length;
5757
}
58+
59+
@override
60+
String toString() => 'NavigationTarget["$filePath", $offset, $length]';
5861
}
5962

6063
/// An additional detail related to a region.

pkg/analysis_server/test/src/edit/nnbd_migration/info_builder_test.dart

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,29 @@ class InfoBuilderTest extends AbstractAnalysisTest {
2828
/// not yet completed.
2929
List<UnitInfo> infos;
3030

31+
/// Assert various properties of the given [detail].
32+
void assertDetail({@required RegionDetail detail, int offset, int length}) {
33+
if (offset != null) {
34+
expect(detail.target.offset, offset);
35+
}
36+
if (length != null) {
37+
expect(detail.target.length, length);
38+
}
39+
}
40+
41+
/// Assert that some target in [targets] has various properties.
42+
void assertInTargets(
43+
{@required Iterable<NavigationTarget> targets, int offset, int length}) {
44+
String failureReasons = [
45+
if (offset != null) 'offset: $offset',
46+
if (length != null) 'length: $length',
47+
].join(' and ');
48+
expect(targets.any((t) {
49+
return (offset == null || offset == t.offset) &&
50+
(length == null || length == t.length);
51+
}), isTrue, reason: 'Expected one of $targets to contain $failureReasons');
52+
}
53+
3154
/// Assert various properties of the given [region]. If an [offset] is
3255
/// provided but no [length] is provided, a default length of `1` will be
3356
/// used.
@@ -72,6 +95,52 @@ class InfoBuilderTest extends AbstractAnalysisTest {
7295
infos = await builder.explainMigration();
7396
}
7497

98+
test_asExpression() async {
99+
addTestFile('''
100+
void f([num a]) {
101+
int b = a as int;
102+
}
103+
''');
104+
await buildInfo();
105+
UnitInfo unit = infos[0];
106+
expect(unit.content, '''
107+
void f([num? a]) {
108+
int? b = a as int?;
109+
}
110+
''');
111+
List<RegionInfo> regions = unit.regions;
112+
expect(regions, hasLength(3));
113+
assertRegion(region: regions[0], offset: 11);
114+
assertRegion(
115+
region: regions[1],
116+
offset: 24,
117+
details: ["This variable is initialized to a nullable value"]);
118+
assertRegion(
119+
region: regions[2],
120+
offset: 38,
121+
details: ["The value of the expression is nullable"]);
122+
}
123+
124+
test_expressionFunctionReturnTarget() async {
125+
addTestFile('''
126+
String g() => 1 == 2 ? "Hello" : null;
127+
''');
128+
await buildInfo();
129+
UnitInfo unit = infos[0];
130+
expect(unit.content, '''
131+
String? g() => 1 == 2 ? "Hello" : null;
132+
''');
133+
assertInTargets(targets: unit.targets, offset: 7, length: 1); // "g"
134+
assertInTargets(targets: unit.targets, offset: 11, length: 2); // "=>"
135+
List<RegionInfo> regions = unit.regions;
136+
expect(regions, hasLength(1));
137+
assertRegion(
138+
region: regions[0],
139+
offset: 6,
140+
details: ["This function returns a nullable value"]);
141+
assertDetail(detail: regions[0].details[0], offset: 11, length: 2);
142+
}
143+
75144
test_field_fieldFormalInitializer_optional() async {
76145
addTestFile('''
77146
class A {
@@ -357,6 +426,30 @@ void f([String? s]) {}
357426
details: ["This parameter has an implicit default value of 'null'"]);
358427
}
359428

429+
test_returnDetailTarget() async {
430+
addTestFile('''
431+
String g() {
432+
return 1 == 2 ? "Hello" : null;
433+
}
434+
''');
435+
await buildInfo();
436+
UnitInfo unit = infos[0];
437+
expect(unit.content, '''
438+
String? g() {
439+
return 1 == 2 ? "Hello" : null;
440+
}
441+
''');
442+
assertInTargets(targets: unit.targets, offset: 7, length: 1); // "g"
443+
assertInTargets(targets: unit.targets, offset: 15, length: 6); // "return"
444+
List<RegionInfo> regions = unit.regions;
445+
expect(regions, hasLength(1));
446+
assertRegion(
447+
region: regions[0],
448+
offset: 6,
449+
details: ["This function returns a nullable value"]);
450+
assertDetail(detail: regions[0].details[0], offset: 15, length: 6);
451+
}
452+
360453
test_returnType_function_expression() async {
361454
addTestFile('''
362455
int _f = null;

0 commit comments

Comments
 (0)