2 mod explicit_counter_loop;
3 mod explicit_into_iter_loop;
4 mod explicit_iter_loop;
6 mod for_loops_over_fallibles;
11 mod missing_spin_loop;
14 mod needless_range_loop;
17 mod single_element_loop;
19 mod while_immutable_condition;
21 mod while_let_on_iterator;
23 use clippy_utils::higher;
24 use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
25 use rustc_lint::{LateContext, LateLintPass};
26 use rustc_session::{declare_lint_pass, declare_tool_lint};
27 use rustc_span::source_map::Span;
28 use utils::{make_iterator_snippet, IncrementVisitor, InitializeVisitor};
30 declare_clippy_lint! {
32 /// Checks for for-loops that manually copy items between
33 /// slices that could be optimized by having a memcpy.
35 /// ### Why is this bad?
36 /// It is not as fast as a memcpy.
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];
49 /// # let src = vec![1];
50 /// # let mut dst = vec![0; 65];
51 /// dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
53 #[clippy::version = "pre 1.29.0"]
56 "manually copying items between slices"
59 declare_clippy_lint! {
61 /// Checks for looping over the range of `0..len` of some
62 /// collection just to get the values by index.
64 /// ### Why is this bad?
65 /// Just iterating the collection itself makes the intent
66 /// more clear and is probably faster.
70 /// let vec = vec!['a', 'b', 'c'];
71 /// for i in 0..vec.len() {
72 /// println!("{}", vec[i]);
78 /// let vec = vec!['a', 'b', 'c'];
80 /// println!("{}", i);
83 #[clippy::version = "pre 1.29.0"]
84 pub NEEDLESS_RANGE_LOOP,
86 "for-looping over a range of indices where an iterator over items would do"
89 declare_clippy_lint! {
91 /// Checks for loops on `x.iter()` where `&x` will do, and
92 /// suggests the latter.
94 /// ### Why is this bad?
97 /// ### Known problems
98 /// False negatives. We currently only warn on some known
103 /// // with `y` a `Vec` or slice:
104 /// # let y = vec![1];
105 /// for x in y.iter() {
112 /// # let y = vec![1];
117 #[clippy::version = "pre 1.29.0"]
118 pub EXPLICIT_ITER_LOOP,
120 "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
123 declare_clippy_lint! {
125 /// Checks for loops on `y.into_iter()` where `y` will do, and
126 /// suggests the latter.
128 /// ### Why is this bad?
133 /// # let y = vec![1];
134 /// // with `y` a `Vec` or slice:
135 /// for x in y.into_iter() {
139 /// can be rewritten to
141 /// # let y = vec![1];
146 #[clippy::version = "pre 1.29.0"]
147 pub EXPLICIT_INTO_ITER_LOOP,
149 "for-looping over `_.into_iter()` when `_` would do"
152 declare_clippy_lint! {
154 /// Checks for loops on `x.next()`.
156 /// ### Why is this bad?
157 /// `next()` returns either `Some(value)` if there was a
158 /// value, or `None` otherwise. The insidious thing is that `Option<_>`
159 /// implements `IntoIterator`, so that possibly one value will be iterated,
160 /// leading to some hard to find bugs. No one will want to write such code
161 /// [except to win an Underhanded Rust
162 /// Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).
166 /// for x in y.next() {
170 #[clippy::version = "pre 1.29.0"]
173 "for-looping over `_.next()` which is probably not intended"
176 declare_clippy_lint! {
178 /// Checks for `for` loops over `Option` or `Result` values.
180 /// ### Why is this bad?
181 /// Readability. This is more clearly expressed as an `if
186 /// # let opt = Some(1);
187 /// # let res: Result<i32, std::io::Error> = Ok(1);
196 /// for x in res.iter() {
203 /// # let opt = Some(1);
204 /// # let res: Result<i32, std::io::Error> = Ok(1);
205 /// if let Some(x) = opt {
209 /// if let Ok(x) = res {
213 #[clippy::version = "1.45.0"]
214 pub FOR_LOOPS_OVER_FALLIBLES,
216 "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"
219 declare_clippy_lint! {
221 /// Detects `loop + match` combinations that are easier
222 /// written as a `while let` loop.
224 /// ### Why is this bad?
225 /// The `while let` loop is usually shorter and more
228 /// ### Known problems
229 /// Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).
233 /// # let y = Some(1);
235 /// let x = match y {
239 /// // .. do something with x
241 /// // is easier written as
242 /// while let Some(x) = y {
243 /// // .. do something with x
246 #[clippy::version = "pre 1.29.0"]
249 "`loop { if let { ... } else break }`, which can be written as a `while let` loop"
252 declare_clippy_lint! {
254 /// Checks for functions collecting an iterator when collect
257 /// ### Why is this bad?
258 /// `collect` causes the allocation of a new data structure,
259 /// when this allocation may not be needed.
263 /// # let iterator = vec![1].into_iter();
264 /// let len = iterator.clone().collect::<Vec<_>>().len();
266 /// let len = iterator.count();
268 #[clippy::version = "1.30.0"]
269 pub NEEDLESS_COLLECT,
271 "collecting an iterator when collect is not needed"
274 declare_clippy_lint! {
276 /// Checks `for` loops over slices with an explicit counter
277 /// and suggests the use of `.enumerate()`.
279 /// ### Why is this bad?
280 /// Using `.enumerate()` makes the intent more clear,
281 /// declutters the code and may be faster in some instances.
285 /// # let v = vec![1];
286 /// # fn bar(bar: usize, baz: usize) {}
296 /// # let v = vec![1];
297 /// # fn bar(bar: usize, baz: usize) {}
298 /// for (i, item) in v.iter().enumerate() { bar(i, *item); }
300 #[clippy::version = "pre 1.29.0"]
301 pub EXPLICIT_COUNTER_LOOP,
303 "for-looping with an explicit counter when `_.enumerate()` would do"
306 declare_clippy_lint! {
308 /// Checks for empty `loop` expressions.
310 /// ### Why is this bad?
311 /// These busy loops burn CPU cycles without doing
312 /// anything. It is _almost always_ a better idea to `panic!` than to have
315 /// If panicking isn't possible, think of the environment and either:
316 /// - block on something
317 /// - sleep the thread for some microseconds
318 /// - yield or pause the thread
320 /// For `std` targets, this can be done with
321 /// [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)
322 /// or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).
324 /// For `no_std` targets, doing this is more complicated, especially because
325 /// `#[panic_handler]`s can't panic. To stop/pause the thread, you will
326 /// probably need to invoke some target-specific intrinsic. Examples include:
327 /// - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)
328 /// - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)
334 #[clippy::version = "pre 1.29.0"]
337 "empty `loop {}`, which should block or sleep"
340 declare_clippy_lint! {
342 /// Checks for `while let` expressions on iterators.
344 /// ### Why is this bad?
345 /// Readability. A simple `for` loop is shorter and conveys
346 /// the intent better.
350 /// while let Some(val) = iter.next() {
357 /// for val in &mut iter {
361 #[clippy::version = "pre 1.29.0"]
362 pub WHILE_LET_ON_ITERATOR,
364 "using a `while let` loop instead of a for loop on an iterator"
367 declare_clippy_lint! {
369 /// Checks for iterating a map (`HashMap` or `BTreeMap`) and
370 /// ignoring either the keys or values.
372 /// ### Why is this bad?
373 /// Readability. There are `keys` and `values` methods that
374 /// can be used to express that don't need the values or keys.
378 /// for (k, _) in &map {
383 /// could be replaced by
386 /// for k in map.keys() {
390 #[clippy::version = "pre 1.29.0"]
393 "looping on a map using `iter` when `keys` or `values` would do"
396 declare_clippy_lint! {
398 /// Checks for loops that will always `break`, `return` or
399 /// `continue` an outer loop.
401 /// ### Why is this bad?
402 /// This loop never loops, all it does is obfuscating the
412 #[clippy::version = "pre 1.29.0"]
415 "any loop that will always `break` or `return`"
418 declare_clippy_lint! {
420 /// Checks for loops which have a range bound that is a mutable variable
422 /// ### Why is this bad?
423 /// One might think that modifying the mutable variable changes the loop bounds
425 /// ### Known problems
426 /// False positive when mutation is followed by a `break`, but the `break` is not immediately
427 /// after the mutation:
432 /// x += 1; // x is a range bound that is mutated
433 /// ..; // some other expression
434 /// break; // leaves the loop, so mutation is not an issue
438 /// False positive on nested loops ([#6072](https://github.com/rust-lang/rust-clippy/issues/6072))
442 /// let mut foo = 42;
443 /// for i in 0..foo {
445 /// println!("{}", i); // prints numbers from 0 to 42, not 0 to 21
448 #[clippy::version = "pre 1.29.0"]
451 "for loop over a range where one of the bounds is a mutable variable"
454 declare_clippy_lint! {
456 /// Checks whether variables used within while loop condition
457 /// can be (and are) mutated in the body.
459 /// ### Why is this bad?
460 /// If the condition is unchanged, entering the body of the loop
461 /// will lead to an infinite loop.
463 /// ### Known problems
464 /// If the `while`-loop is in a closure, the check for mutation of the
465 /// condition variables in the body can cause false negatives. For example when only `Upvar` `a` is
466 /// in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.
472 /// println!("let me loop forever!");
475 #[clippy::version = "pre 1.29.0"]
476 pub WHILE_IMMUTABLE_CONDITION,
478 "variables used within while expression are not mutated in the body"
481 declare_clippy_lint! {
483 /// Checks whether a for loop is being used to push a constant
484 /// value into a Vec.
486 /// ### Why is this bad?
487 /// This kind of operation can be expressed more succinctly with
488 /// `vec![item; SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also
489 /// have better performance.
495 /// let mut vec: Vec<u8> = Vec::new();
508 /// let mut vec: Vec<u8> = vec![item1; 20];
509 /// vec.resize(20 + 30, item2);
511 #[clippy::version = "1.47.0"]
514 "the same item is pushed inside of a for loop"
517 declare_clippy_lint! {
519 /// Checks whether a for loop has a single element.
521 /// ### Why is this bad?
522 /// There is no reason to have a loop of a
528 /// for item in &[item1] {
529 /// println!("{}", item);
536 /// let item = &item1;
537 /// println!("{}", item);
539 #[clippy::version = "1.49.0"]
540 pub SINGLE_ELEMENT_LOOP,
542 "there is no reason to have a single element loop"
545 declare_clippy_lint! {
547 /// Check for unnecessary `if let` usage in a for loop
548 /// where only the `Some` or `Ok` variant of the iterator element is used.
550 /// ### Why is this bad?
551 /// It is verbose and can be simplified
552 /// by first calling the `flatten` method on the `Iterator`.
557 /// let x = vec![Some(1), Some(2), Some(3)];
559 /// if let Some(n) = n {
560 /// println!("{}", n);
566 /// let x = vec![Some(1), Some(2), Some(3)];
567 /// for n in x.into_iter().flatten() {
568 /// println!("{}", n);
571 #[clippy::version = "1.52.0"]
574 "for loops over `Option`s or `Result`s with a single expression can be simplified"
577 declare_clippy_lint! {
579 /// Check for empty spin loops
581 /// ### Why is this bad?
582 /// The loop body should have something like `thread::park()` or at least
583 /// `std::hint::spin_loop()` to avoid needlessly burning cycles and conserve
584 /// energy. Perhaps even better use an actual lock, if possible.
586 /// ### Known problems
587 /// This lint doesn't currently trigger on `while let` or
588 /// `loop { match .. { .. } }` loops, which would be considered idiomatic in
589 /// combination with e.g. `AtomicBool::compare_exchange_weak`.
594 /// use core::sync::atomic::{AtomicBool, Ordering};
595 /// let b = AtomicBool::new(true);
596 /// // give a ref to `b` to another thread,wait for it to become false
597 /// while b.load(Ordering::Acquire) {};
601 ///# use core::sync::atomic::{AtomicBool, Ordering};
602 ///# let b = AtomicBool::new(true);
603 /// while b.load(Ordering::Acquire) {
604 /// std::hint::spin_loop()
607 #[clippy::version = "1.61.0"]
608 pub MISSING_SPIN_LOOP,
610 "An empty busy waiting loop"
613 declare_clippy_lint! {
615 /// Check for manual implementations of Iterator::find
617 /// ### Why is this bad?
618 /// It doesn't affect performance, but using `find` is shorter and easier to read.
623 /// fn example(arr: Vec<i32>) -> Option<i32> {
634 /// fn example(arr: Vec<i32>) -> Option<i32> {
635 /// arr.into_iter().find(|&el| el == 1)
638 #[clippy::version = "1.61.0"]
641 "manual implementation of `Iterator::find`"
644 declare_lint_pass!(Loops => [
649 EXPLICIT_INTO_ITER_LOOP,
651 FOR_LOOPS_OVER_FALLIBLES,
654 EXPLICIT_COUNTER_LOOP,
656 WHILE_LET_ON_ITERATOR,
660 WHILE_IMMUTABLE_CONDITION,
667 impl<'tcx> LateLintPass<'tcx> for Loops {
668 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
669 let for_loop = higher::ForLoop::hir(expr);
670 if let Some(higher::ForLoop {
678 // we don't want to check expanded macros
679 // this check is not at the top of the function
680 // since higher::for_loop expressions are marked as expansions
681 if body.span.from_expansion() {
684 check_for_loop(cx, pat, arg, body, expr, span);
685 if let ExprKind::Block(block, _) = body.kind {
686 never_loop::check(cx, block, loop_id, span, for_loop.as_ref());
690 // we don't want to check expanded macros
691 if expr.span.from_expansion() {
695 // check for never_loop
696 if let ExprKind::Loop(block, ..) = expr.kind {
697 never_loop::check(cx, block, expr.hir_id, expr.span, None);
700 // check for `loop { if let {} else break }` that could be `while let`
701 // (also matches an explicit "match" instead of "if let")
702 // (even if the "match" or "if let" is used for declaration)
703 if let ExprKind::Loop(block, _, LoopSource::Loop, _) = expr.kind {
704 // also check for empty `loop {}` statements, skipping those in #[panic_handler]
705 empty_loop::check(cx, expr, block);
706 while_let_loop::check(cx, expr, block);
709 while_let_on_iterator::check(cx, expr);
711 if let Some(higher::While { condition, body }) = higher::While::hir(expr) {
712 while_immutable_condition::check(cx, condition, body);
713 missing_spin_loop::check(cx, condition, body);
716 needless_collect::check(expr, cx);
720 fn check_for_loop<'tcx>(
721 cx: &LateContext<'tcx>,
724 body: &'tcx Expr<'_>,
725 expr: &'tcx Expr<'_>,
728 let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
729 if !is_manual_memcpy_triggered {
730 needless_range_loop::check(cx, pat, arg, body, expr);
731 explicit_counter_loop::check(cx, pat, arg, body, expr);
733 check_for_loop_arg(cx, pat, arg);
734 for_kv_map::check(cx, pat, arg, body);
735 mut_range_bound::check(cx, arg, body);
736 single_element_loop::check(cx, pat, arg, body, expr);
737 same_item_push::check(cx, pat, arg, body, expr);
738 manual_flatten::check(cx, pat, arg, body, span);
739 manual_find::check(cx, pat, arg, body, span, expr);
742 fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
743 let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
745 if let ExprKind::MethodCall(method, [self_arg], _) = arg.kind {
746 let method_name = method.ident.as_str();
747 // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
749 "iter" | "iter_mut" => {
750 explicit_iter_loop::check(cx, self_arg, arg, method_name);
751 for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name));
754 explicit_iter_loop::check(cx, self_arg, arg, method_name);
755 explicit_into_iter_loop::check(cx, self_arg, arg);
756 for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name));
759 next_loop_linted = iter_next_loop::check(cx, arg);
765 if !next_loop_linted {
766 for_loops_over_fallibles::check(cx, pat, arg, None);