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