diff options
Diffstat (limited to 'src/librustdoc/visit_lib.rs')
-rw-r--r-- | src/librustdoc/visit_lib.rs | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs new file mode 100644 index 000000000..f01ec3866 --- /dev/null +++ b/src/librustdoc/visit_lib.rs @@ -0,0 +1,82 @@ +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_middle::middle::privacy::{AccessLevel, AccessLevels}; +use rustc_middle::ty::TyCtxt; + +// FIXME: this may not be exhaustive, but is sufficient for rustdocs current uses + +/// Similar to `librustc_privacy::EmbargoVisitor`, but also takes +/// specific rustdoc annotations into account (i.e., `doc(hidden)`) +pub(crate) struct LibEmbargoVisitor<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + // Accessibility levels for reachable nodes + access_levels: &'a mut AccessLevels<DefId>, + // Previous accessibility level, None means unreachable + prev_level: Option<AccessLevel>, + // Keeps track of already visited modules, in case a module re-exports its parent + visited_mods: FxHashSet<DefId>, +} + +impl<'a, 'tcx> LibEmbargoVisitor<'a, 'tcx> { + pub(crate) fn new(cx: &'a mut crate::core::DocContext<'tcx>) -> LibEmbargoVisitor<'a, 'tcx> { + LibEmbargoVisitor { + tcx: cx.tcx, + access_levels: &mut cx.cache.access_levels, + prev_level: Some(AccessLevel::Public), + visited_mods: FxHashSet::default(), + } + } + + pub(crate) fn visit_lib(&mut self, cnum: CrateNum) { + let did = cnum.as_def_id(); + self.update(did, Some(AccessLevel::Public)); + self.visit_mod(did); + } + + // Updates node level and returns the updated level + fn update(&mut self, did: DefId, level: Option<AccessLevel>) -> Option<AccessLevel> { + let is_hidden = self.tcx.is_doc_hidden(did); + + let old_level = self.access_levels.map.get(&did).cloned(); + // Accessibility levels can only grow + if level > old_level && !is_hidden { + self.access_levels.map.insert(did, level.unwrap()); + level + } else { + old_level + } + } + + pub(crate) fn visit_mod(&mut self, def_id: DefId) { + if !self.visited_mods.insert(def_id) { + return; + } + + for item in self.tcx.module_children(def_id).iter() { + if let Some(def_id) = item.res.opt_def_id() { + if self.tcx.def_key(def_id).parent.map_or(false, |d| d == def_id.index) + || item.vis.is_public() + { + self.visit_item(item.res); + } + } + } + } + + fn visit_item(&mut self, res: Res<!>) { + let def_id = res.def_id(); + let vis = self.tcx.visibility(def_id); + let inherited_item_level = if vis.is_public() { self.prev_level } else { None }; + + let item_level = self.update(def_id, inherited_item_level); + + if let Res::Def(DefKind::Mod, _) = res { + let orig_level = self.prev_level; + + self.prev_level = item_level; + self.visit_mod(def_id); + self.prev_level = orig_level; + } + } +} |