diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 0b9620f98fb41..a5629fe1d1186 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -195,7 +195,69 @@ impl Item { } crate fn links(&self, cache: &Cache) -> Vec { - self.attrs.links(self.def_id.krate, cache) + use crate::html::format::href; + use crate::html::render::CURRENT_DEPTH; + + cache + .intra_doc_links + .get(&self.def_id) + .map_or(&[][..], |v| v.as_slice()) + .iter() + .filter_map(|ItemLink { link: s, link_text, did, fragment }| { + match *did { + Some(did) => { + if let Some((mut href, ..)) = href(did, cache) { + if let Some(ref fragment) = *fragment { + href.push('#'); + href.push_str(fragment); + } + Some(RenderedLink { + original_text: s.clone(), + new_text: link_text.clone(), + href, + }) + } else { + None + } + } + None => { + if let Some(ref fragment) = *fragment { + let url = match cache.extern_locations.get(&self.def_id.krate) { + Some(&(_, _, ExternalLocation::Local)) => { + let depth = CURRENT_DEPTH.with(|l| l.get()); + "../".repeat(depth) + } + Some(&(_, _, ExternalLocation::Remote(ref s))) => s.to_string(), + Some(&(_, _, ExternalLocation::Unknown)) | None => String::from( + // NOTE: intentionally doesn't pass crate name to avoid having + // different primitive links between crates + if UnstableFeatures::from_environment(None).is_nightly_build() { + "https://doc.rust-lang.org/nightly" + } else { + "https://doc.rust-lang.org" + }, + ), + }; + // This is a primitive so the url is done "by hand". + let tail = fragment.find('#').unwrap_or_else(|| fragment.len()); + Some(RenderedLink { + original_text: s.clone(), + new_text: link_text.clone(), + href: format!( + "{}{}std/primitive.{}.html{}", + url, + if !url.ends_with('/') { "/" } else { "" }, + &fragment[..tail], + &fragment[tail..] + ), + }) + } else { + panic!("This isn't a primitive?!"); + } + } + } + }) + .collect() } crate fn is_crate(&self) -> bool { @@ -572,15 +634,13 @@ crate struct Attributes { crate other_attrs: Vec, crate cfg: Option>, crate span: Option, - /// map from Rust paths to resolved defs and potential URL fragments - crate links: Vec, crate inner_docs: bool, } #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] /// A link that has not yet been rendered. /// -/// This link will be turned into a rendered link by [`Attributes::links`] +/// This link will be turned into a rendered link by [`Item::links`]. crate struct ItemLink { /// The original link written in the markdown pub(crate) link: String, @@ -806,7 +866,6 @@ impl Attributes { other_attrs, cfg: if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) }, span: sp, - links: vec![], inner_docs, } } @@ -850,72 +909,6 @@ impl Attributes { if self.doc_strings.is_empty() { None } else { Some(self.doc_strings.iter().collect()) } } - /// Gets links as a vector - /// - /// Cache must be populated before call - crate fn links(&self, krate: CrateNum, cache: &Cache) -> Vec { - use crate::html::format::href; - use crate::html::render::CURRENT_DEPTH; - - self.links - .iter() - .filter_map(|ItemLink { link: s, link_text, did, fragment }| { - match *did { - Some(did) => { - if let Some((mut href, ..)) = href(did, cache) { - if let Some(ref fragment) = *fragment { - href.push('#'); - href.push_str(fragment); - } - Some(RenderedLink { - original_text: s.clone(), - new_text: link_text.clone(), - href, - }) - } else { - None - } - } - None => { - if let Some(ref fragment) = *fragment { - let url = match cache.extern_locations.get(&krate) { - Some(&(_, _, ExternalLocation::Local)) => { - let depth = CURRENT_DEPTH.with(|l| l.get()); - "../".repeat(depth) - } - Some(&(_, _, ExternalLocation::Remote(ref s))) => s.to_string(), - Some(&(_, _, ExternalLocation::Unknown)) | None => String::from( - // NOTE: intentionally doesn't pass crate name to avoid having - // different primitive links between crates - if UnstableFeatures::from_environment(None).is_nightly_build() { - "https://doc.rust-lang.org/nightly" - } else { - "https://doc.rust-lang.org" - }, - ), - }; - // This is a primitive so the url is done "by hand". - let tail = fragment.find('#').unwrap_or_else(|| fragment.len()); - Some(RenderedLink { - original_text: s.clone(), - new_text: link_text.clone(), - href: format!( - "{}{}std/primitive.{}.html{}", - url, - if !url.ends_with('/') { "/" } else { "" }, - &fragment[..tail], - &fragment[tail..] - ), - }) - } else { - panic!("This isn't a primitive?!"); - } - } - } - }) - .collect() - } - crate fn get_doc_aliases(&self) -> Box<[String]> { let mut aliases = FxHashSet::default(); @@ -942,7 +935,6 @@ impl PartialEq for Attributes { self.doc_strings == rhs.doc_strings && self.cfg == rhs.cfg && self.span == rhs.span - && self.links == rhs.links && self .other_attrs .iter() @@ -958,7 +950,6 @@ impl Hash for Attributes { self.doc_strings.hash(hasher); self.cfg.hash(hasher); self.span.hash(hasher); - self.links.hash(hasher); for attr in &self.other_attrs { attr.id.hash(hasher); } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 0a59bae941edf..b2b895cc6726e 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -120,6 +120,11 @@ crate struct Cache { // when gathering trait documentation on a type, hold impls here while // folding and add them to the cache later on if we find the trait. orphan_trait_impls: Vec<(DefId, FxHashSet, Impl)>, + + /// All intra-doc links resolved so far. + /// + /// Links are indexed by the DefId of the item they document. + crate intra_doc_links: BTreeMap>, } /// This struct is used to wrap the `cache` and `tcx` in order to run `DocFolder`. diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index c470dc5700513..10d5b9807b010 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -24,6 +24,16 @@ use std::collections::HashSet; impl JsonRenderer<'_> { pub(super) fn convert_item(&self, item: clean::Item) -> Option { let deprecation = item.deprecation(self.tcx); + let links = self + .cache + .intra_doc_links + .get(&item.def_id) + .into_iter() + .flatten() + .filter_map(|clean::ItemLink { link, did, .. }| { + did.map(|did| (link.clone(), from_def_id(did))) + }) + .collect(); let clean::Item { span, name, attrs, kind, visibility, def_id } = item; let inner = match *kind { clean::StrippedItem(_) => return None, @@ -36,13 +46,6 @@ impl JsonRenderer<'_> { span: self.convert_span(span), visibility: self.convert_visibility(visibility), docs: attrs.collapsed_doc_value(), - links: attrs - .links - .into_iter() - .filter_map(|clean::ItemLink { link, did, .. }| { - did.map(|did| (link, from_def_id(did))) - }) - .collect(), attrs: attrs .other_attrs .iter() @@ -50,6 +53,7 @@ impl JsonRenderer<'_> { .collect(), deprecation: deprecation.map(from_deprecation), inner, + links, }) } diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 401e2ed2884a0..31e3c11c1b087 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -788,7 +788,7 @@ fn is_derive_trait_collision(ns: &PerNS DocFolder for LinkCollector<'a, 'tcx> { - fn fold_item(&mut self, mut item: Item) -> Option { + fn fold_item(&mut self, item: Item) -> Option { use rustc_middle::ty::DefIdTree; let parent_node = if item.is_fake() { @@ -873,7 +873,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { for md_link in markdown_links(&doc) { let link = self.resolve_link(&item, &doc, &self_name, parent_node, krate, md_link); if let Some(link) = link { - item.attrs.links.push(link); + self.cx.cache.intra_doc_links.entry(item.def_id).or_default().push(link); } } }