diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs index 62547b6669f7a..3350d8f548a86 100644 --- a/src/libfmt_macros/lib.rs +++ b/src/libfmt_macros/lib.rs @@ -352,8 +352,13 @@ impl<'a> Parser<'a> { None => { let tmp = self.cur.clone(); match self.word() { - word if word.len() > 0 && self.consume('$') => { - CountIsName(word) + word if word.len() > 0 => { + if self.consume('$') { + CountIsName(word) + } else { + self.cur = tmp; + CountImplied + } } _ => { self.cur = tmp; diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index 4f6885f05ed16..9cd46067c107b 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -333,32 +333,37 @@ impl<'a> CFGBuilder<'a> { // [discr] // | // v 2 - // [guard1] + // [cond1] // / \ // | \ - // v 3 | - // [pat1] | - // | - // v 4 | - // [body1] v - // | [guard2] - // | / \ - // | [body2] \ - // | | ... - // | | | - // v 5 v v - // [....expr....] + // v 3 \ + // [pat1] \ + // | | + // v 4 | + // [guard1] | + // | | + // | | + // v 5 v + // [body1] [cond2] + // | / \ + // | ... ... + // | | | + // v 6 v v + // [.....expr.....] // let discr_exit = self.expr(discr.clone(), pred); // 1 let expr_exit = self.add_node(expr.id, []); - let mut guard_exit = discr_exit; + let mut cond_exit = discr_exit; for arm in arms.iter() { - guard_exit = self.opt_expr(arm.guard, guard_exit); // 2 + cond_exit = self.add_dummy_node([cond_exit]); // 2 let pats_exit = self.pats_any(arm.pats.as_slice(), - guard_exit); // 3 - let body_exit = self.expr(arm.body.clone(), pats_exit); // 4 - self.add_contained_edge(body_exit, expr_exit); // 5 + cond_exit); // 3 + let guard_exit = self.opt_expr(arm.guard, + pats_exit); // 4 + let body_exit = self.expr(arm.body.clone(), + guard_exit); // 5 + self.add_contained_edge(body_exit, expr_exit); // 6 } expr_exit } diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index e458b82f03634..4d10676a5892e 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -11,6 +11,10 @@ use middle::const_eval::{compare_const_vals, const_bool, const_float, const_nil, const_val}; use middle::const_eval::{const_expr_to_pat, eval_const_expr, lookup_const_by_id}; use middle::def::*; +use middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Init}; +use middle::expr_use_visitor::{JustWrite, LoanCause, MutateMode}; +use middle::expr_use_visitor::{WriteAndRead}; +use middle::mem_categorization::cmt; use middle::pat_util::*; use middle::ty::*; use middle::ty; @@ -143,7 +147,16 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) { arm.pats.as_slice()); } - // Second, check for unreachable arms. + // Second, if there is a guard on each arm, make sure it isn't + // assigning or borrowing anything mutably. + for arm in arms.iter() { + match arm.guard { + Some(guard) => check_for_mutation_in_guard(cx, &*guard), + None => {} + } + } + + // Third, check for unreachable arms. check_arms(cx, arms.as_slice()); // Finally, check if the whole match expression is exhaustive. @@ -903,3 +916,53 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt, }); } } + +/// Ensures that a pattern guard doesn't borrow by mutable reference or +/// assign. +fn check_for_mutation_in_guard<'a>(cx: &'a MatchCheckCtxt<'a>, guard: &Expr) { + let mut checker = MutationChecker { + cx: cx, + }; + let mut visitor = ExprUseVisitor::new(&mut checker, checker.cx.tcx); + visitor.walk_expr(guard); +} + +struct MutationChecker<'a> { + cx: &'a MatchCheckCtxt<'a>, +} + +impl<'a> Delegate for MutationChecker<'a> { + fn consume(&mut self, _: NodeId, _: Span, _: cmt, _: ConsumeMode) {} + fn consume_pat(&mut self, _: &Pat, _: cmt, _: ConsumeMode) {} + fn borrow(&mut self, + _: NodeId, + span: Span, + _: cmt, + _: Region, + kind: BorrowKind, + _: LoanCause) { + match kind { + MutBorrow => { + self.cx + .tcx + .sess + .span_err(span, + "cannot mutably borrow in a pattern guard") + } + ImmBorrow | UniqueImmBorrow => {} + } + } + fn decl_without_init(&mut self, _: NodeId, _: Span) {} + fn mutate(&mut self, _: NodeId, span: Span, _: cmt, mode: MutateMode) { + match mode { + JustWrite | WriteAndRead => { + self.cx + .tcx + .sess + .span_err(span, "cannot assign in a pattern guard") + } + Init => {} + } + } +} + diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 7995317d49fd1..605811555a168 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -292,7 +292,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { self.walk_expr(expr) } - fn walk_expr(&mut self, expr: &ast::Expr) { + pub fn walk_expr(&mut self, expr: &ast::Expr) { debug!("walk_expr(expr={})", expr.repr(self.tcx())); self.walk_adjustment(expr); diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index eed058878e082..45af6debe05b0 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1730,17 +1730,19 @@ fn item_struct(w: &mut fmt::Formatter, it: &clean::Item, } }).peekable(); match s.struct_type { - doctree::Plain if fields.peek().is_some() => { - try!(write!(w, "

Fields

\n")); - for field in fields { - try!(write!(w, "")); + doctree::Plain => { + if fields.peek().is_some() { + try!(write!(w, "

Fields

\n
\ - {stab}{name}", - stab = ConciseStability(&field.stability), - name = field.name.get_ref().as_slice())); - try!(document(w, field)); - try!(write!(w, "
")); + for field in fields { + try!(write!(w, "")); + } + try!(write!(w, "
\ + {stab}{name}", + stab = ConciseStability(&field.stability), + name = field.name.get_ref().as_slice())); + try!(document(w, field)); + try!(write!(w, "
")); } - try!(write!(w, "")); } _ => {} } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 0116518d537a7..878994369d0d4 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1954,19 +1954,6 @@ impl<'a> Parser<'a> { token::BINOP(token::OR) | token::OROR => { return self.parse_lambda_expr(); }, - _ if self.eat_keyword(keywords::Proc) => { - let decl = self.parse_proc_decl(); - let body = self.parse_expr(); - let fakeblock = P(ast::Block { - view_items: Vec::new(), - stmts: Vec::new(), - expr: Some(body), - id: ast::DUMMY_NODE_ID, - rules: DefaultBlock, - span: body.span, - }); - return self.mk_expr(lo, body.span.hi, ExprProc(decl, fakeblock)); - }, // FIXME #13626: Should be able to stick in // token::SELF_KEYWORD_NAME token::IDENT(id @ ast::Ident{ @@ -1978,48 +1965,6 @@ impl<'a> Parser<'a> { ex = ExprPath(path); hi = self.last_span.hi; } - _ if self.eat_keyword(keywords::If) => { - return self.parse_if_expr(); - }, - _ if self.eat_keyword(keywords::For) => { - return self.parse_for_expr(None); - }, - _ if self.eat_keyword(keywords::While) => { - return self.parse_while_expr(); - }, - _ if Parser::token_is_lifetime(&self.token) => { - let lifetime = self.get_lifetime(); - self.bump(); - self.expect(&token::COLON); - if self.eat_keyword(keywords::For) { - return self.parse_for_expr(Some(lifetime)) - } else if self.eat_keyword(keywords::Loop) { - return self.parse_loop_expr(Some(lifetime)) - } else { - self.fatal("expected `for` or `loop` after a label") - } - }, - _ if self.eat_keyword(keywords::Loop) => { - return self.parse_loop_expr(None); - }, - _ if self.eat_keyword(keywords::Continue) => { - let lo = self.span.lo; - let ex = if Parser::token_is_lifetime(&self.token) { - let lifetime = self.get_lifetime(); - self.bump(); - ExprAgain(Some(lifetime)) - } else { - ExprAgain(None) - }; - let hi = self.span.hi; - return self.mk_expr(lo, hi, ex); - }, - _ if self.eat_keyword(keywords::Match) => { - return self.parse_match_expr(); - }, - _ if self.eat_keyword(keywords::Unsafe) => { - return self.parse_block_expr(lo, UnsafeBlock(ast::UserProvided)); - }, token::LBRACKET => { self.bump(); @@ -2057,88 +2002,158 @@ impl<'a> Parser<'a> { } hi = self.last_span.hi; }, - _ if self.eat_keyword(keywords::Return) => { - // RETURN expression - if can_begin_expr(&self.token) { - let e = self.parse_expr(); - hi = e.span.hi; - ex = ExprRet(Some(e)); - } else { ex = ExprRet(None); } - }, - _ if self.eat_keyword(keywords::Break) => { - // BREAK expression + _ => { + if self.eat_keyword(keywords::Proc) { + let decl = self.parse_proc_decl(); + let body = self.parse_expr(); + let fakeblock = P(ast::Block { + view_items: Vec::new(), + stmts: Vec::new(), + expr: Some(body), + id: ast::DUMMY_NODE_ID, + rules: DefaultBlock, + span: body.span, + }); + return self.mk_expr(lo, body.span.hi, ExprProc(decl, fakeblock)); + } + if self.eat_keyword(keywords::If) { + return self.parse_if_expr(); + } + if self.eat_keyword(keywords::For) { + return self.parse_for_expr(None); + } + if self.eat_keyword(keywords::While) { + return self.parse_while_expr(); + } if Parser::token_is_lifetime(&self.token) { let lifetime = self.get_lifetime(); self.bump(); - ex = ExprBreak(Some(lifetime)); - } else { - ex = ExprBreak(None); + self.expect(&token::COLON); + if self.eat_keyword(keywords::For) { + return self.parse_for_expr(Some(lifetime)) + } + if self.eat_keyword(keywords::Loop) { + return self.parse_loop_expr(Some(lifetime)) + } + self.fatal("expected `for` or `loop` after a label") } - hi = self.span.hi; - }, - _ if self.token == token::MOD_SEP || - is_ident(&self.token) && !self.is_keyword(keywords::True) && - !self.is_keyword(keywords::False) => { - let pth = self.parse_path(LifetimeAndTypesWithColons).path; - - // `!`, as an operator, is prefix, so we know this isn't that - if self.token == token::NOT { - // MACRO INVOCATION expression - self.bump(); - - let ket = token::close_delimiter_for(&self.token) - .unwrap_or_else(|| self.fatal("expected open delimiter")); - self.bump(); - - let tts = self.parse_seq_to_end(&ket, - seq_sep_none(), - |p| p.parse_token_tree()); + if self.eat_keyword(keywords::Loop) { + return self.parse_loop_expr(None); + } + if self.eat_keyword(keywords::Continue) { + let lo = self.span.lo; + let ex = if Parser::token_is_lifetime(&self.token) { + let lifetime = self.get_lifetime(); + self.bump(); + ExprAgain(Some(lifetime)) + } else { + ExprAgain(None) + }; let hi = self.span.hi; + return self.mk_expr(lo, hi, ex); + } + if self.eat_keyword(keywords::Match) { + return self.parse_match_expr(); + } + if self.eat_keyword(keywords::Unsafe) { + return self.parse_block_expr( + lo, + UnsafeBlock(ast::UserProvided)); + } + if self.eat_keyword(keywords::Return) { + // RETURN expression + if can_begin_expr(&self.token) { + let e = self.parse_expr(); + hi = e.span.hi; + ex = ExprRet(Some(e)); + } else { + ex = ExprRet(None); + } + } else if self.eat_keyword(keywords::Break) { + // BREAK expression + if Parser::token_is_lifetime(&self.token) { + let lifetime = self.get_lifetime(); + self.bump(); + ex = ExprBreak(Some(lifetime)); + } else { + ex = ExprBreak(None); + } + hi = self.span.hi; + } else if self.token == token::MOD_SEP || + is_ident(&self.token) && + !self.is_keyword(keywords::True) && + !self.is_keyword(keywords::False) { + let pth = + self.parse_path(LifetimeAndTypesWithColons).path; + + // `!`, as an operator, is prefix, so we know this isn't that + if self.token == token::NOT { + // MACRO INVOCATION expression + self.bump(); - return self.mk_mac_expr(lo, hi, MacInvocTT(pth, tts, EMPTY_CTXT)); - } else if self.token == token::LBRACE { - // This is a struct literal, unless we're prohibited from - // parsing struct literals here. - if self.restriction != RESTRICT_NO_STRUCT_LITERAL { - // It's a struct literal. + let ket = token::close_delimiter_for(&self.token) + .unwrap_or_else(|| { + self.fatal("expected open delimiter") + }); self.bump(); - let mut fields = Vec::new(); - let mut base = None; - while self.token != token::RBRACE { - if self.eat(&token::DOTDOT) { - base = Some(self.parse_expr()); - break; + let tts = self.parse_seq_to_end( + &ket, + seq_sep_none(), + |p| p.parse_token_tree()); + let hi = self.span.hi; + + return self.mk_mac_expr(lo, + hi, + MacInvocTT(pth, + tts, + EMPTY_CTXT)); + } + if self.token == token::LBRACE { + // This is a struct literal, unless we're prohibited + // from parsing struct literals here. + if self.restriction != RESTRICT_NO_STRUCT_LITERAL { + // It's a struct literal. + self.bump(); + let mut fields = Vec::new(); + let mut base = None; + + while self.token != token::RBRACE { + if self.eat(&token::DOTDOT) { + base = Some(self.parse_expr()); + break; + } + + fields.push(self.parse_field()); + self.commit_expr(fields.last().unwrap().expr, + &[token::COMMA], + &[token::RBRACE]); } - fields.push(self.parse_field()); - self.commit_expr(fields.last().unwrap().expr, - &[token::COMMA], &[token::RBRACE]); - } + if fields.len() == 0 && base.is_none() { + let last_span = self.last_span; + self.span_err(last_span, + "structure literal must either \ + have at least one field or use \ + functional structure update \ + syntax"); + } - if fields.len() == 0 && base.is_none() { - let last_span = self.last_span; - self.span_err(last_span, - "structure literal must either have at \ - least one field or use functional \ - structure update syntax"); + hi = self.span.hi; + self.expect(&token::RBRACE); + ex = ExprStruct(pth, fields, base); + return self.mk_expr(lo, hi, ex); } - - hi = self.span.hi; - self.expect(&token::RBRACE); - ex = ExprStruct(pth, fields, base); - return self.mk_expr(lo, hi, ex); } - } - hi = pth.span.hi; - ex = ExprPath(pth); - }, - _ => { - // other literal expression - let lit = self.parse_lit(); - hi = lit.span.hi; - ex = ExprLit(box(GC) lit); + hi = pth.span.hi; + ex = ExprPath(pth); + } else { + // other literal expression + let lit = self.parse_lit(); + hi = lit.span.hi; + ex = ExprLit(box(GC) lit); + } } } @@ -2501,37 +2516,41 @@ impl<'a> Parser<'a> { } }; } - token::IDENT(_, _) if self.is_keyword(keywords::Box) => { - self.bump(); + token::IDENT(_, _) => { + if self.is_keyword(keywords::Box) { + self.bump(); - // Check for a place: `box(PLACE) EXPR`. - if self.eat(&token::LPAREN) { - // Support `box() EXPR` as the default. - if !self.eat(&token::RPAREN) { - let place = self.parse_expr(); - self.expect(&token::RPAREN); - let subexpression = self.parse_prefix_expr(); - hi = subexpression.span.hi; - ex = ExprBox(place, subexpression); - return self.mk_expr(lo, hi, ex); + // Check for a place: `box(PLACE) EXPR`. + if self.eat(&token::LPAREN) { + // Support `box() EXPR` as the default. + if !self.eat(&token::RPAREN) { + let place = self.parse_expr(); + self.expect(&token::RPAREN); + let subexpression = self.parse_prefix_expr(); + hi = subexpression.span.hi; + ex = ExprBox(place, subexpression); + return self.mk_expr(lo, hi, ex); + } } - } - // Otherwise, we use the unique pointer default. - let subexpression = self.parse_prefix_expr(); - hi = subexpression.span.hi; - // HACK: turn `box [...]` into a boxed-vec - ex = match subexpression.node { - ExprVec(..) | ExprRepeat(..) => { - let last_span = self.last_span; - self.obsolete(last_span, ObsoleteOwnedVector); - ExprVstore(subexpression, ExprVstoreUniq) - } - ExprLit(lit) if lit_is_str(lit) => { - ExprVstore(subexpression, ExprVstoreUniq) - } - _ => self.mk_unary(UnUniq, subexpression) - }; + // Otherwise, we use the unique pointer default. + let subexpression = self.parse_prefix_expr(); + hi = subexpression.span.hi; + // HACK: turn `box [...]` into a boxed-vec + ex = match subexpression.node { + ExprVec(..) | ExprRepeat(..) => { + let last_span = self.last_span; + self.obsolete(last_span, ObsoleteOwnedVector); + ExprVstore(subexpression, ExprVstoreUniq) + } + ExprLit(lit) if lit_is_str(lit) => { + ExprVstore(subexpression, ExprVstoreUniq) + } + _ => self.mk_unary(UnUniq, subexpression) + }; + } else { + return self.parse_dot_or_call_expr() + } } _ => return self.parse_dot_or_call_expr() } @@ -3832,17 +3851,6 @@ impl<'a> Parser<'a> { } SelfStatic } - token::IDENT(..) if self.is_self_ident() => { - let self_ident = self.expect_self_ident(); - - // Determine whether this is the fully explicit form, `self: - // TYPE`. - if self.eat(&token::COLON) { - SelfExplicit(self.parse_ty(false), self_ident) - } else { - SelfValue(self_ident) - } - } token::BINOP(token::STAR) => { // Possibly "*self" or "*mut self" -- not supported. Try to avoid // emitting cryptic "unexpected token" errors. @@ -3860,30 +3868,47 @@ impl<'a> Parser<'a> { // error case, making bogus self ident: SelfValue(special_idents::self_) } - _ if Parser::token_is_mutability(&self.token) && - self.look_ahead(1, |t| token::is_keyword(keywords::Self, t)) => { - mutbl_self = self.parse_mutability(); - let self_ident = self.expect_self_ident(); - - // Determine whether this is the fully explicit form, `self: - // TYPE`. - if self.eat(&token::COLON) { - SelfExplicit(self.parse_ty(false), self_ident) + token::IDENT(..) => { + if self.is_self_ident() { + let self_ident = self.expect_self_ident(); + + // Determine whether this is the fully explicit form, `self: + // TYPE`. + if self.eat(&token::COLON) { + SelfExplicit(self.parse_ty(false), self_ident) + } else { + SelfValue(self_ident) + } + } else if Parser::token_is_mutability(&self.token) && + self.look_ahead(1, |t| { + token::is_keyword(keywords::Self, t) + }) { + mutbl_self = self.parse_mutability(); + let self_ident = self.expect_self_ident(); + + // Determine whether this is the fully explicit form, + // `self: TYPE`. + if self.eat(&token::COLON) { + SelfExplicit(self.parse_ty(false), self_ident) + } else { + SelfValue(self_ident) + } + } else if Parser::token_is_mutability(&self.token) && + self.look_ahead(1, |t| *t == token::TILDE) && + self.look_ahead(2, |t| { + token::is_keyword(keywords::Self, t) + }) { + mutbl_self = self.parse_mutability(); + self.bump(); + drop(self.expect_self_ident()); + let last_span = self.last_span; + self.obsolete(last_span, ObsoleteOwnedSelf); + SelfStatic } else { - SelfValue(self_ident) + SelfStatic } } - _ if Parser::token_is_mutability(&self.token) && - self.look_ahead(1, |t| *t == token::TILDE) && - self.look_ahead(2, |t| token::is_keyword(keywords::Self, t)) => { - mutbl_self = self.parse_mutability(); - self.bump(); - drop(self.expect_self_ident()); - let last_span = self.last_span; - self.obsolete(last_span, ObsoleteOwnedSelf); - SelfStatic - } - _ => SelfStatic + _ => SelfStatic, }; let explicit_self_sp = mk_sp(lo, self.span.hi); diff --git a/src/test/compile-fail/borrowck-lend-flow-match.rs b/src/test/compile-fail/borrowck-lend-flow-match.rs index c6020df2bc2ea..049bec3d37bef 100644 --- a/src/test/compile-fail/borrowck-lend-flow-match.rs +++ b/src/test/compile-fail/borrowck-lend-flow-match.rs @@ -11,9 +11,6 @@ #![allow(unused_variable)] #![allow(dead_assignment)] -fn cond() -> bool { fail!() } -fn link<'a>(v: &'a uint, w: &mut &'a uint) -> bool { *w = v; true } - fn separate_arms() { // Here both arms perform assignments, but only is illegal. @@ -31,28 +28,4 @@ fn separate_arms() { x.clone(); // just to prevent liveness warnings } -fn guard() { - // Here the guard performs a borrow. This borrow "infects" all - // subsequent arms (but not the prior ones). - - let mut a = box 3u; - let mut b = box 4u; - let mut w = &*a; - match 22i { - _ if cond() => { - b = box 5u; - } - - _ if link(&*b, &mut w) => { - b = box 6u; //~ ERROR cannot assign - } - - _ => { - b = box 7u; //~ ERROR cannot assign - } - } - - b = box 8; //~ ERROR cannot assign -} - fn main() {} diff --git a/src/test/compile-fail/borrowck-mutate-in-guard.rs b/src/test/compile-fail/borrowck-mutate-in-guard.rs new file mode 100644 index 0000000000000..8a904a3b5fb89 --- /dev/null +++ b/src/test/compile-fail/borrowck-mutate-in-guard.rs @@ -0,0 +1,33 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +enum Enum<'a> { + A(&'a int), + B(bool), +} + +fn foo() -> int { + let mut n = 42; + let mut x = A(&mut n); + match x { + A(_) if { x = B(false); false } => 1, + //~^ ERROR cannot assign in a pattern guard + A(_) if { let y = &mut x; *y = B(false); false } => 1, + //~^ ERROR cannot mutably borrow in a pattern guard + //~^^ ERROR cannot assign in a pattern guard + A(p) => *p, + B(_) => 2, + } +} + +fn main() { + foo(); +} + diff --git a/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot index 251798fc7ed88..f0907d8d708f2 100644 --- a/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot @@ -7,14 +7,15 @@ digraph block { N5[label="expr 7777i"]; N6[label="expr [7i, 77i, 777i, 7777i]"]; N7[label="expr match [7i, 77i, 777i, 7777i] { [x, y, ..] => x + y }"]; - N8[label="local x"]; - N9[label="local y"]; - N10[label="pat .."]; - N11[label="pat [x, y, ..]"]; - N12[label="expr x"]; - N13[label="expr y"]; - N14[label="expr x + y"]; - N15[label="block { match [7i, 77i, 777i, 7777i] { [x, y, ..] => x + y }; }"]; + N8[label="(dummy_node)"]; + N9[label="local x"]; + N10[label="local y"]; + N11[label="pat .."]; + N12[label="pat [x, y, ..]"]; + N13[label="expr x"]; + N14[label="expr y"]; + N15[label="expr x + y"]; + N16[label="block { match [7i, 77i, 777i, 7777i] { [x, y, ..] => x + y }; }"]; N0 -> N2; N2 -> N3; N3 -> N4; @@ -27,7 +28,8 @@ digraph block { N11 -> N12; N12 -> N13; N13 -> N14; - N14 -> N7; - N7 -> N15; - N15 -> N1; + N14 -> N15; + N15 -> N7; + N7 -> N16; + N16 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot index 2be43dcaa7b66..dbfa4dd6fe92c 100644 --- a/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot @@ -8,18 +8,20 @@ digraph block { N6[label="local _y"]; N7[label="expr x"]; N8[label="expr match x { E13a => _y = 1, E13b(v) => _y = v + 1 }"]; - N9[label="local E13a"]; - N10[label="expr 1"]; - N11[label="expr _y"]; - N12[label="expr _y = 1"]; - N13[label="local v"]; - N14[label="pat E13b(v)"]; - N15[label="expr v"]; - N16[label="expr 1"]; - N17[label="expr v + 1"]; - N18[label="expr _y"]; - N19[label="expr _y = v + 1"]; - N20[label="block {\l let x = E13b(13);\l let _y;\l match x { E13a => _y = 1, E13b(v) => _y = v + 1 }\l}\l"]; + N9[label="(dummy_node)"]; + N10[label="local E13a"]; + N11[label="expr 1"]; + N12[label="expr _y"]; + N13[label="expr _y = 1"]; + N14[label="(dummy_node)"]; + N15[label="local v"]; + N16[label="pat E13b(v)"]; + N17[label="expr v"]; + N18[label="expr 1"]; + N19[label="expr v + 1"]; + N20[label="expr _y"]; + N21[label="expr _y = v + 1"]; + N22[label="block {\l let x = E13b(13);\l let _y;\l match x { E13a => _y = 1, E13b(v) => _y = v + 1 }\l}\l"]; N0 -> N2; N2 -> N3; N3 -> N4; @@ -30,15 +32,17 @@ digraph block { N9 -> N10; N10 -> N11; N11 -> N12; - N12 -> N8; - N7 -> N13; - N13 -> N14; + N12 -> N13; + N13 -> N8; + N9 -> N14; N14 -> N15; N15 -> N16; N16 -> N17; N17 -> N18; N18 -> N19; - N19 -> N8; - N8 -> N20; - N20 -> N1; + N19 -> N20; + N20 -> N21; + N21 -> N8; + N8 -> N22; + N22 -> N1; }