about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/debuginfo.rs8
-rw-r--r--compiler/rustc_error_messages/locales/en-US/passes.ftl3
-rw-r--r--compiler/rustc_expand/src/base.rs39
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs6
-rw-r--r--compiler/rustc_middle/src/ty/context.rs2
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs17
-rw-r--r--compiler/rustc_passes/src/check_attr.rs14
-rw-r--r--compiler/rustc_passes/src/errors.rs9
-rw-r--r--compiler/rustc_span/src/hygiene.rs28
-rw-r--r--compiler/rustc_span/src/lib.rs7
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--src/test/debuginfo/collapse-debuginfo-no-attr-flag.rs61
-rw-r--r--src/test/debuginfo/collapse-debuginfo-no-attr.rs60
-rw-r--r--src/test/debuginfo/collapse-debuginfo-with-attr-flag.rs63
-rw-r--r--src/test/debuginfo/collapse-debuginfo-with-attr.rs59
-rw-r--r--src/test/ui/attributes/collapse-debuginfo-invalid.rs110
-rw-r--r--src/test/ui/attributes/collapse-debuginfo-invalid.stderr222
-rw-r--r--src/test/ui/feature-gates/feature-gate-collapse_debuginfo.rs7
-rw-r--r--src/test/ui/feature-gates/feature-gate-collapse_debuginfo.stderr12
21 files changed, 700 insertions, 36 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
index 3ad0c420eaf..463de6a91c7 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
@@ -68,9 +68,9 @@ impl DebugContext {
     ) -> (Lrc<SourceFile>, u64, u64) {
         // Based on https://github.com/rust-lang/rust/blob/e369d87b015a84653343032833d65d0545fd3f26/src/librustc_codegen_ssa/mir/mod.rs#L116-L131
         // In order to have a good line stepping behavior in debugger, we overwrite debug
-        // locations of macro expansions with that of the outermost expansion site
-        // (unless the crate is being compiled with `-Z debug-macros`).
-        let span = if !span.from_expansion() || tcx.sess.opts.unstable_opts.debug_macros {
+        // locations of macro expansions with that of the outermost expansion site (when the macro is
+        // annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided).
+        let span = if tcx.should_collapse_debuginfo(span) {
             span
         } else {
             // Walk up the macro expansion chain until we reach a non-expanded span.
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index 8c3186efc63..157c1c82311 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -3,7 +3,7 @@ use rustc_index::vec::IndexVec;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::mir;
 use rustc_middle::ty;
-use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
 use rustc_session::config::DebugInfo;
 use rustc_span::symbol::{kw, Symbol};
 use rustc_span::{BytePos, Span};
@@ -93,15 +93,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     }
 
     /// In order to have a good line stepping behavior in debugger, we overwrite debug
-    /// locations of macro expansions with that of the outermost expansion site
-    /// (unless the crate is being compiled with `-Z debug-macros`).
+    /// locations of macro expansions with that of the outermost expansion site (when the macro is
+    /// annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided).
     fn adjust_span_for_debugging(&self, mut span: Span) -> Span {
         // Bail out if debug info emission is not enabled.
         if self.debug_context.is_none() {
             return span;
         }
 
-        if span.from_expansion() && !self.cx.sess().opts.unstable_opts.debug_macros {
+        if self.cx.tcx().should_collapse_debuginfo(span) {
             // Walk up the macro expansion chain until we reach a non-expanded span.
             // We also stop at the function body level because no line stepping can occur
             // at the level above that.
diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl
index 7374f6d3f27..556a6452f1a 100644
--- a/compiler/rustc_error_messages/locales/en-US/passes.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl
@@ -265,3 +265,6 @@ passes_rustc_lint_opt_deny_field_access = `#[rustc_lint_opt_deny_field_access]`
 
 passes_link_ordinal = attribute should be applied to a foreign function or static
     .label = not a foreign function or static
+
+passes_collapse_debuginfo = `collapse_debuginfo` attribute should be applied to macro definitions
+    .label = not a macro definition
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 2bb522caa2d..e1da3ecdec7 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -693,10 +693,6 @@ pub struct SyntaxExtension {
     pub span: Span,
     /// List of unstable features that are treated as stable inside this macro.
     pub allow_internal_unstable: Option<Lrc<[Symbol]>>,
-    /// Suppresses the `unsafe_code` lint for code produced by this macro.
-    pub allow_internal_unsafe: bool,
-    /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`) for this macro.
-    pub local_inner_macros: bool,
     /// The macro's stability info.
     pub stability: Option<Stability>,
     /// The macro's deprecation info.
@@ -708,6 +704,13 @@ pub struct SyntaxExtension {
     /// Built-in macros have a couple of special properties like availability
     /// in `#[no_implicit_prelude]` modules, so we have to keep this flag.
     pub builtin_name: Option<Symbol>,
+    /// Suppresses the `unsafe_code` lint for code produced by this macro.
+    pub allow_internal_unsafe: bool,
+    /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`) for this macro.
+    pub local_inner_macros: bool,
+    /// Should debuginfo for the macro be collapsed to the outermost expansion site (in other
+    /// words, was the macro definition annotated with `#[collapse_debuginfo]`)?
+    pub collapse_debuginfo: bool,
 }
 
 impl SyntaxExtension {
@@ -729,14 +732,15 @@ impl SyntaxExtension {
         SyntaxExtension {
             span: DUMMY_SP,
             allow_internal_unstable: None,
-            allow_internal_unsafe: false,
-            local_inner_macros: false,
             stability: None,
             deprecation: None,
             helper_attrs: Vec::new(),
             edition,
             builtin_name: None,
             kind,
+            allow_internal_unsafe: false,
+            local_inner_macros: false,
+            collapse_debuginfo: false,
         }
     }
 
@@ -754,12 +758,13 @@ impl SyntaxExtension {
         let allow_internal_unstable =
             attr::allow_internal_unstable(sess, &attrs).collect::<Vec<Symbol>>();
 
-        let mut local_inner_macros = false;
-        if let Some(macro_export) = sess.find_by_name(attrs, sym::macro_export) {
-            if let Some(l) = macro_export.meta_item_list() {
-                local_inner_macros = attr::list_contains_name(&l, sym::local_inner_macros);
-            }
-        }
+        let allow_internal_unsafe = sess.contains_name(attrs, sym::allow_internal_unsafe);
+        let local_inner_macros = sess
+            .find_by_name(attrs, sym::macro_export)
+            .and_then(|macro_export| macro_export.meta_item_list())
+            .map_or(false, |l| attr::list_contains_name(&l, sym::local_inner_macros));
+        let collapse_debuginfo = sess.contains_name(attrs, sym::collapse_debuginfo);
+        tracing::debug!(?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe);
 
         let (builtin_name, helper_attrs) = sess
             .find_by_name(attrs, sym::rustc_builtin_macro)
@@ -801,13 +806,14 @@ impl SyntaxExtension {
             span,
             allow_internal_unstable: (!allow_internal_unstable.is_empty())
                 .then(|| allow_internal_unstable.into()),
-            allow_internal_unsafe: sess.contains_name(attrs, sym::allow_internal_unsafe),
-            local_inner_macros,
             stability: stability.map(|(s, _)| s),
             deprecation: attr::find_deprecation(&sess, attrs).map(|(d, _)| d),
             helper_attrs,
             edition,
             builtin_name,
+            allow_internal_unsafe,
+            local_inner_macros,
+            collapse_debuginfo,
         }
     }
 
@@ -852,11 +858,12 @@ impl SyntaxExtension {
             call_site,
             self.span,
             self.allow_internal_unstable.clone(),
-            self.allow_internal_unsafe,
-            self.local_inner_macros,
             self.edition,
             macro_def_id,
             parent_module,
+            self.allow_internal_unsafe,
+            self.local_inner_macros,
+            self.collapse_debuginfo,
         )
     }
 }
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 2cbf348f13a..9232b774e26 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -338,6 +338,8 @@ declare_features! (
     (active, closure_track_caller, "1.57.0", Some(87417), None),
     /// Allows to use the `#[cmse_nonsecure_entry]` attribute.
     (active, cmse_nonsecure_entry, "1.48.0", Some(75835), None),
+    /// Allows use of the `#[collapse_debuginfo]` attribute.
+    (active, collapse_debuginfo, "CURRENT_RUSTC_VERSION", Some(100758), None),
     /// Allows `async {}` expressions in const contexts.
     (active, const_async_blocks, "1.53.0", Some(85368), None),
     // Allows limiting the evaluation steps of const expressions
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 8d88a90e395..b50c972e6f6 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -478,6 +478,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         experimental!(deprecated_safe),
     ),
 
+    // `#[collapse_debuginfo]`
+    gated!(
+        collapse_debuginfo, Normal, template!(Word), WarnFollowing,
+        experimental!(collapse_debuginfo)
+    ),
+
     // ==========================================================================
     // Internal attributes: Stability, deprecation, and unsafe:
     // ==========================================================================
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index afb2d5b2ba5..94eddef944a 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -1590,7 +1590,7 @@ impl<'tcx> TyCtxt<'tcx> {
         })
     }
 
-    // Returns the `DefId` and the `BoundRegionKind` corresponding to the given region.
+    /// Returns the `DefId` and the `BoundRegionKind` corresponding to the given region.
     pub fn is_suitable_region(self, region: Region<'tcx>) -> Option<FreeRegionInfo> {
         let (suitable_region_binding_scope, bound_region) = match *region {
             ty::ReFree(ref free_region) => {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index df72260597f..3ff62647105 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -2470,6 +2470,23 @@ impl<'tcx> TyCtxt<'tcx> {
         (ident, scope)
     }
 
+    /// Returns `true` if the debuginfo for `span` should be collapsed to the outermost expansion
+    /// site. Only applies when `Span` is the result of macro expansion.
+    ///
+    /// - If the `collapse_debuginfo` feature is enabled then debuginfo is not collapsed by default
+    ///   and only when a macro definition is annotated with `#[collapse_debuginfo]`.
+    /// - If `collapse_debuginfo` is not enabled, then debuginfo is collapsed by default.
+    ///
+    /// When `-Zdebug-macros` is provided then debuginfo will never be collapsed.
+    pub fn should_collapse_debuginfo(self, span: Span) -> bool {
+        !self.sess.opts.unstable_opts.debug_macros
+            && if self.features().collapse_debuginfo {
+                span.in_macro_expansion_with_collapse_debuginfo()
+            } else {
+                span.from_expansion()
+            }
+    }
+
     pub fn is_object_safe(self, key: DefId) -> bool {
         self.object_safety_violations(key).is_empty()
     }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index bb684e784c5..ebe4d2e9dd0 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -131,6 +131,7 @@ impl CheckAttrVisitor<'_> {
                 | sym::rustc_if_this_changed
                 | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr),
                 sym::cmse_nonsecure_entry => self.check_cmse_nonsecure_entry(attr, span, target),
+                sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target),
                 sym::const_trait => self.check_const_trait(attr, span, target),
                 sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target),
                 sym::must_use => self.check_must_use(hir_id, &attr, span, target),
@@ -431,6 +432,19 @@ impl CheckAttrVisitor<'_> {
         }
     }
 
