diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index f644ecb88b931..58d0aedb02399 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -145,6 +145,7 @@ pub(crate) fn try_inline_glob( cx: &mut DocContext<'_>, res: Res, visited: &mut FxHashSet, + inlined_names: &mut FxHashSet<(ItemType, Symbol)>, ) -> Option> { let did = res.opt_def_id()?; if did.is_local() { @@ -153,8 +154,17 @@ pub(crate) fn try_inline_glob( match res { Res::Def(DefKind::Mod, did) => { - let m = build_module(cx, did, visited); - Some(m.items) + let mut items = build_module_items(cx, did, visited, inlined_names); + items.drain_filter(|item| { + if let Some(name) = item.name { + // If an item with the same type and name already exists, + // it takes priority over the inlined stuff. + !inlined_names.insert((item.type_(), name)) + } else { + false + } + }); + Some(items) } // glob imports on things like enums aren't inlined even for local exports, so just bail _ => None, @@ -517,6 +527,18 @@ fn build_module( did: DefId, visited: &mut FxHashSet, ) -> clean::Module { + let items = build_module_items(cx, did, visited, &mut FxHashSet::default()); + + let span = clean::Span::new(cx.tcx.def_span(did)); + clean::Module { items, span } +} + +fn build_module_items( + cx: &mut DocContext<'_>, + did: DefId, + visited: &mut FxHashSet, + inlined_names: &mut FxHashSet<(ItemType, Symbol)>, +) -> Vec { let mut items = Vec::new(); // If we're re-exporting a re-export it may actually re-export something in @@ -526,7 +548,13 @@ fn build_module( if item.vis.is_public() { let res = item.res.expect_non_local(); if let Some(def_id) = res.mod_def_id() { - if did == def_id || !visited.insert(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 + // inline it, or mark any of its contents as visited. + if did == def_id + || inlined_names.contains(&(ItemType::Module, item.ident.name)) + || !visited.insert(def_id) + { continue; } } @@ -563,8 +591,7 @@ fn build_module( } } - let span = clean::Span::new(cx.tcx.def_span(did)); - clean::Module { items, span } + items } pub(crate) fn print_inlined_const(tcx: TyCtxt<'_>, did: DefId) -> String { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 5fb9747a944d1..e18b0c2a77f97 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -71,7 +71,7 @@ impl<'tcx> Clean<'tcx, Item> for DocModule<'tcx> { // priority to the not-imported one, so we should, too. items.extend(self.items.iter().flat_map(|(item, renamed)| { // First, lower everything other than imports. - if matches!(item.kind, hir::ItemKind::Use(..)) { + if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) { return Vec::new(); } let v = clean_maybe_renamed_item(cx, item, *renamed); @@ -84,20 +84,13 @@ impl<'tcx> Clean<'tcx, Item> for DocModule<'tcx> { })); items.extend(self.items.iter().flat_map(|(item, renamed)| { // Now we actually lower the imports, skipping everything else. - if !matches!(item.kind, hir::ItemKind::Use(..)) { - return Vec::new(); + if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind { + let name = renamed.unwrap_or_else(|| cx.tcx.hir().name(item.hir_id())); + clean_use_statement(item, name, path, hir::UseKind::Glob, cx, &mut inserted) + } else { + // skip everything else + Vec::new() } - let mut v = clean_maybe_renamed_item(cx, item, *renamed); - v.drain_filter(|item| { - if let Some(name) = item.name { - // If an item with the same type and name already exists, - // it takes priority over the inlined stuff. - !inserted.insert((item.type_(), name)) - } else { - false - } - }); - v })); // determine if we should display the inner contents or @@ -1962,7 +1955,7 @@ fn clean_maybe_renamed_item<'tcx>( return clean_extern_crate(item, name, orig_name, cx); } ItemKind::Use(path, kind) => { - return clean_use_statement(item, name, path, kind, cx); + return clean_use_statement(item, name, path, kind, cx, &mut FxHashSet::default()); } _ => unreachable!("not yet converted"), }; @@ -2083,6 +2076,7 @@ fn clean_use_statement<'tcx>( path: &hir::Path<'tcx>, kind: hir::UseKind, cx: &mut DocContext<'tcx>, + inlined_names: &mut FxHashSet<(ItemType, Symbol)>, ) -> Vec { // We need this comparison because some imports (for std types for example) // are "inserted" as well but directly by the compiler and they should not be @@ -2148,7 +2142,8 @@ fn clean_use_statement<'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) { + if let Some(items) = inline::try_inline_glob(cx, path.res, &mut visited, inlined_names) + { return items; } } diff --git a/src/test/rustdoc/auxiliary/issue-100204-aux.rs b/src/test/rustdoc/auxiliary/issue-100204-aux.rs new file mode 100644 index 0000000000000..df1b59069be99 --- /dev/null +++ b/src/test/rustdoc/auxiliary/issue-100204-aux.rs @@ -0,0 +1,13 @@ +#![crate_name="first"] + +pub mod prelude { + pub use crate::Bot; +} + +pub struct Bot; + +impl Bot { + pub fn new() -> Bot { + Bot + } +} diff --git a/src/test/rustdoc/issue-100204-inline-impl-through-glob-import.rs b/src/test/rustdoc/issue-100204-inline-impl-through-glob-import.rs new file mode 100644 index 0000000000000..3e20c5c0741ef --- /dev/null +++ b/src/test/rustdoc/issue-100204-inline-impl-through-glob-import.rs @@ -0,0 +1,14 @@ +// aux-build:issue-100204-aux.rs +// build-aux-docs +// ignore-cross-compile + +#![crate_name="second"] + +extern crate first; + +pub mod prelude {} + +// @has first/struct.Bot.html '//h4[@class="code-header"]' 'pub fn new() -> Bot' +// @has second/struct.Bot.html '//h4[@class="code-header"]' 'pub fn new() -> Bot' +#[doc(inline)] +pub use first::*;