]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/needless_bitwise_bool.rs
Merge commit '9e3cd88718cd1912a515d26dbd9c4019fd5a9577' into clippyup
[rust.git] / clippy_lints / src / needless_bitwise_bool.rs
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 }