mirror of
https://github.com/vlang/v.git
synced 2025-09-13 22:42:26 +03:00
parent
7c3c5891b0
commit
62bdf990d0
23 changed files with 294 additions and 37 deletions
|
@ -23,7 +23,7 @@ fn htonl64(payload_len u64) []u8 {
|
||||||
|
|
||||||
// create_masking_key returns a new masking key to use when masking websocket messages
|
// create_masking_key returns a new masking key to use when masking websocket messages
|
||||||
fn create_masking_key() []u8 {
|
fn create_masking_key() []u8 {
|
||||||
return rand.bytes(4) or { [0, 0, 0, 0] }
|
return rand.bytes(4) or { [u8(0), 0, 0, 0] }
|
||||||
}
|
}
|
||||||
|
|
||||||
// create_key_challenge_response creates a key challenge response from security key
|
// create_key_challenge_response creates a key challenge response from security key
|
||||||
|
|
|
@ -19,7 +19,10 @@ pub fn test_u64toa_large_values() {
|
||||||
mut buf := [20]u8{}
|
mut buf := [20]u8{}
|
||||||
|
|
||||||
len := unsafe {
|
len := unsafe {
|
||||||
u64toa(&buf[0], v) or { assert err.msg() == 'Maximum size of 100MB exceeded!' }
|
u64toa(&buf[0], v) or {
|
||||||
|
assert err.msg() == 'Maximum size of 100MB exceeded!'
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v < 100_000_000 {
|
if v < 100_000_000 {
|
||||||
|
@ -38,7 +41,10 @@ pub fn test_u64toa_edge_cases() {
|
||||||
|
|
||||||
// Test zero value
|
// Test zero value
|
||||||
len := unsafe {
|
len := unsafe {
|
||||||
u64toa(&buf[0], 0) or { assert false }
|
u64toa(&buf[0], 0) or {
|
||||||
|
assert false
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert len == 1
|
assert len == 1
|
||||||
|
|
|
@ -126,6 +126,7 @@ mut:
|
||||||
generic_fns map[string]bool // register generic fns that needs recheck once
|
generic_fns map[string]bool // register generic fns that needs recheck once
|
||||||
inside_sql bool // to handle sql table fields pseudo variables
|
inside_sql bool // to handle sql table fields pseudo variables
|
||||||
inside_selector_expr bool
|
inside_selector_expr bool
|
||||||
|
inside_or_block_value bool // true inside or-block where its value is used `f(g() or { true })`
|
||||||
inside_interface_deref bool
|
inside_interface_deref bool
|
||||||
inside_decl_rhs bool
|
inside_decl_rhs bool
|
||||||
inside_if_guard bool // true inside the guard condition of `if x := opt() {}`
|
inside_if_guard bool // true inside the guard condition of `if x := opt() {}`
|
||||||
|
@ -1354,16 +1355,25 @@ fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_return
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if node.stmts.len == 0 {
|
if node.stmts.len == 0 {
|
||||||
if ret_type != ast.void_type {
|
if expr is ast.CallExpr && expr.is_return_used {
|
||||||
// x := f() or {}
|
// x := f() or {}, || f() or {} etc
|
||||||
c.error('assignment requires a non empty `or {}` block', node.pos)
|
c.error('expression requires a non empty `or {}` block', node.pos)
|
||||||
|
} else if expr !is ast.CallExpr && ret_type != ast.void_type {
|
||||||
|
// _ := sql db {... } or { }
|
||||||
|
c.error('expression requires a non empty `or {}` block', node.pos)
|
||||||
}
|
}
|
||||||
// allow `f() or {}`
|
// allow `f() or {}`
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mut valid_stmts := node.stmts.filter(it !is ast.SemicolonStmt)
|
mut valid_stmts := node.stmts.filter(it !is ast.SemicolonStmt)
|
||||||
mut last_stmt := if valid_stmts.len > 0 { valid_stmts.last() } else { node.stmts.last() }
|
mut last_stmt := if valid_stmts.len > 0 { valid_stmts.last() } else { node.stmts.last() }
|
||||||
|
if expr !is ast.CallExpr || (expr is ast.CallExpr && expr.is_return_used) {
|
||||||
|
// requires a block returning an unwrapped type of expr return type
|
||||||
c.check_or_last_stmt(mut last_stmt, ret_type, expr_return_type.clear_option_and_result())
|
c.check_or_last_stmt(mut last_stmt, ret_type, expr_return_type.clear_option_and_result())
|
||||||
|
} else {
|
||||||
|
// allow f() or { var = 123 }
|
||||||
|
c.check_or_last_stmt(mut last_stmt, ast.void_type, expr_return_type.clear_option_and_result())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut c Checker) check_or_last_stmt(mut stmt ast.Stmt, ret_type ast.Type, expr_return_type ast.Type) {
|
fn (mut c Checker) check_or_last_stmt(mut stmt ast.Stmt, ret_type ast.Type, expr_return_type ast.Type) {
|
||||||
|
@ -1372,6 +1382,10 @@ fn (mut c Checker) check_or_last_stmt(mut stmt ast.Stmt, ret_type ast.Type, expr
|
||||||
ast.ExprStmt {
|
ast.ExprStmt {
|
||||||
c.expected_type = ret_type
|
c.expected_type = ret_type
|
||||||
c.expected_or_type = ret_type.clear_option_and_result()
|
c.expected_or_type = ret_type.clear_option_and_result()
|
||||||
|
if c.inside_or_block_value && stmt.expr is ast.None && ret_type.has_flag(.option) {
|
||||||
|
// return call() or { none } where fn returns an Option type
|
||||||
|
return
|
||||||
|
}
|
||||||
last_stmt_typ := c.expr(mut stmt.expr)
|
last_stmt_typ := c.expr(mut stmt.expr)
|
||||||
if last_stmt_typ.has_flag(.option) || last_stmt_typ == ast.none_type {
|
if last_stmt_typ.has_flag(.option) || last_stmt_typ == ast.none_type {
|
||||||
if stmt.expr in [ast.Ident, ast.SelectorExpr, ast.CallExpr, ast.None, ast.CastExpr] {
|
if stmt.expr in [ast.Ident, ast.SelectorExpr, ast.CallExpr, ast.None, ast.CastExpr] {
|
||||||
|
@ -1437,11 +1451,13 @@ fn (mut c Checker) check_or_last_stmt(mut stmt ast.Stmt, ret_type ast.Type, expr
|
||||||
}
|
}
|
||||||
ast.Return {}
|
ast.Return {}
|
||||||
else {
|
else {
|
||||||
|
if stmt !is ast.AssertStmt || c.inside_or_block_value {
|
||||||
expected_type_name := c.table.type_to_str(ret_type.clear_option_and_result())
|
expected_type_name := c.table.type_to_str(ret_type.clear_option_and_result())
|
||||||
c.error('last statement in the `or {}` block should be an expression of type `${expected_type_name}` or exit parent scope',
|
c.error('last statement in the `or {}` block should be an expression of type `${expected_type_name}` or exit parent scope',
|
||||||
stmt.pos)
|
stmt.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if mut stmt is ast.ExprStmt {
|
} else if mut stmt is ast.ExprStmt {
|
||||||
match mut stmt.expr {
|
match mut stmt.expr {
|
||||||
ast.IfExpr {
|
ast.IfExpr {
|
||||||
|
|
|
@ -737,9 +737,17 @@ fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
old_expected_or_type := c.expected_or_type
|
||||||
c.expected_or_type = node.return_type.clear_flag(.result)
|
c.expected_or_type = node.return_type.clear_flag(.result)
|
||||||
c.stmts_ending_with_expression(mut node.or_block.stmts, c.expected_or_type)
|
c.stmts_ending_with_expression(mut node.or_block.stmts, c.expected_or_type)
|
||||||
c.expected_or_type = ast.void_type
|
|
||||||
|
if node.or_block.kind == .block {
|
||||||
|
old_inside_or_block_value := c.inside_or_block_value
|
||||||
|
c.inside_or_block_value = true
|
||||||
|
c.check_or_expr(node.or_block, typ, c.expected_or_type, node)
|
||||||
|
c.inside_or_block_value = old_inside_or_block_value
|
||||||
|
}
|
||||||
|
c.expected_or_type = old_expected_or_type
|
||||||
|
|
||||||
if !c.inside_const && c.table.cur_fn != unsafe { nil } && !c.table.cur_fn.is_main
|
if !c.inside_const && c.table.cur_fn != unsafe { nil } && !c.table.cur_fn.is_main
|
||||||
&& !c.table.cur_fn.is_test {
|
&& !c.table.cur_fn.is_test {
|
||||||
|
|
|
@ -65,6 +65,9 @@ pub fn (mut c Checker) lambda_expr(mut node ast.LambdaExpr, exp_typ ast.Type) as
|
||||||
is_expr: false
|
is_expr: false
|
||||||
typ: return_type
|
typ: return_type
|
||||||
}
|
}
|
||||||
|
if mut node.expr is ast.CallExpr && node.expr.is_return_used {
|
||||||
|
node.expr.is_return_used = false
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
stmts << ast.Return{
|
stmts << ast.Return{
|
||||||
pos: node.pos
|
pos: node.pos
|
||||||
|
|
12
vlib/v/checker/or_block_assert_err.out
Normal file
12
vlib/v/checker/or_block_assert_err.out
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
vlib/v/checker/or_block_assert_err.vv:10:22: error: last statement in the `or {}` block should be an expression of type `int` or exit parent scope
|
||||||
|
8 |
|
||||||
|
9 | f() or { assert true }
|
||||||
|
10 | a := f() or { assert true }
|
||||||
|
| ~~~~~~
|
||||||
|
11 | dump(a)
|
||||||
|
12 | g(f() or { assert true })
|
||||||
|
vlib/v/checker/or_block_assert_err.vv:12:19: error: last statement in the `or {}` block should be an expression of type `int` or exit parent scope
|
||||||
|
10 | a := f() or { assert true }
|
||||||
|
11 | dump(a)
|
||||||
|
12 | g(f() or { assert true })
|
||||||
|
| ~~~~~~
|
12
vlib/v/checker/or_block_assert_err.vv
Normal file
12
vlib/v/checker/or_block_assert_err.vv
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
fn g(x int) {
|
||||||
|
dump(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() ?int {
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
|
||||||
|
f() or { assert true }
|
||||||
|
a := f() or { assert true }
|
||||||
|
dump(a)
|
||||||
|
g(f() or { assert true })
|
56
vlib/v/checker/tests/call_empty_or_block_err.out
Normal file
56
vlib/v/checker/tests/call_empty_or_block_err.out
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
vlib/v/checker/tests/call_empty_or_block_err.vv:9:2: warning: unused variable: `a`
|
||||||
|
7 |
|
||||||
|
8 | fn main() {
|
||||||
|
9 | a := foo() or { foo() or {} }
|
||||||
|
| ^
|
||||||
|
10 |
|
||||||
|
11 | // must be error
|
||||||
|
vlib/v/checker/tests/call_empty_or_block_err.vv:12:2: warning: unused variable: `y`
|
||||||
|
10 |
|
||||||
|
11 | // must be error
|
||||||
|
12 | y := if c := foo() {
|
||||||
|
| ^
|
||||||
|
13 | dump(c)
|
||||||
|
14 | bar() or {}
|
||||||
|
vlib/v/checker/tests/call_empty_or_block_err.vv:20:2: warning: unused variable: `z`
|
||||||
|
18 |
|
||||||
|
19 | // ok
|
||||||
|
20 | z := if d := foo() {
|
||||||
|
| ^
|
||||||
|
21 | dump(d)
|
||||||
|
22 | bar() or {}
|
||||||
|
vlib/v/checker/tests/call_empty_or_block_err.vv:29:2: warning: unused variable: `w`
|
||||||
|
27 |
|
||||||
|
28 | // ok
|
||||||
|
29 | w := foo() or {
|
||||||
|
| ^
|
||||||
|
30 | bar() or {}
|
||||||
|
31 | false
|
||||||
|
vlib/v/checker/tests/call_empty_or_block_err.vv:35:2: warning: unused variable: `b`
|
||||||
|
33 |
|
||||||
|
34 | // ok
|
||||||
|
35 | b := foo() or {
|
||||||
|
| ^
|
||||||
|
36 | foo() or {}
|
||||||
|
37 | false
|
||||||
|
vlib/v/checker/tests/call_empty_or_block_err.vv:9:24: error: expression requires a non empty `or {}` block
|
||||||
|
7 |
|
||||||
|
8 | fn main() {
|
||||||
|
9 | a := foo() or { foo() or {} }
|
||||||
|
| ~~~~~
|
||||||
|
10 |
|
||||||
|
11 | // must be error
|
||||||
|
vlib/v/checker/tests/call_empty_or_block_err.vv:14:9: error: expression requires a non empty `or {}` block
|
||||||
|
12 | y := if c := foo() {
|
||||||
|
13 | dump(c)
|
||||||
|
14 | bar() or {}
|
||||||
|
| ~~~~~
|
||||||
|
15 | } else {
|
||||||
|
16 | false
|
||||||
|
vlib/v/checker/tests/call_empty_or_block_err.vv:14:3: error: the final expression in `if` or `match`, must have a value of a non-void type
|
||||||
|
12 | y := if c := foo() {
|
||||||
|
13 | dump(c)
|
||||||
|
14 | bar() or {}
|
||||||
|
| ~~~~~
|
||||||
|
15 | } else {
|
||||||
|
16 | false
|
39
vlib/v/checker/tests/call_empty_or_block_err.vv
Normal file
39
vlib/v/checker/tests/call_empty_or_block_err.vv
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
fn foo() !bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar() ! {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
a := foo() or { foo() or {} }
|
||||||
|
|
||||||
|
// must be error
|
||||||
|
y := if c := foo() {
|
||||||
|
dump(c)
|
||||||
|
bar() or {}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ok
|
||||||
|
z := if d := foo() {
|
||||||
|
dump(d)
|
||||||
|
bar() or {}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ok
|
||||||
|
w := foo() or {
|
||||||
|
bar() or {}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ok
|
||||||
|
b := foo() or {
|
||||||
|
foo() or {}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
6
vlib/v/checker/tests/fn_call_or_block_err.out
Normal file
6
vlib/v/checker/tests/fn_call_or_block_err.out
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
vlib/v/checker/tests/fn_call_or_block_err.vv:7:18: error: wrong return type `int literal` in the `or {}` block, expected `string`
|
||||||
|
5 | }
|
||||||
|
6 |
|
||||||
|
7 | y := Aa(f() or { 2 })
|
||||||
|
| ^
|
||||||
|
8 | println(y)
|
8
vlib/v/checker/tests/fn_call_or_block_err.vv
Normal file
8
vlib/v/checker/tests/fn_call_or_block_err.vv
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
type Aa = string | int
|
||||||
|
|
||||||
|
fn f() !string {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
y := Aa(f() or { 2 })
|
||||||
|
println(y)
|
14
vlib/v/checker/tests/lambda_or_block_err.out
Normal file
14
vlib/v/checker/tests/lambda_or_block_err.out
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
vlib/v/checker/tests/lambda_or_block_err.vv:10:10: error: cannot use `!int` as type `int` in return argument
|
||||||
|
8 |
|
||||||
|
9 | fn main() {
|
||||||
|
10 | foo(|i| bar(i))
|
||||||
|
| ~~~~~~
|
||||||
|
11 | foo(|i| bar(i) or {})
|
||||||
|
12 | foo(|i| bar(i) or { 0 })
|
||||||
|
vlib/v/checker/tests/lambda_or_block_err.vv:11:17: error: expression requires a non empty `or {}` block
|
||||||
|
9 | fn main() {
|
||||||
|
10 | foo(|i| bar(i))
|
||||||
|
11 | foo(|i| bar(i) or {})
|
||||||
|
| ~~~~~
|
||||||
|
12 | foo(|i| bar(i) or { 0 })
|
||||||
|
13 | }
|
13
vlib/v/checker/tests/lambda_or_block_err.vv
Normal file
13
vlib/v/checker/tests/lambda_or_block_err.vv
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
fn foo(callback fn (int) int) {
|
||||||
|
dump([1, 2, 3].map(callback))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar(a int) !int {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo(|i| bar(i))
|
||||||
|
foo(|i| bar(i) or {})
|
||||||
|
foo(|i| bar(i) or { 0 })
|
||||||
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
vlib/v/checker/tests/or_block_check_err.vv:6:36: error: assignment requires a non empty `or {}` block
|
vlib/v/checker/tests/or_block_check_err.vv:6:36: error: expression requires a non empty `or {}` block
|
||||||
4 |
|
4 |
|
||||||
5 | fn main() {
|
5 | fn main() {
|
||||||
6 | _ = callexpr_with_or_block_call() or {}.replace('a', 'b')
|
6 | _ = callexpr_with_or_block_call() or {}.replace('a', 'b')
|
||||||
| ~~~~~
|
| ~~~~~
|
||||||
7 | _ = (callexpr_with_or_block_call() or {}).replace('a', 'b')
|
7 | _ = (callexpr_with_or_block_call() or {}).replace('a', 'b')
|
||||||
8 |
|
8 |
|
||||||
vlib/v/checker/tests/or_block_check_err.vv:7:37: error: assignment requires a non empty `or {}` block
|
vlib/v/checker/tests/or_block_check_err.vv:7:37: error: expression requires a non empty `or {}` block
|
||||||
5 | fn main() {
|
5 | fn main() {
|
||||||
6 | _ = callexpr_with_or_block_call() or {}.replace('a', 'b')
|
6 | _ = callexpr_with_or_block_call() or {}.replace('a', 'b')
|
||||||
7 | _ = (callexpr_with_or_block_call() or {}).replace('a', 'b')
|
7 | _ = (callexpr_with_or_block_call() or {}).replace('a', 'b')
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
vlib/v/checker/tests/orm_no_default_value.vv:11:4: error: assignment requires a non empty `or {}` block
|
vlib/v/checker/tests/orm_no_default_value.vv:11:4: error: expression requires a non empty `or {}` block
|
||||||
9 | _ := sql db {
|
9 | _ := sql db {
|
||||||
10 | select from Person
|
10 | select from Person
|
||||||
11 | } or {
|
11 | } or {
|
||||||
|
|
|
@ -276,6 +276,7 @@ pub fn (mut e Eval) register_symbol(stmt ast.Stmt, mod string, file string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@[noreturn]
|
||||||
fn (e &Eval) error(msg string) {
|
fn (e &Eval) error(msg string) {
|
||||||
eprintln('> V interpreter backtrace:')
|
eprintln('> V interpreter backtrace:')
|
||||||
e.print_backtrace()
|
e.print_backtrace()
|
||||||
|
|
|
@ -188,7 +188,7 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
|
||||||
// Always use this in -autofree, since ?: can have tmp expressions that have to be freed.
|
// Always use this in -autofree, since ?: can have tmp expressions that have to be freed.
|
||||||
needs_tmp_var := g.need_tmp_var_in_if(node)
|
needs_tmp_var := g.need_tmp_var_in_if(node)
|
||||||
needs_conds_order := g.needs_conds_order(node)
|
needs_conds_order := g.needs_conds_order(node)
|
||||||
tmp := if needs_tmp_var { g.new_tmp_var() } else { '' }
|
tmp := if node.typ != ast.void_type && needs_tmp_var { g.new_tmp_var() } else { '' }
|
||||||
mut cur_line := ''
|
mut cur_line := ''
|
||||||
mut raw_state := false
|
mut raw_state := false
|
||||||
if needs_tmp_var {
|
if needs_tmp_var {
|
||||||
|
@ -210,7 +210,9 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
|
||||||
}
|
}
|
||||||
cur_line = g.go_before_last_stmt()
|
cur_line = g.go_before_last_stmt()
|
||||||
g.empty_line = true
|
g.empty_line = true
|
||||||
|
if tmp != '' {
|
||||||
g.writeln('${styp} ${tmp}; /* if prepend */')
|
g.writeln('${styp} ${tmp}; /* if prepend */')
|
||||||
|
}
|
||||||
if g.infix_left_var_name.len > 0 {
|
if g.infix_left_var_name.len > 0 {
|
||||||
g.writeln('if (${g.infix_left_var_name}) {')
|
g.writeln('if (${g.infix_left_var_name}) {')
|
||||||
g.indent++
|
g.indent++
|
||||||
|
|
|
@ -787,7 +787,9 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr {
|
||||||
|
|
||||||
right_op_pos := p.tok.pos()
|
right_op_pos := p.tok.pos()
|
||||||
old_assign_rhs := p.inside_assign_rhs
|
old_assign_rhs := p.inside_assign_rhs
|
||||||
|
if op in [.decl_assign, .assign] {
|
||||||
p.inside_assign_rhs = true
|
p.inside_assign_rhs = true
|
||||||
|
}
|
||||||
right = p.expr(precedence)
|
right = p.expr(precedence)
|
||||||
p.inside_assign_rhs = old_assign_rhs
|
p.inside_assign_rhs = old_assign_rhs
|
||||||
if op in [.plus, .minus, .mul, .div, .mod, .lt, .eq] && mut right is ast.PrefixExpr {
|
if op in [.plus, .minus, .mul, .div, .mod, .lt, .eq] && mut right is ast.PrefixExpr {
|
||||||
|
|
|
@ -872,7 +872,10 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
|
||||||
if p.tok.kind == .lcbr {
|
if p.tok.kind == .lcbr {
|
||||||
tmp := p.label_names
|
tmp := p.label_names
|
||||||
p.label_names = []
|
p.label_names = []
|
||||||
|
old_assign_rhs := p.inside_assign_rhs
|
||||||
|
p.inside_assign_rhs = false
|
||||||
stmts = p.parse_block_no_scope(false)
|
stmts = p.parse_block_no_scope(false)
|
||||||
|
p.inside_assign_rhs = old_assign_rhs
|
||||||
label_names = p.label_names.clone()
|
label_names = p.label_names.clone()
|
||||||
p.label_names = tmp
|
p.label_names = tmp
|
||||||
}
|
}
|
||||||
|
@ -1164,16 +1167,19 @@ fn (mut p Parser) fn_params() ([]ast.Param, bool, bool, bool) {
|
||||||
fn (mut p Parser) spawn_expr() ast.SpawnExpr {
|
fn (mut p Parser) spawn_expr() ast.SpawnExpr {
|
||||||
p.next()
|
p.next()
|
||||||
spos := p.tok.pos()
|
spos := p.tok.pos()
|
||||||
|
old_inside_assign_rhs := p.inside_assign_rhs
|
||||||
|
p.inside_assign_rhs = false
|
||||||
expr := p.expr(0)
|
expr := p.expr(0)
|
||||||
call_expr := if expr is ast.CallExpr {
|
p.inside_assign_rhs = old_inside_assign_rhs
|
||||||
|
mut call_expr := if expr is ast.CallExpr {
|
||||||
expr
|
expr
|
||||||
} else {
|
} else {
|
||||||
p.error_with_pos('expression in `spawn` must be a function call', expr.pos())
|
p.error_with_pos('expression in `spawn` must be a function call', expr.pos())
|
||||||
ast.CallExpr{
|
ast.CallExpr{
|
||||||
scope: p.scope
|
scope: p.scope
|
||||||
is_return_used: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
call_expr.is_return_used = true
|
||||||
pos := spos.extend(p.prev_tok.pos())
|
pos := spos.extend(p.prev_tok.pos())
|
||||||
p.register_auto_import('sync.threads')
|
p.register_auto_import('sync.threads')
|
||||||
p.table.gostmts++
|
p.table.gostmts++
|
||||||
|
@ -1186,16 +1192,19 @@ fn (mut p Parser) spawn_expr() ast.SpawnExpr {
|
||||||
fn (mut p Parser) go_expr() ast.GoExpr {
|
fn (mut p Parser) go_expr() ast.GoExpr {
|
||||||
p.next()
|
p.next()
|
||||||
spos := p.tok.pos()
|
spos := p.tok.pos()
|
||||||
|
old_inside_assign_rhs := p.inside_assign_rhs
|
||||||
|
p.inside_assign_rhs = false
|
||||||
expr := p.expr(0)
|
expr := p.expr(0)
|
||||||
call_expr := if expr is ast.CallExpr {
|
p.inside_assign_rhs = old_inside_assign_rhs
|
||||||
|
mut call_expr := if expr is ast.CallExpr {
|
||||||
expr
|
expr
|
||||||
} else {
|
} else {
|
||||||
p.error_with_pos('expression in `go` must be a function call', expr.pos())
|
p.error_with_pos('expression in `go` must be a function call', expr.pos())
|
||||||
ast.CallExpr{
|
ast.CallExpr{
|
||||||
scope: p.scope
|
scope: p.scope
|
||||||
is_return_used: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
call_expr.is_return_used = true
|
||||||
pos := spos.extend(p.prev_tok.pos())
|
pos := spos.extend(p.prev_tok.pos())
|
||||||
// p.register_auto_import('coroutines')
|
// p.register_auto_import('coroutines')
|
||||||
p.table.gostmts++
|
p.table.gostmts++
|
||||||
|
|
|
@ -170,13 +170,6 @@ fn (mut p Parser) if_expr(is_comptime bool, is_expr bool) ast.IfExpr {
|
||||||
}
|
}
|
||||||
p.open_scope()
|
p.open_scope()
|
||||||
stmts := p.parse_block_no_scope(false)
|
stmts := p.parse_block_no_scope(false)
|
||||||
// if the last expr is a callexpr mark its return as used
|
|
||||||
if p.inside_assign_rhs && stmts.len > 0 && stmts.last() is ast.ExprStmt {
|
|
||||||
mut last_expr := stmts.last() as ast.ExprStmt
|
|
||||||
if mut last_expr.expr is ast.CallExpr {
|
|
||||||
last_expr.expr.is_return_used = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
branches << ast.IfBranch{
|
branches << ast.IfBranch{
|
||||||
cond: cond
|
cond: cond
|
||||||
stmts: stmts
|
stmts: stmts
|
||||||
|
@ -361,12 +354,6 @@ fn (mut p Parser) match_expr() ast.MatchExpr {
|
||||||
pos := branch_first_pos.extend_with_last_line(branch_last_pos, p.prev_tok.line_nr)
|
pos := branch_first_pos.extend_with_last_line(branch_last_pos, p.prev_tok.line_nr)
|
||||||
branch_pos := branch_first_pos.extend_with_last_line(p.tok.pos(), p.tok.line_nr)
|
branch_pos := branch_first_pos.extend_with_last_line(p.tok.pos(), p.tok.line_nr)
|
||||||
post_comments := p.eat_comments()
|
post_comments := p.eat_comments()
|
||||||
if p.inside_assign_rhs && stmts.len > 0 && stmts.last() is ast.ExprStmt {
|
|
||||||
mut last_expr := stmts.last() as ast.ExprStmt
|
|
||||||
if mut last_expr.expr is ast.CallExpr {
|
|
||||||
last_expr.expr.is_return_used = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
branches << ast.MatchBranch{
|
branches << ast.MatchBranch{
|
||||||
exprs: exprs
|
exprs: exprs
|
||||||
ecmnts: ecmnts
|
ecmnts: ecmnts
|
||||||
|
|
|
@ -593,6 +593,8 @@ fn (mut p Parser) parse_block() []ast.Stmt {
|
||||||
fn (mut p Parser) parse_block_no_scope(is_top_level bool) []ast.Stmt {
|
fn (mut p Parser) parse_block_no_scope(is_top_level bool) []ast.Stmt {
|
||||||
p.check(.lcbr)
|
p.check(.lcbr)
|
||||||
mut stmts := []ast.Stmt{cap: 20}
|
mut stmts := []ast.Stmt{cap: 20}
|
||||||
|
old_assign_rhs := p.inside_assign_rhs
|
||||||
|
p.inside_assign_rhs = false
|
||||||
if p.tok.kind != .rcbr {
|
if p.tok.kind != .rcbr {
|
||||||
mut count := 0
|
mut count := 0
|
||||||
for p.tok.kind !in [.eof, .rcbr] {
|
for p.tok.kind !in [.eof, .rcbr] {
|
||||||
|
@ -608,13 +610,51 @@ fn (mut p Parser) parse_block_no_scope(is_top_level bool) []ast.Stmt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
p.inside_assign_rhs = old_assign_rhs
|
||||||
if is_top_level {
|
if is_top_level {
|
||||||
p.top_level_statement_end()
|
p.top_level_statement_end()
|
||||||
}
|
}
|
||||||
p.check(.rcbr)
|
p.check(.rcbr)
|
||||||
|
// on assignment the last callexpr must be marked as return used recursively
|
||||||
|
if p.inside_assign_rhs && stmts.len > 0 {
|
||||||
|
mut last_stmt := stmts.last()
|
||||||
|
p.mark_last_call_return_as_used(mut last_stmt)
|
||||||
|
}
|
||||||
return stmts
|
return stmts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut p Parser) mark_last_call_return_as_used(mut last_stmt ast.Stmt) {
|
||||||
|
match mut last_stmt {
|
||||||
|
ast.ExprStmt {
|
||||||
|
match mut last_stmt.expr {
|
||||||
|
ast.CallExpr {
|
||||||
|
// last stmt on block is CallExpr
|
||||||
|
last_stmt.expr.is_return_used = true
|
||||||
|
}
|
||||||
|
ast.IfExpr {
|
||||||
|
// last stmt on block is: if .. { foo() } else { bar() }
|
||||||
|
for mut branch in last_stmt.expr.branches {
|
||||||
|
if branch.stmts.len > 0 {
|
||||||
|
mut last_if_stmt := branch.stmts.last()
|
||||||
|
p.mark_last_call_return_as_used(mut last_if_stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast.ConcatExpr {
|
||||||
|
// last stmt on block is: a, b, c := ret1(), ret2(), ret3()
|
||||||
|
for mut expr in last_stmt.expr.vals {
|
||||||
|
if mut expr is ast.CallExpr {
|
||||||
|
expr.is_return_used = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut p Parser) next() {
|
fn (mut p Parser) next() {
|
||||||
p.prev_tok = p.tok
|
p.prev_tok = p.tok
|
||||||
p.tok = p.peek_tok
|
p.tok = p.peek_tok
|
||||||
|
|
17
vlib/v/tests/fns/call_or_empty_block_test.v
Normal file
17
vlib/v/tests/fns/call_or_empty_block_test.v
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
fn foo() ! {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar() ?int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_main() {
|
||||||
|
y := if a := bar() {
|
||||||
|
dump(a)
|
||||||
|
foo() or {}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
assert y
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ fn find_startswith_string(a []string, search string) ?string {
|
||||||
fn find_any_startswith_string(a []string, b []string, search string) ?string {
|
fn find_any_startswith_string(a []string, b []string, search string) ?string {
|
||||||
// cannot convert 'struct _option_string' to 'struct string'
|
// cannot convert 'struct _option_string' to 'struct string'
|
||||||
// V wants the or {} block to return a string, but find_startswith_string returns ?string
|
// V wants the or {} block to return a string, but find_startswith_string returns ?string
|
||||||
return find_startswith_string(a, search) or { find_startswith_string(b, search) }
|
return find_startswith_string(a, search) or { find_startswith_string(b, search)? }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_any_startswith_string_unwrapped(a []string, b []string, search string) ?string {
|
fn find_any_startswith_string_unwrapped(a []string, b []string, search string) ?string {
|
||||||
|
@ -26,4 +26,10 @@ fn test_main() {
|
||||||
var2 := find_any_startswith_string_unwrapped(['foobar', 'barfoo'], ['deadbeef', 'beefdead'],
|
var2 := find_any_startswith_string_unwrapped(['foobar', 'barfoo'], ['deadbeef', 'beefdead'],
|
||||||
'dead')
|
'dead')
|
||||||
dump(var2)
|
dump(var2)
|
||||||
|
assert var2 != none
|
||||||
|
|
||||||
|
var3 := find_any_startswith_string_unwrapped(['foobar', 'barfoo'], ['deadbeef', 'beefdead'],
|
||||||
|
'error')
|
||||||
|
dump(var3)
|
||||||
|
assert var3 == none
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue