Add quick fix for `UNNECESSARY_FINAL`

Fixes #48778

Change-Id: I86b0b8344768db9653c16507a88d4f454dc86ed6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/240840
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/remove_unnecessary_final.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_unnecessary_final.dart
new file mode 100644
index 0000000..73078a5
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_unnecessary_final.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2022, 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/dart/ast/token.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class RemoveUnnecessaryFinal extends CorrectionProducer {
+  @override
+  bool get canBeAppliedInBulk => true;
+
+  @override
+  bool get canBeAppliedToFile => true;
+
+  @override
+  FixKind get fixKind => DartFixKind.REMOVE_UNNECESSARY_FINAL;
+
+  @override
+  FixKind get multiFixKind => DartFixKind.REMOVE_UNNECESSARY_FINAL_MULTI;
+
+  @override
+  Future<void> compute(ChangeBuilder builder) async {
+    var node = this.node;
+    Token? keyword;
+    if (node is FieldFormalParameter) {
+      keyword = node.keyword;
+    } else if (node is SuperFormalParameter) {
+      keyword = node.keyword;
+    }
+    if (keyword == null) return;
+
+    await builder.addDartFileEdit(file, (builder) {
+      builder.addDeletion(range.startStart(keyword!, keyword.next!));
+    });
+  }
+}
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 5adb652..cba9147 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
@@ -1456,7 +1456,7 @@
 HintCode.UNNECESSARY_CAST:
   status: hasFix
 HintCode.UNNECESSARY_FINAL:
-  status: needsEvaluation
+  status: hasFix
 HintCode.UNNECESSARY_IGNORE:
   status: needsEvaluation
 HintCode.UNNECESSARY_IMPORT:
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index ed8f791..e5e3585 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -1128,6 +1128,16 @@
     DartFixKindPriority.IN_FILE,
     'Remove all unnecessary casts in file',
   );
+  static const REMOVE_UNNECESSARY_FINAL = FixKind(
+    'dart.fix.remove.unnecessaryFinal',
+    DartFixKindPriority.DEFAULT,
+    "Remove unnecessary 'final'",
+  );
+  static const REMOVE_UNNECESSARY_FINAL_MULTI = FixKind(
+    'dart.fix.remove.unnecessaryFinal.multi',
+    DartFixKindPriority.IN_FILE,
+    "Remove all unnecessary 'final's in file",
+  );
   static const REMOVE_UNNECESSARY_CONST = FixKind(
     'dart.fix.remove.unnecessaryConst',
     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 c79ab5f..61583a8 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -139,6 +139,7 @@
 import 'package:analysis_server/src/services/correction/dart/remove_type_annotation.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_type_arguments.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_unnecessary_cast.dart';
+import 'package:analysis_server/src/services/correction/dart/remove_unnecessary_final.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_unnecessary_late.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_unnecessary_new.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_unnecessary_parentheses.dart';
@@ -1326,6 +1327,9 @@
     HintCode.UNNECESSARY_CAST: [
       RemoveUnnecessaryCast.new,
     ],
+    HintCode.UNNECESSARY_FINAL: [
+      RemoveUnnecessaryFinal.new,
+    ],
     HintCode.UNNECESSARY_IMPORT: [
       RemoveUnusedImport.new,
     ],
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_final_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_final_test.dart
new file mode 100644
index 0000000..70f9035f
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_final_test.dart
@@ -0,0 +1,122 @@
+// Copyright (c) 2022, 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/src/error/codes.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(RemoveUnnecessaryFinalBulkTest);
+    defineReflectiveTests(RemoveUnnecessaryFinalMultiTest);
+    defineReflectiveTests(RemoveUnnecessaryFinalTest);
+  });
+}
+
+@reflectiveTest
+class RemoveUnnecessaryFinalBulkTest extends BulkFixProcessorTest {
+  Future<void> test_assignment() async {
+    await resolveTestCode('''
+class A {
+  A(final this.value);
+  int value;
+}
+class B extends A {
+  B(final super.value);
+}
+''');
+    await assertHasFix('''
+class A {
+  A(this.value);
+  int value;
+}
+class B extends A {
+  B(super.value);
+}
+''');
+  }
+}
+
+@reflectiveTest
+class RemoveUnnecessaryFinalMultiTest extends FixProcessorTest {
+  @override
+  FixKind get kind => DartFixKind.REMOVE_UNNECESSARY_FINAL_MULTI;
+
+  Future<void> test_multi() async {
+    await resolveTestCode('''
+class A {
+  A(final this.v1, final this.v2);
+  int v1;
+  int v2;
+}
+''');
+    await assertHasFixAllFix(HintCode.UNNECESSARY_FINAL, '''
+class A {
+  A(this.v1, this.v2);
+  int v1;
+  int v2;
+}
+''');
+  }
+}
+
+@reflectiveTest
+class RemoveUnnecessaryFinalTest extends FixProcessorTest {
+  @override
+  FixKind get kind => DartFixKind.REMOVE_UNNECESSARY_FINAL;
+
+  Future<void> test_positional() async {
+    await resolveTestCode('''
+class C {
+  C([final this.value = 0]);
+  int value;
+}
+''');
+    await assertHasFix('''
+class C {
+  C([this.value = 0]);
+  int value;
+}
+''');
+  }
+
+  Future<void> test_super() async {
+    await resolveTestCode('''
+class A {
+  A(this.value);
+  int value;
+}
+class B extends A {
+  B(final super.value);
+}
+''');
+    await assertHasFix('''
+class A {
+  A(this.value);
+  int value;
+}
+class B extends A {
+  B(super.value);
+}
+''');
+  }
+
+  Future<void> test_this() async {
+    await resolveTestCode('''
+class C {
+  C(final this.value);
+  int value;
+}
+''');
+    await assertHasFix('''
+class C {
+  C(this.value);
+  int value;
+}
+''');
+  }
+}
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 768c4c6..291306b 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
@@ -169,6 +169,7 @@
 import 'remove_type_arguments_test.dart' as remove_type_arguments;
 import 'remove_unnecessary_cast_test.dart' as remove_unnecessary_cast;
 import 'remove_unnecessary_const_test.dart' as remove_unnecessary_const;
+import 'remove_unnecessary_final_test.dart' as remove_unnecessary_final;
 import 'remove_unnecessary_late_test.dart' as remove_unnecessary_late;
 import 'remove_unnecessary_new_test.dart' as remove_unnecessary_new;
 import 'remove_unnecessary_parentheses_test.dart'
@@ -383,6 +384,7 @@
     remove_type_arguments.main();
     remove_unnecessary_cast.main();
     remove_unnecessary_const.main();
+    remove_unnecessary_final.main();
     remove_unnecessary_late.main();
     remove_unnecessary_new.main();
     remove_unnecessary_parentheses.main();