From f4859ffb11c30a6e56129d5621ffbb3f2b099b53 Mon Sep 17 00:00:00 2001 From: shove Date: Tue, 8 Aug 2023 14:06:03 +0800 Subject: [PATCH] checker: fix missing or_block check for left expr of CallExpr(fix #19061) (#19074) --- vlib/net/html/tag_test.v | 14 +++++----- vlib/v/checker/checker.v | 2 ++ vlib/v/checker/fn.v | 3 +++ .../tests/generics_struct_init_err.out | 7 +++++ vlib/v/checker/tests/option_type_call_err.out | 6 ++--- vlib/v/checker/tests/or_block_check_err.out | 27 +++++++++++++++++++ vlib/v/checker/tests/or_block_check_err.vv | 11 ++++++++ vlib/v/checker/tests/result_type_call_err.out | 4 +-- 8 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 vlib/v/checker/tests/or_block_check_err.out create mode 100644 vlib/v/checker/tests/or_block_check_err.vv diff --git a/vlib/net/html/tag_test.v b/vlib/net/html/tag_test.v index 8fe131fc18..082dee986c 100644 --- a/vlib/net/html/tag_test.v +++ b/vlib/net/html/tag_test.v @@ -32,17 +32,17 @@ const ( fn test_search_tag_by_type() { mut dom := parse(html.html) - tag := dom.get_tag('body')[0] - assert tag.get_tag('div') or { assert false }.attributes['id'] == '1st' - assert tag.get_tag_by_attribute('href') or { assert false }.content == 'V' + tag := dom.get_tags(GetTagsOptions{'body'})[0] + assert tag.get_tag('div')?.attributes['id'] == '1st' + assert tag.get_tag_by_attribute('href')?.content == 'V' // TODO: update after improved parsing to not add trailing white space to attribute values - assert tag.get_tag_by_attribute_value('id', '3rd') or { assert false }.str() == '
' - assert tag.get_tag_by_class_name('foo') or { assert false }.attributes['class'] == 'foo bar' + assert tag.get_tag_by_attribute_value('id', '3rd')?.str() == '
' + assert tag.get_tag_by_class_name('foo')?.attributes['class'] == 'foo bar' } fn test_search_tags_by_type() { mut dom := parse(html.html) - tag := dom.get_tag_by_attribute_value('id', '2nd')[0] + tag := dom.get_tags_by_attribute_value('id', '2nd')[0] assert tag.get_tags('div').len == 5 assert tag.get_tags_by_attribute('href')[2].content == 'vpm' assert tag.get_tags_by_attribute_value('class', 'bar').len == 3 @@ -65,7 +65,7 @@ fn generate_temp_html_with_classes() string { fn test_search_by_class() { mut dom := parse(generate_temp_html_with_classes()) - tag := dom.get_tag('body')[0] + tag := dom.get_tags(GetTagsOptions{'body'})[0] single_class_tags := tag.get_tags_by_class_name('single') common_class_tags := tag.get_tags_by_class_name('common') complex_class_tags := tag.get_tags_by_class_name('complex-0', 'complex-1', 'complex-2') diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 5e0f0c0257..e681d1370d 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1146,6 +1146,8 @@ fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type ast.Type) ast.Typ c.check_expr_opt_call(expr.expr, ret_type) } else if expr is ast.AsCast { c.check_expr_opt_call(expr.expr, ret_type) + } else if expr is ast.ParExpr { + c.check_expr_opt_call(expr.expr, ret_type) } return ret_type } diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 957c4252b2..38b0202454 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -490,6 +490,9 @@ fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type { c.error('unknown function: ${node.name}', node.pos) } } + // If the left expr has an or_block, it needs to be checked for legal or_block statement. + return_type := c.expr(mut node.left) + c.check_expr_opt_call(node.left, return_type) // TODO merge logic from method_call and fn_call // First check everything that applies to both fns and methods old_inside_fn_arg := c.inside_fn_arg diff --git a/vlib/v/checker/tests/generics_struct_init_err.out b/vlib/v/checker/tests/generics_struct_init_err.out index f6b7d2048e..7da3d9f920 100644 --- a/vlib/v/checker/tests/generics_struct_init_err.out +++ b/vlib/v/checker/tests/generics_struct_init_err.out @@ -5,6 +5,13 @@ vlib/v/checker/tests/generics_struct_init_err.vv:14:2: notice: uninitialized `fn | ~~~~~~~~~~~~~~~~~ 15 | } 16 | +vlib/v/checker/tests/generics_struct_init_err.vv:58:8: error: cannot initialize builtin type `FnHolder1[neg]` + 56 | ret = holder_call_12(neg, 3) + 57 | assert ret == -3 + 58 | ret = FnHolder1{neg}.call(4) + | ~~~~~~~~~~~~~~ + 59 | assert ret == -4 + 60 | vlib/v/checker/tests/generics_struct_init_err.vv:67:8: error: could not infer generic type `T` in generic struct `FnHolder2[T]` 65 | ret = holder_call_22(neg, 5) 66 | assert ret == -5 diff --git a/vlib/v/checker/tests/option_type_call_err.out b/vlib/v/checker/tests/option_type_call_err.out index 0f249a9538..f789eeeec1 100644 --- a/vlib/v/checker/tests/option_type_call_err.out +++ b/vlib/v/checker/tests/option_type_call_err.out @@ -1,6 +1,6 @@ -vlib/v/checker/tests/option_type_call_err.vv:4:5: error: Result type cannot be called directly - 2 | +vlib/v/checker/tests/option_type_call_err.vv:4:5: error: os.ls() returns a Result, so it should have either an `or {}` block, or `!` at the end + 2 | 3 | fn main() { 4 | os.ls('.').filter(it.ends_with('.v')) or { return } | ~~~~~~~ - 5 | } \ No newline at end of file + 5 | } diff --git a/vlib/v/checker/tests/or_block_check_err.out b/vlib/v/checker/tests/or_block_check_err.out new file mode 100644 index 0000000000..27c763d536 --- /dev/null +++ b/vlib/v/checker/tests/or_block_check_err.out @@ -0,0 +1,27 @@ +vlib/v/checker/tests/or_block_check_err.vv:6:36: error: assignment requires a non empty `or {}` block + 4 | + 5 | fn main() { + 6 | _ = callexpr_with_or_block_call() or {}.replace('a', 'b') + | ~~~~~ + 7 | _ = (callexpr_with_or_block_call() or {}).replace('a', 'b') + 8 | +vlib/v/checker/tests/or_block_check_err.vv:7:37: error: assignment requires a non empty `or {}` block + 5 | fn main() { + 6 | _ = callexpr_with_or_block_call() or {}.replace('a', 'b') + 7 | _ = (callexpr_with_or_block_call() or {}).replace('a', 'b') + | ~~~~~ + 8 | + 9 | _ = callexpr_with_or_block_call() or { eprintln('error') }.replace('a', 'b') +vlib/v/checker/tests/or_block_check_err.vv:9:41: error: `or` block must provide a default value of type `string`, or return/continue/break or call a [noreturn] function like panic(err) or exit(1) + 7 | _ = (callexpr_with_or_block_call() or {}).replace('a', 'b') + 8 | + 9 | _ = callexpr_with_or_block_call() or { eprintln('error') }.replace('a', 'b') + | ~~~~~~~~~~~~~~~~~ + 10 | _ = (callexpr_with_or_block_call() or { eprintln('error') }).replace('a', 'b') + 11 | } +vlib/v/checker/tests/or_block_check_err.vv:10:42: error: `or` block must provide a default value of type `string`, or return/continue/break or call a [noreturn] function like panic(err) or exit(1) + 8 | + 9 | _ = callexpr_with_or_block_call() or { eprintln('error') }.replace('a', 'b') + 10 | _ = (callexpr_with_or_block_call() or { eprintln('error') }).replace('a', 'b') + | ~~~~~~~~~~~~~~~~~ + 11 | } diff --git a/vlib/v/checker/tests/or_block_check_err.vv b/vlib/v/checker/tests/or_block_check_err.vv new file mode 100644 index 0000000000..76e7f02fbd --- /dev/null +++ b/vlib/v/checker/tests/or_block_check_err.vv @@ -0,0 +1,11 @@ +fn callexpr_with_or_block_call() !string { + return error('') +} + +fn main() { + _ = callexpr_with_or_block_call() or {}.replace('a', 'b') + _ = (callexpr_with_or_block_call() or {}).replace('a', 'b') + + _ = callexpr_with_or_block_call() or { eprintln('error') }.replace('a', 'b') + _ = (callexpr_with_or_block_call() or { eprintln('error') }).replace('a', 'b') +} diff --git a/vlib/v/checker/tests/result_type_call_err.out b/vlib/v/checker/tests/result_type_call_err.out index 3b64fa682a..c8b79f74ec 100644 --- a/vlib/v/checker/tests/result_type_call_err.out +++ b/vlib/v/checker/tests/result_type_call_err.out @@ -1,5 +1,5 @@ -vlib/v/checker/tests/result_type_call_err.vv:12:2: error: Result type cannot be called directly - 10 | +vlib/v/checker/tests/result_type_call_err.vv:12:2: error: new_foo() returns a Result, so it should have either an `or {}` block, or `!` at the end + 10 | 11 | fn main() { 12 | new_foo().foo() | ~~~~~~~~~