checker: check goto label exists (#8523)
parent
82482167ce
commit
de37b52d4b
|
@ -337,6 +337,7 @@ pub mut:
|
||||||
next_comments []Comment // coments that are one line after the decl; used for InterfaceDecl
|
next_comments []Comment // coments that are one line after the decl; used for InterfaceDecl
|
||||||
source_file &File = 0
|
source_file &File = 0
|
||||||
scope &Scope
|
scope &Scope
|
||||||
|
label_names []string
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GenericParam {
|
pub struct GenericParam {
|
||||||
|
|
|
@ -3273,6 +3273,9 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
ast.GotoLabel {}
|
ast.GotoLabel {}
|
||||||
ast.GotoStmt {
|
ast.GotoStmt {
|
||||||
|
if node.name !in c.cur_fn.label_names {
|
||||||
|
c.error('unknown label `$node.name`', node.pos)
|
||||||
|
}
|
||||||
// TODO: check label doesn't bypass variable declarations
|
// TODO: check label doesn't bypass variable declarations
|
||||||
}
|
}
|
||||||
ast.HashStmt {
|
ast.HashStmt {
|
||||||
|
@ -5281,7 +5284,6 @@ pub fn (mut c Checker) offset_of(node ast.OffsetOf) table.Type {
|
||||||
c.error('first argument of __offsetof must be struct', node.pos)
|
c.error('first argument of __offsetof must be struct', node.pos)
|
||||||
return table.u32_type
|
return table.u32_type
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.table.struct_has_field(node.struct_type, node.field) {
|
if !c.table.struct_has_field(node.struct_type, node.field) {
|
||||||
c.error('struct `$sym.name` has no field called `$node.field`', node.pos)
|
c.error('struct `$sym.name` has no field called `$node.field`', node.pos)
|
||||||
}
|
}
|
||||||
|
@ -5540,7 +5542,6 @@ fn (mut c Checker) fetch_and_verify_orm_fields(info table.Struct, pos token.Posi
|
||||||
fn (mut c Checker) post_process_generic_fns() {
|
fn (mut c Checker) post_process_generic_fns() {
|
||||||
// Loop thru each generic function concrete type.
|
// Loop thru each generic function concrete type.
|
||||||
// Check each specific fn instantiation.
|
// Check each specific fn instantiation.
|
||||||
|
|
||||||
for i in 0 .. c.file.generic_fns.len {
|
for i in 0 .. c.file.generic_fns.len {
|
||||||
if c.table.fn_gen_types.len == 0 {
|
if c.table.fn_gen_types.len == 0 {
|
||||||
// no concrete types, so just skip:
|
// no concrete types, so just skip:
|
||||||
|
@ -5689,7 +5690,6 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
||||||
}
|
}
|
||||||
c.fn_scope = node.scope
|
c.fn_scope = node.scope
|
||||||
c.stmts(node.stmts)
|
c.stmts(node.stmts)
|
||||||
|
|
||||||
if c.fn_mut_arg_names.len > 0 {
|
if c.fn_mut_arg_names.len > 0 {
|
||||||
c.fn_mut_arg_names.clear()
|
c.fn_mut_arg_names.clear()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
vlib/v/checker/tests/goto_label.vv:5:7: error: unknown label `a1`
|
||||||
|
3 | goto f2
|
||||||
|
4 | f1:
|
||||||
|
5 | goto a1
|
||||||
|
| ~~
|
||||||
|
6 | _ = fn(){
|
||||||
|
7 | goto f1
|
||||||
|
vlib/v/checker/tests/goto_label.vv:7:8: error: unknown label `f1`
|
||||||
|
5 | goto a1
|
||||||
|
6 | _ = fn(){
|
||||||
|
7 | goto f1
|
||||||
|
| ~~
|
||||||
|
8 | goto f2
|
||||||
|
9 | goto a1
|
||||||
|
vlib/v/checker/tests/goto_label.vv:8:8: error: unknown label `f2`
|
||||||
|
6 | _ = fn(){
|
||||||
|
7 | goto f1
|
||||||
|
8 | goto f2
|
||||||
|
| ~~
|
||||||
|
9 | goto a1
|
||||||
|
10 | a1:
|
||||||
|
vlib/v/checker/tests/goto_label.vv:14:7: error: unknown label `a1`
|
||||||
|
12 | }
|
||||||
|
13 | f2:
|
||||||
|
14 | goto a1
|
||||||
|
| ~~
|
||||||
|
15 | goto f1 // back
|
||||||
|
16 | goto f2
|
||||||
|
vlib/v/checker/tests/goto_label.vv:22:7: error: unknown label `f1`
|
||||||
|
20 | goto g1 // forward
|
||||||
|
21 | g1:
|
||||||
|
22 | goto f1
|
||||||
|
| ~~
|
||||||
|
23 | goto a1
|
||||||
|
24 | goto g1 // back
|
||||||
|
vlib/v/checker/tests/goto_label.vv:23:7: error: unknown label `a1`
|
||||||
|
21 | g1:
|
||||||
|
22 | goto f1
|
||||||
|
23 | goto a1
|
||||||
|
| ~~
|
||||||
|
24 | goto g1 // back
|
||||||
|
25 | goto undefined
|
||||||
|
vlib/v/checker/tests/goto_label.vv:25:7: error: unknown label `undefined`
|
||||||
|
23 | goto a1
|
||||||
|
24 | goto g1 // back
|
||||||
|
25 | goto undefined
|
||||||
|
| ~~~~~~~~~
|
||||||
|
26 | }
|
||||||
|
27 |
|
|
@ -0,0 +1,32 @@
|
||||||
|
fn f() {
|
||||||
|
goto f1 // forward
|
||||||
|
goto f2
|
||||||
|
f1:
|
||||||
|
goto a1
|
||||||
|
_ = fn(){
|
||||||
|
goto f1
|
||||||
|
goto f2
|
||||||
|
goto a1
|
||||||
|
a1:
|
||||||
|
goto a1
|
||||||
|
}
|
||||||
|
f2:
|
||||||
|
goto a1
|
||||||
|
goto f1 // back
|
||||||
|
goto f2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn g() {
|
||||||
|
goto g1 // forward
|
||||||
|
g1:
|
||||||
|
goto f1
|
||||||
|
goto a1
|
||||||
|
goto g1 // back
|
||||||
|
goto undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// implicit main
|
||||||
|
goto m1
|
||||||
|
m1:
|
||||||
|
goto m1
|
||||||
|
|
|
@ -427,7 +427,9 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
is_builtin: p.builtin_mod || p.mod in util.builtin_module_parts
|
is_builtin: p.builtin_mod || p.mod in util.builtin_module_parts
|
||||||
attrs: p.attrs
|
attrs: p.attrs
|
||||||
scope: p.scope
|
scope: p.scope
|
||||||
|
label_names: p.label_names
|
||||||
}
|
}
|
||||||
|
p.label_names = []
|
||||||
p.close_scope()
|
p.close_scope()
|
||||||
return fn_decl
|
return fn_decl
|
||||||
}
|
}
|
||||||
|
@ -514,8 +516,13 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
|
||||||
p.error_with_pos('unexpected `$p.tok.kind` after anonymous function signature, expecting `{`',
|
p.error_with_pos('unexpected `$p.tok.kind` after anonymous function signature, expecting `{`',
|
||||||
p.tok.position())
|
p.tok.position())
|
||||||
}
|
}
|
||||||
|
mut label_names := []string{}
|
||||||
if p.tok.kind == .lcbr {
|
if p.tok.kind == .lcbr {
|
||||||
|
tmp := p.label_names
|
||||||
|
p.label_names = []
|
||||||
stmts = p.parse_block_no_scope(false)
|
stmts = p.parse_block_no_scope(false)
|
||||||
|
label_names = p.label_names
|
||||||
|
p.label_names = tmp
|
||||||
}
|
}
|
||||||
p.close_scope()
|
p.close_scope()
|
||||||
mut func := table.Fn{
|
mut func := table.Fn{
|
||||||
|
@ -542,6 +549,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
|
||||||
pos: pos.extend(p.prev_tok.position())
|
pos: pos.extend(p.prev_tok.position())
|
||||||
file: p.file_name
|
file: p.file_name
|
||||||
scope: p.scope
|
scope: p.scope
|
||||||
|
label_names: label_names
|
||||||
}
|
}
|
||||||
typ: typ
|
typ: typ
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ mut:
|
||||||
warnings []errors.Warning
|
warnings []errors.Warning
|
||||||
vet_errors []vet.Error
|
vet_errors []vet.Error
|
||||||
cur_fn_name string
|
cur_fn_name string
|
||||||
|
label_names []string
|
||||||
in_generic_params bool // indicates if parsing between `<` and `>` of a method/function
|
in_generic_params bool // indicates if parsing between `<` and `>` of a method/function
|
||||||
name_error bool // indicates if the token is not a name or the name is on another line
|
name_error bool // indicates if the token is not a name or the name is on another line
|
||||||
}
|
}
|
||||||
|
@ -552,6 +553,7 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
|
||||||
file: p.file_name
|
file: p.file_name
|
||||||
return_type: table.void_type
|
return_type: table.void_type
|
||||||
scope: p.scope
|
scope: p.scope
|
||||||
|
label_names: p.label_names
|
||||||
}
|
}
|
||||||
} else if p.pref.is_fmt {
|
} else if p.pref.is_fmt {
|
||||||
return p.stmt(false)
|
return p.stmt(false)
|
||||||
|
@ -656,6 +658,10 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
|
||||||
// `label:`
|
// `label:`
|
||||||
spos := p.tok.position()
|
spos := p.tok.position()
|
||||||
name := p.check_name()
|
name := p.check_name()
|
||||||
|
if name in p.label_names {
|
||||||
|
p.error_with_pos('duplicate label `$name`', spos)
|
||||||
|
}
|
||||||
|
p.label_names << name
|
||||||
p.next()
|
p.next()
|
||||||
if p.tok.kind == .key_for {
|
if p.tok.kind == .key_for {
|
||||||
mut stmt := p.stmt(is_top_level)
|
mut stmt := p.stmt(is_top_level)
|
||||||
|
|
Loading…
Reference in New Issue