1
+ use std:: borrow:: Cow ;
1
2
use std:: hash:: Hash ;
2
3
use std:: path:: PathBuf ;
3
4
use std:: sync:: { Arc , OnceLock as OnceCell } ;
@@ -765,9 +766,7 @@ impl Item {
765
766
const ALLOWED_ATTRIBUTES : & [ Symbol ] =
766
767
& [ sym:: export_name, sym:: link_section, sym:: no_mangle, sym:: non_exhaustive] ;
767
768
768
- use rustc_abi:: IntegerType ;
769
-
770
- let mut attrs: Vec < String > = self
769
+ let mut attrs: Vec < _ > = self
771
770
. attrs
772
771
. other_attrs
773
772
. iter ( )
@@ -798,23 +797,40 @@ impl Item {
798
797
} )
799
798
. collect ( ) ;
800
799
801
- // Add #[repr(...)]
802
- if let Some ( def_id) = self . def_id ( )
803
- && let ItemType :: Struct | ItemType :: Enum | ItemType :: Union = self . type_ ( )
804
- {
805
- let adt = tcx. adt_def ( def_id) ;
806
- let repr = adt. repr ( ) ;
807
- let mut out = Vec :: new ( ) ;
808
- if repr. c ( ) {
809
- out. push ( "C" ) ;
810
- }
811
- if repr. transparent ( ) {
812
- // Render `repr(transparent)` iff the non-1-ZST field is public or at least one
813
- // field is public in case all fields are 1-ZST fields.
814
- let render_transparent = is_json
815
- || cache. document_private
816
- || adt
817
- . all_fields ( )
800
+ if let Some ( repr) = self . repr ( tcx, cache, is_json) {
801
+ attrs. push ( repr) ;
802
+ }
803
+
804
+ attrs
805
+ }
806
+
807
+ /// Compute the *public* `#[repr]` of this item.
808
+ ///
809
+ /// Read more about it here:
810
+ /// <https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type>.
811
+ fn repr < ' tcx > ( & self , tcx : TyCtxt < ' tcx > , cache : & Cache , is_json : bool ) -> Option < String > {
812
+ let def_id = self . def_id ( ) ?;
813
+ let ( ItemType :: Struct | ItemType :: Enum | ItemType :: Union ) = self . type_ ( ) else {
814
+ return None ;
815
+ } ;
816
+ let adt = tcx. adt_def ( def_id) ;
817
+ let repr = adt. repr ( ) ;
818
+
819
+ let is_visible = |def_id| cache. document_hidden || !tcx. is_doc_hidden ( def_id) ;
820
+ let is_field_public = |field : & ' tcx ty:: FieldDef | {
821
+ ( cache. document_private || field. vis . is_public ( ) ) && is_visible ( field. did )
822
+ } ;
823
+
824
+ if repr. transparent ( ) {
825
+ // `repr(transparent)` can only be applied to structs and single-variant enums.
826
+ let var = adt. variant ( rustc_abi:: FIRST_VARIANT ) ;
827
+ // `repr(transparent)` is public iff the non-1-ZST field is public or
828
+ // at least one field is public in case all fields are 1-ZST fields.
829
+ let is_public = is_json
830
+ || is_visible ( var. def_id )
831
+ && var
832
+ . fields
833
+ . iter ( )
818
834
. find ( |field| {
819
835
let ty =
820
836
field. ty ( tcx, ty:: GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
@@ -824,44 +840,63 @@ impl Item {
824
840
. is_ok_and ( |layout| !layout. is_1zst ( ) )
825
841
} )
826
842
. map_or_else (
827
- || adt . all_fields ( ) . any ( |field| field . vis . is_public ( ) ) ,
828
- |field| field . vis . is_public ( ) ,
843
+ || var . fields . is_empty ( ) || var . fields . iter ( ) . any ( is_field_public ) ,
844
+ is_field_public ,
829
845
) ;
830
846
831
- if render_transparent {
832
- out. push ( "transparent" ) ;
847
+ // Since `repr(transparent)` can't have any other reprs or
848
+ // repr modifiers beside it, we can safely return early here.
849
+ return is_public. then ( || "#[repr(transparent)]" . into ( ) ) ;
850
+ }
851
+
852
+ // Fast path which avoids looking through the variants and fields in
853
+ // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
854
+ // FIXME: This check is not forward compatible!
855
+ if !repr. c ( )
856
+ && !repr. simd ( )
857
+ && repr. int . is_none ( )
858
+ && repr. pack . is_none ( )
859
+ && repr. align . is_none ( )
860
+ {
861
+ return None ;
862
+ }
863
+
864
+ let is_public = is_json
865
+ || adt. variants ( ) . iter ( ) . all ( |variant| {
866
+ is_visible ( variant. def_id ) && variant. fields . iter ( ) . all ( is_field_public)
867
+ } ) ;
868
+ if !is_public {
869
+ return None ;
870
+ }
871
+
872
+ let mut result = Vec :: < Cow < ' _ , _ > > :: new ( ) ;
873
+
874
+ if repr. c ( ) {
875
+ result. push ( "C" . into ( ) ) ;
876
+ }
877
+ if repr. simd ( ) {
878
+ result. push ( "simd" . into ( ) ) ;
879
+ }
880
+ if let Some ( int) = repr. int {
881
+ let prefix = if int. is_signed ( ) { 'i' } else { 'u' } ;
882
+ let int = match int {
883
+ rustc_abi:: IntegerType :: Pointer ( _) => format ! ( "{prefix}size" ) ,
884
+ rustc_abi:: IntegerType :: Fixed ( int, _) => {
885
+ format ! ( "{prefix}{}" , int. size( ) . bytes( ) * 8 )
833
886
}
834
- }
835
- if repr. simd ( ) {
836
- out. push ( "simd" ) ;
837
- }
838
- let pack_s;
839
- if let Some ( pack) = repr. pack {
840
- pack_s = format ! ( "packed({})" , pack. bytes( ) ) ;
841
- out. push ( & pack_s) ;
842
- }
843
- let align_s;
844
- if let Some ( align) = repr. align {
845
- align_s = format ! ( "align({})" , align. bytes( ) ) ;
846
- out. push ( & align_s) ;
847
- }
848
- let int_s;
849
- if let Some ( int) = repr. int {
850
- int_s = match int {
851
- IntegerType :: Pointer ( is_signed) => {
852
- format ! ( "{}size" , if is_signed { 'i' } else { 'u' } )
853
- }
854
- IntegerType :: Fixed ( size, is_signed) => {
855
- format ! ( "{}{}" , if is_signed { 'i' } else { 'u' } , size. size( ) . bytes( ) * 8 )
856
- }
857
- } ;
858
- out. push ( & int_s) ;
859
- }
860
- if !out. is_empty ( ) {
861
- attrs. push ( format ! ( "#[repr({})]" , out. join( ", " ) ) ) ;
862
- }
887
+ } ;
888
+ result. push ( int. into ( ) ) ;
863
889
}
864
- attrs
890
+
891
+ // Render modifiers last.
892
+ if let Some ( pack) = repr. pack {
893
+ result. push ( format ! ( "packed({})" , pack. bytes( ) ) . into ( ) ) ;
894
+ }
895
+ if let Some ( align) = repr. align {
896
+ result. push ( format ! ( "align({})" , align. bytes( ) ) . into ( ) ) ;
897
+ }
898
+
899
+ ( !result. is_empty ( ) ) . then ( || format ! ( "#[repr({})]" , result. join( ", " ) ) )
865
900
}
866
901
867
902
pub fn is_doc_hidden ( & self ) -> bool {
0 commit comments