From d39a55ac11861b2b034f971edfab4708c6b6d01f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kr=C3=BCger?= <45282134+UweKrueger@users.noreply.github.com> Date: Mon, 31 May 2021 16:09:57 +0200 Subject: [PATCH] checker: forbid leaving function from inside `defer` block (#10285) --- vlib/v/parser/fn.v | 6 +++++ vlib/v/parser/parser.v | 30 ++++++++++++++++--------- vlib/v/parser/tests/defer_propagate.out | 7 ++++++ vlib/v/parser/tests/defer_propagate.vv | 16 +++++++++++++ vlib/v/parser/tests/defer_return.out | 7 ++++++ vlib/v/parser/tests/defer_return.vv | 5 +++++ vlib/v/parser/tests/defer_return2.out | 7 ++++++ vlib/v/parser/tests/defer_return2.vv | 12 ++++++++++ vlib/v/parser/tests/nested_defer.out | 7 ++++++ vlib/v/parser/tests/nested_defer.vv | 14 ++++++++++++ vlib/v/tests/nest_defer_fn_test.v | 30 +++++++++++++++++++++++++ 11 files changed, 130 insertions(+), 11 deletions(-) create mode 100644 vlib/v/parser/tests/defer_propagate.out create mode 100644 vlib/v/parser/tests/defer_propagate.vv create mode 100644 vlib/v/parser/tests/defer_return.out create mode 100644 vlib/v/parser/tests/defer_return.vv create mode 100644 vlib/v/parser/tests/defer_return2.out create mode 100644 vlib/v/parser/tests/defer_return2.vv create mode 100644 vlib/v/parser/tests/nested_defer.out create mode 100644 vlib/v/parser/tests/nested_defer.vv create mode 100644 vlib/v/tests/nest_defer_fn_test.v diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 610ab60037..8416896469 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -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{ diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index d935896542..5d8ccbb093 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -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 { diff --git a/vlib/v/parser/tests/defer_propagate.out b/vlib/v/parser/tests/defer_propagate.out new file mode 100644 index 0000000000..93dae37fc9 --- /dev/null +++ b/vlib/v/parser/tests/defer_propagate.out @@ -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 diff --git a/vlib/v/parser/tests/defer_propagate.vv b/vlib/v/parser/tests/defer_propagate.vv new file mode 100644 index 0000000000..ad92bb9cd0 --- /dev/null +++ b/vlib/v/parser/tests/defer_propagate.vv @@ -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) +} diff --git a/vlib/v/parser/tests/defer_return.out b/vlib/v/parser/tests/defer_return.out new file mode 100644 index 0000000000..2debd10475 --- /dev/null +++ b/vlib/v/parser/tests/defer_return.out @@ -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 | } diff --git a/vlib/v/parser/tests/defer_return.vv b/vlib/v/parser/tests/defer_return.vv new file mode 100644 index 0000000000..0bcc7407e2 --- /dev/null +++ b/vlib/v/parser/tests/defer_return.vv @@ -0,0 +1,5 @@ +fn main() { + defer { + return + } +} diff --git a/vlib/v/parser/tests/defer_return2.out b/vlib/v/parser/tests/defer_return2.out new file mode 100644 index 0000000000..54628e4d8f --- /dev/null +++ b/vlib/v/parser/tests/defer_return2.out @@ -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 diff --git a/vlib/v/parser/tests/defer_return2.vv b/vlib/v/parser/tests/defer_return2.vv new file mode 100644 index 0000000000..82f62aaa07 --- /dev/null +++ b/vlib/v/parser/tests/defer_return2.vv @@ -0,0 +1,12 @@ +fn test1() int { + defer { + return 12 + } + a := 3 + return a +} + +fn main() { + x := test1() + println(x) +} diff --git a/vlib/v/parser/tests/nested_defer.out b/vlib/v/parser/tests/nested_defer.out new file mode 100644 index 0000000000..c5da8dcb9a --- /dev/null +++ b/vlib/v/parser/tests/nested_defer.out @@ -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 | } diff --git a/vlib/v/parser/tests/nested_defer.vv b/vlib/v/parser/tests/nested_defer.vv new file mode 100644 index 0000000000..7abc2d3fe6 --- /dev/null +++ b/vlib/v/parser/tests/nested_defer.vv @@ -0,0 +1,14 @@ +fn test1() int { + mut a := 0 + defer { + a = 12 + defer { + a = 13 + } + } + return a + +fn main() { + x := test1() + println(x) +} diff --git a/vlib/v/tests/nest_defer_fn_test.v b/vlib/v/tests/nest_defer_fn_test.v new file mode 100644 index 0000000000..d7e0f60d06 --- /dev/null +++ b/vlib/v/tests/nest_defer_fn_test.v @@ -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 +}