Version 2.12.0-185.0.dev

Merge commit 'd407e16cff550aab55004bdd0fd53a76ce290c49' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_unnecessary_parentheses.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_unnecessary_parentheses.dart
new file mode 100644
index 0000000..b3d860c
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_unnecessary_parentheses.dart
@@ -0,0 +1,31 @@
+// 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: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';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class RemoveUnnecessaryParentheses extends CorrectionProducer {
+  @override
+  FixKind get fixKind => DartFixKind.REMOVE_UNNECESSARY_PARENTHESES;
+
+  @override
+  Future<void> compute(ChangeBuilder builder) async {
+    var outer = coveredNode;
+    if (outer is ParenthesizedExpression &&
+        outer.parent is! ParenthesizedExpression) {
+      await builder.addDartFileEdit(file, (builder) {
+        builder.addDeletion(range.token(outer.leftParenthesis));
+        builder.addDeletion(range.token(outer.rightParenthesis));
+      });
+    }
+  }
+
+  /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
+  static RemoveUnnecessaryParentheses newInstance() =>
+      RemoveUnnecessaryParentheses();
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index 67f5bed..ec4fa0c 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -400,6 +400,11 @@
       'Remove unnecessary const keyword');
   static const REMOVE_UNNECESSARY_NEW = FixKind(
       'dart.fix.remove.unnecessaryNew', 50, 'Remove unnecessary new keyword');
+  static const REMOVE_UNNECESSARY_PARENTHESES = FixKind(
+      'dart.fix.remove.unnecessaryParentheses',
+      50,
+      'Remove unnecessary parentheses',
+      appliedTogetherMessage: 'Remove all unnecessary parentheses in file');
   static const REMOVE_UNUSED_CATCH_CLAUSE = FixKind(
       'dart.fix.remove.unusedCatchClause', 50, "Remove unused 'catch' clause");
   static const REMOVE_UNUSED_CATCH_STACK = FixKind(
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 e240962..832147a 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -111,6 +111,7 @@
 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_new.dart';
+import 'package:analysis_server/src/services/correction/dart/remove_unnecessary_parentheses.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_unused.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_unused_catch_clause.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_unused_catch_stack.dart';
@@ -478,6 +479,9 @@
     LintNames.unnecessary_overrides: [
       RemoveMethodDeclaration.newInstance,
     ],
+    LintNames.unnecessary_parenthesis: [
+      RemoveUnnecessaryParentheses.newInstance,
+    ],
     LintNames.unnecessary_this: [
       RemoveThisExpression.newInstance,
     ],
diff --git a/pkg/analysis_server/lib/src/services/linter/lint_names.dart b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
index c291cc2..48910dc 100644
--- a/pkg/analysis_server/lib/src/services/linter/lint_names.dart
+++ b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
@@ -95,6 +95,7 @@
   static const String unnecessary_null_in_if_null_operators =
       'unnecessary_null_in_if_null_operators';
   static const String unnecessary_overrides = 'unnecessary_overrides';
+  static const String unnecessary_parenthesis = 'unnecessary_parenthesis';
   static const String unnecessary_this = 'unnecessary_this';
   static const String use_full_hex_values_for_flutter_colors =
       'use_full_hex_values_for_flutter_colors';
diff --git a/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart b/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
index 46ff4d4..70b6a66 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
@@ -31,6 +31,19 @@
   /// Return the lint code being tested.
   String get lintCode;
 
+  /// Return the [LintCode] for the [lintCode] (which is actually a name).
+  Future<LintCode> lintCodeByName(String name) async {
+    var errors = await _computeErrors();
+    var lintCodeSet = errors
+        .map((error) => error.errorCode)
+        .where((errorCode) => errorCode.name == name)
+        .toSet();
+    if (lintCodeSet.length != 1) {
+      fail('Expected exactly one LintCode, actually: $lintCodeSet');
+    }
+    return lintCodeSet.single;
+  }
+
   bool Function(AnalysisError) lintNameFilter(String name) {
     return (e) {
       return e.errorCode is LintCode && e.errorCode.name == name;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_parentheses_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_parentheses_test.dart
new file mode 100644
index 0000000..206e56f
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_parentheses_test.dart
@@ -0,0 +1,78 @@
+// 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:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/linter/lint_names.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(RemoveUnnecessaryParenthesesTest);
+  });
+}
+
+@reflectiveTest
+class RemoveUnnecessaryParenthesesTest extends FixProcessorLintTest {
+  @override
+  FixKind get kind => DartFixKind.REMOVE_UNNECESSARY_PARENTHESES;
+
+  @override
+  String get lintCode => LintNames.unnecessary_parenthesis;
+
+  Future<void> test_all() async {
+    await resolveTestCode('''
+void f() {
+  (1);
+  (22);
+  (333);
+}
+''');
+    var lintCode = await lintCodeByName(this.lintCode);
+    await assertHasFixAllFix(lintCode, '''
+void f() {
+  1;
+  22;
+  333;
+}
+''');
+  }
+
+  Future<void> test_double_atInner() async {
+    await resolveTestCode('''
+void f() {
+  ((42));
+}
+''');
+    await assertNoFix(errorFilter: (e) => e.offset == 14);
+  }
+
+  Future<void> test_double_atOuter() async {
+    await resolveTestCode('''
+void f() {
+  ((42));
+}
+''');
+    await assertHasFix('''
+void f() {
+  (42);
+}
+''', errorFilter: (e) => e.offset == 13);
+  }
+
+  Future<void> test_single() async {
+    await resolveTestCode('''
+void f() {
+  (42);
+}
+''');
+    await assertHasFix('''
+void f() {
+  42;
+}
+''');
+  }
+}
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 6b82811..fc73623 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
@@ -128,6 +128,8 @@
 import 'remove_unnecessary_cast_test.dart' as remove_unnecessary_cast;
 import 'remove_unnecessary_const_test.dart' as remove_unnecessary_const;
 import 'remove_unnecessary_new_test.dart' as remove_unnecessary_new;
+import 'remove_unnecessary_parentheses_test.dart'
+    as remove_unnecessary_parentheses;
 import 'remove_unused_catch_clause_test.dart' as remove_unused_catch_clause;
 import 'remove_unused_catch_stack_test.dart' as remove_unused_catch_stack;
 import 'remove_unused_element_test.dart' as remove_unused_element;
@@ -283,6 +285,7 @@
     remove_unnecessary_cast.main();
     remove_unnecessary_const.main();
     remove_unnecessary_new.main();
+    remove_unnecessary_parentheses.main();
     remove_unused_catch_clause.main();
     remove_unused_catch_stack.main();
     remove_unused_element.main();
diff --git a/tools/VERSION b/tools/VERSION
index f4ded95..e708aa8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 184
+PRERELEASE 185
 PRERELEASE_PATCH 0
\ No newline at end of file