checker, orm: add error for unchecked option multi return types, fix undefined behavior (#21106)

This commit is contained in:
Turiiya 2024-03-28 06:31:32 +01:00 committed by GitHub
parent f172a040ef
commit b98dca585c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 66 additions and 33 deletions

View File

@ -508,12 +508,13 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int,
return error("references attribute needs to be in the format [references], [references: 'tablename'], or [references: 'tablename(field_id)']")
}
if attr.arg.contains('(') {
ref_table, ref_field := attr.arg.split_once('(')
if !ref_field.ends_with(')') {
return error("explicit references attribute should be written as [references: 'tablename(field_id)']")
if ref_table, ref_field := attr.arg.split_once('(') {
if !ref_field.ends_with(')') {
return error("explicit references attribute should be written as [references: 'tablename(field_id)']")
}
references_table = ref_table
references_field = ref_field[..ref_field.len - 1]
}
references_table = ref_table
references_field = ref_field[..ref_field.len - 1]
} else {
references_table = attr.arg
references_field = 'id'

View File

@ -1161,17 +1161,20 @@ fn (mut c Checker) check_expr_option_or_result_call(expr ast.Expr, ret_type ast.
}
}
if expr_ret_type.has_option_or_result() {
if expr.or_block.kind == .absent && expr_ret_type.has_flag(.result) {
if expr.or_block.kind == .absent {
ret_sym := c.table.sym(expr_ret_type)
ret_typ_tok := if expr_ret_type.has_flag(.option) { '?' } else { '!' }
if c.inside_defer {
c.error('${expr.name}() returns `${ret_typ_tok}${ret_sym.name}`, so it should have an `or {}` block at the end',
expr.pos)
} else {
c.error('${expr.name}() returns `${ret_typ_tok}${ret_sym.name}`, so it should have either an `or {}` block, or `${ret_typ_tok}` at the end',
expr.pos)
if expr_ret_type.has_flag(.result)
|| (expr_ret_type.has_flag(.option) && ret_sym.kind == .multi_return) {
ret_typ_tok := if expr_ret_type.has_flag(.option) { '?' } else { '!' }
if c.inside_defer {
c.error('${expr.name}() returns `${ret_typ_tok}${ret_sym.name}`, so it should have an `or {}` block at the end',
expr.pos)
} else {
c.error('${expr.name}() returns `${ret_typ_tok}${ret_sym.name}`, so it should have either an `or {}` block, or `${ret_typ_tok}` at the end',
expr.pos)
}
}
} else if expr.or_block.kind != .absent {
} else {
c.check_or_expr(expr.or_block, ret_type, expr_ret_type, expr)
}
return ret_type.clear_flag(.result)

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/option_multi_return_err.vv:6:14: error: split() returns `?(string, string)`, so it should have either an `or {}` block, or `?` at the end
4 |
5 | fn main() {
6 | foo, bar := split('foo/bar')
| ~~~~~~~~~~~~~~~~
7 | println('${foo}.${bar}')
8 | }

View File

@ -0,0 +1,8 @@
fn split(str string) ?(string, string) {
return none
}
fn main() {
foo, bar := split('foo/bar')
println('${foo}.${bar}')
}

View File

@ -1,7 +1,11 @@
struct MmapRangeLocal {}
fn addr2range() ?(&MmapRangeLocal, ?u64, u64) {
return none
fn addr2range(none_ bool) ?(&MmapRangeLocal, ?u64, u64) {
if none_ {
return none
} else {
return &MmapRangeLocal{}, none, 1
}
}
fn foo(val ?int) (?int, ?int) {
@ -23,39 +27,48 @@ fn tuple2() ?(string, int) {
}
fn tuple3() ?(?int, ?int) {
return none, none
}
fn tuple4() ?(?int, ?int) {
return none
}
fn tuple4(none_ bool) ?(?int, ?int) {
if none_ {
return none, none
} else {
return 1, none
}
}
fn test_tuple_1() {
a, b := tuple()
a, b := tuple()?
assert a == 1
assert b == 2
}
fn test_tuple_2() {
a, b := tuple2()
a, b := tuple2()?
assert a == ''
assert b == 2
}
fn test_tuple_3() {
a, b := tuple3()
assert a == none
assert b == none
a, b := tuple3() or { return }
assert false
}
fn test_tuple_4() {
a, b := tuple4()
assert a == none
if _, _ := tuple4(true) {
assert false
}
a, b := tuple4(false)?
assert a? == 1
assert b == none
}
fn test_none_ret() {
_, b, c := addr2range()
if _, _, _ := addr2range(true) {
assert false
}
_, b, c := addr2range(false)?
assert b == none
assert c == 0
assert c == 1
}

View File

@ -10,8 +10,9 @@ fn wrapper(data string) ?(int, string) {
}
fn test_return_option_call() {
dump(wrapper('issue'))
dump(wrapper('foobar'))
dump(wrapper(''))
assert true
dump(wrapper('issue')?)
dump(wrapper('foobar')?)
if a, b := wrapper('') {
assert false, '${a}, ${b}'
}
}