Skip to content
This repository was archived by the owner on Nov 20, 2024. It is now read-only.

Lint about comparing booleans to boolean literals #3973

Merged
merged 3 commits into from
Apr 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions example/all.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ linter:
- no_duplicate_case_values
- no_leading_underscores_for_library_prefixes
- no_leading_underscores_for_local_identifiers
- no_literal_bool_comparisons
- no_logic_in_create_state
- no_runtimeType_toString
- non_constant_identifier_names
Expand Down
2 changes: 2 additions & 0 deletions lib/src/rules.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ import 'rules/no_default_cases.dart';
import 'rules/no_duplicate_case_values.dart';
import 'rules/no_leading_underscores_for_library_prefixes.dart';
import 'rules/no_leading_underscores_for_local_identifiers.dart';
import 'rules/no_literal_bool_comparisons.dart';
import 'rules/no_logic_in_create_state.dart';
import 'rules/no_runtimeType_toString.dart';
import 'rules/non_constant_identifier_names.dart';
Expand Down Expand Up @@ -321,6 +322,7 @@ void registerLintRules({bool inTestMode = false}) {
..register(NoAdjacentStringsInList())
..register(NoDefaultCases())
..register(NoDuplicateCaseValues())
..register(NoLiteralBoolComparisons())
..register(NonConstantIdentifierNames())
..register(NoLeadingUnderscoresForLibraryPrefixes())
..register(NoLeadingUnderscoresForLocalIdentifiers())
Expand Down
82 changes: 82 additions & 0 deletions lib/src/rules/no_literal_bool_comparisons.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// 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:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/type.dart';

import '../analyzer.dart';

const _desc = r"Don't compare booleans to boolean literals.";

const _details = r'''
From [Effective Dart](https://dart.dev/guides/language/effective-dart/usage#dont-use-true-or-false-in-equality-operations):
**DON'T** use `true` or `false` in equality operations.
This lint applies only if the expression is of a non-nullable `bool` type.
**BAD:**
```dart
if (someBool == true) {
}
while (someBool == false) {
}
```
**GOOD:**
```dart
if (someBool) {
}
while (!someBool) {
}
```
''';

class NoLiteralBoolComparisons extends LintRule {
NoLiteralBoolComparisons()
: super(
name: 'no_literal_bool_comparisons',
description: _desc,
details: _details,
group: Group.style,
);

@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
if (!context.isEnabled(Feature.non_nullable)) return;

var visitor = _Visitor(this, context);
registry.addBinaryExpression(this, visitor);
}
}

class _Visitor extends SimpleAstVisitor<void> {
final LintRule rule;
final LinterContext context;

_Visitor(this.rule, this.context);

@override
void visitBinaryExpression(BinaryExpression node) {
if (node.operator.type == TokenType.EQ_EQ ||
node.operator.type == TokenType.BANG_EQ) {
var left = node.leftOperand;
var right = node.rightOperand;
if (right is BooleanLiteral && isBool(left.staticType)) {
rule.reportLint(right);
} else if (left is BooleanLiteral && isBool(right.staticType)) {
rule.reportLint(left);
}
}
}

bool isBool(DartType? type) =>
type != null &&
type.isDartCoreBool &&
context.typeSystem.isNonNullable(type);
}
34 changes: 34 additions & 0 deletions test_data/rules/no_literal_bool_comparisons.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class TestClass {
bool x = false;
}

void foo() {
var x = false;
if (x == true) // LINT
{
print('oh');
}
var f = true;
while (true == f) // LINT
{
print('oh');
f = false;
}

var c = TestClass();
if (c.x == true) // LINT
{
print('oh');
}

print((x && f) != true); // LINT
print(x && (f != true)); // LINT
}

void bar(bool x, bool? y) {
print(x == true); // LINT
print(x != true); // LINT
print(y == true); // OK

if (x && true) print('oh'); // OK
}