diff --git a/src/doc/rustdoc/src/advanced-features.md b/src/doc/rustdoc/src/advanced-features.md index c02c9aebe7eef..f49edb2ac78b4 100644 --- a/src/doc/rustdoc/src/advanced-features.md +++ b/src/doc/rustdoc/src/advanced-features.md @@ -89,20 +89,30 @@ https://doc.rust-lang.org/stable/std/?search=%s&go_to_first=true This URL adds the `go_to_first=true` query parameter which can be appended to any `rustdoc` search URL to automatically go to the first result. -## `#[repr(transparent)]`: Documenting the transparent representation +## `#[repr(...)]`: Documenting the representation of a type + +Generally, rustdoc only displays the representation of a given type if none of its variants are +`#[doc(hidden)]` and if all of its fields are public and not `#[doc(hidden)]` since it's likely +not meant to be considered part of the public ABI otherwise. + +Note that there's no way to overwrite that heuristic and force rustdoc to show the representation +regardless. + +### `#[repr(transparent)]` You can read more about `#[repr(transparent)]` itself in the [Rust Reference][repr-trans-ref] and in the [Rustonomicon][repr-trans-nomicon]. Since this representation is only considered part of the public ABI if the single field with non-trivial -size or alignment is public and if the documentation does not state otherwise, Rustdoc helpfully displays -the attribute if and only if the non-1-ZST field is public or at least one field is public in case all -fields are 1-ZST fields. The term *1-ZST* refers to types that are one-aligned and zero-sized. +size or alignment is public and if the documentation does not state otherwise, rustdoc helpfully displays +the attribute if and only if the non-1-ZST field is public and not `#[doc(hidden)]` or +– in case all fields are 1-ZST fields — at least one field is public and not `#[doc(hidden)]`. +The term *1-ZST* refers to types that are one-aligned and zero-sized. It would seem that one can manually hide the attribute with `#[cfg_attr(not(doc), repr(transparent))]` if one wishes to declare the representation as private even if the non-1-ZST field is public. However, due to [current limitations][cross-crate-cfg-doc], this method is not always guaranteed to work. -Therefore, if you would like to do so, you should always write it down in prose independently of whether +Therefore, if you would like to do so, you should always write that down in prose independently of whether you use `cfg_attr` or not. [repr-trans-ref]: https://doc.rust-lang.org/reference/type-layout.html#the-transparent-representation diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index dcd67cb7ebc77..53ae8afc027d0 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -801,50 +801,6 @@ impl Item { Some(tcx.visibility(def_id)) } - /// Get a list of attributes excluding `#[repr]` to display. - /// - /// Only used by the HTML output-format. - fn attributes_without_repr(&self) -> Vec { - self.attrs - .other_attrs - .iter() - .filter_map(|attr| match attr { - hir::Attribute::Parsed(AttributeKind::LinkSection { name, .. }) => { - Some(format!("#[unsafe(link_section = \"{name}\")]")) - } - hir::Attribute::Parsed(AttributeKind::NoMangle(..)) => { - Some("#[unsafe(no_mangle)]".to_string()) - } - hir::Attribute::Parsed(AttributeKind::ExportName { name, .. }) => { - Some(format!("#[unsafe(export_name = \"{name}\")]")) - } - hir::Attribute::Parsed(AttributeKind::NonExhaustive(..)) => { - Some("#[non_exhaustive]".to_string()) - } - _ => None, - }) - .collect() - } - - /// Get a list of attributes to display on this item. - /// - /// Only used by the HTML output-format. - pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache) -> Vec { - let mut attrs = self.attributes_without_repr(); - - if let Some(repr_attr) = self.repr(tcx, cache) { - attrs.push(repr_attr); - } - attrs - } - - /// Returns a stringified `#[repr(...)]` attribute. - /// - /// Only used by the HTML output-format. - 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() } @@ -854,74 +810,6 @@ impl Item { } } -/// Return a string representing the `#[repr]` attribute if present. -/// -/// Only used by the HTML output-format. -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 { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index b4ef47d1e2694..143911cb104da 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -51,7 +51,9 @@ use askama::Template; use itertools::Either; use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; -use rustc_hir::attrs::{DeprecatedSince, Deprecation}; +use rustc_hir as hir; +use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation}; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ConstStability, Mutability, RustcVersion, StabilityLevel, StableSince}; use rustc_middle::ty::print::PrintTraitRefExt; @@ -1310,43 +1312,6 @@ fn render_assoc_item( }) } -struct CodeAttribute(String); - -fn render_code_attribute(prefix: &str, code_attr: CodeAttribute, w: &mut impl fmt::Write) { - write!( - w, - "
{prefix}{attr}
", - prefix = prefix, - attr = code_attr.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, - prefix: &str, - cx: &Context<'_>, -) { - for attr in it.attributes(cx.tcx(), cx.cache()) { - render_code_attribute(prefix, CodeAttribute(attr), 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) { - render_code_attribute("", CodeAttribute(repr), w); - } -} - #[derive(Copy, Clone)] enum AssocItemLink<'a> { Anchor(Option<&'a str>), @@ -2959,3 +2924,142 @@ fn render_call_locations( w.write_str("") } + +fn render_attributes_in_code( + w: &mut impl fmt::Write, + item: &clean::Item, + prefix: &str, + cx: &Context<'_>, +) { + for attr in &item.attrs.other_attrs { + let hir::Attribute::Parsed(kind) = attr else { continue }; + let attr = match kind { + AttributeKind::LinkSection { name, .. } => { + Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}")))) + } + AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"), + AttributeKind::ExportName { name, .. } => { + Cow::Owned(format!("#[unsafe(export_name = {})]", Escape(&format!("{name:?}")))) + } + AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"), + _ => continue, + }; + render_code_attribute(prefix, attr.as_ref(), w); + } + + if let Some(def_id) = item.def_id() + && let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) + { + render_code_attribute(prefix, &repr, w); + } +} + +fn render_repr_attribute_in_code(w: &mut impl fmt::Write, cx: &Context<'_>, def_id: DefId) { + if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) { + render_code_attribute("", &repr, w); + } +} + +fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) { + write!(w, "
{prefix}{attr}
").unwrap(); +} + +/// Compute the *public* `#[repr]` of the item given by `DefId`. +/// +/// Read more about it here: +/// . +fn repr_attribute<'tcx>( + tcx: TyCtxt<'tcx>, + cache: &Cache, + def_id: DefId, +) -> Option> { + let adt = match tcx.def_kind(def_id) { + DefKind::Struct | DefKind::Enum | DefKind::Union => tcx.adt_def(def_id), + _ => return None, + }; + let repr = adt.repr(); + + let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id); + let is_public_field = |field: &ty::FieldDef| { + (cache.document_private || field.vis.is_public()) && is_visible(field.did) + }; + + if repr.transparent() { + // The transparent repr is public iff the non-1-ZST field is public and visible or + // – in case all fields are 1-ZST fields — at least one field is public and visible. + let is_public = 'is_public: { + // `#[repr(transparent)]` can only be applied to structs and single-variant enums. + let var = adt.variant(rustc_abi::FIRST_VARIANT); // the first and only variant + + if !is_visible(var.def_id) { + break 'is_public false; + } + + // Side note: There can only ever be one or zero non-1-ZST fields. + let non_1zst_field = var.fields.iter().find(|field| { + let ty = ty::TypingEnv::post_analysis(tcx, field.did) + .as_query_input(tcx.type_of(field.did).instantiate_identity()); + tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst()) + }); + + match non_1zst_field { + Some(field) => is_public_field(field), + None => var.fields.is_empty() || var.fields.iter().any(is_public_field), + } + }; + + // Since the transparent repr can't have any other reprs or + // repr modifiers beside it, we can safely return early here. + return is_public.then(|| "#[repr(transparent)]".into()); + } + + // Fast path which avoids looking through the variants and fields in + // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`. + // FIXME: This check is not very robust / forward compatible! + if !repr.c() + && !repr.simd() + && repr.int.is_none() + && repr.pack.is_none() + && repr.align.is_none() + { + return None; + } + + // The repr is public iff all components are public and visible. + let is_public = adt + .variants() + .iter() + .all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field)); + if !is_public { + return None; + } + + let mut result = Vec::>::new(); + + if repr.c() { + result.push("C".into()); + } + if repr.simd() { + result.push("simd".into()); + } + if let Some(int) = repr.int { + let prefix = if int.is_signed() { 'i' } else { 'u' }; + let int = match int { + rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"), + rustc_abi::IntegerType::Fixed(int, _) => { + format!("{prefix}{}", int.size().bytes() * 8) + } + }; + result.push(int.into()); + } + + // Render modifiers last. + if let Some(pack) = repr.pack { + result.push(format!("packed({})", pack.bytes()).into()); + } + if let Some(align) = repr.align { + result.push(format!("align({})", align.bytes()).into()); + } + + (!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into()) +} diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index afa438f259690..adfc7481c73ae 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -21,7 +21,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_impl, - render_repr_attributes_in_code, render_rightside, render_stability_since_raw, + render_repr_attribute_in_code, render_rightside, render_stability_since_raw, render_stability_since_raw_with_extra, write_section_heading, }; use crate::clean; @@ -1555,7 +1555,7 @@ impl<'clean> DisplayEnum<'clean> { wrap_item(w, |w| { if is_type_alias { // For now the only attributes we render for type aliases are `repr` attributes. - render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Enum); + render_repr_attribute_in_code(w, cx, self.def_id); } else { render_attributes_in_code(w, it, "", cx); } @@ -2017,7 +2017,7 @@ impl<'a> DisplayStruct<'a> { wrap_item(w, |w| { if is_type_alias { // For now the only attributes we render for type aliases are `repr` attributes. - render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Struct); + render_repr_attribute_in_code(w, cx, self.def_id); } else { render_attributes_in_code(w, it, "", cx); } @@ -2371,7 +2371,7 @@ fn render_union( fmt::from_fn(move |mut f| { if is_type_alias { // For now the only attributes we render for type aliases are `repr` attributes. - render_repr_attributes_in_code(f, cx, def_id, ItemType::Union); + render_repr_attribute_in_code(f, cx, def_id); } else { render_attributes_in_code(f, it, "", cx); } diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs index 42f2fbd93b1ee..de7c89a9fa375 100644 --- a/tests/rustdoc-gui/src/test_docs/lib.rs +++ b/tests/rustdoc-gui/src/test_docs/lib.rs @@ -459,10 +459,10 @@ pub fn safe_fn() {} #[repr(C)] pub struct WithGenerics { - s: S, - t: T, - e: E, - p: P, + pub s: S, + pub t: T, + pub e: E, + pub p: P, } pub struct StructWithPublicUndocumentedFields { diff --git a/tests/rustdoc/attribute-rendering.rs b/tests/rustdoc/attribute-rendering.rs deleted file mode 100644 index fb40d0a9887cf..0000000000000 --- a/tests/rustdoc/attribute-rendering.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![crate_name = "foo"] - -//@ has 'foo/fn.f.html' -//@ has - //*[@'class="code-attribute"]' '#[unsafe(export_name = "f")]' -//@ has - //*[@'class="rust item-decl"]' 'pub fn f()' -#[unsafe(export_name = "\ -f")] -pub fn f() {} diff --git a/tests/rustdoc/attributes.rs b/tests/rustdoc/attributes.rs index 33e4e31bec6e3..429a42a7252cd 100644 --- a/tests/rustdoc/attributes.rs +++ b/tests/rustdoc/attributes.rs @@ -9,6 +9,18 @@ pub extern "C" fn f() {} #[unsafe(export_name = "bar")] pub extern "C" fn g() {} +//@ has foo/fn.escape_special.html '//*[@class="code-attribute"]' \ +// '#[unsafe(export_name = "\n\"\n")]' +#[unsafe(export_name = "\n\" +")] +pub extern "C" fn escape_special() {} + +// issue: +//@ has foo/fn.escape_html.html '//*[@class="code-attribute"]' \ +// '#[unsafe(export_name = "")]' +#[unsafe(export_name = "")] +pub extern "C" fn escape_html() {} + //@ has foo/fn.example.html '//*[@class="code-attribute"]' '#[unsafe(link_section = ".text")]' #[unsafe(link_section = ".text")] pub extern "C" fn example() {} diff --git a/tests/rustdoc/auxiliary/ext-repr.rs b/tests/rustdoc/auxiliary/ext-repr.rs new file mode 100644 index 0000000000000..25acaa49449e6 --- /dev/null +++ b/tests/rustdoc/auxiliary/ext-repr.rs @@ -0,0 +1,5 @@ +#[repr(i8)] +pub enum ReprI8 { + Var0, + Var1, +} diff --git a/tests/rustdoc/inline_cross/attributes.rs b/tests/rustdoc/inline_cross/attributes.rs index 4747f8ad67c1f..1657b7bdc8f77 100644 --- a/tests/rustdoc/inline_cross/attributes.rs +++ b/tests/rustdoc/inline_cross/attributes.rs @@ -1,7 +1,20 @@ +// Ensure that we render attributes on inlined cross-crate re-exported items. +// issue: + //@ aux-crate:attributes=attributes.rs //@ edition:2021 #![crate_name = "user"] -//@ has 'user/struct.NonExhaustive.html' -//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[non_exhaustive]' +//@ has 'user/fn.no_mangle.html' '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]' +pub use attributes::no_mangle; + +//@ has 'user/fn.link_section.html' '//pre[@class="rust item-decl"]' \ +// '#[unsafe(link_section = ".here")]' +pub use attributes::link_section; + +//@ has 'user/fn.export_name.html' '//pre[@class="rust item-decl"]' \ +// '#[unsafe(export_name = "exonym")]' +pub use attributes::export_name; + +//@ has 'user/struct.NonExhaustive.html' '//pre[@class="rust item-decl"]' '#[non_exhaustive]' pub use attributes::NonExhaustive; diff --git a/tests/rustdoc/inline_cross/auxiliary/attributes.rs b/tests/rustdoc/inline_cross/auxiliary/attributes.rs index c6f155d4ba5a9..6068d38558586 100644 --- a/tests/rustdoc/inline_cross/auxiliary/attributes.rs +++ b/tests/rustdoc/inline_cross/auxiliary/attributes.rs @@ -1,2 +1,11 @@ +#[unsafe(no_mangle)] +pub fn no_mangle() {} + +#[unsafe(link_section = ".here")] +pub fn link_section() {} + +#[unsafe(export_name = "exonym")] +pub fn export_name() {} + #[non_exhaustive] pub struct NonExhaustive; diff --git a/tests/rustdoc/inline_cross/auxiliary/repr.rs b/tests/rustdoc/inline_cross/auxiliary/repr.rs deleted file mode 100644 index 0211e1a86588f..0000000000000 --- a/tests/rustdoc/inline_cross/auxiliary/repr.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![feature(repr_simd)] - -#[repr(C, align(8))] -pub struct ReprC { - field: u8, -} -#[repr(simd, packed(2))] -pub struct ReprSimd { - field: [u8; 1], -} -#[repr(transparent)] -pub struct ReprTransparent { - pub field: u8, -} -#[repr(isize)] -pub enum ReprIsize { - Bla, -} -#[repr(u8)] -pub enum ReprU8 { - Bla, -} - -#[repr(transparent)] // private -pub struct ReprTransparentPrivField { - field: u32, // non-1-ZST field -} - -#[repr(transparent)] // public -pub struct ReprTransparentPriv1ZstFields { - marker0: Marker, - pub main: u64, // non-1-ZST field - marker1: Marker, -} - -#[repr(transparent)] // private -pub struct ReprTransparentPrivFieldPub1ZstFields { - main: [u16; 0], // non-1-ZST field - pub marker: Marker, -} - -pub struct Marker; // 1-ZST diff --git a/tests/rustdoc/inline_cross/repr.rs b/tests/rustdoc/inline_cross/repr.rs deleted file mode 100644 index d13e560b8d77f..0000000000000 --- a/tests/rustdoc/inline_cross/repr.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Regression test for . -// This test ensures that the re-exported items still have the `#[repr(...)]` attribute. - -//@ aux-build:repr.rs - -#![crate_name = "foo"] - -extern crate repr; - -//@ has 'foo/struct.ReprC.html' -//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C, align(8))]' -pub use repr::ReprC; -//@ has 'foo/struct.ReprSimd.html' -//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(simd, packed(2))]' -pub use repr::ReprSimd; -//@ has 'foo/struct.ReprTransparent.html' -//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' -pub use repr::ReprTransparent; -//@ has 'foo/enum.ReprIsize.html' -//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(isize)]' -pub use repr::ReprIsize; -//@ has 'foo/enum.ReprU8.html' -//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(u8)]' -pub use repr::ReprU8; - -// Regression test for . -// Check that we show `#[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. - -//@ has 'foo/struct.ReprTransparentPrivField.html' -//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' -pub use repr::ReprTransparentPrivField; - -//@ has 'foo/struct.ReprTransparentPriv1ZstFields.html' -//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' -pub use repr::ReprTransparentPriv1ZstFields; - -//@ has 'foo/struct.ReprTransparentPrivFieldPub1ZstFields.html' -//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' -pub use repr::ReprTransparentPrivFieldPub1ZstFields; diff --git a/tests/rustdoc/reexport/auxiliary/reexports-attrs.rs b/tests/rustdoc/reexport/auxiliary/reexports-attrs.rs deleted file mode 100644 index 96fa8209cde84..0000000000000 --- a/tests/rustdoc/reexport/auxiliary/reexports-attrs.rs +++ /dev/null @@ -1,14 +0,0 @@ -#[unsafe(no_mangle)] -pub fn f0() {} - -#[unsafe(link_section = ".here")] -pub fn f1() {} - -#[unsafe(export_name = "f2export")] -pub fn f2() {} - -#[repr(u8)] -pub enum T0 { V1 } - -#[non_exhaustive] -pub enum T1 {} diff --git a/tests/rustdoc/reexport/reexport-attrs.rs b/tests/rustdoc/reexport/reexport-attrs.rs deleted file mode 100644 index aec0a11c0c6a0..0000000000000 --- a/tests/rustdoc/reexport/reexport-attrs.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ aux-build: reexports-attrs.rs - -#![crate_name = "foo"] - -extern crate reexports_attrs; - -//@ has 'foo/fn.f0.html' '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]' -pub use reexports_attrs::f0; - -//@ has 'foo/fn.f1.html' '//pre[@class="rust item-decl"]' '#[unsafe(link_section = ".here")]' -pub use reexports_attrs::f1; - -//@ has 'foo/fn.f2.html' '//pre[@class="rust item-decl"]' '#[unsafe(export_name = "f2export")]' -pub use reexports_attrs::f2; - -//@ has 'foo/enum.T0.html' '//pre[@class="rust item-decl"]' '#[repr(u8)]' -pub use reexports_attrs::T0; - -//@ has 'foo/enum.T1.html' '//pre[@class="rust item-decl"]' '#[non_exhaustive]' -pub use reexports_attrs::T1; diff --git a/tests/rustdoc/repr.rs b/tests/rustdoc/repr.rs index f4f683b3d81a2..1e8fad6ec0a66 100644 --- a/tests/rustdoc/repr.rs +++ b/tests/rustdoc/repr.rs @@ -1,29 +1,163 @@ -// Regression test for . -// Check that we show `#[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. +// Test the rendering of `#[repr]` on ADTs. +#![feature(repr_simd)] // only used for the `ReprSimd` test case + +// Check the "local case" (HIR cleaning) // + +// Don't render the default repr which is `Rust`. +//@ has 'repr/struct.ReprDefault.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(Rust)]' +pub struct ReprDefault; + +// Don't render the `Rust` repr — even if given explicitly — since it's the default. +//@ has 'repr/struct.ReprRust.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(Rust)]' +#[repr(Rust)] // omitted +pub struct ReprRust; + +//@ has 'repr/struct.ReprCPubFields.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C)]' +#[repr(C)] // public +pub struct ReprCPubFields { + pub a: u32, + pub b: u32, +} + +//@ has 'repr/struct.ReprCPrivField.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C)]' +#[repr(C)] // private... +pub struct ReprCPrivField { + a: u32, // ...since this is private + pub b: u32, +} + +//@ has 'repr/enum.ReprIsize.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(isize)]' +#[repr(isize)] // public +pub enum ReprIsize { + Bla, +} + +//@ has 'repr/enum.ReprU32HiddenVariant.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(u32)]' +#[repr(u32)] // private... +pub enum ReprU32HiddenVariant { + #[doc(hidden)] + Hidden, // ...since this is hidden + Public, +} + +//@ has 'repr/struct.ReprAlignHiddenField.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(align(4))]' +#[repr(align(4))] // private... +pub struct ReprAlignHiddenField { + #[doc(hidden)] + pub hidden: i16, // ...since this field is hidden +} + +//@ has 'repr/struct.ReprSimd.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(simd, packed(2))]' +#[repr(simd, packed(2))] // public +pub struct ReprSimd { + pub field: [u8; 1], +} + +//@ has 'repr/enum.ReprU32Align.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(u32, align(8))]' +#[repr(u32, align(8))] // public +pub enum ReprU32Align { + Variant(u16), +} + +//@ has 'repr/enum.ReprCHiddenVariantField.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C)]' +#[repr(C)] // private... +pub enum ReprCHiddenVariantField { + Variant { #[doc(hidden)] field: () }, //...since this field is hidden +} //@ has 'repr/struct.ReprTransparentPrivField.html' //@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' -#[repr(transparent)] // private +#[repr(transparent)] // private... pub struct ReprTransparentPrivField { - field: u32, // non-1-ZST field + field: u32, // ...since the non-1-ZST field is private } //@ has 'repr/struct.ReprTransparentPriv1ZstFields.html' //@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' -#[repr(transparent)] // public +#[repr(transparent)] // public... pub struct ReprTransparentPriv1ZstFields { marker0: Marker, - pub main: u64, // non-1-ZST field + pub main: u64, // ...since the non-1-ZST field is public and visible marker1: Marker, +} // the two private 1-ZST fields don't matter + +//@ has 'repr/struct.ReprTransparentPrivFieldPub1ZstField.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' +#[repr(transparent)] // private... +pub struct ReprTransparentPrivFieldPub1ZstField { + main: [u16; 0], // ...since the non-1-ZST field is private + pub marker: Marker, // this public 1-ZST field doesn't matter } //@ has 'repr/struct.ReprTransparentPub1ZstField.html' //@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' -#[repr(transparent)] // public +#[repr(transparent)] // public... pub struct ReprTransparentPub1ZstField { - marker0: Marker, - pub marker1: Marker, + marker0: Marker, // ...since we don't have a non-1-ZST field... + pub marker1: Marker, // ...and this field is public and visible +} + +//@ has 'repr/struct.ReprTransparentUnitStruct.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' +#[repr(transparent)] // public +pub struct ReprTransparentUnitStruct; + +//@ has 'repr/enum.ReprTransparentEnumUnitVariant.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' +#[repr(transparent)] // public +pub enum ReprTransparentEnumUnitVariant { + Variant, +} + +//@ has 'repr/enum.ReprTransparentEnumHiddenUnitVariant.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' +#[repr(transparent)] // private +pub enum ReprTransparentEnumHiddenUnitVariant { + #[doc(hidden)] Variant(u32), +} + +//@ has 'repr/enum.ReprTransparentEnumPub1ZstField.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' +#[repr(transparent)] // public... +pub enum ReprTransparentEnumPub1ZstField { + Variant { + field: u64, // ...since the non-1-ZST field is public + #[doc(hidden)] + marker: Marker, // this hidden 1-ZST field doesn't matter + }, +} + +//@ has 'repr/enum.ReprTransparentEnumHidden1ZstField.html' +//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' +#[repr(transparent)] // private... +pub enum ReprTransparentEnumHidden1ZstField { + Variant { + #[doc(hidden)] + field: u64, // ...since the non-1-ZST field is public + }, } struct Marker; // 1-ZST + +// Check the "extern case" (middle cleaning) // + +// Internally, HIR and middle cleaning share `#[repr]` rendering. +// Thus we'll only test the very basics in this section. + +//@ aux-build: ext-repr.rs +extern crate ext_repr as ext; + +// Regression test for . +//@ has 'repr/enum.ReprI8.html' +//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(i8)]' +pub use ext::ReprI8;