mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 00:34:06 +00:00
parent
19423b5944
commit
5feeca0156
@ -8,6 +8,7 @@ use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{walk_item, Visitor};
|
||||
use rustc_hir::Node;
|
||||
use rustc_hir::CRATE_HIR_ID;
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
|
||||
@ -67,6 +68,7 @@ pub(crate) struct RustdocVisitor<'a, 'tcx> {
|
||||
inside_public_path: bool,
|
||||
exact_paths: FxHashMap<DefId, Vec<Symbol>>,
|
||||
modules: Vec<Module<'tcx>>,
|
||||
map: Map<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
||||
@ -79,6 +81,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
||||
hir::CRATE_HIR_ID,
|
||||
cx.tcx.hir().root_module().spans.inner_span,
|
||||
);
|
||||
let map = cx.tcx.hir();
|
||||
|
||||
RustdocVisitor {
|
||||
cx,
|
||||
@ -87,6 +90,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
||||
inside_public_path: true,
|
||||
exact_paths: FxHashMap::default(),
|
||||
modules: vec![om],
|
||||
map,
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,95 +99,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
||||
self.exact_paths.entry(did).or_insert_with(|| def_id_to_path(tcx, did));
|
||||
}
|
||||
|
||||
pub(crate) fn visit(mut self) -> Module<'tcx> {
|
||||
let root_module = self.cx.tcx.hir().root_module();
|
||||
self.visit_mod_contents(CRATE_HIR_ID, root_module);
|
||||
|
||||
let mut top_level_module = self.modules.pop().unwrap();
|
||||
|
||||
// `#[macro_export] macro_rules!` items are reexported at the top level of the
|
||||
// crate, regardless of where they're defined. We want to document the
|
||||
// top level rexport of the macro, not its original definition, since
|
||||
// the rexport defines the path that a user will actually see. Accordingly,
|
||||
// we add the rexport as an item here, and then skip over the original
|
||||
// definition in `visit_item()` below.
|
||||
//
|
||||
// We also skip `#[macro_export] macro_rules!` that have already been inserted,
|
||||
// it can happen if within the same module a `#[macro_export] macro_rules!`
|
||||
// is declared but also a reexport of itself producing two exports of the same
|
||||
// macro in the same module.
|
||||
let mut inserted = FxHashSet::default();
|
||||
for export in self.cx.tcx.module_reexports(CRATE_DEF_ID).unwrap_or(&[]) {
|
||||
if let Res::Def(DefKind::Macro(_), def_id) = export.res &&
|
||||
let Some(local_def_id) = def_id.as_local() &&
|
||||
self.cx.tcx.has_attr(def_id, sym::macro_export) &&
|
||||
inserted.insert(def_id)
|
||||
{
|
||||
let item = self.cx.tcx.hir().expect_item(local_def_id);
|
||||
top_level_module.items.push((item, None, None));
|
||||
}
|
||||
}
|
||||
|
||||
self.cx.cache.hidden_cfg = self
|
||||
.cx
|
||||
.tcx
|
||||
.hir()
|
||||
.attrs(CRATE_HIR_ID)
|
||||
.iter()
|
||||
.filter(|attr| attr.has_name(sym::doc))
|
||||
.flat_map(|attr| attr.meta_item_list().into_iter().flatten())
|
||||
.filter(|attr| attr.has_name(sym::cfg_hide))
|
||||
.flat_map(|attr| {
|
||||
attr.meta_item_list()
|
||||
.unwrap_or(&[])
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
Cfg::parse(attr.meta_item()?)
|
||||
.map_err(|e| self.cx.sess().diagnostic().span_err(e.span, e.msg))
|
||||
.ok()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.chain(
|
||||
[Cfg::Cfg(sym::test, None), Cfg::Cfg(sym::doc, None), Cfg::Cfg(sym::doctest, None)]
|
||||
.into_iter(),
|
||||
)
|
||||
.collect();
|
||||
|
||||
self.cx.cache.exact_paths = self.exact_paths;
|
||||
top_level_module
|
||||
}
|
||||
|
||||
/// This method will go through the given module items in two passes:
|
||||
/// 1. The items which are not glob imports/reexports.
|
||||
/// 2. The glob imports/reexports.
|
||||
fn visit_mod_contents(&mut self, id: hir::HirId, m: &'tcx hir::Mod<'tcx>) {
|
||||
debug!("Going through module {:?}", m);
|
||||
let def_id = self.cx.tcx.hir().local_def_id(id).to_def_id();
|
||||
// Keep track of if there were any private modules in the path.
|
||||
let orig_inside_public_path = self.inside_public_path;
|
||||
self.inside_public_path &= self.cx.tcx.visibility(def_id).is_public();
|
||||
|
||||
// Reimplementation of `walk_mod` because we need to do it in two passes (explanations in
|
||||
// the second loop):
|
||||
for &i in m.item_ids {
|
||||
let item = self.cx.tcx.hir().item(i);
|
||||
if !matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
|
||||
self.visit_item(item);
|
||||
}
|
||||
}
|
||||
for &i in m.item_ids {
|
||||
let item = self.cx.tcx.hir().item(i);
|
||||
// To match the way import precedence works, visit glob imports last.
|
||||
// Later passes in rustdoc will de-duplicate by name and kind, so if glob-
|
||||
// imported items appear last, then they'll be the ones that get discarded.
|
||||
if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
|
||||
self.visit_item(item);
|
||||
}
|
||||
}
|
||||
self.inside_public_path = orig_inside_public_path;
|
||||
}
|
||||
|
||||
/// Tries to resolve the target of a `pub use` statement and inlines the
|
||||
/// target if it is defined locally and would not be documented otherwise,
|
||||
/// or when it is specifically requested with `please_inline`.
|
||||
@ -408,6 +323,65 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn visit(mut self) -> Module<'tcx> {
|
||||
let root_module = self.cx.tcx.hir().root_module();
|
||||
self.visit_mod_contents(CRATE_HIR_ID, root_module);
|
||||
|
||||
let mut top_level_module = self.modules.pop().unwrap();
|
||||
|
||||
// `#[macro_export] macro_rules!` items are reexported at the top level of the
|
||||
// crate, regardless of where they're defined. We want to document the
|
||||
// top level rexport of the macro, not its original definition, since
|
||||
// the rexport defines the path that a user will actually see. Accordingly,
|
||||
// we add the rexport as an item here, and then skip over the original
|
||||
// definition in `visit_item()` below.
|
||||
//
|
||||
// We also skip `#[macro_export] macro_rules!` that have already been inserted,
|
||||
// it can happen if within the same module a `#[macro_export] macro_rules!`
|
||||
// is declared but also a reexport of itself producing two exports of the same
|
||||
// macro in the same module.
|
||||
let mut inserted = FxHashSet::default();
|
||||
for export in self.cx.tcx.module_reexports(CRATE_DEF_ID).unwrap_or(&[]) {
|
||||
if let Res::Def(DefKind::Macro(_), def_id) = export.res &&
|
||||
let Some(local_def_id) = def_id.as_local() &&
|
||||
self.cx.tcx.has_attr(def_id, sym::macro_export) &&
|
||||
inserted.insert(def_id)
|
||||
{
|
||||
let item = self.cx.tcx.hir().expect_item(local_def_id);
|
||||
top_level_module.items.push((item, None, None));
|
||||
}
|
||||
}
|
||||
|
||||
self.cx.cache.hidden_cfg = self
|
||||
.cx
|
||||
.tcx
|
||||
.hir()
|
||||
.attrs(CRATE_HIR_ID)
|
||||
.iter()
|
||||
.filter(|attr| attr.has_name(sym::doc))
|
||||
.flat_map(|attr| attr.meta_item_list().into_iter().flatten())
|
||||
.filter(|attr| attr.has_name(sym::cfg_hide))
|
||||
.flat_map(|attr| {
|
||||
attr.meta_item_list()
|
||||
.unwrap_or(&[])
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
Cfg::parse(attr.meta_item()?)
|
||||
.map_err(|e| self.cx.sess().diagnostic().span_err(e.span, e.msg))
|
||||
.ok()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.chain(
|
||||
[Cfg::Cfg(sym::test, None), Cfg::Cfg(sym::doc, None), Cfg::Cfg(sym::doctest, None)]
|
||||
.into_iter(),
|
||||
)
|
||||
.collect();
|
||||
|
||||
self.cx.cache.exact_paths = self.exact_paths;
|
||||
top_level_module
|
||||
}
|
||||
|
||||
/// This method will create a new module and push it onto the "modules stack" then call
|
||||
/// `visit_mod_contents`. Once done, it'll remove it from the "modules stack" and instead
|
||||
/// add into the list of modules of the current module.
|
||||
@ -419,6 +393,35 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
||||
let last = self.modules.pop().unwrap();
|
||||
self.modules.last_mut().unwrap().mods.push(last);
|
||||
}
|
||||
|
||||
/// This method will go through the given module items in two passes:
|
||||
/// 1. The items which are not glob imports/reexports.
|
||||
/// 2. The glob imports/reexports.
|
||||
fn visit_mod_contents(&mut self, id: hir::HirId, m: &'tcx hir::Mod<'tcx>) {
|
||||
debug!("Going through module {:?}", m);
|
||||
let def_id = self.cx.tcx.hir().local_def_id(id).to_def_id();
|
||||
// Keep track of if there were any private modules in the path.
|
||||
let orig_inside_public_path = self.inside_public_path;
|
||||
self.inside_public_path &= self.cx.tcx.visibility(def_id).is_public();
|
||||
|
||||
// Reimplementation of `walk_mod`:
|
||||
for &i in m.item_ids {
|
||||
let item = self.cx.tcx.hir().item(i);
|
||||
if !matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
|
||||
self.visit_item(item);
|
||||
}
|
||||
}
|
||||
for &i in m.item_ids {
|
||||
let item = self.cx.tcx.hir().item(i);
|
||||
// To match the way import precedence works, visit glob imports last.
|
||||
// Later passes in rustdoc will de-duplicate by name and kind, so if glob-
|
||||
// imported items appear last, then they'll be the ones that get discarded.
|
||||
if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
|
||||
self.visit_item(item);
|
||||
}
|
||||
}
|
||||
self.inside_public_path = orig_inside_public_path;
|
||||
}
|
||||
}
|
||||
|
||||
// We need to implement this visitor so it'll go everywhere and retrieve items we're interested in
|
||||
@ -427,7 +430,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RustdocVisitor<'a, 'tcx> {
|
||||
type NestedFilter = nested_filter::All;
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.cx.tcx.hir()
|
||||
self.map
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) {
|
||||
|
Loading…
Reference in New Issue
Block a user