Skip to content

Commit efc09bd

Browse files
committed
Auto merge of #7133 - arya-k:master, r=llogiq
Add `needless_bitwise_bool` lint fixes #6827 fixes #1594 changelog: Add ``[`needless_bitwise_bool`]`` lint Creates a new `bitwise_bool` lint to convert `x & y` to `x && y` when both `x` and `y` are booleans. I also had to adjust thh `needless_bool` lint slightly, and fix a couple failing dogfood tests. I made it a correctness lint as per flip1995's comment [here](#3385 (comment)), from a previous WIP attempt at this lint.
2 parents acdf43f + 0dcde71 commit efc09bd

File tree

6 files changed

+181
-0
lines changed

6 files changed

+181
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2549,6 +2549,7 @@ Released 2018-09-13
25492549
[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
25502550
[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
25512551
[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
2552+
[`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool
25522553
[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
25532554
[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
25542555
[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference

clippy_lints/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ mod mut_reference;
290290
mod mutable_debug_assertion;
291291
mod mutex_atomic;
292292
mod needless_arbitrary_self_type;
293+
mod needless_bitwise_bool;
293294
mod needless_bool;
294295
mod needless_borrow;
295296
mod needless_borrowed_ref;
@@ -836,6 +837,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
836837
mutex_atomic::MUTEX_ATOMIC,
837838
mutex_atomic::MUTEX_INTEGER,
838839
needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
840+
needless_bitwise_bool::NEEDLESS_BITWISE_BOOL,
839841
needless_bool::BOOL_COMPARISON,
840842
needless_bool::NEEDLESS_BOOL,
841843
needless_borrow::NEEDLESS_BORROW,
@@ -1022,6 +1024,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10221024
let type_complexity_threshold = conf.type_complexity_threshold;
10231025
store.register_late_pass(move || box types::Types::new(vec_box_size_threshold, type_complexity_threshold));
10241026
store.register_late_pass(|| box booleans::NonminimalBool);
1027+
store.register_late_pass(|| box needless_bitwise_bool::NeedlessBitwiseBool);
10251028
store.register_late_pass(|| box eq_op::EqOp);
10261029
store.register_late_pass(|| box enum_clike::UnportableVariant);
10271030
store.register_late_pass(|| box float_literal::FloatLiteral);
@@ -1397,6 +1400,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
13971400
LintId::of(misc::USED_UNDERSCORE_BINDING),
13981401
LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
13991402
LintId::of(mut_mut::MUT_MUT),
1403+
LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
14001404
LintId::of(needless_continue::NEEDLESS_CONTINUE),
14011405
LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
14021406
LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::in_macro;
3+
use clippy_utils::source::snippet_opt;
4+
use if_chain::if_chain;
5+
use rustc_errors::Applicability;
6+
use rustc_hir::{BinOpKind, Expr, ExprKind};
7+
use rustc_lint::{LateContext, LateLintPass};
8+
use rustc_middle::ty;
9+
use rustc_session::{declare_lint_pass, declare_tool_lint};
10+
11+
declare_clippy_lint! {
12+
/// **What it does:**
13+
/// Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using
14+
/// a lazy and.
15+
///
16+
/// **Why is this bad?**
17+
/// The bitwise operators do not support short-circuiting, so it may hinder code performance.
18+
/// Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold`
19+
///
20+
/// **Known problems:**
21+
/// This lint evaluates only when the right side is determined to have no side effects. At this time, that
22+
/// determination is quite conservative.
23+
///
24+
/// **Example:**
25+
///
26+
/// ```rust
27+
/// let (x,y) = (true, false);
28+
/// if x & !y {} // where both x and y are booleans
29+
/// ```
30+
/// Use instead:
31+
/// ```rust
32+
/// let (x,y) = (true, false);
33+
/// if x && !y {}
34+
/// ```
35+
pub NEEDLESS_BITWISE_BOOL,
36+
pedantic,
37+
"Boolean expressions that use bitwise rather than lazy operators"
38+
}
39+
40+
declare_lint_pass!(NeedlessBitwiseBool => [NEEDLESS_BITWISE_BOOL]);
41+
42+
fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
43+
let ty = cx.typeck_results().expr_ty(expr);
44+
if_chain! {
45+
if !in_macro(expr.span);
46+
if let (&ExprKind::Binary(ref op, _, right), &ty::Bool) = (&expr.kind, &ty.kind());
47+
if op.node == BinOpKind::BitAnd || op.node == BinOpKind::BitOr;
48+
if let ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..) = right.kind;
49+
if !right.can_have_side_effects();
50+
then {
51+
return true;
52+
}
53+
}
54+
false
55+
}
56+
57+
fn suggession_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
58+
if let ExprKind::Binary(ref op, left, right) = expr.kind {
59+
if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) {
60+
let op_snippet = match op.node {
61+
BinOpKind::BitAnd => "&&",
62+
_ => "||",
63+
};
64+
return Some(format!("{} {} {}", l_snippet, op_snippet, r_snippet));
65+
}
66+
}
67+
None
68+
}
69+
70+
impl LateLintPass<'_> for NeedlessBitwiseBool {
71+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
72+
if is_bitwise_operation(cx, expr) {
73+
span_lint_and_then(
74+
cx,
75+
NEEDLESS_BITWISE_BOOL,
76+
expr.span,
77+
"use of bitwise operator instead of lazy operator between booleans",
78+
|diag| {
79+
if let Some(sugg) = suggession_snippet(cx, expr) {
80+
diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable);
81+
}
82+
},
83+
);
84+
}
85+
}
86+
}

