Skip to content

Commit 1190584

Browse files
committed
Auto merge of #156106 - dianne:dbg-crater, r=<try>
[do not merge] crater `dbg!` changes against stable-1.95.0
2 parents 5980761 + 67f8de0 commit 1190584

16 files changed

Lines changed: 315 additions & 161 deletions

File tree

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -545,8 +545,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
545545
}
546546

547547
// for dbg!(x) which may take ownership, suggest dbg!(&x) instead
548-
// but here we actually do not check whether the macro name is `dbg!`
549-
// so that we may extend the scope a bit larger to cover more cases
550548
fn suggest_ref_for_dbg_args(
551549
&self,
552550
body: &hir::Expr<'_>,
@@ -560,31 +558,38 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
560558
});
561559
let Some(var_info) = var_info else { return };
562560
let arg_name = var_info.name;
563-
struct MatchArgFinder {
564-
expr_span: Span,
565-
match_arg_span: Option<Span>,
561+
struct DbgArgFinder<'tcx> {
562+
tcx: TyCtxt<'tcx>,
563+
move_span: Span,
566564
arg_name: Symbol,
565+
dbg_arg_span: Option<Span> = None,
567566
}
568-
impl Visitor<'_> for MatchArgFinder {
567+
impl Visitor<'_> for DbgArgFinder<'_> {
569568
fn visit_expr(&mut self, e: &hir::Expr<'_>) {
570-
// dbg! is expanded into a match pattern, we need to find the right argument span
571-
if let hir::ExprKind::Match(expr, ..) = &e.kind
569+
// dbg! is expanded into assignments, we need to find the right argument span
570+
if let hir::ExprKind::Assign(_, arg, _) = &e.kind
572571
&& let hir::ExprKind::Path(hir::QPath::Resolved(
573572
_,
574573
path @ Path { segments: [seg], .. },
575-
)) = &expr.kind
574+
)) = &arg.kind
576575
&& seg.ident.name == self.arg_name
577-
&& self.expr_span.source_callsite().contains(expr.span)
576+
&& self.move_span.source_equal(arg.span)
577+
&& e.span.macro_backtrace().any(|expn| {
578+
expn.macro_def_id.is_some_and(|macro_def_id| {
579+
self.tcx.is_diagnostic_item(sym::dbg_macro, macro_def_id)
580+
})
581+
})
578582
{
579-
self.match_arg_span = Some(path.span);
583+
self.dbg_arg_span = Some(path.span);
584+
return;
580585
}
581586
hir::intravisit::walk_expr(self, e);
582587
}
583588
}
584589

