diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 2b12d118bca0e..765f7c61bd392 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use crate::clean::cfg::Cfg; use crate::clean::inline::{load_attrs, merge_attrs}; -use crate::clean::{Crate, Item}; +use crate::clean::{Crate, Item, ItemKind}; use crate::core::DocContext; use crate::fold::DocFolder; use crate::passes::Pass; @@ -26,30 +26,50 @@ struct CfgPropagator<'a, 'tcx> { cx: &'a mut DocContext<'tcx>, } -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(); +impl<'a, 'tcx> CfgPropagator<'a, 'tcx> { + // Some items need to merge their attributes with their parents' otherwise a few of them + // (mostly `cfg` ones) will be missing. + fn merge_with_parent_attributes(&mut self, item: &mut Item) { + let check_parent = match &*item.kind { + // impl blocks can be in different modules with different cfg and we need to get them + // as well. + ItemKind::ImplItem(_) => false, + kind if kind.is_non_assoc() => true, + _ => return, + }; - 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 Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) + else { return }; + + let hir = self.cx.tcx.hir(); + let hir_id = hir.local_def_id_to_hir_id(def_id); + + if check_parent { 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) { + return; + } + } - // 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) { - if let Some(def_id) = hir.opt_local_def_id(parent_hir_id) { - attrs.extend_from_slice(load_attrs(self.cx, def_id.to_def_id())); - } - } - let (_, cfg) = - merge_attrs(self.cx, None, item.attrs.other_attrs.as_slice(), Some(&attrs)); - item.cfg = cfg; + let mut attrs = Vec::new(); + for (parent_hir_id, _) in hir.parent_iter(hir_id) { + if let Some(def_id) = hir.opt_local_def_id(parent_hir_id) { + attrs.extend_from_slice(load_attrs(self.cx, def_id.to_def_id())); } } + let (_, cfg) = merge_attrs(self.cx, None, item.attrs.other_attrs.as_slice(), Some(&attrs)); + item.cfg = cfg; + } +} + +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(); + + self.merge_with_parent_attributes(&mut item); + let new_cfg = match (self.parent_cfg.take(), item.cfg.take()) { (None, None) => None, (Some(rc), None) | (None, Some(rc)) => Some(rc), diff --git a/src/test/rustdoc/doc_auto_cfg_nested_impl.rs b/src/test/rustdoc/doc_auto_cfg_nested_impl.rs new file mode 100644 index 0000000000000..4d73e0d829ad5 --- /dev/null +++ b/src/test/rustdoc/doc_auto_cfg_nested_impl.rs @@ -0,0 +1,24 @@ +// Regression test for . + +#![feature(doc_auto_cfg)] +#![crate_type = "lib"] +#![crate_name = "foo"] + +pub struct S; +pub trait MyTrait1 {} +pub trait MyTrait2 {} + +// @has foo/struct.S.html +// @has - '//*[@id="impl-MyTrait1-for-S"]//*[@class="stab portability"]' \ +// 'Available on non-crate feature coolstuff only.' +#[cfg(not(feature = "coolstuff"))] +impl MyTrait1 for S {} + +#[cfg(not(feature = "coolstuff"))] +mod submod { + use crate::{S, MyTrait2}; + // This impl should also have the `not(feature = "coolstuff")`. + // @has - '//*[@id="impl-MyTrait2-for-S"]//*[@class="stab portability"]' \ + // 'Available on non-crate feature coolstuff only.' + impl MyTrait2 for S {} +}