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 } ;
@@ -764,11 +765,12 @@ impl Item {
764
765
Some ( tcx. visibility ( def_id) )
765
766
}
766
767
767
- pub ( crate ) fn attributes_without_repr ( & self , tcx : TyCtxt < ' _ > , is_json : bool ) -> Vec < String > {
768
+ pub ( crate ) fn attributes ( & self , tcx : TyCtxt < ' _ > , cache : & Cache , is_json : bool ) -> Vec < String > {
768
769
const ALLOWED_ATTRIBUTES : & [ Symbol ] =
769
770
& [ sym:: export_name, sym:: link_section, sym:: no_mangle, sym:: non_exhaustive] ;
770
771
771
- self . attrs
772
+ let mut attrs: Vec < _ > = self
773
+ . attrs
772
774
. other_attrs
773
775
. iter ( )
774
776
. filter_map ( |attr| {
@@ -786,36 +788,24 @@ impl Item {
786
788
} ) ,
787
789
}
788
790
} else if attr. has_any_name ( ALLOWED_ATTRIBUTES ) {
789
- Some (
790
- rustc_hir_pretty:: attribute_to_string ( & tcx, attr)
791
- . replace ( "\\ \n " , "" )
792
- . replace ( '\n' , "" )
793
- . replace ( " " , " " ) ,
794
- )
791
+ let attr = rustc_hir_pretty:: attribute_to_string ( & tcx, attr)
792
+ . replace ( "\\ \n " , "" )
793
+ . replace ( '\n' , "" )
794
+ . replace ( " " , " " ) ;
795
+ Some ( attr)
795
796
} else {
796
797
None
797
798
}
798
799
} )
799
- . collect ( )
800
- }
800
+ . collect ( ) ;
801
801
802
- pub ( crate ) fn attributes_and_repr (
803
- & self ,
804
- tcx : TyCtxt < ' _ > ,
805
- cache : & Cache ,
806
- is_json : bool ,
807
- ) -> Vec < String > {
808
- let mut attrs = self . attributes_without_repr ( tcx, is_json) ;
809
-
810
- if let Some ( repr_attr) = self . repr ( tcx, cache, is_json) {
811
- attrs. push ( repr_attr) ;
802
+ if let Some ( def_id) = self . def_id ( )
803
+ && let Some ( attr) = repr_attribute ( tcx, cache, def_id, is_json)
804
+ {
805
+ attrs. push ( attr) ;
812
806
}
813
- attrs
814
- }
815
807
816
- /// Returns a stringified `#[repr(...)]` attribute.
817
- pub ( crate ) fn repr ( & self , tcx : TyCtxt < ' _ > , cache : & Cache , is_json : bool ) -> Option < String > {
818
- repr_attributes ( tcx, cache, self . def_id ( ) ?, self . type_ ( ) , is_json)
808
+ attrs
819
809
}
820
810
821
811
pub fn is_doc_hidden ( & self ) -> bool {
@@ -827,71 +817,123 @@ impl Item {
827
817
}
828
818
}
829
819
830
- pub ( crate ) fn repr_attributes (
831
- tcx : TyCtxt < ' _ > ,
820
+ /// Compute the *public* `#[repr]` of the item given by `DefId`.
821
+ ///
822
+ /// Read more about it here:
823
+ /// <https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type>.
824
+ pub ( crate ) fn repr_attribute < ' tcx > (
825
+ tcx : TyCtxt < ' tcx > ,
832
826
cache : & Cache ,
833
827
def_id : DefId ,
834
- item_type : ItemType ,
835
828
is_json : bool ,
836
829
) -> Option < String > {
837
- use rustc_abi:: IntegerType ;
830
+ let adt = match tcx. def_kind ( def_id) {
831
+ DefKind :: Struct | DefKind :: Enum | DefKind :: Union => tcx. adt_def ( def_id) ,
832
+ _ => return None ,
833
+ } ;
834
+ let repr = adt. repr ( ) ;
838
835
839
- if !matches ! ( item_type, ItemType :: Struct | ItemType :: Enum | ItemType :: Union ) {
836
+ let is_visible = |def_id| cache. document_hidden || !tcx. is_doc_hidden ( def_id) ;
837
+ let is_public_field = |field : & ty:: FieldDef | {
838
+ ( cache. document_private || field. vis . is_public ( ) ) && is_visible ( field. did )
839
+ } ;
840
+ let is_exhaustive = |def_id| !tcx. has_attr ( def_id, sym:: non_exhaustive) ;
841
+
842
+ if repr. transparent ( ) {
843
+ // The transparent repr is public iff the non-1-ZST field is public and visible or
844
+ // – in case all fields are 1-ZST fields — the type is exhaustive and at least
845
+ // one field is public and visible.
846
+ let is_public = ' is_public: {
847
+ if is_json {
848
+ break ' is_public true ;
849
+ }
850
+
851
+ // `#[repr(transparent)]` can only be applied to structs and single-variant enums.
852
+ let var = adt. variant ( rustc_abi:: FIRST_VARIANT ) ; // the first and only variant
853
+
854
+ // Therefore, if we have an enum, we don't care if it is `#[non_exhaustive]` or not
855
+ // since the user isn't allowed to add more variants to it later anyway.
856
+
857
+ if !is_visible ( var. def_id ) {
858
+ break ' is_public false ;
859
+ }
860
+
861
+ // Side note: There can only ever be one or zero non-1-ZST fields.
862
+ let non_1zst_field = var. fields . iter ( ) . find ( |field| {
863
+ let ty = ty:: TypingEnv :: post_analysis ( tcx, field. did )
864
+ . as_query_input ( tcx. type_of ( field. did ) . instantiate_identity ( ) ) ;
865
+ tcx. layout_of ( ty) . is_ok_and ( |layout| !layout. is_1zst ( ) )
866
+ } ) ;
867
+
868
+ match non_1zst_field {
869
+ // We don't care if the containing variant is `#[non_exhaustive]` or not as the
870
+ // user is only allowed to add more *1-ZST* fields which don't matter in the
871
+ // presence of this non-1-ZST field.
872
+ Some ( field) => is_public_field ( field) ,
873
+ None => {
874
+ is_exhaustive ( var. def_id )
875
+ && ( var. fields . is_empty ( ) || var. fields . iter ( ) . any ( is_public_field) )
876
+ }
877
+ }
878
+ } ;
879
+
880
+ // Since the transparent repr can't have any other reprs or
881
+ // repr modifiers beside it, we can safely return early here.
882
+ return is_public. then ( || "#[repr(transparent)]" . into ( ) ) ;
883
+ }
884
+
885
+ // Fast path which avoids looking through the variants and fields in
886
+ // the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
887
+ // FIXME: This check is not very robust / forward compatible!
888
+ if !repr. c ( )
889
+ && !repr. simd ( )
890
+ && repr. int . is_none ( )
891
+ && repr. pack . is_none ( )
892
+ && repr. align . is_none ( )
893
+ {
840
894
return None ;
841
895
}
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" ) ;
896
+
897
+ // The repr is public iff all components are public, visible and exhaustive.
898
+ let is_public = is_json
899
+ || is_exhaustive ( def_id)
900
+ && adt. variants ( ) . iter ( ) . all ( |variant| {
901
+ is_exhaustive ( variant. def_id )
902
+ && is_visible ( variant. def_id )
903
+ && variant. fields . iter ( ) . all ( is_public_field)
904
+ } ) ;
905
+ if !is_public {
906
+ return None ;
847
907
}
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
- || is_json
853
- || adt
854
- . all_fields ( )
855
- . find ( |field| {
856
- let ty = field. ty ( tcx, ty:: GenericArgs :: identity_for_item ( tcx, field. did ) ) ;
857
- tcx. layout_of ( ty:: TypingEnv :: post_analysis ( tcx, field. did ) . as_query_input ( ty) )
858
- . is_ok_and ( |layout| !layout. is_1zst ( ) )
859
- } )
860
- . map_or_else (
861
- || adt. all_fields ( ) . any ( |field| field. vis . is_public ( ) ) ,
862
- |field| field. vis . is_public ( ) ,
863
- ) ;
864
908
865
- if render_transparent {
866
- out. push ( "transparent" ) ;
867
- }
909
+ let mut result = Vec :: < Cow < ' _ , _ > > :: new ( ) ;
910
+
911
+ if repr. c ( ) {
912
+ result. push ( "C" . into ( ) ) ;
868
913
}
869
914
if repr. simd ( ) {
870
- out. push ( "simd" ) ;
871
- }
872
- let pack_s;
873
- if let Some ( pack) = repr. pack {
874
- pack_s = format ! ( "packed({})" , pack. bytes( ) ) ;
875
- out. push ( & pack_s) ;
915
+ result. push ( "simd" . into ( ) ) ;
876
916
}
877
- let align_s;
878
- if let Some ( align) = repr. align {
879
- align_s = format ! ( "align({})" , align. bytes( ) ) ;
880
- out. push ( & align_s) ;
881
- }
882
- let int_s;
883
917
if let Some ( int) = repr. int {
884
- int_s = match int {
885
- IntegerType :: Pointer ( is_signed) => {
886
- format ! ( "{}size" , if is_signed { 'i' } else { 'u' } )
887
- }
888
- IntegerType :: Fixed ( size, is_signed) => {
889
- format ! ( "{}{}" , if is_signed { 'i' } else { 'u' } , size. size( ) . bytes( ) * 8 )
918
+ let prefix = if int. is_signed ( ) { 'i' } else { 'u' } ;
919
+ let int = match int {
920
+ rustc_abi:: IntegerType :: Pointer ( _) => format ! ( "{prefix}size" ) ,
921
+ rustc_abi:: IntegerType :: Fixed ( int, _) => {
922
+ format ! ( "{prefix}{}" , int. size( ) . bytes( ) * 8 )
890
923
}
891
924
} ;
892
- out . push ( & int_s ) ;
925
+ result . push ( int . into ( ) ) ;
893
926
}
894
- if !out. is_empty ( ) { Some ( format ! ( "#[repr({})]" , out. join( ", " ) ) ) } else { None }
927
+
928
+ // Render modifiers last.
929
+ if let Some ( pack) = repr. pack {
930
+ result. push ( format ! ( "packed({})" , pack. bytes( ) ) . into ( ) ) ;
931
+ }
932
+ if let Some ( align) = repr. align {
933
+ result. push ( format ! ( "align({})" , align. bytes( ) ) . into ( ) ) ;
934
+ }
935
+
936
+ ( !result. is_empty ( ) ) . then ( || format ! ( "#[repr({})]" , result. join( ", " ) ) )
895
937
}
896
938
897
939
#[ derive( Clone , Debug ) ]
0 commit comments