@@ -24,6 +24,7 @@ use rustc_ast::MacroDef;
24
24
use rustc_ast:: visit:: { VisitorResult , try_visit} ;
25
25
use rustc_data_structures:: fx:: FxHashSet ;
26
26
use rustc_data_structures:: intern:: Interned ;
27
+ use rustc_errors:: MultiSpan ;
27
28
use rustc_hir:: def:: { DefKind , Res } ;
28
29
use rustc_hir:: def_id:: { CRATE_DEF_ID , DefId , LocalDefId , LocalModDefId } ;
29
30
use rustc_hir:: intravisit:: { self , Visitor } ;
@@ -38,7 +39,7 @@ use rustc_middle::ty::{
38
39
use rustc_middle:: { bug, span_bug} ;
39
40
use rustc_session:: lint;
40
41
use rustc_span:: hygiene:: Transparency ;
41
- use rustc_span:: { Ident , Span , kw, sym} ;
42
+ use rustc_span:: { Ident , Span , Symbol , kw, sym} ;
42
43
use tracing:: debug;
43
44
use { rustc_attr_parsing as attr, rustc_hir as hir} ;
44
45
@@ -921,37 +922,95 @@ impl<'tcx> NamePrivacyVisitor<'tcx> {
921
922
& mut self ,
922
923
hir_id : hir:: HirId , // ID of the field use
923
924
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`
925
925
def : ty:: AdtDef < ' tcx > , // definition of the struct or enum
926
926
field : & ' tcx ty:: FieldDef ,
927
- in_update_syntax : bool ,
928
- struct_span : Span ,
929
- ) {
927
+ ) -> bool {
930
928
if def. is_enum ( ) {
931
- return ;
929
+ return true ;
932
930
}
933
931
934
932
// definition of the field
935
933
let ident = Ident :: new ( kw:: Empty , use_ctxt) ;
936
934
let def_id = self . tcx . adjust_ident_and_get_scope ( ident, def. did ( ) , hir_id) . 1 ;
937
- if !field. vis . is_accessible_from ( def_id, self . tcx ) {
938
- self . tcx . dcx ( ) . emit_err ( FieldIsPrivate {
939
- span,
940
- struct_span : if self . tcx . sess . source_map ( ) . is_multiline ( span. between ( struct_span) ) {
941
- Some ( struct_span)
942
- } else {
943
- None
944
- } ,
945
- field_name : field. name ,
946
- variant_descr : def. variant_descr ( ) ,
947
- def_path_str : self . tcx . def_path_str ( def. did ( ) ) ,
948
- label : if in_update_syntax {
949
- FieldIsPrivateLabel :: IsUpdateSyntax { span, field_name : field. name }
950
- } else {
951
- FieldIsPrivateLabel :: Other { span }
952
- } ,
953
- } ) ;
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 ;
954
948
}
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
+ } ) ;
955
1014
}
956
1015
957
1016
fn check_expanded_fields (
@@ -963,15 +1022,23 @@ impl<'tcx> NamePrivacyVisitor<'tcx> {
963
1022
span : Span ,
964
1023
struct_span : Span ,
965
1024
) {
1025
+ let mut failed_fields = vec ! [ ] ;
966
1026
for ( vf_index, variant_field) in variant. fields . iter_enumerated ( ) {
967
1027
let field =
968
1028
fields. iter ( ) . find ( |f| self . typeck_results ( ) . field_index ( f. hir_id ) == vf_index) ;
969
1029
let ( hir_id, use_ctxt, span) = match field {
970
1030
Some ( field) => ( field. hir_id , field. ident . span , field. span ) ,
971
1031
None => ( hir_id, span, span) ,
972
1032
} ;
973
- self . check_field ( hir_id, use_ctxt, span, adt, variant_field, true , struct_span) ;
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
+ }
974
1040
}
1041
+ self . emit_unreachable_field_error ( failed_fields, adt, Some ( span) , struct_span) ;
975
1042
}
976
1043
}
977
1044
@@ -1017,19 +1084,15 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
1017
1084
) ;
1018
1085
}
1019
1086
hir:: StructTailExpr :: None => {
1087
+ let mut failed_fields = vec ! [ ] ;
1020
1088
for field in fields {
1021
- 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 ) ;
1022
1090
let index = self . typeck_results ( ) . field_index ( field. hir_id ) ;
1023
- self . check_field (
1024
- hir_id,
1025
- use_ctxt,
1026
- span,
1027
- adt,
1028
- & variant. fields [ index] ,
1029
- false ,
1030
- qpath. span ( ) ,
1031
- ) ;
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
+ }
1032
1094
}
1095
+ self . emit_unreachable_field_error ( failed_fields, adt, None , qpath. span ( ) ) ;
1033
1096
}
1034
1097
}
1035
1098
}
@@ -1042,19 +1105,15 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
1042
1105
let res = self . typeck_results ( ) . qpath_res ( qpath, pat. hir_id ) ;
1043
1106
let adt = self . typeck_results ( ) . pat_ty ( pat) . ty_adt_def ( ) . unwrap ( ) ;
1044
1107
let variant = adt. variant_of_res ( res) ;
1108
+ let mut failed_fields = vec ! [ ] ;
1045
1109
for field in fields {
1046
- 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 ) ;
1047
1111
let index = self . typeck_results ( ) . field_index ( field. hir_id ) ;
1048
- self . check_field (
1049
- hir_id,
1050
- use_ctxt,
1051
- span,
1052
- adt,
1053
- & variant. fields [ index] ,
1054
- false ,
1055
- qpath. span ( ) ,
1056
- ) ;
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
+ }
1057
1115
}
1116
+ self . emit_unreachable_field_error ( failed_fields, adt, None , qpath. span ( ) ) ;
1058
1117
}
1059
1118
1060
1119
intravisit:: walk_pat ( self , pat) ;
0 commit comments