]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/operators/bit_mask.rs
merge rustc history
[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!(
68                                 "incompatible bit mask: `_ & {}` can never be equal to `{}`",
69                                 mask_value, cmp_value
70                             ),
71                         );
72                     }
73                 } else if mask_value == 0 {
74                     span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
75                 }
76             },
77             BinOpKind::BitOr => {
78                 if mask_value | cmp_value != cmp_value {
79                     span_lint(
80                         cx,
81                         BAD_BIT_MASK,
82                         span,
83                         &format!(
84                             "incompatible bit mask: `_ | {}` can never be equal to `{}`",
85                             mask_value, cmp_value
86                         ),
87                     );
88                 }
89             },
90             _ => (),
91         },
92         BinOpKind::Lt | BinOpKind::Ge => match bit_op {
93             BinOpKind::BitAnd => {
94                 if mask_value < cmp_value {
95                     span_lint(
96                         cx,
97                         BAD_BIT_MASK,
98                         span,
99                         &format!(
100                             "incompatible bit mask: `_ & {}` will always be lower than `{}`",
101                             mask_value, cmp_value
102                         ),
103                     );
104                 } else if mask_value == 0 {
105                     span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
106                 }
107             },
108             BinOpKind::BitOr => {
109                 if mask_value >= cmp_value {
110                     span_lint(
111                         cx,
112                         BAD_BIT_MASK,
113                         span,
114                         &format!(
115                             "incompatible bit mask: `_ | {}` will never be lower than `{}`",
116                             mask_value, cmp_value
117                         ),
118                     );
119                 } else {
120                     check_ineffective_lt(cx, span, mask_value, cmp_value, "|");
121                 }
122             },
123             BinOpKind::BitXor => check_ineffective_lt(cx, span, mask_value, cmp_value, "^"),
124             _ => (),
125         },
126         BinOpKind::Le | BinOpKind::Gt => match bit_op {
127             BinOpKind::BitAnd => {
128                 if mask_value <= cmp_value {
129                     span_lint(
130                         cx,
131                         BAD_BIT_MASK,
132                         span,
133                         &format!(
134                             "incompatible bit mask: `_ & {}` will never be higher than `{}`",
135                             mask_value, cmp_value
136                         ),
137                     );
138                 } else if mask_value == 0 {
139                     span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
140                 }
141             },
142             BinOpKind::BitOr => {
143                 if mask_value > cmp_value {
144                     span_lint(
145                         cx,
146                         BAD_BIT_MASK,
147                         span,
148                         &format!(
149                             "incompatible bit mask: `_ | {}` will always be higher than `{}`",
150                             mask_value, cmp_value
151                         ),
152                     );
153                 } else {
154                     check_ineffective_gt(cx, span, mask_value, cmp_value, "|");
155                 }
156             },
157             BinOpKind::BitXor => check_ineffective_gt(cx, span, mask_value, cmp_value, "^"),
158             _ => (),
159         },
160         _ => (),
161     }
162 }
163
164 fn check_ineffective_lt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: &str) {
165     if c.is_power_of_two() && m < c {
166         span_lint(
167             cx,
168             INEFFECTIVE_BIT_MASK,
169             span,
170             &format!(
171                 "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
172                 op, m, c
173             ),
174         );
175     }
176 }
177
178 fn check_ineffective_gt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: &str) {
179     if (c + 1).is_power_of_two() && m <= c {
180         span_lint(
181             cx,
182             INEFFECTIVE_BIT_MASK,
183             span,
184             &format!(
185                 "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
186                 op, m, c
187             ),
188         );
189     }
190 }
191
192 fn fetch_int_literal(cx: &LateContext<'_>, lit: &Expr<'_>) -> Option<u128> {
193     match constant(cx, cx.typeck_results(), lit)?.0 {
194         Constant::Int(n) => Some(n),
195         _ => None,
196     }
197 }