checker: forbid leaving function from inside `defer` block (#10285)

pull/10291/head
Uwe Krüger 2021-05-31 16:09:57 +02:00 committed by GitHub
parent 2376b343ba
commit d39a55ac11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 130 additions and 11 deletions

View File

@ -81,6 +81,9 @@ pub fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr
if p.tok.kind == .question {
// `foo()?`
p.next()
if p.inside_defer {
p.error_with_pos('error propagation not allowed inside `defer` blocks', p.prev_tok.position())
}
or_kind = .propagate
}
if fn_name in p.imported_symbols {
@ -595,6 +598,8 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
p.tok.position())
return ast.AnonFn{}
}
old_inside_defer := p.inside_defer
p.inside_defer = false
p.open_scope()
if p.pref.backend != .js {
p.scope.detached_from_parent = true
@ -660,6 +665,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
func.name = name
idx := p.table.find_or_register_fn_type(p.mod, func, true, false)
typ := ast.new_type(idx)
p.inside_defer = old_inside_defer
// name := p.table.get_type_name(typ)
return ast.AnonFn{
decl: ast.FnDecl{

View File

@ -742,7 +742,11 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
return p.comment_stmt()
}
.key_return {
return p.return_stmt()
if p.inside_defer {
return p.error_with_pos('`return` not allowed inside `defer` block', p.tok.position())
} else {
return p.return_stmt()
}
}
.dollar {
match p.peek_tok.kind {
@ -793,16 +797,20 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
return p.hash()
}
.key_defer {
p.next()
spos := p.tok.position()
p.inside_defer = true
p.defer_vars = []ast.Ident{}
stmts := p.parse_block()
p.inside_defer = false
return ast.DeferStmt{
stmts: stmts
defer_vars: p.defer_vars.clone()
pos: spos.extend_with_last_line(p.tok.position(), p.prev_tok.line_nr)
if p.inside_defer {
return p.error_with_pos('`defer` blocks cannot be nested', p.tok.position())
} else {
p.next()
spos := p.tok.position()
p.inside_defer = true
p.defer_vars = []ast.Ident{}
stmts := p.parse_block()
p.inside_defer = false
return ast.DeferStmt{
stmts: stmts
defer_vars: p.defer_vars.clone()
pos: spos.extend_with_last_line(p.tok.position(), p.prev_tok.line_nr)
}
}
}
.key_go {

View File

@ -0,0 +1,7 @@
vlib/v/parser/tests/defer_propagate.vv:9:15: error: error propagation not allowed inside `defer` blocks
7 | mut a := 0
8 | defer {
9 | a = test1() ?
| ^
10 | }
11 | return a

View File

@ -0,0 +1,16 @@
fn test1() ?int {
a := 3
return a
}
fn test2() ?int {
mut a := 0
defer {
a = test1() ?
}
return a
fn main() {
x := test2() or { -1 }
println(x)
}

View File

@ -0,0 +1,7 @@
vlib/v/parser/tests/defer_return.vv:3:3: error: `return` not allowed inside `defer` block
1 | fn main() {
2 | defer {
3 | return
| ~~~~~~
4 | }
5 | }

View File

@ -0,0 +1,5 @@
fn main() {
defer {
return
}
}

View File

@ -0,0 +1,7 @@
vlib/v/parser/tests/defer_return2.vv:3:3: error: `return` not allowed inside `defer` block
1 | fn test1() int {
2 | defer {
3 | return 12
| ~~~~~~
4 | }
5 | a := 3

View File

@ -0,0 +1,12 @@
fn test1() int {
defer {
return 12
}
a := 3
return a
}
fn main() {
x := test1()
println(x)
}

View File

@ -0,0 +1,7 @@
vlib/v/parser/tests/nested_defer.vv:5:3: error: `defer` blocks cannot be nested
3 | defer {
4 | a = 12
5 | defer {
| ~~~~~
6 | a = 13
7 | }

View File

@ -0,0 +1,14 @@
fn test1() int {
mut a := 0
defer {
a = 12
defer {
a = 13
}
}
return a
fn main() {
x := test1()
println(x)
}

View File

@ -0,0 +1,30 @@
struct Fst {
mut:
f fn () int
}
fn setfn(mut f Fst) {
mut i := 0
f.f = fn () int {
return 5
}
defer {
if i > 0 {
f.f = fn () int {
a := 0
defer {
assert a == 0
}
return 7
}
}
}
i = 1
}
fn test_nested_defer() {
mut g := Fst{}
setfn(mut g)
x := g.f()
assert x == 7
}