Skip to content

Commit 42bed03

Browse files
committed
Pass on the DefId so rustdoc can name it in suggestions
Look at this beauty: ```rust error: unresolved link to `S::h` --> intra-link-errors.rs:51:6 | 51 | /// [type@S::h] | ^^^^^^^^^ help: to link to the associated function, use its disambiguator: `S::h()` | = note: this link resolves to the associated function `h`, which is not in the type namespace ```
1 parent fcb2199 commit 42bed03

File tree

3 files changed

+52
-34
lines changed

3 files changed

+52
-34
lines changed

src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ struct LinkCollector<'a, 'tcx> {
121121
/// This is used to store the kind of associated items,
122122
/// because `clean` and the disambiguator code expect them to be different.
123123
/// See the code for associated items on inherent impls for details.
124-
kind_side_channel: Cell<Option<DefKind>>,
124+
kind_side_channel: Cell<Option<(DefKind, DefId)>>,
125125
}
126126

127127
impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
@@ -381,7 +381,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
381381
) => {
382382
debug!("looking for associated item named {} for item {:?}", item_name, did);
383383
// Checks if item_name belongs to `impl SomeItem`
384-
let kind = cx
384+
let assoc_item = cx
385385
.tcx
386386
.inherent_impls(did)
387387
.iter()
@@ -393,7 +393,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
393393
imp,
394394
)
395395
})
396-
.map(|item| item.kind)
396+
.map(|item| (item.kind, item.def_id))
397397
// There should only ever be one associated item that matches from any inherent impl
398398
.next()
399399
// Check if item_name belongs to `impl SomeTrait for SomeItem`
@@ -409,7 +409,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
409409
kind
410410
});
411411

412-
if let Some(kind) = kind {
412+
if let Some((kind, id)) = assoc_item {
413413
let out = match kind {
414414
ty::AssocKind::Fn => "method",
415415
ty::AssocKind::Const => "associatedconstant",
@@ -425,7 +425,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
425425
// HACK(jynelson): `clean` expects the type, not the associated item.
426426
// but the disambiguator logic expects the associated item.
427427
// Store the kind in a side channel so that only the disambiguator logic looks at it.
428-
self.kind_side_channel.set(Some(kind.as_def_kind()));
428+
self.kind_side_channel.set(Some((kind.as_def_kind(), id)));
429429
Ok((ty_res, Some(format!("{}.{}", out, item_name))))
430430
})
431431
} else if ns == Namespace::ValueNS {
@@ -525,7 +525,7 @@ fn resolve_associated_trait_item(
525525
item_name: Symbol,
526526
ns: Namespace,
527527
cx: &DocContext<'_>,
528-
) -> Option<ty::AssocKind> {
528+
) -> Option<(ty::AssocKind, DefId)> {
529529
let ty = cx.tcx.type_of(did);
530530
// First consider automatic impls: `impl From<T> for T`
531531
let implicit_impls = crate::clean::get_auto_trait_and_blanket_impls(cx, ty, did);
@@ -553,7 +553,7 @@ fn resolve_associated_trait_item(
553553
// but provided methods come directly from `tcx`.
554554
// Fortunately, we don't need the whole method, we just need to know
555555
// what kind of associated item it is.
556-
Some((assoc.def_id, kind))
556+
Some((kind, assoc.def_id))
557557
});
558558
let assoc = items.next();
559559
debug_assert_eq!(items.count(), 0);
@@ -575,7 +575,7 @@ fn resolve_associated_trait_item(
575575
ns,
576576
trait_,
577577
)
578-
.map(|assoc| (assoc.def_id, assoc.kind))
578+
.map(|assoc| (assoc.kind, assoc.def_id))
579579
}
580580
}
581581
_ => panic!("get_impls returned something that wasn't an impl"),
@@ -592,12 +592,12 @@ fn resolve_associated_trait_item(
592592
cx.tcx
593593
.associated_items(trait_)
594594
.find_by_name_and_namespace(cx.tcx, Ident::with_dummy_span(item_name), ns, trait_)
595-
.map(|assoc| (assoc.def_id, assoc.kind))
595+
.map(|assoc| (assoc.kind, assoc.def_id))
596596
}));
597597
}
598598
// FIXME: warn about ambiguity
599599
debug!("the candidates were {:?}", candidates);
600-
candidates.pop().map(|(_, kind)| kind)
600+
candidates.pop()
601601
}
602602

603603
/// Given a type, return all traits in scope in `module` implemented by that type.
@@ -851,18 +851,21 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
851851

