Skip to content

Commit 73d880b

Browse files
devoncarewcommit-bot@chromium.org
authored andcommitted
[analyzer] add a quick fix for the prefer_relative_imports lint
Change-Id: Ic69ebe799f425fd09e002b65d66a4382f5f96ead Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/120160 Commit-Queue: Devon Carew <devoncarew@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Phil Quitslund <pquitslund@google.com>
1 parent 6955247 commit 73d880b

File tree

7 files changed

+170
-1
lines changed

7 files changed

+170
-1
lines changed

pkg/analysis_server/lib/src/services/correction/base_processor.dart

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dar
2424
import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
2525
import 'package:analyzer_plugin/utilities/range_factory.dart';
2626
import 'package:meta/meta.dart';
27+
import 'package:path/path.dart' as path;
2728

2829
/// Base class for common processor functionality.
2930
abstract class BaseProcessor {
@@ -963,6 +964,65 @@ abstract class BaseProcessor {
963964
return null;
964965
}
965966

967+
Future<ChangeBuilder> createBuilder_convertToRelativeImport() async {
968+
var node = this.node;
969+
if (node is StringLiteral) {
970+
node = node.parent;
971+
}
972+
if (node is! ImportDirective) {
973+
return null;
974+
}
975+
976+
ImportDirective importDirective = node;
977+
978+
// Ignore if invalid URI.
979+
if (importDirective.uriSource == null) {
980+
return null;
981+
}
982+
983+
// Ignore if the uri is not a package: uri.
984+
Uri sourceUri = resolvedResult.uri;
985+
if (sourceUri.scheme != 'package') {
986+
return null;
987+
}
988+
989+
Uri importUri;
990+
try {
991+
importUri = Uri.parse(importDirective.uriContent);
992+
} on FormatException {
993+
return null;
994+
}
995+
996+
// Ignore if import uri is not a package: uri.
997+
if (importUri.scheme != 'package') {
998+
return null;
999+
}
1000+
1001+
// Verify that the source's uri and the import uri have the same package
1002+
// name.
1003+
List<String> sourceSegments = sourceUri.pathSegments;
1004+
List<String> importSegments = importUri.pathSegments;
1005+
if (sourceSegments.isEmpty ||
1006+
importSegments.isEmpty ||
1007+
sourceSegments.first != importSegments.first) {
1008+
return null;
1009+
}
1010+
1011+
final String relativePath = path.relative(
1012+
importUri.path,
1013+
from: path.dirname(sourceUri.path),
1014+
);
1015+
1016+
DartChangeBuilder changeBuilder = _newDartChangeBuilder();
1017+
await changeBuilder.addFileEdit(file, (builder) {
1018+
builder.addSimpleReplacement(
1019+
range.node(importDirective.uri).getExpanded(-1),
1020+
relativePath,
1021+
);
1022+
});
1023+
return changeBuilder;
1024+
}
1025+
9661026
Future<ChangeBuilder> createBuilder_inlineAdd() async {
9671027
AstNode node = this.node;
9681028
if (node is! SimpleIdentifier || node.parent is! MethodInvocation) {

pkg/analysis_server/lib/src/services/correction/fix.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ class DartFixKind {
205205
const FixKind('CONVERT_TO_NULL_AWARE', 50, "Convert to use '?.'");
206206
static const CONVERT_TO_PACKAGE_IMPORT = const FixKind(
207207
'CONVERT_TO_PACKAGE_IMPORT', 50, "Convert to 'package:' import");
208+
static const CONVERT_TO_RELATIVE_IMPORT = const FixKind(
209+
'CONVERT_TO_RELATIVE_IMPORT', 50, "Convert to relative import");
208210
static const CONVERT_TO_SINGLE_QUOTED_STRING = const FixKind(
209211
'CONVERT_TO_SINGLE_QUOTED_STRING', 50, "Convert to single quoted string");
210212
static const CONVERT_TO_SPREAD =

pkg/analysis_server/lib/src/services/correction/fix_internal.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,9 @@ class FixProcessor extends BaseProcessor {
693693
if (name == LintNames.prefer_null_aware_operators) {
694694
await _addFix_convertToNullAware();
695695
}
696+
if (name == LintNames.prefer_relative_imports) {
697+
await _addFix_convertToRelativeImport();
698+
}
696699
if (name == LintNames.prefer_single_quotes) {
697700
await _addFix_convertSingleQuotes();
698701
}
@@ -1528,6 +1531,11 @@ class FixProcessor extends BaseProcessor {
15281531
_addFixFromBuilder(changeBuilder, DartFixKind.CONVERT_TO_PACKAGE_IMPORT);
15291532
}
15301533

1534+
Future<void> _addFix_convertToRelativeImport() async {
1535+
final changeBuilder = await createBuilder_convertToRelativeImport();
1536+
_addFixFromBuilder(changeBuilder, DartFixKind.CONVERT_TO_RELATIVE_IMPORT);
1537+
}
1538+
15311539
Future<void> _addFix_createClass() async {
15321540
Element prefixElement = null;
15331541
String name = null;

pkg/analysis_server/lib/src/services/linter/lint_names.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class LintNames {
5050
static const String prefer_is_not_empty = 'prefer_is_not_empty';
5151
static const String prefer_null_aware_operators =
5252
'prefer_null_aware_operators';
53+
static const String prefer_relative_imports = 'prefer_relative_imports';
5354
static const String prefer_single_quotes = 'prefer_single_quotes';
5455
static const String prefer_spread_collections = 'prefer_spread_collections';
5556
static const String slash_for_doc_comments = 'slash_for_doc_comments';
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analysis_server/src/services/correction/fix.dart';
6+
import 'package:analysis_server/src/services/linter/lint_names.dart';
7+
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
8+
import 'package:test_reflective_loader/test_reflective_loader.dart';
9+
10+
import 'fix_processor.dart';
11+
12+
main() {
13+
defineReflectiveSuite(() {
14+
defineReflectiveTests(ConvertToRelativeImportTest);
15+
});
16+
}
17+
18+
@reflectiveTest
19+
class ConvertToRelativeImportTest extends FixProcessorLintTest {
20+
@override
21+
FixKind get kind => DartFixKind.CONVERT_TO_RELATIVE_IMPORT;
22+
23+
@override
24+
String get lintCode => LintNames.prefer_relative_imports;
25+
26+
test_relativeImport() async {
27+
addSource('/home/test/lib/foo.dart', '');
28+
testFile = convertPath('/home/test/lib/src/test.dart');
29+
await resolveTestUnit('''
30+
import /*LINT*/'package:test/foo.dart';
31+
''');
32+
33+
await assertHasFix('''
34+
import /*LINT*/'../foo.dart';
35+
''');
36+
}
37+
38+
test_relativeImportSameDirectory() async {
39+
addSource('/home/test/lib/foo.dart', '');
40+
testFile = convertPath('/home/test/lib/bar.dart');
41+
await resolveTestUnit('''
42+
import /*LINT*/'package:test/foo.dart';
43+
''');
44+
45+
await assertHasFix('''
46+
import /*LINT*/'foo.dart';
47+
''');
48+
}
49+
50+
test_relativeImportSubDirectory() async {
51+
addSource('/home/test/lib/baz/foo.dart', '');
52+
testFile = convertPath('/home/test/lib/test.dart');
53+
await resolveTestUnit('''
54+
import /*LINT*/'package:test/baz/foo.dart';
55+
''');
56+
57+
await assertHasFix('''
58+
import /*LINT*/'baz/foo.dart';
59+
''');
60+
}
61+
62+
test_relativeImportRespectQuoteStyle() async {
63+
addSource('/home/test/lib/foo.dart', '');
64+
testFile = convertPath('/home/test/lib/bar.dart');
65+
await resolveTestUnit('''
66+
import /*LINT*/"package:test/foo.dart";
67+
''');
68+
69+
await assertHasFix('''
70+
import /*LINT*/"foo.dart";
71+
''');
72+
}
73+
74+
test_relativeImportGarbledUri() async {
75+
addSource('/home/test/lib/foo.dart', '');
76+
testFile = convertPath('/home/test/lib/bar.dart');
77+
await resolveTestUnit('''
78+
import /*LINT*/'package:test/foo';
79+
''');
80+
81+
await assertHasFix('''
82+
import /*LINT*/'foo';
83+
''');
84+
}
85+
86+
// Validate we don't get a fix with imports referencing different packages.
87+
test_relativeImportDifferentPackages() async {
88+
addSource('/home/test1/lib/foo.dart', '');
89+
testFile = convertPath('/home/test2/lib/bar.dart');
90+
await resolveTestUnit('''
91+
import /*LINT*/'package:test1/foo.dart';
92+
''');
93+
94+
await assertNoFix();
95+
}
96+
}

pkg/analysis_server/test/src/services/correction/fix/test_all.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import 'convert_to_int_literal_test.dart' as convert_to_int_literal;
4646
import 'convert_to_named_arguments_test.dart' as convert_to_named_arguments;
4747
import 'convert_to_null_aware_test.dart' as convert_to_null_aware;
4848
import 'convert_to_package_import_test.dart' as convert_to_package_import;
49+
import 'convert_to_relative_import_test.dart' as convert_to_relative_import;
4950
import 'convert_to_single_quoted_string_test.dart'
5051
as convert_to_single_quoted_string;
5152
import 'convert_to_spread_test.dart' as convert_to_spread;
@@ -172,6 +173,7 @@ main() {
172173
convert_to_named_arguments.main();
173174
convert_to_null_aware.main();
174175
convert_to_package_import.main();
176+
convert_to_relative_import.main();
175177
convert_to_single_quoted_string.main();
176178
convert_to_spread.main();
177179
create_class.main();

pkg/analyzer/lib/dart/ast/ast.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1234,7 +1234,7 @@ abstract class CompilationUnit implements AstNode {
12341234
/// directive in a comment at the top of the file.
12351235
///
12361236
/// Might be `null` if, for example, this [CompilationUnit] has been
1237-
/// resynthesized from a summary,
1237+
/// resynthesized from a summary.
12381238
FeatureSet get featureSet;
12391239

12401240
/// Return the line information for this compilation unit.

0 commit comments

Comments
 (0)