checker: check goto label exists (#8523)

pull/8536/head
Nick Treleaven 2021-02-03 14:20:10 +00:00 committed by GitHub
parent 82482167ce
commit de37b52d4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 99 additions and 3 deletions

View File

@ -337,6 +337,7 @@ pub mut:
next_comments []Comment // coments that are one line after the decl; used for InterfaceDecl
source_file &File = 0
scope &Scope
label_names []string
}
pub struct GenericParam {

View File

@ -3273,6 +3273,9 @@ fn (mut c Checker) stmt(node ast.Stmt) {
}
ast.GotoLabel {}
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
}
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)
return table.u32_type
}
if !c.table.struct_has_field(node.struct_type, node.field) {
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() {
// Loop thru each generic function concrete type.
// Check each specific fn instantiation.
for i in 0 .. c.file.generic_fns.len {
if c.table.fn_gen_types.len == 0 {
// 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.stmts(node.stmts)
if c.fn_mut_arg_names.len > 0 {
c.fn_mut_arg_names.clear()
}

View File

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

View File

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

View File

@ -427,7 +427,9 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
is_builtin: p.builtin_mod || p.mod in util.builtin_module_parts
attrs: p.attrs
scope: p.scope
label_names: p.label_names
}
p.label_names = []
p.close_scope()
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.tok.position())
}
mut label_names := []string{}
if p.tok.kind == .lcbr {
tmp := p.label_names
p.label_names = []
stmts = p.parse_block_no_scope(false)
label_names = p.label_names
p.label_names = tmp
}
p.close_scope()
mut func := table.Fn{
@ -542,6 +549,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
pos: pos.extend(p.prev_tok.position())
file: p.file_name
scope: p.scope
label_names: label_names
}
typ: typ
}

View File

@ -69,6 +69,7 @@ mut:
warnings []errors.Warning
vet_errors []vet.Error
cur_fn_name string
label_names []string
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
}
@ -552,6 +553,7 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
file: p.file_name
return_type: table.void_type
scope: p.scope
label_names: p.label_names
}
} else if p.pref.is_fmt {
return p.stmt(false)
@ -656,6 +658,10 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
// `label:`
spos := p.tok.position()
name := p.check_name()
if name in p.label_names {
p.error_with_pos('duplicate label `$name`', spos)
}
p.label_names << name
p.next()
if p.tok.kind == .key_for {
mut stmt := p.stmt(is_top_level)