Skip to content

Commit 9e11902

Browse files
committed
Store 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.
1 parent 16143d1 commit 9e11902

File tree

4 files changed

+82
-82
lines changed

4 files changed

+82
-82
lines changed

src/librustdoc/clean/types.rs

+64-73
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,69 @@ impl Item {
193193
}
194194

195195
crate fn links(&self, cache: &Cache) -> Vec<RenderedLink> {
196-
self.attrs.links(self.def_id.krate, cache)
196+
use crate::html::format::href;
197+
use crate::html::render::CURRENT_DEPTH;
198+
199+
cache
200+
.intra_doc_links
201+
.get(&self.def_id)
202+
.map_or(&[][..], |v| v.as_slice())
203+
.iter()
204+
.filter_map(|ItemLink { link: s, link_text, did, fragment }| {
205+
match *did {
206+
Some(did) => {
207+
if let Some((mut href, ..)) = href(did, cache) {
208+
if let Some(ref fragment) = *fragment {
209+
href.push('#');
210+
href.push_str(fragment);
211+
}
212+
Some(RenderedLink {
213+
original_text: s.clone(),
214+
new_text: link_text.clone(),
215+
href,
216+
})
217+
} else {
218+
None
219+
}
220+
}
221+
None => {
222+
if let Some(ref fragment) = *fragment {
223+
let url = match cache.extern_locations.get(&self.def_id.krate) {
224+
Some(&(_, _, ExternalLocation::Local)) => {
225+
let depth = CURRENT_DEPTH.with(|l| l.get());
226+
"../".repeat(depth)
227+
}
228+
Some(&(_, _, ExternalLocation::Remote(ref s))) => s.to_string(),
229+
Some(&(_, _, ExternalLocation::Unknown)) | None => String::from(
230+
// NOTE: intentionally doesn't pass crate name to avoid having
231+
// different primitive links between crates
232+
if UnstableFeatures::from_environment(None).is_nightly_build() {
233+
"https://doc.rust-lang.org/nightly"
234+
} else {
235+
"https://doc.rust-lang.org"
236+
},
237+
),
238+
};
239+
// This is a primitive so the url is done "by hand".
240+
let tail = fragment.find('#').unwrap_or_else(|| fragment.len());
241+
Some(RenderedLink {
242+
original_text: s.clone(),
243+
new_text: link_text.clone(),
244+
href: format!(
245+
"{}{}std/primitive.{}.html{}",
246+
url,
247+
if !url.ends_with('/') { "/" } else { "" },
248+
&fragment[..tail],
249+
&fragment[tail..]
250+
),
251+
})
252+
} else {
253+
panic!("This isn't a primitive?!");
254+
}
255+
}
256+
}
257+
})
258+
.collect()
197259
}
198260

199261
crate fn is_crate(&self) -> bool {
@@ -570,15 +632,13 @@ crate struct Attributes {
570632
crate other_attrs: Vec<ast::Attribute>,
571633
crate cfg: Option<Arc<Cfg>>,
572634
crate span: Option<rustc_span::Span>,
573-
/// map from Rust paths to resolved defs and potential URL fragments
574-
crate links: Vec<ItemLink>,
575635
crate inner_docs: bool,
576636
}
577637

578638
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
579639
/// A link that has not yet been rendered.
580640
///
581-
/// This link will be turned into a rendered link by [`Attributes::links`]
641+
/// This link will be turned into a rendered link by [`Item::links`].
582642
crate struct ItemLink {
583643
/// The original link written in the markdown
584644
pub(crate) link: String,
@@ -804,7 +864,6 @@ impl Attributes {
804864
other_attrs,
805865
cfg: if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) },
806866
span: sp,
807-
links: vec![],
808867
inner_docs,
809868
}
810869
}
@@ -848,72 +907,6 @@ impl Attributes {
848907
if self.doc_strings.is_empty() { None } else { Some(self.doc_strings.iter().collect()) }
849908
}
850909

