Skip to content

rustdoc: preserve parent doc cfg for macro_export macros#155954

Merged
rust-bors[bot] merged 1 commit intorust-lang:mainfrom
cijiugechu:doc_cfg-decl-macro
May 1, 2026
Merged

rustdoc: preserve parent doc cfg for macro_export macros#155954
rust-bors[bot] merged 1 commit intorust-lang:mainfrom
cijiugechu:doc_cfg-decl-macro

Conversation

@cijiugechu
Copy link
Copy Markdown
Member

@cijiugechu cijiugechu commented Apr 29, 2026

The detached-item context is discovered before propagate_doc_cfg, it is carried on the clean item and consumed by the pass.

Closes #100916

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output. labels Apr 29, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 29, 2026

r? @notriddle

rustbot has assigned @notriddle.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: rustdoc
  • rustdoc expanded to 9 candidates
  • Random selection from GuillaumeGomez, fmease, lolbinarycat, notriddle


/// This function goes through the attributes list (`new_attrs`) and extracts the attributes that
/// affect the cfg state propagated to detached items.
fn add_cfg_state_attributes(attrs: &mut Vec<Attribute>, new_attrs: &[Attribute]) {
Copy link
Copy Markdown
Member Author

@cijiugechu cijiugechu Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept them(this function and add_only_cfg_attributes) separate because a shared helper would make the logic harder to follow.

View changes since the review

@cijiugechu cijiugechu added the F-doc_cfg `#![feature(doc_cfg)]` label Apr 29, 2026
@cijiugechu cijiugechu changed the title rustdoc: preserve parent doc cfg for macro_export macros rustdoc: preserve parent doc cfg for macro_export macros Apr 29, 2026
@notriddle
Copy link
Copy Markdown
Contributor

Please don't plumb a new attribute through clean::ItemInner if you can avoid it. Doing that causes rustdoc to use more memory, and it's often avoidable.

When I change the code to call cfg_parent_ids_for_detached_item from CfgPropagator itself, as described in this diff, the new test cases still pass.

diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index f0371f8482d..554b81b14cd 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -131,7 +131,6 @@ fn synthesize_auto_trait_impl<'tcx>(
             item_id: clean::ItemId::Auto { trait_: trait_def_id, for_: item_def_id },
             cfg: None,
             inline_stmt_id: None,
-            cfg_parent_ids: Default::default(),
         }),
     })
 }
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index 6978d03bcf8..8dda831cac8 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -126,7 +126,6 @@ pub(crate) fn synthesize_blanket_impls(
                     })),
                     cfg: None,
                     inline_stmt_id: None,
-                    cfg_parent_ids: Default::default(),
                 }),
             });
         }
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 112516a564c..3ca22b0360b 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -725,7 +725,6 @@ fn build_module_items(
                         )),
                         cfg: None,
                         inline_stmt_id: None,
-                        cfg_parent_ids: Default::default(),
                     }),
                 });
             } else if let Some(i) = try_inline(cx, res, item.ident.name, attrs, visited) {
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 9b21ebb645c..4b52ce0e36c 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -101,10 +101,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
         if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
             return Vec::new();
         }
-        let mut v = clean_maybe_renamed_item(cx, item, entry.renamed, &entry.import_ids);
-        for item in &mut v {
-            item.inner.cfg_parent_ids = entry.cfg_parent_ids.iter().copied().collect();
-        }
+        let v = clean_maybe_renamed_item(cx, item, entry.renamed, &entry.import_ids);
         for item in &v {
             if let Some(name) = item.name
                 && (cx.document_hidden() || !item.is_doc_hidden())
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index cfb1ce268f0..e2db9132320 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -25,6 +25,7 @@
     DocFragment, add_doc_fragment, attrs_to_doc_fragments, inner_docs, span_of_fragments,
 };
 use rustc_session::Session;
+use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{Symbol, kw, sym};
 use rustc_span::{DUMMY_SP, FileName, Ident, Loc, RemapPathScopeComponents};
