about summary refs log tree commit diff
path: root/compiler/rustc_parse
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2022-06-08 13:32:21 -0700
committerGitHub <noreply@github.com>2022-06-08 13:32:21 -0700
commitf12a1c23bc14788fd68a7f359290e9ab7759d1a1 (patch)
tree3a6841f5c1456ac7bbef49192bc9db755250eac7 /compiler/rustc_parse
parente0409200d978a377d4646e8da7be60483206ed64 (diff)
parentc1b1ec7e071b0f08985787a9a897bf78bc286e7e (diff)
downloadrust-f12a1c23bc14788fd68a7f359290e9ab7759d1a1.tar.gz
rust-f12a1c23bc14788fd68a7f359290e9ab7759d1a1.zip
Rollup merge of #97857 - ChayimFriedman2:box-identifier-help, r=compiler-errors
Suggest escaping `box` as identifier

Fixes #97810.
Diffstat (limited to 'compiler/rustc_parse')
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs61
1 files changed, 57 insertions, 4 deletions
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 2ad3f3ec19d..ca7915ed17a 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<PatField>, bool)> {
         let mut fields = Vec::new();