@@ -713,17 +713,15 @@ impl Item {
713713 Some ( tcx. visibility ( def_id) )
714714 }
715715
716- pub ( crate ) fn attributes (
716+ pub ( crate ) fn attributes < ' tcx > (
717717 & self ,
718- tcx : TyCtxt < ' _ > ,
718+ tcx : TyCtxt < ' tcx > ,
719719 cache : & Cache ,
720720 keep_as_is : bool ,
721721 ) -> Vec < String > {
722722 const ALLOWED_ATTRIBUTES : & [ Symbol ] =
723723 & [ sym:: export_name, sym:: link_section, sym:: no_mangle, sym:: non_exhaustive] ;
724724
725- use rustc_abi:: IntegerType ;
726-
727725 let mut attrs: Vec < String > = self
728726 . attrs
729727 . other_attrs
@@ -743,67 +741,111 @@ impl Item {
743741 }
744742 } )
745743 . collect ( ) ;
746- if !keep_as_is
747- && let Some ( def_id) = self . def_id ( )
748- && let ItemType :: Struct | ItemType :: Enum | ItemType :: Union = self . type_ ( )
749- {
750- let adt = tcx. adt_def ( def_id) ;
751- let repr = adt. repr ( ) ;
752- let mut out = Vec :: new ( ) ;
753- if repr. c ( ) {
754- out. push ( "C" ) ;
755- }
756- if repr. transparent ( ) {
757- // Render `repr(transparent)` iff the non-1-ZST field is public or at least one
758- // field is public in case all fields are 1-ZST fields.
759- let render_transparent = cache. document_private
760- || adt
761- . all_fields ( )
762- . find ( |field| {
763- let ty =
764- field. ty ( tcx, ty:: GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
765- tcx. layout_of ( tcx. param_env ( field. did ) . and ( ty) )
766- . is_ok_and ( |layout| !layout. is_1zst ( ) )
767- } )
768- . map_or_else (
769- || adt. all_fields ( ) . any ( |field| field. vis . is_public ( ) ) ,
770- |field| field. vis . is_public ( ) ,
771- ) ;
772744
773- if render_transparent {
774- out. push ( "transparent" ) ;
775- }
776- }
777- if repr. simd ( ) {
778- out. push ( "simd" ) ;
779- }
780- let pack_s;
781- if let Some ( pack) = repr. pack {
782- pack_s = format ! ( "packed({})" , pack. bytes( ) ) ;
783- out. push ( & pack_s) ;
784- }
785- let align_s;
786- if let Some ( align) = repr. align {
787- align_s = format ! ( "align({})" , align. bytes( ) ) ;
788- out. push ( & align_s) ;
789- }
790- let int_s;
791- if let Some ( int) = repr. int {
792- int_s = match int {
793- IntegerType :: Pointer ( is_signed) => {
794- format ! ( "{}size" , if is_signed { 'i' } else { 'u' } )
745+ if !keep_as_is && let Some ( repr) = self . repr ( tcx, cache) {
746+ attrs. push ( repr) ;
747+ }
748+
749+ attrs
750+ }
751+
752+ /// Compute the *public* `#[repr]` of this item.
753+ ///
754+ /// Read more about it here:
755+ /// https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type
756+ fn repr < ' tcx > ( & self , tcx : TyCtxt < ' tcx > , cache : & Cache ) -> Option < String > {
757+ let def_id = self . def_id ( ) ?;
758+ let ( ItemType :: Struct | ItemType :: Enum | ItemType :: Union ) = self . type_ ( ) else {
759+ return None ;
760+ } ;
761+
762+ let adt = tcx. adt_def ( def_id) ;
763+ let repr = adt. repr ( ) ;
764+
765+ let is_visible = |def_id| cache. document_hidden || !tcx. is_doc_hidden ( def_id) ;
766+ let is_field_public =
767+ |field : & ' tcx ty:: FieldDef | field. vis . is_public ( ) && is_visible ( field. did ) ;
768+
769+ if repr. transparent ( ) {
770+ // `repr(transparent)` is public iff the non-1-ZST field is public or
771+ // at least one field is public in case all fields are 1-ZST fields.
772+ let is_public = cache. document_private
773+ || adt. variants ( ) . iter ( ) . all ( |variant| {
774+ if !is_visible ( variant. def_id ) {
775+ return false ;
795776 }
796- IntegerType :: Fixed ( size, is_signed) => {
797- format ! ( "{}{}" , if is_signed { 'i' } else { 'u' } , size. size( ) . bytes( ) * 8 )
777+
778+ let field = variant. fields . iter ( ) . find ( |field| {
779+ let args = ty:: GenericArgs :: identity_for_item ( tcx, field. did ) ;
780+ let ty = field. ty ( tcx, args) ;
781+ tcx. layout_of ( tcx. param_env ( field. did ) . and ( ty) )
782+ . is_ok_and ( |layout| !layout. is_1zst ( ) )
783+ } ) ;
784+
785+ if let Some ( field) = field {
786+ return is_field_public ( field) ;
798787 }
799- } ;
800- out. push ( & int_s) ;
801- }
802- if !out. is_empty ( ) {
803- attrs. push ( format ! ( "#[repr({})]" , out. join( ", " ) ) ) ;
804- }
788+
789+ adt. variants ( ) . iter ( ) . all ( |variant| {
790+ variant. fields . is_empty ( ) || variant. fields . iter ( ) . any ( is_field_public)
791+ } )
792+ } ) ;
793+
794+ // Since `repr(transparent)` can't have any other reprs or
795+ // repr modifiers beside it, we can safely return early here.
796+ return is_public. then ( || "#[repr(transparent)]" . into ( ) ) ;
805797 }
806- attrs
798+
799+ let is_public = cache. document_private
800+ || if adt. is_enum ( ) {
801+ // FIXME(fmease): Should we take the visibility of fields of variants into account?
802+ // FIXME(fmease): `any` or `all`?
803+ adt. variants ( ) . is_empty ( )
804+ || adt. variants ( ) . iter ( ) . any ( |variant| is_visible ( variant. def_id ) )
805+ } else {
806+ // FIXME(fmease): `all` or `any`?
807+ adt. all_fields ( ) . all ( is_field_public)
808+ } ;
809+ if !is_public {
810+ return None ;
811+ }
812+
813+ let mut result = Vec :: new ( ) ;
814+
815+ if repr. c ( ) {
816+ result. push ( "C" ) ;
817+ }
818+ if repr. simd ( ) {
819+ result. push ( "simd" ) ;
820+ }
821+ let pack_s;
822+ if let Some ( pack) = repr. pack {
823+ pack_s = format ! ( "packed({})" , pack. bytes( ) ) ;
824+ result. push ( & pack_s) ;
825+ }
826+ let align_s;
827+ if let Some ( align) = repr. align {
828+ align_s = format ! ( "align({})" , align. bytes( ) ) ;
829+ result. push ( & align_s) ;
830+ }
831+ let int_s;
832+ if let Some ( int) = repr. int {
833+ int_s = match int {
834+ rustc_abi:: IntegerType :: Pointer ( is_signed) => {
835+ format ! ( "{}size" , if is_signed { 'i' } else { 'u' } )
836+ }
837+ rustc_abi:: IntegerType :: Fixed ( size, is_signed) => {
838+ format ! ( "{}{}" , if is_signed { 'i' } else { 'u' } , size. size( ) . bytes( ) * 8 )
839+ }
840+ } ;
841+ result. push ( & int_s) ;
842+ }
843+
844+ if result. is_empty ( ) {
845+ return None ;
846+ }
847+
848+ Some ( format ! ( "#[repr({})]" , result. join( ", " ) ) )
807849 }
808850
809851 pub fn is_doc_hidden ( & self ) -> bool {
0 commit comments