Skip to content

Commit 70c7e4d

Browse files
committed
Auto merge of rust-lang#114855 - Urgau:rustdoc-typedef-inner-variants, r=GuillaumeGomez
rustdoc: show inner enum and struct in type definition for concrete type This PR implements the [Display enum variants for generic enum in type def page](https://rust-lang.zulipchat.com/#narrow/stream/266220-rustdoc/topic/Display.20enum.20variants.20for.20generic.20enum.20in.20type.20def.20page) #rustdoc/zulip proposal. This proposal comes from looking at [`TyKind`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/type.TyKind.html) typedef from the compiler. On that page, the documentation is able to show the layout for each variant, but not the variants themselves. This proposal suggests showing the fields and variants for those "concrete type". This would mean that instead of having many unresolved generics, like in `IrTyKind`: ```rust Array(I::Ty, I::Const), Slice(I::Ty), RawPtr(I::TypeAndMut), Ref(I::Region, I::Ty, I::Mutability), FnDef(I::DefId, I::GenericArgsRef), ``` those would be resolved with direct links to the proper types in the `TyKind` typedef page: ```rust Array(Ty<'tcx>, Const<'tcx>), Slice(Ty<'tcx>), RawPtr(TypeAndMut<'tcx>), Ref(Region<'tcx>, Ty<'tcx>, Mutability<'tcx>), FnDef(DefId<'tcx>, GenericArgsRef<'tcx>), ``` Saving both time and confusion. ----- <details> <summary>Old description</summary> I've chosen to add the enums and structs under the "Show Aliased Type" details, as well as showing the variants and fields under the usual "Variants" and "Fields" sections. ~~*under new the `Inner Variants` and `Inner Fields` sections (except for their names, they are identical to the one found in the enum, struct and union pages). Those sections are complementary and do not replace anything else.*~~ This PR proposes the following condition for showing the aliased type (basically, has the aliased type some generics that are all of them resolved): - the typedef does NOT have any generics (modulo lifetimes) - AND the aliased type has some generics </details> ### Examples ```rust pub enum IrTyKind<'a, I: Interner> { /// Doc comment for AdtKind AdtKind(&'a I::Adt), /// and another one for TyKind TyKind(I::Adt, I::Ty), // no comment StructKind { a: I::Adt, }, } pub type TyKind<'a> = IrTyKind<'a, TyCtxt>; ``` ![TyKind](https://github.com/rust-lang/rust/assets/3616612/13307679-6d48-40d6-ad50-6db0b7f36ac7) <details> <summary>Old</summary> ![image](https://github.com/rust-lang/rust/assets/3616612/4147c049-d056-42d4-8a01-d43ebe747308) ![TyKind](https://user-images.githubusercontent.com/3616612/260988247-34831aa9-470d-4286-ad9f-3e8002153a92.png) ![TyKind](https://github.com/rust-lang/rust/assets/3616612/62381bb3-fa0f-4b05-926d-77759cf9115a) </details> ```rust pub struct One<T> { pub val: T, #[doc(hidden)] pub inner_tag: u64, __hidden: T, } /// `One` with `u64` as payload pub type OneU64 = One<u64>; ``` ![OneU64](https://github.com/rust-lang/rust/assets/3616612/d551b474-ce88-4f8c-bc94-5c88aba51424) <details> <summary>Old</summary> ![image](https://github.com/rust-lang/rust/assets/3616612/1a3f53c0-17bf-4aa7-894d-3fedc15b33da) ![OneU64](https://github.com/rust-lang/rust/assets/3616612/7b124a5b-e287-4efb-b9ca-fdcd1cdeeba8) ![OneU64](https://github.com/rust-lang/rust/assets/3616612/ddd962be-4f76-4ecd-81bd-531f3dd23832) </details> r? `@GuillaumeGomez`
2 parents f06b7c5 + 85e4b89 commit 70c7e4d

File tree

12 files changed

+636
-146
lines changed

12 files changed

+636
-146
lines changed

src/doc/rustdoc/src/how-to-read-rustdoc.md

+16
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@ followed by a list of fields or variants for Rust types.
3838
Finally, the page lists associated functions and trait implementations,
3939
including automatic and blanket implementations that `rustdoc` knows about.
4040

41+
### Sections
42+
43+
<!-- FIXME: Implementations -->
44+
<!-- FIXME: Trait Implementations -->
45+
<!-- FIXME: Implementors -->
46+
<!-- FIXME: Auto Trait Implementations -->
47+
48+
#### Aliased Type
49+
50+
A type alias is expanded at compile time to its
51+
[aliased type](https://doc.rust-lang.org/reference/items/type-aliases.html).
52+
That may involve substituting some or all of the type parameters in the target
53+
type with types provided by the type alias definition. The Aliased Type section
54+
shows the result of this expansion, including the types of public fields or
55+
variants, which may depend on those substitutions.
56+
4157
### Navigation
4258

4359
Subheadings, variants, fields, and many other things in this documentation

src/librustdoc/clean/inline.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ use rustc_span::symbol::{kw, sym, Symbol};
2020
use crate::clean::{
2121
self, clean_fn_decl_from_did_and_sig, clean_generics, clean_impl_item, clean_middle_assoc_item,
2222
clean_middle_field, clean_middle_ty, clean_trait_ref_with_bindings, clean_ty,
23-
clean_ty_generics, clean_variant_def, utils, Attributes, AttributesExt, ImplKind, ItemId, Type,
23+
clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes,
24+
AttributesExt, ImplKind, ItemId, Type,
2425
};
2526
use crate::core::DocContext;
2627
use crate::formats::item_type::ItemType;
@@ -289,16 +290,14 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union {
289290

290291
fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::TypeAlias> {
291292
let predicates = cx.tcx.explicit_predicates_of(did);
292-
let type_ = clean_middle_ty(
293-
ty::Binder::dummy(cx.tcx.type_of(did).instantiate_identity()),
294-
cx,
295-
Some(did),
296-
None,
297-
);
293+
let ty = cx.tcx.type_of(did).instantiate_identity();
294+
let type_ = clean_middle_ty(ty::Binder::dummy(ty), cx, Some(did), None);
295+
let inner_type = clean_ty_alias_inner_type(ty, cx);
298296

299297
Box::new(clean::TypeAlias {
300298
type_,
301299
generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
300+
inner_type,
302301
item_type: None,
303302
})
304303
}

src/librustdoc/clean/mod.rs

+132-4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
2424
use rustc_middle::metadata::Reexport;
2525
use rustc_middle::middle::resolve_bound_vars as rbv;
2626
use rustc_middle::ty::fold::TypeFolder;
27+
use rustc_middle::ty::GenericArgsRef;
2728
use rustc_middle::ty::TypeVisitableExt;
2829
use rustc_middle::ty::{self, AdtKind, EarlyBinder, Ty, TyCtxt};
2930
use rustc_middle::{bug, span_bug};
@@ -955,6 +956,43 @@ fn clean_ty_generics<'tcx>(
955956
}
956957
}
957958

959+
fn clean_ty_alias_inner_type<'tcx>(
960+
ty: Ty<'tcx>,
961+
cx: &mut DocContext<'tcx>,
962+
) -> Option<TypeAliasInnerType> {
963+
let ty::Adt(adt_def, args) = ty.kind() else {
964+
return None;
965+
};
966+
967+
Some(if adt_def.is_enum() {
968+
let variants: rustc_index::IndexVec<_, _> = adt_def
969+
.variants()
970+
.iter()
971+
.map(|variant| clean_variant_def_with_args(variant, args, cx))
972+
.collect();
973+
974+
TypeAliasInnerType::Enum {
975+
variants,
976+
is_non_exhaustive: adt_def.is_variant_list_non_exhaustive(),
977+
}
978+
} else {
979+
let variant = adt_def
980+
.variants()
981+
.iter()
982+
.next()
983+
.unwrap_or_else(|| bug!("a struct or union should always have one variant def"));
984+
985+
let fields: Vec<_> =
986+
clean_variant_def_with_args(variant, args, cx).kind.inner_items().cloned().collect();
987+
988+
if adt_def.is_struct() {
989+
TypeAliasInnerType::Struct { ctor_kind: variant.ctor_kind(), fields }
990+
} else {
991+
TypeAliasInnerType::Union { fields }
992+
}
993+
})
994+
}
995+
958996
fn clean_proc_macro<'tcx>(
959997
item: &hir::Item<'tcx>,
960998
name: &mut Symbol,
@@ -1222,6 +1260,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
12221260
Box::new(TypeAlias {
12231261
type_: clean_ty(default, cx),
12241262
generics,
1263+
inner_type: None,
12251264
item_type: Some(item_type),
12261265
}),
12271266
bounds,
@@ -1264,7 +1303,12 @@ pub(crate) fn clean_impl_item<'tcx>(
12641303
None,
12651304
);
12661305
AssocTypeItem(
1267-
Box::new(TypeAlias { type_, generics, item_type: Some(item_type) }),
1306+
Box::new(TypeAlias {
1307+
type_,
1308+
generics,
1309+
inner_type: None,
1310+
item_type: Some(item_type),
1311+
}),
12681312
Vec::new(),
12691313
)
12701314
}
@@ -1471,6 +1515,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
14711515
None,
14721516
),
14731517
generics,
1518+
inner_type: None,
14741519
item_type: None,
14751520
}),
14761521
bounds,
@@ -1490,6 +1535,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
14901535
None,
14911536
),
14921537
generics,
1538+
inner_type: None,
14931539
item_type: None,
14941540
}),
14951541
// Associated types inside trait or inherent impls are not allowed to have
@@ -2363,6 +2409,83 @@ pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocCont
23632409
)
23642410
}
23652411

