From 3ef219a7c65b2025588929c6407bd5832aebccfd Mon Sep 17 00:00:00 2001 From: Felix Ehlers Date: Thu, 1 Jan 2026 09:29:30 +0100 Subject: [PATCH] checker: error for unwrapped option/result types used with in operator (fix #26208) (#26223) --- vlib/v/checker/infix.v | 17 ++++---------- .../checker/tests/option_in_operator_err.out | 13 +++++++++++ .../v/checker/tests/option_in_operator_err.vv | 23 +++++++++++++++++++ ...wrapped_option_result_in_operation_err.out | 8 +++---- 4 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 vlib/v/checker/tests/option_in_operator_err.out create mode 100644 vlib/v/checker/tests/option_in_operator_err.vv diff --git a/vlib/v/checker/infix.v b/vlib/v/checker/infix.v index 462f226d9..4a2e33711 100644 --- a/vlib/v/checker/infix.v +++ b/vlib/v/checker/infix.v @@ -255,6 +255,11 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { } } .key_in, .not_in { + if left_type.has_flag(.option) || left_type.has_flag(.result) { + option_or_result := if left_type.has_flag(.option) { 'Option' } else { 'Result' } + c.error('unwrapped ${option_or_result} cannot be used with `${node.op.str()}`', + left_pos) + } match right_final_sym.kind { .array { if left_sym.kind !in [.sum_type, .interface] { @@ -315,18 +320,6 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { } } } - if mut node.left is ast.CallExpr { - if node.left.return_type.has_flag(.option) - || node.left.return_type.has_flag(.result) { - option_or_result := if node.left.return_type.has_flag(.option) { - 'option' - } else { - 'result' - } - c.error('unwrapped ${option_or_result} cannot be used with `${node.op.str()}`', - left_pos) - } - } node.promoted_type = ast.bool_type return ast.bool_type } diff --git a/vlib/v/checker/tests/option_in_operator_err.out b/vlib/v/checker/tests/option_in_operator_err.out new file mode 100644 index 000000000..a69c08722 --- /dev/null +++ b/vlib/v/checker/tests/option_in_operator_err.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/option_in_operator_err.vv:19:11: error: unwrapped Option cannot be used with `in` + 17 | + 18 | // Optional struct field with map + 19 | _ = item.owner_id in m + | ~~~~~~~~ + 20 | + 21 | // Optional function return with array +vlib/v/checker/tests/option_in_operator_err.vv:22:6: error: unwrapped Option cannot be used with `in` + 20 | + 21 | // Optional function return with array + 22 | _ = get_opt() in arr + | ~~~~~~~~~ + 23 | } diff --git a/vlib/v/checker/tests/option_in_operator_err.vv b/vlib/v/checker/tests/option_in_operator_err.vv new file mode 100644 index 000000000..752f72960 --- /dev/null +++ b/vlib/v/checker/tests/option_in_operator_err.vv @@ -0,0 +1,23 @@ +struct Item { + owner_id ?string +} + +fn get_opt() ?int { + return 1 +} + +fn main() { + item := Item{ + owner_id: 'key' + } + arr := [1, 2, 3] + m := { + 'key': 1 + } + + // Optional struct field with map + _ = item.owner_id in m + + // Optional function return with array + _ = get_opt() in arr +} diff --git a/vlib/v/checker/tests/unwrapped_option_result_in_operation_err.out b/vlib/v/checker/tests/unwrapped_option_result_in_operation_err.out index 6b2e8d7a5..0708c848e 100644 --- a/vlib/v/checker/tests/unwrapped_option_result_in_operation_err.out +++ b/vlib/v/checker/tests/unwrapped_option_result_in_operation_err.out @@ -1,25 +1,25 @@ -vlib/v/checker/tests/unwrapped_option_result_in_operation_err.vv:3:6: error: unwrapped result cannot be used with `!in` +vlib/v/checker/tests/unwrapped_option_result_in_operation_err.vv:3:6: error: unwrapped Result cannot be used with `!in` 1 | fn main() { 2 | list := ['string'] 3 | _ = return_string_or_error() !in list | ~~~~~~~~~~~~~~~~~~~~~~~~ 4 | _ = return_string_or_error() in list 5 | _ = return_string_or_none() in list -vlib/v/checker/tests/unwrapped_option_result_in_operation_err.vv:4:6: error: unwrapped result cannot be used with `in` +vlib/v/checker/tests/unwrapped_option_result_in_operation_err.vv:4:6: error: unwrapped Result cannot be used with `in` 2 | list := ['string'] 3 | _ = return_string_or_error() !in list 4 | _ = return_string_or_error() in list | ~~~~~~~~~~~~~~~~~~~~~~~~ 5 | _ = return_string_or_none() in list 6 | _ = return_string_or_error() !in list -vlib/v/checker/tests/unwrapped_option_result_in_operation_err.vv:5:6: error: unwrapped option cannot be used with `in` +vlib/v/checker/tests/unwrapped_option_result_in_operation_err.vv:5:6: error: unwrapped Option cannot be used with `in` 3 | _ = return_string_or_error() !in list 4 | _ = return_string_or_error() in list 5 | _ = return_string_or_none() in list | ~~~~~~~~~~~~~~~~~~~~~~~ 6 | _ = return_string_or_error() !in list 7 | } -vlib/v/checker/tests/unwrapped_option_result_in_operation_err.vv:6:6: error: unwrapped result cannot be used with `!in` +vlib/v/checker/tests/unwrapped_option_result_in_operation_err.vv:6:6: error: unwrapped Result cannot be used with `!in` 4 | _ = return_string_or_error() in list 5 | _ = return_string_or_none() in list 6 | _ = return_string_or_error() !in list -- 2.39.5