Skip to content

Commit aeeb197

Browse files
committed
rustdoc: link sibling where possible, even when not One True Path
This is, first of all, a size hack aimed at the `core::arch` modules that share items (x86-64/x86, aarch64/x86). They inline a lot of items from shared modules, and if we can avoid writing `../arm/WHATEVER` all over the aarch64 module, we can shave a few bytes off every link. Also, clicking a link in the 64 bit module and landing in the 32 bit module makes no sense.
1 parent c5de414 commit aeeb197

File tree

3 files changed

+96
-12
lines changed

3 files changed

+96
-12
lines changed

src/librustdoc/html/format.rs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -725,13 +725,25 @@ pub(crate) fn href_with_root_path(
725725
}
726726

727727
let mut is_remote = false;
728-
let (fqp, shortty, url_parts) = match cache.paths.get(&did) {
729-
Some(&(ref fqp, shortty)) => (fqp, shortty, {
730-
let module_fqp = to_module_fqp(shortty, fqp.as_slice());
731-
debug!(?fqp, ?shortty, ?module_fqp);
732-
href_relative_parts(module_fqp, relative_to).collect()
733-
}),
734-
None => {
728+
let chain_fqp: Vec<Symbol>;
729+
let (fqp, shortty, url_parts) =
730+
if let Some(&(shortty, name)) = cx.current_module_linkable_items.get(&did) {
731+
// If this item exists in the current module, then link to that.
732+
// The path within the current module might not be the One True Path,
733+
// but link to it anyway, since a relative path to a sibling is shorter.
734+
if shortty == ItemType::Module {
735+
(relative_to, shortty, UrlPartsBuilder::singleton(name.as_str()))
736+
} else {
737+
chain_fqp = relative_to.iter().copied().chain([name]).collect();
738+
(&chain_fqp, shortty, UrlPartsBuilder::new())
739+
}
740+
} else if let Some(&(ref fqp, shortty)) = cache.paths.get(&did) {
741+
// If this item exists in the current crate, then link to that.
742+
(fqp, shortty, {
743+
let module_fqp = to_module_fqp(shortty, fqp.as_slice());
744+
href_relative_parts(module_fqp, relative_to).collect()
745+
})
746+
} else {
735747
// Associated items are handled differently with "jump to def". The anchor is generated
736748
// directly here whereas for intra-doc links, we have some extra computation being
737749
// performed there.
@@ -746,8 +758,8 @@ pub(crate) fn href_with_root_path(
746758
} else {
747759
return generate_item_def_id_path(did, original_did, cx, root_path, def_kind);
748760
}
749-
}
750-
};
761+
};
762+
debug!(?fqp, ?shortty, ?url_parts);
751763
make_href(root_path, shortty, url_parts, fqp, is_remote)
752764
}
753765

src/librustdoc/html/render/context.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ pub(crate) struct Context<'tcx> {
4949
/// Current hierarchy of components leading down to what's currently being
5050
/// rendered
5151
pub(crate) current: Vec<Symbol>,
52+
/// Set of visible DefIds in the current module.
53+
/// This generates optimal link hrefs in the common case when
54+
/// an entire module is glob-inlined and its children link to each other.
55+
pub(crate) current_module_linkable_items: DefIdMap<(ItemType, Symbol)>,
5256
/// The current destination folder of where HTML artifacts should be placed.
5357
/// This changes as the context descends into the module hierarchy.
5458
pub(crate) dst: PathBuf,
@@ -79,9 +83,9 @@ pub(crate) struct Context<'tcx> {
7983

