about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-09-30 23:17:20 +0000
committerbors <bors@rust-lang.org>2014-09-30 23:17:20 +0000
commit57a05cf49b3149427e3fe190c07f8343a29ad86c (patch)
treee936d71b10c6f881337d44a960e1b8901cd50b38 /src/libsyntax
parent88d1a22f76a774b2df3c904ceb54c86e58a859c3 (diff)
parente723051a2eb840a710f2b79afc1b42b0b707d0e4 (diff)
downloadrust-57a05cf49b3149427e3fe190c07f8343a29ad86c.tar.gz
rust-57a05cf49b3149427e3fe190c07f8343a29ad86c.zip
auto merge of #17634 : jakub-/rust/if_let, r=kballard
Continuation of https://github.com/rust-lang/rust/pull/16741.
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast.rs9
-rw-r--r--src/libsyntax/config.rs4
-rw-r--r--src/libsyntax/ext/build.rs2
-rw-r--r--src/libsyntax/ext/expand.rs93
-rw-r--r--src/libsyntax/feature_gate.rs6
-rw-r--r--src/libsyntax/fold.rs11
-rw-r--r--src/libsyntax/parse/classify.rs1
-rw-r--r--src/libsyntax/parse/parser.rs39
-rw-r--r--src/libsyntax/print/pprust.rs35
-rw-r--r--src/libsyntax/visit.rs8
10 files changed, 186 insertions, 22 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 0fee3ff3218..59c824d0eae 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -521,6 +521,7 @@ 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>>),
     // 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.
