Skip to content

Commit 2c35abe

Browse files
committed
rustdoc: show inner enum and struct in type definition for concrete type
1 parent 08cdb40 commit 2c35abe

File tree

6 files changed

+311
-5
lines changed

6 files changed

+311
-5
lines changed

src/librustdoc/clean/inline.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@ fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::TypeAlias
299299
Box::new(clean::TypeAlias {
300300
type_,
301301
generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
302+
// FIXME: Figure-out how to handle inner_type with
303+
// clean_ty_typedef_inner_type
304+
inner_type: None,
302305
item_type: None,
303306
})
304307
}

src/librustdoc/clean/mod.rs

Lines changed: 112 additions & 4 deletions
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,46 @@ 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+
let has_stripped_variants = adt_def.variants().len() != variants.len();
975+
TypeAliasInnerType::Enum {
976+
variants,
977+
has_stripped_variants,
978+
is_non_exhaustive: adt_def.is_variant_list_non_exhaustive(),
979+
}
980+
} else {
981+
let variant = adt_def
982+
.variants()
983+
.iter()
984+
.next()
985+
.unwrap_or_else(|| bug!("a struct or union should always have one variant def"));
986+
987+
let fields: Vec<_> =
988+
clean_variant_def_with_args(variant, args, cx).kind.inner_items().cloned().collect();
989+
990+
let has_stripped_fields = variant.fields.len() != fields.len();
991+
if adt_def.is_struct() {
992+
TypeAliasInnerType::Struct { ctor_kind: variant.ctor_kind(), fields, has_stripped_fields }
993+
} else {
994+
TypeAliasInnerType::Union { fields, has_stripped_fields }
995+
}
996+
})
997+
}
998+
958999
fn clean_proc_macro<'tcx>(
9591000
item: &hir::Item<'tcx>,
9601001
name: &mut Symbol,
@@ -1222,6 +1263,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
12221263
Box::new(TypeAlias {
12231264
type_: clean_ty(default, cx),
12241265
generics,
1266+
inner_type: None,
12251267
item_type: Some(item_type),
12261268
}),
12271269
bounds,
@@ -1264,7 +1306,12 @@ pub(crate) fn clean_impl_item<'tcx>(
12641306
None,
12651307
);
12661308
AssocTypeItem(
1267-
Box::new(TypeAlias { type_, generics, item_type: Some(item_type) }),
1309+
Box::new(TypeAlias {
1310+
type_,
1311+
generics,
1312+
inner_type: None,
1313+
item_type: Some(item_type),
1314+
}),
12681315
Vec::new(),
12691316
)
12701317
}
@@ -1471,6 +1518,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
14711518
None,
14721519
),
14731520
generics,
1521+
inner_type: None,
14741522
item_type: None,
14751523
}),
14761524
bounds,
@@ -1490,6 +1538,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
14901538
None,
14911539
),
14921540
generics,
1541+
inner_type: None,
14931542
item_type: None,
14941543
}),
14951544
// Associated types inside trait or inherent impls are not allowed to have
@@ -2350,6 +2399,60 @@ pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocCont
23502399
)
23512400
}
23522401

