diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 15890fff0c336..10593b824fd70 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -608,6 +608,9 @@ impl Item { UnionItem(ref union_) => Some(union_.has_stripped_entries()), EnumItem(ref enum_) => Some(enum_.has_stripped_entries()), VariantItem(ref v) => v.has_stripped_entries(), + TypeAliasItem(ref type_alias) => { + type_alias.inner_type.as_ref().and_then(|t| t.has_stripped_entries()) + } _ => None, } } @@ -759,14 +762,11 @@ impl Item { Some(tcx.visibility(def_id)) } - pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Vec { + pub(crate) fn attributes_witout_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec { const ALLOWED_ATTRIBUTES: &[Symbol] = &[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive]; - use rustc_abi::IntegerType; - - let mut attrs: Vec = self - .attrs + self.attrs .other_attrs .iter() .filter_map(|attr| { @@ -774,7 +774,7 @@ impl Item { match attr { hir::Attribute::Parsed(AttributeKind::Deprecation { .. }) => { // rustdoc-json stores this in `Item::deprecation`, so we - // don't want it it `Item::attrs`. + // don't want it in `Item::attrs`. None } rustc_hir::Attribute::Parsed(rustc_attr_parsing::AttributeKind::Repr( @@ -803,73 +803,28 @@ impl Item { None } }) - .collect(); + .collect() + } - // Add #[repr(...)] - if let Some(def_id) = self.def_id() - && let ItemType::Struct | ItemType::Enum | ItemType::Union = self.type_() - { - let adt = tcx.adt_def(def_id); - let repr = adt.repr(); - let mut out = Vec::new(); - if repr.c() { - out.push("C"); - } - if repr.transparent() { - // Render `repr(transparent)` iff the non-1-ZST field is public or at least one - // field is public in case all fields are 1-ZST fields. - let render_transparent = cache.document_private - || adt - .all_fields() - .find(|field| { - let ty = - field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did)); - tcx.layout_of( - ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty), - ) - .is_ok_and(|layout| !layout.is_1zst()) - }) - .map_or_else( - || adt.all_fields().any(|field| field.vis.is_public()), - |field| field.vis.is_public(), - ); + pub(crate) fn attributes_and_repr( + &self, + tcx: TyCtxt<'_>, + cache: &Cache, + is_json: bool, + ) -> Vec { + let mut attrs = self.attributes_witout_repr(tcx, is_json); - if render_transparent { - out.push("transparent"); - } - } - if repr.simd() { - out.push("simd"); - } - let pack_s; - if let Some(pack) = repr.pack { - pack_s = format!("packed({})", pack.bytes()); - out.push(&pack_s); - } - let align_s; - if let Some(align) = repr.align { - align_s = format!("align({})", align.bytes()); - out.push(&align_s); - } - let int_s; - if let Some(int) = repr.int { - int_s = match int { - IntegerType::Pointer(is_signed) => { - format!("{}size", if is_signed { 'i' } else { 'u' }) - } - IntegerType::Fixed(size, is_signed) => { - format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) - } - }; - out.push(&int_s); - } - if !out.is_empty() { - attrs.push(format!("#[repr({})]", out.join(", "))); - } + if let Some(repr_attr) = self.repr(tcx, cache) { + attrs.push(repr_attr); } attrs } + /// Returns a `#[repr(...)]` representation. + pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache) -> Option { + repr_attributes(tcx, cache, self.def_id()?, self.type_()) + } + pub fn is_doc_hidden(&self) -> bool { self.attrs.is_doc_hidden() } @@ -879,6 +834,71 @@ impl Item { } } +pub(crate) fn repr_attributes( + tcx: TyCtxt<'_>, + cache: &Cache, + def_id: DefId, + item_type: ItemType, +) -> Option { + use rustc_abi::IntegerType; + + if !matches!(item_type, ItemType::Struct | ItemType::Enum | ItemType::Union) { + return None; + } + let adt = tcx.adt_def(def_id); + let repr = adt.repr(); + let mut out = Vec::new(); + if repr.c() { + out.push("C"); + } + if repr.transparent() { + // Render `repr(transparent)` iff the non-1-ZST field is public or at least one + // field is public in case all fields are 1-ZST fields. + let render_transparent = cache.document_private + || adt + .all_fields() + .find(|field| { + let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did)); + tcx.layout_of(ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty)) + .is_ok_and(|layout| !layout.is_1zst()) + }) + .map_or_else( + || adt.all_fields().any(|field| field.vis.is_public()), + |field| field.vis.is_public(), + ); + + if render_transparent { + out.push("transparent"); + } + } + if repr.simd() { + out.push("simd"); + } + let pack_s; + if let Some(pack) = repr.pack { + pack_s = format!("packed({})", pack.bytes()); + out.push(&pack_s); + } + let align_s; + if let Some(align) = repr.align { + align_s = format!("align({})", align.bytes()); + out.push(&align_s); + } + let int_s; + if let Some(int) = repr.int { + int_s = match int { + IntegerType::Pointer(is_signed) => { + format!("{}size", if is_signed { 'i' } else { 'u' }) + } + IntegerType::Fixed(size, is_signed) => { + format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) + } + }; + out.push(&int_s); + } + if !out.is_empty() { Some(format!("#[repr({})]", out.join(", "))) } else { None } +} + #[derive(Clone, Debug)] pub(crate) enum ItemKind { ExternCrateItem { @@ -2113,7 +2133,7 @@ impl Enum { self.variants.iter().any(|f| f.is_stripped()) } - pub(crate) fn variants(&self) -> impl Iterator { + pub(crate) fn non_stripped_variants(&self) -> impl Iterator { self.variants.iter().filter(|v| !v.is_stripped()) } } @@ -2351,6 +2371,16 @@ pub(crate) enum TypeAliasInnerType { Struct { ctor_kind: Option, fields: Vec }, } +impl TypeAliasInnerType { + fn has_stripped_entries(&self) -> Option { + Some(match self { + Self::Enum { variants, .. } => variants.iter().any(|v| v.is_stripped()), + Self::Union { fields } => fields.iter().any(|f| f.is_stripped()), + Self::Struct { fields, .. } => fields.iter().any(|f| f.is_stripped()), + }) + } +} + #[derive(Clone, Debug)] pub(crate) struct TypeAlias { pub(crate) type_: Type, diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index beaa6497b8caa..baee5e283bef7 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1194,18 +1194,38 @@ fn render_assoc_item( // a whitespace prefix and newline. fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display { fmt::from_fn(move |f| { - for a in it.attributes(cx.tcx(), cx.cache(), false) { + for a in it.attributes_and_repr(cx.tcx(), cx.cache(), false) { writeln!(f, "{prefix}{a}")?; } Ok(()) }) } +struct CodeAttribute(String); + +impl CodeAttribute { + fn render_into(self, w: &mut impl fmt::Write) { + write!(w, "
{}
", self.0).unwrap(); + } +} + // When an attribute is rendered inside a tag, it is formatted using // a div to produce a newline after it. fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) { - for attr in it.attributes(cx.tcx(), cx.cache(), false) { - write!(w, "
{attr}
").unwrap(); + for attr in it.attributes_and_repr(cx.tcx(), cx.cache(), false) { + CodeAttribute(attr).render_into(w); + } +} + +/// used for type aliases to only render their `repr` attribute. +fn render_repr_attributes_in_code( + w: &mut impl fmt::Write, + cx: &Context<'_>, + def_id: DefId, + item_type: ItemType, +) { + if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) { + CodeAttribute(repr).render_into(w); } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 39a631b637bd4..5f81ec68548ff 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -20,7 +20,7 @@ use super::{ collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference, item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls, render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre, - render_impl, render_rightside, render_stability_since_raw, + render_impl, render_repr_attributes_in_code, render_rightside, render_stability_since_raw, render_stability_since_raw_with_extra, write_section_heading, }; use crate::clean; @@ -1278,94 +1278,58 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> match inner_type { clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => { - let variants_iter = || variants.iter().filter(|i| !i.is_stripped()); let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity(); let enum_def_id = ty.ty_adt_def().unwrap().did(); - 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( - cx, - Some(&t.generics), - variants, - variants_count, - has_stripped_entries, - *is_non_exhaustive, - enum_def_id, - ) - ) - })?; - write!(w, "{}", item_variants(cx, it, variants, enum_def_id))?; + DisplayEnum { + variants, + generics: &t.generics, + is_non_exhaustive: *is_non_exhaustive, + def_id: enum_def_id, + } + .render_into(cx, it, true, w)?; } 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; + let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity(); + let union_def_id = ty.ty_adt_def().unwrap().did(); - write!( - w, - "union {}{}{}", - it.name.unwrap(), - t.generics.print(cx), - render_struct_fields( - Some(&t.generics), - None, - fields, - "", - true, - has_stripped_fields, - cx, - ), - ) - })?; - write!(w, "{}", item_fields(cx, it, fields, None))?; + ItemUnion { + cx, + it, + fields, + generics: &t.generics, + is_type_alias: true, + def_id: union_def_id, + } + .render_into(w)?; } 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; + let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity(); + let struct_def_id = ty.ty_adt_def().unwrap().did(); - write!( - w, - "struct {}{}{}", - it.name.unwrap(), - t.generics.print(cx), - render_struct_fields( - Some(&t.generics), - *ctor_kind, - fields, - "", - true, - has_stripped_fields, - cx, - ), - ) - })?; - write!(w, "{}", item_fields(cx, it, fields, None))?; + DisplayStruct { + ctor_kind: *ctor_kind, + generics: &t.generics, + fields, + def_id: struct_def_id, + } + .render_into(cx, it, true, w)?; } } + } else { + 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 + // associated items from the aliased type (see discussion in #32077), but + // we need #14072 to make sense of the generics. + write!( + w, + "{}{}", + render_assoc_items(cx, it, def_id, AssocItemRender::All), + document_type_layout(cx, def_id) + )?; } - 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 - // associated items from the aliased type (see discussion in #32077), but - // we need #14072 to make sense of the generics. - write!( - w, - "{}{}", - render_assoc_items(cx, it, def_id, AssocItemRender::All), - document_type_layout(cx, def_id) - )?; - // [RUSTDOCIMPL] type.impl // // Include type definitions from the alias target type. @@ -1463,50 +1427,82 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> }) } -fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display { - item_template!( - #[template(path = "item_union.html")] - struct ItemUnion<'a, 'cx> { - cx: &'a Context<'cx>, - it: &'a clean::Item, - s: &'a clean::Union, - }, - methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items] - ); +item_template!( + #[template(path = "item_union.html")] + struct ItemUnion<'a, 'cx> { + cx: &'a Context<'cx>, + it: &'a clean::Item, + fields: &'a [clean::Item], + generics: &'a clean::Generics, + is_type_alias: bool, + def_id: DefId, + }, + methods = [document, document_type_layout, render_assoc_items] +); + +impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { + fn render_union(&self) -> impl Display { + render_union(self.it, Some(&self.generics), &self.fields, self.cx) + } - impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { - fn render_union(&self) -> impl Display { - render_union(self.it, Some(&self.s.generics), &self.s.fields, self.cx) - } + fn document_field(&self, field: &'a clean::Item) -> impl Display { + document(self.cx, field, Some(self.it), HeadingOffset::H3) + } - fn document_field(&self, field: &'a clean::Item) -> impl Display { - document(self.cx, field, Some(self.it), HeadingOffset::H3) - } + fn stability_field(&self, field: &clean::Item) -> Option { + field.stability_class(self.cx.tcx()) + } - fn stability_field(&self, field: &clean::Item) -> Option { - field.stability_class(self.cx.tcx()) - } + fn print_ty(&self, ty: &'a clean::Type) -> impl Display { + ty.print(self.cx) + } - fn print_ty(&self, ty: &'a clean::Type) -> impl Display { - ty.print(self.cx) - } + fn fields_iter( + &self, + ) -> iter::Peekable> { + self.fields + .iter() + .filter_map(|f| match f.kind { + clean::StructFieldItem(ref ty) => Some((f, ty)), + _ => None, + }) + .peekable() + } - fn fields_iter( - &self, - ) -> iter::Peekable> { - self.s - .fields - .iter() - .filter_map(|f| match f.kind { - clean::StructFieldItem(ref ty) => Some((f, ty)), - _ => None, - }) - .peekable() - } + fn render_attributes_in_pre(&self) -> impl fmt::Display { + fmt::from_fn(move |f| { + if !self.is_type_alias { + for a in self.it.attributes_and_repr(self.cx.tcx(), self.cx.cache(), false) { + writeln!(f, "{a}")?; + } + } else { + // For now we only render `repr` attributes for type aliases. + if let Some(repr) = clean::repr_attributes( + self.cx.tcx(), + self.cx.cache(), + self.def_id, + ItemType::Union, + ) { + writeln!(f, "{repr}")?; + }; + } + Ok(()) + }) } +} +fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display { fmt::from_fn(|w| { - ItemUnion { cx, it, s }.render_into(w).unwrap(); + ItemUnion { + cx, + it, + fields: &s.fields, + generics: &s.generics, + is_type_alias: false, + def_id: it.def_id().unwrap(), + } + .render_into(w) + .unwrap(); Ok(()) }) } @@ -1533,41 +1529,79 @@ fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Displa }) } -fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display { - fmt::from_fn(|w| { - let count_variants = e.variants().count(); +struct DisplayEnum<'a> { + variants: &'a IndexVec, + generics: &'a clean::Generics, + is_non_exhaustive: bool, + def_id: DefId, +} + +impl<'a> DisplayEnum<'a> { + fn render_into( + self, + cx: &Context<'_>, + it: &clean::Item, + is_type_alias: bool, + w: &mut W, + ) -> fmt::Result { + let variants_count = self.variants.iter().filter(|i| !i.is_stripped()).count(); + let variants_len = self.variants.len(); + let has_stripped_entries = variants_len != variants_count; + wrap_item(w, |w| { - render_attributes_in_code(w, it, cx); + if !is_type_alias { + render_attributes_in_code(w, it, cx); + } else { + // For now we only render `repr` attributes for type aliases. + render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Enum); + } write!( w, "{}enum {}{}{}", visibility_print_with_space(it, cx), it.name.unwrap(), - e.generics.print(cx), + self.generics.print(cx), render_enum_fields( cx, - Some(&e.generics), - &e.variants, - count_variants, - e.has_stripped_entries(), - it.is_non_exhaustive(), - it.def_id().unwrap(), + Some(self.generics), + self.variants, + variants_count, + has_stripped_entries, + self.is_non_exhaustive, + self.def_id, ), ) })?; - write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; + let def_id = it.item_id.expect_def_id(); + let layout_def_id = if !is_type_alias { + write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; + def_id + } else { + self.def_id + }; - if count_variants != 0 { - write!(w, "{}", item_variants(cx, it, &e.variants, it.def_id().unwrap()))?; + if variants_count != 0 { + write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?; } - let def_id = it.item_id.expect_def_id(); write!( w, "{}{}", render_assoc_items(cx, it, def_id, AssocItemRender::All), - document_type_layout(cx, def_id) + document_type_layout(cx, layout_def_id) ) + } +} + +fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display { + fmt::from_fn(|w| { + DisplayEnum { + variants: &e.variants, + generics: &e.generics, + is_non_exhaustive: it.is_non_exhaustive(), + def_id: it.def_id().unwrap(), + } + .render_into(cx, it, false, w) }) } @@ -1955,27 +1989,59 @@ fn item_constant( }) } -fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display { - fmt::from_fn(|w| { +struct DisplayStruct<'a> { + ctor_kind: Option, + generics: &'a clean::Generics, + fields: &'a [clean::Item], + def_id: DefId, +} + +impl<'a> DisplayStruct<'a> { + fn render_into( + self, + cx: &Context<'_>, + it: &clean::Item, + is_type_alias: bool, + w: &mut W, + ) -> fmt::Result { wrap_item(w, |w| { - render_attributes_in_code(w, it, cx); + if !is_type_alias { + render_attributes_in_code(w, it, cx); + } else { + // For now we only render `repr` attributes for type aliases. + render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Struct); + } write!( w, "{}", - render_struct(it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx) + render_struct(it, Some(self.generics), self.ctor_kind, self.fields, "", true, cx) ) })?; - let def_id = it.item_id.expect_def_id(); + if !is_type_alias { + write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?; + } + let def_id = it.item_id.expect_def_id(); write!( w, - "{}{}{}{}", - document(cx, it, None, HeadingOffset::H2), - item_fields(cx, it, &s.fields, s.ctor_kind), + "{}{}{}", + item_fields(cx, it, self.fields, self.ctor_kind), render_assoc_items(cx, it, def_id, AssocItemRender::All), document_type_layout(cx, def_id), ) + } +} + +fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display { + fmt::from_fn(|w| { + DisplayStruct { + ctor_kind: s.ctor_kind, + generics: &s.generics, + fields: s.fields.as_slice(), + def_id: it.def_id().unwrap(), + } + .render_into(cx, it, false, w) }) } diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index cd0c9775f5c97..e94c888366c5b 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -593,7 +593,7 @@ fn sidebar_enum<'a>( deref_id_map: &'a DefIdMap, ) { let mut variants = e - .variants() + .non_stripped_variants() .filter_map(|v| v.name) .map(|name| Link::new(format!("variant.{name}"), name.to_string())) .collect::>(); diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html index b1c1d5a63a03c..99a9bc874dd59 100644 --- a/src/librustdoc/html/templates/item_union.html +++ b/src/librustdoc/html/templates/item_union.html @@ -2,15 +2,16 @@ {{ self.render_attributes_in_pre()|safe }} {{ self.render_union()|safe }}
-{{ self.document()|safe }} +{% if !self.is_type_alias %} + {{ self.document()|safe }} +{% endif %} {% if self.fields_iter().peek().is_some() %}

{# #} Fields§ {# #}

{% for (field, ty) in self.fields_iter() %} {% let name = field.name.expect("union field name") %} - {# #} + {# #} § {# #} {{ name }}: {{+ self.print_ty(ty)|safe }} {# #} diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index f446c9fbbd8b3..8ad540e9235ce 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -40,7 +40,7 @@ impl JsonRenderer<'_> { }) .collect(); let docs = item.opt_doc_value(); - let attrs = item.attributes(self.tcx, self.cache(), true); + let attrs = item.attributes_and_repr(self.tcx, self.cache(), true); let span = item.span(self.tcx); let visibility = item.visibility(self.tcx); let clean::ItemInner { name, item_id, .. } = *item.inner; diff --git a/tests/rustdoc/type-alias/repr.rs b/tests/rustdoc/type-alias/repr.rs new file mode 100644 index 0000000000000..cf90798036099 --- /dev/null +++ b/tests/rustdoc/type-alias/repr.rs @@ -0,0 +1,42 @@ +// This test ensures that the `repr` attribute is displayed in type aliases. +// +// Regression test for . + +#![crate_name = "foo"] + +/// bla +#[repr(C)] +pub struct Foo1; + +//@ has 'foo/type.Bar1.html' +//@ has - '//*[@class="rust item-decl"]/code' '#[repr(C)]pub struct Bar1;' +// Ensures that we see the doc comment of the type alias and not of the aliased type. +//@ has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'bar' +/// bar +pub type Bar1 = Foo1; + +/// bla +#[repr(C)] +pub union Foo2 { + pub a: u8, +} + +//@ has 'foo/type.Bar2.html' +//@ matches - '//*[@class="rust item-decl"]' '#\[repr\(C\)\]\npub union Bar2 \{*' +// Ensures that we see the doc comment of the type alias and not of the aliased type. +//@ has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'bar' +/// bar +pub type Bar2 = Foo2; + +/// bla +#[repr(C)] +pub enum Foo3 { + A, +} + +//@ has 'foo/type.Bar3.html' +//@ matches - '//*[@class="rust item-decl"]' '#\[repr\(C\)\]pub enum Bar3 \{*' +// Ensures that we see the doc comment of the type alias and not of the aliased type. +//@ has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'bar' +/// bar +pub type Bar3 = Foo3; diff --git a/tests/rustdoc/type-layout.rs b/tests/rustdoc/type-layout.rs index 6de435dbcc143..482b8b597dd30 100644 --- a/tests/rustdoc/type-layout.rs +++ b/tests/rustdoc/type-layout.rs @@ -61,7 +61,7 @@ pub type TypeAlias = X; pub type GenericTypeAlias = (Generic<(u32, ())>, Generic); // Regression test for the rustdoc equivalent of #85103. -//@ hasraw type_layout/type.Edges.html 'Encountered an error during type layout; the type failed to be normalized.' +//@ hasraw type_layout/type.Edges.html 'Unable to compute type layout, possibly due to this type having generic parameters. Layout can only be computed for concrete, fully-instantiated types.' pub type Edges<'a, E> = std::borrow::Cow<'a, [E]>; //@ !hasraw type_layout/trait.MyTrait.html 'Size: '