]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/visit_lib.rs
Rollup merge of #103560 - zbyrn:issue-103358-fix, r=cjgillot
[rust.git] / src / librustdoc / visit_lib.rs
1 use rustc_data_structures::fx::FxHashSet;
2 use rustc_hir::def::{DefKind, Res};
3 use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_ID};
4 use rustc_middle::middle::privacy::{EffectiveVisibilities, Level};
5 use rustc_middle::ty::{TyCtxt, Visibility};
6
7 // FIXME: this may not be exhaustive, but is sufficient for rustdocs current uses
8
9 /// Similar to `librustc_privacy::EmbargoVisitor`, but also takes
10 /// specific rustdoc annotations into account (i.e., `doc(hidden)`)
11 pub(crate) struct LibEmbargoVisitor<'a, 'tcx> {
12     tcx: TyCtxt<'tcx>,
13     // Effective visibilities for reachable nodes
14     effective_visibilities: &'a mut EffectiveVisibilities<DefId>,
15     // Previous level, None means unreachable
16     prev_level: Option<Level>,
17     // Keeps track of already visited modules, in case a module re-exports its parent
18     visited_mods: FxHashSet<DefId>,
19 }
20
21 impl<'a, 'tcx> LibEmbargoVisitor<'a, 'tcx> {
22     pub(crate) fn new(cx: &'a mut crate::core::DocContext<'tcx>) -> LibEmbargoVisitor<'a, 'tcx> {
23         LibEmbargoVisitor {
24             tcx: cx.tcx,
25             effective_visibilities: &mut cx.cache.effective_visibilities,
26             prev_level: Some(Level::Direct),
27             visited_mods: FxHashSet::default(),
28         }
29     }
30
31     pub(crate) fn visit_lib(&mut self, cnum: CrateNum) {
32         let did = cnum.as_def_id();
33         self.update(did, Some(Level::Direct));
34         self.visit_mod(did);
35     }
36
37     // Updates node level and returns the updated level
38     fn update(&mut self, did: DefId, level: Option<Level>) -> Option<Level> {
39         let is_hidden = self.tcx.is_doc_hidden(did);
40
41         let old_level = self.effective_visibilities.public_at_level(did);
42         // Visibility levels can only grow
43         if level > old_level && !is_hidden {
44             self.effective_visibilities.set_public_at_level(
45                 did,
46                 || Visibility::Restricted(CRATE_DEF_ID),
47                 level.unwrap(),
48             );
49             level
50         } else {
51             old_level
52         }
53     }
54
55     pub(crate) fn visit_mod(&mut self, def_id: DefId) {
56         if !self.visited_mods.insert(def_id) {
57             return;
58         }
59
60         for item in self.tcx.module_children(def_id).iter() {
61             if let Some(def_id) = item.res.opt_def_id() {
62                 if self.tcx.def_key(def_id).parent.map_or(false, |d| d == def_id.index)
63                     || item.vis.is_public()
64                 {
65                     self.visit_item(item.res);
66                 }
67             }
68         }
69     }
70
71     fn visit_item(&mut self, res: Res<!>) {
72         let def_id = res.def_id();
73         let vis = self.tcx.visibility(def_id);
74         let inherited_item_level = if vis.is_public() { self.prev_level } else { None };
75
76         let item_level = self.update(def_id, inherited_item_level);
77
78         if let Res::Def(DefKind::Mod, _) = res {
79             let orig_level = self.prev_level;
80
81             self.prev_level = item_level;
82             self.visit_mod(def_id);
83             self.prev_level = orig_level;
84         }
85     }
86 }