Skip to content

Commit 919f698

Browse files
committed
Auto merge of #10404 - dnbln:feat/unused_enumerate_index, r=blyxyas
Add `unused_enumerate_index` lint A lint for unused `.enumerate()` indexes (`for (_, x) in iter.enumerate()`). I wasn't able to find a `rustc_span::sym::Enumerate`, so the code for checking that it's the correct `Enumerate` iterator is a bit weird. --- changelog: New lint: [`unused_enumerate_index`]: A new lint for checking that the indexes from `.enumerate()` calls are used. [#10404](#10404) <!-- changelog_checked -->
2 parents 7d34406 + bb9cc6d commit 919f698

10 files changed

+252
-24
lines changed

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5561,6 +5561,7 @@ Released 2018-09-13
55615561
[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
55625562
[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async
55635563
[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
5564+
[`unused_enumerate_index`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_enumerate_index
55645565
[`unused_format_specs`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_format_specs
55655566
[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
55665567
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label

Diff for: clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
274274
crate::loops::NEVER_LOOP_INFO,
275275
crate::loops::SAME_ITEM_PUSH_INFO,
276276
crate::loops::SINGLE_ELEMENT_LOOP_INFO,
277+
crate::loops::UNUSED_ENUMERATE_INDEX_INFO,
277278
crate::loops::WHILE_IMMUTABLE_CONDITION_INFO,
278279
crate::loops::WHILE_LET_LOOP_INFO,
279280
crate::loops::WHILE_LET_ON_ITERATOR_INFO,

Diff for: clippy_lints/src/loops/for_kv_map.rs

+1-11
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
use super::FOR_KV_MAP;
22
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
33
use clippy_utils::source::snippet;
4-
use clippy_utils::sugg;
54
use clippy_utils::ty::is_type_diagnostic_item;
6-
use clippy_utils::visitors::is_local_used;
5+
use clippy_utils::{pat_is_wild, sugg};
76
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
87
use rustc_lint::LateContext;
98
use rustc_middle::ty;
@@ -55,12 +54,3 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx
5554
}
5655
}
5756
}
58-
59-
/// Returns `true` if the pattern is a `PatWild` or an ident prefixed with `_`.
60-
fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
61-
match *pat {
62-
PatKind::Wild => true,
63-
PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => !is_local_used(cx, body, id),
64-
_ => false,
65-
}
66-
}

Diff for: clippy_lints/src/loops/mod.rs

+32-1
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 uses of the `enumerate` method where the index is unused (`_`)
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+
/// println!("{x}");
593+
/// }
594+
/// ```
595+
/// Use instead:
596+
/// ```rust
597+
/// let v = vec![1, 2, 3, 4];
598+
/// for x in v.iter() {
599+
/// println!("{x}");
600+
/// }
601+
/// ```
602+
#[clippy::version = "1.75.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<'_>) {

Diff for: clippy_lints/src/loops/unused_enumerate_index.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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::{pat_is_wild, sugg};
5+
use rustc_hir::def::DefKind;
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 PatKind::Tuple(tuple, _) = pat.kind else {
13+
return;
14+
};
15+
16+
let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind else {
17+
return;
18+
};
19+
20+
let ty = cx.typeck_results().expr_ty(arg);
21+
22+
if !pat_is_wild(cx, &tuple[0].kind, body) {
23+
return;
24+
}
25+
26+
let name = match *ty.kind() {
27+
ty::Adt(base, _substs) => cx.tcx.def_path_str(base.did()),
28+
_ => return,
29+
};
30+
31+
if name != "std::iter::Enumerate" && name != "core::iter::Enumerate" {
32+
return;
33+
}
34+
35+
let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) else {
36+
return;
37+
};
38+
39+
let call_name = cx.tcx.def_path_str(call_id);
40+
41+
if call_name != "std::iter::Iterator::enumerate" && call_name != "core::iter::Iterator::enumerate" {
42+
return;
43+
}
44+
45+
span_lint_and_then(
46+
cx,
47+
UNUSED_ENUMERATE_INDEX,
48+
arg.span,
49+
"you seem to use `.enumerate()` and immediately discard the index",
50+
|diag| {
51+
let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter");
52+
multispan_sugg(
53+
diag,
54+
"remove the `.enumerate()` call",
55+
vec![
56+
(pat.span, snippet(cx, tuple[1].span, "..").into_owned()),
57+
(arg.span, base_iter.to_string()),
58+
],
59+
);
60+
},
61+
);
62+
}

Diff for: clippy_lints/src/methods/iter_kv_map.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
use super::ITER_KV_MAP;
44
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_sugg, span_lint_and_then};
55
use clippy_utils::source::{snippet, snippet_with_applicability};
6-
use clippy_utils::sugg;
76
use clippy_utils::ty::is_type_diagnostic_item;
8-
use clippy_utils::visitors::is_local_used;
7+
use clippy_utils::{pat_is_wild, sugg};
98
use rustc_hir::{BindingAnnotation, Body, BorrowKind, ByRef, Expr, ExprKind, Mutability, Pat, PatKind};
109
use rustc_lint::{LateContext, LintContext};
1110
use rustc_middle::ty;
@@ -84,13 +83,3 @@ pub(super) fn check<'tcx>(
8483
}
8584
}
8685
}
87-
88-
/// Returns `true` if the pattern is a `PatWild`, or is an ident prefixed with `_`
89-
/// that is not locally used.
90-
fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
91-
match *pat {
92-
PatKind::Wild => true,
93-
PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => !is_local_used(cx, body, id),
94-
_ => false,
95-
}
96-
}

