diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index a7048e788b65a..f367edcbf5a81 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -300,7 +300,7 @@ pub(crate) fn build_impls( } /// `parent_module` refers to the parent of the re-export, not the original item -fn merge_attrs( +pub(crate) fn merge_attrs( cx: &mut DocContext<'_>, parent_module: Option, old_attrs: Attrs<'_>, diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 4c39021903c5b..909a47d07b166 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -482,7 +482,7 @@ impl Item { cx: &mut DocContext<'_>, cfg: Option>, ) -> Item { - trace!("name={:?}, def_id={:?}", name, def_id); + trace!("name={:?}, def_id={:?} cfg={:?}", name, def_id, cfg); // Primitives and Keywords are written in the source code as private modules. // The modules need to be private so that nobody actually uses them, but the @@ -801,6 +801,31 @@ impl ItemKind { | KeywordItem => [].iter(), } } + + /// Returns `true` if this item does not appear inside an impl block. + pub(crate) fn is_non_assoc(&self) -> bool { + matches!( + self, + StructItem(_) + | UnionItem(_) + | EnumItem(_) + | TraitItem(_) + | ModuleItem(_) + | ExternCrateItem { .. } + | FunctionItem(_) + | TypedefItem(_) + | OpaqueTyItem(_) + | StaticItem(_) + | ConstantItem(_) + | TraitAliasItem(_) + | ForeignFunctionItem(_) + | ForeignStaticItem(_) + | ForeignTypeItem + | MacroItem(_) + | ProcMacroItem(_) + | PrimitiveItem(_) + ) + } } #[derive(Clone, Debug)] diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index b1d2872019ef6..6272f47f460ca 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -516,7 +516,14 @@ fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option cfg.as_deref().cloned(), }; - debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.and_then(|p| p.cfg.as_ref()), cfg); + debug!( + "Portability {:?} {:?} (parent: {:?}) - {:?} = {:?}", + item.name, + item.cfg, + parent, + parent.and_then(|p| p.cfg.as_ref()), + cfg + ); Some(format!("
{}
", cfg?.render_long_html())) } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index a5668b318dcdc..912601dda20b1 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -477,7 +477,7 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> (cfg, _) => cfg.as_deref().cloned(), }; - debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.cfg, cfg); + debug!("Portability name={:?} {:?} - {:?} = {:?}", item.name, item.cfg, parent.cfg, cfg); if let Some(ref cfg) = cfg { tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html()); } diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 0c5d836551823..21d295bb1f88e 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -2,29 +2,53 @@ use std::sync::Arc; use crate::clean::cfg::Cfg; +use crate::clean::inline::{load_attrs, merge_attrs}; use crate::clean::{Crate, Item}; use crate::core::DocContext; use crate::fold::DocFolder; use crate::passes::Pass; +use rustc_hir::def_id::LocalDefId; + pub(crate) const PROPAGATE_DOC_CFG: Pass = Pass { name: "propagate-doc-cfg", run: propagate_doc_cfg, description: "propagates `#[doc(cfg(...))]` to child items", }; -pub(crate) fn propagate_doc_cfg(cr: Crate, _: &mut DocContext<'_>) -> Crate { - CfgPropagator { parent_cfg: None }.fold_crate(cr) +pub(crate) fn propagate_doc_cfg(cr: Crate, cx: &mut DocContext<'_>) -> Crate { + CfgPropagator { parent_cfg: None, parent: None, cx }.fold_crate(cr) } -struct CfgPropagator { +struct CfgPropagator<'a, 'tcx> { parent_cfg: Option>, + parent: Option, + cx: &'a mut DocContext<'tcx>, } -impl DocFolder for CfgPropagator { +impl<'a, 'tcx> DocFolder for CfgPropagator<'a, 'tcx> { fn fold_item(&mut self, mut item: Item) -> Option { let old_parent_cfg = self.parent_cfg.clone(); + if item.kind.is_non_assoc() && + let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) { + let hir = self.cx.tcx.hir(); + let hir_id = hir.local_def_id_to_hir_id(def_id); + let expected_parent = hir.get_parent_item(hir_id); + + // If parents are different, it means that `item` is a reexport and we need to compute + // the actual `cfg` by iterating through its "real" parents. + if self.parent != Some(expected_parent) { + let mut attrs = Vec::new(); + for (parent_hir_id, _) in hir.parent_iter(hir_id) { + let def_id = hir.local_def_id(parent_hir_id).to_def_id(); + attrs.extend_from_slice(load_attrs(self.cx, def_id)); + } + let (_, cfg) = + merge_attrs(self.cx, None, item.attrs.other_attrs.as_slice(), Some(&attrs)); + item.cfg = cfg; + } + } let new_cfg = match (self.parent_cfg.take(), item.cfg.take()) { (None, None) => None, (Some(rc), None) | (None, Some(rc)) => Some(rc), @@ -37,8 +61,15 @@ impl DocFolder for CfgPropagator { self.parent_cfg = new_cfg.clone(); item.cfg = new_cfg; + let old_parent = + if let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) { + self.parent.replace(def_id) + } else { + self.parent.take() + }; let result = self.fold_item_recur(item); self.parent_cfg = old_parent_cfg; + self.parent = old_parent; Some(result) } diff --git a/src/test/rustdoc/cfg_doc_reexport.rs b/src/test/rustdoc/cfg_doc_reexport.rs new file mode 100644 index 0000000000000..addb6709db1da --- /dev/null +++ b/src/test/rustdoc/cfg_doc_reexport.rs @@ -0,0 +1,33 @@ +#![feature(doc_cfg)] +#![feature(no_core)] + +#![crate_name = "foo"] +#![no_core] + +// @has 'foo/index.html' +// @has - '//*[@class="item-left module-item"]/*[@class="stab portability"]' 'foobar' +// @has - '//*[@class="item-left module-item"]/*[@class="stab portability"]' 'bar' + +#[doc(cfg(feature = "foobar"))] +mod imp_priv { + // @has 'foo/struct.BarPriv.html' + // @has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \ + // 'Available on crate feature foobar only.' + pub struct BarPriv {} + impl BarPriv { + pub fn test() {} + } +} +#[doc(cfg(feature = "foobar"))] +pub use crate::imp_priv::*; + +pub mod bar { + // @has 'foo/bar/struct.Bar.html' + // @has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \ + // 'Available on crate feature bar only.' + #[doc(cfg(feature = "bar"))] + pub struct Bar; +} + +#[doc(cfg(feature = "bar"))] +pub use bar::Bar;