@@ -359,9 +360,6 @@ pub(crate) struct ItemInner {
     /// The crate metadata doesn't hold this information, so the `use` statement
     /// always belongs to the current crate.
     pub(crate) inline_stmt_id: Option<LocalDefId>,
-    /// Lexical parents whose cfg/doc(cfg) information applies even though the item is documented
-    /// elsewhere, such as `#[macro_export] macro_rules!` items rendered at the crate root.
-    pub(crate) cfg_parent_ids: ThinVec<LocalDefId>,
     pub(crate) cfg: Option<Arc<Cfg>>,
 }
 
@@ -408,6 +406,23 @@ fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
 }
 
 impl Item {
+    pub(crate) fn cfg_parent_ids_for_detached_item(&self, tcx: TyCtxt<'_>) -> Vec<LocalDefId> {
+        let Some(def_id) = self.inline_stmt_id.or(self.item_id.as_local_def_id()) else {
+            return Vec::new();
+        };
+        let mut ids = Vec::new();
+        let mut next = def_id;
+        while let Some(parent) = tcx.opt_local_parent(next) {
+            if parent == CRATE_DEF_ID {
+                break;
+            }
+            ids.push(parent);
+            next = parent;
+        }
+        ids.reverse();
+        ids
+    }
+
     /// Returns the effective stability of the item.
     ///
     /// This method should only be called after the `propagate-stability` pass has been run.
@@ -560,7 +575,6 @@ pub(crate) fn from_def_id_and_attrs_and_parts(
                 name,
                 cfg,
                 inline_stmt_id: None,
-                cfg_parent_ids: ThinVec::new(),
             }),
         }
     }
