1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

checker: fix missing unwrap check for option to non-option argument (#18460)

This commit is contained in:
Felipe Pena 2023-06-16 03:46:33 -03:00 committed by GitHub
parent 5d0af69bfd
commit ccb70b1292
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 124 additions and 1 deletions

View File

@ -234,6 +234,12 @@ fn (mut c Checker) check_expected_call_arg(got ast.Type, expected_ ast.Type, lan
got_typ_str, expected_typ_str := c.get_string_names_of(got, expected) got_typ_str, expected_typ_str := c.get_string_names_of(got, expected)
return error('cannot use `${got_typ_str}` as `${expected_typ_str}`') return error('cannot use `${got_typ_str}` as `${expected_typ_str}`')
} }
if !expected.has_flag(.option) && got.has_flag(.option) && (arg.expr !is ast.Ident
|| (arg.expr is ast.Ident && c.get_ct_type_var(arg.expr) != .field_var)) {
got_typ_str, expected_typ_str := c.get_string_names_of(got, expected)
return error('cannot use `${got_typ_str}` as `${expected_typ_str}`, it must be unwrapped first')
}
} }
// check int signed/unsigned mismatch // check int signed/unsigned mismatch
if got == ast.int_literal_type_idx && expected in ast.unsigned_integer_type_idxs if got == ast.int_literal_type_idx && expected in ast.unsigned_integer_type_idxs

View File

@ -9,6 +9,15 @@ import v.token
import v.util import v.util
import v.pkgconfig import v.pkgconfig
[inline]
fn (mut c Checker) get_ct_type_var(node ast.Expr) ast.ComptimeVarKind {
return if node is ast.Ident && (node as ast.Ident).obj is ast.Var {
(node.obj as ast.Var).ct_type_var
} else {
.no_comptime
}
}
[inline] [inline]
fn (mut c Checker) get_comptime_var_type(node ast.Expr) ast.Type { fn (mut c Checker) get_comptime_var_type(node ast.Expr) ast.Type {
if node is ast.Ident && (node as ast.Ident).obj is ast.Var { if node is ast.Ident && (node as ast.Ident).obj is ast.Var {

View File

@ -1,3 +1,17 @@
vlib/v/checker/tests/option_fn_err.vv:34:16: error: cannot use `?int` as `int`, it must be unwrapped first in argument 1 to `twice`
32 | foo()
33 | _ := bar(0)
34 | println(twice(bar(0)))
| ~~~~~~
35 |
36 | // anon fn
vlib/v/checker/tests/option_fn_err.vv:37:16: error: cannot use `?int` as `int`, it must be unwrapped first in argument 1 to `anon`
35 |
36 | // anon fn
37 | fn (_ int) {}(bar(0))
| ~~~~~~
38 |
39 | // assert
vlib/v/checker/tests/option_fn_err.vv:40:9: error: assert can be used only with `bool` expressions, but found `bool` instead vlib/v/checker/tests/option_fn_err.vv:40:9: error: assert can be used only with `bool` expressions, but found `bool` instead
38 | 38 |
39 | // assert 39 | // assert
@ -12,6 +26,20 @@ vlib/v/checker/tests/option_fn_err.vv:45:3: error: cannot assign an Option value
| ~~~~~~~~~~~~~ | ~~~~~~~~~~~~~
46 | opt: bar(0) 46 | opt: bar(0)
47 | } 47 | }
vlib/v/checker/tests/option_fn_err.vv:48:8: error: cannot use `?int` as `int`, it must be unwrapped first in argument 1 to `Data.add`
46 | opt: bar(0)
47 | }
48 | v.add(bar(0)) // call method
| ~~~~~~
49 | v.f(bar(0)) // call fn field
50 |
vlib/v/checker/tests/option_fn_err.vv:49:6: error: cannot use `?int` as `int`, it must be unwrapped first in argument 1 to `Data.f`
47 | }
48 | v.add(bar(0)) // call method
49 | v.f(bar(0)) // call fn field
| ~~~~~~
50 |
51 | // array
vlib/v/checker/tests/option_fn_err.vv:56:27: error: cannot use unwrapped Option as initializer vlib/v/checker/tests/option_fn_err.vv:56:27: error: cannot use unwrapped Option as initializer
54 | // init 54 | // init
55 | _ := [bar(0)] 55 | _ := [bar(0)]
@ -26,6 +54,34 @@ vlib/v/checker/tests/option_fn_err.vv:60:13: error: cannot use Option or Result
| ~~~~~~~~ | ~~~~~~~~
61 | // array builtin methods 61 | // array builtin methods
62 | arr.insert(0, bar(0)) 62 | arr.insert(0, bar(0))
vlib/v/checker/tests/option_fn_err.vv:62:16: error: cannot use `?int` as `voidptr`, it must be unwrapped first in argument 2 to `[]int.insert`
60 | println(arr[bar(0)])
61 | // array builtin methods
62 | arr.insert(0, bar(0))
| ~~~~~~
63 | arr.prepend(bar(0))
64 | arr.contains(bar(0))
vlib/v/checker/tests/option_fn_err.vv:63:14: error: cannot use `?int` as `voidptr`, it must be unwrapped first in argument 1 to `[]int.prepend`
61 | // array builtin methods
62 | arr.insert(0, bar(0))
63 | arr.prepend(bar(0))
| ~~~~~~
64 | arr.contains(bar(0))
65 | arr.index(bar(0))
vlib/v/checker/tests/option_fn_err.vv:64:15: error: cannot use `?int` as `int`, it must be unwrapped first in argument 1 to `.contains()`
62 | arr.insert(0, bar(0))
63 | arr.prepend(bar(0))
64 | arr.contains(bar(0))
| ~~~~~~
65 | arr.index(bar(0))
66 | println(arr.map(bar(0)))
vlib/v/checker/tests/option_fn_err.vv:65:12: error: cannot use `?int` as `int`, it must be unwrapped first in argument 1 to `.index()`
63 | arr.prepend(bar(0))
64 | arr.contains(bar(0))
65 | arr.index(bar(0))
| ~~~~~~
66 | println(arr.map(bar(0)))
67 | println(arr.filter(bar(true)))
vlib/v/checker/tests/option_fn_err.vv:67:21: error: type mismatch, `bar` must return a bool vlib/v/checker/tests/option_fn_err.vv:67:21: error: type mismatch, `bar` must return a bool
65 | arr.index(bar(0)) 65 | arr.index(bar(0))
66 | println(arr.map(bar(0))) 66 | println(arr.map(bar(0)))

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/option_ptr_without_unwrapp_err.vv:7:13: error: cannot use `?&Node` as `Node`, it must be unwrapped first in argument 1 to `set_trace`
5 | fn set_trace(n Node) {
6 | if n.parent != none {
7 | set_trace(n.parent)
| ~~~~~~~~
8 | }
9 | }

