about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLieselotte <52315535+she3py@users.noreply.github.com>2024-01-03 15:27:58 +0100
committerLieselotte <52315535+she3py@users.noreply.github.com>2024-01-03 15:27:58 +0100
commit4e0baddbbf1a6ceadda28f2eb7edc188822942f3 (patch)
tree9c5fab2d6c7cd2a9c2be4312ac28dbb238ac5337
parente51e98dde6a60637b6a71b8105245b629ac3fe77 (diff)
downloadrust-4e0baddbbf1a6ceadda28f2eb7edc188822942f3.tar.gz
rust-4e0baddbbf1a6ceadda28f2eb7edc188822942f3.zip
Recover parentheses in range patterns
Co-authored-by: León Orell Valerian Liehr <me@fmease.dev>
-rw-r--r--compiler/rustc_parse/messages.ftl3
-rw-r--r--compiler/rustc_parse/src/errors.rs21
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs56
-rw-r--r--tests/ui/half-open-range-patterns/range_pat_interactions2.rs3
-rw-r--r--tests/ui/half-open-range-patterns/range_pat_interactions2.stderr17
-rw-r--r--tests/ui/parser/pat-recover-ranges.rs19
-rw-r--r--tests/ui/parser/pat-recover-ranges.stderr50
7 files changed, 151 insertions, 18 deletions
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index c6bddbfacd6..aa8c8538b54 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -767,6 +767,9 @@ parse_unexpected_if_with_if = unexpected `if` in the condition expression
 parse_unexpected_lifetime_in_pattern = unexpected lifetime `{$symbol}` in pattern
     .suggestion = remove the lifetime
 
+parse_unexpected_paren_in_range_pat = range pattern bounds cannot have parentheses
+parse_unexpected_paren_in_range_pat_sugg = remove these parentheses
+
 parse_unexpected_parentheses_in_for_head = unexpected parentheses surrounding `for` loop head
     .suggestion = remove parentheses in `for` loop
 
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index e276b34ca37..d67ae149344 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -2379,6 +2379,27 @@ pub(crate) struct ExpectedCommaAfterPatternField {
 }
 
 #[derive(Diagnostic)]
+#[diag(parse_unexpected_paren_in_range_pat)]
+pub(crate) struct UnexpectedParenInRangePat {
+    #[primary_span]
+    pub span: Vec<Span>,
+    #[subdiagnostic]
+    pub sugg: UnexpectedParenInRangePatSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(
+    parse_unexpected_paren_in_range_pat_sugg,
+    applicability = "machine-applicable"
+)]
+pub(crate) struct UnexpectedParenInRangePatSugg {
+    #[suggestion_part(code = "")]
+    pub start_span: Span,
+    #[suggestion_part(code = "")]
+    pub end_span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(parse_return_types_use_thin_arrow)]
 pub(crate) struct ReturnTypesUseThinArrow {
     #[primary_span]
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?
diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions2.rs b/tests/ui/half-open-range-patterns/range_pat_interactions2.rs
index 4615ebd688a..0e96cfe7858 100644
--- a/tests/ui/half-open-range-patterns/range_pat_interactions2.rs
+++ b/tests/ui/half-open-range-patterns/range_pat_interactions2.rs
@@ -8,8 +8,7 @@ fn main() {
     for x in -9 + 1..=(9 - 2) {
         match x as i32 {
             0..=(5+1) => errors_only.push(x),
-            //~^ error: inclusive range with no end
-            //~| error: expected one of `=>`, `if`, or `|`, found `(`
+            //~^ error: expected `)`, found `+`
             1 | -3..0 => first_or.push(x),
             y @ (0..5 | 6) => or_two.push(y),
             y @ 0..const { 5 + 1 } => assert_eq!(y, 5),
diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr
index 13a5542a474..a54f29a3b32 100644
--- a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr
+++ b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr
@@ -1,17 +1,8 @@
-error[E0586]: inclusive range with no end
-  --> $DIR/range_pat_interactions2.rs:10:14
+error: expected `)`, found `+`
+  --> $DIR/range_pat_interactions2.rs:10:19
    |
 LL |             0..=(5+1) => errors_only.push(x),
-   |              ^^^ help: use `..` instead
-   |
-   = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
-
-error: expected one of `=>`, `if`, or `|`, found `(`
-  --> $DIR/range_pat_interactions2.rs:10:17
-   |
-LL |             0..=(5+1) => errors_only.push(x),
-   |                 ^ expected one of `=>`, `if`, or `|`
+   |                   ^ expected `)`
 
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0586`.
diff --git a/tests/ui/parser/pat-recover-ranges.rs b/tests/ui/parser/pat-recover-ranges.rs
new file mode 100644
index 00000000000..65a6fc6fe21
--- /dev/null
+++ b/tests/ui/parser/pat-recover-ranges.rs
@@ -0,0 +1,19 @@
+fn main() {
+    match -1 {
+        0..=1 => (),
+        0..=(1) => (),
+        //~^ error: range pattern bounds cannot have parentheses
+        (-12)..=4 => (),
+        //~^ error: range pattern bounds cannot have parentheses
+        (0)..=(-4) => (),
+        //~^ error: range pattern bounds cannot have parentheses
+        //~| error: range pattern bounds cannot have parentheses
+    };
+}
+
+macro_rules! m {
+    ($pat:pat) => {};
+    (($s:literal)..($e:literal)) => {};
+}
+
+m!((7)..(7));
diff --git a/tests/ui/parser/pat-recover-ranges.stderr b/tests/ui/parser/pat-recover-ranges.stderr
new file mode 100644
index 00000000000..0d722b5aa95
--- /dev/null
+++ b/tests/ui/parser/pat-recover-ranges.stderr
@@ -0,0 +1,50 @@
+error: range pattern bounds cannot have parentheses
+  --> $DIR/pat-recover-ranges.rs:4:13
+   |
+LL |         0..=(1) => (),
+   |             ^ ^
+   |
+help: remove these parentheses
+   |
+LL -         0..=(1) => (),
+LL +         0..=1 => (),
+   |
+
+error: range pattern bounds cannot have parentheses
+  --> $DIR/pat-recover-ranges.rs:6:9
+   |
+LL |         (-12)..=4 => (),
+   |         ^   ^
+   |
+help: remove these parentheses
+   |
+LL -         (-12)..=4 => (),
+LL +         -12..=4 => (),
+   |
+
+error: range pattern bounds cannot have parentheses
+  --> $DIR/pat-recover-ranges.rs:8:9
+   |
+LL |         (0)..=(-4) => (),
+   |         ^ ^
+   |
+help: remove these parentheses
+   |
+LL -         (0)..=(-4) => (),
+LL +         0..=(-4) => (),
+   |
+
+error: range pattern bounds cannot have parentheses
+  --> $DIR/pat-recover-ranges.rs:8:15
+   |
+LL |         (0)..=(-4) => (),
+   |               ^  ^
+   |
+help: remove these parentheses
+   |
+LL -         (0)..=(-4) => (),
+LL +         (0)..=-4 => (),
+   |
+
+error: aborting due to 4 previous errors
+