@@ -2449,7 +2463,7 @@ mod size_asserts {
     static_assert_size!(GenericParamDef, 40);
     static_assert_size!(Generics, 16);
     static_assert_size!(Item, 8);
-    static_assert_size!(ItemInner, 144);
+    static_assert_size!(ItemInner, 136);
     static_assert_size!(ItemKind, 48);
     static_assert_size!(PathSegment, 32);
     static_assert_size!(Type, 32);
diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs
index 4cd6f6c240f..1334595272f 100644
--- a/src/librustdoc/passes/propagate_doc_cfg.rs
+++ b/src/librustdoc/passes/propagate_doc_cfg.rs
@@ -75,7 +75,7 @@ impl CfgPropagator<'_, '_> {
     // (mostly `cfg` ones) will be missing.
     fn merge_with_parent_attributes(&mut self, item: &mut Item) {
         let mut attrs = Vec::new();
-        // We only need to merge an item attributes with its parent's in case it's an impl as an
+        // We need to merge an item attributes with its parent's in case it's an impl as an
         // impl might not be defined in the same module as the item it implements.
         //
         // Otherwise, `cfg_info` already tracks everything we need so nothing else to do!
@@ -88,13 +88,26 @@ fn merge_with_parent_attributes(&mut self, item: &mut Item) {
                 next_def_id = parent_def_id;
             }
         }
-        for parent_def_id in &item.cfg_parent_ids {
-            let mut parent_attrs = Vec::new();
-            add_cfg_state_attributes(
-                &mut parent_attrs,
-                load_attrs(self.cx.tcx, parent_def_id.to_def_id()),
-            );
-            merge_attrs(self.cx.tcx, &[], Some((&parent_attrs, None)), &mut self.cfg_info);
+        // We also need to merge an item attributes with its parent's in case it's a macro with
+        // the `#[macro_export]` attribute, because it might not be defined at crate root.
+        if matches!(item.kind, ItemKind::MacroItem(_))
+            && item.inner.attrs.other_attrs.iter().any(|attr| {
+                matches!(
+                    attr,
+                    rustc_hir::Attribute::Parsed(
+                        rustc_hir::attrs::AttributeKind::MacroExport { .. }
+                    )
+                )
+            })
+        {
+            for parent_def_id in &item.cfg_parent_ids_for_detached_item(self.cx.tcx) {
+                let mut parent_attrs = Vec::new();
+                add_cfg_state_attributes(
+                    &mut parent_attrs,
+                    load_attrs(self.cx.tcx, parent_def_id.to_def_id()),
+                );
+                merge_attrs(self.cx.tcx, &[], Some((&parent_attrs, None)), &mut self.cfg_info);
+            }
         }
 
         let (_, cfg) = merge_attrs(
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 7a6a9bdd54a..5f119c23841 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -72,7 +72,6 @@ pub(crate) struct ItemEntry<'hir> {
     pub(crate) item: &'hir hir::Item<'hir>,
     pub(crate) renamed: Option<Symbol>,
     pub(crate) import_ids: Vec<LocalDefId>,
-    pub(crate) cfg_parent_ids: Vec<LocalDefId>,
 }
 
 impl Module<'_> {
@@ -150,21 +149,6 @@ fn store_path(&mut self, did: DefId) {
         self.exact_paths.entry(did).or_insert_with(|| def_id_to_path(tcx, did));
     }
 
-    fn cfg_parent_ids_for_detached_item(&self, def_id: LocalDefId) -> Vec<LocalDefId> {
-        let tcx = self.cx.tcx;
-        let mut ids = Vec::new();
-        let mut next = def_id;
-        while let Some(parent) = tcx.opt_local_parent(next) {
-            if parent == CRATE_DEF_ID {
-                break;
-            }
-            ids.push(parent);
-            next = parent;
-        }
-        ids.reverse();
-        ids
-    }
-
     pub(crate) fn visit(mut self) -> Module<'tcx> {
         let root_module = self.cx.tcx.hir_root_module();
         self.visit_mod_contents(CRATE_DEF_ID, root_module);
@@ -192,10 +176,9 @@ pub(crate) fn visit(mut self) -> Module<'tcx> {
             {
                 let item = self.cx.tcx.hir_expect_item(local_def_id);
                 let (ident, _, _) = item.expect_macro();
-                let cfg_parent_ids = self.cfg_parent_ids_for_detached_item(local_def_id);
                 top_level_module.items.insert(
                     (local_def_id, Some(ident.name)),
-                    ItemEntry { item, renamed: None, import_ids: Vec::new(), cfg_parent_ids },
+                    ItemEntry { item, renamed: None, import_ids: Vec::new() },
                 );
             }
         }
@@ -436,17 +419,13 @@ fn add_to_current_mod(
                     .items
                     .entry(key)
                     .and_modify(|v| v.import_ids.push(import_id))
-                    .or_insert_with(|| ItemEntry {
-                        item,
-                        renamed,
-                        import_ids: vec![import_id],
-                        cfg_parent_ids: Vec::new(),
-                    });
+                    .or_insert_with(|| ItemEntry { item, renamed, import_ids: vec![import_id] });
             } else {
-                self.modules.last_mut().unwrap().items.insert(
-                    key,
-                    ItemEntry { item, renamed, import_ids: Vec::new(), cfg_parent_ids: Vec::new() },
-                );
+                self.modules
+                    .last_mut()
+                    .unwrap()
+                    .items
+                    .insert(key, ItemEntry { item, renamed, import_ids: Vec::new() });
             }
         }
     }

Is there something I missed?

@notriddle
Copy link
Copy Markdown
Contributor

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 30, 2026
@cijiugechu
Copy link
Copy Markdown
Member Author

cijiugechu commented Apr 30, 2026

Is there something I missed?

No. I originally treated this as provenance metadata discovered in visit_ast, so the pass would consume explicit detached-item context instead of reinferring it; I updated accordingly.

@cijiugechu
Copy link
Copy Markdown
Member Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Apr 30, 2026
@notriddle
Copy link
Copy Markdown
Contributor

@bors try

@rust-timer queue

@rust-timer

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Apr 30, 2026
@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Apr 30, 2026
rustdoc: preserve parent doc cfg for `macro_export` macros
@rust-bors
Copy link
Copy Markdown
Contributor

rust-bors Bot commented Apr 30, 2026

