Skip to content

Commit ed73524

Browse files
committed
rustdoc: rewrite the proc macro intra doc link fix entirly
I thought of an edge case that was impossible to handle with the old impl, so I went ahead and rewrote the whole thing to use a more robust solution.
1 parent 415dfa7 commit ed73524

File tree

3 files changed

+87
-17
lines changed

3 files changed

+87
-17
lines changed

src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use rustc_resolve::rustdoc::{
2222
MalformedGenerics, has_primitive_or_keyword_docs, prepare_to_doc_link_resolution,
2323
source_span_for_markdown_range, strip_generics_from_path,
2424
};
25-
use rustc_session::lint::Lint;
25+
use rustc_session::{lint::Lint, config::CrateType};
2626
use rustc_span::BytePos;
2727
use rustc_span::hygiene::MacroKind;
2828
use rustc_span::symbol::{Ident, Symbol, sym};
@@ -1174,7 +1174,6 @@ impl LinkCollector<'_, '_> {
11741174
#[allow(rustc::potential_query_instability)]
11751175
pub(crate) fn resolve_ambiguities(&mut self) {
11761176
let mut ambiguous_links = mem::take(&mut self.ambiguous_links);
1177-
11781177
for ((item_id, path_str), info_items) in ambiguous_links.iter_mut() {
11791178
for info in info_items {
11801179
info.resolved.retain(|(res, _)| match res {
@@ -2219,17 +2218,6 @@ fn report_malformed_generics(
22192218
);
22202219
}
22212220

2222-
fn refer_to_single_item(v: &[Res]) -> bool {
2223-
// proc macros can exist in multiple namespaces at once,
2224-
// so we need to compare DefIds
2225-
v.iter()
2226-
.try_reduce(|l, r| match (l, r) {
2227-
(Res::Def(_, lid), Res::Def(_, rid)) if lid == rid => Some(l),
2228-
_ => None,
2229-
})
2230-
.is_some()
2231-
}
2232-
22332221
/// Report an ambiguity error, where there were multiple possible resolutions.
22342222
///
22352223
/// If all `candidates` have the same kind, it's not possible to disambiguate so in this case,
@@ -2243,16 +2231,56 @@ fn ambiguity_error(
22432231
emit_error: bool,
22442232
) -> bool {
22452233
let mut descrs = FxHashSet::default();
2246-
let kinds = candidates
2234+
// proc macros can exist in multiple namespaces at once,
2235+
// so we need to compare DefIds to remove
2236+
// the candidite in the fn namespace
2237+
let mut possible_proc_macro_id = None;
2238+
let is_proc_macro_crate =
2239+
cx.tcx.crate_types() == &[CrateType::ProcMacro];
2240+
let mut kinds = candidates
22472241
.iter()
22482242
.map(
22492243
|(res, def_id)| {
2250-
if let Some(def_id) = def_id { Res::from_def_id(cx.tcx, *def_id) } else { *res }
2244+
let r = if let Some(def_id) = def_id { Res::from_def_id(cx.tcx, *def_id) } else { *res };
2245+
if is_proc_macro_crate &&
2246+
let Res::Def(DefKind::Macro(_), id) = r
2247+
{
2248+
possible_proc_macro_id = Some(id);
2249+
}
2250+
r
22512251
},
22522252
)
2253-
.filter(|res| descrs.insert(res.descr()))
22542253
.collect::<Vec<_>>();
2255-
if descrs.len() == 1 || refer_to_single_item(&kinds) {
2254+
// in order to properly dedup proc macros,
2255+
// we have to do it in two passes,
2256+
// completing the full traversal to find
2257+
// the possible duplicate in the macro namespace,
2258+
// then another full traversal to eliminate the
2259+
// candidite in the fn namespace.
2260+
// thus, we have to do an iteration after
2261+
// collection is finished.
2262+
//
2263+
// as an optimization, we only deduplicate if we're in a proc-macro crate,
2264+
// and only if we already found something that looks like a proc macro.
2265+
if is_proc_macro_crate &&
2266+
let Some(macro_id) = possible_proc_macro_id
2267+
{
2268+
kinds.retain(|res| {
2269+
if let Res::Def(DefKind::Fn, fn_id) = res &&
2270+
macro_id == *fn_id
2271+
{
2272+
false
2273+
} else {
2274+
true
2275+
}
2276+
});
2277+
}
2278+
2279+
kinds.retain(|res| {
2280+
descrs.insert(res.descr())
2281+
});
2282+
2283+
if descrs.len() == 1 {
22562284
// There is no way for users to disambiguate at this point, so better return the first
22572285
// candidate and not show a warning.
22582286
return false;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//@ compile-flags: --crate-type=proc-macro --document-private-items
2+
#![deny(rustdoc::broken_intra_doc_links)]
3+
4+
//! Link to [`m`].
5+
//~^ ERROR `m` is both a module and a macro
6+
7+
// test a further edge case related to https://github.com/rust-lang/rust/issues/91274
8+
9+
// we need to make sure that when there is actually an ambiguity in a proc-macro crate, we print out a sensible error.
10+
// because proc macro crates can't normally export modules,
11+
// this can only happen in --document-private-items mode.
12+
13+
extern crate proc_macro;
14+
15+
mod m {}
16+
17+
#[proc_macro]
18+
pub fn m(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
19+
input
20+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: `m` is both a module and a macro
2+
--> $DIR/bad-link-to-proc-macro.rs:4:15
3+
|
4+
LL | //! Link to [`m`].
5+
| ^ ambiguous link
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/bad-link-to-proc-macro.rs:2:9
9+
|
10+
LL | #![deny(rustdoc::broken_intra_doc_links)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
help: to link to the module, prefix with `mod@`
13+
|
14+
LL | //! Link to [`mod@m`].
15+
| ++++
16+
help: to link to the macro, add an exclamation mark
17+
|
18+
LL | //! Link to [`m!`].
19+
| +
20+
21+
error: aborting due to 1 previous error
22+

0 commit comments

Comments
 (0)