| 1 | import os |
| 2 | |
| 3 | const vexe = @VEXE |
| 4 | const tests_dir = os.dir(@FILE) |
| 5 | const v3_dir = os.dir(tests_dir) |
| 6 | const vlib_dir = os.dir(v3_dir) |
| 7 | const v3_src = os.join_path(v3_dir, 'v3.v') |
| 8 | |
| 9 | fn build_v3_review_checker() string { |
| 10 | v3_bin := os.join_path(os.temp_dir(), 'v3_review_checker_regressions_test') |
| 11 | build := os.execute('${vexe} -path "${vlib_dir}|@vlib|@vmodules" -o ${v3_bin} ${v3_src}') |
| 12 | assert build.exit_code == 0, build.output |
| 13 | return v3_bin |
| 14 | } |
| 15 | |
| 16 | fn run_bad(v3_bin string, name string, src string, expected string) { |
| 17 | bad_src := os.join_path(os.temp_dir(), 'v3_${name}.v') |
| 18 | os.write_file(bad_src, src) or { panic(err) } |
| 19 | bad_bin := os.join_path(os.temp_dir(), 'v3_${name}') |
| 20 | result := os.execute('${v3_bin} ${bad_src} -b c -o ${bad_bin}') |
| 21 | assert result.exit_code != 0, '${name}: expected failure, got success\n${result.output}' |
| 22 | assert result.output.contains(expected), '${name}: expected `${expected}` in\n${result.output}' |
| 23 | assert !result.output.contains('C compilation failed'), '${name}: reached C compilation\n${result.output}' |
| 24 | } |
| 25 | |
| 26 | fn run_good(v3_bin string, name string, src string) string { |
| 27 | good_src := os.join_path(os.temp_dir(), 'v3_${name}.v') |
| 28 | os.write_file(good_src, src) or { panic(err) } |
| 29 | good_bin := os.join_path(os.temp_dir(), 'v3_${name}') |
| 30 | compile := os.execute('${v3_bin} ${good_src} -b c -o ${good_bin}') |
| 31 | assert compile.exit_code == 0, '${name}: compile failed\n${compile.output}' |
| 32 | assert !compile.output.contains('C compilation failed'), '${name}: C compilation failed\n${compile.output}' |
| 33 | run := os.execute(good_bin) |
| 34 | assert run.exit_code == 0, '${name}: run failed\n${run.output}' |
| 35 | return run.output.trim_space() |
| 36 | } |
| 37 | |
| 38 | fn test_reject_pointer_expressions_for_value_returns() { |
| 39 | v3_bin := build_v3_review_checker() |
| 40 | run_bad(v3_bin, 'bad_return_pointer_to_value', |
| 41 | 'fn f() int {\n\tx := 1\n\treturn &x\n}\nfn main() {}\n', 'cannot return `&int` as `int`') |
| 42 | run_bad(v3_bin, 'bad_result_return_pointer_to_value', |
| 43 | 'fn f() !int {\n\tx := 1\n\treturn &x\n}\nfn main() {}\n', 'cannot return `&int` as `!int`') |
| 44 | } |
| 45 | |
| 46 | fn test_restrict_synthetic_hex_fallback_receivers() { |
| 47 | v3_bin := build_v3_review_checker() |
| 48 | run_bad(v3_bin, 'bad_struct_hex_method', 'struct S {}\nfn main() {\n\t_ := S{}.hex()\n}\n', |
| 49 | 'unknown function') |
| 50 | run_bad(v3_bin, 'bad_int_array_hex_method', 'fn main() {\n\t_ := [1, 2].hex()\n}\n', |
| 51 | 'unknown function') |
| 52 | run_bad(v3_bin, 'bad_map_hex_method', |
| 53 | 'fn main() {\n\tm := map[string]int{}\n\t_ := m.hex()\n}\n', 'unknown function') |
| 54 | run_bad(v3_bin, 'bad_float_hex_method', 'fn main() {\n\t_ := f32(1.5).hex()\n}\n', |
| 55 | 'unknown function') |
| 56 | run_bad(v3_bin, 'bad_pointer_hex_method', |
| 57 | 'fn main() {\n\tx := 1\n\tp := &x\n\t_ := p.hex()\n}\n', 'unknown function') |
| 58 | run_bad(v3_bin, 'bad_numeric_hex_arg', |
| 59 | 'fn side_effect() int {\n\treturn 1\n}\n\nfn main() {\n\t_ := u8(1).hex(side_effect())\n}\n', |
| 60 | 'argument count mismatch') |
| 61 | out := run_good(v3_bin, 'supported_hex_methods', |
| 62 | "fn main() {\n\tprintln(u8(15).hex())\n\tprintln(i64(255).hex())\n\tprintln([u8(1), 15, 255].hex())\n\tprintln(char(65).hex())\n\tprintln(`A`.hex())\n\tprintln('abc'.hex())\n}\n") |
| 63 | assert out == '0f\nff\n010fff\n41\n41\n616263' |
| 64 | } |
| 65 | |
| 66 | fn test_auto_str_rejects_arguments() { |
| 67 | v3_bin := build_v3_review_checker() |
| 68 | run_bad(v3_bin, 'bad_auto_str_arg', |
| 69 | 'struct S {\n\tx int\n}\n\nfn side_effect() int {\n\treturn 1\n}\n\nfn main() {\n\t_ := S{\n\t\tx: 1\n\t}.str(side_effect())\n}\n', |
| 70 | 'argument count mismatch') |
| 71 | } |
| 72 | |
| 73 | fn test_pointer_hex_receiver_methods_are_allowed() { |
| 74 | v3_bin := build_v3_review_checker() |
| 75 | out := run_good(v3_bin, 'good_pointer_hex_receiver_method', |
| 76 | "struct S {\n\tvalue int\n}\n\nfn (s &S) hex() string {\n\treturn 'ptr:' + int_str(s.value)\n}\n\nfn main() {\n\ts := S{\n\t\tvalue: 7\n\t}\n\tp := &s\n\tprintln(p.hex())\n}\n") |
| 77 | assert out == 'ptr:7' |
| 78 | } |
| 79 | |
| 80 | fn test_map_keys_and_values_reject_arguments() { |
| 81 | v3_bin := build_v3_review_checker() |
| 82 | run_bad(v3_bin, 'bad_map_keys_arg', |
| 83 | 'fn main() {\n\tm := map[string]int{}\n\t_ := m.keys(123)\n}\n', 'argument count mismatch') |
| 84 | run_bad(v3_bin, 'bad_map_values_arg', |
| 85 | "fn main() {\n\tm := map[string]int{}\n\t_ := m.values('x')\n}\n", |
| 86 | 'argument count mismatch') |
| 87 | } |
| 88 | |
| 89 | fn test_array_insert_and_prepend_reject_wrong_arity() { |
| 90 | v3_bin := build_v3_review_checker() |
| 91 | run_bad(v3_bin, 'bad_array_prepend_missing_arg', |
| 92 | 'fn main() {\n\tmut a := [1, 2]\n\ta.prepend()\n}\n', 'argument count mismatch') |
| 93 | run_bad(v3_bin, 'bad_array_prepend_extra_arg', |
| 94 | 'fn side_effect() int {\n\treturn 3\n}\nfn main() {\n\tmut a := [1, 2]\n\ta.prepend(0, side_effect())\n}\n', |
| 95 | 'argument count mismatch') |
| 96 | run_bad(v3_bin, 'bad_array_insert_missing_arg', |
| 97 | 'fn main() {\n\tmut a := [1, 2]\n\ta.insert(0)\n}\n', 'argument count mismatch') |
| 98 | run_bad(v3_bin, 'bad_array_insert_extra_arg', |
| 99 | 'fn side_effect() int {\n\treturn 3\n}\nfn main() {\n\tmut a := [1, 2]\n\ta.insert(0, 1, side_effect())\n}\n', |
| 100 | 'argument count mismatch') |
| 101 | run_bad(v3_bin, 'bad_array_prepend_arg_type', |
| 102 | "fn main() {\n\tmut a := [1, 2]\n\ta.prepend('x')\n}\n", 'cannot use') |
| 103 | run_bad(v3_bin, 'bad_array_insert_index_type', |
| 104 | "fn main() {\n\tmut a := [1, 2]\n\ta.insert('0', 3)\n}\n", 'cannot use') |
| 105 | run_bad(v3_bin, 'bad_array_insert_value_type', |
| 106 | "fn main() {\n\tmut a := [1, 2]\n\ta.insert(0, 'x')\n}\n", 'cannot use') |
| 107 | run_bad(v3_bin, 'bad_array_prepend_many_arg_type', |
| 108 | "fn main() {\n\tmut a := [1, 2]\n\ta.prepend(['x'])\n}\n", 'cannot use') |
| 109 | run_bad(v3_bin, 'bad_array_insert_many_arg_type', |
| 110 | "fn main() {\n\tmut a := [1, 2]\n\ta.insert(0, ['x'])\n}\n", 'cannot use') |
| 111 | } |
| 112 | |
| 113 | fn test_array_insert_and_prepend_accept_many_operands() { |
| 114 | v3_bin := build_v3_review_checker() |
| 115 | out := run_good(v3_bin, 'good_array_insert_prepend_many_operands', |
| 116 | "type Strings = []string\n\nfn main() {\n\tmut a := [3, 4]\n\ta.insert(0, [1, 2])\n\tb := [5, 6]\n\ta.insert(1, b)\n\ta.prepend([0])\n\tfixed := [7, 8]!\n\ta.insert(a.len, fixed)\n\tassert a == [0, 1, 5, 6, 2, 3, 4, 7, 8]\n\tmut strs := Strings(['hi'])\n\tstrs.insert(0, ['there'])\n\tstrs.prepend(['hello'])\n\tassert strs == ['hello', 'there', 'hi']\n\tprintln('ok')\n}\n") |
| 117 | assert out == 'ok' |
| 118 | } |
| 119 | |
| 120 | fn test_comptime_if_selected_bodies_are_checked() { |
| 121 | v3_bin := build_v3_review_checker() |
| 122 | run_bad(v3_bin, 'bad_concrete_comptime_if_selected_call', |
| 123 | 'fn main() {\n\t$if int is int {\n\t\tmissing_selected_symbol()\n\t}\n}\n', |
| 124 | 'unknown function `missing_selected_symbol`') |
| 125 | out := run_good(v3_bin, 'good_generic_comptime_if_unselected_branch_is_not_checked', |
| 126 | "fn ok() {}\n\nfn f[T]() {\n\t$if T is int {\n\t\tok()\n\t} $else {\n\t\tonly_for_other_t()\n\t}\n}\n\nfn main() {\n\tf[int]()\n\tprintln('ok')\n}\n") |
| 127 | assert out == 'ok' |
| 128 | } |
| 129 | |
| 130 | fn test_explicit_generic_calls_use_all_type_arguments() { |
| 131 | v3_bin := build_v3_review_checker() |
| 132 | out := run_good(v3_bin, 'good_multi_explicit_generic_call', |
| 133 | "struct Pair[A, B] {\n\tleft A\n\tright B\n}\n\nfn make_pair[A, B]() Pair[A, B] {\n\treturn Pair[A, B]{}\n}\n\nfn expect_pair(p Pair[int, string]) string {\n\treturn 'ok'\n}\n\nfn main() {\n\tp := make_pair[int, string]()\n\tprintln(expect_pair(p))\n}\n") |
| 134 | assert out == 'ok' |
| 135 | nested := run_good(v3_bin, 'good_nested_explicit_generic_call', |
| 136 | "struct Pair[A, B] {\n\tleft A\n\tright B\n}\n\nstruct Box[T] {\n\tvalue T\n}\n\nfn wrap[T]() Box[T] {\n\treturn Box[T]{}\n}\n\nfn expect_box(b Box[Pair[int, string]]) string {\n\treturn 'ok'\n}\n\nfn main() {\n\tb := wrap[Pair[int, string]]()\n\tprintln(expect_box(b))\n}\n") |
| 137 | assert nested == 'ok' |
| 138 | run_bad(v3_bin, 'bad_explicit_generic_too_many_type_args', |
| 139 | 'fn id[T](x T) T {\n\treturn x\n}\n\nfn main() {\n\t_ := id[int, string](1)\n}\n', |
| 140 | 'generic argument count mismatch') |
| 141 | } |
| 142 | |
| 143 | fn test_reject_escaping_capturing_fn_literals() { |
| 144 | v3_bin := build_v3_review_checker() |
| 145 | run_bad(v3_bin, 'bad_return_capturing_fn_literal', |
| 146 | 'fn make(x int) fn () int {\n\treturn fn [x] () int {\n\t\treturn x\n\t}\n}\nfn main() {}\n', |
| 147 | 'capturing fn literal cannot be stored or returned') |
| 148 | run_bad(v3_bin, 'bad_store_capturing_fn_literal_alias', |
| 149 | 'fn main() {\n\tx := 1\n\tf := fn [x] () int {\n\t\treturn x\n\t}\n\tmut cbs := []fn () int{}\n\tcbs << f\n}\n', |
| 150 | 'capturing fn literal cannot be stored or returned') |
| 151 | } |
| 152 | |
| 153 | fn test_generic_functions_report_missing_return() { |
| 154 | v3_bin := build_v3_review_checker() |
| 155 | run_bad(v3_bin, 'bad_generic_missing_return', 'fn f[T]() int {\n}\nfn main() {}\n', |
| 156 | 'missing return at end of function `f`') |
| 157 | } |
| 158 | |
| 159 | fn test_local_identifiers_shadow_module_consts() { |
| 160 | v3_bin := build_v3_review_checker() |
| 161 | out := run_good(v3_bin, 'good_const_shadowed_by_param_and_local', |
| 162 | "const shadowed_value = 'const'\n\nfn param_shadow(shadowed_value int) int {\n\treturn shadowed_value + 1\n}\n\nfn local_shadow() int {\n\tshadowed_value := 2\n\treturn shadowed_value + 1\n}\n\nfn main() {\n\tprintln(int_str(param_shadow(1)))\n\tprintln(int_str(local_shadow()))\n\tprintln(shadowed_value)\n}\n") |
| 163 | assert out == '2\n3\nconst' |
| 164 | } |
| 165 | |