+    /// Checks if `#[collapse_debuginfo]` is applied to a macro.
+    fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) -> bool {
+        match target {
+            Target::MacroDef => true,
+            _ => {
+                self.tcx
+                    .sess
+                    .emit_err(errors::CollapseDebuginfo { attr_span: attr.span, defn_span: span });
+                false
+            }
+        }
+    }
+
     /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
     fn check_track_caller(
         &self,
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 901f56ad96d..96cc8ae988c 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -649,3 +649,12 @@ pub struct RustcLintOptDenyFieldAccess {
     #[label]
     pub span: Span,
 }
+
+#[derive(SessionDiagnostic)]
+#[diag(passes::collapse_debuginfo)]
+pub struct CollapseDebuginfo {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub defn_span: Span,
+}
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs
index e8ddb4ed17a..191186af6fa 100644
--- a/compiler/rustc_span/src/hygiene.rs
+++ b/compiler/rustc_span/src/hygiene.rs
@@ -944,12 +944,6 @@ pub struct ExpnData {
     /// internally without forcing the whole crate to opt-in
     /// to them.
     pub allow_internal_unstable: Option<Lrc<[Symbol]>>,
-    /// Whether the macro is allowed to use `unsafe` internally
-    /// even if the user crate has `#![forbid(unsafe_code)]`.
-    pub allow_internal_unsafe: bool,
-    /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`)
-    /// for a given macro.
-    pub local_inner_macros: bool,
     /// Edition of the crate in which the macro is defined.
     pub edition: Edition,
     /// The `DefId` of the macro being invoked,
@@ -957,6 +951,13 @@ pub struct ExpnData {
     pub macro_def_id: Option<DefId>,
     /// The normal module (`mod`) in which the expanded macro was defined.
     pub parent_module: Option<DefId>,
+    /// Suppresses the `unsafe_code` lint for code produced by this macro.
+    pub allow_internal_unsafe: bool,
+    /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`) for this macro.
+    pub local_inner_macros: bool,
+    /// Should debuginfo for the macro be collapsed to the outermost expansion site (in other
+    /// words, was the macro definition annotated with `#[collapse_debuginfo]`)?
+    pub collapse_debuginfo: bool,
 }
 
 impl !PartialEq for ExpnData {}
