diff --git a/src/doc/grammar.md b/src/doc/grammar.md index d7a29ea530952..83aad7267e746 100644 --- a/src/doc/grammar.md +++ b/src/doc/grammar.md @@ -661,7 +661,7 @@ match_pat : pat [ '|' pat ] * [ "if" expr ] ? ; ### If let expressions ```antlr -if_let_expr : "if" "let" pat '=' expr '{' block '}' +if_let_expr : "if" "let" pat [ '|' pat ] * '=' expr '{' block '}' else_tail ? ; else_tail : "else" [ if_expr | if_let_expr | '{' block '}' ] ; ``` @@ -669,7 +669,7 @@ else_tail : "else" [ if_expr | if_let_expr | '{' block '}' ] ; ### While let loops ```antlr -while_let_expr : "while" "let" pat '=' expr '{' block '}' ; +while_let_expr : "while" "let" pat [ '|' pat ] * '=' expr '{' block '}' ; ``` ### Return expressions diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 6d6fdffa95095..e68c8d28fc5ab 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -732,11 +732,11 @@ pub enum Expr_ { ExprLit(P), ExprCast(P, P), ExprIf(P, P, Option>), - ExprIfLet(P, P, P, Option>), + ExprIfLet(Vec>, P, P, Option>), // FIXME #6993: change to Option ... or not, if these are hygienic. ExprWhile(P, P, Option), // FIXME #6993: change to Option ... or not, if these are hygienic. - ExprWhileLet(P, P, P, Option), + ExprWhileLet(Vec>, P, P, Option), // FIXME #6993: change to Option ... or not, if these are hygienic. ExprForLoop(P, P, P, Option), // Conditionless loop (can be exited with break, cont, or ret) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index bea57ae14e4af..ad793bf731b24 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -92,7 +92,7 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { // Desugar ExprWhileLet // From: `[opt_ident]: while let = ` - ast::ExprWhileLet(pat, expr, body, opt_ident) => { + ast::ExprWhileLet(pats, expr, body, opt_ident) => { // to: // // [opt_ident]: loop { @@ -105,7 +105,7 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { // ` => ` let pat_arm = { let body_expr = fld.cx.expr_block(body); - fld.cx.arm(pat.span, vec![pat], body_expr) + fld.cx.arm(span, pats, body_expr) }; // `_ => break` @@ -127,8 +127,8 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { } // Desugar ExprIfLet - // From: `if let = []` - ast::ExprIfLet(pat, expr, body, mut elseopt) => { + // From: `if let = []` + ast::ExprIfLet(pats, expr, body, mut elseopt) => { // to: // // match { @@ -140,7 +140,7 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { // ` => ` let pat_arm = { let body_expr = fld.cx.expr_block(body); - fld.cx.arm(pat.span, vec![pat], body_expr) + fld.cx.arm(span, pats, body_expr) }; // `[_ if => ,]` diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 959e3bdb31476..be37ed9be31cd 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1288,8 +1288,8 @@ pub fn noop_fold_expr(Expr {id, node, span}: Expr, folder: &mut T) -> folder.fold_block(tr), fl.map(|x| folder.fold_expr(x))) } - ExprIfLet(pat, expr, tr, fl) => { - ExprIfLet(folder.fold_pat(pat), + ExprIfLet(pats, expr, tr, fl) => { + ExprIfLet(pats.move_map(|x| folder.fold_pat(x)), folder.fold_expr(expr), folder.fold_block(tr), fl.map(|x| folder.fold_expr(x))) @@ -1299,8 +1299,8 @@ pub fn noop_fold_expr(Expr {id, node, span}: Expr, folder: &mut T) -> folder.fold_block(body), opt_ident.map(|i| folder.fold_ident(i))) } - ExprWhileLet(pat, expr, body, opt_ident) => { - ExprWhileLet(folder.fold_pat(pat), + ExprWhileLet(pats, expr, body, opt_ident) => { + ExprWhileLet(pats.move_map(|x| folder.fold_pat(x)), folder.fold_expr(expr), folder.fold_block(body), opt_ident.map(|i| folder.fold_ident(i))) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 88df6d6d4cd2f..436ddc14e6fa4 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3066,7 +3066,7 @@ impl<'a> Parser<'a> { pub fn parse_if_let_expr(&mut self) -> P { let lo = self.last_span.lo; self.expect_keyword(keywords::Let); - let pat = self.parse_pat(); + let pats = self.parse_pats(); self.expect(&token::Eq); let expr = self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL); let thn = self.parse_block(); @@ -3076,7 +3076,7 @@ impl<'a> Parser<'a> { } else { (thn.span.hi, None) }; - self.mk_expr(lo, hi, ExprIfLet(pat, expr, thn, els)) + self.mk_expr(lo, hi, ExprIfLet(pats, expr, thn, els)) } // `|args| expr` @@ -3139,12 +3139,12 @@ impl<'a> Parser<'a> { pub fn parse_while_let_expr(&mut self, opt_ident: Option) -> P { let lo = self.last_span.lo; self.expect_keyword(keywords::Let); - let pat = self.parse_pat(); + let pats = self.parse_pats(); self.expect(&token::Eq); let expr = self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL); let body = self.parse_block(); let hi = body.span.hi; - return self.mk_expr(lo, hi, ExprWhileLet(pat, expr, body, opt_ident)); + return self.mk_expr(lo, hi, ExprWhileLet(pats, expr, body, opt_ident)); } pub fn parse_loop_expr(&mut self, opt_ident: Option) -> P { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 883c2295a3655..9a3e10e5aa104 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1459,11 +1459,11 @@ impl<'a> State<'a> { self.print_else(e.as_ref().map(|e| &**e)) } // "another else-if-let" - ast::ExprIfLet(ref pat, ref expr, ref then, ref e) => { + ast::ExprIfLet(ref pats, ref expr, ref then, ref e) => { try!(self.cbox(indent_unit - 1)); try!(self.ibox(0)); try!(word(&mut self.s, " else if let ")); - try!(self.print_pat(&**pat)); + try!(self.print_pats(pats)); try!(space(&mut self.s)); try!(self.word_space("=")); try!(self.print_expr(&**expr)); @@ -1497,10 +1497,10 @@ impl<'a> State<'a> { self.print_else(elseopt) } - pub fn print_if_let(&mut self, pat: &ast::Pat, expr: &ast::Expr, blk: &ast::Block, + pub fn print_if_let(&mut self, pats: &[P], expr: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) -> io::Result<()> { try!(self.head("if let")); - try!(self.print_pat(pat)); + try!(self.print_pats(pats)); try!(space(&mut self.s)); try!(self.word_space("=")); try!(self.print_expr(expr)); @@ -1721,8 +1721,8 @@ impl<'a> State<'a> { ast::ExprIf(ref test, ref blk, ref elseopt) => { try!(self.print_if(&**test, &**blk, elseopt.as_ref().map(|e| &**e))); } - ast::ExprIfLet(ref pat, ref expr, ref blk, ref elseopt) => { - try!(self.print_if_let(&**pat, &**expr, &** blk, elseopt.as_ref().map(|e| &**e))); + ast::ExprIfLet(ref pats, ref expr, ref blk, ref elseopt) => { + try!(self.print_if_let(&**pats, &**expr, &** blk, elseopt.as_ref().map(|e| &**e))); } ast::ExprWhile(ref test, ref blk, opt_ident) => { if let Some(ident) = opt_ident { @@ -1734,13 +1734,13 @@ impl<'a> State<'a> { try!(space(&mut self.s)); try!(self.print_block(&**blk)); } - ast::ExprWhileLet(ref pat, ref expr, ref blk, opt_ident) => { + ast::ExprWhileLet(ref pats, ref expr, ref blk, opt_ident) => { if let Some(ident) = opt_ident { try!(self.print_ident(ident)); try!(self.word_space(":")); } try!(self.head("while let")); - try!(self.print_pat(&**pat)); + try!(self.print_pats(pats)); try!(space(&mut self.s)); try!(self.word_space("=")); try!(self.print_expr(&**expr)); @@ -2253,6 +2253,22 @@ impl<'a> State<'a> { self.ann.post(self, NodePat(pat)) } + pub fn print_pats(&mut self, pats: &[P]) -> io::Result<()> { + match pats { + [] => {}, + [ref p, ref ps..] => { + try!(self.print_pat(&*p)); + + for p in *ps { + try!(space(&mut self.s)); + try!(self.word_space("|")); + try!(self.print_pat(&*p)); + } + } + } + Ok(()) + } + fn print_arm(&mut self, arm: &ast::Arm) -> io::Result<()> { // I have no idea why this check is necessary, but here it // is :( diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 33d8d56b4b114..2459e6ac2925b 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -805,14 +805,18 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_expr(&**subexpression); visitor.visit_block(&**block) } - ExprIfLet(ref pattern, ref subexpression, ref if_block, ref optional_else) => { - visitor.visit_pat(&**pattern); + ExprIfLet(ref patterns, ref subexpression, ref if_block, ref optional_else) => { + for pattern in patterns { + visitor.visit_pat(&**pattern) + } visitor.visit_expr(&**subexpression); visitor.visit_block(&**if_block); walk_expr_opt(visitor, optional_else); } - ExprWhileLet(ref pattern, ref subexpression, ref block, _) => { - visitor.visit_pat(&**pattern); + ExprWhileLet(ref patterns, ref subexpression, ref block, _) => { + for pattern in patterns { + visitor.visit_pat(&**pattern); + } visitor.visit_expr(&**subexpression); visitor.visit_block(&**block); } diff --git a/src/test/run-pass/if-let-multiple-patterns.rs b/src/test/run-pass/if-let-multiple-patterns.rs new file mode 100644 index 0000000000000..b68e06bc4571d --- /dev/null +++ b/src/test/run-pass/if-let-multiple-patterns.rs @@ -0,0 +1,24 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +enum Foo { One(u8), Two(u8), Three } +use Foo::*; + +fn main () { + let x = Two(42); + let mut result = 0; + + if let One(n) | Two(n) = x { + result = n; + } + + + assert_eq!(result, 42) +} diff --git a/src/test/run-pass/while-let-multiple-patterns.rs b/src/test/run-pass/while-let-multiple-patterns.rs new file mode 100644 index 0000000000000..23370ef274267 --- /dev/null +++ b/src/test/run-pass/while-let-multiple-patterns.rs @@ -0,0 +1,39 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[derive(Debug)] +enum Foo { One, Two, Three } +use Foo::*; + +fn inc(x: Foo) -> Foo { + match x { + One => Two, + Two => Three, + Three => Three, + } +} + +pub fn main() { + let mut x = Some(2); + while let Some(1) | Some(2) = x { + x = None; + } + + assert_eq!(x , None); + + let mut x = One; + let mut n = 0; + while let One | Two = x { + n += 1; + x = inc(x); + } + + assert_eq!(n, 2); +}