@@ -528,7 +529,7 @@ pub enum Expr_ {
     // Conditionless loop (can be exited with break, cont, or ret)
     // FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
     ExprLoop(P<Block>, Option<Ident>),
-    ExprMatch(P<Expr>, Vec<Arm>),
+    ExprMatch(P<Expr>, Vec<Arm>, MatchSource),
     ExprFnBlock(CaptureClause, P<FnDecl>, P<Block>),
     ExprProc(P<FnDecl>, P<Block>),
     ExprUnboxedFn(CaptureClause, UnboxedClosureKind, P<FnDecl>, P<Block>),
@@ -577,6 +578,12 @@ pub struct QPath {
 }
 
 #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
+pub enum MatchSource {
+    MatchNormal,
+    MatchIfLetDesugar
+}
+
+#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
 pub enum CaptureClause {
     CaptureByValue,
     CaptureByRef,
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index 5b17f6f004a..41b7218da0c 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -210,10 +210,10 @@ fn fold_expr(cx: &mut Context, expr: P<ast::Expr>) -> P<ast::Expr> {
         fold::noop_fold_expr(ast::Expr {
             id: id,
             node: match node {
-                ast::ExprMatch(m, arms) => {
+                ast::ExprMatch(m, arms, source) => {
                     ast::ExprMatch(m, arms.into_iter()
                                         .filter(|a| (cx.in_cfg)(a.attrs.as_slice()))
-                                        .collect())
+                                        .collect(), source)
                 }
                 _ => node
             },
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 0586868eb45..f2b806f43cc 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -845,7 +845,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     }
 
     fn expr_match(&self, span: Span, arg: P<ast::Expr>, arms: Vec<ast::Arm>) -> P<Expr> {
-        self.expr(span, ast::ExprMatch(arg, arms))
+        self.expr(span, ast::ExprMatch(arg, arms, ast::MatchNormal))
     }
 
     fn expr_if(&self, span: Span, cond: P<ast::Expr>,
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 9f3df1a7623..fa3ccc8cf32 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -39,7 +39,7 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
     e.and_then(|ast::Expr {id, node, span}| match node {
         // expr_mac should really be expr_ext or something; it's the
         // entry-point for all syntax extensions.
-        ExprMac(mac) => {
+        ast::ExprMac(mac) => {
             let expanded_expr = match expand_mac_invoc(mac, span,
                                                        |r| r.make_expr(),
                                                        mark_expr, fld) {
@@ -67,6 +67,97 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
             fld.cx.expr(span, ast::ExprWhile(cond, body, opt_ident))
         }
 
+        // Desugar ExprIfLet
+        // From: `if let <pat> = <expr> <body> [<elseopt>]`
+        ast::ExprIfLet(pat, expr, body, mut elseopt) => {
+            // to:
+            //
+            //   match <expr> {
+            //     <pat> => <body>,
+            //     [_ if <elseopt_if_cond> => <elseopt_if_body>,]
+            //     _ => [<elseopt> | ()]
+            //   }
+
+            // `<pat> => <body>`
+            let pat_arm = {
+                let body_expr = fld.cx.expr_block(body);
+                fld.cx.arm(pat.span, vec![pat], body_expr)
+            };
+
+            // `[_ if <elseopt_if_cond> => <elseopt_if_body>,]`
+            let else_if_arms = {
+                let mut arms = vec![];
+                loop {
+                    let elseopt_continue = elseopt
+                        .and_then(|els| els.and_then(|els| match els.node {
+                        // else if
+                        ast::ExprIf(cond, then, elseopt) => {
+                            let pat_under = fld.cx.pat_wild(span);
+                            arms.push(ast::Arm {
+                                attrs: vec![],
+                                pats: vec![pat_under],
+                                guard: Some(cond),
+                                body: fld.cx.expr_block(then)
+                            });
+                            elseopt.map(|elseopt| (elseopt, true))
+                        }
+                        _ => Some((P(els), false))
+                    }));
+                    match elseopt_continue {
+                        Some((e, true)) => {
+                            elseopt = Some(e);
+                        }
+                        Some((e, false)) => {
+                            elseopt = Some(e);
+                            break;
+                        }
+                        None => {
+                            elseopt = None;
+                            break;
+                        }
+                    }
+                }
+                arms
+            };
+
+            // `_ => [<elseopt> | ()]`
+            let else_arm = {
+                let pat_under = fld.cx.pat_wild(span);
+                let else_expr = elseopt.unwrap_or_else(|| fld.cx.expr_lit(span, ast::LitNil));
+                fld.cx.arm(span, vec![pat_under], else_expr)
+            };
+
+            let mut arms = Vec::with_capacity(else_if_arms.len() + 2);
+            arms.push(pat_arm);
+            arms.push_all_move(else_if_arms);
+            arms.push(else_arm);
+
+            let match_expr = fld.cx.expr(span, ast::ExprMatch(expr, arms, ast::MatchIfLetDesugar));
+            fld.fold_expr(match_expr)
+        }
+
+        // Desugar support for ExprIfLet in the ExprIf else position
+        ast::ExprIf(cond, blk, elseopt) => {
+            let elseopt = elseopt.map(|els| els.and_then(|els| match els.node {
+                ast::ExprIfLet(..) => {
+                    // wrap the if-let expr in a block
+                    let span = els.span;
+                    let blk = P(ast::Block {
+                        view_items: vec![],
+                        stmts: vec![],
+                        expr: Some(P(els)),
+                        id: ast::DUMMY_NODE_ID,
+                        rules: ast::DefaultBlock,
+                        span: span
+                    });
+                    fld.cx.expr_block(blk)
+                }
+                _ => P(els)
+            }));
+            let if_expr = fld.cx.expr(span, ast::ExprIf(cond, blk, elseopt));
+            if_expr.map(|e| noop_fold_expr(e, fld))
+        }
+
         ast::ExprLoop(loop_block, opt_ident) => {
             let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
             fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident))
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 1c6ee8acc94..ca6d488772c 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -71,6 +71,8 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
     ("associated_types", Active),
     ("visible_private_types", Active),
 
+    ("if_let", Active),
+
     // if you change this list without updating src/doc/rust.md, cmr will be sad
 
     // A temporary feature gate used to enable parser extensions needed
@@ -356,6 +358,10 @@ impl<'a, 'v> Visitor<'v> for Context<'a> {
                                   e.span,
                                   "tuple indexing is experimental");
             }
+            ast::ExprIfLet(..) => {
+                self.gate_feature("if_let", e.span,
+                                  "`if let` syntax is experimental");
+            }
             _ => {}
         }
         visit::walk_expr(self, e);
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 53be7f2c20c..31bec58a4da 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -1205,6 +1205,12 @@ 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),
+                          folder.fold_expr(expr),
+                          folder.fold_block(tr),
+                          fl.map(|x| folder.fold_expr(x)))
+            }
             ExprWhile(cond, body, opt_ident) => {
                 ExprWhile(folder.fold_expr(cond),
                           folder.fold_block(body),
@@ -1220,9 +1226,10 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
                 ExprLoop(folder.fold_block(body),
                         opt_ident.map(|i| folder.fold_ident(i)))
             }
-            ExprMatch(expr, arms) => {
+            ExprMatch(expr, arms, source) => {
                 ExprMatch(folder.fold_expr(expr),
-                        arms.move_map(|x| folder.fold_arm(x)))
+                        arms.move_map(|x| folder.fold_arm(x)),
+                        source)
             }
             ExprFnBlock(capture_clause, decl, body) => {
                 ExprFnBlock(capture_clause,
diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs
index cdd221aca7c..cb57318445e 100644
--- a/src/libsyntax/parse/classify.rs
+++ b/src/libsyntax/parse/classify.rs
@@ -24,6 +24,7 @@ use ast;
 pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
     match e.node {
         ast::ExprIf(..)
+        | ast::ExprIfLet(..)
         | ast::ExprMatch(..)
         | ast::ExprBlock(_)
         | ast::ExprWhile(..)
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 415ff6a4097..0780e68a062 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -23,7 +23,7 @@ use ast::{DeclLocal, DefaultBlock, UnDeref, BiDiv, EMPTY_CTXT, EnumDef, Explicit
 use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain};
 use ast::{ExprAssign, ExprAssignOp, ExprBinary, ExprBlock, ExprBox};
 use ast::{ExprBreak, ExprCall, ExprCast};
-use ast::{ExprField, ExprTupField, ExprFnBlock, ExprIf, ExprIndex, ExprSlice};
+use ast::{ExprField, ExprTupField, ExprFnBlock, ExprIf, ExprIfLet, ExprIndex, ExprSlice};
 use ast::{ExprLit, ExprLoop, ExprMac};
 use ast::{ExprMethodCall, ExprParen, ExprPath, ExprProc};
 use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary, ExprUnboxedFn};
@@ -38,7 +38,7 @@ use ast::{ItemMac, ItemMod, ItemStruct, ItemTrait, ItemTy};
 use ast::{LifetimeDef, Lit, Lit_};
 use ast::{LitBool, LitChar, LitByte, LitBinary};
 use ast::{LitNil, LitStr, LitInt, Local, LocalLet};
-use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, Matcher, MatchNonterminal};
+use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, Matcher, MatchNonterminal, MatchNormal};
 use ast::{MatchSeq, MatchTok, Method, MutTy, BiMul, Mutability};
 use ast::{MethodImplItem, NamedField, UnNeg, NoReturn, UnNot};
 use ast::{Pat, PatEnum, PatIdent, PatLit, PatRange, PatRegion, PatStruct};
@@ -576,12 +576,11 @@ impl<'a> Parser<'a> {
     /// If the next token is the given keyword, eat it and return
     /// true. Otherwise, return false.
     pub fn eat_keyword(&mut self, kw: keywords::Keyword) -> bool {
-        match self.token {
-            token::IDENT(sid, false) if kw.to_name() == sid.name => {
-                self.bump();
-                true
-            }
-            _ => false
+        if self.is_keyword(kw) {
+            self.bump();
+            true
+        } else {
+            false
         }
     }
 
@@ -2860,8 +2859,11 @@ impl<'a> Parser<'a> {
         }
     }
 
-    /// Parse an 'if' expression ('if' token already eaten)
+    /// Parse an 'if' or 'if let' expression ('if' token already eaten)
     pub fn parse_if_expr(&mut self) -> P<Expr> {
+        if self.is_keyword(keywords::Let) {
+            return self.parse_if_let_expr();
+        }
         let lo = self.last_span.lo;
         let cond = self.parse_expr_res(RestrictionNoStructLiteral);
         let thn = self.parse_block();
@@ -2875,6 +2877,23 @@ impl<'a> Parser<'a> {
         self.mk_expr(lo, hi, ExprIf(cond, thn, els))
     }
 
+    /// Parse an 'if let' expression ('if' token already eaten)
+    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();
+        self.expect(&token::EQ);
+        let expr = self.parse_expr_res(RestrictionNoStructLiteral);
+        let thn = self.parse_block();
+        let (hi, els) = if self.eat_keyword(keywords::Else) {
+            let expr = self.parse_else_expr();
+            (expr.span.hi, Some(expr))
+        } else {
+            (thn.span.hi, None)
+        };
+        self.mk_expr(lo, hi, ExprIfLet(pat, expr, thn, els))
+    }
+
     // `|args| expr`
     pub fn parse_lambda_expr(&mut self, capture_clause: CaptureClause)
                              -> P<Expr> {
@@ -2956,7 +2975,7 @@ impl<'a> Parser<'a> {
         }
         let hi = self.span.hi;
         self.bump();
-        return self.mk_expr(lo, hi, ExprMatch(discriminant, arms));
+        return self.mk_expr(lo, hi, ExprMatch(discriminant, arms, MatchNormal));
     }
 
     pub fn parse_arm(&mut self) -> Arm {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index ae4ba611bab..a8e99b4e85f 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1307,6 +1307,19 @@ impl<'a> State<'a> {
                         try!(self.print_block(&**then));
                         self.print_else(e.as_ref().map(|e| &**e))
                     }
+                    // "another else-if-let"
+                    ast::ExprIfLet(ref pat, ref expr, ref then, ref e) => {
+                        try!(self.cbox(indent_unit - 1u));
+                        try!(self.ibox(0u));
+                        try!(word(&mut self.s, " else if let "));
+                        try!(self.print_pat(&**pat));
+                        try!(space(&mut self.s));
+                        try!(self.word_space("="));
+                        try!(self.print_expr(&**expr));
+                        try!(space(&mut self.s));
+                        try!(self.print_block(&**then));
+                        self.print_else(e.as_ref().map(|e| &**e))
+                    }
                     // "final else"
                     ast::ExprBlock(ref b) => {
                         try!(self.cbox(indent_unit - 1u));
@@ -1325,15 +1338,26 @@ impl<'a> State<'a> {
     }
 
     pub fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block,
-                    elseopt: Option<&ast::Expr>, chk: bool) -> IoResult<()> {
+                    elseopt: Option<&ast::Expr>) -> IoResult<()> {
         try!(self.head("if"));
-        if chk { try!(self.word_nbsp("check")); }
         try!(self.print_expr(test));
         try!(space(&mut self.s));
         try!(self.print_block(blk));
         self.print_else(elseopt)
     }
 
+    pub fn print_if_let(&mut self, pat: &ast::Pat, expr: &ast::Expr, blk: &ast::Block,
+                        elseopt: Option<&ast::Expr>) -> IoResult<()> {
+        try!(self.head("if let"));
+        try!(self.print_pat(pat));
+        try!(space(&mut self.s));
+        try!(self.word_space("="));
+        try!(self.print_expr(expr));
+        try!(space(&mut self.s));
+        try!(self.print_block(blk));
+        self.print_else(elseopt)
+    }
+
     pub fn print_mac(&mut self, m: &ast::Mac) -> IoResult<()> {
         match m.node {
             // I think it's reasonable to hide the ctxt here:
@@ -1474,7 +1498,10 @@ impl<'a> State<'a> {
                 try!(self.print_type(&**ty));
             }
             ast::ExprIf(ref test, ref blk, ref elseopt) => {
-                try!(self.print_if(&**test, &**blk, elseopt.as_ref().map(|e| &**e), false));
+                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::ExprWhile(ref test, ref blk, opt_ident) => {
                 for ident in opt_ident.iter() {
@@ -1508,7 +1535,7 @@ impl<'a> State<'a> {
                 try!(space(&mut self.s));
                 try!(self.print_block(&**blk));
             }
-            ast::ExprMatch(ref expr, ref arms) => {
+            ast::ExprMatch(ref expr, ref arms, _) => {
                 try!(self.cbox(indent_unit));
                 try!(self.ibox(4));
                 try!(self.word_nbsp("match"));
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 3b2ed30b76d..6fc79e2c42a 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -730,13 +730,19 @@ 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);
+            visitor.visit_expr(&**subexpression);
+            visitor.visit_block(&**if_block);
+            walk_expr_opt(visitor, optional_else);
+        }
         ExprForLoop(ref pattern, ref subexpression, ref block, _) => {
             visitor.visit_pat(&**pattern);
             visitor.visit_expr(&**subexpression);
             visitor.visit_block(&**block)
         }
         ExprLoop(ref block, _) => visitor.visit_block(&**block),
-        ExprMatch(ref subexpression, ref arms) => {
+        ExprMatch(ref subexpression, ref arms, _) => {
             visitor.visit_expr(&**subexpression);
             for arm in arms.iter() {
                 visitor.visit_arm(arm)