mirror of https://github.com/rust-lang/rust.git
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:
commit
57a05cf49b
|
@ -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}
|
||||
|
|
|
@ -151,5 +151,6 @@ register_diagnostics!(
|
|||
E0157,
|
||||
E0158,
|
||||
E0159,
|
||||
E0161
|
||||
E0161,
|
||||
E0162
|
||||
)
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
// |
|
||||
|
|
|
@ -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!()
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -293,6 +293,7 @@ mod svh_visitor {
|
|||
ExprForLoop(..) => SawExprForLoop,
|
||||
|
||||
// just syntactic artifacts, expanded away by time of SVH.
|
||||
ExprIfLet(..) => unreachable!(),
|
||||
ExprMac(..) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
},
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(..)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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 }) {}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue