Skip to content

Commit de287df

Browse files
committed
rustdoc: Cache preprocessed markdown links
1 parent 72ed101 commit de287df

File tree

4 files changed

+68
-45
lines changed

4 files changed

+68
-45
lines changed

src/librustdoc/core.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ use crate::clean::inline::build_external_trait;
2929
use crate::clean::{self, ItemId, TraitWithExtraInfo};
3030
use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions};
3131
use crate::formats::cache::Cache;
32-
use crate::html::markdown::MarkdownLink;
32+
use crate::passes::collect_intra_doc_links::PreprocessedMarkdownLink;
3333
use crate::passes::{self, Condition::*};
3434

3535
crate use rustc_session::config::{DebuggingOptions, Input, Options};
3636

3737
crate struct ResolverCaches {
38-
crate markdown_links: Option<FxHashMap<String, Vec<MarkdownLink>>>,
38+
crate markdown_links: Option<FxHashMap<String, Vec<PreprocessedMarkdownLink>>>,
3939
crate doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option<Res<NodeId>>>,
4040
/// Traits in scope for a given module.
4141
/// See `collect_intra_doc_links::traits_implemented_by` for more details.

src/librustdoc/html/markdown.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1255,7 +1255,7 @@ crate struct MarkdownLink {
12551255
pub range: Range<usize>,
12561256
}
12571257

1258-
crate fn markdown_links(md: &str) -> Vec<MarkdownLink> {
1258+
crate fn markdown_links<R>(md: &str, filter_map: impl Fn(MarkdownLink) -> Option<R>) -> Vec<R> {
12591259
if md.is_empty() {
12601260
return vec![];
12611261
}
@@ -1295,11 +1295,12 @@ crate fn markdown_links(md: &str) -> Vec<MarkdownLink> {
12951295

12961296
let mut push = |link: BrokenLink<'_>| {
12971297
let span = span_for_link(&link.reference, link.span);
1298-
links.borrow_mut().push(MarkdownLink {
1298+
filter_map(MarkdownLink {
12991299
kind: LinkType::ShortcutUnknown,
13001300
link: link.reference.to_string(),
13011301
range: span,
1302-
});
1302+
})
1303+
.map(|link| links.borrow_mut().push(link));
13031304
None
13041305
};
13051306
let p = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(&mut push))
@@ -1314,7 +1315,8 @@ crate fn markdown_links(md: &str) -> Vec<MarkdownLink> {
13141315
if let Event::Start(Tag::Link(kind, dest, _)) = ev.0 {
13151316
debug!("found link: {dest}");
13161317
let span = span_for_link(&dest, ev.1);
1317-
links.borrow_mut().push(MarkdownLink { kind, link: dest.into_string(), range: span });
1318+
filter_map(MarkdownLink { kind, link: dest.into_string(), range: span })
1319+
.map(|link| links.borrow_mut().push(link));
13181320
}
13191321
}
13201322

src/librustdoc/passes/collect_intra_doc_links.rs

+51-33
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ impl TryFrom<ResolveRes> for Res {
160160
}
161161

162162
/// A link failed to resolve.
163-
#[derive(Debug)]
163+
#[derive(Clone, Debug)]
164164
enum ResolutionFailure<'a> {
165165
/// This resolved, but with the wrong namespace.
166166
WrongNamespace {
@@ -200,7 +200,7 @@ enum ResolutionFailure<'a> {
200200
Dummy,
201201
}
202202

203-
#[derive(Debug)]
203+
#[derive(Clone, Debug)]
204204
enum MalformedGenerics {
205205
/// This link has unbalanced angle brackets.
206206
///
@@ -253,6 +253,7 @@ impl ResolutionFailure<'_> {
253253
}
254254
}
255255

256+
#[derive(Clone, Copy)]
256257
enum AnchorFailure {
257258
/// User error: `[std#x#y]` is not valid
258259
MultipleAnchors,
@@ -1064,7 +1065,7 @@ impl<'a, 'tcx> DocVisitor for LinkCollector<'a, 'tcx> {
10641065
.take()
10651066
.expect("`markdown_links` are already borrowed");
10661067
if !tmp_links.contains_key(&doc) {
1067-
tmp_links.insert(doc.clone(), markdown_links(&doc));
1068+
tmp_links.insert(doc.clone(), preprocessed_markdown_links(&doc));
10681069
}
10691070
for md_link in &tmp_links[&doc] {
10701071
let link = self.resolve_link(&item, &doc, parent_node, md_link);
@@ -1088,34 +1089,38 @@ impl<'a, 'tcx> DocVisitor for LinkCollector<'a, 'tcx> {
10881089
}
10891090
}
10901091

1091-
enum PreprocessingError<'a> {
1092+
enum PreprocessingError {
10921093
Anchor(AnchorFailure),
10931094
Disambiguator(Range<usize>, String),
1094-
Resolution(ResolutionFailure<'a>, String, Option<Disambiguator>),
1095+
Resolution(ResolutionFailure<'static>, String, Option<Disambiguator>),
10951096
}
10961097

1097-
impl From<AnchorFailure> for PreprocessingError<'_> {
1098+
impl From<AnchorFailure> for PreprocessingError {
10981099
fn from(err: AnchorFailure) -> Self {
10991100
Self::Anchor(err)
11001101
}
11011102
}
11021103

1104+
#[derive(Clone)]
11031105
struct PreprocessingInfo {
11041106
path_str: String,
11051107
disambiguator: Option<Disambiguator>,
11061108
extra_fragment: Option<String>,
11071109
link_text: String,
11081110
}
11091111

1112+
// Not a typedef to avoid leaking several private structures from this module.
1113+
crate struct PreprocessedMarkdownLink(Result<PreprocessingInfo, PreprocessingError>, MarkdownLink);
1114+
11101115
/// Returns:
11111116
/// - `None` if the link should be ignored.
11121117
/// - `Some(Err)` if the link should emit an error
11131118
/// - `Some(Ok)` if the link is valid
11141119
///
11151120
/// `link_buffer` is needed for lifetime reasons; it will always be overwritten and the contents ignored.
1116-
fn preprocess_link<'a>(
1117-
ori_link: &'a MarkdownLink,
1118-
) -> Option<Result<PreprocessingInfo, PreprocessingError<'a>>> {
1121+
fn preprocess_link(
1122+
ori_link: &MarkdownLink,
1123+
) -> Option<Result<PreprocessingInfo, PreprocessingError>> {
11191124
// [] is mostly likely not supposed to be a link
11201125
if ori_link.link.is_empty() {
11211126
return None;
@@ -1194,6 +1199,12 @@ fn preprocess_link<'a>(
11941199
}))
11951200
}
11961201

1202+
fn preprocessed_markdown_links(s: &str) -> Vec<PreprocessedMarkdownLink> {
1203+
markdown_links(s, |link| {
1204+
preprocess_link(&link).map(|pp_link| PreprocessedMarkdownLink(pp_link, link))
1205+
})
1206+
}
1207+
11971208
impl LinkCollector<'_, '_> {
11981209
/// This is the entry point for resolving an intra-doc link.
11991210
///
@@ -1203,8 +1214,9 @@ impl LinkCollector<'_, '_> {
12031214
item: &Item,
12041215
dox: &str,
12051216
parent_node: Option<DefId>,
1206-
ori_link: &MarkdownLink,
1217+
link: &PreprocessedMarkdownLink,
12071218
) -> Option<ItemLink> {
1219+
let PreprocessedMarkdownLink(pp_link, ori_link) = link;
12081220
trace!("considering link '{}'", ori_link.link);
12091221

12101222
let diag_info = DiagnosticInfo {
@@ -1214,28 +1226,29 @@ impl LinkCollector<'_, '_> {
12141226
link_range: ori_link.range.clone(),
12151227
};
12161228

1217-
let PreprocessingInfo { ref path_str, disambiguator, extra_fragment, link_text } =
1218-
match preprocess_link(&ori_link)? {
1219-
Ok(x) => x,
1220-
Err(err) => {
1221-
match err {
1222-
PreprocessingError::Anchor(err) => anchor_failure(self.cx, diag_info, err),
1223-
PreprocessingError::Disambiguator(range, msg) => {
1224-
disambiguator_error(self.cx, diag_info, range, &msg)
1225-
}
1226-
PreprocessingError::Resolution(err, path_str, disambiguator) => {
1227-
resolution_failure(
1228-
self,
1229-
diag_info,
1230-
&path_str,
1231-
disambiguator,
1232-
smallvec![err],
1233-
);
1234-
}
1229+
let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text } = match pp_link
1230+
{
1231+
Ok(x) => x,
1232+
Err(err) => {
1233+
match err {
1234+
PreprocessingError::Anchor(err) => anchor_failure(self.cx, diag_info, *err),
1235+
PreprocessingError::Disambiguator(range, msg) => {
1236+
disambiguator_error(self.cx, diag_info, range.clone(), msg)
1237+
}
1238+
PreprocessingError::Resolution(err, path_str, disambiguator) => {
1239+
resolution_failure(
1240+
self,
1241+
diag_info,
1242+
path_str,
1243+
*disambiguator,
1244+
smallvec![err.clone()],
1245+
);
12351246
}
1236-
return None;
12371247
}
1238-
};
1248+
return None;
1249+
}
1250+
};
1251+
let disambiguator = *disambiguator;
12391252

12401253
let inner_docs = item.inner_docs(self.cx.tcx);
12411254

@@ -1272,7 +1285,7 @@ impl LinkCollector<'_, '_> {
12721285
module_id,
12731286
dis: disambiguator,
12741287
path_str: path_str.to_owned(),
1275-
extra_fragment,
1288+
extra_fragment: extra_fragment.clone(),
12761289
},
12771290
diag_info.clone(), // this struct should really be Copy, but Range is not :(
12781291
matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut),
@@ -1343,7 +1356,7 @@ impl LinkCollector<'_, '_> {
13431356

13441357
Some(ItemLink {
13451358
link: ori_link.link.clone(),
1346-
link_text,
1359+
link_text: link_text.clone(),
13471360
did: res.def_id(self.cx.tcx),
13481361
fragment,
13491362
})
@@ -1365,7 +1378,12 @@ impl LinkCollector<'_, '_> {
13651378
&diag_info,
13661379
)?;
13671380
let id = clean::register_res(self.cx, rustc_hir::def::Res::Def(kind, id));
1368-
Some(ItemLink { link: ori_link.link.clone(), link_text, did: id, fragment })
1381+
Some(ItemLink {
1382+
link: ori_link.link.clone(),
1383+
link_text: link_text.clone(),
1384+
did: id,
1385+
fragment,
1386+
})
13691387
}
13701388
}
13711389
}

src/librustdoc/passes/collect_intra_doc_links/early.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::clean::Attributes;
22
use crate::core::ResolverCaches;
3-
use crate::html::markdown::{markdown_links, MarkdownLink};
4-
use crate::passes::collect_intra_doc_links::preprocess_link;
3+
use crate::passes::collect_intra_doc_links::preprocessed_markdown_links;
4+
use crate::passes::collect_intra_doc_links::PreprocessedMarkdownLink;
55

66
use rustc_ast::visit::{self, AssocCtxt, Visitor};
77
use rustc_ast::{self as ast, ItemKind};
@@ -72,7 +72,7 @@ struct EarlyDocLinkResolver<'r, 'ra> {
7272
resolver: &'r mut Resolver<'ra>,
7373
current_mod: LocalDefId,
7474
visited_mods: DefIdSet,
75-
markdown_links: FxHashMap<String, Vec<MarkdownLink>>,
75+
markdown_links: FxHashMap<String, Vec<PreprocessedMarkdownLink>>,
7676
doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option<Res<ast::NodeId>>>,
7777
traits_in_scope: DefIdMap<Vec<TraitCandidate>>,
7878
all_traits: Vec<DefId>,
@@ -203,9 +203,12 @@ impl EarlyDocLinkResolver<'_, '_> {
203203
let mut need_traits_in_scope = false;
204204
for (doc_module, doc) in attrs.collapsed_doc_value_by_module_level() {
205205
assert_eq!(doc_module, None);
206-
for link in self.markdown_links.entry(doc).or_insert_with_key(|doc| markdown_links(doc))
207-
{
208-
if let Some(Ok(pinfo)) = preprocess_link(&link) {
206+
let links = self
207+
.markdown_links
208+
.entry(doc)
209+
.or_insert_with_key(|doc| preprocessed_markdown_links(doc));
210+
for PreprocessedMarkdownLink(pp_link, _) in links {
211+
if let Ok(pinfo) = pp_link {
209212
// FIXME: Resolve the path in all namespaces and resolve its prefixes too.
210213
let ns = TypeNS;
211214
self.doc_link_resolutions

0 commit comments

Comments
 (0)