Version 2.15.0-274.0.dev
Merge commit 'fc79ced1aec05cd54a45365ea299e0f53c8e8634' into 'dev'
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index b1b2b36..18b8ab3 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -45,9 +45,6 @@
/// The next completion response id.
int _nextCompletionId = 0;
- /// Code completion performance for the last completion operation.
- CompletionPerformance? performance;
-
/// A list of code completion performance measurements for the latest
/// completion operation up to [performanceListMaxLength] measurements.
final RecentBuffer<CompletionPerformance> performanceList =
@@ -285,81 +282,93 @@
return;
}
- var resolvedUnit = await server.getResolvedUnit(file);
- if (resolvedUnit == null) {
- server.sendResponse(Response.fileNotAnalyzed(request, file));
- return;
- }
-
- server.requestStatistics?.addItemTimeNow(request, 'resolvedUnit');
-
- if (offset < 0 || offset > resolvedUnit.content.length) {
- server.sendResponse(Response.invalidParameter(
- request,
- 'params.offset',
- 'Expected offset between 0 and source length inclusive,'
- ' but found $offset'));
- return;
- }
-
- final completionPerformance = CompletionPerformance();
- recordRequest(completionPerformance, file, resolvedUnit.content, offset);
-
- await completionPerformance.runRequestOperation((performance) async {
- var completionRequest = DartCompletionRequest(
- resolvedUnit: resolvedUnit,
- offset: offset,
- dartdocDirectiveInfo: server.getDartdocDirectiveInfoFor(
- resolvedUnit,
- ),
- documentationCache: server.getDocumentationCacheFor(resolvedUnit),
- );
- setNewRequest(completionRequest);
-
- var librariesToImport = <Uri>[];
- var suggestions = <CompletionSuggestion>[];
- try {
- suggestions = await computeSuggestions(
- budget: budget,
- performance: performance,
- request: completionRequest,
- librariesToImport: librariesToImport,
+ var performance = OperationPerformanceImpl('<root>');
+ performance.runAsync(
+ 'request',
+ (performance) async {
+ var resolvedUnit = await performance.runAsync(
+ 'getResolvedUnit',
+ (performance) async {
+ return await server.getResolvedUnit(file);
+ },
);
- } on AbortCompletion {
- return server.sendResponse(
+ if (resolvedUnit == null) {
+ server.sendResponse(Response.fileNotAnalyzed(request, file));
+ return;
+ }
+
+ if (offset < 0 || offset > resolvedUnit.content.length) {
+ server.sendResponse(Response.invalidParameter(
+ request,
+ 'params.offset',
+ 'Expected offset between 0 and source length inclusive,'
+ ' but found $offset'));
+ return;
+ }
+
+ final completionPerformance = CompletionPerformance(
+ operation: performance,
+ path: file,
+ content: resolvedUnit.content,
+ offset: offset,
+ );
+ performanceList.add(completionPerformance);
+
+ var completionRequest = DartCompletionRequest(
+ resolvedUnit: resolvedUnit,
+ offset: offset,
+ dartdocDirectiveInfo: server.getDartdocDirectiveInfoFor(
+ resolvedUnit,
+ ),
+ documentationCache: server.getDocumentationCacheFor(resolvedUnit),
+ );
+ setNewRequest(completionRequest);
+
+ var librariesToImport = <Uri>[];
+ var suggestions = <CompletionSuggestion>[];
+ try {
+ suggestions = await computeSuggestions(
+ budget: budget,
+ performance: performance,
+ request: completionRequest,
+ librariesToImport: librariesToImport,
+ );
+ } on AbortCompletion {
+ return server.sendResponse(
+ CompletionGetSuggestions2Result(
+ completionRequest.replacementOffset,
+ completionRequest.replacementLength,
+ [],
+ [],
+ true,
+ ).toResponse(request.id),
+ );
+ }
+
+ performance.run('filter', (performance) {
+ performance.getDataInt('count').add(suggestions.length);
+ suggestions = fuzzyFilterSort(
+ pattern: completionRequest.targetPrefix,
+ suggestions: suggestions,
+ );
+ performance.getDataInt('matchCount').add(suggestions.length);
+ });
+
+ var lengthRestricted = suggestions.take(params.maxResults).toList();
+ var isIncomplete = lengthRestricted.length < suggestions.length;
+ completionPerformance.suggestionCount = lengthRestricted.length;
+
+ server.sendResponse(
CompletionGetSuggestions2Result(
completionRequest.replacementOffset,
completionRequest.replacementLength,
- [],
- [],
- true,
+ lengthRestricted,
+ librariesToImport.map((e) => '$e').toList(),
+ isIncomplete,
).toResponse(request.id),
);
- }
-
- performance.run('filter', (performance) {
- performance.getDataInt('count').add(suggestions.length);
- suggestions = fuzzyFilterSort(
- pattern: completionRequest.targetPrefix,
- suggestions: suggestions,
- );
- performance.getDataInt('matchCount').add(suggestions.length);
- });
-
- var lengthRestricted = suggestions.take(params.maxResults).toList();
- var isIncomplete = lengthRestricted.length < suggestions.length;
- completionPerformance.suggestionCount = lengthRestricted.length;
-
- server.sendResponse(
- CompletionGetSuggestions2Result(
- completionRequest.replacementOffset,
- completionRequest.replacementLength,
- lengthRestricted,
- librariesToImport.map((e) => '$e').toList(),
- isIncomplete,
- ).toResponse(request.id),
- );
- });
+ },
+ );
}
@override
@@ -410,161 +419,160 @@
Future<void> processRequest(Request request) async {
var budget = CompletionBudget(budgetDuration);
- final performance = this.performance = CompletionPerformance();
+ // extract and validate params
+ var params = CompletionGetSuggestionsParams.fromRequest(request);
+ var file = params.file;
+ var offset = params.offset;
- await performance.runRequestOperation((perf) async {
- // extract and validate params
- var params = CompletionGetSuggestionsParams.fromRequest(request);
- var file = params.file;
- var offset = params.offset;
-
- if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
- return;
- }
- if (file.endsWith('.yaml')) {
- // Return the response without results.
- var completionId = (_nextCompletionId++).toString();
- server.sendResponse(CompletionGetSuggestionsResult(completionId)
- .toResponse(request.id));
- // Send a notification with results.
- final suggestions = computeYamlSuggestions(file, offset);
- sendCompletionNotification(
- completionId,
- suggestions.replacementOffset,
- suggestions.replacementLength,
- suggestions.suggestions,
- null,
- null,
- null,
- null,
- );
- return;
- } else if (!file.endsWith('.dart')) {
- // Return the response without results.
- var completionId = (_nextCompletionId++).toString();
- server.sendResponse(CompletionGetSuggestionsResult(completionId)
- .toResponse(request.id));
- // Send a notification with results.
- sendCompletionNotification(
- completionId, offset, 0, [], null, null, null, null);
- return;
- }
-
- var resolvedUnit = await server.getResolvedUnit(file);
- if (resolvedUnit == null) {
- server.sendResponse(Response.fileNotAnalyzed(request, 'params.offset'));
- return;
- }
-
- server.requestStatistics?.addItemTimeNow(request, 'resolvedUnit');
-
- if (offset < 0 || offset > resolvedUnit.content.length) {
- server.sendResponse(Response.invalidParameter(
- request,
- 'params.offset',
- 'Expected offset between 0 and source length inclusive,'
- ' but found $offset'));
- return;
- }
-
- recordRequest(performance, file, resolvedUnit.content, offset);
-
- var declarationsTracker = server.declarationsTracker;
- if (declarationsTracker == null) {
- server.sendResponse(Response.unsupportedFeature(
- request.id, 'Completion is not enabled.'));
- return;
- }
-
- var completionRequest = DartCompletionRequest(
- resolvedUnit: resolvedUnit,
- offset: offset,
- dartdocDirectiveInfo: server.getDartdocDirectiveInfoFor(
- resolvedUnit,
- ),
- documentationCache: server.getDocumentationCacheFor(resolvedUnit),
- );
-
- var completionId = (_nextCompletionId++).toString();
-
- setNewRequest(completionRequest);
-
- // initial response without results
- server.sendResponse(
- CompletionGetSuggestionsResult(completionId).toResponse(request.id));
-
- // If the client opted into using available suggestion sets,
- // create the kinds set, so signal the completion manager about opt-in.
- Set<ElementKind>? includedElementKinds;
- Set<String>? includedElementNames;
- List<IncludedSuggestionRelevanceTag>? includedSuggestionRelevanceTags;
- if (subscriptions.contains(CompletionService.AVAILABLE_SUGGESTION_SETS)) {
- includedElementKinds = <ElementKind>{};
- includedElementNames = <String>{};
- includedSuggestionRelevanceTags = <IncludedSuggestionRelevanceTag>[];
- }
-
- // Compute suggestions in the background
- try {
- var suggestions = <CompletionSuggestion>[];
- try {
- suggestions = await computeSuggestions(
- budget: budget,
- performance: perf,
- request: completionRequest,
- includedElementKinds: includedElementKinds,
- includedElementNames: includedElementNames,
- includedSuggestionRelevanceTags: includedSuggestionRelevanceTags,
- );
- } on AbortCompletion {
- // Continue with empty suggestions list.
- }
- String? libraryFile;
- var includedSuggestionSets = <IncludedSuggestionSet>[];
- if (includedElementKinds != null && includedElementNames != null) {
- libraryFile = resolvedUnit.libraryElement.source.fullName;
- server.sendNotification(
- createExistingImportsNotification(resolvedUnit),
- );
- computeIncludedSetList(
- declarationsTracker,
- resolvedUnit,
- includedSuggestionSets,
- includedElementNames,
- );
- }
-
- const SEND_NOTIFICATION_TAG = 'send notification';
- perf.run(SEND_NOTIFICATION_TAG, (_) {
- sendCompletionNotification(
- completionId,
- completionRequest.replacementOffset,
- completionRequest.replacementLength,
- suggestions,
- libraryFile,
- includedSuggestionSets,
- includedElementKinds?.toList(),
- includedSuggestionRelevanceTags,
- );
- });
-
- performance.suggestionCount = suggestions.length;
- } finally {
- ifMatchesRequestClear(completionRequest);
- }
- });
- }
-
- /// If tracking code completion performance over time, then
- /// record addition information about the request in the performance record.
- void recordRequest(CompletionPerformance performance, String path,
- String content, int offset) {
- performance.path = path;
- if (performanceListMaxLength == 0) {
+ if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
return;
}
- performance.setContentsAndOffset(content, offset);
- performanceList.add(performance);
+
+ var performance = OperationPerformanceImpl('<root>');
+ performance.runAsync(
+ 'request',
+ (performance) async {
+ if (file.endsWith('.yaml')) {
+ // Return the response without results.
+ var completionId = (_nextCompletionId++).toString();
+ server.sendResponse(CompletionGetSuggestionsResult(completionId)
+ .toResponse(request.id));
+ // Send a notification with results.
+ final suggestions = computeYamlSuggestions(file, offset);
+ sendCompletionNotification(
+ completionId,
+ suggestions.replacementOffset,
+ suggestions.replacementLength,
+ suggestions.suggestions,
+ null,
+ null,
+ null,
+ null,
+ );
+ return;
+ } else if (!file.endsWith('.dart')) {
+ // Return the response without results.
+ var completionId = (_nextCompletionId++).toString();
+ server.sendResponse(CompletionGetSuggestionsResult(completionId)
+ .toResponse(request.id));
+ // Send a notification with results.
+ sendCompletionNotification(
+ completionId, offset, 0, [], null, null, null, null);
+ return;
+ }
+
+ var resolvedUnit = await server.getResolvedUnit(file);
+ if (resolvedUnit == null) {
+ server
+ .sendResponse(Response.fileNotAnalyzed(request, 'params.offset'));
+ return;
+ }
+
+ server.requestStatistics?.addItemTimeNow(request, 'resolvedUnit');
+
+ if (offset < 0 || offset > resolvedUnit.content.length) {
+ server.sendResponse(Response.invalidParameter(
+ request,
+ 'params.offset',
+ 'Expected offset between 0 and source length inclusive,'
+ ' but found $offset'));
+ return;
+ }
+
+ final completionPerformance = CompletionPerformance(
+ operation: performance,
+ path: file,
+ content: resolvedUnit.content,
+ offset: offset,
+ );
+ performanceList.add(completionPerformance);
+
+ var declarationsTracker = server.declarationsTracker;
+ if (declarationsTracker == null) {
+ server.sendResponse(Response.unsupportedFeature(
+ request.id, 'Completion is not enabled.'));
+ return;
+ }
+
+ var completionRequest = DartCompletionRequest(
+ resolvedUnit: resolvedUnit,
+ offset: offset,
+ dartdocDirectiveInfo: server.getDartdocDirectiveInfoFor(
+ resolvedUnit,
+ ),
+ documentationCache: server.getDocumentationCacheFor(resolvedUnit),
+ );
+
+ var completionId = (_nextCompletionId++).toString();
+
+ setNewRequest(completionRequest);
+
+ // initial response without results
+ server.sendResponse(CompletionGetSuggestionsResult(completionId)
+ .toResponse(request.id));
+
+ // If the client opted into using available suggestion sets,
+ // create the kinds set, so signal the completion manager about opt-in.
+ Set<ElementKind>? includedElementKinds;
+ Set<String>? includedElementNames;
+ List<IncludedSuggestionRelevanceTag>? includedSuggestionRelevanceTags;
+ if (subscriptions
+ .contains(CompletionService.AVAILABLE_SUGGESTION_SETS)) {
+ includedElementKinds = <ElementKind>{};
+ includedElementNames = <String>{};
+ includedSuggestionRelevanceTags = <IncludedSuggestionRelevanceTag>[];
+ }
+
+ // Compute suggestions in the background
+ try {
+ var suggestions = <CompletionSuggestion>[];
+ try {
+ suggestions = await computeSuggestions(
+ budget: budget,
+ performance: performance,
+ request: completionRequest,
+ includedElementKinds: includedElementKinds,
+ includedElementNames: includedElementNames,
+ includedSuggestionRelevanceTags: includedSuggestionRelevanceTags,
+ );
+ } on AbortCompletion {
+ // Continue with empty suggestions list.
+ }
+ String? libraryFile;
+ var includedSuggestionSets = <IncludedSuggestionSet>[];
+ if (includedElementKinds != null && includedElementNames != null) {
+ libraryFile = resolvedUnit.libraryElement.source.fullName;
+ server.sendNotification(
+ createExistingImportsNotification(resolvedUnit),
+ );
+ computeIncludedSetList(
+ declarationsTracker,
+ resolvedUnit,
+ includedSuggestionSets,
+ includedElementNames,
+ );
+ }
+
+ const SEND_NOTIFICATION_TAG = 'send notification';
+ performance.run(SEND_NOTIFICATION_TAG, (_) {
+ sendCompletionNotification(
+ completionId,
+ completionRequest.replacementOffset,
+ completionRequest.replacementLength,
+ suggestions,
+ libraryFile,
+ includedSuggestionSets,
+ includedElementKinds?.toList(),
+ includedSuggestionRelevanceTags,
+ );
+ });
+
+ completionPerformance.suggestionCount = suggestions.length;
+ } finally {
+ ifMatchesRequestClear(completionRequest);
+ }
+ },
+ );
}
/// Send completion notification results.
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index 85f3765..a0bf84c 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -26,6 +26,7 @@
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/src/services/available_declarations.dart';
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
+import 'package:analyzer/src/util/performance/operation_performance.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
import 'package:analyzer_plugin/src/utilities/completion/completion_target.dart';
@@ -202,216 +203,223 @@
String? triggerCharacter,
CancellationToken token,
) async {
- final performance = CompletionPerformance();
- performance.path = unit.path;
- performance.setContentsAndOffset(unit.content, offset);
- server.performanceStats.completion.add(performance);
-
- return await performance.runRequestOperation((perf) async {
- final completionRequest = DartCompletionRequest(
- resolvedUnit: unit,
- offset: offset,
- dartdocDirectiveInfo: server.getDartdocDirectiveInfoFor(unit),
- completionPreference: CompletionPreference.replace,
- );
- final target = completionRequest.target;
-
- if (triggerCharacter != null) {
- if (!_triggerCharacterValid(offset, triggerCharacter, target)) {
- return success([]);
- }
- }
-
- Set<ElementKind>? includedElementKinds;
- Set<String>? includedElementNames;
- List<IncludedSuggestionRelevanceTag>? includedSuggestionRelevanceTags;
- if (includeSuggestionSets) {
- includedElementKinds = <ElementKind>{};
- includedElementNames = <String>{};
- includedSuggestionRelevanceTags = <IncludedSuggestionRelevanceTag>[];
- }
-
- try {
- var contributor = DartCompletionManager(
- budget: CompletionBudget(CompletionBudget.defaultDuration),
- includedElementKinds: includedElementKinds,
- includedElementNames: includedElementNames,
- includedSuggestionRelevanceTags: includedSuggestionRelevanceTags,
+ var performance = OperationPerformanceImpl('<root>');
+ return await performance.runAsync(
+ 'request',
+ (performance) async {
+ final completionPerformance = CompletionPerformance(
+ operation: performance,
+ path: unit.path,
+ content: unit.content,
+ offset: offset,
);
+ server.performanceStats.completion.add(completionPerformance);
- final serverSuggestions = await contributor.computeSuggestions(
- completionRequest,
- perf,
+ final completionRequest = DartCompletionRequest(
+ resolvedUnit: unit,
+ offset: offset,
+ dartdocDirectiveInfo: server.getDartdocDirectiveInfoFor(unit),
+ completionPreference: CompletionPreference.replace,
);
+ final target = completionRequest.target;
- final insertLength = _computeInsertLength(
- offset,
- completionRequest.replacementOffset,
- completionRequest.replacementLength,
- );
-
- if (token.isCancellationRequested) {
- return cancelled();
+ if (triggerCharacter != null) {
+ if (!_triggerCharacterValid(offset, triggerCharacter, target)) {
+ return success([]);
+ }
}
- /// completeFunctionCalls should be suppressed if the target is an
- /// invocation that already has an argument list, otherwise we would
- /// insert dupes.
- final completeFunctionCalls = _hasExistingArgList(target.entity)
- ? false
- : server.clientConfiguration.global.completeFunctionCalls;
+ Set<ElementKind>? includedElementKinds;
+ Set<String>? includedElementNames;
+ List<IncludedSuggestionRelevanceTag>? includedSuggestionRelevanceTags;
+ if (includeSuggestionSets) {
+ includedElementKinds = <ElementKind>{};
+ includedElementNames = <String>{};
+ includedSuggestionRelevanceTags = <IncludedSuggestionRelevanceTag>[];
+ }
- final results = serverSuggestions.map(
- (item) {
- var itemReplacementOffset =
- item.replacementOffset ?? completionRequest.replacementOffset;
- var itemReplacementLength =
- item.replacementLength ?? completionRequest.replacementLength;
- var itemInsertLength = insertLength;
-
- // Recompute the insert length if it may be affected by the above.
- if (item.replacementOffset != null ||
- item.replacementLength != null) {
- itemInsertLength = _computeInsertLength(
- offset, itemReplacementOffset, itemInsertLength);
- }
-
- return toCompletionItem(
- capabilities,
- unit.lineInfo,
- item,
- itemReplacementOffset,
- itemInsertLength,
- itemReplacementLength,
- // TODO(dantup): Including commit characters in every completion
- // increases the payload size. The LSP spec is ambigious
- // about how this should be handled (and VS Code requires it) but
- // this should be removed (or made conditional based on a capability)
- // depending on how the spec is updated.
- // https://github.com/microsoft/vscode-languageserver-node/issues/673
- includeCommitCharacters:
- server.clientConfiguration.global.previewCommitCharacters,
- completeFunctionCalls: completeFunctionCalls,
- );
- },
- ).toList();
-
- // Now compute items in suggestion sets.
- var includedSuggestionSets = <IncludedSuggestionSet>[];
- final declarationsTracker = server.declarationsTracker;
- if (declarationsTracker != null &&
- includedElementKinds != null &&
- includedElementNames != null &&
- includedSuggestionRelevanceTags != null) {
- computeIncludedSetList(
- declarationsTracker,
- unit,
- includedSuggestionSets,
- includedElementNames,
+ try {
+ var contributor = DartCompletionManager(
+ budget: CompletionBudget(CompletionBudget.defaultDuration),
+ includedElementKinds: includedElementKinds,
+ includedElementNames: includedElementNames,
+ includedSuggestionRelevanceTags: includedSuggestionRelevanceTags,
);
- // Build a fast lookup for imported symbols so that we can filter out
- // duplicates.
- final alreadyImportedSymbols = _buildLookupOfImportedSymbols(unit);
+ final serverSuggestions = await contributor.computeSuggestions(
+ completionRequest,
+ performance,
+ );
- includedSuggestionSets.forEach((includedSet) {
- final library = declarationsTracker.getLibrary(includedSet.id);
- if (library == null) {
- return;
- }
+ final insertLength = _computeInsertLength(
+ offset,
+ completionRequest.replacementOffset,
+ completionRequest.replacementLength,
+ );
- // Make a fast lookup for tag relevance.
- final tagBoosts = <String, int>{};
- includedSuggestionRelevanceTags!
- .forEach((t) => tagBoosts[t.tag] = t.relevanceBoost);
+ if (token.isCancellationRequested) {
+ return cancelled();
+ }
- // Only specific types of child declarations should be included.
- // This list matches what's in _protocolAvailableSuggestion in
- // the DAS implementation.
- bool shouldIncludeChild(Declaration child) =>
- child.kind == DeclarationKind.CONSTRUCTOR ||
- child.kind == DeclarationKind.ENUM_CONSTANT ||
- (child.kind == DeclarationKind.GETTER && child.isStatic) ||
- (child.kind == DeclarationKind.FIELD && child.isStatic);
+ /// completeFunctionCalls should be suppressed if the target is an
+ /// invocation that already has an argument list, otherwise we would
+ /// insert dupes.
+ final completeFunctionCalls = _hasExistingArgList(target.entity)
+ ? false
+ : server.clientConfiguration.global.completeFunctionCalls;
- // Collect declarations and their children.
- final allDeclarations = library.declarations
- .followedBy(library.declarations
- .expand((decl) => decl.children.where(shouldIncludeChild)))
- .toList();
+ final results = serverSuggestions.map(
+ (item) {
+ var itemReplacementOffset =
+ item.replacementOffset ?? completionRequest.replacementOffset;
+ var itemReplacementLength =
+ item.replacementLength ?? completionRequest.replacementLength;
+ var itemInsertLength = insertLength;
- final setResults = allDeclarations
- // Filter to only the kinds we should return.
- .where((item) => includedElementKinds!
- .contains(protocolElementKind(item.kind)))
- .where((item) {
- // Check existing imports to ensure we don't already import
- // this element (this exact element from its declaring
- // library, not just something with the same name). If we do
- // we'll want to skip it.
- final declaringUri =
- item.parent?.locationLibraryUri ?? item.locationLibraryUri!;
+ // Recompute the insert length if it may be affected by the above.
+ if (item.replacementOffset != null ||
+ item.replacementLength != null) {
+ itemInsertLength = _computeInsertLength(
+ offset, itemReplacementOffset, itemInsertLength);
+ }
- // For enums and named constructors, only the parent enum/class is in
- // the list of imported symbols so we use the parents name.
- final nameKey = item.kind == DeclarationKind.ENUM_CONSTANT ||
- item.kind == DeclarationKind.CONSTRUCTOR
- ? item.parent!.name
- : item.name;
- final key = _createImportedSymbolKey(nameKey, declaringUri);
- final importingUris = alreadyImportedSymbols[key];
+ return toCompletionItem(
+ capabilities,
+ unit.lineInfo,
+ item,
+ itemReplacementOffset,
+ itemInsertLength,
+ itemReplacementLength,
+ // TODO(dantup): Including commit characters in every completion
+ // increases the payload size. The LSP spec is ambigious
+ // about how this should be handled (and VS Code requires it) but
+ // this should be removed (or made conditional based on a capability)
+ // depending on how the spec is updated.
+ // https://github.com/microsoft/vscode-languageserver-node/issues/673
+ includeCommitCharacters:
+ server.clientConfiguration.global.previewCommitCharacters,
+ completeFunctionCalls: completeFunctionCalls,
+ );
+ },
+ ).toList();
- // Keep it only if:
- // - no existing imports include it
- // (in which case all libraries will be offered as
- // auto-imports)
- // - this is the first imported URI that includes it
- // (we don't want to repeat it for each imported library that
- // includes it)
- return importingUris == null ||
- importingUris.first == '${library.uri}';
- }).map((item) => declarationToCompletionItem(
- capabilities,
- unit.path,
- offset,
- includedSet,
- library,
- tagBoosts,
- unit.lineInfo,
- item,
- completionRequest.replacementOffset,
- insertLength,
- completionRequest.replacementLength,
- // TODO(dantup): Including commit characters in every completion
- // increases the payload size. The LSP spec is ambigious
- // about how this should be handled (and VS Code requires it) but
- // this should be removed (or made conditional based on a capability)
- // depending on how the spec is updated.
- // https://github.com/microsoft/vscode-languageserver-node/issues/673
- includeCommitCharacters: server
- .clientConfiguration.global.previewCommitCharacters,
- completeFunctionCalls: completeFunctionCalls,
- ));
- results.addAll(setResults);
- });
+ // Now compute items in suggestion sets.
+ var includedSuggestionSets = <IncludedSuggestionSet>[];
+ final declarationsTracker = server.declarationsTracker;
+ if (declarationsTracker != null &&
+ includedElementKinds != null &&
+ includedElementNames != null &&
+ includedSuggestionRelevanceTags != null) {
+ computeIncludedSetList(
+ declarationsTracker,
+ unit,
+ includedSuggestionSets,
+ includedElementNames,
+ );
+
+ // Build a fast lookup for imported symbols so that we can filter out
+ // duplicates.
+ final alreadyImportedSymbols = _buildLookupOfImportedSymbols(unit);
+
+ includedSuggestionSets.forEach((includedSet) {
+ final library = declarationsTracker.getLibrary(includedSet.id);
+ if (library == null) {
+ return;
+ }
+
+ // Make a fast lookup for tag relevance.
+ final tagBoosts = <String, int>{};
+ includedSuggestionRelevanceTags!
+ .forEach((t) => tagBoosts[t.tag] = t.relevanceBoost);
+
+ // Only specific types of child declarations should be included.
+ // This list matches what's in _protocolAvailableSuggestion in
+ // the DAS implementation.
+ bool shouldIncludeChild(Declaration child) =>
+ child.kind == DeclarationKind.CONSTRUCTOR ||
+ child.kind == DeclarationKind.ENUM_CONSTANT ||
+ (child.kind == DeclarationKind.GETTER && child.isStatic) ||
+ (child.kind == DeclarationKind.FIELD && child.isStatic);
+
+ // Collect declarations and their children.
+ final allDeclarations = library.declarations
+ .followedBy(library.declarations.expand(
+ (decl) => decl.children.where(shouldIncludeChild)))
+ .toList();
+
+ final setResults = allDeclarations
+ // Filter to only the kinds we should return.
+ .where((item) => includedElementKinds!
+ .contains(protocolElementKind(item.kind)))
+ .where((item) {
+ // Check existing imports to ensure we don't already import
+ // this element (this exact element from its declaring
+ // library, not just something with the same name). If we do
+ // we'll want to skip it.
+ final declaringUri =
+ item.parent?.locationLibraryUri ?? item.locationLibraryUri!;
+
+ // For enums and named constructors, only the parent enum/class is in
+ // the list of imported symbols so we use the parents name.
+ final nameKey = item.kind == DeclarationKind.ENUM_CONSTANT ||
+ item.kind == DeclarationKind.CONSTRUCTOR
+ ? item.parent!.name
+ : item.name;
+ final key = _createImportedSymbolKey(nameKey, declaringUri);
+ final importingUris = alreadyImportedSymbols[key];
+
+ // Keep it only if:
+ // - no existing imports include it
+ // (in which case all libraries will be offered as
+ // auto-imports)
+ // - this is the first imported URI that includes it
+ // (we don't want to repeat it for each imported library that
+ // includes it)
+ return importingUris == null ||
+ importingUris.first == '${library.uri}';
+ }).map((item) => declarationToCompletionItem(
+ capabilities,
+ unit.path,
+ offset,
+ includedSet,
+ library,
+ tagBoosts,
+ unit.lineInfo,
+ item,
+ completionRequest.replacementOffset,
+ insertLength,
+ completionRequest.replacementLength,
+ // TODO(dantup): Including commit characters in every completion
+ // increases the payload size. The LSP spec is ambigious
+ // about how this should be handled (and VS Code requires it) but
+ // this should be removed (or made conditional based on a capability)
+ // depending on how the spec is updated.
+ // https://github.com/microsoft/vscode-languageserver-node/issues/673
+ includeCommitCharacters: server
+ .clientConfiguration.global.previewCommitCharacters,
+ completeFunctionCalls: completeFunctionCalls,
+ ));
+ results.addAll(setResults);
+ });
+ }
+
+ // Perform fuzzy matching based on the identifier in front of the caret to
+ // reduce the size of the payload.
+ final fuzzyPattern = completionRequest.targetPrefix;
+ final fuzzyMatcher =
+ FuzzyMatcher(fuzzyPattern, matchStyle: MatchStyle.TEXT);
+
+ final matchingResults =
+ results.where((e) => fuzzyMatcher.score(e.label) > 0).toList();
+
+ completionPerformance.suggestionCount = results.length;
+
+ return success(matchingResults);
+ } on AbortCompletion {
+ return success([]);
}
-
- // Perform fuzzy matching based on the identifier in front of the caret to
- // reduce the size of the payload.
- final fuzzyPattern = completionRequest.targetPrefix;
- final fuzzyMatcher =
- FuzzyMatcher(fuzzyPattern, matchStyle: MatchStyle.TEXT);
-
- final matchingResults =
- results.where((e) => fuzzyMatcher.score(e.label) > 0).toList();
-
- performance.suggestionCount = results.length;
-
- return success(matchingResults);
- } on AbortCompletion {
- return success([]);
- }
- });
+ },
+ );
}
Future<ErrorOr<List<CompletionItem>>> _getServerYamlItems(
diff --git a/pkg/analysis_server/lib/src/services/completion/completion_performance.dart b/pkg/analysis_server/lib/src/services/completion/completion_performance.dart
index b0c2ac4..7815def 100644
--- a/pkg/analysis_server/lib/src/services/completion/completion_performance.dart
+++ b/pkg/analysis_server/lib/src/services/completion/completion_performance.dart
@@ -35,16 +35,19 @@
/// Overall performance of a code completion operation.
class CompletionPerformance {
- String? path;
- String snippet = '';
+ final OperationPerformance operation;
+ final String path;
+ final String snippet;
int suggestionCount = -1;
- OperationPerformance? _operation;
+
+ CompletionPerformance({
+ required this.operation,
+ required this.path,
+ required String content,
+ required int offset,
+ }) : snippet = _computeCompletionSnippet(content, offset);
int get elapsedInMilliseconds {
- var operation = _operation;
- if (operation == null) {
- throw StateError('Access of elapsed time before the operation is run');
- }
return operation.elapsed.inMilliseconds;
}
@@ -52,21 +55,4 @@
if (suggestionCount < 1) return '';
return '$suggestionCount';
}
-
- Future<T> runRequestOperation<T>(
- Future<T> Function(OperationPerformanceImpl) operation,
- ) async {
- var rootOperation = OperationPerformanceImpl('<root>');
- try {
- return rootOperation.runAsync('<request>', (performance) async {
- return await operation(performance);
- });
- } finally {
- _operation = rootOperation.children.first;
- }
- }
-
- void setContentsAndOffset(String contents, int offset) {
- snippet = _computeCompletionSnippet(contents, offset);
- }
}
diff --git a/pkg/analysis_server/lib/src/status/diagnostics.dart b/pkg/analysis_server/lib/src/status/diagnostics.dart
index 0d4629c..5ab8cff 100644
--- a/pkg/analysis_server/lib/src/status/diagnostics.dart
+++ b/pkg/analysis_server/lib/src/status/diagnostics.dart
@@ -206,7 +206,7 @@
buf.writeln(
'<tr><th>Time</th><th>Results</th><th>Source</th><th>Snippet</th></tr>');
for (var completion in completions) {
- var shortName = pathContext.basename(completion.path ?? '<missing path>');
+ var shortName = pathContext.basename(completion.path);
buf.writeln('<tr>'
'<td class="pre right">${printMilliseconds(completion.elapsedInMilliseconds)}</td>'
'<td class="right">${completion.suggestionCountStr}</td>'
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart b/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
index 4926490..e5d3720 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
@@ -5,7 +5,6 @@
import 'dart:async';
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
-import 'package:analysis_server/src/services/completion/completion_performance.dart';
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart'
show DartCompletionRequest;
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
@@ -530,24 +529,21 @@
Future computeSuggestions({int times = 200}) async {
result = await session.getResolvedUnit(testFile) as ResolvedUnitResult;
- return await CompletionPerformance().runRequestOperation(
- (performance) async {
- // Build the request
- var request = DartCompletionRequest(
- resolvedUnit: result,
- offset: completionOffset,
- dartdocDirectiveInfo: dartdocInfo,
- );
- var range = request.target.computeReplacementRange(request.offset);
- replacementOffset = range.offset;
- replacementLength = range.length;
-
- // Request completions
- suggestions = await computeContributedSuggestions(request);
- expect(suggestions, isNotNull, reason: 'expected suggestions');
- },
+ // Build the request
+ var request = DartCompletionRequest(
+ resolvedUnit: result,
+ offset: completionOffset,
+ dartdocDirectiveInfo: dartdocInfo,
);
+
+ var range = request.target.computeReplacementRange(request.offset);
+ replacementOffset = range.offset;
+ replacementLength = range.length;
+
+ // Request completions
+ suggestions = await computeContributedSuggestions(request);
+ expect(suggestions, isNotNull, reason: 'expected suggestions');
}
Never failedCompletion(String message,
diff --git a/pkg/analysis_server/test/stress/completion/completion_runner.dart b/pkg/analysis_server/test/stress/completion/completion_runner.dart
index 136684a..e0754be 100644
--- a/pkg/analysis_server/test/stress/completion/completion_runner.dart
+++ b/pkg/analysis_server/test/stress/completion/completion_runner.dart
@@ -2,7 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:analysis_server/src/services/completion/completion_performance.dart';
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
import 'package:analysis_server/src/utilities/null_string_sink.dart';
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
@@ -11,6 +10,7 @@
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/file_system/overlay_file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer/src/util/performance/operation_performance.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
/// A runner that can request code completion at the location of each identifier
@@ -60,7 +60,6 @@
var contributor = DartCompletionManager(
budget: CompletionBudget(const Duration(seconds: 30)),
);
- var statistics = CompletionPerformance();
var stamp = 1;
var fileCount = 0;
@@ -104,13 +103,9 @@
resolvedUnit: result,
offset: offset,
);
- var suggestions = await statistics.runRequestOperation(
- (performance) async {
- return await contributor.computeSuggestions(
- dartRequest,
- performance,
- );
- },
+ var suggestions = await contributor.computeSuggestions(
+ dartRequest,
+ OperationPerformanceImpl('<root>'),
);
timer.stop();
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics.dart b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
index c7a90fc..9b76591 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
@@ -10,7 +10,6 @@
import 'package:analysis_server/src/domains/completion/available_suggestions.dart';
import 'package:analysis_server/src/protocol/protocol_internal.dart';
import 'package:analysis_server/src/protocol_server.dart' as protocol;
-import 'package:analysis_server/src/services/completion/completion_performance.dart';
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
import 'package:analysis_server/src/services/completion/dart/documentation_cache.dart';
import 'package:analysis_server/src/services/completion/dart/feature_computer.dart';
@@ -1375,21 +1374,13 @@
documentationCache: documentationCache,
);
- late OpType opType;
- late List<protocol.CompletionSuggestion> suggestions;
- await CompletionPerformance().runRequestOperation(
- (performance) async {
- opType = OpType.forCompletion(request.target, request.offset);
- suggestions = await _computeCompletionSuggestions(
- listener,
- performance,
- request,
- metrics.availableSuggestions ? declarationsTracker : null,
- metrics.availableSuggestions
- ? availableSuggestionsParams
- : null,
- );
- },
+ var opType = OpType.forCompletion(request.target, request.offset);
+ var suggestions = await _computeCompletionSuggestions(
+ listener,
+ OperationPerformanceImpl('<root>'),
+ request,
+ metrics.availableSuggestions ? declarationsTracker : null,
+ metrics.availableSuggestions ? availableSuggestionsParams : null,
);
stopwatch.stop();
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index b53caa5..ffac3f2 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -4750,12 +4750,27 @@
void _checkUseOfCovariantInParameters(FormalParameterList node) {
var parent = node.parent;
- if (_enclosingClass != null &&
- parent is MethodDeclaration &&
- !parent.isStatic) {
+ if (_enclosingClass != null && parent is MethodDeclaration) {
+ // Either [parent] is a static method, in which case `EXTRANEOUS_MODIFIER`
+ // is reported by the parser, or [parent] is an instance method, in which
+ // case any use of `covariant` is legal.
return;
}
+ if (_enclosingExtension != null) {
+ // `INVALID_USE_OF_COVARIANT_IN_EXTENSION` is reported by the parser.
+ return;
+ }
+
+ if (parent is FunctionExpression) {
+ var parent2 = parent.parent;
+ if (parent2 is FunctionDeclaration && parent2.parent is CompilationUnit) {
+ // `EXTRANEOUS_MODIFIER` is reported by the parser, for library-level
+ // functions.
+ return;
+ }
+ }
+
NodeList<FormalParameter> parameters = node.parameters;
int length = parameters.length;
for (int i = 0; i < length; i++) {
@@ -4765,14 +4780,10 @@
}
var keyword = parameter.covariantKeyword;
if (keyword != null) {
- if (_enclosingExtension != null) {
- // Reported by the parser.
- } else {
- errorReporter.reportErrorForToken(
- CompileTimeErrorCode.INVALID_USE_OF_COVARIANT,
- keyword,
- );
- }
+ errorReporter.reportErrorForToken(
+ CompileTimeErrorCode.INVALID_USE_OF_COVARIANT,
+ keyword,
+ );
}
}
}
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_use_of_covariant_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_use_of_covariant_test.dart
new file mode 100644
index 0000000..20b7d5c
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/invalid_use_of_covariant_test.dart
@@ -0,0 +1,121 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/dart/error/hint_codes.g.dart';
+import 'package:analyzer/src/error/codes.g.dart';
+import 'package:analyzer/src/generated/parser.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(InvalidUseOfCovariantTest);
+ });
+}
+
+@reflectiveTest
+class InvalidUseOfCovariantTest extends PubPackageResolutionTest {
+ test_functionExpression() async {
+ await assertErrorsInCode('''
+Function f = (covariant int x) {};
+''', [
+ error(CompileTimeErrorCode.INVALID_USE_OF_COVARIANT, 14, 9),
+ ]);
+ }
+
+ test_functionType_inFunctionTypedParameterOfInstanceMethod() async {
+ await assertErrorsInCode('''
+class C {
+ void m(void p(covariant int)) {}
+}
+''', [
+ error(CompileTimeErrorCode.INVALID_USE_OF_COVARIANT, 26, 9),
+ ]);
+ }
+
+ test_functionType_inParameterOfInstanceMethod() async {
+ await assertErrorsInCode('''
+class C {
+ void m(void Function(covariant int) p) {}
+}
+''', [
+ error(CompileTimeErrorCode.INVALID_USE_OF_COVARIANT, 33, 9),
+ ]);
+ }
+
+ test_functionType_inTypeAlias() async {
+ await assertErrorsInCode('''
+typedef F = void Function(covariant int);
+''', [
+ error(CompileTimeErrorCode.INVALID_USE_OF_COVARIANT, 26, 9),
+ ]);
+ }
+
+ test_functionType_inTypeArgument() async {
+ await assertErrorsInCode('''
+List<void Function(covariant int)> a = [];
+}
+''', [
+ error(CompileTimeErrorCode.INVALID_USE_OF_COVARIANT, 19, 9),
+ // TODO(srawlins): Recover better from this situation (`covariant` in
+ // parameter in type argument).
+ error(ParserErrorCode.EXPECTED_EXECUTABLE, 43, 1),
+ ]);
+ }
+
+ test_functionType_inTypeParameterBound() async {
+ await assertErrorsInCode('''
+void foo<T extends void Function(covariant int)>() {}
+}
+''', [
+ error(CompileTimeErrorCode.INVALID_USE_OF_COVARIANT, 33, 9),
+ // TODO(srawlins): Recover better from this situation (`covariant` in
+ // parameter in bound).
+ error(ParserErrorCode.EXPECTED_EXECUTABLE, 54, 1),
+ ]);
+ }
+
+ test_localFunction() async {
+ await assertErrorsInCode('''
+void foo() {
+ void f(covariant int x) {}
+}
+''', [
+ error(HintCode.UNUSED_ELEMENT, 20, 1),
+ error(CompileTimeErrorCode.INVALID_USE_OF_COVARIANT, 22, 9),
+ ]);
+ }
+
+ test_staticFunction() async {
+ await assertErrorsInCode('''
+class C {
+ static void m(covariant int x) {}
+}
+''', [
+ // INVALID_USE_OF_COVARIANT is not reported here; it would be redundant.
+ error(ParserErrorCode.EXTRANEOUS_MODIFIER, 26, 9),
+ ]);
+ }
+
+ test_staticFunction_onMixin() async {
+ await assertErrorsInCode('''
+mixin M {
+ static void m(covariant int x) {}
+}
+''', [
+ // INVALID_USE_OF_COVARIANT is not reported here; it would be redundant.
+ error(ParserErrorCode.EXTRANEOUS_MODIFIER, 26, 9),
+ ]);
+ }
+
+ test_topLevelFunction() async {
+ await assertErrorsInCode('''
+void f(covariant int x) {}
+''', [
+ // INVALID_USE_OF_COVARIANT is not reported here; it would be redundant.
+ error(ParserErrorCode.EXTRANEOUS_MODIFIER, 7, 9),
+ ]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 263958a..3d245fa 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -354,6 +354,7 @@
import 'invalid_uri_test.dart' as invalid_uri;
import 'invalid_use_of_covariant_in_extension_test.dart'
as invalid_use_of_covariant_in_extension;
+import 'invalid_use_of_covariant_test.dart' as invalid_use_of_covariant;
import 'invalid_use_of_internal_member_test.dart'
as invalid_use_of_internal_member;
import 'invalid_use_of_protected_member_test.dart'
@@ -951,6 +952,7 @@
invalid_type_argument_in_const_map.main();
invalid_type_argument_in_const_set.main();
invalid_uri.main();
+ invalid_use_of_covariant.main();
invalid_use_of_covariant_in_extension.main();
invalid_use_of_internal_member.main();
invalid_use_of_protected_member.main();
diff --git a/tools/VERSION b/tools/VERSION
index 948e5f3..5fbe1c7 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 273
+PRERELEASE 274
PRERELEASE_PATCH 0
\ No newline at end of file