syntax: Rewrite parsing of patterns
This commit is contained in:
parent
5e30f05a05
commit
35c1bdb2b4
@ -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};
|
||||
@ -86,12 +86,10 @@
|
||||
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())
|
||||
_ => {
|
||||
// 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();
|
||||
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 {
|
||||
// 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();
|
||||
self.bump();
|
||||
pat = PatStruct(enum_path, fields, etc);
|
||||
// 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,
|
||||
|
@ -11,7 +11,7 @@
|
||||
fn main() {
|
||||
match 42 {
|
||||
x < 7 => (),
|
||||
//~^ error: unexpected token: `<`
|
||||
//~^ error: expected one of `=>`, `@`, `if`, or `|`, found `<`
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
@ -9,4 +9,4 @@
|
||||
// except according to those terms.
|
||||
|
||||
fn a(B<) {}
|
||||
//~^ error: unexpected token: `<`
|
||||
//~^ error: expected one of `:` or `@`, found `<`
|
||||
|
@ -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!")
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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() {}
|
||||
|
@ -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 `..`
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
@ -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 `)`
|
||||
}
|
||||
|
@ -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 `<-`
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user