Skip to content

Commit b15fb0e

Browse files
FMorschelsrawlins
authored andcommitted
[DAS] Fixes FunctionType.call completion
Bug: #61319 Change-Id: Id757b79f46bacb071ccf60f08080c621229313e1 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/451544 Reviewed-by: Keerti Parthasarathy <[email protected]> Reviewed-by: Samuel Rawlins <[email protected]> Auto-Submit: Felipe Morschel <[email protected]>
1 parent 4fe0461 commit b15fb0e

File tree

11 files changed

+382
-121
lines changed

11 files changed

+382
-121
lines changed

pkg/analysis_server/lib/plugin/protocol/protocol_dart.dart

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -150,29 +150,8 @@ String getElementDisplayName(engine.Element element) {
150150
}
151151
}
152152

153-
String? getParametersString(engine.Element element) {
154-
// TODO(scheglov): expose the corresponding feature from ExecutableElement
155-
List<engine.FormalParameterElement> parameters;
156-
if (element is engine.ExecutableElement) {
157-
// valid getters don't have parameters
158-
if (element.kind == engine.ElementKind.GETTER &&
159-
element.formalParameters.isEmpty) {
160-
return null;
161-
}
162-
parameters = element.formalParameters.toList();
163-
} else if (element is engine.TypeAliasElement) {
164-
var aliasedType = element.aliasedType;
165-
if (aliasedType is FunctionType) {
166-
parameters = aliasedType.formalParameters.toList();
167-
} else {
168-
return null;
169-
}
170-
} else {
171-
return null;
172-
}
173-
153+
String getParametersListString(List<engine.FormalParameterElement> parameters) {
174154
parameters.sort(_preferRequiredParams);
175-
176155
var sb = StringBuffer();
177156
var closeOptionalString = '';
178157
for (var parameter in parameters) {
@@ -199,6 +178,30 @@ String? getParametersString(engine.Element element) {
199178
return '($sb)';
200179
}
201180

181+
String? getParametersString(engine.Element element) {
182+
// TODO(scheglov): expose the corresponding feature from ExecutableElement
183+
List<engine.FormalParameterElement> parameters;
184+
if (element is engine.ExecutableElement) {
185+
// valid getters don't have parameters
186+
if (element.kind == engine.ElementKind.GETTER &&
187+
element.formalParameters.isEmpty) {
188+
return null;
189+
}
190+
parameters = element.formalParameters.toList();
191+
} else if (element is engine.TypeAliasElement) {
192+
var aliasedType = element.aliasedType;
193+
if (aliasedType is FunctionType) {
194+
parameters = aliasedType.formalParameters.toList();
195+
} else {
196+
return null;
197+
}
198+
} else {
199+
return null;
200+
}
201+
202+
return getParametersListString(parameters);
203+
}
204+
202205
String? _getTypeParametersString(engine.Element element) {
203206
List<engine.TypeParameterElement>? typeParameters;
204207
if (element is engine.InterfaceElement) {

pkg/analysis_server/lib/src/handler/legacy/completion_utils.dart

Lines changed: 95 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'package:analysis_server/src/services/completion/dart/utilities.dart';
1212
import 'package:analysis_server/src/utilities/extensions/ast.dart';
1313
import 'package:analysis_server/src/utilities/extensions/element.dart';
1414
import 'package:analyzer/dart/element/element.dart' as e;
15+
import 'package:analyzer/dart/element/type.dart';
1516
import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
1617
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
1718
import 'package:analyzer_plugin/utilities/completion/relevance.dart';
@@ -81,7 +82,7 @@ Future<CompletionSuggestion?> candidateToCompletionSuggestion(
8182
false,
8283
displayText: data?.displayText ?? candidate.displayText,
8384
),
84-
85+
FunctionCall functionCall => _getFunctionCallSuggestion(functionCall),
8586
MethodSuggestion(kind: var suggestionKind) =>
8687
// TODO(brianwilkerson): Correctly set the kind of suggestion in cases
8788
// where `isFunctionalArgument` would return `true` so we can stop
@@ -196,23 +197,6 @@ Future<CompletionSuggestion?> candidateToCompletionSuggestion(
196197
libraryUriStr,
197198
requiredImports,
198199
);
199-
case FunctionCall():
200-
return CompletionSuggestion(
201-
CompletionSuggestionKind.INVOCATION,
202-
candidate.relevanceScore,
203-
e.MethodElement.CALL_METHOD_NAME,
204-
e.MethodElement.CALL_METHOD_NAME.length,
205-
0,
206-
false,
207-
false,
208-
displayText: candidate.completion,
209-
element: _getElementForFunctionCall(),
210-
returnType: 'void',
211-
parameterNames: [],
212-
parameterTypes: [],
213-
requiredParameterCount: 0,
214-
hasNamedParameters: false,
215-
);
216200
case IdentifierSuggestion():
217201
return CompletionSuggestion(
218202
CompletionSuggestionKind.IDENTIFIER,
@@ -447,36 +431,63 @@ Future<CompletionSuggestion?> candidateToCompletionSuggestion(
447431
}
448432
}
449433

434+
({
435+
List<String> parameterNames,
436+
List<String> parameterTypes,
437+
int requiredParameterCount,
438+
bool hasNamedParameters,
439+
CompletionDefaultArgumentList defaultArgumentList,
440+
})
441+
createParametersCompletionData(
442+
List<e.FormalParameterElement> formalParameters,
443+
) {
444+
var parameterNames = formalParameters.map((parameter) {
445+
return parameter.displayName;
446+
}).toList();
447+
var parameterTypes = formalParameters.map((
448+
e.FormalParameterElement parameter,
449+
) {
450+
return parameter.type.getDisplayString();
451+
}).toList();
452+
453+
var requiredParameters = formalParameters.where(
454+
(e.FormalParameterElement param) => param.isRequiredPositional,
455+
);
456+
var requiredParameterCount = requiredParameters.length;
457+
458+
var namedParameters = formalParameters.where(
459+
(e.FormalParameterElement param) => param.isNamed,
460+
);
461+
var hasNamedParameters = namedParameters.isNotEmpty;
462+
463+
var defaultArgumentList = computeCompletionDefaultArgumentList(
464+
requiredParameters,
465+
namedParameters,
466+
);
467+
return (
468+
parameterNames: parameterNames,
469+
parameterTypes: parameterTypes,
470+
requiredParameterCount: requiredParameterCount,
471+
hasNamedParameters: hasNamedParameters,
472+
defaultArgumentList: defaultArgumentList,
473+
);
474+
}
475+
450476
_ParameterData _createParameterData(e.Element element) {
451477
List<String>? parameterNames;
452478
List<String>? parameterTypes;
453479
int? requiredParameterCount;
454480
bool? hasNamedParameters;
455481
CompletionDefaultArgumentList? defaultArgumentList;
456482
if (element is e.ExecutableElement && element is! e.PropertyAccessorElement) {
457-
parameterNames = element.formalParameters.map((parameter) {
458-
return parameter.displayName;
459-
}).toList();
460-
parameterTypes = element.formalParameters.map((
461-
e.FormalParameterElement parameter,
462-
) {
463-
return parameter.type.getDisplayString();
464-
}).toList();
465-
466-
var requiredParameters = element.formalParameters.where(
467-
(e.FormalParameterElement param) => param.isRequiredPositional,
468-
);
469-
requiredParameterCount = requiredParameters.length;
470-
471-
var namedParameters = element.formalParameters.where(
472-
(e.FormalParameterElement param) => param.isNamed,
473-
);
474-
hasNamedParameters = namedParameters.isNotEmpty;
475-
476-
defaultArgumentList = computeCompletionDefaultArgumentList(
477-
element,
478-
requiredParameters,
479-
namedParameters,
483+
(
484+
:parameterNames,
485+
:parameterTypes,
486+
:requiredParameterCount,
487+
:hasNamedParameters,
488+
:defaultArgumentList,
489+
) = createParametersCompletionData(
490+
element.formalParameters,
480491
);
481492
}
482493
return _ParameterData(
@@ -592,13 +603,44 @@ String? _getDeclaringType(e.Element element) {
592603
return declaringType;
593604
}
594605

595-
Element _getElementForFunctionCall() {
606+
Element _getElementForFunctionCall(
607+
FunctionType type,
608+
String parameters,
609+
String? typeParameters,
610+
) {
596611
return Element(
597612
ElementKind.METHOD,
598613
e.MethodElement.CALL_METHOD_NAME,
599614
Element.makeFlags(),
600-
parameters: '()',
601-
returnType: 'void',
615+
parameters: parameters,
616+
typeParameters: typeParameters,
617+
returnType: type.returnType.getDisplayString(),
618+
);
619+
}
620+
621+
DartCompletionSuggestion _getFunctionCallSuggestion(FunctionCall functionCall) {
622+
var FunctionCall(:kind, :relevanceScore, :completion, :type) = functionCall;
623+
var record = createParametersCompletionData(type.formalParameters);
624+
return DartCompletionSuggestion(
625+
kind,
626+
relevanceScore,
627+
completion,
628+
completion.length,
629+
0,
630+
false,
631+
false,
632+
displayText: completion,
633+
element: _getElementForFunctionCall(
634+
type,
635+
'(${record.parameterNames.join(',')})',
636+
record.parameterTypes.joinWithCommaAndSurroundWithAngle(),
637+
),
638+
returnType: type.returnType.getDisplayString(),
639+
parameterNames: record.parameterNames,
640+
parameterTypes: record.parameterTypes,
641+
requiredParameterCount: record.requiredParameterCount,
642+
hasNamedParameters: record.hasNamedParameters,
643+
defaultArgumentListString: record.defaultArgumentList.text,
602644
);
603645
}
604646

@@ -771,3 +813,12 @@ class _ParameterData {
771813
this.defaultArgumentList,
772814
);
773815
}
816+
817+
extension on List<String> {
818+
String? joinWithCommaAndSurroundWithAngle() {
819+
if (isEmpty) {
820+
return null;
821+
}
822+
return '<${join(', ')}>';
823+
}
824+
}

pkg/analysis_server/lib/src/lsp/completion_utils.dart

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,12 @@ lsp.CompletionItem? toLspCompletionItem(
196196
: null;
197197
var isCallable =
198198
element != null &&
199-
(element is ConstructorElement ||
200-
element is LocalFunctionElement ||
201-
element is TopLevelFunctionElement ||
202-
element is MethodElement);
199+
(element is ConstructorElement ||
200+
element is LocalFunctionElement ||
201+
element is TopLevelFunctionElement ||
202+
element is MethodElement) ||
203+
suggestion is FunctionCall &&
204+
suggestion.kind == server.CompletionSuggestionKind.INVOCATION;
203205
var isInvocation =
204206
(suggestion is ExecutableSuggestion &&
205207
suggestion.kind == server.CompletionSuggestionKind.INVOCATION) ||
@@ -315,7 +317,6 @@ lsp.CompletionItem? toLspCompletionItem(
315317
);
316318

317319
defaultArgumentList = computeCompletionDefaultArgumentList(
318-
element,
319320
requiredParameters,
320321
namedParameters,
321322
);
@@ -589,9 +590,7 @@ CompletionDetail _getCompletionDetail(
589590
required bool isInvocation,
590591
}) {
591592
String? returnType;
592-
if (suggestion is FunctionCall) {
593-
returnType = 'void';
594-
} else if (suggestion is RecordFieldSuggestion) {
593+
if (suggestion is RecordFieldSuggestion) {
595594
returnType = suggestion.field.type.getDisplayString();
596595
}
597596
var element = suggestion is ElementBasedSuggestion
@@ -632,6 +631,11 @@ CompletionDetail _getCompletionDetail(
632631
returnType = completionSetterTypePattern.firstMatch(parameters)?.group(1);
633632
parameters = null;
634633
}
634+
} else if (suggestion is FunctionCall) {
635+
// Function calls may not have an element (for example the call method on
636+
// Function), so use the function type to get parameters if available.
637+
parameters = getParametersListString(suggestion.type.formalParameters);
638+
returnType = suggestion.type.returnType.getDisplayString();
635639
}
636640

637641
var truncatedParameters = switch (parameters) {
@@ -711,7 +715,6 @@ String _getDisplayText(
711715
) {
712716
return switch (suggestion) {
713717
SuggestionData(:var displayText) => displayText,
714-
FunctionCall() => 'call()',
715718
OverrideSuggestion(:var data, :var completion) =>
716719
data?.displayText ?? completion,
717720
TypedSuggestion(:var data, :var completion) =>

pkg/analysis_server/lib/src/services/completion/dart/candidate_suggestion.dart

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -388,13 +388,27 @@ final class FormalParameterSuggestion extends CandidateSuggestion
388388

389389
/// The information about a candidate suggestion based on the method `call`
390390
/// defined on the class `Function`.
391-
final class FunctionCall extends CandidateSuggestion {
391+
final class FunctionCall extends TypedExecutableSuggestion {
392+
final ExecutableElement? element;
393+
394+
@override
395+
final FunctionType type;
396+
392397
/// Initialize a newly created candidate suggestion to suggest the method
393398
/// `call` defined on the class `Function`.
394-
FunctionCall({required super.matcherScore});
399+
FunctionCall({
400+
required super.matcherScore,
401+
required this.type,
402+
required super.replacementRange,
403+
required super.importData,
404+
required super.kind,
405+
required this.element,
406+
super.addTypeAnnotation,
407+
super.keyword,
408+
});
395409

396410
@override
397-
String get completion => 'call()';
411+
String get baseCompletion => 'call';
398412
}
399413

400414
/// The information about a candidate suggestion based on a getter.
@@ -719,7 +733,8 @@ mixin MemberSuggestion implements ElementBasedSuggestion {
719733

720734
/// The information about a candidate suggestion based on a method.
721735
final class MethodSuggestion extends TypedExecutableSuggestion
722-
with MemberSuggestion {
736+
with MemberSuggestion
737+
implements ElementBasedSuggestion {
723738
@override
724739
final MethodElement element;
725740

@@ -973,7 +988,8 @@ sealed class ReplacementSuggestion extends CandidateSuggestion {
973988

974989
/// The information about a candidate suggestion for Flutter's `setState` method.
975990
final class SetStateMethodSuggestion extends TypedExecutableSuggestion
976-
with MemberSuggestion, SuggestionData {
991+
with MemberSuggestion, SuggestionData
992+
implements ElementBasedSuggestion {
977993
@override
978994
final MethodElement element;
979995

@@ -1526,8 +1542,6 @@ extension SuggestionBuilderExtension on SuggestionBuilder {
15261542
distance: suggestion.distance,
15271543
relevance: relevance,
15281544
);
1529-
case FunctionCall():
1530-
suggestFunctionCall();
15311545
case IdentifierSuggestion():
15321546
suggestName(
15331547
suggestion.completion,

0 commit comments

Comments
 (0)