-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
179 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
use clippy_utils::diagnostics::span_lint_and_then; | ||
use clippy_utils::in_macro; | ||
use clippy_utils::source::snippet_opt; | ||
use if_chain::if_chain; | ||
use rustc_errors::Applicability; | ||
use rustc_hir::{BinOpKind, Expr, ExprKind}; | ||
use rustc_lint::{LateContext, LateLintPass}; | ||
use rustc_middle::ty; | ||
use rustc_session::{declare_lint_pass, declare_tool_lint}; | ||
|
||
declare_clippy_lint! { | ||
/// **What it does:** | ||
/// Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using | ||
/// a lazy and. | ||
/// | ||
/// **Why is this bad?** | ||
/// The bitwise operators do not support short-circuiting, so it may hinder code performance. | ||
/// Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold` | ||
/// | ||
/// **Known problems:** | ||
/// This lint evaluates only when the right side is determined to have no side effects. At this time, that | ||
/// determination is quite conservative. | ||
/// | ||
/// **Example:** | ||
/// | ||
/// ```rust | ||
/// if x & !y {} // where both x and y are booleans | ||
/// ``` | ||
/// Use instead: | ||
/// ```rust | ||
/// if x && !y {} | ||
/// ``` | ||
pub NEEDLESS_BITWISE_BOOL, | ||
pedantic, | ||
"Boolean expressions that use bitwise rather than lazy operators" | ||
} | ||
|
||
declare_lint_pass!(NeedlessBitwiseBool => [NEEDLESS_BITWISE_BOOL]); | ||
|
||
fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | ||
let ty = cx.typeck_results().expr_ty(expr); | ||
if_chain! { | ||
if !in_macro(expr.span); | ||
if let (&ExprKind::Binary(ref op, _, right), &ty::Bool) = (&expr.kind, &ty.kind()); | ||
if op.node == BinOpKind::BitAnd || op.node == BinOpKind::BitOr; | ||
if let ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..) = right.kind; | ||
if !right.can_have_side_effects(); | ||
then { | ||
return true; | ||
} | ||
} | ||
false | ||
} | ||
|
||
fn suggession_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> { | ||
if let ExprKind::Binary(ref op, left, right) = expr.kind { | ||
if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) { | ||
let op_snippet = match op.node { | ||
BinOpKind::BitAnd => "&&", | ||
_ => "||", | ||
}; | ||
return Some(format!("{} {} {}", l_snippet, op_snippet, r_snippet)); | ||
} | ||
} | ||
None | ||
} | ||
|
||
impl LateLintPass<'_> for NeedlessBitwiseBool { | ||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { | ||
if is_bitwise_operation(cx, expr) { | ||
span_lint_and_then( | ||
cx, | ||
NEEDLESS_BITWISE_BOOL, | ||
expr.span, | ||
"use of bitwise operator instead of logical operator between booleans", | ||
|diag| { | ||
if let Some(sugg) = suggession_snippet(cx, expr) { | ||
diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable); | ||
} | ||
}, | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// run-rustfix | ||
|
||
#![warn(clippy::needless_bitwise_bool)] | ||
|
||
fn returns_bool() -> bool { | ||
true | ||
} | ||
|
||
const fn const_returns_bool() -> bool { | ||
false | ||
} | ||
|
||
fn main() { | ||
let (x, y) = (false, true); | ||
if x & y { | ||
println!("true") | ||
} | ||
if returns_bool() & x { | ||
println!("true") | ||
} | ||
if !returns_bool() & returns_bool() { | ||
println!("true") | ||
} | ||
if y && !x { | ||
println!("true") | ||
} | ||
|
||
// BELOW: lints we hope to catch as `Expr::can_have_side_effects` improves. | ||
if y & !const_returns_bool() { | ||
println!("true") // This is a const function, in an UnOp | ||
} | ||
|
||
if y & "abcD".is_empty() { | ||
println!("true") // This is a const method call | ||
} | ||
|
||
if y & (0 < 1) { | ||
println!("true") // This is a BinOp with no side effects | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// run-rustfix | ||
|
||
#![warn(clippy::needless_bitwise_bool)] | ||
|
||
fn returns_bool() -> bool { | ||
true | ||
} | ||
|
||
const fn const_returns_bool() -> bool { | ||
false | ||
} | ||
|
||
fn main() { | ||
let (x, y) = (false, true); | ||
if x & y { | ||
println!("true") | ||
} | ||
if returns_bool() & x { | ||
println!("true") | ||
} | ||
if !returns_bool() & returns_bool() { | ||
println!("true") | ||
} | ||
if y & !x { | ||
println!("true") | ||
} | ||
|
||
// BELOW: lints we hope to catch as `Expr::can_have_side_effects` improves. | ||
if y & !const_returns_bool() { | ||
println!("true") // This is a const function, in an UnOp | ||
} | ||
|
||
if y & "abcD".is_empty() { | ||
println!("true") // This is a const method call | ||
} | ||
|
||
if y & (0 < 1) { | ||
println!("true") // This is a BinOp with no side effects | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
error: use of bitwise operator instead of logical operator between booleans | ||
--> $DIR/needless_bitwise_bool.rs:24:8 | ||
| | ||
LL | if y & !x { | ||
| ^^^^^^ help: try: `y && !x` | ||
| | ||
= note: `-D clippy::needless-bitwise-bool` implied by `-D warnings` | ||
|
||
error: aborting due to previous error | ||
|