Skip to content

Commit 850437b

Browse files
bugadanijyn514
andcommitted
Cache link resolution results in current module
Co-authored-by: Joshua Nelson <[email protected]>
1 parent 331e740 commit 850437b

File tree

1 file changed

+79
-14
lines changed

1 file changed

+79
-14
lines changed

src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! [RFC 1946]: https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md
44
55
use rustc_ast as ast;
6-
use rustc_data_structures::stable_set::FxHashSet;
6+
use rustc_data_structures::{fx::FxHashMap, stable_set::FxHashSet};
77
use rustc_errors::{Applicability, DiagnosticBuilder};
88
use rustc_expand::base::SyntaxExtensionKind;
99
use rustc_hir as hir;
@@ -168,6 +168,31 @@ enum AnchorFailure {
168168
RustdocAnchorConflict(Res),
169169
}
170170

171+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
172+
struct CacheKey {
173+
module_id: DefId,
174+
dis: Option<Disambiguator>,
175+
path_str: String,
176+
extra_fragment: Option<String>,
177+
}
178+
179+
impl CacheKey {
180+
fn new(
181+
module_id: DefId,
182+
dis: Option<Disambiguator>,
183+
path_str: String,
184+
extra_fragment: Option<String>,
185+
) -> Self {
186+
Self { module_id, dis, path_str, extra_fragment }
187+
}
188+
}
189+
190+
#[derive(Clone, Debug, Hash)]
191+
struct CachedLink {
192+
pub res: (Res, Option<String>),
193+
pub side_channel: Option<(DefKind, DefId)>,
194+
}
195+
171196
struct LinkCollector<'a, 'tcx> {
172197
cx: &'a DocContext<'tcx>,
173198
/// A stack of modules used to decide what scope to resolve in.
@@ -179,11 +204,18 @@ struct LinkCollector<'a, 'tcx> {
179204
/// because `clean` and the disambiguator code expect them to be different.
180205
/// See the code for associated items on inherent impls for details.
181206
kind_side_channel: Cell<Option<(DefKind, DefId)>>,
207+
/// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link
208+
visited_links: FxHashMap<CacheKey, CachedLink>,
182209
}
183210

184211
impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
185212
fn new(cx: &'a DocContext<'tcx>) -> Self {
186-
LinkCollector { cx, mod_ids: Vec::new(), kind_side_channel: Cell::new(None) }
213+
LinkCollector {
214+
cx,
215+
mod_ids: Vec::new(),
216+
kind_side_channel: Cell::new(None),
217+
visited_links: FxHashMap::default(),
218+
}
187219
}
188220

189221
/// Given a full link, parse it as an [enum struct variant].
@@ -937,7 +969,7 @@ impl LinkCollector<'_, '_> {
937969
///
938970
/// FIXME(jynelson): this is way too many arguments
939971
fn resolve_link(
940-
&self,
972+
&mut self,
941973
item: &Item,
942974
dox: &str,
943975
self_name: &Option<String>,
@@ -962,6 +994,7 @@ impl LinkCollector<'_, '_> {
962994
let link = ori_link.replace("`", "");
963995
let parts = link.split('#').collect::<Vec<_>>();
964996
let (link, extra_fragment) = if parts.len() > 2 {
997+
// A valid link can't have multiple #'s
965998
anchor_failure(cx, &item, &link, dox, link_range, AnchorFailure::MultipleAnchors);
966999
return None;
9671000
} else if parts.len() == 2 {
@@ -1075,16 +1108,9 @@ impl LinkCollector<'_, '_> {
10751108
return None;
10761109
}
10771110

1078-
let (mut res, mut fragment) = self.resolve_with_disambiguator(
1079-
disambiguator,
1080-
item,
1081-
dox,
1082-
path_str,
1083-
module_id,
1084-
extra_fragment,
1085-
&ori_link,
1086-
link_range.clone(),
1087-
)?;
1111+
let key = CacheKey::new(module_id, disambiguator, path_str.to_owned(), extra_fragment);
1112+
let (mut res, mut fragment) =
1113+
self.resolve_with_disambiguator_cached(key, item, dox, &ori_link, link_range.clone())?;
10881114

10891115
// Check for a primitive which might conflict with a module
10901116
// Report the ambiguity and require that the user specify which one they meant.
@@ -1192,6 +1218,45 @@ impl LinkCollector<'_, '_> {
11921218
}
11931219
}
11941220

1221+
fn resolve_with_disambiguator_cached(
1222+
&mut self,
1223+
key: CacheKey,
1224+
item: &Item,
1225+
dox: &str,
1226+
ori_link: &str,
1227+
link_range: Option<Range<usize>>,
1228+
) -> Option<(Res, Option<String>)> {
1229+
// Try to look up both the result and the corresponding side channel value
1230+
if let Some(ref cached) = self.visited_links.get(&key) {
1231+
self.kind_side_channel.set(cached.side_channel.clone());
1232+
Some(cached.res.clone())
1233+
} else {
1234+
match self.resolve_with_disambiguator(
1235+
key.dis,
1236+
item,
1237+
dox,
1238+
&key.path_str,
1239+
key.module_id,
1240+
key.extra_fragment.clone(),
1241+
ori_link,
1242+
link_range,
1243+
) {
1244+
Some(res) => {
1245+
// Store result for the actual namespace
1246+
self.visited_links.insert(
1247+
key,
1248+
CachedLink {
1249+
res: res.clone(),
1250+
side_channel: self.kind_side_channel.clone().into_inner(),
1251+
},
1252+
);
1253+
Some(res)
1254+
}
1255+
_ => None,
1256+
}
1257+
}
1258+
}
1259+
11951260
/// After parsing the disambiguator, resolve the main part of the link.
11961261
// FIXME(jynelson): wow this is just so much
11971262
fn resolve_with_disambiguator(
@@ -1356,7 +1421,7 @@ impl LinkCollector<'_, '_> {
13561421
}
13571422
}
13581423

1359-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1424+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
13601425
/// Disambiguators for a link.
13611426
enum Disambiguator {
13621427
/// `prim@`

0 commit comments

Comments
 (0)