@@ -24,6 +24,7 @@ use rustc_ast::MacroDef;
2424use rustc_ast:: visit:: { VisitorResult , try_visit} ;
2525use rustc_data_structures:: fx:: FxHashSet ;
2626use rustc_data_structures:: intern:: Interned ;
27+ use rustc_errors:: MultiSpan ;
2728use rustc_hir:: def:: { DefKind , Res } ;
2829use rustc_hir:: def_id:: { CRATE_DEF_ID , DefId , LocalDefId , LocalModDefId } ;
2930use rustc_hir:: intravisit:: { self , Visitor } ;
@@ -38,7 +39,7 @@ use rustc_middle::ty::{
3839use rustc_middle:: { bug, span_bug} ;
3940use rustc_session:: lint;
4041use rustc_span:: hygiene:: Transparency ;
41- use rustc_span:: { Ident , Span , kw, sym} ;
42+ use rustc_span:: { Ident , Span , Symbol , kw, sym} ;
4243use tracing:: debug;
4344use { rustc_attr_parsing as attr, rustc_hir as hir} ;
4445
@@ -921,31 +922,95 @@ impl<'tcx> NamePrivacyVisitor<'tcx> {
921922 & mut self ,
922923 hir_id : hir:: HirId , // ID of the field use
923924 use_ctxt : Span , // syntax context of the field name at the use site
924- span : Span , // span of the field pattern, e.g., `x: 0`
925925 def : ty:: AdtDef < ' tcx > , // definition of the struct or enum
926926 field : & ' tcx ty:: FieldDef ,
927- in_update_syntax : bool ,
928- ) {
927+ ) -> bool {
929928 if def. is_enum ( ) {
930- return ;
929+ return true ;
931930 }
932931
933932 // definition of the field
934933 let ident = Ident :: new ( kw:: Empty , use_ctxt) ;
935934 let def_id = self . tcx . adjust_ident_and_get_scope ( ident, def. did ( ) , hir_id) . 1 ;
936- if !field. vis . is_accessible_from ( def_id, self . tcx ) {
937- self . tcx . dcx ( ) . emit_err ( FieldIsPrivate {
938- span,
939- field_name : field. name ,
940- variant_descr : def. variant_descr ( ) ,
941- def_path_str : self . tcx . def_path_str ( def. did ( ) ) ,
942- label : if in_update_syntax {
943- FieldIsPrivateLabel :: IsUpdateSyntax { span, field_name : field. name }
944- } else {
945- FieldIsPrivateLabel :: Other { span }
946- } ,
947- } ) ;
935+ !field. vis . is_accessible_from ( def_id, self . tcx )
936+ }
937+
938+ // Checks that a field in a struct constructor (expression or pattern) is accessible.
939+ fn emit_unreachable_field_error (
940+ & mut self ,
941+ fields : Vec < ( Symbol , Span , bool /* field is present */ ) > ,
942+ def : ty:: AdtDef < ' tcx > , // definition of the struct or enum
943+ update_syntax : Option < Span > ,
944+ struct_span : Span ,
945+ ) {
946+ if def. is_enum ( ) || fields. is_empty ( ) {
947+ return ;
948948 }
949+
950+ // error[E0451]: fields `beta` and `gamma` of struct `Alpha` are private
951+ // --> $DIR/visibility.rs:18:13
952+ // |
953+ // LL | let _x = Alpha {
954+ // | ----- in this type # from `def`
955+ // LL | beta: 0,
956+ // | ^^^^^^^ private field # `fields.2` is `true`
957+ // LL | ..
958+ // | ^^ field `gamma` is private # `fields.2` is `false`
959+
960+ // Get the list of all private fields for the main message.
961+ let field_names: Vec < _ > = fields. iter ( ) . map ( |( name, _, _) | name) . collect ( ) ;
962+ let field_names = match & field_names[ ..] {
963+ [ ] => return ,
964+ [ name] => format ! ( "`{name}`" ) ,
965+ [ fields @ .., last] => format ! (
966+ "{} and `{last}`" ,
967+ fields. iter( ) . map( |f| format!( "`{f}`" ) ) . collect:: <Vec <_>>( ) . join( ", " ) ,
968+ ) ,
969+ } ;
970+ let span: MultiSpan = fields. iter ( ) . map ( |( _, span, _) | * span) . collect :: < Vec < Span > > ( ) . into ( ) ;
971+
972+ // Get the list of all private fields when pointing at the `..rest`.
973+ let rest_field_names: Vec < _ > =
974+ fields. iter ( ) . filter ( |( _, _, is_present) | !is_present) . map ( |( n, _, _) | n) . collect ( ) ;
975+ let rest_len = rest_field_names. len ( ) ;
976+ let rest_field_names = match & rest_field_names[ ..] {
977+ [ ] => String :: new ( ) ,
978+ [ name] => format ! ( "`{name}`" ) ,
979+ [ fields @ .., last] => format ! (
980+ "{} and `{last}`" ,
981+ fields. iter( ) . map( |f| format!( "`{f}`" ) ) . collect:: <Vec <_>>( ) . join( ", " ) ,
982+ ) ,
983+ } ;
984+ // Get all the labels for each field or `..rest` in the primary MultiSpan.
985+ let labels = fields
986+ . iter ( )
987+ . filter ( |( _, _, is_present) | * is_present)
988+ . map ( |( _, span, _) | FieldIsPrivateLabel :: Other { span : * span } )
989+ . chain ( update_syntax. iter ( ) . map ( |span| FieldIsPrivateLabel :: IsUpdateSyntax {
990+ span : * span,
991+ rest_field_names : rest_field_names. clone ( ) ,
992+ rest_len,
993+ } ) )
994+ . collect ( ) ;
995+
996+ self . tcx . dcx ( ) . emit_err ( FieldIsPrivate {
997+ span,
998+ struct_span : if self
999+ . tcx
1000+ . sess
1001+ . source_map ( )
1002+ . is_multiline ( fields[ 0 ] . 1 . between ( struct_span) )
1003+ {
1004+ Some ( struct_span)
1005+ } else {
1006+ None
1007+ } ,
1008+ field_names : field_names. clone ( ) ,
1009+ variant_descr : def. variant_descr ( ) ,
1010+ def_path_str : self . tcx . def_path_str ( def. did ( ) ) ,
1011+ labels,
1012+ len : fields. len ( ) ,
1013+ } ) ;
9491014 }
9501015
9511016 fn check_expanded_fields (
@@ -955,16 +1020,25 @@ impl<'tcx> NamePrivacyVisitor<'tcx> {
9551020 fields : & [ hir:: ExprField < ' tcx > ] ,
9561021 hir_id : hir:: HirId ,
9571022 span : Span ,
1023+ struct_span : Span ,
9581024 ) {
1025+ let mut failed_fields = vec ! [ ] ;
9591026 for ( vf_index, variant_field) in variant. fields . iter_enumerated ( ) {
9601027 let field =
9611028 fields. iter ( ) . find ( |f| self . typeck_results ( ) . field_index ( f. hir_id ) == vf_index) ;
9621029 let ( hir_id, use_ctxt, span) = match field {
9631030 Some ( field) => ( field. hir_id , field. ident . span , field. span ) ,
9641031 None => ( hir_id, span, span) ,
9651032 } ;
966- self . check_field ( hir_id, use_ctxt, span, adt, variant_field, true ) ;
1033+ if self . check_field ( hir_id, use_ctxt, adt, variant_field) {
1034+ let name = match field {
1035+ Some ( field) => field. ident . name ,
1036+ None => variant_field. name ,
1037+ } ;
1038+ failed_fields. push ( ( name, span, field. is_some ( ) ) ) ;
1039+ }
9671040 }
1041+ self . emit_unreachable_field_error ( failed_fields, adt, Some ( span) , struct_span) ;
9681042 }
9691043}
9701044
@@ -990,24 +1064,35 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
9901064 // If the expression uses FRU we need to make sure all the unmentioned fields
9911065 // are checked for privacy (RFC 736). Rather than computing the set of
9921066 // unmentioned fields, just check them all.
993- self . check_expanded_fields ( adt, variant, fields, base. hir_id , base. span ) ;
1067+ self . check_expanded_fields (
1068+ adt,
1069+ variant,
1070+ fields,
1071+ base. hir_id ,
1072+ base. span ,
1073+ qpath. span ( ) ,
1074+ ) ;
9941075 }
9951076 hir:: StructTailExpr :: DefaultFields ( span) => {
996- self . check_expanded_fields ( adt, variant, fields, expr. hir_id , span) ;
1077+ self . check_expanded_fields (
1078+ adt,
1079+ variant,
1080+ fields,
1081+ expr. hir_id ,
1082+ span,
1083+ qpath. span ( ) ,
1084+ ) ;
9971085 }
9981086 hir:: StructTailExpr :: None => {
1087+ let mut failed_fields = vec ! [ ] ;
9991088 for field in fields {
1000- let ( hir_id, use_ctxt, span ) = ( field. hir_id , field. ident . span , field . span ) ;
1089+ let ( hir_id, use_ctxt) = ( field. hir_id , field. ident . span ) ;
10011090 let index = self . typeck_results ( ) . field_index ( field. hir_id ) ;
1002- self . check_field (
1003- hir_id,
1004- use_ctxt,
1005- span,
1006- adt,
1007- & variant. fields [ index] ,
1008- false ,
1009- ) ;
1091+ if self . check_field ( hir_id, use_ctxt, adt, & variant. fields [ index] ) {
1092+ failed_fields. push ( ( field. ident . name , field. ident . span , true ) ) ;
1093+ }
10101094 }
1095+ self . emit_unreachable_field_error ( failed_fields, adt, None , qpath. span ( ) ) ;
10111096 }
10121097 }
10131098 }
@@ -1020,11 +1105,15 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
10201105 let res = self . typeck_results ( ) . qpath_res ( qpath, pat. hir_id ) ;
10211106 let adt = self . typeck_results ( ) . pat_ty ( pat) . ty_adt_def ( ) . unwrap ( ) ;
10221107 let variant = adt. variant_of_res ( res) ;
1108+ let mut failed_fields = vec ! [ ] ;
10231109 for field in fields {
1024- let ( hir_id, use_ctxt, span ) = ( field. hir_id , field. ident . span , field . span ) ;
1110+ let ( hir_id, use_ctxt) = ( field. hir_id , field. ident . span ) ;
10251111 let index = self . typeck_results ( ) . field_index ( field. hir_id ) ;
1026- self . check_field ( hir_id, use_ctxt, span, adt, & variant. fields [ index] , false ) ;
1112+ if self . check_field ( hir_id, use_ctxt, adt, & variant. fields [ index] ) {
1113+ failed_fields. push ( ( field. ident . name , field. ident . span , true ) ) ;
1114+ }
10271115 }
1116+ self . emit_unreachable_field_error ( failed_fields, adt, None , qpath. span ( ) ) ;
10281117 }
10291118
10301119 intravisit:: walk_pat ( self , pat) ;
0 commit comments