]>
Commit | Line | Data |
---|---|---|
17df50a5 | 1 | use clippy_utils::diagnostics::span_lint_and_then; |
17df50a5 XL |
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! { | |
94222f64 | 11 | /// ### What it does |
17df50a5 XL |
12 | /// Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using |
13 | /// a lazy and. | |
14 | /// | |
94222f64 | 15 | /// ### Why is this bad? |
17df50a5 XL |
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 | /// | |
94222f64 | 19 | /// ### Known problems |
17df50a5 XL |
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 | /// | |
94222f64 | 23 | /// ### Example |
17df50a5 XL |
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 | /// ``` | |
a2a8927a | 33 | #[clippy::version = "1.54.0"] |
17df50a5 XL |
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! { | |
a2a8927a | 44 | if !expr.span.from_expansion(); |
17df50a5 XL |
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 | ||
04454e1e | 56 | fn suggesstion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> { |
17df50a5 XL |
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| { | |
04454e1e | 78 | if let Some(sugg) = suggesstion_snippet(cx, expr) { |
17df50a5 XL |
79 | diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable); |
80 | } | |
81 | }, | |
82 | ); | |
83 | } | |
84 | } | |
85 | } |