From d4997ca71dd791f6e296f5e8c7a0c09a3dcf0a0b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 2 May 2026 03:44:34 +0200 Subject: [PATCH 1/3] Fix `doc_cfg` feature on reexports --- src/librustdoc/passes/propagate_doc_cfg.rs | 4 ++- tests/rustdoc-html/doc-cfg/reexports.rs | 34 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/rustdoc-html/doc-cfg/reexports.rs diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 1334595272f60..570117fd43e35 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -78,8 +78,10 @@ impl CfgPropagator<'_, '_> { // 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. // + // Same if it's an inlined item: we need to get the full original `cfg`. + // // Otherwise, `cfg_info` already tracks everything we need so nothing else to do! - if matches!(item.kind, ItemKind::ImplItem(_)) + if (matches!(item.kind, ItemKind::ImplItem(_)) || item.inline_stmt_id.is_some()) && let Some(mut next_def_id) = item.item_id.as_local_def_id() { while let Some(parent_def_id) = self.cx.tcx.opt_local_parent(next_def_id) { diff --git a/tests/rustdoc-html/doc-cfg/reexports.rs b/tests/rustdoc-html/doc-cfg/reexports.rs new file mode 100644 index 0000000000000..6522b3231122a --- /dev/null +++ b/tests/rustdoc-html/doc-cfg/reexports.rs @@ -0,0 +1,34 @@ +// This test ensures that reexports cfgs are correctly computed. +// Regression test for . + +// ignore-tidy-linelength + +#![feature(doc_cfg)] +#![crate_name = "foo"] + +//@has 'foo/struct.FlatBanana.html' +//@has - '//*[@class="item-info"]/*[@class="stab portability"]' 'Available on non-crate feature banana and non-crate feature yoyo only.' + +//@has 'foo/struct.SubBanana.html' +//@has - '//*[@class="item-info"]/*[@class="stab portability"]' 'Available on non-crate feature ananas and non-crate feature banana only.' + +//@has 'foo/struct.Yolo.html' +//@has - '//*[@class="item-info"]/*[@class="stab portability"]' 'Available on non-crate feature ananas and non-crate feature banana only.' + +#[cfg(not(feature = "yoyo"))] +pub use self::banana::*; +pub use self::banana::SubBanana as Yolo; + +#[cfg(not(feature = "banana"))] +mod banana { + /// Depends on `banana` feature. + pub struct FlatBanana {} + + #[cfg(not(feature = "ananas"))] + mod sub_banana { + /// Also depends on `banana` feature. + pub struct SubBanana {} + } + + pub use self::sub_banana::*; +} From 6f644d8a77c3c9baabfbed80147ae185e004b64f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 3 May 2026 03:26:34 +0200 Subject: [PATCH 2/3] Better use of conditions in `propagate_doc_cfg` --- src/librustdoc/passes/propagate_doc_cfg.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 570117fd43e35..a329948433357 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -81,18 +81,18 @@ impl CfgPropagator<'_, '_> { // Same if it's an inlined item: we need to get the full original `cfg`. // // Otherwise, `cfg_info` already tracks everything we need so nothing else to do! - if (matches!(item.kind, ItemKind::ImplItem(_)) || item.inline_stmt_id.is_some()) - && let Some(mut next_def_id) = item.item_id.as_local_def_id() - { - while let Some(parent_def_id) = self.cx.tcx.opt_local_parent(next_def_id) { - let x = load_attrs(self.cx.tcx, parent_def_id.to_def_id()); - add_only_cfg_attributes(&mut attrs, x); - next_def_id = parent_def_id; + if matches!(item.kind, ItemKind::ImplItem(_)) || item.inline_stmt_id.is_some() { + if let Some(mut next_def_id) = item.item_id.as_local_def_id() { + while let Some(parent_def_id) = self.cx.tcx.opt_local_parent(next_def_id) { + let x = load_attrs(self.cx.tcx, parent_def_id.to_def_id()); + add_only_cfg_attributes(&mut attrs, x); + next_def_id = parent_def_id; + } } } // 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(_)) + else if matches!(item.kind, ItemKind::MacroItem(_)) && item.inner.attrs.other_attrs.iter().any(|attr| { matches!( attr, From ba27564259a61696999feaa36150bdb035d76006 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 3 May 2026 03:27:09 +0200 Subject: [PATCH 3/3] Keep the original import `DefId` when inlining items through multiple reexports --- src/librustdoc/visit_ast.rs | 16 +++++++++++----- tests/rustdoc-html/doc-cfg/reexports.rs | 8 ++++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 9e989bbd5fd9e..d0b02c20644fe 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -244,6 +244,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { res: Res, renamed: Option, please_inline: bool, + import_id: Option, ) -> bool { debug!("maybe_inline_local (renamed: {renamed:?}) res: {res:?}"); @@ -338,20 +339,20 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let prev = mem::replace(&mut self.inlining, true); for &i in m.item_ids { let i = tcx.hir_item(i); - self.visit_item_inner(i, None, Some(def_id)); + self.visit_item_inner(i, None, Some(import_id.unwrap_or(def_id))); } self.inlining = prev; true } Node::Item(it) if !is_glob => { let prev = mem::replace(&mut self.inlining, true); - self.visit_item_inner(it, renamed, Some(def_id)); + self.visit_item_inner(it, renamed, Some(import_id.unwrap_or(def_id))); self.inlining = prev; true } Node::ForeignItem(it) if !is_glob => { let prev = mem::replace(&mut self.inlining, true); - self.visit_foreign_item_inner(it, renamed, Some(def_id)); + self.visit_foreign_item_inner(it, renamed, Some(import_id.unwrap_or(def_id))); self.inlining = prev; true } @@ -519,8 +520,13 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { hir::UseKind::Glob => None, hir::UseKind::ListStem => unreachable!(), }; - if self.maybe_inline_local(item.owner_id.def_id, res, ident, please_inline) - { + if self.maybe_inline_local( + item.owner_id.def_id, + res, + ident, + please_inline, + import_id, + ) { debug!("Inlining {:?}", item.owner_id.def_id); continue; } diff --git a/tests/rustdoc-html/doc-cfg/reexports.rs b/tests/rustdoc-html/doc-cfg/reexports.rs index 6522b3231122a..b954731b88222 100644 --- a/tests/rustdoc-html/doc-cfg/reexports.rs +++ b/tests/rustdoc-html/doc-cfg/reexports.rs @@ -10,13 +10,13 @@ //@has - '//*[@class="item-info"]/*[@class="stab portability"]' 'Available on non-crate feature banana and non-crate feature yoyo only.' //@has 'foo/struct.SubBanana.html' -//@has - '//*[@class="item-info"]/*[@class="stab portability"]' 'Available on non-crate feature ananas and non-crate feature banana only.' - -//@has 'foo/struct.Yolo.html' -//@has - '//*[@class="item-info"]/*[@class="stab portability"]' 'Available on non-crate feature ananas and non-crate feature banana only.' +//@has - '//*[@class="item-info"]/*[@class="stab portability"]' 'Available on non-crate feature ananas and non-crate feature banana and non-crate feature yoyo only.' #[cfg(not(feature = "yoyo"))] pub use self::banana::*; + +//@has 'foo/struct.Yolo.html' +//@has - '//*[@class="item-info"]/*[@class="stab portability"]' 'Available on non-crate feature ananas and non-crate feature banana only.' pub use self::banana::SubBanana as Yolo; #[cfg(not(feature = "banana"))]