Skip to content

Commit

Permalink
Fixes FP in redundant_closure_call when closures are passed to macros
Browse files Browse the repository at this point in the history
There are cases where the closure call is needed in some macros, this in
particular occurs when the closure has parameters. To handle this case,
we allow the lint when there are no parameters in the closure, or the
closure is outside a macro invocation.

fixes: #11274, #1553
changelog: FP: [`redundant_closure_call`] when closures with parameters
are passed in macros.
  • Loading branch information
m-rph committed Jan 3, 2024
1 parent 0153ca9 commit 6558956
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 2 deletions.
31 changes: 29 additions & 2 deletions clippy_lints/src/redundant_closure_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::rustc_lint::LintContext;
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::get_parent_expr;
use clippy_utils::sugg::Sugg;
use hir::Param;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor};
Expand All @@ -11,6 +12,7 @@ use rustc_middle::hir::nested_filter;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_session::declare_lint_pass;
use rustc_span::{DesugaringKind, ExpnKind};

declare_clippy_lint! {
/// ### What it does
Expand Down Expand Up @@ -87,7 +89,12 @@ fn find_innermost_closure<'tcx>(
cx: &LateContext<'tcx>,
mut expr: &'tcx hir::Expr<'tcx>,
mut steps: usize,
) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::FnDecl<'tcx>, ty::Asyncness)> {
) -> Option<(
&'tcx hir::Expr<'tcx>,
&'tcx hir::FnDecl<'tcx>,
ty::Asyncness,
&'tcx [Param<'tcx>],
)> {
let mut data = None;

while let hir::ExprKind::Closure(closure) = expr.kind
Expand All @@ -108,6 +115,7 @@ fn find_innermost_closure<'tcx>(
} else {
ty::Asyncness::No
},
body.params,
));
steps -= 1;
}
Expand Down Expand Up @@ -136,6 +144,23 @@ fn get_parent_call_exprs<'tcx>(
(expr, depth)
}

/// This has been pulled from `in_external_macro`, and the checks for being outside the
/// current crate have been removed.
fn in_macro(expr: &'_ hir::Expr<'_>) -> bool {
let expn_data = expr.span.ctxt().outer_expn_data();
!matches!(
expn_data.kind,
ExpnKind::Root
| ExpnKind::Desugaring(
DesugaringKind::ForLoop
| DesugaringKind::WhileLoop
| DesugaringKind::OpaqueTy
| DesugaringKind::Async
| DesugaringKind::Await,
)
)
}

impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
if in_external_macro(cx.sess(), expr.span) {
Expand All @@ -150,7 +175,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
// without this check, we'd end up linting twice.
&& !matches!(recv.kind, hir::ExprKind::Call(..))
&& let (full_expr, call_depth) = get_parent_call_exprs(cx, expr)
&& let Some((body, fn_decl, coroutine_kind)) = find_innermost_closure(cx, recv, call_depth)
&& let Some((body, fn_decl, coroutine_kind, params)) = find_innermost_closure(cx, recv, call_depth)
// outside macros we lint properly. Inside macros, we lint only ||() style closures.
&& (!in_macro(expr) || params.is_empty())
{
span_lint_and_then(
cx,
Expand Down
9 changes: 9 additions & 0 deletions tests/ui/redundant_closure_call_fixable.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,12 @@ mod issue11707 {
fn avoid_double_parens() {
std::convert::identity(13_i32 + 36_i32).leading_zeros();
}

fn fp_11274() {
macro_rules! m {
($closure:expr) => {
$closure(1)
};
}
m!(|x| println!("{x}"));
}
9 changes: 9 additions & 0 deletions tests/ui/redundant_closure_call_fixable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,12 @@ mod issue11707 {
fn avoid_double_parens() {
std::convert::identity((|| 13_i32 + 36_i32)()).leading_zeros();
}

fn fp_11274() {
macro_rules! m {
($closure:expr) => {
$closure(1)
};
}
m!(|x| println!("{x}"));
}

0 comments on commit 6558956

Please sign in to comment.