about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/eta_reduction.rs92
-rw-r--r--tests/ui/eta.fixed18
-rw-r--r--tests/ui/eta.rs18
3 files changed, 89 insertions, 39 deletions
diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs
index 645f9306849..92f31095d2f 100644
--- a/clippy_lints/src/eta_reduction.rs
+++ b/clippy_lints/src/eta_reduction.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::higher::VecArgs;
 use clippy_utils::source::{snippet_opt, snippet_with_applicability};
 use clippy_utils::ty::get_type_diagnostic_name;
@@ -108,14 +108,20 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
         {
             let vec_crate = if is_no_std_crate(cx) { "alloc" } else { "std" };
             // replace `|| vec![]` with `Vec::new`
-            span_lint_and_sugg(
+            span_lint_hir_and_then(
                 cx,
                 REDUNDANT_CLOSURE,
+                expr.hir_id,
                 expr.span,
                 "redundant closure",
-                "replace the closure with `Vec::new`",
-                format!("{vec_crate}::vec::Vec::new"),
-                Applicability::MachineApplicable,
+                |diag| {
+                    diag.span_suggestion(
+                        expr.span,
+                        "replace the closure with `Vec::new`",
+                        format!("{vec_crate}::vec::Vec::new"),
+                        Applicability::MachineApplicable,
+                    );
+                },
             );
         }
         // skip `foo(|| macro!())`
@@ -197,41 +203,48 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
                 // For now ignore all callee types which reference a type parameter.
                 && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_)))
             {
-                span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
-                    if let Some(mut snippet) = snippet_opt(cx, callee.span) {
-                        if path_to_local(callee).is_some_and(|l| {
-                            // FIXME: Do we really need this `local_used_in` check?
-                            // Isn't it checking something like... `callee(callee)`?
-                            // If somehow this check is needed, add some test for it,
-                            // 'cuz currently nothing changes after deleting this check.
-                            local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
-                        }) {
-                            match cx
-                                .tcx
-                                .infer_ctxt()
-                                .build(cx.typing_mode())
-                                .err_ctxt()
-                                .type_implements_fn_trait(
-                                    cx.param_env,
-                                    Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
-                                    ty::PredicatePolarity::Positive,
-                                ) {
-                                // Mutable closure is used after current expr; we cannot consume it.
-                                Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"),
-                                Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => {
-                                    snippet = format!("&{snippet}");
-                                },
-                                _ => (),
+                span_lint_hir_and_then(
+                    cx,
+                    REDUNDANT_CLOSURE,
+                    expr.hir_id,
+                    expr.span,
+                    "redundant closure",
+                    |diag| {
+                        if let Some(mut snippet) = snippet_opt(cx, callee.span) {
+                            if path_to_local(callee).is_some_and(|l| {
+                                // FIXME: Do we really need this `local_used_in` check?
+                                // Isn't it checking something like... `callee(callee)`?
+                                // If somehow this check is needed, add some test for it,
+                                // 'cuz currently nothing changes after deleting this check.
+                                local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
+                            }) {
+                                match cx
+                                    .tcx
+                                    .infer_ctxt()
+                                    .build(cx.typing_mode())
+                                    .err_ctxt()
+                                    .type_implements_fn_trait(
+                                        cx.param_env,
+                                        Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
+                                        ty::PredicatePolarity::Positive,
+                                    ) {
+                                    // Mutable closure is used after current expr; we cannot consume it.
+                                    Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"),
+                                    Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => {
+                                        snippet = format!("&{snippet}");
+                                    },
+                                    _ => (),
+                                }
                             }
+                            diag.span_suggestion(
+                                expr.span,
+                                "replace the closure with the function itself",
+                                snippet,
+                                Applicability::MachineApplicable,
+                            );
                         }
-                        diag.span_suggestion(
-                            expr.span,
-                            "replace the closure with the function itself",
-                            snippet,
-                            Applicability::MachineApplicable,
-                        );
-                    }
-                });
+                    },
+                );
             }
         },
         ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => {
@@ -244,9 +257,10 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
                     Some(span) => format!("::{}", snippet_with_applicability(cx, span, "<..>", &mut app)),
                     None => String::new(),
                 };
-                span_lint_and_then(
+                span_lint_hir_and_then(
                     cx,
                     REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
+                    expr.hir_id,
                     expr.span,
                     "redundant closure",
                     |diag| {
diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed
index 0ba631fda05..c93b83f53ec 100644
--- a/tests/ui/eta.fixed
+++ b/tests/ui/eta.fixed
@@ -543,3 +543,21 @@ mod issue_13073 {
         //~^ redundant_closure
     }
 }
+
+fn issue_14789() {
+    _ = Some(1u8).map(
+        #[expect(clippy::redundant_closure)]
+        |a| foo(a),
+    );
+
+    _ = Some("foo").map(
+        #[expect(clippy::redundant_closure_for_method_calls)]
+        |s| s.to_owned(),
+    );
+
+    let _: Vec<u8> = None.map_or_else(
+        #[expect(clippy::redundant_closure)]
+        || vec![],
+        std::convert::identity,
+    );
+}
diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs
index 4d8b29d450c..273c8b21f4a 100644
--- a/tests/ui/eta.rs
+++ b/tests/ui/eta.rs
@@ -543,3 +543,21 @@ mod issue_13073 {
         //~^ redundant_closure
     }
 }
+
+fn issue_14789() {
+    _ = Some(1u8).map(
+        #[expect(clippy::redundant_closure)]
+        |a| foo(a),
+    );
+
+    _ = Some("foo").map(
+        #[expect(clippy::redundant_closure_for_method_calls)]
+        |s| s.to_owned(),
+    );
+
+    let _: Vec<u8> = None.map_or_else(
+        #[expect(clippy::redundant_closure)]
+        || vec![],
+        std::convert::identity,
+    );
+}