1+ use std:: borrow:: Cow ;
12use std:: hash:: Hash ;
23use std:: path:: PathBuf ;
34use std:: sync:: { Arc , OnceLock as OnceCell } ;
@@ -772,11 +773,10 @@ impl Item {
772773 Some ( tcx. visibility ( def_id) )
773774 }
774775
775- /// Get a list of attributes excluding `#[repr]` to display.
776- ///
777- /// Only used by the HTML output-format.
778- fn attributes_without_repr ( & self ) -> Vec < String > {
779- self . attrs
776+ // FIXME(fmease): Move into html/ mod, this is for HTML output only.
777+ pub ( crate ) fn attributes ( & self , tcx : TyCtxt < ' _ > , cache : & Cache ) -> Vec < String > {
778+ let mut attrs: Vec < _ > = self
779+ . attrs
780780 . other_attrs
781781 . iter ( )
782782 . filter_map ( |attr| match attr {
@@ -794,26 +794,15 @@ impl Item {
794794 }
795795 _ => None ,
796796 } )
797- . collect ( )
798- }
797+ . collect ( ) ;
799798
800- /// Get a list of attributes to display on this item.
801- ///
802- /// Only used by the HTML output-format.
803- pub ( crate ) fn attributes ( & self , tcx : TyCtxt < ' _ > , cache : & Cache ) -> Vec < String > {
804- let mut attrs = self . attributes_without_repr ( ) ;
805-
806- if let Some ( repr_attr) = self . repr ( tcx, cache) {
807- attrs. push ( repr_attr) ;
799+ if let Some ( def_id) = self . def_id ( )
800+ && let Some ( attr) = repr_attribute ( tcx, cache, def_id)
801+ {
802+ attrs. push ( attr) ;
808803 }
809- attrs
810- }
811804
812- /// Returns a stringified `#[repr(...)]` attribute.
813- ///
814- /// Only used by the HTML output-format.
815- pub ( crate ) fn repr ( & self , tcx : TyCtxt < ' _ > , cache : & Cache ) -> Option < String > {
816- repr_attributes ( tcx, cache, self . def_id ( ) ?, self . type_ ( ) )
805+ attrs
817806 }
818807
819808 pub fn is_doc_hidden ( & self ) -> bool {
@@ -825,72 +814,107 @@ impl Item {
825814 }
826815}
827816
828- /// Return a string representing the `#[repr]` attribute if present .
817+ /// Compute the *public* `#[repr]` of the item given by `DefId` .
829818///
830- /// Only used by the HTML output-format.
831- pub ( crate ) fn repr_attributes (
832- tcx : TyCtxt < ' _ > ,
819+ /// Read more about it here:
820+ /// <https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type>.
821+ ///
822+ /// For use in the HTML output only.
823+ // FIXME(fmease): Move into html/ mod
824+ pub ( crate ) fn repr_attribute < ' tcx > (
825+ tcx : TyCtxt < ' tcx > ,
833826 cache : & Cache ,
834827 def_id : DefId ,
835- item_type : ItemType ,
836828) -> Option < String > {
837- use rustc_abi:: IntegerType ;
829+ let adt = match tcx. def_kind ( def_id) {
830+ DefKind :: Struct | DefKind :: Enum | DefKind :: Union => tcx. adt_def ( def_id) ,
831+ _ => return None ,
832+ } ;
833+ let repr = adt. repr ( ) ;
834+
835+ let is_visible = |def_id| cache. document_hidden || !tcx. is_doc_hidden ( def_id) ;
836+ let is_public_field = |field : & ty:: FieldDef | {
837+ ( cache. document_private || field. vis . is_public ( ) ) && is_visible ( field. did )
838+ } ;
838839
839- if !matches ! ( item_type, ItemType :: Struct | ItemType :: Enum | ItemType :: Union ) {
840+ if repr. transparent ( ) {
841+ // The transparent repr is public iff the non-1-ZST field is public and visible or
842+ // – in case all fields are 1-ZST fields — at least one field is public and visible.
843+ let is_public = ' is_public: {
844+ // `#[repr(transparent)]` can only be applied to structs and single-variant enums.
845+ let var = adt. variant ( rustc_abi:: FIRST_VARIANT ) ; // the first and only variant
846+
847+ if !is_visible ( var. def_id ) {
848+ break ' is_public false ;
849+ }
850+
851+ // Side note: There can only ever be one or zero non-1-ZST fields.
852+ let non_1zst_field = var. fields . iter ( ) . find ( |field| {
853+ let ty = ty:: TypingEnv :: post_analysis ( tcx, field. did )
854+ . as_query_input ( tcx. type_of ( field. did ) . instantiate_identity ( ) ) ;
855+ tcx. layout_of ( ty) . is_ok_and ( |layout| !layout. is_1zst ( ) )
856+ } ) ;
857+
858+ match non_1zst_field {
859+ Some ( field) => is_public_field ( field) ,
860+ None => var. fields . is_empty ( ) || var. fields . iter ( ) . any ( is_public_field) ,
861+ }
862+ } ;
863+
864+ // Since the transparent repr can't have any other reprs or
865+ // repr modifiers beside it, we can safely return early here.
866+ return is_public. then ( || "#[repr(transparent)]" . into ( ) ) ;
867+ }
868+
869+ // Fast path which avoids looking through the variants and fields in
870+ // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
871+ // FIXME: This check is not very robust / forward compatible!
872+ if !repr. c ( )
873+ && !repr. simd ( )
874+ && repr. int . is_none ( )
875+ && repr. pack . is_none ( )
876+ && repr. align . is_none ( )
877+ {
840878 return None ;
841879 }
842- let adt = tcx. adt_def ( def_id) ;
843- let repr = adt. repr ( ) ;
844- let mut out = Vec :: new ( ) ;
845- if repr. c ( ) {
846- out. push ( "C" ) ;
880+
881+ // The repr is public iff all components are public and visible.
882+ let is_public = adt
883+ . variants ( )
884+ . iter ( )
885+ . all ( |variant| is_visible ( variant. def_id ) && variant. fields . iter ( ) . all ( is_public_field) ) ;
886+ if !is_public {
887+ return None ;
847888 }
848- if repr. transparent ( ) {
849- // Render `repr(transparent)` iff the non-1-ZST field is public or at least one
850- // field is public in case all fields are 1-ZST fields.
851- let render_transparent = cache. document_private
852- || adt
853- . all_fields ( )
854- . find ( |field| {
855- let ty = field. ty ( tcx, ty:: GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
856- tcx. layout_of ( ty:: TypingEnv :: post_analysis ( tcx, field. did ) . as_query_input ( ty) )
857- . is_ok_and ( |layout| !layout. is_1zst ( ) )
858- } )
859- . map_or_else (
860- || adt. all_fields ( ) . any ( |field| field. vis . is_public ( ) ) ,
861- |field| field. vis . is_public ( ) ,
862- ) ;
863889
864- if render_transparent {
865- out. push ( "transparent" ) ;
866- }
890+ let mut result = Vec :: < Cow < ' _ , _ > > :: new ( ) ;
891+
892+ if repr. c ( ) {
893+ result. push ( "C" . into ( ) ) ;
867894 }
868895 if repr. simd ( ) {
869- out. push ( "simd" ) ;
870- }
871- let pack_s;
872- if let Some ( pack) = repr. pack {
873- pack_s = format ! ( "packed({})" , pack. bytes( ) ) ;
874- out. push ( & pack_s) ;
896+ result. push ( "simd" . into ( ) ) ;
875897 }
876- let align_s;
877- if let Some ( align) = repr. align {
878- align_s = format ! ( "align({})" , align. bytes( ) ) ;
879- out. push ( & align_s) ;
880- }
881- let int_s;
882898 if let Some ( int) = repr. int {
883- int_s = match int {
884- IntegerType :: Pointer ( is_signed) => {
885- format ! ( "{}size" , if is_signed { 'i' } else { 'u' } )
886- }
887- IntegerType :: Fixed ( size, is_signed) => {
888- format ! ( "{}{}" , if is_signed { 'i' } else { 'u' } , size. size( ) . bytes( ) * 8 )
899+ let prefix = if int. is_signed ( ) { 'i' } else { 'u' } ;
900+ let int = match int {
901+ rustc_abi:: IntegerType :: Pointer ( _) => format ! ( "{prefix}size" ) ,
902+ rustc_abi:: IntegerType :: Fixed ( int, _) => {
903+ format ! ( "{prefix}{}" , int. size( ) . bytes( ) * 8 )
889904 }
890905 } ;
891- out . push ( & int_s ) ;
906+ result . push ( int . into ( ) ) ;
892907 }
893- if !out. is_empty ( ) { Some ( format ! ( "#[repr({})]" , out. join( ", " ) ) ) } else { None }
908+
909+ // Render modifiers last.
910+ if let Some ( pack) = repr. pack {
911+ result. push ( format ! ( "packed({})" , pack. bytes( ) ) . into ( ) ) ;
912+ }
913+ if let Some ( align) = repr. align {
914+ result. push ( format ! ( "align({})" , align. bytes( ) ) . into ( ) ) ;
915+ }
916+
917+ ( !result. is_empty ( ) ) . then ( || format ! ( "#[repr({})]" , result. join( ", " ) ) )
894918}
895919
896920#[ derive( Clone , Debug ) ]
0 commit comments