585-
let mut finder = MatchArgFinder { expr_span: move_span, match_arg_span: None, arg_name };
590+
let mut finder = DbgArgFinder { tcx: self.infcx.tcx, move_span, arg_name, .. };
586591
finder.visit_expr(body);
587-
if let Some(macro_arg_span) = finder.match_arg_span {
592+
if let Some(macro_arg_span) = finder.dbg_arg_span {
588593
err.span_suggestion_verbose(
589594
macro_arg_span.shrink_to_lo(),
590595
"consider borrowing instead of transferring ownership",

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,7 @@ symbols! {
763763
custom_test_frameworks,
764764
d,
765765
d32,
766+
dbg_macro,
766767
dead_code,
767768
dealloc,
768769
debug,

library/std/src/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,10 @@
255255
#![allow(unused_features)]
256256
//
257257
// Features:
258-
#![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count, rt))]
258+
#![cfg_attr(
259+
test,
260+
feature(internal_output_capture, print_internals, super_let, update_panic_count, rt)
261+
)]
259262
#![cfg_attr(
260263
all(target_vendor = "fortanix", target_env = "sgx"),
261264
feature(slice_index_methods, coerce_unsized, sgx_platform)
@@ -469,7 +472,9 @@ extern crate std as realstd;
469472

470473
// The standard macros that are not built-in to the compiler.
471474
#[macro_use]
472-
mod macros;
475+
#[doc(hidden)]
476+
#[unstable(feature = "std_internals", issue = "none")]
477+
pub mod macros;
473478

474479
// The runtime entry point and a few unstable public functions used by the
475480
// compiler

library/std/src/macros.rs

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
//! library.
66
// ignore-tidy-dbg
77

8+
#[cfg(test)]
9+
mod tests;
10+
811
#[doc = include_str!("../../core/src/macros/panic.md")]
912
#[macro_export]
1013
#[rustc_builtin_macro(std_panic)]
@@ -347,35 +350,73 @@ macro_rules! eprintln {
347350
/// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html
348351
/// [`log`]: https://crates.io/crates/log
349352
#[macro_export]
353+
#[allow_internal_unstable(std_internals)]
350354
#[cfg_attr(not(test), rustc_diagnostic_item = "dbg_macro")]
351355
#[stable(feature = "dbg_macro", since = "1.32.0")]
352356
macro_rules! dbg {
353-
// NOTE: We cannot use `concat!` to make a static string as a format argument
354-
// of `eprintln!` because `file!` could contain a `{` or
355-
// `$val` expression could be a block (`{ .. }`), in which case the `eprintln!`
356-
// will be malformed.
357357
() => {
358358
$crate::eprintln!("[{}:{}:{}]", $crate::file!(), $crate::line!(), $crate::column!())
359359
};
360-
($val:expr $(,)?) => {
361-
// Use of `match` here is intentional because it affects the lifetimes
362-
// of temporaries - https://stackoverflow.com/a/48732525/1063961
363-
match $val {
364-
tmp => {
365-
$crate::eprintln!("[{}:{}:{}] {} = {:#?}",
366-
$crate::file!(),
367-
$crate::line!(),
368-
$crate::column!(),
369-
$crate::stringify!($val),
370-
// The `&T: Debug` check happens here (not in the format literal desugaring)
371-
// to avoid format literal related messages and suggestions.
372-
&&tmp as &dyn $crate::fmt::Debug,
373-
);
374-
tmp
375-
}
376-
}
377-
};
378360
($($val:expr),+ $(,)?) => {
379-
($($crate::dbg!($val)),+,)
361+
$crate::macros::dbg_internal!(() () ($($val),+))
380362
};
381363
}
364+
365+
/// Internal macro that processes a list of expressions, binds their results, calls `eprint!` with
366+
/// the collected information, and returns all the evaluated expressions in a tuple.
367+
///
368+
/// E.g. `dbg_internal!(() () (1, 2))` expands into
369+
/// ```rust, ignore
370+
/// {
371+
/// let tmp_1;
372+
/// let tmp_2;
373+
/// super let _ = (tmp_1 = 1);
374+
/// super let _ = (tmp_2 = 2);
375+
/// eprint!("...", &tmp_1, &tmp_2, /* some other arguments */);
376+
/// (tmp_1, tmp_2)
377+
/// }
378+
/// ```
379+
///
380+
/// This is necessary so that `dbg!` outputs don't get torn, see #136703.
381+
/// `super let` is used to avoid creating a temporary scope around `dbg!`'s arguments. Nested
382+
/// `match` is insufficient because match arms introduce temporary scopes (#153850) and using a
383+
/// single match on a tuple containing all the arguments is insufficient because the borrow checker
384+
/// thinks that tuple can outlive the `dbg!` invocation if dropping the temporary places the tuple's
385+
/// elements were moved out of panics (not actually possible; they've been moved from). See #155902.
386+
#[doc(hidden)]
387+
#[allow_internal_unstable(std_internals, super_let)]
388+
#[rustc_macro_transparency = "semiopaque"]
389+
#[unstable(feature = "std_internals", issue = "none")]
390+
pub macro dbg_internal {
391+
(($($piece:literal),+) ($($processed:expr => $bound:ident),+) ()) => {{
392+
$(let $bound);+;
393+
$(super let _ = ($bound = $processed));+;
394+
$crate::eprint!(
395+
$crate::concat!($($piece),+),
396+
$(
397+
$crate::stringify!($processed),
398+
// The `&T: Debug` check happens here (not in the format literal desugaring)
399+
// to avoid format literal related messages and suggestions.
400+
&&$bound as &dyn $crate::fmt::Debug
401+
),+,
402+
// The location returned here is that of the macro invocation, so
403+
// it will be the same for all expressions. Thus, label these
404+
// arguments so that they can be reused in every piece of the
405+
// formatting template.
406+
file=$crate::file!(),
407+
line=$crate::line!(),
408+
column=$crate::column!()
409+
);
410+
// Comma separate the variables only when necessary so that this will
411+
// not yield a tuple for a single expression, but rather just parenthesize
412+
// the expression.
413+
($($bound),+)
414+
}},
415+
(($($piece:literal),*) ($($processed:expr => $bound:ident),*) ($val:expr $(,$rest:expr)*)) => {
416+
$crate::macros::dbg_internal!(
417+
($($piece,)* "[{file}:{line}:{column}] {} = {:#?}\n")
418+
($($processed => $bound,)* $val => tmp)
419+
($($rest),*)
420+
)
421+
},
422+
}

library/std/src/macros/tests.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// ignore-tidy-dbg
2+
3+
use core::fmt::Debug;
4+
5+
/// Test for <https://github.com/rust-lang/rust/issues/153850>:
6+
/// `dbg!` shouldn't drop arguments' temporaries.
7+
#[test]
8+
fn no_dropping_temps() {
9+
fn temp() {}
10+
11+
*dbg!(&temp());
12+
*dbg!(&temp(), 1).0;
13+
*dbg!(0, &temp()).1;
14+
*dbg!(0, &temp(), 2).1;
15+
}
16+
17+
/// Test for <https://github.com/rust-lang/rust/issues/154988>:
18+
/// `dbg!` shouldn't create a temporary that lives past its invocation.
19+
#[test]
20+
fn no_leaking_internal_temps_from_dbg() {
21+
#[derive(Debug)]
22+
struct Foo;
23+
24+
#[derive(Debug)]
25+
struct Bar<'a>(#[allow(unused)] &'a Foo);
26+
impl Drop for Bar<'_> {
27+
fn drop(&mut self) {}
28+
}
29+
30+
let foo = Foo;
31+
let bar = Bar(&foo);
32+
// If `dbg!` creates a `(Bar<'_>,)` temporary that lasts past its expansion, this will fail
33+
// to compile, because it will be dropped after `foo`, which it borrows from. The tuple
34+
// mimics the drop order of block tail expressions before Rust 2024: first the result of `dbg!`
35+
// is dropped, then `foo`, then any temporaries left over from `dbg!` are dropped, if present.
36+
(drop(dbg!(bar)), drop(foo));
37+
}
38+
39+
/// Test for <https://github.com/rust-lang/rust/issues/155902>:
40+
/// `dbg!` shouldn't create a temporary that borrowck thinks can live past its invocation on a false
41+
/// unwind path.
42+
#[test]
43+
fn no_leaking_internal_temps_from_dbg_even_on_false_unwind() {
44+
#[derive(Debug)]
45+
struct Foo;
46+
impl Drop for Foo {
47+
fn drop(&mut self) {}
48+
}
49+
50+
#[derive(Debug)]
51+
struct Bar<'a>(#[allow(unused)] &'a Foo);
52+
impl Drop for Bar<'_> {
53+
fn drop(&mut self) {}
54+
}
55+
56+
{
57+
let foo = Foo;
58+
let bar = Bar(&foo);
59+
// The temporaries of this `super let`'s scrutinee will outlive `bar` and `foo`, emulating
60+
// the drop order of block tail expressions before Rust 2024. If borrowck thinks that a
61+
// panic from moving `bar` is possible and that a `Bar<'_>`-containing temporary lives past
62+
// the end of the block because of that, this will fail to compile. Because `Foo` implements
63+
// `Drop`, it's an error for `foo` to be dropped before such a temporary when unwinding;
64+
// otherwise, `foo` would just live to the end of the stack frame when unwinding.
65+
super let _ = drop(dbg!(bar));
66+
}
67+
}

