]> git.lizzy.rs Git - rust.git/commitdiff
Recover on '..X' / '..=X' / '...X' range patterns.
authorMazdak Farrokhzad <twingoow@gmail.com>
Tue, 9 Jul 2019 01:55:13 +0000 (03:55 +0200)
committerMazdak Farrokhzad <twingoow@gmail.com>
Sun, 28 Jul 2019 04:53:38 +0000 (06:53 +0200)
src/libsyntax/parse/parser.rs

index 04e4dcfcb1aedbb24a73befd2603d0496faf3c94..e1bbd7631e5ea32b65973ec7d8622962aae43809 100644 (file)
@@ -3730,6 +3730,13 @@ fn parse_pat_range_end(&mut self) -> PResult<'a, P<Expr>> {
         }
     }
 
+    /// Is the current token suitable as the start of a range patterns end?
+    fn is_pat_range_end_start(&self) -> bool {
+        self.token.is_path_start() // e.g. `MY_CONST`;
+            || self.token == token::Dot // e.g. `.5` for recovery;
+            || self.token.can_begin_literal_or_bool() // e.g. `42`.
+    }
+
     // helper function to decide whether to parse as ident binding or to try to do
     // something more complex like range patterns
     fn parse_as_ident(&mut self) -> bool {
@@ -3802,6 +3809,26 @@ pub fn parse_pat(&mut self, expected: Option<&'static str>) -> PResult<'a, P<Pat
         self.parse_pat_with_range_pat(true, expected)
     }
 
+    /// Parse a range-to pattern, e.g. `..X` and `..=X` for recovery.
+    fn parse_pat_range_to(&mut self, re: RangeEnd, form: &str) -> PResult<'a, PatKind> {
+        let lo = self.prev_span;
+        let end = self.parse_pat_range_end()?;
+        let range_span = lo.to(end.span);
+        let begin = self.mk_expr(range_span, ExprKind::Err, ThinVec::new());
+
+        self.diagnostic()
+            .struct_span_err(range_span, &format!("`{}X` range patterns are not supported", form))
+            .span_suggestion(
+                range_span,
+                "try using the minimum value for the type",
+                format!("MIN{}{}", form, pprust::expr_to_string(&end)),
+                Applicability::HasPlaceholders,
+            )
+            .emit();
+
+        Ok(PatKind::Range(begin, end, respan(lo, re)))
+    }
+
     /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
     /// allowed).
     fn parse_pat_with_range_pat(
@@ -3843,9 +3870,24 @@ fn parse_pat_with_range_pat(
                 pat = PatKind::Slice(slice);
             }
             token::DotDot => {
-                // Parse `..`.
                 self.bump();
-                pat = PatKind::Rest;
+                pat = if self.is_pat_range_end_start() {
+                    // Parse `..42` for recovery.
+                    self.parse_pat_range_to(RangeEnd::Excluded, "..")?
+                } else {
+                    // A rest pattern `..`.
+                    PatKind::Rest
+                };
+            }
+            token::DotDotEq => {
+                // Parse `..=42` for recovery.
+                self.bump();
+                pat = self.parse_pat_range_to(RangeEnd::Included(RangeSyntax::DotDotEq), "..=")?;
+            }
+            token::DotDotDot => {
+                // Parse `...42` for recovery.
+                self.bump();
+                pat = self.parse_pat_range_to(RangeEnd::Included(RangeSyntax::DotDotDot), "...")?;
             }
             // At this point, token != &, &&, (, [
             _ => if self.eat_keyword(kw::Underscore) {
@@ -3915,8 +3957,7 @@ fn parse_pat_with_range_pat(
                         let begin = self.mk_expr(span, ExprKind::Path(qself, path), ThinVec::new());
                         self.bump();
                         let end = self.parse_pat_range_end()?;
-                        let op = Spanned { span: op_span, node: end_kind };
-                        pat = PatKind::Range(begin, end, op);
+                        pat = PatKind::Range(begin, end, respan(op_span, end_kind));
                     }
                     token::OpenDelim(token::Brace) => {
                         if qself.is_some() {
@@ -3966,8 +4007,7 @@ fn parse_pat_with_range_pat(
                                         on a range-operator token")
                             };
                             let end = self.parse_pat_range_end()?;
-                            let op = Spanned { span: op_span, node: end_kind };
-                            pat = PatKind::Range(begin, end, op);
+                            pat = PatKind::Range(begin, end, respan(op_span, end_kind))
                         } else {
                             pat = PatKind::Lit(begin);
                         }