From 81a61f5df57b71121f6a2a99c4d32fb6da1b4cb2 Mon Sep 17 00:00:00 2001 From: Tim Marston Date: Fri, 22 Sep 2023 21:34:36 +0100 Subject: [PATCH] checker: ORM use of none/options and operations (added tests) --- vlib/v/checker/orm.v | 15 +++-- vlib/v/checker/tests/orm_fkey_attribute.out | 7 +++ vlib/v/checker/tests/orm_fkey_attribute.vv | 19 +++++++ vlib/v/checker/tests/orm_multidim_array.out | 7 +++ vlib/v/checker/tests/orm_multidim_array.vv | 20 +++++++ .../tests/orm_op_with_option_and_none.out | 49 +++++++++++++++++ .../tests/orm_op_with_option_and_none.vv | 55 +++++++++++++++++++ vlib/v/checker/tests/orm_table_attributes.out | 7 +++ vlib/v/checker/tests/orm_table_attributes.vv | 13 +++++ 9 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 vlib/v/checker/tests/orm_fkey_attribute.out create mode 100644 vlib/v/checker/tests/orm_fkey_attribute.vv create mode 100644 vlib/v/checker/tests/orm_multidim_array.out create mode 100644 vlib/v/checker/tests/orm_multidim_array.vv create mode 100644 vlib/v/checker/tests/orm_op_with_option_and_none.out create mode 100644 vlib/v/checker/tests/orm_op_with_option_and_none.vv create mode 100644 vlib/v/checker/tests/orm_table_attributes.out create mode 100644 vlib/v/checker/tests/orm_table_attributes.vv diff --git a/vlib/v/checker/orm.v b/vlib/v/checker/orm.v index 1e01520c397ac8..d7f8a72827379b 100644 --- a/vlib/v/checker/orm.v +++ b/vlib/v/checker/orm.v @@ -339,7 +339,7 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type { fn (mut c Checker) check_orm_struct_field_attrs(node ast.SqlStmtLine, field ast.StructField) { for attr in field.attrs { if attr.name == 'nonull' { - c.warn('[nonull] attributes are deprecated (all regular fields are now "not null"), use option fields for columns which can be null', + c.warn('`nonull` attribute is deprecated; non-optional fields are always "NOT NULL", use Option fields where they can be NULL', node.pos) } } @@ -483,7 +483,7 @@ fn (mut c Checker) check_sql_expr_type_is_int(expr &ast.Expr, sql_keyword string } fn (mut c Checker) orm_error(message string, pos token.Pos) { - c.error('orm: ${message}', pos) + c.error('ORM: ${message}', pos) } // check_expr_has_no_fn_calls_with_non_orm_return_type checks that an expression has no function calls @@ -517,6 +517,13 @@ fn (mut c Checker) check_expr_has_no_fn_calls_with_non_orm_return_type(expr &ast } else if expr is ast.InfixExpr { c.check_expr_has_no_fn_calls_with_non_orm_return_type(expr.left) c.check_expr_has_no_fn_calls_with_non_orm_return_type(expr.right) + if expr.right_type.has_flag(.option) && expr.op !in [.key_is, .not_is] { + c.warn('comparison with Option value probably isn\'t intended; use "is none" and "!is none" to select by NULL', + expr.pos) + } else if expr.right_type == ast.none_type && expr.op !in [.key_is, .not_is] { + c.warn('comparison with none probably isn\'t intended; use "is none" and "!is none" to select by NULL', + expr.pos) + } } } @@ -590,10 +597,10 @@ fn (mut c Checker) check_orm_or_expr(mut expr ORMExpr) { if expr.or_expr.kind == .absent { if c.inside_defer { - c.error('V ORM returns a result, so it should have an `or {}` block at the end', + c.error('ORM returns a result, so it should have an `or {}` block at the end', expr.pos) } else { - c.error('V ORM returns a result, so it should have either an `or {}` block, or `!` at the end', + c.error('ORM returns a result, so it should have either an `or {}` block, or `!` at the end', expr.pos) } } else { diff --git a/vlib/v/checker/tests/orm_fkey_attribute.out b/vlib/v/checker/tests/orm_fkey_attribute.out new file mode 100644 index 00000000000000..53501cedc2d28d --- /dev/null +++ b/vlib/v/checker/tests/orm_fkey_attribute.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/orm_fkey_attribute.vv:11:15: error: ORM: the `fkey` attribute must have an argument + 9 | id int [primary; sql: serial] + 10 | name string + 11 | user User [fkey] + | ~~~~~~ + 12 | } + 13 | diff --git a/vlib/v/checker/tests/orm_fkey_attribute.vv b/vlib/v/checker/tests/orm_fkey_attribute.vv new file mode 100644 index 00000000000000..b4ff10436ed98d --- /dev/null +++ b/vlib/v/checker/tests/orm_fkey_attribute.vv @@ -0,0 +1,19 @@ +import db.sqlite + +struct User { + id int [primary; sql: serial] +} + +[table: foo] +struct Foo { + id int [primary; sql: serial] + name string + user User [fkey] +} + +fn main() { + db := sqlite.connect(':memory:')! + sql db { + create table Foo + }! +} diff --git a/vlib/v/checker/tests/orm_multidim_array.out b/vlib/v/checker/tests/orm_multidim_array.out new file mode 100644 index 00000000000000..52bfd47ab4020d --- /dev/null +++ b/vlib/v/checker/tests/orm_multidim_array.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/orm_multidim_array.vv:12:5: error: ORM: multi-dimension array fields are not supported + 10 | id int [primary; sql: serial] + 11 | users []User [fkey: id] + 12 | bad [][]User [fkey: id] + | ~~~~~~~~~~~~ + 13 | } + 14 | diff --git a/vlib/v/checker/tests/orm_multidim_array.vv b/vlib/v/checker/tests/orm_multidim_array.vv new file mode 100644 index 00000000000000..2c475ee9ac92df --- /dev/null +++ b/vlib/v/checker/tests/orm_multidim_array.vv @@ -0,0 +1,20 @@ +import db.sqlite + +[table: users] +struct User { + id int [pimary] +} + +[table:foos] +struct Foo { + id int [primary; sql: serial] + users []User [fkey: id] + bad [][]User [fkey: id] +} + +fn main() { + db := sqlite.connect(':memory:')! + sql db { + create table Foo + }! +} diff --git a/vlib/v/checker/tests/orm_op_with_option_and_none.out b/vlib/v/checker/tests/orm_op_with_option_and_none.out new file mode 100644 index 00000000000000..01ee85cbe75fb2 --- /dev/null +++ b/vlib/v/checker/tests/orm_op_with_option_and_none.out @@ -0,0 +1,49 @@ +vlib/v/checker/tests/orm_op_with_option_and_none.vv:11:30: warning: comparison with Option value probably isn't intended; use "is none" and "!is none" to select by NULL + 9 | db := sqlite.connect(':memory:')! + 10 | _ := sql db { + 11 | select from Foo where name == val + | ~~ + 12 | }! + 13 | } +vlib/v/checker/tests/orm_op_with_option_and_none.vv:25:30: warning: comparison with none probably isn't intended; use "is none" and "!is none" to select by NULL + 23 | + 24 | _ := sql db { + 25 | select from Foo where name == none + | ~~ + 26 | }! + 27 | +vlib/v/checker/tests/orm_op_with_option_and_none.vv:29:30: warning: comparison with none probably isn't intended; use "is none" and "!is none" to select by NULL + 27 | + 28 | _ := sql db { + 29 | select from Foo where name != none + | ~~ + 30 | }! + 31 | +vlib/v/checker/tests/orm_op_with_option_and_none.vv:33:30: warning: comparison with none probably isn't intended; use "is none" and "!is none" to select by NULL + 31 | + 32 | _ := sql db { + 33 | select from Foo where name < none + | ^ + 34 | }! + 35 | +vlib/v/checker/tests/orm_op_with_option_and_none.vv:37:30: warning: comparison with none probably isn't intended; use "is none" and "!is none" to select by NULL + 35 | + 36 | _ := sql db { + 37 | select from Foo where name > none + | ^ + 38 | }! + 39 | +vlib/v/checker/tests/orm_op_with_option_and_none.vv:41:30: warning: comparison with none probably isn't intended; use "is none" and "!is none" to select by NULL + 39 | + 40 | _ := sql db { + 41 | select from Foo where name <= none + | ~~ + 42 | }! + 43 | +vlib/v/checker/tests/orm_op_with_option_and_none.vv:45:30: warning: comparison with none probably isn't intended; use "is none" and "!is none" to select by NULL + 43 | + 44 | _ := sql db { + 45 | select from Foo where name >= none + | ~~ + 46 | }! + 47 | diff --git a/vlib/v/checker/tests/orm_op_with_option_and_none.vv b/vlib/v/checker/tests/orm_op_with_option_and_none.vv new file mode 100644 index 00000000000000..5c6b4dca84ea64 --- /dev/null +++ b/vlib/v/checker/tests/orm_op_with_option_and_none.vv @@ -0,0 +1,55 @@ +import db.sqlite + +struct Foo { + id u64 [primary; sql: serial] + name ?string +} + +fn bar(val ?string) ! { + db := sqlite.connect(':memory:')! + _ := sql db { + select from Foo where name == val + }! +} + +fn main() { + db := sqlite.connect(':memory:')! + + sql db { + create table Foo + }! + + bar(none) or {} + + _ := sql db { + select from Foo where name == none + }! + + _ := sql db { + select from Foo where name != none + }! + + _ := sql db { + select from Foo where name < none + }! + + _ := sql db { + select from Foo where name > none + }! + + _ := sql db { + select from Foo where name <= none + }! + + _ := sql db { + select from Foo where name >= none + }! + + _ := sql db { + select from Foo where name is none + }! + + _ := sql db { + select from Foo where name !is none + }! +} diff --git a/vlib/v/checker/tests/orm_table_attributes.out b/vlib/v/checker/tests/orm_table_attributes.out new file mode 100644 index 00000000000000..6cb017048ffc68 --- /dev/null +++ b/vlib/v/checker/tests/orm_table_attributes.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/orm_table_attributes.vv:3:1: error: ORM: table attribute must have an argument + 1 | import db.sqlite + 2 | + 3 | [table] + | ~~~~~~~ + 4 | struct Foo { + 5 | id int [primary; sql: serial] diff --git a/vlib/v/checker/tests/orm_table_attributes.vv b/vlib/v/checker/tests/orm_table_attributes.vv new file mode 100644 index 00000000000000..a836578450e0ed --- /dev/null +++ b/vlib/v/checker/tests/orm_table_attributes.vv @@ -0,0 +1,13 @@ +import db.sqlite + +[table] +struct Foo { + id int [primary; sql: serial] +} + +fn main() { + db := sqlite.connect(':memory:')! + sql db { + create table Foo + }! +}