diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index f82b89fdd5f98..caf95ad0eac3b 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -725,13 +725,25 @@ pub(crate) fn href_with_root_path( } let mut is_remote = false; - let (fqp, shortty, url_parts) = match cache.paths.get(&did) { - Some(&(ref fqp, shortty)) => (fqp, shortty, { - let module_fqp = to_module_fqp(shortty, fqp.as_slice()); - debug!(?fqp, ?shortty, ?module_fqp); - href_relative_parts(module_fqp, relative_to).collect() - }), - None => { + let chain_fqp: Vec; + let (fqp, shortty, url_parts) = + if let Some(&(shortty, name)) = cx.current_module_linkable_items.get(&did) { + // If this item exists in the current module, then link to that. + // The path within the current module might not be the One True Path, + // but link to it anyway, since a relative path to a sibling is shorter. + if shortty == ItemType::Module { + (relative_to, shortty, UrlPartsBuilder::singleton(name.as_str())) + } else { + chain_fqp = relative_to.iter().copied().chain([name]).collect(); + (&chain_fqp, shortty, UrlPartsBuilder::new()) + } + } else if let Some(&(ref fqp, shortty)) = cache.paths.get(&did) { + // If this item exists in the current crate, then link to that. + (fqp, shortty, { + let module_fqp = to_module_fqp(shortty, fqp.as_slice()); + href_relative_parts(module_fqp, relative_to).collect() + }) + } else { // Associated items are handled differently with "jump to def". The anchor is generated // directly here whereas for intra-doc links, we have some extra computation being // performed there. @@ -746,8 +758,8 @@ pub(crate) fn href_with_root_path( } else { return generate_item_def_id_path(did, original_did, cx, root_path, def_kind); } - } - }; + }; + debug!(?fqp, ?shortty, ?url_parts); make_href(root_path, shortty, url_parts, fqp, is_remote) } diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index db1119eca1d3d..756f1b6029540 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -49,6 +49,10 @@ pub(crate) struct Context<'tcx> { /// Current hierarchy of components leading down to what's currently being /// rendered pub(crate) current: Vec, + /// Set of visible DefIds in the current module. + /// This generates optimal link hrefs in the common case when + /// an entire module is glob-inlined and its children link to each other. + pub(crate) current_module_linkable_items: DefIdMap<(ItemType, Symbol)>, /// The current destination folder of where HTML artifacts should be placed. /// This changes as the context descends into the module hierarchy. pub(crate) dst: PathBuf, @@ -79,9 +83,9 @@ pub(crate) struct Context<'tcx> { // `Context` is cloned a lot, so we don't want the size to grow unexpectedly. #[cfg(all(not(windows), target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Context<'_>, 160); +rustc_data_structures::static_assert_size!(Context<'_>, 192); #[cfg(all(windows, target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Context<'_>, 168); +rustc_data_structures::static_assert_size!(Context<'_>, 200); /// Shared mutable state used in [`Context`] and elsewhere. pub(crate) struct SharedContext<'tcx> { @@ -561,6 +565,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { let mut cx = Context { current: Vec::new(), + current_module_linkable_items: Default::default(), dst, render_redirect_pages: false, id_map, @@ -591,6 +596,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { fn make_child_renderer(&self) -> Self { Self { current: self.current.clone(), + current_module_linkable_items: self.current_module_linkable_items.clone(), dst: self.dst.clone(), render_redirect_pages: self.render_redirect_pages, deref_id_map: Default::default(), @@ -785,8 +791,38 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { info!("Recursing into {}", self.dst.display()); if !item.is_stripped() { + // Populate a list of items that are directly in the module + // or inlined into it. + // When an intra-doc link or named type needs linked, + // these are preferred over more distant paths. + let (clean::StrippedItem(box clean::ModuleItem(ref module)) + | clean::ModuleItem(ref module)) = *item.kind + else { + unreachable!() + }; + self.current_module_linkable_items = Default::default(); + for item in &module.items { + if item.is_stripped() { + continue; + } + if let Some(def_id) = item.def_id() + && let Some(name) = item.name + { + if let Some((path, item_type)) = self.cache().paths.get(&def_id) + && *item_type == item.type_() + && &path[path.len() - 1..] == &self.current[..] + { + // Avoid populating this list with no-ops. + // If this module is already the One True Path, + // that's sufficient. + continue; + } + self.current_module_linkable_items.insert(def_id, (item.type_(), name)); + } + } + // Render the current module to HTML. + // Buf will be empty if the module is stripped and there is no redirect for it. let buf = self.render_item(item, true); - // buf will be empty if the module is stripped and there is no redirect for it if !buf.is_empty() { self.shared.ensure_dir(&self.dst)?; let joint_dst = self.dst.join("index.html"); diff --git a/tests/rustdoc/intra-doc/glob-inline-siblings.rs b/tests/rustdoc/intra-doc/glob-inline-siblings.rs new file mode 100644 index 0000000000000..c2f5b01673491 --- /dev/null +++ b/tests/rustdoc/intra-doc/glob-inline-siblings.rs @@ -0,0 +1,45 @@ +#![deny(rustdoc::broken_intra_doc_links)] +#![allow(nonstandard_style)] + +// Reduced case from `core::arch::{arm, aarch64}`. +// Both versions of arm should link to the struct that they inlined. +// aarch64 should not link to the inlined copy in arm 32. + +mod arm_shared { + pub struct uint32x4_t; + pub struct uint32x4x2_t(pub uint32x4_t, pub uint32x4_t); + pub fn vabaq_u32( + _a: uint32x4_t, + _b: uint32x4_t, + _c: uint32x4_t + ) -> uint32x4_t { + uint32x4_t + } +} + +pub mod arm { + pub use crate::arm_shared::*; + // @has glob_inline_siblings/arm/fn.vabaq_u32.html '//a[@href="struct.uint32x4_t.html"]' 'uint32x4_t' + // @has glob_inline_siblings/arm/struct.uint32x4x2_t.html '//a[@href="struct.uint32x4_t.html"]' 'uint32x4_t' + // @!has glob_inline_siblings/arm/fn.vabaq_u32.html '//a[@href="../aarch64/struct.uint32x4_t.html"]' 'uint32x4_t' + // @!has glob_inline_siblings/arm/struct.uint32x4x2_t.html '//a[@href="../aarch64/struct.uint32x4_t.html"]' 'uint32x4_t' + + /// [uint32x4_t] + // @has glob_inline_siblings/arm/struct.LocalThing.html '//a[@href="struct.uint32x4_t.html"]' 'uint32x4_t' + // @!has glob_inline_siblings/arm/struct.LocalThing.html '//a[@href="../aarch64/struct.uint32x4_t.html"]' 'uint32x4_t' + pub struct LocalThing; +} + + +pub mod aarch64 { + pub use crate::arm_shared::*; + // @has glob_inline_siblings/aarch64/fn.vabaq_u32.html '//a[@href="struct.uint32x4_t.html"]' 'uint32x4_t' + // @has glob_inline_siblings/aarch64/struct.uint32x4x2_t.html '//a[@href="struct.uint32x4_t.html"]' 'uint32x4_t' + // @!has glob_inline_siblings/aarch64/fn.vabaq_u32.html '//a[@href="../arm/struct.uint32x4_t.html"]' 'uint32x4_t' + // @!has glob_inline_siblings/aarch64/struct.uint32x4x2_t.html '//a[@href="../arm/struct.uint32x4_t.html"]' 'uint32x4_t' + + /// [uint32x4_t] + // @has glob_inline_siblings/aarch64/struct.LocalThing.html '//a[@href="struct.uint32x4_t.html"]' 'uint32x4_t' + // @!has glob_inline_siblings/aarch64/struct.LocalThing.html '//a[@href="../arm/struct.uint32x4_t.html"]' 'uint32x4_t' + pub struct LocalThing; +}