| 1 | module markused |
| 2 | |
| 3 | import os |
| 4 | import v3.flat |
| 5 | import v3.gen.c as cgen |
| 6 | import v3.parser |
| 7 | import v3.pref |
| 8 | import v3.transform |
| 9 | import v3.types |
| 10 | |
| 11 | const custom_str_tests_dir = os.dir(@FILE) |
| 12 | const custom_str_v3_dir = os.dir(custom_str_tests_dir) |
| 13 | const custom_str_v3_src = os.join_path(custom_str_v3_dir, 'v3.v') |
| 14 | const custom_str_vexe = @VEXE |
| 15 | |
| 16 | // parse_checked_two_file_source reads parse checked two file source input for v3 tests. |
| 17 | fn parse_checked_two_file_source(name string, main_source string, module_rel string, module_source string) (&flat.FlatAst, &types.TypeChecker) { |
| 18 | root := os.join_path(os.temp_dir(), 'v3_markused_${name}_project') |
| 19 | if os.exists(root) { |
| 20 | os.rmdir_all(root) or { panic(err) } |
| 21 | } |
| 22 | os.mkdir_all(root) or { panic(err) } |
| 23 | main_src := os.join_path(root, 'main.v') |
| 24 | module_src := os.join_path(root, module_rel) |
| 25 | os.mkdir_all(os.dir(module_src)) or { panic(err) } |
| 26 | os.write_file(main_src, main_source) or { panic(err) } |
| 27 | os.write_file(module_src, module_source) or { panic(err) } |
| 28 | prefs := pref.new_preferences() |
| 29 | mut p := parser.Parser.new(prefs) |
| 30 | mut a := p.parse_files([main_src, module_src]) |
| 31 | mut tc := types.TypeChecker.new(a) |
| 32 | tc.collect(a) |
| 33 | tc.diagnose_unknown_calls = true |
| 34 | tc.diagnostic_files[main_src] = true |
| 35 | tc.diagnostic_files[module_src] = true |
| 36 | tc.check_semantics() |
| 37 | assert tc.errors.len == 0, tc.errors.str() |
| 38 | return a, &tc |
| 39 | } |
| 40 | |
| 41 | // build_v3_bin builds v3 bin data for v3 tests. |
| 42 | fn build_v3_bin(name string) string { |
| 43 | v3_bin := os.join_path(os.temp_dir(), 'v3_markused_${name}') |
| 44 | build := os.execute('${custom_str_vexe} -o ${v3_bin} ${custom_str_v3_src}') |
| 45 | assert build.exit_code == 0, build.output |
| 46 | return v3_bin |
| 47 | } |
| 48 | |
| 49 | // imported_enum_main_source supports imported enum main source handling for v3 tests. |
| 50 | fn imported_enum_main_source(expr string) string { |
| 51 | return ' |
| 52 | module main |
| 53 | |
| 54 | import colors |
| 55 | |
| 56 | fn main() { |
| 57 | c := colors.Color.red |
| 58 | ${expr} |
| 59 | } |
| 60 | ' |
| 61 | } |
| 62 | |
| 63 | // imported_optional_enum_main_source |
| 64 | // supports helper handling in v3 tests. |
| 65 | fn imported_optional_enum_main_source(expr string) string { |
| 66 | return ' |
| 67 | module main |
| 68 | |
| 69 | import colors |
| 70 | |
| 71 | fn maybe_color() ?colors.Color { |
| 72 | return colors.Color.red |
| 73 | } |
| 74 | |
| 75 | fn main() { |
| 76 | ${expr} |
| 77 | } |
| 78 | ' |
| 79 | } |
| 80 | |
| 81 | // imported_enum_module_source supports imported enum module source handling for v3 tests. |
| 82 | fn imported_enum_module_source() string { |
| 83 | return ' |
| 84 | module colors |
| 85 | |
| 86 | pub enum Color { |
| 87 | red |
| 88 | blue |
| 89 | } |
| 90 | |
| 91 | pub fn (c Color) str() string { |
| 92 | return "red" |
| 93 | } |
| 94 | ' |
| 95 | } |
| 96 | |
| 97 | // imported_operator_main_source supports imported operator main source handling for v3 tests. |
| 98 | fn imported_operator_main_source(expr string) string { |
| 99 | return ' |
| 100 | module main |
| 101 | |
| 102 | import vectors |
| 103 | |
| 104 | fn main() { |
| 105 | a := vectors.new_vec(1) |
| 106 | b := vectors.new_vec(2) |
| 107 | ${expr} |
| 108 | } |
| 109 | ' |
| 110 | } |
| 111 | |
| 112 | // imported_operator_module_source supports imported operator module source handling for v3 tests. |
| 113 | fn imported_operator_module_source() string { |
| 114 | return ' |
| 115 | module vectors |
| 116 | |
| 117 | pub struct Vec { |
| 118 | x int |
| 119 | } |
| 120 | |
| 121 | pub fn new_vec(x int) Vec { |
| 122 | return Vec{ |
| 123 | x: x |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | pub fn (a Vec) + (b Vec) Vec { |
| 128 | return Vec{ |
| 129 | x: a.x + b.x |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | pub fn (a Vec) < (b Vec) bool { |
| 134 | return a.x < b.x |
| 135 | } |
| 136 | ' |
| 137 | } |
| 138 | |
| 139 | // imported_operator_usage_source supports imported operator usage source handling for v3 tests. |
| 140 | fn imported_operator_usage_source() string { |
| 141 | return 'sum := a + b |
| 142 | gt := b > a |
| 143 | if gt { |
| 144 | _ := sum |
| 145 | }' |
| 146 | } |
| 147 | |
| 148 | // imported_struct_default_main_source supports imported_struct_default_main_source handling. |
| 149 | fn imported_struct_default_main_source(expr string) string { |
| 150 | return ' |
| 151 | module main |
| 152 | |
| 153 | import defaults |
| 154 | |
| 155 | fn main() { |
| 156 | ${expr} |
| 157 | } |
| 158 | ' |
| 159 | } |
| 160 | |
| 161 | // imported_struct_default_module_source supports imported_struct_default_module_source handling. |
| 162 | fn imported_struct_default_module_source() string { |
| 163 | return ' |
| 164 | module defaults |
| 165 | |
| 166 | pub struct Box { |
| 167 | value int = default_value() |
| 168 | } |
| 169 | |
| 170 | pub fn default_value() int { |
| 171 | return 7 |
| 172 | } |
| 173 | |
| 174 | pub fn maybe_box() ?Box { |
| 175 | return none |
| 176 | } |
| 177 | ' |
| 178 | } |
| 179 | |
| 180 | // test_string_interpolation_seeds_imported_enum_str_method validates this v3 regression case. |
| 181 | fn test_string_interpolation_seeds_imported_enum_str_method() { |
| 182 | a, tc := parse_checked_two_file_source('imported_enum_interp_str', |
| 183 | imported_enum_main_source('_ := "\${c}"'), 'colors/colors.v', imported_enum_module_source()) |
| 184 | mut used := mark_used(a, tc) |
| 185 | assert used['colors.Color.str'] |
| 186 | } |
| 187 | |
| 188 | // test_optional_string_interpolation_seeds_imported_enum_str_method |
| 189 | // validates this v3 regression case. |
| 190 | fn test_optional_string_interpolation_seeds_imported_enum_str_method() { |
| 191 | a, tc := parse_checked_two_file_source('imported_optional_enum_interp_str', |
| 192 | imported_optional_enum_main_source('_ := "\${maybe_color()}"'), 'colors/colors.v', |
| 193 | imported_enum_module_source()) |
| 194 | mut used := mark_used(a, tc) |
| 195 | assert used['colors.Color.str'] |
| 196 | } |
| 197 | |
| 198 | // test_imported_operator_infix_seeds_operator_methods validates this v3 regression case. |
| 199 | fn test_imported_operator_infix_seeds_operator_methods() { |
| 200 | a, tc := parse_checked_two_file_source('imported_operator_infix', |
| 201 | imported_operator_main_source(imported_operator_usage_source()), 'vectors/vectors.v', |
| 202 | imported_operator_module_source()) |
| 203 | mut used := mark_used(a, tc) |
| 204 | assert used['vectors.Vec.+'] |
| 205 | assert used['vectors.Vec.<'] |
| 206 | } |
| 207 | |
| 208 | // test_flag_enum_seeds_string_plus_helper validates this v3 regression case: a `[flag]` |
| 209 | // enum's synthesized `<Enum>__autostr` builds its string with `string__plus`, but that |
| 210 | // generated body is invisible to markused, so the helper must be seeded from the flag-enum |
| 211 | // declaration or it could be pruned and leave the autostr calling an undefined helper. |
| 212 | fn test_flag_enum_seeds_string_plus_helper() { |
| 213 | main_src := ' |
| 214 | module main |
| 215 | |
| 216 | import colors |
| 217 | |
| 218 | @[flag] |
| 219 | enum Perm { |
| 220 | read |
| 221 | write |
| 222 | } |
| 223 | |
| 224 | fn main() { |
| 225 | p := Perm.read | Perm.write |
| 226 | _ := p |
| 227 | _ := colors.Color.red |
| 228 | } |
| 229 | ' |
| 230 | a, tc := parse_checked_two_file_source('flag_enum_string_plus', main_src, 'colors/colors.v', |
| 231 | imported_enum_module_source()) |
| 232 | mut used := mark_used(a, tc) |
| 233 | assert used['string__plus'] |
| 234 | } |
| 235 | |
| 236 | // test_optional_struct_zero_seeds_imported_default_helper validates this v3 regression case. |
| 237 | fn test_optional_struct_zero_seeds_imported_default_helper() { |
| 238 | a, tc := parse_checked_two_file_source('imported_struct_default_or', |
| 239 | imported_struct_default_main_source('box := defaults.maybe_box() or { return }\n\t_ := box'), |
| 240 | 'defaults/defaults.v', imported_struct_default_module_source()) |
| 241 | mut used := mark_used(a, tc) |
| 242 | assert used['defaults.default_value'] |
| 243 | } |
| 244 | |
| 245 | // test_string_interpolation_lowers_to_imported_enum_str_after_used_filter_transform |
| 246 | // validates this v3 regression case. |
| 247 | fn test_string_interpolation_lowers_to_imported_enum_str_after_used_filter_transform() { |
| 248 | mut a, mut tc := parse_checked_two_file_source('imported_enum_interp_str_cgen', |
| 249 | imported_enum_main_source('_ := "\${c}"'), 'colors/colors.v', imported_enum_module_source()) |
| 250 | mut used := mark_used(a, tc) |
| 251 | assert used['colors.Color.str'] |
| 252 | used = transform.transform_with_used(mut a, tc, used) |
| 253 | tc.diagnose_unknown_calls = false |
| 254 | tc.reject_unlowered_map_mutation = true |
| 255 | tc.annotate_types() |
| 256 | mut g := cgen.FlatGen.new() |
| 257 | c_code := g.gen_with_used_options(a, used, tc, true) |
| 258 | assert c_code.contains('colors__Color__str(') |
| 259 | } |
| 260 | |
| 261 | // test_imported_operator_infix_lowers_after_used_filter_transform |
| 262 | // validates this v3 regression case. |
| 263 | fn test_imported_operator_infix_lowers_after_used_filter_transform() { |
| 264 | mut a, mut tc := parse_checked_two_file_source('imported_operator_infix_cgen', |
| 265 | imported_operator_main_source(imported_operator_usage_source()), 'vectors/vectors.v', |
| 266 | imported_operator_module_source()) |
| 267 | mut used := mark_used(a, tc) |
| 268 | assert used['vectors.Vec.+'] |
| 269 | assert used['vectors.Vec.<'] |
| 270 | used = transform.transform_with_used(mut a, tc, used) |
| 271 | tc.diagnose_unknown_calls = false |
| 272 | tc.reject_unlowered_map_mutation = true |
| 273 | tc.annotate_types() |
| 274 | mut g := cgen.FlatGen.new() |
| 275 | c_code := g.gen_with_used_options(a, used, tc, true) |
| 276 | assert c_code.contains('vectors__Vec__plus(') |
| 277 | assert c_code.contains('vectors__Vec__lt(') |
| 278 | } |
| 279 | |
| 280 | // test_optional_struct_zero_lowers_to_imported_default_after_used_filter_transform |
| 281 | // validates this v3 regression case. |
| 282 | fn test_optional_struct_zero_lowers_to_imported_default_after_used_filter_transform() { |
| 283 | mut a, mut tc := parse_checked_two_file_source('imported_struct_default_or_cgen', |
| 284 | imported_struct_default_main_source('box := defaults.maybe_box() or { return }\n\t_ := box'), |
| 285 | 'defaults/defaults.v', imported_struct_default_module_source()) |
| 286 | mut used := mark_used(a, tc) |
| 287 | assert used['defaults.default_value'] |
| 288 | used = transform.transform_with_used(mut a, tc, used) |
| 289 | tc.diagnose_unknown_calls = false |
| 290 | tc.reject_unlowered_map_mutation = true |
| 291 | tc.annotate_types() |
| 292 | mut g := cgen.FlatGen.new() |
| 293 | c_code := g.gen_with_used_options(a, used, tc, true) |
| 294 | assert c_code.contains('defaults__default_value(') |
| 295 | } |
| 296 | |
| 297 | // test_optional_string_interpolation_lowers_to_imported_enum_str_after_used_filter_transform |
| 298 | // validates this v3 regression case. |
| 299 | fn test_optional_string_interpolation_lowers_to_imported_enum_str_after_used_filter_transform() { |
| 300 | mut a, mut tc := parse_checked_two_file_source('imported_optional_enum_interp_str_cgen', |
| 301 | imported_optional_enum_main_source('_ := "\${maybe_color()}"'), 'colors/colors.v', |
| 302 | imported_enum_module_source()) |
| 303 | mut used := mark_used(a, tc) |
| 304 | assert used['colors.Color.str'] |
| 305 | used = transform.transform_with_used(mut a, tc, used) |
| 306 | tc.diagnose_unknown_calls = false |
| 307 | tc.reject_unlowered_map_mutation = true |
| 308 | tc.annotate_types() |
| 309 | mut g := cgen.FlatGen.new() |
| 310 | c_code := g.gen_with_used_options(a, used, tc, true) |
| 311 | assert c_code.contains('colors__Color__str(') |
| 312 | } |
| 313 | |
| 314 | // test_imported_enum_print_compile_keeps_str_method validates this v3 regression case. |
| 315 | fn test_imported_enum_print_compile_keeps_str_method() { |
| 316 | v3_bin := build_v3_bin('imported_enum_print_str_test') |
| 317 | |
| 318 | root := os.join_path(os.temp_dir(), 'v3_markused_imported_enum_print_input') |
| 319 | if os.exists(root) { |
| 320 | os.rmdir_all(root) or { panic(err) } |
| 321 | } |
| 322 | os.mkdir_all(os.join_path(root, 'colors')) or { panic(err) } |
| 323 | os.write_file(os.join_path(root, 'main.v'), imported_enum_main_source('println(c)')) or { |
| 324 | panic(err) |
| 325 | } |
| 326 | os.write_file(os.join_path(root, 'colors/colors.v'), imported_enum_module_source()) or { |
| 327 | panic(err) |
| 328 | } |
| 329 | bin := os.join_path(os.temp_dir(), 'v3_markused_imported_enum_print_input_bin') |
| 330 | compile := os.execute('${v3_bin} -o ${bin} ${root}') |
| 331 | assert compile.exit_code == 0, compile.output |
| 332 | } |
| 333 | |
| 334 | // test_imported_operator_compile_keeps_operator_methods validates this v3 regression case. |
| 335 | fn test_imported_operator_compile_keeps_operator_methods() { |
| 336 | v3_bin := build_v3_bin('imported_operator_test') |
| 337 | |
| 338 | root := os.join_path(os.temp_dir(), 'v3_markused_imported_operator_input') |
| 339 | if os.exists(root) { |
| 340 | os.rmdir_all(root) or { panic(err) } |
| 341 | } |
| 342 | os.mkdir_all(os.join_path(root, 'vectors')) or { panic(err) } |
| 343 | os.write_file(os.join_path(root, 'main.v'), |
| 344 | imported_operator_main_source(imported_operator_usage_source())) or { panic(err) } |
| 345 | os.write_file(os.join_path(root, 'vectors/vectors.v'), imported_operator_module_source()) or { |
| 346 | panic(err) |
| 347 | } |
| 348 | bin := os.join_path(os.temp_dir(), 'v3_markused_imported_operator_input_bin') |
| 349 | compile := os.execute('${v3_bin} -o ${bin} ${root}') |
| 350 | assert compile.exit_code == 0, compile.output |
| 351 | } |
| 352 | |
| 353 | // test_optional_struct_zero_compile_keeps_imported_default_helper |
| 354 | // validates this v3 regression case. |
| 355 | fn test_optional_struct_zero_compile_keeps_imported_default_helper() { |
| 356 | v3_bin := build_v3_bin('imported_struct_default_or_test') |
| 357 | |
| 358 | root := os.join_path(os.temp_dir(), 'v3_markused_imported_struct_default_or_input') |
| 359 | if os.exists(root) { |
| 360 | os.rmdir_all(root) or { panic(err) } |
| 361 | } |
| 362 | os.mkdir_all(os.join_path(root, 'defaults')) or { panic(err) } |
| 363 | os.write_file(os.join_path(root, 'main.v'), |
| 364 | imported_struct_default_main_source('box := defaults.maybe_box() or { return }\n\t_ := box')) or { |
| 365 | panic(err) |
| 366 | } |
| 367 | os.write_file(os.join_path(root, 'defaults/defaults.v'), |
| 368 | imported_struct_default_module_source()) or { panic(err) } |
| 369 | bin := os.join_path(os.temp_dir(), 'v3_markused_imported_struct_default_or_input_bin') |
| 370 | compile := os.execute('${v3_bin} -o ${bin} ${root}') |
| 371 | assert compile.exit_code == 0, compile.output |
| 372 | } |
| 373 | |
| 374 | // test_imported_optional_enum_interpolation_compile_keeps_str_method |
| 375 | // validates this v3 regression case. |
| 376 | fn test_imported_optional_enum_interpolation_compile_keeps_str_method() { |
| 377 | v3_bin := build_v3_bin('imported_optional_enum_interp_str_test') |
| 378 | |
| 379 | root := os.join_path(os.temp_dir(), 'v3_markused_imported_optional_enum_interp_input') |
| 380 | if os.exists(root) { |
| 381 | os.rmdir_all(root) or { panic(err) } |
| 382 | } |
| 383 | os.mkdir_all(os.join_path(root, 'colors')) or { panic(err) } |
| 384 | os.write_file(os.join_path(root, 'main.v'), |
| 385 | imported_optional_enum_main_source('_ := "\${maybe_color()}"')) or { panic(err) } |
| 386 | os.write_file(os.join_path(root, 'colors/colors.v'), imported_enum_module_source()) or { |
| 387 | panic(err) |
| 388 | } |
| 389 | bin := os.join_path(os.temp_dir(), 'v3_markused_imported_optional_enum_interp_input_bin') |
| 390 | compile := os.execute('${v3_bin} -o ${bin} ${root}') |
| 391 | assert compile.exit_code == 0, compile.output |
| 392 | } |
| 393 | |