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