@@ -713,17 +713,15 @@ impl Item {
713
713
Some ( tcx. visibility ( def_id) )
714
714
}
715
715
716
- pub ( crate ) fn attributes (
716
+ pub ( crate ) fn attributes < ' tcx > (
717
717
& self ,
718
- tcx : TyCtxt < ' _ > ,
718
+ tcx : TyCtxt < ' tcx > ,
719
719
cache : & Cache ,
720
720
keep_as_is : bool ,
721
721
) -> Vec < String > {
722
722
const ALLOWED_ATTRIBUTES : & [ Symbol ] =
723
723
& [ sym:: export_name, sym:: link_section, sym:: no_mangle, sym:: non_exhaustive] ;
724
724
725
- use rustc_abi:: IntegerType ;
726
-
727
725
let mut attrs: Vec < String > = self
728
726
. attrs
729
727
. other_attrs
@@ -743,67 +741,122 @@ impl Item {
743
741
}
744
742
} )
745
743
. 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
- ) ;
772
744
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 ;
795
776
}
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) ;
798
787
}
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 ( ) ) ;
805
797
}
806
- attrs
798
+
799
+ // Fast path which avoids looking through the variants and fields in
800
+ // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
801
+ if !repr. c ( )
802
+ && !repr. simd ( )
803
+ && repr. int . is_none ( )
804
+ && repr. pack . is_none ( )
805
+ && repr. align . is_none ( )
806
+ {
807
+ return None ;
808
+ }
809
+
810
+ let is_public = cache. document_private
811
+ || if adt. is_enum ( ) {
812
+ // FIXME(fmease): Should we take the visibility of fields of variants into account?
813
+ // FIXME(fmease): `any` or `all`?
814
+ adt. variants ( ) . is_empty ( )
815
+ || adt. variants ( ) . iter ( ) . any ( |variant| is_visible ( variant. def_id ) )
816
+ } else {
817
+ // FIXME(fmease): `all` or `any`?
818
+ adt. all_fields ( ) . all ( is_field_public)
819
+ } ;
820
+ if !is_public {
821
+ return None ;
822
+ }
823
+
824
+ let mut result = Vec :: new ( ) ;
825
+
826
+ if repr. c ( ) {
827
+ result. push ( "C" ) ;
828
+ }
829
+ if repr. simd ( ) {
830
+ result. push ( "simd" ) ;
831
+ }
832
+ let int_s;
833
+ if let Some ( int) = repr. int {
834
+ int_s = match int {
835
+ rustc_abi:: IntegerType :: Pointer ( is_signed) => {
836
+ format ! ( "{}size" , if is_signed { 'i' } else { 'u' } )
837
+ }
838
+ rustc_abi:: IntegerType :: Fixed ( size, is_signed) => {
839
+ format ! ( "{}{}" , if is_signed { 'i' } else { 'u' } , size. size( ) . bytes( ) * 8 )
840
+ }
841
+ } ;
842
+ result. push ( & int_s) ;
843
+ }
844
+ let pack_s;
845
+ if let Some ( pack) = repr. pack {
846
+ pack_s = format ! ( "packed({})" , pack. bytes( ) ) ;
847
+ result. push ( & pack_s) ;
848
+ }
849
+ let align_s;
850
+ if let Some ( align) = repr. align {
851
+ align_s = format ! ( "align({})" , align. bytes( ) ) ;
852
+ result. push ( & align_s) ;
853
+ }
854
+
855
+ if result. is_empty ( ) {
856
+ return None ;
857
+ }
858
+
859
+ Some ( format ! ( "#[repr({})]" , result. join( ", " ) ) )
807
860
}
808
861
809
862
pub fn is_doc_hidden ( & self ) -> bool {
0 commit comments