Skip to content

Commit c3ddf2e

Browse files
FMorschelCommit Queue
authored andcommitted
[DAS] Fixes "Add import" fixes to show relative import for extensions
Fixes: #60972 Change-Id: I3fbde7115d1439a8dd2a6ab588e8c339d66660a5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/442708 Reviewed-by: Brian Wilkerson <[email protected]> Auto-Submit: Felipe Morschel <[email protected]> Reviewed-by: Samuel Rawlins <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]>
1 parent 00240e8 commit c3ddf2e

File tree

3 files changed

+115
-136
lines changed

3 files changed

+115
-136
lines changed

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

Lines changed: 27 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ class ImportLibrary extends MultiCorrectionProducer {
227227
String name,
228228
List<ElementKind> kinds, {
229229
String? prefix,
230+
bool canBePrefixed = true,
230231
}) async {
231232
// Ignore the element if the name is private.
232233
if (name.startsWith('_')) {
@@ -252,7 +253,6 @@ class ImportLibrary extends MultiCorrectionProducer {
252253
if (!kinds.contains(element.kind)) {
253254
continue;
254255
}
255-
var importPrefix = import.prefix?.element;
256256
// Maybe update a "show"/"hide" directive.
257257
var (
258258
combinatorProducer,
@@ -265,7 +265,8 @@ class ImportLibrary extends MultiCorrectionProducer {
265265
prefix: prefix,
266266
);
267267
// Maybe apply a prefix.
268-
if (importPrefix != null) {
268+
var importPrefix = import.prefix?.element;
269+
if (canBePrefixed && importPrefix != null) {
269270
producers.add(
270271
_ImportLibraryPrefix(
271272
libraryElement,
@@ -397,74 +398,52 @@ class ImportLibrary extends MultiCorrectionProducer {
397398
// additional analysis.
398399
var foundImport = false;
399400
var names = <_PrefixedName>[];
401+
var extensionsInLibrary =
402+
<LibraryImport?, List<InstantiatedExtensionWithMember>>{};
400403
for (var import in unitResult.libraryFragment.libraryImports) {
401-
// prepare element
402404
var importedLibrary = import.importedLibrary;
403405
if (importedLibrary == null || importedLibrary != libraryToImport) {
404406
continue;
405407
}
406408
foundImport = true;
407-
var instantiatedExtensions = importedLibrary.exportedExtensions
409+
extensionsInLibrary[import] = importedLibrary.exportedExtensions
408410
.havingMemberWithBaseName(memberName)
409411
.applicableTo(
410412
targetLibrary: libraryElement2,
411413
targetType: targetType as TypeImpl,
412414
);
413-
for (var instantiatedExtension in instantiatedExtensions) {
414-
// If the import has a combinator that needs to be updated, then offer
415-
// to update it.
416-
var libraryElement = import.importedLibrary;
417-
if (libraryElement == null) {
418-
continue;
419-
}
415+
}
416+
417+
// If the library at the URI is not already imported, we return a correction
418+
// producer that will either add an import or not based on the result of
419+
// analyzing the library.
420+
if (!foundImport) {
421+
extensionsInLibrary[null] = libraryToImport.exportedExtensions
422+
.havingMemberWithBaseName(memberName)
423+
.applicableTo(
424+
targetLibrary: libraryElement2,
425+
targetType: targetType as TypeImpl,
426+
);
427+
}
428+
for (var entry in extensionsInLibrary.entries) {
429+
var extensionsInLibrary = entry.value;
430+
for (var instantiatedExtension in extensionsInLibrary) {
420431
names.add(
421432
_PrefixedName(
422433
name: instantiatedExtension.extension.name!,
423434
ignorePrefix: true,
424435
producerGenerators: (prefix, name) async {
425-
var producers = <ResolvedCorrectionProducer>[];
426-
var (
427-
importLibraryCombinator,
428-
importLibraryCombinatorMultiple,
429-
) = await _importEditCombinators(
430-
import,
431-
libraryElement,
432-
libraryToImport.uri.toString(),
436+
return await _importLibraryForElement(
433437
name,
438+
prefix: prefix,
439+
canBePrefixed: false,
440+
[ElementKind.EXTENSION],
434441
);
435-
if (importLibraryCombinator != null) {
436-
producers.add(importLibraryCombinator);
437-
if (importLibraryCombinatorMultiple != null) {
438-
producers.add(importLibraryCombinatorMultiple);
439-
}
440-
}
441-
return producers;
442442
},
443443
),
444444
);
445445
}
446446
}
447-
448-
// If the library at the URI is not already imported, we return a correction
449-
// producer that will either add an import or not based on the result of
450-
// analyzing the library.
451-
if (!foundImport) {
452-
names.add(
453-
_PrefixedName(
454-
name: memberName.name,
455-
producerGenerators: (prefix, name) async {
456-
return [
457-
_ImportLibraryContainingExtension(
458-
libraryToImport,
459-
targetType,
460-
memberName,
461-
context: context,
462-
),
463-
];
464-
},
465-
),
466-
);
467-
}
468447
return names;
469448
}
470449

@@ -914,8 +893,7 @@ class _ImportLibraryCombinatorMultiple extends ResolvedCorrectionProducer {
914893
if (names.isNotEmpty) {
915894
newCombinatorCode = ' ${keyword.lexeme} ${names.join(', ')}';
916895
}
917-
var libraryPath =
918-
unitResult.libraryElement.firstFragment.source.fullName;
896+
var libraryPath = unitResult.libraryElement.firstFragment.source.fullName;
919897
await builder.addDartFileEdit(libraryPath, (builder) {
920898
builder.addSimpleReplacement(
921899
SourceRange(offset - 1, length + 1),
@@ -940,55 +918,6 @@ class _ImportLibraryCombinatorMultiple extends ResolvedCorrectionProducer {
940918
}
941919
}
942920

943-
/// A correction processor that can add an import of a library containing an
944-
/// extension, but which does so only if the extension applies to a given type.
945-
class _ImportLibraryContainingExtension extends ResolvedCorrectionProducer {
946-
/// The library defining the extension.
947-
LibraryElement library;
948-
949-
/// The type of the target that the extension must apply to.
950-
DartType targetType;
951-
952-
/// The name of the member that the extension must declare.
953-
Name memberName;
954-
955-
/// The URI that is being proposed for the import directive.
956-
String _uriText = '';
957-
958-
_ImportLibraryContainingExtension(
959-
this.library,
960-
this.targetType,
961-
this.memberName, {
962-
required super.context,
963-
});
964-
965-
@override
966-
CorrectionApplicability get applicability =>
967-
// TODO(applicability): comment on why.
968-
CorrectionApplicability.singleLocation;
969-
970-
@override
971-
List<String> get fixArguments => [_uriText];
972-
973-
@override
974-
FixKind get fixKind => DartFixKind.IMPORT_LIBRARY_PROJECT1;
975-
976-
@override
977-
Future<void> compute(ChangeBuilder builder) async {
978-
var instantiatedExtensions = library.exportedExtensions
979-
.havingMemberWithBaseName(memberName)
980-
.applicableTo(
981-
targetLibrary: libraryElement2,
982-
targetType: targetType as TypeImpl,
983-
);
984-
if (instantiatedExtensions.isNotEmpty) {
985-
await builder.addDartFileEdit(file, (builder) {
986-
_uriText = builder.importLibrary(library.uri);
987-
});
988-
}
989-
}
990-
}
991-
992921
/// A correction processor that can add a prefix to an identifier defined in a
993922
/// library that is already imported but that is imported with a prefix.
994923
class _ImportLibraryPrefix extends ResolvedCorrectionProducer {

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,61 +910,91 @@ abstract final class DartFixKind {
910910
DartFixKindPriority.standard + 5,
911911
"Use imported library '{0}' with prefix '{1}'",
912912
);
913+
914+
/// {@template dart.fix.import.libraryProject1}
915+
/// Import defining library.
916+
/// {@endtemplate}
913917
static const IMPORT_LIBRARY_PROJECT1 = FixKind(
914918
'dart.fix.import.libraryProject1',
915919
DartFixKindPriority.standard + 3,
916920
"Import library '{0}'",
917921
);
922+
923+
/// {@macro dart.fix.import.libraryProject1}
918924
static const IMPORT_LIBRARY_PROJECT1_PREFIXED = FixKind(
919925
'dart.fix.import.libraryProject1Prefixed',
920926
DartFixKindPriority.standard + 3,
921927
"Import library '{0}' with prefix '{1}'",
922928
);
929+
930+
/// {@macro dart.fix.import.libraryProject1}
923931
static const IMPORT_LIBRARY_PROJECT1_PREFIXED_SHOW = FixKind(
924932
'dart.fix.import.libraryProject1PrefixedShow',
925933
DartFixKindPriority.standard + 3,
926934
"Import library '{0}' with prefix '{1}' and 'show'",
927935
);
936+
937+
/// {@macro dart.fix.import.libraryProject1}
928938
static const IMPORT_LIBRARY_PROJECT1_SHOW = FixKind(
929939
'dart.fix.import.libraryProject1Show',
930940
DartFixKindPriority.standard + 3,
931941
"Import library '{0}' with 'show'",
932942
);
943+
944+
/// {@template dart.fix.import.libraryProject2}
945+
/// Import export library.
946+
/// {@endtemplate}
933947
static const IMPORT_LIBRARY_PROJECT2 = FixKind(
934948
'dart.fix.import.libraryProject2',
935949
DartFixKindPriority.standard + 2,
936950
"Import library '{0}'",
937951
);
952+
953+
/// {@macro dart.fix.import.libraryProject2}
938954
static const IMPORT_LIBRARY_PROJECT2_PREFIXED = FixKind(
939955
'dart.fix.import.libraryProject2Prefixed',
940956
DartFixKindPriority.standard + 2,
941957
"Import library '{0}' with prefix '{1}'",
942958
);
959+
960+
/// {@macro dart.fix.import.libraryProject2}
943961
static const IMPORT_LIBRARY_PROJECT2_PREFIXED_SHOW = FixKind(
944962
'dart.fix.import.libraryProject2PrefixedShow',
945963
DartFixKindPriority.standard + 2,
946964
"Import library '{0}' with prefix '{1}' and 'show'",
947965
);
966+
967+
/// {@macro dart.fix.import.libraryProject2}
948968
static const IMPORT_LIBRARY_PROJECT2_SHOW = FixKind(
949969
'dart.fix.import.libraryProject2Show',
950970
DartFixKindPriority.standard + 2,
951971
"Import library '{0}' with 'show'",
952972
);
973+
974+
/// {@template dart.fix.import.libraryProject3}
975+
/// Import non-API.
976+
/// {@endtemplate}
953977
static const IMPORT_LIBRARY_PROJECT3 = FixKind(
954978
'dart.fix.import.libraryProject3',
955979
DartFixKindPriority.standard + 1,
956980
"Import library '{0}'",
957981
);
982+
983+
/// {@macro dart.fix.import.libraryProject3}
958984
static const IMPORT_LIBRARY_PROJECT3_PREFIXED = FixKind(
959985
'dart.fix.import.libraryProject3Prefixed',
960986
DartFixKindPriority.standard + 1,
961987
"Import library '{0}' with prefix '{1}'",
962988
);
989+
990+
/// {@macro dart.fix.import.libraryProject3}
963991
static const IMPORT_LIBRARY_PROJECT3_PREFIXED_SHOW = FixKind(
964992
'dart.fix.import.libraryProject3PrefixedShow',
965993
DartFixKindPriority.standard + 1,
966994
"Import library '{0}' with prefix '{1}' and 'show'",
967995
);
996+
997+
/// {@macro dart.fix.import.libraryProject3}
968998
static const IMPORT_LIBRARY_PROJECT3_SHOW = FixKind(
969999
'dart.fix.import.libraryProject3Show',
9701000
DartFixKindPriority.standard + 1,

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

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,26 @@ class B extends A {
469469
''');
470470
}
471471

472+
Future<void> test_extension_notImported_lint() async {
473+
newFile('$testPackageLibPath/lib.dart', '''
474+
extension E on int {
475+
void m() {}
476+
}
477+
''');
478+
await resolveTestCode('''
479+
void f(int o) {
480+
o.m();
481+
}
482+
''');
483+
await assertHasFix('''
484+
import 'lib.dart';
485+
486+
void f(int o) {
487+
o.m();
488+
}
489+
''', matchFixMessage: "Import library 'lib.dart'");
490+
}
491+
472492
Future<void> test_extension_notImported_method() async {
473493
newFile('$testPackageLibPath/lib.dart', '''
474494
extension E on String {
@@ -636,44 +656,6 @@ void f(String s) {
636656
''');
637657
}
638658

639-
Future<void> test_extension_otherPackage_exported_fromSrc() async {
640-
var pkgRootPath = '$packagesRootPath/aaa';
641-
642-
newFile('$pkgRootPath/lib/a.dart', r'''
643-
export 'src/b.dart';
644-
''');
645-
646-
newFile('$pkgRootPath/lib/src/b.dart', r'''
647-
extension IntExtension on int {
648-
int get foo => 0;
649-
}
650-
''');
651-
652-
writeTestPackageConfig(
653-
config:
654-
PackageConfigFileBuilder()..add(name: 'aaa', rootPath: pkgRootPath),
655-
);
656-
657-
updateTestPubspecFile('''
658-
dependencies:
659-
aaa: any
660-
''');
661-
662-
await resolveTestCode('''
663-
void f() {
664-
0.foo;
665-
}
666-
''');
667-
668-
await assertHasFix('''
669-
import 'package:aaa/a.dart';
670-
671-
void f() {
672-
0.foo;
673-
}
674-
''');
675-
}
676-
677659
Future<void> test_extensionType_name_notImported() async {
678660
newFile('$testPackageLibPath/lib.dart', '''
679661
extension type ET(String it) {}
@@ -2248,6 +2230,44 @@ void f() {}
22482230
''');
22492231
}
22502232

2233+
Future<void> test_extension_otherPackage_exported_fromSrc() async {
2234+
var pkgRootPath = '$packagesRootPath/aaa';
2235+
2236+
newFile('$pkgRootPath/lib/a.dart', r'''
2237+
export 'src/b.dart';
2238+
''');
2239+
2240+
newFile('$pkgRootPath/lib/src/b.dart', r'''
2241+
extension IntExtension on int {
2242+
int get foo => 0;
2243+
}
2244+
''');
2245+
2246+
writeTestPackageConfig(
2247+
config:
2248+
PackageConfigFileBuilder()..add(name: 'aaa', rootPath: pkgRootPath),
2249+
);
2250+
2251+
updateTestPubspecFile('''
2252+
dependencies:
2253+
aaa: any
2254+
''');
2255+
2256+
await resolveTestCode('''
2257+
void f() {
2258+
0.foo;
2259+
}
2260+
''');
2261+
2262+
await assertHasFix('''
2263+
import 'package:aaa/a.dart';
2264+
2265+
void f() {
2266+
0.foo;
2267+
}
2268+
''');
2269+
}
2270+
22512271
Future<void> test_lib() async {
22522272
newFile('$packagesRootPath/my_pkg/lib/a.dart', '''
22532273
export 'b.dart';

0 commit comments

Comments
 (0)