Skip to content

Commit e66a8c2

Browse files
committed
Auto merge of #87285 - GuillaumeGomez:intra-doc-span, r=estebank
Improve intra doc errors display #87169 `@jyn514` This is what I had in mind to avoid having duplicated backticks. I also gave a try to simply updating the span for the suggestion/help messages but I think this current one is better because less "noisy". Anyway, that allows you to see the result. ;)
2 parents 4927238 + 0f7f85e commit e66a8c2

11 files changed

+239
-116
lines changed

src/librustdoc/passes/collect_intra_doc_links.rs

+73-24
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use rustc_resolve::ParentScope;
1919
use rustc_session::lint::Lint;
2020
use rustc_span::hygiene::{MacroKind, SyntaxContext};
2121
use rustc_span::symbol::{sym, Ident, Symbol};
22-
use rustc_span::DUMMY_SP;
22+
use rustc_span::{BytePos, DUMMY_SP};
2323
use smallvec::{smallvec, SmallVec};
2424

2525
use pulldown_cmark::LinkType;
@@ -1193,16 +1193,20 @@ impl LinkCollector<'_, '_> {
11931193
let report_mismatch = |specified: Disambiguator, resolved: Disambiguator| {
11941194
// The resolved item did not match the disambiguator; give a better error than 'not found'
11951195
let msg = format!("incompatible link kind for `{}`", path_str);
1196-
let callback = |diag: &mut DiagnosticBuilder<'_>, sp| {
1196+
let callback = |diag: &mut DiagnosticBuilder<'_>, sp: Option<rustc_span::Span>| {
11971197
let note = format!(
11981198
"this link resolved to {} {}, which is not {} {}",
11991199
resolved.article(),
12001200
resolved.descr(),
12011201
specified.article(),
12021202
specified.descr()
12031203
);
1204-
diag.note(&note);
1205-
suggest_disambiguator(resolved, diag, path_str, dox, sp, &ori_link.range);
1204+
if let Some(sp) = sp {
1205+
diag.span_label(sp, &note);
1206+
} else {
1207+
diag.note(&note);
1208+
}
1209+
suggest_disambiguator(resolved, diag, path_str, &ori_link.link, sp);
12061210
};
12071211
report_diagnostic(self.cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, &diag_info, callback);
12081212
};
@@ -1699,6 +1703,51 @@ impl Suggestion {
16991703
Self::RemoveDisambiguator => path_str.into(),
17001704
}
17011705
}
1706+
1707+
fn as_help_span(
1708+
&self,
1709+
path_str: &str,
1710+
ori_link: &str,
1711+
sp: rustc_span::Span,
1712+
) -> Vec<(rustc_span::Span, String)> {
1713+
let inner_sp = match ori_link.find('(') {
1714+
Some(index) => sp.with_hi(sp.lo() + BytePos(index as _)),
1715+
None => sp,
1716+
};
1717+
let inner_sp = match ori_link.find('!') {
1718+
Some(index) => inner_sp.with_hi(inner_sp.lo() + BytePos(index as _)),
1719+
None => inner_sp,
1720+
};
1721+
let inner_sp = match ori_link.find('@') {
1722+
Some(index) => inner_sp.with_lo(inner_sp.lo() + BytePos(index as u32 + 1)),
1723+
None => inner_sp,
1724+
};
1725+
match self {
1726+
Self::Prefix(prefix) => {
1727+
// FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
1728+
let mut sugg = vec![(sp.with_hi(inner_sp.lo()), format!("{}@", prefix))];
1729+
if sp.hi() != inner_sp.hi() {
1730+
sugg.push((inner_sp.shrink_to_hi().with_hi(sp.hi()), String::new()));
1731+
}
1732+
sugg
1733+
}
1734+
Self::Function => {
1735+
let mut sugg = vec![(inner_sp.shrink_to_hi().with_hi(sp.hi()), "()".to_string())];
1736+
if sp.lo() != inner_sp.lo() {
1737+
sugg.push((inner_sp.shrink_to_lo().with_lo(sp.lo()), String::new()));
1738+
}
1739+
sugg
1740+
}
1741+
Self::Macro => {
1742+
let mut sugg = vec![(inner_sp.shrink_to_hi(), "!".to_string())];
1743+
if sp.lo() != inner_sp.lo() {
1744+
sugg.push((inner_sp.shrink_to_lo().with_lo(sp.lo()), String::new()));
1745+
}
1746+
sugg
1747+
}
1748+
Self::RemoveDisambiguator => return vec![(sp, path_str.into())],
1749+
}
1750+
}
17021751
}
17031752

