1+ use std:: borrow:: Cow ;
12use std:: hash:: Hash ;
23use std:: path:: PathBuf ;
34use std:: sync:: { Arc , OnceLock as OnceCell } ;
@@ -763,9 +764,7 @@ impl Item {
763764 const ALLOWED_ATTRIBUTES : & [ Symbol ] =
764765 & [ sym:: export_name, sym:: link_section, sym:: no_mangle, sym:: non_exhaustive] ;
765766
766- use rustc_abi:: IntegerType ;
767-
768- let mut attrs: Vec < String > = self
767+ let mut attrs: Vec < _ > = self
769768 . attrs
770769 . other_attrs
771770 . iter ( )
@@ -781,9 +780,6 @@ impl Item {
781780 ..,
782781 ) ) => {
783782 // We have separate pretty-printing logic for `#[repr(..)]` attributes.
784- // For example, there are circumstances where `#[repr(transparent)]`
785- // is applied but should not be publicly shown in rustdoc
786- // because it isn't public API.
787783 None
788784 }
789785 _ => Some ( {
@@ -805,69 +801,103 @@ impl Item {
805801 } )
806802 . collect ( ) ;
807803
808- // Add #[repr(...)]
809- if let Some ( def_id) = self . def_id ( )
810- && let ItemType :: Struct | ItemType :: Enum | ItemType :: Union = self . type_ ( )
804+ if let Some ( repr) = self . repr ( tcx, cache, is_json) {
805+ attrs. push ( repr) ;
806+ }
807+
808+ attrs
809+ }
810+
811+ /// Compute the *public* `#[repr]` of this item.
812+ ///
813+ /// Read more about it here:
814+ /// https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type
815+ fn repr < ' tcx > ( & self , tcx : TyCtxt < ' tcx > , cache : & Cache , is_json : bool ) -> Option < String > {
816+ let def_id = self . def_id ( ) ?;
817+ let ( ItemType :: Struct | ItemType :: Enum | ItemType :: Union ) = self . type_ ( ) else {
818+ return None ;
819+ } ;
820+ let adt = tcx. adt_def ( def_id) ;
821+ let repr = adt. repr ( ) ;
822+
823+ let is_visible = |def_id| cache. document_hidden || !tcx. is_doc_hidden ( def_id) ;
824+ let is_field_public = |field : & ' tcx ty:: FieldDef | {
825+ ( cache. document_private || field. vis . is_public ( ) ) && is_visible ( field. did )
826+ } ;
827+
828+ if repr. transparent ( ) {
829+ // `repr(transparent)` can only be applied to structs and one-variant enums.
830+ let var = adt. variant ( rustc_abi:: FIRST_VARIANT ) ;
831+ // `repr(transparent)` is public iff the non-1-ZST field is public or
832+ // at least one field is public in case all fields are 1-ZST fields.
833+ let is_public = is_json || is_visible ( var. def_id )
834+ && var
835+ . fields
836+ . iter ( )
837+ . find ( |field| {
838+ let ty = field. ty ( tcx, ty:: GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
839+ tcx. layout_of (
840+ ty:: TypingEnv :: post_analysis ( tcx, field. did ) . as_query_input ( ty) ,
841+ )
842+ . is_ok_and ( |layout| !layout. is_1zst ( ) )
843+ } )
844+ . map_or_else (
845+ || var. fields . is_empty ( ) || var. fields . iter ( ) . any ( is_field_public) ,
846+ is_field_public,
847+ ) ;
848+
849+ // Since `repr(transparent)` can't have any other reprs or
850+ // repr modifiers beside it, we can safely return early here.
851+ return is_public. then ( || "#[repr(transparent)]" . into ( ) ) ;
852+ }
853+
854+ // Fast path which avoids looking through the variants and fields in
855+ // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
856+ // FIXME: This check is not forward compatible!
857+ if !repr. c ( )
858+ && !repr. simd ( )
859+ && repr. int . is_none ( )
860+ && repr. pack . is_none ( )
861+ && repr. align . is_none ( )
811862 {
812- let adt = tcx. adt_def ( def_id) ;
813- let repr = adt. repr ( ) ;
814- let mut out = Vec :: new ( ) ;
815- if repr. c ( ) {
816- out. push ( "C" ) ;
817- }
818- if repr. transparent ( ) {
819- // Render `repr(transparent)` iff the non-1-ZST field is public or at least one
820- // field is public in case all fields are 1-ZST fields.
821- let render_transparent = cache. document_private
822- || adt
823- . all_fields ( )
824- . find ( |field| {
825- let ty =
826- field. ty ( tcx, ty:: GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
827- tcx. layout_of (
828- ty:: TypingEnv :: post_analysis ( tcx, field. did ) . as_query_input ( ty) ,
829- )
830- . is_ok_and ( |layout| !layout. is_1zst ( ) )
831- } )
832- . map_or_else (
833- || adt. all_fields ( ) . any ( |field| field. vis . is_public ( ) ) ,
834- |field| field. vis . is_public ( ) ,
835- ) ;
863+ return None ;
864+ }
836865
837- if render_transparent {
838- out. push ( "transparent" ) ;
866+ let is_public = is_json || adt. variants ( ) . iter ( ) . all ( |variant| {
867+ is_visible ( variant. def_id ) && variant. fields . iter ( ) . all ( is_field_public)
868+ } ) ;
869+ if !is_public {
870+ return None ;
871+ }
872+
873+ let mut result = Vec :: < Cow < ' _ , _ > > :: new ( ) ;
874+
875+ if repr. c ( ) {
876+ result. push ( "C" . into ( ) ) ;
877+ }
878+ if repr. simd ( ) {
879+ result. push ( "simd" . into ( ) ) ;
880+ }
881+ if let Some ( int) = repr. int {
882+ let prefix = if int. is_signed ( ) { 'i' } else { 'u' } ;
883+ let int = match int {
884+ rustc_abi:: IntegerType :: Pointer ( _) => format ! ( "{prefix}size" ) ,
885+ rustc_abi:: IntegerType :: Fixed ( int, _) => {
886+ format ! ( "{prefix}{}" , int. size( ) . bytes( ) * 8 )
839887 }
840- }
841- if repr. simd ( ) {
842- out. push ( "simd" ) ;
843- }
844- let pack_s;
845- if let Some ( pack) = repr. pack {
846- pack_s = format ! ( "packed({})" , pack. bytes( ) ) ;
847- out. push ( & pack_s) ;
848- }
849- let align_s;
850- if let Some ( align) = repr. align {
851- align_s = format ! ( "align({})" , align. bytes( ) ) ;
852- out. push ( & align_s) ;
853- }
854- let int_s;
855- if let Some ( int) = repr. int {
856- int_s = match int {
857- IntegerType :: Pointer ( is_signed) => {
858- format ! ( "{}size" , if is_signed { 'i' } else { 'u' } )
859- }
860- IntegerType :: Fixed ( size, is_signed) => {
861- format ! ( "{}{}" , if is_signed { 'i' } else { 'u' } , size. size( ) . bytes( ) * 8 )
862- }
863- } ;
864- out. push ( & int_s) ;
865- }
866- if !out. is_empty ( ) {
867- attrs. push ( format ! ( "#[repr({})]" , out. join( ", " ) ) ) ;
868- }
888+ } ;
889+ result. push ( int. into ( ) ) ;
869890 }
870- attrs
891+
892+ // Render modifiers last.
893+ if let Some ( pack) = repr. pack {
894+ result. push ( format ! ( "packed({})" , pack. bytes( ) ) . into ( ) ) ;
895+ }
896+ if let Some ( align) = repr. align {
897+ result. push ( format ! ( "align({})" , align. bytes( ) ) . into ( ) ) ;
898+ }
899+
900+ ( !result. is_empty ( ) ) . then ( || format ! ( "#[repr({})]" , result. join( ", " ) ) )
871901 }
872902
873903 pub fn is_doc_hidden ( & self ) -> bool {
0 commit comments