Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion library/std/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,38 @@ macro_rules! dbg {
}
};
($($val:expr),+ $(,)?) => {
($($crate::dbg!($val)),+,)
{
// Eagerly evaluate all arguments then print them all at once to
// avoid tearing prints. We can't simply hold the stderr lock while
// evaluating the arguments because that could lead to deadlocks if
// any of the expressions also attempt to write to stderr on another
// thread.
use $crate::fmt::Write;
let mut output = $crate::string::String::with_capacity(
// Make an educated guess about the final size to avoid
// multiple reallocations.
const { 0 $(+ 100 + $crate::file!().len() + $crate::stringify!($val).len())+ },
);
let eager_eval = ($(
// Use of `match` here is intentional because it affects the lifetimes
// of temporaries - https://stackoverflow.com/a/48732525/1063961
match $val {
tmp => {
let _ = $crate::writeln!(&mut output, "[{}:{}:{}] {} = {:#?}",
$crate::file!(),
$crate::line!(),
$crate::column!(),
$crate::stringify!($val),
// The `&T: Debug` check happens here (not in the format literal desugaring)
// to avoid format literal related messages and suggestions.
&&tmp as &dyn $crate::fmt::Debug,
);
tmp
}
}
),+,);
$crate::eprint!("{}", output);
eager_eval
}
};
}
87 changes: 55 additions & 32 deletions src/tools/clippy/clippy_lints/src/dbg_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,28 @@ impl LateLintPass<'_> for DbgMacro {
let mut applicability = Applicability::MachineApplicable;
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())
ExprKind::Block(blk, _label) => {
// dbg!(2, 3)
if let Some((first, last)) = find_multi_input_exprs(&blk.stmts) {
let snippet = snippet_with_applicability(
cx,
first.span.source_callsite().to(last.span.source_callsite()),
"..",
&mut applicability,
);
(macro_call.span, format!("({snippet})"))
// dbg!()
} else {
(macro_call.span, String::from("()"))
// 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)
Expand All @@ -96,28 +107,6 @@ impl LateLintPass<'_> for DbgMacro {
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!(),
};

Expand Down Expand Up @@ -169,3 +158,37 @@ fn is_async_move_desugar<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx
fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option<MacroCall> {
macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id))
}

fn find_multi_input_exprs<'a>(statements: &'a [Stmt<'a>]) -> Option<(Expr<'a>, Expr<'a>)> {
// Multi-input dbg!(a, b, ...) macro contains a statement of the form:
// let eager_eval = ($( match $val { ... } ),*);
// which we're looking for here.
for stmt in statements {
if let StmtKind::Let(LetStmt {
init:
Some(Expr {
kind:
ExprKind::Tup(
[
Expr {
kind: ExprKind::Match(first, ..),
..
},
..,
Expr {
kind: ExprKind::Match(last, ..),
..
},
],
),
..
}),
..
}) = stmt.kind
{
return Some((**first, **last));
}
}

None
}
Loading