]> git.lizzy.rs Git - rust.git/commitdiff
syntax: Rewrite parsing of patterns
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Tue, 31 Mar 2015 06:10:11 +0000 (09:10 +0300)
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Thu, 2 Apr 2015 10:34:26 +0000 (13:34 +0300)
src/libsyntax/parse/parser.rs
src/test/compile-fail/issue-22426-1.rs
src/test/compile-fail/issue-22426-2.rs
src/test/compile-fail/issue-22426-3.rs
src/test/parse-fail/issue-22647.rs
src/test/parse-fail/issue-22712.rs
src/test/parse-fail/match-vec-invalid.rs
src/test/parse-fail/omitted-arg-in-item-fn.rs
src/test/parse-fail/removed-syntax-larrow-init.rs
src/test/parse-fail/struct-literal-in-match-discriminant.rs

index c721624323923e18a999552bcb6b825af5af48d9..cb7b93d2ef24b7c3c76a5bb2cdf457bde80152bc 100644 (file)
@@ -40,8 +40,8 @@
 use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, MatchSource};
 use ast::{MutTy, BiMul, Mutability};
 use ast::{MethodImplItem, NamedField, UnNeg, NoReturn, UnNot};
-use ast::{Pat, PatEnum, PatIdent, PatLit, PatRange, PatRegion, PatStruct};
-use ast::{PatTup, PatBox, PatWild, PatWildMulti, PatWildSingle};
+use ast::{Pat, PatBox, PatEnum, PatIdent, PatLit, PatMac, PatRange, PatRegion};
+use ast::{PatStruct, PatTup, PatVec, PatWild, PatWildMulti, PatWildSingle};
 use ast::{PolyTraitRef, QSelf};
 use ast::{Return, BiShl, BiShr, Stmt, StmtDecl};
 use ast::{StmtExpr, StmtSemi, StmtMac, StructDef, StructField};
     flags Restrictions: u8 {
         const UNRESTRICTED                  = 0b0000,
         const RESTRICTION_STMT_EXPR         = 0b0001,
-        const RESTRICTION_NO_BAR_OP         = 0b0010,
-        const RESTRICTION_NO_STRUCT_LITERAL = 0b0100,
+        const RESTRICTION_NO_STRUCT_LITERAL = 0b0010,
     }
 }
 
-
 type ItemInfo = (Ident, Item_, Option<Vec<Attribute> >);
 
 /// How to parse a path. There are four different kinds of paths, all of which
