Skip to content

Commit 06454c8

Browse files
committed
feat: unused_enumerate_index lint
1 parent 929a288 commit 06454c8

File tree

7 files changed

+144
-1
lines changed

7 files changed

+144
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5559,6 +5559,7 @@ Released 2018-09-13
55595559
[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
55605560
[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async
55615561
[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
5562+
[`unused_enumerate_index`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_enumerate_index
55625563
[`unused_format_specs`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_format_specs
55635564
[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
55645565
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
271271
crate::loops::NEVER_LOOP_INFO,
272272
crate::loops::SAME_ITEM_PUSH_INFO,
273273
crate::loops::SINGLE_ELEMENT_LOOP_INFO,
274+
crate::loops::UNUSED_ENUMERATE_INDEX_INFO,
274275
crate::loops::WHILE_IMMUTABLE_CONDITION_INFO,
275276
crate::loops::WHILE_LET_LOOP_INFO,
276277
crate::loops::WHILE_LET_ON_ITERATOR_INFO,

clippy_lints/src/loops/mod.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod needless_range_loop;
1414
mod never_loop;
1515
mod same_item_push;
1616
mod single_element_loop;
17+
mod unused_enumerate_index;
1718
mod utils;
1819
mod while_immutable_condition;
1920
mod while_let_loop;
@@ -577,6 +578,33 @@ declare_clippy_lint! {
577578
"manual implementation of `Iterator::find`"
578579
}
579580

581+
declare_clippy_lint! {
582+
/// ### What it does
583+
/// Checks for `for (_, v) in a.iter().enumerate()`
584+
///
585+
/// ### Why is this bad?
586+
/// The index from `.enumerate()` is immediately dropped.
587+
///
588+
/// ### Example
589+
/// ```rust
590+
/// let v = vec![1, 2, 3, 4];
591+
/// for (_, x) in v.iter().enumerate() {
592+
/// print!("{x}")
593+
/// }
594+
/// ```
595+
/// Use instead:
596+
/// ```rust
597+
/// let v = vec![1, 2, 3, 4];
598+
/// for x in v.iter() {
599+
/// print!("{x}")
600+
/// }
601+
/// ```
602+
#[clippy::version = "1.69.0"]
603+
pub UNUSED_ENUMERATE_INDEX,
604+
style,
605+
"using .enumerate() and immediately dropping the index"
606+
}
607+
580608
declare_clippy_lint! {
581609
/// ### What it does
582610
/// Looks for loops that check for emptiness of a `Vec` in the condition and pop an element
@@ -619,6 +647,7 @@ impl Loops {
619647
}
620648
}
621649
}
650+
622651
impl_lint_pass!(Loops => [
623652
MANUAL_MEMCPY,
624653
MANUAL_FLATTEN,
@@ -638,7 +667,8 @@ impl_lint_pass!(Loops => [
638667
SINGLE_ELEMENT_LOOP,
639668
MISSING_SPIN_LOOP,
640669
MANUAL_FIND,
641-
MANUAL_WHILE_LET_SOME
670+
MANUAL_WHILE_LET_SOME,
671+
UNUSED_ENUMERATE_INDEX,
642672
]);
643673

644674
impl<'tcx> LateLintPass<'tcx> for Loops {
@@ -717,6 +747,7 @@ impl Loops {
717747
same_item_push::check(cx, pat, arg, body, expr);
718748
manual_flatten::check(cx, pat, arg, body, span);
719749
manual_find::check(cx, pat, arg, body, span, expr);
750+
unused_enumerate_index::check(cx, pat, arg, body);
720751
}
721752

722753
fn check_for_loop_arg(&self, cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) {
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use super::UNUSED_ENUMERATE_INDEX;
2+
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
3+
use clippy_utils::source::snippet;
4+
use clippy_utils::sugg;
5+
use clippy_utils::visitors::is_local_used;
6+
use rustc_hir::{Expr, ExprKind, Pat, PatKind};
7+
use rustc_lint::LateContext;
8+
use rustc_middle::ty;
9+
10+
/// Checks for the `UNUSED_ENUMERATE_INDEX` lint.
11+
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
12+
let pat_span = pat.span;
13+
14+
let PatKind::Tuple(pat, _) = pat.kind else {
15+
return;
16+
};
17+
18+
if pat.len() != 2 {
19+
return;
20+
}
21+
22+
let arg_span = arg.span;
23+
24+
let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind else {
25+
return;
26+
};
27+
28+
if method.ident.as_str() != "enumerate" {
29+
return;
30+
}
31+
32+
let ty = cx.typeck_results().expr_ty(arg);
33+
34+
if !pat_is_wild(cx, &pat[0].kind, body) {
35+
return;
36+
}
37+
38+
let new_pat_span = pat[1].span;
39+
40+
let name = match *ty.kind() {
41+
ty::Adt(base, _substs) => cx.tcx.def_path_str(base.did()),
42+
_ => return,
43+
};
44+
45+
if name != "std::iter::Enumerate" && name != "core::iter::Enumerate" {
46+
return;
47+
}
48+
49+
span_lint_and_then(
50+
cx,
51+
UNUSED_ENUMERATE_INDEX,
52+
arg_span,
53+
"you seem to use `.enumerate()` and immediately discard the index",
54+
|diag| {
55+
let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter");
56+
multispan_sugg(
57+
diag,
58+
"remove the `.enumerate()` call",
59+
vec![
60+
(pat_span, snippet(cx, new_pat_span, "value").into_owned()),
61+
(arg_span, base_iter.to_string()),
62+
],
63+
);
64+
},
65+
);
66+
}
67+
68+
/// Returns `true` if the pattern is a `PatWild` or an ident prefixed with `_`.
69+
fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
70+
match *pat {
71+
PatKind::Wild => true,
72+
PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => !is_local_used(cx, body, id),
73+
_ => false,
74+
}
75+
}

tests/ui/unused_enumerate_index.fixed

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// run-rustfix
2+
#![allow(unused)]
3+
#![warn(clippy::unused_enumerate_index)]
4+
5+
fn main() {
6+
let v = [1, 2, 3];
7+
for x in v.iter() {
8+
print!("{x}");
9+
}
10+
}

tests/ui/unused_enumerate_index.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// run-rustfix
2+
#![allow(unused)]
3+
#![warn(clippy::unused_enumerate_index)]
4+
5+
fn main() {
6+
let v = [1, 2, 3];
7+
for (_, x) in v.iter().enumerate() {
8+
print!("{x}");
9+
}
10+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: you seem to use `.enumerate()` and immediately discard the index
2+
--> $DIR/unused_enumerate_index.rs:7:19
3+
|
4+
LL | for (_, x) in v.iter().enumerate() {
5+
| ^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::unused-enumerate-index` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::unused_enumerate_index)]`
9+
help: remove the `.enumerate()` call
10+
|
11+
LL | for x in v.iter() {
12+
| ~ ~~~~~~~~
13+
14+
error: aborting due to previous error
15+

0 commit comments

Comments
 (0)