vq / vlib / v3 / tests / markused_test.v
1056 lines · 918 sloc · 26.56 KB · 2b81918e62db9ab38787518603a7f93bee80868c
Raw
1import os
2import v3.flat
3import v3.gen.c as cgen
4import v3.markused
5import v3.parser
6import v3.pref
7import v3.transform
8import v3.types
9
10const vexe = @VEXE
11const tests_dir = os.dir(@FILE)
12const v3_dir = os.dir(tests_dir)
13const vlib_dir = os.dir(v3_dir)
14const v3_src = os.join_path(v3_dir, 'v3.v')
15
16// parse_checked_source reads parse checked source input for v3 tests.
17fn parse_checked_source(name string, source string) (&flat.FlatAst, &types.TypeChecker) {
18 src := os.join_path(os.temp_dir(), 'v3_markused_${name}.v')
19 os.write_file(src, source) or { panic(err) }
20 prefs := pref.new_preferences()
21 mut p := parser.Parser.new(prefs)
22 mut a := p.parse_file(src)
23 mut tc := types.TypeChecker.new(a)
24 tc.collect(a)
25 tc.diagnose_unknown_calls = true
26 tc.diagnostic_files[src] = true
27 tc.check_semantics()
28 assert tc.errors.len == 0, tc.errors.str()
29 return a, &tc
30}
31
32fn parse_checked_project(name string, files map[string]string, main_file string) (&flat.FlatAst, &types.TypeChecker) {
33 root := os.join_path(os.temp_dir(), 'v3_markused_${name}')
34 os.rmdir_all(root) or {}
35 for rel, source in files {
36 path := os.join_path(root, rel)
37 os.mkdir_all(os.dir(path)) or { panic(err) }
38 os.write_file(path, source) or { panic(err) }
39 }
40 mut paths := []string{}
41 if main_file.len > 0 {
42 paths << os.join_path(root, main_file)
43 }
44 mut rel_paths := []string{}
45 for rel, _ in files {
46 if rel != main_file {
47 rel_paths << rel
48 }
49 }
50 rel_paths.sort()
51 for rel in rel_paths {
52 paths << os.join_path(root, rel)
53 }
54 prefs := pref.new_preferences()
55 mut p := parser.Parser.new(prefs)
56 mut a := p.parse_files(paths)
57 mut tc := types.TypeChecker.new(a)
58 tc.collect(a)
59 tc.diagnose_unknown_calls = true
60 for path in paths {
61 tc.diagnostic_files[path] = true
62 }
63 tc.check_semantics()
64 assert tc.errors.len == 0, tc.errors.str()
65 return a, &tc
66}
67
68fn parse_checked_project_in_order(name string, rels []string, sources []string) (&flat.FlatAst, &types.TypeChecker) {
69 root := os.join_path(os.temp_dir(), 'v3_markused_${name}')
70 os.rmdir_all(root) or {}
71 mut paths := []string{}
72 for i, rel in rels {
73 path := os.join_path(root, rel)
74 os.mkdir_all(os.dir(path)) or { panic(err) }
75 os.write_file(path, sources[i]) or { panic(err) }
76 paths << path
77 }
78 prefs := pref.new_preferences()
79 mut p := parser.Parser.new(prefs)
80 mut a := p.parse_files(paths)
81 mut tc := types.TypeChecker.new(a)
82 tc.collect(a)
83 tc.diagnose_unknown_calls = true
84 for path in paths {
85 tc.diagnostic_files[path] = true
86 }
87 tc.check_semantics()
88 assert tc.errors.len == 0, tc.errors.str()
89 return a, &tc
90}
91
92// mark_used_source updates mark used source state for v3 tests.
93fn mark_used_source(name string, source string) map[string]bool {
94 a, tc := parse_checked_source(name, source)
95 return markused.mark_used(a, tc)
96}
97
98// build_v3_bin builds v3 bin data for v3 tests.
99fn build_v3_bin(name string) string {
100 v3_bin := os.join_path(os.temp_dir(), 'v3_markused_${name}')
101 build :=
102 os.execute('${vexe} -gc none -path "${vlib_dir}|@vlib|@vmodules" -o ${v3_bin} ${v3_src}')
103 assert build.exit_code == 0, build.output
104 return v3_bin
105}
106
107// test_map_literals_seed_new_map_runtime_helper validates this v3 regression case.
108fn test_map_literals_seed_new_map_runtime_helper() {
109 used := mark_used_source('map_literal_new_map', '
110fn make_map() map[string]int {
111 return map[string]int{}
112}
113
114fn main() {
115 _ := make_map()
116}
117')
118 assert used['new_map']
119}
120
121// test_optional_map_or_seeds_new_map_runtime_helper validates this v3 regression case.
122fn test_optional_map_or_seeds_new_map_runtime_helper() {
123 used := mark_used_source('option_map_or_new_map', '
124fn maybe_map() ?map[string]int {
125 return none
126}
127
128fn main() {
129 m := maybe_map() or { return }
130 _ := m
131}
132')
133 assert used['new_map']
134}
135
136// test_string_membership_seeds_contains_runtime_helpers validates this v3 regression case.
137fn test_string_membership_seeds_contains_runtime_helpers() {
138 used := mark_used_source('string_membership_contains', '
139fn has_needle() bool {
140 return "bc" in "abcd"
141}
142
143fn main() {
144 _ := has_needle()
145}
146')
147 assert used['string__contains']
148 assert used['string__contains_u8']
149}
150
151// test_string_interpolation_seeds_string_plus_and_formatter_helpers
152// validates this v3 regression case.
153fn test_string_interpolation_seeds_string_plus_and_formatter_helpers() {
154 used := mark_used_source('string_interp_plus_formatter', '
155fn message(name string) string {
156 return "hello \${name} \${true}"
157}
158
159fn main() {
160 _ := message("v")
161}
162')
163 assert used['string__plus']
164 assert used['bool.str']
165}
166
167// test_print_bool_seeds_formatter_runtime_helper validates this v3 regression case.
168fn test_print_bool_seeds_formatter_runtime_helper() {
169 used := mark_used_source('print_bool_formatter', '
170fn println(s string) {}
171
172fn main() {
173 println(true)
174}
175')
176 assert used['bool.str']
177}
178
179// test_string_compound_assign_seeds_string_plus_runtime_helper validates this v3 regression case.
180fn test_string_compound_assign_seeds_string_plus_runtime_helper() {
181 used := mark_used_source('string_plus_assign', '
182fn main() {
183 mut s := "a"
184 s += "b"
185 _ := s
186}
187')
188 assert used['string__plus']
189}
190
191fn test_moduleless_export_after_module_file_is_rooted() {
192 a, tc := parse_checked_project_in_order('moduleless_export_after_module', [
193 'helper/helper.v',
194 'lonely.v',
195 ], ['module helper
196
197pub fn helper_marker() int {
198 return 1
199}
200', "@[export: 'raw_lonely']
201fn lonely() int {
202 return 7
203}
204"])
205 used := markused.mark_used(a, tc)
206 assert used['lonely']
207}
208
209fn test_receiver_method_call_in_selector_assign_rhs_is_used() {
210 used := mark_used_source('receiver_method_selector_assign_rhs', '
211struct Builder {
212mut:
213 name string
214}
215
216fn (b &Builder) main_module_name() string {
217 return "main"
218}
219
220fn build_with_options() string {
221 mut b := Builder{}
222 b.name = b.main_module_name()
223 return b.name
224}
225
226fn main() {
227 _ := build_with_options()
228}
229')
230 assert used['Builder.main_module_name']
231}
232
233fn test_unreachable_interface_implementer_method_is_not_rooted() {
234 used := mark_used_source('unreachable_interface_implementer', '
235interface Reader {
236 read() int
237}
238
239struct File {}
240
241fn (f File) read() int {
242 return 1
243}
244
245fn main() {}
246')
247 assert !used['File.read']
248}
249
250fn test_reachable_interface_dispatch_keeps_implementer_method() {
251 used := mark_used_source('reachable_interface_dispatch', '
252interface Reader {
253 read() int
254}
255
256struct File {}
257
258fn (f File) read() int {
259 return 1
260}
261
262fn call_reader(r Reader) int {
263 return r.read()
264}
265
266fn main() {
267 _ := call_reader(File{})
268}
269')
270 assert used['File.read']
271}
272
273fn test_global_interface_dispatch_keeps_implementer_method() {
274 used := mark_used_source('global_interface_dispatch', '
275interface Reader {
276 read() int
277}
278
279struct File {}
280
281__global default_reader &Reader
282
283fn (f File) read() int {
284 return 1
285}
286
287fn call_default_reader() int {
288 return default_reader.read()
289}
290
291fn main() {
292 _ := call_default_reader()
293}
294')
295 assert used['Reader.read']
296 assert used['File.read']
297}
298
299fn test_unreachable_interface_dispatch_stub_is_not_emitted_after_used_filter_transform() {
300 mut a, mut tc := parse_checked_source('unreachable_interface_dispatch_cgen', '
301interface Reader {
302 read() int
303}
304
305struct File {}
306
307fn (f File) read() int {
308 return 1
309}
310
311fn main() {}
312')
313 mut used := markused.mark_used(a, tc)
314 assert !used['Reader.read']
315 assert !used['File.read']
316 used = transform.transform_with_used(mut a, tc, used)
317 tc.diagnose_unknown_calls = false
318 tc.reject_unlowered_map_mutation = true
319 tc.annotate_types()
320 mut g := cgen.FlatGen.new()
321 c_code := g.gen_with_used_options(a, used, tc, true)
322 assert !c_code.contains('Reader__read(')
323}
324
325fn test_short_interface_dispatch_does_not_emit_imported_name_collision_stub() {
326 mut a, mut tc := parse_checked_project('interface_dispatch_name_collision', {
327 'main.v': 'module main
328
329import moda
330
331interface Reader {
332 read() int
333}
334
335struct Local {}
336
337fn (l Local) read() int {
338 return 1
339}
340
341fn call_reader(r Reader) int {
342 return r.read()
343}
344
345fn main() {
346 _ := call_reader(Local{})
347}
348'
349 'moda/moda.v': 'module moda
350
351interface Reader {
352 read(path string) string
353}
354
355struct Remote {}
356
357fn (r Remote) read(path string) string {
358 return path
359}
360'
361 }, 'main.v')
362 mut used := markused.mark_used(a, tc)
363 assert used['Reader.read']
364 assert !used['moda.Reader.read']
365 assert !used['moda.Remote.read']
366 used = transform.transform_with_used(mut a, tc, used)
367 tc.diagnose_unknown_calls = false
368 tc.reject_unlowered_map_mutation = true
369 tc.annotate_types()
370 mut g := cgen.FlatGen.new()
371 c_code := g.gen_with_used_options(a, used, tc, true)
372 assert c_code.contains('Reader__read(')
373 assert !c_code.contains('moda__Reader__read(')
374}
375
376fn test_local_receiver_method_does_not_root_imported_interface_by_short_name() {
377 mut a, mut tc := parse_checked_project('local_receiver_imported_interface_short_name', {
378 'main.v': 'module main
379
380import moda
381
382struct Reader {}
383
384fn (r Reader) read() int {
385 return 1
386}
387
388fn main() {
389 _ := Reader{}.read()
390}
391'
392 'moda/moda.v': 'module moda
393
394pub interface Reader {
395 read() int
396}
397
398pub struct Remote {}
399
400pub fn (r Remote) read() int {
401 return 2
402}
403'
404 }, 'main.v')
405 mut used := markused.mark_used(a, tc)
406 assert used['Reader.read']
407 assert !used['moda.Reader.read']
408 assert !used['moda.Remote.read']
409 used = transform.transform_with_used(mut a, tc, used)
410 tc.diagnose_unknown_calls = false
411 tc.reject_unlowered_map_mutation = true
412 tc.annotate_types()
413 mut g := cgen.FlatGen.new()
414 c_code := g.gen_with_used_options(a, used, tc, true)
415 assert c_code.contains('Reader__read(')
416 assert !c_code.contains('moda__Reader__read(')
417 assert !c_code.contains('moda__Remote__read(')
418}
419
420fn test_imported_interface_dispatch_is_emitted_when_exactly_used() {
421 mut a, mut tc := parse_checked_project('imported_interface_dispatch_used', {
422 'main.v': 'module main
423
424import moda
425
426fn call_reader(r moda.Reader) string {
427 return r.read("ok")
428}
429
430fn main() {
431 _ := call_reader(moda.Remote{})
432}
433'
434 'moda/moda.v': 'module moda
435
436pub interface Reader {
437 read(path string) string
438}
439
440pub struct Remote {}
441
442pub fn (r Remote) read(path string) string {
443 return path
444}
445'
446 }, 'main.v')
447 mut used := markused.mark_used(a, tc)
448 assert used['moda.Reader.read']
449 assert used['moda.Remote.read']
450 used = transform.transform_with_used(mut a, tc, used)
451 tc.diagnose_unknown_calls = false
452 tc.reject_unlowered_map_mutation = true
453 tc.annotate_types()
454 mut g := cgen.FlatGen.new()
455 c_code := g.gen_with_used_options(a, used, tc, true)
456 assert c_code.contains('moda__Reader__read(')
457 assert c_code.contains('moda__Remote__read(')
458}
459
460fn test_interface_dispatch_target_does_not_use_bare_method_key_for_imported_homonym() {
461 mut a, mut tc := parse_checked_project('interface_dispatch_target_homonym', {
462 'main.v': 'module main
463
464import moda
465
466interface Reader {
467 read() int
468}
469
470struct Local {}
471
472fn (l Local) read() int {
473 return 1
474}
475
476fn read() int {
477 return 9
478}
479
480fn call_reader(r Reader) int {
481 return r.read()
482}
483
484fn main() {
485 _ := read()
486 _ := call_reader(Local{})
487}
488'
489 'moda/moda.v': 'module moda
490
491pub struct Remote {}
492
493pub fn (r Remote) read() int {
494 return 2
495}
496'
497 }, 'main.v')
498 mut used := markused.mark_used(a, tc)
499 assert used['read']
500 assert used['Reader.read']
501 used.delete('moda.Remote.read')
502 used.delete('Remote.read')
503 used.delete('moda__Remote__read')
504 used = transform.transform_with_used(mut a, tc, used)
505 tc.diagnose_unknown_calls = false
506 tc.reject_unlowered_map_mutation = true
507 tc.annotate_types()
508 mut g := cgen.FlatGen.new()
509 c_code := g.gen_with_used_options(a, used, tc, true)
510 assert c_code.contains('Reader__read(')
511 assert !c_code.contains('moda__Remote__read(')
512}
513
514fn test_unused_main_method_with_interface_dispatch_is_pruned_with_stub() {
515 mut a, mut tc := parse_checked_source('unused_main_method_interface_dispatch_cgen', '
516interface Reader {
517 read() int
518}
519
520struct File {}
521
522fn (f File) read() int {
523 return 1
524}
525
526struct X {}
527
528fn (x X) unused(r Reader) int {
529 return r.read()
530}
531
532fn main() {}
533')
534 mut used := markused.mark_used(a, tc)
535 assert !used['X.unused']
536 assert !used['Reader.read']
537 used = transform.transform_with_used(mut a, tc, used)
538 tc.diagnose_unknown_calls = false
539 tc.reject_unlowered_map_mutation = true
540 tc.annotate_types()
541 mut g := cgen.FlatGen.new()
542 c_code := g.gen_with_used_options(a, used, tc, true)
543 assert !c_code.contains('X__unused(')
544 assert !c_code.contains('Reader__read(')
545}
546
547fn test_unused_main_helper_with_method_call_is_pruned_with_method() {
548 mut a, mut tc := parse_checked_source('unused_main_helper_method_call_cgen',
549 'module main\n\nstruct X {}\n\nfn (x X) m() int {\n\treturn 1\n}\n\nfn helper() int {\n\treturn X{}.m()\n}\n\nfn main() {}\n')
550 mut used := markused.mark_used(a, tc)
551 assert !used['helper']
552 assert !used['X.m']
553 used = transform.transform_with_used(mut a, tc, used)
554 tc.diagnose_unknown_calls = false
555 tc.reject_unlowered_map_mutation = true
556 tc.annotate_types()
557 mut g := cgen.FlatGen.new()
558 c_code := g.gen_with_used_options(a, used, tc, true)
559 assert !c_code.contains('helper(')
560 assert !c_code.contains('X__m(')
561}
562
563fn test_reachable_main_fn_literal_is_emitted_after_used_filter_transform() {
564 mut a, mut tc := parse_checked_source('reachable_main_fn_literal_cgen',
565 'module main\n\nfn callback_value(cb fn () int) int {\n\treturn cb()\n}\n\nfn main() {\n\t_ := callback_value(fn () int {\n\t\treturn 7\n\t})\n}\n')
566 mut used := markused.mark_used(a, tc)
567 assert used['callback_value']
568 used = transform.transform_with_used(mut a, tc, used)
569 tc.diagnose_unknown_calls = false
570 tc.reject_unlowered_map_mutation = true
571 tc.annotate_types()
572 mut g := cgen.FlatGen.new()
573 c_code := g.gen_with_used_options(a, used, tc, true)
574 assert c_code.contains('int __anon_fn_')
575 assert c_code.contains('callback_value(__anon_fn_')
576}
577
578fn test_top_level_fn_value_roots_helper() {
579 mut a, mut tc := parse_checked_source('top_level_fn_value_helper_cgen',
580 'module main\n\nfn helper() int {\n\treturn 41\n}\n\nf := helper\nprintln(int_str(f() + 1))\n')
581 mut used := markused.mark_used(a, tc)
582 assert used['helper']
583 used = transform.transform_with_used(mut a, tc, used)
584 tc.diagnose_unknown_calls = false
585 tc.reject_unlowered_map_mutation = true
586 tc.annotate_types()
587 mut g := cgen.FlatGen.new()
588 c_code := g.gen_with_used_options(a, used, tc, true)
589 assert c_code.contains('helper(')
590}
591
592fn test_top_level_fn_value_compile_keeps_helper() {
593 v3_bin := build_v3_bin('top_level_fn_value_test')
594
595 src := os.join_path(os.temp_dir(), 'v3_markused_top_level_fn_value_input.v')
596 bin := os.join_path(os.temp_dir(), 'v3_markused_top_level_fn_value_input')
597 os.write_file(src, '
598fn helper() int {
599 return 41
600}
601
602f := helper
603println(int_str(f() + 1))
604') or {
605 panic(err)
606 }
607 compile := os.execute('${v3_bin} -o ${bin} ${src}')
608 assert compile.exit_code == 0, compile.output
609 run := os.execute(bin)
610 assert run.exit_code == 0, run.output
611 assert run.output.trim_space() == '42'
612 c_code := os.read_file(bin + '.c') or { panic(err) }
613 assert c_code.contains('helper('), c_code
614}
615
616fn test_top_level_fn_value_respects_prior_local_shadow() {
617 used := mark_used_source('top_level_fn_value_prior_shadow', '
618fn helper() int {
619 return 1
620}
621
622helper := 10
623f := helper
624println(int_str(f))
625')
626 assert !used['helper']
627}
628
629fn test_local_fn_value_keeps_helper_before_future_local_shadow() {
630 used := mark_used_source('local_fn_value_future_shadow', '
631fn cb() int {
632 return 1
633}
634
635fn takes(f fn () int) int {
636 return f()
637}
638
639fn main() {
640 takes(cb)
641 cb := 0
642 _ = cb
643}
644')
645 assert used['cb']
646}
647
648fn test_local_ident_reference_does_not_root_dead_function() {
649 mut a, mut tc := parse_checked_source('local_ident_shadow_dead_fn_cgen', '
650fn C.v3_dead_local_shadow_should_not_link() int
651
652fn unused() int {
653 return C.v3_dead_local_shadow_should_not_link()
654}
655
656fn echo(unused int) int {
657 println(unused)
658 return unused
659}
660
661fn main() {
662 unused := 1
663 println(unused)
664 _ := echo(unused)
665}
666')
667 mut used := markused.mark_used(a, tc)
668 assert !used['unused']
669 used = transform.transform_with_used(mut a, tc, used)
670 tc.diagnose_unknown_calls = false
671 tc.reject_unlowered_map_mutation = true
672 tc.annotate_types()
673 mut g := cgen.FlatGen.new()
674 c_code := g.gen_with_used_options(a, used, tc, true)
675 assert !c_code.contains('unused('), c_code
676 assert !c_code.contains('v3_dead_local_shadow_should_not_link'), c_code
677}
678
679fn test_local_fn_value_call_does_not_root_shadowed_dead_function() {
680 mut a, mut tc := parse_checked_source('local_fn_value_shadow_dead_fn_cgen', '
681fn C.v3_dead_local_fn_value_shadow_should_not_link() int
682
683fn unused() int {
684 return C.v3_dead_local_fn_value_shadow_should_not_link()
685}
686
687fn used() int {
688 return 7
689}
690
691fn main() {
692 unused := used
693 println(unused())
694}
695')
696 mut used := markused.mark_used(a, tc)
697 assert used['used']
698 assert !used['unused']
699 used = transform.transform_with_used(mut a, tc, used)
700 tc.diagnose_unknown_calls = false
701 tc.reject_unlowered_map_mutation = true
702 tc.annotate_types()
703 mut g := cgen.FlatGen.new()
704 c_code := g.gen_with_used_options(a, used, tc, true)
705 assert c_code.contains('used('), c_code
706 assert !c_code.contains('int unused(void)'), c_code
707 assert !c_code.contains('v3_dead_local_fn_value_shadow_should_not_link'), c_code
708}
709
710fn test_flag_default_value_lowering_keeps_escape_helper() {
711 mut a, mut tc := parse_checked_source('flag_default_value_escape_helper_cgen',
712 'module main\n\nfn escape_default_string(value string) string {\n\treturn value\n}\n\nfn flag_default_value(value string) string {\n\treturn value\n}\n\nfn main() {\n\t_ := flag_default_value("abc")\n}\n')
713 mut used := markused.mark_used(a, tc)
714 assert used['escape_default_string']
715 used = transform.transform_with_used(mut a, tc, used)
716 tc.diagnose_unknown_calls = false
717 tc.reject_unlowered_map_mutation = true
718 tc.annotate_types()
719 mut g := cgen.FlatGen.new()
720 c_code := g.gen_with_used_options(a, used, tc, true)
721 assert c_code.contains('escape_default_string(')
722}
723
724// test_return_local_address_seeds_memdup_runtime_helper validates this v3 regression case.
725fn test_return_local_address_seeds_memdup_runtime_helper() {
726 used := mark_used_source('return_local_address_memdup', '
727struct Box {
728 x int
729}
730
731fn make_box() &Box {
732 b := Box{
733 x: 1
734 }
735 return &b
736}
737
738fn main() {
739 _ := make_box()
740}
741')
742 assert used['memdup']
743}
744
745// test_map_literals_lower_to_new_map_after_used_filter_transform validates this v3 regression case.
746fn test_map_literals_lower_to_new_map_after_used_filter_transform() {
747 mut a, mut tc := parse_checked_source('map_literal_new_map_cgen', '
748fn make_map() map[string]int {
749 return map[string]int{}
750}
751
752fn main() {
753 _ := make_map()
754}
755')
756 mut used := markused.mark_used(a, tc)
757 assert used['new_map']
758 used = transform.transform_with_used(mut a, tc, used)
759 tc.diagnose_unknown_calls = false
760 tc.reject_unlowered_map_mutation = true
761 tc.annotate_types()
762 mut g := cgen.FlatGen.new()
763 c_code := g.gen_with_used_options(a, used, tc, true)
764 assert c_code.contains('new_map(sizeof(string), sizeof(int)')
765}
766
767// test_optional_map_or_lowers_to_new_map_after_used_filter_transform
768// validates this v3 regression case.
769fn test_optional_map_or_lowers_to_new_map_after_used_filter_transform() {
770 mut a, mut tc := parse_checked_source('option_map_or_new_map_cgen', '
771fn maybe_map() ?map[string]int {
772 return none
773}
774
775fn main() {
776 m := maybe_map() or { return }
777 _ := m
778}
779')
780 mut used := markused.mark_used(a, tc)
781 assert used['new_map']
782 used = transform.transform_with_used(mut a, tc, used)
783 tc.diagnose_unknown_calls = false
784 tc.reject_unlowered_map_mutation = true
785 tc.annotate_types()
786 mut g := cgen.FlatGen.new()
787 c_code := g.gen_with_used_options(a, used, tc, true)
788 assert c_code.contains('new_map(sizeof(string), sizeof(int)')
789}
790
791// test_string_membership_lowers_to_contains_after_used_filter_transform
792// validates this v3 regression case.
793fn test_string_membership_lowers_to_contains_after_used_filter_transform() {
794 mut a, mut tc := parse_checked_source('string_membership_contains_cgen', '
795fn has_needle() bool {
796 return "bc" in "abcd"
797}
798
799fn main() {
800 _ := has_needle()
801}
802')
803 mut used := markused.mark_used(a, tc)
804 assert used['string__contains']
805 assert used['string__contains_u8']
806 used = transform.transform_with_used(mut a, tc, used)
807 tc.diagnose_unknown_calls = false
808 tc.reject_unlowered_map_mutation = true
809 tc.annotate_types()
810 mut g := cgen.FlatGen.new()
811 c_code := g.gen_with_used_options(a, used, tc, true)
812 assert c_code.contains('string__contains(')
813}
814
815// test_string_compound_assign_lowers_to_plus_after_used_filter_transform
816// validates this v3 regression case.
817fn test_string_compound_assign_lowers_to_plus_after_used_filter_transform() {
818 mut a, mut tc := parse_checked_source('string_plus_assign_cgen', '
819fn main() {
820 mut s := "a"
821 s += "b"
822 _ := s
823}
824')
825 mut used := markused.mark_used(a, tc)
826 assert used['string__plus']
827 used = transform.transform_with_used(mut a, tc, used)
828 tc.diagnose_unknown_calls = false
829 tc.reject_unlowered_map_mutation = true
830 tc.annotate_types()
831 mut g := cgen.FlatGen.new()
832 c_code := g.gen_with_used_options(a, used, tc, true)
833 assert c_code.contains('string__plus(')
834}
835
836// test_string_interpolation_lowers_to_formatter_after_used_filter_transform
837// validates this v3 regression case.
838fn test_string_interpolation_lowers_to_formatter_after_used_filter_transform() {
839 mut a, mut tc := parse_checked_source('string_interp_formatter_cgen', '
840fn main() {
841 _ := "\${true}"
842}
843')
844 mut used := markused.mark_used(a, tc)
845 assert used['bool.str']
846 used = transform.transform_with_used(mut a, tc, used)
847 tc.diagnose_unknown_calls = false
848 tc.reject_unlowered_map_mutation = true
849 tc.annotate_types()
850 mut g := cgen.FlatGen.new()
851 c_code := g.gen_with_used_options(a, used, tc, true)
852 assert c_code.contains('bool__str(')
853}
854
855// test_print_bool_lowers_to_formatter_after_used_filter_transform
856// validates this v3 regression case.
857fn test_print_bool_lowers_to_formatter_after_used_filter_transform() {
858 mut a, mut tc := parse_checked_source('print_bool_formatter_cgen', '
859fn println(s string) {}
860
861fn main() {
862 println(true)
863}
864')
865 mut used := markused.mark_used(a, tc)
866 assert used['bool.str']
867 used = transform.transform_with_used(mut a, tc, used)
868 tc.diagnose_unknown_calls = false
869 tc.reject_unlowered_map_mutation = true
870 tc.annotate_types()
871 mut g := cgen.FlatGen.new()
872 c_code := g.gen_with_used_options(a, used, tc, true)
873 assert c_code.contains('bool__str(')
874}
875
876// test_return_local_address_lowers_to_memdup_after_used_filter_transform
877// validates this v3 regression case.
878fn test_return_local_address_lowers_to_memdup_after_used_filter_transform() {
879 mut a, mut tc := parse_checked_source('return_local_address_memdup_cgen', '
880struct Box {
881 x int
882}
883
884fn make_box() &Box {
885 b := Box{
886 x: 1
887 }
888 return &b
889}
890
891fn main() {
892 _ := make_box()
893}
894')
895 mut used := markused.mark_used(a, tc)
896 assert used['memdup']
897 used = transform.transform_with_used(mut a, tc, used)
898 tc.diagnose_unknown_calls = false
899 tc.reject_unlowered_map_mutation = true
900 tc.annotate_types()
901 mut g := cgen.FlatGen.new()
902 c_code := g.gen_with_used_options(a, used, tc, true)
903 assert c_code.contains('memdup(')
904}
905
906// test_map_literal_compile_keeps_new_map_runtime_helper validates this v3 regression case.
907fn test_map_literal_compile_keeps_new_map_runtime_helper() {
908 v3_bin := build_v3_bin('map_literal_test')
909
910 src := os.join_path(os.temp_dir(), 'v3_markused_map_literal_input.v')
911 bin := os.join_path(os.temp_dir(), 'v3_markused_map_literal_input')
912 os.write_file(src, '
913fn make_map() map[string]int {
914 return map[string]int{}
915}
916
917fn main() {
918 _ := make_map()
919}
920') or {
921 panic(err)
922 }
923 compile := os.execute('${v3_bin} -o ${bin} ${src}')
924 assert compile.exit_code == 0, compile.output
925}
926
927// test_optional_map_or_compile_keeps_new_map_runtime_helper validates this v3 regression case.
928fn test_optional_map_or_compile_keeps_new_map_runtime_helper() {
929 v3_bin := build_v3_bin('option_map_or_test')
930
931 src := os.join_path(os.temp_dir(), 'v3_markused_option_map_or_input.v')
932 bin := os.join_path(os.temp_dir(), 'v3_markused_option_map_or_input')
933 os.write_file(src, '
934fn maybe_map() ?map[string]int {
935 return none
936}
937
938fn main() {
939 m := maybe_map() or { return }
940 _ := m
941}
942') or {
943 panic(err)
944 }
945 compile := os.execute('${v3_bin} -o ${bin} ${src}')
946 assert compile.exit_code == 0, compile.output
947}
948
949// test_return_local_address_compile_keeps_memdup_runtime_helper validates this v3 regression case.
950fn test_return_local_address_compile_keeps_memdup_runtime_helper() {
951 v3_bin := build_v3_bin('return_local_address_test')
952
953 src := os.join_path(os.temp_dir(), 'v3_markused_return_local_address_input.v')
954 bin := os.join_path(os.temp_dir(), 'v3_markused_return_local_address_input')
955 os.write_file(src, '
956struct Box {
957 x int
958}
959
960fn make_box() &Box {
961 b := Box{
962 x: 1
963 }
964 return &b
965}
966
967fn main() {
968 _ := make_box()
969}
970') or {
971 panic(err)
972 }
973 compile := os.execute('${v3_bin} -o ${bin} ${src}')
974 assert compile.exit_code == 0, compile.output
975}
976
977// test_print_bool_compile_keeps_formatter_runtime_helper validates this v3 regression case.
978fn test_print_bool_compile_keeps_formatter_runtime_helper() {
979 v3_bin := build_v3_bin('print_bool_test')
980
981 src := os.join_path(os.temp_dir(), 'v3_markused_print_bool_input.v')
982 bin := os.join_path(os.temp_dir(), 'v3_markused_print_bool_input')
983 os.write_file(src, '
984fn main() {
985 println(true)
986}
987') or { panic(err) }
988 compile := os.execute('${v3_bin} -o ${bin} ${src}')
989 assert compile.exit_code == 0, compile.output
990}
991
992// test_print_signed_width_compile_keeps_str_runtime_helpers validates this v3 regression case.
993fn test_print_signed_width_compile_keeps_str_runtime_helpers() {
994 v3_bin := build_v3_bin('print_signed_width_test')
995
996 src := os.join_path(os.temp_dir(), 'v3_markused_print_signed_width_input.v')
997 bin := os.join_path(os.temp_dir(), 'v3_markused_print_signed_width_input')
998 os.write_file(src, "
999fn main() {
1000 println(i8(-5))
1001 println(i16(-300))
1002 println(i32(-70000))
1003 println(i64(-5000000000))
1004 println('\${i64(42)}')
1005}
1006") or {
1007 panic(err)
1008 }
1009 compile := os.execute('${v3_bin} -o ${bin} ${src}')
1010 assert compile.exit_code == 0, compile.output
1011 assert !compile.output.contains('implicit declaration'), compile.output
1012 run := os.execute(bin)
1013 assert run.exit_code == 0, run.output
1014 assert run.output.trim_space() == '-5\n-300\n-70000\n-5000000000\n42', run.output
1015}
1016
1017// test_string_plus_compile_keeps_plus_runtime_helper validates this v3 regression case.
1018fn test_string_plus_compile_keeps_plus_runtime_helper() {
1019 v3_bin := build_v3_bin('string_plus_test')
1020
1021 src := os.join_path(os.temp_dir(), 'v3_markused_string_plus_input.v')
1022 bin := os.join_path(os.temp_dir(), 'v3_markused_string_plus_input')
1023 os.write_file(src, '
1024fn main() {
1025 flag := true
1026 mut s := "a"
1027 s += "\${flag}"
1028 _ := s
1029}
1030') or {
1031 panic(err)
1032 }
1033 compile := os.execute('${v3_bin} -o ${bin} ${src}')
1034 assert compile.exit_code == 0, compile.output
1035}
1036
1037// test_string_membership_compile_keeps_contains_runtime_helpers validates this v3 regression case.
1038fn test_string_membership_compile_keeps_contains_runtime_helpers() {
1039 v3_bin := build_v3_bin('string_membership_test')
1040
1041 src := os.join_path(os.temp_dir(), 'v3_markused_string_membership_input.v')
1042 bin := os.join_path(os.temp_dir(), 'v3_markused_string_membership_input')
1043 os.write_file(src, '
1044fn has_needle() bool {
1045 return "bc" in "abcd"
1046}
1047
1048fn main() {
1049 _ := has_needle()
1050}
1051') or {
1052 panic(err)
1053 }
1054 compile := os.execute('${v3_bin} -o ${bin} ${src}')
1055 assert compile.exit_code == 0, compile.output
1056}
1057