quick fix for `MIXIN_SUBTYPE_OF_BASE_IS_NOT_BASE`

Change-Id: I0a397f3aa1fd697014ff20debfbe08a1ea2f62e2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/368181
Auto-Submit: Phil Quitslund <pquitslund@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/dart/add_class_modifier.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_class_modifier.dart
new file mode 100644
index 0000000..6ac2b08
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_class_modifier.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2024, 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:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+
+class AddClassModifier extends ResolvedCorrectionProducer {
+  final String _modifier;
+
+  AddClassModifier.base() : this._('base');
+
+  AddClassModifier._(this._modifier);
+
+  @override
+  CorrectionApplicability get applicability =>
+      CorrectionApplicability.acrossSingleFile;
+
+  @override
+  List<String> get fixArguments => [_modifier];
+
+  @override
+  FixKind get fixKind => DartFixKind.ADD_CLASS_MODIFIER;
+
+  @override
+  FixKind get multiFixKind => DartFixKind.ADD_CLASS_MODIFIER_MULTI;
+
+  @override
+  Future<void> compute(ChangeBuilder builder) async {
+    var node = this.node;
+    if (node is! NamedCompilationUnitMember) return;
+
+    await builder.addDartFileEdit(file, (builder) {
+      builder.addSimpleInsertion(
+          node.firstTokenAfterCommentAndMetadata.offset, '$_modifier ');
+    });
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index c143e98..8c2e1ac 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -45,8 +45,8 @@
 #
 # Stats:
 # -  42 "needsEvaluation"
-# - 360 "needsFix"
-# - 391 "hasFix"
+# - 359 "needsFix"
+# - 392 "hasFix"
 # - 516 "noFix"
 
 AnalysisOptionsErrorCode.INCLUDED_FILE_PARSE_ERROR:
@@ -1118,9 +1118,7 @@
 CompileTimeErrorCode.MIXIN_ON_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER:
   status: noFix
 CompileTimeErrorCode.MIXIN_SUBTYPE_OF_BASE_IS_NOT_BASE:
-  status: needsFix
-  notes: |-
-    Add `base`.
+  status: hasFix
 CompileTimeErrorCode.MIXIN_SUBTYPE_OF_FINAL_IS_NOT_BASE:
   status: needsFix
   notes: |-
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index f0c2d69..33069c6 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -61,6 +61,16 @@
     DartFixKindPriority.IN_FILE,
     'Add empty argument lists everywhere in file',
   );
+  static const ADD_CLASS_MODIFIER = FixKind(
+    'dart.fix.add.class.modifier',
+    DartFixKindPriority.DEFAULT,
+    "Add ''{0}'' modifier",
+  );
+  static const ADD_CLASS_MODIFIER_MULTI = FixKind(
+    'dart.fix.add.class.modifier.multi',
+    DartFixKindPriority.IN_FILE,
+    "Add ''{0}'' modifier everywhere in file",
+  );
   static const ADD_CONST = FixKind(
     'dart.fix.add.const',
     DartFixKindPriority.DEFAULT,
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 641a979..55edad2 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -6,6 +6,7 @@
 import 'package:analysis_server/src/services/correction/dart/add_async.dart';
 import 'package:analysis_server/src/services/correction/dart/add_await.dart';
 import 'package:analysis_server/src/services/correction/dart/add_call_super.dart';
+import 'package:analysis_server/src/services/correction/dart/add_class_modifier.dart';
 import 'package:analysis_server/src/services/correction/dart/add_const.dart';
 import 'package:analysis_server/src/services/correction/dart/add_diagnostic_property_reference.dart';
 import 'package:analysis_server/src/services/correction/dart/add_empty_argument_list.dart';
@@ -1057,6 +1058,9 @@
   CompileTimeErrorCode.MIXIN_CLASS_DECLARATION_EXTENDS_NOT_OBJECT: [
     RemoveExtendsClause.new,
   ],
+  CompileTimeErrorCode.MIXIN_SUBTYPE_OF_BASE_IS_NOT_BASE: [
+    AddClassModifier.base,
+  ],
   CompileTimeErrorCode.MIXIN_OF_DISALLOWED_CLASS: [
     RemoveNameFromDeclarationClause.new,
   ],
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_class_modifier_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_class_modifier_test.dart
new file mode 100644
index 0000000..b00c2f3
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_class_modifier_test.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2024, 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:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'fix_processor.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(AddClassModifierTest);
+  });
+}
+
+@reflectiveTest
+class AddClassModifierTest extends FixProcessorTest {
+  @override
+  FixKind get kind => DartFixKind.ADD_CLASS_MODIFIER;
+
+  Future<void> test_mixinSubtypeOfBaseIsNotBase() async {
+    await resolveTestCode('''
+base class A {}
+mixin B implements A {}
+''');
+    await assertHasFix('''
+base class A {}
+base mixin B implements A {}
+''');
+  }
+
+  Future<void> test_mixinSubtypeOfBaseIsNotBase_withDoc() async {
+    await resolveTestCode('''
+base class A {}
+// Doc.
+mixin B implements A {}
+''');
+    await assertHasFix('''
+base class A {}
+// Doc.
+base mixin B implements A {}
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
index 595e1ad..b48d0f5 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
@@ -7,6 +7,7 @@
 import 'add_async_test.dart' as add_async;
 import 'add_await_test.dart' as add_await;
 import 'add_call_super_test.dart' as add_call_super;
+import 'add_class_modifier_test.dart' as add_class_modifier;
 import 'add_const_test.dart' as add_const;
 import 'add_curly_braces_test.dart' as add_curly_braces;
 import 'add_diagnostic_property_reference_test.dart'
@@ -295,6 +296,7 @@
     add_async.main();
     add_await.main();
     add_call_super.main();
+    add_class_modifier.main();
     add_const.main();
     add_curly_braces.main();
     add_diagnostic_property_reference.main();