@@ -222,8 +222,10 @@ impl<'a, 'tcx, V> TypeVisitor<'tcx> for DefIdVisitorSkeleton<'_, 'a, 'tcx, V>
222
222
}
223
223
}
224
224
225
- fn def_id_visibility < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > , def_id : DefId )
226
- -> ( ty:: Visibility , Span , & ' static str ) {
225
+ fn def_id_visibility < ' a , ' tcx > (
226
+ tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
227
+ def_id : DefId ,
228
+ ) -> ( ty:: Visibility , Span , & ' static str ) {
227
229
match tcx. hir ( ) . as_local_node_id ( def_id) {
228
230
Some ( node_id) => {
229
231
let vis = match tcx. hir ( ) . get ( node_id) {
@@ -799,22 +801,71 @@ struct NamePrivacyVisitor<'a, 'tcx: 'a> {
799
801
tables : & ' a ty:: TypeckTables < ' tcx > ,
800
802
current_item : ast:: NodeId ,
801
803
empty_tables : & ' a ty:: TypeckTables < ' tcx > ,
804
+ reported_tuple_structs : FxHashSet < Span > ,
802
805
}
803
806
804
807
impl < ' a , ' tcx > NamePrivacyVisitor < ' a , ' tcx > {
805
808
// Checks that a field in a struct constructor (expression or pattern) is accessible.
806
- fn check_field ( & mut self ,
807
- use_ctxt : Span , // syntax context of the field name at the use site
808
- span : Span , // span of the field pattern, e.g., `x: 0`
809
- def : & ' tcx ty:: AdtDef , // definition of the struct or enum
810
- field : & ' tcx ty:: FieldDef ) { // definition of the field
809
+ fn check_field (
810
+ & mut self ,
811
+ use_ctxt : Span , // syntax context of the field name at the use site
812
+ span : Span , // span of the field pattern, e.g., `x: 0`
813
+ def : & ' tcx ty:: AdtDef , // definition of the struct or enum
814
+ field : & ' tcx ty:: FieldDef , // definition of the field
815
+ ) -> Option < ( String /* field name */ , Span ) > {
811
816
let ident = Ident :: new ( keywords:: Invalid . name ( ) , use_ctxt) ;
812
817
let def_id = self . tcx . adjust_ident ( ident, def. did , self . current_item ) . 1 ;
813
818
if !def. is_enum ( ) && !field. vis . is_accessible_from ( def_id, self . tcx ) {
814
- struct_span_err ! ( self . tcx. sess, span, E0451 , "field `{}` of {} `{}` is private" ,
815
- field. ident, def. variant_descr( ) , self . tcx. item_path_str( def. did) )
816
- . span_label ( span, format ! ( "field `{}` is private" , field. ident) )
817
- . emit ( ) ;
819
+ return Some ( ( field. ident . to_string ( ) , span) ) ;
820
+ }
821
+ None
822
+ }
823
+
824
+ /// If appropriate, construct a privacy error pointing at all the fields of a literal struct
825
+ /// that are private both when constructing an instance or destructuring a pattern.
826
+ fn emit_field_checks (
827
+ & mut self ,
828
+ // d: Def,
829
+ def : & ' tcx ty:: AdtDef , // definition of the struct or enum
830
+ span : Span , // struct span at use site
831
+ fields : Vec < ( String , Span ) > , // inaccessible ADT fields
832
+ action : & str , // "built" or "destructured" depending of where this happened
833
+ ) {
834
+ let item_path = self . tcx . item_path_str ( def. did ) ;
835
+
836
+ if !fields. is_empty ( ) {
837
+ self . reported_tuple_structs . insert ( span) ;
838
+ let mut err = struct_span_err ! (
839
+ self . tcx. sess,
840
+ fields. iter( ) . map( |( _, sp) | * sp) . collect:: <Vec <Span >>( ) ,
841
+ E0451 ,
842
+ "field{} of {} `{}` {} private" ,
843
+ if fields. len( ) == 1 {
844
+ format!( " `{}`" , fields[ 0 ] . 0 )
845
+ } else {
846
+ "s" . to_owned( )
847
+ } ,
848
+ def. variant_descr( ) ,
849
+ item_path,
850
+ if fields. len( ) == 1 {
851
+ "is"
852
+ } else {
853
+ "are"
854
+ } ,
855
+ ) ;
856
+ err. span_label ( span, format ! (
857
+ "`{}` cannot be {} due to private field{}" ,
858
+ item_path,
859
+ action,
860
+ if fields. len( ) == 1 { "" } else { "s" } ,
861
+ ) ) ;
862
+ for ( _field_name, field) in fields {
863
+ err. span_label ( field, "private field" ) ;
864
+ }
865
+
866
+ // Point at definition
867
+ err. span_label ( self . tcx . def_span ( def. did ) , format ! ( "`{}` defined here" , item_path) ) ;
868
+ err. emit ( ) ;
818
869
}
819
870
}
820
871
}
@@ -867,6 +918,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
867
918
let def = self . tables . qpath_def ( qpath, expr. hir_id ) ;
868
919
let adt = self . tables . expr_ty ( expr) . ty_adt_def ( ) . unwrap ( ) ;
869
920
let variant = adt. variant_of_def ( def) ;
921
+ let mut field_errors = vec ! [ ] ;
870
922
if let Some ( ref base) = * base {
871
923
// If the expression uses FRU we need to make sure all the unmentioned fields
872
924
// are checked for privacy (RFC 736). Rather than computing the set of
@@ -879,13 +931,48 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
879
931
Some ( field) => ( field. ident . span , field. span ) ,
880
932
None => ( base. span , base. span ) ,
881
933
} ;
882
- self . check_field ( use_ctxt, span, adt, variant_field) ;
934
+ if let Some ( err) = self . check_field ( use_ctxt, span, adt, variant_field) {
935
+ field_errors. push ( err) ;
936
+ }
883
937
}
884
938
} else {
885
939
for field in fields {
886
940
let use_ctxt = field. ident . span ;
887
941
let index = self . tcx . field_index ( field. id , self . tables ) ;
888
- self . check_field ( use_ctxt, field. span , adt, & variant. fields [ index] ) ;
942
+ if let Some ( err) = self . check_field (
943
+ use_ctxt,
944
+ field. span ,
945
+ adt,
946
+ & variant. fields [ index] ,
947
+ ) {
948
+ field_errors. push ( err) ;
949
+ }
950
+ }
951
+ }
952
+ self . emit_field_checks ( adt, expr. span , field_errors, "built" ) ;
953
+ }
954
+ hir:: ExprKind :: Call ( ref path, ref fields) => {
955
+ if let hir:: ExprKind :: Path ( qpath) = & path. node {
956
+ let def = self . tables . qpath_def ( qpath, path. hir_id ) ;
957
+ if let Some ( _) = def. opt_def_id ( ) {
958
+ if let Some ( adt) = self . tables . expr_ty ( expr) . ty_adt_def ( ) {
959
+ if let Some ( variant) = adt. opt_variant_of_def ( def) {
960
+ let mut field_errors = vec ! [ ] ;
961
+ for ( idx, field) in variant. fields . iter ( ) . enumerate ( ) {
962
+ let use_ctxt = fields. get ( idx) . map ( |f| f. span )
963
+ . unwrap_or ( path. span ) ;
964
+ if let Some ( err) = self . check_field (
965
+ use_ctxt,
966
+ use_ctxt,
967
+ adt,
968
+ & field,
969
+ ) {
970
+ field_errors. push ( err) ;
971
+ }
972
+ }
973
+ self . emit_field_checks ( adt, path. span , field_errors, "built" ) ;
974
+ }
975
+ }
889
976
}
890
977
}
891
978
}
@@ -901,11 +988,39 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
901
988
let def = self . tables . qpath_def ( qpath, pat. hir_id ) ;
902
989
let adt = self . tables . pat_ty ( pat) . ty_adt_def ( ) . unwrap ( ) ;
903
990
let variant = adt. variant_of_def ( def) ;
991
+ let mut field_errors = vec ! [ ] ;
904
992
for field in fields {
905
993
let use_ctxt = field. node . ident . span ;
906
994
let index = self . tcx . field_index ( field. node . id , self . tables ) ;
907
- self . check_field ( use_ctxt, field. span , adt, & variant. fields [ index] ) ;
995
+ if let Some ( err) = self . check_field (
996
+ use_ctxt,
997
+ field. span ,
998
+ adt,
999
+ & variant. fields [ index] ,
1000
+ ) {
1001
+ field_errors. push ( err) ;
1002
+ }
1003
+ }
1004
+ self . emit_field_checks ( adt, pat. span , field_errors, "destructured" ) ;
1005
+ }
1006
+ PatKind :: TupleStruct ( ref qpath, ref patterns, ..) => {
1007
+ let def = self . tables . qpath_def ( qpath, pat. hir_id ) ;
1008
+ let adt = self . tables . pat_ty ( pat) . ty_adt_def ( ) . unwrap ( ) ;
1009
+ let variant = adt. variant_of_def ( def) ;
1010
+ let mut field_errors = vec ! [ ] ;
1011
+ for ( vf_index, variant_field) in variant. fields . iter ( ) . enumerate ( ) {
1012
+ if let Some ( pat) = patterns. get ( vf_index) {
1013
+ if let Some ( err) = self . check_field (
1014
+ pat. span ,
1015
+ pat. span ,
1016
+ adt,
1017
+ variant_field,
1018
+ ) {
1019
+ field_errors. push ( err) ;
1020
+ }
1021
+ }
908
1022
}
1023
+ self . emit_field_checks ( adt, pat. span , field_errors, "destructured" ) ;
909
1024
}
910
1025
_ => { }
911
1026
}
@@ -927,11 +1042,13 @@ struct TypePrivacyVisitor<'a, 'tcx: 'a> {
927
1042
in_body : bool ,
928
1043
span : Span ,
929
1044
empty_tables : & ' a ty:: TypeckTables < ' tcx > ,
1045
+ reported_tuple_structs : FxHashSet < Span > ,
930
1046
}
931
1047
932
1048
impl < ' a , ' tcx > TypePrivacyVisitor < ' a , ' tcx > {
933
1049
fn item_is_accessible ( & self , did : DefId ) -> bool {
934
- def_id_visibility ( self . tcx , did) . 0 . is_accessible_from ( self . current_item , self . tcx )
1050
+ let ( a, ..) = def_id_visibility ( self . tcx , did) ;
1051
+ a. is_accessible_from ( self . current_item , self . tcx )
935
1052
}
936
1053
937
1054
// Take node-id of an expression or pattern and check its type for privacy.
@@ -951,11 +1068,33 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
951
1068
}
952
1069
953
1070
fn check_def_id ( & mut self , def_id : DefId , kind : & str , descr : & dyn fmt:: Display ) -> bool {
954
- let is_error = !self . item_is_accessible ( def_id) ;
955
- if is_error {
956
- self . tcx . sess . span_err ( self . span , & format ! ( "{} `{}` is private" , kind, descr) ) ;
1071
+ let is_ok = self . item_is_accessible ( def_id) ;
1072
+ if !is_ok {
1073
+ match self . tcx . hir ( ) . as_local_node_id ( def_id) {
1074
+ Some ( node_id) => {
1075
+ match self . tcx . hir ( ) . get ( node_id) {
1076
+ Node :: StructCtor ( hir:: VariantData :: Tuple ( ..) ) => {
1077
+ // Ignore tuple structs, as they are handled in `visit_path`
1078
+ return false ;
1079
+ }
1080
+ _ => { }
1081
+ }
1082
+ }
1083
+ _ => { }
1084
+ }
1085
+ let msg = if let Some ( def) = self . tcx . describe_def ( def_id) {
1086
+ format ! ( "{} `{}` is private" , def. kind_name( ) , self . tcx. item_path_str( def_id) )
1087
+ } else {
1088
+ format ! ( "{} `{}` is private" , kind, descr)
1089
+ } ;
1090
+ if !self . reported_tuple_structs . iter ( ) . any ( |sp| sp. overlaps ( self . span ) ) {
1091
+ self . tcx . sess
1092
+ . struct_span_err ( self . span , & msg)
1093
+ . span_label ( self . span , "private" )
1094
+ . emit ( ) ;
1095
+ }
957
1096
}
958
- is_error
1097
+ !is_ok
959
1098
}
960
1099
}
961
1100
@@ -1079,14 +1218,37 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
1079
1218
hir:: QPath :: TypeRelative ( _, ref segment) => segment. ident . to_string ( ) ,
1080
1219
} ;
1081
1220
let msg = format ! ( "{} `{}` is private" , def. kind_name( ) , name) ;
1082
- self . tcx . sess . span_err ( span, & msg) ;
1221
+ let label = format ! ( "{} not accessible from here" , def. kind_name( ) ) ;
1222
+ self . tcx . sess . struct_span_err ( span, & msg)
1223
+ . span_label ( span, label)
1224
+ . emit ( ) ;
1083
1225
return ;
1084
1226
}
1085
1227
}
1086
1228
1087
1229
intravisit:: walk_qpath ( self , qpath, id, span) ;
1088
1230
}
1089
1231
1232
+ // Prohibit access to tuple structs that are either unreachable *or* have private fields.
1233
+ fn visit_path ( & mut self , path : & ' tcx hir:: Path , _id : hir:: HirId ) {
1234
+ // We handle tuple struct visibility here to only complain about bare paths referencing an
1235
+ // unreachable tuple struct or one that has private fields.
1236
+ if let Def :: StructCtor ( def_id, hir:: def:: CtorKind :: Fn ) = path. def {
1237
+ if !self . item_is_accessible ( def_id) &&
1238
+ // only report if this is a bare path, not part of a tuple struct literal
1239
+ !self . reported_tuple_structs . iter ( ) . any ( |sp| sp. overlaps ( path. span ) )
1240
+ {
1241
+ let kind_name = path. def . kind_name ( ) ;
1242
+ let sp = path. span ;
1243
+ let msg = format ! ( "{} `{}` is private" , kind_name, path) ;
1244
+ let label = format ! ( "{} not accesssible from here" , kind_name) ;
1245
+ self . tcx . sess . struct_span_err ( sp, & msg)
1246
+ . span_label ( sp, label)
1247
+ . emit ( ) ;
1248
+ }
1249
+ }
1250
+ }
1251
+
1090
1252
// Check types of patterns.
1091
1253
fn visit_pat ( & mut self , pattern : & ' tcx hir:: Pat ) {
1092
1254
if self . check_expr_pat_type ( pattern. hir_id , pattern. span ) {
@@ -1770,6 +1932,7 @@ fn check_mod_privacy<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>, module_def_id: DefId) {
1770
1932
tables : & empty_tables,
1771
1933
current_item : DUMMY_NODE_ID ,
1772
1934
empty_tables : & empty_tables,
1935
+ reported_tuple_structs : FxHashSet :: default ( ) ,
1773
1936
} ;
1774
1937
let ( module, span, node_id) = tcx. hir ( ) . get_module ( module_def_id) ;
1775
1938
intravisit:: walk_mod ( & mut visitor, module, node_id) ;
@@ -1783,6 +1946,7 @@ fn check_mod_privacy<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>, module_def_id: DefId) {
1783
1946
in_body : false ,
1784
1947
span,
1785
1948
empty_tables : & empty_tables,
1949
+ reported_tuple_structs : visitor. reported_tuple_structs ,
1786
1950
} ;
1787
1951
intravisit:: walk_mod ( & mut visitor, module, node_id) ;
1788
1952
}
0 commit comments