From e8df5a78613a97a38bb4b5cb868d02229a596ddc Mon Sep 17 00:00:00 2001 From: Mark aka walkingdevel <104449470+walkingdevel@users.noreply.github.com> Date: Sat, 29 Apr 2023 08:36:21 +0000 Subject: [PATCH] checker: check `db` type implements `orm.Connection` and isn't an `Option`. (#18078) --- vlib/v/checker/orm.v | 39 +++++++++++++++++-- ...ke_operator_with_non_string_type_error.out | 24 ++++++------ ...ike_operator_with_non_string_type_error.vv | 4 +- .../tests/orm_db_expr_option_error.out | 28 +++++++++++++ .../checker/tests/orm_db_expr_option_error.vv | 33 ++++++++++++++++ vlib/v/parser/tests/orm_no_error_handler.out | 12 +++--- vlib/v/parser/tests/orm_no_error_handler.vv | 4 +- 7 files changed, 121 insertions(+), 23 deletions(-) create mode 100644 vlib/v/checker/tests/orm_db_expr_option_error.out create mode 100644 vlib/v/checker/tests/orm_db_expr_option_error.vv diff --git a/vlib/v/checker/orm.v b/vlib/v/checker/orm.v index 301b6fe456..84b6d3c6e9 100644 --- a/vlib/v/checker/orm.v +++ b/vlib/v/checker/orm.v @@ -7,8 +7,9 @@ import v.token import v.util const ( - fkey_attr_name = 'fkey' - v_orm_prefix = 'V ORM' + fkey_attr_name = 'fkey' + v_orm_prefix = 'V ORM' + connection_interface_name = 'orm.Connection' ) type ORMExpr = ast.SqlExpr | ast.SqlStmt @@ -18,8 +19,13 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type { defer { c.inside_sql = false } - sym := c.table.sym(node.table_expr.typ) + + if !c.check_db_expr(node.db_expr) { + return ast.void_type + } c.ensure_type_exists(node.table_expr.typ, node.pos) or { return ast.void_type } + sym := c.table.sym(node.table_expr.typ) + old_ts := c.cur_orm_ts c.cur_orm_ts = *sym defer { @@ -150,6 +156,9 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type { } fn (mut c Checker) sql_stmt(mut node ast.SqlStmt) ast.Type { + if !c.check_db_expr(node.db_expr) { + return ast.void_type + } node.db_expr_type = c.table.unaliased_type(c.expr(node.db_expr)) for mut line in node.lines { @@ -496,3 +505,27 @@ fn (mut c Checker) check_orm_or_expr(expr ORMExpr) { c.expected_or_type = ast.void_type } } + +fn (mut c Checker) check_db_expr(db_expr &ast.Expr) bool { + connection_type_index := c.table.find_type_idx(checker.connection_interface_name) + connection_typ := ast.Type(connection_type_index) + db_expr_type := c.expr(db_expr) + + // If we didn't find `orm.Connection`, we don't have any imported modules + // that depend on `orm` and implement the `orm.Connection` interface. + if connection_type_index == 0 { + c.error('expected a type that implements the `${checker.connection_interface_name}` interface', + db_expr.pos()) + return false + } + + is_implemented := c.type_implements(db_expr_type, connection_typ, db_expr.pos()) + is_option := db_expr_type.has_flag(.option) + + if is_implemented && is_option { + c.error(c.expected_msg(db_expr_type, db_expr_type.clear_flag(.option)), db_expr.pos()) + return false + } + + return true +} diff --git a/vlib/v/checker/tests/like_operator_with_non_string_type_error.out b/vlib/v/checker/tests/like_operator_with_non_string_type_error.out index 70c135a6f1..7561d1b6d5 100644 --- a/vlib/v/checker/tests/like_operator_with_non_string_type_error.out +++ b/vlib/v/checker/tests/like_operator_with_non_string_type_error.out @@ -1,14 +1,14 @@ -vlib/v/checker/tests/like_operator_with_non_string_type_error.vv:10:36: error: the right operand of the `like` operator must be a string type - 8 | - 9 | println(sql db { - 10 | select from User where name like 10 +vlib/v/checker/tests/like_operator_with_non_string_type_error.vv:12:36: error: the right operand of the `like` operator must be a string type + 10 | + 11 | println(sql db { + 12 | select from User where name like 10 | ~~ - 11 | }!) - 12 | -vlib/v/checker/tests/like_operator_with_non_string_type_error.vv:14:26: error: the left operand of the `like` operator must be an identifier with a string type - 12 | - 13 | println(sql db { - 14 | select from User where 10 like true + 13 | }!) + 14 | +vlib/v/checker/tests/like_operator_with_non_string_type_error.vv:16:26: error: the left operand of the `like` operator must be an identifier with a string type + 14 | + 15 | println(sql db { + 16 | select from User where 10 like true | ~~ - 15 | }!) - 16 | } + 17 | }!) + 18 | } diff --git a/vlib/v/checker/tests/like_operator_with_non_string_type_error.vv b/vlib/v/checker/tests/like_operator_with_non_string_type_error.vv index 6c20cf1089..3f3465f64d 100644 --- a/vlib/v/checker/tests/like_operator_with_non_string_type_error.vv +++ b/vlib/v/checker/tests/like_operator_with_non_string_type_error.vv @@ -1,10 +1,12 @@ +import db.sqlite + struct User { id int [primary] name string } fn main() { - db := '' + db := sqlite.connect(':memory:')! println(sql db { select from User where name like 10 diff --git a/vlib/v/checker/tests/orm_db_expr_option_error.out b/vlib/v/checker/tests/orm_db_expr_option_error.out new file mode 100644 index 0000000000..c04311c847 --- /dev/null +++ b/vlib/v/checker/tests/orm_db_expr_option_error.out @@ -0,0 +1,28 @@ +vlib/v/checker/tests/orm_db_expr_option_error.vv:16:11: error: expected `sqlite.DB`, not `?sqlite.DB` + 14 | db_option := connect_option() + 15 | + 16 | _ := sql db_option { + | ~~~~~~~~~ + 17 | select from Account + 18 | }! +vlib/v/checker/tests/orm_db_expr_option_error.vv:22:11: error: `Account` doesn't implement method `select` of interface `orm.Connection` + 20 | account := Account{} + 21 | + 22 | _ := sql account { + | ~~~~~~~ + 23 | select from Account + 24 | }! +vlib/v/checker/tests/orm_db_expr_option_error.vv:26:6: error: `Account` doesn't implement method `select` of interface `orm.Connection` + 24 | }! + 25 | + 26 | sql account { + | ~~~~~~~ + 27 | insert account into Account + 28 | }! +vlib/v/checker/tests/orm_db_expr_option_error.vv:30:6: error: expected `sqlite.DB`, not `?sqlite.DB` + 28 | }! + 29 | + 30 | sql db_option { + | ~~~~~~~~~ + 31 | insert account into Account + 32 | }! diff --git a/vlib/v/checker/tests/orm_db_expr_option_error.vv b/vlib/v/checker/tests/orm_db_expr_option_error.vv new file mode 100644 index 0000000000..0e86c34d02 --- /dev/null +++ b/vlib/v/checker/tests/orm_db_expr_option_error.vv @@ -0,0 +1,33 @@ +module main + +import db.sqlite + +struct Account { + id int [primary] +} + +fn connect_option() ?sqlite.DB { + return sqlite.connect(':memory:') or { return none } +} + +fn main() { + db_option := connect_option() + + _ := sql db_option { + select from Account + }! + + account := Account{} + + _ := sql account { + select from Account + }! + + sql account { + insert account into Account + }! + + sql db_option { + insert account into Account + }! +} diff --git a/vlib/v/parser/tests/orm_no_error_handler.out b/vlib/v/parser/tests/orm_no_error_handler.out index 9e0cb75124..843b868431 100644 --- a/vlib/v/parser/tests/orm_no_error_handler.out +++ b/vlib/v/parser/tests/orm_no_error_handler.out @@ -1,7 +1,7 @@ -vlib/v/parser/tests/orm_no_error_handler.vv:8:7: error: V ORM returns a result, so it should have either an `or {}` block, or `!` at the end - 6 | db := true - 7 | - 8 | _ := sql db { +vlib/v/parser/tests/orm_no_error_handler.vv:10:7: error: V ORM returns a result, so it should have either an `or {}` block, or `!` at the end + 8 | db := sqlite.connect(':memory:')! + 9 | + 10 | _ := sql db { | ~~~~~~~~ - 9 | select from User - 10 | } + 11 | select from User + 12 | } diff --git a/vlib/v/parser/tests/orm_no_error_handler.vv b/vlib/v/parser/tests/orm_no_error_handler.vv index cfaa5fcad4..191d081eea 100644 --- a/vlib/v/parser/tests/orm_no_error_handler.vv +++ b/vlib/v/parser/tests/orm_no_error_handler.vv @@ -1,9 +1,11 @@ +import db.sqlite + struct User { id int } fn main() { - db := true + db := sqlite.connect(':memory:')! _ := sql db { select from User