851-
/// Gets links as a vector
852-
///
853-
/// Cache must be populated before call
854-
crate fn links(&self, krate: CrateNum, cache: &Cache) -> Vec<RenderedLink> {
855-
use crate::html::format::href;
856-
use crate::html::render::CURRENT_DEPTH;
857-
858-
self.links
859-
.iter()
860-
.filter_map(|ItemLink { link: s, link_text, did, fragment }| {
861-
match *did {
862-
Some(did) => {
863-
if let Some((mut href, ..)) = href(did, cache) {
864-
if let Some(ref fragment) = *fragment {
865-
href.push('#');
866-
href.push_str(fragment);
867-
}
868-
Some(RenderedLink {
869-
original_text: s.clone(),
870-
new_text: link_text.clone(),
871-
href,
872-
})
873-
} else {
874-
None
875-
}
876-
}
877-
None => {
878-
if let Some(ref fragment) = *fragment {
879-
let url = match cache.extern_locations.get(&krate) {
880-
Some(&(_, _, ExternalLocation::Local)) => {
881-
let depth = CURRENT_DEPTH.with(|l| l.get());
882-
"../".repeat(depth)
883-
}
884-
Some(&(_, _, ExternalLocation::Remote(ref s))) => s.to_string(),
885-
Some(&(_, _, ExternalLocation::Unknown)) | None => String::from(
886-
// NOTE: intentionally doesn't pass crate name to avoid having
887-
// different primitive links between crates
888-
if UnstableFeatures::from_environment(None).is_nightly_build() {
889-
"https://doc.rust-lang.org/nightly"
890-
} else {
891-
"https://doc.rust-lang.org"
892-
},
893-
),
894-
};
895-
// This is a primitive so the url is done "by hand".
896-
let tail = fragment.find('#').unwrap_or_else(|| fragment.len());
897-
Some(RenderedLink {
898-
original_text: s.clone(),
899-
new_text: link_text.clone(),
900-
href: format!(
901-
"{}{}std/primitive.{}.html{}",
902-
url,
903-
if !url.ends_with('/') { "/" } else { "" },
904-
&fragment[..tail],
905-
&fragment[tail..]
906-
),
907-
})
908-
} else {
909-
panic!("This isn't a primitive?!");
910-
}
911-
}
912-
}
913-
})
914-
.collect()
915-
}
916-
917910
crate fn get_doc_aliases(&self) -> Box<[String]> {
918911
let mut aliases = FxHashSet::default();
919912

@@ -940,7 +933,6 @@ impl PartialEq for Attributes {
940933
self.doc_strings == rhs.doc_strings
941934
&& self.cfg == rhs.cfg
942935
&& self.span == rhs.span
943-
&& self.links == rhs.links
944936
&& self
945937
.other_attrs
946938
.iter()
@@ -956,7 +948,6 @@ impl Hash for Attributes {
956948
self.doc_strings.hash(hasher);
957949
self.cfg.hash(hasher);
958950
self.span.hash(hasher);
959-
self.links.hash(hasher);
960951
for attr in &self.other_attrs {
961952
attr.id.hash(hasher);
962953
}

src/librustdoc/formats/cache.rs

+5
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ crate struct Cache {
120120
// when gathering trait documentation on a type, hold impls here while
121121
// folding and add them to the cache later on if we find the trait.
122122
orphan_trait_impls: Vec<(DefId, FxHashSet<DefId>, Impl)>,
123+
124+
/// All intra-doc links resolved so far.
125+
///
126+
/// Links are indexed by the DefId of the item they document.
127+
crate intra_doc_links: BTreeMap<DefId, Vec<clean::ItemLink>>,
123128
}
124129

125130
/// This struct is used to wrap the `cache` and `tcx` in order to run `DocFolder`.

src/librustdoc/json/conversions.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ use std::collections::HashSet;
2424
impl JsonRenderer<'_> {
2525
pub(super) fn convert_item(&self, item: clean::Item) -> Option<Item> {
2626
let deprecation = item.deprecation(self.tcx);
27+
let links = self
28+
.cache
29+
.intra_doc_links
30+
.get(&item.def_id)
31+
.into_iter()
32+
.flatten()
33+
.filter_map(|clean::ItemLink { link, did, .. }| {
34+
did.map(|did| (link.clone(), from_def_id(did)))
35+
})
36+
.collect();
2737
let clean::Item { span, name, attrs, kind, visibility, def_id } = item;
2838
let inner = match *kind {
2939
clean::StrippedItem(_) => return None,
@@ -36,20 +46,14 @@ impl JsonRenderer<'_> {
3646
span: self.convert_span(span),
3747
visibility: self.convert_visibility(visibility),
3848
docs: attrs.collapsed_doc_value(),
39-
links: attrs
40-
.links
41-
.into_iter()
42-
.filter_map(|clean::ItemLink { link, did, .. }| {
43-
did.map(|did| (link, from_def_id(did)))
44-
})
45-
.collect(),
4649
attrs: attrs
4750
.other_attrs
4851
.iter()
4952
.map(rustc_ast_pretty::pprust::attribute_to_string)
5053
.collect(),
5154
deprecation: deprecation.map(from_deprecation),
5255
inner,
56+
links,
5357
})
5458
}
5559

src/librustdoc/passes/collect_intra_doc_links.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,7 @@ fn is_derive_trait_collision<T>(ns: &PerNS<Result<(Res, T), ResolutionFailure<'_
788788
}
789789

790790
impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
791-
fn fold_item(&mut self, mut item: Item) -> Option<Item> {
791+
fn fold_item(&mut self, item: Item) -> Option<Item> {
792792
use rustc_middle::ty::DefIdTree;
793793

794794
let parent_node = if item.is_fake() {
@@ -873,7 +873,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
873873
for md_link in markdown_links(&doc) {
874874
let link = self.resolve_link(&item, &doc, &self_name, parent_node, krate, md_link);
875875
if let Some(link) = link {
876-
item.attrs.links.push(link);
876+
self.cx.cache.intra_doc_links.entry(item.def_id).or_default().push(link);
877877
}
878878
}
879879
}

0 commit comments

Comments
 (0)