2402+
pub(crate) fn clean_variant_def_with_args<'tcx>(
2403+
variant: &ty::VariantDef,
2404+
args: &GenericArgsRef<'tcx>,
2405+
cx: &mut DocContext<'tcx>,
2406+
) -> Item {
2407+
let discriminant = match variant.discr {
2408+
ty::VariantDiscr::Explicit(def_id) => Some(Discriminant { expr: None, value: def_id }),
2409+
ty::VariantDiscr::Relative(_) => None,
2410+
};
2411+
2412+
let kind = match variant.ctor_kind() {
2413+
Some(CtorKind::Const) => VariantKind::CLike,
2414+
Some(CtorKind::Fn) => VariantKind::Tuple(
2415+
variant
2416+
.fields
2417+
.iter()
2418+
.filter(|field| field.vis.is_public())
2419+
.map(|field| {
2420+
let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args);
2421+
clean_field_with_def_id(
2422+
field.did,
2423+
field.name,
2424+
clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None),
2425+
cx,
2426+
)
2427+
})
2428+
.collect(),
2429+
),
2430+
None => VariantKind::Struct(VariantStruct {
2431+
fields: variant
2432+
.fields
2433+
.iter()
2434+
.filter(|field| field.vis.is_public())
2435+
.map(|field| {
2436+
let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args);
2437+
clean_field_with_def_id(
2438+
field.did,
2439+
field.name,
2440+
clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None),
2441+
cx,
2442+
)
2443+
})
2444+
.collect(),
2445+
}),
2446+
};
2447+
2448+
Item::from_def_id_and_parts(
2449+
variant.def_id,
2450+
Some(variant.name),
2451+
VariantItem(Variant { kind, discriminant }),
2452+
cx,
2453+
)
2454+
}
2455+
23532456
fn clean_variant_data<'tcx>(
23542457
variant: &hir::VariantData<'tcx>,
23552458
disr_expr: &Option<hir::AnonConst>,
@@ -2604,7 +2707,7 @@ fn clean_maybe_renamed_item<'tcx>(
26042707
ItemKind::TyAlias(hir_ty, generics) => {
26052708
*cx.current_type_aliases.entry(def_id).or_insert(0) += 1;
26062709
let rustdoc_ty = clean_ty(hir_ty, cx);
2607-
let ty = clean_middle_ty(
2710+
let type_ = clean_middle_ty(
26082711
ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)),
26092712
cx,
26102713
None,
@@ -2617,10 +2720,15 @@ fn clean_maybe_renamed_item<'tcx>(
26172720
cx.current_type_aliases.remove(&def_id);
26182721
}
26192722
}
2723+
2724+
let ty = cx.tcx.type_of(def_id).instantiate_identity();
2725+
let inner_type = clean_ty_alias_inner_type(ty, cx);
2726+
26202727
TypeAliasItem(Box::new(TypeAlias {
2621-
type_: rustdoc_ty,
26222728
generics,
2623-
item_type: Some(ty),
2729+
inner_type,
2730+
type_: rustdoc_ty,
2731+
item_type: Some(type_),
26242732
}))
26252733
}
26262734
ItemKind::Enum(ref def, generics) => EnumItem(Enum {

src/librustdoc/clean/types.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2229,10 +2229,31 @@ pub(crate) struct PathSegment {
22292229
pub(crate) args: GenericArgs,
22302230
}
22312231

2232+
#[derive(Clone, Debug)]
2233+
pub(crate) enum TypeAliasInnerType {
2234+
Enum {
2235+
variants: IndexVec<VariantIdx, Item>,
2236+
has_stripped_variants: bool,
2237+
is_non_exhaustive: bool,
2238+
},
2239+
Union {
2240+
fields: Vec<Item>,
2241+
has_stripped_fields: bool,
2242+
},
2243+
Struct {
2244+
ctor_kind: Option<CtorKind>,
2245+
fields: Vec<Item>,
2246+
has_stripped_fields: bool,
2247+
},
2248+
}
2249+
22322250
#[derive(Clone, Debug)]
22332251
pub(crate) struct TypeAlias {
22342252
pub(crate) type_: Type,
22352253
pub(crate) generics: Generics,
2254+
/// Inner `AdtDef` type, ie `type TyKind = IrTyKind<Adt, Ty>`,
2255+
/// to be shown directly on the typedef page.
2256+
pub(crate) inner_type: Option<TypeAliasInnerType>,
22362257
/// `type_` can come from either the HIR or from metadata. If it comes from HIR, it may be a type
22372258
/// alias instead of the final type. This will always have the final type, regardless of whether
22382259
/// `type_` came from HIR or from metadata.

src/librustdoc/html/render/print_item.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,100 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
12371237

12381238
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
12391239

1240+
// Only show inner variants if:
1241+
// - the typealias does NOT have any generics (modulo lifetimes)
1242+
// - AND the aliased type has some generics
1243+
//
1244+
// Otherwise, showing a non-generic type is rendurant with its own page, or
1245+
// if it still has some generics, it's not as useful.
1246+
let should_print_inner_type = t
1247+
.generics
1248+
.params
1249+
.iter()
1250+
.all(|param| matches!(param.kind, clean::GenericParamDefKind::Lifetime { .. }))
1251+
&& t.generics.where_predicates.is_empty()
1252+
&& t.type_.generics().is_some_and(|generics| !generics.is_empty());
1253+
1254+
if should_print_inner_type {
1255+
fn toggle<W, F>(w: &mut W, f: F)
1256+
where
1257+
W: fmt::Write,
1258+
F: FnOnce(&mut W),
1259+
{
1260+
write!(
1261+
w,
1262+
"<details class=\"toggle\">\
1263+
<summary>\
1264+
<span>Show Aliased Type</span>\
1265+
</summary>",
1266+
)
1267+
.unwrap();
1268+
f(w);
1269+
write!(w, "</details>").unwrap();
1270+
}
1271+
1272+
match &t.inner_type {
1273+
Some(clean::TypeAliasInnerType::Enum {
1274+
variants,
1275+
has_stripped_variants: has_stripped_entries,
1276+
is_non_exhaustive,
1277+
}) => {
1278+
toggle(w, |w| {
1279+
wrap_item(w, |w| {
1280+
write!(w, "enum {}{}", it.name.unwrap(), t.generics.print(cx));
1281+
render_enum_fields(
1282+
w,
1283+
cx,
1284+
None,
1285+
variants.iter(),
1286+
variants.len(),
1287+
*has_stripped_entries,
1288+
*is_non_exhaustive,
1289+
)
1290+
});
1291+
item_variants(w, cx, it, variants.iter());
1292+
});
1293+
}
1294+
Some(clean::TypeAliasInnerType::Union { fields, has_stripped_fields }) => {
1295+
toggle(w, |w| {
1296+
wrap_item(w, |w| {
1297+
write!(w, "union {}{}", it.name.unwrap(), t.generics.print(cx));
1298+
render_struct_fields(
1299+
w,
1300+
None,
1301+
None,
1302+
fields,
1303+
"",
1304+
true,
1305+
*has_stripped_fields,
1306+
cx,
1307+
);
1308+
});
1309+
item_fields(w, cx, it, fields, None);
1310+
});
1311+
}
1312+
Some(clean::TypeAliasInnerType::Struct { ctor_kind, fields, has_stripped_fields }) => {
1313+
toggle(w, |w| {
1314+
wrap_item(w, |w| {
1315+
write!(w, "struct {}{}", it.name.unwrap(), t.generics.print(cx));
1316+
render_struct_fields(
1317+
w,
1318+
None,
1319+
*ctor_kind,
1320+
fields,
1321+
"",
1322+
true,
1323+
*has_stripped_fields,
1324+
cx,
1325+
);
1326+
});
1327+
item_fields(w, cx, it, fields, None);
1328+
});
1329+
}
1330+
None => {}
1331+
}
1332+
}
1333+
12401334
let def_id = it.item_id.expect_def_id();
12411335
// Render any items associated directly to this alias, as otherwise they
12421336
// won't be visible anywhere in the docs. It would be nice to also show

src/librustdoc/json/conversions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,7 @@ pub(crate) fn from_macro_kind(kind: rustc_span::hygiene::MacroKind) -> MacroKind
789789

790790
impl FromWithTcx<Box<clean::TypeAlias>> for TypeAlias {
791791
fn from_tcx(type_alias: Box<clean::TypeAlias>, tcx: TyCtxt<'_>) -> Self {
792-
let clean::TypeAlias { type_, generics, item_type: _ } = *type_alias;
792+
let clean::TypeAlias { type_, generics, item_type: _, inner_type: _ } = *type_alias;
793793
TypeAlias { type_: type_.into_tcx(tcx), generics: generics.into_tcx(tcx) }
794794
}
795795
}

0 commit comments

Comments
 (0)