☀️ Try build successful (CI)
Build commit: b90dfc5 (b90dfc57c315ee4d869934606a65dce3b80432ff, parent: f53b654a8882fd5fc036c4ca7a4ff41ce32497a6)

@rust-timer

This comment has been minimized.

@rust-timer
Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (b90dfc5): comparison URL.

Overall result: no relevant changes - no action needed

Benchmarking means the PR may be perf-sensitive. Consider adding rollup=never if this change is not fit for rolling up.

@rustbot label: -S-waiting-on-perf -perf-regression

Instruction count

This perf run didn't have relevant results for this metric.

Max RSS (memory usage)

Results (primary 2.0%, secondary 2.7%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
2.0% [1.7%, 2.2%] 3
Regressions ❌
(secondary)
2.7% [1.9%, 4.5%] 8
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 2.0% [1.7%, 2.2%] 3

Cycles

Results (primary 2.2%, secondary 2.7%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
2.2% [2.2%, 2.2%] 1
Regressions ❌
(secondary)
2.7% [2.7%, 2.7%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 2.2% [2.2%, 2.2%] 1

Binary size

Results (secondary 0.1%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
0.1% [0.1%, 0.1%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Bootstrap: 487.787s -> 482.444s (-1.10%)
Artifact size: 390.97 MiB -> 390.98 MiB (0.00%)

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Apr 30, 2026
@notriddle
Copy link
Copy Markdown
Contributor

This sounds fine.

@bors r+

@rust-bors
Copy link
Copy Markdown
Contributor

rust-bors Bot commented Apr 30, 2026

📌 Commit 09e9d3f has been approved by notriddle

It is now in the queue for this repository.

@rust-bors rust-bors Bot added the S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. label Apr 30, 2026
@rust-bors rust-bors Bot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Apr 30, 2026
rust-bors Bot pushed a commit that referenced this pull request May 1, 2026
Rollup of 19 pull requests

Successful merges:

 - #155237 (Disentangle AST crates and error crates)
 - #155249 (Fix: On wasm targets, call `panic_in_cleanup` if panic occurs in cleanup)
 - #155853 (Use `_mcount` as the mcount symbol name on RISC-V Linux GNU targets)
 - #155919 (simplify `ast_fragments!`)
 - #155939 (Add feature gate for view_types experiment)
 - #155954 (rustdoc: preserve parent doc cfg for `macro_export` macros)
 - #155974 (add `c_variadic_experimental_arch` feature)
 - #155991 (Catch unwinds from the global ctxt callback to complete queries profiling data in more cases)
 - #156003 (Pass Session to optimize_and_codegen_fat_lto)
 - #153566 (Add suggestion for E0401 on inner const items)
 - #154610 (Suggest public re-exports when a private module makes an import path inaccessible)
 - #155523 (Reorganize `tests/ui/issues/` - 02)
 - #155821 (c-variadic: document `Clone` and `Drop` instances and require `VaArgSafe: Copy`)
 - #155980 (Move `feature*` methods from `parse` mod to `errors` mod.)
 - #155987 (Make lifting infallible)
 - #155988 (tests/run-make/print-cfg: add Android target_env case)
 - #156000 (Fix ICE when using -Zinstrument-mcount and -Clinker-flavor=lld)
 - #156002 (Allow to use `Diagnostic` directly in `SharedContext::emit_lint`)
 - #156015 (rustc-dev-guide subtree update)
@rust-bors rust-bors Bot merged commit 0a05ddf into rust-lang:main May 1, 2026
12 checks passed
@rustbot rustbot added this to the 1.97.0 milestone May 1, 2026
rust-timer added a commit that referenced this pull request May 1, 2026
Rollup merge of #155954 - cijiugechu:doc_cfg-decl-macro, r=notriddle

rustdoc: preserve parent doc cfg for `macro_export` macros

The detached-item context is discovered before `propagate_doc_cfg`, it is carried on the clean item and consumed by the pass.

Closes #100916
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

F-doc_cfg `#![feature(doc_cfg)]` S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rustdoc doesn't automatically render "ambient" #[cfg]s on decl macros

4 participants