2412+
pub(crate) fn clean_variant_def_with_args<'tcx>(
2413+
variant: &ty::VariantDef,
2414+
args: &GenericArgsRef<'tcx>,
2415+
cx: &mut DocContext<'tcx>,
2416+
) -> Item {
2417+
let discriminant = match variant.discr {
2418+
ty::VariantDiscr::Explicit(def_id) => Some(Discriminant { expr: None, value: def_id }),
2419+
ty::VariantDiscr::Relative(_) => None,
2420+
};
2421+
2422+
use rustc_middle::traits::ObligationCause;
2423+
use rustc_trait_selection::infer::TyCtxtInferExt;
2424+
use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
2425+
2426+
let infcx = cx.tcx.infer_ctxt().build();
2427+
let kind = match variant.ctor_kind() {
2428+
Some(CtorKind::Const) => VariantKind::CLike,
2429+
Some(CtorKind::Fn) => VariantKind::Tuple(
2430+
variant
2431+
.fields
2432+
.iter()
2433+
.map(|field| {
2434+
let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args);
2435+
2436+
// normalize the type to only show concrete types
2437+
// note: we do not use try_normalize_erasing_regions since we
2438+
// do care about showing the regions
2439+
let ty = infcx
2440+
.at(&ObligationCause::dummy(), cx.param_env)
2441+
.query_normalize(ty)
2442+
.map(|normalized| normalized.value)
2443+
.unwrap_or(ty);
2444+
2445+
clean_field_with_def_id(
2446+
field.did,
2447+
field.name,
2448+
clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None),
2449+
cx,
2450+
)
2451+
})
2452+
.collect(),
2453+
),
2454+
None => VariantKind::Struct(VariantStruct {
2455+
fields: variant
2456+
.fields
2457+
.iter()
2458+
.map(|field| {
2459+
let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args);
2460+
2461+
// normalize the type to only show concrete types
2462+
// note: we do not use try_normalize_erasing_regions since we
2463+
// do care about showing the regions
2464+
let ty = infcx
2465+
.at(&ObligationCause::dummy(), cx.param_env)
2466+
.query_normalize(ty)
2467+
.map(|normalized| normalized.value)
2468+
.unwrap_or(ty);
2469+
2470+
clean_field_with_def_id(
2471+
field.did,
2472+
field.name,
2473+
clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None),
2474+
cx,
2475+
)
2476+
})
2477+
.collect(),
2478+
}),
2479+
};
2480+
2481+
Item::from_def_id_and_parts(
2482+
variant.def_id,
2483+
Some(variant.name),
2484+
VariantItem(Variant { kind, discriminant }),
2485+
cx,
2486+
)
2487+
}
2488+
23662489
fn clean_variant_data<'tcx>(
23672490
variant: &hir::VariantData<'tcx>,
23682491
disr_expr: &Option<hir::AnonConst>,
@@ -2617,7 +2740,7 @@ fn clean_maybe_renamed_item<'tcx>(
26172740
ItemKind::TyAlias(hir_ty, generics) => {
26182741
*cx.current_type_aliases.entry(def_id).or_insert(0) += 1;
26192742
let rustdoc_ty = clean_ty(hir_ty, cx);
2620-
let ty = clean_middle_ty(
2743+
let type_ = clean_middle_ty(
26212744
ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)),
26222745
cx,
26232746
None,
@@ -2630,10 +2753,15 @@ fn clean_maybe_renamed_item<'tcx>(
26302753
cx.current_type_aliases.remove(&def_id);
26312754
}
26322755
}
2756+
2757+
let ty = cx.tcx.type_of(def_id).instantiate_identity();
2758+
let inner_type = clean_ty_alias_inner_type(ty, cx);
2759+
26332760
TypeAliasItem(Box::new(TypeAlias {
2634-
type_: rustdoc_ty,
26352761
generics,
2636-
item_type: Some(ty),
2762+
inner_type,
2763+
type_: rustdoc_ty,
2764+
item_type: Some(type_),
26372765
}))
26382766
}
26392767
ItemKind::Enum(ref def, generics) => EnumItem(Enum {

src/librustdoc/clean/types.rs

+10
Original file line numberDiff line numberDiff line change
@@ -2230,10 +2230,20 @@ pub(crate) struct PathSegment {
22302230
pub(crate) args: GenericArgs,
22312231
}
22322232

2233+
#[derive(Clone, Debug)]
2234+
pub(crate) enum TypeAliasInnerType {
2235+
Enum { variants: IndexVec<VariantIdx, Item>, is_non_exhaustive: bool },
2236+
Union { fields: Vec<Item> },
2237+
Struct { ctor_kind: Option<CtorKind>, fields: Vec<Item> },
2238+
}
2239+
22332240
#[derive(Clone, Debug)]
22342241
pub(crate) struct TypeAlias {
22352242
pub(crate) type_: Type,
22362243
pub(crate) generics: Generics,
2244+
/// Inner `AdtDef` type, ie `type TyKind = IrTyKind<Adt, Ty>`,
2245+
/// to be shown directly on the typedef page.
2246+
pub(crate) inner_type: Option<TypeAliasInnerType>,
22372247
/// `type_` can come from either the HIR or from metadata. If it comes from HIR, it may be a type
22382248
/// alias instead of the final type. This will always have the final type, regardless of whether
22392249
/// `type_` came from HIR or from metadata.

src/librustdoc/fold.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,31 @@ pub(crate) trait DocFolder: Sized {
5252

5353
VariantItem(Variant { kind, discriminant })
5454
}
55+
TypeAliasItem(mut typealias) => {
56+
typealias.inner_type = typealias.inner_type.map(|inner_type| match inner_type {
57+
TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
58+
let variants = variants
59+
.into_iter_enumerated()
60+
.filter_map(|(_, x)| self.fold_item(x))
61+
.collect();
62+
63+
TypeAliasInnerType::Enum { variants, is_non_exhaustive }
64+
}
65+
TypeAliasInnerType::Union { fields } => {
66+
let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
67+
TypeAliasInnerType::Union { fields }
68+
}
69+
TypeAliasInnerType::Struct { ctor_kind, fields } => {
70+
let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
71+
TypeAliasInnerType::Struct { ctor_kind, fields }
72+
}
73+
});
74+
75+
TypeAliasItem(typealias)
76+
}
5577
ExternCrateItem { src: _ }
5678
| ImportItem(_)
5779
| FunctionItem(_)
58-
| TypeAliasItem(_)
5980
| OpaqueTyItem(_)
6081
| StaticItem(_)
6182
| ConstantItem(_)

src/librustdoc/formats/cache.rs

+1
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
457457
| clean::StructItem(..)
458458
| clean::UnionItem(..)
459459
| clean::VariantItem(..)
460+
| clean::TypeAliasItem(..)
460461
| clean::ImplItem(..) => {
461462
self.cache.parent_stack.push(ParentStackItem::new(&item));
462463
(self.fold_item_recur(item), true)

0 commit comments

Comments
 (0)