8084
// `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
8185
#[cfg(all(not(windows), target_pointer_width = "64"))]
82-
rustc_data_structures::static_assert_size!(Context<'_>, 160);
86+
rustc_data_structures::static_assert_size!(Context<'_>, 192);
8387
#[cfg(all(windows, target_pointer_width = "64"))]
84-
rustc_data_structures::static_assert_size!(Context<'_>, 168);
88+
rustc_data_structures::static_assert_size!(Context<'_>, 200);
8589

8690
/// Shared mutable state used in [`Context`] and elsewhere.
8791
pub(crate) struct SharedContext<'tcx> {
@@ -561,6 +565,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
561565

562566
let mut cx = Context {
563567
current: Vec::new(),
568+
current_module_linkable_items: Default::default(),
564569
dst,
565570
render_redirect_pages: false,
566571
id_map,
@@ -591,6 +596,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
591596
fn make_child_renderer(&self) -> Self {
592597
Self {
593598
current: self.current.clone(),
599+
current_module_linkable_items: self.current_module_linkable_items.clone(),
594600
dst: self.dst.clone(),
595601
render_redirect_pages: self.render_redirect_pages,
596602
deref_id_map: Default::default(),
@@ -785,8 +791,29 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
785791
info!("Recursing into {}", self.dst.display());
786792

787793
if !item.is_stripped() {
794+
// Populate a list of items that are directly in the module
795+
// or inlined into it.
796+
// When an intra-doc link or named type needs linked,
797+
// these are preferred over more distant paths.
798+
let (clean::StrippedItem(box clean::ModuleItem(ref module))
799+
| clean::ModuleItem(ref module)) = *item.kind
800+
else {
801+
unreachable!()
802+
};
803+
self.current_module_linkable_items = Default::default();
804+
for item in &module.items {
805+
if item.is_stripped() {
806+
continue;
807+
}
808+
if let Some(def_id) = item.def_id()
809+
&& let Some(name) = item.name
810+
{
811+
self.current_module_linkable_items.insert(def_id, (item.type_(), name));
812+
}
813+
}
814+
// Render the current module to HTML.
815+
// Buf will be empty if the module is stripped and there is no redirect for it.
788816
let buf = self.render_item(item, true);
789-
// buf will be empty if the module is stripped and there is no redirect for it
790817
if !buf.is_empty() {
791818
self.shared.ensure_dir(&self.dst)?;
792819
let joint_dst = self.dst.join("index.html");
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#![deny(rustdoc::broken_intra_doc_links)]
2+
#![allow(nonstandard_style)]
3+
4+
// Reduced case from `core::arch::{arm, aarch64}`.
5+
// Both versions of arm should link to the struct that they inlined.
6+
// aarch64 should not link to the inlined copy in arm 32.
7+
8+
mod arm_shared {
9+
pub struct uint32x4_t;
10+
pub struct uint32x4x2_t(pub uint32x4_t, pub uint32x4_t);
11+
pub fn vabaq_u32(
12+
_a: uint32x4_t,
13+
_b: uint32x4_t,
14+
_c: uint32x4_t
15+
) -> uint32x4_t {
16+
uint32x4_t
17+
}
18+
}
19+
20+
pub mod arm {
21+
pub use crate::arm_shared::*;
22+
// @has glob_inline_siblings/arm/fn.vabaq_u32.html '//a[@href="struct.uint32x4_t.html"]' 'uint32x4_t'
23+
// @has glob_inline_siblings/arm/struct.uint32x4x2_t.html '//a[@href="struct.uint32x4_t.html"]' 'uint32x4_t'
24+
// @!has glob_inline_siblings/arm/fn.vabaq_u32.html '//a[@href="../aarch64/struct.uint32x4_t.html"]' 'uint32x4_t'
25+
// @!has glob_inline_siblings/arm/struct.uint32x4x2_t.html '//a[@href="../aarch64/struct.uint32x4_t.html"]' 'uint32x4_t'
26+
27+
/// [uint32x4_t]
28+
// @has glob_inline_siblings/arm/struct.LocalThing.html '//a[@href="struct.uint32x4_t.html"]' 'uint32x4_t'
29+
// @!has glob_inline_siblings/arm/struct.LocalThing.html '//a[@href="../aarch64/struct.uint32x4_t.html"]' 'uint32x4_t'
30+
pub struct LocalThing;
31+
}
32+
33+
34+
pub mod aarch64 {
35+
pub use crate::arm_shared::*;
36+
// @has glob_inline_siblings/aarch64/fn.vabaq_u32.html '//a[@href="struct.uint32x4_t.html"]' 'uint32x4_t'
37+
// @has glob_inline_siblings/aarch64/struct.uint32x4x2_t.html '//a[@href="struct.uint32x4_t.html"]' 'uint32x4_t'
38+
// @!has glob_inline_siblings/aarch64/fn.vabaq_u32.html '//a[@href="../arm/struct.uint32x4_t.html"]' 'uint32x4_t'
39+
// @!has glob_inline_siblings/aarch64/struct.uint32x4x2_t.html '//a[@href="../arm/struct.uint32x4_t.html"]' 'uint32x4_t'
40+
41+
/// [uint32x4_t]
42+
// @has glob_inline_siblings/aarch64/struct.LocalThing.html '//a[@href="struct.uint32x4_t.html"]' 'uint32x4_t'
43+
// @!has glob_inline_siblings/aarch64/struct.LocalThing.html '//a[@href="../arm/struct.uint32x4_t.html"]' 'uint32x4_t'
44+
pub struct LocalThing;
45+
}

0 commit comments

Comments
 (0)