Skip to content

Commit 1dc748f

Browse files
committed
Auto merge of #75916 - jyn514:unify-error-reporting, r=euclio
Unify error reporting for intra-doc links - Give a suggestion even if there is no span available - Give a more accurate description of the change than 'use the disambiguator' - Write much less code Closes #75836. r? @euclio cc @pickfire - this gets rid of 'disambiguator' like you suggested in #75079 (comment).
2 parents d8424f6 + d3c5817 commit 1dc748f

7 files changed

+154
-116
lines changed

Diff for: src/librustc_hir/def.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -451,13 +451,19 @@ impl<Id> Res<Id> {
451451
}
452452
}
453453

454-
pub fn matches_ns(&self, ns: Namespace) -> bool {
454+
/// Returns `None` if this is `Res::Err`
455+
pub fn ns(&self) -> Option<Namespace> {
455456
match self {
456-
Res::Def(kind, ..) => kind.ns() == Some(ns),
457-
Res::PrimTy(..) | Res::SelfTy(..) | Res::ToolMod => ns == Namespace::TypeNS,
458-
Res::SelfCtor(..) | Res::Local(..) => ns == Namespace::ValueNS,
459-
Res::NonMacroAttr(..) => ns == Namespace::MacroNS,
460-
Res::Err => true,
457+
Res::Def(kind, ..) => kind.ns(),
458+
Res::PrimTy(..) | Res::SelfTy(..) | Res::ToolMod => Some(Namespace::TypeNS),
459+
Res::SelfCtor(..) | Res::Local(..) => Some(Namespace::ValueNS),
460+
Res::NonMacroAttr(..) => Some(Namespace::MacroNS),
461+
Res::Err => None,
461462
}
462463
}
464+
465+
/// Always returns `true` if `self` is `Res::Err`
466+
pub fn matches_ns(&self, ns: Namespace) -> bool {
467+
self.ns().map_or(true, |actual_ns| actual_ns == ns)
468+
}
463469
}

Diff for: src/librustdoc/passes/collect_intra_doc_links.rs

+56-68
Original file line numberDiff line numberDiff line change
@@ -829,16 +829,13 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
829829
}
830830
let candidates =
831831
candidates.map(|candidate| candidate.map(|(res, _)| res));
832-
let candidates = [TypeNS, ValueNS, MacroNS]
833-
.iter()
834-
.filter_map(|&ns| candidates[ns].map(|res| (res, ns)));
835832
ambiguity_error(
836833
cx,
837834
&item,
838835
path_str,
839836
&dox,
840837
link_range,
841-
candidates.collect(),
838+
candidates.present_items().collect(),
842839
);
843840
continue;
844841
}
@@ -880,7 +877,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
880877
fragment = Some(path.to_owned());
881878
} else {
882879
// `[char]` when a `char` module is in scope
883-
let candidates = vec![(res, TypeNS), (prim, TypeNS)];
880+
let candidates = vec![res, prim];
884881
ambiguity_error(cx, &item, path_str, &dox, link_range, candidates);
885882
continue;
886883
}
@@ -898,20 +895,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
898895
specified.article(),
899896
specified.descr()
900897
);
901-
let suggestion = resolved.display_for(path_str);
902-
let help_msg =
903-
format!("to link to the {}, use its disambiguator", resolved.descr());
904898
diag.note(&note);
905-
if let Some(sp) = sp {
906-
diag.span_suggestion(
907-
sp,
908-
&help_msg,
909-
suggestion,
910-
Applicability::MaybeIncorrect,
911-
);
912-
} else {
913-
diag.help(&format!("{}: {}", help_msg, suggestion));
914-
}
899+
suggest_disambiguator(resolved, diag, path_str, &dox, sp, &link_range);
915900
});
916901
};
917902
if let Res::PrimTy(_) = res {
@@ -1047,17 +1032,32 @@ impl Disambiguator {
10471032
}
10481033
}
10491034

