Skip to content

Commit d9edb05

Browse files
committed
rustdoc: fix quadratic time in intra-doc link pass
In the collect_intra_doc_links pass, links to a given item that occurred repeatedly were getting inserted into a Vec<clean::ItemLink> repeatedly. This led to n^2 behavior (where n = the number of pages generated), particularly for the intra-doc link on the `Into<U> for T where U: From<T>` blanket implementation, since that link appears on every single struct page.
1 parent 4c0f500 commit d9edb05

File tree

3 files changed

+14
-10
lines changed

3 files changed

+14
-10
lines changed

src/librustdoc/clean/types.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -482,10 +482,12 @@ impl Item {
482482
pub(crate) fn links(&self, cx: &Context<'_>) -> Vec<RenderedLink> {
483483
use crate::html::format::{href, link_tooltip};
484484

485-
cx.cache()
485+
let Some(links) = cx.cache()
486486
.intra_doc_links
487-
.get(&self.item_id)
488-
.map_or(&[][..], |v| v.as_slice())
487+
.get(&self.item_id) else {
488+
return vec![]
489+
};
490+
links
489491
.iter()
490492
.filter_map(|ItemLink { link: s, link_text, page_id: id, ref fragment }| {
491493
debug!(?id);
@@ -513,10 +515,12 @@ impl Item {
513515
/// the link text, but does need to know which `[]`-bracketed names
514516
/// are actually links.
515517
pub(crate) fn link_names(&self, cache: &Cache) -> Vec<RenderedLink> {
516-
cache
518+
let Some(links) = cache
517519
.intra_doc_links
518-
.get(&self.item_id)
519-
.map_or(&[][..], |v| v.as_slice())
520+
.get(&self.item_id) else {
521+
return vec![];
522+
};
523+
links
520524
.iter()
521525
.map(|ItemLink { link: s, link_text, .. }| RenderedLink {
522526
original_text: s.clone(),
@@ -1014,7 +1018,7 @@ pub(crate) fn collapse_doc_fragments(doc_strings: &[DocFragment]) -> String {
10141018
/// A link that has not yet been rendered.
10151019
///
10161020
/// This link will be turned into a rendered link by [`Item::links`].
1017-
#[derive(Clone, Debug, PartialEq, Eq)]
1021+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
10181022
pub(crate) struct ItemLink {
10191023
/// The original link written in the markdown
10201024
pub(crate) link: Box<str>,

src/librustdoc/formats/cache.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::mem;
22

3-
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
3+
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
44
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet};
55
use rustc_middle::ty::{self, TyCtxt};
66
use rustc_span::Symbol;
@@ -118,7 +118,7 @@ pub(crate) struct Cache {
118118
/// All intra-doc links resolved so far.
119119
///
120120
/// Links are indexed by the DefId of the item they document.
121-
pub(crate) intra_doc_links: FxHashMap<ItemId, Vec<clean::ItemLink>>,
121+
pub(crate) intra_doc_links: FxHashMap<ItemId, FxIndexSet<clean::ItemLink>>,
122122
/// Cfg that have been hidden via #![doc(cfg_hide(...))]
123123
pub(crate) hidden_cfg: FxHashSet<clean::cfg::Cfg>,
124124
}

src/librustdoc/passes/collect_intra_doc_links.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -918,7 +918,7 @@ impl LinkCollector<'_, '_> {
918918
for md_link in preprocessed_markdown_links(&doc) {
919919
let link = self.resolve_link(item, item_id, module_id, &doc, &md_link);
920920
if let Some(link) = link {
921-
self.cx.cache.intra_doc_links.entry(item.item_id).or_default().push(link);
921+
self.cx.cache.intra_doc_links.entry(item.item_id).or_default().insert(link);
922922
}
923923
}
924924
}

0 commit comments

Comments
 (0)