diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 2ad3f3ec19d57..ca7915ed17a50 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -360,10 +360,7 @@ impl<'a> Parser<'a> { let mutbl = self.parse_mutability(); self.parse_pat_ident(BindingMode::ByRef(mutbl))? } else if self.eat_keyword(kw::Box) { - // Parse `box pat` - let pat = self.parse_pat_with_range_pat(false, None)?; - self.sess.gated_spans.gate(sym::box_patterns, lo.to(self.prev_token.span)); - PatKind::Box(pat) + self.parse_pat_box()? } else if self.check_inline_const(0) { // Parse `const pat` let const_expr = self.parse_const_block(lo.to(self.token.span), true)?; @@ -915,6 +912,62 @@ impl<'a> Parser<'a> { Ok(PatKind::TupleStruct(qself, path, fields)) } + /// Are we sure this could not possibly be the start of a pattern? + /// + /// Currently, this only accounts for tokens that can follow identifiers + /// in patterns, but this can be extended as necessary. + fn isnt_pattern_start(&self) -> bool { + [ + token::Eq, + token::Colon, + token::Comma, + token::Semi, + token::At, + token::OpenDelim(Delimiter::Brace), + token::CloseDelim(Delimiter::Brace), + token::CloseDelim(Delimiter::Parenthesis), + ] + .contains(&self.token.kind) + } + + /// Parses `box pat` + fn parse_pat_box(&mut self) -> PResult<'a, PatKind> { + let box_span = self.prev_token.span; + + if self.isnt_pattern_start() { + self.struct_span_err( + self.token.span, + format!("expected pattern, found {}", super::token_descr(&self.token)), + ) + .span_note(box_span, "`box` is a reserved keyword") + .span_suggestion_verbose( + box_span.shrink_to_lo(), + "escape `box` to use it as an identifier", + "r#", + Applicability::MaybeIncorrect, + ) + .emit(); + + // We cannot use `parse_pat_ident()` since it will complain `box` + // is not an identifier. + let sub = if self.eat(&token::At) { + Some(self.parse_pat_no_top_alt(Some("binding pattern"))?) + } else { + None + }; + + Ok(PatKind::Ident( + BindingMode::ByValue(Mutability::Not), + Ident::new(kw::Box, box_span), + sub, + )) + } else { + let pat = self.parse_pat_with_range_pat(false, None)?; + self.sess.gated_spans.gate(sym::box_patterns, box_span.to(self.prev_token.span)); + Ok(PatKind::Box(pat)) + } + } + /// Parses the fields of a struct-like pattern. fn parse_pat_fields(&mut self) -> PResult<'a, (Vec, bool)> { let mut fields = Vec::new(); diff --git a/src/test/ui/parser/keyword-box-as-identifier.rs b/src/test/ui/parser/keyword-box-as-identifier.rs index 33961bb308467..2cf49b66be61c 100644 --- a/src/test/ui/parser/keyword-box-as-identifier.rs +++ b/src/test/ui/parser/keyword-box-as-identifier.rs @@ -1,3 +1,10 @@ fn main() { - let box = "foo"; //~ error: expected pattern, found `=` + let box = 0; + //~^ ERROR expected pattern, found `=` + let box: bool; + //~^ ERROR expected pattern, found `:` + let mut box = 0; + //~^ ERROR expected pattern, found `=` + let (box,) = (0,); + //~^ ERROR expected pattern, found `,` } diff --git a/src/test/ui/parser/keyword-box-as-identifier.stderr b/src/test/ui/parser/keyword-box-as-identifier.stderr index 8b185948498d8..eaa1f8003c53a 100644 --- a/src/test/ui/parser/keyword-box-as-identifier.stderr +++ b/src/test/ui/parser/keyword-box-as-identifier.stderr @@ -1,8 +1,66 @@ error: expected pattern, found `=` --> $DIR/keyword-box-as-identifier.rs:2:13 | -LL | let box = "foo"; - | ^ expected pattern +LL | let box = 0; + | ^ + | +note: `box` is a reserved keyword + --> $DIR/keyword-box-as-identifier.rs:2:9 + | +LL | let box = 0; + | ^^^ +help: escape `box` to use it as an identifier + | +LL | let r#box = 0; + | ++ + +error: expected pattern, found `:` + --> $DIR/keyword-box-as-identifier.rs:4:12 + | +LL | let box: bool; + | ^ + | +note: `box` is a reserved keyword + --> $DIR/keyword-box-as-identifier.rs:4:9 + | +LL | let box: bool; + | ^^^ +help: escape `box` to use it as an identifier + | +LL | let r#box: bool; + | ++ + +error: expected pattern, found `=` + --> $DIR/keyword-box-as-identifier.rs:6:17 + | +LL | let mut box = 0; + | ^ + | +note: `box` is a reserved keyword + --> $DIR/keyword-box-as-identifier.rs:6:13 + | +LL | let mut box = 0; + | ^^^ +help: escape `box` to use it as an identifier + | +LL | let mut r#box = 0; + | ++ + +error: expected pattern, found `,` + --> $DIR/keyword-box-as-identifier.rs:8:13 + | +LL | let (box,) = (0,); + | ^ + | +note: `box` is a reserved keyword + --> $DIR/keyword-box-as-identifier.rs:8:10 + | +LL | let (box,) = (0,); + | ^^^ +help: escape `box` to use it as an identifier + | +LL | let (r#box,) = (0,); + | ++ -error: aborting due to previous error +error: aborting due to 4 previous errors