auto merge of #17634 : jakub-/rust/if_let, r=kballard

Continuation of https://github.com/rust-lang/rust/pull/16741.
This commit is contained in:
bors 2014-09-30 23:17:20 +00:00
commit 57a05cf49b
28 changed files with 402 additions and 37 deletions

View File

@ -2441,6 +2441,8 @@ The currently implemented features of the reference compiler are:
* `default_type_params` - Allows use of default type parameters. The future of
this feature is uncertain.
* `if_let` - Allows use of the `if let` syntax.
* `intrinsics` - Allows use of the "rust-intrinsics" ABI. Compiler intrinsics
are inherently unstable and no promise about them is made.
@ -3229,7 +3231,7 @@ for i in range(0u, 256) {
if_expr : "if" no_struct_literal_expr '{' block '}'
else_tail ? ;
else_tail : "else" [ if_expr
else_tail : "else" [ if_expr | if_let_expr
| '{' block '}' ] ;
```
@ -3434,6 +3436,19 @@ let message = match maybe_digit {
};
```
### If let expressions
```{.ebnf .gram}
if_let_expr : "if" "let" pat '=' expr '{' block '}'
else_tail ? ;
else_tail : "else" [ if_expr | if_let_expr | '{' block '}' ] ;
```
An `if let` expression is semantically identical to an `if` expression but in place
of a condition expression it expects a refutable let statement. If the value of the
expression on the right hand side of the let statement matches the pattern, the corresponding
block will execute, otherwise flow proceeds to the first `else` block that follows.
### Return expressions
```{.ebnf .gram}

View File

@ -151,5 +151,6 @@ register_diagnostics!(
E0157,
E0158,
E0159,
E0161
E0161,
E0162
)

View File

@ -1092,7 +1092,10 @@ impl LintPass for UnnecessaryParens {
let (value, msg, struct_lit_needs_parens) = match e.node {
ast::ExprIf(ref cond, _, _) => (cond, "`if` condition", true),
ast::ExprWhile(ref cond, _, _) => (cond, "`while` condition", true),
ast::ExprMatch(ref head, _) => (head, "`match` head expression", true),
ast::ExprMatch(ref head, _, source) => match source {
ast::MatchNormal => (head, "`match` head expression", true),
ast::MatchIfLetDesugar => (head, "`if let` head expression", true)
},
ast::ExprRet(Some(ref value)) => (value, "`return` value", false),
ast::ExprAssign(_, ref value) => (value, "assigned value", false),
ast::ExprAssignOp(_, _, ref value) => (value, "assigned value", false),
@ -1242,7 +1245,7 @@ impl LintPass for UnusedMut {
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
match e.node {
ast::ExprMatch(_, ref arms) => {
ast::ExprMatch(_, ref arms, _) => {
for a in arms.iter() {
self.check_unused_mut_pat(cx, a.pats.as_slice())
}

View File

@ -222,6 +222,10 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
self.add_node(expr.id, [then_exit, else_exit]) // 4, 5
}
ast::ExprIfLet(..) => {
self.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
}
ast::ExprWhile(ref cond, ref body, _) => {
//
// [pred]
@ -322,7 +326,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
expr_exit
}
ast::ExprMatch(ref discr, ref arms) => {
ast::ExprMatch(ref discr, ref arms, _) => {
//
// [pred]
// |

View File

@ -147,7 +147,7 @@ pub fn check_crate(tcx: &ty::ctxt) {
fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
visit::walk_expr(cx, ex);
match ex.node {
ExprMatch(ref scrut, ref arms) => {
ExprMatch(ref scrut, ref arms, source) => {
// First, check legality of move bindings.
for arm in arms.iter() {
check_legality_of_move_bindings(cx,
@ -184,7 +184,7 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
}
// Fourth, check for unreachable arms.
check_arms(cx, inlined_arms.as_slice());
check_arms(cx, inlined_arms.as_slice(), source);
// Finally, check if the whole match expression is exhaustive.
// Check for empty enum, because is_useful only works on inhabited types.
@ -252,13 +252,31 @@ fn check_for_static_nan(cx: &MatchCheckCtxt, pats: &[P<Pat>]) {
}
// Check for unreachable patterns
fn check_arms(cx: &MatchCheckCtxt, arms: &[(Vec<P<Pat>>, Option<&Expr>)]) {
fn check_arms(cx: &MatchCheckCtxt, arms: &[(Vec<P<Pat>>, Option<&Expr>)], source: MatchSource) {
let mut seen = Matrix(vec![]);
let mut printed_if_let_err = false;
for &(ref pats, guard) in arms.iter() {
for pat in pats.iter() {
let v = vec![&**pat];
match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) {
NotUseful => span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern"),
NotUseful => {
if source == MatchIfLetDesugar {
if printed_if_let_err {
// we already printed an irrefutable if-let pattern error.
// We don't want two, that's just confusing.
} else {
// find the first arm pattern so we can use its span
let &(ref first_arm_pats, _) = &arms[0];
let first_pat = first_arm_pats.get(0);
let span = first_pat.span;
span_err!(cx.tcx.sess, span, E0162, "irrefutable if-let pattern");
printed_if_let_err = true;
}
} else {
span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern");
}
}
Useful => (),
UsefulWithWitness(_) => unreachable!()
}

View File

@ -374,7 +374,11 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,TYPER> {
}
}
ast::ExprMatch(ref discr, ref arms) => {
ast::ExprIfLet(..) => {
self.tcx().sess.span_bug(expr.span, "non-desugared ExprIfLet");
}
ast::ExprMatch(ref discr, ref arms, _) => {
let discr_cmt = return_if_err!(self.mc.cat_expr(&**discr));
self.borrow_expr(&**discr, ty::ReEmpty, ty::ImmBorrow, MatchDiscriminant);

View File

@ -481,6 +481,9 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
visit::walk_expr(ir, expr);
}
ExprIfLet(..) => {
ir.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
}
ExprForLoop(ref pat, _, _, _) => {
pat_util::pat_bindings(&ir.tcx.def_map, &**pat, |bm, p_id, sp, path1| {
debug!("adding local variable {} from for loop with bm {:?}",
@ -1011,6 +1014,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
self.propagate_through_expr(&**cond, ln)
}
ExprIfLet(..) => {
self.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
}
ExprWhile(ref cond, ref blk, _) => {
self.propagate_through_loop(expr, WhileLoop(&**cond), &**blk, succ)
}
@ -1026,7 +1033,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
self.propagate_through_loop(expr, LoopLoop, &**blk, succ)
}
ExprMatch(ref e, ref arms) => {
ExprMatch(ref e, ref arms, _) => {
//
// (e)
// |
@ -1470,6 +1477,9 @@ fn check_expr(this: &mut Liveness, expr: &Expr) {
ExprPath(..) | ExprBox(..) => {
visit::walk_expr(this, expr);
}
ExprIfLet(..) => {
this.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
}
}
}

View File

@ -505,6 +505,10 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
ast::ExprForLoop(..) => {
Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty))
}
ast::ExprIfLet(..) => {
self.tcx().sess.span_bug(expr.span, "non-desugared ExprIfLet");
}
}
}

View File

@ -3576,6 +3576,11 @@ fn populate_scope_map(cx: &CrateContext,
}
}
ast::ExprIfLet(..) => {
cx.sess().span_bug(exp.span, "debuginfo::populate_scope_map() - \
Found unexpanded if-let.");
}
ast::ExprWhile(ref cond_exp, ref loop_body, _) => {
walk_expr(cx, &**cond_exp, scope_stack, scope_map);
@ -3654,7 +3659,7 @@ fn populate_scope_map(cx: &CrateContext,
}
}
ast::ExprMatch(ref discriminant_exp, ref arms) => {
ast::ExprMatch(ref discriminant_exp, ref arms, _) => {
walk_expr(cx, &**discriminant_exp, scope_stack, scope_map);
// For each arm we have to first walk the pattern as these might

View File

@ -1013,7 +1013,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
ast::ExprIf(ref cond, ref thn, ref els) => {
controlflow::trans_if(bcx, expr.id, &**cond, &**thn, els.as_ref().map(|e| &**e), dest)
}
ast::ExprMatch(ref discr, ref arms) => {
ast::ExprMatch(ref discr, ref arms, _) => {
_match::trans_match(bcx, expr, &**discr, arms.as_slice(), dest)
}
ast::ExprBlock(ref blk) => {

View File

@ -3635,6 +3635,10 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind {
RvalueDpsExpr
}
ast::ExprIfLet(..) => {
tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
}
ast::ExprLit(ref lit) if lit_is_str(&**lit) => {
RvalueDpsExpr
}

View File

@ -4106,6 +4106,9 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
check_then_else(fcx, &**cond, &**then_blk, opt_else_expr.as_ref().map(|e| &**e),
id, expr.span, expected);
}
ast::ExprIfLet(..) => {
tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
}
ast::ExprWhile(ref cond, ref body, _) => {
check_expr_has_type(fcx, &**cond, ty::mk_bool());
check_block_no_value(fcx, &**body);
@ -4143,7 +4146,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
fcx.write_nil(id);
}
}
ast::ExprMatch(ref discrim, ref arms) => {
ast::ExprMatch(ref discrim, ref arms, _) => {
_match::check_match(fcx, expr, &**discrim, arms.as_slice());
}
ast::ExprFnBlock(_, ref decl, ref body) => {

View File

@ -725,7 +725,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
visit::walk_expr(rcx, expr);
}
ast::ExprMatch(ref discr, ref arms) => {
ast::ExprMatch(ref discr, ref arms, _) => {
link_match(rcx, &**discr, arms.as_slice());
visit::walk_expr(rcx, expr);

View File

@ -92,6 +92,7 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
ast::ExprMethodCall(..) => {
explain_span(cx, "method call", expr.span)
},
ast::ExprMatch(_, _, ast::MatchIfLetDesugar) => explain_span(cx, "if let", expr.span),
ast::ExprMatch(..) => explain_span(cx, "match", expr.span),
_ => explain_span(cx, "expression", expr.span)
}

View File

@ -293,6 +293,7 @@ mod svh_visitor {
ExprForLoop(..) => SawExprForLoop,
// just syntactic artifacts, expanded away by time of SVH.
ExprIfLet(..) => unreachable!(),
ExprMac(..) => unreachable!(),
}
}

View File

@ -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>),
@ -576,6 +577,12 @@ pub struct QPath {
pub item_name: Ident,
}
#[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,

View File

@ -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
},

View File

@ -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>,

View File

@ -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))

View File

@ -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);

View File

@ -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,

View File

@ -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(..)

View File

@ -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 {

View File

@ -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"));

View File

@ -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)

View File

@ -0,0 +1,57 @@
// Copyright 2014 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.
#![feature(macro_rules,if_let)]
fn macros() {
macro_rules! foo{
($p:pat, $e:expr, $b:block) => {{
if let $p = $e $b
}}
}
macro_rules! bar{
($p:pat, $e:expr, $b:block) => {{
foo!($p, $e, $b)
}}
}
foo!(a, 1i, { //~ ERROR irrefutable if-let
println!("irrefutable pattern");
});
bar!(a, 1i, { //~ ERROR irrefutable if-let
println!("irrefutable pattern");
});
}
pub fn main() {
if let a = 1i { //~ ERROR irrefutable if-let
println!("irrefutable pattern");
}
if let a = 1i { //~ ERROR irrefutable if-let
println!("irrefutable pattern");
} else if true {
println!("else-if in irrefutable if-let");
} else {
println!("else in irrefutable if-let");
}
if let 1i = 2i {
println!("refutable pattern");
} else if let a = 1i { //~ ERROR irrefutable if-let
println!("irrefutable pattern");
}
if true {
println!("if");
} else if let a = 1i { //~ ERROR irrefutable if-let
println!("irrefutable pattern");
}
}

View File

@ -9,6 +9,7 @@
// except according to those terms.
#![deny(unnecessary_parens)]
#![feature(if_let)]
#[deriving(Eq, PartialEq)]
struct X { y: bool }
@ -32,6 +33,7 @@ fn main() {
match (true) { //~ ERROR unnecessary parentheses around `match` head expression
_ => {}
}
if let 1i = (1i) {} //~ ERROR unnecessary parentheses around `if let` head expression
let v = X { y: false };
// struct lits needs parens, so these shouldn't warn.
if (v == X { y: true }) {}

View File

@ -0,0 +1,69 @@
// Copyright 2014 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.
#![feature(if_let)]
pub fn main() {
let x = Some(3i);
if let Some(y) = x {
assert_eq!(y, 3i);
} else {
fail!("if-let failed");
}
let mut worked = false;
if let Some(_) = x {
worked = true;
}
assert!(worked);
let clause: uint;
if let None = Some("test") {
clause = 1;
} else if 4u > 5 {
clause = 2;
} else if let Ok(()) = Err::<(),&'static str>("test") {
clause = 3;
} else {
clause = 4;
}
assert_eq!(clause, 4u);
if 3i > 4 {
fail!("bad math");
} else if let 1 = 2i {
fail!("bad pattern match");
}
enum Foo {
One,
Two(uint),
Three(String, int)
}
let foo = Three("three".to_string(), 42i);
if let One = foo {
fail!("bad pattern match");
} else if let Two(_x) = foo {
fail!("bad pattern match");
} else if let Three(s, _) = foo {
assert_eq!(s.as_slice(), "three");
} else {
fail!("bad else");
}
if false {
fail!("wat");
} else if let a@Two(_) = Two(42u) {
if let Two(b) = a {
assert_eq!(b, 42u);
} else {
fail!("fail in nested if-let");
}
}
}