@@ -243,10 +243,30 @@ impl<'a> AstValidator<'a> {
243243 }
244244 }
245245 }
246+ TyKind :: AnonymousStruct ( ref fields, ..) | TyKind :: AnonymousUnion ( ref fields, ..) => {
247+ self . with_banned_assoc_ty_bound ( |this| {
248+ walk_list ! ( this, visit_struct_field_def, fields)
249+ } ) ;
250+ }
246251 _ => visit:: walk_ty ( self , t) ,
247252 }
248253 }
249254
255+ fn visit_struct_field_def ( & mut self , field : & ' a FieldDef ) {
256+ if let Some ( ident) = field. ident {
257+ if ident. name == kw:: Underscore {
258+ self . check_anonymous_field ( field) ;
259+ self . visit_vis ( & field. vis ) ;
260+ self . visit_ident ( ident) ;
261+ self . visit_ty_common ( & field. ty ) ;
262+ self . walk_ty ( & field. ty ) ;
263+ walk_list ! ( self , visit_attribute, & field. attrs) ;
264+ return ;
265+ }
266+ }
267+ self . visit_field_def ( field) ;
268+ }
269+
250270 fn err_handler ( & self ) -> & rustc_errors:: Handler {
251271 & self . session . diagnostic ( )
252272 }
@@ -288,6 +308,66 @@ impl<'a> AstValidator<'a> {
288308 }
289309 }
290310
311+ fn check_anonymous_field ( & self , field : & FieldDef ) {
312+ let FieldDef { ty, .. } = field;
313+ match & ty. kind {
314+ TyKind :: AnonymousStruct ( ..) | TyKind :: AnonymousUnion ( ..) => {
315+ // We already checked for `kw::Underscore` before calling this function,
316+ // so skip the check
317+ }
318+ TyKind :: Path ( ..) => {
319+ // If the anonymous field contains a Path as type, we can't determine
320+ // if the path is a valid struct or union, so skip the check
321+ }
322+ _ => {
323+ let msg = "unnamed fields can only have struct or union types" ;
324+ let label = "not a struct or union" ;
325+ self . err_handler ( )
326+ . struct_span_err ( field. span , msg)
327+ . span_label ( ty. span , label)
328+ . emit ( ) ;
329+ }
330+ }
331+ }
332+
333+ fn deny_anonymous_struct ( & self , ty : & Ty ) {
334+ match & ty. kind {
335+ TyKind :: AnonymousStruct ( ..) => {
336+ self . err_handler ( )
337+ . struct_span_err (
338+ ty. span ,
339+ "anonymous structs are not allowed outside of unnamed struct or union fields" ,
340+ )
341+ . span_label ( ty. span , "anonymous struct declared here" )
342+ . emit ( ) ;
343+ }
344+ TyKind :: AnonymousUnion ( ..) => {
345+ self . err_handler ( )
346+ . struct_span_err (
347+ ty. span ,
348+ "anonymous unions are not allowed outside of unnamed struct or union fields" ,
349+ )
350+ . span_label ( ty. span , "anonymous union declared here" )
351+ . emit ( ) ;
352+ }
353+ _ => { }
354+ }
355+ }
356+
357+ fn deny_anonymous_field ( & self , field : & FieldDef ) {
358+ if let Some ( ident) = field. ident {
359+ if ident. name == kw:: Underscore {
360+ self . err_handler ( )
361+ . struct_span_err (
362+ field. span ,
363+ "anonymous fields are not allowed outside of structs or unions" ,
364+ )
365+ . span_label ( ident. span , "anonymous field declared here" )
366+ . emit ( ) ;
367+ }
368+ }
369+ }
370+
291371 fn check_trait_fn_not_const ( & self , constness : Const ) {
292372 if let Const :: Yes ( span) = constness {
293373 self . session . emit_err ( TraitFnConst { span } ) ;
@@ -974,6 +1054,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
9741054
9751055 fn visit_ty ( & mut self , ty : & ' a Ty ) {
9761056 self . visit_ty_common ( ty) ;
1057+ self . deny_anonymous_struct ( ty) ;
9771058 self . walk_ty ( ty)
9781059 }
9791060
@@ -988,6 +1069,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
9881069 }
9891070
9901071 fn visit_field_def ( & mut self , field : & ' a FieldDef ) {
1072+ self . deny_anonymous_field ( field) ;
9911073 visit:: walk_field_def ( self , field)
9921074 }
9931075
@@ -1179,10 +1261,42 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
11791261 self . check_mod_file_item_asciionly ( item. ident ) ;
11801262 }
11811263 }
1182- ItemKind :: Union ( vdata, ..) => {
1264+ ItemKind :: Struct ( vdata, generics) => match vdata {
1265+ // Duplicating the `Visitor` logic allows catching all cases
1266+ // of `Anonymous(Struct, Union)` outside of a field struct or union.
1267+ //
1268+ // Inside `visit_ty` the validator catches every `Anonymous(Struct, Union)` it
1269+ // encounters, and only on `ItemKind::Struct` and `ItemKind::Union`
1270+ // it uses `visit_ty_common`, which doesn't contain that specific check.
1271+ VariantData :: Struct ( fields, ..) => {
1272+ self . visit_vis ( & item. vis ) ;
1273+ self . visit_ident ( item. ident ) ;
1274+ self . visit_generics ( generics) ;
1275+ self . with_banned_assoc_ty_bound ( |this| {
1276+ walk_list ! ( this, visit_struct_field_def, fields) ;
1277+ } ) ;
1278+ walk_list ! ( self , visit_attribute, & item. attrs) ;
1279+ return ;
1280+ }
1281+ _ => { }
1282+ } ,
1283+ ItemKind :: Union ( vdata, generics) => {
11831284 if vdata. fields ( ) . is_empty ( ) {
11841285 self . err_handler ( ) . span_err ( item. span , "unions cannot have zero fields" ) ;
11851286 }
1287+ match vdata {
1288+ VariantData :: Struct ( fields, ..) => {
1289+ self . visit_vis ( & item. vis ) ;
1290+ self . visit_ident ( item. ident ) ;
1291+ self . visit_generics ( generics) ;
1292+ self . with_banned_assoc_ty_bound ( |this| {
1293+ walk_list ! ( this, visit_struct_field_def, fields) ;
1294+ } ) ;
1295+ walk_list ! ( self , visit_attribute, & item. attrs) ;
1296+ return ;
1297+ }
1298+ _ => { }
1299+ }
11861300 }
11871301 ItemKind :: Const ( def, .., None ) => {
11881302 self . check_defaultness ( item. span , * def) ;
0 commit comments