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