diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 10c73fd64bc19..a6f92294b0bec 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -765,12 +765,26 @@ impl<'a> Parser<'a> { if self.eat(&token::Colon) { self.parse_generic_bounds(None)? } else { Vec::new() }; generics.where_clause = self.parse_where_clause()?; - let default = if self.eat(&token::Eq) { Some(self.parse_ty()?) } else { None }; + let default = + if self.eat(&token::Eq) { Some(self.parse_ty_recover_anon_adt()?) } else { None }; self.expect_semi()?; Ok((ident, ItemKind::TyAlias(Box::new(TyAliasKind(def, generics, bounds, default))))) } + /// Parses a type. If we encounter an anonymous `union` or `struct`, we parse it, but still + /// allow `static C: union = union::val;` to be parsed correctly. The anonymous ADTs will be + /// disallowed elsewhere. + fn parse_ty_recover_anon_adt(&mut self) -> PResult<'a, P> { + if (self.token.is_keyword(kw::Union) | self.token.is_keyword(kw::Struct)) + && self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) + { + self.parse_ty_allow_anon_adt() + } else { + self.parse_ty() + } + } + /// Parses a `UseTree`. /// /// ```text @@ -1060,13 +1074,14 @@ impl<'a> Parser<'a> { // Parse the type of a `const` or `static mut?` item. // That is, the `":" $ty` fragment. let ty = if self.eat(&token::Colon) { - self.parse_ty()? + self.parse_ty_recover_anon_adt()? } else { self.recover_missing_const_type(id, m) }; let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None }; self.expect_semi()?; + Ok((id, ty, expr)) } @@ -1438,7 +1453,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, FieldDef> { let name = self.parse_field_ident(adt_ty, lo)?; self.expect_field_ty_separator()?; - let ty = self.parse_ty()?; + let ty = self.parse_ty_allow_anon_adt()?; if self.token.kind == token::Eq { self.bump(); let const_expr = self.parse_anon_const_expr()?; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 299fc916ac97f..abf62aeb0dab0 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -37,6 +37,12 @@ pub(super) enum AllowPlus { No, } +#[derive(Copy, Clone, PartialEq)] +pub(super) enum AllowAnonymousType { + Yes, + No, +} + #[derive(PartialEq)] pub(super) enum RecoverQPath { Yes, @@ -98,9 +104,19 @@ impl<'a> Parser<'a> { AllowCVariadic::No, RecoverQPath::Yes, RecoverReturnSign::Yes, + AllowAnonymousType::No, ) } + pub fn parse_ty_allow_anon_adt(&mut self) -> PResult<'a, P> { + self.parse_ty_common( + AllowPlus::Yes, + AllowCVariadic::No, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + AllowAnonymousType::Yes, + ) + } /// Parse a type suitable for a function or function pointer parameter. /// The difference from `parse_ty` is that this version allows `...` /// (`CVarArgs`) at the top level of the type. @@ -110,6 +126,7 @@ impl<'a> Parser<'a> { AllowCVariadic::Yes, RecoverQPath::Yes, RecoverReturnSign::Yes, + AllowAnonymousType::No, ) } @@ -125,6 +142,7 @@ impl<'a> Parser<'a> { AllowCVariadic::No, RecoverQPath::Yes, RecoverReturnSign::Yes, + AllowAnonymousType::No, ) } @@ -135,6 +153,7 @@ impl<'a> Parser<'a> { AllowCVariadic::Yes, RecoverQPath::Yes, RecoverReturnSign::OnlyFatArrow, + AllowAnonymousType::No, ) } @@ -152,6 +171,7 @@ impl<'a> Parser<'a> { AllowCVariadic::No, recover_qpath, recover_return_sign, + AllowAnonymousType::No, )?; FnRetTy::Ty(ty) } else if recover_return_sign.can_recover(&self.token.kind) { @@ -171,6 +191,7 @@ impl<'a> Parser<'a> { AllowCVariadic::No, recover_qpath, recover_return_sign, + AllowAnonymousType::No, )?; FnRetTy::Ty(ty) } else { @@ -184,6 +205,7 @@ impl<'a> Parser<'a> { allow_c_variadic: AllowCVariadic, recover_qpath: RecoverQPath, recover_return_sign: RecoverReturnSign, + allow_anonymous: AllowAnonymousType, ) -> PResult<'a, P> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); @@ -226,19 +248,11 @@ impl<'a> Parser<'a> { } } else if self.eat_keyword(kw::Impl) { self.parse_impl_ty(&mut impl_dyn_multi)? - } else if self.token.is_keyword(kw::Union) + } else if allow_anonymous == AllowAnonymousType::Yes + && (self.token.is_keyword(kw::Union) | self.token.is_keyword(kw::Struct)) && self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) { - self.bump(); - let (fields, recovered) = self.parse_record_struct_body("union")?; - let span = lo.to(self.prev_token.span); - self.sess.gated_spans.gate(sym::unnamed_fields, span); - TyKind::AnonymousUnion(fields, recovered) - } else if self.eat_keyword(kw::Struct) { - let (fields, recovered) = self.parse_record_struct_body("struct")?; - let span = lo.to(self.prev_token.span); - self.sess.gated_spans.gate(sym::unnamed_fields, span); - TyKind::AnonymousStruct(fields, recovered) + 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() { @@ -263,7 +277,27 @@ impl<'a> Parser<'a> { 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); - return Err(err); + + if allow_anonymous == AllowAnonymousType::No + && (self.token.is_keyword(kw::Union) || self.token.is_keyword(kw::Struct)) + && self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) + { + // Recover the parser from anonymous types anywhere other than field types. + let snapshot = self.clone(); + match self.parse_anonymous_ty(lo) { + Ok(ty) => { + err.delay_as_bug(); + ty + } + Err(mut snapshot_err) => { + snapshot_err.cancel(); + *self = snapshot; + return Err(err); + } + } + } else { + return Err(err); + } }; let span = lo.to(self.prev_token.span); @@ -275,6 +309,22 @@ impl<'a> Parser<'a> { self.maybe_recover_from_bad_qpath(ty, allow_qpath_recovery) } + fn parse_anonymous_ty(&mut self, lo: Span) -> PResult<'a, TyKind> { + let is_union = self.token.is_keyword(kw::Union); + self.bump(); + self.parse_record_struct_body(if is_union { "union" } else { "struct" }).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. diff --git a/src/test/pretty/anonymous-types.rs b/src/test/pretty/anonymous-types.rs index 5ff452e8e43c4..983a879ae1f97 100644 --- a/src/test/pretty/anonymous-types.rs +++ b/src/test/pretty/anonymous-types.rs @@ -16,9 +16,4 @@ struct Foo { e: f32, } -type A = - struct { - field: u8, - }; - fn main() { } diff --git a/src/test/ui/parser/issue-88583-union-as-ident.rs b/src/test/ui/parser/issue-88583-union-as-ident.rs new file mode 100644 index 0000000000000..b3d66d46b1d4b --- /dev/null +++ b/src/test/ui/parser/issue-88583-union-as-ident.rs @@ -0,0 +1,15 @@ +// check-pass + +#![allow(non_camel_case_types)] + +struct union; + +impl union { + pub fn new() -> Self { + union { } + } +} + +fn main() { + let _u = union::new(); +}