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