@@ -969,11 +970,12 @@ impl ExpnData {
         call_site: Span,
         def_site: Span,
         allow_internal_unstable: Option<Lrc<[Symbol]>>,
-        allow_internal_unsafe: bool,
-        local_inner_macros: bool,
         edition: Edition,
         macro_def_id: Option<DefId>,
         parent_module: Option<DefId>,
+        allow_internal_unsafe: bool,
+        local_inner_macros: bool,
+        collapse_debuginfo: bool,
     ) -> ExpnData {
         ExpnData {
             kind,
@@ -981,12 +983,13 @@ impl ExpnData {
             call_site,
             def_site,
             allow_internal_unstable,
-            allow_internal_unsafe,
-            local_inner_macros,
             edition,
             macro_def_id,
             parent_module,
             disambiguator: 0,
+            allow_internal_unsafe,
+            local_inner_macros,
+            collapse_debuginfo,
         }
     }
 
@@ -1004,12 +1007,13 @@ impl ExpnData {
             call_site,
             def_site: DUMMY_SP,
             allow_internal_unstable: None,
-            allow_internal_unsafe: false,
-            local_inner_macros: false,
             edition,
             macro_def_id,
             parent_module,
             disambiguator: 0,
+            allow_internal_unsafe: false,
+            local_inner_macros: false,
+            collapse_debuginfo: false,
         }
     }
 
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index 34e2e92bdfc..26b4ebeab1b 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -564,6 +564,13 @@ impl Span {
         self.ctxt() != SyntaxContext::root()
     }
 
