]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/bit_mask.rs
Add license header to Rust files
[rust.git] / clippy_lints / src / bit_mask.rs
1 // Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
3 //
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
9
10
11 use crate::rustc::hir::*;
12 use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
13 use crate::rustc::{declare_tool_lint, lint_array};
14 use if_chain::if_chain;
15 use crate::syntax::ast::LitKind;
16 use crate::syntax::source_map::Span;
17 use crate::utils::{span_lint, span_lint_and_then};
18 use crate::utils::sugg::Sugg;
19 use crate::consts::{constant, Constant};
20 use crate::rustc_errors::Applicability;
21
22 /// **What it does:** Checks for incompatible bit masks in comparisons.
23 ///
24 /// The formula for detecting if an expression of the type `_ <bit_op> m
25 /// <cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of
26 /// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
27 /// table:
28 ///
29 /// |Comparison  |Bit Op|Example     |is always|Formula               |
30 /// |------------|------|------------|---------|----------------------|
31 /// |`==` or `!=`| `&`  |`x & 2 == 3`|`false`  |`c & m != c`          |
32 /// |`<`  or `>=`| `&`  |`x & 2 < 3` |`true`   |`m < c`               |
33 /// |`>`  or `<=`| `&`  |`x & 1 > 1` |`false`  |`m <= c`              |
34 /// |`==` or `!=`| `|`  |`x | 1 == 0`|`false`  |`c | m != c`          |
35 /// |`<`  or `>=`| `|`  |`x | 1 < 1` |`false`  |`m >= c`              |
36 /// |`<=` or `>` | `|`  |`x | 1 > 0` |`true`   |`m > c`               |
37 ///
38 /// **Why is this bad?** If the bits that the comparison cares about are always
39 /// set to zero or one by the bit mask, the comparison is constant `true` or
40 /// `false` (depending on mask, compared value, and operators).
41 ///
42 /// So the code is actively misleading, and the only reason someone would write
43 /// this intentionally is to win an underhanded Rust contest or create a
44 /// test-case for this lint.
45 ///
46 /// **Known problems:** None.
47 ///
48 /// **Example:**
49 /// ```rust
50 /// if (x & 1 == 2) { … }
51 /// ```
52 declare_clippy_lint! {
53     pub BAD_BIT_MASK,
54     correctness,
55     "expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
56 }
57
58 /// **What it does:** Checks for bit masks in comparisons which can be removed
59 /// without changing the outcome. The basic structure can be seen in the
60 /// following table:
61 ///
62 /// |Comparison| Bit Op  |Example    |equals |
63 /// |----------|---------|-----------|-------|
64 /// |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|
65 /// |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|
66 ///
67 /// **Why is this bad?** Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
68 /// but still a bit misleading, because the bit mask is ineffective.
69 ///
70 /// **Known problems:** False negatives: This lint will only match instances
71 /// where we have figured out the math (which is for a power-of-two compared
72 /// value). This means things like `x | 1 >= 7` (which would be better written
73 /// as `x >= 6`) will not be reported (but bit masks like this are fairly
74 /// uncommon).
75 ///
76 /// **Example:**
77 /// ```rust
78 /// if (x | 1 > 3) { … }
79 /// ```
80 declare_clippy_lint! {
81     pub INEFFECTIVE_BIT_MASK,
82     correctness,
83     "expressions where a bit mask will be rendered useless by a comparison, e.g. `(x | 1) > 2`"
84 }
85
86 /// **What it does:** Checks for bit masks that can be replaced by a call
87 /// to `trailing_zeros`
88 ///
89 /// **Why is this bad?** `x.trailing_zeros() > 4` is much clearer than `x & 15
90 /// == 0`
91 ///
92 /// **Known problems:** llvm generates better code for `x & 15 == 0` on x86
93 ///
94 /// **Example:**
95 /// ```rust
96 /// x & 0x1111 == 0
97 /// ```
98 declare_clippy_lint! {
99     pub VERBOSE_BIT_MASK,
100     style,
101     "expressions where a bit mask is less readable than the corresponding method call"
102 }
103
104 #[derive(Copy, Clone)]
105 pub struct BitMask {
106     verbose_bit_mask_threshold: u64,
107 }
108
109 impl BitMask {
110     pub fn new(verbose_bit_mask_threshold: u64) -> Self {
111         Self {
112             verbose_bit_mask_threshold,
113         }
114     }
115 }
116
117 impl LintPass for BitMask {
118     fn get_lints(&self) -> LintArray {
119         lint_array!(BAD_BIT_MASK, INEFFECTIVE_BIT_MASK, VERBOSE_BIT_MASK)
120     }
121 }
122
123 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BitMask {
124     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
125         if let ExprKind::Binary(ref cmp, ref left, ref right) = e.node {
126             if cmp.node.is_comparison() {
127                 if let Some(cmp_opt) = fetch_int_literal(cx, right) {
128                     check_compare(cx, left, cmp.node, cmp_opt, e.span)
129                 } else if let Some(cmp_val) = fetch_int_literal(cx, left) {
130                     check_compare(cx, right, invert_cmp(cmp.node), cmp_val, e.span)
131                 }
132             }
133         }
134         if_chain! {
135             if let ExprKind::Binary(ref op, ref left, ref right) = e.node;
136             if BinOpKind::Eq == op.node;
137             if let ExprKind::Binary(ref op1, ref left1, ref right1) = left.node;
138             if BinOpKind::BitAnd == op1.node;
139             if let ExprKind::Lit(ref lit) = right1.node;
140             if let LitKind::Int(n, _) = lit.node;
141             if let ExprKind::Lit(ref lit1) = right.node;
142             if let LitKind::Int(0, _) = lit1.node;
143             if n.leading_zeros() == n.count_zeros();
144             if n > u128::from(self.verbose_bit_mask_threshold);
145             then {
146                 span_lint_and_then(cx,
147                                    VERBOSE_BIT_MASK,
148                                    e.span,
149                                    "bit mask could be simplified with a call to `trailing_zeros`",
150                                    |db| {
151                     let sugg = Sugg::hir(cx, left1, "...").maybe_par();
152                     db.span_suggestion_with_applicability(
153                         e.span,
154                         "try",
155                         format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()),
156                         Applicability::MaybeIncorrect,
157                     );
158                 });
159             }
160         }
161     }
162 }
163
164 fn invert_cmp(cmp: BinOpKind) -> BinOpKind {
165     match cmp {
166         BinOpKind::Eq => BinOpKind::Eq,
167         BinOpKind::Ne => BinOpKind::Ne,
168         BinOpKind::Lt => BinOpKind::Gt,
169         BinOpKind::Gt => BinOpKind::Lt,
170         BinOpKind::Le => BinOpKind::Ge,
171         BinOpKind::Ge => BinOpKind::Le,
172         _ => BinOpKind::Or, // Dummy
173     }
174 }
175
176
177 fn check_compare(cx: &LateContext<'_, '_>, bit_op: &Expr, cmp_op: BinOpKind, cmp_value: u128, span: Span) {
178     if let ExprKind::Binary(ref op, ref left, ref right) = bit_op.node {
179         if op.node != BinOpKind::BitAnd && op.node != BinOpKind::BitOr {
180             return;
181         }
182         fetch_int_literal(cx, right)
183             .or_else(|| fetch_int_literal(cx, left))
184             .map_or((), |mask| check_bit_mask(cx, op.node, cmp_op, mask, cmp_value, span))
185     }
186 }
187
188 fn check_bit_mask(cx: &LateContext<'_, '_>, bit_op: BinOpKind, cmp_op: BinOpKind, mask_value: u128, cmp_value: u128, span: Span) {
189     match cmp_op {
190         BinOpKind::Eq | BinOpKind::Ne => match bit_op {
191             BinOpKind::BitAnd => if mask_value & cmp_value != cmp_value {
192                 if cmp_value != 0 {
193                     span_lint(
194                         cx,
195                         BAD_BIT_MASK,
196                         span,
197                         &format!(
198                             "incompatible bit mask: `_ & {}` can never be equal to `{}`",
199                             mask_value,
200                             cmp_value
201                         ),
202                     );
203                 }
204             } else if mask_value == 0 {
205                 span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
206             },
207             BinOpKind::BitOr => if mask_value | cmp_value != cmp_value {
208                 span_lint(
209                     cx,
210                     BAD_BIT_MASK,
211                     span,
212                     &format!(
213                         "incompatible bit mask: `_ | {}` can never be equal to `{}`",
214                         mask_value,
215                         cmp_value
216                     ),
217                 );
218             },
219             _ => (),
220         },
221         BinOpKind::Lt | BinOpKind::Ge => match bit_op {
222             BinOpKind::BitAnd => if mask_value < cmp_value {
223                 span_lint(
224                     cx,
225                     BAD_BIT_MASK,
226                     span,
227                     &format!(
228                         "incompatible bit mask: `_ & {}` will always be lower than `{}`",
229                         mask_value,
230                         cmp_value
231                     ),
232                 );
233             } else if mask_value == 0 {
234                 span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
235             },
236             BinOpKind::BitOr => if mask_value >= cmp_value {
237                 span_lint(
238                     cx,
239                     BAD_BIT_MASK,
240                     span,
241                     &format!(
242                         "incompatible bit mask: `_ | {}` will never be lower than `{}`",
243                         mask_value,
244                         cmp_value
245                     ),
246                 );
247             } else {
248                 check_ineffective_lt(cx, span, mask_value, cmp_value, "|");
249             },
250             BinOpKind::BitXor => check_ineffective_lt(cx, span, mask_value, cmp_value, "^"),
251             _ => (),
252         },
253         BinOpKind::Le | BinOpKind::Gt => match bit_op {
254             BinOpKind::BitAnd => if mask_value <= cmp_value {
255                 span_lint(
256                     cx,
257                     BAD_BIT_MASK,
258                     span,
259                     &format!(
260                         "incompatible bit mask: `_ & {}` will never be higher than `{}`",
261                         mask_value,
262                         cmp_value
263                     ),
264                 );
265             } else if mask_value == 0 {
266                 span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
267             },
268             BinOpKind::BitOr => if mask_value > cmp_value {
269                 span_lint(
270                     cx,
271                     BAD_BIT_MASK,
272                     span,
273                     &format!(
274                         "incompatible bit mask: `_ | {}` will always be higher than `{}`",
275                         mask_value,
276                         cmp_value
277                     ),
278                 );
279             } else {
280                 check_ineffective_gt(cx, span, mask_value, cmp_value, "|");
281             },
282             BinOpKind::BitXor => check_ineffective_gt(cx, span, mask_value, cmp_value, "^"),
283             _ => (),
284         },
285         _ => (),
286     }
287 }
288
289 fn check_ineffective_lt(cx: &LateContext<'_, '_>, span: Span, m: u128, c: u128, op: &str) {
290     if c.is_power_of_two() && m < c {
291         span_lint(
292             cx,
293             INEFFECTIVE_BIT_MASK,
294             span,
295             &format!(
296                 "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
297                 op,
298                 m,
299                 c
300             ),
301         );
302     }
303 }
304
305 fn check_ineffective_gt(cx: &LateContext<'_, '_>, span: Span, m: u128, c: u128, op: &str) {
306     if (c + 1).is_power_of_two() && m <= c {
307         span_lint(
308             cx,
309             INEFFECTIVE_BIT_MASK,
310             span,
311             &format!(
312                 "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
313                 op,
314                 m,
315                 c
316             ),
317         );
318     }
319 }
320
321 fn fetch_int_literal(cx: &LateContext<'_, '_>, lit: &Expr) -> Option<u128> {
322     match constant(cx, cx.tables, lit)?.0 {
323         Constant::Int(n) => Some(n),
324         _ => None,
325     }
326 }