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