diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 475dd348e7b63..3e3b481430043 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -16,6 +16,9 @@ expand_attributes_wrong_form = expand_cannot_be_name_of_macro = `{$trait_ident}` cannot be a name of {$macro_type} macro +expand_collapse_debuginfo_illegal = + illegal value for attribute #[collapse_debuginfo(no|external|yes)] + expand_count_repetition_misplaced = `count` can not be placed inside the inner-most repetition diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 0a1c44303970a..edc9c5a9130a6 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1,5 +1,6 @@ #![deny(rustc::untranslatable_diagnostic)] +use crate::base::ast::NestedMetaItem; use crate::errors; use crate::expand::{self, AstFragment, Invocation}; use crate::module::DirOwnership; @@ -19,6 +20,7 @@ use rustc_feature::Features; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, RegisteredTools}; use rustc_parse::{parser, MACRO_ARGUMENTS}; +use rustc_session::config::CollapseMacroDebuginfo; use rustc_session::errors::report_lit_error; use rustc_session::{parse::ParseSess, Limit, Session}; use rustc_span::def_id::{CrateNum, DefId, LocalDefId}; @@ -761,6 +763,55 @@ impl SyntaxExtension { } } + fn collapse_debuginfo_by_name(sess: &Session, attr: &Attribute) -> CollapseMacroDebuginfo { + use crate::errors::CollapseMacroDebuginfoIllegal; + // #[collapse_debuginfo] without enum value (#[collapse_debuginfo(no/external/yes)]) + // considered as `yes` + attr.meta_item_list().map_or(CollapseMacroDebuginfo::Yes, |l| { + let [NestedMetaItem::MetaItem(item)] = &l[..] else { + sess.dcx().emit_err(CollapseMacroDebuginfoIllegal { span: attr.span }); + return CollapseMacroDebuginfo::Unspecified; + }; + if !item.is_word() { + sess.dcx().emit_err(CollapseMacroDebuginfoIllegal { span: item.span }); + CollapseMacroDebuginfo::Unspecified + } else { + match item.name_or_empty() { + sym::no => CollapseMacroDebuginfo::No, + sym::external => CollapseMacroDebuginfo::External, + sym::yes => CollapseMacroDebuginfo::Yes, + _ => { + sess.dcx().emit_err(CollapseMacroDebuginfoIllegal { span: item.span }); + CollapseMacroDebuginfo::Unspecified + } + } + } + }) + } + + /// if-ext - if macro from different crate (related to callsite code) + /// | cmd \ attr | no | (unspecified) | external | yes | + /// | no | no | no | no | no | + /// | (unspecified) | no | no | if-ext | yes | + /// | external | no | if-ext | if-ext | yes | + /// | yes | yes | yes | yes | yes | + fn get_collapse_debuginfo(sess: &Session, attrs: &[ast::Attribute], is_local: bool) -> bool { + let collapse_debuginfo_attr = attr::find_by_name(attrs, sym::collapse_debuginfo) + .map(|v| Self::collapse_debuginfo_by_name(sess, v)) + .unwrap_or(CollapseMacroDebuginfo::Unspecified); + let flag = sess.opts.unstable_opts.collapse_macro_debuginfo; + let attr = collapse_debuginfo_attr; + let ext = !is_local; + #[rustfmt::skip] + let collapse_table = [ + [false, false, false, false], + [false, false, ext, true], + [false, ext, ext, true], + [true, true, true, true], + ]; + collapse_table[flag as usize][attr as usize] + } + /// Constructs a syntax extension with the given properties /// and other properties converted from attributes. pub fn new( @@ -772,6 +823,7 @@ impl SyntaxExtension { edition: Edition, name: Symbol, attrs: &[ast::Attribute], + is_local: bool, ) -> SyntaxExtension { let allow_internal_unstable = attr::allow_internal_unstable(sess, attrs).collect::>(); @@ -780,8 +832,8 @@ impl SyntaxExtension { let local_inner_macros = attr::find_by_name(attrs, sym::macro_export) .and_then(|macro_export| macro_export.meta_item_list()) .is_some_and(|l| attr::list_contains_name(&l, sym::local_inner_macros)); - let collapse_debuginfo = attr::contains_name(attrs, sym::collapse_debuginfo); - tracing::debug!(?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); + let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, is_local); + tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); let (builtin_name, helper_attrs) = attr::find_by_name(attrs, sym::rustc_builtin_macro) .map(|attr| { diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 2b43fae6852a1..4a1c00f0104fd 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -58,6 +58,13 @@ pub(crate) struct ResolveRelativePath { pub path: String, } +#[derive(Diagnostic)] +#[diag(expand_collapse_debuginfo_illegal)] +pub(crate) struct CollapseMacroDebuginfoIllegal { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(expand_macro_const_stability)] pub(crate) struct MacroConstStability { diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 363b52aef8a78..1a39708ed8ed8 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -367,6 +367,7 @@ pub fn compile_declarative_macro( edition, def.ident.name, &def.attrs, + def.id != DUMMY_NODE_ID, ) }; let dummy_syn_ext = || (mk_syn_ext(Box::new(macro_rules_dummy_expander)), Vec::new()); diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 4442b67df6e28..68b6f69854dd6 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -469,7 +469,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // `#[collapse_debuginfo]` gated!( - collapse_debuginfo, Normal, template!(Word), WarnFollowing, + collapse_debuginfo, Normal, template!(Word, List: "no|external|yes"), ErrorFollowing, experimental!(collapse_debuginfo) ), diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 588139a303c52..555c822ad6d68 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -4,10 +4,10 @@ use rustc_data_structures::profiling::TimePassesFormat; use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; use rustc_session::config::{ build_configuration, build_session_options, rustc_optgroups, BranchProtection, CFGuard, Cfg, - DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, Externs, - FunctionReturn, InliningThreshold, Input, InstrumentCoverage, InstrumentXRay, - LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli, NextSolverConfig, OomStrategy, - Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, Polonius, + CollapseMacroDebuginfo, DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, + ExternLocation, Externs, FunctionReturn, InliningThreshold, Input, InstrumentCoverage, + InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli, NextSolverConfig, + OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, }; use rustc_session::lint::Level; @@ -742,6 +742,7 @@ fn test_unstable_options_tracking_hash() { }) ); tracked!(codegen_backend, Some("abc".to_string())); + tracked!(collapse_macro_debuginfo, CollapseMacroDebuginfo::Yes); tracked!(crate_attr, vec!["abc".to_string()]); tracked!(cross_crate_inline_threshold, InliningThreshold::Always); tracked!(debug_info_for_profiling, true); diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 49e849964be47..20e3ae3ba9492 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1027,6 +1027,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self.root.edition, Symbol::intern(name), &attrs, + false, ) } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index ad9296a4cc88a..a5d9e0fcf44cd 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2535,8 +2535,7 @@ impl<'tcx> TyCtxt<'tcx> { if self.sess.opts.unstable_opts.debug_macros || !span.from_expansion() { return span; } - let collapse_debuginfo_enabled = self.features().collapse_debuginfo; - hygiene::walk_chain_collapsed(span, upto, collapse_debuginfo_enabled) + hygiene::walk_chain_collapsed(span, upto, self.features().collapse_debuginfo) } #[inline] diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index ac15c3b407b27..e751ff13a34ca 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3190,12 +3190,12 @@ pub enum WasiExecModel { /// how the hash should be calculated when adding a new command-line argument. pub(crate) mod dep_tracking { use super::{ - BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, DebugInfoCompression, - ErrorOutputType, FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentXRay, - LinkerPluginLto, LocationDetail, LtoCli, NextSolverConfig, OomStrategy, OptLevel, - OutFileName, OutputType, OutputTypes, Polonius, RemapPathScopeComponents, ResolveDocLinks, - SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, - WasiExecModel, + BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, CrateType, DebugInfo, + DebugInfoCompression, ErrorOutputType, FunctionReturn, InliningThreshold, + InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail, LtoCli, + NextSolverConfig, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes, Polonius, + RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, + SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, }; use crate::lint; use crate::utils::NativeLib; @@ -3274,6 +3274,7 @@ pub(crate) mod dep_tracking { LtoCli, DebugInfo, DebugInfoCompression, + CollapseMacroDebuginfo, UnstableFeatures, NativeLib, SanitizerSet, @@ -3437,6 +3438,25 @@ pub enum ProcMacroExecutionStrategy { CrossThread, } +/// How to perform collapse macros debug info +/// if-ext - if macro from different crate (related to callsite code) +/// | cmd \ attr | no | (unspecified) | external | yes | +/// | no | no | no | no | no | +/// | (unspecified) | no | no | if-ext | yes | +/// | external | no | if-ext | if-ext | yes | +/// | yes | yes | yes | yes | yes | +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum CollapseMacroDebuginfo { + /// Don't collapse debuginfo for the macro + No = 0, + /// Unspecified value + Unspecified = 1, + /// Collapse debuginfo if the macro comes from a different crate + External = 2, + /// Collapse debuginfo for the macro + Yes = 3, +} + /// Which format to use for `-Z dump-mono-stats` #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum DumpMonoStatsFormat { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 5bc88ae6ed172..1337ade62c019 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -388,6 +388,7 @@ mod desc { pub const parse_cfprotection: &str = "`none`|`no`|`n` (default), `branch`, `return`, or `full`|`yes`|`y` (equivalent to `branch` and `return`)"; pub const parse_debuginfo: &str = "either an integer (0, 1, 2), `none`, `line-directives-only`, `line-tables-only`, `limited`, or `full`"; pub const parse_debuginfo_compression: &str = "one of `none`, `zlib`, or `zstd`"; + pub const parse_collapse_macro_debuginfo: &str = "one of `no`, `external`, or `yes`"; pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`"; pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of(); pub const parse_optimization_fuel: &str = "crate=integer"; @@ -1302,6 +1303,19 @@ mod parse { true } + pub(crate) fn parse_collapse_macro_debuginfo( + slot: &mut CollapseMacroDebuginfo, + v: Option<&str>, + ) -> bool { + *slot = match v { + Some("no") => CollapseMacroDebuginfo::No, + Some("external") => CollapseMacroDebuginfo::External, + Some("yes") => CollapseMacroDebuginfo::Yes, + _ => return false, + }; + true + } + pub(crate) fn parse_proc_macro_execution_strategy( slot: &mut ProcMacroExecutionStrategy, v: Option<&str>, @@ -1534,6 +1548,9 @@ options! { "instrument control-flow architecture protection"), codegen_backend: Option = (None, parse_opt_string, [TRACKED], "the backend to use"), + collapse_macro_debuginfo: CollapseMacroDebuginfo = (CollapseMacroDebuginfo::Unspecified, + parse_collapse_macro_debuginfo, [TRACKED], + "set option to collapse debuginfo for macros"), combine_cgu: bool = (false, parse_bool, [TRACKED], "combine CGUs into a single one"), crate_attr: Vec = (Vec::new(), parse_string_push, [TRACKED], diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 5be794f3e17b9..3d26efec5a654 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -464,12 +464,15 @@ impl HygieneData { &self, mut span: Span, to: Span, - collapse_debuginfo_enabled: bool, + collapse_debuginfo_feature_enabled: bool, ) -> Span { let orig_span = span; let mut ret_span = span; - debug!("walk_chain_collapsed({:?}, {:?})", span, to); + debug!( + "walk_chain_collapsed({:?}, {:?}), feature_enable={}", + span, to, collapse_debuginfo_feature_enabled, + ); debug!("walk_chain_collapsed: span ctxt = {:?}", span.ctxt()); while !span.eq_ctxt(to) && span.from_expansion() { let outer_expn = self.outer_expn(span.ctxt()); @@ -477,7 +480,7 @@ impl HygieneData { let expn_data = self.expn_data(outer_expn); debug!("walk_chain_collapsed({:?}): expn_data={:?}", span, expn_data); span = expn_data.call_site; - if !collapse_debuginfo_enabled || expn_data.collapse_debuginfo { + if !collapse_debuginfo_feature_enabled || expn_data.collapse_debuginfo { ret_span = span; } } @@ -601,8 +604,14 @@ pub fn walk_chain(span: Span, to: SyntaxContext) -> Span { HygieneData::with(|data| data.walk_chain(span, to)) } -pub fn walk_chain_collapsed(span: Span, to: Span, collapse_debuginfo_enabled: bool) -> Span { - HygieneData::with(|hdata| hdata.walk_chain_collapsed(span, to, collapse_debuginfo_enabled)) +pub fn walk_chain_collapsed( + span: Span, + to: Span, + collapse_debuginfo_feature_enabled: bool, +) -> Span { + HygieneData::with(|hdata| { + hdata.walk_chain_collapsed(span, to, collapse_debuginfo_feature_enabled) + }) } pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 72b62a795fc50..44795022cbab3 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -749,6 +749,7 @@ symbols! { extern_in_paths, extern_prelude, extern_types, + external, external_doc, f, f16c_target_feature, @@ -1811,6 +1812,7 @@ symbols! { xmm_reg, yeet_desugar_details, yeet_expr, + yes, yield_expr, ymm_reg, zmm_reg, diff --git a/tests/debuginfo/collapse-debuginfo-external-attr.rs b/tests/debuginfo/collapse-debuginfo-external-attr.rs new file mode 100644 index 0000000000000..f36b0833ad519 --- /dev/null +++ b/tests/debuginfo/collapse-debuginfo-external-attr.rs @@ -0,0 +1,31 @@ +// ignore-lldb +#![feature(collapse_debuginfo)] + +// Test that local macro debug info is not collapsed with #[collapse_debuginfo(external)] + +// compile-flags:-g + +// === GDB TESTS =================================================================================== + +// gdb-command:run +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#one_callsite[...] +// gdb-command:continue + +fn one() { + println!("one"); +} + +#[collapse_debuginfo(external)] +macro_rules! outer { + () => { + one(); // #one_callsite + }; +} + +fn main() { + let ret = 0; // #break + outer!(); + std::process::exit(ret); +} diff --git a/tests/debuginfo/collapse-debuginfo-external-flag-overriden-by-attr.rs b/tests/debuginfo/collapse-debuginfo-external-flag-overriden-by-attr.rs new file mode 100644 index 0000000000000..e5cbc1a685d06 --- /dev/null +++ b/tests/debuginfo/collapse-debuginfo-external-flag-overriden-by-attr.rs @@ -0,0 +1,34 @@ +// ignore-lldb +#![feature(collapse_debuginfo)] + +// Test that macro attribute #[collapse_debuginfo(no)] +// overrides "collapse_macro_debuginfo=external" flag + +// compile-flags:-g -Z collapse_macro_debuginfo=external + +// === GDB TESTS =================================================================================== + +// gdb-command:run +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#one_callsite[...] +// gdb-command:next +// gdb-command:frame +// gdb-command:continue + +fn one() { + println!("one"); +} + +#[collapse_debuginfo(no)] +macro_rules! outer { + () => { + one(); // #one_callsite + }; +} + +fn main() { + let ret = 0; // #break + outer!(); + std::process::exit(ret); +} diff --git a/tests/debuginfo/collapse-debuginfo-external-flag.rs b/tests/debuginfo/collapse-debuginfo-external-flag.rs new file mode 100644 index 0000000000000..9a0aef38ea6c1 --- /dev/null +++ b/tests/debuginfo/collapse-debuginfo-external-flag.rs @@ -0,0 +1,26 @@ +// ignore-lldb +#![feature(collapse_debuginfo)] + +// Test that println macro debug info is collapsed with "collapse_macro_debuginfo=external" flag + +// compile-flags:-g -Z collapse_macro_debuginfo=external + +// === GDB TESTS =================================================================================== + +// gdb-command:run +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#println_callsite[...] +// gdb-command:continue + +macro_rules! outer { + () => { + println!("one"); // #println_callsite + }; +} + +fn main() { + let ret = 0; // #break + outer!(); + std::process::exit(ret); +} diff --git a/tests/debuginfo/collapse-debuginfo-no-attr.rs b/tests/debuginfo/collapse-debuginfo-no-attr.rs index 230c8795be365..d156c381a150e 100644 --- a/tests/debuginfo/collapse-debuginfo-no-attr.rs +++ b/tests/debuginfo/collapse-debuginfo-no-attr.rs @@ -4,7 +4,7 @@ // Test that line numbers are not replaced with those of the outermost expansion site when the // `collapse_debuginfo` feature is active and the attribute is not provided. -// compile-flags:-g +// compile-flags:-g -Z collapse_macro_debuginfo=no // === GDB TESTS =================================================================================== diff --git a/tests/debuginfo/collapse-debuginfo-with-yes-flag.rs b/tests/debuginfo/collapse-debuginfo-with-yes-flag.rs new file mode 100644 index 0000000000000..76a97a325d7ef --- /dev/null +++ b/tests/debuginfo/collapse-debuginfo-with-yes-flag.rs @@ -0,0 +1,57 @@ +// ignore-lldb +#![feature(collapse_debuginfo)] + +// Test that line numbers are replaced with those of the outermost expansion site when the +// `collapse_debuginfo` feature is active and the command line flag is provided. + +// compile-flags:-g -Z collapse_macro_debuginfo=yes + +// === GDB TESTS =================================================================================== + +// gdb-command:run +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc1[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc2[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc3[...] +// gdb-command:continue + +fn one() { + println!("one"); +} +fn two() { + println!("two"); +} +fn three() { + println!("three"); +} +fn four() { + println!("four"); +} + +macro_rules! outer { + ($b:block) => { + one(); + inner!(); + $b + }; +} + +macro_rules! inner { + () => { + two(); + }; +} + +fn main() { + let ret = 0; // #break + outer!({ // #loc1 + three(); // #loc2 + four(); // #loc3 + }); + std::process::exit(ret); +}