1050-
fn display_for(self, path_str: &str) -> String {
1035+
/// WARNING: panics on `Res::Err`
1036+
fn from_res(res: Res) -> Self {
1037+
match res {
1038+
Res::Def(kind, _) => Disambiguator::Kind(kind),
1039+
Res::PrimTy(_) => Disambiguator::Primitive,
1040+
_ => Disambiguator::Namespace(res.ns().expect("can't call `from_res` on Res::err")),
1041+
}
1042+
}
1043+
1044+
/// Return (description of the change, suggestion)
1045+
fn display_for(self, path_str: &str) -> (&'static str, String) {
1046+
const PREFIX: &str = "prefix with the item kind";
1047+
const FUNCTION: &str = "add parentheses";
1048+
const MACRO: &str = "add an exclamation mark";
1049+
10511050
let kind = match self {
1052-
Disambiguator::Primitive => return format!("prim@{}", path_str),
1051+
Disambiguator::Primitive => return (PREFIX, format!("prim@{}", path_str)),
10531052
Disambiguator::Kind(kind) => kind,
10541053
Disambiguator::Namespace(_) => panic!("display_for cannot be used on namespaces"),
10551054
};
10561055
if kind == DefKind::Macro(MacroKind::Bang) {
1057-
return format!("{}!", path_str);
1056+
return (MACRO, format!("{}!", path_str));
10581057
} else if kind == DefKind::Fn || kind == DefKind::AssocFn {
1059-
return format!("{}()", path_str);
1058+
return (FUNCTION, format!("{}()", path_str));
10601059
}
1060+
10611061
let prefix = match kind {
10621062
DefKind::Struct => "struct",
10631063
DefKind::Enum => "enum",
@@ -1079,7 +1079,9 @@ impl Disambiguator {
10791079
Namespace::MacroNS => "macro",
10801080
},
10811081
};
1082-
format!("{}@{}", prefix, path_str)
1082+
1083+
// FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
1084+
(PREFIX, format!("{}@{}", prefix, path_str))
10831085
}
10841086

10851087
fn ns(self) -> Namespace {
@@ -1247,12 +1249,12 @@ fn ambiguity_error(
12471249
path_str: &str,
12481250
dox: &str,
12491251
link_range: Option<Range<usize>>,
1250-
candidates: Vec<(Res, Namespace)>,
1252+
candidates: Vec<Res>,
12511253
) {
12521254
let mut msg = format!("`{}` is ", path_str);
12531255

12541256
match candidates.as_slice() {
1255-
[(first_def, _), (second_def, _)] => {
1257+
[first_def, second_def] => {
12561258
msg += &format!(
12571259
"both {} {} and {} {}",
12581260
first_def.article(),
@@ -1263,7 +1265,7 @@ fn ambiguity_error(
12631265
}
12641266
_ => {
12651267
let mut candidates = candidates.iter().peekable();
1266-
while let Some((res, _)) = candidates.next() {
1268+
while let Some(res) = candidates.next() {
12671269
if candidates.peek().is_some() {
12681270
msg += &format!("{} {}, ", res.article(), res.descr());
12691271
} else {
@@ -1276,52 +1278,38 @@ fn ambiguity_error(
12761278
report_diagnostic(cx, &msg, item, dox, link_range.clone(), |diag, sp| {
12771279
if let Some(sp) = sp {
12781280
diag.span_label(sp, "ambiguous link");
1281+
} else {
1282+
diag.note("ambiguous link");
1283+
}
12791284

1280-
let link_range = link_range.expect("must have a link range if we have a span");
1281-
1282-
for (res, ns) in candidates {
1283-
let (action, mut suggestion) = match res {
1284-
Res::Def(DefKind::AssocFn | DefKind::Fn, _) => {
1285-
("add parentheses", format!("{}()", path_str))
1286-
}
1287-
Res::Def(DefKind::Macro(MacroKind::Bang), _) => {
1288-
("add an exclamation mark", format!("{}!", path_str))
1289-
}
1290-
_ => {
1291-
let type_ = match (res, ns) {
1292-
(Res::PrimTy(_), _) => "prim",
1293-
(Res::Def(DefKind::Const, _), _) => "const",
1294-
(Res::Def(DefKind::Static, _), _) => "static",
1295-
(Res::Def(DefKind::Struct, _), _) => "struct",
1296-
(Res::Def(DefKind::Enum, _), _) => "enum",
1297-
(Res::Def(DefKind::Union, _), _) => "union",
1298-
(Res::Def(DefKind::Trait, _), _) => "trait",
1299-
(Res::Def(DefKind::Mod, _), _) => "module",
1300-
(_, TypeNS) => "type",
1301-
(_, ValueNS) => "value",
1302-
(Res::Def(DefKind::Macro(MacroKind::Derive), _), MacroNS) => "derive",
1303-
(_, MacroNS) => "macro",
1304-
};
1305-
1306-
// FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
1307-
("prefix with the item type", format!("{}@{}", type_, path_str))
1308-
}
1309-
};
1285+
for res in candidates {
1286+
let disambiguator = Disambiguator::from_res(res);
1287+
suggest_disambiguator(disambiguator, diag, path_str, dox, sp, &link_range);
1288+
}
1289+
});
1290+
}
13101291

1311-
if dox.bytes().nth(link_range.start) == Some(b'`') {
1312-
suggestion = format!("`{}`", suggestion);
1313-
}
1292+
fn suggest_disambiguator(
1293+
disambiguator: Disambiguator,
1294+
diag: &mut DiagnosticBuilder<'_>,
1295+
path_str: &str,
1296+
dox: &str,
1297+
sp: Option<rustc_span::Span>,
1298+
link_range: &Option<Range<usize>>,
1299+
) {
1300+
let (action, mut suggestion) = disambiguator.display_for(path_str);
1301+
let help = format!("to link to the {}, {}", disambiguator.descr(), action);
13141302

1315-
// FIXME: Create a version of this suggestion for when we don't have the span.
1316-
diag.span_suggestion(
1317-
sp,
1318-
&format!("to link to the {}, {}", res.descr(), action),
1319-
suggestion,
1320-
Applicability::MaybeIncorrect,
1321-
);
1322-
}
1303+
if let Some(sp) = sp {
1304+
let link_range = link_range.as_ref().expect("must have a link range if we have a span");
1305+
if dox.bytes().nth(link_range.start) == Some(b'`') {
1306+
suggestion = format!("`{}`", suggestion);
13231307
}
1324-
});
1308+
1309+
diag.span_suggestion(sp, &help, suggestion, Applicability::MaybeIncorrect);
1310+
} else {
1311+
diag.help(&format!("{}: {}", help, suggestion));
1312+
}
13251313
}
13261314

13271315
fn privacy_error(

Diff for: src/test/rustdoc-ui/intra-link-prim-conflict.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
1919
/// [struct@char]
2020
//~^ ERROR incompatible link
21-
//~| HELP use its disambiguator
21+
//~| HELP prefix with the item kind
2222
//~| NOTE resolved to a module
2323
pub mod char {}
2424

2525
pub mod inner {
2626
//! [struct@char]
2727
//~^ ERROR incompatible link
28-
//~| HELP use its disambiguator
28+
//~| HELP prefix with the item kind
2929
//~| NOTE resolved to a builtin type
3030
}

Diff for: src/test/rustdoc-ui/intra-link-prim-conflict.stderr

+18-10
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ note: the lint level is defined here
99
|
1010
LL | #![deny(broken_intra_doc_links)]
1111
| ^^^^^^^^^^^^^^^^^^^^^^
12-
help: to link to the module, prefix with the item type
12+
help: to link to the module, prefix with the item kind
1313
|
14-
LL | /// [module@char]
15-
| ^^^^^^^^^^^
16-
help: to link to the builtin type, prefix with the item type
14+
LL | /// [mod@char]
15+
| ^^^^^^^^
16+
help: to link to the builtin type, prefix with the item kind
1717
|
1818
LL | /// [prim@char]
1919
| ^^^^^^^^^
@@ -24,11 +24,11 @@ error: `char` is both a module and a builtin type
2424
LL | /// [type@char]
2525
| ^^^^^^^^^ ambiguous link
2626
|
27-
help: to link to the module, prefix with the item type
27+
help: to link to the module, prefix with the item kind
2828
|
29-
LL | /// [module@char]
30-
| ^^^^^^^^^^^
31-
help: to link to the builtin type, prefix with the item type
29+
LL | /// [mod@char]
30+
| ^^^^^^^^
31+
help: to link to the builtin type, prefix with the item kind
3232
|
3333
LL | /// [prim@char]
3434
| ^^^^^^^^^
@@ -37,17 +37,25 @@ error: incompatible link kind for `char`
3737
--> $DIR/intra-link-prim-conflict.rs:19:6
3838
|
3939
LL | /// [struct@char]
40-
| ^^^^^^^^^^^ help: to link to the module, use its disambiguator: `mod@char`
40+
| ^^^^^^^^^^^
4141
|
4242
= note: this link resolved to a module, which is not a struct
43+
help: to link to the module, prefix with the item kind
44+
|
45+
LL | /// [mod@char]
46+
| ^^^^^^^^
4347

4448
error: incompatible link kind for `char`
4549
--> $DIR/intra-link-prim-conflict.rs:26:10
4650
|
4751
LL | //! [struct@char]
48-
| ^^^^^^^^^^^ help: to link to the builtin type, use its disambiguator: `prim@char`
52+
| ^^^^^^^^^^^
4953
|
5054
= note: this link resolved to a builtin type, which is not a struct
55+
help: to link to the builtin type, prefix with the item kind
56+
|
57+
LL | //! [prim@char]
58+
| ^^^^^^^^^
5159

5260
error: aborting due to 4 previous errors
5361

Diff for: src/test/rustdoc-ui/intra-links-ambiguity.stderr

+8-8
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ note: the lint level is defined here
99
|
1010
LL | #![deny(broken_intra_doc_links)]
1111
| ^^^^^^^^^^^^^^^^^^^^^^
12-
help: to link to the struct, prefix with the item type
12+
help: to link to the struct, prefix with the item kind
1313
|
1414
LL | /// [`struct@ambiguous`] is ambiguous.
1515
| ^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ error: `ambiguous` is both a struct and a function
2424
LL | /// [ambiguous] is ambiguous.
2525
| ^^^^^^^^^ ambiguous link
2626
|
27-
help: to link to the struct, prefix with the item type
27+
help: to link to the struct, prefix with the item kind
2828
|
2929
LL | /// [struct@ambiguous] is ambiguous.
3030
| ^^^^^^^^^^^^^^^^
@@ -39,7 +39,7 @@ error: `multi_conflict` is a struct, a function, and a macro
3939
LL | /// [`multi_conflict`] is a three-way conflict.
4040
| ^^^^^^^^^^^^^^^^ ambiguous link
4141
|
42-
help: to link to the struct, prefix with the item type
42+
help: to link to the struct, prefix with the item kind
4343
|
4444
LL | /// [`struct@multi_conflict`] is a three-way conflict.
4545
| ^^^^^^^^^^^^^^^^^^^^^^^
@@ -58,11 +58,11 @@ error: `type_and_value` is both a module and a constant
5858
LL | /// Ambiguous [type_and_value].
5959
| ^^^^^^^^^^^^^^ ambiguous link
6060
|
61-
help: to link to the module, prefix with the item type
61+
help: to link to the module, prefix with the item kind
6262
|
63-
LL | /// Ambiguous [module@type_and_value].
64-
| ^^^^^^^^^^^^^^^^^^^^^
65-
help: to link to the constant, prefix with the item type
63+
LL | /// Ambiguous [mod@type_and_value].
64+
| ^^^^^^^^^^^^^^^^^^
65+
help: to link to the constant, prefix with the item kind
6666
|
6767
LL | /// Ambiguous [const@type_and_value].
6868
| ^^^^^^^^^^^^^^^^^^^^
@@ -73,7 +73,7 @@ error: `foo::bar` is both an enum and a function
7373
LL | /// Ambiguous non-implied shortcut link [`foo::bar`].
7474
| ^^^^^^^^^^ ambiguous link
7575
|
76-
help: to link to the enum, prefix with the item type
76+
help: to link to the enum, prefix with the item kind
7777
|
7878
LL | /// Ambiguous non-implied shortcut link [`enum@foo::bar`].
7979
| ^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)