diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs index 506a0f4304ad..e5cc898612e9 100644 --- a/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,11 +1,12 @@ -use clippy_utils::diagnostics::{multispan_sugg, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_hir_and_then}; use clippy_utils::paths::{CORE_ITER_ENUMERATE_METHOD, CORE_ITER_ENUMERATE_STRUCT}; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::{expr_or_init, is_trait_method, match_def_path, pat_is_wild}; -use rustc_hir::{Expr, ExprKind, PatKind}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::AdtDef; -use rustc_span::sym; +use rustc_span::{sym, Span}; use crate::loops::UNUSED_ENUMERATE_INDEX; @@ -76,6 +77,18 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, // Make sure the method call is `std::iter::Iterator::enumerate`. && match_def_path(cx, enumerate_defid, &CORE_ITER_ENUMERATE_METHOD) { + // Check if the tuple type was explicit. It may be the type system _needs_ the type of the element + // that would be explicited in the closure. + let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) { + // We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`. + // Fallback to `..` if we fail getting either snippet. + Some(ty_span) => snippet_opt(cx, elem.span) + .and_then(|binding_name| snippet_opt(cx, ty_span).map(|ty_name| format!("{binding_name}: {ty_name}"))) + .unwrap_or_else(|| "..".to_string()), + // Otherwise, we have no explicit type. We can replace with the binding name of the element. + None => snippet(cx, elem.span, "..").into_owned(), + }; + // Suggest removing the tuple from the closure and the preceding call to `enumerate`, whose span we // can get from the `MethodCall`. span_lint_hir_and_then( @@ -85,11 +98,12 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, enumerate_span, "you seem to use `.enumerate()` and immediately discard the index", |diag| { - multispan_sugg( + multispan_sugg_with_applicability( diag, "remove the `.enumerate()` call", + Applicability::MachineApplicable, vec![ - (closure_param.span, snippet(cx, elem.span, "..").into_owned()), + (closure_param.span, new_closure_param), ( enumerate_span.with_lo(enumerate_recv.span.source_callsite().hi()), String::new(), @@ -100,3 +114,22 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, ); } } + +/// Find the span of the explicit type of the element. +/// +/// # Returns +/// If the tuple argument: +/// * Has no explicit type, returns `None` +/// * Has an explicit tuple type with an implicit element type (`(usize, _)`), returns `None` +/// * Has an explicit tuple type with an explicit element type (`(_, i32)`), returns the span for +/// the element type. +fn find_elem_explicit_type_span(fn_decl: &FnDecl<'_>) -> Option { + if let [tuple_ty] = fn_decl.inputs + && let TyKind::Tup([_idx_ty, elem_ty]) = tuple_ty.kind + && !matches!(elem_ty.kind, TyKind::Err(..) | TyKind::Infer) + { + Some(elem_ty.span) + } else { + None + } +} diff --git a/tests/ui/unused_enumerate_index.fixed b/tests/ui/unused_enumerate_index.fixed index 1224eb54ba5c..cffd02b0acc8 100644 --- a/tests/ui/unused_enumerate_index.fixed +++ b/tests/ui/unused_enumerate_index.fixed @@ -1,4 +1,4 @@ -#![allow(unused)] +#![allow(unused, clippy::map_identity)] #![warn(clippy::unused_enumerate_index)] use std::iter::Enumerate; @@ -89,4 +89,18 @@ fn main() { #[allow(clippy::unused_enumerate_index)] let v = [1].iter().enumerate(); v.map(|(_, _x)| {}); + + // This should keep the explicit type of `x`. + let v = [1, 2, 3].iter().copied(); + let x = v.map(|x: i32| x).sum::(); + assert_eq!(x, 6); + + // This should keep the explicit type of `x`. + let v = [1, 2, 3].iter().copied(); + let x = v.map(|x: i32| x).sum::(); + assert_eq!(x, 6); + + let v = [1, 2, 3].iter().copied(); + let x = v.map(|x| x).sum::(); + assert_eq!(x, 6); } diff --git a/tests/ui/unused_enumerate_index.rs b/tests/ui/unused_enumerate_index.rs index 66f98b690cbe..f2b5f8b91247 100644 --- a/tests/ui/unused_enumerate_index.rs +++ b/tests/ui/unused_enumerate_index.rs @@ -1,4 +1,4 @@ -#![allow(unused)] +#![allow(unused, clippy::map_identity)] #![warn(clippy::unused_enumerate_index)] use std::iter::Enumerate; @@ -89,4 +89,18 @@ fn main() { #[allow(clippy::unused_enumerate_index)] let v = [1].iter().enumerate(); v.map(|(_, _x)| {}); + + // This should keep the explicit type of `x`. + let v = [1, 2, 3].iter().copied().enumerate(); + let x = v.map(|(_, x): (usize, i32)| x).sum::(); + assert_eq!(x, 6); + + // This should keep the explicit type of `x`. + let v = [1, 2, 3].iter().copied().enumerate(); + let x = v.map(|(_, x): (_, i32)| x).sum::(); + assert_eq!(x, 6); + + let v = [1, 2, 3].iter().copied().enumerate(); + let x = v.map(|(_, x)| x).sum::(); + assert_eq!(x, 6); } diff --git a/tests/ui/unused_enumerate_index.stderr b/tests/ui/unused_enumerate_index.stderr index f8babf428c02..6ec07dcbff0a 100644 --- a/tests/ui/unused_enumerate_index.stderr +++ b/tests/ui/unused_enumerate_index.stderr @@ -58,5 +58,41 @@ LL - _ = mac2!().enumerate().map(|(_, _v)| {}); LL + _ = mac2!().map(|_v| {}); | -error: aborting due to 5 previous errors +error: you seem to use `.enumerate()` and immediately discard the index + --> tests/ui/unused_enumerate_index.rs:94:39 + | +LL | let v = [1, 2, 3].iter().copied().enumerate(); + | ^^^^^^^^^^^ + | +help: remove the `.enumerate()` call + | +LL ~ let v = [1, 2, 3].iter().copied(); +LL ~ let x = v.map(|x: i32| x).sum::(); + | + +error: you seem to use `.enumerate()` and immediately discard the index + --> tests/ui/unused_enumerate_index.rs:99:39 + | +LL | let v = [1, 2, 3].iter().copied().enumerate(); + | ^^^^^^^^^^^ + | +help: remove the `.enumerate()` call + | +LL ~ let v = [1, 2, 3].iter().copied(); +LL ~ let x = v.map(|x: i32| x).sum::(); + | + +error: you seem to use `.enumerate()` and immediately discard the index + --> tests/ui/unused_enumerate_index.rs:103:39 + | +LL | let v = [1, 2, 3].iter().copied().enumerate(); + | ^^^^^^^^^^^ + | +help: remove the `.enumerate()` call + | +LL ~ let v = [1, 2, 3].iter().copied(); +LL ~ let x = v.map(|x| x).sum::(); + | + +error: aborting due to 8 previous errors