rustdoc: Fix glob import inlining

Filter away names that are not actually imported by the glob, e.g. because they are shadowed by something else
This commit is contained in:
Vadim Petrochenkov 2023-01-16 01:31:21 +04:00
parent 481725984b
commit 3b0d306b94
4 changed files with 28 additions and 4 deletions

View File

@ -9,7 +9,7 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::Mutability;
use rustc_metadata::creader::{CStore, LoadedMacro};
use rustc_middle::ty::{self, TyCtxt};
@ -162,6 +162,7 @@ pub(crate) fn try_inline(
pub(crate) fn try_inline_glob(
cx: &mut DocContext<'_>,
res: Res,
current_mod: LocalDefId,
visited: &mut FxHashSet<DefId>,
inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
) -> Option<Vec<clean::Item>> {
@ -172,7 +173,16 @@ pub(crate) fn try_inline_glob(
match res {
Res::Def(DefKind::Mod, did) => {
let mut items = build_module_items(cx, did, visited, inlined_names);
// Use the set of module reexports to filter away names that are not actually
// reexported by the glob, e.g. because they are shadowed by something else.
let reexports = cx
.tcx
.module_reexports(current_mod)
.unwrap_or_default()
.iter()
.filter_map(|child| child.res.opt_def_id())
.collect();
let mut items = build_module_items(cx, did, visited, inlined_names, Some(&reexports));
items.drain_filter(|item| {
if let Some(name) = item.name {
// If an item with the same type and name already exists,
@ -563,7 +573,7 @@ fn build_module(
did: DefId,
visited: &mut FxHashSet<DefId>,
) -> clean::Module {
let items = build_module_items(cx, did, visited, &mut FxHashSet::default());
let items = build_module_items(cx, did, visited, &mut FxHashSet::default(), None);
let span = clean::Span::new(cx.tcx.def_span(did));
clean::Module { items, span }
@ -574,6 +584,7 @@ fn build_module_items(
did: DefId,
visited: &mut FxHashSet<DefId>,
inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
allowed_def_ids: Option<&FxHashSet<DefId>>,
) -> Vec<clean::Item> {
let mut items = Vec::new();
@ -583,6 +594,11 @@ fn build_module_items(
for &item in cx.tcx.module_children(did).iter() {
if item.vis.is_public() {
let res = item.res.expect_non_local();
if let Some(def_id) = res.opt_def_id()
&& let Some(allowed_def_ids) = allowed_def_ids
&& !allowed_def_ids.contains(&def_id) {
continue;
}
if let Some(def_id) = res.mod_def_id() {
// If we're inlining a glob import, it's possible to have
// two distinct modules with the same name. We don't want to

View File

@ -2441,7 +2441,8 @@ fn clean_use_statement_inner<'tcx>(
let inner = if kind == hir::UseKind::Glob {
if !denied {
let mut visited = FxHashSet::default();
if let Some(items) = inline::try_inline_glob(cx, path.res, &mut visited, inlined_names)
if let Some(items) =
inline::try_inline_glob(cx, path.res, current_mod, &mut visited, inlined_names)
{
return items;
}

View File

@ -3,3 +3,5 @@
pub struct SomeStruct;
pub fn some_fn() {}
pub enum Shadowed {}

View File

@ -6,6 +6,11 @@
// @has cross_glob/struct.SomeStruct.html
// @has cross_glob/fn.some_fn.html
// @!has cross_glob/enum.Shadowed.html
// @!has cross_glob/index.html '//code' 'pub use inner::*;'
#[doc(inline)]
pub use inner::*;
// This type shadows the glob-imported enum `Shadowed`.
// @has cross_glob/type.Shadowed.html
pub type Shadowed = u8;