src/tools/clippy/clippy_lints/src/dbg_macro.rs

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use clippy_config::Conf;
22
use clippy_utils::diagnostics::span_lint_and_then;
3-
use clippy_utils::{is_in_test, sym};
43
use clippy_utils::macros::{MacroCall, macro_backtrace};
54
use clippy_utils::source::snippet_with_applicability;
5+
use clippy_utils::{is_in_test, sym};
66
use rustc_data_structures::fx::FxHashSet;
77
use rustc_errors::Applicability;
88
use rustc_hir::{Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind};
@@ -75,13 +75,33 @@ impl LateLintPass<'_> for DbgMacro {
7575
"the `dbg!` macro is intended as a debugging tool",
7676
|diag| {
7777
let mut applicability = Applicability::MachineApplicable;
78-
let (sugg_span, suggestion) = match is_async_move_desugar(expr)
79-
.unwrap_or(expr)
80-
.peel_drop_temps()
81-
.kind
82-
{
78+
let dbg_expn = is_async_move_desugar(expr).unwrap_or(expr).peel_drop_temps();
79+
// `dbg!` always expands to a block. If it was given arguments, it assigns names to them
80+
// using `super let _ = (tmp = $arg);` statements.
81+
let ExprKind::Block(block, _) = dbg_expn.kind else {
82+
unreachable!()
83+
};
84+
let args: Vec<_> = block
85+
.stmts
86+
.iter()
87+
.filter_map(|stmt| {
88+
if let StmtKind::Let(LetStmt {
89+
super_: Some(_),
90+
init: Some(init),
91+
..
92+
}) = stmt.kind
93+
&& let ExprKind::Assign(_, arg, _) = init.kind
94+
{
95+
Some(arg)
96+
} else {
97+
None
98+
}
99+
})
100+
.collect();
101+
102+
let (sugg_span, suggestion) = match args.as_slice() {
83103
// dbg!()
84-
ExprKind::Block(..) => {
104+
[] => {
85105
// If the `dbg!` macro is a "free" statement and not contained within other expressions,
86106
// remove the whole statement.
87107
if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id)
@@ -92,26 +112,15 @@ impl LateLintPass<'_> for DbgMacro {
92112
(macro_call.span, String::from("()"))
93113
}
94114
},
95-
// dbg!(1)
96-
ExprKind::Match(val, ..) => (
97-
macro_call.span,
98-
snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability)
99-
.to_string(),
100-
),
101-
// dbg!(2, 3)
102-
ExprKind::Tup(
103-
[
104-
Expr {
105-
kind: ExprKind::Match(first, ..),
106-
..
107-
},
108-
..,
109-
Expr {
110-
kind: ExprKind::Match(last, ..),
111-
..
112-
},
113-
],
114-
) => {
115+
// dbg!(1) => 1
116+
[val] => {
117+
let suggestion =
118+
snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability)
119+
.to_string();
120+
(macro_call.span, suggestion)
121+
},
122+
// dbg!(2, 3) => (2, 3)
123+
[first, .., last] => {
115124
let snippet = snippet_with_applicability(
116125
cx,
117126
first.span.source_callsite().to(last.span.source_callsite()),
@@ -120,7 +129,6 @@ impl LateLintPass<'_> for DbgMacro {
120129
);
121130
(macro_call.span, format!("({snippet})"))
122131
},
123-
_ => unreachable!(),
124132
};
125133

126134
diag.span_suggestion(

src/tools/clippy/clippy_utils/src/sym.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,6 @@ generate! {
199199
cx,
200200
cycle,
201201
cyclomatic_complexity,
202-
dbg_macro,
203202
de,
204203
debug_struct,
205204
deprecated_in_future,

src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: Undefined Behavior: memory access failed: ALLOC has been freed, so this p
22
--> tests/fail/dangling_pointers/dangling_primitive.rs:LL:CC
33
|
44
LL | dbg!(*ptr);
5-
| ^^^^^^^^^^ Undefined Behavior occurred here
5+
| ^^^^ Undefined Behavior occurred here
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ error: Undefined Behavior: reading memory at ALLOC[0x0..0x4], but memory is unin
77
--> tests/fail/function_calls/return_pointer_on_unwind.rs:LL:CC
88
|
99
LL | dbg!(x.0);
10-
| ^^^^^^^^^ Undefined Behavior occurred here
10+
| ^^^ Undefined Behavior occurred here
1111
|
1212
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
1313
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

0 commit comments

Comments
 (0)