Skip to content

Commit 7e29512

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 becebb3 commit 7e29512

File tree

3 files changed

+85
-11
lines changed

3 files changed

+85
-11
lines changed

src/librustdoc/html/format.rs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -725,13 +725,26 @@ 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+
debug!(?fqp, ?shortty, ?module_fqp);
745+
href_relative_parts(module_fqp, relative_to).collect()
746+
})
747+
} else {
735748
// Associated items are handled differently with "jump to def". The anchor is generated
736749
// directly here whereas for intra-doc links, we have some extra computation being
737750
// performed there.
@@ -746,8 +759,7 @@ pub(crate) fn href_with_root_path(
746759
} else {
747760
return generate_item_def_id_path(did, original_did, cx, root_path, def_kind);
748761
}
749-
}
750-
};
762+
};
751763
make_href(root_path, shortty, url_parts, fqp, is_remote)
752764
}
753765

src/librustdoc/html/render/context.rs

Lines changed: 29 additions & 2 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,7 +83,7 @@ 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_arch = "x86_64", target_pointer_width = "64"))]
82-
rustc_data_structures::static_assert_size!(Context<'_>, 160);
86+
rustc_data_structures::static_assert_size!(Context<'_>, 192);
8387

8488
/// Shared mutable state used in [`Context`] and elsewhere.
8589
pub(crate) struct SharedContext<'tcx> {
@@ -559,6 +563,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
559563

560564
let mut cx = Context {
561565
current: Vec::new(),
566+
current_module_linkable_items: Default::default(),
562567
dst,
563568
render_redirect_pages: false,
564569
id_map,
@@ -589,6 +594,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
589594
fn make_child_renderer(&self) -> Self {
590595
Self {
591596
current: self.current.clone(),
597+
current_module_linkable_items: self.current_module_linkable_items.clone(),
592598
dst: self.dst.clone(),
593599
render_redirect_pages: self.render_redirect_pages,
594600
deref_id_map: Default::default(),
@@ -783,8 +789,29 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
783789
info!("Recursing into {}", self.dst.display());
784790

785791
if !item.is_stripped() {
792+
// Populate a list of items that are directly in the module
793+
// or inlined into it.
794+
// When an intra-doc link or named type needs linked,
795+
// these are preferred over more distant paths.
796+
let (clean::StrippedItem(box clean::ModuleItem(ref module))
797+
| clean::ModuleItem(ref module)) = *item.kind
798+
else {
799+
unreachable!()
800+
};
801+
self.current_module_linkable_items = Default::default();
802+
for item in &module.items {
803+
if item.is_stripped() {
804+
continue;
805+
}
806+
if let Some(def_id) = item.def_id()
807+
&& let Some(name) = item.name
808+
{
809+
self.current_module_linkable_items.insert(def_id, (item.type_(), name));
810+
}
811+
}
812+
// Render the current module to HTML.
813+
// Buf will be empty if the module is stripped and there is no redirect for it.
786814
let buf = self.render_item(item, true);
787-
// buf will be empty if the module is stripped and there is no redirect for it
788815
if !buf.is_empty() {
789816
self.shared.ensure_dir(&self.dst)?;
790817
let joint_dst = self.dst.join("index.html");
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
28+
29+
pub mod aarch64 {
30+
pub use crate::arm_shared::*;
31+
// @has glob_inline_siblings/aarch64/fn.vabaq_u32.html '//a[@href="struct.uint32x4_t.html"]' 'uint32x4_t'
32+
// @has glob_inline_siblings/aarch64/struct.uint32x4x2_t.html '//a[@href="struct.uint32x4_t.html"]' 'uint32x4_t'
33+
// @!has glob_inline_siblings/aarch64/fn.vabaq_u32.html '//a[@href="../arm/struct.uint32x4_t.html"]' 'uint32x4_t'
34+
// @!has glob_inline_siblings/aarch64/struct.uint32x4x2_t.html '//a[@href="../arm/struct.uint32x4_t.html"]' 'uint32x4_t'
35+
}

0 commit comments

Comments
 (0)