]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs
Auto merge of #81156 - DrMeepster:read_buf, r=joshtriplett
[rust.git] / src / tools / clippy / clippy_lints / src / needless_bitwise_bool.rs
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, where performance may be improved by using
13     /// a lazy and.
14     ///
15     /// ### Why is this bad?
16     /// The bitwise operators do not support short-circuiting, so it may hinder code performance.
17     /// Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold`
18     ///
19     /// ### Known problems
20     /// This lint evaluates only when the right side is determined to have no side effects. At this time, that
21     /// determination is quite conservative.
22     ///
23     /// ### Example
24     /// ```rust
25     /// let (x,y) = (true, false);
26     /// if x & !y {} // where both x and y are booleans
27     /// ```
28     /// Use instead:
29     /// ```rust
30     /// let (x,y) = (true, false);
31     /// if x && !y {}
32     /// ```
33     #[clippy::version = "1.54.0"]
34     pub NEEDLESS_BITWISE_BOOL,
35     pedantic,
36     "Boolean expressions that use bitwise rather than lazy operators"
37 }
38
39 declare_lint_pass!(NeedlessBitwiseBool => [NEEDLESS_BITWISE_BOOL]);
40
41 fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
42     let ty = cx.typeck_results().expr_ty(expr);
43     if_chain! {
44         if !expr.span.from_expansion();
45         if let (&ExprKind::Binary(ref op, _, right), &ty::Bool) = (&expr.kind, &ty.kind());
46         if op.node == BinOpKind::BitAnd || op.node == BinOpKind::BitOr;
47         if let ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..) = right.kind;
48         if !right.can_have_side_effects();
49         then {
50             return true;
51         }
52     }
53     false
54 }
55
56 fn suggession_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
57     if let ExprKind::Binary(ref op, left, right) = expr.kind {
58         if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) {
59             let op_snippet = match op.node {
60                 BinOpKind::BitAnd => "&&",
61                 _ => "||",
62             };
63             return Some(format!("{} {} {}", l_snippet, op_snippet, r_snippet));
64         }
65     }
66     None
67 }
68
69 impl LateLintPass<'_> for NeedlessBitwiseBool {
70     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
71         if is_bitwise_operation(cx, expr) {
72             span_lint_and_then(
73                 cx,
74                 NEEDLESS_BITWISE_BOOL,
75                 expr.span,
76                 "use of bitwise operator instead of lazy operator between booleans",
77                 |diag| {
78                     if let Some(sugg) = suggession_snippet(cx, expr) {
79                         diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable);
80                     }
81                 },
82             );
83         }
84     }
85 }