diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index 06376c57119d..152516baf734 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -5,7 +5,7 @@ use clippy_utils::macros::{MacroCall, macro_backtrace}; use clippy_utils::source::snippet_with_applicability; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Node}; +use rustc_hir::{Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::{Span, SyntaxContext, sym}; @@ -60,6 +60,8 @@ impl LateLintPass<'_> for DbgMacro { if cur_syntax_ctxt != self.prev_ctxt && let Some(macro_call) = first_dbg_macro_in_expansion(cx, expr.span) && !macro_call.span.in_external_macro(cx.sess().source_map()) && + // avoids exprs generated by the desugaring of coroutines + !is_coroutine_desugar(expr) && self.checked_dbg_call_site.insert(macro_call.span) && // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml !(self.allow_dbg_in_tests && is_in_test(cx.tcx, expr.hir_id)) @@ -73,50 +75,51 @@ impl LateLintPass<'_> for DbgMacro { "the `dbg!` macro is intended as a debugging tool", |diag| { let mut applicability = Applicability::MachineApplicable; - - let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { - // dbg!() - ExprKind::Block(..) => { - // If the `dbg!` macro is a "free" statement and not contained within other expressions, - // remove the whole statement. - if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) - && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) - { - (macro_call.span.to(semi_span), String::new()) - } else { - (macro_call.span, String::from("()")) - } - }, - // dbg!(1) - ExprKind::Match(val, ..) => ( - macro_call.span, - snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) - .to_string(), - ), - // dbg!(2, 3) - ExprKind::Tup( - [ - Expr { - kind: ExprKind::Match(first, ..), - .. - }, - .., - Expr { - kind: ExprKind::Match(last, ..), - .. - }, - ], - ) => { - let snippet = snippet_with_applicability( - cx, - first.span.source_callsite().to(last.span.source_callsite()), - "..", - &mut applicability, - ); - (macro_call.span, format!("({snippet})")) - }, - _ => unreachable!(), - }; + let (sugg_span, suggestion) = + match is_async_move_desugar(expr).unwrap_or(expr).peel_drop_temps().kind { + // dbg!() + ExprKind::Block(..) => { + // If the `dbg!` macro is a "free" statement and not contained within other expressions, + // remove the whole statement. + if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) + && let Some(semi_span) = + cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) + { + (macro_call.span.to(semi_span), String::new()) + } else { + (macro_call.span, String::from("()")) + } + }, + // dbg!(1) + ExprKind::Match(val, ..) => ( + macro_call.span, + snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) + .to_string(), + ), + // dbg!(2, 3) + ExprKind::Tup( + [ + Expr { + kind: ExprKind::Match(first, ..), + .. + }, + .., + Expr { + kind: ExprKind::Match(last, ..), + .. + }, + ], + ) => { + let snippet = snippet_with_applicability( + cx, + first.span.source_callsite().to(last.span.source_callsite()), + "..", + &mut applicability, + ); + (macro_call.span, format!("({snippet})")) + }, + _ => unreachable!(), + }; diag.span_suggestion( sugg_span, @@ -134,6 +137,35 @@ impl LateLintPass<'_> for DbgMacro { } } +fn is_coroutine_desugar(expr: &Expr<'_>) -> bool { + matches!( + expr.kind, + ExprKind::Closure(Closure { + kind: ClosureKind::Coroutine(CoroutineKind::Desugared(..)) | ClosureKind::CoroutineClosure(..), + .. + }) + ) +} + +fn is_async_move_desugar<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Block(block, _) = expr.kind + && let [ + Stmt { + kind: + StmtKind::Let(LetStmt { + source: LocalSource::AsyncFn, + .. + }), + .. + }, + ] = block.stmts + { + return block.expr; + } + + None +} + fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option { macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id)) } diff --git a/tests/ui/dbg_macro/dbg_macro.fixed b/tests/ui/dbg_macro/dbg_macro.fixed index 3b9dee81898a..5993c2faf0dd 100644 --- a/tests/ui/dbg_macro/dbg_macro.fixed +++ b/tests/ui/dbg_macro/dbg_macro.fixed @@ -123,3 +123,19 @@ mod issue12131 { //~^ dbg_macro } } + +mod issue14914 { + use std::future::Future; + + fn takes_async_fn(_f: F) + where + F: FnOnce(i32) -> Fut, + Fut: Future, + { + } + + fn should_not_panic() { + takes_async_fn(async |val| val); + //~^ dbg_macro + } +} diff --git a/tests/ui/dbg_macro/dbg_macro.rs b/tests/ui/dbg_macro/dbg_macro.rs index 1dbbc6fe9845..58d7e106e238 100644 --- a/tests/ui/dbg_macro/dbg_macro.rs +++ b/tests/ui/dbg_macro/dbg_macro.rs @@ -123,3 +123,19 @@ mod issue12131 { //~^ dbg_macro } } + +mod issue14914 { + use std::future::Future; + + fn takes_async_fn(_f: F) + where + F: FnOnce(i32) -> Fut, + Fut: Future, + { + } + + fn should_not_panic() { + takes_async_fn(async |val| dbg!(val)); + //~^ dbg_macro + } +} diff --git a/tests/ui/dbg_macro/dbg_macro.stderr b/tests/ui/dbg_macro/dbg_macro.stderr index f1412023cc89..5a65b38a85c4 100644 --- a/tests/ui/dbg_macro/dbg_macro.stderr +++ b/tests/ui/dbg_macro/dbg_macro.stderr @@ -230,5 +230,17 @@ LL - print!("{}", dbg!(s)); LL + print!("{}", s); | -error: aborting due to 19 previous errors +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro.rs:138:36 + | +LL | takes_async_fn(async |val| dbg!(val)); + | ^^^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL - takes_async_fn(async |val| dbg!(val)); +LL + takes_async_fn(async |val| val); + | + +error: aborting due to 20 previous errors