[wildcards] ensure bound `__` underscores are suggested in completions
See: https://github.com/dart-lang/sdk/issues/56361
Change-Id: Ia1b1a0fef17fb68f476d2ade96a0b5761152336b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/378720
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Phil Quitslund <pquitslund@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 f2e2b11..7394dce 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
@@ -26,10 +26,6 @@
/// A helper class that produces candidate suggestions for all of the
/// declarations that are in scope at the completion location.
class DeclarationHelper {
- /// The regular expression used to detect an unused identifier (a sequence of
- /// one or more underscores with no other characters).
- static final RegExp UnusedIdentifier = RegExp(r'^_+$');
-
/// The completion request being processed.
final DartCompletionRequest request;
@@ -667,7 +663,7 @@
required String? prefix,
}) {
// Don't suggest declarations in wildcard prefixed namespaces.
- if (prefix == '_') return;
+ if (_isWildcard(prefix)) return;
var importData = ImportData(
libraryUri: library.source.uri,
@@ -1392,9 +1388,8 @@
return true;
}
- /// Returns `true` if the [identifier] is composed of one or more underscore
- /// characters and nothing else.
- bool _isUnused(String identifier) => UnusedIdentifier.hasMatch(identifier);
+ /// Returns `true` if the [identifier] is a wildcard (a single `_`).
+ bool _isWildcard(String? identifier) => identifier == '_';
/// Record that the given [operation] should be performed in the second pass.
void _recordOperation(NotImportedOperation operation) {
@@ -1631,7 +1626,7 @@
return;
}
// Don't suggest wildcard local functions.
- if (element.name == '_') return;
+ if (_isWildcard(element.name)) return;
var matcherScore = state.matcher.score(element.displayName);
if (matcherScore != -1) {
var suggestion = LocalFunctionSuggestion(
@@ -1705,7 +1700,7 @@
/// Adds a suggestion for the parameter represented by the [element].
void _suggestParameter(ParameterElement element) {
if (visibilityTracker.isVisible(element: element, importData: null)) {
- if (mustBeConstant || _isUnused(element.name)) {
+ if (mustBeConstant || _isWildcard(element.name)) {
return;
}
var matcherScore = state.matcher.score(element.displayName);
diff --git a/pkg/analysis_server/test/services/completion/dart/declaration/wildcard_variables_test.dart b/pkg/analysis_server/test/services/completion/dart/declaration/wildcard_variables_test.dart
index e880934..ff191d4 100644
--- a/pkg/analysis_server/test/services/completion/dart/declaration/wildcard_variables_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/declaration/wildcard_variables_test.dart
@@ -65,7 +65,7 @@
@reflectiveTest
class WildcardForLoopTest extends AbstractCompletionDriverTest {
@override
- Set<String> allowedIdentifiers = {'_'};
+ Set<String> allowedIdentifiers = {'_', '__'};
@override
bool get includeKeywords => false;
@@ -85,6 +85,23 @@
''');
}
+ Future<void> test_forEach_argumentList_underscores() async {
+ await computeSuggestions('''
+void p(Object o) {}
+
+void f() {
+ for (var __ in []) {
+ p(^);
+ }
+}
+''');
+ assertResponse('''
+suggestions
+ __
+ kind: localVariable
+''');
+ }
+
Future<void> test_forParts_argumentList() async {
await computeSuggestions('''
void p(Object o) {}
@@ -99,12 +116,29 @@
suggestions
''');
}
+
+ Future<void> test_forParts_argumentList_underscores() async {
+ await computeSuggestions('''
+void p(Object o) {}
+
+void f() {
+ for (var __ = 0; ;) {
+ p(^);
+ }
+}
+''');
+ assertResponse('''
+suggestions
+ __
+ kind: localVariable
+''');
+ }
}
@reflectiveTest
class WildcardImportPrefixTest extends AbstractCompletionDriverTest {
@override
- Set<String> allowedIdentifiers = {'_', 'isBlank'};
+ Set<String> allowedIdentifiers = {'_', '__', 'isBlank'};
@override
bool get includeKeywords => false;
@@ -131,6 +165,31 @@
''');
}
+ Future<void> test_argumentList_underscores() async {
+ newFile('$testPackageLibPath/ext.dart', '''
+extension ES on String {
+ bool get isBlank => false;
+}
+''');
+
+ await computeSuggestions('''
+import 'ext.dart' as __;
+
+void p(Object o) {}
+
+void f() {
+ p(^);
+}
+''');
+ assertResponse('''
+suggestions
+ __.ES
+ kind: extensionInvocation
+ __
+ kind: library
+''');
+ }
+
Future<void> test_stringExtension_argumentList() async {
newFile('$testPackageLibPath/ext.dart', '''
extension ES on String {
@@ -158,7 +217,7 @@
@reflectiveTest
class WildcardLocalVariableTest extends AbstractCompletionDriverTest {
@override
- Set<String> allowedIdentifiers = {'_', 'b'};
+ Set<String> allowedIdentifiers = {'_', '__', 'b'};
@override
bool get includeKeywords => false;
@@ -192,12 +251,28 @@
suggestions
''');
}
+
+ Future<void> test_argumentList_underscores() async {
+ await computeSuggestions('''
+void p(Object o) {}
+
+void f() {
+ var __ = 0;
+ p(^);
+}
+''');
+ assertResponse(r'''
+suggestions
+ __
+ kind: localVariable
+''');
+ }
}
@reflectiveTest
class WildcardParameterTest extends AbstractCompletionDriverTest {
@override
- Set<String> allowedIdentifiers = {'_', 'b'};
+ Set<String> allowedIdentifiers = {'_', '__', 'b'};
@override
bool get includeKeywords => false;
@@ -216,6 +291,21 @@
kind: parameter
''');
}
+
+ Future<void> test_argumentList_underscores() async {
+ await computeSuggestions('''
+void p(Object o) {}
+
+void f(int __) {
+ p(^);
+}
+''');
+ assertResponse('''
+suggestions
+ __
+ kind: parameter
+''');
+ }
}
/// Top level variables are binding so not technically wildcards but look just