@@ -2603,13 +2601,6 @@ pub fn parse_binops(&mut self) -> P<Expr> {
     pub fn parse_more_binops(&mut self, lhs: P<Expr>, min_prec: usize) -> P<Expr> {
         if self.expr_is_complete(&*lhs) { return lhs; }
 
-        // Prevent dynamic borrow errors later on by limiting the
-        // scope of the borrows.
-        if self.token == token::BinOp(token::Or) &&
-            self.restrictions.contains(RESTRICTION_NO_BAR_OP) {
-            return lhs;
-        }
-
         self.expected_tokens.push(TokenType::Operator);
 
         let cur_op_span = self.span;
@@ -2956,6 +2947,22 @@ fn parse_pats(&mut self) -> Vec<P<Pat>> {
         };
     }
 
+    fn parse_pat_tuple_elements(&mut self) -> Vec<P<Pat>> {
+        let mut fields = vec![];
+        if !self.check(&token::CloseDelim(token::Paren)) {
+            fields.push(self.parse_pat());
+            if self.look_ahead(1, |t| *t != token::CloseDelim(token::Paren)) {
+                while self.eat(&token::Comma) && !self.check(&token::CloseDelim(token::Paren)) {
+                    fields.push(self.parse_pat());
+                }
+            }
+            if fields.len() == 1 {
+                self.expect(&token::Comma);
+            }
+        }
+        fields
+    }
+
     fn parse_pat_vec_elements(
         &mut self,
     ) -> (Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>) {
@@ -3087,250 +3094,145 @@ fn parse_pat_fields(&mut self) -> (Vec<codemap::Spanned<ast::FieldPat>> , bool)
         return (fields, etc);
     }
 
+    fn parse_pat_range_end(&mut self) -> P<Expr> {
+        if self.is_path_start() {
+            let lo = self.span.lo;
+            let path = self.parse_path(LifetimeAndTypesWithColons);
+            let hi = self.last_span.hi;
+            self.mk_expr(lo, hi, ExprPath(None, path))
+        } else {
+            self.parse_literal_maybe_minus()
+        }
+    }
+
+    fn is_path_start(&self) -> bool {
+        (self.token == token::ModSep || self.token.is_ident() || self.token.is_path())
+            && !self.token.is_keyword(keywords::True) && !self.token.is_keyword(keywords::False)
+    }
+
     /// Parse a pattern.
     pub fn parse_pat(&mut self) -> P<Pat> {
         maybe_whole!(self, NtPat);
 
         let lo = self.span.lo;
-        let mut hi;
         let pat;
         match self.token {
-            // parse _
           token::Underscore => {
+            // Parse _
             self.bump();
             pat = PatWild(PatWildSingle);
-            hi = self.last_span.hi;
-            return P(ast::Pat {
-                id: ast::DUMMY_NODE_ID,
-                node: pat,
-                span: mk_sp(lo, hi)
-            })
           }
           token::BinOp(token::And) | token::AndAnd => {
-            // parse &pat and &mut pat
-            let lo = self.span.lo;
+            // Parse &pat / &mut pat
             self.expect_and();
-            let mutability = if self.eat_keyword(keywords::Mut) {
-                ast::MutMutable
-            } else {
-                ast::MutImmutable
-            };
-            let sub = self.parse_pat();
-            pat = PatRegion(sub, mutability);
-            hi = self.last_span.hi;
-            return P(ast::Pat {
-                id: ast::DUMMY_NODE_ID,
-                node: pat,
-                span: mk_sp(lo, hi)
-            })
+            let mutbl = self.parse_mutability();
+            let subpat = self.parse_pat();
+            pat = PatRegion(subpat, mutbl);
           }
           token::OpenDelim(token::Paren) => {
-            // parse (pat,pat,pat,...) as tuple
+            // Parse (pat,pat,pat,...) as tuple pattern
             self.bump();
-            if self.check(&token::CloseDelim(token::Paren)) {
-                self.bump();
-                pat = PatTup(vec![]);
-            } else {
-                let mut fields = vec!(self.parse_pat());
-                if self.look_ahead(1, |t| *t != token::CloseDelim(token::Paren)) {
-                    while self.check(&token::Comma) {
-                        self.bump();
-                        if self.check(&token::CloseDelim(token::Paren)) { break; }
-                        fields.push(self.parse_pat());
-                    }
-                }
-                if fields.len() == 1 { self.expect(&token::Comma); }
-                self.expect(&token::CloseDelim(token::Paren));
-                pat = PatTup(fields);
-            }
-            hi = self.last_span.hi;
-            return P(ast::Pat {
-                id: ast::DUMMY_NODE_ID,
-                node: pat,
-                span: mk_sp(lo, hi)
-            })
+            let fields = self.parse_pat_tuple_elements();
+            self.expect(&token::CloseDelim(token::Paren));
+            pat = PatTup(fields);
           }
           token::OpenDelim(token::Bracket) => {
-            // parse [pat,pat,...] as vector pattern
+            // Parse [pat,pat,...] as vector pattern
             self.bump();
-            let (before, slice, after) =
-                self.parse_pat_vec_elements();
-
+            let (before, slice, after) = self.parse_pat_vec_elements();
             self.expect(&token::CloseDelim(token::Bracket));
-            pat = ast::PatVec(before, slice, after);
-            hi = self.last_span.hi;
-            return P(ast::Pat {
-                id: ast::DUMMY_NODE_ID,
-                node: pat,
-                span: mk_sp(lo, hi)
-            })
+            pat = PatVec(before, slice, after);
           }
-          _ => {}
-        }
-        // at this point, token != _, ~, &, &&, (, [
-
-        if (!(self.token.is_ident() || self.token.is_path())
-              && self.token != token::ModSep)
-                || self.token.is_keyword(keywords::True)
-                || self.token.is_keyword(keywords::False) {
-            // Parse an expression pattern or exp ... exp.
-            //
-            // These expressions are limited to literals (possibly
-            // preceded by unary-minus) or identifiers.
-            let val = self.parse_literal_maybe_minus();
-            if (self.check(&token::DotDotDot)) &&
-                    self.look_ahead(1, |t| {
-                        *t != token::Comma && *t != token::CloseDelim(token::Bracket)
-                    }) {
-                self.bump();
-                let end = if self.token.is_ident() || self.token.is_path() {
-                    let path = self.parse_path(LifetimeAndTypesWithColons);
-                    let hi = self.span.hi;
-                    self.mk_expr(lo, hi, ExprPath(None, path))
-                } else {
-                    self.parse_literal_maybe_minus()
-                };
-                pat = PatRange(val, end);
-            } else {
-                pat = PatLit(val);
-            }
-        } else if self.eat_keyword(keywords::Mut) {
-            pat = self.parse_pat_ident(BindByValue(MutMutable));
-        } else if self.eat_keyword(keywords::Ref) {
-            // parse ref pat
-            let mutbl = self.parse_mutability();
-            pat = self.parse_pat_ident(BindByRef(mutbl));
-        } else if self.eat_keyword(keywords::Box) {
-            // `box PAT`
-            //
-            // FIXME(#13910): Rename to `PatBox` and extend to full DST
-            // support.
-            let sub = self.parse_pat();
-            pat = PatBox(sub);
-            hi = self.last_span.hi;
-            return P(ast::Pat {
-                id: ast::DUMMY_NODE_ID,
-                node: pat,
-                span: mk_sp(lo, hi)
-            })
-        } else {
-            let can_be_enum_or_struct = self.look_ahead(1, |t| {
-                match *t {
-                    token::OpenDelim(_) | token::Lt | token::ModSep => true,
-                    _ => false,
-                }
-            });
-
-            if self.look_ahead(1, |t| *t == token::DotDotDot) &&
-                    self.look_ahead(2, |t| {
-                        *t != token::Comma && *t != token::CloseDelim(token::Bracket)
-                    }) {
-                let start = self.parse_expr_res(RESTRICTION_NO_BAR_OP);
-                self.eat(&token::DotDotDot);
-                let end = self.parse_expr_res(RESTRICTION_NO_BAR_OP);
-                pat = PatRange(start, end);
-            } else if self.token.is_plain_ident() && !can_be_enum_or_struct {
-                let id = self.parse_ident();
-                let id_span = self.last_span;
-                let pth1 = codemap::Spanned{span:id_span, node: id};
-                if self.eat(&token::Not) {
-                    // macro invocation
-                    let delim = self.expect_open_delim();
-                    let tts = self.parse_seq_to_end(&token::CloseDelim(delim),
-                                                    seq_sep_none(),
-                                                    |p| p.parse_token_tree());
-
-                    let mac = MacInvocTT(ident_to_path(id_span,id), tts, EMPTY_CTXT);
-                    pat = ast::PatMac(codemap::Spanned {node: mac, span: self.span});
-                } else {
-                    let sub = if self.eat(&token::At) {
-                        // parse foo @ pat
-                        Some(self.parse_pat())
-                    } else {
-                        // or just foo
-                        None
-                    };
-                    pat = PatIdent(BindByValue(MutImmutable), pth1, sub);
-                }
-            } else if self.look_ahead(1, |t| *t == token::Lt) {
-                self.bump();
-                self.unexpected()
-            } else {
-                // parse an enum pat
-                let enum_path = self.parse_path(LifetimeAndTypesWithColons);
-                match self.token {
-                    token::OpenDelim(token::Brace) => {
-                        self.bump();
-                        let (fields, etc) =
-                            self.parse_pat_fields();
+          _ => {
+            // At this point, token != _, &, &&, (, [
+            if self.eat_keyword(keywords::Mut) {
+                // Parse mut ident @ pat
+                pat = self.parse_pat_ident(BindByValue(MutMutable));
+            } else if self.eat_keyword(keywords::Ref) {
+                // Parse ref ident @ pat / ref mut ident @ pat
+                let mutbl = self.parse_mutability();
+                pat = self.parse_pat_ident(BindByRef(mutbl));
+            } else if self.eat_keyword(keywords::Box) {
+                // Parse box pat
+                let subpat = self.parse_pat();
+                pat = PatBox(subpat);
+            } else if self.is_path_start() {
+                // Parse pattern starting with a path
+                if self.token.is_plain_ident() && self.look_ahead(1, |t| *t != token::DotDotDot &&
+                        *t != token::OpenDelim(token::Brace) &&
+                        *t != token::OpenDelim(token::Paren)) {
+                    // Plain idents have some extra abilities here compared to general paths
+                    if self.look_ahead(1, |t| *t == token::Not) {
+                        // Parse macro invocation
+                        let ident = self.parse_ident();
+                        let ident_span = self.last_span;
+                        let path = ident_to_path(ident_span, ident);
                         self.bump();
-                        pat = PatStruct(enum_path, fields, etc);
+                        let delim = self.expect_open_delim();
+                        let tts = self.parse_seq_to_end(&token::CloseDelim(delim),
+                                seq_sep_none(), |p| p.parse_token_tree());
+                        let mac = MacInvocTT(path, tts, EMPTY_CTXT);
+                        pat = PatMac(codemap::Spanned {node: mac, span: self.span});
+                    } else {
+                        // Parse ident @ pat
+                        // This can give false positives and parse nullary enums,
+                        // they are dealt with later in resolve
+                        pat = self.parse_pat_ident(BindByValue(MutImmutable));
                     }
-                    token::DotDotDot => {
+                } else {
+                    // Parse as a general path
+                    let path = self.parse_path(LifetimeAndTypesWithColons);
+                    match self.token {
+                      token::DotDotDot => {
+                        // Parse range
                         let hi = self.last_span.hi;
-                        let start = self.mk_expr(lo, hi, ExprPath(None, enum_path));
-                        self.eat(&token::DotDotDot);
-                        let end = if self.token.is_ident() || self.token.is_path() {
-                            let path = self.parse_path(LifetimeAndTypesWithColons);
-                            let hi = self.span.hi;
-                            self.mk_expr(lo, hi, ExprPath(None, path))
+                        let begin = self.mk_expr(lo, hi, ExprPath(None, path));
+                        self.bump();
+                        let end = self.parse_pat_range_end();
+                        pat = PatRange(begin, end);
+                      }
+                      token::OpenDelim(token::Brace) => {
+                        // Parse struct pattern
+                        self.bump();
+                        let (fields, etc) = self.parse_pat_fields();
+                        self.bump();
+                        pat = PatStruct(path, fields, etc);
+                      }
+                      token::OpenDelim(token::Paren) => {
+                        // Parse tuple struct or enum pattern
+                        if self.look_ahead(1, |t| *t == token::DotDot) {
+                            // This is a "top constructor only" pat
+                            self.bump();
+                            self.bump();
+                            self.expect(&token::CloseDelim(token::Paren));
+                            pat = PatEnum(path, None);
                         } else {
-                            self.parse_literal_maybe_minus()
-                        };
-                        pat = PatRange(start, end);
-                    }
-                    _ => {
-                        let mut args: Vec<P<Pat>> = Vec::new();
-                        match self.token {
-                          token::OpenDelim(token::Paren) => {
-                            let is_dotdot = self.look_ahead(1, |t| {
-                                match *t {
-                                    token::DotDot => true,
-                                    _ => false,
-                                }
-                            });
-                            if is_dotdot {
-                                // This is a "top constructor only" pat
-                                self.bump();
-                                self.bump();
-                                self.expect(&token::CloseDelim(token::Paren));
-                                pat = PatEnum(enum_path, None);
-                            } else {
-                                args = self.parse_enum_variant_seq(
-                                    &token::OpenDelim(token::Paren),
+                            let args = self.parse_enum_variant_seq(&token::OpenDelim(token::Paren),
                                     &token::CloseDelim(token::Paren),
-                                    seq_sep_trailing_allowed(token::Comma),
-                                    |p| p.parse_pat()
-                                );
-                                pat = PatEnum(enum_path, Some(args));
-                            }
-                          },
-                          _ => {
-                              if !enum_path.global &&
-                                  enum_path.segments.len() == 1 &&
-                                  enum_path.segments[0].parameters.is_empty()
-                              {
-                                // NB: If enum_path is a single identifier,
-                                // this should not be reachable due to special
-                                // handling further above.
-                                //
-                                // However, previously a PatIdent got emitted
-                                // here, so we preserve the branch just in case.
-                                //
-                                // A rewrite of the logic in this function
-                                // would probably make this obvious.
-                                self.span_bug(enum_path.span,
-                                              "ident only path should have been covered already");
-                              } else {
-                                  pat = PatEnum(enum_path, Some(args));
-                              }
-                          }
+                                    seq_sep_trailing_allowed(token::Comma), |p| p.parse_pat());
+                            pat = PatEnum(path, Some(args));
                         }
+                      }
+                      _ => {
+                        // Parse nullary enum
+                        pat = PatEnum(path, Some(vec![]));
+                      }
                     }
                 }
+            } else {
+                // Try to parse everything else as literal with optional minus
+                let begin = self.parse_literal_maybe_minus();
+                if self.eat(&token::DotDotDot) {
+                    let end = self.parse_pat_range_end();
+                    pat = PatRange(begin, end);
+                } else {
+                    pat = PatLit(begin);
+                }
             }
+          }
         }
-        hi = self.last_span.hi;
+
+        let hi = self.last_span.hi;
         P(ast::Pat {
             id: ast::DUMMY_NODE_ID,
             node: pat,
index f026a5db551e4ab69f785fe05bb1af35fdeed215..6d3d1207788303f7036e206441060274724d0707 100644 (file)
@@ -11,7 +11,7 @@
 fn main() {
   match 42 {
     x < 7 => (),
-   //~^ error: unexpected token: `<`
+   //~^ error: expected one of `=>`, `@`, `if`, or `|`, found `<`
     _ => ()
   }
 }
index ea5180e3eec61f67273a57ad6c6edd96caef8e3e..6a0653041d45b194d344a7cb6ab9d34dfae78c5c 100644 (file)
@@ -9,4 +9,4 @@
 // except according to those terms.
 
 fn a(B<) {}
-   //~^ error: unexpected token: `<`
+   //~^ error: expected one of `:` or `@`, found `<`
index 2e0b5d6b80fa7f6a7888065c6b0e430a043fe4a6..8ea2bcf900c97066e7f4f798328b45bd57343acd 100644 (file)
@@ -14,7 +14,7 @@ impl<T> Foo<T> {
     fn foo(&self) {
         match *self {
             Foo<T>(x, y) => {
-            //~^ error: unexpected token: `<`
+            //~^ error: expected one of `=>`, `@`, `if`, or `|`, found `<`
               println!("Goodbye, World!")
             }
         }
index 5de8627001087ea474eba75aa7f003a3caf3e762..1ace57edba3d8152d5d7732f9ccace943e529747 100644 (file)
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 fn main() {
-    let caller<F> = |f: F|  //~ ERROR unexpected token: `<`
+    let caller<F> = |f: F|  //~ ERROR expected one of `:`, `;`, `=`, or `@`, found `<`
     where F: Fn() -> i32
     {
         let x = f();
index abc9e599467e99c661caa098b478b26a32ffa721..ed936cdd9a934c486c90a15c0adf81f09207c633 100644 (file)
@@ -13,7 +13,7 @@ struct Foo<B> {
 }
 
 fn bar() {
-    let Foo<Vec<u8>>  //~ ERROR unexpected token: `<`
+    let Foo<Vec<u8>>  //~ ERROR expected one of `:`, `;`, `=`, or `@`, found `<`
 }
 
 fn main() {}
index 3e073d34f3261914fa34010a811ea6fc420cc98a..0fc00c350748adeaf5169447159f7b91a2fd566e 100644 (file)
@@ -11,7 +11,7 @@
 fn main() {
     let a = Vec::new();
     match a {
-        [1, tail.., tail..] => {}, //~ ERROR: expected one of `!`, `,`, or `@`, found `..`
+        [1, tail.., tail..] => {}, //~ ERROR: expected one of `,` or `@`, found `..`
         _ => ()
     }
 }
index 729b45df8b430823377fed370fd455d23c6def1e..2826d3b4af94213e2faa515e76299798d9eb9e20 100644 (file)
@@ -8,5 +8,5 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-fn foo(x) { //~ ERROR expected one of `!`, `:`, or `@`, found `)`
+fn foo(x) { //~ ERROR expected one of `:` or `@`, found `)`
 }
index 1474cc9dd396d268f6321bb4fb6b6355939d20e0..116848c42c28e07eec74191f34262c048f9abbbd 100644 (file)
@@ -11,5 +11,5 @@
 fn removed_moves() {
     let mut x = 0;
     let y <- x;
-    //~^ ERROR expected one of `!`, `:`, `;`, `=`, or `@`, found `<-`
+    //~^ ERROR expected one of `:`, `;`, `=`, or `@`, found `<-`
 }
index cdcf98a42f94b0582e7cb2211b8c7f096eb16948..85addc822939d15883d4f8c8996155e7f3955870 100644 (file)
@@ -14,7 +14,7 @@ struct Foo {
 
 fn main() {
     match Foo {
-        x: 3    //~ ERROR expected one of `!`, `=>`, `@`, `if`, or `|`, found `:`
+        x: 3    //~ ERROR expected one of `=>`, `@`, `if`, or `|`, found `:`
     } {
         Foo {
             x: x