rustdoc: Use own logic to print #[repr(..)] attributes in JSON output.

This commit is contained in:
Predrag Gruevski 2025-03-04 22:02:06 +00:00
parent d93f678fa5
commit bafdbcadd5
10 changed files with 82 additions and 43 deletions

View file

@ -768,12 +768,22 @@ impl Item {
.iter() .iter()
.filter_map(|attr| { .filter_map(|attr| {
if is_json { if is_json {
if matches!(attr, hir::Attribute::Parsed(AttributeKind::Deprecation { .. })) { match attr {
hir::Attribute::Parsed(AttributeKind::Deprecation { .. }) => {
// rustdoc-json stores this in `Item::deprecation`, so we // rustdoc-json stores this in `Item::deprecation`, so we
// don't want it it `Item::attrs`. // don't want it it `Item::attrs`.
None None
} else { }
Some(rustc_hir_pretty::attribute_to_string(&tcx, attr)) rustc_hir::Attribute::Parsed(rustc_attr_parsing::AttributeKind::Repr(
..,
)) => {
// We have separate pretty-printing logic for `#[repr(..)]` attributes.
// For example, there are circumstances where `#[repr(transparent)]`
// is applied but should not be publicly shown in rustdoc
// because it isn't public API.
None
}
_ => Some(rustc_hir_pretty::attribute_to_string(&tcx, attr)),
} }
} else if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) { } else if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) {
Some( Some(
@ -789,8 +799,7 @@ impl Item {
.collect(); .collect();
// Add #[repr(...)] // Add #[repr(...)]
if !is_json if let Some(def_id) = self.def_id()
&& let Some(def_id) = self.def_id()
&& let ItemType::Struct | ItemType::Enum | ItemType::Union = self.type_() && let ItemType::Struct | ItemType::Enum | ItemType::Union = self.type_()
{ {
let adt = tcx.adt_def(def_id); let adt = tcx.adt_def(def_id);

View file

@ -30,7 +30,7 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
/// This integer is incremented with every breaking change to the API, /// This integer is incremented with every breaking change to the API,
/// and is returned along with the JSON blob as [`Crate::format_version`]. /// and is returned along with the JSON blob as [`Crate::format_version`].
/// Consuming code should assert that this value matches the format version(s) that it supports. /// Consuming code should assert that this value matches the format version(s) that it supports.
pub const FORMAT_VERSION: u32 = 42; pub const FORMAT_VERSION: u32 = 43;
/// The root of the emitted JSON blob. /// The root of the emitted JSON blob.
/// ///
@ -120,9 +120,23 @@ pub struct Item {
pub docs: Option<String>, pub docs: Option<String>,
/// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs /// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs
pub links: HashMap<String, Id>, pub links: HashMap<String, Id>,
/// Stringified versions of parsed attributes on this item. /// Attributes on this item.
/// Essentially debug printed (e.g. `#[inline]` becomes something similar to `#[attr="Inline(Hint)"]`). ///
/// Equivalent to the hir pretty-printing of attributes. /// Does not include `#[deprecated]` attributes: see the [`Self::deprecation`] field instead.
///
/// Some attributes appear in pretty-printed Rust form, regardless of their formatting
/// in the original source code. For example:
/// - `#[non_exhaustive]` and `#[must_use]` are represented as themselves.
/// - `#[no_mangle]` and `#[export_name]` are also represented as themselves.
/// - `#[repr(C)]` and other reprs also appear as themselves,
/// though potentially with a different order: e.g. `repr(i8, C)` may become `repr(C, i8)`.
/// Multiple repr attributes on the same item may be combined into an equivalent single attr.
///
/// Other attributes may appear debug-printed. For example:
/// - `#[inline]` becomes something similar to `#[attr="Inline(Hint)"]`.
///
/// As an internal implementation detail subject to change, this debug-printing format
/// is currently equivalent to the HIR pretty-printing of parsed attributes.
pub attrs: Vec<String>, pub attrs: Vec<String>,
/// Information about the items deprecation, if present. /// Information about the items deprecation, if present.
pub deprecation: Option<Deprecation>, pub deprecation: Option<Deprecation>,

View file

@ -1,6 +1,6 @@
#![no_std] #![no_std]
//@ is "$.index[?(@.name=='Aligned')].attrs" '["#[attr = Repr([ReprAlign(Align(4 bytes))])]\n"]' //@ is "$.index[?(@.name=='Aligned')].attrs" '["#[repr(align(4))]"]'
#[repr(align(4))] #[repr(align(4))]
pub struct Aligned { pub struct Aligned {
a: i8, a: i8,

View file

@ -1,16 +1,16 @@
#![no_std] #![no_std]
//@ is "$.index[?(@.name=='ReprCStruct')].attrs" '["#[attr = Repr([ReprC])]\n"]' //@ is "$.index[?(@.name=='ReprCStruct')].attrs" '["#[repr(C)]"]'
#[repr(C)] #[repr(C)]
pub struct ReprCStruct(pub i64); pub struct ReprCStruct(pub i64);
//@ is "$.index[?(@.name=='ReprCEnum')].attrs" '["#[attr = Repr([ReprC])]\n"]' //@ is "$.index[?(@.name=='ReprCEnum')].attrs" '["#[repr(C)]"]'
#[repr(C)] #[repr(C)]
pub enum ReprCEnum { pub enum ReprCEnum {
First, First,
} }
//@ is "$.index[?(@.name=='ReprCUnion')].attrs" '["#[attr = Repr([ReprC])]\n"]' //@ is "$.index[?(@.name=='ReprCUnion')].attrs" '["#[repr(C)]"]'
#[repr(C)] #[repr(C)]
pub union ReprCUnion { pub union ReprCUnion {
pub left: i64, pub left: i64,

View file

@ -1,34 +1,35 @@
#![no_std] #![no_std]
// Combinations of `#[repr(..)]` attributes. // Combinations of `#[repr(..)]` attributes.
// Rustdoc JSON emits normalized output, regardless of the original source.
//@ is "$.index[?(@.name=='ReprCI8')].attrs" '["#[attr = Repr([ReprC, ReprInt(SignedInt(I8))])]\n"]' //@ is "$.index[?(@.name=='ReprCI8')].attrs" '["#[repr(C, i8)]"]'
#[repr(C, i8)] #[repr(C, i8)]
pub enum ReprCI8 { pub enum ReprCI8 {
First, First,
} }
//@ is "$.index[?(@.name=='SeparateReprCI16')].attrs" '["#[attr = Repr([ReprC, ReprInt(SignedInt(I16))])]\n"]' //@ is "$.index[?(@.name=='SeparateReprCI16')].attrs" '["#[repr(C, i16)]"]'
#[repr(C)] #[repr(C)]
#[repr(i16)] #[repr(i16)]
pub enum SeparateReprCI16 { pub enum SeparateReprCI16 {
First, First,
} }
//@ is "$.index[?(@.name=='ReversedReprCUsize')].attrs" '["#[attr = Repr([ReprInt(UnsignedInt(Usize)), ReprC])]\n"]' //@ is "$.index[?(@.name=='ReversedReprCUsize')].attrs" '["#[repr(C, usize)]"]'
#[repr(usize, C)] #[repr(usize, C)]
pub enum ReversedReprCUsize { pub enum ReversedReprCUsize {
First, First,
} }
//@ is "$.index[?(@.name=='ReprCPacked')].attrs" '["#[attr = Repr([ReprC, ReprPacked(Align(1 bytes))])]\n"]' //@ is "$.index[?(@.name=='ReprCPacked')].attrs" '["#[repr(C, packed(1))]"]'
#[repr(C, packed)] #[repr(C, packed)]
pub struct ReprCPacked { pub struct ReprCPacked {
a: i8, a: i8,
b: i64, b: i64,
} }
//@ is "$.index[?(@.name=='SeparateReprCPacked')].attrs" '["#[attr = Repr([ReprC, ReprPacked(Align(2 bytes))])]\n"]' //@ is "$.index[?(@.name=='SeparateReprCPacked')].attrs" '["#[repr(C, packed(2))]"]'
#[repr(C)] #[repr(C)]
#[repr(packed(2))] #[repr(packed(2))]
pub struct SeparateReprCPacked { pub struct SeparateReprCPacked {
@ -36,21 +37,21 @@ pub struct SeparateReprCPacked {
b: i64, b: i64,
} }
//@ is "$.index[?(@.name=='ReversedReprCPacked')].attrs" '["#[attr = Repr([ReprPacked(Align(2 bytes)), ReprC])]\n"]' //@ is "$.index[?(@.name=='ReversedReprCPacked')].attrs" '["#[repr(C, packed(2))]"]'
#[repr(packed(2), C)] #[repr(packed(2), C)]
pub struct ReversedReprCPacked { pub struct ReversedReprCPacked {
a: i8, a: i8,
b: i64, b: i64,
} }
//@ is "$.index[?(@.name=='ReprCAlign')].attrs" '["#[attr = Repr([ReprC, ReprAlign(Align(16 bytes))])]\n"]' //@ is "$.index[?(@.name=='ReprCAlign')].attrs" '["#[repr(C, align(16))]"]'
#[repr(C, align(16))] #[repr(C, align(16))]
pub struct ReprCAlign { pub struct ReprCAlign {
a: i8, a: i8,
b: i64, b: i64,
} }
//@ is "$.index[?(@.name=='SeparateReprCAlign')].attrs" '["#[attr = Repr([ReprC, ReprAlign(Align(2 bytes))])]\n"]' //@ is "$.index[?(@.name=='SeparateReprCAlign')].attrs" '["#[repr(C, align(2))]"]'
#[repr(C)] #[repr(C)]
#[repr(align(2))] #[repr(align(2))]
pub struct SeparateReprCAlign { pub struct SeparateReprCAlign {
@ -58,20 +59,20 @@ pub struct SeparateReprCAlign {
b: i64, b: i64,
} }
//@ is "$.index[?(@.name=='ReversedReprCAlign')].attrs" '["#[attr = Repr([ReprAlign(Align(2 bytes)), ReprC])]\n"]' //@ is "$.index[?(@.name=='ReversedReprCAlign')].attrs" '["#[repr(C, align(2))]"]'
#[repr(align(2), C)] #[repr(align(2), C)]
pub struct ReversedReprCAlign { pub struct ReversedReprCAlign {
a: i8, a: i8,
b: i64, b: i64,
} }
//@ is "$.index[?(@.name=='AlignedExplicitRepr')].attrs" '["#[attr = Repr([ReprC, ReprAlign(Align(16 bytes)), ReprInt(SignedInt(Isize))])]\n"]' //@ is "$.index[?(@.name=='AlignedExplicitRepr')].attrs" '["#[repr(C, align(16), isize)]"]'
#[repr(C, align(16), isize)] #[repr(C, align(16), isize)]
pub enum AlignedExplicitRepr { pub enum AlignedExplicitRepr {
First, First,
} }
//@ is "$.index[?(@.name=='ReorderedAlignedExplicitRepr')].attrs" '["#[attr = Repr([ReprInt(SignedInt(Isize)), ReprC, ReprAlign(Align(16 bytes))])]\n"]' //@ is "$.index[?(@.name=='ReorderedAlignedExplicitRepr')].attrs" '["#[repr(C, align(16), isize)]"]'
#[repr(isize, C, align(16))] #[repr(isize, C, align(16))]
pub enum ReorderedAlignedExplicitRepr { pub enum ReorderedAlignedExplicitRepr {
First, First,

View file

@ -1,18 +1,18 @@
#![no_std] #![no_std]
//@ is "$.index[?(@.name=='I8')].attrs" '["#[attr = Repr([ReprInt(SignedInt(I8))])]\n"]' //@ is "$.index[?(@.name=='I8')].attrs" '["#[repr(i8)]"]'
#[repr(i8)] #[repr(i8)]
pub enum I8 { pub enum I8 {
First, First,
} }
//@ is "$.index[?(@.name=='I32')].attrs" '["#[attr = Repr([ReprInt(SignedInt(I32))])]\n"]' //@ is "$.index[?(@.name=='I32')].attrs" '["#[repr(i32)]"]'
#[repr(i32)] #[repr(i32)]
pub enum I32 { pub enum I32 {
First, First,
} }
//@ is "$.index[?(@.name=='Usize')].attrs" '["#[attr = Repr([ReprInt(UnsignedInt(Usize))])]\n"]' //@ is "$.index[?(@.name=='Usize')].attrs" '["#[repr(usize)]"]'
#[repr(usize)] #[repr(usize)]
pub enum Usize { pub enum Usize {
First, First,

View file

@ -1,16 +1,16 @@
#![no_std] #![no_std]
// Note the normalization: // Note the normalization:
// `#[repr(packed)]` in has the implict "1" in rustdoc JSON. // `#[repr(packed)]` in source becomes `#[repr(packed(1))]` in rustdoc JSON.
//
//@ is "$.index[?(@.name=='Packed')].attrs" '["#[attr = Repr([ReprPacked(Align(1 bytes))])]\n"]' //@ is "$.index[?(@.name=='Packed')].attrs" '["#[repr(packed(1))]"]'
#[repr(packed)] #[repr(packed)]
pub struct Packed { pub struct Packed {
a: i8, a: i8,
b: i64, b: i64,
} }
//@ is "$.index[?(@.name=='PackedAligned')].attrs" '["#[attr = Repr([ReprPacked(Align(4 bytes))])]\n"]' //@ is "$.index[?(@.name=='PackedAligned')].attrs" '["#[repr(packed(4))]"]'
#[repr(packed(4))] #[repr(packed(4))]
pub struct PackedAligned { pub struct PackedAligned {
a: i8, a: i8,

View file

@ -1,22 +1,37 @@
#![no_std] #![no_std]
// Rustdoc JSON currently includes `#[repr(transparent)]` // Rustdoc JSON *only* includes `#[repr(transparent)]`
// even if the transparency is not part of the public API // if the transparency is public API:
// - if a non-1-ZST field exists, it has to be public
// - otherwise, all fields are 1-ZST and at least one of them is public
// //
// https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent // More info: https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent
//@ is "$.index[?(@.name=='Transparent')].attrs" '["#[attr = Repr([ReprTransparent])]\n"]' // Here, the non-1-ZST field is public.
// We expect `#[repr(transparent)]` in the attributes.
//
//@ is "$.index[?(@.name=='Transparent')].attrs" '["#[repr(transparent)]"]'
#[repr(transparent)] #[repr(transparent)]
pub struct Transparent(pub i64); pub struct Transparent(pub i64);
//@ is "$.index[?(@.name=='TransparentNonPub')].attrs" '["#[attr = Repr([ReprTransparent])]\n"]' // Here the non-1-ZST field isn't public, so the attribute isn't included.
//
//@ has "$.index[?(@.name=='TransparentNonPub')]"
//@ is "$.index[?(@.name=='TransparentNonPub')].attrs" '[]'
#[repr(transparent)] #[repr(transparent)]
pub struct TransparentNonPub(i64); pub struct TransparentNonPub(i64);
//@ is "$.index[?(@.name=='AllZst')].attrs" '["#[attr = Repr([ReprTransparent])]\n"]' // Only 1-ZST fields here, and one of them is public.
// We expect `#[repr(transparent)]` in the attributes.
//
//@ is "$.index[?(@.name=='AllZst')].attrs" '["#[repr(transparent)]"]'
#[repr(transparent)] #[repr(transparent)]
pub struct AllZst<'a>(pub core::marker::PhantomData<&'a ()>, ()); pub struct AllZst<'a>(pub core::marker::PhantomData<&'a ()>, ());
//@ is "$.index[?(@.name=='AllZstNotPublic')].attrs" '["#[attr = Repr([ReprTransparent])]\n"]' // Only 1-ZST fields here but none of them are public.
// The attribute isn't included.
//
//@ has "$.index[?(@.name=='AllZstNotPublic')]"
//@ is "$.index[?(@.name=='AllZstNotPublic')].attrs" '[]'
#[repr(transparent)] #[repr(transparent)]
pub struct AllZstNotPublic<'a>(core::marker::PhantomData<&'a ()>, ()); pub struct AllZstNotPublic<'a>(core::marker::PhantomData<&'a ()>, ());

View file

@ -1,5 +1,5 @@
#[repr(i32)] #[repr(i32)]
//@ is "$.index[?(@.name=='Foo')].attrs" '["#[attr = Repr([ReprInt(SignedInt(I32))])]\n"]' //@ is "$.index[?(@.name=='Foo')].attrs" '["#[repr(i32)]"]'
pub enum Foo { pub enum Foo {
//@ is "$.index[?(@.name=='Struct')].inner.variant.discriminant" null //@ is "$.index[?(@.name=='Struct')].inner.variant.discriminant" null
//@ count "$.index[?(@.name=='Struct')].inner.variant.kind.struct.fields[*]" 0 //@ count "$.index[?(@.name=='Struct')].inner.variant.kind.struct.fields[*]" 0

View file

@ -1,5 +1,5 @@
#[repr(u32)] #[repr(u32)]
//@ is "$.index[?(@.name=='Foo')].attrs" '["#[attr = Repr([ReprInt(UnsignedInt(U32))])]\n"]' //@ is "$.index[?(@.name=='Foo')].attrs" '["#[repr(u32)]"]'
pub enum Foo { pub enum Foo {
//@ is "$.index[?(@.name=='Tuple')].inner.variant.discriminant" null //@ is "$.index[?(@.name=='Tuple')].inner.variant.discriminant" null
//@ count "$.index[?(@.name=='Tuple')].inner.variant.kind.tuple[*]" 0 //@ count "$.index[?(@.name=='Tuple')].inner.variant.kind.tuple[*]" 0