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)']") return error("references attribute needs to be in the format [references], [references: 'tablename'], or [references: 'tablename(field_id)']")
} }
if attr.arg.contains('(') { if attr.arg.contains('(') {
ref_table, ref_field := attr.arg.split_once('(') if ref_table, ref_field := attr.arg.split_once('(') {
if !ref_field.ends_with(')') { if !ref_field.ends_with(')') {
return error("explicit references attribute should be written as [references: 'tablename(field_id)']") 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 { } else {
references_table = attr.arg references_table = attr.arg
references_field = 'id' 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_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_sym := c.table.sym(expr_ret_type)
ret_typ_tok := if expr_ret_type.has_flag(.option) { '?' } else { '!' } if expr_ret_type.has_flag(.result)
if c.inside_defer { || (expr_ret_type.has_flag(.option) && ret_sym.kind == .multi_return) {
c.error('${expr.name}() returns `${ret_typ_tok}${ret_sym.name}`, so it should have an `or {}` block at the end', ret_typ_tok := if expr_ret_type.has_flag(.option) { '?' } else { '!' }
expr.pos) if c.inside_defer {
} else { c.error('${expr.name}() returns `${ret_typ_tok}${ret_sym.name}`, so it should have an `or {}` block at the end',
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)
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) c.check_or_expr(expr.or_block, ret_type, expr_ret_type, expr)
} }
return ret_type.clear_flag(.result) 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 {} struct MmapRangeLocal {}
fn addr2range() ?(&MmapRangeLocal, ?u64, u64) { fn addr2range(none_ bool) ?(&MmapRangeLocal, ?u64, u64) {
return none if none_ {
return none
} else {
return &MmapRangeLocal{}, none, 1
}
} }
fn foo(val ?int) (?int, ?int) { fn foo(val ?int) (?int, ?int) {
@ -23,39 +27,48 @@ fn tuple2() ?(string, int) {
} }
fn tuple3() ?(?int, ?int) { fn tuple3() ?(?int, ?int) {
return none, none
}
fn tuple4() ?(?int, ?int) {
return none return none
} }
fn tuple4(none_ bool) ?(?int, ?int) {
if none_ {
return none, none
} else {
return 1, none
}
}
fn test_tuple_1() { fn test_tuple_1() {
a, b := tuple() a, b := tuple()?
assert a == 1 assert a == 1
assert b == 2 assert b == 2
} }
fn test_tuple_2() { fn test_tuple_2() {
a, b := tuple2() a, b := tuple2()?
assert a == '' assert a == ''
assert b == 2 assert b == 2
} }
fn test_tuple_3() { fn test_tuple_3() {
a, b := tuple3() a, b := tuple3() or { return }
assert a == none assert false
assert b == none
} }
fn test_tuple_4() { fn test_tuple_4() {
a, b := tuple4() if _, _ := tuple4(true) {
assert a == none assert false
}
a, b := tuple4(false)?
assert a? == 1
assert b == none assert b == none
} }
fn test_none_ret() { fn test_none_ret() {
_, b, c := addr2range() if _, _, _ := addr2range(true) {
assert false
}
_, b, c := addr2range(false)?
assert b == none 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() { fn test_return_option_call() {
dump(wrapper('issue')) dump(wrapper('issue')?)
dump(wrapper('foobar')) dump(wrapper('foobar')?)
dump(wrapper('')) if a, b := wrapper('') {
assert true assert false, '${a}, ${b}'
}
} }