checker: check labelled break/continue is inside a matching `for` loop (#6910)

pull/6918/head
Nick Treleaven 2020-11-22 19:51:07 +00:00 committed by GitHub
parent a1827d7f98
commit 51c737669d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 86 additions and 1 deletions

View File

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

View File

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

View File

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