+ convert_to_boolean_expression correction
Fixes: https://github.com/dart-lang/sdk/issues/52304
Change-Id: Ic080188be5abc70fe1f0da0a7117f31d64a16aa2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/302121
Commit-Queue: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_boolean_expression.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_boolean_expression.dart
new file mode 100644
index 0000000..5e736d7
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_boolean_expression.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2023, 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 ConvertToBooleanExpression extends CorrectionProducer {
+ @override
+ bool get canBeAppliedInBulk => true;
+ @override
+ bool get canBeAppliedToFile => true;
+
+ @override
+ FixKind get fixKind => DartFixKind.CONVERT_TO_BOOL_EXPRESSION;
+
+ @override
+ FixKind get multiFixKind => DartFixKind.CONVERT_TO_BOOL_EXPRESSION_MULTI;
+
+ @override
+ Future<void> compute(ChangeBuilder builder) async {
+ AstNode? node = this.node;
+ if (node is BooleanLiteral) node = node.parent;
+ if (node is! BinaryExpression) return;
+
+ var rightOperand = node.rightOperand;
+ var leftOperand = node.leftOperand;
+
+ Expression expression;
+ BooleanLiteral literal;
+
+ var deleteRange = range.endEnd(leftOperand, rightOperand);
+
+ if (rightOperand is BooleanLiteral) {
+ literal = rightOperand;
+ expression = node.leftOperand;
+ } else if (leftOperand is BooleanLiteral) {
+ literal = leftOperand;
+ expression = node.rightOperand;
+ deleteRange = range.startStart(leftOperand, rightOperand);
+ } else {
+ return;
+ }
+
+ var negated = !isPositiveCase(node, literal);
+ await builder.addDartFileEdit(file, (builder) {
+ if (negated) {
+ builder.addSimpleInsertion(expression.offset, '!');
+ }
+ builder.addDeletion(deleteRange);
+ });
+ }
+
+ static bool isPositiveCase(
+ BinaryExpression expression, BooleanLiteral literal) {
+ if (expression.operator.lexeme == '==') return literal.value;
+ return !literal.value;
+ }
+}
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 b99b44f..e50cbef 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
@@ -2140,8 +2140,7 @@
LintCode.no_leading_underscores_for_local_identifiers:
status: hasFix
LintCode.no_literal_bool_comparisons:
- status: needsFix
- notes: The fix is to remove `== true` etc.
+ status: hasFix
LintCode.no_logic_in_create_state:
status: noFix
LintCode.no_runtimeType_toString:
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index 3005539..fa6e57d 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -418,6 +418,16 @@
DartFixKindPriority.IN_FILE,
'Convert the quotes and remove escapes everywhere in file',
);
+ static const CONVERT_TO_BOOL_EXPRESSION = FixKind(
+ 'dart.fix.convert.toBoolExpression',
+ DartFixKindPriority.DEFAULT,
+ 'Convert to boolean expression',
+ );
+ static const CONVERT_TO_BOOL_EXPRESSION_MULTI = FixKind(
+ 'dart.fix.convert.toBoolExpression.multi',
+ DartFixKindPriority.DEFAULT,
+ 'Convert to boolean expressions everywhere in file',
+ );
static const CONVERT_TO_CASCADE = FixKind(
'dart.fix.convert.toCascade',
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 dce3afc..d431b34 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -55,6 +55,7 @@
import 'package:analysis_server/src/services/correction/dart/convert_into_is_not.dart';
import 'package:analysis_server/src/services/correction/dart/convert_map_from_iterable_to_for_literal.dart';
import 'package:analysis_server/src/services/correction/dart/convert_quotes.dart';
+import 'package:analysis_server/src/services/correction/dart/convert_to_boolean_expression.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_cascade.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_contains.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_expression_function_body.dart';
@@ -539,6 +540,9 @@
LintNames.no_leading_underscores_for_library_prefixes: [
RemoveLeadingUnderscore.new,
],
+ LintNames.no_literal_bool_comparisons: [
+ ConvertToBooleanExpression.new,
+ ],
LintNames.no_leading_underscores_for_local_identifiers: [
RemoveLeadingUnderscore.new,
],
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 c16ebb7..b04a5d9 100644
--- a/pkg/analysis_server/lib/src/services/linter/lint_names.dart
+++ b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
@@ -76,6 +76,8 @@
static const String leading_newlines_in_multiline_strings =
'leading_newlines_in_multiline_strings';
static const String library_annotations = 'library_annotations';
+ static const String no_literal_bool_comparisons =
+ 'no_literal_bool_comparisons';
static const String no_duplicate_case_values = 'no_duplicate_case_values';
static const String no_leading_underscores_for_library_prefixes =
'no_leading_underscores_for_library_prefixes';
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_boolean_expression_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_boolean_expression_test.dart
new file mode 100644
index 0000000..dbfdf8b
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_boolean_expression_test.dart
@@ -0,0 +1,149 @@
+// Copyright (c) 2023, 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(ConvertToBoolExpressionBulkTest);
+ defineReflectiveTests(ConvertToBoolExpressionTest);
+ });
+}
+
+@reflectiveTest
+class ConvertToBoolExpressionBulkTest extends BulkFixProcessorTest {
+ @override
+ String get lintCode => LintNames.no_literal_bool_comparisons;
+
+ Future<void> test_singleFile() async {
+ await resolveTestCode('''
+void f(bool value) {
+ if (value != false || value == false) print(value);
+}
+''');
+ await assertHasFix('''
+void f(bool value) {
+ if (value || !value) print(value);
+}
+''');
+ }
+}
+
+@reflectiveTest
+class ConvertToBoolExpressionTest extends FixProcessorLintTest {
+ @override
+ FixKind get kind => DartFixKind.CONVERT_TO_BOOL_EXPRESSION;
+
+ @override
+ String get lintCode => LintNames.no_literal_bool_comparisons;
+
+ Future<void> test_ifFalse() async {
+ await resolveTestCode(r'''
+void f(bool value) {
+ if (value == false) print(value);
+}
+''');
+ await assertHasFix(r'''
+void f(bool value) {
+ if (!value) print(value);
+}
+''');
+ }
+
+ Future<void> test_ifFalse_reversed() async {
+ await resolveTestCode(r'''
+void f(bool value) {
+ if (false == value) print(value);
+}
+''');
+ await assertHasFix(r'''
+void f(bool value) {
+ if (!value) print(value);
+}
+''');
+ }
+
+ Future<void> test_ifNotFalse() async {
+ await resolveTestCode(r'''
+void f(bool value) {
+ if (value != false) print(value);
+}
+''');
+ await assertHasFix(r'''
+void f(bool value) {
+ if (value) print(value);
+}
+''');
+ }
+
+ Future<void> test_ifNotFalse_reversed() async {
+ await resolveTestCode(r'''
+void f(bool value) {
+ if (false != value) print(value);
+}
+''');
+ await assertHasFix(r'''
+void f(bool value) {
+ if (value) print(value);
+}
+''');
+ }
+
+ Future<void> test_ifNotTrue() async {
+ await resolveTestCode(r'''
+void f(bool value) {
+ if (value != true) print(value);
+}
+''');
+ await assertHasFix(r'''
+void f(bool value) {
+ if (!value) print(value);
+}
+''');
+ }
+
+ Future<void> test_ifNotTrue_reversed() async {
+ await resolveTestCode(r'''
+void f(bool value) {
+ if (true != value) print(value);
+}
+''');
+ await assertHasFix(r'''
+void f(bool value) {
+ if (!value) print(value);
+}
+''');
+ }
+
+ Future<void> test_ifTrue() async {
+ await resolveTestCode(r'''
+void f(bool value) {
+ if (value == true) print(value);
+}
+''');
+ await assertHasFix(r'''
+void f(bool value) {
+ if (value) print(value);
+}
+''');
+ }
+
+ Future<void> test_ifTrue_reversed() async {
+ await resolveTestCode(r'''
+void f(bool value) {
+ if (true == value) print(value);
+}
+''');
+ await assertHasFix(r'''
+void f(bool value) {
+ if (value) print(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 d212aca..8ae29ae 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
@@ -64,6 +64,8 @@
import 'convert_into_expression_body_test.dart' as convert_into_expression_body;
import 'convert_into_is_not_test.dart' as convert_into_is_not;
import 'convert_quotes_test.dart' as convert_quotes;
+import 'convert_to_boolean_expression_test.dart'
+ as convert_to_boolean_expression;
import 'convert_to_cascade_test.dart' as convert_to_cascade;
import 'convert_to_contains_test.dart' as convert_to_contains;
import 'convert_to_double_quoted_string_test.dart'
@@ -322,6 +324,7 @@
convert_into_expression_body.main();
convert_into_is_not.main();
convert_quotes.main();
+ convert_to_boolean_expression.main();
convert_to_cascade.main();
convert_to_contains.main();
convert_to_double_quoted_string.main();