Auto merge of #83833 - jyn514:no-resolver, r=GuillaumeGomez

rustdoc: Store intra-doc links in Cache instead of on items directly

Items are first built after rustdoc creates the TyCtxt. To allow
resolving the links before the TyCtxt is built, the links can't be
stored on `clean::Item` directly.

Helps with https://github.com/rust-lang/rust/issues/83761. Opening this early because I think it might decrease memory usage.
This commit is contained in:
bors 2021-04-07 04:45:12 +00:00
commit 1c158b6a8b
4 changed files with 82 additions and 82 deletions

View File

@ -195,7 +195,69 @@ impl Item {
} }
crate fn links(&self, cache: &Cache) -> Vec<RenderedLink> { crate fn links(&self, cache: &Cache) -> Vec<RenderedLink> {
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 { crate fn is_crate(&self) -> bool {
@ -572,15 +634,13 @@ crate struct Attributes {
crate other_attrs: Vec<ast::Attribute>, crate other_attrs: Vec<ast::Attribute>,
crate cfg: Option<Arc<Cfg>>, crate cfg: Option<Arc<Cfg>>,
crate span: Option<rustc_span::Span>, crate span: Option<rustc_span::Span>,
/// map from Rust paths to resolved defs and potential URL fragments
crate links: Vec<ItemLink>,
crate inner_docs: bool, crate inner_docs: bool,
} }
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
/// A link that has not yet been rendered. /// 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 { crate struct ItemLink {
/// The original link written in the markdown /// The original link written in the markdown
pub(crate) link: String, pub(crate) link: String,
@ -806,7 +866,6 @@ impl Attributes {
other_attrs, other_attrs,
cfg: if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) }, cfg: if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) },
span: sp, span: sp,
links: vec![],
inner_docs, inner_docs,
} }
} }
@ -850,72 +909,6 @@ impl Attributes {
if self.doc_strings.is_empty() { None } else { Some(self.doc_strings.iter().collect()) } 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<RenderedLink> {
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]> { crate fn get_doc_aliases(&self) -> Box<[String]> {
let mut aliases = FxHashSet::default(); let mut aliases = FxHashSet::default();
@ -942,7 +935,6 @@ impl PartialEq for Attributes {
self.doc_strings == rhs.doc_strings self.doc_strings == rhs.doc_strings
&& self.cfg == rhs.cfg && self.cfg == rhs.cfg
&& self.span == rhs.span && self.span == rhs.span
&& self.links == rhs.links
&& self && self
.other_attrs .other_attrs
.iter() .iter()
@ -958,7 +950,6 @@ impl Hash for Attributes {
self.doc_strings.hash(hasher); self.doc_strings.hash(hasher);
self.cfg.hash(hasher); self.cfg.hash(hasher);
self.span.hash(hasher); self.span.hash(hasher);
self.links.hash(hasher);
for attr in &self.other_attrs { for attr in &self.other_attrs {
attr.id.hash(hasher); attr.id.hash(hasher);
} }

View File

@ -120,6 +120,11 @@ crate struct Cache {
// when gathering trait documentation on a type, hold impls here while // 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. // folding and add them to the cache later on if we find the trait.
orphan_trait_impls: Vec<(DefId, FxHashSet<DefId>, Impl)>, orphan_trait_impls: Vec<(DefId, FxHashSet<DefId>, Impl)>,
/// All intra-doc links resolved so far.
///
/// Links are indexed by the DefId of the item they document.
crate intra_doc_links: BTreeMap<DefId, Vec<clean::ItemLink>>,
} }
/// This struct is used to wrap the `cache` and `tcx` in order to run `DocFolder`. /// This struct is used to wrap the `cache` and `tcx` in order to run `DocFolder`.

View File

@ -24,6 +24,16 @@ use std::collections::HashSet;
impl JsonRenderer<'_> { impl JsonRenderer<'_> {
pub(super) fn convert_item(&self, item: clean::Item) -> Option<Item> { pub(super) fn convert_item(&self, item: clean::Item) -> Option<Item> {
let deprecation = item.deprecation(self.tcx); 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 clean::Item { span, name, attrs, kind, visibility, def_id } = item;
let inner = match *kind { let inner = match *kind {
clean::StrippedItem(_) => return None, clean::StrippedItem(_) => return None,
@ -36,13 +46,6 @@ impl JsonRenderer<'_> {
span: self.convert_span(span), span: self.convert_span(span),
visibility: self.convert_visibility(visibility), visibility: self.convert_visibility(visibility),
docs: attrs.collapsed_doc_value(), 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 attrs: attrs
.other_attrs .other_attrs
.iter() .iter()
@ -50,6 +53,7 @@ impl JsonRenderer<'_> {
.collect(), .collect(),
deprecation: deprecation.map(from_deprecation), deprecation: deprecation.map(from_deprecation),
inner, inner,
links,
}) })
} }

View File

@ -788,7 +788,7 @@ fn is_derive_trait_collision<T>(ns: &PerNS<Result<(Res, T), ResolutionFailure<'_
} }
impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
fn fold_item(&mut self, mut item: Item) -> Option<Item> { fn fold_item(&mut self, item: Item) -> Option<Item> {
use rustc_middle::ty::DefIdTree; use rustc_middle::ty::DefIdTree;
let parent_node = if item.is_fake() { 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) { for md_link in markdown_links(&doc) {
let link = self.resolve_link(&item, &doc, &self_name, parent_node, krate, md_link); let link = self.resolve_link(&item, &doc, &self_name, parent_node, krate, md_link);
if let Some(link) = 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);
} }
} }
} }