852852
// used for reporting better errors
853853
let check_full_res = |this: &mut Self, ns| {
854-
match this.resolve(path_str, ns, &current_item, base_node, &extra_fragment) {
855-
Ok(res) => {
856-
debug!(
857-
"check_full_res: saw res for {} in {:?} ns: {:?}",
858-
path_str, ns, res.0
859-
);
860-
Some(res.0)
861-
}
862-
Err(ErrorKind::Resolve(kind)) => kind.full_res(),
863-
// TODO: add `Res` to AnchorFailure
864-
Err(ErrorKind::AnchorFailure(_)) => None,
865-
}
854+
let res =
855+
match this.resolve(path_str, ns, &current_item, base_node, &extra_fragment)
856+
{
857+
Ok(res) => {
858+
debug!(
859+
"check_full_res: saw res for {} in {:?} ns: {:?}",
860+
path_str, ns, res.0
861+
);
862+
Some(res.0)
863+
}
864+
Err(ErrorKind::Resolve(kind)) => kind.full_res(),
865+
// TODO: add `Res` to AnchorFailure
866+
Err(ErrorKind::AnchorFailure(_)) => None,
867+
};
868+
this.kind_side_channel.take().map(|(kind, id)| Res::Def(kind, id)).or(res)
866869
};
867870

868871
match disambiguator.map(Disambiguator::ns) {
@@ -876,7 +879,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
876879
if kind.full_res().is_none() {
877880
let other_ns = if ns == ValueNS { TypeNS } else { ValueNS };
878881
if let Some(res) = check_full_res(self, other_ns) {
879-
kind = ResolutionFailure::WrongNamespace(res, other_ns);
882+
// recall that this stores the _expected_ namespace
883+
kind = ResolutionFailure::WrongNamespace(res, ns);
880884
}
881885
}
882886
resolution_failure(
@@ -1092,7 +1096,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
10921096
// Disallow e.g. linking to enums with `struct@`
10931097
if let Res::Def(kind, _) = res {
10941098
debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator);
1095-
match (self.kind_side_channel.take().unwrap_or(kind), disambiguator) {
1099+
match (self.kind_side_channel.take().map(|(kind, _)| kind).unwrap_or(kind), disambiguator) {
10961100
| (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const)))
10971101
// NOTE: this allows 'method' to mean both normal functions and associated functions
10981102
// This can't cause ambiguity because both are in the same namespace.

src/test/rustdoc-ui/intra-link-errors.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,18 @@ pub struct S;
4949
pub enum E { A, B, C }
5050

5151
/// [type@S::h]
52+
//~^ ERROR unresolved link
53+
//~| HELP to link to the associated function
54+
//~| NOTE not in the type namespace
5255
impl S {
5356
pub fn h() {}
5457
}
5558

5659
/// [type@T::g]
60+
//~^ ERROR unresolved link
61+
//~| HELP to link to the associated function
62+
//~| NOTE not in the type namespace
63+
5764
/// [T::h!]
5865
pub trait T {
5966
fn g() {}

src/test/rustdoc-ui/intra-link-errors.stderr

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,27 +80,34 @@ error: unresolved link to `S`
8080
--> $DIR/intra-link-errors.rs:41:6
8181
|
8282
LL | /// [S!]
83-
| ^^ help: to link to the unit struct, use its disambiguator: `value@S`
83+
| ^^ help: to link to the struct, use its disambiguator: `struct@S`
8484
|
85-
= note: this link resolves to the unit struct `S`, which is not in the macro namespace
85+
= note: this link resolves to the struct `S`, which is not in the macro namespace
8686

8787
error: unresolved link to `T::g`
88-
--> $DIR/intra-link-errors.rs:56:6
88+
--> $DIR/intra-link-errors.rs:59:6
8989
|
9090
LL | /// [type@T::g]
91-
| ^^^^^^^^^
91+
| ^^^^^^^^^ help: to link to the associated function, use its disambiguator: `T::g()`
9292
|
93-
= note: this link partially resolves to the trait `T`
94-
= note: `T` has no field, variant, or associated item named `g`
93+
= note: this link resolves to the associated function `g`, which is not in the type namespace
94+
95+
error: unresolved link to `T::h`
96+
--> $DIR/intra-link-errors.rs:64:6
97+
|
98+
LL | /// [T::h!]
99+
| ^^^^^
100+
|
101+
= note: no item named `T::h` is in scope
102+
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
95103

96104
error: unresolved link to `S::h`
97105
--> $DIR/intra-link-errors.rs:51:6
98106
|
99107
LL | /// [type@S::h]
100-
| ^^^^^^^^^
108+
| ^^^^^^^^^ help: to link to the associated function, use its disambiguator: `S::h()`
101109
|
102-
= note: this link partially resolves to the struct `S`
103-
= note: `S` has no field, variant, or associated item named `h`
110+
= note: this link resolves to the associated function `h`, which is not in the type namespace
104111

105-
error: aborting due to 11 previous errors
112+
error: aborting due to 12 previous errors
106113

0 commit comments

Comments
 (0)