View File

@ -0,0 +1,17 @@
struct Node {
parent ?&Node
}
fn set_trace(n Node) {
if n.parent != none {
set_trace(n.parent)
}
}
fn main() {
mut initial_node := Node{
parent: none
}
set_trace(initial_node)
assert true
}

View File

@ -549,7 +549,8 @@ fn (mut g Gen) struct_init_field(sfield ast.StructInitField, language ast.Langua
} else { } else {
if sfield.typ != ast.voidptr_type && sfield.typ != ast.nil_type if sfield.typ != ast.voidptr_type && sfield.typ != ast.nil_type
&& (sfield.expected_type.is_ptr() && !sfield.expected_type.has_flag(.shared_f)) && (sfield.expected_type.is_ptr() && !sfield.expected_type.has_flag(.shared_f))
&& !(sfield.typ.is_ptr() || sfield.typ.is_pointer()) && !sfield.typ.is_number() { && !sfield.expected_type.has_flag(.option) && !(sfield.typ.is_ptr()
|| sfield.typ.is_pointer()) && !sfield.typ.is_number() {
g.write('/* autoref */&') g.write('/* autoref */&')
} }

View File

@ -0,0 +1,27 @@
struct Node {
pub mut:
parent ?&Node
id int
}
fn set_trace(n &Node) int {
if n.parent != none {
set_trace(n.parent or { &Node{} })
assert n.id != 0
} else {
assert n.id == 1
}
return n.id
}
fn test_main() {
mut initial_node := &Node{
parent: none
id: 1
}
mut child_node := &Node{
parent: initial_node
id: 2
}
assert set_trace(child_node) == 2
}