diff options
Diffstat (limited to 'src/librustdoc')
| -rw-r--r-- | src/librustdoc/clean/types.rs | 20 | ||||
| -rw-r--r-- | src/librustdoc/config.rs | 3 | ||||
| -rw-r--r-- | src/librustdoc/html/layout.rs | 29 | ||||
| -rw-r--r-- | src/librustdoc/html/layout/tests.rs | 24 | ||||
| -rw-r--r-- | src/librustdoc/html/static/css/rustdoc.css | 4 | ||||
| -rw-r--r-- | src/librustdoc/html/templates/page.html | 2 | ||||
| -rw-r--r-- | src/librustdoc/passes/collect_intra_doc_links.rs | 7 | ||||
| -rw-r--r-- | src/librustdoc/scrape_examples.rs | 7 |
8 files changed, 89 insertions, 7 deletions
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 782311e593b..26b087feb16 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -480,10 +480,28 @@ impl Item { } } + /// If the item has doc comments from a reexport, returns the item id of that reexport, + /// otherwise returns returns the item id. + /// + /// This is used as a key for caching intra-doc link resolution, + /// to prevent two reexports of the same item from using the same cache. + pub(crate) fn item_or_reexport_id(&self) -> ItemId { + // added documentation on a reexport is always prepended. + self.attrs + .doc_strings + .first() + .map(|x| x.item_id) + .flatten() + .map(ItemId::from) + .unwrap_or(self.item_id) + } + pub(crate) fn links(&self, cx: &Context<'_>) -> Vec<RenderedLink> { use crate::html::format::{href, link_tooltip}; - let Some(links) = cx.cache().intra_doc_links.get(&self.item_id) else { return vec![] }; + let Some(links) = cx.cache().intra_doc_links.get(&self.item_or_reexport_id()) else { + return vec![]; + }; links .iter() .filter_map(|ItemLink { link: s, link_text, page_id: id, fragment }| { diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index fed4296fa22..c52c7236883 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -804,8 +804,7 @@ impl Options { let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx); let with_examples = matches.opt_strs("with-examples"); - let call_locations = - crate::scrape_examples::load_call_locations(with_examples, dcx, &mut loaded_paths); + let call_locations = crate::scrape_examples::load_call_locations(with_examples, dcx); let doctest_build_args = matches.opt_strs("doctest-build-arg"); let unstable_features = 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<T: Display, S: Display>( @@ -134,3 +144,22 @@ pub(crate) fn redirect(url: &str) -> String { </html>"##, ) } + +/// Conservatively determines if `href` is relative to the current origin, +/// so that `crossorigin` may be safely removed from `<link>` 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/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 99b3da8b2cd..c48863b4681 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1838,6 +1838,10 @@ instead, we check that it's not a "finger" cursor. border-right: 3px solid var(--target-border-color); } +a.tooltip { + font-family: var(--font-family); +} + .code-header a.tooltip { color: inherit; margin-right: 15px; 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 @@ <meta name="description" content="{{page.description}}"> {# #} <title>{{page.title}}</title> {# #} <script>if(window.location.protocol!=="file:") {# Hack to skip preloading fonts locally - see #98769 #} - document.head.insertAdjacentHTML("beforeend","{{files.source_serif_4_regular}},{{files.fira_sans_italic}},{{files.fira_sans_regular}},{{files.fira_sans_medium_italic}},{{files.fira_sans_medium}},{{files.source_code_pro_regular}},{{files.source_code_pro_semibold}}".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}${f}">`).join("")) {# #} + document.head.insertAdjacentHTML("beforeend","{{files.source_serif_4_regular}},{{files.fira_sans_italic}},{{files.fira_sans_regular}},{{files.fira_sans_medium_italic}},{{files.fira_sans_medium}},{{files.source_code_pro_regular}},{{files.source_code_pro_semibold}}".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"{% if !static_root_path_may_remove_crossorigin() %} crossorigin{% endif %} href="{{static_root_path|safe}}${f}">`).join("")) {# #} </script> {# #} <link rel="stylesheet" {#+ #} href="{{static_root_path|safe}}{{files.normalize_css}}"> {# #} diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index c9fa3a4837f..bcb676cd1f1 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1082,7 +1082,12 @@ impl LinkCollector<'_, '_> { for md_link in preprocessed_markdown_links(&doc) { let link = self.resolve_link(&doc, item, item_id, module_id, &md_link); if let Some(link) = link { - self.cx.cache.intra_doc_links.entry(item.item_id).or_default().insert(link); + self.cx + .cache + .intra_doc_links + .entry(item.item_or_reexport_id()) + .or_default() + .insert(link); } } } diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index 4d29c74e1eb..9f71d6ae789 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -333,11 +333,14 @@ pub(crate) fn run( pub(crate) fn load_call_locations( with_examples: Vec<String>, dcx: DiagCtxtHandle<'_>, - loaded_paths: &mut Vec<PathBuf>, ) -> AllCallLocations { let mut all_calls: AllCallLocations = FxIndexMap::default(); for path in with_examples { - loaded_paths.push(path.clone().into()); + // FIXME: Figure out why this line is causing this feature to crash in specific contexts. + // Full issue backlog is available here: <https://github.com/rust-lang/rust/pull/144600>. + // + // Can be checked with `tests/run-make/rustdoc-scrape-examples-paths`. + // loaded_paths.push(path.clone().into()); let bytes = match fs::read(&path) { Ok(bytes) => bytes, Err(e) => dcx.fatal(format!("failed to load examples: {e}")), |
