2 mod explicit_counter_loop
;
3 mod explicit_into_iter_loop
;
4 mod explicit_iter_loop
;
10 mod manual_while_let_some
;
11 mod missing_spin_loop
;
13 mod needless_range_loop
;
16 mod single_element_loop
;
18 mod while_immutable_condition
;
20 mod while_let_on_iterator
;
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}
;
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 because it eliminates
67 /// the bounds check that is done when indexing.
71 /// let vec = vec!['a', 'b', 'c'];
72 /// for i in 0..vec.len() {
73 /// println!("{}", vec[i]);
79 /// let vec = vec!['a', 'b', 'c'];
81 /// println!("{}", i);
84 #[clippy::version = "pre 1.29.0"]
85 pub NEEDLESS_RANGE_LOOP
,
87 "for-looping over a range of indices where an iterator over items would do"
90 declare_clippy_lint
! {
92 /// Checks for loops on `x.iter()` where `&x` will do, and
93 /// suggests the latter.
95 /// ### Why is this bad?
98 /// ### Known problems
99 /// False negatives. We currently only warn on some known
104 /// // with `y` a `Vec` or slice:
105 /// # let y = vec![1];
106 /// for x in y.iter() {
113 /// # let y = vec![1];
118 #[clippy::version = "pre 1.29.0"]
119 pub EXPLICIT_ITER_LOOP
,
121 "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
124 declare_clippy_lint
! {
126 /// Checks for loops on `y.into_iter()` where `y` will do, and
127 /// suggests the latter.
129 /// ### Why is this bad?
134 /// # let y = vec![1];
135 /// // with `y` a `Vec` or slice:
136 /// for x in y.into_iter() {
140 /// can be rewritten to
142 /// # let y = vec![1];
147 #[clippy::version = "pre 1.29.0"]
148 pub EXPLICIT_INTO_ITER_LOOP
,
150 "for-looping over `_.into_iter()` when `_` would do"
153 declare_clippy_lint
! {
155 /// Checks for loops on `x.next()`.
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).
167 /// for x in y.next() {
171 #[clippy::version = "pre 1.29.0"]
174 "for-looping over `_.next()` which is probably not intended"
177 declare_clippy_lint
! {
179 /// Detects `loop + match` combinations that are easier
180 /// written as a `while let` loop.
182 /// ### Why is this bad?
183 /// The `while let` loop is usually shorter and more
186 /// ### Known problems
187 /// Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).
191 /// # let y = Some(1);
193 /// let x = match y {
197 /// // .. do something with x
199 /// // is easier written as
200 /// while let Some(x) = y {
201 /// // .. do something with x
204 #[clippy::version = "pre 1.29.0"]
207 "`loop { if let { ... } else break }`, which can be written as a `while let` loop"
210 declare_clippy_lint
! {
212 /// Checks `for` loops over slices with an explicit counter
213 /// and suggests the use of `.enumerate()`.
215 /// ### Why is this bad?
216 /// Using `.enumerate()` makes the intent more clear,
217 /// declutters the code and may be faster in some instances.
221 /// # let v = vec![1];
222 /// # fn bar(bar: usize, baz: usize) {}
232 /// # let v = vec![1];
233 /// # fn bar(bar: usize, baz: usize) {}
234 /// for (i, item) in v.iter().enumerate() { bar(i, *item); }
236 #[clippy::version = "pre 1.29.0"]
237 pub EXPLICIT_COUNTER_LOOP
,
239 "for-looping with an explicit counter when `_.enumerate()` would do"
242 declare_clippy_lint
! {
244 /// Checks for empty `loop` expressions.
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
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
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).
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)
270 #[clippy::version = "pre 1.29.0"]
273 "empty `loop {}`, which should block or sleep"
276 declare_clippy_lint
! {
278 /// Checks for `while let` expressions on iterators.
280 /// ### Why is this bad?
281 /// Readability. A simple `for` loop is shorter and conveys
282 /// the intent better.
286 /// while let Some(val) = iter.next() {
293 /// for val in &mut iter {
297 #[clippy::version = "pre 1.29.0"]
298 pub WHILE_LET_ON_ITERATOR
,
300 "using a `while let` loop instead of a for loop on an iterator"
303 declare_clippy_lint
! {
305 /// Checks for iterating a map (`HashMap` or `BTreeMap`) and
306 /// ignoring either the keys or values.
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.
314 /// for (k, _) in &map {
319 /// could be replaced by
322 /// for k in map.keys() {
326 #[clippy::version = "pre 1.29.0"]
329 "looping on a map using `iter` when `keys` or `values` would do"
332 declare_clippy_lint
! {
334 /// Checks for loops that will always `break`, `return` or
335 /// `continue` an outer loop.
337 /// ### Why is this bad?
338 /// This loop never loops, all it does is obfuscating the
348 #[clippy::version = "pre 1.29.0"]
351 "any loop that will always `break` or `return`"
354 declare_clippy_lint
! {
356 /// Checks for loops which have a range bound that is a mutable variable
358 /// ### Why is this bad?
359 /// One might think that modifying the mutable variable changes the loop bounds
361 /// ### Known problems
362 /// False positive when mutation is followed by a `break`, but the `break` is not immediately
363 /// after the mutation:
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
374 /// False positive on nested loops ([#6072](https://github.com/rust-lang/rust-clippy/issues/6072))
378 /// let mut foo = 42;
379 /// for i in 0..foo {
381 /// println!("{}", i); // prints numbers from 0 to 42, not 0 to 21
384 #[clippy::version = "pre 1.29.0"]
387 "for loop over a range where one of the bounds is a mutable variable"
390 declare_clippy_lint
! {
392 /// Checks whether variables used within while loop condition
393 /// can be (and are) mutated in the body.
395 /// ### Why is this bad?
396 /// If the condition is unchanged, entering the body of the loop
397 /// will lead to an infinite loop.
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.
408 /// println!("let me loop forever!");
411 #[clippy::version = "pre 1.29.0"]
412 pub WHILE_IMMUTABLE_CONDITION
,
414 "variables used within while expression are not mutated in the body"
417 declare_clippy_lint
! {
419 /// Checks whether a for loop is being used to push a constant
420 /// value into a Vec.
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.
431 /// let mut vec: Vec<u8> = Vec::new();
444 /// let mut vec: Vec<u8> = vec![item1; 20];
445 /// vec.resize(20 + 30, item2);
447 #[clippy::version = "1.47.0"]
450 "the same item is pushed inside of a for loop"
453 declare_clippy_lint
! {
455 /// Checks whether a for loop has a single element.
457 /// ### Why is this bad?
458 /// There is no reason to have a loop of a
464 /// for item in &[item1] {
465 /// println!("{}", item);
472 /// let item = &item1;
473 /// println!("{}", item);
475 #[clippy::version = "1.49.0"]
476 pub SINGLE_ELEMENT_LOOP
,
478 "there is no reason to have a single element loop"
481 declare_clippy_lint
! {
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.
486 /// ### Why is this bad?
487 /// It is verbose and can be simplified
488 /// by first calling the `flatten` method on the `Iterator`.
493 /// let x = vec![Some(1), Some(2), Some(3)];
495 /// if let Some(n) = n {
496 /// println!("{}", n);
502 /// let x = vec![Some(1), Some(2), Some(3)];
503 /// for n in x.into_iter().flatten() {
504 /// println!("{}", n);
507 #[clippy::version = "1.52.0"]
510 "for loops over `Option`s or `Result`s with a single expression can be simplified"
513 declare_clippy_lint
! {
515 /// Checks for empty spin loops
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.
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`.
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) {};
537 ///# use core::sync::atomic::{AtomicBool, Ordering};
538 ///# let b = AtomicBool::new(true);
539 /// while b.load(Ordering::Acquire) {
540 /// std::hint::spin_loop()
543 #[clippy::version = "1.61.0"]
544 pub MISSING_SPIN_LOOP
,
546 "An empty busy waiting loop"
549 declare_clippy_lint
! {
551 /// Checks for manual implementations of Iterator::find
553 /// ### Why is this bad?
554 /// It doesn't affect performance, but using `find` is shorter and easier to read.
559 /// fn example(arr: Vec<i32>) -> Option<i32> {
570 /// fn example(arr: Vec<i32>) -> Option<i32> {
571 /// arr.into_iter().find(|&el| el == 1)
574 #[clippy::version = "1.64.0"]
577 "manual implementation of `Iterator::find`"
580 declare_clippy_lint
! {
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.
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()`.
591 /// let mut numbers = vec![1, 2, 3, 4, 5];
592 /// while !numbers.is_empty() {
593 /// let number = numbers.pop().unwrap();
599 /// let mut numbers = vec![1, 2, 3, 4, 5];
600 /// while let Some(number) = numbers.pop() {
604 #[clippy::version = "1.71.0"]
605 pub MANUAL_WHILE_LET_SOME
,
607 "checking for emptiness of a `Vec` in the loop condition and popping an element in the body"
614 pub fn new(msrv
: Msrv
) -> Self {
618 impl_lint_pass
!(Loops
=> [
623 EXPLICIT_INTO_ITER_LOOP
,
626 EXPLICIT_COUNTER_LOOP
,
628 WHILE_LET_ON_ITERATOR
,
632 WHILE_IMMUTABLE_CONDITION
,
637 MANUAL_WHILE_LET_SOME
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
{
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() {
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());
663 // we don't want to check expanded macros
664 if expr
.span
.from_expansion() {
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
);
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
);
682 while_let_on_iterator
::check(cx
, expr
);
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
);
691 extract_msrv_attr
!(LateContext
);
695 fn check_for_loop
<'tcx
>(
697 cx
: &LateContext
<'tcx
>,
700 body
: &'tcx Expr
<'_
>,
701 expr
: &'tcx Expr
<'_
>,
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
);
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
);
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
);
725 explicit_into_iter_loop
::check(cx
, self_arg
, arg
);
728 iter_next_loop
::check(cx
, arg
);