@@ -222,8 +222,10 @@ impl<'a, 'tcx, V> TypeVisitor<'tcx> for DefIdVisitorSkeleton<'_, 'a, 'tcx, V>
222222 }
223223}
224224
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 ) {
227229 match tcx. hir ( ) . as_local_node_id ( def_id) {
228230 Some ( node_id) => {
229231 let vis = match tcx. hir ( ) . get ( node_id) {
@@ -799,22 +801,71 @@ struct NamePrivacyVisitor<'a, 'tcx: 'a> {
799801 tables : & ' a ty:: TypeckTables < ' tcx > ,
800802 current_item : ast:: NodeId ,
801803 empty_tables : & ' a ty:: TypeckTables < ' tcx > ,
804+ reported_tuple_structs : FxHashSet < Span > ,
802805}
803806
804807impl < ' a , ' tcx > NamePrivacyVisitor < ' a , ' tcx > {
805808 // 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 ) > {
811816 let ident = Ident :: new ( keywords:: Invalid . name ( ) , use_ctxt) ;
812817 let def_id = self . tcx . adjust_ident ( ident, def. did , self . current_item ) . 1 ;
813818 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 ( ) ;
818869 }
819870 }
820871}
@@ -867,6 +918,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
867918 let def = self . tables . qpath_def ( qpath, expr. hir_id ) ;
868919 let adt = self . tables . expr_ty ( expr) . ty_adt_def ( ) . unwrap ( ) ;
869920 let variant = adt. variant_of_def ( def) ;
921+ let mut field_errors = vec ! [ ] ;
870922 if let Some ( ref base) = * base {
871923 // If the expression uses FRU we need to make sure all the unmentioned fields
872924 // 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> {
879931 Some ( field) => ( field. ident . span , field. span ) ,
880932 None => ( base. span , base. span ) ,
881933 } ;
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+ }
883937 }
884938 } else {
885939 for field in fields {
886940 let use_ctxt = field. ident . span ;
887941 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+ }
889976 }
890977 }
891978 }
@@ -901,11 +988,39 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
901988 let def = self . tables . qpath_def ( qpath, pat. hir_id ) ;
902989 let adt = self . tables . pat_ty ( pat) . ty_adt_def ( ) . unwrap ( ) ;
903990 let variant = adt. variant_of_def ( def) ;
991+ let mut field_errors = vec ! [ ] ;
904992 for field in fields {
905993 let use_ctxt = field. node . ident . span ;
906994 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+ }
9081022 }
1023+ self . emit_field_checks ( adt, pat. span , field_errors, "destructured" ) ;
9091024 }
9101025 _ => { }
9111026 }
@@ -927,11 +1042,13 @@ struct TypePrivacyVisitor<'a, 'tcx: 'a> {
9271042 in_body : bool ,
9281043 span : Span ,
9291044 empty_tables : & ' a ty:: TypeckTables < ' tcx > ,
1045+ reported_tuple_structs : FxHashSet < Span > ,
9301046}
9311047
9321048impl < ' a , ' tcx > TypePrivacyVisitor < ' a , ' tcx > {
9331049 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 )
9351052 }
9361053
9371054 // Take node-id of an expression or pattern and check its type for privacy.
@@ -951,11 +1068,33 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
9511068 }
9521069
9531070 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+ }
9571096 }
958- is_error
1097+ !is_ok
9591098 }
9601099}
9611100
@@ -1079,14 +1218,37 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
10791218 hir:: QPath :: TypeRelative ( _, ref segment) => segment. ident . to_string ( ) ,
10801219 } ;
10811220 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 ( ) ;
10831225 return ;
10841226 }
10851227 }
10861228
10871229 intravisit:: walk_qpath ( self , qpath, id, span) ;
10881230 }
10891231
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+
10901252 // Check types of patterns.
10911253 fn visit_pat ( & mut self , pattern : & ' tcx hir:: Pat ) {
10921254 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) {
17701932 tables : & empty_tables,
17711933 current_item : DUMMY_NODE_ID ,
17721934 empty_tables : & empty_tables,
1935+ reported_tuple_structs : FxHashSet :: default ( ) ,
17731936 } ;
17741937 let ( module, span, node_id) = tcx. hir ( ) . get_module ( module_def_id) ;
17751938 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) {
17831946 in_body : false ,
17841947 span,
17851948 empty_tables : & empty_tables,
1949+ reported_tuple_structs : visitor. reported_tuple_structs ,
17861950 } ;
17871951 intravisit:: walk_mod ( & mut visitor, module, node_id) ;
17881952}
0 commit comments