about summary refs log tree commit diff
path: root/compiler/rustc_parse/src/parser
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2024-01-04 08:33:24 +0100
committerGitHub <noreply@github.com>2024-01-04 08:33:24 +0100
commit3325ba640c3459fe027a33ad307dd5973a5a7529 (patch)
tree995da59d9f4182de76d7a4e2cd63c6ad351db81a /compiler/rustc_parse/src/parser
parent6526b164a3d2fe89fcce2df243cc36cbfef272c4 (diff)
parent4e0baddbbf1a6ceadda28f2eb7edc188822942f3 (diff)
downloadrust-3325ba640c3459fe027a33ad307dd5973a5a7529.tar.gz
rust-3325ba640c3459fe027a33ad307dd5973a5a7529.zip
Rollup merge of #119397 - ShE3py:pat-range-paren-recovery, r=fmease
Recover parentheses in range patterns

Before:
```rs
match n {
    (0).. => (),
    _ => ()
}
```
```
error: expected one of `=>`, `if`, or `|`, found `..`
 --> src/lib.rs:3:12
  |
3 |         (0).. => (),
  |            ^^ expected one of `=>`, `if`, or `|`
  ```
 After:
```
error: range pattern bounds cannot have parentheses
 --> main.rs:3:5
  |
3 |     (0).. => (),
  |     ^ ^
  |
help: remove these parentheses
  |
3 -     (0).. => (),
3 +     0.. => (),
  |
```

This sets the groundwork for #118625, which will extend the recovery to expressions like `(0 + 1)..` where users may tend to add parentheses to avoid dealing with precedence.

---

```@rustbot``` label +A-parser +A-patterns +A-diagnostics
Diffstat (limited to 'compiler/rustc_parse/src/parser')
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs56
1 files changed, 53 insertions, 3 deletions
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index afbc2537578..7d17b1d4c4d 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -6,7 +6,8 @@ use crate::errors::{
     InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern,
     PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern,
     SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg,
-    TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam,
+    TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat,
+    UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam,
     UnexpectedVertVertInPattern,
 };
 use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
@@ -579,6 +580,8 @@ impl<'a> Parser<'a> {
 
     /// Parse a tuple or parenthesis pattern.
     fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> {
+        let open_paren = self.token.span;
+
         let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| {
             p.parse_pat_allow_top_alt(
                 None,
@@ -591,7 +594,29 @@ impl<'a> Parser<'a> {
         // Here, `(pat,)` is a tuple pattern.
         // For backward compatibility, `(..)` is a tuple pattern as well.
         Ok(if fields.len() == 1 && !(trailing_comma || fields[0].is_rest()) {
-            PatKind::Paren(fields.into_iter().next().unwrap())
+            let pat = fields.into_iter().next().unwrap();
+            let close_paren = self.prev_token.span;
+
+            match &pat.kind {
+                // recover ranges with parentheses around the `(start)..`
+                PatKind::Lit(begin)
+                    if self.may_recover()
+                        && let Some(form) = self.parse_range_end() =>
+                {
+                    self.dcx().emit_err(UnexpectedParenInRangePat {
+                        span: vec![open_paren, close_paren],
+                        sugg: UnexpectedParenInRangePatSugg {
+                            start_span: open_paren,
+                            end_span: close_paren,
+                        },
+                    });
+
+                    self.parse_pat_range_begin_with(begin.clone(), form)?
+                }
+
+                // (pat) with optional parentheses
+                _ => PatKind::Paren(pat),
+            }
         } else {
             PatKind::Tuple(fields)
         })
@@ -794,11 +819,21 @@ impl<'a> Parser<'a> {
                 || t.can_begin_literal_maybe_minus() // e.g. `42`.
                 || t.is_whole_expr()
                 || t.is_lifetime() // recover `'a` instead of `'a'`
+                || (self.may_recover() // recover leading `(`
+                    && t.kind == token::OpenDelim(Delimiter::Parenthesis)
+                    && self.look_ahead(dist + 1, |t| t.kind != token::OpenDelim(Delimiter::Parenthesis))
+                    && self.is_pat_range_end_start(dist + 1))
             })
     }
 
+    /// Parse a range pattern end bound
     fn parse_pat_range_end(&mut self) -> PResult<'a, P<Expr>> {
-        if self.check_inline_const(0) {
+        // recover leading `(`
+        let open_paren = (self.may_recover()
+            && self.eat_noexpect(&token::OpenDelim(Delimiter::Parenthesis)))
+        .then_some(self.prev_token.span);
+
+        let bound = if self.check_inline_const(0) {
             self.parse_const_block(self.token.span, true)
         } else if self.check_path() {
             let lo = self.token.span;
@@ -814,7 +849,22 @@ impl<'a> Parser<'a> {
             Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path)))
         } else {
             self.parse_literal_maybe_minus()
+        }?;
+
+        // recover trailing `)`
+        if let Some(open_paren) = open_paren {
+            self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
+
+            self.dcx().emit_err(UnexpectedParenInRangePat {
+                span: vec![open_paren, self.prev_token.span],
+                sugg: UnexpectedParenInRangePatSugg {
+                    start_span: open_paren,
+                    end_span: self.prev_token.span,
+                },
+            });
         }
+
+        Ok(bound)
     }
 
     /// Is this the start of a pattern beginning with a path?