Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
22 changes: 21 additions & 1 deletion src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,27 @@ impl Item {

/// Generates the HTML file name based on the item kind.
pub(crate) fn html_filename(&self) -> String {
format!("{type_}.{name}.html", type_ = self.type_(), name = self.name.unwrap())
let type_ = match self.type_() {
ItemType::Struct | ItemType::Union | ItemType::Enum | ItemType::ForeignType => "type",
type_ => type_.as_str(),
};
format!("{type_}.{name}.html", name = self.name.unwrap())
}

/// Generates the HTML file for the legacy redirect
/// <https://gist.github.com/notriddle/08752eb42289c82a69638d65553571c4>
pub(crate) fn html_redirect_filename(&self) -> Option<String> {
let redirect_type = match self.type_() {
ItemType::Struct => "struct",
ItemType::Union => "union",
ItemType::Enum => "enum",
ItemType::ForeignType => "foreigntype",
ItemType::Macro => {
return Some(format!("macro.{name}!.html", name = self.name.unwrap()));
}
_ => return None,
};
Some(format!("{redirect_type}.{name}.html", name = self.name.unwrap()))
}

/// Returns a `FnHeader` if `self` is a function item, otherwise returns `None`.
Expand Down
5 changes: 5 additions & 0 deletions src/librustdoc/html/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,11 @@ fn make_href(
ItemType::Module => {
url_parts.push("index.html");
}
// <https://gist.github.com/notriddle/08752eb42289c82a69638d65553571c4>
ItemType::Struct | ItemType::Union | ItemType::Enum | ItemType::ForeignType => {
let last = fqp.last().unwrap();
url_parts.push_fmt(format_args!("type.{last}.html"));
}
_ => {
let last = fqp.last().unwrap();
url_parts.push_fmt(format_args!("{shortty}.{last}.html"));
Expand Down
60 changes: 42 additions & 18 deletions src/librustdoc/html/render/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use crate::formats::FormatRenderer;
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
use crate::html::escape::Escape;
use crate::html::format::href_relative_parts;
use crate::html::macro_expansion::ExpandedCode;
use crate::html::markdown::{self, ErrorCodes, IdMap, plain_text_summary};
use crate::html::render::write_shared::write_shared;
Expand Down Expand Up @@ -303,14 +304,18 @@ impl<'tcx> Context<'tcx> {
// preventing an infinite redirection loop in the generated
// documentation.

let path = fmt::from_fn(|f| {
for name in &info.parts[..info.parts.len() - 1] {
write!(f, "{name}/")?;
}
write!(f, "{}", print_ty_path(info.ty, info.parts.last().unwrap().as_str()))
});
match self.shared.redirections {
Some(ref redirections) => {
let path = fmt::from_fn(|f| {
for name in &info.parts[..info.parts.len() - 1] {
write!(f, "{name}/")?;
}
write!(
f,
"{}",
print_ty_path(info.ty, info.parts.last().unwrap().as_str())
)
});
let mut current_path = String::new();
for name in &self.current {
current_path.push_str(name.as_str());
Expand All @@ -324,7 +329,13 @@ impl<'tcx> Context<'tcx> {
redirections.borrow_mut().insert(current_path, path.to_string());
}
None => {
return layout::redirect(&format!("{root}{path}", root = self.root_path()));
let mut path =
href_relative_parts(&info.parts[..info.parts.len() - 1], &self.current);
path.push_fmt(format_args!(
"{}",
print_ty_path(info.ty, info.parts.last().unwrap().as_str())
));
return layout::redirect(&path.finish());
}
}
}
Expand Down Expand Up @@ -794,19 +805,32 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
self.shared.fs.write(joint_dst, buf)?;
// If the item is a macro, redirect from the old macro URL (with !)
// to the new one (without).
let item_type = item.type_();
if item_type == ItemType::Macro {
let name = item.name.as_ref().unwrap();
let redir_name = format!("{item_type}.{name}!.html");
if let Some(html_redirect_filename) = item.html_redirect_filename() {
let to_parts = self
.cache()
.paths
.get(&item.item_id.expect_def_id())
.map_or(&self.current, |info| &info.parts);
if let Some(ref redirections) = self.shared.redirections {
let crate_name = &self.shared.layout.krate;
redirections.borrow_mut().insert(
format!("{crate_name}/{redir_name}"),
format!("{crate_name}/{file_name}"),
);
let to_path = fmt::from_fn(|f| {
for name in &to_parts[..to_parts.len() - 1] {
write!(f, "{name}/")?;
}
write!(f, "{}", item.html_filename())
});
let from_path = fmt::from_fn(|f| {
for name in &self.current {
write!(f, "{name}/")?;
}
write!(f, "{}", html_redirect_filename)
});
redirections.borrow_mut().insert(from_path.to_string(), to_path.to_string());
} else {
let v = layout::redirect(&file_name);
let redir_dst = self.dst.join(redir_name);
let mut to_path =
href_relative_parts(&to_parts[..to_parts.len() - 1], &self.current);
to_path.push(&item.html_filename());
let v = layout::redirect(&to_path.finish());
let redir_dst = self.dst.join(html_redirect_filename);
self.shared.fs.write(redir_dst, v)?;
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/librustdoc/html/render/print_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2326,6 +2326,10 @@ pub(super) fn print_item_path(item: &clean::Item) -> impl Display {
pub(super) fn print_ty_path(ty: ItemType, name: &str) -> impl Display {
fmt::from_fn(move |f| match ty {
ItemType::Module => write!(f, "{}index.html", ensure_trailing_slash(name)),
// <https://gist.github.com/notriddle/08752eb42289c82a69638d65553571c4>
ItemType::Struct | ItemType::Union | ItemType::Enum | ItemType::ForeignType => {
write!(f, "type.{name}.html")
}
_ => write!(f, "{ty}.{name}.html"),
})
}
Expand Down
7 changes: 7 additions & 0 deletions src/librustdoc/html/static/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,13 @@ function preLoadCss(cssUrl) {
let path;
if (shortty === "mod") {
path = `${modpath}${name}/index.html`;
} else if (
shortty === "struct" ||
shortty === "union" ||
shortty === "enum" ||
shortty === "foreigntype"
) {
path = `${modpath}type.${name}.html`;
} else if (!isMacro) {
path = `${modpath}${shortty}.${name}.html`;
} else {
Expand Down
19 changes: 17 additions & 2 deletions src/librustdoc/html/static/js/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -2171,6 +2171,21 @@ class DocSearch {
let path = item.modulePath;
let exactPath = item.exactModulePath;

/**
* @param {string} type
* @returns {string}
*/
const normalizeType = type => {
if (type === "struct" ||
type === "union" ||
type === "enum" ||
type === "foreigntype"
) {
return "type";
}
return type;
};

if (type === "mod") {
displayPath = path + "::";
href = this.rootPath + path.replace(/::/g, "/") + "/" +
Expand Down Expand Up @@ -2220,13 +2235,13 @@ class DocSearch {
anchor = item.entry.associatedItemDisambiguatorOrExternCrateUrl + "/" + anchor;
}
href = this.rootPath + path.replace(/::/g, "/") +
"/" + pageType +
"/" + normalizeType(pageType) +
"." + pageName +
".html#" + anchor;
} else {
displayPath = item.modulePath + "::";
href = this.rootPath + item.modulePath.replace(/::/g, "/") +
"/" + type + "." + name + ".html";
"/" + normalizeType(type) + "." + name + ".html";
}
if (item.traitParent) {
const tparent = item.traitParent;
Expand Down
4 changes: 4 additions & 0 deletions src/librustdoc/passes/lint/redundant_explicit_links.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,10 @@ fn local_href_for_res(cx: &DocContext<'_>, module_id: DefId, res: Res<NodeId>) -
let mut url_parts = href_relative_parts(module_fqp, &current_fqp);
match item_type {
ItemType::Module => url_parts.push("index.html"),
// <https://gist.github.com/notriddle/08752eb42289c82a69638d65553571c4>
ItemType::Struct | ItemType::Union | ItemType::Enum | ItemType::ForeignType => {
url_parts.push_fmt(format_args!("type.{last}.html", last = fqp.last()?))
}
_ => url_parts.push_fmt(format_args!(
"{}.{last}.html",
item_type.as_str(),
Expand Down
32 changes: 16 additions & 16 deletions src/tools/linkchecker/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ use html5ever::tokenizer::{
const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[
// These try to link to std::collections, but are defined in alloc
// https://github.com/rust-lang/rust/issues/74481
("std/collections/btree_map/struct.BTreeMap.html", &["#insert-and-complex-keys"]),
("std/collections/btree_set/struct.BTreeSet.html", &["#insert-and-complex-keys"]),
("alloc/collections/btree_map/struct.BTreeMap.html", &["#insert-and-complex-keys"]),
("alloc/collections/btree_set/struct.BTreeSet.html", &["#insert-and-complex-keys"]),
("std/collections/btree_map/type.BTreeMap.html", &["#insert-and-complex-keys"]),
("std/collections/btree_set/type.BTreeSet.html", &["#insert-and-complex-keys"]),
("alloc/collections/btree_map/type.BTreeMap.html", &["#insert-and-complex-keys"]),
("alloc/collections/btree_set/type.BTreeSet.html", &["#insert-and-complex-keys"]),

// These try to link to various things in std, but are defined in core.
// The docs in std::primitive use proper intra-doc links, so these seem fine to special-case.
Expand All @@ -50,24 +50,24 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[
("alloc/slice/trait.Join.html", &["#method.join"]),
("alloc/slice/trait.Concat.html", &["#method.concat"]),
("alloc/slice/index.html", &["#method.concat", "#method.join"]),
("alloc/vec/struct.Vec.html", &["#method.sort_by_key", "#method.sort_by_cached_key"]),
("alloc/bstr/struct.ByteStr.html", &[
("alloc/vec/type.Vec.html", &["#method.sort_by_key", "#method.sort_by_cached_key"]),
("alloc/bstr/type.ByteStr.html", &[
"#method.to_ascii_uppercase",
"#method.to_ascii_lowercase",
"core/slice::sort_by_key",
"core\\slice::sort_by_key",
"#method.sort_by_cached_key",
"#method.sort_by_key"
]),
("alloc/bstr/struct.ByteString.html", &[
("alloc/bstr/type.ByteString.html", &[
"#method.to_ascii_uppercase",
"#method.to_ascii_lowercase",
"core/slice::sort_by_key",
"core\\slice::sort_by_key",
"#method.sort_by_cached_key",
"#method.sort_by_key"
]),
("core/bstr/struct.ByteStr.html", &[
("core/bstr/type.ByteStr.html", &[
"#method.to_ascii_uppercase",
"#method.to_ascii_lowercase",
"core/bstr/slice::sort_by_key",
Expand All @@ -78,29 +78,29 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[
("core/primitive.slice.html", &["#method.to_ascii_uppercase", "#method.to_ascii_lowercase",
"core/slice::sort_by_key", "core\\slice::sort_by_key",
"#method.sort_by_cached_key"]),
("core/io/struct.IoSlice.html", &[
("core/io/type.IoSlice.html", &[
"#method.to_ascii_uppercase",
"#method.to_ascii_lowercase",
"core/io/slice::sort_by_key",
"core\\io\\slice::sort_by_key",
"#method.sort_by_cached_key"
]),
("core/io/struct.IoSliceMut.html", &[
("core/io/type.IoSliceMut.html", &[
"#method.to_ascii_uppercase",
"#method.to_ascii_lowercase",
"core/io/slice::sort_by_key",
"core\\io\\slice::sort_by_key",
"#method.sort_by_cached_key"
]),
("alloc/io/struct.IoSlice.html", &[
("alloc/io/type.IoSlice.html", &[
"#method.to_ascii_uppercase",
"#method.to_ascii_lowercase",
"alloc/io/slice::sort_by_key",
"alloc\\io\\slice::sort_by_key",
"#method.sort_by_key",
"#method.sort_by_cached_key"
]),
("alloc/io/struct.IoSliceMut.html", &[
("alloc/io/type.IoSliceMut.html", &[
"#method.to_ascii_uppercase",
"#method.to_ascii_lowercase",
"alloc/io/slice::sort_by_key",
Expand All @@ -113,10 +113,10 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[
#[rustfmt::skip]
const INTRA_DOC_LINK_EXCEPTIONS: &[(&str, &[&str])] = &[
// This is being used in the sense of 'inclusive range', not a markdown link
("core/ops/struct.RangeInclusive.html", &["begin</code>, <code>end"]),
("std/ops/struct.RangeInclusive.html", &["begin</code>, <code>end"]),
("core/range/legacy/struct.RangeInclusive.html", &["begin</code>, <code>end"]),
("std/range/legacy/struct.RangeInclusive.html", &["begin</code>, <code>end"]),
("core/ops/type.RangeInclusive.html", &["begin</code>, <code>end"]),
("std/ops/type.RangeInclusive.html", &["begin</code>, <code>end"]),
("core/range/legacy/type.RangeInclusive.html", &["begin</code>, <code>end"]),
("std/range/legacy/type.RangeInclusive.html", &["begin</code>, <code>end"]),
("core/slice/trait.SliceIndex.html", &["begin</code>, <code>end"]),
("alloc/slice/trait.SliceIndex.html", &["begin</code>, <code>end"]),
("std/slice/trait.SliceIndex.html", &["begin</code>, <code>end"]),
Expand Down
6 changes: 4 additions & 2 deletions tests/run-make/rustdoc/map-file/expected.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"foo/macro.foo!.html": "foo/macro.foo.html",
"foo/private/struct.Quz.html": "foo/struct.Quz.html",
"foo/hidden/struct.Bar.html": "foo/struct.Bar.html"
"foo/private/type.Quz.html": "foo/type.Quz.html",
"foo/struct.Quz.html": "foo/type.Quz.html",
"foo/hidden/type.Bar.html": "foo/type.Bar.html",

@notriddle notriddle Oct 11, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: "foo/hidden/struct.Bar.html": "foo/type.Bar.html" is missing

View changes since the review

"foo/struct.Bar.html": "foo/type.Bar.html"
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
//@ has index.html '//ul[@class="all-items"]//a[@href="tango/index.html"]' 'tango'
//@ has help.html
//@ has settings.html
//@ has quebec/struct.Quebec.html
//@ has sierra/struct.Sierra.html
//@ has quebec/type.Quebec.html
//@ has sierra/type.Sierra.html
//@ has tango/trait.Tango.html
//@ hasraw sierra/struct.Sierra.html 'Tango'
//@ hasraw trait.impl/tango/trait.Tango.js 'struct.Sierra.html'
//@ hasraw sierra/type.Sierra.html 'Tango'
//@ hasraw trait.impl/tango/trait.Tango.js 'type.Sierra.html'
//@ hasraw search.index/name/*.js 'Tango'
//@ hasraw search.index/name/*.js 'Sierra'
//@ hasraw search.index/name/*.js 'Quebec'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
//@ has index.html '//ul[@class="all-items"]//a[@href="tango/index.html"]' 'tango'
//@ has help.html
//@ has settings.html
//@ !has quebec/struct.Quebec.html
//@ !has quebec/type.Quebec.html
//@ !has romeo/type.Romeo.html
//@ !has sierra/struct.Sierra.html
//@ !has sierra/type.Sierra.html
//@ !has tango/trait.Tango.html
//@ hasraw trait.impl/tango/trait.Tango.js 'struct.Sierra.html'
//@ hasraw trait.impl/tango/trait.Tango.js 'type.Sierra.html'
//@ hasraw search.index/name/*.js 'Quebec'
//@ hasraw search.index/name/*.js 'Romeo'
//@ hasraw search.index/name/*.js 'Sierra'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//@ !has index.html
//@ !has help.html
//@ !has settings.html
//@ has sierra/struct.Sierra.html
//@ hasraw sierra/struct.Sierra.html 'Tango'
//@ has sierra/type.Sierra.html
//@ hasraw sierra/type.Sierra.html 'Tango'
//@ !has trait.impl/tango/trait.Tango.js
//@ !has search.index/name/*.js

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//@ !has index.html
//@ !has help.html
//@ !has settings.html
//@ has sierra/struct.Sierra.html
//@ has sierra/type.Sierra.html
//@ has tango/trait.Tango.html
//@ hasraw sierra/struct.Sierra.html 'Tango'
//@ hasraw sierra/type.Sierra.html 'Tango'
//@ !has trait.impl/tango/trait.Tango.js
//@ !has search.index/name/*.js

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//@ has quebec/struct.Quebec.html
//@ has sierra/struct.Sierra.html
//@ has quebec/type.Quebec.html
//@ has sierra/type.Sierra.html
//@ has tango/trait.Tango.html
//@ hasraw sierra/struct.Sierra.html 'Tango'
//@ hasraw trait.impl/tango/trait.Tango.js 'struct.Sierra.html'
//@ hasraw sierra/type.Sierra.html 'Tango'
//@ hasraw trait.impl/tango/trait.Tango.js 'type.Sierra.html'
//@ hasraw search.index/name/*.js 'Tango'
//@ hasraw search.index/name/*.js 'Sierra'
//@ !hasraw search.index/name/*.js 'Quebec'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
//@ has index.html '//ul[@class="all-items"]//a[@href="tango/index.html"]' 'tango'
//@ has help.html
//@ has settings.html
//@ has sierra/struct.Sierra.html
//@ hasraw trait.impl/tango/trait.Tango.js 'struct.Sierra.html'
//@ has sierra/type.Sierra.html
//@ hasraw trait.impl/tango/trait.Tango.js 'type.Sierra.html'
//@ hasraw search.index/name/*.js 'Tango'
//@ hasraw search.index/name/*.js 'Sierra'
//@ hasraw search.index/name/*.js 'Quebec'
Expand Down
Loading
Loading