From a88dc82d935061dd90b93334d59e2cb39899bfae Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 11 Mar 2026 14:03:47 +0300 Subject: [PATCH] checker(js): allows you to pass a numeric value to JS.Number without a cast (fixes #24677) --- vlib/v/checker/checker.v | 7 +++++++ .../checker/tests/js_number_requires_explicit_cast.js.out | 6 ++++++ vlib/v/checker/tests/js_number_requires_explicit_cast.vv | 8 ++++++++ vlib/v/compiler_errors_test.v | 5 ++++- 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 vlib/v/checker/tests/js_number_requires_explicit_cast.js.out create mode 100644 vlib/v/checker/tests/js_number_requires_explicit_cast.vv diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 6cd4f8c88..7b3c894ef 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1505,6 +1505,13 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to if typ_sym.name == 'JS.Any' { return true } + if inter_sym.kind == .interface && c.table.get_attrs(inter_sym).any(it.name == 'single_impl') { + if pos.file_idx != -1 { + c.error('cannot use `${styp}` as `${inter_sym.name}` without an explicit cast (e.g. `${inter_sym.name}(value)`)', + pos) + } + return false + } if typ_sym.kind == .function && inter_sym.name != 'JS.Any' { c.error('cannot implement interface `${inter_sym.name}` using function', pos) return false diff --git a/vlib/v/checker/tests/js_number_requires_explicit_cast.js.out b/vlib/v/checker/tests/js_number_requires_explicit_cast.js.out new file mode 100644 index 000000000..f4d5b56e1 --- /dev/null +++ b/vlib/v/checker/tests/js_number_requires_explicit_cast.js.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/js_number_requires_explicit_cast.vv:7:4: error: cannot use `int` as `JS.Number` without an explicit cast (e.g. `JS.Number(value)`) + 5 | fn main() { + 6 | d := 1 + 7 | x(d) + | ^ + 8 | } diff --git a/vlib/v/checker/tests/js_number_requires_explicit_cast.vv b/vlib/v/checker/tests/js_number_requires_explicit_cast.vv new file mode 100644 index 000000000..2341695e7 --- /dev/null +++ b/vlib/v/checker/tests/js_number_requires_explicit_cast.vv @@ -0,0 +1,8 @@ +fn x(a JS.Number) { + println(a) +} + +fn main() { + d := 1 + x(d) +} diff --git a/vlib/v/compiler_errors_test.v b/vlib/v/compiler_errors_test.v index 8016e1ddb..643322b48 100644 --- a/vlib/v/compiler_errors_test.v +++ b/vlib/v/compiler_errors_test.v @@ -95,8 +95,10 @@ fn test_all() { run_dir := '${checker_dir}/run' su_dir := 'vlib/v/tests/skip_unused' no_closures_dir := 'vlib/v/tests/no_closures' + js_checker_tests := ['js_number_requires_explicit_cast.vv'] - checker_tests := get_tests_in_dir(checker_dir, false).filter(!it.contains('with_check_option')) + checker_tests := get_tests_in_dir(checker_dir, false).filter(!it.contains('with_check_option') + && it !in js_checker_tests) parser_tests := get_tests_in_dir(parser_dir, false) scanner_tests := get_tests_in_dir(scanner_dir, false) global_tests := get_tests_in_dir(global_dir, false) @@ -171,6 +173,7 @@ fn test_all() { } tasks.add('', parser_dir, '', '.out', parser_tests, false) tasks.add('', checker_dir, '', '.out', checker_tests, false) + tasks.add('', checker_dir, '-b js', '.js.out', js_checker_tests, false) tasks.add('', scanner_dir, '', '.out', scanner_tests, false) tasks.add('', checker_dir, '-enable-globals run', '.run.out', ['globals_error.vv'], false) -- 2.39.5