diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index 13645e7144a9b..2c9d4004226c7 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -9,6 +9,7 @@ use syntax::ast::{PathSegment, IsAuto, Constness, IsAsync, Unsafety, Defaultness use syntax::ast::{Visibility, VisibilityKind, Mutability, FnHeader, ForeignItem, ForeignItemKind}; use syntax::ast::{Ty, TyKind, Generics, TraitRef, EnumDef, VariantData, StructField}; use syntax::ast::{Mac, MacDelimiter, Block, BindingMode, FnDecl, FnSig, SelfKind, Param}; +use syntax::print::pprust; use syntax::ptr::P; use syntax::ThinVec; use syntax::token; @@ -16,7 +17,7 @@ use syntax::tokenstream::{TokenTree, TokenStream}; use syntax::source_map::{self, respan, Span}; use syntax::struct_span_err; use syntax_pos::BytePos; -use syntax_pos::symbol::{kw, sym}; +use syntax_pos::symbol::{kw, sym, Symbol}; use rustc_error_codes::*; @@ -1336,11 +1337,17 @@ impl<'a> Parser<'a> { /// Parses the part of an enum declaration following the `{`. fn parse_enum_def(&mut self, _generics: &Generics) -> PResult<'a, EnumDef> { let mut variants = Vec::new(); + // FIXME: Consider using `parse_delim_comma_seq`. + // We could then remove eating comma in `recover_nested_adt_item`. while self.token != token::CloseDelim(token::Brace) { let variant_attrs = self.parse_outer_attributes()?; let vlo = self.token.span; let vis = self.parse_visibility(FollowedByType::No)?; + if !self.recover_nested_adt_item(kw::Enum)? { + // Item already parsed, we need to skip this variant. + continue + } let ident = self.parse_ident()?; let struct_def = if self.check(&token::OpenDelim(token::Brace)) { @@ -1742,6 +1749,33 @@ impl<'a> Parser<'a> { ).emit(); } + /// Checks if current token is one of tokens which cannot be nested like `kw::Enum`. In case + /// it is, we try to parse the item and report error about nested types. + fn recover_nested_adt_item(&mut self, keyword: Symbol) -> PResult<'a, bool> { + if self.token.is_keyword(kw::Enum) || + self.token.is_keyword(kw::Struct) || + self.token.is_keyword(kw::Union) + { + let kw_token = self.token.clone(); + let kw_str = pprust::token_to_string(&kw_token); + let item = self.parse_item()?; + self.eat(&token::Comma); + + self.struct_span_err( + kw_token.span, + &format!("`{}` definition cannot be nested inside `{}`", kw_str, keyword), + ).span_suggestion( + item.unwrap().span, + &format!("consider creating a new `{}` definition instead of nesting", kw_str), + String::new(), + Applicability::MaybeIncorrect, + ).emit(); + // We successfully parsed the item but we must inform the caller about nested problem. + return Ok(false) + } + Ok(true) + } + fn mk_item(&self, span: Span, ident: Ident, kind: ItemKind, vis: Visibility, attrs: Vec) -> P { P(Item { diff --git a/src/test/ui/enum/nested-enum.rs b/src/test/ui/enum/nested-enum.rs new file mode 100644 index 0000000000000..80957b8a14c23 --- /dev/null +++ b/src/test/ui/enum/nested-enum.rs @@ -0,0 +1,8 @@ +enum Foo { + enum Bar { Baz }, //~ ERROR `enum` definition cannot be nested inside `enum` + struct Quux { field: u8 }, //~ ERROR `struct` definition cannot be nested inside `enum` + union Wibble { field: u8 }, //~ ERROR `union` definition cannot be nested inside `enum` + Bat, +} + +fn main() { } diff --git a/src/test/ui/enum/nested-enum.stderr b/src/test/ui/enum/nested-enum.stderr new file mode 100644 index 0000000000000..7d6f57e88a826 --- /dev/null +++ b/src/test/ui/enum/nested-enum.stderr @@ -0,0 +1,26 @@ +error: `enum` definition cannot be nested inside `enum` + --> $DIR/nested-enum.rs:2:5 + | +LL | enum Bar { Baz }, + | ^^^^------------ + | | + | help: consider creating a new `enum` definition instead of nesting + +error: `struct` definition cannot be nested inside `enum` + --> $DIR/nested-enum.rs:3:5 + | +LL | struct Quux { field: u8 }, + | ^^^^^^------------------- + | | + | help: consider creating a new `struct` definition instead of nesting + +error: `union` definition cannot be nested inside `enum` + --> $DIR/nested-enum.rs:4:5 + | +LL | union Wibble { field: u8 }, + | ^^^^^--------------------- + | | + | help: consider creating a new `union` definition instead of nesting + +error: aborting due to 3 previous errors +