From 63a291febac3ba2cb48787fed24388c2817ef4a2 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 9 Apr 2017 12:00:34 -0400 Subject: [PATCH] Fix rustdoc infinitely recursing when an external crate reexports itself Previously, rustdoc's LibEmbargoVisitor unconditionally visited the child modules of an external crate. If a module re-exported its parent via 'pub use super::*', rustdoc would re-walk the parent, leading to infinite recursion. This commit makes LibEmbargoVisitor store already visited modules in an FxHashSet, ensuring that each module is only walked once. Fixes #40936 --- src/librustdoc/visit_lib.rs | 8 ++++++++ src/test/rustdoc/auxiliary/issue-40936.rs | 15 +++++++++++++++ src/test/rustdoc/issue-40936.rs | 16 ++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 src/test/rustdoc/auxiliary/issue-40936.rs create mode 100644 src/test/rustdoc/issue-40936.rs diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs index 852c98eb2fd52..40a6ffe9505fd 100644 --- a/src/librustdoc/visit_lib.rs +++ b/src/librustdoc/visit_lib.rs @@ -13,6 +13,7 @@ use rustc::middle::privacy::{AccessLevels, AccessLevel}; use rustc::hir::def::Def; use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId}; use rustc::ty::Visibility; +use rustc::util::nodemap::FxHashSet; use std::cell::RefMut; @@ -29,6 +30,8 @@ pub struct LibEmbargoVisitor<'a, 'b: 'a, 'tcx: 'b> { access_levels: RefMut<'a, AccessLevels>, // Previous accessibility level, None means unreachable prev_level: Option, + // Keeps track of already visited modules, in case a module re-exports its parent + visited_mods: FxHashSet, } impl<'a, 'b, 'tcx> LibEmbargoVisitor<'a, 'b, 'tcx> { @@ -38,6 +41,7 @@ impl<'a, 'b, 'tcx> LibEmbargoVisitor<'a, 'b, 'tcx> { cstore: &*cx.sess().cstore, access_levels: cx.access_levels.borrow_mut(), prev_level: Some(AccessLevel::Public), + visited_mods: FxHashSet() } } @@ -62,6 +66,10 @@ impl<'a, 'b, 'tcx> LibEmbargoVisitor<'a, 'b, 'tcx> { } pub fn visit_mod(&mut self, def_id: DefId) { + if !self.visited_mods.insert(def_id) { + return; + } + for item in self.cstore.item_children(def_id) { self.visit_item(item.def); } diff --git a/src/test/rustdoc/auxiliary/issue-40936.rs b/src/test/rustdoc/auxiliary/issue-40936.rs new file mode 100644 index 0000000000000..54cc18cca23ac --- /dev/null +++ b/src/test/rustdoc/auxiliary/issue-40936.rs @@ -0,0 +1,15 @@ +// Copyright 2017 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. + +pub mod outermod { + pub mod innermod { + pub use super::*; + } +} diff --git a/src/test/rustdoc/issue-40936.rs b/src/test/rustdoc/issue-40936.rs new file mode 100644 index 0000000000000..3e02eec1b9c30 --- /dev/null +++ b/src/test/rustdoc/issue-40936.rs @@ -0,0 +1,16 @@ +// Copyright 2017 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:issue-40936.rs +// build-aux-docs + +#![crate_name = "foo"] + +extern crate issue_40936;