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()
.filter_map(|attr| {
if is_json {
if matches!(attr, hir::Attribute::Parsed(AttributeKind::Deprecation { .. })) {
// rustdoc-json stores this in `Item::deprecation`, so we
// don't want it it `Item::attrs`.
None
} else {
Some(rustc_hir_pretty::attribute_to_string(&tcx, attr))
match attr {
hir::Attribute::Parsed(AttributeKind::Deprecation { .. }) => {
// rustdoc-json stores this in `Item::deprecation`, so we
// don't want it it `Item::attrs`.
None
}
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()) {
Some(
@ -789,8 +799,7 @@ impl Item {
.collect();
// Add #[repr(...)]
if !is_json
&& let Some(def_id) = self.def_id()
if let Some(def_id) = self.def_id()
&& let ItemType::Struct | ItemType::Enum | ItemType::Union = self.type_()
{
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,
/// 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.
pub const FORMAT_VERSION: u32 = 42;
pub const FORMAT_VERSION: u32 = 43;
/// The root of the emitted JSON blob.
///
@ -120,9 +120,23 @@ pub struct Item {
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
pub links: HashMap<String, Id>,
/// Stringified versions of parsed 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.
/// Attributes on this item.
///
/// 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>,
/// Information about the items deprecation, if present.
pub deprecation: Option<Deprecation>,

View file

@ -1,6 +1,6 @@
#![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))]
pub struct Aligned {
a: i8,

View file

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

View file

@ -1,34 +1,35 @@
#![no_std]
// 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)]
pub enum ReprCI8 {
First,
}
//@ is "$.index[?(@.name=='SeparateReprCI16')].attrs" '["#[attr = Repr([ReprC, ReprInt(SignedInt(I16))])]\n"]'
//@ is "$.index[?(@.name=='SeparateReprCI16')].attrs" '["#[repr(C, i16)]"]'
#[repr(C)]
#[repr(i16)]
pub enum SeparateReprCI16 {
First,
}
//@ is "$.index[?(@.name=='ReversedReprCUsize')].attrs" '["#[attr = Repr([ReprInt(UnsignedInt(Usize)), ReprC])]\n"]'
//@ is "$.index[?(@.name=='ReversedReprCUsize')].attrs" '["#[repr(C, usize)]"]'
#[repr(usize, C)]
pub enum ReversedReprCUsize {
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)]
pub struct ReprCPacked {
a: i8,
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(packed(2))]
pub struct SeparateReprCPacked {
@ -36,21 +37,21 @@ pub struct SeparateReprCPacked {
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)]
pub struct ReversedReprCPacked {
a: i8,
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))]
pub struct ReprCAlign {
a: i8,
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(align(2))]
pub struct SeparateReprCAlign {
@ -58,20 +59,20 @@ pub struct SeparateReprCAlign {
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)]
pub struct ReversedReprCAlign {
a: i8,
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)]
pub enum AlignedExplicitRepr {
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))]
pub enum ReorderedAlignedExplicitRepr {
First,

View file

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

View file

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

View file

@ -1,22 +1,37 @@
#![no_std]
// Rustdoc JSON currently includes `#[repr(transparent)]`
// even if the transparency is not part of the public API
// Rustdoc JSON *only* includes `#[repr(transparent)]`
// 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)]
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)]
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)]
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)]
pub struct AllZstNotPublic<'a>(core::marker::PhantomData<&'a ()>, ());

View file

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

View file

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