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