From 4762694e4a4d04295c578a344d217e34067c608c Mon Sep 17 00:00:00 2001 From: binarycat Date: Sat, 12 Jul 2025 15:05:30 -0500 Subject: rustdoc: never try to link to unnamable types --- src/librustdoc/html/format.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/librustdoc/html') diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index be8a2d511e9..f4cf819fc59 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -368,6 +368,8 @@ pub(crate) enum HrefError { Private, // Not in external cache, href link should be in same page NotInExternalCache, + /// Refers to an unnamable item, such as one defined within a function or const block. + UnnamableItem, } /// This function is to get the external macro path because they are not in the cache used in @@ -498,7 +500,13 @@ fn url_parts( builder.extend(module_fqp.iter().copied()); Ok(builder) } - ExternalLocation::Local => Ok(href_relative_parts(module_fqp, relative_to)), + ExternalLocation::Local => { + if module_fqp.iter().any(|sym| sym.as_str() == "_") { + Err(HrefError::UnnamableItem) + } else { + Ok(href_relative_parts(module_fqp, relative_to)) + } + } ExternalLocation::Unknown => Err(HrefError::DocumentationNotBuilt), } } -- cgit 1.4.1-3-g733a5 From f57283d6141fcc430e739fc2ad633e8997a66310 Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 15 Jul 2025 16:06:03 -0500 Subject: rustdoc: actually never link to unnamable types --- src/librustdoc/html/format.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'src/librustdoc/html') diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index f4cf819fc59..90d385aa135 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -481,6 +481,20 @@ fn generate_item_def_id_path( Ok((url_parts, shortty, fqp)) } +/// Checks if the given defid refers to an item that is unnamable, such as one defined in a const block. +fn is_unnamable(tcx: TyCtxt<'_>, did: DefId) -> bool { + let mut cur_did = did; + while let Some(parent) = tcx.opt_parent(cur_did) { + match tcx.def_kind(parent) { + // items defined in these can be linked to + DefKind::Mod | DefKind::Impl { .. } | DefKind::ForeignMod => cur_did = parent, + // everything else does not have docs generated for it + _ => return true, + } + } + return false; +} + fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] { if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] } } @@ -500,13 +514,7 @@ fn url_parts( builder.extend(module_fqp.iter().copied()); Ok(builder) } - ExternalLocation::Local => { - if module_fqp.iter().any(|sym| sym.as_str() == "_") { - Err(HrefError::UnnamableItem) - } else { - Ok(href_relative_parts(module_fqp, relative_to)) - } - } + ExternalLocation::Local => Ok(href_relative_parts(module_fqp, relative_to)), ExternalLocation::Unknown => Err(HrefError::DocumentationNotBuilt), } } @@ -560,6 +568,9 @@ pub(crate) fn href_with_root_path( } _ => original_did, }; + if is_unnamable(cx.tcx(), did) { + return Err(HrefError::UnnamableItem); + } let cache = cx.cache(); let relative_to = &cx.current; -- cgit 1.4.1-3-g733a5 From 05a62c8a1172795709219aa5afc833eb1ab57c25 Mon Sep 17 00:00:00 2001 From: binarycat Date: Sat, 19 Jul 2025 14:26:47 -0500 Subject: impl items are never unnamable --- src/librustdoc/html/format.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/librustdoc/html') diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 90d385aa135..457af8e994d 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -486,8 +486,14 @@ fn is_unnamable(tcx: TyCtxt<'_>, did: DefId) -> bool { let mut cur_did = did; while let Some(parent) = tcx.opt_parent(cur_did) { match tcx.def_kind(parent) { - // items defined in these can be linked to - DefKind::Mod | DefKind::Impl { .. } | DefKind::ForeignMod => cur_did = parent, + // items defined in these can be linked to, as long as they are visible + DefKind::Mod | DefKind::ForeignMod => cur_did = parent, + // items in impls can be linked to, + // as long as we can link to the item the impl is on. + // since associated traits are not a thing, + // it should not be possible to refer to an impl item if + // the base type is not namable. + DefKind::Impl { .. } => return false, // everything else does not have docs generated for it _ => return true, } -- cgit 1.4.1-3-g733a5 From f516d4c9e341847d8f452adef68fe99cfdcfccc0 Mon Sep 17 00:00:00 2001 From: Mingwei Samuel Date: Fri, 25 Jul 2025 13:21:50 -0700 Subject: rustdoc font links only emit `crossorigin` when needed The `crossorigin` attribute may cause issues when the href is not actuall across origins. Specifically, the tag causes the browser to send a preflight OPTIONS request to the href even if it is same-origin. Some tempermental servers may reject all CORS preflect requests even if they're actually same-origin, which causes a CORS error and prevents the fonts from loading, even later on. This commit fixes that problem by not emitting `crossorigin` if the url looks like a domain-relative url. Co-authored-by: Guillaume Gomez --- src/librustdoc/html/layout.rs | 29 +++++++++++++++++++++++++++++ src/librustdoc/html/layout/tests.rs | 24 ++++++++++++++++++++++++ src/librustdoc/html/templates/page.html | 2 +- 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/librustdoc/html/layout/tests.rs (limited to 'src/librustdoc/html') diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index 1f92c521d46..2782e8e0058 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -8,6 +8,9 @@ use super::static_files::{STATIC_FILES, StaticFiles}; use crate::externalfiles::ExternalHtml; use crate::html::render::{StylePath, ensure_trailing_slash}; +#[cfg(test)] +mod tests; + pub(crate) struct Layout { pub(crate) logo: String, pub(crate) favicon: String, @@ -68,6 +71,13 @@ struct PageLayout<'a> { display_krate_version_extra: &'a str, } +impl PageLayout<'_> { + /// See [`may_remove_crossorigin`]. + fn static_root_path_may_remove_crossorigin(&self) -> bool { + may_remove_crossorigin(&self.static_root_path) + } +} + pub(crate) use crate::html::render::sidebar::filters; pub(crate) fn render( @@ -134,3 +144,22 @@ pub(crate) fn redirect(url: &str) -> String { "##, ) } + +/// Conservatively determines if `href` is relative to the current origin, +/// so that `crossorigin` may be safely removed from `` elements. +pub(crate) fn may_remove_crossorigin(href: &str) -> bool { + // Reject scheme-relative URLs (`//example.com/`). + if href.starts_with("//") { + return false; + } + // URL is interpreted as having a scheme iff: it starts with an ascii alpha, and only + // contains ascii alphanumeric or `+` `-` `.` up to the `:`. + // https://url.spec.whatwg.org/#url-parsing + let has_scheme = href.split_once(':').is_some_and(|(scheme, _rest)| { + let mut chars = scheme.chars(); + chars.next().is_some_and(|c| c.is_ascii_alphabetic()) + && chars.all(|c| c.is_ascii_alphanumeric() || c == '+' || c == '-' || c == '.') + }); + // Reject anything with a scheme (`http:`, etc.). + !has_scheme +} diff --git a/src/librustdoc/html/layout/tests.rs b/src/librustdoc/html/layout/tests.rs new file mode 100644 index 00000000000..d4a19ee9abf --- /dev/null +++ b/src/librustdoc/html/layout/tests.rs @@ -0,0 +1,24 @@ +#[test] +fn test_may_remove_crossorigin() { + use super::may_remove_crossorigin; + + assert!(may_remove_crossorigin("font.woff2")); + assert!(may_remove_crossorigin("/font.woff2")); + assert!(may_remove_crossorigin("./font.woff2")); + assert!(may_remove_crossorigin(":D/font.woff2")); + assert!(may_remove_crossorigin("../font.woff2")); + + assert!(!may_remove_crossorigin("//example.com/static.files")); + assert!(!may_remove_crossorigin("http://example.com/static.files")); + assert!(!may_remove_crossorigin("https://example.com/static.files")); + assert!(!may_remove_crossorigin("https://example.com:8080/static.files")); + + assert!(!may_remove_crossorigin("ftp://example.com/static.files")); + assert!(!may_remove_crossorigin("blob:http://example.com/static.files")); + assert!(!may_remove_crossorigin("javascript:alert('Hello, world!')")); + assert!(!may_remove_crossorigin("//./C:")); + assert!(!may_remove_crossorigin("file:////C:")); + assert!(!may_remove_crossorigin("file:///./C:")); + assert!(!may_remove_crossorigin("data:,Hello%2C%20World%21")); + assert!(!may_remove_crossorigin("hi...:hello")); +} diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index 7af99e7097c..398436e3fe1 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -7,7 +7,7 @@ {# #} {{page.title}} {# #} {# #} {# #} -- cgit 1.4.1-3-g733a5