Skip to content

Commit 2b618ed

Browse files
committed
rustdoc: Correctly resolve variant and struct fields on alias
1 parent 2c98517 commit 2b618ed

File tree

3 files changed

+113
-36
lines changed

3 files changed

+113
-36
lines changed

src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ fn full_res(tcx: TyCtxt<'_>, (base, assoc_item): (Res, Option<DefId>)) -> Res {
462462
}
463463

464464
/// Given a primitive type, try to resolve an associated item.
465-
fn resolve_primitive_associated_item<'tcx>(
465+
fn resolve_primitive_inherent_assoc_item<'tcx>(
466466
tcx: TyCtxt<'tcx>,
467467
prim_ty: PrimitiveType,
468468
ns: Namespace,
@@ -597,33 +597,30 @@ fn resolve_associated_item<'tcx>(
597597
let item_ident = Ident::with_dummy_span(item_name);
598598

599599
match root_res {
600-
Res::Primitive(prim) => {
601-
let items = resolve_primitive_associated_item(tcx, prim, ns, item_ident);
602-
if !items.is_empty() {
603-
items
604-
// Inherent associated items take precedence over items that come from trait impls.
605-
} else {
606-
primitive_type_to_ty(tcx, prim)
607-
.map(|ty| {
608-
resolve_associated_trait_item(ty, module_id, item_ident, ns, tcx)
609-
.iter()
610-
.map(|item| (root_res, item.def_id))
611-
.collect::<Vec<_>>()
612-
})
613-
.unwrap_or_default()
614-
}
615-
}
616-
Res::Def(DefKind::TyAlias, did) => {
600+
Res::Def(DefKind::TyAlias, alias_did) => {
617601
// Resolve the link on the type the alias points to.
618602
// FIXME: if the associated item is defined directly on the type alias,
619603
// it will show up on its documentation page, we should link there instead.
620-
let Some(res) = ty_to_res(tcx, tcx.type_of(did).instantiate_identity()) else {
621-
return Vec::new();
604+
let Some(aliased_res) = ty_to_res(tcx, tcx.type_of(alias_did).instantiate_identity())
605+
else {
606+
return vec![];
622607
};
623-
resolve_associated_item(tcx, res, item_name, ns, disambiguator, module_id)
608+
let aliased_items =
609+
resolve_associated_item(tcx, aliased_res, item_name, ns, disambiguator, module_id);
610+
aliased_items
611+
.into_iter()
612+
.map(|(res, assoc_did)| {
613+
if is_assoc_item_on_alias_page(tcx, assoc_did) {
614+
(root_res, assoc_did)
615+
} else {
616+
(res, assoc_did)
617+
}
618+
})
619+
.collect()
624620
}
621+
Res::Primitive(prim) => resolve_assoc_on_primitive(tcx, prim, ns, item_ident, module_id),
625622
Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, did) => {
626-
resolve_assoc_on_adt(tcx, did, item_name, ns, disambiguator, module_id)
623+
resolve_assoc_on_adt(tcx, did, item_ident, ns, disambiguator, module_id)
627624
}
628625
Res::Def(DefKind::ForeignTy, did) => {
629626
resolve_assoc_on_simple_type(tcx, did, item_ident, ns, module_id)
@@ -640,23 +637,56 @@ fn resolve_associated_item<'tcx>(
640637
}
641638
}
642639

640+
// FIXME: make this fully complete by also including ALL inherent impls
641+
// and trait impls BUT ONLY if on alias directly
642+
fn is_assoc_item_on_alias_page<'tcx>(tcx: TyCtxt<'tcx>, assoc_did: DefId) -> bool {
643+
match tcx.def_kind(assoc_did) {
644+
// Variants and fields always have docs on the alias page.
645+
DefKind::Variant | DefKind::Field => true,
646+
_ => false,
647+
}
648+
}
649+
650+
fn resolve_assoc_on_primitive<'tcx>(
651+
tcx: TyCtxt<'tcx>,
652+
prim: PrimitiveType,
653+
ns: Namespace,
654+
item_ident: Ident,
655+
module_id: DefId,
656+
) -> Vec<(Res, DefId)> {
657+
let root_res = Res::Primitive(prim);
658+
let items = resolve_primitive_inherent_assoc_item(tcx, prim, ns, item_ident);
659+
if !items.is_empty() {
660+
items
661+
// Inherent associated items take precedence over items that come from trait impls.
662+
} else {
663+
primitive_type_to_ty(tcx, prim)
664+
.map(|ty| {
665+
resolve_associated_trait_item(ty, module_id, item_ident, ns, tcx)
666+
.iter()
667+
.map(|item| (root_res, item.def_id))
668+
.collect::<Vec<_>>()
669+
})
670+
.unwrap_or_default()
671+
}
672+
}
673+
643674
fn resolve_assoc_on_adt<'tcx>(
644675
tcx: TyCtxt<'tcx>,
645676
adt_def_id: DefId,
646-
item_name: Symbol,
677+
item_ident: Ident,
647678
ns: Namespace,
648679
disambiguator: Option<Disambiguator>,
649680
module_id: DefId,
650681
) -> Vec<(Res, DefId)> {
651-
debug!("looking for associated item named {item_name} for item {adt_def_id:?}");
682+
debug!("looking for associated item named {item_ident} for item {adt_def_id:?}");
652683
let root_res = Res::from_def_id(tcx, adt_def_id);
653684
let adt_ty = tcx.type_of(adt_def_id).instantiate_identity();
654685
let adt_def = adt_ty.ty_adt_def().expect("must be ADT");
655-
let item_ident = Ident::with_dummy_span(item_name);
656686
// Checks if item_name is a variant of the `SomeItem` enum
657687
if ns == TypeNS && adt_def.is_enum() {
658688
for variant in adt_def.variants() {
659-
if variant.name == item_name {
689+
if variant.name == item_ident.name {
660690
return vec![(root_res, variant.def_id)];
661691
}
662692
}
@@ -665,7 +695,7 @@ fn resolve_assoc_on_adt<'tcx>(
665695
if let Some(Disambiguator::Kind(DefKind::Field)) = disambiguator
666696
&& (adt_def.is_struct() || adt_def.is_union())
667697
{
668-
return resolve_structfield(adt_def, item_name)
698+
return resolve_structfield(adt_def, item_ident.name)
669699
.into_iter()
670700
.map(|did| (root_res, did))
671701
.collect();
@@ -677,7 +707,7 @@ fn resolve_assoc_on_adt<'tcx>(
677707
}
678708

679709
if ns == Namespace::ValueNS && (adt_def.is_struct() || adt_def.is_union()) {
680-
return resolve_structfield(adt_def, item_name)
710+
return resolve_structfield(adt_def, item_ident.name)
681711
.into_iter()
682712
.map(|did| (root_res, did))
683713
.collect();

tests/rustdoc-html/intra-doc/adt-through-alias.rs

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,35 @@
33
//! [`TheStructAlias::the_field`]
44
//! [`TheEnumAlias::TheVariant`]
55
//! [`TheEnumAlias::TheVariant::the_field`]
6+
//! [`TheUnionAlias::f1`]
7+
//!
8+
//! [`TheStruct::trait_`]
9+
//! [`TheStructAlias::trait_`]
10+
//! [`TheEnum::trait_`]
11+
//! [`TheEnumAlias::trait_`]
12+
//!
13+
//! [`TheStruct::inherent`]
14+
//! [`TheStructAlias::inherent`]
15+
//! [`TheEnum::inherent`]
16+
//! [`TheEnumAlias::inherent`]
617
7-
// FIXME: this should resolve to the alias's version
8-
//@ has foo/index.html '//a[@href="struct.TheStruct.html#structfield.the_field"]' 'TheStructAlias::the_field'
9-
// FIXME: this should resolve to the alias's version
10-
//@ has foo/index.html '//a[@href="enum.TheEnum.html#variant.TheVariant"]' 'TheEnumAlias::TheVariant'
18+
//@ has foo/index.html '//a[@href="type.TheStructAlias.html#structfield.the_field"]' 'TheStructAlias::the_field'
19+
//@ has foo/index.html '//a[@href="type.TheEnumAlias.html#variant.TheVariant"]' 'TheEnumAlias::TheVariant'
1120
//@ has foo/index.html '//a[@href="type.TheEnumAlias.html#variant.TheVariant.field.the_field"]' 'TheEnumAlias::TheVariant::the_field'
21+
//@ has foo/index.html '//a[@href="type.TheUnionAlias.html#structfield.f1"]' 'TheUnionAlias::f1'
22+
23+
//@ has foo/index.html '//a[@href="struct.TheStruct.html#method.trait_"]' 'TheStruct::trait_'
24+
//@ has foo/index.html '//a[@href="struct.TheStruct.html#method.trait_"]' 'TheStructAlias::trait_'
25+
//@ has foo/index.html '//a[@href="enum.TheEnum.html#method.trait_"]' 'TheEnum::trait_'
26+
// FIXME: this one should resolve to alias since it's impl Trait for TheEnumAlias
27+
//@ has foo/index.html '//a[@href="enum.TheEnum.html#method.trait_"]' 'TheEnumAlias::trait_'
28+
29+
//@ has foo/index.html '//a[@href="struct.TheStruct.html#method.inherent"]' 'TheStruct::inherent'
30+
// FIXME: this one should resolve to alias
31+
//@ has foo/index.html '//a[@href="struct.TheStruct.html#method.inherent"]' 'TheStructAlias::inherent'
32+
//@ has foo/index.html '//a[@href="enum.TheEnum.html#method.inherent"]' 'TheEnum::inherent'
33+
// FIXME: this one should resolve to alias
34+
//@ has foo/index.html '//a[@href="enum.TheEnum.html#method.inherent"]' 'TheEnumAlias::inherent'
1235

1336
pub struct TheStruct {
1437
pub the_field: i32,
@@ -22,4 +45,27 @@ pub enum TheEnum {
2245

2346
pub type TheEnumAlias = TheEnum;
2447

48+
pub trait Trait {
49+
fn trait_() {}
50+
}
51+
52+
impl Trait for TheStruct {}
53+
54+
impl Trait for TheEnumAlias {}
55+
56+
impl TheStruct {
57+
pub fn inherent() {}
58+
}
59+
60+
impl TheEnumAlias {
61+
pub fn inherent() {}
62+
}
63+
64+
pub union TheUnion {
65+
pub f1: usize,
66+
pub f2: isize,
67+
}
68+
69+
pub type TheUnionAlias = TheUnion;
70+
2571
fn main() {}

tests/rustdoc-html/intra-doc/associated-items.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ pub fn foo() {}
1313
//@ has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.method"]' 'link from struct'
1414
//@ has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.clone"]' 'MyStruct::clone'
1515
//@ has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#associatedtype.Input"]' 'MyStruct::Input'
16-
pub struct MyStruct { foo: () }
16+
pub struct MyStruct {
17+
foo: (),
18+
}
1719

1820
impl Clone for MyStruct {
1921
fn clone(&self) -> Self {
@@ -31,8 +33,7 @@ impl T for MyStruct {
3133

3234
/// [link from method][MyStruct::method] on method
3335
//@ has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.method"]' 'link from method'
34-
fn method(i: usize) {
35-
}
36+
fn method(i: usize) {}
3637
}
3738

3839
/// Ambiguity between which trait to use
@@ -57,7 +58,7 @@ impl T2 for S {
5758
fn ambiguous_method() {}
5859
}
5960

60-
//@ has associated_items/enum.MyEnum.html '//a/@href' 'enum.MyEnum.html#variant.MyVariant'
61+
//@ has associated_items/enum.MyEnum.html '//a/@href' 'type.MyEnumAlias.html#variant.MyVariant'
6162
/// Link to [MyEnumAlias::MyVariant]
6263
pub enum MyEnum {
6364
MyVariant,

0 commit comments

Comments
 (0)