Skip to content
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/doc/grammar.md
Original file line number Diff line number Diff line change
Expand Up @@ -661,15 +661,15 @@ 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 '}' ] ;
```

### While let loops

```antlr
while_let_expr : "while" "let" pat '=' expr '{' block '}' ;
while_let_expr : "while" "let" pat [ '|' pat ] * '=' expr '{' block '}' ;
```

### Return expressions
Expand Down
4 changes: 2 additions & 2 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -732,11 +732,11 @@ pub enum Expr_ {
ExprLit(P<Lit>),
ExprCast(P<Expr>, P<Ty>),
ExprIf(P<Expr>, P<Block>, Option<P<Expr>>),
ExprIfLet(P<Pat>, P<Expr>, P<Block>, Option<P<Expr>>),
ExprIfLet(Vec<P<Pat>>, P<Expr>, P<Block>, Option<P<Expr>>),
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
ExprWhile(P<Expr>, P<Block>, Option<Ident>),
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
ExprWhileLet(P<Pat>, P<Expr>, P<Block>, Option<Ident>),
ExprWhileLet(Vec<P<Pat>>, P<Expr>, P<Block>, Option<Ident>),
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
ExprForLoop(P<Pat>, P<Expr>, P<Block>, Option<Ident>),
// Conditionless loop (can be exited with break, cont, or ret)
Expand Down
10 changes: 5 additions & 5 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {

// Desugar ExprWhileLet
// From: `[opt_ident]: while let <pat> = <expr> <body>`
ast::ExprWhileLet(pat, expr, body, opt_ident) => {
ast::ExprWhileLet(pats, expr, body, opt_ident) => {
// to:
//
// [opt_ident]: loop {
Expand All @@ -105,7 +105,7 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
// `<pat> => <body>`
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`
Expand All @@ -127,8 +127,8 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
}

// Desugar ExprIfLet
// From: `if let <pat> = <expr> <body> [<elseopt>]`
ast::ExprIfLet(pat, expr, body, mut elseopt) => {
// From: `if let <pats> = <expr> <body> [<elseopt>]`
ast::ExprIfLet(pats, expr, body, mut elseopt) => {
// to:
//
// match <expr> {
Expand All @@ -140,7 +140,7 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
// `<pat> => <body>`
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 <elseopt_if_cond> => <elseopt_if_body>,]`
Expand Down
8 changes: 4 additions & 4 deletions src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1288,8 +1288,8 @@ pub fn noop_fold_expr<T: Folder>(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)))
Expand All @@ -1299,8 +1299,8 @@ pub fn noop_fold_expr<T: Folder>(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)))
Expand Down
8 changes: 4 additions & 4 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3066,7 +3066,7 @@ impl<'a> Parser<'a> {
pub fn parse_if_let_expr(&mut self) -> P<Expr> {
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();
Expand All @@ -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`
Expand Down Expand Up @@ -3139,12 +3139,12 @@ impl<'a> Parser<'a> {
pub fn parse_while_let_expr(&mut self, opt_ident: Option<ast::Ident>) -> P<Expr> {
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<ast::Ident>) -> P<Expr> {
Expand Down
30 changes: 22 additions & 8 deletions src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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<ast::Pat>], 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));
Expand Down Expand Up @@ -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 {
Expand All @@ -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));
Expand Down Expand Up @@ -2253,6 +2253,20 @@ impl<'a> State<'a> {
self.ann.post(self, NodePat(pat))
}

pub fn print_pats(&mut self, pats: &[P<ast::Pat>]) -> io::Result<()> {
let mut first = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

match pats {
  [] => {},
  [p, ..ps] => {
    try!(self.print_pat(&**p));

    for p in ps {
      try!(space(&mut self.s));
      try!(self.word_space("|"));
      try!(self.print_pat(&**p));
    }
  }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you go: d570bac

for ref p in pats {
if first {
first = false;
} else {
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 :(
Expand Down
12 changes: 8 additions & 4 deletions src/libsyntax/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
24 changes: 24 additions & 0 deletions src/test/run-pass/if-let-multiple-patterns.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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)
}
39 changes: 39 additions & 0 deletions src/test/run-pass/while-let-multiple-patterns.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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);
}