Skip to content

Commit 759c771

Browse files
test
1 parent 26fb306 commit 759c771

12 files changed

+241
-231
lines changed

src/librustdoc/clean/types.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -1228,15 +1228,14 @@ impl Attributes {
12281228
for attr in self.other_attrs.lists(sym::doc).filter(|a| a.has_name(sym::alias)) {
12291229
if let Some(values) = attr.meta_item_list() {
12301230
for l in values {
1231-
match l.lit().unwrap().kind {
1232-
ast::LitKind::Str(s, _) => {
1233-
aliases.insert(s);
1234-
}
1235-
_ => unreachable!(),
1231+
if let Some(lit) = l.lit()
1232+
&& let ast::LitKind::Str(s, _) = lit.kind
1233+
{
1234+
aliases.insert(s);
12361235
}
12371236
}
1238-
} else {
1239-
aliases.insert(attr.value_str().unwrap());
1237+
} else if let Some(value) = attr.value_str() {
1238+
aliases.insert(value);
12401239
}
12411240
}
12421241
aliases.into_iter().collect::<Vec<_>>().into()

src/librustdoc/core.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions};
3131
use crate::formats::cache::Cache;
3232
use crate::passes::Condition::*;
3333
use crate::passes::collect_intra_doc_links::LinkCollector;
34-
use crate::passes::{self};
34+
use crate::passes;
3535

3636
pub(crate) struct DocContext<'tcx> {
3737
pub(crate) tcx: TyCtxt<'tcx>,
@@ -441,21 +441,23 @@ pub(crate) fn run_global_ctxt(
441441
}
442442
}
443443

444+
debug!("running pass {}", passes::collect_intra_doc_links::COLLECT_INTRA_DOC_LINKS.name);
444445
let (mut krate, LinkCollector { visited_links, ambiguous_links, .. }) =
445446
tcx.sess.time("collect_intra_doc_links", || {
446447
passes::collect_intra_doc_links::collect_intra_doc_links(krate, &mut ctxt)
447448
});
448-
tcx.sess.time("check_lint_expectations", || tcx.check_expectations(Some(sym::rustdoc)));
449-
450-
if let Some(guar) = tcx.dcx().has_errors() {
451-
return Err(guar);
452-
}
453449

454450
krate = tcx.sess.time("create_format_cache", || Cache::populate(&mut ctxt, krate));
455451

456452
let mut collector = LinkCollector { cx: &mut ctxt, visited_links, ambiguous_links };
457453
collector.resolve_ambiguities();
458454

455+
tcx.sess.time("check_lint_expectations", || tcx.check_expectations(Some(sym::rustdoc)));
456+
457+
if let Some(guar) = tcx.dcx().has_errors() {
458+
return Err(guar);
459+
}
460+
459461
Ok((krate, ctxt.render_options, ctxt.cache))
460462
}
461463

src/librustdoc/html/markdown.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1688,7 +1688,7 @@ pub(crate) struct MarkdownLink {
16881688
pub range: MarkdownLinkRange,
16891689
}
16901690

1691-
#[derive(Clone, Debug)]
1691+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
16921692
pub(crate) enum MarkdownLinkRange {
16931693
/// Normally, markdown link warnings point only at the destination.
16941694
Destination(Range<usize>),

src/librustdoc/passes/collect_intra_doc_links.rs

+79-58
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,17 @@ use crate::clean::{self, Crate, Item, ItemId, ItemLink, PrimitiveType};
3434
use crate::core::DocContext;
3535
use crate::html::markdown::{MarkdownLink, MarkdownLinkRange, markdown_links};
3636
use crate::lint::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS};
37+
use crate::passes::Pass;
3738
use crate::visit::DocVisitor;
3839