+    /// Returns `true` if `span` originates in a macro's expansion where debuginfo should be
+    /// collapsed.
+    pub fn in_macro_expansion_with_collapse_debuginfo(self) -> bool {
+        let outer_expn = self.ctxt().outer_expn_data();
+        matches!(outer_expn.kind, ExpnKind::Macro(..)) && outer_expn.collapse_debuginfo
+    }
+
     /// Returns `true` if `span` originates in a derive-macro's expansion.
     pub fn in_derive_expansion(self) -> bool {
         matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _))
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 1779ff4bcf1..416c42e3d51 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -487,6 +487,7 @@ symbols! {
         cmse_nonsecure_entry,
         coerce_unsized,
         cold,
+        collapse_debuginfo,
         column,
         column_macro,
         compare_and_swap,
diff --git a/src/test/debuginfo/collapse-debuginfo-no-attr-flag.rs b/src/test/debuginfo/collapse-debuginfo-no-attr-flag.rs
new file mode 100644
index 00000000000..413f6120105
--- /dev/null
+++ b/src/test/debuginfo/collapse-debuginfo-no-attr-flag.rs
@@ -0,0 +1,61 @@
+// ignore-lldb
+#![feature(collapse_debuginfo)]
+
+// Test that line numbers are not replaced with those of the outermost expansion site when the
+// `collapse_debuginfo` is active, `-Zdebug-macros` is provided and `#[collapse_debuginfo]` not
+// being used.
+
+// compile-flags:-g -Zdebug-macros
+
+// === 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:next
+// gdb-command:frame
+// gdb-check:[...]#loc4[...]
+// 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(); // #loc1
+        inner!();
+        $b
+    };
+}
+
+macro_rules! inner {
+    () => {
+        two(); // #loc2
+    };
+}
+
+fn main() {
+    let ret = 0; // #break
+    outer!({
+        three(); // #loc3
+        four(); // #loc4
+    });
+    std::process::exit(ret);
+}
diff --git a/src/test/debuginfo/collapse-debuginfo-no-attr.rs b/src/test/debuginfo/collapse-debuginfo-no-attr.rs
new file mode 100644
index 00000000000..230c8795be3
--- /dev/null
+++ b/src/test/debuginfo/collapse-debuginfo-no-attr.rs
@@ -0,0 +1,60 @@
+// ignore-lldb
+#![feature(collapse_debuginfo)]
+
+// 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
+
+// === 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:next
+// gdb-command:frame
+// gdb-check:[...]#loc4[...]
+// 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(); // #loc1
+        inner!();
+        $b
+    };
+}
+
+macro_rules! inner {
+    () => {
+        two(); // #loc2
+    };
+}
+
+fn main() {
+    let ret = 0; // #break
+    outer!({
+        three(); // #loc3
+        four(); // #loc4
+    });
+    std::process::exit(ret);
+}
diff --git a/src/test/debuginfo/collapse-debuginfo-with-attr-flag.rs b/src/test/debuginfo/collapse-debuginfo-with-attr-flag.rs
new file mode 100644
index 00000000000..183cf537e85
--- /dev/null
+++ b/src/test/debuginfo/collapse-debuginfo-with-attr-flag.rs
@@ -0,0 +1,63 @@
+// ignore-lldb
+#![feature(collapse_debuginfo)]
+
+// Test that line numbers are not replaced with those of the outermost expansion site when the
+// `collapse_debuginfo` is active and `-Zdebug-macros` is provided, despite `#[collapse_debuginfo]`
+// being used.
+
+// compile-flags:-g -Zdebug-macros
+
+// === 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:next
+// gdb-command:frame
+// gdb-check:[...]#loc4[...]
+// gdb-command:continue
+
+fn one() {
+    println!("one");
+}
+fn two() {
+    println!("two");
+}
+fn three() {
+    println!("three");
+}
+fn four() {
+    println!("four");
+}
+
+#[collapse_debuginfo]
+macro_rules! outer {
+    ($b:block) => {
+        one(); // #loc1
+        inner!();
+        $b
+    };
+}
+
+#[collapse_debuginfo]
+macro_rules! inner {
+    () => {
+        two(); // #loc2
+    };
+}
+
+fn main() {
+    let ret = 0; // #break
+    outer!({
+        three(); // #loc3
+        four(); // #loc4
+    });
+    std::process::exit(ret);
+}
diff --git a/src/test/debuginfo/collapse-debuginfo-with-attr.rs b/src/test/debuginfo/collapse-debuginfo-with-attr.rs
new file mode 100644
index 00000000000..34d03c18bc7
--- /dev/null
+++ b/src/test/debuginfo/collapse-debuginfo-with-attr.rs
@@ -0,0 +1,59 @@
+// 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 attribute is provided.
+
+// compile-flags:-g
+
+// === 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");
+}
+
+#[collapse_debuginfo]
+macro_rules! outer {
+    ($b:block) => {
+        one();
+        inner!();
+        $b
+    };
+}
+
+#[collapse_debuginfo]
+macro_rules! inner {
+    () => {
+        two();
+    };
+}
+
+fn main() {
+    let ret = 0; // #break
+    outer!({ // #loc1
+        three(); // #loc2
+        four(); // #loc3
+    });
+    std::process::exit(ret);
+}
diff --git a/src/test/ui/attributes/collapse-debuginfo-invalid.rs b/src/test/ui/attributes/collapse-debuginfo-invalid.rs
new file mode 100644
index 00000000000..42d8982c118
--- /dev/null
+++ b/src/test/ui/attributes/collapse-debuginfo-invalid.rs
@@ -0,0 +1,110 @@
+#![feature(collapse_debuginfo)]
+#![feature(stmt_expr_attributes)]
+#![feature(type_alias_impl_trait)]
+#![no_std]
+
+// Test that the `#[collapse_debuginfo]` attribute can only be used on macro definitions.
+
+#[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+extern crate std;
+
+#[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+use std::collections::HashMap;
+
+#[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+static FOO: u32 = 3;
+
+#[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+const BAR: u32 = 3;
+
+#[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+fn foo() {
+    let _ = #[collapse_debuginfo] || { };
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+    #[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+    let _ = 3;
+    let _ = #[collapse_debuginfo] 3;
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+    match (3, 4) {
+        #[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+        _ => (),
+    }
+}
+
+#[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+mod bar {
+}
+
+#[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+type Map = HashMap<u32, u32>;
+
+#[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+enum Foo {
+    #[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+    Variant,
+}
+
+#[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+struct Bar {
+    #[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+    field: u32,
+}
+
+#[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+union Qux {
+    a: u32,
+    b: u16
+}
+
+#[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+trait Foobar {
+    #[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+    type Bar;
+}
+
+#[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+type AFoobar = impl Foobar;
+
+impl Foobar for Bar {
+    type Bar = u32;
+}
+
+fn constraining() -> AFoobar {
+    Bar { field: 3 }
+}
+
+#[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+impl Bar {
+    #[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+    const FOO: u32 = 3;
+
+    #[collapse_debuginfo]
+//~^ ERROR `collapse_debuginfo` attribute should be applied to macro definitions
+    fn bar(&self) {}
+}
+
+#[collapse_debuginfo]
+macro_rules! finally {
+    ($e:expr) => { $e }
+}
+
+fn main() {}
diff --git a/src/test/ui/attributes/collapse-debuginfo-invalid.stderr b/src/test/ui/attributes/collapse-debuginfo-invalid.stderr
new file mode 100644
index 00000000000..01c47609108
--- /dev/null
+++ b/src/test/ui/attributes/collapse-debuginfo-invalid.stderr
@@ -0,0 +1,222 @@
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:8:1
+   |
+LL | #[collapse_debuginfo]
+   | ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | extern crate std;
+   | ----------------- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:12:1
+   |
+LL | #[collapse_debuginfo]
+   | ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | use std::collections::HashMap;
+   | ------------------------------ not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:16:1
+   |
+LL | #[collapse_debuginfo]
+   | ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | static FOO: u32 = 3;
+   | -------------------- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:20:1
+   |
+LL | #[collapse_debuginfo]
+   | ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | const BAR: u32 = 3;
+   | ------------------- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:24:1
+   |
+LL |   #[collapse_debuginfo]
+   |   ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | / fn foo() {
+LL | |     let _ = #[collapse_debuginfo] || { };
+LL | |
+LL | |     #[collapse_debuginfo]
+...  |
+LL | |     }
+LL | | }
+   | |_- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:27:13
+   |
+LL |     let _ = #[collapse_debuginfo] || { };
+   |             ^^^^^^^^^^^^^^^^^^^^^ ------ not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:29:5
+   |
+LL |     #[collapse_debuginfo]
+   |     ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |     let _ = 3;
+   |     ---------- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:32:13
+   |
+LL |     let _ = #[collapse_debuginfo] 3;
+   |             ^^^^^^^^^^^^^^^^^^^^^ - not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:35:9
+   |
+LL |         #[collapse_debuginfo]
+   |         ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |         _ => (),
+   |         ------- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:41:1
+   |
+LL |   #[collapse_debuginfo]
+   |   ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | / mod bar {
+LL | | }
+   | |_- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:46:1
+   |
+LL | #[collapse_debuginfo]
+   | ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | type Map = HashMap<u32, u32>;
+   | ----------------------------- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:50:1
+   |
+LL |   #[collapse_debuginfo]
+   |   ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | / enum Foo {
+LL | |     #[collapse_debuginfo]
+LL | |
+LL | |     Variant,
+LL | | }
+   | |_- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:53:5
+   |
+LL |     #[collapse_debuginfo]
+   |     ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |     Variant,
+   |     ------- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:58:1
+   |
+LL |   #[collapse_debuginfo]
+   |   ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | / struct Bar {
+LL | |     #[collapse_debuginfo]
+LL | |
+LL | |     field: u32,
+LL | | }
+   | |_- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:61:5
+   |
+LL |     #[collapse_debuginfo]
+   |     ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |     field: u32,
+   |     ---------- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:66:1
+   |
+LL |   #[collapse_debuginfo]
+   |   ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | / union Qux {
+LL | |     a: u32,
+LL | |     b: u16
+LL | | }
+   | |_- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:73:1
+   |
+LL |   #[collapse_debuginfo]
+   |   ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | / trait Foobar {
+LL | |     #[collapse_debuginfo]
+LL | |
+LL | |     type Bar;
+LL | | }
+   | |_- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:81:1
+   |
+LL | #[collapse_debuginfo]
+   | ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | type AFoobar = impl Foobar;
+   | --------------------------- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:93:1
+   |
+LL |   #[collapse_debuginfo]
+   |   ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | / impl Bar {
+LL | |     #[collapse_debuginfo]
+LL | |
+LL | |     const FOO: u32 = 3;
+...  |
+LL | |     fn bar(&self) {}
+LL | | }
+   | |_- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:76:5
+   |
+LL |     #[collapse_debuginfo]
+   |     ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |     type Bar;
+   |     --------- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:96:5
+   |
+LL |     #[collapse_debuginfo]
+   |     ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |     const FOO: u32 = 3;
+   |     ------------------- not a macro definition
+
+error: `collapse_debuginfo` attribute should be applied to macro definitions
+  --> $DIR/collapse-debuginfo-invalid.rs:100:5
+   |
+LL |     #[collapse_debuginfo]
+   |     ^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |     fn bar(&self) {}
+   |     ---------------- not a macro definition
+
+error: aborting due to 22 previous errors
+
diff --git a/src/test/ui/feature-gates/feature-gate-collapse_debuginfo.rs b/src/test/ui/feature-gates/feature-gate-collapse_debuginfo.rs
new file mode 100644
index 00000000000..f73bf579f6d
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-collapse_debuginfo.rs
@@ -0,0 +1,7 @@
+#[collapse_debuginfo]
+//~^ ERROR the `#[collapse_debuginfo]` attribute is an experimental feature
+macro_rules! foo {
+    ($e:expr) => { $e }
+}
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-collapse_debuginfo.stderr b/src/test/ui/feature-gates/feature-gate-collapse_debuginfo.stderr
new file mode 100644
index 00000000000..2cbde893af9
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-collapse_debuginfo.stderr
@@ -0,0 +1,12 @@
+error[E0658]: the `#[collapse_debuginfo]` attribute is an experimental feature
+  --> $DIR/feature-gate-collapse_debuginfo.rs:1:1
+   |
+LL | #[collapse_debuginfo]
+   | ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #100758 <https://github.com/rust-lang/rust/issues/100758> for more information
+   = help: add `#![feature(collapse_debuginfo)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.