summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/visit_ast.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/visit_ast.rs')
-rw-r--r--src/librustdoc/visit_ast.rs152
1 files changed, 123 insertions, 29 deletions
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index f54b70b41..6b7ad4cf2 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -5,11 +5,12 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LocalDefIdSet};
-use rustc_hir::intravisit::{walk_item, Visitor};
+use rustc_hir::intravisit::{walk_body, walk_item, Visitor};
use rustc_hir::{Node, CRATE_HIR_ID};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
+use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::Span;
@@ -26,6 +27,8 @@ pub(crate) struct Module<'hir> {
pub(crate) where_inner: Span,
pub(crate) mods: Vec<Module<'hir>>,
pub(crate) def_id: LocalDefId,
+ pub(crate) renamed: Option<Symbol>,
+ pub(crate) import_id: Option<LocalDefId>,
/// The key is the item `ItemId` and the value is: (item, renamed, import_id).
/// We use `FxIndexMap` to keep the insert order.
pub(crate) items: FxIndexMap<
@@ -36,11 +39,19 @@ pub(crate) struct Module<'hir> {
}
impl Module<'_> {
- pub(crate) fn new(name: Symbol, def_id: LocalDefId, where_inner: Span) -> Self {
+ pub(crate) fn new(
+ name: Symbol,
+ def_id: LocalDefId,
+ where_inner: Span,
+ renamed: Option<Symbol>,
+ import_id: Option<LocalDefId>,
+ ) -> Self {
Module {
name,
def_id,
where_inner,
+ renamed,
+ import_id,
mods: Vec::new(),
items: FxIndexMap::default(),
foreigns: Vec::new(),
@@ -59,9 +70,16 @@ fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec<Symbol> {
std::iter::once(crate_name).chain(relative).collect()
}
-pub(crate) fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut def_id: LocalDefId) -> bool {
+pub(crate) fn inherits_doc_hidden(
+ tcx: TyCtxt<'_>,
+ mut def_id: LocalDefId,
+ stop_at: Option<LocalDefId>,
+) -> bool {
let hir = tcx.hir();
while let Some(id) = tcx.opt_local_parent(def_id) {
+ if let Some(stop_at) = stop_at && id == stop_at {
+ return false;
+ }
def_id = id;
if tcx.is_doc_hidden(def_id.to_def_id()) {
return true;
@@ -87,6 +105,8 @@ pub(crate) struct RustdocVisitor<'a, 'tcx> {
inside_public_path: bool,
exact_paths: DefIdMap<Vec<Symbol>>,
modules: Vec<Module<'tcx>>,
+ is_importable_from_parent: bool,
+ inside_body: bool,
}
impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
@@ -98,6 +118,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
cx.tcx.crate_name(LOCAL_CRATE),
CRATE_DEF_ID,
cx.tcx.hir().root_module().spans.inner_span,
+ None,
+ None,
);
RustdocVisitor {
@@ -107,6 +129,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
inside_public_path: true,
exact_paths: Default::default(),
modules: vec![om],
+ is_importable_from_parent: true,
+ inside_body: false,
}
}
@@ -133,14 +157,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
// 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_children_reexports(CRATE_DEF_ID) {
- if let Res::Def(DefKind::Macro(_), def_id) = export.res &&
+ for child in self.cx.tcx.module_children_local(CRATE_DEF_ID) {
+ if !child.reexport_chain.is_empty() &&
+ let Res::Def(DefKind::Macro(_), def_id) = child.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.insert((local_def_id, Some(item.ident.name)), (item, None, None));
+ let item = self.cx.tcx.hir().expect_item(local_def_id);
+ top_level_module.items.insert((local_def_id, Some(item.ident.name)), (item, None, None));
}
}
@@ -255,9 +280,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
return false;
};
- let is_private =
- !self.cx.cache.effective_visibilities.is_directly_public(self.cx.tcx, ori_res_did);
- let is_hidden = inherits_doc_hidden(self.cx.tcx, res_did);
+ let is_private = !self.cx.cache.effective_visibilities.is_directly_public(tcx, ori_res_did);
+ let is_hidden = inherits_doc_hidden(tcx, res_did, None);
// Only inline if requested or if the item would otherwise be stripped.
if (!please_inline && !is_private && !is_hidden) || is_no_inline {
@@ -265,7 +289,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
}
if !please_inline &&
- let Some(item_def_id) = reexport_chain(self.cx.tcx, def_id, res_did).iter()
+ let Some(item_def_id) = reexport_chain(tcx, def_id, res_did).iter()
.flat_map(|reexport| reexport.id()).map(|id| id.expect_local())
.chain(iter::once(res_did)).nth(1) &&
item_def_id != def_id &&
@@ -273,22 +297,38 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
.cx
.cache
.effective_visibilities
- .is_directly_public(self.cx.tcx, item_def_id.to_def_id()) &&
- !inherits_doc_hidden(self.cx.tcx, item_def_id)
+ .is_directly_public(tcx, item_def_id.to_def_id()) &&
+ !inherits_doc_hidden(tcx, item_def_id, None)
{
// The imported item is public and not `doc(hidden)` so no need to inline it.
return false;
}
- if !self.view_item_stack.insert(res_did) {
+ let is_bang_macro = matches!(
+ tcx.hir().get_by_def_id(res_did),
+ Node::Item(&hir::Item { kind: hir::ItemKind::Macro(_, MacroKind::Bang), .. })
+ );
+
+ if !self.view_item_stack.insert(res_did) && !is_bang_macro {
return false;
}
let ret = match tcx.hir().get_by_def_id(res_did) {
+ // Bang macros are handled a bit on their because of how they are handled by the
+ // compiler. If they have `#[doc(hidden)]` and the re-export doesn't have
+ // `#[doc(inline)]`, then we don't inline it.
+ Node::Item(_)
+ if is_bang_macro
+ && !please_inline
+ && renamed.is_some()
+ && self.cx.tcx.is_doc_hidden(ori_res_did) =>
+ {
+ return false;
+ }
Node::Item(&hir::Item { kind: hir::ItemKind::Mod(ref m), .. }) if glob => {
let prev = mem::replace(&mut self.inlining, true);
for &i in m.item_ids {
- let i = self.cx.tcx.hir().item(i);
+ let i = tcx.hir().item(i);
self.visit_item_inner(i, None, Some(def_id));
}
self.inlining = prev;
@@ -319,11 +359,23 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
renamed: Option<Symbol>,
parent_id: Option<LocalDefId>,
) {
- self.modules
- .last_mut()
- .unwrap()
- .items
- .insert((item.owner_id.def_id, renamed), (item, renamed, parent_id));
+ if self.is_importable_from_parent
+ // If we're inside an item, only impl blocks and `macro_rules!` with the `macro_export`
+ // attribute can still be visible.
+ || match item.kind {
+ hir::ItemKind::Impl(..) => true,
+ hir::ItemKind::Macro(_, MacroKind::Bang) => {
+ self.cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export)
+ }
+ _ => false,
+ }
+ {
+ self.modules
+ .last_mut()
+ .unwrap()
+ .items
+ .insert((item.owner_id.def_id, renamed), (item, renamed, parent_id));
+ }
}
fn visit_item_inner(
@@ -331,8 +383,28 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
item: &'tcx hir::Item<'_>,
renamed: Option<Symbol>,
import_id: Option<LocalDefId>,
- ) -> bool {
+ ) {
debug!("visiting item {:?}", item);
+ if self.inside_body {
+ // Only impls can be "seen" outside a body. For example:
+ //
+ // ```
+ // struct Bar;
+ //
+ // fn foo() {
+ // impl Bar { fn bar() {} }
+ // }
+ // Bar::bar();
+ // ```
+ if let hir::ItemKind::Impl(impl_) = item.kind &&
+ // Don't duplicate impls when inlining or if it's implementing a trait, we'll pick
+ // them up regardless of where they're located.
+ impl_.of_trait.is_none()
+ {
+ self.add_to_current_mod(item, None, None);
+ }
+ return;
+ }
let name = renamed.unwrap_or(item.ident.name);
let tcx = self.cx.tcx;
@@ -411,7 +483,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
}
}
hir::ItemKind::Mod(ref m) => {
- self.enter_mod(item.owner_id.def_id, m, name);
+ self.enter_mod(item.owner_id.def_id, m, name, renamed, import_id);
}
hir::ItemKind::Fn(..)
| hir::ItemKind::ExternCrate(..)
@@ -420,7 +492,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
| hir::ItemKind::Union(..)
| hir::ItemKind::TyAlias(..)
| hir::ItemKind::OpaqueTy(hir::OpaqueTy {
- origin: hir::OpaqueTyOrigin::TyAlias, ..
+ origin: hir::OpaqueTyOrigin::TyAlias { .. },
+ ..
})
| hir::ItemKind::Static(..)
| hir::ItemKind::Trait(..)
@@ -448,7 +521,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
}
}
}
- true
}
fn visit_foreign_item_inner(
@@ -465,8 +537,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
/// 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.
- fn enter_mod(&mut self, id: LocalDefId, m: &'tcx hir::Mod<'tcx>, name: Symbol) {
- self.modules.push(Module::new(name, id, m.spans.inner_span));
+ fn enter_mod(
+ &mut self,
+ id: LocalDefId,
+ m: &'tcx hir::Mod<'tcx>,
+ name: Symbol,
+ renamed: Option<Symbol>,
+ import_id: Option<LocalDefId>,
+ ) {
+ self.modules.push(Module::new(name, id, m.spans.inner_span, renamed, import_id));
self.visit_mod_contents(id, m);
@@ -485,9 +564,18 @@ impl<'a, 'tcx> Visitor<'tcx> for RustdocVisitor<'a, 'tcx> {
}
fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) {
- if self.visit_item_inner(i, None, None) {
- walk_item(self, i);
- }
+ self.visit_item_inner(i, None, None);
+ let new_value = self.is_importable_from_parent
+ && matches!(
+ i.kind,
+ hir::ItemKind::Mod(..)
+ | hir::ItemKind::ForeignMod { .. }
+ | hir::ItemKind::Impl(..)
+ | hir::ItemKind::Trait(..)
+ );
+ let prev = mem::replace(&mut self.is_importable_from_parent, new_value);
+ walk_item(self, i);
+ self.is_importable_from_parent = prev;
}
fn visit_mod(&mut self, _: &hir::Mod<'tcx>, _: Span, _: hir::HirId) {
@@ -513,4 +601,10 @@ impl<'a, 'tcx> Visitor<'tcx> for RustdocVisitor<'a, 'tcx> {
fn visit_lifetime(&mut self, _: &hir::Lifetime) {
// Unneeded.
}
+
+ fn visit_body(&mut self, b: &'tcx hir::Body<'tcx>) {
+ let prev = mem::replace(&mut self.inside_body, true);
+ walk_body(self, b);
+ self.inside_body = prev;
+ }
}