diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 23e0c2625eeeb..a8f4848bf89f2 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -38,7 +38,7 @@ use super::Clean; /// /// The returned value is `None` if the definition could not be inlined, /// and `Some` of a vector of items if it was successfully expanded. -pub fn try_inline(cx: &DocContext, def: Def, name: ast::Name) +pub fn try_inline(cx: &DocContext, def: Def, name: ast::Name, visited: &mut FxHashSet) -> Option> { if def == Def::Err { return None } let did = def.def_id(); @@ -87,7 +87,7 @@ pub fn try_inline(cx: &DocContext, def: Def, name: ast::Name) Def::StructCtor(..) => return Some(Vec::new()), Def::Mod(did) => { record_extern_fqn(cx, did, clean::TypeKind::Module); - clean::ModuleItem(build_module(cx, did)) + clean::ModuleItem(build_module(cx, did, visited)) } Def::Static(did, mtbl) => { record_extern_fqn(cx, did, clean::TypeKind::Static); @@ -385,24 +385,24 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec) { }); } -fn build_module(cx: &DocContext, did: DefId) -> clean::Module { +fn build_module(cx: &DocContext, did: DefId, visited: &mut FxHashSet) -> clean::Module { let mut items = Vec::new(); - fill_in(cx, did, &mut items); + fill_in(cx, did, &mut items, visited); return clean::Module { items, is_crate: false, }; - fn fill_in(cx: &DocContext, did: DefId, items: &mut Vec) { + fn fill_in(cx: &DocContext, did: DefId, items: &mut Vec, + visited: &mut FxHashSet) { // If we're re-exporting a re-export it may actually re-export something in // two namespaces, so the target may be listed twice. Make sure we only // visit each node at most once. - let mut visited = FxHashSet(); for &item in cx.tcx.item_children(did).iter() { let def_id = item.def.def_id(); if item.vis == ty::Visibility::Public { - if !visited.insert(def_id) { continue } - if let Some(i) = try_inline(cx, item.def, item.ident.name) { + if did == def_id || !visited.insert(def_id) { continue } + if let Some(i) = try_inline(cx, item.def, item.ident.name, visited) { items.extend(i) } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 6623d5ab3c2c2..a7d32664f1342 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -3673,7 +3673,8 @@ impl Clean> for doctree::Import { } else { let name = self.name; if !denied { - if let Some(items) = inline::try_inline(cx, path.def, name) { + let mut visited = FxHashSet(); + if let Some(items) = inline::try_inline(cx, path.def, name, &mut visited) { return items; } } diff --git a/src/test/rustdoc/auxiliary/mod-stackoverflow.rs b/src/test/rustdoc/auxiliary/mod-stackoverflow.rs new file mode 100644 index 0000000000000..f03593dbee60b --- /dev/null +++ b/src/test/rustdoc/auxiliary/mod-stackoverflow.rs @@ -0,0 +1,21 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Cmetadata=aux + +pub mod tree { + pub use tree; +} + +pub mod tree2 { + pub mod prelude { + pub use tree2; + } +} diff --git a/src/test/rustdoc/mod-stackoverflow.rs b/src/test/rustdoc/mod-stackoverflow.rs new file mode 100644 index 0000000000000..1e2f6dbe78045 --- /dev/null +++ b/src/test/rustdoc/mod-stackoverflow.rs @@ -0,0 +1,16 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:mod-stackoverflow.rs +// ignore-cross-compile + +extern crate mod_stackoverflow; +pub use mod_stackoverflow::tree; +pub use mod_stackoverflow::tree2;