Skip to content

Commit 3e1a3fd

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 138fd56 commit 3e1a3fd

File tree

5 files changed

+88
-82
lines changed

5 files changed

+88
-82
lines changed

src/librustdoc/clean/types.rs

Lines changed: 68 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use crate::core::DocContext;
4040
use crate::formats::cache::Cache;
4141
use crate::formats::item_type::ItemType;
4242
use crate::html::render::cache::ExternalLocation;
43+
use crate::passes::collect_intra_doc_links::EarlyIntraDocLink;
4344

4445
use self::FnRetTy::*;
4546
use self::ItemKind::*;
@@ -192,8 +193,74 @@ impl Item {
192193
self.attrs.collapsed_doc_value()
193194
}
194195

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

199266
crate fn is_crate(&self) -> bool {
@@ -570,8 +637,6 @@ crate struct Attributes {
570637
crate other_attrs: Vec<ast::Attribute>,
571638
crate cfg: Option<Arc<Cfg>>,
572639
crate span: Option<rustc_span::Span>,
573-
/// map from Rust paths to resolved defs and potential URL fragments
574-
crate links: Vec<ItemLink>,
575640
crate inner_docs: bool,
576641
}
577642

@@ -804,7 +869,6 @@ impl Attributes {
804869
other_attrs,
805870
cfg: if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) },
806871
span: sp,
807-
links: vec![],
808872
inner_docs,
809873
}
810874
}
@@ -848,72 +912,6 @@ impl Attributes {
848912
if self.doc_strings.is_empty() { None } else { Some(self.doc_strings.iter().collect()) }
849913
}
850914

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-
917915
crate fn get_doc_aliases(&self) -> FxHashSet<String> {
918916
let mut aliases = FxHashSet::default();
919917

@@ -940,7 +938,6 @@ impl PartialEq for Attributes {
940938
self.doc_strings == rhs.doc_strings
941939
&& self.cfg == rhs.cfg
942940
&& self.span == rhs.span
943-
&& self.links == rhs.links
944941
&& self
945942
.other_attrs
946943
.iter()
@@ -956,7 +953,6 @@ impl Hash for Attributes {
956953
self.doc_strings.hash(hasher);
957954
self.cfg.hash(hasher);
958955
self.span.hash(hasher);
959-
self.links.hash(hasher);
960956
for attr in &self.other_attrs {
961957
attr.id.hash(hasher);
962958
}

src/librustdoc/formats/cache.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use crate::formats::Impl;
1717
use crate::html::markdown::short_markdown_summary;
1818
use crate::html::render::cache::{extern_location, get_index_search_type, ExternalLocation};
1919
use crate::html::render::IndexItem;
20+
use crate::passes::collect_intra_doc_links::EarlyIntraDocLink;
2021

2122
/// This cache is used to store information about the [`clean::Crate`] being
2223
/// rendered in order to provide more useful documentation. This contains
@@ -124,6 +125,9 @@ crate struct Cache {
124125
/// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias,
125126
/// we need the alias element to have an array of items.
126127
crate aliases: BTreeMap<String, Vec<usize>>,
128+
129+
/// TODO: docs
130+
crate intra_doc_links: FxHashMap<DefId, Vec<EarlyIntraDocLink>>,
127131
}
128132

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

src/librustdoc/json/conversions.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ 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 = item
28+
.resolved_links(&self.cache)
29+
.filter_map(|clean::ItemLink { link, did, .. }| {
30+
did.map(|did| (link.clone(), from_def_id(did)))
31+
})
32+
.collect();
2733
let clean::Item { span, name, attrs, kind, visibility, def_id } = item;
2834
let inner = match *kind {
2935
clean::StrippedItem(_) => return None,
@@ -36,20 +42,14 @@ impl JsonRenderer<'_> {
3642
span: self.convert_span(span),
3743
visibility: self.convert_visibility(visibility),
3844
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(),
4645
attrs: attrs
4746
.other_attrs
4847
.iter()
4948
.map(rustc_ast_pretty::pprust::attribute_to_string)
5049
.collect(),
5150
deprecation: deprecation.map(from_deprecation),
5251
inner,
52+
links,
5353
})
5454
}
5555

src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ crate fn collect_intra_doc_links(krate: Crate, cx: &mut DocContext<'_>) -> Crate
5555
.fold_crate(krate)
5656
}
5757

58+
/// TODO: docs
59+
crate enum EarlyIntraDocLink {
60+
Resolved(clean::ItemLink),
61+
}
62+
5863
/// Top-level errors emitted by this pass.
5964
enum ErrorKind<'a> {
6065
Resolve(Box<ResolutionFailure<'a>>),
@@ -820,7 +825,7 @@ fn is_derive_trait_collision<T>(ns: &PerNS<Result<(Res, T), ResolutionFailure<'_
820825
}
821826

822827
impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
823-
fn fold_item(&mut self, mut item: Item) -> Option<Item> {
828+
fn fold_item(&mut self, item: Item) -> Option<Item> {
824829
use rustc_middle::ty::DefIdTree;
825830

826831
let parent_node = if item.is_fake() {
@@ -905,7 +910,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
905910
for md_link in markdown_links(&doc) {
906911
let link = self.resolve_link(&item, &doc, &self_name, parent_node, krate, md_link);
907912
if let Some(link) = link {
908-
item.attrs.links.push(link);
913+
let link = EarlyIntraDocLink::Resolved(link);
914+
self.cx.cache.intra_doc_links.entry(item.def_id).or_default().push(link);
909915
}
910916
}
911917
}

src/librustdoc/passes/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ crate use self::unindent_comments::UNINDENT_COMMENTS;
3030
mod propagate_doc_cfg;
3131
crate use self::propagate_doc_cfg::PROPAGATE_DOC_CFG;
3232

33-
mod collect_intra_doc_links;
33+
crate mod collect_intra_doc_links;
3434
crate use self::collect_intra_doc_links::COLLECT_INTRA_DOC_LINKS;
3535

3636
mod doc_test_lints;

0 commit comments

Comments
 (0)