Skip to content

Commit af6ed0c

Browse files
committed
Added bitwise_bool lint
1 parent 0d4e24e commit af6ed0c

File tree

6 files changed

+187
-0
lines changed

6 files changed

+187
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2305,6 +2305,7 @@ Released 2018-09-13
23052305
[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
23062306
[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
23072307
[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
2308+
[`bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#bitwise_bool
23082309
[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
23092310
[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
23102311
[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions

clippy_lints/src/bitwise_bool.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::source::snippet_opt;
3+
use if_chain::if_chain;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{BinOpKind, Expr, ExprKind};
6+
use rustc_lint::{LateContext, LateLintPass};
7+
use rustc_middle::ty;
8+
use rustc_session::{declare_lint_pass, declare_tool_lint};
9+
10+
declare_clippy_lint! {
11+
/// **What it does:**
12+
/// Checks for uses of bitwise and/or operators between booleans, eg. `x & y` or `x | y`.
13+
///
14+
/// **Why is this bad?**
15+
/// The bitwise operators do not support short-circuiting, so it may hinder code performance.
16+
/// Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold`
17+
///
18+
/// **Known problems:**
19+
/// Boolean operators short circuit. If the programmer wishes to explicitly avoid
20+
/// short circuiting, then the bitwise operators could be desired.
21+
///
22+
/// **Example:**
23+
///
24+
/// ```rust
25+
/// if x & y {} // where both x and y are booleans
26+
/// ```
27+
/// Use instead:
28+
/// ```rust
29+
/// if x && y {}
30+
/// ```
31+
pub BITWISE_BOOL,
32+
correctness,
33+
"Boolean expressions that use bitwise rather than logical operators"
34+
}
35+
36+
declare_lint_pass!(BitwiseBool => [BITWISE_BOOL]);
37+
38+
fn is_bitwise_comparison(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
39+
let ty = cx.typeck_results().expr_ty(expr);
40+
if let (&ExprKind::Binary(ref op, _, _), &ty::Bool) = (&expr.kind, &ty.kind()) {
41+
op.node == BinOpKind::BitAnd || op.node == BinOpKind::BitOr
42+
} else {
43+
false
44+
}
45+
}
46+
47+
fn suggession_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
48+
if_chain! {
49+
if let &ExprKind::Binary(ref op, left, right) = &expr.kind;
50+
if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span));
51+
then {
52+
let op_snippet = match op.node {
53+
BinOpKind::BitAnd => "&&",
54+
_ => "||",
55+
};
56+
Some(format!("{} {} {}", l_snippet, op_snippet, r_snippet))
57+
} else {
58+
None
59+
}
60+
}
61+
}
62+
63+
impl LateLintPass<'_> for BitwiseBool {
64+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
65+
if is_bitwise_comparison(cx, expr) {
66+
span_lint_and_then(
67+
cx,
68+
BITWISE_BOOL,
69+
expr.span,
70+
"use of bitwise operator instead of logical operator between booleans",
71+
|diag| {
72+
if let Some(sugg) = suggession_snippet(cx, expr) {
73+
diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable);
74+
}
75+
},
76+
);
77+
}
78+
}
79+
}

