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 } ;
@@ -772,11 +773,10 @@ impl Item {
772
773
Some ( tcx. visibility ( def_id) )
773
774
}
774
775
775
- /// Get a list of attributes excluding `#[repr]` to display.
776
- ///
777
- /// Only used by the HTML output-format.
778
- fn attributes_without_repr ( & self ) -> Vec < String > {
779
- self . attrs
776
+ // FIXME(fmease): Move into html/ mod, this is for HTML output only.
777
+ pub ( crate ) fn attributes ( & self , tcx : TyCtxt < ' _ > , cache : & Cache ) -> Vec < String > {
778
+ let mut attrs: Vec < _ > = self
779
+ . attrs
780
780
. other_attrs
781
781
. iter ( )
782
782
. filter_map ( |attr| match attr {
@@ -794,26 +794,15 @@ impl Item {
794
794
}
795
795
_ => None ,
796
796
} )
797
- . collect ( )
798
- }
797
+ . collect ( ) ;
799
798
800
- /// Get a list of attributes to display on this item.
801
- ///
802
- /// Only used by the HTML output-format.
803
- pub ( crate ) fn attributes ( & self , tcx : TyCtxt < ' _ > , cache : & Cache ) -> Vec < String > {
804
- let mut attrs = self . attributes_without_repr ( ) ;
805
-
806
- if let Some ( repr_attr) = self . repr ( tcx, cache) {
807
- attrs. push ( repr_attr) ;
799
+ if let Some ( def_id) = self . def_id ( )
800
+ && let Some ( attr) = repr_attribute ( tcx, cache, def_id)
801
+ {
802
+ attrs. push ( attr) ;
808
803
}
809
- attrs
810
- }
811
804
812
- /// Returns a stringified `#[repr(...)]` attribute.
813
- ///
814
- /// Only used by the HTML output-format.
815
- pub ( crate ) fn repr ( & self , tcx : TyCtxt < ' _ > , cache : & Cache ) -> Option < String > {
816
- repr_attributes ( tcx, cache, self . def_id ( ) ?, self . type_ ( ) )
805
+ attrs
817
806
}
818
807
819
808
pub fn is_doc_hidden ( & self ) -> bool {
@@ -825,72 +814,107 @@ impl Item {
825
814
}
826
815
}
827
816
828
- /// Return a string representing the `#[repr]` attribute if present .
817
+ /// Compute the *public* `#[repr]` of the item given by `DefId` .
829
818
///
830
- /// Only used by the HTML output-format.
831
- pub ( crate ) fn repr_attributes (
832
- tcx : TyCtxt < ' _ > ,
819
+ /// Read more about it here:
820
+ /// <https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type>.
821
+ ///
822
+ /// For use in the HTML output only.
823
+ // FIXME(fmease): Move into html/ mod
824
+ pub ( crate ) fn repr_attribute < ' tcx > (
825
+ tcx : TyCtxt < ' tcx > ,
833
826
cache : & Cache ,
834
827
def_id : DefId ,
835
- item_type : ItemType ,
836
828
) -> Option < String > {
837
- use rustc_abi:: IntegerType ;
829
+ let adt = match tcx. def_kind ( def_id) {
830
+ DefKind :: Struct | DefKind :: Enum | DefKind :: Union => tcx. adt_def ( def_id) ,
831
+ _ => return None ,
832
+ } ;
833
+ let repr = adt. repr ( ) ;
834
+
835
+ let is_visible = |def_id| cache. document_hidden || !tcx. is_doc_hidden ( def_id) ;
836
+ let is_public_field = |field : & ty:: FieldDef | {
837
+ ( cache. document_private || field. vis . is_public ( ) ) && is_visible ( field. did )
838
+ } ;
838
839
839
- if !matches ! ( item_type, ItemType :: Struct | ItemType :: Enum | ItemType :: Union ) {
840
+ if repr. transparent ( ) {
841
+ // The transparent repr is public iff the non-1-ZST field is public and visible or
842
+ // – in case all fields are 1-ZST fields — at least one field is public and visible.
843
+ let is_public = ' is_public: {
844
+ // `#[repr(transparent)]` can only be applied to structs and single-variant enums.
845
+ let var = adt. variant ( rustc_abi:: FIRST_VARIANT ) ; // the first and only variant
846
+
847
+ if !is_visible ( var. def_id ) {
848
+ break ' is_public false ;
849
+ }
850
+
851
+ // Side note: There can only ever be one or zero non-1-ZST fields.
852
+ let non_1zst_field = var. fields . iter ( ) . find ( |field| {
853
+ let ty = ty:: TypingEnv :: post_analysis ( tcx, field. did )
854
+ . as_query_input ( tcx. type_of ( field. did ) . instantiate_identity ( ) ) ;
855
+ tcx. layout_of ( ty) . is_ok_and ( |layout| !layout. is_1zst ( ) )
856
+ } ) ;
857
+
858
+ match non_1zst_field {
859
+ Some ( field) => is_public_field ( field) ,
860
+ None => var. fields . is_empty ( ) || var. fields . iter ( ) . any ( is_public_field) ,
861
+ }
862
+ } ;
863
+
864
+ // Since the transparent repr can't have any other reprs or
865
+ // repr modifiers beside it, we can safely return early here.
866
+ return is_public. then ( || "#[repr(transparent)]" . into ( ) ) ;
867
+ }
868
+
869
+ // Fast path which avoids looking through the variants and fields in
870
+ // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
871
+ // FIXME: This check is not very robust / forward compatible!
872
+ if !repr. c ( )
873
+ && !repr. simd ( )
874
+ && repr. int . is_none ( )
875
+ && repr. pack . is_none ( )
876
+ && repr. align . is_none ( )
877
+ {
840
878
return None ;
841
879
}
842
- let adt = tcx. adt_def ( def_id) ;
843
- let repr = adt. repr ( ) ;
844
- let mut out = Vec :: new ( ) ;
845
- if repr. c ( ) {
846
- out. push ( "C" ) ;
880
+
881
+ // The repr is public iff all components are public and visible.
882
+ let is_public = adt
883
+ . variants ( )
884
+ . iter ( )
885
+ . all ( |variant| is_visible ( variant. def_id ) && variant. fields . iter ( ) . all ( is_public_field) ) ;
886
+ if !is_public {
887
+ return None ;
847
888
}
848
- if repr. transparent ( ) {
849
- // Render `repr(transparent)` iff the non-1-ZST field is public or at least one
850
- // field is public in case all fields are 1-ZST fields.
851
- let render_transparent = cache. document_private
852
- || adt
853
- . all_fields ( )
854
- . find ( |field| {
855
- let ty = field. ty ( tcx, ty:: GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
856
- tcx. layout_of ( ty:: TypingEnv :: post_analysis ( tcx, field. did ) . as_query_input ( ty) )
857
- . is_ok_and ( |layout| !layout. is_1zst ( ) )
858
- } )
859
- . map_or_else (
860
- || adt. all_fields ( ) . any ( |field| field. vis . is_public ( ) ) ,
861
- |field| field. vis . is_public ( ) ,
862
- ) ;
863
889
864
- if render_transparent {
865
- out. push ( "transparent" ) ;
866
- }
890
+ let mut result = Vec :: < Cow < ' _ , _ > > :: new ( ) ;
891
+
892
+ if repr. c ( ) {
893
+ result. push ( "C" . into ( ) ) ;
867
894
}
868
895
if repr. simd ( ) {
869
- out. push ( "simd" ) ;
870
- }
871
- let pack_s;
872
- if let Some ( pack) = repr. pack {
873
- pack_s = format ! ( "packed({})" , pack. bytes( ) ) ;
874
- out. push ( & pack_s) ;
896
+ result. push ( "simd" . into ( ) ) ;
875
897
}
876
- let align_s;
877
- if let Some ( align) = repr. align {
878
- align_s = format ! ( "align({})" , align. bytes( ) ) ;
879
- out. push ( & align_s) ;
880
- }
881
- let int_s;
882
898
if let Some ( int) = repr. int {
883
- int_s = match int {
884
- IntegerType :: Pointer ( is_signed) => {
885
- format ! ( "{}size" , if is_signed { 'i' } else { 'u' } )
886
- }
887
- IntegerType :: Fixed ( size, is_signed) => {
888
- format ! ( "{}{}" , if is_signed { 'i' } else { 'u' } , size. size( ) . bytes( ) * 8 )
899
+ let prefix = if int. is_signed ( ) { 'i' } else { 'u' } ;
900
+ let int = match int {
901
+ rustc_abi:: IntegerType :: Pointer ( _) => format ! ( "{prefix}size" ) ,
902
+ rustc_abi:: IntegerType :: Fixed ( int, _) => {
903
+ format ! ( "{prefix}{}" , int. size( ) . bytes( ) * 8 )
889
904
}
890
905
} ;
891
- out . push ( & int_s ) ;
906
+ result . push ( int . into ( ) ) ;
892
907
}
893
- if !out. is_empty ( ) { Some ( format ! ( "#[repr({})]" , out. join( ", " ) ) ) } else { None }
908
+
909
+ // Render modifiers last.
910
+ if let Some ( pack) = repr. pack {
911
+ result. push ( format ! ( "packed({})" , pack. bytes( ) ) . into ( ) ) ;
912
+ }
913
+ if let Some ( align) = repr. align {
914
+ result. push ( format ! ( "align({})" , align. bytes( ) ) . into ( ) ) ;
915
+ }
916
+
917
+ ( !result. is_empty ( ) ) . then ( || format ! ( "#[repr({})]" , result. join( ", " ) ) )
894
918
}
895
919
896
920
#[ derive( Clone , Debug ) ]
0 commit comments