syntax: implement foreach .. in .. { .. } via desugaring.
This commit is contained in:
parent
9a2d183d6a
commit
c29e9fb60b
17 changed files with 215 additions and 3 deletions
|
@ -239,6 +239,8 @@ impl CFGBuilder {
|
||||||
expr_exit
|
expr_exit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||||
|
|
||||||
ast::expr_loop(ref body, _) => {
|
ast::expr_loop(ref body, _) => {
|
||||||
//
|
//
|
||||||
// [pred]
|
// [pred]
|
||||||
|
|
|
@ -583,6 +583,8 @@ impl<'self, O:DataFlowOperator> PropagationContext<'self, O> {
|
||||||
copy_bits(new_loop_scope.break_bits, in_out);
|
copy_bits(new_loop_scope.break_bits, in_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||||
|
|
||||||
ast::expr_loop(ref blk, _) => {
|
ast::expr_loop(ref blk, _) => {
|
||||||
//
|
//
|
||||||
// (expr) <--+
|
// (expr) <--+
|
||||||
|
|
|
@ -503,6 +503,7 @@ fn visit_expr(expr: @expr, (this, vt): (@mut IrMaps, vt<@mut IrMaps>)) {
|
||||||
this.add_live_node_for_node(expr.id, ExprNode(expr.span));
|
this.add_live_node_for_node(expr.id, ExprNode(expr.span));
|
||||||
visit::visit_expr(expr, (this, vt));
|
visit::visit_expr(expr, (this, vt));
|
||||||
}
|
}
|
||||||
|
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||||
expr_binary(_, op, _, _) if ast_util::lazy_binop(op) => {
|
expr_binary(_, op, _, _) if ast_util::lazy_binop(op) => {
|
||||||
this.add_live_node_for_node(expr.id, ExprNode(expr.span));
|
this.add_live_node_for_node(expr.id, ExprNode(expr.span));
|
||||||
visit::visit_expr(expr, (this, vt));
|
visit::visit_expr(expr, (this, vt));
|
||||||
|
@ -1057,6 +1058,8 @@ impl Liveness {
|
||||||
self.propagate_through_loop(expr, Some(cond), blk, succ)
|
self.propagate_through_loop(expr, Some(cond), blk, succ)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||||
|
|
||||||
// Note that labels have been resolved, so we don't need to look
|
// Note that labels have been resolved, so we don't need to look
|
||||||
// at the label ident
|
// at the label ident
|
||||||
expr_loop(ref blk, _) => {
|
expr_loop(ref blk, _) => {
|
||||||
|
@ -1487,6 +1490,7 @@ fn check_expr(expr: @expr, (this, vt): (@Liveness, vt<@Liveness>)) {
|
||||||
expr_paren(*) | expr_fn_block(*) | expr_path(*) | expr_self(*) => {
|
expr_paren(*) | expr_fn_block(*) | expr_path(*) | expr_self(*) => {
|
||||||
visit::visit_expr(expr, (this, vt));
|
visit::visit_expr(expr, (this, vt));
|
||||||
}
|
}
|
||||||
|
expr_for_loop(*) => fail!("non-desugared expr_for_loop")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -435,6 +435,8 @@ impl mem_categorization_ctxt {
|
||||||
ast::expr_inline_asm(*) => {
|
ast::expr_inline_asm(*) => {
|
||||||
return self.cat_rvalue_node(expr, expr_ty);
|
return self.cat_rvalue_node(expr, expr_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -487,6 +487,8 @@ impl VisitContext {
|
||||||
self.consume_block(blk, visitor);
|
self.consume_block(blk, visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||||
|
|
||||||
expr_unary(_, _, lhs) => {
|
expr_unary(_, _, lhs) => {
|
||||||
if !self.use_overloaded_operator(
|
if !self.use_overloaded_operator(
|
||||||
expr, lhs, [], visitor)
|
expr, lhs, [], visitor)
|
||||||
|
|
|
@ -5016,6 +5016,8 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||||
|
|
||||||
expr_break(Some(label)) | expr_again(Some(label)) => {
|
expr_break(Some(label)) | expr_again(Some(label)) => {
|
||||||
match self.search_ribs(self.label_ribs, label, expr.span,
|
match self.search_ribs(self.label_ribs, label, expr.span,
|
||||||
DontAllowCapturingSelf) {
|
DontAllowCapturingSelf) {
|
||||||
|
|
|
@ -401,7 +401,9 @@ pub fn mark_for_expr(cx: &Context, e: &expr) {
|
||||||
expr_match(*) | expr_block(_) | expr_if(*) | expr_while(*) |
|
expr_match(*) | expr_block(_) | expr_if(*) | expr_while(*) |
|
||||||
expr_break(_) | expr_again(_) | expr_unary(*) | expr_lit(_) |
|
expr_break(_) | expr_again(_) | expr_unary(*) | expr_lit(_) |
|
||||||
expr_mac(_) | expr_addr_of(*) | expr_ret(_) | expr_loop(*) |
|
expr_mac(_) | expr_addr_of(*) | expr_ret(_) | expr_loop(*) |
|
||||||
expr_loop_body(_) | expr_do_body(_) => ()
|
expr_loop_body(_) | expr_do_body(_) => (),
|
||||||
|
|
||||||
|
expr_for_loop(*) => fail!("non-desugared expr_for_loop")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3240,6 +3240,8 @@ pub fn expr_kind(tcx: ctxt,
|
||||||
RvalueStmtExpr
|
RvalueStmtExpr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||||
|
|
||||||
ast::expr_lit(_) | // Note: lit_str is carved out above
|
ast::expr_lit(_) | // Note: lit_str is carved out above
|
||||||
ast::expr_unary(*) |
|
ast::expr_unary(*) |
|
||||||
ast::expr_addr_of(*) |
|
ast::expr_addr_of(*) |
|
||||||
|
|
|
@ -2559,6 +2559,8 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
|
||||||
fcx.write_nil(id);
|
fcx.write_nil(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ast::expr_for_loop(*) =>
|
||||||
|
fail!("non-desugared expr_for_loop"),
|
||||||
ast::expr_loop(ref body, _) => {
|
ast::expr_loop(ref body, _) => {
|
||||||
check_block_no_value(fcx, (body));
|
check_block_no_value(fcx, (body));
|
||||||
if !may_break(tcx, expr.id, body) {
|
if !may_break(tcx, expr.id, body) {
|
||||||
|
|
|
@ -1041,6 +1041,7 @@ pub mod guarantor {
|
||||||
rcx.fcx.tcx(), rcx.fcx.inh.method_map, expr));
|
rcx.fcx.tcx(), rcx.fcx.inh.method_map, expr));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -465,6 +465,7 @@ pub enum expr_ {
|
||||||
expr_cast(@expr, Ty),
|
expr_cast(@expr, Ty),
|
||||||
expr_if(@expr, Block, Option<@expr>),
|
expr_if(@expr, Block, Option<@expr>),
|
||||||
expr_while(@expr, Block),
|
expr_while(@expr, Block),
|
||||||
|
expr_for_loop(@pat, @expr, Block),
|
||||||
/* Conditionless loop (can be exited with break, cont, or ret)
|
/* Conditionless loop (can be exited with break, cont, or ret)
|
||||||
Same semantics as while(true) { body }, but typestate knows that the
|
Same semantics as while(true) { body }, but typestate knows that the
|
||||||
(implicit) condition is always true. */
|
(implicit) condition is always true. */
|
||||||
|
|
|
@ -16,11 +16,12 @@ use ast_util::{new_rename, new_mark, resolve};
|
||||||
use attr;
|
use attr;
|
||||||
use attr::AttrMetaMethods;
|
use attr::AttrMetaMethods;
|
||||||
use codemap;
|
use codemap;
|
||||||
use codemap::{span, ExpnInfo, NameAndSpan};
|
use codemap::{span, spanned, ExpnInfo, NameAndSpan};
|
||||||
use ext::base::*;
|
use ext::base::*;
|
||||||
use fold::*;
|
use fold::*;
|
||||||
use parse;
|
use parse;
|
||||||
use parse::{parse_item_from_source_str};
|
use parse::{parse_item_from_source_str};
|
||||||
|
use parse::token;
|
||||||
use parse::token::{ident_to_str, intern};
|
use parse::token::{ident_to_str, intern};
|
||||||
use visit;
|
use visit;
|
||||||
use visit::Visitor;
|
use visit::Visitor;
|
||||||
|
@ -99,6 +100,159 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Desugar expr_for_loop
|
||||||
|
// From: `foreach <src_pat> in <src_expr> <src_loop_block>`
|
||||||
|
ast::expr_for_loop(src_pat, src_expr, ref src_loop_block) => {
|
||||||
|
let src_pat = src_pat.clone();
|
||||||
|
let src_expr = src_expr.clone();
|
||||||
|
|
||||||
|
// Expand any interior macros etc.
|
||||||
|
// NB: we don't fold pats yet. Curious.
|
||||||
|
let src_expr = fld.fold_expr(src_expr).clone();
|
||||||
|
let src_loop_block = fld.fold_block(src_loop_block).clone();
|
||||||
|
|
||||||
|
let span = s;
|
||||||
|
let lo = s.lo;
|
||||||
|
let hi = s.hi;
|
||||||
|
|
||||||
|
pub fn mk_expr(cx: @ExtCtxt, span: span,
|
||||||
|
node: expr_) -> @ast::expr {
|
||||||
|
@ast::expr {
|
||||||
|
id: cx.next_id(),
|
||||||
|
node: node,
|
||||||
|
span: span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mk_block(cx: @ExtCtxt,
|
||||||
|
stmts: &[@ast::stmt],
|
||||||
|
expr: Option<@ast::expr>,
|
||||||
|
span: span) -> ast::Block {
|
||||||
|
ast::Block {
|
||||||
|
view_items: ~[],
|
||||||
|
stmts: stmts.to_owned(),
|
||||||
|
expr: expr,
|
||||||
|
id: cx.next_id(),
|
||||||
|
rules: ast::DefaultBlock,
|
||||||
|
span: span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mk_simple_path(ident: ast::ident, span: span) -> ast::Path {
|
||||||
|
ast::Path {
|
||||||
|
span: span,
|
||||||
|
global: false,
|
||||||
|
idents: ~[ident],
|
||||||
|
rp: None,
|
||||||
|
types: ~[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// to:
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let _i = &mut <src_expr>;
|
||||||
|
// loop {
|
||||||
|
// match i.next() {
|
||||||
|
// None => break,
|
||||||
|
// Some(<src_pat>) => <src_loop_block>
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
let local_ident = token::gensym_ident("i");
|
||||||
|
let some_ident = token::str_to_ident("Some");
|
||||||
|
let none_ident = token::str_to_ident("None");
|
||||||
|
let next_ident = token::str_to_ident("next");
|
||||||
|
|
||||||
|
let local_path_1 = mk_simple_path(local_ident, span);
|
||||||
|
let local_path_2 = mk_simple_path(local_ident, span);
|
||||||
|
let some_path = mk_simple_path(some_ident, span);
|
||||||
|
let none_path = mk_simple_path(none_ident, span);
|
||||||
|
|
||||||
|
// `let i = &mut <src_expr>`
|
||||||
|
let iter_decl_stmt = {
|
||||||
|
let ty = ast::Ty {
|
||||||
|
id: cx.next_id(),
|
||||||
|
node: ast::ty_infer,
|
||||||
|
span: span
|
||||||
|
};
|
||||||
|
let local = @ast::Local {
|
||||||
|
is_mutbl: false,
|
||||||
|
ty: ty,
|
||||||
|
pat: @ast::pat {
|
||||||
|
id: cx.next_id(),
|
||||||
|
node: ast::pat_ident(ast::bind_infer, local_path_1, None),
|
||||||
|
span: src_expr.span
|
||||||
|
},
|
||||||
|
init: Some(mk_expr(cx, src_expr.span,
|
||||||
|
ast::expr_addr_of(ast::m_mutbl, src_expr))),
|
||||||
|
id: cx.next_id(),
|
||||||
|
span: src_expr.span,
|
||||||
|
};
|
||||||
|
let e = @spanned(src_expr.span.lo,
|
||||||
|
src_expr.span.hi,
|
||||||
|
ast::decl_local(local));
|
||||||
|
@spanned(lo, hi, ast::stmt_decl(e, cx.next_id()))
|
||||||
|
};
|
||||||
|
|
||||||
|
// `None => break;`
|
||||||
|
let none_arm = {
|
||||||
|
let break_expr = mk_expr(cx, span, ast::expr_break(None));
|
||||||
|
let break_stmt = @spanned(lo, hi, ast::stmt_expr(break_expr, cx.next_id()));
|
||||||
|
let none_block = mk_block(cx, [break_stmt], None, span);
|
||||||
|
let none_pat = @ast::pat {
|
||||||
|
id: cx.next_id(),
|
||||||
|
node: ast::pat_ident(ast::bind_infer, none_path, None),
|
||||||
|
span: span
|
||||||
|
};
|
||||||
|
ast::arm {
|
||||||
|
pats: ~[none_pat],
|
||||||
|
guard: None,
|
||||||
|
body: none_block
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// `Some(<src_pat>) => <src_loop_block>`
|
||||||
|
let some_arm = {
|
||||||
|
let pat = @ast::pat {
|
||||||
|
id: cx.next_id(),
|
||||||
|
node: ast::pat_enum(some_path, Some(~[src_pat])),
|
||||||
|
span: src_pat.span
|
||||||
|
};
|
||||||
|
ast::arm {
|
||||||
|
pats: ~[pat],
|
||||||
|
guard: None,
|
||||||
|
body: src_loop_block
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// `match i.next() { ... }`
|
||||||
|
let match_stmt = {
|
||||||
|
let local_expr = mk_expr(cx, span, ast::expr_path(local_path_2));
|
||||||
|
let next_call_expr = mk_expr(cx, span,
|
||||||
|
ast::expr_method_call(cx.next_id(),
|
||||||
|
local_expr, next_ident,
|
||||||
|
~[], ~[], ast::NoSugar));
|
||||||
|
let match_expr = mk_expr(cx, span, ast::expr_match(next_call_expr,
|
||||||
|
~[none_arm, some_arm]));
|
||||||
|
@spanned(lo, hi, ast::stmt_expr(match_expr, cx.next_id()))
|
||||||
|
};
|
||||||
|
|
||||||
|
// `loop { ... }`
|
||||||
|
let loop_block = {
|
||||||
|
let loop_body_block = mk_block(cx, [match_stmt], None, span);
|
||||||
|
let loop_body_expr = mk_expr(cx, span, ast::expr_loop(loop_body_block, None));
|
||||||
|
let loop_body_stmt = @spanned(lo, hi, ast::stmt_expr(loop_body_expr, cx.next_id()));
|
||||||
|
mk_block(cx, [iter_decl_stmt,
|
||||||
|
loop_body_stmt],
|
||||||
|
None, span)
|
||||||
|
};
|
||||||
|
|
||||||
|
(ast::expr_block(loop_block), span)
|
||||||
|
}
|
||||||
|
|
||||||
_ => orig(e, s, fld)
|
_ => orig(e, s, fld)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -559,6 +559,11 @@ pub fn noop_fold_expr(e: &expr_, fld: @ast_fold) -> expr_ {
|
||||||
expr_while(cond, ref body) => {
|
expr_while(cond, ref body) => {
|
||||||
expr_while(fld.fold_expr(cond), fld.fold_block(body))
|
expr_while(fld.fold_expr(cond), fld.fold_block(body))
|
||||||
}
|
}
|
||||||
|
expr_for_loop(pat, iter, ref body) => {
|
||||||
|
expr_for_loop(fld.fold_pat(pat),
|
||||||
|
fld.fold_expr(iter),
|
||||||
|
fld.fold_block(body))
|
||||||
|
}
|
||||||
expr_loop(ref body, opt_ident) => {
|
expr_loop(ref body, opt_ident) => {
|
||||||
expr_loop(
|
expr_loop(
|
||||||
fld.fold_block(body),
|
fld.fold_block(body),
|
||||||
|
|
|
@ -28,6 +28,7 @@ pub fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool {
|
||||||
| ast::expr_block(_)
|
| ast::expr_block(_)
|
||||||
| ast::expr_while(*)
|
| ast::expr_while(*)
|
||||||
| ast::expr_loop(*)
|
| ast::expr_loop(*)
|
||||||
|
| ast::expr_for_loop(*)
|
||||||
| ast::expr_call(_, _, ast::DoSugar)
|
| ast::expr_call(_, _, ast::DoSugar)
|
||||||
| ast::expr_call(_, _, ast::ForSugar)
|
| ast::expr_call(_, _, ast::ForSugar)
|
||||||
| ast::expr_method_call(_, _, _, _, _, ast::DoSugar)
|
| ast::expr_method_call(_, _, _, _, _, ast::DoSugar)
|
||||||
|
|
|
@ -29,7 +29,7 @@ use ast::{expr_method_call, expr_paren, expr_path, expr_repeat};
|
||||||
use ast::{expr_ret, expr_self, expr_struct, expr_tup, expr_unary};
|
use ast::{expr_ret, expr_self, expr_struct, expr_tup, expr_unary};
|
||||||
use ast::{expr_vec, expr_vstore, expr_vstore_mut_box};
|
use ast::{expr_vec, expr_vstore, expr_vstore_mut_box};
|
||||||
use ast::{expr_vstore_slice, expr_vstore_box};
|
use ast::{expr_vstore_slice, expr_vstore_box};
|
||||||
use ast::{expr_vstore_mut_slice, expr_while, extern_fn, Field, fn_decl};
|
use ast::{expr_vstore_mut_slice, expr_while, expr_for_loop, extern_fn, Field, fn_decl};
|
||||||
use ast::{expr_vstore_uniq, Onceness, Once, Many};
|
use ast::{expr_vstore_uniq, Onceness, Once, Many};
|
||||||
use ast::{foreign_item, foreign_item_static, foreign_item_fn, foreign_mod};
|
use ast::{foreign_item, foreign_item_static, foreign_item_fn, foreign_mod};
|
||||||
use ast::{ident, impure_fn, inherited, item, item_, item_static};
|
use ast::{ident, impure_fn, inherited, item, item_, item_static};
|
||||||
|
@ -1622,6 +1622,8 @@ impl Parser {
|
||||||
hi = self.span.hi;
|
hi = self.span.hi;
|
||||||
} else if self.eat_keyword(keywords::If) {
|
} else if self.eat_keyword(keywords::If) {
|
||||||
return self.parse_if_expr();
|
return self.parse_if_expr();
|
||||||
|
} else if self.eat_keyword(keywords::ForEach) {
|
||||||
|
return self.parse_for_expr();
|
||||||
} else if self.eat_keyword(keywords::For) {
|
} else if self.eat_keyword(keywords::For) {
|
||||||
return self.parse_sugary_call_expr(lo, ~"for", ForSugar,
|
return self.parse_sugary_call_expr(lo, ~"for", ForSugar,
|
||||||
expr_loop_body);
|
expr_loop_body);
|
||||||
|
@ -2323,6 +2325,21 @@ impl Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse a 'foreach' .. 'in' expression ('foreach' token already eaten)
|
||||||
|
pub fn parse_for_expr(&self) -> @expr {
|
||||||
|
// Parse: `foreach <src_pat> in <src_expr> <src_loop_block>`
|
||||||
|
|
||||||
|
let lo = self.last_span.lo;
|
||||||
|
let pat = self.parse_pat();
|
||||||
|
self.expect_keyword(keywords::In);
|
||||||
|
let expr = self.parse_expr();
|
||||||
|
let loop_block = self.parse_block();
|
||||||
|
let hi = self.span.hi;
|
||||||
|
|
||||||
|
self.mk_expr(lo, hi, expr_for_loop(pat, expr, loop_block))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// parse a 'for' or 'do'.
|
// parse a 'for' or 'do'.
|
||||||
// the 'for' and 'do' expressions parse as calls, but look like
|
// the 'for' and 'do' expressions parse as calls, but look like
|
||||||
// function calls followed by a closure expression.
|
// function calls followed by a closure expression.
|
||||||
|
|
|
@ -1228,6 +1228,14 @@ pub fn print_expr(s: @ps, expr: &ast::expr) {
|
||||||
space(s.s);
|
space(s.s);
|
||||||
print_block(s, blk);
|
print_block(s, blk);
|
||||||
}
|
}
|
||||||
|
ast::expr_for_loop(pat, iter, ref blk) => {
|
||||||
|
head(s, "foreach");
|
||||||
|
print_pat(s, pat);
|
||||||
|
word_space(s, "in");
|
||||||
|
print_expr(s, iter);
|
||||||
|
space(s.s);
|
||||||
|
print_block(s, blk);
|
||||||
|
}
|
||||||
ast::expr_loop(ref blk, opt_ident) => {
|
ast::expr_loop(ref blk, opt_ident) => {
|
||||||
for opt_ident.iter().advance |ident| {
|
for opt_ident.iter().advance |ident| {
|
||||||
word(s.s, "'");
|
word(s.s, "'");
|
||||||
|
|
|
@ -512,6 +512,11 @@ pub fn visit_expr<E:Clone>(ex: @expr, (e, v): (E, vt<E>)) {
|
||||||
(v.visit_expr)(x, (e.clone(), v));
|
(v.visit_expr)(x, (e.clone(), v));
|
||||||
(v.visit_block)(b, (e.clone(), v));
|
(v.visit_block)(b, (e.clone(), v));
|
||||||
}
|
}
|
||||||
|
expr_for_loop(pat, iter, ref b) => {
|
||||||
|
(v.visit_pat)(pat, (e.clone(), v));
|
||||||
|
(v.visit_expr)(iter, (e.clone(), v));
|
||||||
|
(v.visit_block)(b, (e.clone(), v));
|
||||||
|
}
|
||||||
expr_loop(ref b, _) => (v.visit_block)(b, (e.clone(), v)),
|
expr_loop(ref b, _) => (v.visit_block)(b, (e.clone(), v)),
|
||||||
expr_match(x, ref arms) => {
|
expr_match(x, ref arms) => {
|
||||||
(v.visit_expr)(x, (e.clone(), v));
|
(v.visit_expr)(x, (e.clone(), v));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue