rustdoc: show inner enum and struct in type definition for concrete type
This commit is contained in:
parent
08cdb40219
commit
2c35abe37c
6 changed files with 311 additions and 5 deletions
|
@ -299,6 +299,9 @@ fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::TypeAlias
|
||||||
Box::new(clean::TypeAlias {
|
Box::new(clean::TypeAlias {
|
||||||
type_,
|
type_,
|
||||||
generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
|
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,
|
item_type: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
|
||||||
use rustc_middle::metadata::Reexport;
|
use rustc_middle::metadata::Reexport;
|
||||||
use rustc_middle::middle::resolve_bound_vars as rbv;
|
use rustc_middle::middle::resolve_bound_vars as rbv;
|
||||||
use rustc_middle::ty::fold::TypeFolder;
|
use rustc_middle::ty::fold::TypeFolder;
|
||||||
|
use rustc_middle::ty::GenericArgsRef;
|
||||||
use rustc_middle::ty::TypeVisitableExt;
|
use rustc_middle::ty::TypeVisitableExt;
|
||||||
use rustc_middle::ty::{self, AdtKind, EarlyBinder, Ty, TyCtxt};
|
use rustc_middle::ty::{self, AdtKind, EarlyBinder, Ty, TyCtxt};
|
||||||
use rustc_middle::{bug, span_bug};
|
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>(
|
fn clean_proc_macro<'tcx>(
|
||||||
item: &hir::Item<'tcx>,
|
item: &hir::Item<'tcx>,
|
||||||
name: &mut Symbol,
|
name: &mut Symbol,
|
||||||
|
@ -1222,6 +1263,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
|
||||||
Box::new(TypeAlias {
|
Box::new(TypeAlias {
|
||||||
type_: clean_ty(default, cx),
|
type_: clean_ty(default, cx),
|
||||||
generics,
|
generics,
|
||||||
|
inner_type: None,
|
||||||
item_type: Some(item_type),
|
item_type: Some(item_type),
|
||||||
}),
|
}),
|
||||||
bounds,
|
bounds,
|
||||||
|
@ -1264,7 +1306,12 @@ pub(crate) fn clean_impl_item<'tcx>(
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
AssocTypeItem(
|
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(),
|
Vec::new(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1471,6 +1518,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
generics,
|
generics,
|
||||||
|
inner_type: None,
|
||||||
item_type: None,
|
item_type: None,
|
||||||
}),
|
}),
|
||||||
bounds,
|
bounds,
|
||||||
|
@ -1490,6 +1538,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
generics,
|
generics,
|
||||||
|
inner_type: None,
|
||||||
item_type: None,
|
item_type: None,
|
||||||
}),
|
}),
|
||||||
// Associated types inside trait or inherent impls are not allowed to have
|
// 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>(
|
fn clean_variant_data<'tcx>(
|
||||||
variant: &hir::VariantData<'tcx>,
|
variant: &hir::VariantData<'tcx>,
|
||||||
disr_expr: &Option<hir::AnonConst>,
|
disr_expr: &Option<hir::AnonConst>,
|
||||||
|
@ -2604,7 +2707,7 @@ fn clean_maybe_renamed_item<'tcx>(
|
||||||
ItemKind::TyAlias(hir_ty, generics) => {
|
ItemKind::TyAlias(hir_ty, generics) => {
|
||||||
*cx.current_type_aliases.entry(def_id).or_insert(0) += 1;
|
*cx.current_type_aliases.entry(def_id).or_insert(0) += 1;
|
||||||
let rustdoc_ty = clean_ty(hir_ty, cx);
|
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)),
|
ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)),
|
||||||
cx,
|
cx,
|
||||||
None,
|
None,
|
||||||
|
@ -2617,10 +2720,15 @@ fn clean_maybe_renamed_item<'tcx>(
|
||||||
cx.current_type_aliases.remove(&def_id);
|
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 {
|
TypeAliasItem(Box::new(TypeAlias {
|
||||||
type_: rustdoc_ty,
|
|
||||||
generics,
|
generics,
|
||||||
item_type: Some(ty),
|
inner_type,
|
||||||
|
type_: rustdoc_ty,
|
||||||
|
item_type: Some(type_),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
ItemKind::Enum(ref def, generics) => EnumItem(Enum {
|
ItemKind::Enum(ref def, generics) => EnumItem(Enum {
|
||||||
|
|
|
@ -2229,10 +2229,31 @@ pub(crate) struct PathSegment {
|
||||||
pub(crate) args: GenericArgs,
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct TypeAlias {
|
pub(crate) struct TypeAlias {
|
||||||
pub(crate) type_: Type,
|
pub(crate) type_: Type,
|
||||||
pub(crate) generics: Generics,
|
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
|
/// `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
|
/// alias instead of the final type. This will always have the final type, regardless of whether
|
||||||
/// `type_` came from HIR or from metadata.
|
/// `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));
|
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();
|
let def_id = it.item_id.expect_def_id();
|
||||||
// Render any items associated directly to this alias, as otherwise they
|
// 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
|
// 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 {
|
impl FromWithTcx<Box<clean::TypeAlias>> for TypeAlias {
|
||||||
fn from_tcx(type_alias: Box<clean::TypeAlias>, tcx: TyCtxt<'_>) -> Self {
|
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) }
|
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…
Add table
Add a link
Reference in a new issue