about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/methods/unused_enumerate_index.rs45
-rw-r--r--tests/ui/unused_enumerate_index.fixed16
-rw-r--r--tests/ui/unused_enumerate_index.rs16
-rw-r--r--tests/ui/unused_enumerate_index.stderr38
4 files changed, 106 insertions, 9 deletions
diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs
index 506a0f4304a..e5cc898612e 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<Span> {
+    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 1224eb54ba5..cffd02b0acc 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::<i32>();
+    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::<i32>();
+    assert_eq!(x, 6);
+
+    let v = [1, 2, 3].iter().copied();
+    let x = v.map(|x| x).sum::<i32>();
+    assert_eq!(x, 6);
 }
diff --git a/tests/ui/unused_enumerate_index.rs b/tests/ui/unused_enumerate_index.rs
index 66f98b690cb..f2b5f8b9124 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::<i32>();
+    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::<i32>();
+    assert_eq!(x, 6);
+
+    let v = [1, 2, 3].iter().copied().enumerate();
+    let x = v.map(|(_, x)| x).sum::<i32>();
+    assert_eq!(x, 6);
 }
diff --git a/tests/ui/unused_enumerate_index.stderr b/tests/ui/unused_enumerate_index.stderr
index f8babf428c0..6ec07dcbff0 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::<i32>();
+   |
+
+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::<i32>();
+   |
+
+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::<i32>();
+   |
+
+error: aborting due to 8 previous errors