From bed40af305e84b422c68caa2b5828547ce459e4a Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 11 Dec 2025 00:31:46 +0100 Subject: [PATCH] std: avoid tearing `dbg!` prints --- library/std/src/lib.rs | 4 +- library/std/src/macros.rs | 77 ++++++++++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 03451214ccb5..8c50c39e7b50 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -468,7 +468,9 @@ // 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 diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 25e2b7ea1370..0bb14552432d 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -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)),+,) - }; + }, }