Skip to content

Commit 2bd0b56

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

File tree

3 files changed

+62
-33
lines changed

3 files changed

+62
-33
lines changed

src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 55 additions & 27 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,29 @@ 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-
}
616600
Res::Def(DefKind::TyAlias, 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(did).instantiate_identity()) else {
605+
return vec![];
622606
};
623-
resolve_associated_item(tcx, res, item_name, ns, disambiguator, module_id)
607+
let aliased_items =
608+
resolve_associated_item(tcx, aliased_res, item_name, ns, disambiguator, module_id);
609+
aliased_items
610+
.into_iter()
611+
.map(|(res, def_id)| {
612+
if is_assoc_item_on_alias_page(tcx, def_id) {
613+
(root_res, def_id)
614+
} else {
615+
(res, def_id)
616+
}
617+
})
618+
.collect()
624619
}
620+
Res::Primitive(prim) => resolve_assoc_on_primitive(tcx, prim, ns, item_ident, module_id),
625621
Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, did) => {
626-
resolve_assoc_on_adt(tcx, did, item_name, ns, disambiguator, module_id)
622+
resolve_assoc_on_adt(tcx, did, item_ident, ns, disambiguator, module_id)
627623
}
628624
Res::Def(DefKind::ForeignTy, did) => {
629625
resolve_assoc_on_simple_type(tcx, did, item_ident, ns, module_id)
@@ -640,23 +636,55 @@ fn resolve_associated_item<'tcx>(
640636
}
641637
}
642638

639+
// FIXME: make this fully complete by also including ALL inherent impls
640+
// and trait impls BUT ONLY if on alias directly
641+
fn is_assoc_item_on_alias_page<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
642+
match tcx.def_kind(def_id) {
643+
DefKind::Variant | DefKind::Field => true,
644+
_ => false,
645+
}
646+
}
647+
648+
fn resolve_assoc_on_primitive<'tcx>(
649+
tcx: TyCtxt<'tcx>,
650+
prim: PrimitiveType,
651+
ns: Namespace,
652+
item_ident: Ident,
653+
module_id: DefId,
654+
) -> Vec<(Res, DefId)> {
655+
let root_res = Res::Primitive(prim);
656+
let items = resolve_primitive_inherent_assoc_item(tcx, prim, ns, item_ident);
657+
if !items.is_empty() {
658+
items
659+
// Inherent associated items take precedence over items that come from trait impls.
660+
} else {
661+
primitive_type_to_ty(tcx, prim)
662+
.map(|ty| {
663+
resolve_associated_trait_item(ty, module_id, item_ident, ns, tcx)
664+
.iter()
665+
.map(|item| (root_res, item.def_id))
666+
.collect::<Vec<_>>()
667+
})
668+
.unwrap_or_default()
669+
}
670+
}
671+
643672
fn resolve_assoc_on_adt<'tcx>(
644673
tcx: TyCtxt<'tcx>,
645674
adt_def_id: DefId,
646-
item_name: Symbol,
675+
item_ident: Ident,
647676
ns: Namespace,
648677
disambiguator: Option<Disambiguator>,
649678
module_id: DefId,
650679
) -> Vec<(Res, DefId)> {
651-
debug!("looking for associated item named {item_name} for item {adt_def_id:?}");
680+
debug!("looking for associated item named {item_ident} for item {adt_def_id:?}");
652681
let root_res = Res::from_def_id(tcx, adt_def_id);
653682
let adt_ty = tcx.type_of(adt_def_id).instantiate_identity();
654683
let adt_def = adt_ty.ty_adt_def().expect("must be ADT");
655-
let item_ident = Ident::with_dummy_span(item_name);
656684
// Checks if item_name is a variant of the `SomeItem` enum
657685
if ns == TypeNS && adt_def.is_enum() {
658686
for variant in adt_def.variants() {
659-
if variant.name == item_name {
687+
if variant.name == item_ident.name {
660688
return vec![(root_res, variant.def_id)];
661689
}
662690
}
@@ -665,7 +693,7 @@ fn resolve_assoc_on_adt<'tcx>(
665693
if let Some(Disambiguator::Kind(DefKind::Field)) = disambiguator
666694
&& (adt_def.is_struct() || adt_def.is_union())
667695
{
668-
return resolve_structfield(adt_def, item_name)
696+
return resolve_structfield(adt_def, item_ident.name)
669697
.into_iter()
670698
.map(|did| (root_res, did))
671699
.collect();
@@ -677,7 +705,7 @@ fn resolve_assoc_on_adt<'tcx>(
677705
}
678706

679707
if ns == Namespace::ValueNS && (adt_def.is_struct() || adt_def.is_union()) {
680-
return resolve_structfield(adt_def, item_name)
708+
return resolve_structfield(adt_def, item_ident.name)
681709
.into_iter()
682710
.map(|did| (root_res, did))
683711
.collect();

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
//! [`TheEnumAlias::TheVariant::the_field`]
66
77
// 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'
8+
//@ has foo/index.html '//a[@href="type.TheStructAlias.html#structfield.the_field"]' 'TheStructAlias::the_field'
99
// FIXME: this should resolve to the alias's version
10-
//@ has foo/index.html '//a[@href="enum.TheEnum.html#variant.TheVariant"]' 'TheEnumAlias::TheVariant'
10+
//@ has foo/index.html '//a[@href="type.TheEnumAlias.html#variant.TheVariant"]' 'TheEnumAlias::TheVariant'
1111
//@ has foo/index.html '//a[@href="type.TheEnumAlias.html#variant.TheVariant.field.the_field"]' 'TheEnumAlias::TheVariant::the_field'
1212

1313
pub struct TheStruct {

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)