checker: check labelled break/continue is inside a matching `for` loop (#6910)
parent
a1827d7f98
commit
51c737669d
|
@ -65,6 +65,7 @@ mut:
|
||||||
vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path**
|
vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path**
|
||||||
vweb_gen_types []table.Type // vweb route checks
|
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
|
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 {
|
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
|
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) {
|
fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
$if trace_checker ? {
|
$if trace_checker ? {
|
||||||
stmt_pos := node.position()
|
stmt_pos := node.position()
|
||||||
|
@ -2458,7 +2472,11 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
if c.in_for_count == 0 {
|
if c.in_for_count == 0 {
|
||||||
c.error('$node.kind.str() statement not within a loop', node.pos)
|
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 {
|
ast.CompFor {
|
||||||
// node.typ = c.expr(node.expr)
|
// node.typ = c.expr(node.expr)
|
||||||
|
@ -2490,7 +2508,9 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
c.stmt(node.init)
|
c.stmt(node.init)
|
||||||
c.expr(node.cond)
|
c.expr(node.cond)
|
||||||
c.stmt(node.inc)
|
c.stmt(node.inc)
|
||||||
|
c.check_loop_label(node.label, node.pos)
|
||||||
c.stmts(node.stmts)
|
c.stmts(node.stmts)
|
||||||
|
c.loop_label = ''
|
||||||
c.in_for_count--
|
c.in_for_count--
|
||||||
}
|
}
|
||||||
ast.ForInStmt {
|
ast.ForInStmt {
|
||||||
|
@ -2545,7 +2565,9 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
node.val_type = value_type
|
node.val_type = value_type
|
||||||
scope.update_var_type(node.val_var, value_type)
|
scope.update_var_type(node.val_var, value_type)
|
||||||
}
|
}
|
||||||
|
c.check_loop_label(node.label, node.pos)
|
||||||
c.stmts(node.stmts)
|
c.stmts(node.stmts)
|
||||||
|
c.loop_label = ''
|
||||||
c.in_for_count--
|
c.in_for_count--
|
||||||
}
|
}
|
||||||
ast.ForStmt {
|
ast.ForStmt {
|
||||||
|
@ -2557,7 +2579,9 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
// TODO: update loop var type
|
// TODO: update loop var type
|
||||||
// how does this work currenly?
|
// how does this work currenly?
|
||||||
|
c.check_loop_label(node.label, node.pos)
|
||||||
c.stmts(node.stmts)
|
c.stmts(node.stmts)
|
||||||
|
c.loop_label = ''
|
||||||
c.in_for_count--
|
c.in_for_count--
|
||||||
}
|
}
|
||||||
ast.GlobalDecl {
|
ast.GlobalDecl {
|
||||||
|
|
|
@ -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}
|
|
@ -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}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue