From daad0fb3566048becf39b466a87bdbe2fd300960 Mon Sep 17 00:00:00 2001 From: zayutaha Date: Tue, 3 Mar 2026 18:08:44 +0530 Subject: [PATCH] fix(explicit_counter_loop): suggest `.take(n)` for `for _ in 0..n` counter loops fix(explicit_counter_loop): suggest `.take(n)` for `for _ in 0..n` counter loops --- .../src/loops/explicit_counter_loop.rs | 21 +++++++++++++ tests/ui/explicit_counter_loop.rs | 31 +++++++++++++++++++ tests/ui/explicit_counter_loop.stderr | 26 +++++++++++++++- 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index 9bfc3aa56648..b813a18b221e 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -2,6 +2,7 @@ use super::{EXPLICIT_COUNTER_LOOP, IncrementVisitor, InitializeVisitor, make_iterator_snippet}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher::Range; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::{EMPTY, Sugg}; use clippy_utils::{get_enclosing_block, is_integer_const, is_integer_literal_untyped}; @@ -83,6 +84,26 @@ pub(super) fn check<'tcx>( snippet }); + // If the loop variable is unused and the range is `0..n`, suggest `(init..).take(n)`. + if pat_snippet == "_" + && let Some(range) = Range::hir(cx, arg) + && range.limits == RangeLimits::HalfOpen + && range.start.is_some_and(|start| is_integer_const(cx, start, 0)) + && let Some(end) = range.end + { + let end = snippet_with_applicability(cx, end.span, "..", &mut applicability); + diag.span_suggestion( + span, + "consider using", + format!( + "{loop_label}for {name} in ({}).take({end})", + initializer.range(&EMPTY, RangeLimits::HalfOpen) + ), + applicability, + ); + return; + } + diag.span_suggestion( span, "consider using", diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index 79968700f8e4..a8145d16c149 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -332,3 +332,34 @@ fn add_assign(&mut self, rhs: u8) { priority += 1; } } + +pub fn issue_16642() { + let mut base = 100; + const MAX: usize = 10; + for _ in 0..MAX { + //~^ explicit_counter_loop + base += 1; + } + + let mut base = 100; + + let nums = vec![1, 2, 3, 4]; + for _ in nums { + //~^ explicit_counter_loop + base += 1; + } + + // inclusive range: should not suggest .take() + let mut base = 100; + for _ in 0..=MAX { + //~^ explicit_counter_loop + base += 1; + } + + // non-zero start: should not suggest .take(), falls through to zip + let mut base = 100; + for _ in 5..MAX { + //~^ explicit_counter_loop + base += 1; + } +} diff --git a/tests/ui/explicit_counter_loop.stderr b/tests/ui/explicit_counter_loop.stderr index 7a83df05ec0a..eb6be74c8805 100644 --- a/tests/ui/explicit_counter_loop.stderr +++ b/tests/ui/explicit_counter_loop.stderr @@ -81,5 +81,29 @@ error: the variable `j` is used as a loop counter LL | for item in &v { | ^^^^^^^^^^^^^^ help: consider using: `for (j, item) in (s + 1..).zip(v.iter())` -error: aborting due to 13 previous errors +error: the variable `base` is used as a loop counter + --> tests/ui/explicit_counter_loop.rs:339:5 + | +LL | for _ in 0..MAX { + | ^^^^^^^^^^^^^^^ help: consider using: `for base in (100..).take(MAX)` + +error: the variable `base` is used as a loop counter + --> tests/ui/explicit_counter_loop.rs:347:5 + | +LL | for _ in nums { + | ^^^^^^^^^^^^^ help: consider using: `for (base, _) in (100..).zip(nums.into_iter())` + +error: the variable `base` is used as a loop counter + --> tests/ui/explicit_counter_loop.rs:354:5 + | +LL | for _ in 0..=MAX { + | ^^^^^^^^^^^^^^^^ help: consider using: `for (base, _) in (100..).zip((0..=MAX))` + +error: the variable `base` is used as a loop counter + --> tests/ui/explicit_counter_loop.rs:361:5 + | +LL | for _ in 5..MAX { + | ^^^^^^^^^^^^^^^ help: consider using: `for (base, _) in (100..).zip((5..MAX))` + +error: aborting due to 17 previous errors