17041753
/// Reports a diagnostic for an intra-doc link.
@@ -1732,7 +1781,16 @@ fn report_diagnostic(
17321781
tcx.struct_span_lint_hir(lint, hir_id, sp, |lint| {
17331782
let mut diag = lint.build(msg);
17341783

1735-
let span = super::source_span_for_markdown_range(tcx, dox, link_range, &item.attrs);
1784+
let span =
1785+
super::source_span_for_markdown_range(tcx, dox, link_range, &item.attrs).map(|sp| {
1786+
if dox.bytes().nth(link_range.start) == Some(b'`')
1787+
&& dox.bytes().nth(link_range.end - 1) == Some(b'`')
1788+
{
1789+
sp.with_lo(sp.lo() + BytePos(1)).with_hi(sp.hi() - BytePos(1))
1790+
} else {
1791+
sp
1792+
}
1793+
});
17361794

17371795
if let Some(sp) = span {
17381796
diag.set_span(sp);
@@ -1938,9 +1996,8 @@ fn resolution_failure(
19381996
disambiguator,
19391997
diag,
19401998
path_str,
1941-
diag_info.dox,
1999+
diag_info.ori_link,
19422000
sp,
1943-
&diag_info.link_range,
19442001
)
19452002
}
19462003

@@ -2007,7 +2064,7 @@ fn anchor_failure(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>, failure: A
20072064
if let Some((fragment_offset, _)) =
20082065
diag_info.ori_link.char_indices().filter(|(_, x)| *x == '#').nth(anchor_idx)
20092066
{
2010-
sp = sp.with_lo(sp.lo() + rustc_span::BytePos(fragment_offset as _));
2067+
sp = sp.with_lo(sp.lo() + BytePos(fragment_offset as _));
20112068
}
20122069
diag.span_label(sp, "invalid anchor");
20132070
}
@@ -2075,14 +2132,7 @@ fn ambiguity_error(
20752132

20762133
for res in candidates {
20772134
let disambiguator = Disambiguator::from_res(res);
2078-
suggest_disambiguator(
2079-
disambiguator,
2080-
diag,
2081-
path_str,
2082-
diag_info.dox,
2083-
sp,
2084-
&diag_info.link_range,
2085-
);
2135+
suggest_disambiguator(disambiguator, diag, path_str, diag_info.ori_link, sp);
20862136
}
20872137
});
20882138
}
@@ -2093,21 +2143,20 @@ fn suggest_disambiguator(
20932143
disambiguator: Disambiguator,
20942144
diag: &mut DiagnosticBuilder<'_>,
20952145
path_str: &str,
2096-
dox: &str,
2146+
ori_link: &str,
20972147
sp: Option<rustc_span::Span>,
2098-
link_range: &Range<usize>,
20992148
) {
21002149
let suggestion = disambiguator.suggestion();
21012150
let help = format!("to link to the {}, {}", disambiguator.descr(), suggestion.descr());
21022151

21032152
if let Some(sp) = sp {
2104-
let msg = if dox.bytes().nth(link_range.start) == Some(b'`') {
2105-
format!("`{}`", suggestion.as_help(path_str))
2153+
let mut spans = suggestion.as_help_span(path_str, ori_link, sp);
2154+
if spans.len() > 1 {
2155+
diag.multipart_suggestion(&help, spans, Applicability::MaybeIncorrect);
21062156
} else {
2107-
suggestion.as_help(path_str)
2108-
};
2109-
2110-
diag.span_suggestion(sp, &help, msg, Applicability::MaybeIncorrect);
2157+
let (sp, suggestion_text) = spans.pop().unwrap();
2158+
diag.span_suggestion_verbose(sp, &help, suggestion_text, Applicability::MaybeIncorrect);
2159+
}
21112160
} else {
21122161
diag.help(&format!("{}: {}", help, suggestion.as_help(path_str)));
21132162
}

src/test/rustdoc-ui/assoc-item-not-in-scope.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: unresolved link to `S::fmt`
2-
--> $DIR/assoc-item-not-in-scope.rs:4:14
2+
--> $DIR/assoc-item-not-in-scope.rs:4:15
33
|
44
LL | /// Link to [`S::fmt`]
5-
| ^^^^^^^^ the struct `S` has no field or associated item named `fmt`
5+
| ^^^^^^ the struct `S` has no field or associated item named `fmt`
66
|
77
note: the lint level is defined here
88
--> $DIR/assoc-item-not-in-scope.rs:1:9

src/test/rustdoc-ui/intra-doc/ambiguity.stderr

+19-19
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,26 @@ LL | #![deny(rustdoc::broken_intra_doc_links)]
1212
help: to link to the module, prefix with `mod@`
1313
|
1414
LL | /// [mod@true]
15-
| ^^^^^^^^
15+
| ^^^^
1616
help: to link to the builtin type, prefix with `prim@`
1717
|
1818
LL | /// [prim@true]
19-
| ^^^^^^^^^
19+
| ^^^^^
2020

2121
error: `ambiguous` is both a struct and a function
22-
--> $DIR/ambiguity.rs:27:6
22+
--> $DIR/ambiguity.rs:27:7
2323
|
2424
LL | /// [`ambiguous`] is ambiguous.
25-
| ^^^^^^^^^^^ ambiguous link
25+
| ^^^^^^^^^ ambiguous link
2626
|
2727
help: to link to the struct, prefix with `struct@`
2828
|
2929
LL | /// [`struct@ambiguous`] is ambiguous.
30-
| ^^^^^^^^^^^^^^^^^^
30+
| ^^^^^^^
3131
help: to link to the function, add parentheses
3232
|
3333
LL | /// [`ambiguous()`] is ambiguous.
34-
| ^^^^^^^^^^^^^
34+
| ^^
3535

3636
error: `ambiguous` is both a struct and a function
3737
--> $DIR/ambiguity.rs:29:6
@@ -42,30 +42,30 @@ LL | /// [ambiguous] is ambiguous.
4242
help: to link to the struct, prefix with `struct@`
4343
|
4444
LL | /// [struct@ambiguous] is ambiguous.
45-
| ^^^^^^^^^^^^^^^^
45+
| ^^^^^^^
4646
help: to link to the function, add parentheses
4747
|
4848
LL | /// [ambiguous()] is ambiguous.
49-
| ^^^^^^^^^^^
49+
| ^^
5050

5151
error: `multi_conflict` is a struct, a function, and a macro
52-
--> $DIR/ambiguity.rs:31:6
52+
--> $DIR/ambiguity.rs:31:7
5353
|
5454
LL | /// [`multi_conflict`] is a three-way conflict.
55-
| ^^^^^^^^^^^^^^^^ ambiguous link
55+
| ^^^^^^^^^^^^^^ ambiguous link
5656
|
5757
help: to link to the struct, prefix with `struct@`
5858
|
5959
LL | /// [`struct@multi_conflict`] is a three-way conflict.
60-
| ^^^^^^^^^^^^^^^^^^^^^^^
60+
| ^^^^^^^
6161
help: to link to the function, add parentheses
6262
|
6363
LL | /// [`multi_conflict()`] is a three-way conflict.
64-
| ^^^^^^^^^^^^^^^^^^
64+
| ^^
6565
help: to link to the macro, add an exclamation mark
6666
|
6767
LL | /// [`multi_conflict!`] is a three-way conflict.
68-
| ^^^^^^^^^^^^^^^^^
68+
| ^
6969

7070
error: `type_and_value` is both a module and a constant
7171
--> $DIR/ambiguity.rs:33:16
@@ -76,26 +76,26 @@ LL | /// Ambiguous [type_and_value].
7676
help: to link to the module, prefix with `mod@`
7777
|
7878
LL | /// Ambiguous [mod@type_and_value].
79-
| ^^^^^^^^^^^^^^^^^^
79+
| ^^^^
8080
help: to link to the constant, prefix with `const@`
8181
|
8282
LL | /// Ambiguous [const@type_and_value].
83-
| ^^^^^^^^^^^^^^^^^^^^
83+
| ^^^^^^
8484

8585
error: `foo::bar` is both an enum and a function
86-
--> $DIR/ambiguity.rs:35:42
86+
--> $DIR/ambiguity.rs:35:43
8787
|
8888
LL | /// Ambiguous non-implied shortcut link [`foo::bar`].
89-
| ^^^^^^^^^^ ambiguous link
89+
| ^^^^^^^^ ambiguous link
9090
|
9191
help: to link to the enum, prefix with `enum@`
9292
|
9393
LL | /// Ambiguous non-implied shortcut link [`enum@foo::bar`].
94-
| ^^^^^^^^^^^^^^^
94+
| ^^^^^
9595
help: to link to the function, add parentheses
9696
|
9797
LL | /// Ambiguous non-implied shortcut link [`foo::bar()`].
98-
| ^^^^^^^^^^^^
98+
| ^^
9999

100100
error: aborting due to 6 previous errors
101101

src/test/rustdoc-ui/intra-doc/disambiguator-mismatch.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#![deny(rustdoc::broken_intra_doc_links)]
22
//~^ NOTE lint level is defined
33
pub enum S {}
4+
fn S() {}
45

6+
#[macro_export]
57
macro_rules! m {
68
() => {};
79
}
@@ -41,6 +43,12 @@ trait T {}
4143
//~| NOTE this link resolved
4244
//~| HELP add an exclamation mark
4345

46+
/// Link to [m()]
47+
//~^ ERROR unresolved link to `m`
48+
//~| NOTE this link resolves to the macro `m`
49+
//~| HELP add an exclamation mark
50+
/// and to [m!()]
51+
4452
/// Link to [const@s]
4553
//~^ ERROR incompatible link kind for `s`
4654
//~| NOTE this link resolved

0 commit comments

Comments
 (0)