Migration: when removing dead code, remove unnecessay `??=`s
Fixes #38676.
Change-Id: Icc24c6f070d7f3e8a74fc9494aef3e4c2583fac4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/146320
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/nnbd_migration/lib/nnbd_migration.dart b/pkg/nnbd_migration/lib/nnbd_migration.dart
index 7388f0c..613c600 100644
--- a/pkg/nnbd_migration/lib/nnbd_migration.dart
+++ b/pkg/nnbd_migration/lib/nnbd_migration.dart
@@ -147,6 +147,12 @@
'Changed a null-aware access into an ordinary access, because the target cannot be null',
kind: NullabilityFixKind.removeDeadCode);
+ /// A null-aware assignment was removed because its LHS is non-nullable.
+ static const removeNullAwareAssignment = const NullabilityFixDescription._(
+ appliedMessage:
+ 'Removed a null-aware assignment, because the target cannot be null',
+ kind: NullabilityFixKind.removeDeadCode);
+
/// A message used to indicate a fix has been applied.
final String appliedMessage;
diff --git a/pkg/nnbd_migration/lib/src/fix_aggregator.dart b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
index 8576d1e..ba4452c 100644
--- a/pkg/nnbd_migration/lib/src/fix_aggregator.dart
+++ b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
@@ -319,6 +319,12 @@
NodeProducingEditPlan _apply(
AssignmentExpression node, FixAggregator aggregator) {
var lhsPlan = aggregator.planForNode(node.leftHandSide);
+ if (isWeakNullAware && !aggregator._warnOnWeakCode) {
+ // Just keep the LHS
+ return aggregator.planner.extract(node, lhsPlan as NodeProducingEditPlan,
+ infoAfter: AtomicEditInfo(
+ NullabilityFixDescription.removeNullAwareAssignment, const {}));
+ }
var operatorPlan = _makeOperatorPlan(aggregator, node, node.operator);
var rhsPlan = aggregator.planForNode(node.rightHandSide);
var innerPlans = <EditPlan>[
@@ -335,6 +341,7 @@
var operatorPlan = super._makeOperatorPlan(aggregator, node, operator);
if (operatorPlan != null) return operatorPlan;
if (isWeakNullAware) {
+ assert(aggregator._warnOnWeakCode);
return aggregator.planner.informativeMessageForToken(node, operator,
info: AtomicEditInfo(
NullabilityFixDescription
diff --git a/pkg/nnbd_migration/test/fix_aggregator_test.dart b/pkg/nnbd_migration/test/fix_aggregator_test.dart
index f60af11..db9f2ba 100644
--- a/pkg/nnbd_migration/test/fix_aggregator_test.dart
+++ b/pkg/nnbd_migration/test/fix_aggregator_test.dart
@@ -144,6 +144,16 @@
expect(edit.length, '??='.length);
}
+ Future<void> test_assignment_weak_null_aware_remove() async {
+ var content = 'f(int x, int y) => x ??= y;';
+ await analyze(content);
+ var previewInfo = run({
+ findNode.assignment('??='): NodeChangeForAssignment()
+ ..isWeakNullAware = true
+ }, warnOnWeakCode: false);
+ expect(previewInfo.applyTo(code), 'f(int x, int y) => x;');
+ }
+
Future<void> test_eliminateDeadIf_changesInKeptCode() async {
await analyze('''
f(int i, int/*?*/ j) {
diff --git a/pkg/nnbd_migration/test/front_end/info_builder_test.dart b/pkg/nnbd_migration/test/front_end/info_builder_test.dart
index 8e84aaf..a1490f0 100644
--- a/pkg/nnbd_migration/test/front_end/info_builder_test.dart
+++ b/pkg/nnbd_migration/test/front_end/info_builder_test.dart
@@ -834,6 +834,45 @@
kind: NullabilityFixKind.checkExpression);
}
+ void test_nullAwareAssignment_remove() async {
+ var unit = await buildInfoForSingleTestFile('''
+int f(int/*!*/ x, int y) => x ??= y;
+''', migratedContent: '''
+int f(int/*!*/ x, int y) => x ??= y;
+''', warnOnWeakCode: false, removeViaComments: false);
+ var codeToRemove = ' ??= y';
+ var removalOffset = unit.content.indexOf(codeToRemove);
+ var region =
+ unit.regions.where((region) => region.offset == removalOffset).single;
+ assertRegion(
+ region: region,
+ length: codeToRemove.length,
+ explanation:
+ 'Removed a null-aware assignment, because the target cannot be '
+ 'null',
+ kind: NullabilityFixKind.removeDeadCode,
+ edits: isEmpty);
+ }
+
+ void test_nullAwareAssignment_unnecessaryInStrongMode() async {
+ var unit = await buildInfoForSingleTestFile('''
+int f(int/*!*/ x, int y) => x ??= y;
+''', migratedContent: '''
+int f(int/*!*/ x, int y) => x ??= y;
+''', warnOnWeakCode: true);
+ var operator = '??=';
+ var operatorOffset = unit.content.indexOf(operator);
+ var region =
+ unit.regions.where((region) => region.offset == operatorOffset).single;
+ assertRegion(
+ region: region,
+ length: operator.length,
+ explanation:
+ 'Null-aware assignment will be unnecessary in strong checking mode',
+ kind: NullabilityFixKind.nullAwareAssignmentUnnecessaryInStrongMode,
+ edits: isEmpty);
+ }
+
void test_nullAwarenessUnnecessaryInStrongMode() async {
var unit = await buildInfoForSingleTestFile('''
int f(String s) => s?.length;