rustdoc/passes/
propagate_doc_cfg.rs1use rustc_attr_ir::{Attribute, AttributeKind, DocAttribute, find_attr};
6use rustc_data_structures::fx::FxHashMap;
7use rustc_span::{ExpnKind, MacroKind};
8
9use crate::clean::inline::{load_attrs, merge_attrs};
10use crate::clean::{CfgInfo, Crate, Item, ItemId, ItemKind};
11use crate::core::DocContext;
12use crate::fold::DocFolder;
13
14pub(super) fn propagate_doc_cfg(cr: Crate, cx: &mut DocContext<'_>) -> Crate {
15 if cx.tcx.features().doc_cfg() {
16 CfgPropagator { cx, cfg_info: CfgInfo::default(), impl_cfg_info: FxHashMap::default() }
17 .fold_crate(cr)
18 } else {
19 cr
20 }
21}
22
23struct CfgPropagator<'a, 'tcx> {
24 cx: &'a mut DocContext<'tcx>,
25 cfg_info: CfgInfo,
26
27 impl_cfg_info: FxHashMap<ItemId, CfgInfo>,
30}
31
32fn add_only_cfg_attributes(attrs: &mut Vec<Attribute>, new_attrs: &[Attribute]) {
35 for attr in new_attrs {
36 if let Attribute::Parsed(AttributeKind::Doc(d)) = attr
37 && !d.cfg.is_empty()
38 {
39 let mut new_attr = DocAttribute::default();
40 new_attr.cfg = d.cfg.clone();
41 attrs.push(Attribute::Parsed(AttributeKind::Doc(Box::new(new_attr))));
42 } else if let Attribute::Parsed(AttributeKind::CfgTrace(..)) = attr {
43 attrs.push(attr.clone());
45 }
46 }
47}
48
49fn add_cfg_state_attributes(attrs: &mut Vec<Attribute>, new_attrs: &[Attribute]) {
52 for attr in new_attrs {
53 if let Attribute::Parsed(AttributeKind::Doc(d)) = attr
54 && (!d.cfg.is_empty() || !d.auto_cfg.is_empty() || !d.auto_cfg_change.is_empty())
55 {
56 let mut new_attr = DocAttribute::default();
57 new_attr.cfg = d.cfg.clone();
58 new_attr.auto_cfg = d.auto_cfg.clone();
59 new_attr.auto_cfg_change = d.auto_cfg_change.clone();
60 attrs.push(Attribute::Parsed(AttributeKind::Doc(Box::new(new_attr))));
61 } else if let Attribute::Parsed(AttributeKind::CfgTrace(..)) = attr {
62 attrs.push(attr.clone());
64 }
65 }
66}
67
68impl CfgPropagator<'_, '_> {
69 fn merge_with_parent_attributes(&mut self, item: &mut Item) {
72 let mut attrs = Vec::new();
73 if matches!(item.kind, ItemKind::ImplItem(_)) || item.inline_stmt_id.is_some() {
80 if let Some(mut next_def_id) = item.item_id.as_local_def_id() {
81 while let Some(parent_def_id) = self.cx.tcx.opt_local_parent(next_def_id) {
82 let x = load_attrs(self.cx.tcx, parent_def_id.to_def_id());
83 add_only_cfg_attributes(&mut attrs, x);
84 next_def_id = parent_def_id;
85 }
86 }
87 }
88 else if matches!(item.kind, ItemKind::MacroItem(_, _))
91 && item
92 .inner
93 .attrs
94 .other_attrs
95 .iter()
96 .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::MacroExport { .. })))
97 {
98 for parent_def_id in &item.cfg_parent_ids_for_detached_item(self.cx.tcx) {
99 let mut parent_attrs = Vec::new();
100 add_cfg_state_attributes(
101 &mut parent_attrs,
102 load_attrs(self.cx.tcx, parent_def_id.to_def_id()),
103 );
104 merge_attrs(self.cx.tcx, &[], Some((&parent_attrs, None)), &mut self.cfg_info);
105 }
106 }
107
108 let (_, cfg) = merge_attrs(
109 self.cx.tcx,
110 item.attrs.other_attrs.as_slice(),
111 Some((&attrs, None)),
112 &mut self.cfg_info,
113 );
114 item.inner.cfg = cfg;
115 }
116}
117
118impl DocFolder for CfgPropagator<'_, '_> {
119 fn fold_item(&mut self, mut item: Item) -> Option<Item> {
120 let old_cfg_info = self.cfg_info.clone();
121
122 if let ItemKind::ImplItem(_) = item.kind
125 && let Some(cfg_info) = self.impl_cfg_info.remove(&item.item_id)
126 {
127 self.cfg_info = cfg_info;
128 }
129 if let ItemKind::PlaceholderImplItem = item.kind {
130 if let Some(impl_def_id) = item.item_id.as_def_id() {
131 let tcx = self.cx.tcx;
132 let expn_data = tcx.expn_that_defined(impl_def_id).expn_data();
133 if matches!(expn_data.kind, ExpnKind::Macro(MacroKind::Derive, _))
134 && let Some(self_ty_def_id) = tcx
137 .type_of(impl_def_id)
138 .instantiate_identity()
139 .skip_norm_wip()
140 .ty_adt_def()
141 .map(|adt| adt.did())
142 && let self_ty_attrs = load_attrs(tcx, self_ty_def_id)
143 && let Some(cfgs_attr_trace) =
144 find_attr!(self_ty_attrs, CfgAttrTrace(cfgs) => cfgs)
145 && !cfgs_attr_trace.is_empty()
146 {
147 let derive_span = expn_data.call_site;
149 let attrs_iter = Attribute::Parsed(AttributeKind::CfgTrace(
150 cfgs_attr_trace
151 .iter()
152 .filter(|(_, span)| span.contains(derive_span))
153 .cloned()
154 .collect(),
155 ));
156 crate::clean::extract_cfg_from_attrs(
157 std::iter::once(&attrs_iter),
158 tcx,
159 &mut self.cfg_info,
160 );
161 }
162 }
163 self.impl_cfg_info.insert(item.item_id, self.cfg_info.clone());
167 } else {
168 self.merge_with_parent_attributes(&mut item);
169 }
170
171 let result = self.fold_item_recur(item);
172 self.cfg_info = old_cfg_info;
173
174 Some(result)
175 }
176}