Version 2.11.0-275.0.dev

Merge commit 'd38b366eb669c2776a95efb449844e2c8da6c71c' into 'dev'
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
index bf576ec..17e9a2d 100644
--- 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
@@ -6,6 +6,8 @@
 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/dart/element/element.dart';
+import 'package:analyzer/source/source_range.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';
@@ -16,41 +18,44 @@
 
   @override
   Future<void> compute(ChangeBuilder 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) {
+    var sourceRange = findIfNull();
+    if (sourceRange == null) {
       return;
     }
-    //
-    // Extract the information needed to build the edit.
-    //
-    var sourceRange =
-        range.endEnd(expression.leftOperand, expression.rightOperand);
-    //
-    // Build the edit.
-    //
+
     await builder.addDartFileEdit(file, (builder) {
       builder.addDeletion(sourceRange);
     });
   }
 
-  /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
+  /// Finds the dead if-null expression above [node].
+  SourceRange 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 range.endEnd(parent.leftOperand, parent.rightOperand);
+      }
+      if (parent is AssignmentExpression &&
+          parent.operator.type == TokenType.QUESTION_QUESTION_EQ &&
+          parent.rightHandSide == child) {
+        var assignee = parent.leftHandSide;
+        if (parent.parent is ExpressionStatement &&
+            assignee is SimpleIdentifier &&
+            assignee.staticElement is PromotableElement) {
+          return utils.getLinesRange(range.node(parent.parent));
+        } else {
+          return range.endEnd(parent.leftHandSide, parent.rightHandSide);
+        }
+      }
+      child = parent;
+      parent = parent.parent;
+    }
+    return null;
+  }
+
+  /// Returns an instance of this class. Used as a tear-off in `FixProcessor`.
   static RemoveDeadIfNull newInstance() => RemoveDeadIfNull();
 }
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 3797ce9..3f86e17 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
@@ -12,12 +12,78 @@
 
 void main() {
   defineReflectiveSuite(() {
+    defineReflectiveTests(DeadNullAwareAssignmentExpressionTest);
     defineReflectiveTests(DeadNullAwareExpressionTest);
     defineReflectiveTests(UnnecessaryNullInIfNullOperatorsTest);
   });
 }
 
 @reflectiveTest
+class DeadNullAwareAssignmentExpressionTest extends FixProcessorTest
+    with WithNullSafetyMixin {
+  @override
+  FixKind get kind => DartFixKind.REMOVE_IF_NULL_OPERATOR;
+
+  Future<void> test_assignmentExpression_nonPromotable() async {
+    await resolveTestCode('''
+class C {
+  int a = 1;
+  void f(int b) {
+    a ??= b;
+  }
+}
+''');
+    await assertHasFix('''
+class C {
+  int a = 1;
+  void f(int b) {
+    a;
+  }
+}
+''');
+  }
+
+  Future<void> test_assignmentExpression_promotable() async {
+    await resolveTestCode('''
+void f(int a, int b) {
+  a ??= b;
+}
+''');
+    await assertHasFix('''
+void f(int a, int b) {
+}
+''');
+  }
+
+  Future<void> test_immediateChild() async {
+    await resolveTestCode('''
+void f(int a, int b) => a ??= b;
+''');
+    await assertHasFix('''
+void f(int a, int b) => a;
+''');
+  }
+
+  Future<void> test_nestedChild() async {
+    await resolveTestCode('''
+void f(int a, int b) => a ??= b * 2 + 1;
+''');
+    await assertHasFix('''
+void f(int a, int b) => a;
+''');
+  }
+
+  Future<void> test_nestedChild_onRight() async {
+    await resolveTestCode('''
+void f(int a, int b, int c) => a = b ??= c;
+''');
+    await assertHasFix('''
+void f(int a, int b, int c) => a = b;
+''');
+  }
+}
+
+@reflectiveTest
 class DeadNullAwareExpressionTest extends FixProcessorTest
     with WithNullSafetyMixin {
   @override
diff --git a/tools/VERSION b/tools/VERSION
index ce12cbe..d9154c3 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 11
 PATCH 0
-PRERELEASE 274
+PRERELEASE 275
 PRERELEASE_PATCH 0
\ No newline at end of file