]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/loops/mod.rs
New upstream version 1.73.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / loops / mod.rs
1 mod empty_loop;
2 mod explicit_counter_loop;
3 mod explicit_into_iter_loop;
4 mod explicit_iter_loop;
5 mod for_kv_map;
6 mod iter_next_loop;
7 mod manual_find;
8 mod manual_flatten;
9 mod manual_memcpy;
10 mod manual_while_let_some;
11 mod missing_spin_loop;
12 mod mut_range_bound;
13 mod needless_range_loop;
14 mod never_loop;
15 mod same_item_push;
16 mod single_element_loop;
17 mod utils;
18 mod while_immutable_condition;
19 mod while_let_loop;
20 mod while_let_on_iterator;
21
22 use clippy_utils::higher;
23 use clippy_utils::msrvs::Msrv;
24 use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
25 use rustc_lint::{LateContext, LateLintPass};
26 use rustc_session::{declare_tool_lint, impl_lint_pass};
27 use rustc_span::source_map::Span;
28 use utils::{make_iterator_snippet, IncrementVisitor, InitializeVisitor};
29
30 declare_clippy_lint! {
31 /// ### What it does
32 /// Checks for for-loops that manually copy items between
33 /// slices that could be optimized by having a memcpy.
34 ///
35 /// ### Why is this bad?
36 /// It is not as fast as a memcpy.
37 ///
38 /// ### Example
39 /// ```rust
40 /// # let src = vec![1];
41 /// # let mut dst = vec![0; 65];
42 /// for i in 0..src.len() {
43 /// dst[i + 64] = src[i];
44 /// }
45 /// ```
46 ///
47 /// Use instead:
48 /// ```rust
49 /// # let src = vec![1];
50 /// # let mut dst = vec![0; 65];
51 /// dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
52 /// ```
53 #[clippy::version = "pre 1.29.0"]
54 pub MANUAL_MEMCPY,
55 perf,
56 "manually copying items between slices"
57 }
58
59 declare_clippy_lint! {
60 /// ### What it does
61 /// Checks for looping over the range of `0..len` of some
62 /// collection just to get the values by index.
63 ///
64 /// ### Why is this bad?
65 /// Just iterating the collection itself makes the intent
66 /// more clear and is probably faster because it eliminates
67 /// the bounds check that is done when indexing.
68 ///
69 /// ### Example
70 /// ```rust
71 /// let vec = vec!['a', 'b', 'c'];
72 /// for i in 0..vec.len() {
73 /// println!("{}", vec[i]);
74 /// }
75 /// ```
76 ///
77 /// Use instead:
78 /// ```rust
79 /// let vec = vec!['a', 'b', 'c'];
80 /// for i in vec {
81 /// println!("{}", i);
82 /// }
83 /// ```
84 #[clippy::version = "pre 1.29.0"]
85 pub NEEDLESS_RANGE_LOOP,
86 style,
87 "for-looping over a range of indices where an iterator over items would do"
88 }
89
90 declare_clippy_lint! {
91 /// ### What it does
92 /// Checks for loops on `x.iter()` where `&x` will do, and
93 /// suggests the latter.
94 ///
95 /// ### Why is this bad?
96 /// Readability.
97 ///
98 /// ### Known problems
99 /// False negatives. We currently only warn on some known
100 /// types.
101 ///
102 /// ### Example
103 /// ```rust
104 /// // with `y` a `Vec` or slice:
105 /// # let y = vec![1];
106 /// for x in y.iter() {
107 /// // ..
108 /// }
109 /// ```
110 ///
111 /// Use instead:
112 /// ```rust
113 /// # let y = vec![1];
114 /// for x in &y {
115 /// // ..
116 /// }
117 /// ```
118 #[clippy::version = "pre 1.29.0"]
119 pub EXPLICIT_ITER_LOOP,
120 pedantic,
121 "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
122 }
123
124 declare_clippy_lint! {
125 /// ### What it does
126 /// Checks for loops on `y.into_iter()` where `y` will do, and
127 /// suggests the latter.
128 ///
129 /// ### Why is this bad?
130 /// Readability.
131 ///
132 /// ### Example
133 /// ```rust
134 /// # let y = vec![1];
135 /// // with `y` a `Vec` or slice:
136 /// for x in y.into_iter() {
137 /// // ..
138 /// }
139 /// ```
140 /// can be rewritten to
141 /// ```rust
142 /// # let y = vec![1];
143 /// for x in y {
144 /// // ..
145 /// }
146 /// ```
147 #[clippy::version = "pre 1.29.0"]
148 pub EXPLICIT_INTO_ITER_LOOP,
149 pedantic,
150 "for-looping over `_.into_iter()` when `_` would do"
151 }
152
153 declare_clippy_lint! {
154 /// ### What it does
155 /// Checks for loops on `x.next()`.
156 ///
157 /// ### Why is this bad?
158 /// `next()` returns either `Some(value)` if there was a
159 /// value, or `None` otherwise. The insidious thing is that `Option<_>`
160 /// implements `IntoIterator`, so that possibly one value will be iterated,
161 /// leading to some hard to find bugs. No one will want to write such code
162 /// [except to win an Underhanded Rust
163 /// Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).
164 ///
165 /// ### Example
166 /// ```ignore
167 /// for x in y.next() {
168 /// ..
169 /// }
170 /// ```
171 #[clippy::version = "pre 1.29.0"]
172 pub ITER_NEXT_LOOP,
173 correctness,
174 "for-looping over `_.next()` which is probably not intended"
175 }
176
177 declare_clippy_lint! {
178 /// ### What it does
179 /// Detects `loop + match` combinations that are easier
180 /// written as a `while let` loop.
181 ///
182 /// ### Why is this bad?
183 /// The `while let` loop is usually shorter and more
184 /// readable.
185 ///
186 /// ### Known problems
187 /// Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).
188 ///
189 /// ### Example
190 /// ```rust,no_run
191 /// # let y = Some(1);
192 /// loop {
193 /// let x = match y {
194 /// Some(x) => x,
195 /// None => break,
196 /// };
197 /// // .. do something with x
198 /// }
199 /// // is easier written as
200 /// while let Some(x) = y {
201 /// // .. do something with x
202 /// };
203 /// ```
204 #[clippy::version = "pre 1.29.0"]
205 pub WHILE_LET_LOOP,
206 complexity,
207 "`loop { if let { ... } else break }`, which can be written as a `while let` loop"
208 }
209
210 declare_clippy_lint! {
211 /// ### What it does
212 /// Checks `for` loops over slices with an explicit counter
213 /// and suggests the use of `.enumerate()`.
214 ///
215 /// ### Why is this bad?
216 /// Using `.enumerate()` makes the intent more clear,
217 /// declutters the code and may be faster in some instances.
218 ///
219 /// ### Example
220 /// ```rust
221 /// # let v = vec![1];
222 /// # fn bar(bar: usize, baz: usize) {}
223 /// let mut i = 0;
224 /// for item in &v {
225 /// bar(i, *item);
226 /// i += 1;
227 /// }
228 /// ```
229 ///
230 /// Use instead:
231 /// ```rust
232 /// # let v = vec![1];
233 /// # fn bar(bar: usize, baz: usize) {}
234 /// for (i, item) in v.iter().enumerate() { bar(i, *item); }
235 /// ```
236 #[clippy::version = "pre 1.29.0"]
237 pub EXPLICIT_COUNTER_LOOP,
238 complexity,
239 "for-looping with an explicit counter when `_.enumerate()` would do"
240 }
241
242 declare_clippy_lint! {
243 /// ### What it does
244 /// Checks for empty `loop` expressions.
245 ///
246 /// ### Why is this bad?
247 /// These busy loops burn CPU cycles without doing
248 /// anything. It is _almost always_ a better idea to `panic!` than to have
249 /// a busy loop.
250 ///
251 /// If panicking isn't possible, think of the environment and either:
252 /// - block on something
253 /// - sleep the thread for some microseconds
254 /// - yield or pause the thread
255 ///
256 /// For `std` targets, this can be done with
257 /// [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)
258 /// or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).
259 ///
260 /// For `no_std` targets, doing this is more complicated, especially because
261 /// `#[panic_handler]`s can't panic. To stop/pause the thread, you will
262 /// probably need to invoke some target-specific intrinsic. Examples include:
263 /// - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)
264 /// - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)
265 ///
266 /// ### Example
267 /// ```no_run
268 /// loop {}
269 /// ```
270 #[clippy::version = "pre 1.29.0"]
271 pub EMPTY_LOOP,
272 suspicious,
273 "empty `loop {}`, which should block or sleep"
274 }
275
276 declare_clippy_lint! {
277 /// ### What it does
278 /// Checks for `while let` expressions on iterators.
279 ///
280 /// ### Why is this bad?
281 /// Readability. A simple `for` loop is shorter and conveys
282 /// the intent better.
283 ///
284 /// ### Example
285 /// ```ignore
286 /// while let Some(val) = iter.next() {
287 /// ..
288 /// }
289 /// ```
290 ///
291 /// Use instead:
292 /// ```ignore
293 /// for val in &mut iter {
294 /// ..
295 /// }
296 /// ```
297 #[clippy::version = "pre 1.29.0"]
298 pub WHILE_LET_ON_ITERATOR,
299 style,
300 "using a `while let` loop instead of a for loop on an iterator"
301 }
302
303 declare_clippy_lint! {
304 /// ### What it does
305 /// Checks for iterating a map (`HashMap` or `BTreeMap`) and
306 /// ignoring either the keys or values.
307 ///
308 /// ### Why is this bad?
309 /// Readability. There are `keys` and `values` methods that
310 /// can be used to express that don't need the values or keys.
311 ///
312 /// ### Example
313 /// ```ignore
314 /// for (k, _) in &map {
315 /// ..
316 /// }
317 /// ```
318 ///
319 /// could be replaced by
320 ///
321 /// ```ignore
322 /// for k in map.keys() {
323 /// ..
324 /// }
325 /// ```
326 #[clippy::version = "pre 1.29.0"]
327 pub FOR_KV_MAP,
328 style,
329 "looping on a map using `iter` when `keys` or `values` would do"
330 }
331
332 declare_clippy_lint! {
333 /// ### What it does
334 /// Checks for loops that will always `break`, `return` or
335 /// `continue` an outer loop.
336 ///
337 /// ### Why is this bad?
338 /// This loop never loops, all it does is obfuscating the
339 /// code.
340 ///
341 /// ### Example
342 /// ```rust
343 /// loop {
344 /// ..;
345 /// break;
346 /// }
347 /// ```
348 #[clippy::version = "pre 1.29.0"]
349 pub NEVER_LOOP,
350 correctness,
351 "any loop that will always `break` or `return`"
352 }
353
354 declare_clippy_lint! {
355 /// ### What it does
356 /// Checks for loops which have a range bound that is a mutable variable
357 ///
358 /// ### Why is this bad?
359 /// One might think that modifying the mutable variable changes the loop bounds
360 ///
361 /// ### Known problems
362 /// False positive when mutation is followed by a `break`, but the `break` is not immediately
363 /// after the mutation:
364 ///
365 /// ```rust
366 /// let mut x = 5;
367 /// for _ in 0..x {
368 /// x += 1; // x is a range bound that is mutated
369 /// ..; // some other expression
370 /// break; // leaves the loop, so mutation is not an issue
371 /// }
372 /// ```
373 ///
374 /// False positive on nested loops ([#6072](https://github.com/rust-lang/rust-clippy/issues/6072))
375 ///
376 /// ### Example
377 /// ```rust
378 /// let mut foo = 42;
379 /// for i in 0..foo {
380 /// foo -= 1;
381 /// println!("{}", i); // prints numbers from 0 to 42, not 0 to 21
382 /// }
383 /// ```
384 #[clippy::version = "pre 1.29.0"]
385 pub MUT_RANGE_BOUND,
386 suspicious,
387 "for loop over a range where one of the bounds is a mutable variable"
388 }
389
390 declare_clippy_lint! {
391 /// ### What it does
392 /// Checks whether variables used within while loop condition
393 /// can be (and are) mutated in the body.
394 ///
395 /// ### Why is this bad?
396 /// If the condition is unchanged, entering the body of the loop
397 /// will lead to an infinite loop.
398 ///
399 /// ### Known problems
400 /// If the `while`-loop is in a closure, the check for mutation of the
401 /// condition variables in the body can cause false negatives. For example when only `Upvar` `a` is
402 /// in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.
403 ///
404 /// ### Example
405 /// ```rust
406 /// let i = 0;
407 /// while i > 10 {
408 /// println!("let me loop forever!");
409 /// }
410 /// ```
411 #[clippy::version = "pre 1.29.0"]
412 pub WHILE_IMMUTABLE_CONDITION,
413 correctness,
414 "variables used within while expression are not mutated in the body"
415 }
416
417 declare_clippy_lint! {
418 /// ### What it does
419 /// Checks whether a for loop is being used to push a constant
420 /// value into a Vec.
421 ///
422 /// ### Why is this bad?
423 /// This kind of operation can be expressed more succinctly with
424 /// `vec![item; SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also
425 /// have better performance.
426 ///
427 /// ### Example
428 /// ```rust
429 /// let item1 = 2;
430 /// let item2 = 3;
431 /// let mut vec: Vec<u8> = Vec::new();
432 /// for _ in 0..20 {
433 /// vec.push(item1);
434 /// }
435 /// for _ in 0..30 {
436 /// vec.push(item2);
437 /// }
438 /// ```
439 ///
440 /// Use instead:
441 /// ```rust
442 /// let item1 = 2;
443 /// let item2 = 3;
444 /// let mut vec: Vec<u8> = vec![item1; 20];
445 /// vec.resize(20 + 30, item2);
446 /// ```
447 #[clippy::version = "1.47.0"]
448 pub SAME_ITEM_PUSH,
449 style,
450 "the same item is pushed inside of a for loop"
451 }
452
453 declare_clippy_lint! {
454 /// ### What it does
455 /// Checks whether a for loop has a single element.
456 ///
457 /// ### Why is this bad?
458 /// There is no reason to have a loop of a
459 /// single element.
460 ///
461 /// ### Example
462 /// ```rust
463 /// let item1 = 2;
464 /// for item in &[item1] {
465 /// println!("{}", item);
466 /// }
467 /// ```
468 ///
469 /// Use instead:
470 /// ```rust
471 /// let item1 = 2;
472 /// let item = &item1;
473 /// println!("{}", item);
474 /// ```
475 #[clippy::version = "1.49.0"]
476 pub SINGLE_ELEMENT_LOOP,
477 complexity,
478 "there is no reason to have a single element loop"
479 }
480
481 declare_clippy_lint! {
482 /// ### What it does
483 /// Checks for unnecessary `if let` usage in a for loop
484 /// where only the `Some` or `Ok` variant of the iterator element is used.
485 ///
486 /// ### Why is this bad?
487 /// It is verbose and can be simplified
488 /// by first calling the `flatten` method on the `Iterator`.
489 ///
490 /// ### Example
491 ///
492 /// ```rust
493 /// let x = vec![Some(1), Some(2), Some(3)];
494 /// for n in x {
495 /// if let Some(n) = n {
496 /// println!("{}", n);
497 /// }
498 /// }
499 /// ```
500 /// Use instead:
501 /// ```rust
502 /// let x = vec![Some(1), Some(2), Some(3)];
503 /// for n in x.into_iter().flatten() {
504 /// println!("{}", n);
505 /// }
506 /// ```
507 #[clippy::version = "1.52.0"]
508 pub MANUAL_FLATTEN,
509 complexity,
510 "for loops over `Option`s or `Result`s with a single expression can be simplified"
511 }
512
513 declare_clippy_lint! {
514 /// ### What it does
515 /// Checks for empty spin loops
516 ///
517 /// ### Why is this bad?
518 /// The loop body should have something like `thread::park()` or at least
519 /// `std::hint::spin_loop()` to avoid needlessly burning cycles and conserve
520 /// energy. Perhaps even better use an actual lock, if possible.
521 ///
522 /// ### Known problems
523 /// This lint doesn't currently trigger on `while let` or
524 /// `loop { match .. { .. } }` loops, which would be considered idiomatic in
525 /// combination with e.g. `AtomicBool::compare_exchange_weak`.
526 ///
527 /// ### Example
528 ///
529 /// ```ignore
530 /// use core::sync::atomic::{AtomicBool, Ordering};
531 /// let b = AtomicBool::new(true);
532 /// // give a ref to `b` to another thread,wait for it to become false
533 /// while b.load(Ordering::Acquire) {};
534 /// ```
535 /// Use instead:
536 /// ```rust,no_run
537 ///# use core::sync::atomic::{AtomicBool, Ordering};
538 ///# let b = AtomicBool::new(true);
539 /// while b.load(Ordering::Acquire) {
540 /// std::hint::spin_loop()
541 /// }
542 /// ```
543 #[clippy::version = "1.61.0"]
544 pub MISSING_SPIN_LOOP,
545 perf,
546 "An empty busy waiting loop"
547 }
548
549 declare_clippy_lint! {
550 /// ### What it does
551 /// Checks for manual implementations of Iterator::find
552 ///
553 /// ### Why is this bad?
554 /// It doesn't affect performance, but using `find` is shorter and easier to read.
555 ///
556 /// ### Example
557 ///
558 /// ```rust
559 /// fn example(arr: Vec<i32>) -> Option<i32> {
560 /// for el in arr {
561 /// if el == 1 {
562 /// return Some(el);
563 /// }
564 /// }
565 /// None
566 /// }
567 /// ```
568 /// Use instead:
569 /// ```rust
570 /// fn example(arr: Vec<i32>) -> Option<i32> {
571 /// arr.into_iter().find(|&el| el == 1)
572 /// }
573 /// ```
574 #[clippy::version = "1.64.0"]
575 pub MANUAL_FIND,
576 complexity,
577 "manual implementation of `Iterator::find`"
578 }
579
580 declare_clippy_lint! {
581 /// ### What it does
582 /// Looks for loops that check for emptiness of a `Vec` in the condition and pop an element
583 /// in the body as a separate operation.
584 ///
585 /// ### Why is this bad?
586 /// Such loops can be written in a more idiomatic way by using a while-let loop and directly
587 /// pattern matching on the return value of `Vec::pop()`.
588 ///
589 /// ### Example
590 /// ```rust
591 /// let mut numbers = vec![1, 2, 3, 4, 5];
592 /// while !numbers.is_empty() {
593 /// let number = numbers.pop().unwrap();
594 /// // use `number`
595 /// }
596 /// ```
597 /// Use instead:
598 /// ```rust
599 /// let mut numbers = vec![1, 2, 3, 4, 5];
600 /// while let Some(number) = numbers.pop() {
601 /// // use `number`
602 /// }
603 /// ```
604 #[clippy::version = "1.71.0"]
605 pub MANUAL_WHILE_LET_SOME,
606 style,
607 "checking for emptiness of a `Vec` in the loop condition and popping an element in the body"
608 }
609
610 pub struct Loops {
611 msrv: Msrv,
612 }
613 impl Loops {
614 pub fn new(msrv: Msrv) -> Self {
615 Self { msrv }
616 }
617 }
618 impl_lint_pass!(Loops => [
619 MANUAL_MEMCPY,
620 MANUAL_FLATTEN,
621 NEEDLESS_RANGE_LOOP,
622 EXPLICIT_ITER_LOOP,
623 EXPLICIT_INTO_ITER_LOOP,
624 ITER_NEXT_LOOP,
625 WHILE_LET_LOOP,
626 EXPLICIT_COUNTER_LOOP,
627 EMPTY_LOOP,
628 WHILE_LET_ON_ITERATOR,
629 FOR_KV_MAP,
630 NEVER_LOOP,
631 MUT_RANGE_BOUND,
632 WHILE_IMMUTABLE_CONDITION,
633 SAME_ITEM_PUSH,
634 SINGLE_ELEMENT_LOOP,
635 MISSING_SPIN_LOOP,
636 MANUAL_FIND,
637 MANUAL_WHILE_LET_SOME
638 ]);
639
640 impl<'tcx> LateLintPass<'tcx> for Loops {
641 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
642 let for_loop = higher::ForLoop::hir(expr);
643 if let Some(higher::ForLoop {
644 pat,
645 arg,
646 body,
647 loop_id,
648 span,
649 }) = for_loop
650 {
651 // we don't want to check expanded macros
652 // this check is not at the top of the function
653 // since higher::for_loop expressions are marked as expansions
654 if body.span.from_expansion() {
655 return;
656 }
657 self.check_for_loop(cx, pat, arg, body, expr, span);
658 if let ExprKind::Block(block, _) = body.kind {
659 never_loop::check(cx, block, loop_id, span, for_loop.as_ref());
660 }
661 }
662
663 // we don't want to check expanded macros
664 if expr.span.from_expansion() {
665 return;
666 }
667
668 // check for never_loop
669 if let ExprKind::Loop(block, ..) = expr.kind {
670 never_loop::check(cx, block, expr.hir_id, expr.span, None);
671 }
672
673 // check for `loop { if let {} else break }` that could be `while let`
674 // (also matches an explicit "match" instead of "if let")
675 // (even if the "match" or "if let" is used for declaration)
676 if let ExprKind::Loop(block, _, LoopSource::Loop, _) = expr.kind {
677 // also check for empty `loop {}` statements, skipping those in #[panic_handler]
678 empty_loop::check(cx, expr, block);
679 while_let_loop::check(cx, expr, block);
680 }
681
682 while_let_on_iterator::check(cx, expr);
683
684 if let Some(higher::While { condition, body, span }) = higher::While::hir(expr) {
685 while_immutable_condition::check(cx, condition, body);
686 missing_spin_loop::check(cx, condition, body);
687 manual_while_let_some::check(cx, condition, body, span);
688 }
689 }
690
691 extract_msrv_attr!(LateContext);
692 }
693
694 impl Loops {
695 fn check_for_loop<'tcx>(
696 &self,
697 cx: &LateContext<'tcx>,
698 pat: &'tcx Pat<'_>,
699 arg: &'tcx Expr<'_>,
700 body: &'tcx Expr<'_>,
701 expr: &'tcx Expr<'_>,
702 span: Span,
703 ) {
704 let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
705 if !is_manual_memcpy_triggered {
706 needless_range_loop::check(cx, pat, arg, body, expr);
707 explicit_counter_loop::check(cx, pat, arg, body, expr);
708 }
709 self.check_for_loop_arg(cx, pat, arg);
710 for_kv_map::check(cx, pat, arg, body);
711 mut_range_bound::check(cx, arg, body);
712 single_element_loop::check(cx, pat, arg, body, expr);
713 same_item_push::check(cx, pat, arg, body, expr);
714 manual_flatten::check(cx, pat, arg, body, span);
715 manual_find::check(cx, pat, arg, body, span, expr);
716 }
717
718 fn check_for_loop_arg(&self, cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) {
719 if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
720 match method.ident.as_str() {
721 "iter" | "iter_mut" => {
722 explicit_iter_loop::check(cx, self_arg, arg, &self.msrv);
723 },
724 "into_iter" => {
725 explicit_into_iter_loop::check(cx, self_arg, arg);
726 },
727 "next" => {
728 iter_next_loop::check(cx, arg);
729 },
730 _ => {},
731 }
732 }
733 }
734 }