Skip to content

Commit 2546264

Browse files
committed
Improved support of collapse_debuginfo attribute for macroses. Enabled by default for builtin and core/std macroses.
1 parent 77d1699 commit 2546264

File tree

12 files changed

+415
-44
lines changed

12 files changed

+415
-44
lines changed

compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs

+1-9
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,7 @@ impl DebugContext {
6868
// In order to have a good line stepping behavior in debugger, we overwrite debug
6969
// locations of macro expansions with that of the outermost expansion site (when the macro is
7070
// annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided).
71-
let span = if tcx.should_collapse_debuginfo(span) {
72-
span
73-
} else {
74-
// Walk up the macro expansion chain until we reach a non-expanded span.
75-
// We also stop at the function body level because no line stepping can occur
76-
// at the level above that.
77-
rustc_span::hygiene::walk_chain(span, function_span.ctxt())
78-
};
79-
71+
let span = tcx.collapsed_debuginfo(span, function_span.ctxt());
8072
match tcx.sess.source_map().lookup_line(span.lo()) {
8173
Ok(SourceFileAndLine { sf: file, line }) => {
8274
let line_pos = file.lines()[line];

compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

+6-11
Original file line numberDiff line numberDiff line change
@@ -228,21 +228,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
228228
/// In order to have a good line stepping behavior in debugger, we overwrite debug
229229
/// locations of macro expansions with that of the outermost expansion site (when the macro is
230230
/// annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided).
231-
fn adjust_span_for_debugging(&self, mut span: Span) -> Span {
231+
fn adjust_span_for_debugging(&self, span: Span) -> Span {
232232
// Bail out if debug info emission is not enabled.
233233
if self.debug_context.is_none() {
234234
return span;
235235
}
236-
237-
if self.cx.tcx().should_collapse_debuginfo(span) {
238-
// Walk up the macro expansion chain until we reach a non-expanded span.
239-
// We also stop at the function body level because no line stepping can occur
240-
// at the level above that.
241-
// Use span of the outermost expansion site, while keeping the original lexical scope.
242-
span = rustc_span::hygiene::walk_chain(span, self.mir.span.ctxt());
243-
}
244-
245-
span
236+
// Walk up the macro expansion chain until we reach a non-expanded span.
237+
// We also stop at the function body level because no line stepping can occur
238+
// at the level above that.
239+
// Use span of the outermost expansion site, while keeping the original lexical scope.
240+
self.cx.tcx().collapsed_debuginfo(span, self.mir.span.ctxt())
246241
}
247242

248243
fn spill_operand_to_stack(

compiler/rustc_expand/src/base.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,8 @@ impl SyntaxExtension {
783783
let local_inner_macros = attr::find_by_name(attrs, sym::macro_export)
784784
.and_then(|macro_export| macro_export.meta_item_list())
785785
.is_some_and(|l| attr::list_contains_name(&l, sym::local_inner_macros));
786-
let collapse_debuginfo = attr::contains_name(attrs, sym::collapse_debuginfo);
786+
let collapse_debuginfo = attr::contains_name(attrs, sym::collapse_debuginfo)
787+
|| attr::contains_name(attrs, sym::rustc_builtin_macro);
787788
tracing::debug!(?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe);
788789

789790
let (builtin_name, helper_attrs) = attr::find_by_name(attrs, sym::rustc_builtin_macro)

compiler/rustc_middle/src/ty/mod.rs

+17-16
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ use rustc_session::lint::LintBuffer;
5050
pub use rustc_session::lint::RegisteredTools;
5151
use rustc_span::hygiene::MacroKind;
5252
use rustc_span::symbol::{kw, sym, Ident, Symbol};
53-
use rustc_span::{ExpnId, ExpnKind, Span};
53+
use rustc_span::{ExpnId, ExpnKind, Span, SyntaxContext};
5454
use rustc_target::abi::{Align, FieldIdx, Integer, IntegerType, VariantIdx};
5555
pub use rustc_target::abi::{ReprFlags, ReprOptions};
5656
pub use rustc_type_ir::{DebugWithInfcx, InferCtxtLike, WithInfcx};
@@ -2515,21 +2515,22 @@ impl<'tcx> TyCtxt<'tcx> {
25152515
(ident, scope)
25162516
}
25172517

2518-
/// Returns `true` if the debuginfo for `span` should be collapsed to the outermost expansion
2519-
/// site. Only applies when `Span` is the result of macro expansion.
2520-
///
2521-
/// - If the `collapse_debuginfo` feature is enabled then debuginfo is not collapsed by default
2522-
/// and only when a macro definition is annotated with `#[collapse_debuginfo]`.
2523-
/// - If `collapse_debuginfo` is not enabled, then debuginfo is collapsed by default.
2524-
///
2525-
/// When `-Zdebug-macros` is provided then debuginfo will never be collapsed.
2526-
pub fn should_collapse_debuginfo(self, span: Span) -> bool {
2527-
!self.sess.opts.unstable_opts.debug_macros
2528-
&& if self.features().collapse_debuginfo {
2529-
span.in_macro_expansion_with_collapse_debuginfo()
2530-
} else {
2531-
span.from_expansion()
2532-
}
2518+
/// Macros in standart library (core/alloc/std)
2519+
pub fn in_std_macro(self, macro_def_id: DefId) -> bool {
2520+
let crate_name = self.crate_name(macro_def_id.krate);
2521+
[sym::core, sym::alloc, sym::std].contains(&crate_name)
2522+
}
2523+
2524+
pub fn collapsed_debuginfo(self, span: Span, upto: SyntaxContext) -> Span {
2525+
let collapse_enabled = !self.sess.opts.unstable_opts.debug_macros;
2526+
let attr_enabled = self.features().collapse_debuginfo;
2527+
rustc_span::hygiene::walk_chain_collapsed(
2528+
span,
2529+
upto,
2530+
collapse_enabled,
2531+
attr_enabled,
2532+
|macro_def_id| self.in_std_macro(macro_def_id),
2533+
)
25332534
}
25342535

25352536
#[inline]

compiler/rustc_span/src/hygiene.rs

+69-3
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ impl HygieneData {
377377
self.local_expn_data[expn_id].as_ref().expect("no expansion data for an expansion ID")
378378
}
379379

380-
fn expn_data(&self, expn_id: ExpnId) -> &ExpnData {
380+
pub fn expn_data(&self, expn_id: ExpnId) -> &ExpnData {
381381
if let Some(expn_id) = expn_id.as_local() {
382382
self.local_expn_data[expn_id].as_ref().expect("no expansion data for an expansion ID")
383383
} else {
@@ -412,7 +412,7 @@ impl HygieneData {
412412
self.syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent
413413
}
414414

415-
fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId {
415+
pub fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId {
416416
self.syntax_context_data[ctxt.0 as usize].outer_expn
417417
}
418418

@@ -443,15 +443,65 @@ impl HygieneData {
443443
}
444444

445445
fn walk_chain(&self, mut span: Span, to: SyntaxContext) -> Span {
446+
let orig_span = span;
446447
debug!("walk_chain({:?}, {:?})", span, to);
447448
debug!("walk_chain: span ctxt = {:?}", span.ctxt());
448-
while span.from_expansion() && span.ctxt() != to {
449+
while span.ctxt() != to && span.from_expansion() {
449450
let outer_expn = self.outer_expn(span.ctxt());
450451
debug!("walk_chain({:?}): outer_expn={:?}", span, outer_expn);
451452
let expn_data = self.expn_data(outer_expn);
452453
debug!("walk_chain({:?}): expn_data={:?}", span, expn_data);
453454
span = expn_data.call_site;
454455
}
456+
debug!("walk_chain: for span {:?} >>> return span = {:?}", orig_span, span);
457+
span
458+
}
459+
460+
/// Returns `true` if the debuginfo for `span` should be collapsed to the outermost expansion
461+
/// site. Only applies when `Span` is the result of macro expansion.
462+
///
463+
/// - If the `collapse_debuginfo` feature is enabled then debuginfo is not collapsed by default
464+
/// and only when a macro definition is annotated with `#[collapse_debuginfo]`.
465+
/// - If `collapse_debuginfo` is not enabled, then debuginfo is collapsed by default.
466+
///
467+
/// When `-Zdebug-macros` is provided then debuginfo will never be collapsed.
468+
fn should_collapse_debuginfo<F: Fn(DefId) -> bool>(
469+
&self,
470+
span: Span,
471+
collapse_enabled: bool,
472+
attr_enabled: bool,
473+
in_std_macro: &F,
474+
) -> bool {
475+
collapse_enabled
476+
&& if attr_enabled {
477+
span.in_macro_expansion_with_collapse_debuginfo(&self, in_std_macro)
478+
} else {
479+
span.from_expansion()
480+
}
481+
}
482+
483+
fn walk_chain_collapsed<F: Fn(DefId) -> bool>(
484+
&self,
485+
mut span: Span,
486+
to: SyntaxContext,
487+
collapse_enabled: bool,
488+
attr_enabled: bool,
489+
in_std_macro: F,
490+
) -> Span {
491+
let orig_span = span;
492+
493+
debug!("walk_chain_collapsed({:?}, {:?})", span, to);
494+
debug!("walk_chain_collapsed: span ctxt = {:?}", span.ctxt());
495+
while span.ctxt() != to
496+
&& self.should_collapse_debuginfo(span, collapse_enabled, attr_enabled, &in_std_macro)
497+
{
498+
let outer_expn = self.outer_expn(span.ctxt());
499+
debug!("walk_chain_collapsed({:?}): outer_expn={:?}", span, outer_expn);
500+
let expn_data = self.expn_data(outer_expn);
501+
debug!("walk_chain_collapsed({:?}): expn_data={:?}", span, expn_data);
502+
span = expn_data.call_site;
503+
}
504+
debug!("walk_chain_collapsed: for span {:?} >>> return span = {:?}", orig_span, span);
455505
span
456506
}
457507

@@ -571,6 +621,22 @@ pub fn walk_chain(span: Span, to: SyntaxContext) -> Span {
571621
HygieneData::with(|data| data.walk_chain(span, to))
572622
}
573623

624+
pub fn walk_chain_collapsed<F: Fn(DefId) -> bool>(
625+
span: Span,
626+
to: SyntaxContext,
627+
collapse_enabled: bool,
628+
attr_enabled: bool,
629+
in_std_macro: F,
630+
) -> Span {
631+
HygieneData::with(|hdata| {
632+
if hdata.should_collapse_debuginfo(span, collapse_enabled, attr_enabled, &in_std_macro) {
633+
hdata.walk_chain_collapsed(span, to, collapse_enabled, attr_enabled, in_std_macro)
634+
} else {
635+
span
636+
}
637+
})
638+
}
639+
574640
pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) {
575641
// The new contexts that need updating are at the end of the list and have `$crate` as a name.
576642
let (len, to_update) = HygieneData::with(|data| {

compiler/rustc_span/src/lib.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ use source_map::SourceMap;
5353
pub mod edition;
5454
use edition::Edition;
5555
pub mod hygiene;
56-
use hygiene::Transparency;
5756
pub use hygiene::{DesugaringKind, ExpnKind, MacroKind};
5857
pub use hygiene::{ExpnData, ExpnHash, ExpnId, LocalExpnId, SyntaxContext};
58+
use hygiene::{HygieneData, Transparency};
5959
use rustc_data_structures::stable_hasher::HashingControls;
6060
pub mod def_id;
6161
use def_id::{CrateNum, DefId, DefPathHash, LocalDefId, LOCAL_CRATE};
@@ -570,9 +570,18 @@ impl Span {
570570

571571
/// Returns `true` if `span` originates in a macro's expansion where debuginfo should be
572572
/// collapsed.
573-
pub fn in_macro_expansion_with_collapse_debuginfo(self) -> bool {
574-
let outer_expn = self.ctxt().outer_expn_data();
575-
matches!(outer_expn.kind, ExpnKind::Macro(..)) && outer_expn.collapse_debuginfo
573+
pub(crate) fn in_macro_expansion_with_collapse_debuginfo<F: Fn(DefId) -> bool>(
574+
self,
575+
hdata: &HygieneData,
576+
in_std_macro: F,
577+
) -> bool {
578+
if self.from_expansion() {
579+
let outer = hdata.expn_data(hdata.outer_expn(self.ctxt())).clone();
580+
let std_macro = outer.macro_def_id.is_some_and(in_std_macro);
581+
!matches!(outer.kind, ExpnKind::Macro(..)) || outer.collapse_debuginfo || std_macro
582+
} else {
583+
false
584+
}
576585
}
577586

578587
/// Returns `true` if `span` originates in a derive-macro's expansion.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// ignore-lldb
2+
#![feature(collapse_debuginfo)]
3+
4+
// Test that statement, skipped by macros, is correctly processed in debuginfo
5+
6+
// compile-flags:-g
7+
8+
// === GDB TESTS ===================================================================================
9+
10+
// gdb-command:run
11+
// gdb-command:next
12+
// gdb-check:[...]#loc2[...]
13+
// gdb-command:step
14+
// gdb-command:frame
15+
// gdb-check:[...]#loc_call1_pre[...]
16+
// gdb-command:next
17+
// gdb-command:frame
18+
// gdb-check:[...]#loc_in_proxy[...]
19+
// gdb-command:finish
20+
// gdb-check:[...]#loc4[...]
21+
// gdb-command:next
22+
// gdb-command:frame
23+
// gdb-check:[...]#loc5[...]
24+
// gdb-command:continue
25+
26+
27+
macro_rules! proxy_println {
28+
($($arg:tt)*) => {{
29+
println!($($arg)*); // #loc_in_proxy
30+
}};
31+
}
32+
33+
// Macro accepts 3 statements and removes the 2nd statement
34+
macro_rules! remove_second_statement {
35+
($s1:stmt; $s2:stmt; $s3:stmt;) => { $s1 $s3 }
36+
}
37+
38+
fn call1() {
39+
let rv = 0; // #loc_call1_pre
40+
proxy_println!("one"); // #loc_call1
41+
}
42+
43+
fn call2() {
44+
proxy_println!("two"); // #loc_call2
45+
}
46+
47+
fn call3() {
48+
proxy_println!("three"); // #loc_call3
49+
}
50+
51+
fn main() {
52+
let ret = 0; // #break, step should go to call1
53+
remove_second_statement! { // #loc1
54+
call1(); // #loc2, breakpoint should set to call1, step should go call3
55+
call2(); // #loc3, breakpoint should set to call3
56+
call3(); // #loc4, breakpoint should set to call3, step should go closing brace
57+
}
58+
std::process::exit(ret); // #loc5
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// ignore-lldb
2+
#![feature(collapse_debuginfo)]
3+
4+
// Test that statement, skipped by macros, is correctly processed in debuginfo
5+
6+
// compile-flags:-g
7+
8+
// === GDB TESTS ===================================================================================
9+
10+
// gdb-command:run
11+
// gdb-command:next
12+
// gdb-command:frame
13+
// gdb-check:[...]#loc2[...]
14+
// gdb-command:step
15+
// gdb-command:frame
16+
// gdb-command:step
17+
// gdb-command:frame
18+
// gdb-check:[...]#loc_call1_println[...]
19+
// gdb-command:finish
20+
// gdb-command:finish
21+
// gdb-check:[...]#loc4[...]
22+
// gdb-command:next
23+
// gdb-command:frame
24+
// gdb-check:[...]#loc5[...]
25+
// gdb-command:continue
26+
27+
// Macro accepts 3 statements and removes the 2nd statement
28+
macro_rules! remove_second_statement {
29+
($s1:stmt; $s2:stmt; $s3:stmt;) => { $s1 $s3 }
30+
}
31+
32+
fn call1() {
33+
(||{
34+
println!("one") // #loc_call1_println
35+
})(); // #loc_call1
36+
}
37+
38+
fn call2() {
39+
println!("two"); // #loc_call2
40+
}
41+
42+
fn call3() {
43+
println!("three"); // #loc_call3
44+
}
45+
46+
fn main() {
47+
let ret = 0; // #break, step should go to call1
48+
remove_second_statement! { // #loc1
49+
call1(); // #loc2, breakpoint should set to call1, step should go call3
50+
call2(); // #loc3, breakpoint should set to call3
51+
call3(); // #loc4, breakpoint should set to call3, step should go closing brace
52+
}
53+
std::process::exit(ret); // #loc5
54+
}

0 commit comments

Comments
 (0)