Skip to content

Commit af83c21

Browse files
committed
rustdoc: correctly resolve link disambiguators through ctors
1 parent 9ff5fc4 commit af83c21

File tree

5 files changed

+123
-1
lines changed

5 files changed

+123
-1
lines changed

src/librustdoc/clean/utils.rs

+17
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ pub(crate) fn synthesize_auto_trait_and_blanket_impls(
503503
/// [`href()`]: crate::html::format::href
504504
pub(crate) fn register_res(cx: &mut DocContext<'_>, res: Res) -> DefId {
505505
use DefKind::*;
506+
use rustc_hir::def::CtorOf;
506507
debug!("register_res({res:?})");
507508

508509
let (kind, did) = match res {
@@ -526,6 +527,22 @@ pub(crate) fn register_res(cx: &mut DocContext<'_>, res: Res) -> DefId {
526527
did,
527528
) => (kind.into(), did),
528529

530+
// Tuple and unit-like structs and enums exist in both type and
531+
// value ns. The item that exists in value ns is the "constructor,"
532+
// a special function or const that returns the constructed value.
533+
//
534+
// It's possible to make an intra-doc link that points at the
535+
// constructor, but it's not actually documented separately from
536+
// the type itself.
537+
Res::Def(Ctor(ctor_of, _), did) => (
538+
match ctor_of {
539+
CtorOf::Struct => Struct,
540+
CtorOf::Variant => Variant,
541+
}
542+
.into(),
543+
cx.tcx.parent(did),
544+
),
545+
529546
_ => panic!("register_res: unexpected {res:?}"),
530547
};
531548
if did.is_local() {

src/librustdoc/passes/collect_intra_doc_links.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1313
use rustc_data_structures::intern::Interned;
1414
use rustc_errors::{Applicability, Diag, DiagMessage};
1515
use rustc_hir::def::Namespace::*;
16-
use rustc_hir::def::{DefKind, Namespace, PerNS};
16+
use rustc_hir::def::{CtorOf, DefKind, Namespace, PerNS};
1717
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
1818
use rustc_hir::{Mutability, Safety};
1919
use rustc_middle::ty::{Ty, TyCtxt};
@@ -428,6 +428,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
428428
) => {
429429
vec![(Res::from_def_id(self.cx.tcx, self.cx.tcx.parent(def_id)), Some(def_id))]
430430
}
431+
Res::Def(DefKind::Ctor(CtorOf::Variant, _), def_id) => {
432+
let def_id = self.cx.tcx.parent(def_id);
433+
vec![(Res::from_def_id(self.cx.tcx, self.cx.tcx.parent(def_id)), Some(def_id))]
434+
}
435+
Res::Def(DefKind::Ctor(CtorOf::Struct, _), def_id) => {
436+
let def_id = self.cx.tcx.parent(def_id);
437+
vec![(Res::from_def_id(self.cx.tcx, def_id), None)]
438+
}
431439
_ => vec![(res, None)],
432440
});
433441
} else if ns == MacroNS {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// https://github.com/rust-lang/rust/issues/130591
2+
#![deny(rustdoc::broken_intra_doc_links)]
3+
#![crate_name = "foo"]
4+
5+
pub enum MyEnum {
6+
Internals,
7+
}
8+
9+
pub use MyEnum::*;
10+
11+
/// [Internals] //~ERROR `Internals` is both
12+
pub struct Internals {
13+
foo: (),
14+
}
15+
16+
pub mod inside {
17+
pub struct Internals2;
18+
}
19+
20+
use inside::*;
21+
22+
/// [Internals2] //~ERROR `Internals2` is both
23+
pub enum Internals2 {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
error: `Internals` is both a variant and a struct
2+
--> $DIR/value-ctor-disambig.rs:11:6
3+
|
4+
LL | /// [Internals]
5+
| ^^^^^^^^^ ambiguous link
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/value-ctor-disambig.rs:2:9
9+
|
10+
LL | #![deny(rustdoc::broken_intra_doc_links)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
help: to link to the variant, prefix with `variant@`
13+
|
14+
LL | /// [variant@Internals]
15+
| ++++++++
16+
help: to link to the struct, prefix with `struct@`
17+
|
18+
LL | /// [struct@Internals]
19+
| +++++++
20+
21+
error: `Internals2` is both a struct and an enum
22+
--> $DIR/value-ctor-disambig.rs:22:6
23+
|
24+
LL | /// [Internals2]
25+
| ^^^^^^^^^^ ambiguous link
26+
|
27+
help: to link to the struct, prefix with `struct@`
28+
|
29+
LL | /// [struct@Internals2]
30+
| +++++++
31+
help: to link to the enum, prefix with `enum@`
32+
|
33+
LL | /// [enum@Internals2]
34+
| +++++
35+
36+
error: aborting due to 2 previous errors
37+

tests/rustdoc/intra-doc/value-ctor.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// https://github.com/rust-lang/rust/issues/130591
2+
#![deny(rustdoc::broken_intra_doc_links)]
3+
#![crate_name = "foo"]
4+
5+
/// [value@Foo::X]
6+
//@ has foo/enum.Foo.html '//a[@href="enum.Foo.html#variant.X"]' 'Foo::X'
7+
pub enum Foo {
8+
X,
9+
}
10+
11+
/// [tst][value@MyStruct]
12+
//@ has foo/struct.MyStruct.html '//a[@href="struct.MyStruct.html"]' 'tst'
13+
pub struct MyStruct;
14+
15+
pub enum MyEnum {
16+
Internals,
17+
}
18+
19+
pub use MyEnum::*;
20+
21+
/// In this context, [a][type@Internals] is a struct, while [b][value@Internals] is an enum variant.
22+
//@ has foo/struct.Internals.html '//a[@href="struct.Internals.html"]' 'a'
23+
//@ has foo/struct.Internals.html '//a[@href="enum.MyEnum.html#variant.Internals"]' 'b'
24+
pub struct Internals {
25+
foo: (),
26+
}
27+
28+
pub mod inside {
29+
pub struct Internals2;
30+
}
31+
32+
use inside::*;
33+
34+
/// In this context, [a][type@Internals2] is an enum, while [b][value@Internals2] is a struct.
35+
//@ has foo/enum.Internals2.html '//a[@href="enum.Internals2.html"]' 'a'
36+
//@ has foo/enum.Internals2.html '//a[@href="inside/struct.Internals2.html"]' 'b'
37+
pub enum Internals2 {}

0 commit comments

Comments
 (0)