]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/passes/propagate_doc_cfg.rs
Rollup merge of #107348 - lcnr:project-solve-new, r=compiler-errors
[rust.git] / src / librustdoc / passes / propagate_doc_cfg.rs
1 //! Propagates [`#[doc(cfg(...))]`](https://github.com/rust-lang/rust/issues/43781) to child items.
2 use std::sync::Arc;
3
4 use crate::clean::cfg::Cfg;
5 use crate::clean::inline::{load_attrs, merge_attrs};
6 use crate::clean::{Crate, Item, ItemKind};
7 use crate::core::DocContext;
8 use crate::fold::DocFolder;
9 use crate::passes::Pass;
10
11 use rustc_hir::def_id::LocalDefId;
12 use rustc_middle::ty::DefIdTree;
13
14 pub(crate) const PROPAGATE_DOC_CFG: Pass = Pass {
15     name: "propagate-doc-cfg",
16     run: propagate_doc_cfg,
17     description: "propagates `#[doc(cfg(...))]` to child items",
18 };
19
20 pub(crate) fn propagate_doc_cfg(cr: Crate, cx: &mut DocContext<'_>) -> Crate {
21     CfgPropagator { parent_cfg: None, parent: None, cx }.fold_crate(cr)
22 }
23
24 struct CfgPropagator<'a, 'tcx> {
25     parent_cfg: Option<Arc<Cfg>>,
26     parent: Option<LocalDefId>,
27     cx: &'a mut DocContext<'tcx>,
28 }
29
30 impl<'a, 'tcx> CfgPropagator<'a, 'tcx> {
31     // Some items need to merge their attributes with their parents' otherwise a few of them
32     // (mostly `cfg` ones) will be missing.
33     fn merge_with_parent_attributes(&mut self, item: &mut Item) {
34         let check_parent = match &*item.kind {
35             // impl blocks can be in different modules with different cfg and we need to get them
36             // as well.
37             ItemKind::ImplItem(_) => false,
38             kind if kind.is_non_assoc() => true,
39             _ => return,
40         };
41
42         let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local())
43             else { return };
44
45         if check_parent {
46             let expected_parent = self.cx.tcx.opt_local_parent(def_id);
47             // If parents are different, it means that `item` is a reexport and we need
48             // to compute the actual `cfg` by iterating through its "real" parents.
49             if self.parent.is_some() && self.parent == expected_parent {
50                 return;
51             }
52         }
53
54         let mut attrs = Vec::new();
55         let mut next_def_id = def_id;
56         while let Some(parent_def_id) = self.cx.tcx.opt_local_parent(next_def_id) {
57             attrs.extend_from_slice(load_attrs(self.cx, parent_def_id.to_def_id()));
58             next_def_id = parent_def_id;
59         }
60
61         let (_, cfg) = merge_attrs(self.cx, None, item.attrs.other_attrs.as_slice(), Some(&attrs));
62         item.cfg = cfg;
63     }
64 }
65
66 impl<'a, 'tcx> DocFolder for CfgPropagator<'a, 'tcx> {
67     fn fold_item(&mut self, mut item: Item) -> Option<Item> {
68         let old_parent_cfg = self.parent_cfg.clone();
69
70         self.merge_with_parent_attributes(&mut item);
71
72         let new_cfg = match (self.parent_cfg.take(), item.cfg.take()) {
73             (None, None) => None,
74             (Some(rc), None) | (None, Some(rc)) => Some(rc),
75             (Some(mut a), Some(b)) => {
76                 let b = Arc::try_unwrap(b).unwrap_or_else(|rc| Cfg::clone(&rc));
77                 *Arc::make_mut(&mut a) &= b;
78                 Some(a)
79             }
80         };
81         self.parent_cfg = new_cfg.clone();
82         item.cfg = new_cfg;
83
84         let old_parent =
85             if let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) {
86                 self.parent.replace(def_id)
87             } else {
88                 self.parent.take()
89             };
90         let result = self.fold_item_recur(item);
91         self.parent_cfg = old_parent_cfg;
92         self.parent = old_parent;
93
94         Some(result)
95     }
96 }