clippy_lints/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ mod atomic_ordering;
176176
mod attrs;
177177
mod await_holding_invalid;
178178
mod bit_mask;
179+
mod bitwise_bool;
179180
mod blacklisted_name;
180181
mod blocks_in_if_conditions;
181182
mod bool_assert_comparison;
@@ -553,6 +554,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
553554
bit_mask::BAD_BIT_MASK,
554555
bit_mask::INEFFECTIVE_BIT_MASK,
555556
bit_mask::VERBOSE_BIT_MASK,
557+
bitwise_bool::BITWISE_BOOL,
556558
blacklisted_name::BLACKLISTED_NAME,
557559
blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
558560
bool_assert_comparison::BOOL_ASSERT_COMPARISON,
@@ -1018,6 +1020,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10181020
let type_complexity_threshold = conf.type_complexity_threshold;
10191021
store.register_late_pass(move || box types::Types::new(vec_box_size_threshold, type_complexity_threshold));
10201022
store.register_late_pass(|| box booleans::NonminimalBool);
1023+
store.register_late_pass(|| box bitwise_bool::BitwiseBool);
10211024
store.register_late_pass(|| box eq_op::EqOp);
10221025
store.register_late_pass(|| box enum_clike::UnportableVariant);
10231026
store.register_late_pass(|| box float_literal::FloatLiteral);
@@ -1452,6 +1455,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
14521455
LintId::of(attrs::USELESS_ATTRIBUTE),
14531456
LintId::of(bit_mask::BAD_BIT_MASK),
14541457
LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
1458+
LintId::of(bitwise_bool::BITWISE_BOOL),
14551459
LintId::of(blacklisted_name::BLACKLISTED_NAME),
14561460
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
14571461
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
@@ -1950,6 +1954,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
19501954
LintId::of(attrs::USELESS_ATTRIBUTE),
19511955
LintId::of(bit_mask::BAD_BIT_MASK),
19521956
LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
1957+
LintId::of(bitwise_bool::BITWISE_BOOL),
19531958
LintId::of(booleans::LOGIC_BUG),
19541959
LintId::of(casts::CAST_REF_TO_MUT),
19551960
LintId::of(copies::IFS_SAME_COND),

tests/ui/bitwise_bool.fixed

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::bitwise_bool)]
4+
5+
fn returns_bool() -> bool {
6+
true
7+
}
8+
9+
fn main() {
10+
let x = true;
11+
let y = false;
12+
if x && y {
13+
println!("true")
14+
}
15+
if x || y {
16+
println!("true")
17+
}
18+
if !x || y {
19+
println!("true")
20+
}
21+
22+
if returns_bool() && x {
23+
println!("true")
24+
}
25+
if y && returns_bool() {
26+
println!("true")
27+
}
28+
if !returns_bool() && returns_bool() {
29+
println!("true")
30+
}
31+
}

tests/ui/bitwise_bool.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::bitwise_bool)]
4+
5+
fn returns_bool() -> bool {
6+
true
7+
}
8+
9+
fn main() {
10+
let x = true;
11+
let y = false;
12+
if x & y {
13+
println!("true")
14+
}
15+
if x | y {
16+
println!("true")
17+
}
18+
if !(x | y) {
19+
println!("true")
20+
}
21+
22+
if returns_bool() & x {
23+
println!("true")
24+
}
25+
if y & returns_bool() {
26+
println!("true")
27+
}
28+
if !returns_bool() & returns_bool() {
29+
println!("true")
30+
}
31+
}

tests/ui/bitwise_bool.stderr

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
error: use of bitwise operator instead of logical operator between booleans
2+
--> $DIR/bitwise_bool.rs:12:8
3+
|
4+
LL | if x & y {
5+
| ^^^^^ help: try: `x && y`
6+
|
7+
= note: `-D clippy::bitwise-bool` implied by `-D warnings`
8+
9+
error: use of bitwise operator instead of logical operator between booleans
10+
--> $DIR/bitwise_bool.rs:15:8
11+
|
12+
LL | if x | y {
13+
| ^^^^^ help: try: `x || y`
14+
15+
error: use of bitwise operator instead of logical operator between booleans
16+
--> $DIR/bitwise_bool.rs:18:9
17+
|
18+
LL | if !(x | y) {
19+
| ^^^^^^^ help: try: `x || y`
20+
21+
error: use of bitwise operator instead of logical operator between booleans
22+
--> $DIR/bitwise_bool.rs:22:8
23+
|
24+
LL | if returns_bool() & x {
25+
| ^^^^^^^^^^^^^^^^^^ help: try: `returns_bool() && x`
26+
27+
error: use of bitwise operator instead of logical operator between booleans
28+
--> $DIR/bitwise_bool.rs:25:8
29+
|
30+
LL | if y & returns_bool() {
31+
| ^^^^^^^^^^^^^^^^^^ help: try: `y && returns_bool()`
32+
33+
error: use of bitwise operator instead of logical operator between booleans
34+
--> $DIR/bitwise_bool.rs:28:8
35+
|
36+
LL | if !returns_bool() & returns_bool() {
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!returns_bool() && returns_bool()`
38+
39+
error: aborting due to 6 previous errors
40+

0 commit comments

Comments
 (0)