Diff for: clippy_utils/src/lib.rs

+12
Original file line numberDiff line numberDiff line change
@@ -2960,3 +2960,15 @@ op_utils! {
29602960
Shl ShlAssign
29612961
Shr ShrAssign
29622962
}
2963+
2964+
/// Returns `true` if the pattern is a `PatWild`, or is an ident prefixed with `_`
2965+
/// that is not locally used.
2966+
pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: impl Visitable<'tcx>) -> bool {
2967+
match *pat {
2968+
PatKind::Wild => true,
2969+
PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
2970+
!visitors::is_local_used(cx, body, id)
2971+
},
2972+
_ => false,
2973+
}
2974+
}

Diff for: tests/ui/unused_enumerate_index.fixed

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#![allow(unused)]
2+
#![warn(clippy::unused_enumerate_index)]
3+
4+
use std::iter::Enumerate;
5+
6+
fn main() {
7+
let v = [1, 2, 3];
8+
for x in v.iter() {
9+
println!("{x}");
10+
}
11+
12+
struct Dummy1;
13+
impl Dummy1 {
14+
fn enumerate(self) -> Vec<usize> {
15+
vec![]
16+
}
17+
}
18+
let dummy = Dummy1;
19+
for x in dummy.enumerate() {
20+
println!("{x}");
21+
}
22+
23+
struct Dummy2;
24+
impl Dummy2 {
25+
fn enumerate(self) -> Enumerate<std::vec::IntoIter<usize>> {
26+
vec![1, 2].into_iter().enumerate()
27+
}
28+
}
29+
let dummy = Dummy2;
30+
for (_, x) in dummy.enumerate() {
31+
println!("{x}");
32+
}
33+
34+
let mut with_used_iterator = [1, 2, 3].into_iter().enumerate();
35+
with_used_iterator.next();
36+
for (_, x) in with_used_iterator {
37+
println!("{x}");
38+
}
39+
40+
struct Dummy3(std::vec::IntoIter<usize>);
41+
42+
impl Iterator for Dummy3 {
43+
type Item = usize;
44+
45+
fn next(&mut self) -> Option<Self::Item> {
46+
self.0.next()
47+
}
48+
49+
fn size_hint(&self) -> (usize, Option<usize>) {
50+
self.0.size_hint()
51+
}
52+
}
53+
54+
let dummy = Dummy3(vec![1, 2, 3].into_iter());
55+
for x in dummy {
56+
println!("{x}");
57+
}
58+
}

Diff for: tests/ui/unused_enumerate_index.rs

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#![allow(unused)]
2+
#![warn(clippy::unused_enumerate_index)]
3+
4+
use std::iter::Enumerate;
5+
6+
fn main() {
7+
let v = [1, 2, 3];
8+
for (_, x) in v.iter().enumerate() {
9+
println!("{x}");
10+
}
11+
12+
struct Dummy1;
13+
impl Dummy1 {
14+
fn enumerate(self) -> Vec<usize> {
15+
vec![]
16+
}
17+
}
18+
let dummy = Dummy1;
19+
for x in dummy.enumerate() {
20+
println!("{x}");
21+
}
22+
23+
struct Dummy2;
24+
impl Dummy2 {
25+
fn enumerate(self) -> Enumerate<std::vec::IntoIter<usize>> {
26+
vec![1, 2].into_iter().enumerate()
27+
}
28+
}
29+
let dummy = Dummy2;
30+
for (_, x) in dummy.enumerate() {
31+
println!("{x}");
32+
}
33+
34+
let mut with_used_iterator = [1, 2, 3].into_iter().enumerate();
35+
with_used_iterator.next();
36+
for (_, x) in with_used_iterator {
37+
println!("{x}");
38+
}
39+
40+
struct Dummy3(std::vec::IntoIter<usize>);
41+
42+
impl Iterator for Dummy3 {
43+
type Item = usize;
44+
45+
fn next(&mut self) -> Option<Self::Item> {
46+
self.0.next()
47+
}
48+
49+
fn size_hint(&self) -> (usize, Option<usize>) {
50+
self.0.size_hint()
51+
}
52+
}
53+
54+
let dummy = Dummy3(vec![1, 2, 3].into_iter());
55+
for (_, x) in dummy.enumerate() {
56+
println!("{x}");
57+
}
58+
}

Diff for: tests/ui/unused_enumerate_index.stderr

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error: you seem to use `.enumerate()` and immediately discard the index
2+
--> $DIR/unused_enumerate_index.rs:8: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: you seem to use `.enumerate()` and immediately discard the index
15+
--> $DIR/unused_enumerate_index.rs:55:19
16+
|
17+
LL | for (_, x) in dummy.enumerate() {
18+
| ^^^^^^^^^^^^^^^^^
19+
|
20+
help: remove the `.enumerate()` call
21+
|
22+
LL | for x in dummy {
23+
| ~ ~~~~~
24+
25+
error: aborting due to 2 previous errors
26+

0 commit comments

Comments
 (0)