mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
all: add $res compile time function to get returned value in defer block (#18382)
This commit is contained in:
parent
ac32d2a803
commit
dbd251793e
36
doc/docs.md
36
doc/docs.md
@ -2119,6 +2119,42 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
To access the result of the function inside a `defer` block the `$res()` expression can be used.
|
||||
`$res()` is only used when a single value is returned, while on multi-return the `$res(idx)`
|
||||
is parameterized.
|
||||
|
||||
```v ignore
|
||||
fn (mut app App) auth_middleware() bool {
|
||||
defer {
|
||||
if !$res() {
|
||||
app.response.status_code = 401
|
||||
app.response.body = 'Unauthorized'
|
||||
}
|
||||
}
|
||||
header := app.get_header('Authorization')
|
||||
if header == '' {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fn (mut app App) auth_with_user_middleware() (bool, string) {
|
||||
defer {
|
||||
if !$res(0) {
|
||||
app.response.status_code = 401
|
||||
app.response.body = 'Unauthorized'
|
||||
} else {
|
||||
app.user = $res(1)
|
||||
}
|
||||
}
|
||||
header := app.get_header('Authorization')
|
||||
if header == '' {
|
||||
return false, ''
|
||||
}
|
||||
return true, 'TestUser'
|
||||
}
|
||||
```
|
||||
|
||||
### Goto
|
||||
|
||||
V allows unconditionally jumping to a label with `goto`. The label name must be contained
|
||||
|
@ -115,6 +115,7 @@ mut:
|
||||
comptime_call_pos int // needed for correctly checking use before decl for templates
|
||||
goto_labels map[string]ast.GotoLabel // to check for unused goto labels
|
||||
enum_data_type ast.Type
|
||||
fn_return_type ast.Type
|
||||
}
|
||||
|
||||
pub fn new_checker(table &ast.Table, pref_ &pref.Preferences) &Checker {
|
||||
|
@ -153,6 +153,40 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
|
||||
// assume string for now
|
||||
return ast.string_type
|
||||
}
|
||||
if node.method_name == 'res' {
|
||||
if !c.inside_defer {
|
||||
c.error('`res` can only be used in defer blocks', node.pos)
|
||||
return ast.void_type
|
||||
}
|
||||
|
||||
if c.fn_return_type == ast.void_type {
|
||||
c.error('`res` can only be used in functions that returns something', node.pos)
|
||||
return ast.void_type
|
||||
}
|
||||
|
||||
sym := c.table.sym(c.fn_return_type)
|
||||
|
||||
if c.fn_return_type.has_flag(.result) {
|
||||
c.error('`res` cannot be used in functions that returns a Result', node.pos)
|
||||
return ast.void_type
|
||||
}
|
||||
|
||||
if sym.info is ast.MultiReturn {
|
||||
if node.args_var == '' {
|
||||
c.error('`res` requires an index of the returned value', node.pos)
|
||||
return ast.void_type
|
||||
}
|
||||
idx := node.args_var.int()
|
||||
if idx < 0 || idx >= sym.info.types.len {
|
||||
c.error('index ${idx} out of range of ${sym.info.types.len} return types',
|
||||
node.pos)
|
||||
return ast.void_type
|
||||
}
|
||||
return sym.info.types[idx]
|
||||
}
|
||||
|
||||
return c.fn_return_type
|
||||
}
|
||||
if node.is_vweb {
|
||||
return ast.string_type
|
||||
}
|
||||
|
@ -102,6 +102,7 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
||||
}
|
||||
}
|
||||
}
|
||||
c.fn_return_type = node.return_type
|
||||
if node.return_type != ast.void_type {
|
||||
if ct_attr_idx := node.attrs.find_comptime_define() {
|
||||
sexpr := node.attrs[ct_attr_idx].ct_expr.str()
|
||||
|
@ -0,0 +1,7 @@
|
||||
vlib/v/checker/tests/defer_use_multi_return_value_with_index_out_of_bounds.vv:3:11: error: index 2 out of range of 2 return types
|
||||
1 | fn test() (string, string) {
|
||||
2 | defer {
|
||||
3 | println($res(2))
|
||||
| ~~~~~~~
|
||||
4 | }
|
||||
5 | return 'test', 'test2'
|
@ -0,0 +1,6 @@
|
||||
fn test() (string, string) {
|
||||
defer {
|
||||
println($res(2))
|
||||
}
|
||||
return 'test', 'test2'
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
vlib/v/checker/tests/defer_use_multi_return_value_without_index.vv:3:11: error: `res` requires an index of the returned value
|
||||
1 | fn test() (string, string) {
|
||||
2 | defer {
|
||||
3 | println($res())
|
||||
| ~~~~~~
|
||||
4 | }
|
||||
5 | return 'test', 'test2'
|
@ -0,0 +1,6 @@
|
||||
fn test() (string, string) {
|
||||
defer {
|
||||
println($res())
|
||||
}
|
||||
return 'test', 'test2'
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
vlib/v/checker/tests/defer_use_returned_value_when_nothing_is_returned.vv:3:11: error: `res` can only be used in functions that returns something
|
||||
1 | fn test() {
|
||||
2 | defer {
|
||||
3 | println($res())
|
||||
| ~~~~~~
|
||||
4 | }
|
||||
5 | }
|
@ -0,0 +1,5 @@
|
||||
fn test() {
|
||||
defer {
|
||||
println($res())
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
vlib/v/checker/tests/defer_use_returned_value_when_result_is_returned.vv:3:11: error: `res` cannot be used in functions that returns a Result
|
||||
1 | fn test() !string {
|
||||
2 | defer {
|
||||
3 | println($res())
|
||||
| ~~~~~~
|
||||
4 | }
|
||||
5 | return 'test'
|
@ -0,0 +1,6 @@
|
||||
fn test() !string {
|
||||
defer {
|
||||
println($res())
|
||||
}
|
||||
return 'test'
|
||||
}
|
6
vlib/v/checker/tests/res_use_outside_defer.out
Normal file
6
vlib/v/checker/tests/res_use_outside_defer.out
Normal file
@ -0,0 +1,6 @@
|
||||
vlib/v/checker/tests/res_use_outside_defer.vv:2:10: error: `res` can only be used in defer blocks
|
||||
1 | fn test() string {
|
||||
2 | println($res())
|
||||
| ~~~~~~
|
||||
3 | return 'test'
|
||||
4 | }
|
4
vlib/v/checker/tests/res_use_outside_defer.vv
Normal file
4
vlib/v/checker/tests/res_use_outside_defer.vv
Normal file
@ -0,0 +1,4 @@
|
||||
fn test() string {
|
||||
println($res())
|
||||
return 'test'
|
||||
}
|
@ -1969,6 +1969,13 @@ pub fn (mut f Fmt) comptime_call(node ast.ComptimeCall) {
|
||||
node.method_name in ['compile_error', 'compile_warn'] {
|
||||
f.write("\$${node.method_name}('${node.args_var}')")
|
||||
}
|
||||
node.method_name == 'res' {
|
||||
if node.args_var != '' {
|
||||
f.write('\$res(${node.args_var})')
|
||||
} else {
|
||||
f.write('\$res()')
|
||||
}
|
||||
}
|
||||
else {
|
||||
inner_args := if node.args_var != '' {
|
||||
node.args_var
|
||||
|
@ -245,8 +245,9 @@ mut:
|
||||
out_fn_start_pos []int // for generating multiple .c files, stores locations of all fn positions in `out` string builder
|
||||
static_modifier string // for parallel_cc
|
||||
|
||||
has_reflection bool
|
||||
reflection_strings &map[string]int
|
||||
has_reflection bool
|
||||
reflection_strings &map[string]int
|
||||
defer_return_tmp_var string
|
||||
}
|
||||
|
||||
// global or const variable definition string
|
||||
@ -4699,6 +4700,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
||||
return
|
||||
}
|
||||
tmpvar := g.new_tmp_var()
|
||||
g.defer_return_tmp_var = tmpvar
|
||||
mut ret_typ := g.typ(g.unwrap_generic(fn_ret_type))
|
||||
if fn_ret_type.has_flag(.generic) && fn_return_is_fixed_array {
|
||||
ret_typ = '_v_${ret_typ}'
|
||||
@ -4742,7 +4744,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
||||
}
|
||||
}
|
||||
g.write_defer_stmts_when_needed()
|
||||
g.writeln('return ${tmpvar};')
|
||||
g.writeln('return ${tmpvar}; //test')
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -4770,7 +4772,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
||||
g.writeln(';')
|
||||
if use_tmp_var {
|
||||
g.write_defer_stmts_when_needed()
|
||||
g.writeln('return ${tmpvar};')
|
||||
g.writeln('return ${tmpvar}; //test1')
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -4878,7 +4880,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
||||
g.writeln(';')
|
||||
}
|
||||
g.write_defer_stmts_when_needed()
|
||||
g.writeln('return ${tmpvar};')
|
||||
g.writeln('return ${tmpvar}; //test2')
|
||||
has_semicolon = true
|
||||
}
|
||||
} else if node.exprs.len >= 1 {
|
||||
@ -4917,7 +4919,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
||||
g.writeln(' }, (${c.option_name}*)(&${tmpvar}), sizeof(${styp}));')
|
||||
g.write_defer_stmts_when_needed()
|
||||
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
|
||||
g.writeln('return ${tmpvar};')
|
||||
g.writeln('return ${tmpvar}; //test4')
|
||||
return
|
||||
}
|
||||
expr_type_is_result := match expr0 {
|
||||
@ -4950,7 +4952,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
||||
g.writeln(' }, (${c.result_name}*)(&${tmpvar}), sizeof(${styp}));')
|
||||
g.write_defer_stmts_when_needed()
|
||||
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
|
||||
g.writeln('return ${tmpvar};')
|
||||
g.writeln('return ${tmpvar}; //test 4')
|
||||
return
|
||||
}
|
||||
// autofree before `return`
|
||||
@ -5028,7 +5030,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
||||
if !g.is_builtin_mod {
|
||||
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
|
||||
}
|
||||
g.write('return ${tmpvar}')
|
||||
g.write('return ${tmpvar} /* test5 */')
|
||||
has_semicolon = false
|
||||
}
|
||||
} else {
|
||||
|
@ -80,6 +80,15 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
|
||||
g.write('_SLIT("${val}")')
|
||||
return
|
||||
}
|
||||
if node.method_name == 'res' {
|
||||
if node.args_var != '' {
|
||||
g.write('${g.defer_return_tmp_var}.arg${node.args_var}')
|
||||
return
|
||||
}
|
||||
|
||||
g.write('${g.defer_return_tmp_var}')
|
||||
return
|
||||
}
|
||||
if node.is_vweb {
|
||||
is_html := node.method_name == 'html'
|
||||
mut cur_line := ''
|
||||
|
@ -10,7 +10,7 @@ import v.token
|
||||
|
||||
const (
|
||||
supported_comptime_calls = ['html', 'tmpl', 'env', 'embed_file', 'pkgconfig', 'compile_error',
|
||||
'compile_warn']
|
||||
'compile_warn', 'res']
|
||||
comptime_types = ['map', 'array', 'int', 'float', 'struct', 'interface', 'enum',
|
||||
'sumtype', 'alias', 'function', 'option']
|
||||
)
|
||||
@ -97,7 +97,7 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
|
||||
}
|
||||
start_pos := p.tok.pos()
|
||||
p.check(.dollar)
|
||||
error_msg := 'only `\$tmpl()`, `\$env()`, `\$embed_file()`, `\$pkgconfig()`, `\$vweb.html()`, `\$compile_error()` and `\$compile_warn()` comptime functions are supported right now'
|
||||
error_msg := 'only `\$tmpl()`, `\$env()`, `\$embed_file()`, `\$pkgconfig()`, `\$vweb.html()`, `\$compile_error()`, `\$compile_warn()` and `\$res()` comptime functions are supported right now'
|
||||
if p.peek_tok.kind == .dot {
|
||||
name := p.check_name() // skip `vweb.html()` TODO
|
||||
if name != 'vweb' {
|
||||
@ -129,6 +129,28 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
|
||||
env_pos: start_pos
|
||||
pos: start_pos.extend(p.prev_tok.pos())
|
||||
}
|
||||
} else if method_name == 'res' {
|
||||
mut has_args := false
|
||||
mut type_index := ''
|
||||
if p.tok.kind == .number {
|
||||
has_args = true
|
||||
type_index = p.tok.lit
|
||||
p.check(.number)
|
||||
}
|
||||
p.check(.rpar)
|
||||
if has_args {
|
||||
return ast.ComptimeCall{
|
||||
scope: 0
|
||||
method_name: method_name
|
||||
args_var: type_index
|
||||
pos: start_pos.extend(p.prev_tok.pos())
|
||||
}
|
||||
}
|
||||
return ast.ComptimeCall{
|
||||
scope: 0
|
||||
method_name: method_name
|
||||
pos: start_pos.extend(p.prev_tok.pos())
|
||||
}
|
||||
}
|
||||
mut literal_string_param := if is_html { '' } else { p.tok.lit }
|
||||
if p.tok.kind == .name {
|
||||
|
40
vlib/v/tests/defer_use_returned_value_test.v
Normal file
40
vlib/v/tests/defer_use_returned_value_test.v
Normal file
@ -0,0 +1,40 @@
|
||||
struct Test {
|
||||
mut:
|
||||
a int
|
||||
b string
|
||||
}
|
||||
|
||||
fn (mut test Test) with_single_return() int {
|
||||
defer {
|
||||
test.a = $res()
|
||||
}
|
||||
return 41
|
||||
}
|
||||
|
||||
fn (mut test Test) with_multi_return() (int, string) {
|
||||
defer {
|
||||
test.a = $res(0)
|
||||
test.b = $res(1)
|
||||
}
|
||||
return 41, 'foo'
|
||||
}
|
||||
|
||||
fn test_with_single_return() {
|
||||
mut test := Test{
|
||||
a: 0
|
||||
}
|
||||
assert test.with_single_return() == 41
|
||||
assert test.a == 41
|
||||
}
|
||||
|
||||
fn test_with_multi_return() {
|
||||
mut test := Test{
|
||||
a: 0
|
||||
b: ''
|
||||
}
|
||||
a, b := test.with_multi_return()
|
||||
assert a == 41
|
||||
assert b == 'foo'
|
||||
assert test.a == a
|
||||
assert test.b == b
|
||||
}
|
Loading…
Reference in New Issue
Block a user