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