diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 5f5582d4f8..55b7207c1a 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -65,6 +65,7 @@ mut: vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path** vweb_gen_types []table.Type // vweb route checks prevent_sum_type_unwrapping_once bool // needed for assign new values to sum type, stopping unwrapping then + loop_label string // set when inside a labelled for loop } pub fn new_checker(table &table.Table, pref &pref.Preferences) Checker { @@ -2424,6 +2425,19 @@ fn is_const_integer(cfield ast.ConstField) ?ast.IntegerLiteral { return none } +[inline] +fn (mut c Checker) check_loop_label(label string, pos token.Position) { + if label.len == 0 { + // ignore + return + } + if c.loop_label.len != 0 { + c.error('nesting of labelled `for` loops is not supported', pos) + return + } + c.loop_label = label +} + fn (mut c Checker) stmt(node ast.Stmt) { $if trace_checker ? { stmt_pos := node.position() @@ -2458,7 +2472,11 @@ fn (mut c Checker) stmt(node ast.Stmt) { if c.in_for_count == 0 { c.error('$node.kind.str() statement not within a loop', node.pos) } - // TODO: check any node.label is in scope for goto + if node.label.len > 0 { + if node.label != c.loop_label { + c.error('invalid label name `$node.label`', node.pos) + } + } } ast.CompFor { // node.typ = c.expr(node.expr) @@ -2490,7 +2508,9 @@ fn (mut c Checker) stmt(node ast.Stmt) { c.stmt(node.init) c.expr(node.cond) c.stmt(node.inc) + c.check_loop_label(node.label, node.pos) c.stmts(node.stmts) + c.loop_label = '' c.in_for_count-- } ast.ForInStmt { @@ -2545,7 +2565,9 @@ fn (mut c Checker) stmt(node ast.Stmt) { node.val_type = value_type scope.update_var_type(node.val_var, value_type) } + c.check_loop_label(node.label, node.pos) c.stmts(node.stmts) + c.loop_label = '' c.in_for_count-- } ast.ForStmt { @@ -2557,7 +2579,9 @@ fn (mut c Checker) stmt(node ast.Stmt) { } // TODO: update loop var type // how does this work currenly? + c.check_loop_label(node.label, node.pos) c.stmts(node.stmts) + c.loop_label = '' c.in_for_count-- } ast.GlobalDecl { diff --git a/vlib/v/checker/tests/labelled_break_continue.out b/vlib/v/checker/tests/labelled_break_continue.out new file mode 100644 index 0000000000..caadcfc450 --- /dev/null +++ b/vlib/v/checker/tests/labelled_break_continue.out @@ -0,0 +1,35 @@ +vlib/v/checker/tests/labelled_break_continue.vv:7:14: error: invalid label name `L2` + 5 | i++ + 6 | for { + 7 | if i < 7 {continue L2} + | ~~~~~~~~ + 8 | else {break L2} + 9 | } +vlib/v/checker/tests/labelled_break_continue.vv:8:10: error: invalid label name `L2` + 6 | for { + 7 | if i < 7 {continue L2} + 8 | else {break L2} + | ~~~~~ + 9 | } + 10 | } +vlib/v/checker/tests/labelled_break_continue.vv:15:14: error: invalid label name `L1` + 13 | i = e + 14 | for { + 15 | if i < 3 {continue L1} + | ~~~~~~~~ + 16 | else {break L1} + 17 | } +vlib/v/checker/tests/labelled_break_continue.vv:16:10: error: invalid label name `L1` + 14 | for { + 15 | if i < 3 {continue L1} + 16 | else {break L1} + | ~~~~~ + 17 | } + 18 | } +vlib/v/checker/tests/labelled_break_continue.vv:21:11: error: nesting of labelled `for` loops is not supported + 19 | // check nested loops (not supported ATM) + 20 | L3: for ;; i++ { + 21 | L4: for { + | ^ + 22 | if i < 17 {continue L3} + 23 | else {break L3} diff --git a/vlib/v/checker/tests/labelled_break_continue.vv b/vlib/v/checker/tests/labelled_break_continue.vv new file mode 100644 index 0000000000..3447ab155c --- /dev/null +++ b/vlib/v/checker/tests/labelled_break_continue.vv @@ -0,0 +1,26 @@ +fn main() { + mut i := 4 + // check branching to a later loop + L1: for { + i++ + for { + if i < 7 {continue L2} + else {break L2} + } + } + // check branching to an earlier loop + L2: for e in [1,2,3,4] { + i = e + for { + if i < 3 {continue L1} + else {break L1} + } + } + // check nested loops (not supported ATM) + L3: for ;; i++ { + L4: for { + if i < 17 {continue L3} + else {break L3} + } + } +}