40+
/// We create an empty pass for `collect_intra_doc_link` so it still listed when displaying
41+
/// passes list.
42+
pub(crate) const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
43+
name: "collect-intra-doc-links",
44+
run: |krate: Crate, _: &mut DocContext<'_>| krate,
45+
description: "resolves intra-doc links",
46+
};
47+
3948
pub(crate) fn collect_intra_doc_links<'a, 'tcx>(
4049
krate: Crate,
4150
cx: &'a mut DocContext<'tcx>,
@@ -247,6 +256,7 @@ pub(crate) struct ResolutionInfo {
247256
dis: Option<Disambiguator>,
248257
path_str: Box<str>,
249258
extra_fragment: Option<String>,
259+
link_range: MarkdownLinkRange,
250260
}
251261

252262
#[derive(Clone, Debug)]
@@ -290,7 +300,7 @@ pub(crate) struct LinkCollector<'a, 'tcx> {
290300
pub(crate) cx: &'a mut DocContext<'tcx>,
291301
/// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link.
292302
/// The link will be `None` if it could not be resolved (i.e. the error was cached).
293-
pub(crate) visited_links: FxHashMap<ResolutionInfo, Option<(Res, Option<UrlFragment>)>>,
303+
pub(crate) visited_links: FxHashMap<ResolutionInfo, Option<Vec<(Res, Option<UrlFragment>)>>>,
294304
/// These links are ambiguous. We need for the cache to have its paths filled. Unfortunately,
295305
/// if we run the `LinkCollector` pass after `Cache::populate`, a lot of items that we need
296306
/// to go through will be removed, making a lot of intra-doc links to not be inferred.
@@ -299,7 +309,7 @@ pub(crate) struct LinkCollector<'a, 'tcx> {
299309
/// inferring them (if possible).
300310
///
301311
/// Key is `(item ID, path str)`.
302-
pub(crate) ambiguous_links: FxHashMap<(ItemId, String), AmbiguousLinks>,
312+
pub(crate) ambiguous_links: FxHashMap<(ItemId, String), Vec<AmbiguousLinks>>,
303313
}
304314

305315
impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
@@ -1079,6 +1089,7 @@ impl LinkCollector<'_, '_> {
10791089
dis: disambiguator,
10801090
path_str: path_str.clone(),
10811091
extra_fragment: extra_fragment.clone(),
1092+
link_range: ori_link.range.clone(),
10821093
},
10831094
diag_info.clone(), // this struct should really be Copy, but Range is not :(
10841095
// For reference-style links we want to report only one error so unsuccessful
@@ -1095,7 +1106,11 @@ impl LinkCollector<'_, '_> {
10951106
diag_info: diag_info.into(),
10961107
resolved,
10971108
};
1098-
self.ambiguous_links.insert((item.item_id, path_str.to_string()), links);
1109+
1110+
self.ambiguous_links
1111+
.entry((item.item_id, path_str.to_string()))
1112+
.or_default()
1113+
.push(links);
10991114
None
11001115
} else if let Some((res, fragment)) = resolved.pop() {
11011116
self.compute_link(res, fragment, path_str, disambiguator, diag_info, link_text)
@@ -1152,51 +1167,60 @@ impl LinkCollector<'_, '_> {
11521167
pub(crate) fn resolve_ambiguities(&mut self) {
11531168
let mut ambiguous_links = mem::take(&mut self.ambiguous_links);
11541169

1155-
for ((item_id, path_str), info) in ambiguous_links.iter_mut() {
1156-
info.resolved.retain(|(res, _)| match res {
1157-
Res::Def(_, def_id) => self.validate_link(*def_id),
1158-
// Primitive types are always valid.
1159-
Res::Primitive(_) => true,
1160-
});
1161-
let diag_info = info.diag_info.into_info();
1162-
match info.resolved.len() {
1163-
1 => {
1164-
let (res, fragment) = info.resolved.pop().unwrap();
1165-
if let Some(link) = self.compute_link(
1166-
res,
1167-
fragment,
1168-
path_str,
1169-
info.disambiguator,
1170-
diag_info,
1171-
&info.link_text,
1172-
) {
1173-
self.save_link(*item_id, link);
1170+
for ((item_id, path_str), info_items) in ambiguous_links.iter_mut() {
1171+
for info in info_items {
1172+
info.resolved.retain(|(res, _)| match res {
1173+
Res::Def(_, def_id) => self.validate_link(*def_id),
1174+
// Primitive types are always valid.
1175+
Res::Primitive(_) => true,
1176+
});
1177+
let diag_info = info.diag_info.into_info();
1178+
match info.resolved.len() {
1179+
1 => {
1180+
let (res, fragment) = info.resolved.pop().unwrap();
1181+
if let Some(link) = self.compute_link(
1182+
res,
1183+
fragment,
1184+
path_str,
1185+
info.disambiguator,
1186+
diag_info,
1187+
&info.link_text,
1188+
) {
1189+
self.save_link(*item_id, link);
1190+
}
1191+
}
1192+
0 => {
1193+
report_diagnostic(
1194+
self.cx.tcx,
1195+
BROKEN_INTRA_DOC_LINKS,
1196+
format!(
1197+
"all items matching `{path_str}` are either private or doc(hidden)"
1198+
),
1199+
&diag_info,
1200+
|diag, sp, _| {
1201+
if let Some(sp) = sp {
1202+
diag.span_label(sp, "unresolved link");
1203+
} else {
1204+
diag.note("unresolved link");
1205+
}
1206+
},
1207+
);
1208+
}
1209+
_ => {
1210+
let candidates = info
1211+
.resolved
1212+
.iter()
1213+
.map(|(res, fragment)| {
1214+
let def_id = if let Some(UrlFragment::Item(def_id)) = fragment {
1215+
Some(*def_id)
1216+
} else {
1217+
None
1218+
};
1219+
(*res, def_id)
1220+
})
1221+
.collect::<Vec<_>>();
1222+
ambiguity_error(self.cx, &diag_info, path_str, &candidates, true);
11741223
}
1175-
}
1176-
0 => {
1177-
report_diagnostic(
1178-
self.cx.tcx,
1179-
BROKEN_INTRA_DOC_LINKS,
1180-
format!(
1181-
"all items matching `{path_str}` are either private or doc(hidden)"
1182-
),
1183-
&diag_info,
1184-
|diag, sp, _| {
1185-
if let Some(sp) = sp {
1186-
diag.span_label(sp, "unresolved link");
1187-
} else {
1188-
diag.note("unresolved link");
1189-
}
1190-
},
1191-
);
1192-
}
1193-
_ => {
1194-
let candidates = info
1195-
.resolved
1196-
.iter()
1197-
.map(|(res, _)| (*res, res.def_id(self.cx.tcx)))
1198-
.collect::<Vec<_>>();
1199-
ambiguity_error(self.cx, &diag_info, path_str, &candidates, true);
12001224
}
12011225
}
12021226
}
@@ -1387,15 +1411,13 @@ impl LinkCollector<'_, '_> {
13871411
diag: DiagnosticInfo<'_>,
13881412
// If errors are cached then they are only reported on first occurrence
13891413
// which we want in some cases but not in others.
1390-
cache_errors: bool,
1414+
_cache_errors: bool,
13911415
// If this call is intended to be recoverable, then pass true to silence.
13921416
// This is only recoverable when path is failed to resolved.
13931417
recoverable: bool,
13941418
) -> Option<Vec<(Res, Option<UrlFragment>)>> {
13951419
if let Some(res) = self.visited_links.get(&key) {
1396-
if res.is_some() || cache_errors {
1397-
return res.clone().map(|r| vec![r]);
1398-
}
1420+
return res.clone();
13991421
}
14001422

14011423
let mut candidates = self.resolve_with_disambiguator(&key, diag.clone(), recoverable);
@@ -1426,25 +1448,24 @@ impl LinkCollector<'_, '_> {
14261448
}
14271449

14281450
let mut resolved = Vec::with_capacity(candidates.len());
1429-
for (res, def_id) in candidates.as_slice() {
1451+
for (res, def_id) in candidates {
14301452
let fragment = match (&key.extra_fragment, def_id) {
14311453
(Some(_), Some(def_id)) => {
1432-
report_anchor_conflict(self.cx, diag, *def_id);
1454+
report_anchor_conflict(self.cx, diag, def_id);
14331455
return None;
14341456
}
14351457
(Some(u_frag), None) => Some(UrlFragment::UserWritten(u_frag.clone())),
1436-
(None, Some(def_id)) => Some(UrlFragment::Item(*def_id)),
1458+
(None, Some(def_id)) => Some(UrlFragment::Item(def_id)),
14371459
(None, None) => None,
14381460
};
1439-
let r = (res.clone(), fragment.clone());
1440-
self.visited_links.insert(key.clone(), Some(r.clone()));
1441-
resolved.push(r);
1461+
resolved.push((res, fragment));
14421462
}
14431463

1444-
if resolved.is_empty() && cache_errors {
1464+
if resolved.is_empty() {
14451465
self.visited_links.insert(key, None);
14461466
None
14471467
} else {
1468+
self.visited_links.insert(key.clone(), Some(resolved.clone()));
14481469
Some(resolved)
14491470
}
14501471
}

src/librustdoc/passes/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ mod propagate_stability;
2727
pub(crate) use self::propagate_stability::PROPAGATE_STABILITY;
2828

2929
pub(crate) mod collect_intra_doc_links;
30+
pub(crate) use self::collect_intra_doc_links::COLLECT_INTRA_DOC_LINKS;
3031

3132
mod check_doc_test_visibility;
3233
pub(crate) use self::check_doc_test_visibility::CHECK_DOC_TEST_VISIBILITY;
@@ -78,6 +79,7 @@ pub(crate) const PASSES: &[Pass] = &[
7879
STRIP_PRIV_IMPORTS,
7980
PROPAGATE_DOC_CFG,
8081
PROPAGATE_STABILITY,
82+
COLLECT_INTRA_DOC_LINKS,
8183
COLLECT_TRAIT_IMPLS,
8284
CALCULATE_DOC_COVERAGE,
8385
RUN_LINTS,
@@ -91,6 +93,7 @@ pub(crate) const DEFAULT_PASSES: &[ConditionalPass] = &[
9193
ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden),
9294
ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate),
9395
ConditionalPass::new(STRIP_PRIV_IMPORTS, WhenDocumentPrivate),
96+
ConditionalPass::always(COLLECT_INTRA_DOC_LINKS),
9497
ConditionalPass::always(PROPAGATE_DOC_CFG),
9598
ConditionalPass::always(PROPAGATE_STABILITY),
9699
ConditionalPass::always(RUN_LINTS),

tests/rustdoc-ui/intra-doc/ambiguity.stderr

+10-25
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,20 @@ help: to link to the struct, prefix with `struct@`
3333
LL | /// [`struct@ambiguous`] is ambiguous.
3434
| +++++++
3535

36-
error: `ambiguous` is both a function and a struct
37-
--> $DIR/ambiguity.rs:29:6
36+
error: `foo::bar` is both a function and an enum
37+
--> $DIR/ambiguity.rs:35:43
3838
|
39-
LL | /// [ambiguous] is ambiguous.
40-
| ^^^^^^^^^ ambiguous link
39+
LL | /// Ambiguous non-implied shortcut link [`foo::bar`].
40+
| ^^^^^^^^ ambiguous link
4141
|
4242
help: to link to the function, add parentheses
4343
|
44-
LL | /// [ambiguous()] is ambiguous.
45-
| ++
46-
help: to link to the struct, prefix with `struct@`
44+
LL | /// Ambiguous non-implied shortcut link [`foo::bar()`].
45+
| ++
46+
help: to link to the enum, prefix with `enum@`
4747
|
48-
LL | /// [struct@ambiguous] is ambiguous.
49-
| +++++++
48+
LL | /// Ambiguous non-implied shortcut link [`enum@foo::bar`].
49+
| +++++
5050

5151
error: `multi_conflict` is a function, a struct, and a macro
5252
--> $DIR/ambiguity.rs:31:7
@@ -82,20 +82,5 @@ help: to link to the module, prefix with `mod@`
8282
LL | /// Ambiguous [mod@type_and_value].
8383
| ++++
8484

85-
error: `foo::bar` is both a function and an enum
86-
--> $DIR/ambiguity.rs:35:43
87-
|
88-
LL | /// Ambiguous non-implied shortcut link [`foo::bar`].
89-
| ^^^^^^^^ ambiguous link
90-
|
91-
help: to link to the function, add parentheses
92-
|
93-
LL | /// Ambiguous non-implied shortcut link [`foo::bar()`].
94-
| ++
95-
help: to link to the enum, prefix with `enum@`
96-
|
97-
LL | /// Ambiguous non-implied shortcut link [`enum@foo::bar`].
98-
| +++++
99-
100-
error: aborting due to 6 previous errors
85+
error: aborting due to 5 previous errors
10186

0 commit comments

Comments
 (0)