rustdoc/passes/
propagate_doc_cfg.rs1use rustc_data_structures::fx::FxHashMap;
4use rustc_hir::attrs::{AttributeKind, DocAttribute};
5use rustc_hir::{Attribute, find_attr};
6use rustc_span::{ExpnKind, MacroKind};
7
8use crate::clean::inline::{load_attrs, merge_attrs};
9use crate::clean::{CfgInfo, Crate, Item, ItemId, ItemKind};
10use crate::core::DocContext;
11use crate::fold::DocFolder;
12use crate::passes::Pass;
13
14pub(crate) const PROPAGATE_DOC_CFG: Pass = Pass {
15 name: "propagate-doc-cfg",
16 run: Some(propagate_doc_cfg),
17 description: "propagates `#[doc(cfg(...))]` to child items",
18};
19
20pub(crate) fn propagate_doc_cfg(cr: Crate, cx: &mut DocContext<'_>) -> Crate {
21 if cx.tcx.features().doc_cfg() {
22 CfgPropagator { cx, cfg_info: CfgInfo::default(), impl_cfg_info: FxHashMap::default() }
23 .fold_crate(cr)
24 } else {
25 cr
26 }
27}
28
29struct CfgPropagator<'a, 'tcx> {
30 cx: &'a mut DocContext<'tcx>,
31 cfg_info: CfgInfo,
32
33 impl_cfg_info: FxHashMap<ItemId, CfgInfo>,
36}
37
38fn add_only_cfg_attributes(attrs: &mut Vec<Attribute>, new_attrs: &[Attribute]) {
41 for attr in new_attrs {
42 if let Attribute::Parsed(AttributeKind::Doc(d)) = attr
43 && !d.cfg.is_empty()
44 {
45 let mut new_attr = DocAttribute::default();
46 new_attr.cfg = d.cfg.clone();
47 attrs.push(Attribute::Parsed(AttributeKind::Doc(Box::new(new_attr))));
48 } else if let Attribute::Parsed(AttributeKind::CfgTrace(..)) = attr {
49 attrs.push(attr.clone());
51 }
52 }
53}
54
55fn add_cfg_state_attributes(attrs: &mut Vec<Attribute>, new_attrs: &[Attribute]) {
58 for attr in new_attrs {
59 if let Attribute::Parsed(AttributeKind::Doc(d)) = attr
60 && (!d.cfg.is_empty() || !d.auto_cfg.is_empty() || !d.auto_cfg_change.is_empty())
61 {
62 let mut new_attr = DocAttribute::default();
63 new_attr.cfg = d.cfg.clone();
64 new_attr.auto_cfg = d.auto_cfg.clone();
65 new_attr.auto_cfg_change = d.auto_cfg_change.clone();
66 attrs.push(Attribute::Parsed(AttributeKind::Doc(Box::new(new_attr))));
67 } else if let Attribute::Parsed(AttributeKind::CfgTrace(..)) = attr {
68 attrs.push(attr.clone());
70 }
71 }
72}
73
74impl CfgPropagator<'_, '_> {
75 fn merge_with_parent_attributes(&mut self, item: &mut Item) {
78 let mut attrs = Vec::new();
79 if matches!(item.kind, ItemKind::ImplItem(_)) || item.inline_stmt_id.is_some() {
86 if let Some(mut next_def_id) = item.item_id.as_local_def_id() {
87 while let Some(parent_def_id) = self.cx.tcx.opt_local_parent(next_def_id) {
88 let x = load_attrs(self.cx.tcx, parent_def_id.to_def_id());
89 add_only_cfg_attributes(&mut attrs, x);
90 next_def_id = parent_def_id;
91 }
92 }
93 }
94 else if matches!(item.kind, ItemKind::MacroItem(_, _))
97 && item.inner.attrs.other_attrs.iter().any(|attr| {
98 matches!(
99 attr,
100 rustc_hir::Attribute::Parsed(
101 rustc_hir::attrs::AttributeKind::MacroExport { .. }
102 )
103 )
104 })
105 {
106 for parent_def_id in &item.cfg_parent_ids_for_detached_item(self.cx.tcx) {
107 let mut parent_attrs = Vec::new();
108 add_cfg_state_attributes(
109 &mut parent_attrs,
110 load_attrs(self.cx.tcx, parent_def_id.to_def_id()),
111 );
112 merge_attrs(self.cx.tcx, &[], Some((&parent_attrs, None)), &mut self.cfg_info);
113 }
114 }
115
116 let (_, cfg) = merge_attrs(
117 self.cx.tcx,
118 item.attrs.other_attrs.as_slice(),
119 Some((&attrs, None)),
120 &mut self.cfg_info,
121 );
122 item.inner.cfg = cfg;
123 }
124}
125
126impl DocFolder for CfgPropagator<'_, '_> {
127 fn fold_item(&mut self, mut item: Item) -> Option<Item> {
128 let old_cfg_info = self.cfg_info.clone();
129
130 if let ItemKind::ImplItem(_) = item.kind
133 && let Some(cfg_info) = self.impl_cfg_info.remove(&item.item_id)
134 {
135 self.cfg_info = cfg_info;
136 }
137 if let ItemKind::PlaceholderImplItem = item.kind {
138 if let Some(impl_def_id) = item.item_id.as_def_id() {
139 let tcx = self.cx.tcx;
140 let expn_data = tcx.expn_that_defined(impl_def_id).expn_data();
141 if matches!(expn_data.kind, ExpnKind::Macro(MacroKind::Derive, _))
142 && let Some(self_ty_def_id) = tcx
145 .type_of(impl_def_id)
146 .instantiate_identity()
147 .skip_norm_wip()
148 .ty_adt_def()
149 .map(|adt| adt.did())
150 && let self_ty_attrs = load_attrs(tcx, self_ty_def_id)
151 && let Some(cfgs_attr_trace) =
152 find_attr!(self_ty_attrs, CfgAttrTrace(cfgs) => cfgs)
153 && !cfgs_attr_trace.is_empty()
154 {
155 let derive_span = expn_data.call_site;
157 let attrs_iter = Attribute::Parsed(AttributeKind::CfgTrace(
158 cfgs_attr_trace
159 .iter()
160 .filter(|(_, span)| span.contains(derive_span))
161 .cloned()
162 .collect(),
163 ));
164 crate::clean::extract_cfg_from_attrs(
165 std::iter::once(&attrs_iter),
166 tcx,
167 &mut self.cfg_info,
168 );
169 }
170 }
171 self.impl_cfg_info.insert(item.item_id, self.cfg_info.clone());
175 } else {
176 self.merge_with_parent_attributes(&mut item);
177 }
178
179 let result = self.fold_item_recur(item);
180 self.cfg_info = old_cfg_info;
181
182 Some(result)
183 }
184}