From 08cdb40219cc8ac2c60e24f1242ac5157c1436ee Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 16 Aug 2023 12:58:58 +0200 Subject: [PATCH 1/8] rustdoc: extract logic for printing enum fields and struct variants --- src/librustdoc/html/render/print_item.rs | 326 ++++++++++++++--------- 1 file changed, 195 insertions(+), 131 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index cb78f903462c1..007c1cf1ef91f 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1332,7 +1332,7 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>( fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) { let tcx = cx.tcx(); let count_variants = e.variants().count(); - wrap_item(w, |mut w| { + wrap_item(w, |w| { render_attributes_in_code(w, it, tcx); write!( w, @@ -1341,148 +1341,179 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: it.name.unwrap(), e.generics.print(cx), ); - if !print_where_clause_and_check(w, &e.generics, cx) { - // If there wasn't a `where` clause, we add a whitespace. - w.write_str(" "); - } + render_enum_fields( + w, + cx, + Some(&e.generics), + e.variants(), + count_variants, + e.has_stripped_entries(), + it.is_non_exhaustive(), + ); + }); - let variants_stripped = e.has_stripped_entries(); - if count_variants == 0 && !variants_stripped { - w.write_str("{}"); - } else { - w.write_str("{\n"); - let toggle = should_hide_fields(count_variants); - if toggle { - toggle_open(&mut w, format_args!("{count_variants} variants")); - } - for v in e.variants() { - w.write_str(" "); - let name = v.name.unwrap(); - match *v.kind { - // FIXME(#101337): Show discriminant - clean::VariantItem(ref var) => match var.kind { - clean::VariantKind::CLike => w.write_str(name.as_str()), - clean::VariantKind::Tuple(ref s) => { - write!(w, "{name}({})", print_tuple_struct_fields(cx, s),); - } - clean::VariantKind::Struct(ref s) => { - render_struct(w, v, None, None, &s.fields, " ", false, cx); - } - }, - _ => unreachable!(), - } - w.write_str(",\n"); - } + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); - if variants_stripped && !it.is_non_exhaustive() { - w.write_str(" // some variants omitted\n"); - } - if toggle { - toggle_close(&mut w); + if count_variants != 0 { + item_variants(w, cx, it, e.variants()); + } + let def_id = it.item_id.expect_def_id(); + write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); + write!(w, "{}", document_type_layout(cx, def_id)); +} + +fn render_enum_fields<'a>( + mut w: &mut Buffer, + cx: &mut Context<'_>, + g: Option<&clean::Generics>, + variants: impl Iterator, + count_variants: usize, + has_stripped_entries: bool, + is_non_exhaustive: bool, +) { + if !g.is_some_and(|g| print_where_clause_and_check(w, g, cx)) { + // If there wasn't a `where` clause, we add a whitespace. + w.write_str(" "); + } + + let variants_stripped = has_stripped_entries; + if count_variants == 0 && !variants_stripped { + w.write_str("{}"); + } else { + w.write_str("{\n"); + let toggle = should_hide_fields(count_variants); + if toggle { + toggle_open(&mut w, format_args!("{count_variants} variants")); + } + const TAB: &str = " "; + for v in variants { + w.write_str(TAB); + let name = v.name.unwrap(); + match *v.kind { + // FIXME(#101337): Show discriminant + clean::VariantItem(ref var) => match var.kind { + clean::VariantKind::CLike => w.write_str(name.as_str()), + clean::VariantKind::Tuple(ref s) => { + write!(w, "{name}({})", print_tuple_struct_fields(cx, s),); + } + clean::VariantKind::Struct(ref s) => { + render_struct(w, v, None, None, &s.fields, TAB, false, cx); + } + }, + _ => unreachable!(), } - w.write_str("}"); + w.write_str(",\n"); } - }); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); + if variants_stripped && !is_non_exhaustive { + w.write_str(" // some variants omitted\n"); + } + if toggle { + toggle_close(&mut w); + } + w.write_str("}"); + } +} - if count_variants != 0 { +fn item_variants<'a>( + w: &mut Buffer, + cx: &mut Context<'_>, + it: &clean::Item, + variants: impl Iterator, +) { + let tcx = cx.tcx(); + write!( + w, + "

\ + Variants{}§\ +

\ + {}\ +
", + document_non_exhaustive_header(it), + document_non_exhaustive(it) + ); + for variant in variants { + let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap())); write!( w, - "

\ - Variants{}§\ -

\ - {}\ -
", - document_non_exhaustive_header(it), - document_non_exhaustive(it) + "
\ + §", ); - for variant in e.variants() { - let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap())); - write!( - w, - "
\ - §", - ); - render_stability_since_raw_with_extra( - w, - variant.stable_since(tcx), - variant.const_stability(tcx), - it.stable_since(tcx), - it.const_stable_since(tcx), - " rightside", - ); - write!(w, "

{name}", name = variant.name.unwrap()); + render_stability_since_raw_with_extra( + w, + variant.stable_since(tcx), + variant.const_stability(tcx), + it.stable_since(tcx), + it.const_stable_since(tcx), + " rightside", + ); + write!(w, "

{name}", name = variant.name.unwrap()); - let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() }; + let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() }; - if let clean::VariantKind::Tuple(ref s) = variant_data.kind { - write!(w, "({})", print_tuple_struct_fields(cx, s)); - } - w.write_str("

"); - - let heading_and_fields = match &variant_data.kind { - clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)), - clean::VariantKind::Tuple(fields) => { - // Documentation on tuple variant fields is rare, so to reduce noise we only emit - // the section if at least one field is documented. - if fields.iter().any(|f| !f.doc_value().is_empty()) { - Some(("Tuple Fields", fields)) - } else { - None - } + if let clean::VariantKind::Tuple(ref s) = variant_data.kind { + write!(w, "({})", print_tuple_struct_fields(cx, s)); + } + w.write_str("
"); + + let heading_and_fields = match &variant_data.kind { + clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)), + clean::VariantKind::Tuple(fields) => { + // Documentation on tuple variant fields is rare, so to reduce noise we only emit + // the section if at least one field is documented. + if fields.iter().any(|f| !f.doc_value().is_empty()) { + Some(("Tuple Fields", fields)) + } else { + None } - clean::VariantKind::CLike => None, - }; + } + clean::VariantKind::CLike => None, + }; - if let Some((heading, fields)) = heading_and_fields { - let variant_id = - cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap())); - write!( - w, - "
\ -

{heading}

\ - {}", - document_non_exhaustive(variant) - ); - for field in fields { - match *field.kind { - clean::StrippedItem(box clean::StructFieldItem(_)) => {} - clean::StructFieldItem(ref ty) => { - let id = cx.derive_id(format!( - "variant.{}.field.{}", - variant.name.unwrap(), - field.name.unwrap() - )); - write!( - w, - "
\ + if let Some((heading, fields)) = heading_and_fields { + let variant_id = + cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap())); + write!( + w, + "
\ +

{heading}

\ + {}", + document_non_exhaustive(variant) + ); + for field in fields { + match *field.kind { + clean::StrippedItem(box clean::StructFieldItem(_)) => {} + clean::StructFieldItem(ref ty) => { + let id = cx.derive_id(format!( + "variant.{}.field.{}", + variant.name.unwrap(), + field.name.unwrap() + )); + write!( + w, + "
\ \ §\ {f}: {t}\ ", - f = field.name.unwrap(), - t = ty.print(cx), - ); - write!( - w, - "{}
", - document(cx, field, Some(variant), HeadingOffset::H5) - ); - } - _ => unreachable!(), + f = field.name.unwrap(), + t = ty.print(cx), + ); + write!( + w, + "{}
", + document(cx, field, Some(variant), HeadingOffset::H5) + ); } + _ => unreachable!(), } - w.write_str("
"); } - - write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4)); + w.write_str("
"); } - write!(w, "
"); + + write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4)); } - let def_id = it.item_id.expect_def_id(); - write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); - write!(w, "{}", document_type_layout(cx, def_id)); + write!(w, "
"); } fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) { @@ -1593,15 +1624,28 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); - let mut fields = s - .fields + item_fields(w, cx, it, &s.fields, s.ctor_kind); + + let def_id = it.item_id.expect_def_id(); + write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); + write!(w, "{}", document_type_layout(cx, def_id)); +} + +fn item_fields( + w: &mut Buffer, + cx: &mut Context<'_>, + it: &clean::Item, + fields: &Vec, + ctor_kind: Option, +) { + let mut fields = fields .iter() .filter_map(|f| match *f.kind { clean::StructFieldItem(ref ty) => Some((f, ty)), _ => None, }) .peekable(); - if let None | Some(CtorKind::Fn) = s.ctor_kind { + if let None | Some(CtorKind::Fn) = ctor_kind { if fields.peek().is_some() { write!( w, @@ -1609,7 +1653,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean {}{}§\ \ {}", - if s.ctor_kind.is_none() { "Fields" } else { "Tuple Fields" }, + if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" }, document_non_exhaustive_header(it), document_non_exhaustive(it) ); @@ -1630,9 +1674,6 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean } } } - let def_id = it.item_id.expect_def_id(); - write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); - write!(w, "{}", document_type_layout(cx, def_id)); } fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) { @@ -1871,7 +1912,7 @@ fn render_union<'a, 'cx: 'a>( } fn render_struct( - mut w: &mut Buffer, + w: &mut Buffer, it: &clean::Item, g: Option<&clean::Generics>, ty: Option, @@ -1891,6 +1932,29 @@ fn render_struct( if let Some(g) = g { write!(w, "{}", g.print(cx)) } + render_struct_fields( + w, + g, + ty, + fields, + tab, + structhead, + it.has_stripped_entries().unwrap_or(false), + cx, + ) +} + +fn render_struct_fields( + mut w: &mut Buffer, + g: Option<&clean::Generics>, + ty: Option, + fields: &[clean::Item], + tab: &str, + structhead: bool, + has_stripped_entries: bool, + cx: &Context<'_>, +) { + let tcx = cx.tcx(); match ty { None => { let where_displayed = @@ -1922,11 +1986,11 @@ fn render_struct( } if has_visible_fields { - if it.has_stripped_entries().unwrap() { + if has_stripped_entries { write!(w, "\n{tab} /* private fields */"); } write!(w, "\n{tab}"); - } else if it.has_stripped_entries().unwrap() { + } else if has_stripped_entries { write!(w, " /* private fields */ "); } if toggle { From 2c35abe37c1e8fb51fa15892f59aaa248a768960 Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 15 Aug 2023 14:13:17 +0200 Subject: [PATCH 2/8] rustdoc: show inner enum and struct in type definition for concrete type --- src/librustdoc/clean/inline.rs | 3 + src/librustdoc/clean/mod.rs | 116 ++++++++++++++++++++++- src/librustdoc/clean/types.rs | 21 ++++ src/librustdoc/html/render/print_item.rs | 94 ++++++++++++++++++ src/librustdoc/json/conversions.rs | 2 +- tests/rustdoc/typedef-inner-variants.rs | 80 ++++++++++++++++ 6 files changed, 311 insertions(+), 5 deletions(-) create mode 100644 tests/rustdoc/typedef-inner-variants.rs diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index cc86a3d747571..25f303d8a52e7 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -299,6 +299,9 @@ fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box( } } +fn clean_ty_alias_inner_type<'tcx>( + ty: Ty<'tcx>, + cx: &mut DocContext<'tcx>, +) -> Option { + 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, @@ -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 { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 9cf3c068b602d..6efe417970716 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2229,10 +2229,31 @@ pub(crate) struct PathSegment { pub(crate) args: GenericArgs, } +#[derive(Clone, Debug)] +pub(crate) enum TypeAliasInnerType { + Enum { + variants: IndexVec, + has_stripped_variants: bool, + is_non_exhaustive: bool, + }, + Union { + fields: Vec, + has_stripped_fields: bool, + }, + Struct { + ctor_kind: Option, + fields: Vec, + 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`, + /// to be shown directly on the typedef page. + pub(crate) inner_type: Option, /// `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. diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 007c1cf1ef91f..09be756f26d22 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -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: &mut W, f: F) + where + W: fmt::Write, + F: FnOnce(&mut W), + { + write!( + w, + "
\ + \ + Show Aliased Type\ + ", + ) + .unwrap(); + f(w); + write!(w, "
").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 diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 66b5798797f61..406c7218f98c0 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -789,7 +789,7 @@ pub(crate) fn from_macro_kind(kind: rustc_span::hygiene::MacroKind) -> MacroKind impl FromWithTcx> for TypeAlias { fn from_tcx(type_alias: Box, 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) } } } diff --git a/tests/rustdoc/typedef-inner-variants.rs b/tests/rustdoc/typedef-inner-variants.rs new file mode 100644 index 0000000000000..e0c5b7dd98ff8 --- /dev/null +++ b/tests/rustdoc/typedef-inner-variants.rs @@ -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 { + /// 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 = IrTyKind; + +// @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; + +// @has 'inner_variants/union.OneOr.html' +pub union OneOr { + 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; + +// @has 'inner_variants/struct.One.html' +pub struct One { + 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; + +// @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 { + 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 = HighlyGenericStruct; From 6b3bba8c3e60e08805a5b4b556c5ec128d350382 Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 16 Aug 2023 17:12:04 +0200 Subject: [PATCH 3/8] rustdoc: handle typedef inner type when doing cross-crate inlining --- src/librustdoc/clean/inline.rs | 16 ++++++---------- .../auxiliary/cross_crate_generic_typedef.rs | 5 +++++ tests/rustdoc/typedef-inner-variants.rs | 11 +++++++++++ 3 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 tests/rustdoc/auxiliary/cross_crate_generic_typedef.rs diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 25f303d8a52e7..dd43ee0338322 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -20,7 +20,8 @@ use rustc_span::symbol::{kw, sym, Symbol}; use crate::clean::{ self, clean_fn_decl_from_did_and_sig, clean_generics, clean_impl_item, clean_middle_assoc_item, clean_middle_field, clean_middle_ty, clean_trait_ref_with_bindings, clean_ty, - clean_ty_generics, clean_variant_def, utils, Attributes, AttributesExt, ImplKind, ItemId, Type, + clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes, + AttributesExt, ImplKind, ItemId, Type, }; use crate::core::DocContext; use crate::formats::item_type::ItemType; @@ -289,19 +290,14 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union { fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box { let predicates = cx.tcx.explicit_predicates_of(did); - let type_ = clean_middle_ty( - ty::Binder::dummy(cx.tcx.type_of(did).instantiate_identity()), - cx, - Some(did), - None, - ); + let ty = cx.tcx.type_of(did).instantiate_identity(); + let type_ = clean_middle_ty(ty::Binder::dummy(ty), cx, Some(did), None); + let inner_type = clean_ty_alias_inner_type(ty, cx); 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, + inner_type, item_type: None, }) } diff --git a/tests/rustdoc/auxiliary/cross_crate_generic_typedef.rs b/tests/rustdoc/auxiliary/cross_crate_generic_typedef.rs new file mode 100644 index 0000000000000..f4e020b3b9599 --- /dev/null +++ b/tests/rustdoc/auxiliary/cross_crate_generic_typedef.rs @@ -0,0 +1,5 @@ +pub struct InlineOne { + pub inline: A +} + +pub type InlineU64 = InlineOne; diff --git a/tests/rustdoc/typedef-inner-variants.rs b/tests/rustdoc/typedef-inner-variants.rs index e0c5b7dd98ff8..46acd86e12fce 100644 --- a/tests/rustdoc/typedef-inner-variants.rs +++ b/tests/rustdoc/typedef-inner-variants.rs @@ -1,5 +1,11 @@ +// This test checks different combinations of structs, enums, and unions +// for the "Show Aliased Type" feature on type definition. + #![crate_name = "inner_variants"] +// aux-build:cross_crate_generic_typedef.rs +extern crate cross_crate_generic_typedef; + pub struct Adt; pub struct Ty; @@ -78,3 +84,8 @@ pub struct HighlyGenericStruct { // @count - '//*[@id="variants"]' 0 // @count - '//*[@id="fields"]' 0 pub type HighlyGenericAABB = HighlyGenericStruct; + +// @has 'inner_variants/type.InlineU64.html' +// @count - '//*[@id="variants"]' 0 +// @count - '//*[@id="fields"]' 1 +pub use cross_crate_generic_typedef::InlineU64; From af6889c28c5ad72433562caf75d2dc97bf494424 Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 18 Aug 2023 17:57:20 +0200 Subject: [PATCH 4/8] rustdoc: bind typedef inner type items to the folding system This let's us handle a multitude of things for free: - #[doc(hidden)] - private fields/variants - --document-private-items - --document-hidden-items And correct in the process the determination of "has stripped items" by doing the same logic done by other ones. --- src/librustdoc/clean/mod.rs | 9 ++----- src/librustdoc/clean/types.rs | 17 +++--------- src/librustdoc/fold.rs | 23 ++++++++++++++++- src/librustdoc/formats/cache.rs | 1 + src/librustdoc/html/render/print_item.rs | 33 ++++++++++++++---------- tests/rustdoc/typedef-inner-variants.rs | 6 ++++- 6 files changed, 53 insertions(+), 36 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index d0d92a1116fc3..892dfe4240b43 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -971,10 +971,8 @@ fn clean_ty_alias_inner_type<'tcx>( .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 { @@ -987,11 +985,10 @@ fn clean_ty_alias_inner_type<'tcx>( 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 } + TypeAliasInnerType::Struct { ctor_kind: variant.ctor_kind(), fields } } else { - TypeAliasInnerType::Union { fields, has_stripped_fields } + TypeAliasInnerType::Union { fields } } }) } @@ -2415,7 +2412,6 @@ pub(crate) fn clean_variant_def_with_args<'tcx>( 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( @@ -2431,7 +2427,6 @@ pub(crate) fn clean_variant_def_with_args<'tcx>( 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( diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 6efe417970716..9cc5d7c2911f2 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2231,20 +2231,9 @@ pub(crate) struct PathSegment { #[derive(Clone, Debug)] pub(crate) enum TypeAliasInnerType { - Enum { - variants: IndexVec, - has_stripped_variants: bool, - is_non_exhaustive: bool, - }, - Union { - fields: Vec, - has_stripped_fields: bool, - }, - Struct { - ctor_kind: Option, - fields: Vec, - has_stripped_fields: bool, - }, + Enum { variants: IndexVec, is_non_exhaustive: bool }, + Union { fields: Vec }, + Struct { ctor_kind: Option, fields: Vec }, } #[derive(Clone, Debug)] diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index ceba643ed5f0c..cf11e2d7899e0 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -52,10 +52,31 @@ pub(crate) trait DocFolder: Sized { VariantItem(Variant { kind, discriminant }) } + TypeAliasItem(mut typealias) => { + typealias.inner_type = typealias.inner_type.map(|inner_type| match inner_type { + TypeAliasInnerType::Enum { variants, is_non_exhaustive } => { + let variants = variants + .into_iter_enumerated() + .filter_map(|(_, x)| self.fold_item(x)) + .collect(); + + TypeAliasInnerType::Enum { variants, is_non_exhaustive } + } + TypeAliasInnerType::Union { fields } => { + let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect(); + TypeAliasInnerType::Union { fields } + } + TypeAliasInnerType::Struct { ctor_kind, fields } => { + let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect(); + TypeAliasInnerType::Struct { ctor_kind, fields } + } + }); + + TypeAliasItem(typealias) + } ExternCrateItem { src: _ } | ImportItem(_) | FunctionItem(_) - | TypeAliasItem(_) | OpaqueTyItem(_) | StaticItem(_) | ConstantItem(_) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 9a34e7cce8ad1..4c6e7dfb9873b 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -457,6 +457,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { | clean::StructItem(..) | clean::UnionItem(..) | clean::VariantItem(..) + | clean::TypeAliasItem(..) | clean::ImplItem(..) => { self.cache.parent_stack.push(ParentStackItem::new(&item)); (self.fold_item_recur(item), true) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 09be756f26d22..4f3a792fd7fed 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1270,30 +1270,34 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c } match &t.inner_type { - Some(clean::TypeAliasInnerType::Enum { - variants, - has_stripped_variants: has_stripped_entries, - is_non_exhaustive, - }) => { + Some(clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive }) => { toggle(w, |w| { + let variants_iter = || variants.iter().filter(|i| !i.is_stripped()); wrap_item(w, |w| { + let variants_len = variants.len(); + let variants_count = variants_iter().count(); + let has_stripped_entries = variants_len != variants_count; + write!(w, "enum {}{}", it.name.unwrap(), t.generics.print(cx)); render_enum_fields( w, cx, None, - variants.iter(), - variants.len(), - *has_stripped_entries, + variants_iter(), + variants_count, + has_stripped_entries, *is_non_exhaustive, ) }); - item_variants(w, cx, it, variants.iter()); + item_variants(w, cx, it, variants_iter()); }); } - Some(clean::TypeAliasInnerType::Union { fields, has_stripped_fields }) => { + Some(clean::TypeAliasInnerType::Union { fields }) => { toggle(w, |w| { wrap_item(w, |w| { + let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); + let has_stripped_fields = fields.len() != fields_count; + write!(w, "union {}{}", it.name.unwrap(), t.generics.print(cx)); render_struct_fields( w, @@ -1302,16 +1306,19 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c fields, "", true, - *has_stripped_fields, + has_stripped_fields, cx, ); }); item_fields(w, cx, it, fields, None); }); } - Some(clean::TypeAliasInnerType::Struct { ctor_kind, fields, has_stripped_fields }) => { + Some(clean::TypeAliasInnerType::Struct { ctor_kind, fields }) => { toggle(w, |w| { wrap_item(w, |w| { + let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); + let has_stripped_fields = fields.len() != fields_count; + write!(w, "struct {}{}", it.name.unwrap(), t.generics.print(cx)); render_struct_fields( w, @@ -1320,7 +1327,7 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c fields, "", true, - *has_stripped_fields, + has_stripped_fields, cx, ); }); diff --git a/tests/rustdoc/typedef-inner-variants.rs b/tests/rustdoc/typedef-inner-variants.rs index 46acd86e12fce..cb9c1248c46c5 100644 --- a/tests/rustdoc/typedef-inner-variants.rs +++ b/tests/rustdoc/typedef-inner-variants.rs @@ -22,6 +22,8 @@ pub enum IrTyKind { TyKind(A, B), // no comment StructKind { a: A, }, + #[doc(hidden)] + Unspecified, } // @has 'inner_variants/type.NearlyTyKind.html' @@ -52,7 +54,9 @@ pub type OneOrF64 = OneOr; // @has 'inner_variants/struct.One.html' pub struct One { pub val: T, - __hidden: T, + #[doc(hidden)] + pub __hidden: T, + __private: T, } // @has 'inner_variants/type.OneU64.html' From 9443f8474548842f15d099f11856fde11bf87bd2 Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 18 Aug 2023 19:02:45 +0200 Subject: [PATCH 5/8] rustdoc: normalize all typedef inner types --- src/librustdoc/clean/mod.rs | 25 +++++++++++++++++++++ tests/rustdoc/typedef-inner-variants.rs | 30 +++++++++++++++++++------ 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 892dfe4240b43..0ebec2d9ba23d 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2406,6 +2406,11 @@ pub(crate) fn clean_variant_def_with_args<'tcx>( ty::VariantDiscr::Relative(_) => None, }; + use rustc_middle::traits::ObligationCause; + use rustc_trait_selection::infer::TyCtxtInferExt; + use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; + + let infcx = cx.tcx.infer_ctxt().build(); let kind = match variant.ctor_kind() { Some(CtorKind::Const) => VariantKind::CLike, Some(CtorKind::Fn) => VariantKind::Tuple( @@ -2414,6 +2419,16 @@ pub(crate) fn clean_variant_def_with_args<'tcx>( .iter() .map(|field| { let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args); + + // normalize the type to only show concrete types + // note: we do not use try_normalize_erasing_regions since we + // do care about showing the regions + let ty = infcx + .at(&ObligationCause::dummy(), cx.param_env) + .query_normalize(ty) + .map(|normalized| normalized.value) + .unwrap_or(ty); + clean_field_with_def_id( field.did, field.name, @@ -2429,6 +2444,16 @@ pub(crate) fn clean_variant_def_with_args<'tcx>( .iter() .map(|field| { let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args); + + // normalize the type to only show concrete types + // note: we do not use try_normalize_erasing_regions since we + // do care about showing the regions + let ty = infcx + .at(&ObligationCause::dummy(), cx.param_env) + .query_normalize(ty) + .map(|normalized| normalized.value) + .unwrap_or(ty); + clean_field_with_def_id( field.did, field.name, diff --git a/tests/rustdoc/typedef-inner-variants.rs b/tests/rustdoc/typedef-inner-variants.rs index cb9c1248c46c5..f3ba097ee0878 100644 --- a/tests/rustdoc/typedef-inner-variants.rs +++ b/tests/rustdoc/typedef-inner-variants.rs @@ -8,6 +8,17 @@ extern crate cross_crate_generic_typedef; pub struct Adt; pub struct Ty; +pub struct TyCtxt; + +pub trait Interner { + type Adt; + type Ty; +} + +impl Interner for TyCtxt { + type Adt = Adt; + type Ty = Ty; +} // @has 'inner_variants/type.AliasTy.html' // @count - '//*[@id="variants"]' 0 @@ -15,11 +26,11 @@ pub struct Ty; pub type AliasTy = Ty; // @has 'inner_variants/enum.IrTyKind.html' -pub enum IrTyKind { +pub enum IrTyKind { /// Doc comment for AdtKind - AdtKind(A), + AdtKind(I::Adt), /// and another one for TyKind - TyKind(A, B), + TyKind(I::Adt, ::Ty), // no comment StructKind { a: A, }, #[doc(hidden)] @@ -29,14 +40,18 @@ pub enum IrTyKind { // @has 'inner_variants/type.NearlyTyKind.html' // @count - '//*[@id="variants"]' 0 // @count - '//*[@id="fields"]' 0 -pub type NearlyTyKind = IrTyKind; +pub type NearlyTyKind = IrTyKind; // @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; +// @has - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code/a[1]' "Adt" +// @has - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code/a[2]' "Adt" +// @has - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code/a[3]' "Ty" +// @has - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code/a[4]' "i64" +pub type TyKind = IrTyKind; // @has 'inner_variants/union.OneOr.html' pub union OneOr { @@ -68,13 +83,14 @@ pub type OneU64 = One; // @has 'inner_variants/struct.OnceA.html' pub struct OnceA<'a, A> { - a: &'a A, // private + pub a: &'a A, } // @has 'inner_variants/type.Once.html' // @count - '//*[@id="variants"]' 0 -// @count - '//*[@id="fields"]' 0 +// @count - '//*[@id="fields"]' 1 // @matches - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code' "struct Once<'a>" +// @matches - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code' "&'a" pub type Once<'a> = OnceA<'a, i64>; // @has 'inner_variants/struct.HighlyGenericStruct.html' From 282acb93c91371bfcb9256c274a932d45207fa03 Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 25 Aug 2023 22:36:58 +0200 Subject: [PATCH 6/8] rustdoc: remove details for type alias inner type and fix sidebar --- src/librustdoc/clean/types.rs | 17 +++ src/librustdoc/html/render/print_item.rs | 143 ++++++++--------------- src/librustdoc/html/render/sidebar.rs | 28 ++++- tests/rustdoc/typedef-inner-variants.rs | 26 +++-- 4 files changed, 110 insertions(+), 104 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 9cc5d7c2911f2..ce3f1bcf215a8 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2252,6 +2252,23 @@ pub(crate) struct TypeAlias { pub(crate) item_type: Option, } +impl TypeAlias { + pub(crate) fn should_display_inner_type(&self) -> bool { + // 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. + self.generics + .params + .iter() + .all(|param| matches!(param.kind, GenericParamDefKind::Lifetime { .. })) + && self.generics.where_predicates.is_empty() + && self.type_.generics().is_some_and(|generics| !generics.is_empty()) + } +} + #[derive(Clone, Debug)] pub(crate) struct OpaqueTy { pub(crate) bounds: Vec, diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 4f3a792fd7fed..fc0549c3f64d1 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1237,104 +1237,63 @@ 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: &mut W, f: F) - where - W: fmt::Write, - F: FnOnce(&mut W), - { - write!( - w, - "
\ - \ - Show Aliased Type\ - ", - ) - .unwrap(); - f(w); - write!(w, "
").unwrap(); - } - - match &t.inner_type { - Some(clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive }) => { - toggle(w, |w| { - let variants_iter = || variants.iter().filter(|i| !i.is_stripped()); - wrap_item(w, |w| { - let variants_len = variants.len(); - let variants_count = variants_iter().count(); - let has_stripped_entries = variants_len != variants_count; - - write!(w, "enum {}{}", it.name.unwrap(), t.generics.print(cx)); - render_enum_fields( - w, - cx, - None, - variants_iter(), - variants_count, - has_stripped_entries, - *is_non_exhaustive, - ) - }); - item_variants(w, cx, it, variants_iter()); + if let Some(inner_type) = &t.inner_type && t.should_display_inner_type() { + write!( + w, + "

\ + Aliased Type§

" + ); + + match inner_type { + clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => { + let variants_iter = || variants.iter().filter(|i| !i.is_stripped()); + wrap_item(w, |w| { + let variants_len = variants.len(); + let variants_count = variants_iter().count(); + let has_stripped_entries = variants_len != variants_count; + + write!(w, "enum {}{}", it.name.unwrap(), t.generics.print(cx)); + render_enum_fields( + w, + cx, + None, + variants_iter(), + variants_count, + has_stripped_entries, + *is_non_exhaustive, + ) }); + item_variants(w, cx, it, variants_iter()); } - Some(clean::TypeAliasInnerType::Union { fields }) => { - toggle(w, |w| { - wrap_item(w, |w| { - let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); - let has_stripped_fields = fields.len() != fields_count; - - 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); + clean::TypeAliasInnerType::Union { fields } => { + wrap_item(w, |w| { + let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); + let has_stripped_fields = fields.len() != fields_count; + + 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 }) => { - toggle(w, |w| { - wrap_item(w, |w| { - let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); - let has_stripped_fields = fields.len() != fields_count; - - 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); + clean::TypeAliasInnerType::Struct { ctor_kind, fields } => { + wrap_item(w, |w| { + let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); + let has_stripped_fields = fields.len() != fields_count; + + 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 => {} } } diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index b96b1536156c6..c82a45303a49e 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -82,7 +82,7 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf clean::PrimitiveItem(_) => sidebar_primitive(cx, it), clean::UnionItem(ref u) => sidebar_union(cx, it, u), clean::EnumItem(ref e) => sidebar_enum(cx, it, e), - clean::TypeAliasItem(_) => sidebar_type_alias(cx, it), + clean::TypeAliasItem(ref t) => sidebar_type_alias(cx, it, t), clean::ModuleItem(ref m) => vec![sidebar_module(&m.items)], clean::ForeignTypeItem => sidebar_foreign_type(cx, it), _ => vec![], @@ -230,8 +230,32 @@ fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec> { +fn sidebar_type_alias<'a>( + cx: &'a Context<'_>, + it: &'a clean::Item, + t: &'a clean::TypeAlias, +) -> Vec> { let mut items = vec![]; + if let Some(inner_type) = &t.inner_type && t.should_display_inner_type() { + match inner_type { + clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => { + let mut variants = variants + .iter() + .filter(|i| !i.is_stripped()) + .filter_map(|v| v.name) + .map(|name| Link::new(format!("variant.{name}"), name.to_string())) + .collect::>(); + variants.sort_unstable(); + + items.push(LinkBlock::new(Link::new("variants", "Variants"), variants)); + } + clean::TypeAliasInnerType::Union { fields } + | clean::TypeAliasInnerType::Struct { ctor_kind: _, fields } => { + let fields = get_struct_fields_name(fields); + items.push(LinkBlock::new(Link::new("fields", "Fields"), fields)); + } + } + } sidebar_assoc_items(cx, it, &mut items); items } diff --git a/tests/rustdoc/typedef-inner-variants.rs b/tests/rustdoc/typedef-inner-variants.rs index f3ba097ee0878..31edb0a8706ac 100644 --- a/tests/rustdoc/typedef-inner-variants.rs +++ b/tests/rustdoc/typedef-inner-variants.rs @@ -43,14 +43,15 @@ pub enum IrTyKind { pub type NearlyTyKind = IrTyKind; // @has 'inner_variants/type.TyKind.html' +// @count - '//*[@id="aliased-type"]' 1 // @count - '//*[@id="variants"]' 1 // @count - '//*[@id="fields"]' 0 // @count - '//*[@class="variant"]' 3 -// @matches - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code' "enum TyKind" -// @has - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code/a[1]' "Adt" -// @has - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code/a[2]' "Adt" -// @has - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code/a[3]' "Ty" -// @has - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code/a[4]' "i64" +// @matches - '//pre[@class="rust item-decl"]//code' "enum TyKind" +// @has - '//pre[@class="rust item-decl"]//code/a[1]' "Adt" +// @has - '//pre[@class="rust item-decl"]//code/a[2]' "Adt" +// @has - '//pre[@class="rust item-decl"]//code/a[3]' "Ty" +// @has - '//pre[@class="rust item-decl"]//code/a[4]' "i64" pub type TyKind = IrTyKind; // @has 'inner_variants/union.OneOr.html' @@ -60,10 +61,11 @@ pub union OneOr { } // @has 'inner_variants/type.OneOrF64.html' +// @count - '//*[@id="aliased-type"]' 1 // @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" +// @matches - '//pre[@class="rust item-decl"]//code' "union OneOrF64" pub type OneOrF64 = OneOr; // @has 'inner_variants/struct.One.html' @@ -75,10 +77,12 @@ pub struct One { } // @has 'inner_variants/type.OneU64.html' +// @count - '//*[@id="aliased-type"]' 1 // @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" +// @matches - '//pre[@class="rust item-decl"]//code' "struct OneU64" +// @matches - '//pre[@class="rust item-decl"]//code' "pub val" pub type OneU64 = One; // @has 'inner_variants/struct.OnceA.html' @@ -87,10 +91,11 @@ pub struct OnceA<'a, A> { } // @has 'inner_variants/type.Once.html' +// @count - '//*[@id="aliased-type"]' 1 // @count - '//*[@id="variants"]' 0 // @count - '//*[@id="fields"]' 1 -// @matches - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code' "struct Once<'a>" -// @matches - '//details[@class="toggle"]//pre[@class="rust item-decl"]//code' "&'a" +// @matches - '//pre[@class="rust item-decl"]//code' "struct Once<'a>" +// @matches - '//pre[@class="rust item-decl"]//code' "&'a" pub type Once<'a> = OnceA<'a, i64>; // @has 'inner_variants/struct.HighlyGenericStruct.html' @@ -100,12 +105,13 @@ pub struct HighlyGenericStruct { // VERIFY that we NOT show the Aliased Type // @has 'inner_variants/type.HighlyGenericAABB.html' -// @count - '//details[@class="toggle"]' 0 +// @count - '//*[@id="aliased-type"]' 1 // @count - '//*[@id="variants"]' 0 // @count - '//*[@id="fields"]' 0 pub type HighlyGenericAABB = HighlyGenericStruct; // @has 'inner_variants/type.InlineU64.html' +// @count - '//*[@id="aliased-type"]' 1 // @count - '//*[@id="variants"]' 0 // @count - '//*[@id="fields"]' 1 pub use cross_crate_generic_typedef::InlineU64; From 706d010c8b590f40cc2fb3de7b744d9a59a93ac9 Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 25 Aug 2023 23:07:03 +0200 Subject: [PATCH 7/8] rustdoc: always print type alias inner type (with it's where clauses) --- src/librustdoc/clean/types.rs | 17 ---------- src/librustdoc/html/render/print_item.rs | 17 +++++++--- src/librustdoc/html/render/sidebar.rs | 2 +- .../typedef-inner-variants-lazy_type_alias.rs | 34 +++++++++++++++++++ tests/rustdoc/typedef-inner-variants.rs | 8 +++-- 5 files changed, 53 insertions(+), 25 deletions(-) create mode 100644 tests/rustdoc/typedef-inner-variants-lazy_type_alias.rs diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index ce3f1bcf215a8..9cc5d7c2911f2 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2252,23 +2252,6 @@ pub(crate) struct TypeAlias { pub(crate) item_type: Option, } -impl TypeAlias { - pub(crate) fn should_display_inner_type(&self) -> bool { - // 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. - self.generics - .params - .iter() - .all(|param| matches!(param.kind, GenericParamDefKind::Lifetime { .. })) - && self.generics.where_predicates.is_empty() - && self.type_.generics().is_some_and(|generics| !generics.is_empty()) - } -} - #[derive(Clone, Debug)] pub(crate) struct OpaqueTy { pub(crate) bounds: Vec, diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index fc0549c3f64d1..2da7cb01c9016 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1237,7 +1237,7 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); - if let Some(inner_type) = &t.inner_type && t.should_display_inner_type() { + if let Some(inner_type) = &t.inner_type { write!( w, "

\ @@ -1256,7 +1256,7 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c render_enum_fields( w, cx, - None, + Some(&t.generics), variants_iter(), variants_count, has_stripped_entries, @@ -1271,7 +1271,16 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c let has_stripped_fields = fields.len() != fields_count; write!(w, "union {}{}", it.name.unwrap(), t.generics.print(cx)); - render_struct_fields(w, None, None, fields, "", true, has_stripped_fields, cx); + render_struct_fields( + w, + Some(&t.generics), + None, + fields, + "", + true, + has_stripped_fields, + cx, + ); }); item_fields(w, cx, it, fields, None); } @@ -1283,7 +1292,7 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c write!(w, "struct {}{}", it.name.unwrap(), t.generics.print(cx)); render_struct_fields( w, - None, + Some(&t.generics), *ctor_kind, fields, "", diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index c82a45303a49e..fce31a8423d16 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -236,7 +236,7 @@ fn sidebar_type_alias<'a>( t: &'a clean::TypeAlias, ) -> Vec> { let mut items = vec![]; - if let Some(inner_type) = &t.inner_type && t.should_display_inner_type() { + if let Some(inner_type) = &t.inner_type { match inner_type { clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => { let mut variants = variants diff --git a/tests/rustdoc/typedef-inner-variants-lazy_type_alias.rs b/tests/rustdoc/typedef-inner-variants-lazy_type_alias.rs new file mode 100644 index 0000000000000..ff84352d7169f --- /dev/null +++ b/tests/rustdoc/typedef-inner-variants-lazy_type_alias.rs @@ -0,0 +1,34 @@ +#![crate_name = "inner_types_lazy"] + +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +// @has 'inner_types_lazy/struct.Pair.html' +pub struct Pair { + pub first: A, + pub second: B, +} + +// @has 'inner_types_lazy/type.ReversedTypesPair.html' +// @count - '//*[@id="aliased-type"]' 1 +// @count - '//*[@id="variants"]' 0 +// @count - '//*[@id="fields"]' 1 +// @count - '//span[@class="where fmt-newline"]' 0 +pub type ReversedTypesPair = Pair; + +// @has 'inner_types_lazy/type.ReadWrite.html' +// @count - '//*[@id="aliased-type"]' 1 +// @count - '//*[@id="variants"]' 0 +// @count - '//*[@id="fields"]' 1 +// @count - '//span[@class="where fmt-newline"]' 2 +pub type ReadWrite = Pair +where + R: std::io::Read, + W: std::io::Write; + +// @has 'inner_types_lazy/type.VecPair.html' +// @count - '//*[@id="aliased-type"]' 1 +// @count - '//*[@id="variants"]' 0 +// @count - '//*[@id="fields"]' 1 +// @count - '//span[@class="where fmt-newline"]' 0 +pub type VecPair = Pair, Vec>; diff --git a/tests/rustdoc/typedef-inner-variants.rs b/tests/rustdoc/typedef-inner-variants.rs index 31edb0a8706ac..b734714fd6432 100644 --- a/tests/rustdoc/typedef-inner-variants.rs +++ b/tests/rustdoc/typedef-inner-variants.rs @@ -38,7 +38,8 @@ pub enum IrTyKind { } // @has 'inner_variants/type.NearlyTyKind.html' -// @count - '//*[@id="variants"]' 0 +// @count - '//*[@id="aliased-type"]' 1 +// @count - '//*[@id="variants"]' 1 // @count - '//*[@id="fields"]' 0 pub type NearlyTyKind = IrTyKind; @@ -103,11 +104,12 @@ pub struct HighlyGenericStruct { pub z: (A, B, C, D) } -// VERIFY that we NOT show the Aliased Type // @has 'inner_variants/type.HighlyGenericAABB.html' // @count - '//*[@id="aliased-type"]' 1 // @count - '//*[@id="variants"]' 0 -// @count - '//*[@id="fields"]' 0 +// @count - '//*[@id="fields"]' 1 +// @matches - '//pre[@class="rust item-decl"]//code' "struct HighlyGenericAABB" +// @matches - '//pre[@class="rust item-decl"]//code' "pub z" pub type HighlyGenericAABB = HighlyGenericStruct; // @has 'inner_variants/type.InlineU64.html' From 85e4b89cf615c112c72243a2d8f4a55010ecbd4b Mon Sep 17 00:00:00 2001 From: Urgau Date: Mon, 28 Aug 2023 10:00:26 +0200 Subject: [PATCH 8/8] rustdoc: start new "Sections" section in the book with Aliased Type --- src/doc/rustdoc/src/how-to-read-rustdoc.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/doc/rustdoc/src/how-to-read-rustdoc.md b/src/doc/rustdoc/src/how-to-read-rustdoc.md index 9deb7009cfe00..cd6e29ffd6637 100644 --- a/src/doc/rustdoc/src/how-to-read-rustdoc.md +++ b/src/doc/rustdoc/src/how-to-read-rustdoc.md @@ -38,6 +38,22 @@ followed by a list of fields or variants for Rust types. Finally, the page lists associated functions and trait implementations, including automatic and blanket implementations that `rustdoc` knows about. +### Sections + + + + + + +#### Aliased Type + +A type alias is expanded at compile time to its +[aliased type](https://doc.rust-lang.org/reference/items/type-aliases.html). +That may involve substituting some or all of the type parameters in the target +type with types provided by the type alias definition. The Aliased Type section +shows the result of this expansion, including the types of public fields or +variants, which may depend on those substitutions. + ### Navigation Subheadings, variants, fields, and many other things in this documentation