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,69 +801,103 @@ 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_ ( )
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 || is_visible ( var. def_id )
834
+ && var
835
+ . fields
836
+ . iter ( )
837
+ . find ( |field| {
838
+ let ty = field. ty ( tcx, ty:: GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
839
+ tcx. layout_of (
840
+ ty:: TypingEnv :: post_analysis ( tcx, field. did ) . as_query_input ( ty) ,
841
+ )
842
+ . is_ok_and ( |layout| !layout. is_1zst ( ) )
843
+ } )
844
+ . map_or_else (
845
+ || var. fields . is_empty ( ) || var. fields . iter ( ) . any ( is_field_public) ,
846
+ is_field_public,
847
+ ) ;
848
+
849
+ // Since `repr(transparent)` can't have any other reprs or
850
+ // repr modifiers beside it, we can safely return early here.
851
+ return is_public. then ( || "#[repr(transparent)]" . into ( ) ) ;
852
+ }
853
+
854
+ // Fast path which avoids looking through the variants and fields in
855
+ // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
856
+ // FIXME: This check is not forward compatible!
857
+ if !repr. c ( )
858
+ && !repr. simd ( )
859
+ && repr. int . is_none ( )
860
+ && repr. pack . is_none ( )
861
+ && repr. align . is_none ( )
811
862
{
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 ( )
824
- . find ( |field| {
825
- let ty =
826
- field. ty ( tcx, ty:: GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
827
- tcx. layout_of (
828
- ty:: TypingEnv :: post_analysis ( tcx, field. did ) . as_query_input ( ty) ,
829
- )
830
- . is_ok_and ( |layout| !layout. is_1zst ( ) )
831
- } )
832
- . map_or_else (
833
- || adt. all_fields ( ) . any ( |field| field. vis . is_public ( ) ) ,
834
- |field| field. vis . is_public ( ) ,
835
- ) ;
863
+ return None ;
864
+ }
836
865
837
- if render_transparent {
838
- out. push ( "transparent" ) ;
866
+ let is_public = is_json || adt. variants ( ) . iter ( ) . all ( |variant| {
867
+ is_visible ( variant. def_id ) && variant. fields . iter ( ) . all ( is_field_public)
868
+ } ) ;
869
+ if !is_public {
870
+ return None ;
871
+ }
872
+
873
+ let mut result = Vec :: < Cow < ' _ , _ > > :: new ( ) ;
874
+
875
+ if repr. c ( ) {
876
+ result. push ( "C" . into ( ) ) ;
877
+ }
878
+ if repr. simd ( ) {
879
+ result. push ( "simd" . into ( ) ) ;
880
+ }
881
+ if let Some ( int) = repr. int {
882
+ let prefix = if int. is_signed ( ) { 'i' } else { 'u' } ;
883
+ let int = match int {
884
+ rustc_abi:: IntegerType :: Pointer ( _) => format ! ( "{prefix}size" ) ,
885
+ rustc_abi:: IntegerType :: Fixed ( int, _) => {
886
+ format ! ( "{prefix}{}" , int. size( ) . bytes( ) * 8 )
839
887
}
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
- }
888
+ } ;
889
+ result. push ( int. into ( ) ) ;
869
890
}
870
- attrs
891
+
892
+ // Render modifiers last.
893
+ if let Some ( pack) = repr. pack {
894
+ result. push ( format ! ( "packed({})" , pack. bytes( ) ) . into ( ) ) ;
895
+ }
896
+ if let Some ( align) = repr. align {
897
+ result. push ( format ! ( "align({})" , align. bytes( ) ) . into ( ) ) ;
898
+ }
899
+
900
+ ( !result. is_empty ( ) ) . then ( || format ! ( "#[repr({})]" , result. join( ", " ) ) )
871
901
}
872
902
873
903
pub fn is_doc_hidden ( & self ) -> bool {
0 commit comments