]>
Commit | Line | Data |
---|---|---|
ed00b5ec | 1 | use clippy_config::msrvs::{self, Msrv}; |
17df50a5 | 2 | use clippy_utils::consts::{constant, Constant}; |
cdc7bbd5 XL |
3 | use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; |
4 | use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; | |
5 | use clippy_utils::sugg::Sugg; | |
add651ee | 6 | use clippy_utils::{get_parent_expr, higher, in_constant, is_integer_const, path_to_local}; |
f20569fa XL |
7 | use rustc_ast::ast::RangeLimits; |
8 | use rustc_errors::Applicability; | |
f2b60f7d | 9 | use rustc_hir::{BinOpKind, Expr, ExprKind, HirId}; |
5099ac24 | 10 | use rustc_lint::{LateContext, LateLintPass}; |
f20569fa | 11 | use rustc_middle::ty; |
4b012472 | 12 | use rustc_session::impl_lint_pass; |
ed00b5ec | 13 | use rustc_span::source_map::Spanned; |
4b012472 | 14 | use rustc_span::Span; |
f20569fa XL |
15 | use std::cmp::Ordering; |
16 | ||
f20569fa | 17 | declare_clippy_lint! { |
94222f64 XL |
18 | /// ### What it does |
19 | /// Checks for exclusive ranges where 1 is added to the | |
f20569fa XL |
20 | /// upper bound, e.g., `x..(y+1)`. |
21 | /// | |
94222f64 XL |
22 | /// ### Why is this bad? |
23 | /// The code is more readable with an inclusive range | |
f20569fa XL |
24 | /// like `x..=y`. |
25 | /// | |
94222f64 XL |
26 | /// ### Known problems |
27 | /// Will add unnecessary pair of parentheses when the | |
28 | /// expression is not wrapped in a pair but starts with an opening parenthesis | |
f20569fa XL |
29 | /// and ends with a closing one. |
30 | /// I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`. | |
31 | /// | |
32 | /// Also in many cases, inclusive ranges are still slower to run than | |
33 | /// exclusive ranges, because they essentially add an extra branch that | |
34 | /// LLVM may fail to hoist out of the loop. | |
35 | /// | |
36 | /// This will cause a warning that cannot be fixed if the consumer of the | |
37 | /// range only accepts a specific range type, instead of the generic | |
38 | /// `RangeBounds` trait | |
39 | /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)). | |
40 | /// | |
94222f64 | 41 | /// ### Example |
ed00b5ec | 42 | /// ```no_run |
923072b8 FG |
43 | /// # let x = 0; |
44 | /// # let y = 1; | |
45 | /// for i in x..(y+1) { | |
46 | /// // .. | |
47 | /// } | |
f20569fa | 48 | /// ``` |
923072b8 FG |
49 | /// |
50 | /// Use instead: | |
ed00b5ec | 51 | /// ```no_run |
923072b8 FG |
52 | /// # let x = 0; |
53 | /// # let y = 1; | |
54 | /// for i in x..=y { | |
55 | /// // .. | |
56 | /// } | |
f20569fa | 57 | /// ``` |
a2a8927a | 58 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
59 | pub RANGE_PLUS_ONE, |
60 | pedantic, | |
61 | "`x..(y+1)` reads better as `x..=y`" | |
62 | } | |
63 | ||
64 | declare_clippy_lint! { | |
94222f64 XL |
65 | /// ### What it does |
66 | /// Checks for inclusive ranges where 1 is subtracted from | |
f20569fa XL |
67 | /// the upper bound, e.g., `x..=(y-1)`. |
68 | /// | |
94222f64 XL |
69 | /// ### Why is this bad? |
70 | /// The code is more readable with an exclusive range | |
f20569fa XL |
71 | /// like `x..y`. |
72 | /// | |
94222f64 XL |
73 | /// ### Known problems |
74 | /// This will cause a warning that cannot be fixed if | |
f20569fa XL |
75 | /// the consumer of the range only accepts a specific range type, instead of |
76 | /// the generic `RangeBounds` trait | |
77 | /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)). | |
78 | /// | |
94222f64 | 79 | /// ### Example |
ed00b5ec | 80 | /// ```no_run |
923072b8 FG |
81 | /// # let x = 0; |
82 | /// # let y = 1; | |
83 | /// for i in x..=(y-1) { | |
84 | /// // .. | |
85 | /// } | |
f20569fa | 86 | /// ``` |
923072b8 FG |
87 | /// |
88 | /// Use instead: | |
ed00b5ec | 89 | /// ```no_run |
923072b8 FG |
90 | /// # let x = 0; |
91 | /// # let y = 1; | |
92 | /// for i in x..y { | |
93 | /// // .. | |
94 | /// } | |
f20569fa | 95 | /// ``` |
a2a8927a | 96 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
97 | pub RANGE_MINUS_ONE, |
98 | pedantic, | |
99 | "`x..=(y-1)` reads better as `x..y`" | |
100 | } | |
101 | ||
102 | declare_clippy_lint! { | |
94222f64 XL |
103 | /// ### What it does |
104 | /// Checks for range expressions `x..y` where both `x` and `y` | |
9c376795 | 105 | /// are constant and `x` is greater to `y`. Also triggers if `x` is equal to `y` when they are conditions to a `for` loop. |
f20569fa | 106 | /// |
94222f64 XL |
107 | /// ### Why is this bad? |
108 | /// Empty ranges yield no values so iterating them is a no-op. | |
f20569fa XL |
109 | /// Moreover, trying to use a reversed range to index a slice will panic at run-time. |
110 | /// | |
94222f64 | 111 | /// ### Example |
f20569fa XL |
112 | /// ```rust,no_run |
113 | /// fn main() { | |
114 | /// (10..=0).for_each(|x| println!("{}", x)); | |
115 | /// | |
116 | /// let arr = [1, 2, 3, 4, 5]; | |
117 | /// let sub = &arr[3..1]; | |
118 | /// } | |
119 | /// ``` | |
120 | /// Use instead: | |
ed00b5ec | 121 | /// ```no_run |
f20569fa XL |
122 | /// fn main() { |
123 | /// (0..=10).rev().for_each(|x| println!("{}", x)); | |
124 | /// | |
125 | /// let arr = [1, 2, 3, 4, 5]; | |
126 | /// let sub = &arr[1..3]; | |
127 | /// } | |
128 | /// ``` | |
a2a8927a | 129 | #[clippy::version = "1.45.0"] |
f20569fa XL |
130 | pub REVERSED_EMPTY_RANGES, |
131 | correctness, | |
132 | "reversing the limits of range expressions, resulting in empty ranges" | |
133 | } | |
134 | ||
135 | declare_clippy_lint! { | |
94222f64 XL |
136 | /// ### What it does |
137 | /// Checks for expressions like `x >= 3 && x < 8` that could | |
f20569fa XL |
138 | /// be more readably expressed as `(3..8).contains(x)`. |
139 | /// | |
94222f64 XL |
140 | /// ### Why is this bad? |
141 | /// `contains` expresses the intent better and has less | |
f20569fa XL |
142 | /// failure modes (such as fencepost errors or using `||` instead of `&&`). |
143 | /// | |
94222f64 | 144 | /// ### Example |
ed00b5ec | 145 | /// ```no_run |
f20569fa XL |
146 | /// // given |
147 | /// let x = 6; | |
148 | /// | |
149 | /// assert!(x >= 3 && x < 8); | |
150 | /// ``` | |
151 | /// Use instead: | |
ed00b5ec | 152 | /// ```no_run |
f20569fa XL |
153 | ///# let x = 6; |
154 | /// assert!((3..8).contains(&x)); | |
155 | /// ``` | |
a2a8927a | 156 | #[clippy::version = "1.49.0"] |
f20569fa XL |
157 | pub MANUAL_RANGE_CONTAINS, |
158 | style, | |
159 | "manually reimplementing {`Range`, `RangeInclusive`}`::contains`" | |
160 | } | |
161 | ||
f20569fa | 162 | pub struct Ranges { |
487cf647 | 163 | msrv: Msrv, |
f20569fa XL |
164 | } |
165 | ||
166 | impl Ranges { | |
167 | #[must_use] | |
487cf647 | 168 | pub fn new(msrv: Msrv) -> Self { |
f20569fa XL |
169 | Self { msrv } |
170 | } | |
171 | } | |
172 | ||
173 | impl_lint_pass!(Ranges => [ | |
f20569fa XL |
174 | RANGE_PLUS_ONE, |
175 | RANGE_MINUS_ONE, | |
176 | REVERSED_EMPTY_RANGES, | |
177 | MANUAL_RANGE_CONTAINS, | |
178 | ]); | |
179 | ||
180 | impl<'tcx> LateLintPass<'tcx> for Ranges { | |
181 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | |
f2b60f7d | 182 | if let ExprKind::Binary(ref op, l, r) = expr.kind { |
487cf647 | 183 | if self.msrv.meets(msrvs::RANGE_CONTAINS) { |
f2b60f7d FG |
184 | check_possible_range_contains(cx, op.node, l, r, expr, expr.span); |
185 | } | |
f20569fa XL |
186 | } |
187 | ||
188 | check_exclusive_range_plus_one(cx, expr); | |
189 | check_inclusive_range_minus_one(cx, expr); | |
190 | check_reversed_empty_range(cx, expr); | |
191 | } | |
192 | extract_msrv_attr!(LateContext); | |
193 | } | |
194 | ||
923072b8 FG |
195 | fn check_possible_range_contains( |
196 | cx: &LateContext<'_>, | |
197 | op: BinOpKind, | |
198 | left: &Expr<'_>, | |
199 | right: &Expr<'_>, | |
200 | expr: &Expr<'_>, | |
201 | span: Span, | |
202 | ) { | |
f20569fa XL |
203 | if in_constant(cx, expr.hir_id) { |
204 | return; | |
205 | } | |
206 | ||
f20569fa XL |
207 | let combine_and = match op { |
208 | BinOpKind::And | BinOpKind::BitAnd => true, | |
209 | BinOpKind::Or | BinOpKind::BitOr => false, | |
210 | _ => return, | |
211 | }; | |
212 | // value, name, order (higher/lower), inclusiveness | |
923072b8 | 213 | if let (Some(l), Some(r)) = (check_range_bounds(cx, left), check_range_bounds(cx, right)) { |
f20569fa XL |
214 | // we only lint comparisons on the same name and with different |
215 | // direction | |
923072b8 | 216 | if l.id != r.id || l.ord == r.ord { |
f20569fa XL |
217 | return; |
218 | } | |
923072b8 FG |
219 | let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l.expr), &l.val, &r.val); |
220 | if combine_and && ord == Some(r.ord) { | |
f20569fa | 221 | // order lower bound and upper bound |
923072b8 FG |
222 | let (l_span, u_span, l_inc, u_inc) = if r.ord == Ordering::Less { |
223 | (l.val_span, r.val_span, l.inc, r.inc) | |
f20569fa | 224 | } else { |
923072b8 | 225 | (r.val_span, l.val_span, r.inc, l.inc) |
f20569fa XL |
226 | }; |
227 | // we only lint inclusive lower bounds | |
228 | if !l_inc { | |
229 | return; | |
230 | } | |
231 | let (range_type, range_op) = if u_inc { | |
232 | ("RangeInclusive", "..=") | |
233 | } else { | |
234 | ("Range", "..") | |
235 | }; | |
236 | let mut applicability = Applicability::MachineApplicable; | |
923072b8 | 237 | let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability); |
f20569fa XL |
238 | let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); |
239 | let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); | |
240 | let space = if lo.ends_with('.') { " " } else { "" }; | |
241 | span_lint_and_sugg( | |
242 | cx, | |
243 | MANUAL_RANGE_CONTAINS, | |
244 | span, | |
e8be2606 | 245 | format!("manual `{range_type}::contains` implementation"), |
f20569fa | 246 | "use", |
2b03887a | 247 | format!("({lo}{space}{range_op}{hi}).contains(&{name})"), |
f20569fa XL |
248 | applicability, |
249 | ); | |
923072b8 | 250 | } else if !combine_and && ord == Some(l.ord) { |
f20569fa XL |
251 | // `!_.contains(_)` |
252 | // order lower bound and upper bound | |
923072b8 FG |
253 | let (l_span, u_span, l_inc, u_inc) = if l.ord == Ordering::Less { |
254 | (l.val_span, r.val_span, l.inc, r.inc) | |
f20569fa | 255 | } else { |
923072b8 | 256 | (r.val_span, l.val_span, r.inc, l.inc) |
f20569fa XL |
257 | }; |
258 | if l_inc { | |
259 | return; | |
260 | } | |
261 | let (range_type, range_op) = if u_inc { | |
262 | ("Range", "..") | |
263 | } else { | |
264 | ("RangeInclusive", "..=") | |
265 | }; | |
266 | let mut applicability = Applicability::MachineApplicable; | |
923072b8 | 267 | let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability); |
f20569fa XL |
268 | let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); |
269 | let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); | |
270 | let space = if lo.ends_with('.') { " " } else { "" }; | |
271 | span_lint_and_sugg( | |
272 | cx, | |
273 | MANUAL_RANGE_CONTAINS, | |
274 | span, | |
e8be2606 | 275 | format!("manual `!{range_type}::contains` implementation"), |
f20569fa | 276 | "use", |
2b03887a | 277 | format!("!({lo}{space}{range_op}{hi}).contains(&{name})"), |
f20569fa XL |
278 | applicability, |
279 | ); | |
280 | } | |
281 | } | |
923072b8 FG |
282 | |
283 | // If the LHS is the same operator, we have to recurse to get the "real" RHS, since they have | |
284 | // the same operator precedence | |
4b012472 FG |
285 | if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind |
286 | && op == lhs_op.node | |
287 | && let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent()) | |
288 | && let Some(snip) = &snippet_opt(cx, new_span) | |
923072b8 | 289 | // Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong |
4b012472 FG |
290 | && snip.matches('(').count() == snip.matches(')').count() |
291 | { | |
292 | check_possible_range_contains(cx, op, new_lhs, right, expr, new_span); | |
923072b8 FG |
293 | } |
294 | } | |
295 | ||
fe692bf9 FG |
296 | struct RangeBounds<'a, 'tcx> { |
297 | val: Constant<'tcx>, | |
923072b8 FG |
298 | expr: &'a Expr<'a>, |
299 | id: HirId, | |
300 | name_span: Span, | |
301 | val_span: Span, | |
302 | ord: Ordering, | |
303 | inc: bool, | |
f20569fa XL |
304 | } |
305 | ||
923072b8 FG |
306 | // Takes a binary expression such as x <= 2 as input |
307 | // Breaks apart into various pieces, such as the value of the number, | |
308 | // hir id of the variable, and direction/inclusiveness of the operator | |
fe692bf9 | 309 | fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> Option<RangeBounds<'a, 'tcx>> { |
cdc7bbd5 | 310 | if let ExprKind::Binary(ref op, l, r) = ex.kind { |
f20569fa XL |
311 | let (inclusive, ordering) = match op.node { |
312 | BinOpKind::Gt => (false, Ordering::Greater), | |
313 | BinOpKind::Ge => (true, Ordering::Greater), | |
314 | BinOpKind::Lt => (false, Ordering::Less), | |
315 | BinOpKind::Le => (true, Ordering::Less), | |
316 | _ => return None, | |
317 | }; | |
5099ac24 | 318 | if let Some(id) = path_to_local(l) { |
49aad941 | 319 | if let Some(c) = constant(cx, cx.typeck_results(), r) { |
923072b8 FG |
320 | return Some(RangeBounds { |
321 | val: c, | |
322 | expr: r, | |
323 | id, | |
324 | name_span: l.span, | |
325 | val_span: r.span, | |
326 | ord: ordering, | |
327 | inc: inclusive, | |
328 | }); | |
f20569fa | 329 | } |
5099ac24 | 330 | } else if let Some(id) = path_to_local(r) { |
49aad941 | 331 | if let Some(c) = constant(cx, cx.typeck_results(), l) { |
923072b8 FG |
332 | return Some(RangeBounds { |
333 | val: c, | |
334 | expr: l, | |
335 | id, | |
336 | name_span: r.span, | |
337 | val_span: l.span, | |
338 | ord: ordering.reverse(), | |
339 | inc: inclusive, | |
340 | }); | |
f20569fa XL |
341 | } |
342 | } | |
343 | } | |
344 | None | |
345 | } | |
346 | ||
f20569fa XL |
347 | // exclusive range plus one: `x..(y+1)` |
348 | fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { | |
4b012472 FG |
349 | if expr.span.can_be_used_for_suggestions() |
350 | && let Some(higher::Range { | |
f20569fa XL |
351 | start, |
352 | end: Some(end), | |
4b012472 FG |
353 | limits: RangeLimits::HalfOpen, |
354 | }) = higher::Range::hir(expr) | |
355 | && let Some(y) = y_plus_one(cx, end) | |
356 | { | |
357 | let span = expr.span; | |
358 | span_lint_and_then( | |
359 | cx, | |
360 | RANGE_PLUS_ONE, | |
361 | span, | |
362 | "an inclusive range would be more readable", | |
363 | |diag| { | |
364 | let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); | |
365 | let end = Sugg::hir(cx, y, "y").maybe_par(); | |
366 | if let Some(is_wrapped) = &snippet_opt(cx, span) { | |
367 | if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') { | |
368 | diag.span_suggestion(span, "use", format!("({start}..={end})"), Applicability::MaybeIncorrect); | |
369 | } else { | |
370 | diag.span_suggestion( | |
371 | span, | |
372 | "use", | |
373 | format!("{start}..={end}"), | |
374 | Applicability::MachineApplicable, // snippet | |
375 | ); | |
f20569fa | 376 | } |
4b012472 FG |
377 | } |
378 | }, | |
379 | ); | |
f20569fa XL |
380 | } |
381 | } | |
382 | ||
383 | // inclusive range minus one: `x..=(y-1)` | |
384 | fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { | |
4b012472 FG |
385 | if expr.span.can_be_used_for_suggestions() |
386 | && let Some(higher::Range { | |
387 | start, | |
388 | end: Some(end), | |
389 | limits: RangeLimits::Closed, | |
390 | }) = higher::Range::hir(expr) | |
391 | && let Some(y) = y_minus_one(cx, end) | |
392 | { | |
393 | span_lint_and_then( | |
394 | cx, | |
395 | RANGE_MINUS_ONE, | |
396 | expr.span, | |
397 | "an exclusive range would be more readable", | |
398 | |diag| { | |
399 | let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string()); | |
400 | let end = Sugg::hir(cx, y, "y").maybe_par(); | |
401 | diag.span_suggestion( | |
402 | expr.span, | |
403 | "use", | |
404 | format!("{start}..{end}"), | |
405 | Applicability::MachineApplicable, // snippet | |
406 | ); | |
407 | }, | |
408 | ); | |
f20569fa XL |
409 | } |
410 | } | |
411 | ||
412 | fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { | |
413 | fn inside_indexing_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | |
414 | matches!( | |
415 | get_parent_expr(cx, expr), | |
416 | Some(Expr { | |
417 | kind: ExprKind::Index(..), | |
418 | .. | |
419 | }) | |
420 | ) | |
421 | } | |
422 | ||
423 | fn is_for_loop_arg(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | |
424 | let mut cur_expr = expr; | |
425 | while let Some(parent_expr) = get_parent_expr(cx, cur_expr) { | |
94222f64 XL |
426 | match higher::ForLoop::hir(parent_expr) { |
427 | Some(higher::ForLoop { arg, .. }) if arg.hir_id == expr.hir_id => return true, | |
f20569fa XL |
428 | _ => cur_expr = parent_expr, |
429 | } | |
430 | } | |
431 | ||
432 | false | |
433 | } | |
434 | ||
435 | fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool { | |
436 | match limits { | |
437 | RangeLimits::HalfOpen => ordering != Ordering::Less, | |
438 | RangeLimits::Closed => ordering == Ordering::Greater, | |
439 | } | |
440 | } | |
441 | ||
4b012472 FG |
442 | if let Some(higher::Range { |
443 | start: Some(start), | |
444 | end: Some(end), | |
445 | limits, | |
446 | }) = higher::Range::hir(expr) | |
447 | && let ty = cx.typeck_results().expr_ty(start) | |
448 | && let ty::Int(_) | ty::Uint(_) = ty.kind() | |
449 | && let Some(start_idx) = constant(cx, cx.typeck_results(), start) | |
450 | && let Some(end_idx) = constant(cx, cx.typeck_results(), end) | |
451 | && let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx) | |
452 | && is_empty_range(limits, ordering) | |
453 | { | |
454 | if inside_indexing_expr(cx, expr) { | |
455 | // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ... | |
456 | if ordering != Ordering::Equal { | |
457 | span_lint( | |
f20569fa XL |
458 | cx, |
459 | REVERSED_EMPTY_RANGES, | |
460 | expr.span, | |
4b012472 | 461 | "this range is reversed and using it to index a slice will panic at run-time", |
f20569fa XL |
462 | ); |
463 | } | |
4b012472 FG |
464 | // ... except in for loop arguments for backwards compatibility with `reverse_range_loop` |
465 | } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) { | |
466 | span_lint_and_then( | |
467 | cx, | |
468 | REVERSED_EMPTY_RANGES, | |
469 | expr.span, | |
470 | "this range is empty so it will yield no values", | |
471 | |diag| { | |
472 | if ordering != Ordering::Equal { | |
473 | let start_snippet = snippet(cx, start.span, "_"); | |
474 | let end_snippet = snippet(cx, end.span, "_"); | |
475 | let dots = match limits { | |
476 | RangeLimits::HalfOpen => "..", | |
477 | RangeLimits::Closed => "..=", | |
478 | }; | |
479 | ||
480 | diag.span_suggestion( | |
481 | expr.span, | |
482 | "consider using the following if you are attempting to iterate over this \ | |
483 | range in reverse", | |
484 | format!("({end_snippet}{dots}{start_snippet}).rev()"), | |
485 | Applicability::MaybeIncorrect, | |
486 | ); | |
487 | } | |
488 | }, | |
489 | ); | |
f20569fa XL |
490 | } |
491 | } | |
492 | } | |
493 | ||
494 | fn y_plus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> { | |
495 | match expr.kind { | |
496 | ExprKind::Binary( | |
497 | Spanned { | |
498 | node: BinOpKind::Add, .. | |
499 | }, | |
cdc7bbd5 XL |
500 | lhs, |
501 | rhs, | |
f20569fa XL |
502 | ) => { |
503 | if is_integer_const(cx, lhs, 1) { | |
504 | Some(rhs) | |
505 | } else if is_integer_const(cx, rhs, 1) { | |
506 | Some(lhs) | |
507 | } else { | |
508 | None | |
509 | } | |
510 | }, | |
511 | _ => None, | |
512 | } | |
513 | } | |
514 | ||
515 | fn y_minus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> { | |
516 | match expr.kind { | |
517 | ExprKind::Binary( | |
518 | Spanned { | |
519 | node: BinOpKind::Sub, .. | |
520 | }, | |
cdc7bbd5 XL |
521 | lhs, |
522 | rhs, | |
f20569fa XL |
523 | ) if is_integer_const(cx, rhs, 1) => Some(lhs), |
524 | _ => None, | |
525 | } | |
526 | } |