diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 97cc76d833e9e..929441125dd52 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use rustc_hir::attrs::Deprecation; use rustc_hir::def::{CtorKind, DefKind}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::bug; @@ -473,6 +473,9 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { // This is a rudimentary check that does not catch all cases, // just the easiest. let mut fallback_map: Vec<(DefId, DefId)> = Default::default(); + // hidden items never enter the map, so dedup here or every + // parent path re-enqueues the whole subtree (#160439) + let mut fallback_seen: DefIdSet = Default::default(); // Issue 46112: We want the map to prefer the shortest // paths when reporting the path to an item. Therefore we @@ -534,6 +537,10 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { } Entry::Vacant(entry) => { if fallback { + // seen already, skip this parent + if !fallback_seen.insert(def_id) { + return; + } // We do all of the same steps to fallback entries as to // preferred entries, except for recording them in a separate map. // It is important to not return early in the fallback cases to diff --git a/tests/run-make/rustdoc/visible-parent-map-hang/dep.rs b/tests/run-make/rustdoc/visible-parent-map-hang/dep.rs new file mode 100644 index 0000000000000..8a95ecd783547 --- /dev/null +++ b/tests/run-make/rustdoc/visible-parent-map-hang/dep.rs @@ -0,0 +1,143 @@ +#![crate_type = "lib"] +#![allow(dead_code)] + +pub trait Tr { fn x(); } +pub trait Tr2 { fn x(); } + +#[doc(hidden)] +pub mod outer { + #[doc(hidden)] + pub mod l0 { + #[doc(hidden)] pub mod s0 { pub use super::l1; } + #[doc(hidden)] pub mod s1 { pub use super::l1; } + #[doc(hidden)] pub mod s2 { pub use super::l1; } + #[doc(hidden)] pub mod s3 { pub use super::l1; } + #[doc(hidden)] pub mod s4 { pub use super::l1; } + #[doc(hidden)] pub mod s5 { pub use super::l1; } + #[doc(hidden)] pub mod s6 { pub use super::l1; } + #[doc(hidden)] pub mod s7 { pub use super::l1; } + #[doc(hidden)] + pub mod l1 { + #[doc(hidden)] pub mod s0 { pub use super::l2; } + #[doc(hidden)] pub mod s1 { pub use super::l2; } + #[doc(hidden)] pub mod s2 { pub use super::l2; } + #[doc(hidden)] pub mod s3 { pub use super::l2; } + #[doc(hidden)] pub mod s4 { pub use super::l2; } + #[doc(hidden)] pub mod s5 { pub use super::l2; } + #[doc(hidden)] pub mod s6 { pub use super::l2; } + #[doc(hidden)] pub mod s7 { pub use super::l2; } + #[doc(hidden)] + pub mod l2 { + #[doc(hidden)] pub mod s0 { pub use super::l3; } + #[doc(hidden)] pub mod s1 { pub use super::l3; } + #[doc(hidden)] pub mod s2 { pub use super::l3; } + #[doc(hidden)] pub mod s3 { pub use super::l3; } + #[doc(hidden)] pub mod s4 { pub use super::l3; } + #[doc(hidden)] pub mod s5 { pub use super::l3; } + #[doc(hidden)] pub mod s6 { pub use super::l3; } + #[doc(hidden)] pub mod s7 { pub use super::l3; } + #[doc(hidden)] + pub mod l3 { + #[doc(hidden)] pub mod s0 { pub use super::l4; } + #[doc(hidden)] pub mod s1 { pub use super::l4; } + #[doc(hidden)] pub mod s2 { pub use super::l4; } + #[doc(hidden)] pub mod s3 { pub use super::l4; } + #[doc(hidden)] pub mod s4 { pub use super::l4; } + #[doc(hidden)] pub mod s5 { pub use super::l4; } + #[doc(hidden)] pub mod s6 { pub use super::l4; } + #[doc(hidden)] pub mod s7 { pub use super::l4; } + #[doc(hidden)] + pub mod l4 { + #[doc(hidden)] pub mod s0 { pub use super::l5; } + #[doc(hidden)] pub mod s1 { pub use super::l5; } + #[doc(hidden)] pub mod s2 { pub use super::l5; } + #[doc(hidden)] pub mod s3 { pub use super::l5; } + #[doc(hidden)] pub mod s4 { pub use super::l5; } + #[doc(hidden)] pub mod s5 { pub use super::l5; } + #[doc(hidden)] pub mod s6 { pub use super::l5; } + #[doc(hidden)] pub mod s7 { pub use super::l5; } + #[doc(hidden)] + pub mod l5 { + #[doc(hidden)] pub mod s0 { pub use super::l6; } + #[doc(hidden)] pub mod s1 { pub use super::l6; } + #[doc(hidden)] pub mod s2 { pub use super::l6; } + #[doc(hidden)] pub mod s3 { pub use super::l6; } + #[doc(hidden)] pub mod s4 { pub use super::l6; } + #[doc(hidden)] pub mod s5 { pub use super::l6; } + #[doc(hidden)] pub mod s6 { pub use super::l6; } + #[doc(hidden)] pub mod s7 { pub use super::l6; } + #[doc(hidden)] + pub mod l6 { + #[doc(hidden)] pub mod s0 { pub use super::l7; } + #[doc(hidden)] pub mod s1 { pub use super::l7; } + #[doc(hidden)] pub mod s2 { pub use super::l7; } + #[doc(hidden)] pub mod s3 { pub use super::l7; } + #[doc(hidden)] pub mod s4 { pub use super::l7; } + #[doc(hidden)] pub mod s5 { pub use super::l7; } + #[doc(hidden)] pub mod s6 { pub use super::l7; } + #[doc(hidden)] pub mod s7 { pub use super::l7; } + #[doc(hidden)] + pub mod l7 { + #[doc(hidden)] pub mod s0 { pub use super::l8; } + #[doc(hidden)] pub mod s1 { pub use super::l8; } + #[doc(hidden)] pub mod s2 { pub use super::l8; } + #[doc(hidden)] pub mod s3 { pub use super::l8; } + #[doc(hidden)] pub mod s4 { pub use super::l8; } + #[doc(hidden)] pub mod s5 { pub use super::l8; } + #[doc(hidden)] pub mod s6 { pub use super::l8; } + #[doc(hidden)] pub mod s7 { pub use super::l8; } + #[doc(hidden)] + pub mod l8 { + #[doc(hidden)] pub mod s0 { pub use super::l9; } + #[doc(hidden)] pub mod s1 { pub use super::l9; } + #[doc(hidden)] pub mod s2 { pub use super::l9; } + #[doc(hidden)] pub mod s3 { pub use super::l9; } + #[doc(hidden)] pub mod s4 { pub use super::l9; } + #[doc(hidden)] pub mod s5 { pub use super::l9; } + #[doc(hidden)] pub mod s6 { pub use super::l9; } + #[doc(hidden)] pub mod s7 { pub use super::l9; } + #[doc(hidden)] + pub mod l9 { + #[doc(hidden)] pub mod s0 { pub use super::l10; } + #[doc(hidden)] pub mod s1 { pub use super::l10; } + #[doc(hidden)] pub mod s2 { pub use super::l10; } + #[doc(hidden)] pub mod s3 { pub use super::l10; } + #[doc(hidden)] pub mod s4 { pub use super::l10; } + #[doc(hidden)] pub mod s5 { pub use super::l10; } + #[doc(hidden)] pub mod s6 { pub use super::l10; } + #[doc(hidden)] pub mod s7 { pub use super::l10; } + #[doc(hidden)] + pub mod l10 { + #[doc(hidden)] pub mod s0 { pub use super::l11; } + #[doc(hidden)] pub mod s1 { pub use super::l11; } + #[doc(hidden)] pub mod s2 { pub use super::l11; } + #[doc(hidden)] pub mod s3 { pub use super::l11; } + #[doc(hidden)] pub mod s4 { pub use super::l11; } + #[doc(hidden)] pub mod s5 { pub use super::l11; } + #[doc(hidden)] pub mod s6 { pub use super::l11; } + #[doc(hidden)] pub mod s7 { pub use super::l11; } + #[doc(hidden)] + pub mod l11 { + #[doc(hidden)] pub mod s0 { pub use super::leaf::S; } + #[doc(hidden)] pub mod s1 { pub use super::leaf::S; } + #[doc(hidden)] pub mod s2 { pub use super::leaf::S; } + #[doc(hidden)] pub mod s3 { pub use super::leaf::S; } + #[doc(hidden)] pub mod s4 { pub use super::leaf::S; } + #[doc(hidden)] pub mod s5 { pub use super::leaf::S; } + #[doc(hidden)] pub mod s6 { pub use super::leaf::S; } + #[doc(hidden)] pub mod s7 { pub use super::leaf::S; } + #[doc(hidden)] pub mod leaf { pub struct S; } + } + } + } + } + } + } + } + } + } + } + } + } +} + diff --git a/tests/run-make/rustdoc/visible-parent-map-hang/main.rs b/tests/run-make/rustdoc/visible-parent-map-hang/main.rs new file mode 100644 index 0000000000000..c4f4eee856bc4 --- /dev/null +++ b/tests/run-make/rustdoc/visible-parent-map-hang/main.rs @@ -0,0 +1,13 @@ +#![crate_type = "lib"] +extern crate dep; +pub use dep::Tr; +use std::fmt; + +pub struct Local; +impl dep::Tr for Local { fn x() {} } +impl dep::Tr2 for Local { fn x() {} } +impl fmt::Display for Local { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "x") } +} +impl Default for Local { fn default() -> Self { Local } } + diff --git a/tests/run-make/rustdoc/visible-parent-map-hang/rmake.rs b/tests/run-make/rustdoc/visible-parent-map-hang/rmake.rs new file mode 100644 index 0000000000000..2d5eff700f3f1 --- /dev/null +++ b/tests/run-make/rustdoc/visible-parent-map-hang/rmake.rs @@ -0,0 +1,36 @@ +// Regression test for https://github.com/rust-lang/rust/issues/160439. +// Nested `#[doc(hidden)]` modules used to make the visible_parent_map BFS +// re-enqueue the same subtree for every parent path, which is exponential. + +use std::process::{Command, Stdio}; +use std::time::{Duration, Instant}; + +use run_make_support::{env_var, rust_lib_name, rustc}; + +fn main() { + rustc().input("dep.rs").edition("2021").crate_type("lib").run(); + + // The bug made rustdoc hang here, so kill it if it takes too long. + // compiletest has no per-test timeout, hence the manual watchdog. + let mut child = Command::new(env_var("RUSTDOC")) + .args(["--edition", "2021", "--crate-name", "main", "-o", "target"]) + .arg(format!("--extern=dep={}", rust_lib_name("dep"))) + .arg("main.rs") + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .unwrap(); + + let deadline = Instant::now() + Duration::from_secs(60); + let status = loop { + if let Some(status) = child.try_wait().unwrap() { + break status; + } + if Instant::now() > deadline { + child.kill().unwrap(); + panic!("rustdoc timed out: visible_parent_map regression?"); + } + std::thread::sleep(Duration::from_millis(100)); + }; + assert!(status.success()); +}