checker: forbid leaving function from inside `defer` block (#10285)
parent
2376b343ba
commit
d39a55ac11
|
@ -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{
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
}
|
|
@ -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 | }
|
|
@ -0,0 +1,5 @@
|
|||
fn main() {
|
||||
defer {
|
||||
return
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,12 @@
|
|||
fn test1() int {
|
||||
defer {
|
||||
return 12
|
||||
}
|
||||
a := 3
|
||||
return a
|
||||
}
|
||||
|
||||
fn main() {
|
||||
x := test1()
|
||||
println(x)
|
||||
}
|
|
@ -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 | }
|
|
@ -0,0 +1,14 @@
|
|||
fn test1() int {
|
||||
mut a := 0
|
||||
defer {
|
||||
a = 12
|
||||
defer {
|
||||
a = 13
|
||||
}
|
||||
}
|
||||
return a
|
||||
|
||||
fn main() {
|
||||
x := test1()
|
||||
println(x)
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue