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