rustdoc: show inner enum and struct in type definition for concrete type

This commit is contained in:
Urgau 2023-08-15 14:13:17 +02:00
parent 08cdb40219
commit 2c35abe37c
6 changed files with 311 additions and 5 deletions

View File

@ -299,6 +299,9 @@ fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::TypeAlias
Box::new(clean::TypeAlias {
type_,
generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
// FIXME: Figure-out how to handle inner_type with
// clean_ty_typedef_inner_type
inner_type: None,
item_type: None,
})
}

View File

@ -24,6 +24,7 @@ use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
use rustc_middle::metadata::Reexport;
use rustc_middle::middle::resolve_bound_vars as rbv;
use rustc_middle::ty::fold::TypeFolder;
use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::TypeVisitableExt;
use rustc_middle::ty::{self, AdtKind, EarlyBinder, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
@ -955,6 +956,46 @@ fn clean_ty_generics<'tcx>(
}
}
fn clean_ty_alias_inner_type<'tcx>(
ty: Ty<'tcx>,
cx: &mut DocContext<'tcx>,
) -> Option<TypeAliasInnerType> {
let ty::Adt(adt_def, args) = ty.kind() else {
return None;
};
Some(if adt_def.is_enum() {
let variants: rustc_index::IndexVec<_, _> = adt_def
.variants()
.iter()
.map(|variant| clean_variant_def_with_args(variant, args, cx))
.collect();
let has_stripped_variants = adt_def.variants().len() != variants.len();
TypeAliasInnerType::Enum {
variants,
has_stripped_variants,
is_non_exhaustive: adt_def.is_variant_list_non_exhaustive(),
}
} else {
let variant = adt_def
.variants()
.iter()
.next()
.unwrap_or_else(|| bug!("a struct or union should always have one variant def"));
let fields: Vec<_> =
clean_variant_def_with_args(variant, args, cx).kind.inner_items().cloned().collect();
let has_stripped_fields = variant.fields.len() != fields.len();
if adt_def.is_struct() {
TypeAliasInnerType::Struct { ctor_kind: variant.ctor_kind(), fields, has_stripped_fields }
} else {
TypeAliasInnerType::Union { fields, has_stripped_fields }
}
})
}
fn clean_proc_macro<'tcx>(
item: &hir::Item<'tcx>,
name: &mut Symbol,
@ -1222,6 +1263,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
Box::new(TypeAlias {
type_: clean_ty(default, cx),
generics,
inner_type: None,
item_type: Some(item_type),
}),
bounds,
@ -1264,7 +1306,12 @@ pub(crate) fn clean_impl_item<'tcx>(
None,
);
AssocTypeItem(
Box::new(TypeAlias { type_, generics, item_type: Some(item_type) }),
Box::new(TypeAlias {
type_,
generics,
inner_type: None,
item_type: Some(item_type),
}),
Vec::new(),
)
}
@ -1471,6 +1518,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
None,
),
generics,
inner_type: None,
item_type: None,
}),
bounds,
@ -1490,6 +1538,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
None,
),
generics,
inner_type: None,
item_type: None,
}),
// 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
)
}
pub(crate) fn clean_variant_def_with_args<'tcx>(
variant: &ty::VariantDef,
args: &GenericArgsRef<'tcx>,
cx: &mut DocContext<'tcx>,
) -> Item {
let discriminant = match variant.discr {
ty::VariantDiscr::Explicit(def_id) => Some(Discriminant { expr: None, value: def_id }),
ty::VariantDiscr::Relative(_) => None,
};
let kind = match variant.ctor_kind() {
Some(CtorKind::Const) => VariantKind::CLike,
Some(CtorKind::Fn) => VariantKind::Tuple(
variant
.fields
.iter()
.filter(|field| field.vis.is_public())
.map(|field| {
let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args);
clean_field_with_def_id(
field.did,
field.name,
clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None),
cx,
)
})
.collect(),
),
None => VariantKind::Struct(VariantStruct {
fields: variant
.fields
.iter()
.filter(|field| field.vis.is_public())
.map(|field| {
let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args);
clean_field_with_def_id(
field.did,
field.name,
clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None),
cx,
)
})
.collect(),
}),
};
Item::from_def_id_and_parts(
variant.def_id,
Some(variant.name),
VariantItem(Variant { kind, discriminant }),
cx,
)
}
fn clean_variant_data<'tcx>(
variant: &hir::VariantData<'tcx>,
disr_expr: &Option<hir::AnonConst>,
@ -2604,7 +2707,7 @@ fn clean_maybe_renamed_item<'tcx>(
ItemKind::TyAlias(hir_ty, generics) => {
*cx.current_type_aliases.entry(def_id).or_insert(0) += 1;
let rustdoc_ty = clean_ty(hir_ty, cx);
let ty = clean_middle_ty(
let type_ = clean_middle_ty(
ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)),
cx,
None,
@ -2617,10 +2720,15 @@ fn clean_maybe_renamed_item<'tcx>(
cx.current_type_aliases.remove(&def_id);
}
}
let ty = cx.tcx.type_of(def_id).instantiate_identity();
let inner_type = clean_ty_alias_inner_type(ty, cx);
TypeAliasItem(Box::new(TypeAlias {
type_: rustdoc_ty,
generics,
item_type: Some(ty),
inner_type,
type_: rustdoc_ty,
item_type: Some(type_),
}))
}
ItemKind::Enum(ref def, generics) => EnumItem(Enum {

View File

@ -2229,10 +2229,31 @@ pub(crate) struct PathSegment {
pub(crate) args: GenericArgs,
}
#[derive(Clone, Debug)]
pub(crate) enum TypeAliasInnerType {
Enum {
variants: IndexVec<VariantIdx, Item>,
has_stripped_variants: bool,
is_non_exhaustive: bool,
},
Union {
fields: Vec<Item>,
has_stripped_fields: bool,
},
Struct {
ctor_kind: Option<CtorKind>,
fields: Vec<Item>,
has_stripped_fields: bool,
},
}
#[derive(Clone, Debug)]
pub(crate) struct TypeAlias {
pub(crate) type_: Type,
pub(crate) generics: Generics,
/// Inner `AdtDef` type, ie `type TyKind = IrTyKind<Adt, Ty>`,
/// to be shown directly on the typedef page.
pub(crate) inner_type: Option<TypeAliasInnerType>,
/// `type_` can come from either the HIR or from metadata. If it comes from HIR, it may be a type
/// alias instead of the final type. This will always have the final type, regardless of whether
/// `type_` came from HIR or from metadata.

View File

@ -1237,6 +1237,100 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
// Only show inner variants if:
// - the typealias does NOT have any generics (modulo lifetimes)
// - AND the aliased type has some generics
//
// Otherwise, showing a non-generic type is rendurant with its own page, or
// if it still has some generics, it's not as useful.
let should_print_inner_type = t
.generics
.params
.iter()
.all(|param| matches!(param.kind, clean::GenericParamDefKind::Lifetime { .. }))
&& t.generics.where_predicates.is_empty()
&& t.type_.generics().is_some_and(|generics| !generics.is_empty());
if should_print_inner_type {
fn toggle<W, F>(w: &mut W, f: F)
where
W: fmt::Write,
F: FnOnce(&mut W),
{
write!(
w,
"<details class=\"toggle\">\
<summary>\
<span>Show Aliased Type</span>\
</summary>",
)
.unwrap();
f(w);
write!(w, "</details>").unwrap();
}
match &t.inner_type {
Some(clean::TypeAliasInnerType::Enum {
variants,
has_stripped_variants: has_stripped_entries,
is_non_exhaustive,
}) => {
toggle(w, |w| {
wrap_item(w, |w| {
write!(w, "enum {}{}", it.name.unwrap(), t.generics.print(cx));
render_enum_fields(
w,
cx,
None,
variants.iter(),
variants.len(),
*has_stripped_entries,
*is_non_exhaustive,
)
});
item_variants(w, cx, it, variants.iter());
});
}
Some(clean::TypeAliasInnerType::Union { fields, has_stripped_fields }) => {
toggle(w, |w| {
wrap_item(w, |w| {
write!(w, "union {}{}", it.name.unwrap(), t.generics.print(cx));
render_struct_fields(
w,
None,
None,
fields,
"",
true,
*has_stripped_fields,
cx,
);
});
item_fields(w, cx, it, fields, None);
});
}
Some(clean::TypeAliasInnerType::Struct { ctor_kind, fields, has_stripped_fields }) => {
toggle(w, |w| {
wrap_item(w, |w| {
write!(w, "struct {}{}", it.name.unwrap(), t.generics.print(cx));
render_struct_fields(
w,
None,
*ctor_kind,
fields,
"",
true,
*has_stripped_fields,
cx,
);
});
item_fields(w, cx, it, fields, None);
});
}
None => {}
}
}
let def_id = it.item_id.expect_def_id();
// Render any items associated directly to this alias, as otherwise they
// won't be visible anywhere in the docs. It would be nice to also show

View File

@ -789,7 +789,7 @@ pub(crate) fn from_macro_kind(kind: rustc_span::hygiene::MacroKind) -> MacroKind
impl FromWithTcx<Box<clean::TypeAlias>> for TypeAlias {
fn from_tcx(type_alias: Box<clean::TypeAlias>, tcx: TyCtxt<'_>) -> Self {
let clean::TypeAlias { type_, generics, item_type: _ } = *type_alias;
let clean::TypeAlias { type_, generics, item_type: _, inner_type: _ } = *type_alias;
TypeAlias { type_: type_.into_tcx(tcx), generics: generics.into_tcx(tcx) }
}
}

View File

@ -0,0 +1,80 @@
#![crate_name = "inner_variants"]
pub struct Adt;
pub struct Ty;
// @has 'inner_variants/type.AliasTy.html'
// @count - '//*[@id="variants"]' 0
// @count - '//*[@id="fields"]' 0
pub type AliasTy = Ty;
// @has 'inner_variants/enum.IrTyKind.html'
pub enum IrTyKind<A, B> {
/// Doc comment for AdtKind
AdtKind(A),
/// and another one for TyKind
TyKind(A, B),
// no comment
StructKind { a: A, },
}
// @has 'inner_variants/type.NearlyTyKind.html'
// @count - '//*[@id="variants"]' 0
// @count - '//*[@id="fields"]' 0
pub type NearlyTyKind<B> = IrTyKind<Adt, B>;
// @has 'inner_variants/type.TyKind.html'
// @count - '//*[@id="variants"]' 1
// @count - '//*[@id="fields"]' 0
// @count - '//*[@class="variant"]' 3
// @matches - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code' "enum TyKind"
pub type TyKind = IrTyKind<Adt, Ty>;
// @has 'inner_variants/union.OneOr.html'
pub union OneOr<A: Copy> {
pub one: i64,
pub or: A,
}
// @has 'inner_variants/type.OneOrF64.html'
// @count - '//*[@id="variants"]' 0
// @count - '//*[@id="fields"]' 1
// @count - '//*[@class="structfield small-section-header"]' 2
// @matches - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code' "union OneOrF64"
pub type OneOrF64 = OneOr<f64>;
// @has 'inner_variants/struct.One.html'
pub struct One<T> {
pub val: T,
__hidden: T,
}
// @has 'inner_variants/type.OneU64.html'
// @count - '//*[@id="variants"]' 0
// @count - '//*[@id="fields"]' 1
// @count - '//*[@class="structfield small-section-header"]' 1
// @matches - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code' "struct OneU64"
pub type OneU64 = One<u64>;
// @has 'inner_variants/struct.OnceA.html'
pub struct OnceA<'a, A> {
a: &'a A, // private
}
// @has 'inner_variants/type.Once.html'
// @count - '//*[@id="variants"]' 0
// @count - '//*[@id="fields"]' 0
// @matches - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code' "struct Once<'a>"
pub type Once<'a> = OnceA<'a, i64>;
// @has 'inner_variants/struct.HighlyGenericStruct.html'
pub struct HighlyGenericStruct<A, B, C, D> {
pub z: (A, B, C, D)
}
// VERIFY that we NOT show the Aliased Type
// @has 'inner_variants/type.HighlyGenericAABB.html'
// @count - '//details[@class="toggle"]' 0
// @count - '//*[@id="variants"]' 0
// @count - '//*[@id="fields"]' 0
pub type HighlyGenericAABB<A, B> = HighlyGenericStruct<A, A, B, B>;