tests/ui/needless_bitwise_bool.fixed

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::needless_bitwise_bool)]
4+
5+
fn returns_bool() -> bool {
6+
true
7+
}
8+
9+
const fn const_returns_bool() -> bool {
10+
false
11+
}
12+
13+
fn main() {
14+
let (x, y) = (false, true);
15+
if x & y {
16+
println!("true")
17+
}
18+
if returns_bool() & x {
19+
println!("true")
20+
}
21+
if !returns_bool() & returns_bool() {
22+
println!("true")
23+
}
24+
if y && !x {
25+
println!("true")
26+
}
27+
28+
// BELOW: lints we hope to catch as `Expr::can_have_side_effects` improves.
29+
if y & !const_returns_bool() {
30+
println!("true") // This is a const function, in an UnOp
31+
}
32+
33+
if y & "abcD".is_empty() {
34+
println!("true") // This is a const method call
35+
}
36+
37+
if y & (0 < 1) {
38+
println!("true") // This is a BinOp with no side effects
39+
}
40+
}

tests/ui/needless_bitwise_bool.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::needless_bitwise_bool)]
4+
5+
fn returns_bool() -> bool {
6+
true
7+
}
8+
9+
const fn const_returns_bool() -> bool {
10+
false
11+
}
12+
13+
fn main() {
14+
let (x, y) = (false, true);
15+
if x & y {
16+
println!("true")
17+
}
18+
if returns_bool() & x {
19+
println!("true")
20+
}
21+
if !returns_bool() & returns_bool() {
22+
println!("true")
23+
}
24+
if y & !x {
25+
println!("true")
26+
}
27+
28+
// BELOW: lints we hope to catch as `Expr::can_have_side_effects` improves.
29+
if y & !const_returns_bool() {
30+
println!("true") // This is a const function, in an UnOp
31+
}
32+
33+
if y & "abcD".is_empty() {
34+
println!("true") // This is a const method call
35+
}
36+
37+
if y & (0 < 1) {
38+
println!("true") // This is a BinOp with no side effects
39+
}
40+
}

tests/ui/needless_bitwise_bool.stderr

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: use of bitwise operator instead of lazy operator between booleans
2+
--> $DIR/needless_bitwise_bool.rs:24:8
3+
|
4+
LL | if y & !x {
5+
| ^^^^^^ help: try: `y && !x`
6+
|
7+
= note: `-D clippy::needless-bitwise-bool` implied by `-D warnings`
8+
9+
error: aborting due to previous error
10+

0 commit comments

Comments
 (0)