Add a fix to remove dead null-aware operators

Change-Id: I4573eb87b1e454923655b2b7010ab84f4698ce79
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/136922
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_dead_if_null.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_dead_if_null.dart
new file mode 100644
index 0000000..ceb57b5
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_dead_if_null.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2020, 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:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/source/source_range.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class RemoveDeadIfNull extends CorrectionProducer {
+  @override
+  Future<void> compute(DartChangeBuilder builder) async {
+    //
+    // Find the dead if-null expression.
+    //
+    BinaryExpression findIfNull() {
+      var child = node;
+      var parent = node.parent;
+      while (parent != null) {
+        if (parent is BinaryExpression &&
+            parent.operator.type == TokenType.QUESTION_QUESTION &&
+            parent.rightOperand == child) {
+          return parent;
+        }
+        child = parent;
+        parent = parent.parent;
+      }
+      return null;
+    }
+
+    var expression = findIfNull();
+    if (expression == null) {
+      return;
+    }
+    //
+    // Extract the information needed to build the edit.
+    //
+    SourceRange sourceRange =
+        range.endEnd(expression.leftOperand, expression.rightOperand);
+    //
+    // Build the edit.
+    //
+    await builder.addFileEdit(file, (DartFileEditBuilder builder) {
+      builder.addDeletion(sourceRange);
+    });
+  }
+}
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 3aaa193..236f62b 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -18,6 +18,7 @@
 import 'package:analysis_server/src/services/correction/dart/convert_to_null_aware.dart';
 import 'package:analysis_server/src/services/correction/dart/convert_to_set_literal.dart';
 import 'package:analysis_server/src/services/correction/dart/convert_to_where_type.dart';
+import 'package:analysis_server/src/services/correction/dart/remove_dead_if_null.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_if_null_operator.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
 import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
@@ -4697,7 +4698,12 @@
     }
 
     var errorCode = error.errorCode;
-    if (errorCode is LintCode) {
+    if (errorCode == StaticWarningCode.DEAD_NULL_AWARE_EXPRESSION) {
+      await compute(
+        RemoveDeadIfNull(),
+        DartFixKind.REMOVE_IF_NULL_OPERATOR,
+      );
+    } else if (errorCode is LintCode) {
       String name = errorCode.name;
       if (name == LintNames.prefer_collection_literals) {
         await compute(
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_if_null_operator_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_if_null_operator_test.dart
index 17c760d..20529c8 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_if_null_operator_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_if_null_operator_test.dart
@@ -11,12 +11,43 @@
 
 void main() {
   defineReflectiveSuite(() {
-    defineReflectiveTests(RemoveIfNullOperatorTest);
+    defineReflectiveTests(DeadNullAwareExpressionTest);
+    defineReflectiveTests(UnnecessaryNullInIfNullOperatorsTest);
   });
 }
 
 @reflectiveTest
-class RemoveIfNullOperatorTest extends FixProcessorLintTest {
+class DeadNullAwareExpressionTest extends FixProcessorTest {
+  @override
+  FixKind get kind => DartFixKind.REMOVE_IF_NULL_OPERATOR;
+
+  @override
+  void setUp() {
+    super.setUp();
+    createAnalysisOptionsFile(experiments: ['non-nullable']);
+  }
+
+  Future<void> test_immediateChild() async {
+    await resolveTestUnit('''
+int f(int a, int b) => a ?? b;
+''');
+    await assertHasFix('''
+int f(int a, int b) => a;
+''');
+  }
+
+  Future<void> test_nestedChild() async {
+    await resolveTestUnit('''
+int f(int a, int b) => a ?? b * 2 + 1;
+''');
+    await assertHasFix('''
+int f(int a, int b) => a;
+''');
+  }
+}
+
+@reflectiveTest
+class UnnecessaryNullInIfNullOperatorsTest extends FixProcessorLintTest {
   @override
   FixKind get kind => DartFixKind.REMOVE_IF_NULL_OPERATOR;