[analysis_server] Check for bulk-fixable errors to skip resolution during bulk fix operations

Change-Id: I21a9fa4ed71839e7e196240c0bda31403cfea96c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/308080
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index 529db27..8edae78 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -533,7 +533,19 @@
   }
 
   /// Returns whether [error] is something that might be fixable.
-  bool _isFixableError(AnalysisError error) => hasFix(error.errorCode);
+  bool _isFixableError(AnalysisError error) {
+    final errorCode = error.errorCode;
+
+    // Special cases that can be bulk fixed by this class but not by
+    // FixProcessor.
+    if (errorCode == WarningCode.DUPLICATE_IMPORT ||
+        errorCode == HintCode.UNNECESSARY_IMPORT ||
+        errorCode == WarningCode.UNUSED_IMPORT) {
+      return true;
+    }
+
+    return FixProcessor.canBulkFix(errorCode);
+  }
 
   /// Return the override set corresponding to the given [result], or `null` if
   /// there is no corresponding configuration file or the file content isn't a
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index 1643b50..4b28c47 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -19,7 +19,8 @@
 bool hasFix(ErrorCode errorCode) {
   if (errorCode is LintCode) {
     var lintName = errorCode.name;
-    return FixProcessor.lintProducerMap.containsKey(lintName);
+    return FixProcessor.lintProducerMap.containsKey(lintName) ||
+        FixProcessor.lintMultiProducerMap.containsKey(lintName);
   }
   // TODO(brianwilkerson) Either deprecate the part of the protocol supported by
   //  this function, or handle error codes associated with non-dart files.
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 0f76947..02f6931 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -5,6 +5,7 @@
 import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
 import 'package:analysis_server/plugin/edit/fix/fix_dart.dart';
 import 'package:analysis_server/src/services/correction/base_processor.dart';
+import 'package:analysis_server/src/services/correction/bulk_fix_processor.dart';
 import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
 import 'package:analysis_server/src/services/correction/dart/add_async.dart';
 import 'package:analysis_server/src/services/correction/dart/add_await.dart';
@@ -376,6 +377,9 @@
 
 /// The computer for Dart fixes.
 class FixProcessor extends BaseProcessor {
+  /// Cached results of [canBulkFix].
+  static final Map<ErrorCode, bool> _bulkFixableErrorCodes = {};
+
   static final Map<String, List<MultiProducerGenerator>> lintMultiProducerMap =
       {
     LintNames.deprecated_member_use_from_same_package: [
@@ -1821,6 +1825,36 @@
     }
   }
 
+  /// Returns whether [errorCode] is an error that can be fixed in bulk.
+  static bool canBulkFix(ErrorCode errorCode) {
+    bool hasBulkFixProducers(List<ProducerGenerator>? producers) {
+      return producers != null &&
+          producers.any((producer) => producer().canBeAppliedInBulk);
+    }
+
+    return _bulkFixableErrorCodes.putIfAbsent(errorCode, () {
+      if (errorCode is LintCode) {
+        final producers = FixProcessor.lintProducerMap[errorCode.name];
+        if (hasBulkFixProducers(producers)) {
+          return true;
+        }
+
+        return FixProcessor.lintMultiProducerMap.containsKey(errorCode.name);
+      }
+
+      final producers = FixProcessor.nonLintProducerMap[errorCode];
+      if (hasBulkFixProducers(producers)) {
+        return true;
+      }
+
+      // We can't do detailed checks on multi-producers because the set of
+      // producers may vary depending on the resolved unit (we must configure
+      // them before we can determine the producers).
+      return FixProcessor.nonLintMultiProducerMap.containsKey(errorCode) ||
+          BulkFixProcessor.nonLintMultiProducerMap.containsKey(errorCode);
+    });
+  }
+
   /// Associate the given correction producer [generator] with the lint with the
   /// given [lintName].
   static void registerFixForLint(String lintName, ProducerGenerator generator) {