Rollup merge of #149869 - joboet:torn-dbg, r=Mark-Simulacrum
std: avoid tearing `dbg!` prints Fixes https://github.com/rust-lang/rust/issues/136703. This is an alternative to rust-lang/rust#149859. Instead of formatting everything into a string, this PR makes multi-expression `dbg!` expand into multiple nested matches, with the final match containing a single `eprint!`. By using macro recursion and relying on hygiene, this allows naming every bound value in that `eprint!`. CC @orlp r? libs
This commit is contained in:
commit
cc666ba8f4
12 changed files with 125 additions and 64 deletions
|
|
@ -468,7 +468,9 @@ extern crate std as realstd;
|
|||
|
||||
// The standard macros that are not built-in to the compiler.
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "std_internals", issue = "none")]
|
||||
pub mod macros;
|
||||
|
||||
// The runtime entry point and a few unstable public functions used by the
|
||||
// compiler
|
||||
|
|
|
|||
|
|
@ -347,35 +347,70 @@ macro_rules! eprintln {
|
|||
/// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html
|
||||
/// [`log`]: https://crates.io/crates/log
|
||||
#[macro_export]
|
||||
#[allow_internal_unstable(std_internals)]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "dbg_macro")]
|
||||
#[stable(feature = "dbg_macro", since = "1.32.0")]
|
||||
macro_rules! dbg {
|
||||
// NOTE: We cannot use `concat!` to make a static string as a format argument
|
||||
// of `eprintln!` because `file!` could contain a `{` or
|
||||
// `$val` expression could be a block (`{ .. }`), in which case the `eprintln!`
|
||||
// will be malformed.
|
||||
() => {
|
||||
$crate::eprintln!("[{}:{}:{}]", $crate::file!(), $crate::line!(), $crate::column!())
|
||||
};
|
||||
($val:expr $(,)?) => {
|
||||
($($val:expr),+ $(,)?) => {
|
||||
$crate::macros::dbg_internal!(() () ($($val),+))
|
||||
};
|
||||
}
|
||||
|
||||
/// Internal macro that processes a list of expressions and produces a chain of
|
||||
/// nested `match`es, one for each expression, before finally calling `eprint!`
|
||||
/// with the collected information and returning all the evaluated expressions
|
||||
/// in a tuple.
|
||||
///
|
||||
/// E.g. `dbg_internal!(() () (1, 2))` expands into
|
||||
/// ```rust, ignore
|
||||
/// match 1 {
|
||||
/// tmp_1 => match 2 {
|
||||
/// tmp_2 => {
|
||||
/// eprint!("...", &tmp_1, &tmp_2, /* some other arguments */);
|
||||
/// (tmp_1, tmp_2)
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This is necessary so that `dbg!` outputs don't get torn, see #136703.
|
||||
#[doc(hidden)]
|
||||
#[rustc_macro_transparency = "semiopaque"]
|
||||
pub macro dbg_internal {
|
||||
(($($piece:literal),+) ($($processed:expr => $bound:expr),+) ()) => {{
|
||||
$crate::eprint!(
|
||||
$crate::concat!($($piece),+),
|
||||
$(
|
||||
$crate::stringify!($processed),
|
||||
// The `&T: Debug` check happens here (not in the format literal desugaring)
|
||||
// to avoid format literal related messages and suggestions.
|
||||
&&$bound as &dyn $crate::fmt::Debug
|
||||
),+,
|
||||
// The location returned here is that of the macro invocation, so
|
||||
// it will be the same for all expressions. Thus, label these
|
||||
// arguments so that they can be reused in every piece of the
|
||||
// formatting template.
|
||||
file=$crate::file!(),
|
||||
line=$crate::line!(),
|
||||
column=$crate::column!()
|
||||
);
|
||||
// Comma separate the variables only when necessary so that this will
|
||||
// not yield a tuple for a single expression, but rather just parenthesize
|
||||
// the expression.
|
||||
($($bound),+)
|
||||
}},
|
||||
(($($piece:literal),*) ($($processed:expr => $bound:expr),*) ($val:expr $(,$rest:expr)*)) => {
|
||||
// Use of `match` here is intentional because it affects the lifetimes
|
||||
// of temporaries - https://stackoverflow.com/a/48732525/1063961
|
||||
match $val {
|
||||
tmp => {
|
||||
$crate::eprintln!("[{}:{}:{}] {} = {:#?}",
|
||||
$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
|
||||
}
|
||||
tmp => $crate::macros::dbg_internal!(
|
||||
($($piece,)* "[{file}:{line}:{column}] {} = {:#?}\n")
|
||||
($($processed => $bound,)* $val => tmp)
|
||||
($($rest),*)
|
||||
),
|
||||
}
|
||||
};
|
||||
($($val:expr),+ $(,)?) => {
|
||||
($($crate::dbg!($val)),+,)
|
||||
};
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::{Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind};
|
||||
use rustc_hir::{Arm, 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};
|
||||
|
|
@ -90,33 +90,27 @@ impl LateLintPass<'_> for DbgMacro {
|
|||
(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})"))
|
||||
ExprKind::Match(first, arms, _) => {
|
||||
let vals = collect_vals(first, arms);
|
||||
let suggestion = match vals.as_slice() {
|
||||
// dbg!(1) => 1
|
||||
&[val] => {
|
||||
snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability)
|
||||
.to_string()
|
||||
}
|
||||
// dbg!(2, 3) => (2, 3)
|
||||
&[first, .., last] => {
|
||||
let snippet = snippet_with_applicability(
|
||||
cx,
|
||||
first.span.source_callsite().to(last.span.source_callsite()),
|
||||
"..",
|
||||
&mut applicability,
|
||||
);
|
||||
format!("({snippet})")
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
(macro_call.span, suggestion)
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
|
@ -169,3 +163,33 @@ 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))
|
||||
}
|
||||
|
||||
/// Extracts all value expressions from the `match`-tree generated by `dbg!`.
|
||||
///
|
||||
/// E.g. from
|
||||
/// ```rust, ignore
|
||||
/// match 1 {
|
||||
/// tmp_1 => match 2 {
|
||||
/// tmp_2 => {
|
||||
/// /* printing */
|
||||
/// (tmp_1, tmp_2)
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// this extracts `1` and `2`.
|
||||
fn collect_vals<'hir>(first: &'hir Expr<'hir>, mut arms: &'hir [Arm<'hir>]) -> Vec<&'hir Expr<'hir>> {
|
||||
let mut vals = vec![first];
|
||||
loop {
|
||||
let [arm] = arms else { unreachable!("dbg! macro expansion only has single-arm matches") };
|
||||
|
||||
match is_async_move_desugar(arm.body).unwrap_or(arm.body).peel_drop_temps().kind {
|
||||
ExprKind::Block(..) => return vals,
|
||||
ExprKind::Match(val, a, _) => {
|
||||
vals.push(val);
|
||||
arms = a;
|
||||
}
|
||||
_ => unreachable!("dbg! macro expansion only results in block or match expressions"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ help: ALLOC was deallocated here:
|
|||
|
|
||||
LL | };
|
||||
| ^
|
||||
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ LL | dbg!(x.0);
|
|||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation:
|
||||
ALLOC (stack variable, size: 132, align: 4) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ error[E0106]: missing lifetime specifier
|
|||
LL | dbg!(b);
|
||||
| ^^^^^^^ expected named lifetime parameter
|
||||
|
|
||||
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0425]: cannot find function `a` in this scope
|
||||
--> $DIR/ice-line-bounds-issue-148732.rs:1:7
|
||||
|
|
@ -37,7 +37,7 @@ LL | dbg!(b);
|
|||
| ^^^^^^^ the trait `Debug` is not implemented for fn item `fn() {b}`
|
||||
|
|
||||
= help: use parentheses to call this function: `b()`
|
||||
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ error[E0308]: mismatched types
|
|||
LL | x => dbg!(x),
|
||||
| ^^^^^^^ expected `()`, found integer
|
||||
|
|
||||
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: you might have meant to return this value
|
||||
|
|
||||
LL | x => return dbg!(x),
|
||||
|
|
@ -16,7 +16,7 @@ error[E0308]: mismatched types
|
|||
LL | dbg!(x)
|
||||
| ^^^^^^^ expected `()`, found integer
|
||||
|
|
||||
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: you might have meant to return this value
|
||||
|
|
||||
LL | return dbg!(x)
|
||||
|
|
@ -28,7 +28,7 @@ error[E0308]: mismatched types
|
|||
LL | _ => dbg!(1)
|
||||
| ^^^^^^^ expected `()`, found integer
|
||||
|
|
||||
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: you might have meant to return this value
|
||||
|
|
||||
LL | _ => return dbg!(1)
|
||||
|
|
@ -40,7 +40,7 @@ error[E0308]: mismatched types
|
|||
LL | _ => {dbg!(1)}
|
||||
| ^^^^^^^ expected `()`, found integer
|
||||
|
|
||||
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: you might have meant to return this value
|
||||
|
|
||||
LL | _ => {return dbg!(1)}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ error[E0277]: `Dummy` doesn't implement `Debug`
|
|||
| ^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `Dummy`
|
||||
|
|
||||
= note: add `#[derive(Debug)]` to `Dummy` or manually `impl Debug for Dummy`
|
||||
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: consider annotating `Dummy` with `#[derive(Debug)]`
|
||||
--> $DIR/auxiliary/dummy_lib.rs:2:1
|
||||
|
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ LL | let _ = dbg!(a);
|
|||
LL | let _ = dbg!(a);
|
||||
| ^^^^^^^ value used here after move
|
||||
|
|
||||
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: consider borrowing instead of transferring ownership
|
||||
|
|
||||
LL | let _ = dbg!(&a);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ LL | let _: NotDebug = dbg!(NotDebug);
|
|||
| ^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `NotDebug`
|
||||
|
|
||||
= note: add `#[derive(Debug)]` to `NotDebug` or manually `impl Debug for NotDebug`
|
||||
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: consider annotating `NotDebug` with `#[derive(Debug)]`
|
||||
|
|
||||
LL + #[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ error[E0308]: mismatched types
|
|||
LL | b"abc".iter().for_each(|x| dbg!(x));
|
||||
| ^^^^^^^ expected `()`, found `&u8`
|
||||
|
|
||||
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/closure-ty-mismatch-issue-128561.rs:8:9
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ error[E0308]: mismatched types
|
|||
LL | let c: S = dbg!(field);
|
||||
| ^^^^^^^^^^^ expected `S`, found `&S`
|
||||
|
|
||||
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: consider using clone here
|
||||
|
|
||||
LL | let c: S = dbg!(field).clone();
|
||||
|
|
@ -38,7 +38,7 @@ error[E0308]: mismatched types
|
|||
LL | let c: S = dbg!(dbg!(field));
|
||||
| ^^^^^^^^^^^^^^^^^ expected `S`, found `&S`
|
||||
|
|
||||
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: consider using clone here
|
||||
|
|
||||
LL | let c: S = dbg!(dbg!(field)).clone();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue