about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEthiraric <ethiraric@gmail.com>2024-03-13 20:28:57 +0100
committerEthiraric <ethiraric@gmail.com>2024-03-14 23:12:47 +0100
commitdadcd94b2af45be66281bd8400ec72485e2f84fc (patch)
treef9df03c1d3a11d6d5cd8c86b293c1d629ef32312
parent7cdeac5773ac7664731d8cb850334559b4b9dab8 (diff)
downloadrust-dadcd94b2af45be66281bd8400ec72485e2f84fc.tar.gz
rust-dadcd94b2af45be66281bd8400ec72485e2f84fc.zip
[`unused_enumerate_index`]: Keep explicit element type
Prior to this change, it might be that the lint would remove an explicit
type that was necessary for the type system to keep track of types.

This should be the last change that prevented this lint to be machine
applicable.
-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