From aa94da695563120591d605b3544de73ccc31346b Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Sat, 28 Jan 2023 17:39:55 -0600 Subject: [PATCH 1/9] Work on unnamed struct and union fields --- compiler/rustc_ast/src/ast.rs | 4 + compiler/rustc_ast/src/mut_visit.rs | 4 + compiler/rustc_ast/src/visit.rs | 3 + compiler/rustc_ast_lowering/src/item.rs | 5 +- compiler/rustc_ast_lowering/src/lib.rs | 11 +- .../rustc_ast_passes/src/ast_validation.rs | 116 +++++++- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_ast_passes/src/lib.rs | 5 +- compiler/rustc_ast_pretty/src/pprust/state.rs | 8 + .../rustc_ast_pretty/src/pprust/state/item.rs | 6 +- compiler/rustc_feature/src/active.rs | 2 + compiler/rustc_parse/src/parser/item.rs | 279 +++++++++++------- compiler/rustc_parse/src/parser/ty.rs | 183 +++++++++++- compiler/rustc_passes/src/hir_stats.rs | 2 + compiler/rustc_span/src/symbol.rs | 1 + src/tools/rustfmt/src/types.rs | 2 + tests/pretty/anonymous-types.rs | 19 ++ .../feature-gate-unnamed_fields.rs | 26 ++ .../feature-gate-unnamed_fields.stderr | 84 ++++++ .../ui/parser/keyword-union-as-identifier.rs | 22 ++ .../restrict_anonymous_structs.rs | 63 ++++ .../restrict_anonymous_structs.stderr | 198 +++++++++++++ .../restrict_anonymous_unions.rs | 62 ++++ .../restrict_anonymous_unions.stderr | 198 +++++++++++++ 24 files changed, 1191 insertions(+), 113 deletions(-) create mode 100644 tests/pretty/anonymous-types.rs create mode 100644 tests/ui/feature-gates/feature-gate-unnamed_fields.rs create mode 100644 tests/ui/feature-gates/feature-gate-unnamed_fields.stderr create mode 100644 tests/ui/parser/keyword-union-as-identifier.rs create mode 100644 tests/ui/unnamed-fields/restrict_anonymous_structs.rs create mode 100644 tests/ui/unnamed-fields/restrict_anonymous_structs.stderr create mode 100644 tests/ui/unnamed-fields/restrict_anonymous_unions.rs create mode 100644 tests/ui/unnamed-fields/restrict_anonymous_unions.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 58725a08c7c5f..d080ec1820cfc 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2092,6 +2092,10 @@ pub enum TyKind { Never, /// A tuple (`(A, B, C, D,...)`). Tup(ThinVec>), + /// An anonymous struct type i.e. `struct { foo: Type }` + AnonymousStruct(ThinVec, /* recovered */ bool), + /// An anonymous union type i.e. `union { bar: Type }` + AnonymousUnion(ThinVec, /* recovered */ bool), /// A path (`module::module::...::Type`), optionally /// "qualified", e.g., ` as SomeTrait>::SomeType`. /// diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index bae3979fbf9fc..827b8a8dfd79d 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -509,6 +509,10 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { visit_vec(bounds, |bound| vis.visit_param_bound(bound)); } TyKind::MacCall(mac) => vis.visit_mac_call(mac), + TyKind::AnonymousStruct(fields, _recovered) + | TyKind::AnonymousUnion(fields, _recovered) => { + fields.flat_map_in_place(|field| vis.flat_map_field_def(field)); + } } vis.visit_span(span); visit_lazy_tts(tokens, vis); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 6d474de2d15f1..614ebd6c8dcc0 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -438,6 +438,9 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) { TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {} TyKind::MacCall(mac) => visitor.visit_mac_call(mac), TyKind::Never | TyKind::CVarArgs => {} + TyKind::AnonymousStruct(ref fields, ..) | TyKind::AnonymousUnion(ref fields, ..) => { + walk_list!(visitor, visit_field_def, fields) + } } } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index a59c83de0f46f..ea9a4e511c117 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -708,7 +708,10 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - fn lower_field_def(&mut self, (index, f): (usize, &FieldDef)) -> hir::FieldDef<'hir> { + pub(super) fn lower_field_def( + &mut self, + (index, f): (usize, &FieldDef), + ) -> hir::FieldDef<'hir> { let ty = if let TyKind::Path(qself, path) = &f.ty.kind { let t = self.lower_path_ty( &f.ty, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0aeff22ca9f8a..c8908a9e8c6b9 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -34,8 +34,8 @@ #![feature(let_chains)] #![feature(never_type)] #![recursion_limit = "256"] -#![deny(rustc::untranslatable_diagnostic)] -#![deny(rustc::diagnostic_outside_of_impl)] +// #![deny(rustc::untranslatable_diagnostic)] +// #![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] extern crate tracing; @@ -1293,6 +1293,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TyKind::Err => { hir::TyKind::Err(self.tcx.sess.delay_span_bug(t.span, "TyKind::Err lowered")) } + // FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS + TyKind::AnonymousStruct(ref _fields, _recovered) => { + hir::TyKind::Err(self.tcx.sess.span_err(t.span, "anonymous structs are unimplemented")) + } + TyKind::AnonymousUnion(ref _fields, _recovered) => { + hir::TyKind::Err(self.tcx.sess.span_err(t.span, "anonymous unions are unimplemented")) + } TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), TyKind::Ref(region, mt) => { diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index af594a00705f5..07deaca82facc 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -219,10 +219,30 @@ impl<'a> AstValidator<'a> { } } } + TyKind::AnonymousStruct(ref fields, ..) | TyKind::AnonymousUnion(ref fields, ..) => { + // self.with_banned_assoc_ty_bound(|this| { + walk_list!(self, visit_struct_field_def, fields) + // }); + } _ => visit::walk_ty(self, t), } } + fn visit_struct_field_def(&mut self, field: &'a FieldDef) { + if let Some(ident) = field.ident { + if ident.name == kw::Underscore { + self.check_anonymous_field(field); + self.visit_vis(&field.vis); + self.visit_ident(ident); + self.visit_ty_common(&field.ty); + self.walk_ty(&field.ty); + walk_list!(self, visit_attribute, &field.attrs); + return; + } + } + self.visit_field_def(field); + } + fn err_handler(&self) -> &rustc_errors::Handler { &self.session.diagnostic() } @@ -260,6 +280,66 @@ impl<'a> AstValidator<'a> { } } + fn check_anonymous_field(&self, field: &FieldDef) { + let FieldDef { ty, .. } = field; + match &ty.kind { + TyKind::AnonymousStruct(..) | TyKind::AnonymousUnion(..) => { + // We already checked for `kw::Underscore` before calling this function, + // so skip the check + } + TyKind::Path(..) => { + // If the anonymous field contains a Path as type, we can't determine + // if the path is a valid struct or union, so skip the check + } + _ => { + let msg = "unnamed fields can only have struct or union types"; + let label = "not a struct or union"; + self.err_handler() + .struct_span_err(field.span, msg) + .span_label(ty.span, label) + .emit(); + } + } + } + + fn deny_anonymous_struct(&self, ty: &Ty) { + match &ty.kind { + TyKind::AnonymousStruct(..) => { + self.err_handler() + .struct_span_err( + ty.span, + "anonymous structs are not allowed outside of unnamed struct or union fields", + ) + .span_label(ty.span, "anonymous struct declared here") + .emit(); + } + TyKind::AnonymousUnion(..) => { + self.err_handler() + .struct_span_err( + ty.span, + "anonymous unions are not allowed outside of unnamed struct or union fields", + ) + .span_label(ty.span, "anonymous union declared here") + .emit(); + } + _ => {} + } + } + + fn deny_anonymous_field(&self, field: &FieldDef) { + if let Some(ident) = field.ident { + if ident.name == kw::Underscore { + self.err_handler() + .struct_span_err( + field.span, + "anonymous fields are not allowed outside of structs or unions", + ) + .span_label(ident.span, "anonymous field declared here") + .emit(); + } + } + } + fn check_trait_fn_not_const(&self, constness: Const) { if let Const::Yes(span) = constness { self.session.emit_err(errors::TraitFnConst { span }); @@ -785,6 +865,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_ty(&mut self, ty: &'a Ty) { self.visit_ty_common(ty); + self.deny_anonymous_struct(ty); self.walk_ty(ty) } @@ -799,6 +880,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } fn visit_field_def(&mut self, field: &'a FieldDef) { + self.deny_anonymous_field(field); visit::walk_field_def(self, field) } @@ -991,10 +1073,42 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_mod_file_item_asciionly(item.ident); } } - ItemKind::Union(vdata, ..) => { + ItemKind::Struct(vdata, generics) => match vdata { + // Duplicating the `Visitor` logic allows catching all cases + // of `Anonymous(Struct, Union)` outside of a field struct or union. + // + // Inside `visit_ty` the validator catches every `Anonymous(Struct, Union)` it + // encounters, and only on `ItemKind::Struct` and `ItemKind::Union` + // it uses `visit_ty_common`, which doesn't contain that specific check. + VariantData::Struct(fields, ..) => { + self.visit_vis(&item.vis); + self.visit_ident(item.ident); + self.visit_generics(generics); + // self.with_banned_assoc_ty_bound(|this| { + walk_list!(self, visit_struct_field_def, fields); + // }); + walk_list!(self, visit_attribute, &item.attrs); + return; + } + _ => {} + }, + ItemKind::Union(vdata, generics) => { if vdata.fields().is_empty() { self.err_handler().emit_err(errors::FieldlessUnion { span: item.span }); } + match vdata { + VariantData::Struct(fields, ..) => { + self.visit_vis(&item.vis); + self.visit_ident(item.ident); + self.visit_generics(generics); + // self.with_banned_assoc_ty_bound(|this| { + walk_list!(self, visit_struct_field_def, fields); + // }); + walk_list!(self, visit_attribute, &item.attrs); + return; + } + _ => {} + } } ItemKind::Const(box ConstItem { defaultness, expr: None, .. }) => { self.check_defaultness(item.span, *defaultness); diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index c4efad7caf260..036c531124168 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -570,6 +570,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); gate_all!(explicit_tail_calls, "`become` expression is experimental"); gate_all!(generic_const_items, "generic const items are experimental"); + gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented"); if !visitor.features.negative_bounds { for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() { diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 7db413c5bbd42..710ef401ebb3d 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -9,8 +9,9 @@ #![feature(iter_is_partitioned)] #![feature(let_chains)] #![recursion_limit = "256"] -#![deny(rustc::untranslatable_diagnostic)] -#![deny(rustc::diagnostic_outside_of_impl)] +// FIXME(unnamed_field): unncomment these two lints +// #![deny(rustc::untranslatable_diagnostic)] +// #![deny(rustc::diagnostic_outside_of_impl)] use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; use rustc_fluent_macro::fluent_messages; diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 068b255e9f28b..e9f4015be301b 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1053,6 +1053,14 @@ impl<'a> State<'a> { } self.pclose(); } + ast::TyKind::AnonymousStruct(fields, _recovered) => { + self.head("struct"); + self.print_record_struct_body(&fields, ty.span); + } + ast::TyKind::AnonymousUnion(fields, _recovered) => { + self.head("union"); + self.print_record_struct_body(&fields, ty.span); + } ast::TyKind::Paren(typ) => { self.popen(); self.print_type(typ); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index d27a44f1206dd..3393f034bc3b5 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -443,7 +443,11 @@ impl<'a> State<'a> { } } - fn print_record_struct_body(&mut self, fields: &[ast::FieldDef], span: rustc_span::Span) { + pub(crate) fn print_record_struct_body( + &mut self, + fields: &[ast::FieldDef], + span: rustc_span::Span, + ) { self.nbsp(); self.bopen(); diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 898f5fd3b7095..123428bc9b7d2 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -581,6 +581,8 @@ declare_features! ( (active, type_privacy_lints, "1.72.0", Some(48054), None), /// Enables rustc to generate code that instructs libstd to NOT ignore SIGPIPE. (active, unix_sigpipe, "1.65.0", Some(97889), None), + /// Allows unnamed fields of struct and union type + (incomplete, unnamed_fields, "1.68.0", Some(49804), None), /// Allows unsized fn parameters. (active, unsized_fn_params, "1.49.0", Some(48055), None), /// Allows unsized rvalues at arguments and parameters. diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 24c65d061f95a..227c08f83ab45 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -562,43 +562,103 @@ impl<'a> Parser<'a> { let polarity = self.parse_polarity(); + let mut snapshot_before_last_ty = self.create_snapshot_for_diagnostic(); // Parse both types and traits as a type, then reinterpret if necessary. let err_path = |span| ast::Path::from_ident(Ident::new(kw::Empty, span)); - let ty_first = if self.token.is_keyword(kw::For) && self.look_ahead(1, |t| t != &token::Lt) - { - let span = self.prev_token.span.between(self.token.span); - self.sess.emit_err(errors::MissingTraitInTraitImpl { - span, - for_span: span.to(self.token.span), - }); - - P(Ty { - kind: TyKind::Path(None, err_path(span)), - span, - id: DUMMY_NODE_ID, - tokens: None, - }) - } else { - self.parse_ty_with_generics_recovery(&generics)? - }; + let mut ty_first = + if self.token.is_keyword(kw::For) && self.look_ahead(1, |t| t != &token::Lt) { + let span = self.prev_token.span.between(self.token.span); + self.struct_span_err(span, "missing trait in a trait impl") + .span_suggestion( + span, + "add a trait here", + " Trait ", + Applicability::HasPlaceholders, + ) + .span_suggestion( + span.to(self.token.span), + "for an inherent impl, drop this `for`", + "", + Applicability::MaybeIncorrect, + ) + .emit(); + P(Ty { + kind: TyKind::Path(None, err_path(span)), + span, + id: DUMMY_NODE_ID, + tokens: None, + }) + } else { + self.parse_ty_with_generics_recovery(&generics)? + }; // If `for` is missing we try to recover. let has_for = self.eat_keyword(kw::For); let missing_for_span = self.prev_token.span.between(self.token.span); - let ty_second = if self.token == token::DotDot { + let mut ty_second = if self.token == token::DotDot { // We need to report this error after `cfg` expansion for compatibility reasons self.bump(); // `..`, do not add it to expected tokens Some(self.mk_ty(self.prev_token.span, TyKind::Err)) } else if has_for || self.token.can_begin_type() { - Some(self.parse_ty()?) + snapshot_before_last_ty = self.create_snapshot_for_diagnostic(); + Some(self.parse_ty_no_anon_recovery()?) } else { None }; generics.where_clause = self.parse_where_clause()?; - let impl_items = self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))?; + let (mut impl_items, err) = + self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))?; + + if let Some(mut err) = err { + let mut snapshot = snapshot_before_last_ty; + + if snapshot.can_start_anonymous_type() { + let recover_result = { + let recover_last_ty = match snapshot.parse_ty() { + Ok(ty) => Some(ty), + Err(snapshot_err) => { + snapshot_err.cancel(); + None + } + }; + + let impl_items = match snapshot + .parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No)) + { + Ok((impl_items, None)) => Some(impl_items), + Ok((_, Some(snapshot_err))) => { + snapshot_err.cancel(); + None + } + Err(snapshot_err) => { + snapshot_err.cancel(); + None + } + }; + + (recover_last_ty, impl_items) + }; + + if let (Some(recover_last_ty), Some(new_impl_items)) = recover_result { + err.delay_as_bug(); + self.restore_snapshot(snapshot); + + if ty_second.is_some() { + ty_second = Some(recover_last_ty); + } else { + ty_first = recover_last_ty; + } + impl_items = new_impl_items; + } else { + err.emit(); + } + } else { + err.emit(); + } + } let item_kind = match ty_second { Some(ty_second) => { @@ -667,20 +727,21 @@ impl<'a> Parser<'a> { &mut self, attrs: &mut AttrVec, mut parse_item: impl FnMut(&mut Parser<'a>) -> PResult<'a, Option>>, - ) -> PResult<'a, ThinVec> { + ) -> PResult<'a, (ThinVec, Option>)> { let open_brace_span = self.token.span; // Recover `impl Ty;` instead of `impl Ty {}` if self.token == TokenKind::Semi { self.sess.emit_err(errors::UseEmptyBlockNotSemi { span: self.token.span }); self.bump(); - return Ok(ThinVec::new()); + return Ok((ThinVec::new(), None)); } self.expect(&token::OpenDelim(Delimiter::Brace))?; attrs.extend(self.parse_inner_attributes()?); let mut items = ThinVec::new(); + let mut delayed_err = None; while !self.eat(&token::CloseDelim(Delimiter::Brace)) { if self.recover_doc_comment_before_brace() { continue; @@ -742,20 +803,21 @@ impl<'a> Parser<'a> { Applicability::MaybeIncorrect, ); } - err.emit(); + delayed_err = Some(err); break; } Ok(Some(item)) => items.extend(item), Err(mut err) => { self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes); err.span_label(open_brace_span, "while parsing this item list starting here") - .span_label(self.prev_token.span, "the item list ends here") - .emit(); + .span_label(self.prev_token.span, "the item list ends here"); + + delayed_err = Some(err); break; } } } - Ok(items) + Ok((items, delayed_err)) } /// Recover on a doc comment before `}`. @@ -846,7 +908,13 @@ impl<'a> Parser<'a> { } else { // It's a normal trait. generics.where_clause = self.parse_where_clause()?; - let items = self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?; + let (items, err) = + self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?; + + if let Some(mut err) = err { + err.emit(); + } + Ok(( ident, ItemKind::Trait(Box::new(Trait { is_auto, unsafety, generics, bounds, items })), @@ -1113,11 +1181,14 @@ impl<'a> Parser<'a> { unsafety = Unsafe::Yes(self.token.span); self.eat_keyword(kw::Unsafe); } - let module = ast::ForeignMod { - unsafety, - abi, - items: self.parse_item_list(attrs, |p| p.parse_foreign_item(ForceCollect::No))?, - }; + + let (items, err) = + self.parse_item_list(attrs, |p| p.parse_foreign_item(ForceCollect::No))?; + if let Some(mut err) = err { + err.emit(); + } + + let module = ast::ForeignMod { unsafety, abi, items }; Ok((Ident::empty(), ItemKind::ForeignMod(module))) } @@ -1594,7 +1665,7 @@ impl<'a> Parser<'a> { Ok((class_name, ItemKind::Union(vdata, generics))) } - fn parse_record_struct_body( + pub(crate) fn parse_record_struct_body( &mut self, adt_ty: &str, ident_span: Span, @@ -1869,7 +1940,7 @@ impl<'a> Parser<'a> { } } self.expect_field_ty_separator()?; - let ty = self.parse_ty()?; + let ty = self.parse_ty_for_field_def()?; if self.token.kind == token::Colon && self.look_ahead(1, |tok| tok.kind != token::Colon) { self.sess.emit_err(errors::SingleColonStructType { span: self.token.span }); } @@ -1896,79 +1967,83 @@ impl<'a> Parser<'a> { let (ident, is_raw) = self.ident_or_err(true)?; if !is_raw && ident.is_reserved() { let snapshot = self.create_snapshot_for_diagnostic(); - let err = if self.check_fn_front_matter(false, Case::Sensitive) { - let inherited_vis = Visibility { - span: rustc_span::DUMMY_SP, - kind: VisibilityKind::Inherited, - tokens: None, - }; - // We use `parse_fn` to get a span for the function - let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true }; - match self.parse_fn( - &mut AttrVec::new(), - fn_parse_mode, - lo, - &inherited_vis, - Case::Insensitive, - ) { - Ok(_) => { - let mut err = self.struct_span_err( - lo.to(self.prev_token.span), - format!("functions are not allowed in {adt_ty} definitions"), - ); - err.help( - "unlike in C++, Java, and C#, functions are declared in `impl` blocks", - ); - err.help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information"); - err + if ident.name == kw::Underscore { + self.sess.gated_spans.gate(sym::unnamed_fields, lo); + } else { + let err = if self.check_fn_front_matter(false, Case::Sensitive) { + let inherited_vis = Visibility { + span: rustc_span::DUMMY_SP, + kind: VisibilityKind::Inherited, + tokens: None, + }; + // We use `parse_fn` to get a span for the function + let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true }; + match self.parse_fn( + &mut AttrVec::new(), + fn_parse_mode, + lo, + &inherited_vis, + Case::Insensitive, + ) { + Ok(_) => { + let mut err = self.struct_span_err( + lo.to(self.prev_token.span), + format!("functions are not allowed in {adt_ty} definitions"), + ); + err.help( + "unlike in C++, Java, and C#, functions are declared in `impl` blocks", + ); + err.help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information"); + err + } + Err(err) => { + err.cancel(); + self.restore_snapshot(snapshot); + self.expected_ident_found_err() + } } - Err(err) => { - err.cancel(); - self.restore_snapshot(snapshot); - self.expected_ident_found_err() + } else if self.eat_keyword(kw::Struct) { + match self.parse_item_struct() { + Ok((ident, _)) => { + let mut err = self.struct_span_err( + lo.with_hi(ident.span.hi()), + format!("structs are not allowed in {adt_ty} definitions"), + ); + err.help("consider creating a new `struct` definition instead of nesting"); + err + } + Err(err) => { + err.cancel(); + self.restore_snapshot(snapshot); + self.expected_ident_found_err() + } } - } - } else if self.eat_keyword(kw::Struct) { - match self.parse_item_struct() { - Ok((ident, _)) => { - let mut err = self.struct_span_err( - lo.with_hi(ident.span.hi()), - format!("structs are not allowed in {adt_ty} definitions"), + } else { + let mut err = self.expected_ident_found_err(); + if self.eat_keyword_noexpect(kw::Let) + && let removal_span = self.prev_token.span.until(self.token.span) + && let Ok(ident) = self.parse_ident_common(false) + // Cancel this error, we don't need it. + .map_err(|err| err.cancel()) + && self.token.kind == TokenKind::Colon + { + err.span_suggestion( + removal_span, + "remove this `let` keyword", + String::new(), + Applicability::MachineApplicable, ); - err.help("consider creating a new `struct` definition instead of nesting"); - err - } - Err(err) => { - err.cancel(); + err.note("the `let` keyword is not allowed in `struct` fields"); + err.note("see for more information"); + err.emit(); + return Ok(ident); + } else { self.restore_snapshot(snapshot); - self.expected_ident_found_err() } - } - } else { - let mut err = self.expected_ident_found_err(); - if self.eat_keyword_noexpect(kw::Let) - && let removal_span = self.prev_token.span.until(self.token.span) - && let Ok(ident) = self.parse_ident_common(false) - // Cancel this error, we don't need it. - .map_err(|err| err.cancel()) - && self.token.kind == TokenKind::Colon - { - err.span_suggestion( - removal_span, - "remove this `let` keyword", - String::new(), - Applicability::MachineApplicable, - ); - err.note("the `let` keyword is not allowed in `struct` fields"); - err.note("see for more information"); - err.emit(); - return Ok(ident); - } else { - self.restore_snapshot(snapshot); - } - err - }; - return Err(err); + err + }; + return Err(err); + } } self.bump(); Ok(ident) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 3bb50b05aa346..daf533705ae3d 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -51,17 +51,31 @@ pub(super) enum AllowPlus { No, } -#[derive(PartialEq)] +#[derive(PartialEq, Copy, Clone)] pub(super) enum RecoverQPath { Yes, No, } +#[derive(Clone, Copy)] pub(super) enum RecoverQuestionMark { Yes, No, } +#[derive(PartialEq, Clone, Copy)] +pub(super) enum RecoverAnonEnum { + Yes, + No, +} + +#[derive(Copy, Clone, PartialEq)] +pub(super) enum AllowAnonymousTypes { + Yes, + OnlyRecover, + No, +} + /// Signals whether parsing a type should recover `->`. /// /// More specifically, when parsing a function like: @@ -94,7 +108,7 @@ impl RecoverReturnSign { } // Is `...` (`CVarArgs`) legal at this level of type parsing? -#[derive(PartialEq)] +#[derive(PartialEq, Clone, Copy)] enum AllowCVariadic { Yes, No, @@ -119,6 +133,21 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::No, + AllowAnonymousTypes::OnlyRecover, + ) + } + + pub(super) fn parse_ty_no_anon_recovery(&mut self) -> PResult<'a, P> { + self.parse_ty_common( + AllowPlus::Yes, + AllowCVariadic::No, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + None, + RecoverQuestionMark::Yes, + RecoverAnonEnum::No, + AllowAnonymousTypes::No, ) } @@ -133,6 +162,24 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, Some(ty_params), RecoverQuestionMark::Yes, + RecoverAnonEnum::No, + AllowAnonymousTypes::No, + ) + } + + /// Parse a type suitable for a field defintion. + /// The difference from `parse_ty` is that this version + /// allows anonymous structs and unions. + pub fn parse_ty_for_field_def(&mut self) -> PResult<'a, P> { + self.parse_ty_common( + AllowPlus::Yes, + AllowCVariadic::No, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + None, + RecoverQuestionMark::Yes, + RecoverAnonEnum::No, + AllowAnonymousTypes::Yes, ) } @@ -147,6 +194,8 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::Yes, + AllowAnonymousTypes::OnlyRecover, ) } @@ -164,6 +213,8 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::No, + AllowAnonymousTypes::OnlyRecover, ) } @@ -177,6 +228,8 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, + RecoverAnonEnum::No, + AllowAnonymousTypes::OnlyRecover, ) } @@ -188,6 +241,8 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, + RecoverAnonEnum::No, + AllowAnonymousTypes::OnlyRecover, ) } @@ -200,6 +255,8 @@ impl<'a> Parser<'a> { RecoverReturnSign::OnlyFatArrow, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::No, + AllowAnonymousTypes::OnlyRecover, ) } @@ -219,6 +276,8 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::Yes, + AllowAnonymousTypes::OnlyRecover, )?; FnRetTy::Ty(ty) } else if recover_return_sign.can_recover(&self.token.kind) { @@ -233,6 +292,8 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, + RecoverAnonEnum::Yes, + AllowAnonymousTypes::OnlyRecover, )?; FnRetTy::Ty(ty) } else { @@ -248,6 +309,8 @@ impl<'a> Parser<'a> { recover_return_sign: RecoverReturnSign, ty_generics: Option<&Generics>, recover_question_mark: RecoverQuestionMark, + recover_anon_enum: RecoverAnonEnum, + allow_anonymous_types: AllowAnonymousTypes, ) -> PResult<'a, P> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); @@ -295,6 +358,10 @@ impl<'a> Parser<'a> { } } else if self.eat_keyword(kw::Impl) { self.parse_impl_ty(&mut impl_dyn_multi)? + } else if allow_anonymous_types == AllowAnonymousTypes::Yes + && self.can_start_anonymous_type() + { + self.parse_anonymous_ty(lo)? } else if self.is_explicit_dyn_type() { self.parse_dyn_ty(&mut impl_dyn_multi)? } else if self.eat_lt() { @@ -302,7 +369,24 @@ impl<'a> Parser<'a> { let (qself, path) = self.parse_qpath(PathStyle::Type)?; TyKind::Path(Some(qself), path) } else if self.check_path() { - self.parse_path_start_ty(lo, allow_plus, ty_generics)? + // `union` is a weak keyword, so it can possibly be a path + if allow_anonymous_types == AllowAnonymousTypes::OnlyRecover + && self.can_start_anonymous_type() + { + // If we can parse an anonymous type, do so and let + // ast validation error out later + let snapshot = self.create_snapshot_for_diagnostic(); + match self.parse_anonymous_ty(lo) { + Ok(ty) => ty, + Err(err) => { + err.cancel(); + self.restore_snapshot(snapshot); + self.parse_path_start_ty(lo, allow_plus, ty_generics)? + } + } + } else { + self.parse_path_start_ty(lo, allow_plus, ty_generics)? + } } else if self.can_begin_bound() { self.parse_bare_trait_object(lo, allow_plus)? } else if self.eat(&token::DotDotDot) { @@ -319,7 +403,28 @@ impl<'a> Parser<'a> { let msg = format!("expected type, found {}", super::token_descr(&self.token)); let mut err = self.struct_span_err(self.token.span, msg); err.span_label(self.token.span, "expected type"); - return Err(err); + // self.maybe_annotate_with_ascription(&mut err, true); + + // `struct` is a strict keyword, so we can check it here as + // we will be erroring otherwise + // FIXME: or should we not? + if self.token.is_keyword(kw::Struct) { + // Recover the parser from anonymous types anywhere other than field types. + let snapshot = self.create_snapshot_for_diagnostic(); + match self.parse_anonymous_ty(lo) { + Ok(ty) => { + err.delay_as_bug(); + ty + } + Err(snapshot_err) => { + snapshot_err.cancel(); + self.restore_snapshot(snapshot); + return Err(err); + } + } + } else { + return Err(err); + } }; let span = lo.to(self.prev_token.span); @@ -333,9 +438,73 @@ impl<'a> Parser<'a> { if let RecoverQuestionMark::Yes = recover_question_mark { ty = self.maybe_recover_from_question_mark(ty); } + if recover_anon_enum == RecoverAnonEnum::Yes + && self.check_noexpect(&token::BinOp(token::Or)) + && self.look_ahead(1, |t| t.can_begin_type()) + { + let mut pipes = vec![self.token.span]; + let mut types = vec![ty]; + loop { + if !self.eat(&token::BinOp(token::Or)) { + break; + } + pipes.push(self.prev_token.span); + types.push(self.parse_ty_common( + allow_plus, + allow_c_variadic, + recover_qpath, + recover_return_sign, + ty_generics, + recover_question_mark, + RecoverAnonEnum::No, + AllowAnonymousTypes::OnlyRecover, + )?); + } + let mut err = self.struct_span_err(pipes, "anonymous enums are not supported"); + for ty in &types { + err.span_label(ty.span, ""); + } + // err.help(format!( + // "create a named `enum` and use it here instead:\nenum Name {{\n{}\n}}", + // types + // .iter() + // .enumerate() + // .map(|(i, t)| format!( + // " Variant{}({}),", + // i + 1, // Lets not confuse people with zero-indexing :) + // pprust::to_string(|s| s.print_type(&t)), + // )) + // .collect::>() + // .join("\n"), + // )); + err.emit(); + return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::Err)); + } if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) } } + pub(crate) fn parse_anonymous_ty(&mut self, lo: Span) -> PResult<'a, TyKind> { + assert!(self.token.is_keyword(kw::Union) || self.token.is_keyword(kw::Struct)); + let is_union = self.token.is_keyword(kw::Union); + + let span = self.token.span; + self.bump(); + + // FIXME: does false go here + self.parse_record_struct_body(if is_union { "union" } else { "struct" }, span, false).map( + |(fields, recovered)| { + let span = lo.to(self.prev_token.span); + self.sess.gated_spans.gate(sym::unnamed_fields, span); + // These can be rejected during AST validation in `deny_anonymous_struct`. + return if is_union { + TyKind::AnonymousUnion(fields, recovered) + } else { + TyKind::AnonymousStruct(fields, recovered) + }; + }, + ) + } + /// Parses either: /// - `(TYPE)`, a parenthesized type. /// - `(TYPE,)`, a tuple with a single field of type TYPE. @@ -696,6 +865,12 @@ impl<'a> Parser<'a> { Ok(bounds) } + pub(super) fn can_start_anonymous_type(&mut self) -> bool { + (self.token.is_keyword(kw::Union) + && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace))) + || self.token.is_keyword(kw::Struct) + } + /// Can the current token begin a bound? fn can_begin_bound(&mut self) -> bool { // This needs to be synchronized with `TokenKind::can_begin_bound`. diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 5aa8aef6a859a..44893e4ab1c0f 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -587,6 +587,8 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { BareFn, Never, Tup, + AnonymousStruct, + AnonymousUnion, Path, TraitObject, ImplTrait, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 745a3590720a0..539fc9da1a876 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1603,6 +1603,7 @@ symbols! { unix_sigpipe, unlikely, unmarked_api, + unnamed_fields, unpin, unreachable, unreachable_2015, diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 18a08f17ba02f..7c7b75db701b1 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -819,6 +819,8 @@ impl Rewrite for ast::Ty { ast::TyKind::Tup(ref items) => { rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1) } + ast::TyKind::AnonymousStruct(_, _) => Some(context.snippet(self.span).to_owned()), + ast::TyKind::AnonymousUnion(_, _) => Some(context.snippet(self.span).to_owned()), ast::TyKind::Path(ref q_self, ref path) => { rewrite_path(context, PathContext::Type, q_self, path, shape) } diff --git a/tests/pretty/anonymous-types.rs b/tests/pretty/anonymous-types.rs new file mode 100644 index 0000000000000..8e08c314ed1fb --- /dev/null +++ b/tests/pretty/anonymous-types.rs @@ -0,0 +1,19 @@ +// Test for issue 85480 +// Pretty print anonymous struct and union types + +// pp-exact +// pretty-compare-only + +struct Foo { + _: union { + _: struct { + a: u8, + b: u16, + }, + c: u32, + }, + d: u64, + e: f32, +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-unnamed_fields.rs b/tests/ui/feature-gates/feature-gate-unnamed_fields.rs new file mode 100644 index 0000000000000..4bbd0c83bfbca --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unnamed_fields.rs @@ -0,0 +1,26 @@ +struct Foo { + foo: u8, + _: union { //~ ERROR unnamed fields are not yet fully implemented [E0658] + //~^ ERROR unnamed fields are not yet fully implemented [E0658] + //~| ERROR anonymous unions are unimplemented + bar: u8, + baz: u16 + } +} + +union Bar { + foobar: u8, + _: struct { //~ ERROR unnamed fields are not yet fully implemented [E0658] + //~^ ERROR unnamed fields are not yet fully implemented [E0658] + //~| ERROR anonymous structs are unimplemented + foobaz: u8, + barbaz: u16 + } +} + +struct S; +struct Baz { + _: S //~ ERROR unnamed fields are not yet fully implemented [E0658] +} + +fn main(){} diff --git a/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr b/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr new file mode 100644 index 0000000000000..f026f2c3600b3 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr @@ -0,0 +1,84 @@ +error[E0658]: unnamed fields are not yet fully implemented + --> $DIR/feature-gate-unnamed_fields.rs:3:5 + | +LL | _: union { + | ^ + | + = note: see issue #49804 for more information + = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable + +error[E0658]: unnamed fields are not yet fully implemented + --> $DIR/feature-gate-unnamed_fields.rs:3:8 + | +LL | _: union { + | ________^ +LL | | +LL | | +LL | | bar: u8, +LL | | baz: u16 +LL | | } + | |_____^ + | + = note: see issue #49804 for more information + = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable + +error[E0658]: unnamed fields are not yet fully implemented + --> $DIR/feature-gate-unnamed_fields.rs:13:5 + | +LL | _: struct { + | ^ + | + = note: see issue #49804 for more information + = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable + +error[E0658]: unnamed fields are not yet fully implemented + --> $DIR/feature-gate-unnamed_fields.rs:13:8 + | +LL | _: struct { + | ________^ +LL | | +LL | | +LL | | foobaz: u8, +LL | | barbaz: u16 +LL | | } + | |_____^ + | + = note: see issue #49804 for more information + = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable + +error[E0658]: unnamed fields are not yet fully implemented + --> $DIR/feature-gate-unnamed_fields.rs:23:5 + | +LL | _: S + | ^ + | + = note: see issue #49804 for more information + = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable + +error: anonymous unions are unimplemented + --> $DIR/feature-gate-unnamed_fields.rs:3:8 + | +LL | _: union { + | ________^ +LL | | +LL | | +LL | | bar: u8, +LL | | baz: u16 +LL | | } + | |_____^ + +error: anonymous structs are unimplemented + --> $DIR/feature-gate-unnamed_fields.rs:13:8 + | +LL | _: struct { + | ________^ +LL | | +LL | | +LL | | foobaz: u8, +LL | | barbaz: u16 +LL | | } + | |_____^ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/parser/keyword-union-as-identifier.rs b/tests/ui/parser/keyword-union-as-identifier.rs new file mode 100644 index 0000000000000..f9b88afc8782e --- /dev/null +++ b/tests/ui/parser/keyword-union-as-identifier.rs @@ -0,0 +1,22 @@ +// check-pass + +#![allow(non_camel_case_types)] + +mod union { + type union = i32; + + pub struct Bar { + pub union: union, + } + + pub fn union() -> Bar { + Bar { + union: 5 + } + } +} + +fn main() { + let union = union::union(); + let _ = union.union; +} diff --git a/tests/ui/unnamed-fields/restrict_anonymous_structs.rs b/tests/ui/unnamed-fields/restrict_anonymous_structs.rs new file mode 100644 index 0000000000000..2670e5335885d --- /dev/null +++ b/tests/ui/unnamed-fields/restrict_anonymous_structs.rs @@ -0,0 +1,63 @@ +#![allow(incomplete_features)] +#![feature(unnamed_fields)] + +fn f() -> struct { field: u8 } {} //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + +fn f2(a: struct { field: u8 } ) {} //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + + +struct F { + field: struct { field: u8 } //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented +} + +union G { + field1: struct { field: u8 } //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented +} + +struct I(struct { field: u8 }, u8); //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + +enum J { + K(struct { field: u8 }), //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented + L { + _ : struct { field: u8 } //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous fields are not allowed outside of structs or unions + //~| ERROR anonymous structs are unimplemented + }, + M { + _ : u8 //~ ERROR anonymous fields are not allowed outside of structs or unions + } +} + +const L: struct { field: u8 } = 0; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + +static M: struct { field: u8 } = 0; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + +type N = struct { field: u8 }; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + +impl struct { field: u8 } {} //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + +trait Foo {} + +impl Foo for struct { field: u8 } {} //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + +fn main() { + let p: [struct { field: u8 }; 1]; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented + + let q: (struct { field: u8 }, u8); //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented + + let c = || -> struct { field: u8 } {}; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented +} diff --git a/tests/ui/unnamed-fields/restrict_anonymous_structs.stderr b/tests/ui/unnamed-fields/restrict_anonymous_structs.stderr new file mode 100644 index 0000000000000..7d2b5ba627ad4 --- /dev/null +++ b/tests/ui/unnamed-fields/restrict_anonymous_structs.stderr @@ -0,0 +1,198 @@ +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:4:11 + | +LL | fn f() -> struct { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:7:10 + | +LL | fn f2(a: struct { field: u8 } ) {} + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:12:12 + | +LL | field: struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:17:13 + | +LL | field1: struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:21:10 + | +LL | struct I(struct { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:25:7 + | +LL | K(struct { field: u8 }), + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous fields are not allowed outside of structs or unions + --> $DIR/restrict_anonymous_structs.rs:28:9 + | +LL | _ : struct { field: u8 } + | -^^^^^^^^^^^^^^^^^^^^^^^ + | | + | anonymous field declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:28:13 + | +LL | _ : struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous fields are not allowed outside of structs or unions + --> $DIR/restrict_anonymous_structs.rs:33:9 + | +LL | _ : u8 + | -^^^^^ + | | + | anonymous field declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:37:10 + | +LL | const L: struct { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:40:11 + | +LL | static M: struct { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:43:10 + | +LL | type N = struct { field: u8 }; + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:46:6 + | +LL | impl struct { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:51:14 + | +LL | impl Foo for struct { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:55:13 + | +LL | let p: [struct { field: u8 }; 1]; + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:58:13 + | +LL | let q: (struct { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:61:19 + | +LL | let c = || -> struct { field: u8 } {}; + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:4:11 + | +LL | fn f() -> struct { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:7:10 + | +LL | fn f2(a: struct { field: u8 } ) {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:12:12 + | +LL | field: struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:17:13 + | +LL | field1: struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:21:10 + | +LL | struct I(struct { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:25:7 + | +LL | K(struct { field: u8 }), + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:28:13 + | +LL | _ : struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:37:10 + | +LL | const L: struct { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:40:11 + | +LL | static M: struct { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:43:10 + | +LL | type N = struct { field: u8 }; + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:46:6 + | +LL | impl struct { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:51:14 + | +LL | impl Foo for struct { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:55:13 + | +LL | let p: [struct { field: u8 }; 1]; + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:58:13 + | +LL | let q: (struct { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:61:19 + | +LL | let c = || -> struct { field: u8 } {}; + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 32 previous errors + diff --git a/tests/ui/unnamed-fields/restrict_anonymous_unions.rs b/tests/ui/unnamed-fields/restrict_anonymous_unions.rs new file mode 100644 index 0000000000000..910830f1c4806 --- /dev/null +++ b/tests/ui/unnamed-fields/restrict_anonymous_unions.rs @@ -0,0 +1,62 @@ +#![allow(incomplete_features)] +#![feature(unnamed_fields)] + +fn f() -> union { field: u8 } {} //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous unions are unimplemented + +fn f2(a: union { field: u8 } ) {} //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous unions are unimplemented + +struct F { + field: union { field: u8 } //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous unions are unimplemented +} + +union G { + field: union { field: u8 } //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous unions are unimplemented +} + +struct I(union { field: u8 }, u8); //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous unions are unimplemented + +enum J { + K(union { field: u8 }), //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous unions are unimplemented + L { + _ : union { field: u8 } //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous fields are not allowed outside of structs or unions + //~| ERROR anonymous unions are unimplemented + }, + M { + _ : u8 //~ ERROR anonymous fields are not allowed outside of structs or unions + } +} + +const L: union { field: u8 } = 0; //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous unions are unimplemented + +static M: union { field: u8 } = 0; //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous unions are unimplemented + +type N = union { field: u8 }; //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous unions are unimplemented + +impl union { field: u8 } {} //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +// //~^ ERROR anonymous unions are unimplemented + +trait Foo {} + +impl Foo for union { field: u8 } {} //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous unions are unimplemented + +fn main() { + let p: [union { field: u8 }; 1]; //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous unions are unimplemented + + let q: (union { field: u8 }, u8); //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous unions are unimplemented + + let c = || -> union { field: u8 } {}; //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous unions are unimplemented +} diff --git a/tests/ui/unnamed-fields/restrict_anonymous_unions.stderr b/tests/ui/unnamed-fields/restrict_anonymous_unions.stderr new file mode 100644 index 0000000000000..31fff541d5ce5 --- /dev/null +++ b/tests/ui/unnamed-fields/restrict_anonymous_unions.stderr @@ -0,0 +1,198 @@ +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:4:11 + | +LL | fn f() -> union { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:7:10 + | +LL | fn f2(a: union { field: u8 } ) {} + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:11:12 + | +LL | field: union { field: u8 } + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:16:12 + | +LL | field: union { field: u8 } + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:20:10 + | +LL | struct I(union { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:24:7 + | +LL | K(union { field: u8 }), + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous fields are not allowed outside of structs or unions + --> $DIR/restrict_anonymous_unions.rs:27:9 + | +LL | _ : union { field: u8 } + | -^^^^^^^^^^^^^^^^^^^^^^ + | | + | anonymous field declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:27:13 + | +LL | _ : union { field: u8 } + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous fields are not allowed outside of structs or unions + --> $DIR/restrict_anonymous_unions.rs:32:9 + | +LL | _ : u8 + | -^^^^^ + | | + | anonymous field declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:36:10 + | +LL | const L: union { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:39:11 + | +LL | static M: union { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:42:10 + | +LL | type N = union { field: u8 }; + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:45:6 + | +LL | impl union { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:50:14 + | +LL | impl Foo for union { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:54:13 + | +LL | let p: [union { field: u8 }; 1]; + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:57:13 + | +LL | let q: (union { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:60:19 + | +LL | let c = || -> union { field: u8 } {}; + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:4:11 + | +LL | fn f() -> union { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:7:10 + | +LL | fn f2(a: union { field: u8 } ) {} + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:11:12 + | +LL | field: union { field: u8 } + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:16:12 + | +LL | field: union { field: u8 } + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:20:10 + | +LL | struct I(union { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:24:7 + | +LL | K(union { field: u8 }), + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:27:13 + | +LL | _ : union { field: u8 } + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:36:10 + | +LL | const L: union { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:39:11 + | +LL | static M: union { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:42:10 + | +LL | type N = union { field: u8 }; + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:45:6 + | +LL | impl union { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:50:14 + | +LL | impl Foo for union { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:54:13 + | +LL | let p: [union { field: u8 }; 1]; + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:57:13 + | +LL | let q: (union { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:60:19 + | +LL | let c = || -> union { field: u8 } {}; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 32 previous errors + From 9ce026c9a15c78d60dab617cea96db657fc1aa45 Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Sun, 29 Jan 2023 06:12:58 -0600 Subject: [PATCH 2/9] Be more conservative --- compiler/rustc_parse/src/parser/ty.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index daf533705ae3d..deac2730f3044 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -405,10 +405,9 @@ impl<'a> Parser<'a> { err.span_label(self.token.span, "expected type"); // self.maybe_annotate_with_ascription(&mut err, true); - // `struct` is a strict keyword, so we can check it here as - // we will be erroring otherwise - // FIXME: or should we not? - if self.token.is_keyword(kw::Struct) { + if allow_anonymous_types == AllowAnonymousTypes::OnlyRecover + && self.token.is_keyword(kw::Struct) + { // Recover the parser from anonymous types anywhere other than field types. let snapshot = self.create_snapshot_for_diagnostic(); match self.parse_anonymous_ty(lo) { From 168655198e5730f97e7881fd07a238de6b2a4f32 Mon Sep 17 00:00:00 2001 From: Frank King Date: Sun, 13 Aug 2023 23:00:18 +0800 Subject: [PATCH 3/9] Fix recovery of anonymous types --- compiler/rustc_ast/src/ast.rs | 4 +- compiler/rustc_ast/src/mut_visit.rs | 3 +- compiler/rustc_ast_lowering/src/item.rs | 5 +- compiler/rustc_ast_lowering/src/lib.rs | 21 +- .../rustc_ast_passes/src/ast_validation.rs | 7 +- compiler/rustc_ast_pretty/src/pprust/state.rs | 4 +- compiler/rustc_parse/src/parser/item.rs | 177 +++++++------ compiler/rustc_parse/src/parser/ty.rs | 244 ++++++++---------- src/tools/rustfmt/src/types.rs | 4 +- .../ui/parser/keyword-union-as-identifier.rs | 44 ++++ .../restrict_anonymous_structs.rs | 0 .../restrict_anonymous_structs.stderr | 0 .../restrict_anonymous_unions.rs | 0 .../restrict_anonymous_unions.stderr | 0 14 files changed, 270 insertions(+), 243 deletions(-) rename tests/ui/{ => union}/unnamed-fields/restrict_anonymous_structs.rs (100%) rename tests/ui/{ => union}/unnamed-fields/restrict_anonymous_structs.stderr (100%) rename tests/ui/{ => union}/unnamed-fields/restrict_anonymous_unions.rs (100%) rename tests/ui/{ => union}/unnamed-fields/restrict_anonymous_unions.stderr (100%) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index d080ec1820cfc..457611286be4d 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2093,9 +2093,9 @@ pub enum TyKind { /// A tuple (`(A, B, C, D,...)`). Tup(ThinVec>), /// An anonymous struct type i.e. `struct { foo: Type }` - AnonymousStruct(ThinVec, /* recovered */ bool), + AnonymousStruct(ThinVec), /// An anonymous union type i.e. `union { bar: Type }` - AnonymousUnion(ThinVec, /* recovered */ bool), + AnonymousUnion(ThinVec), /// A path (`module::module::...::Type`), optionally /// "qualified", e.g., ` as SomeTrait>::SomeType`. /// diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 827b8a8dfd79d..282c8ffddb5b7 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -509,8 +509,7 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { visit_vec(bounds, |bound| vis.visit_param_bound(bound)); } TyKind::MacCall(mac) => vis.visit_mac_call(mac), - TyKind::AnonymousStruct(fields, _recovered) - | TyKind::AnonymousUnion(fields, _recovered) => { + TyKind::AnonymousStruct(fields) | TyKind::AnonymousUnion(fields) => { fields.flat_map_in_place(|field| vis.flat_map_field_def(field)); } } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index ea9a4e511c117..a59c83de0f46f 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -708,10 +708,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - pub(super) fn lower_field_def( - &mut self, - (index, f): (usize, &FieldDef), - ) -> hir::FieldDef<'hir> { + fn lower_field_def(&mut self, (index, f): (usize, &FieldDef)) -> hir::FieldDef<'hir> { let ty = if let TyKind::Path(qself, path) = &f.ty.kind { let t = self.lower_path_ty( &f.ty, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index c8908a9e8c6b9..1a40d300278f9 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -34,8 +34,8 @@ #![feature(let_chains)] #![feature(never_type)] #![recursion_limit = "256"] -// #![deny(rustc::untranslatable_diagnostic)] -// #![deny(rustc::diagnostic_outside_of_impl)] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] extern crate tracing; @@ -1294,12 +1294,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::TyKind::Err(self.tcx.sess.delay_span_bug(t.span, "TyKind::Err lowered")) } // FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS - TyKind::AnonymousStruct(ref _fields, _recovered) => { - hir::TyKind::Err(self.tcx.sess.span_err(t.span, "anonymous structs are unimplemented")) - } - TyKind::AnonymousUnion(ref _fields, _recovered) => { - hir::TyKind::Err(self.tcx.sess.span_err(t.span, "anonymous unions are unimplemented")) - } + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] + TyKind::AnonymousStruct(ref _fields) => hir::TyKind::Err( + self.tcx.sess.span_err(t.span, "anonymous structs are unimplemented"), + ), + // FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] + TyKind::AnonymousUnion(ref _fields) => hir::TyKind::Err( + self.tcx.sess.span_err(t.span, "anonymous unions are unimplemented"), + ), TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), TyKind::Ref(region, mt) => { diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 07deaca82facc..53d9397e887af 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -221,7 +221,7 @@ impl<'a> AstValidator<'a> { } TyKind::AnonymousStruct(ref fields, ..) | TyKind::AnonymousUnion(ref fields, ..) => { // self.with_banned_assoc_ty_bound(|this| { - walk_list!(self, visit_struct_field_def, fields) + walk_list!(self, visit_struct_field_def, fields) // }); } _ => visit::walk_ty(self, t), @@ -865,6 +865,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_ty(&mut self, ty: &'a Ty) { self.visit_ty_common(ty); + tracing::info!(?ty); self.deny_anonymous_struct(ty); self.walk_ty(ty) } @@ -1085,7 +1086,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.visit_ident(item.ident); self.visit_generics(generics); // self.with_banned_assoc_ty_bound(|this| { - walk_list!(self, visit_struct_field_def, fields); + walk_list!(self, visit_struct_field_def, fields); // }); walk_list!(self, visit_attribute, &item.attrs); return; @@ -1102,7 +1103,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.visit_ident(item.ident); self.visit_generics(generics); // self.with_banned_assoc_ty_bound(|this| { - walk_list!(self, visit_struct_field_def, fields); + walk_list!(self, visit_struct_field_def, fields); // }); walk_list!(self, visit_attribute, &item.attrs); return; diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index e9f4015be301b..c74bb3b8d83a7 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1053,11 +1053,11 @@ impl<'a> State<'a> { } self.pclose(); } - ast::TyKind::AnonymousStruct(fields, _recovered) => { + ast::TyKind::AnonymousStruct(fields) => { self.head("struct"); self.print_record_struct_body(&fields, ty.span); } - ast::TyKind::AnonymousUnion(fields, _recovered) => { + ast::TyKind::AnonymousUnion(fields) => { self.head("union"); self.print_record_struct_body(&fields, ty.span); } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 227c08f83ab45..a4370853a0e61 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1,5 +1,5 @@ use super::diagnostics::{dummy_arg, ConsumeClosingDelim}; -use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; +use super::ty::{AllowPlus, RecoverAnonymousStructOrUnion, RecoverQPath, RecoverReturnSign}; use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken}; use crate::errors::{self, MacroExpandsToAdtField}; use crate::fluent_generated as fluent; @@ -602,7 +602,7 @@ impl<'a> Parser<'a> { Some(self.mk_ty(self.prev_token.span, TyKind::Err)) } else if has_for || self.token.can_begin_type() { snapshot_before_last_ty = self.create_snapshot_for_diagnostic(); - Some(self.parse_ty_no_anon_recovery()?) + Some(self.parse_ty()?) } else { None }; @@ -615,7 +615,7 @@ impl<'a> Parser<'a> { if let Some(mut err) = err { let mut snapshot = snapshot_before_last_ty; - if snapshot.can_start_anonymous_type() { + if snapshot.can_start_anonymous_union() { let recover_result = { let recover_last_ty = match snapshot.parse_ty() { Ok(ty) => Some(ty), @@ -1665,11 +1665,26 @@ impl<'a> Parser<'a> { Ok((class_name, ItemKind::Union(vdata, generics))) } - pub(crate) fn parse_record_struct_body( + fn parse_record_struct_body( &mut self, adt_ty: &str, ident_span: Span, parsed_where: bool, + ) -> PResult<'a, (ThinVec, /* recovered */ bool)> { + self.parse_record_struct_body_common( + adt_ty, + ident_span, + parsed_where, + RecoverAnonymousStructOrUnion::No, + ) + } + + pub(crate) fn parse_record_struct_body_common( + &mut self, + adt_ty: &str, + ident_span: Span, + parsed_where: bool, + recover_anonymous_struct_or_union: RecoverAnonymousStructOrUnion, ) -> PResult<'a, (ThinVec, /* recovered */ bool)> { let mut fields = ThinVec::new(); let mut recovered = false; @@ -1683,6 +1698,16 @@ impl<'a> Parser<'a> { match field { Ok(field) => fields.push(field), Err(mut err) => { + // When recovering the anonymous structs or unions, we should't emit the error + // immediately, because it may also be a type path `union` followed by a block, + // such as `impl union { fn foo() {} }`. Here we are actaully not parsing a + // record struct body but an `impl` body. + // + // Instead, the error should be thrown and handled by the caller + // `parse_anonymous_struct_or_union`. + if recover_anonymous_struct_or_union == RecoverAnonymousStructOrUnion::Yes { + return Err(err); + } err.span_label(ident_span, format!("while parsing this {adt_ty}")); err.emit(); break; @@ -1965,85 +1990,83 @@ impl<'a> Parser<'a> { /// for better diagnostics and suggestions. fn parse_field_ident(&mut self, adt_ty: &str, lo: Span) -> PResult<'a, Ident> { let (ident, is_raw) = self.ident_or_err(true)?; - if !is_raw && ident.is_reserved() { + if ident.name == kw::Underscore { + self.sess.gated_spans.gate(sym::unnamed_fields, lo); + } else if !is_raw && ident.is_reserved() { let snapshot = self.create_snapshot_for_diagnostic(); - if ident.name == kw::Underscore { - self.sess.gated_spans.gate(sym::unnamed_fields, lo); - } else { - let err = if self.check_fn_front_matter(false, Case::Sensitive) { - let inherited_vis = Visibility { - span: rustc_span::DUMMY_SP, - kind: VisibilityKind::Inherited, - tokens: None, - }; - // We use `parse_fn` to get a span for the function - let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true }; - match self.parse_fn( - &mut AttrVec::new(), - fn_parse_mode, - lo, - &inherited_vis, - Case::Insensitive, - ) { - Ok(_) => { - let mut err = self.struct_span_err( - lo.to(self.prev_token.span), - format!("functions are not allowed in {adt_ty} definitions"), - ); - err.help( - "unlike in C++, Java, and C#, functions are declared in `impl` blocks", - ); - err.help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information"); - err - } - Err(err) => { - err.cancel(); - self.restore_snapshot(snapshot); - self.expected_ident_found_err() - } + let err = if self.check_fn_front_matter(false, Case::Sensitive) { + let inherited_vis = Visibility { + span: rustc_span::DUMMY_SP, + kind: VisibilityKind::Inherited, + tokens: None, + }; + // We use `parse_fn` to get a span for the function + let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true }; + match self.parse_fn( + &mut AttrVec::new(), + fn_parse_mode, + lo, + &inherited_vis, + Case::Insensitive, + ) { + Ok(_) => { + let mut err = self.struct_span_err( + lo.to(self.prev_token.span), + format!("functions are not allowed in {adt_ty} definitions"), + ); + err.help( + "unlike in C++, Java, and C#, functions are declared in `impl` blocks", + ); + err.help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information"); + err } - } else if self.eat_keyword(kw::Struct) { - match self.parse_item_struct() { - Ok((ident, _)) => { - let mut err = self.struct_span_err( - lo.with_hi(ident.span.hi()), - format!("structs are not allowed in {adt_ty} definitions"), - ); - err.help("consider creating a new `struct` definition instead of nesting"); - err - } - Err(err) => { - err.cancel(); - self.restore_snapshot(snapshot); - self.expected_ident_found_err() - } + Err(err) => { + err.cancel(); + self.restore_snapshot(snapshot); + self.expected_ident_found_err() } - } else { - let mut err = self.expected_ident_found_err(); - if self.eat_keyword_noexpect(kw::Let) - && let removal_span = self.prev_token.span.until(self.token.span) - && let Ok(ident) = self.parse_ident_common(false) - // Cancel this error, we don't need it. - .map_err(|err| err.cancel()) - && self.token.kind == TokenKind::Colon - { - err.span_suggestion( - removal_span, - "remove this `let` keyword", - String::new(), - Applicability::MachineApplicable, + } + } else if self.eat_keyword(kw::Struct) { + match self.parse_item_struct() { + Ok((ident, _)) => { + let mut err = self.struct_span_err( + lo.with_hi(ident.span.hi()), + format!("structs are not allowed in {adt_ty} definitions"), ); - err.note("the `let` keyword is not allowed in `struct` fields"); - err.note("see for more information"); - err.emit(); - return Ok(ident); - } else { + err.help("consider creating a new `struct` definition instead of nesting"); + err + } + Err(err) => { + err.cancel(); self.restore_snapshot(snapshot); + self.expected_ident_found_err() } - err - }; - return Err(err); - } + } + } else { + let mut err = self.expected_ident_found_err(); + if self.eat_keyword_noexpect(kw::Let) + && let removal_span = self.prev_token.span.until(self.token.span) + && let Ok(ident) = self.parse_ident_common(false) + // Cancel this error, we don't need it. + .map_err(|err| err.cancel()) + && self.token.kind == TokenKind::Colon + { + err.span_suggestion( + removal_span, + "remove this `let` keyword", + String::new(), + Applicability::MachineApplicable, + ); + err.note("the `let` keyword is not allowed in `struct` fields"); + err.note("see for more information"); + err.emit(); + return Ok(ident); + } else { + self.restore_snapshot(snapshot); + } + err + }; + return Err(err); } self.bump(); Ok(ident) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index deac2730f3044..d26ebaed76058 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -51,31 +51,23 @@ pub(super) enum AllowPlus { No, } -#[derive(PartialEq, Copy, Clone)] +#[derive(PartialEq)] pub(super) enum RecoverQPath { Yes, No, } -#[derive(Clone, Copy)] pub(super) enum RecoverQuestionMark { Yes, No, } #[derive(PartialEq, Clone, Copy)] -pub(super) enum RecoverAnonEnum { +pub(crate) enum RecoverAnonymousStructOrUnion { Yes, No, } -#[derive(Copy, Clone, PartialEq)] -pub(super) enum AllowAnonymousTypes { - Yes, - OnlyRecover, - No, -} - /// Signals whether parsing a type should recover `->`. /// /// More specifically, when parsing a function like: @@ -108,7 +100,7 @@ impl RecoverReturnSign { } // Is `...` (`CVarArgs`) legal at this level of type parsing? -#[derive(PartialEq, Clone, Copy)] +#[derive(PartialEq)] enum AllowCVariadic { Yes, No, @@ -133,21 +125,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - RecoverAnonEnum::No, - AllowAnonymousTypes::OnlyRecover, - ) - } - - pub(super) fn parse_ty_no_anon_recovery(&mut self) -> PResult<'a, P> { - self.parse_ty_common( - AllowPlus::Yes, - AllowCVariadic::No, - RecoverQPath::Yes, - RecoverReturnSign::Yes, - None, - RecoverQuestionMark::Yes, - RecoverAnonEnum::No, - AllowAnonymousTypes::No, + RecoverAnonymousStructOrUnion::Yes, ) } @@ -162,8 +140,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, Some(ty_params), RecoverQuestionMark::Yes, - RecoverAnonEnum::No, - AllowAnonymousTypes::No, + RecoverAnonymousStructOrUnion::Yes, ) } @@ -178,8 +155,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - RecoverAnonEnum::No, - AllowAnonymousTypes::Yes, + RecoverAnonymousStructOrUnion::No, ) } @@ -194,8 +170,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - RecoverAnonEnum::Yes, - AllowAnonymousTypes::OnlyRecover, + RecoverAnonymousStructOrUnion::Yes, ) } @@ -213,8 +188,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - RecoverAnonEnum::No, - AllowAnonymousTypes::OnlyRecover, + RecoverAnonymousStructOrUnion::Yes, ) } @@ -228,8 +202,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, - RecoverAnonEnum::No, - AllowAnonymousTypes::OnlyRecover, + RecoverAnonymousStructOrUnion::Yes, ) } @@ -241,8 +214,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, - RecoverAnonEnum::No, - AllowAnonymousTypes::OnlyRecover, + RecoverAnonymousStructOrUnion::Yes, ) } @@ -255,8 +227,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::OnlyFatArrow, None, RecoverQuestionMark::Yes, - RecoverAnonEnum::No, - AllowAnonymousTypes::OnlyRecover, + RecoverAnonymousStructOrUnion::Yes, ) } @@ -276,8 +247,7 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, - RecoverAnonEnum::Yes, - AllowAnonymousTypes::OnlyRecover, + RecoverAnonymousStructOrUnion::Yes, )?; FnRetTy::Ty(ty) } else if recover_return_sign.can_recover(&self.token.kind) { @@ -292,8 +262,7 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, - RecoverAnonEnum::Yes, - AllowAnonymousTypes::OnlyRecover, + RecoverAnonymousStructOrUnion::Yes, )?; FnRetTy::Ty(ty) } else { @@ -309,8 +278,7 @@ impl<'a> Parser<'a> { recover_return_sign: RecoverReturnSign, ty_generics: Option<&Generics>, recover_question_mark: RecoverQuestionMark, - recover_anon_enum: RecoverAnonEnum, - allow_anonymous_types: AllowAnonymousTypes, + recover_anonymous_struct_or_union: RecoverAnonymousStructOrUnion, ) -> PResult<'a, P> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); @@ -358,10 +326,6 @@ impl<'a> Parser<'a> { } } else if self.eat_keyword(kw::Impl) { self.parse_impl_ty(&mut impl_dyn_multi)? - } else if allow_anonymous_types == AllowAnonymousTypes::Yes - && self.can_start_anonymous_type() - { - self.parse_anonymous_ty(lo)? } else if self.is_explicit_dyn_type() { self.parse_dyn_ty(&mut impl_dyn_multi)? } else if self.eat_lt() { @@ -370,20 +334,9 @@ impl<'a> Parser<'a> { TyKind::Path(Some(qself), path) } else if self.check_path() { // `union` is a weak keyword, so it can possibly be a path - if allow_anonymous_types == AllowAnonymousTypes::OnlyRecover - && self.can_start_anonymous_type() - { - // If we can parse an anonymous type, do so and let - // ast validation error out later - let snapshot = self.create_snapshot_for_diagnostic(); - match self.parse_anonymous_ty(lo) { - Ok(ty) => ty, - Err(err) => { - err.cancel(); - self.restore_snapshot(snapshot); - self.parse_path_start_ty(lo, allow_plus, ty_generics)? - } - } + if self.can_start_anonymous_union() + && let Some(ty_kind) = self.parse_anonymous_struct_or_union(lo, recover_anonymous_struct_or_union)? { + ty_kind } else { self.parse_path_start_ty(lo, allow_plus, ty_generics)? } @@ -400,28 +353,13 @@ impl<'a> Parser<'a> { } } } else { - let msg = format!("expected type, found {}", super::token_descr(&self.token)); - let mut err = self.struct_span_err(self.token.span, msg); - err.span_label(self.token.span, "expected type"); - // self.maybe_annotate_with_ascription(&mut err, true); - - if allow_anonymous_types == AllowAnonymousTypes::OnlyRecover - && self.token.is_keyword(kw::Struct) - { - // Recover the parser from anonymous types anywhere other than field types. - let snapshot = self.create_snapshot_for_diagnostic(); - match self.parse_anonymous_ty(lo) { - Ok(ty) => { - err.delay_as_bug(); - ty - } - Err(snapshot_err) => { - snapshot_err.cancel(); - self.restore_snapshot(snapshot); - return Err(err); - } - } + if self.can_start_anonymous_struct() + && let Some(ty_kind) = self.parse_anonymous_struct_or_union(lo, recover_anonymous_struct_or_union)? { + ty_kind } else { + let msg = format!("expected type, found {}", super::token_descr(&self.token)); + let mut err = self.struct_span_err(self.token.span, msg); + err.span_label(self.token.span, "expected type"); return Err(err); } }; @@ -437,71 +375,87 @@ impl<'a> Parser<'a> { if let RecoverQuestionMark::Yes = recover_question_mark { ty = self.maybe_recover_from_question_mark(ty); } - if recover_anon_enum == RecoverAnonEnum::Yes - && self.check_noexpect(&token::BinOp(token::Or)) - && self.look_ahead(1, |t| t.can_begin_type()) - { - let mut pipes = vec![self.token.span]; - let mut types = vec![ty]; - loop { - if !self.eat(&token::BinOp(token::Or)) { - break; - } - pipes.push(self.prev_token.span); - types.push(self.parse_ty_common( - allow_plus, - allow_c_variadic, - recover_qpath, - recover_return_sign, - ty_generics, - recover_question_mark, - RecoverAnonEnum::No, - AllowAnonymousTypes::OnlyRecover, - )?); - } - let mut err = self.struct_span_err(pipes, "anonymous enums are not supported"); - for ty in &types { - err.span_label(ty.span, ""); - } - // err.help(format!( - // "create a named `enum` and use it here instead:\nenum Name {{\n{}\n}}", - // types - // .iter() - // .enumerate() - // .map(|(i, t)| format!( - // " Variant{}({}),", - // i + 1, // Lets not confuse people with zero-indexing :) - // pprust::to_string(|s| s.print_type(&t)), - // )) - // .collect::>() - // .join("\n"), - // )); - err.emit(); - return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::Err)); - } if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) } } - pub(crate) fn parse_anonymous_ty(&mut self, lo: Span) -> PResult<'a, TyKind> { + /// Parse an anonymous struct or union, and returns: + /// - `Ok(Some(ty_kind))`, when successful. + /// - `Ok(None)`, when failed, we restore the snapshot before parsing. + /// This may happen during recovery. We have to be converative because + /// the following block is more likely to be an `impl` block or `fn` block + /// instead of an anonymous struct or union. + /// - `Err(err)`, a parse error which should be handled by the caller. + fn parse_anonymous_struct_or_union( + &mut self, + lo: Span, + recover_anonymous_struct_or_union: RecoverAnonymousStructOrUnion, + ) -> PResult<'a, Option> { assert!(self.token.is_keyword(kw::Union) || self.token.is_keyword(kw::Struct)); let is_union = self.token.is_keyword(kw::Union); + let snapshot = self.create_snapshot_for_diagnostic(); let span = self.token.span; self.bump(); - // FIXME: does false go here - self.parse_record_struct_body(if is_union { "union" } else { "struct" }, span, false).map( - |(fields, recovered)| { - let span = lo.to(self.prev_token.span); - self.sess.gated_spans.gate(sym::unnamed_fields, span); - // These can be rejected during AST validation in `deny_anonymous_struct`. - return if is_union { - TyKind::AnonymousUnion(fields, recovered) + // FIXME(performance): the struct body will be parsed twice when we restore the snapshot. + match self.parse_anonymous_struct_or_union_body( + if is_union { "union" } else { "struct" }, + span, + recover_anonymous_struct_or_union, + ) { + Ok(fields) => { + // During recovery, an empty anonymous struct or union is more likely to be + // a (maybe-wrong) identifier followed by an empty block, so we should + // give up this recovery. e.g.: + // ``` + // impl struct {} + // impl union {} + // impl Foo for struct {} + // impl Foo for union {} + // fn foo() -> struct {} + // fn foo() -> union {} + // ``` + if recover_anonymous_struct_or_union == RecoverAnonymousStructOrUnion::Yes + && fields.is_empty() + { + self.restore_snapshot(snapshot); + Ok(None) + } else { + let span = lo.to(self.prev_token.span); + self.sess.gated_spans.gate(sym::unnamed_fields, span); + // These can be rejected during AST validation in `deny_anonymous_struct_or_union`. + if is_union { + Ok(Some(TyKind::AnonymousUnion(fields))) + } else { + Ok(Some(TyKind::AnonymousStruct(fields))) + } + } + } + Err(err) => { + if recover_anonymous_struct_or_union == RecoverAnonymousStructOrUnion::Yes { + err.cancel(); + self.restore_snapshot(snapshot); + Ok(None) } else { - TyKind::AnonymousStruct(fields, recovered) - }; - }, + Err(err) + } + } + } + } + + fn parse_anonymous_struct_or_union_body( + &mut self, + adt_ty: &str, + ident_span: Span, + recover_anonymous_struct_or_union: RecoverAnonymousStructOrUnion, + ) -> PResult<'a, ThinVec> { + self.parse_record_struct_body_common( + adt_ty, + ident_span, + false, + recover_anonymous_struct_or_union, ) + .map(|(fields, _recovered)| fields) } /// Parses either: @@ -864,10 +818,14 @@ impl<'a> Parser<'a> { Ok(bounds) } - pub(super) fn can_start_anonymous_type(&mut self) -> bool { - (self.token.is_keyword(kw::Union) - && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace))) - || self.token.is_keyword(kw::Struct) + pub(super) fn can_start_anonymous_union(&mut self) -> bool { + self.token.is_keyword(kw::Union) + && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace)) + } + + pub(super) fn can_start_anonymous_struct(&mut self) -> bool { + self.token.is_keyword(kw::Struct) + && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace)) } /// Can the current token begin a bound? diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 7c7b75db701b1..d2ba5590fc566 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -819,8 +819,8 @@ impl Rewrite for ast::Ty { ast::TyKind::Tup(ref items) => { rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1) } - ast::TyKind::AnonymousStruct(_, _) => Some(context.snippet(self.span).to_owned()), - ast::TyKind::AnonymousUnion(_, _) => Some(context.snippet(self.span).to_owned()), + ast::TyKind::AnonymousStruct(_) => Some(context.snippet(self.span).to_owned()), + ast::TyKind::AnonymousUnion(_) => Some(context.snippet(self.span).to_owned()), ast::TyKind::Path(ref q_self, ref path) => { rewrite_path(context, PathContext::Type, q_self, path, shape) } diff --git a/tests/ui/parser/keyword-union-as-identifier.rs b/tests/ui/parser/keyword-union-as-identifier.rs index f9b88afc8782e..dcb582a606e4b 100644 --- a/tests/ui/parser/keyword-union-as-identifier.rs +++ b/tests/ui/parser/keyword-union-as-identifier.rs @@ -1,6 +1,7 @@ // check-pass #![allow(non_camel_case_types)] +#![allow(non_upper_case_globals)] mod union { type union = i32; @@ -16,7 +17,50 @@ mod union { } } +mod struct_union { + pub struct union { + pub union: u32 + } + static union: union = union { union: 0 }; + + impl union { + pub fn union() -> &'static union { + &union + } + } + impl union {} + trait Foo {} + impl Foo for union {} + trait Bar { + fn bar() {} + } + impl Bar for union {} +} + +mod union_union { + pub union union { + pub union: u32 + } + const union: union = union { union: 0 }; + impl union { + pub fn union() -> union { + union + } + } +} + +mod trait_union { + pub trait union { + fn union() {} + } + impl union for () {} +} + fn main() { let union = union::union(); let _ = union.union; + let _ = struct_union::union::union().union; + let union = union_union::union::union(); + let _ = unsafe { union.union }; + <() as trait_union::union>::union(); } diff --git a/tests/ui/unnamed-fields/restrict_anonymous_structs.rs b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs similarity index 100% rename from tests/ui/unnamed-fields/restrict_anonymous_structs.rs rename to tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs diff --git a/tests/ui/unnamed-fields/restrict_anonymous_structs.stderr b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr similarity index 100% rename from tests/ui/unnamed-fields/restrict_anonymous_structs.stderr rename to tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr diff --git a/tests/ui/unnamed-fields/restrict_anonymous_unions.rs b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs similarity index 100% rename from tests/ui/unnamed-fields/restrict_anonymous_unions.rs rename to tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs diff --git a/tests/ui/unnamed-fields/restrict_anonymous_unions.stderr b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr similarity index 100% rename from tests/ui/unnamed-fields/restrict_anonymous_unions.stderr rename to tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr From 28b89e05f60485334045c9fb478b0eaab4fa9159 Mon Sep 17 00:00:00 2001 From: Frank King Date: Mon, 14 Aug 2023 08:50:10 +0800 Subject: [PATCH 4/9] Improve the code style --- compiler/rustc_ast/src/ast.rs | 4 +- compiler/rustc_ast/src/mut_visit.rs | 2 +- compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_ast_lowering/src/lib.rs | 4 +- .../rustc_ast_passes/src/ast_validation.rs | 84 ++++++++----------- compiler/rustc_ast_pretty/src/pprust/state.rs | 4 +- compiler/rustc_parse/src/parser/item.rs | 10 ++- compiler/rustc_parse/src/parser/ty.rs | 84 ++++++++++++------- compiler/rustc_passes/src/hir_stats.rs | 4 +- src/tools/rustfmt/src/types.rs | 4 +- .../rustfmt/tests/target}/anonymous-types.rs | 0 11 files changed, 108 insertions(+), 94 deletions(-) rename {tests/pretty => src/tools/rustfmt/tests/target}/anonymous-types.rs (100%) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 457611286be4d..395540764ea05 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2093,9 +2093,9 @@ pub enum TyKind { /// A tuple (`(A, B, C, D,...)`). Tup(ThinVec>), /// An anonymous struct type i.e. `struct { foo: Type }` - AnonymousStruct(ThinVec), + AnonStruct(ThinVec), /// An anonymous union type i.e. `union { bar: Type }` - AnonymousUnion(ThinVec), + AnonUnion(ThinVec), /// A path (`module::module::...::Type`), optionally /// "qualified", e.g., ` as SomeTrait>::SomeType`. /// diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 282c8ffddb5b7..55a1772adb1d0 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -509,7 +509,7 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { visit_vec(bounds, |bound| vis.visit_param_bound(bound)); } TyKind::MacCall(mac) => vis.visit_mac_call(mac), - TyKind::AnonymousStruct(fields) | TyKind::AnonymousUnion(fields) => { + TyKind::AnonStruct(fields) | TyKind::AnonUnion(fields) => { fields.flat_map_in_place(|field| vis.flat_map_field_def(field)); } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 614ebd6c8dcc0..ddbbf5a10bcdd 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -438,7 +438,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) { TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {} TyKind::MacCall(mac) => visitor.visit_mac_call(mac), TyKind::Never | TyKind::CVarArgs => {} - TyKind::AnonymousStruct(ref fields, ..) | TyKind::AnonymousUnion(ref fields, ..) => { + TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => { walk_list!(visitor, visit_field_def, fields) } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 1a40d300278f9..79ed6b9b2aac7 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1296,13 +1296,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] - TyKind::AnonymousStruct(ref _fields) => hir::TyKind::Err( + TyKind::AnonStruct(ref _fields) => hir::TyKind::Err( self.tcx.sess.span_err(t.span, "anonymous structs are unimplemented"), ), // FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] - TyKind::AnonymousUnion(ref _fields) => hir::TyKind::Err( + TyKind::AnonUnion(ref _fields) => hir::TyKind::Err( self.tcx.sess.span_err(t.span, "anonymous unions are unimplemented"), ), TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 53d9397e887af..510d28b764e89 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -219,7 +219,7 @@ impl<'a> AstValidator<'a> { } } } - TyKind::AnonymousStruct(ref fields, ..) | TyKind::AnonymousUnion(ref fields, ..) => { + TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => { // self.with_banned_assoc_ty_bound(|this| { walk_list!(self, visit_struct_field_def, fields) // }); @@ -229,18 +229,17 @@ impl<'a> AstValidator<'a> { } fn visit_struct_field_def(&mut self, field: &'a FieldDef) { - if let Some(ident) = field.ident { - if ident.name == kw::Underscore { - self.check_anonymous_field(field); + if let Some(ident) = field.ident && + ident.name == kw::Underscore { + self.check_unnamed_field_ty(&field.ty, field.span); self.visit_vis(&field.vis); self.visit_ident(ident); self.visit_ty_common(&field.ty); self.walk_ty(&field.ty); walk_list!(self, visit_attribute, &field.attrs); - return; - } + } else { + self.visit_field_def(field); } - self.visit_field_def(field); } fn err_handler(&self) -> &rustc_errors::Handler { @@ -280,55 +279,41 @@ impl<'a> AstValidator<'a> { } } - fn check_anonymous_field(&self, field: &FieldDef) { - let FieldDef { ty, .. } = field; - match &ty.kind { - TyKind::AnonymousStruct(..) | TyKind::AnonymousUnion(..) => { - // We already checked for `kw::Underscore` before calling this function, - // so skip the check - } - TyKind::Path(..) => { - // If the anonymous field contains a Path as type, we can't determine - // if the path is a valid struct or union, so skip the check - } - _ => { - let msg = "unnamed fields can only have struct or union types"; - let label = "not a struct or union"; - self.err_handler() - .struct_span_err(field.span, msg) - .span_label(ty.span, label) - .emit(); - } + fn check_unnamed_field_ty(&self, ty: &Ty, span: Span) { + if matches!( + &ty.kind, + // We already checked for `kw::Underscore` before calling this function, + // so skip the check + TyKind::AnonStruct(..) | TyKind::AnonUnion(..) + // If the anonymous field contains a Path as type, we can't determine + // if the path is a valid struct or union, so skip the check + | TyKind::Path(..) + ) { + return; } + let msg = "unnamed fields can only have struct or union types"; + let label = "not a struct or union"; + self.err_handler().struct_span_err(span, msg).span_label(ty.span, label).emit(); } - fn deny_anonymous_struct(&self, ty: &Ty) { - match &ty.kind { - TyKind::AnonymousStruct(..) => { - self.err_handler() - .struct_span_err( - ty.span, - "anonymous structs are not allowed outside of unnamed struct or union fields", - ) - .span_label(ty.span, "anonymous struct declared here") - .emit(); - } - TyKind::AnonymousUnion(..) => { - self.err_handler() + fn deny_anon_struct_or_union(&self, ty: &Ty) { + let struct_or_union = match &ty.kind { + TyKind::AnonStruct(..) => "struct", + TyKind::AnonUnion(..) => "union", + _ => return, + }; + self.err_handler() .struct_span_err( ty.span, - "anonymous unions are not allowed outside of unnamed struct or union fields", + format!("anonymous {struct_or_union}s are not allowed outside of unnamed struct or union fields"), ) - .span_label(ty.span, "anonymous union declared here") + .span_label(ty.span, format!("anonymous {struct_or_union} declared here")) .emit(); - } - _ => {} - } } - fn deny_anonymous_field(&self, field: &FieldDef) { - if let Some(ident) = field.ident { - if ident.name == kw::Underscore { + fn deny_unnamed_field(&self, field: &FieldDef) { + if let Some(ident) = field.ident && + ident.name == kw::Underscore { self.err_handler() .struct_span_err( field.span, @@ -336,7 +321,6 @@ impl<'a> AstValidator<'a> { ) .span_label(ident.span, "anonymous field declared here") .emit(); - } } } @@ -866,7 +850,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_ty(&mut self, ty: &'a Ty) { self.visit_ty_common(ty); tracing::info!(?ty); - self.deny_anonymous_struct(ty); + self.deny_anon_struct_or_union(ty); self.walk_ty(ty) } @@ -881,7 +865,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } fn visit_field_def(&mut self, field: &'a FieldDef) { - self.deny_anonymous_field(field); + self.deny_unnamed_field(field); visit::walk_field_def(self, field) } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index c74bb3b8d83a7..187c5bee8438f 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1053,11 +1053,11 @@ impl<'a> State<'a> { } self.pclose(); } - ast::TyKind::AnonymousStruct(fields) => { + ast::TyKind::AnonStruct(fields) => { self.head("struct"); self.print_record_struct_body(&fields, ty.span); } - ast::TyKind::AnonymousUnion(fields) => { + ast::TyKind::AnonUnion(fields) => { self.head("union"); self.print_record_struct_body(&fields, ty.span); } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index a4370853a0e61..c0439578ff8e4 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1,5 +1,5 @@ use super::diagnostics::{dummy_arg, ConsumeClosingDelim}; -use super::ty::{AllowPlus, RecoverAnonymousStructOrUnion, RecoverQPath, RecoverReturnSign}; +use super::ty::{AllowPlus, MaybeRecoverAnonStructOrUnion, RecoverQPath, RecoverReturnSign}; use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken}; use crate::errors::{self, MacroExpandsToAdtField}; use crate::fluent_generated as fluent; @@ -1675,7 +1675,7 @@ impl<'a> Parser<'a> { adt_ty, ident_span, parsed_where, - RecoverAnonymousStructOrUnion::No, + MaybeRecoverAnonStructOrUnion::Parse, ) } @@ -1684,7 +1684,7 @@ impl<'a> Parser<'a> { adt_ty: &str, ident_span: Span, parsed_where: bool, - recover_anonymous_struct_or_union: RecoverAnonymousStructOrUnion, + maybe_recover_anon_struct_or_union: MaybeRecoverAnonStructOrUnion, ) -> PResult<'a, (ThinVec, /* recovered */ bool)> { let mut fields = ThinVec::new(); let mut recovered = false; @@ -1705,7 +1705,9 @@ impl<'a> Parser<'a> { // // Instead, the error should be thrown and handled by the caller // `parse_anonymous_struct_or_union`. - if recover_anonymous_struct_or_union == RecoverAnonymousStructOrUnion::Yes { + if maybe_recover_anon_struct_or_union + == MaybeRecoverAnonStructOrUnion::Recover + { return Err(err); } err.span_label(ident_span, format!("while parsing this {adt_ty}")); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index d26ebaed76058..ff82d7e2034b3 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -62,10 +62,36 @@ pub(super) enum RecoverQuestionMark { No, } +/// When there is an anonymous struct or union, whether we should parse it, e.g. +/// ```ignore (the feature `unnamed_field` is not fully implemented yet) +/// #[repr(C)] +/// struct Foo { +/// _: struct { +/// a: u32, +/// b: u64, +/// _: union { +/// c: f64, +/// d: char, +/// } +/// }, +/// } +/// ``` +/// or recover it, e.g. +/// ```compile_fail +/// // This will be recovered as an anonymous struct +/// impl struct { foo: Foo } { +/// fn foo() {} +/// } +/// // This will be first recovered as an anonymous union (but will fail) +/// // and then parsed as a type named `union` followed by a `fn` block. +/// fn bar() -> union { +/// let _i = 0; +/// } +/// ``` #[derive(PartialEq, Clone, Copy)] -pub(crate) enum RecoverAnonymousStructOrUnion { - Yes, - No, +pub(crate) enum MaybeRecoverAnonStructOrUnion { + Parse, + Recover, } /// Signals whether parsing a type should recover `->`. @@ -125,7 +151,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - RecoverAnonymousStructOrUnion::Yes, + MaybeRecoverAnonStructOrUnion::Recover, ) } @@ -140,7 +166,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, Some(ty_params), RecoverQuestionMark::Yes, - RecoverAnonymousStructOrUnion::Yes, + MaybeRecoverAnonStructOrUnion::Recover, ) } @@ -155,7 +181,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - RecoverAnonymousStructOrUnion::No, + MaybeRecoverAnonStructOrUnion::Parse, ) } @@ -170,7 +196,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - RecoverAnonymousStructOrUnion::Yes, + MaybeRecoverAnonStructOrUnion::Recover, ) } @@ -188,7 +214,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - RecoverAnonymousStructOrUnion::Yes, + MaybeRecoverAnonStructOrUnion::Recover, ) } @@ -202,7 +228,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, - RecoverAnonymousStructOrUnion::Yes, + MaybeRecoverAnonStructOrUnion::Recover, ) } @@ -214,7 +240,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, - RecoverAnonymousStructOrUnion::Yes, + MaybeRecoverAnonStructOrUnion::Recover, ) } @@ -227,7 +253,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::OnlyFatArrow, None, RecoverQuestionMark::Yes, - RecoverAnonymousStructOrUnion::Yes, + MaybeRecoverAnonStructOrUnion::Recover, ) } @@ -247,7 +273,7 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, - RecoverAnonymousStructOrUnion::Yes, + MaybeRecoverAnonStructOrUnion::Recover, )?; FnRetTy::Ty(ty) } else if recover_return_sign.can_recover(&self.token.kind) { @@ -262,7 +288,7 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, - RecoverAnonymousStructOrUnion::Yes, + MaybeRecoverAnonStructOrUnion::Recover, )?; FnRetTy::Ty(ty) } else { @@ -278,7 +304,7 @@ impl<'a> Parser<'a> { recover_return_sign: RecoverReturnSign, ty_generics: Option<&Generics>, recover_question_mark: RecoverQuestionMark, - recover_anonymous_struct_or_union: RecoverAnonymousStructOrUnion, + maybe_recover_anon_struct_or_union: MaybeRecoverAnonStructOrUnion, ) -> PResult<'a, P> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); @@ -335,7 +361,7 @@ impl<'a> Parser<'a> { } else if self.check_path() { // `union` is a weak keyword, so it can possibly be a path if self.can_start_anonymous_union() - && let Some(ty_kind) = self.parse_anonymous_struct_or_union(lo, recover_anonymous_struct_or_union)? { + && let Some(ty_kind) = self.parse_anon_struct_or_union(lo, maybe_recover_anon_struct_or_union)? { ty_kind } else { self.parse_path_start_ty(lo, allow_plus, ty_generics)? @@ -354,7 +380,7 @@ impl<'a> Parser<'a> { } } else { if self.can_start_anonymous_struct() - && let Some(ty_kind) = self.parse_anonymous_struct_or_union(lo, recover_anonymous_struct_or_union)? { + && let Some(ty_kind) = self.parse_anon_struct_or_union(lo, maybe_recover_anon_struct_or_union)? { ty_kind } else { let msg = format!("expected type, found {}", super::token_descr(&self.token)); @@ -385,10 +411,12 @@ impl<'a> Parser<'a> { /// the following block is more likely to be an `impl` block or `fn` block /// instead of an anonymous struct or union. /// - `Err(err)`, a parse error which should be handled by the caller. - fn parse_anonymous_struct_or_union( + /// This will only happen when we are actually parsing instead of recovering + /// from an anonymous struct or union. + fn parse_anon_struct_or_union( &mut self, lo: Span, - recover_anonymous_struct_or_union: RecoverAnonymousStructOrUnion, + maybe_recover_anon_struct_or_union: MaybeRecoverAnonStructOrUnion, ) -> PResult<'a, Option> { assert!(self.token.is_keyword(kw::Union) || self.token.is_keyword(kw::Struct)); let is_union = self.token.is_keyword(kw::Union); @@ -398,10 +426,10 @@ impl<'a> Parser<'a> { self.bump(); // FIXME(performance): the struct body will be parsed twice when we restore the snapshot. - match self.parse_anonymous_struct_or_union_body( + match self.parse_anon_struct_or_union_body( if is_union { "union" } else { "struct" }, span, - recover_anonymous_struct_or_union, + maybe_recover_anon_struct_or_union, ) { Ok(fields) => { // During recovery, an empty anonymous struct or union is more likely to be @@ -415,7 +443,7 @@ impl<'a> Parser<'a> { // fn foo() -> struct {} // fn foo() -> union {} // ``` - if recover_anonymous_struct_or_union == RecoverAnonymousStructOrUnion::Yes + if maybe_recover_anon_struct_or_union == MaybeRecoverAnonStructOrUnion::Recover && fields.is_empty() { self.restore_snapshot(snapshot); @@ -423,16 +451,16 @@ impl<'a> Parser<'a> { } else { let span = lo.to(self.prev_token.span); self.sess.gated_spans.gate(sym::unnamed_fields, span); - // These can be rejected during AST validation in `deny_anonymous_struct_or_union`. + // These can be rejected during AST validation in `deny_anon_struct_or_union`. if is_union { - Ok(Some(TyKind::AnonymousUnion(fields))) + Ok(Some(TyKind::AnonUnion(fields))) } else { - Ok(Some(TyKind::AnonymousStruct(fields))) + Ok(Some(TyKind::AnonStruct(fields))) } } } Err(err) => { - if recover_anonymous_struct_or_union == RecoverAnonymousStructOrUnion::Yes { + if maybe_recover_anon_struct_or_union == MaybeRecoverAnonStructOrUnion::Recover { err.cancel(); self.restore_snapshot(snapshot); Ok(None) @@ -443,17 +471,17 @@ impl<'a> Parser<'a> { } } - fn parse_anonymous_struct_or_union_body( + fn parse_anon_struct_or_union_body( &mut self, adt_ty: &str, ident_span: Span, - recover_anonymous_struct_or_union: RecoverAnonymousStructOrUnion, + maybe_recover_anon_struct_or_union: MaybeRecoverAnonStructOrUnion, ) -> PResult<'a, ThinVec> { self.parse_record_struct_body_common( adt_ty, ident_span, false, - recover_anonymous_struct_or_union, + maybe_recover_anon_struct_or_union, ) .map(|(fields, _recovered)| fields) } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 44893e4ab1c0f..24087a4eabbcd 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -587,8 +587,8 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { BareFn, Never, Tup, - AnonymousStruct, - AnonymousUnion, + AnonStruct, + AnonUnion, Path, TraitObject, ImplTrait, diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index d2ba5590fc566..5e8edd8f8bf4e 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -819,8 +819,8 @@ impl Rewrite for ast::Ty { ast::TyKind::Tup(ref items) => { rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1) } - ast::TyKind::AnonymousStruct(_) => Some(context.snippet(self.span).to_owned()), - ast::TyKind::AnonymousUnion(_) => Some(context.snippet(self.span).to_owned()), + ast::TyKind::AnonStruct(_) => Some(context.snippet(self.span).to_owned()), + ast::TyKind::AnonUnion(_) => Some(context.snippet(self.span).to_owned()), ast::TyKind::Path(ref q_self, ref path) => { rewrite_path(context, PathContext::Type, q_self, path, shape) } diff --git a/tests/pretty/anonymous-types.rs b/src/tools/rustfmt/tests/target/anonymous-types.rs similarity index 100% rename from tests/pretty/anonymous-types.rs rename to src/tools/rustfmt/tests/target/anonymous-types.rs From 5cc403e05d4d51e3d70c77eba00e4f1bf52d10df Mon Sep 17 00:00:00 2001 From: Frank King Date: Wed, 16 Aug 2023 19:35:06 +0800 Subject: [PATCH 5/9] Change the version number of the feature --- compiler/rustc_feature/src/active.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 123428bc9b7d2..cde9dbd8e9233 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -582,7 +582,7 @@ declare_features! ( /// Enables rustc to generate code that instructs libstd to NOT ignore SIGPIPE. (active, unix_sigpipe, "1.65.0", Some(97889), None), /// Allows unnamed fields of struct and union type - (incomplete, unnamed_fields, "1.68.0", Some(49804), None), + (incomplete, unnamed_fields, "CURRENT_RUSTC_VERSION", Some(49804), None), /// Allows unsized fn parameters. (active, unsized_fn_params, "1.49.0", Some(48055), None), /// Allows unsized rvalues at arguments and parameters. From 2514c9daa7a38227f3343aec99b53a42273284c7 Mon Sep 17 00:00:00 2001 From: Frank King Date: Wed, 16 Aug 2023 19:35:46 +0800 Subject: [PATCH 6/9] Try another recovery strategy --- compiler/rustc_ast/src/token.rs | 2 + compiler/rustc_parse/src/parser/item.rs | 144 +++------- compiler/rustc_parse/src/parser/ty.rs | 255 +++++++++++++----- .../ui/parser/keyword-union-as-identifier.rs | 8 +- 4 files changed, 228 insertions(+), 181 deletions(-) diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index f4ad0efa42344..3e6a5cd31bca9 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -486,6 +486,8 @@ impl Token { Lt | BinOp(Shl) | // associated path ModSep => true, // global path Interpolated(ref nt) => matches!(**nt, NtTy(..) | NtPath(..)), + // for anonymous structs or unions, thay only appears in specific positions + // (type of struct fields or union fields), we don't consider them as regular types _ => false, } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index c0439578ff8e4..0723d663f8b29 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1,5 +1,5 @@ use super::diagnostics::{dummy_arg, ConsumeClosingDelim}; -use super::ty::{AllowPlus, MaybeRecoverAnonStructOrUnion, RecoverQPath, RecoverReturnSign}; +use super::ty::{AllowAnonStructOrUnion, AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken}; use crate::errors::{self, MacroExpandsToAdtField}; use crate::fluent_generated as fluent; @@ -562,103 +562,42 @@ impl<'a> Parser<'a> { let polarity = self.parse_polarity(); - let mut snapshot_before_last_ty = self.create_snapshot_for_diagnostic(); // Parse both types and traits as a type, then reinterpret if necessary. let err_path = |span| ast::Path::from_ident(Ident::new(kw::Empty, span)); - let mut ty_first = - if self.token.is_keyword(kw::For) && self.look_ahead(1, |t| t != &token::Lt) { - let span = self.prev_token.span.between(self.token.span); - self.struct_span_err(span, "missing trait in a trait impl") - .span_suggestion( - span, - "add a trait here", - " Trait ", - Applicability::HasPlaceholders, - ) - .span_suggestion( - span.to(self.token.span), - "for an inherent impl, drop this `for`", - "", - Applicability::MaybeIncorrect, - ) - .emit(); - P(Ty { - kind: TyKind::Path(None, err_path(span)), - span, - id: DUMMY_NODE_ID, - tokens: None, - }) - } else { - self.parse_ty_with_generics_recovery(&generics)? - }; + let ty_first = if self.token.is_keyword(kw::For) && self.look_ahead(1, |t| t != &token::Lt) + { + let span = self.prev_token.span.between(self.token.span); + self.sess.emit_err(errors::MissingTraitInTraitImpl { + span, + for_span: span.to(self.token.span), + }); + P(Ty { + kind: TyKind::Path(None, err_path(span)), + span, + id: DUMMY_NODE_ID, + tokens: None, + }) + } else { + self.parse_ty_with_generics_recovery(&generics)? + }; // If `for` is missing we try to recover. let has_for = self.eat_keyword(kw::For); let missing_for_span = self.prev_token.span.between(self.token.span); - let mut ty_second = if self.token == token::DotDot { + let ty_second = if self.token == token::DotDot { // We need to report this error after `cfg` expansion for compatibility reasons self.bump(); // `..`, do not add it to expected tokens Some(self.mk_ty(self.prev_token.span, TyKind::Err)) } else if has_for || self.token.can_begin_type() { - snapshot_before_last_ty = self.create_snapshot_for_diagnostic(); - Some(self.parse_ty()?) + Some(self.parse_second_ty_for_item_impl()?) } else { None }; generics.where_clause = self.parse_where_clause()?; - let (mut impl_items, err) = - self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))?; - - if let Some(mut err) = err { - let mut snapshot = snapshot_before_last_ty; - - if snapshot.can_start_anonymous_union() { - let recover_result = { - let recover_last_ty = match snapshot.parse_ty() { - Ok(ty) => Some(ty), - Err(snapshot_err) => { - snapshot_err.cancel(); - None - } - }; - - let impl_items = match snapshot - .parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No)) - { - Ok((impl_items, None)) => Some(impl_items), - Ok((_, Some(snapshot_err))) => { - snapshot_err.cancel(); - None - } - Err(snapshot_err) => { - snapshot_err.cancel(); - None - } - }; - - (recover_last_ty, impl_items) - }; - - if let (Some(recover_last_ty), Some(new_impl_items)) = recover_result { - err.delay_as_bug(); - self.restore_snapshot(snapshot); - - if ty_second.is_some() { - ty_second = Some(recover_last_ty); - } else { - ty_first = recover_last_ty; - } - impl_items = new_impl_items; - } else { - err.emit(); - } - } else { - err.emit(); - } - } + let impl_items = self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))?; let item_kind = match ty_second { Some(ty_second) => { @@ -727,21 +666,20 @@ impl<'a> Parser<'a> { &mut self, attrs: &mut AttrVec, mut parse_item: impl FnMut(&mut Parser<'a>) -> PResult<'a, Option>>, - ) -> PResult<'a, (ThinVec, Option>)> { + ) -> PResult<'a, ThinVec> { let open_brace_span = self.token.span; // Recover `impl Ty;` instead of `impl Ty {}` if self.token == TokenKind::Semi { self.sess.emit_err(errors::UseEmptyBlockNotSemi { span: self.token.span }); self.bump(); - return Ok((ThinVec::new(), None)); + return Ok(ThinVec::new()); } self.expect(&token::OpenDelim(Delimiter::Brace))?; attrs.extend(self.parse_inner_attributes()?); let mut items = ThinVec::new(); - let mut delayed_err = None; while !self.eat(&token::CloseDelim(Delimiter::Brace)) { if self.recover_doc_comment_before_brace() { continue; @@ -803,21 +741,21 @@ impl<'a> Parser<'a> { Applicability::MaybeIncorrect, ); } - delayed_err = Some(err); + err.emit(); break; } Ok(Some(item)) => items.extend(item), Err(mut err) => { self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes); err.span_label(open_brace_span, "while parsing this item list starting here") - .span_label(self.prev_token.span, "the item list ends here"); + .span_label(self.prev_token.span, "the item list ends here") + .emit(); - delayed_err = Some(err); break; } } } - Ok((items, delayed_err)) + Ok(items) } /// Recover on a doc comment before `}`. @@ -908,13 +846,7 @@ impl<'a> Parser<'a> { } else { // It's a normal trait. generics.where_clause = self.parse_where_clause()?; - let (items, err) = - self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?; - - if let Some(mut err) = err { - err.emit(); - } - + let items = self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?; Ok(( ident, ItemKind::Trait(Box::new(Trait { is_auto, unsafety, generics, bounds, items })), @@ -1182,13 +1114,11 @@ impl<'a> Parser<'a> { self.eat_keyword(kw::Unsafe); } - let (items, err) = - self.parse_item_list(attrs, |p| p.parse_foreign_item(ForceCollect::No))?; - if let Some(mut err) = err { - err.emit(); - } - - let module = ast::ForeignMod { unsafety, abi, items }; + let module = ast::ForeignMod { + unsafety, + abi, + items: self.parse_item_list(attrs, |p| p.parse_foreign_item(ForceCollect::No))?, + }; Ok((Ident::empty(), ItemKind::ForeignMod(module))) } @@ -1675,7 +1605,7 @@ impl<'a> Parser<'a> { adt_ty, ident_span, parsed_where, - MaybeRecoverAnonStructOrUnion::Parse, + AllowAnonStructOrUnion::Yes, ) } @@ -1684,7 +1614,7 @@ impl<'a> Parser<'a> { adt_ty: &str, ident_span: Span, parsed_where: bool, - maybe_recover_anon_struct_or_union: MaybeRecoverAnonStructOrUnion, + allow_anon_struct_or_union: AllowAnonStructOrUnion<'a>, ) -> PResult<'a, (ThinVec, /* recovered */ bool)> { let mut fields = ThinVec::new(); let mut recovered = false; @@ -1704,9 +1634,9 @@ impl<'a> Parser<'a> { // record struct body but an `impl` body. // // Instead, the error should be thrown and handled by the caller - // `parse_anonymous_struct_or_union`. - if maybe_recover_anon_struct_or_union - == MaybeRecoverAnonStructOrUnion::Recover + // `parse_anon_struct_or_union`. + if let AllowAnonStructOrUnion::RecoverNonEmptyOrElse(_) = + allow_anon_struct_or_union { return Err(err); } diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index ff82d7e2034b3..022ab34a074b6 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -62,36 +62,98 @@ pub(super) enum RecoverQuestionMark { No, } -/// When there is an anonymous struct or union, whether we should parse it, e.g. -/// ```ignore (the feature `unnamed_field` is not fully implemented yet) -/// #[repr(C)] -/// struct Foo { -/// _: struct { -/// a: u32, -/// b: u64, -/// _: union { -/// c: f64, -/// d: char, -/// } -/// }, -/// } -/// ``` -/// or recover it, e.g. -/// ```compile_fail -/// // This will be recovered as an anonymous struct -/// impl struct { foo: Foo } { -/// fn foo() {} -/// } -/// // This will be first recovered as an anonymous union (but will fail) -/// // and then parsed as a type named `union` followed by a `fn` block. -/// fn bar() -> union { -/// let _i = 0; -/// } -/// ``` -#[derive(PartialEq, Clone, Copy)] -pub(crate) enum MaybeRecoverAnonStructOrUnion { - Parse, - Recover, +/// Whether allowed to parse or recover an anonymous struct or union. +#[derive(Debug, Clone, Copy)] +pub(crate) enum AllowAnonStructOrUnion<'a> { + /// Parse the anonymous structs or unions regularly. + /// + /// It is always okay to parse it in fields definitions, and nice to + /// recover. + /// ```ignore (feature-not-ready) + /// #[repr(C)] + /// struct Foo { + /// _: struct { + /// a: u32, + /// b: u64, + /// _: union { + /// c: f64, + /// d: char, + /// } + /// }, + /// } + /// ``` + /// Similarly, for types of parameters, where there is no second way to parse + /// the anonymous structs or unions, we can parse it and report an error that + /// anonymous structs or unions are not allowed here. + Yes, + /// Recover an anonymous struct or union for diagnostic, but be more + /// conservative than `Yes` if there are other ways to parse it. + /// + /// This mode is preferred where types are allowed to be followed by a block + /// `{` ... `}`, where the block cannot contain field definitions. Such as: + /// - `impl $(Trait for)? Type {}` + /// - `fn foo() -> Type {}` + /// + /// Except for some special cases (will discuss below), anonymous structs or + /// unions with empty fields are always ambiguous since the empty block `{}` + /// can also be valid `impl` bodies or `fn` bodies, so we usually shouldn't + /// allow recovery of empty anonymous structs or unions. + /// + /// # Examples + /// ## Empty blocks + /// ``` + /// # #![allow(non_camel_case_types)] + /// # trait Foo {} + /// # struct union {} + /// impl union {} + /// impl Foo for union {} + /// fn foo() -> union { + /// union {} + /// } + /// ``` + /// ## Non-empty blocks + /// ```compile_fail + /// // This will be recovered as an anonymous struct + /// impl struct { foo: Foo } { + /// fn foo() {} + /// } + /// // This will be first recovered as an anonymous union (but will fail) + /// // and then parsed as a type named `union` followed by an `fn` block. + /// fn bar() -> union { + /// let _i = 0; + /// } + /// ``` + /// # A Special Case + /// In `impl` items, even if for an anonymous struct or union with + /// empty fields, we can still allow recovering it as long as it is followed by + /// some tokens such as `where`, `for`, or `{`. + /// ```compile_fail + /// impl union {} for union {} where union: union {} + /// ``` + RecoverNonEmptyOrElse(/* allow_followed_by */ Option) -> bool>), + /// Disallow parsing of anonymous structs or unions. + /// + /// Generally, we should avoid parsing or recovering anonymous structs or unions + /// if we have no information of what can appear in a block `{` ... `}` followed + /// by a type, (such as a `$ty:ty { /* any macro rules */ }` in macros), or + /// otherwise the following codes will be broken. + /// ``` + /// macro_rules! ty { + /// ($ty:ty { $($field:ident : $field_ty:ty)* }) => {}; + /// } + /// ty!(union { union: union }); + /// ty!(union {}); + /// ``` + No, +} + +impl<'a> AllowAnonStructOrUnion<'a> { + fn recover_non_empty_or_else(allow_followed_by: fn(&Parser<'a>) -> bool) -> Self { + Self::RecoverNonEmptyOrElse(Some(allow_followed_by)) + } + fn recover_non_empty() -> Self { + Self::RecoverNonEmptyOrElse(None) + } } /// Signals whether parsing a type should recover `->`. @@ -151,7 +213,29 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - MaybeRecoverAnonStructOrUnion::Recover, + AllowAnonStructOrUnion::recover_non_empty(), + ) + } + + fn can_follow_first_ty_in_item_impl(&self) -> bool { + self.token.is_keyword(kw::For) || self.can_follow_second_ty_in_item_impl() + } + + fn can_follow_second_ty_in_item_impl(&self) -> bool { + self.token.is_keyword(kw::Where) || self.token == token::OpenDelim(Delimiter::Brace) + } + + pub(super) fn parse_second_ty_for_item_impl(&mut self) -> PResult<'a, P> { + self.parse_ty_common( + AllowPlus::Yes, + AllowCVariadic::No, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + None, + RecoverQuestionMark::Yes, + AllowAnonStructOrUnion::recover_non_empty_or_else( + Self::can_follow_second_ty_in_item_impl, + ), ) } @@ -166,7 +250,9 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, Some(ty_params), RecoverQuestionMark::Yes, - MaybeRecoverAnonStructOrUnion::Recover, + AllowAnonStructOrUnion::recover_non_empty_or_else( + Self::can_follow_first_ty_in_item_impl, + ), ) } @@ -181,7 +267,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - MaybeRecoverAnonStructOrUnion::Parse, + AllowAnonStructOrUnion::Yes, ) } @@ -196,7 +282,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - MaybeRecoverAnonStructOrUnion::Recover, + AllowAnonStructOrUnion::Yes, ) } @@ -214,7 +300,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - MaybeRecoverAnonStructOrUnion::Recover, + AllowAnonStructOrUnion::No, ) } @@ -228,7 +314,7 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, - MaybeRecoverAnonStructOrUnion::Recover, + AllowAnonStructOrUnion::No, ) } @@ -240,7 +326,8 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, - MaybeRecoverAnonStructOrUnion::Recover, + // Disable the entry of parsing `$ty:ty` in macros + AllowAnonStructOrUnion::No, ) } @@ -253,7 +340,8 @@ impl<'a> Parser<'a> { RecoverReturnSign::OnlyFatArrow, None, RecoverQuestionMark::Yes, - MaybeRecoverAnonStructOrUnion::Recover, + // The `where` clause can be followed by a valid empty block `{}`. + AllowAnonStructOrUnion::recover_non_empty(), ) } @@ -273,7 +361,8 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, - MaybeRecoverAnonStructOrUnion::Recover, + // Return types can be followed by a valid empty block `{}`. + AllowAnonStructOrUnion::recover_non_empty(), )?; FnRetTy::Ty(ty) } else if recover_return_sign.can_recover(&self.token.kind) { @@ -288,7 +377,8 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, - MaybeRecoverAnonStructOrUnion::Recover, + // Return types can be followed by a valid empty block `{}`. + AllowAnonStructOrUnion::recover_non_empty(), )?; FnRetTy::Ty(ty) } else { @@ -304,7 +394,7 @@ impl<'a> Parser<'a> { recover_return_sign: RecoverReturnSign, ty_generics: Option<&Generics>, recover_question_mark: RecoverQuestionMark, - maybe_recover_anon_struct_or_union: MaybeRecoverAnonStructOrUnion, + allow_anon_struct_or_union: AllowAnonStructOrUnion<'a>, ) -> PResult<'a, P> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); @@ -360,8 +450,8 @@ impl<'a> Parser<'a> { TyKind::Path(Some(qself), path) } else if self.check_path() { // `union` is a weak keyword, so it can possibly be a path - if self.can_start_anonymous_union() - && let Some(ty_kind) = self.parse_anon_struct_or_union(lo, maybe_recover_anon_struct_or_union)? { + if self.can_start_anon_union() + && let Some(ty_kind) = self.parse_anon_struct_or_union(lo, allow_anon_struct_or_union)? { ty_kind } else { self.parse_path_start_ty(lo, allow_plus, ty_generics)? @@ -379,8 +469,8 @@ impl<'a> Parser<'a> { } } } else { - if self.can_start_anonymous_struct() - && let Some(ty_kind) = self.parse_anon_struct_or_union(lo, maybe_recover_anon_struct_or_union)? { + if self.can_start_anon_struct() + && let Some(ty_kind) = self.parse_anon_struct_or_union(lo, allow_anon_struct_or_union)? { ty_kind } else { let msg = format!("expected type, found {}", super::token_descr(&self.token)); @@ -406,18 +496,20 @@ impl<'a> Parser<'a> { /// Parse an anonymous struct or union, and returns: /// - `Ok(Some(ty_kind))`, when successful. - /// - `Ok(None)`, when failed, we restore the snapshot before parsing. - /// This may happen during recovery. We have to be converative because - /// the following block is more likely to be an `impl` block or `fn` block - /// instead of an anonymous struct or union. + /// - `Ok(None)`, when failed and recovery is diabled. /// - `Err(err)`, a parse error which should be handled by the caller. /// This will only happen when we are actually parsing instead of recovering /// from an anonymous struct or union. + /// + /// See also [`AllowAnonStructOrUnion`]. fn parse_anon_struct_or_union( &mut self, lo: Span, - maybe_recover_anon_struct_or_union: MaybeRecoverAnonStructOrUnion, + allow_anon_struct_or_union: AllowAnonStructOrUnion<'a>, ) -> PResult<'a, Option> { + if let AllowAnonStructOrUnion::No = allow_anon_struct_or_union { + return Ok(None); + } assert!(self.token.is_keyword(kw::Union) || self.token.is_keyword(kw::Struct)); let is_union = self.token.is_keyword(kw::Union); @@ -429,26 +521,24 @@ impl<'a> Parser<'a> { match self.parse_anon_struct_or_union_body( if is_union { "union" } else { "struct" }, span, - maybe_recover_anon_struct_or_union, + allow_anon_struct_or_union, ) { - Ok(fields) => { - // During recovery, an empty anonymous struct or union is more likely to be - // a (maybe-wrong) identifier followed by an empty block, so we should - // give up this recovery. e.g.: - // ``` - // impl struct {} - // impl union {} - // impl Foo for struct {} - // impl Foo for union {} - // fn foo() -> struct {} - // fn foo() -> union {} - // ``` - if maybe_recover_anon_struct_or_union == MaybeRecoverAnonStructOrUnion::Recover - && fields.is_empty() + Ok(fields) => match allow_anon_struct_or_union { + // Don't recover an anonymous struct or union followed by an empty block + AllowAnonStructOrUnion::RecoverNonEmptyOrElse(None) if fields.is_empty() => { + self.restore_snapshot(snapshot); + Ok(None) + } + // Don't recover an anonymous struct or union followed by an empty block, + // except it is followed by some specific tokens. + AllowAnonStructOrUnion::RecoverNonEmptyOrElse(Some(allow_followed_by)) + if fields.is_empty() && !allow_followed_by(self) => { self.restore_snapshot(snapshot); Ok(None) - } else { + } + // Parse the anonymous struct or union and return. + _ => { let span = lo.to(self.prev_token.span); self.sess.gated_spans.gate(sym::unnamed_fields, span); // These can be rejected during AST validation in `deny_anon_struct_or_union`. @@ -458,9 +548,11 @@ impl<'a> Parser<'a> { Ok(Some(TyKind::AnonStruct(fields))) } } - } + }, Err(err) => { - if maybe_recover_anon_struct_or_union == MaybeRecoverAnonStructOrUnion::Recover { + // Don't recover an anonymous struct or union with errors. + if let AllowAnonStructOrUnion::RecoverNonEmptyOrElse(_) = allow_anon_struct_or_union + { err.cancel(); self.restore_snapshot(snapshot); Ok(None) @@ -475,15 +567,32 @@ impl<'a> Parser<'a> { &mut self, adt_ty: &str, ident_span: Span, - maybe_recover_anon_struct_or_union: MaybeRecoverAnonStructOrUnion, + allow_anon_struct_or_union: AllowAnonStructOrUnion<'a>, ) -> PResult<'a, ThinVec> { self.parse_record_struct_body_common( adt_ty, ident_span, false, - maybe_recover_anon_struct_or_union, + allow_anon_struct_or_union, ) - .map(|(fields, _recovered)| fields) + .map(|(mut fields, recovered)| { + // Don't recover an anonymous struct or union if it is already recovered. + // + // If it is a recovered struct or union, we should not accept it in case that + // the non-empty block happens to be a valid block. e.g.: + // ``` + // type union = u32; + // static union: union = 0; + // fn foo() -> union { + // union + // } + // ``` + // here the `union { union }` will be recovered as a single field union. + if let AllowAnonStructOrUnion::RecoverNonEmptyOrElse(_) = allow_anon_struct_or_union && recovered { + fields.clear(); + } + fields + }) } /// Parses either: @@ -846,12 +955,12 @@ impl<'a> Parser<'a> { Ok(bounds) } - pub(super) fn can_start_anonymous_union(&mut self) -> bool { + pub(super) fn can_start_anon_union(&mut self) -> bool { self.token.is_keyword(kw::Union) && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace)) } - pub(super) fn can_start_anonymous_struct(&mut self) -> bool { + pub(super) fn can_start_anon_struct(&mut self) -> bool { self.token.is_keyword(kw::Struct) && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace)) } diff --git a/tests/ui/parser/keyword-union-as-identifier.rs b/tests/ui/parser/keyword-union-as-identifier.rs index dcb582a606e4b..7062557d73126 100644 --- a/tests/ui/parser/keyword-union-as-identifier.rs +++ b/tests/ui/parser/keyword-union-as-identifier.rs @@ -24,7 +24,7 @@ mod struct_union { static union: union = union { union: 0 }; impl union { - pub fn union() -> &'static union { + pub fn union<'union>() -> &'union union { &union } } @@ -56,6 +56,10 @@ mod trait_union { impl union for () {} } +macro_rules! ty { + ($ty:ty { $($field:ident:$field_ty:ty)* }) => {}; +} + fn main() { let union = union::union(); let _ = union.union; @@ -63,4 +67,6 @@ fn main() { let union = union_union::union::union(); let _ = unsafe { union.union }; <() as trait_union::union>::union(); + ty!(union {}); + ty!(union { union: union }); } From 624ce6f4e0071e5a0c09cd0ed6732fe6f2e955df Mon Sep 17 00:00:00 2001 From: Frank King Date: Thu, 17 Aug 2023 21:59:58 +0800 Subject: [PATCH 7/9] Improve AST validation strategies --- .../rustc_ast_passes/src/ast_validation.rs | 15 ++- compiler/rustc_ast_passes/src/lib.rs | 5 +- .../restrict_anonymous_structs.rs | 34 ++++-- .../restrict_anonymous_structs.stderr | 114 +++++++++++------- .../restrict_anonymous_unions.rs | 34 ++++-- .../restrict_anonymous_unions.stderr | 110 ++++++++++------- 6 files changed, 194 insertions(+), 118 deletions(-) diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 510d28b764e89..f30834f65179d 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -221,7 +221,7 @@ impl<'a> AstValidator<'a> { } TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => { // self.with_banned_assoc_ty_bound(|this| { - walk_list!(self, visit_struct_field_def, fields) + walk_list!(self, visit_field_def, fields) // }); } _ => visit::walk_ty(self, t), @@ -231,7 +231,7 @@ impl<'a> AstValidator<'a> { fn visit_struct_field_def(&mut self, field: &'a FieldDef) { if let Some(ident) = field.ident && ident.name == kw::Underscore { - self.check_unnamed_field_ty(&field.ty, field.span); + self.check_unnamed_field_ty(&field.ty, ident.span); self.visit_vis(&field.vis); self.visit_ident(ident); self.visit_ty_common(&field.ty); @@ -279,6 +279,8 @@ impl<'a> AstValidator<'a> { } } + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] fn check_unnamed_field_ty(&self, ty: &Ty, span: Span) { if matches!( &ty.kind, @@ -296,6 +298,8 @@ impl<'a> AstValidator<'a> { self.err_handler().struct_span_err(span, msg).span_label(ty.span, label).emit(); } + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] fn deny_anon_struct_or_union(&self, ty: &Ty) { let struct_or_union = match &ty.kind { TyKind::AnonStruct(..) => "struct", @@ -311,15 +315,17 @@ impl<'a> AstValidator<'a> { .emit(); } + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] fn deny_unnamed_field(&self, field: &FieldDef) { if let Some(ident) = field.ident && ident.name == kw::Underscore { self.err_handler() .struct_span_err( field.span, - "anonymous fields are not allowed outside of structs or unions", + "unnamed fields are not allowed outside of structs or unions", ) - .span_label(ident.span, "anonymous field declared here") + .span_label(ident.span, "unnamed field declared here") .emit(); } } @@ -849,7 +855,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_ty(&mut self, ty: &'a Ty) { self.visit_ty_common(ty); - tracing::info!(?ty); self.deny_anon_struct_or_union(ty); self.walk_ty(ty) } diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 710ef401ebb3d..7db413c5bbd42 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -9,9 +9,8 @@ #![feature(iter_is_partitioned)] #![feature(let_chains)] #![recursion_limit = "256"] -// FIXME(unnamed_field): unncomment these two lints -// #![deny(rustc::untranslatable_diagnostic)] -// #![deny(rustc::diagnostic_outside_of_impl)] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; use rustc_fluent_macro::fluent_messages; diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs index 2670e5335885d..e6539b387a76a 100644 --- a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs +++ b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs @@ -9,28 +9,40 @@ fn f2(a: struct { field: u8 } ) {} //~ ERROR anonymous structs are not allowed o struct F { - field: struct { field: u8 } //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + field: struct { field: u8 }, //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields //~^ ERROR anonymous structs are unimplemented + _: struct { field: u8 }, + //~^ ERROR anonymous structs are unimplemented +} + +struct G { + _: (u8, u8), //~ ERROR unnamed fields can only have struct or union types } -union G { - field1: struct { field: u8 } //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +union H { + field: struct { field: u8 }, //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented + _: struct { field: u8 }, //~^ ERROR anonymous structs are unimplemented } -struct I(struct { field: u8 }, u8); //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +union I { + _: (u8, u8), //~ ERROR unnamed fields can only have struct or union types +} + +struct J(struct { field: u8 }, u8); //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields //~^ ERROR anonymous structs are unimplemented -enum J { - K(struct { field: u8 }), //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +enum K { + L(struct { field: u8 }), //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields //~^ ERROR anonymous structs are unimplemented - L { - _ : struct { field: u8 } //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields - //~^ ERROR anonymous fields are not allowed outside of structs or unions + M { + _ : struct { field: u8 }, //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR unnamed fields are not allowed outside of structs or unions //~| ERROR anonymous structs are unimplemented }, - M { - _ : u8 //~ ERROR anonymous fields are not allowed outside of structs or unions + N { + _ : u8, //~ ERROR unnamed fields are not allowed outside of structs or unions } } diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr index 7d2b5ba627ad4..3c7383b1a2d5a 100644 --- a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr +++ b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr @@ -13,93 +13,105 @@ LL | fn f2(a: struct { field: u8 } ) {} error: anonymous structs are not allowed outside of unnamed struct or union fields --> $DIR/restrict_anonymous_structs.rs:12:12 | -LL | field: struct { field: u8 } +LL | field: struct { field: u8 }, | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here +error: unnamed fields can only have struct or union types + --> $DIR/restrict_anonymous_structs.rs:19:5 + | +LL | _: (u8, u8), + | ^ -------- not a struct or union + error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:17:13 + --> $DIR/restrict_anonymous_structs.rs:23:12 | -LL | field1: struct { field: u8 } - | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here +LL | field: struct { field: u8 }, + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: unnamed fields can only have struct or union types + --> $DIR/restrict_anonymous_structs.rs:30:5 + | +LL | _: (u8, u8), + | ^ -------- not a struct or union error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:21:10 + --> $DIR/restrict_anonymous_structs.rs:33:10 | -LL | struct I(struct { field: u8 }, u8); +LL | struct J(struct { field: u8 }, u8); | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:25:7 + --> $DIR/restrict_anonymous_structs.rs:37:7 | -LL | K(struct { field: u8 }), +LL | L(struct { field: u8 }), | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here -error: anonymous fields are not allowed outside of structs or unions - --> $DIR/restrict_anonymous_structs.rs:28:9 +error: unnamed fields are not allowed outside of structs or unions + --> $DIR/restrict_anonymous_structs.rs:40:9 | -LL | _ : struct { field: u8 } +LL | _ : struct { field: u8 }, | -^^^^^^^^^^^^^^^^^^^^^^^ | | - | anonymous field declared here + | unnamed field declared here error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:28:13 + --> $DIR/restrict_anonymous_structs.rs:40:13 | -LL | _ : struct { field: u8 } +LL | _ : struct { field: u8 }, | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here -error: anonymous fields are not allowed outside of structs or unions - --> $DIR/restrict_anonymous_structs.rs:33:9 +error: unnamed fields are not allowed outside of structs or unions + --> $DIR/restrict_anonymous_structs.rs:45:9 | -LL | _ : u8 +LL | _ : u8, | -^^^^^ | | - | anonymous field declared here + | unnamed field declared here error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:37:10 + --> $DIR/restrict_anonymous_structs.rs:49:10 | LL | const L: struct { field: u8 } = 0; | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:40:11 + --> $DIR/restrict_anonymous_structs.rs:52:11 | LL | static M: struct { field: u8 } = 0; | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:43:10 + --> $DIR/restrict_anonymous_structs.rs:55:10 | LL | type N = struct { field: u8 }; | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:46:6 + --> $DIR/restrict_anonymous_structs.rs:58:6 | LL | impl struct { field: u8 } {} | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:51:14 + --> $DIR/restrict_anonymous_structs.rs:63:14 | LL | impl Foo for struct { field: u8 } {} | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:55:13 + --> $DIR/restrict_anonymous_structs.rs:67:13 | LL | let p: [struct { field: u8 }; 1]; | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:58:13 + --> $DIR/restrict_anonymous_structs.rs:70:13 | LL | let q: (struct { field: u8 }, u8); | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:61:19 + --> $DIR/restrict_anonymous_structs.rs:73:19 | LL | let c = || -> struct { field: u8 } {}; | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here @@ -119,80 +131,92 @@ LL | fn f2(a: struct { field: u8 } ) {} error: anonymous structs are unimplemented --> $DIR/restrict_anonymous_structs.rs:12:12 | -LL | field: struct { field: u8 } +LL | field: struct { field: u8 }, | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:17:13 + --> $DIR/restrict_anonymous_structs.rs:14:8 | -LL | field1: struct { field: u8 } - | ^^^^^^^^^^^^^^^^^^^^ +LL | _: struct { field: u8 }, + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:23:12 + | +LL | field: struct { field: u8 }, + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:25:8 + | +LL | _: struct { field: u8 }, + | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:21:10 + --> $DIR/restrict_anonymous_structs.rs:33:10 | -LL | struct I(struct { field: u8 }, u8); +LL | struct J(struct { field: u8 }, u8); | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:25:7 + --> $DIR/restrict_anonymous_structs.rs:37:7 | -LL | K(struct { field: u8 }), +LL | L(struct { field: u8 }), | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:28:13 + --> $DIR/restrict_anonymous_structs.rs:40:13 | -LL | _ : struct { field: u8 } +LL | _ : struct { field: u8 }, | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:37:10 + --> $DIR/restrict_anonymous_structs.rs:49:10 | LL | const L: struct { field: u8 } = 0; | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:40:11 + --> $DIR/restrict_anonymous_structs.rs:52:11 | LL | static M: struct { field: u8 } = 0; | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:43:10 + --> $DIR/restrict_anonymous_structs.rs:55:10 | LL | type N = struct { field: u8 }; | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:46:6 + --> $DIR/restrict_anonymous_structs.rs:58:6 | LL | impl struct { field: u8 } {} | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:51:14 + --> $DIR/restrict_anonymous_structs.rs:63:14 | LL | impl Foo for struct { field: u8 } {} | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:55:13 + --> $DIR/restrict_anonymous_structs.rs:67:13 | LL | let p: [struct { field: u8 }; 1]; | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:58:13 + --> $DIR/restrict_anonymous_structs.rs:70:13 | LL | let q: (struct { field: u8 }, u8); | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:61:19 + --> $DIR/restrict_anonymous_structs.rs:73:19 | LL | let c = || -> struct { field: u8 } {}; | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 32 previous errors +error: aborting due to 36 previous errors diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs index 910830f1c4806..8d68b106b9f4d 100644 --- a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs +++ b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs @@ -8,28 +8,40 @@ fn f2(a: union { field: u8 } ) {} //~ ERROR anonymous unions are not allowed out //~^ ERROR anonymous unions are unimplemented struct F { - field: union { field: u8 } //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + field: union { field: u8 }, //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields //~^ ERROR anonymous unions are unimplemented + _: union { field: u8 }, + //~^ ERROR anonymous unions are unimplemented +} + +struct G { + _: (u8, u8), //~ ERROR unnamed fields can only have struct or union types } -union G { - field: union { field: u8 } //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +union H { + field: union { field: u8 }, //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous unions are unimplemented + _: union { field: u8 }, //~^ ERROR anonymous unions are unimplemented } -struct I(union { field: u8 }, u8); //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +union I { + _: (u8, u8), //~ ERROR unnamed fields can only have struct or union types +} + +struct J(union { field: u8 }, u8); //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields //~^ ERROR anonymous unions are unimplemented -enum J { - K(union { field: u8 }), //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +enum K { + L(union { field: u8 }), //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields //~^ ERROR anonymous unions are unimplemented - L { - _ : union { field: u8 } //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields - //~^ ERROR anonymous fields are not allowed outside of structs or unions + M { + _ : union { field: u8 }, //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR unnamed fields are not allowed outside of structs or unions //~| ERROR anonymous unions are unimplemented }, - M { - _ : u8 //~ ERROR anonymous fields are not allowed outside of structs or unions + N { + _ : u8, //~ ERROR unnamed fields are not allowed outside of structs or unions } } diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr index 31fff541d5ce5..473c800dde9cf 100644 --- a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr +++ b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr @@ -13,93 +13,105 @@ LL | fn f2(a: union { field: u8 } ) {} error: anonymous unions are not allowed outside of unnamed struct or union fields --> $DIR/restrict_anonymous_unions.rs:11:12 | -LL | field: union { field: u8 } +LL | field: union { field: u8 }, | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here +error: unnamed fields can only have struct or union types + --> $DIR/restrict_anonymous_unions.rs:18:5 + | +LL | _: (u8, u8), + | ^ -------- not a struct or union + error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:16:12 + --> $DIR/restrict_anonymous_unions.rs:22:12 | -LL | field: union { field: u8 } +LL | field: union { field: u8 }, | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here +error: unnamed fields can only have struct or union types + --> $DIR/restrict_anonymous_unions.rs:29:5 + | +LL | _: (u8, u8), + | ^ -------- not a struct or union + error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:20:10 + --> $DIR/restrict_anonymous_unions.rs:32:10 | -LL | struct I(union { field: u8 }, u8); +LL | struct J(union { field: u8 }, u8); | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:24:7 + --> $DIR/restrict_anonymous_unions.rs:36:7 | -LL | K(union { field: u8 }), +LL | L(union { field: u8 }), | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here -error: anonymous fields are not allowed outside of structs or unions - --> $DIR/restrict_anonymous_unions.rs:27:9 +error: unnamed fields are not allowed outside of structs or unions + --> $DIR/restrict_anonymous_unions.rs:39:9 | -LL | _ : union { field: u8 } +LL | _ : union { field: u8 }, | -^^^^^^^^^^^^^^^^^^^^^^ | | - | anonymous field declared here + | unnamed field declared here error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:27:13 + --> $DIR/restrict_anonymous_unions.rs:39:13 | -LL | _ : union { field: u8 } +LL | _ : union { field: u8 }, | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here -error: anonymous fields are not allowed outside of structs or unions - --> $DIR/restrict_anonymous_unions.rs:32:9 +error: unnamed fields are not allowed outside of structs or unions + --> $DIR/restrict_anonymous_unions.rs:44:9 | -LL | _ : u8 +LL | _ : u8, | -^^^^^ | | - | anonymous field declared here + | unnamed field declared here error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:36:10 + --> $DIR/restrict_anonymous_unions.rs:48:10 | LL | const L: union { field: u8 } = 0; | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:39:11 + --> $DIR/restrict_anonymous_unions.rs:51:11 | LL | static M: union { field: u8 } = 0; | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:42:10 + --> $DIR/restrict_anonymous_unions.rs:54:10 | LL | type N = union { field: u8 }; | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:45:6 + --> $DIR/restrict_anonymous_unions.rs:57:6 | LL | impl union { field: u8 } {} | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:50:14 + --> $DIR/restrict_anonymous_unions.rs:62:14 | LL | impl Foo for union { field: u8 } {} | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:54:13 + --> $DIR/restrict_anonymous_unions.rs:66:13 | LL | let p: [union { field: u8 }; 1]; | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:57:13 + --> $DIR/restrict_anonymous_unions.rs:69:13 | LL | let q: (union { field: u8 }, u8); | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:60:19 + --> $DIR/restrict_anonymous_unions.rs:72:19 | LL | let c = || -> union { field: u8 } {}; | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here @@ -119,80 +131,92 @@ LL | fn f2(a: union { field: u8 } ) {} error: anonymous unions are unimplemented --> $DIR/restrict_anonymous_unions.rs:11:12 | -LL | field: union { field: u8 } +LL | field: union { field: u8 }, | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:16:12 + --> $DIR/restrict_anonymous_unions.rs:13:8 + | +LL | _: union { field: u8 }, + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:22:12 | -LL | field: union { field: u8 } +LL | field: union { field: u8 }, | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:20:10 + --> $DIR/restrict_anonymous_unions.rs:24:8 + | +LL | _: union { field: u8 }, + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:32:10 | -LL | struct I(union { field: u8 }, u8); +LL | struct J(union { field: u8 }, u8); | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:24:7 + --> $DIR/restrict_anonymous_unions.rs:36:7 | -LL | K(union { field: u8 }), +LL | L(union { field: u8 }), | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:27:13 + --> $DIR/restrict_anonymous_unions.rs:39:13 | -LL | _ : union { field: u8 } +LL | _ : union { field: u8 }, | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:36:10 + --> $DIR/restrict_anonymous_unions.rs:48:10 | LL | const L: union { field: u8 } = 0; | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:39:11 + --> $DIR/restrict_anonymous_unions.rs:51:11 | LL | static M: union { field: u8 } = 0; | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:42:10 + --> $DIR/restrict_anonymous_unions.rs:54:10 | LL | type N = union { field: u8 }; | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:45:6 + --> $DIR/restrict_anonymous_unions.rs:57:6 | LL | impl union { field: u8 } {} | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:50:14 + --> $DIR/restrict_anonymous_unions.rs:62:14 | LL | impl Foo for union { field: u8 } {} | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:54:13 + --> $DIR/restrict_anonymous_unions.rs:66:13 | LL | let p: [union { field: u8 }; 1]; | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:57:13 + --> $DIR/restrict_anonymous_unions.rs:69:13 | LL | let q: (union { field: u8 }, u8); | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:60:19 + --> $DIR/restrict_anonymous_unions.rs:72:19 | LL | let c = || -> union { field: u8 } {}; | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 32 previous errors +error: aborting due to 36 previous errors From bdaa0c9d225d93c31651beacc9699678e7ee2a7c Mon Sep 17 00:00:00 2001 From: Frank King Date: Fri, 18 Aug 2023 08:56:31 +0800 Subject: [PATCH 8/9] Use diagnostic structs instead of manual emitted errors --- compiler/rustc_ast_passes/messages.ftl | 12 +++++++++ .../rustc_ast_passes/src/ast_validation.rs | 27 +++++-------------- compiler/rustc_ast_passes/src/errors.rs | 27 +++++++++++++++++++ 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 2f0ac0c2b1987..429dc2c94a9cc 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -1,3 +1,7 @@ +ast_passes_anon_struct_or_union_not_allowed = + anonymous {$struct_or_union}s are not allowed outside of unnamed struct or union fields + .label = anonymous {$struct_or_union} declared here + ast_passes_assoc_const_without_body = associated constant in `impl` without body .suggestion = provide a definition for the constant @@ -162,6 +166,14 @@ ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation} ast_passes_invalid_label = invalid label name `{$name}` +ast_passes_invalid_unnamed_field = + unnamed fields are not allowed outside of structs or unions + .label = unnamed field declared here + +ast_passes_invalid_unnamed_field_ty = + unnamed fields can only have struct or union types + .label = not a struct or union + ast_passes_item_underscore = `{$kind}` items in this context need a name .label = `_` is not a valid name for this `{$kind}` item diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index f30834f65179d..9e05ec36bd956 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -279,8 +279,6 @@ impl<'a> AstValidator<'a> { } } - #[allow(rustc::untranslatable_diagnostic)] - #[allow(rustc::diagnostic_outside_of_impl)] fn check_unnamed_field_ty(&self, ty: &Ty, span: Span) { if matches!( &ty.kind, @@ -293,13 +291,9 @@ impl<'a> AstValidator<'a> { ) { return; } - let msg = "unnamed fields can only have struct or union types"; - let label = "not a struct or union"; - self.err_handler().struct_span_err(span, msg).span_label(ty.span, label).emit(); + self.err_handler().emit_err(errors::InvalidUnnamedFieldTy { span, ty_span: ty.span }); } - #[allow(rustc::untranslatable_diagnostic)] - #[allow(rustc::diagnostic_outside_of_impl)] fn deny_anon_struct_or_union(&self, ty: &Ty) { let struct_or_union = match &ty.kind { TyKind::AnonStruct(..) => "struct", @@ -307,26 +301,17 @@ impl<'a> AstValidator<'a> { _ => return, }; self.err_handler() - .struct_span_err( - ty.span, - format!("anonymous {struct_or_union}s are not allowed outside of unnamed struct or union fields"), - ) - .span_label(ty.span, format!("anonymous {struct_or_union} declared here")) - .emit(); + .emit_err(errors::AnonStructOrUnionNotAllowed { struct_or_union, span: ty.span }); } - #[allow(rustc::untranslatable_diagnostic)] - #[allow(rustc::diagnostic_outside_of_impl)] fn deny_unnamed_field(&self, field: &FieldDef) { if let Some(ident) = field.ident && ident.name == kw::Underscore { self.err_handler() - .struct_span_err( - field.span, - "unnamed fields are not allowed outside of structs or unions", - ) - .span_label(ident.span, "unnamed field declared here") - .emit(); + .emit_err(errors::InvalidUnnamedField { + span: field.span, + ident_span: ident.span + }); } } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index ab8015c4a43b6..d706cb170e831 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -701,3 +701,30 @@ pub struct ConstraintOnNegativeBound { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(ast_passes_invalid_unnamed_field_ty)] +pub struct InvalidUnnamedFieldTy { + #[primary_span] + pub span: Span, + #[label] + pub ty_span: Span, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_invalid_unnamed_field)] +pub struct InvalidUnnamedField { + #[primary_span] + pub span: Span, + #[label] + pub ident_span: Span, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_anon_struct_or_union_not_allowed)] +pub struct AnonStructOrUnionNotAllowed { + #[primary_span] + #[label] + pub span: Span, + pub struct_or_union: &'static str, +} From ea6c448c5f80e87bf282b026b9c40b8da418b241 Mon Sep 17 00:00:00 2001 From: Frank King Date: Wed, 23 Aug 2023 20:21:18 +0800 Subject: [PATCH 9/9] Disable the recovery of anonymous structs or unions --- .../rustc_ast_passes/src/ast_validation.rs | 6 - compiler/rustc_parse/src/parser/item.rs | 33 +- compiler/rustc_parse/src/parser/ty.rs | 300 +++--------------- .../restrict_anonymous_structs.rs | 40 +-- .../restrict_anonymous_structs.stderr | 170 +--------- .../restrict_anonymous_unions.rs | 39 +-- .../restrict_anonymous_unions.stderr | 170 +--------- 7 files changed, 73 insertions(+), 685 deletions(-) diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 9e05ec36bd956..371ae7947ca0a 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -220,9 +220,7 @@ impl<'a> AstValidator<'a> { } } TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => { - // self.with_banned_assoc_ty_bound(|this| { walk_list!(self, visit_field_def, fields) - // }); } _ => visit::walk_ty(self, t), } @@ -1059,9 +1057,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.visit_vis(&item.vis); self.visit_ident(item.ident); self.visit_generics(generics); - // self.with_banned_assoc_ty_bound(|this| { walk_list!(self, visit_struct_field_def, fields); - // }); walk_list!(self, visit_attribute, &item.attrs); return; } @@ -1076,9 +1072,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.visit_vis(&item.vis); self.visit_ident(item.ident); self.visit_generics(generics); - // self.with_banned_assoc_ty_bound(|this| { walk_list!(self, visit_struct_field_def, fields); - // }); walk_list!(self, visit_attribute, &item.attrs); return; } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 0723d663f8b29..138e41e00b9de 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1,5 +1,5 @@ use super::diagnostics::{dummy_arg, ConsumeClosingDelim}; -use super::ty::{AllowAnonStructOrUnion, AllowPlus, RecoverQPath, RecoverReturnSign}; +use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken}; use crate::errors::{self, MacroExpandsToAdtField}; use crate::fluent_generated as fluent; @@ -590,7 +590,7 @@ impl<'a> Parser<'a> { self.bump(); // `..`, do not add it to expected tokens Some(self.mk_ty(self.prev_token.span, TyKind::Err)) } else if has_for || self.token.can_begin_type() { - Some(self.parse_second_ty_for_item_impl()?) + Some(self.parse_ty()?) } else { None }; @@ -1595,26 +1595,11 @@ impl<'a> Parser<'a> { Ok((class_name, ItemKind::Union(vdata, generics))) } - fn parse_record_struct_body( + pub(crate) fn parse_record_struct_body( &mut self, adt_ty: &str, ident_span: Span, parsed_where: bool, - ) -> PResult<'a, (ThinVec, /* recovered */ bool)> { - self.parse_record_struct_body_common( - adt_ty, - ident_span, - parsed_where, - AllowAnonStructOrUnion::Yes, - ) - } - - pub(crate) fn parse_record_struct_body_common( - &mut self, - adt_ty: &str, - ident_span: Span, - parsed_where: bool, - allow_anon_struct_or_union: AllowAnonStructOrUnion<'a>, ) -> PResult<'a, (ThinVec, /* recovered */ bool)> { let mut fields = ThinVec::new(); let mut recovered = false; @@ -1628,18 +1613,6 @@ impl<'a> Parser<'a> { match field { Ok(field) => fields.push(field), Err(mut err) => { - // When recovering the anonymous structs or unions, we should't emit the error - // immediately, because it may also be a type path `union` followed by a block, - // such as `impl union { fn foo() {} }`. Here we are actaully not parsing a - // record struct body but an `impl` body. - // - // Instead, the error should be thrown and handled by the caller - // `parse_anon_struct_or_union`. - if let AllowAnonStructOrUnion::RecoverNonEmptyOrElse(_) = - allow_anon_struct_or_union - { - return Err(err); - } err.span_label(ident_span, format!("while parsing this {adt_ty}")); err.emit(); break; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 022ab34a074b6..3fc9bbf96ff3a 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -62,100 +62,6 @@ pub(super) enum RecoverQuestionMark { No, } -/// Whether allowed to parse or recover an anonymous struct or union. -#[derive(Debug, Clone, Copy)] -pub(crate) enum AllowAnonStructOrUnion<'a> { - /// Parse the anonymous structs or unions regularly. - /// - /// It is always okay to parse it in fields definitions, and nice to - /// recover. - /// ```ignore (feature-not-ready) - /// #[repr(C)] - /// struct Foo { - /// _: struct { - /// a: u32, - /// b: u64, - /// _: union { - /// c: f64, - /// d: char, - /// } - /// }, - /// } - /// ``` - /// Similarly, for types of parameters, where there is no second way to parse - /// the anonymous structs or unions, we can parse it and report an error that - /// anonymous structs or unions are not allowed here. - Yes, - /// Recover an anonymous struct or union for diagnostic, but be more - /// conservative than `Yes` if there are other ways to parse it. - /// - /// This mode is preferred where types are allowed to be followed by a block - /// `{` ... `}`, where the block cannot contain field definitions. Such as: - /// - `impl $(Trait for)? Type {}` - /// - `fn foo() -> Type {}` - /// - /// Except for some special cases (will discuss below), anonymous structs or - /// unions with empty fields are always ambiguous since the empty block `{}` - /// can also be valid `impl` bodies or `fn` bodies, so we usually shouldn't - /// allow recovery of empty anonymous structs or unions. - /// - /// # Examples - /// ## Empty blocks - /// ``` - /// # #![allow(non_camel_case_types)] - /// # trait Foo {} - /// # struct union {} - /// impl union {} - /// impl Foo for union {} - /// fn foo() -> union { - /// union {} - /// } - /// ``` - /// ## Non-empty blocks - /// ```compile_fail - /// // This will be recovered as an anonymous struct - /// impl struct { foo: Foo } { - /// fn foo() {} - /// } - /// // This will be first recovered as an anonymous union (but will fail) - /// // and then parsed as a type named `union` followed by an `fn` block. - /// fn bar() -> union { - /// let _i = 0; - /// } - /// ``` - /// # A Special Case - /// In `impl` items, even if for an anonymous struct or union with - /// empty fields, we can still allow recovering it as long as it is followed by - /// some tokens such as `where`, `for`, or `{`. - /// ```compile_fail - /// impl union {} for union {} where union: union {} - /// ``` - RecoverNonEmptyOrElse(/* allow_followed_by */ Option) -> bool>), - /// Disallow parsing of anonymous structs or unions. - /// - /// Generally, we should avoid parsing or recovering anonymous structs or unions - /// if we have no information of what can appear in a block `{` ... `}` followed - /// by a type, (such as a `$ty:ty { /* any macro rules */ }` in macros), or - /// otherwise the following codes will be broken. - /// ``` - /// macro_rules! ty { - /// ($ty:ty { $($field:ident : $field_ty:ty)* }) => {}; - /// } - /// ty!(union { union: union }); - /// ty!(union {}); - /// ``` - No, -} - -impl<'a> AllowAnonStructOrUnion<'a> { - fn recover_non_empty_or_else(allow_followed_by: fn(&Parser<'a>) -> bool) -> Self { - Self::RecoverNonEmptyOrElse(Some(allow_followed_by)) - } - fn recover_non_empty() -> Self { - Self::RecoverNonEmptyOrElse(None) - } -} - /// Signals whether parsing a type should recover `->`. /// /// More specifically, when parsing a function like: @@ -213,29 +119,6 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - AllowAnonStructOrUnion::recover_non_empty(), - ) - } - - fn can_follow_first_ty_in_item_impl(&self) -> bool { - self.token.is_keyword(kw::For) || self.can_follow_second_ty_in_item_impl() - } - - fn can_follow_second_ty_in_item_impl(&self) -> bool { - self.token.is_keyword(kw::Where) || self.token == token::OpenDelim(Delimiter::Brace) - } - - pub(super) fn parse_second_ty_for_item_impl(&mut self) -> PResult<'a, P> { - self.parse_ty_common( - AllowPlus::Yes, - AllowCVariadic::No, - RecoverQPath::Yes, - RecoverReturnSign::Yes, - None, - RecoverQuestionMark::Yes, - AllowAnonStructOrUnion::recover_non_empty_or_else( - Self::can_follow_second_ty_in_item_impl, - ), ) } @@ -250,9 +133,6 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, Some(ty_params), RecoverQuestionMark::Yes, - AllowAnonStructOrUnion::recover_non_empty_or_else( - Self::can_follow_first_ty_in_item_impl, - ), ) } @@ -260,15 +140,18 @@ impl<'a> Parser<'a> { /// The difference from `parse_ty` is that this version /// allows anonymous structs and unions. pub fn parse_ty_for_field_def(&mut self) -> PResult<'a, P> { - self.parse_ty_common( - AllowPlus::Yes, - AllowCVariadic::No, - RecoverQPath::Yes, - RecoverReturnSign::Yes, - None, - RecoverQuestionMark::Yes, - AllowAnonStructOrUnion::Yes, - ) + if self.can_begin_anon_struct_or_union() { + self.parse_anon_struct_or_union() + } else { + self.parse_ty_common( + AllowPlus::Yes, + AllowCVariadic::No, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + None, + RecoverQuestionMark::Yes, + ) + } } /// Parse a type suitable for a function or function pointer parameter. @@ -282,7 +165,6 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - AllowAnonStructOrUnion::Yes, ) } @@ -300,7 +182,6 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::Yes, - AllowAnonStructOrUnion::No, ) } @@ -314,7 +195,6 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, - AllowAnonStructOrUnion::No, ) } @@ -326,8 +206,6 @@ impl<'a> Parser<'a> { RecoverReturnSign::Yes, None, RecoverQuestionMark::No, - // Disable the entry of parsing `$ty:ty` in macros - AllowAnonStructOrUnion::No, ) } @@ -340,8 +218,6 @@ impl<'a> Parser<'a> { RecoverReturnSign::OnlyFatArrow, None, RecoverQuestionMark::Yes, - // The `where` clause can be followed by a valid empty block `{}`. - AllowAnonStructOrUnion::recover_non_empty(), ) } @@ -361,8 +237,6 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, - // Return types can be followed by a valid empty block `{}`. - AllowAnonStructOrUnion::recover_non_empty(), )?; FnRetTy::Ty(ty) } else if recover_return_sign.can_recover(&self.token.kind) { @@ -377,8 +251,6 @@ impl<'a> Parser<'a> { recover_return_sign, None, RecoverQuestionMark::Yes, - // Return types can be followed by a valid empty block `{}`. - AllowAnonStructOrUnion::recover_non_empty(), )?; FnRetTy::Ty(ty) } else { @@ -394,7 +266,6 @@ impl<'a> Parser<'a> { recover_return_sign: RecoverReturnSign, ty_generics: Option<&Generics>, recover_question_mark: RecoverQuestionMark, - allow_anon_struct_or_union: AllowAnonStructOrUnion<'a>, ) -> PResult<'a, P> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); @@ -449,13 +320,7 @@ impl<'a> Parser<'a> { let (qself, path) = self.parse_qpath(PathStyle::Type)?; TyKind::Path(Some(qself), path) } else if self.check_path() { - // `union` is a weak keyword, so it can possibly be a path - if self.can_start_anon_union() - && let Some(ty_kind) = self.parse_anon_struct_or_union(lo, allow_anon_struct_or_union)? { - ty_kind - } else { - self.parse_path_start_ty(lo, allow_plus, ty_generics)? - } + self.parse_path_start_ty(lo, allow_plus, ty_generics)? } else if self.can_begin_bound() { self.parse_bare_trait_object(lo, allow_plus)? } else if self.eat(&token::DotDotDot) { @@ -469,15 +334,10 @@ impl<'a> Parser<'a> { } } } else { - if self.can_start_anon_struct() - && let Some(ty_kind) = self.parse_anon_struct_or_union(lo, allow_anon_struct_or_union)? { - ty_kind - } else { - let msg = format!("expected type, found {}", super::token_descr(&self.token)); - let mut err = self.struct_span_err(self.token.span, msg); - err.span_label(self.token.span, "expected type"); - return Err(err); - } + let msg = format!("expected type, found {}", super::token_descr(&self.token)); + let mut err = self.struct_span_err(self.token.span, msg); + err.span_label(self.token.span, "expected type"); + return Err(err); }; let span = lo.to(self.prev_token.span); @@ -494,105 +354,34 @@ impl<'a> Parser<'a> { if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) } } - /// Parse an anonymous struct or union, and returns: - /// - `Ok(Some(ty_kind))`, when successful. - /// - `Ok(None)`, when failed and recovery is diabled. - /// - `Err(err)`, a parse error which should be handled by the caller. - /// This will only happen when we are actually parsing instead of recovering - /// from an anonymous struct or union. - /// - /// See also [`AllowAnonStructOrUnion`]. - fn parse_anon_struct_or_union( - &mut self, - lo: Span, - allow_anon_struct_or_union: AllowAnonStructOrUnion<'a>, - ) -> PResult<'a, Option> { - if let AllowAnonStructOrUnion::No = allow_anon_struct_or_union { - return Ok(None); - } + /// Parse an anonymous struct or union (only for field definitions): + /// ```ignore (feature-not-ready) + /// #[repr(C)] + /// struct Foo { + /// _: struct { // anonymous struct + /// x: u32, + /// y: f64, + /// } + /// _: union { // anonymous union + /// z: u32, + /// w: f64, + /// } + /// } + /// ``` + fn parse_anon_struct_or_union(&mut self) -> PResult<'a, P> { assert!(self.token.is_keyword(kw::Union) || self.token.is_keyword(kw::Struct)); let is_union = self.token.is_keyword(kw::Union); - let snapshot = self.create_snapshot_for_diagnostic(); - let span = self.token.span; + let lo = self.token.span; self.bump(); - // FIXME(performance): the struct body will be parsed twice when we restore the snapshot. - match self.parse_anon_struct_or_union_body( - if is_union { "union" } else { "struct" }, - span, - allow_anon_struct_or_union, - ) { - Ok(fields) => match allow_anon_struct_or_union { - // Don't recover an anonymous struct or union followed by an empty block - AllowAnonStructOrUnion::RecoverNonEmptyOrElse(None) if fields.is_empty() => { - self.restore_snapshot(snapshot); - Ok(None) - } - // Don't recover an anonymous struct or union followed by an empty block, - // except it is followed by some specific tokens. - AllowAnonStructOrUnion::RecoverNonEmptyOrElse(Some(allow_followed_by)) - if fields.is_empty() && !allow_followed_by(self) => - { - self.restore_snapshot(snapshot); - Ok(None) - } - // Parse the anonymous struct or union and return. - _ => { - let span = lo.to(self.prev_token.span); - self.sess.gated_spans.gate(sym::unnamed_fields, span); - // These can be rejected during AST validation in `deny_anon_struct_or_union`. - if is_union { - Ok(Some(TyKind::AnonUnion(fields))) - } else { - Ok(Some(TyKind::AnonStruct(fields))) - } - } - }, - Err(err) => { - // Don't recover an anonymous struct or union with errors. - if let AllowAnonStructOrUnion::RecoverNonEmptyOrElse(_) = allow_anon_struct_or_union - { - err.cancel(); - self.restore_snapshot(snapshot); - Ok(None) - } else { - Err(err) - } - } - } - } - - fn parse_anon_struct_or_union_body( - &mut self, - adt_ty: &str, - ident_span: Span, - allow_anon_struct_or_union: AllowAnonStructOrUnion<'a>, - ) -> PResult<'a, ThinVec> { - self.parse_record_struct_body_common( - adt_ty, - ident_span, - false, - allow_anon_struct_or_union, - ) - .map(|(mut fields, recovered)| { - // Don't recover an anonymous struct or union if it is already recovered. - // - // If it is a recovered struct or union, we should not accept it in case that - // the non-empty block happens to be a valid block. e.g.: - // ``` - // type union = u32; - // static union: union = 0; - // fn foo() -> union { - // union - // } - // ``` - // here the `union { union }` will be recovered as a single field union. - if let AllowAnonStructOrUnion::RecoverNonEmptyOrElse(_) = allow_anon_struct_or_union && recovered { - fields.clear(); - } - fields - }) + let (fields, _recovered) = + self.parse_record_struct_body(if is_union { "union" } else { "struct" }, lo, false)?; + let span = lo.to(self.prev_token.span); + self.sess.gated_spans.gate(sym::unnamed_fields, span); + // These can be rejected during AST validation in `deny_anon_struct_or_union`. + let kind = if is_union { TyKind::AnonUnion(fields) } else { TyKind::AnonStruct(fields) }; + Ok(self.mk_ty(span, kind)) } /// Parses either: @@ -955,13 +744,8 @@ impl<'a> Parser<'a> { Ok(bounds) } - pub(super) fn can_start_anon_union(&mut self) -> bool { - self.token.is_keyword(kw::Union) - && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace)) - } - - pub(super) fn can_start_anon_struct(&mut self) -> bool { - self.token.is_keyword(kw::Struct) + pub(super) fn can_begin_anon_struct_or_union(&mut self) -> bool { + (self.token.is_keyword(kw::Struct) || self.token.is_keyword(kw::Union)) && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace)) } diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs index e6539b387a76a..192bbba5a5b3a 100644 --- a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs +++ b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs @@ -1,13 +1,6 @@ #![allow(incomplete_features)] #![feature(unnamed_fields)] -fn f() -> struct { field: u8 } {} //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields -//~^ ERROR anonymous structs are unimplemented - -fn f2(a: struct { field: u8 } ) {} //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields -//~^ ERROR anonymous structs are unimplemented - - struct F { field: struct { field: u8 }, //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields //~^ ERROR anonymous structs are unimplemented @@ -30,12 +23,7 @@ union I { _: (u8, u8), //~ ERROR unnamed fields can only have struct or union types } -struct J(struct { field: u8 }, u8); //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields -//~^ ERROR anonymous structs are unimplemented - enum K { - L(struct { field: u8 }), //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields - //~^ ERROR anonymous structs are unimplemented M { _ : struct { field: u8 }, //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields //~^ ERROR unnamed fields are not allowed outside of structs or unions @@ -46,30 +34,4 @@ enum K { } } -const L: struct { field: u8 } = 0; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields -//~^ ERROR anonymous structs are unimplemented - -static M: struct { field: u8 } = 0; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields -//~^ ERROR anonymous structs are unimplemented - -type N = struct { field: u8 }; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields -//~^ ERROR anonymous structs are unimplemented - -impl struct { field: u8 } {} //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields -//~^ ERROR anonymous structs are unimplemented - -trait Foo {} - -impl Foo for struct { field: u8 } {} //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields -//~^ ERROR anonymous structs are unimplemented - -fn main() { - let p: [struct { field: u8 }; 1]; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields - //~^ ERROR anonymous structs are unimplemented - - let q: (struct { field: u8 }, u8); //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields - //~^ ERROR anonymous structs are unimplemented - - let c = || -> struct { field: u8 } {}; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields - //~^ ERROR anonymous structs are unimplemented -} +fn main() {} diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr index 3c7383b1a2d5a..fd731766c01ff 100644 --- a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr +++ b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr @@ -1,53 +1,29 @@ error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:4:11 - | -LL | fn f() -> struct { field: u8 } {} - | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here - -error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:7:10 - | -LL | fn f2(a: struct { field: u8 } ) {} - | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here - -error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:12:12 + --> $DIR/restrict_anonymous_structs.rs:5:12 | LL | field: struct { field: u8 }, | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here error: unnamed fields can only have struct or union types - --> $DIR/restrict_anonymous_structs.rs:19:5 + --> $DIR/restrict_anonymous_structs.rs:12:5 | LL | _: (u8, u8), | ^ -------- not a struct or union error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:23:12 + --> $DIR/restrict_anonymous_structs.rs:16:12 | LL | field: struct { field: u8 }, | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here error: unnamed fields can only have struct or union types - --> $DIR/restrict_anonymous_structs.rs:30:5 + --> $DIR/restrict_anonymous_structs.rs:23:5 | LL | _: (u8, u8), | ^ -------- not a struct or union -error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:33:10 - | -LL | struct J(struct { field: u8 }, u8); - | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here - -error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:37:7 - | -LL | L(struct { field: u8 }), - | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here - error: unnamed fields are not allowed outside of structs or unions - --> $DIR/restrict_anonymous_structs.rs:40:9 + --> $DIR/restrict_anonymous_structs.rs:28:9 | LL | _ : struct { field: u8 }, | -^^^^^^^^^^^^^^^^^^^^^^^ @@ -55,168 +31,48 @@ LL | _ : struct { field: u8 }, | unnamed field declared here error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:40:13 + --> $DIR/restrict_anonymous_structs.rs:28:13 | LL | _ : struct { field: u8 }, | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here error: unnamed fields are not allowed outside of structs or unions - --> $DIR/restrict_anonymous_structs.rs:45:9 + --> $DIR/restrict_anonymous_structs.rs:33:9 | LL | _ : u8, | -^^^^^ | | | unnamed field declared here -error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:49:10 - | -LL | const L: struct { field: u8 } = 0; - | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here - -error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:52:11 - | -LL | static M: struct { field: u8 } = 0; - | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here - -error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:55:10 - | -LL | type N = struct { field: u8 }; - | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here - -error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:58:6 - | -LL | impl struct { field: u8 } {} - | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here - -error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:63:14 - | -LL | impl Foo for struct { field: u8 } {} - | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here - -error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:67:13 - | -LL | let p: [struct { field: u8 }; 1]; - | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here - -error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:70:13 - | -LL | let q: (struct { field: u8 }, u8); - | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here - -error: anonymous structs are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_structs.rs:73:19 - | -LL | let c = || -> struct { field: u8 } {}; - | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here - error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:4:11 - | -LL | fn f() -> struct { field: u8 } {} - | ^^^^^^^^^^^^^^^^^^^^ - -error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:7:10 - | -LL | fn f2(a: struct { field: u8 } ) {} - | ^^^^^^^^^^^^^^^^^^^^ - -error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:12:12 + --> $DIR/restrict_anonymous_structs.rs:5:12 | LL | field: struct { field: u8 }, | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:14:8 + --> $DIR/restrict_anonymous_structs.rs:7:8 | LL | _: struct { field: u8 }, | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:23:12 + --> $DIR/restrict_anonymous_structs.rs:16:12 | LL | field: struct { field: u8 }, | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:25:8 + --> $DIR/restrict_anonymous_structs.rs:18:8 | LL | _: struct { field: u8 }, | ^^^^^^^^^^^^^^^^^^^^ error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:33:10 - | -LL | struct J(struct { field: u8 }, u8); - | ^^^^^^^^^^^^^^^^^^^^ - -error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:37:7 - | -LL | L(struct { field: u8 }), - | ^^^^^^^^^^^^^^^^^^^^ - -error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:40:13 + --> $DIR/restrict_anonymous_structs.rs:28:13 | LL | _ : struct { field: u8 }, | ^^^^^^^^^^^^^^^^^^^^ -error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:49:10 - | -LL | const L: struct { field: u8 } = 0; - | ^^^^^^^^^^^^^^^^^^^^ - -error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:52:11 - | -LL | static M: struct { field: u8 } = 0; - | ^^^^^^^^^^^^^^^^^^^^ - -error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:55:10 - | -LL | type N = struct { field: u8 }; - | ^^^^^^^^^^^^^^^^^^^^ - -error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:58:6 - | -LL | impl struct { field: u8 } {} - | ^^^^^^^^^^^^^^^^^^^^ - -error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:63:14 - | -LL | impl Foo for struct { field: u8 } {} - | ^^^^^^^^^^^^^^^^^^^^ - -error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:67:13 - | -LL | let p: [struct { field: u8 }; 1]; - | ^^^^^^^^^^^^^^^^^^^^ - -error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:70:13 - | -LL | let q: (struct { field: u8 }, u8); - | ^^^^^^^^^^^^^^^^^^^^ - -error: anonymous structs are unimplemented - --> $DIR/restrict_anonymous_structs.rs:73:19 - | -LL | let c = || -> struct { field: u8 } {}; - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 36 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs index 8d68b106b9f4d..c69266089bba5 100644 --- a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs +++ b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs @@ -1,12 +1,6 @@ #![allow(incomplete_features)] #![feature(unnamed_fields)] -fn f() -> union { field: u8 } {} //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields -//~^ ERROR anonymous unions are unimplemented - -fn f2(a: union { field: u8 } ) {} //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields -//~^ ERROR anonymous unions are unimplemented - struct F { field: union { field: u8 }, //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields //~^ ERROR anonymous unions are unimplemented @@ -29,12 +23,7 @@ union I { _: (u8, u8), //~ ERROR unnamed fields can only have struct or union types } -struct J(union { field: u8 }, u8); //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields -//~^ ERROR anonymous unions are unimplemented - enum K { - L(union { field: u8 }), //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields - //~^ ERROR anonymous unions are unimplemented M { _ : union { field: u8 }, //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields //~^ ERROR unnamed fields are not allowed outside of structs or unions @@ -45,30 +34,4 @@ enum K { } } -const L: union { field: u8 } = 0; //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields -//~^ ERROR anonymous unions are unimplemented - -static M: union { field: u8 } = 0; //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields -//~^ ERROR anonymous unions are unimplemented - -type N = union { field: u8 }; //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields -//~^ ERROR anonymous unions are unimplemented - -impl union { field: u8 } {} //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields -// //~^ ERROR anonymous unions are unimplemented - -trait Foo {} - -impl Foo for union { field: u8 } {} //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields -//~^ ERROR anonymous unions are unimplemented - -fn main() { - let p: [union { field: u8 }; 1]; //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields - //~^ ERROR anonymous unions are unimplemented - - let q: (union { field: u8 }, u8); //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields - //~^ ERROR anonymous unions are unimplemented - - let c = || -> union { field: u8 } {}; //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields - //~^ ERROR anonymous unions are unimplemented -} +fn main() {} diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr index 473c800dde9cf..c65cad775a90c 100644 --- a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr +++ b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr @@ -1,53 +1,29 @@ error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:4:11 - | -LL | fn f() -> union { field: u8 } {} - | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here - -error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:7:10 - | -LL | fn f2(a: union { field: u8 } ) {} - | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here - -error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:11:12 + --> $DIR/restrict_anonymous_unions.rs:5:12 | LL | field: union { field: u8 }, | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here error: unnamed fields can only have struct or union types - --> $DIR/restrict_anonymous_unions.rs:18:5 + --> $DIR/restrict_anonymous_unions.rs:12:5 | LL | _: (u8, u8), | ^ -------- not a struct or union error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:22:12 + --> $DIR/restrict_anonymous_unions.rs:16:12 | LL | field: union { field: u8 }, | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here error: unnamed fields can only have struct or union types - --> $DIR/restrict_anonymous_unions.rs:29:5 + --> $DIR/restrict_anonymous_unions.rs:23:5 | LL | _: (u8, u8), | ^ -------- not a struct or union -error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:32:10 - | -LL | struct J(union { field: u8 }, u8); - | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here - -error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:36:7 - | -LL | L(union { field: u8 }), - | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here - error: unnamed fields are not allowed outside of structs or unions - --> $DIR/restrict_anonymous_unions.rs:39:9 + --> $DIR/restrict_anonymous_unions.rs:28:9 | LL | _ : union { field: u8 }, | -^^^^^^^^^^^^^^^^^^^^^^ @@ -55,168 +31,48 @@ LL | _ : union { field: u8 }, | unnamed field declared here error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:39:13 + --> $DIR/restrict_anonymous_unions.rs:28:13 | LL | _ : union { field: u8 }, | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here error: unnamed fields are not allowed outside of structs or unions - --> $DIR/restrict_anonymous_unions.rs:44:9 + --> $DIR/restrict_anonymous_unions.rs:33:9 | LL | _ : u8, | -^^^^^ | | | unnamed field declared here -error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:48:10 - | -LL | const L: union { field: u8 } = 0; - | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here - -error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:51:11 - | -LL | static M: union { field: u8 } = 0; - | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here - -error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:54:10 - | -LL | type N = union { field: u8 }; - | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here - -error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:57:6 - | -LL | impl union { field: u8 } {} - | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here - -error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:62:14 - | -LL | impl Foo for union { field: u8 } {} - | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here - -error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:66:13 - | -LL | let p: [union { field: u8 }; 1]; - | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here - -error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:69:13 - | -LL | let q: (union { field: u8 }, u8); - | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here - -error: anonymous unions are not allowed outside of unnamed struct or union fields - --> $DIR/restrict_anonymous_unions.rs:72:19 - | -LL | let c = || -> union { field: u8 } {}; - | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here - error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:4:11 - | -LL | fn f() -> union { field: u8 } {} - | ^^^^^^^^^^^^^^^^^^^ - -error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:7:10 - | -LL | fn f2(a: union { field: u8 } ) {} - | ^^^^^^^^^^^^^^^^^^^ - -error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:11:12 + --> $DIR/restrict_anonymous_unions.rs:5:12 | LL | field: union { field: u8 }, | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:13:8 + --> $DIR/restrict_anonymous_unions.rs:7:8 | LL | _: union { field: u8 }, | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:22:12 + --> $DIR/restrict_anonymous_unions.rs:16:12 | LL | field: union { field: u8 }, | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:24:8 + --> $DIR/restrict_anonymous_unions.rs:18:8 | LL | _: union { field: u8 }, | ^^^^^^^^^^^^^^^^^^^ error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:32:10 - | -LL | struct J(union { field: u8 }, u8); - | ^^^^^^^^^^^^^^^^^^^ - -error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:36:7 - | -LL | L(union { field: u8 }), - | ^^^^^^^^^^^^^^^^^^^ - -error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:39:13 + --> $DIR/restrict_anonymous_unions.rs:28:13 | LL | _ : union { field: u8 }, | ^^^^^^^^^^^^^^^^^^^ -error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:48:10 - | -LL | const L: union { field: u8 } = 0; - | ^^^^^^^^^^^^^^^^^^^ - -error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:51:11 - | -LL | static M: union { field: u8 } = 0; - | ^^^^^^^^^^^^^^^^^^^ - -error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:54:10 - | -LL | type N = union { field: u8 }; - | ^^^^^^^^^^^^^^^^^^^ - -error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:57:6 - | -LL | impl union { field: u8 } {} - | ^^^^^^^^^^^^^^^^^^^ - -error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:62:14 - | -LL | impl Foo for union { field: u8 } {} - | ^^^^^^^^^^^^^^^^^^^ - -error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:66:13 - | -LL | let p: [union { field: u8 }; 1]; - | ^^^^^^^^^^^^^^^^^^^ - -error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:69:13 - | -LL | let q: (union { field: u8 }, u8); - | ^^^^^^^^^^^^^^^^^^^ - -error: anonymous unions are unimplemented - --> $DIR/restrict_anonymous_unions.rs:72:19 - | -LL | let c = || -> union { field: u8 } {}; - | ^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 36 previous errors +error: aborting due to 12 previous errors