]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/operators/bit_mask.rs
Merge branch 'master' of http://localhost:8000/rust-lang/rust.git:at_commit=75dd959a3...
[rust.git] / src / tools / clippy / clippy_lints / src / operators / bit_mask.rs
1 use clippy_utils::consts::{constant, Constant};
2 use clippy_utils::diagnostics::span_lint;
3 use rustc_hir::{BinOpKind, Expr, ExprKind};
4 use rustc_lint::LateContext;
5 use rustc_span::source_map::Span;
6
7 use super::{BAD_BIT_MASK, INEFFECTIVE_BIT_MASK};
8
9 pub(super) fn check<'tcx>(
10     cx: &LateContext<'tcx>,
11     e: &'tcx Expr<'_>,
12     op: BinOpKind,
13     left: &'tcx Expr<'_>,
14     right: &'tcx Expr<'_>,
15 ) {
16     if op.is_comparison() {
17         if let Some(cmp_opt) = fetch_int_literal(cx, right) {
18             check_compare(cx, left, op, cmp_opt, e.span);
19         } else if let Some(cmp_val) = fetch_int_literal(cx, left) {
20             check_compare(cx, right, invert_cmp(op), cmp_val, e.span);
21         }
22     }
23 }
24
25 #[must_use]
26 fn invert_cmp(cmp: BinOpKind) -> BinOpKind {
27     match cmp {
28         BinOpKind::Eq => BinOpKind::Eq,
29         BinOpKind::Ne => BinOpKind::Ne,
30         BinOpKind::Lt => BinOpKind::Gt,
31         BinOpKind::Gt => BinOpKind::Lt,
32         BinOpKind::Le => BinOpKind::Ge,
33         BinOpKind::Ge => BinOpKind::Le,
34         _ => BinOpKind::Or, // Dummy
35     }
36 }
37
38 fn check_compare(cx: &LateContext<'_>, bit_op: &Expr<'_>, cmp_op: BinOpKind, cmp_value: u128, span: Span) {
39     if let ExprKind::Binary(op, left, right) = &bit_op.kind {
40         if op.node != BinOpKind::BitAnd && op.node != BinOpKind::BitOr {
41             return;
42         }
43         fetch_int_literal(cx, right)
44             .or_else(|| fetch_int_literal(cx, left))
45             .map_or((), |mask| check_bit_mask(cx, op.node, cmp_op, mask, cmp_value, span));
46     }
47 }
48
49 #[allow(clippy::too_many_lines)]
50 fn check_bit_mask(
51     cx: &LateContext<'_>,
52     bit_op: BinOpKind,
53     cmp_op: BinOpKind,
54     mask_value: u128,
55     cmp_value: u128,
56     span: Span,
57 ) {
58     match cmp_op {
59         BinOpKind::Eq | BinOpKind::Ne => match bit_op {
60             BinOpKind::BitAnd => {
61                 if mask_value & cmp_value != cmp_value {
62                     if cmp_value != 0 {
63                         span_lint(
64                             cx,
65                             BAD_BIT_MASK,
66                             span,
67                             &format!("incompatible bit mask: `_ & {mask_value}` can never be equal to `{cmp_value}`"),
68                         );
69                     }
70                 } else if mask_value == 0 {
71                     span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
72                 }
73             },
74             BinOpKind::BitOr => {
75                 if mask_value | cmp_value != cmp_value {
76                     span_lint(
77                         cx,
78                         BAD_BIT_MASK,
79                         span,
80                         &format!("incompatible bit mask: `_ | {mask_value}` can never be equal to `{cmp_value}`"),
81                     );
82                 }
83             },
84             _ => (),
85         },
86         BinOpKind::Lt | BinOpKind::Ge => match bit_op {
87             BinOpKind::BitAnd => {
88                 if mask_value < cmp_value {
89                     span_lint(
90                         cx,
91                         BAD_BIT_MASK,
92                         span,
93                         &format!("incompatible bit mask: `_ & {mask_value}` will always be lower than `{cmp_value}`"),
94                     );
95                 } else if mask_value == 0 {
96                     span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
97                 }
98             },
99             BinOpKind::BitOr => {
100                 if mask_value >= cmp_value {
101                     span_lint(
102                         cx,
103                         BAD_BIT_MASK,
104                         span,
105                         &format!("incompatible bit mask: `_ | {mask_value}` will never be lower than `{cmp_value}`"),
106                     );
107                 } else {
108                     check_ineffective_lt(cx, span, mask_value, cmp_value, "|");
109                 }
110             },
111             BinOpKind::BitXor => check_ineffective_lt(cx, span, mask_value, cmp_value, "^"),
112             _ => (),
113         },
114         BinOpKind::Le | BinOpKind::Gt => match bit_op {
115             BinOpKind::BitAnd => {
116                 if mask_value <= cmp_value {
117                     span_lint(
118                         cx,
119                         BAD_BIT_MASK,
120                         span,
121                         &format!("incompatible bit mask: `_ & {mask_value}` will never be higher than `{cmp_value}`"),
122                     );
123                 } else if mask_value == 0 {
124                     span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
125                 }
126             },
127             BinOpKind::BitOr => {
128                 if mask_value > cmp_value {
129                     span_lint(
130                         cx,
131                         BAD_BIT_MASK,
132                         span,
133                         &format!("incompatible bit mask: `_ | {mask_value}` will always be higher than `{cmp_value}`"),
134                     );
135                 } else {
136                     check_ineffective_gt(cx, span, mask_value, cmp_value, "|");
137                 }
138             },
139             BinOpKind::BitXor => check_ineffective_gt(cx, span, mask_value, cmp_value, "^"),
140             _ => (),
141         },
142         _ => (),
143     }
144 }
145
146 fn check_ineffective_lt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: &str) {
147     if c.is_power_of_two() && m < c {
148         span_lint(
149             cx,
150             INEFFECTIVE_BIT_MASK,
151             span,
152             &format!("ineffective bit mask: `x {op} {m}` compared to `{c}`, is the same as x compared directly"),
153         );
154     }
155 }
156
157 fn check_ineffective_gt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: &str) {
158     if (c + 1).is_power_of_two() && m <= c {
159         span_lint(
160             cx,
161             INEFFECTIVE_BIT_MASK,
162             span,
163             &format!("ineffective bit mask: `x {op} {m}` compared to `{c}`, is the same as x compared directly"),
164         );
165     }
166 }
167
168 fn fetch_int_literal(cx: &LateContext<'_>, lit: &Expr<'_>) -> Option<u128> {
169     match constant(cx, cx.typeck_results(), lit)?.0 {
170         Constant::Int(n) => Some(n),
171         _ => None,
172     }
173 }