rustdoc/passes/
propagate_doc_cfg.rs1use rustc_data_structures::fx::FxHashMap;
4use rustc_hir::Attribute;
5use rustc_hir::attrs::{AttributeKind, DocAttribute};
6
7use crate::clean::inline::{load_attrs, merge_attrs};
8use crate::clean::{CfgInfo, Crate, Item, ItemId, ItemKind};
9use crate::core::DocContext;
10use crate::fold::DocFolder;
11use crate::passes::Pass;
12
13pub(crate) const PROPAGATE_DOC_CFG: Pass = Pass {
14 name: "propagate-doc-cfg",
15 run: Some(propagate_doc_cfg),
16 description: "propagates `#[doc(cfg(...))]` to child items",
17};
18
19pub(crate) fn propagate_doc_cfg(cr: Crate, cx: &mut DocContext<'_>) -> Crate {
20 if cx.tcx.features().doc_cfg() {
21 CfgPropagator { cx, cfg_info: CfgInfo::default(), impl_cfg_info: FxHashMap::default() }
22 .fold_crate(cr)
23 } else {
24 cr
25 }
26}
27
28struct CfgPropagator<'a, 'tcx> {
29 cx: &'a mut DocContext<'tcx>,
30 cfg_info: CfgInfo,
31
32 impl_cfg_info: FxHashMap<ItemId, CfgInfo>,
35}
36
37fn add_only_cfg_attributes(attrs: &mut Vec<Attribute>, new_attrs: &[Attribute]) {
40 for attr in new_attrs {
41 if let Attribute::Parsed(AttributeKind::Doc(d)) = attr
42 && !d.cfg.is_empty()
43 {
44 let mut new_attr = DocAttribute::default();
45 new_attr.cfg = d.cfg.clone();
46 attrs.push(Attribute::Parsed(AttributeKind::Doc(Box::new(new_attr))));
47 } else if let Attribute::Parsed(AttributeKind::CfgTrace(..)) = attr {
48 attrs.push(attr.clone());
50 }
51 }
52}
53
54fn add_cfg_state_attributes(attrs: &mut Vec<Attribute>, new_attrs: &[Attribute]) {
57 for attr in new_attrs {
58 if let Attribute::Parsed(AttributeKind::Doc(d)) = attr
59 && (!d.cfg.is_empty() || !d.auto_cfg.is_empty() || !d.auto_cfg_change.is_empty())
60 {
61 let mut new_attr = DocAttribute::default();
62 new_attr.cfg = d.cfg.clone();
63 new_attr.auto_cfg = d.auto_cfg.clone();
64 new_attr.auto_cfg_change = d.auto_cfg_change.clone();
65 attrs.push(Attribute::Parsed(AttributeKind::Doc(Box::new(new_attr))));
66 } else if let Attribute::Parsed(AttributeKind::CfgTrace(..)) = attr {
67 attrs.push(attr.clone());
69 }
70 }
71}
72
73impl CfgPropagator<'_, '_> {
74 fn merge_with_parent_attributes(&mut self, item: &mut Item) {
77 let mut attrs = Vec::new();
78 if matches!(item.kind, ItemKind::ImplItem(_)) || item.inline_stmt_id.is_some() {
85 if let Some(mut next_def_id) = item.item_id.as_local_def_id() {
86 while let Some(parent_def_id) = self.cx.tcx.opt_local_parent(next_def_id) {
87 let x = load_attrs(self.cx.tcx, parent_def_id.to_def_id());
88 add_only_cfg_attributes(&mut attrs, x);
89 next_def_id = parent_def_id;
90 }
91 }
92 }
93 else if matches!(item.kind, ItemKind::MacroItem(_))
96 && item.inner.attrs.other_attrs.iter().any(|attr| {
97 matches!(
98 attr,
99 rustc_hir::Attribute::Parsed(
100 rustc_hir::attrs::AttributeKind::MacroExport { .. }
101 )
102 )
103 })
104 {
105 for parent_def_id in &item.cfg_parent_ids_for_detached_item(self.cx.tcx) {
106 let mut parent_attrs = Vec::new();
107 add_cfg_state_attributes(
108 &mut parent_attrs,
109 load_attrs(self.cx.tcx, parent_def_id.to_def_id()),
110 );
111 merge_attrs(self.cx.tcx, &[], Some((&parent_attrs, None)), &mut self.cfg_info);
112 }
113 }
114
115 let (_, cfg) = merge_attrs(
116 self.cx.tcx,
117 item.attrs.other_attrs.as_slice(),
118 Some((&attrs, None)),
119 &mut self.cfg_info,
120 );
121 item.inner.cfg = cfg;
122 }
123}
124
125impl DocFolder for CfgPropagator<'_, '_> {
126 fn fold_item(&mut self, mut item: Item) -> Option<Item> {
127 let old_cfg_info = self.cfg_info.clone();
128
129 if let ItemKind::ImplItem(_) = item.kind
132 && let Some(cfg_info) = self.impl_cfg_info.remove(&item.item_id)
133 {
134 self.cfg_info = cfg_info;
135 }
136
137 if let ItemKind::PlaceholderImplItem = item.kind {
138 self.impl_cfg_info.insert(item.item_id, self.cfg_info.clone());
142 } else {
143 self.merge_with_parent_attributes(&mut item);
144 }
145
146 let result = self.fold_item_recur(item);
147 self.cfg_info = old_cfg_info;
148
149 Some(result)
150 }
151}