3 use syntax
::ast
::LitKind
;
4 use syntax
::codemap
::Span
;
5 use utils
::{span_lint, span_lint_and_then}
;
7 use consts
::{constant, Constant}
;
9 /// **What it does:** Checks for incompatible bit masks in comparisons.
11 /// The formula for detecting if an expression of the type `_ <bit_op> m
12 /// <cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of
13 /// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
16 /// |Comparison |Bit Op|Example |is always|Formula |
17 /// |------------|------|------------|---------|----------------------|
18 /// |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |
19 /// |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |
20 /// |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |
21 /// |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |
22 /// |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |
23 /// |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |
25 /// **Why is this bad?** If the bits that the comparison cares about are always
26 /// set to zero or one by the bit mask, the comparison is constant `true` or
27 /// `false` (depending on mask, compared value, and operators).
29 /// So the code is actively misleading, and the only reason someone would write
30 /// this intentionally is to win an underhanded Rust contest or create a
31 /// test-case for this lint.
33 /// **Known problems:** None.
37 /// if (x & 1 == 2) { … }
39 declare_clippy_lint
! {
42 "expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
45 /// **What it does:** Checks for bit masks in comparisons which can be removed
46 /// without changing the outcome. The basic structure can be seen in the
49 /// |Comparison| Bit Op |Example |equals |
50 /// |----------|---------|-----------|-------|
51 /// |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|
52 /// |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|
54 /// **Why is this bad?** Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
55 /// but still a bit misleading, because the bit mask is ineffective.
57 /// **Known problems:** False negatives: This lint will only match instances
58 /// where we have figured out the math (which is for a power-of-two compared
59 /// value). This means things like `x | 1 >= 7` (which would be better written
60 /// as `x >= 6`) will not be reported (but bit masks like this are fairly
65 /// if (x | 1 > 3) { … }
67 declare_clippy_lint
! {
68 pub INEFFECTIVE_BIT_MASK
,
70 "expressions where a bit mask will be rendered useless by a comparison, e.g. `(x | 1) > 2`"
73 /// **What it does:** Checks for bit masks that can be replaced by a call
74 /// to `trailing_zeros`
76 /// **Why is this bad?** `x.trailing_zeros() > 4` is much clearer than `x & 15
79 /// **Known problems:** llvm generates better code for `x & 15 == 0` on x86
85 declare_clippy_lint
! {
88 "expressions where a bit mask is less readable than the corresponding method call"
91 #[derive(Copy, Clone)]
93 verbose_bit_mask_threshold
: u64,
97 pub fn new(verbose_bit_mask_threshold
: u64) -> Self {
99 verbose_bit_mask_threshold
,
104 impl LintPass
for BitMask
{
105 fn get_lints(&self) -> LintArray
{
106 lint_array
!(BAD_BIT_MASK
, INEFFECTIVE_BIT_MASK
, VERBOSE_BIT_MASK
)
110 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for BitMask
{
111 fn check_expr(&mut self, cx
: &LateContext
<'a
, 'tcx
>, e
: &'tcx Expr
) {
112 if let ExprBinary(ref cmp
, ref left
, ref right
) = e
.node
{
113 if cmp
.node
.is_comparison() {
114 if let Some(cmp_opt
) = fetch_int_literal(cx
, right
) {
115 check_compare(cx
, left
, cmp
.node
, cmp_opt
, &e
.span
)
116 } else if let Some(cmp_val
) = fetch_int_literal(cx
, left
) {
117 check_compare(cx
, right
, invert_cmp(cmp
.node
), cmp_val
, &e
.span
)
122 if let Expr_
::ExprBinary(ref op
, ref left
, ref right
) = e
.node
;
123 if BinOp_
::BiEq
== op
.node
;
124 if let Expr_
::ExprBinary(ref op1
, ref left1
, ref right1
) = left
.node
;
125 if BinOp_
::BiBitAnd
== op1
.node
;
126 if let Expr_
::ExprLit(ref lit
) = right1
.node
;
127 if let LitKind
::Int(n
, _
) = lit
.node
;
128 if let Expr_
::ExprLit(ref lit1
) = right
.node
;
129 if let LitKind
::Int(0, _
) = lit1
.node
;
130 if n
.leading_zeros() == n
.count_zeros();
131 if n
> u128
::from(self.verbose_bit_mask_threshold
);
133 span_lint_and_then(cx
,
136 "bit mask could be simplified with a call to `trailing_zeros`",
138 let sugg
= Sugg
::hir(cx
, left1
, "...").maybe_par();
139 db
.span_suggestion(e
.span
, "try", format
!("{}.trailing_zeros() >= {}", sugg
, n
.count_ones()));
146 fn invert_cmp(cmp
: BinOp_
) -> BinOp_
{
159 fn check_compare(cx
: &LateContext
, bit_op
: &Expr
, cmp_op
: BinOp_
, cmp_value
: u128
, span
: &Span
) {
160 if let ExprBinary(ref op
, ref left
, ref right
) = bit_op
.node
{
161 if op
.node
!= BiBitAnd
&& op
.node
!= BiBitOr
{
164 fetch_int_literal(cx
, right
)
165 .or_else(|| fetch_int_literal(cx
, left
))
166 .map_or((), |mask
| check_bit_mask(cx
, op
.node
, cmp_op
, mask
, cmp_value
, span
))
170 fn check_bit_mask(cx
: &LateContext
, bit_op
: BinOp_
, cmp_op
: BinOp_
, mask_value
: u128
, cmp_value
: u128
, span
: &Span
) {
172 BiEq
| BiNe
=> match bit_op
{
173 BiBitAnd
=> if mask_value
& cmp_value
!= cmp_value
{
180 "incompatible bit mask: `_ & {}` can never be equal to `{}`",
186 } else if mask_value
== 0 {
187 span_lint(cx
, BAD_BIT_MASK
, *span
, "&-masking with zero");
189 BiBitOr
=> if mask_value
| cmp_value
!= cmp_value
{
195 "incompatible bit mask: `_ | {}` can never be equal to `{}`",
203 BiLt
| BiGe
=> match bit_op
{
204 BiBitAnd
=> if mask_value
< cmp_value
{
210 "incompatible bit mask: `_ & {}` will always be lower than `{}`",
215 } else if mask_value
== 0 {
216 span_lint(cx
, BAD_BIT_MASK
, *span
, "&-masking with zero");
218 BiBitOr
=> if mask_value
>= cmp_value
{
224 "incompatible bit mask: `_ | {}` will never be lower than `{}`",
230 check_ineffective_lt(cx
, *span
, mask_value
, cmp_value
, "|");
232 BiBitXor
=> check_ineffective_lt(cx
, *span
, mask_value
, cmp_value
, "^"),
235 BiLe
| BiGt
=> match bit_op
{
236 BiBitAnd
=> if mask_value
<= cmp_value
{
242 "incompatible bit mask: `_ & {}` will never be higher than `{}`",
247 } else if mask_value
== 0 {
248 span_lint(cx
, BAD_BIT_MASK
, *span
, "&-masking with zero");
250 BiBitOr
=> if mask_value
> cmp_value
{
256 "incompatible bit mask: `_ | {}` will always be higher than `{}`",
262 check_ineffective_gt(cx
, *span
, mask_value
, cmp_value
, "|");
264 BiBitXor
=> check_ineffective_gt(cx
, *span
, mask_value
, cmp_value
, "^"),
271 fn check_ineffective_lt(cx
: &LateContext
, span
: Span
, m
: u128
, c
: u128
, op
: &str) {
272 if c
.is_power_of_two() && m
< c
{
275 INEFFECTIVE_BIT_MASK
,
278 "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
287 fn check_ineffective_gt(cx
: &LateContext
, span
: Span
, m
: u128
, c
: u128
, op
: &str) {
288 if (c
+ 1).is_power_of_two() && m
<= c
{
291 INEFFECTIVE_BIT_MASK
,
294 "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
303 fn fetch_int_literal(cx
: &LateContext
, lit
: &Expr
) -> Option
<u128
> {
304 match constant(cx
, cx
.tables
, lit
)?
.0 {
305 Constant
::Int(n
) => Some(n
),