[analysis_server] Prevent duplicate completions for extensions for libraries that import them
Fixes https://github.com/dart-lang/sdk/issues/56320
Change-Id: I4645165857c082cb26ac09eb12df3106769fb25a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/382821
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/declaration_helper.dart b/pkg/analysis_server/lib/src/services/completion/dart/declaration_helper.dart
index 334fb20..ed1399f 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/declaration_helper.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/declaration_helper.dart
@@ -463,14 +463,16 @@
required Set<String> excludedGetters,
required bool includeMethods,
required bool includeSetters}) {
- var applicableExtensions = library.accessibleExtensions.applicableTo(
- targetLibrary: library,
- // Ignore nullability, consistent with non-extension members.
- targetType: type.isDartCoreNull
- ? type
- : library.typeSystem.promoteToNonNull(type),
- strictCasts: false,
- );
+ var applicableExtensions = library.exportNamespace.definedNames.values
+ .whereType<ExtensionElement>()
+ .applicableTo(
+ targetLibrary: library,
+ // Ignore nullability, consistent with non-extension members.
+ targetType: type.isDartCoreNull
+ ? type
+ : library.typeSystem.promoteToNonNull(type),
+ strictCasts: false,
+ );
var importData = ImportData(
libraryUri: library.source.uri, prefix: null, isNotImported: true);
for (var instantiatedExtension in applicableExtensions) {
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/not_imported_completion_pass.dart b/pkg/analysis_server/lib/src/services/completion/dart/not_imported_completion_pass.dart
index 03b0a17..524b5b6 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/not_imported_completion_pass.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/not_imported_completion_pass.dart
@@ -143,7 +143,7 @@
var element = elementResult.element;
if (element == library) {
// Don't suggest elements from the library in which completion is being
- // requested. They've already been sugpested.
+ // requested. They've already been suggested.
continue;
}
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index 97e08d6..6a597ef 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -3319,7 +3319,6 @@
}
/// Verify extensions can be auto-imported if not already in-scope.
- @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/56320')
Future<void> test_unimportedSymbols_extension() async {
// Define extensions in 'extensions.dart'.
newFile(
diff --git a/pkg/analysis_server/test/src/services/completion/dart/completion_test.dart b/pkg/analysis_server/test/src/services/completion/dart/completion_test.dart
index 95c021b..cda1ade 100644
--- a/pkg/analysis_server/test/src/services/completion/dart/completion_test.dart
+++ b/pkg/analysis_server/test/src/services/completion/dart/completion_test.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/src/protocol_server.dart';
+import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../../../completion_test_support.dart';
@@ -223,21 +224,51 @@
assertHasCompletion('m');
}
- @failingTest
Future<void> test_explicitTarget_method_notImported() async {
- // Available suggestions data doesn't yet have information about extension
- // methods.
- newFile(convertPath('/project/bin/lib.dart'), '''
+ newFile(convertPath('$testPackageLibPath/extensions.dart'), '''
extension E on String {
void m() {}
}
''');
+ // Files that merely import extensions do not add completions for them.
+ newFile(convertPath('$testPackageLibPath/imported.dart'), '''
+import 'extensions.dart';
+''');
await getTestCodeSuggestions('''
void f(String s) {
s.^;
}
''');
- assertHasCompletion('m');
+ var matchingSuggestions = suggestions.where((s) => s.completion == 'm');
+ expect(matchingSuggestions, hasLength(1));
+ expect(
+ matchingSuggestions.single.libraryUri, 'package:test/extensions.dart');
+ }
+
+ /// Extensions can also be imported from libraries that export them.
+ Future<void> test_explicitTarget_method_notImported_exported() async {
+ newFile(convertPath('$testPackageLibPath/extensions.dart'), '''
+extension E on String {
+ void m() {}
+}
+''');
+ newFile(convertPath('$testPackageLibPath/reexported.dart'), '''
+export 'extensions.dart';
+''');
+ await getTestCodeSuggestions('''
+void f(String s) {
+ s.^;
+}
+''');
+ var matchingSuggestions = suggestions.where((s) => s.completion == 'm');
+ expect(matchingSuggestions, hasLength(2));
+ expect(
+ matchingSuggestions.map((s) => s.libraryUri),
+ [
+ 'package:test/extensions.dart',
+ 'package:test/reexported.dart',
+ ],
+ );
}
Future<void> test_explicitTarget_method_sameUnit() async {