Emit lint about redundant closure on the closure node itself (#14791)
Fixes rust-lang/rust-clippy#14789 changelog: [`redundant_closure`], [`redundant_closure_for_method_calls`]: allow lint to be allowed or expected on the closure expression itself
This commit is contained in:
commit
6330f9119b
3 changed files with 89 additions and 39 deletions
|
|
@ -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| {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue