rustdoc: show inner enum and struct in type definition for concrete type
This commit is contained in:
parent
08cdb40219
commit
2c35abe37c
@ -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,
|
||||
})
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
80
tests/rustdoc/typedef-inner-variants.rs
Normal file
80
tests/rustdoc/typedef-inner-variants.rs
Normal 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>;
|
Loading…
x
Reference in New Issue
Block a user