Skip to content

Commit ca2a9fe

Browse files
authored
Lint about comparing booleans to boolean literals (#3973)
* Lint about comparing booleans to boolean literals * fixup! Lint about comparing booleans to boolean literals * Add test that the lint works for any expression, not just variables
1 parent 4c2af04 commit ca2a9fe

File tree

4 files changed

+119
-0
lines changed

4 files changed

+119
-0
lines changed

example/all.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ linter:
9797
- no_duplicate_case_values
9898
- no_leading_underscores_for_library_prefixes
9999
- no_leading_underscores_for_local_identifiers
100+
- no_literal_bool_comparisons
100101
- no_logic_in_create_state
101102
- no_runtimeType_toString
102103
- non_constant_identifier_names

lib/src/rules.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ import 'rules/no_default_cases.dart';
104104
import 'rules/no_duplicate_case_values.dart';
105105
import 'rules/no_leading_underscores_for_library_prefixes.dart';
106106
import 'rules/no_leading_underscores_for_local_identifiers.dart';
107+
import 'rules/no_literal_bool_comparisons.dart';
107108
import 'rules/no_logic_in_create_state.dart';
108109
import 'rules/no_runtimeType_toString.dart';
109110
import 'rules/non_constant_identifier_names.dart';
@@ -331,6 +332,7 @@ void registerLintRules({bool inTestMode = false}) {
331332
..register(NoAdjacentStringsInList())
332333
..register(NoDefaultCases())
333334
..register(NoDuplicateCaseValues())
335+
..register(NoLiteralBoolComparisons())
334336
..register(NonConstantIdentifierNames())
335337
..register(NoLeadingUnderscoresForLibraryPrefixes())
336338
..register(NoLeadingUnderscoresForLocalIdentifiers())
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/dart/analysis/features.dart';
6+
import 'package:analyzer/dart/ast/ast.dart';
7+
import 'package:analyzer/dart/ast/token.dart';
8+
import 'package:analyzer/dart/ast/visitor.dart';
9+
import 'package:analyzer/dart/element/type.dart';
10+
11+
import '../analyzer.dart';
12+
13+
const _desc = r"Don't compare booleans to boolean literals.";
14+
15+
const _details = r'''
16+
From [Effective Dart](https://dart.dev/guides/language/effective-dart/usage#dont-use-true-or-false-in-equality-operations):
17+
18+
**DON'T** use `true` or `false` in equality operations.
19+
20+
This lint applies only if the expression is of a non-nullable `bool` type.
21+
22+
**BAD:**
23+
```dart
24+
if (someBool == true) {
25+
}
26+
while (someBool == false) {
27+
}
28+
```
29+
30+
**GOOD:**
31+
```dart
32+
if (someBool) {
33+
}
34+
while (!someBool) {
35+
}
36+
```
37+
''';
38+
39+
class NoLiteralBoolComparisons extends LintRule {
40+
NoLiteralBoolComparisons()
41+
: super(
42+
name: 'no_literal_bool_comparisons',
43+
description: _desc,
44+
details: _details,
45+
group: Group.style,
46+
);
47+
48+
@override
49+
void registerNodeProcessors(
50+
NodeLintRegistry registry, LinterContext context) {
51+
if (!context.isEnabled(Feature.non_nullable)) return;
52+
53+
var visitor = _Visitor(this, context);
54+
registry.addBinaryExpression(this, visitor);
55+
}
56+
}
57+
58+
class _Visitor extends SimpleAstVisitor<void> {
59+
final LintRule rule;
60+
final LinterContext context;
61+
62+
_Visitor(this.rule, this.context);
63+
64+
@override
65+
void visitBinaryExpression(BinaryExpression node) {
66+
if (node.operator.type == TokenType.EQ_EQ ||
67+
node.operator.type == TokenType.BANG_EQ) {
68+
var left = node.leftOperand;
69+
var right = node.rightOperand;
70+
if (right is BooleanLiteral && isBool(left.staticType)) {
71+
rule.reportLint(right);
72+
} else if (left is BooleanLiteral && isBool(right.staticType)) {
73+
rule.reportLint(left);
74+
}
75+
}
76+
}
77+
78+
bool isBool(DartType? type) =>
79+
type != null &&
80+
type.isDartCoreBool &&
81+
context.typeSystem.isNonNullable(type);
82+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
class TestClass {
2+
bool x = false;
3+
}
4+
5+
void foo() {
6+
var x = false;
7+
if (x == true) // LINT
8+
{
9+
print('oh');
10+
}
11+
var f = true;
12+
while (true == f) // LINT
13+
{
14+
print('oh');
15+
f = false;
16+
}
17+
18+
var c = TestClass();
19+
if (c.x == true) // LINT
20+
{
21+
print('oh');
22+
}
23+
24+
print((x && f) != true); // LINT
25+
print(x && (f != true)); // LINT
26+
}
27+
28+
void bar(bool x, bool? y) {
29+
print(x == true); // LINT
30+
print(x != true); // LINT
31+
print(y == true); // OK
32+
33+
if (x && true) print('oh'); // OK
34+
}

0 commit comments

Comments
 (0)