v / vlib / v2 / markused / markused_test.v
1531 lines · 1511 sloc · 31.99 KB · b831b0eec9b5b2756784b5dabf3808d47d6a39ae
Raw
1// vtest build: macos
2module markused
3
4import v2.ast
5import v2.token
6import v2.types
7
8fn pos(id int) token.Pos {
9 return token.Pos{
10 offset: id
11 id: id
12 }
13}
14
15fn test_interface_name_from_type_handles_unresolved_alias_base_type() {
16 mut env := types.Environment.new()
17 w := new_walker([]ast.File{}, env, MarkUsedOptions{})
18 assert w.interface_name_from_type(types.Type(types.Alias{
19 name: 'UnresolvedAlias'
20 })) == ''
21}
22
23fn test_mark_used_tracks_transitive_function_calls() {
24 mut env := types.Environment.new()
25 files := [
26 ast.File{
27 mod: 'main'
28 name: 'main.v'
29 stmts: [
30 ast.Stmt(ast.FnDecl{
31 name: 'main'
32 typ: ast.FnType{}
33 pos: pos(1)
34 stmts: [
35 ast.Stmt(ast.ExprStmt{
36 expr: ast.CallExpr{
37 lhs: ast.Ident{
38 name: 'foo'
39 pos: pos(2)
40 }
41 pos: pos(2)
42 }
43 }),
44 ]
45 }),
46 ast.Stmt(ast.FnDecl{
47 name: 'foo'
48 typ: ast.FnType{}
49 pos: pos(3)
50 stmts: [
51 ast.Stmt(ast.ExprStmt{
52 expr: ast.CallExpr{
53 lhs: ast.Ident{
54 name: 'bar'
55 pos: pos(4)
56 }
57 pos: pos(4)
58 }
59 }),
60 ]
61 }),
62 ast.Stmt(ast.FnDecl{
63 name: 'bar'
64 typ: ast.FnType{}
65 pos: pos(5)
66 }),
67 ast.Stmt(ast.FnDecl{
68 name: 'dead'
69 typ: ast.FnType{}
70 pos: pos(6)
71 }),
72 ]
73 },
74 ]
75 used := mark_used(files, env)
76 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
77 foo_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
78 bar_key := decl_key('main', files[0].stmts[2] as ast.FnDecl, env)
79 dead_key := decl_key('main', files[0].stmts[3] as ast.FnDecl, env)
80 assert used[main_key]
81 assert used[foo_key]
82 assert used[bar_key]
83 assert !used[dead_key]
84}
85
86fn test_mark_used_walks_duplicate_declaration_with_body() {
87 mut env := types.Environment.new()
88 files := [
89 ast.File{
90 mod: 'main'
91 name: 'main.v'
92 stmts: [
93 ast.Stmt(ast.FnDecl{
94 name: 'main'
95 typ: ast.FnType{}
96 pos: pos(20)
97 stmts: [
98 ast.Stmt(ast.ExprStmt{
99 expr: ast.CallExpr{
100 lhs: ast.Ident{
101 name: 'foo'
102 pos: pos(21)
103 }
104 pos: pos(21)
105 }
106 }),
107 ]
108 }),
109 ast.Stmt(ast.FnDecl{
110 name: 'foo'
111 typ: ast.FnType{}
112 pos: pos(22)
113 }),
114 ast.Stmt(ast.FnDecl{
115 name: 'foo'
116 typ: ast.FnType{}
117 pos: pos(23)
118 stmts: [
119 ast.Stmt(ast.ExprStmt{
120 expr: ast.CallExpr{
121 lhs: ast.Ident{
122 name: 'bar'
123 pos: pos(24)
124 }
125 pos: pos(24)
126 }
127 }),
128 ]
129 }),
130 ast.Stmt(ast.FnDecl{
131 name: 'bar'
132 typ: ast.FnType{}
133 pos: pos(25)
134 }),
135 ]
136 },
137 ]
138 used := mark_used(files, env)
139 foo_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
140 bar_key := decl_key('main', files[0].stmts[3] as ast.FnDecl, env)
141 assert used[foo_key]
142 assert used[bar_key]
143}
144
145fn test_mark_used_tracks_method_calls_with_env_types() {
146 mut env := types.Environment.new()
147 env.set_expr_type(12, types.Struct{
148 name: 'Widget'
149 })
150 files := [
151 ast.File{
152 mod: 'main'
153 name: 'main.v'
154 stmts: [
155 ast.Stmt(ast.FnDecl{
156 name: 'main'
157 typ: ast.FnType{}
158 pos: pos(10)
159 stmts: [
160 ast.Stmt(ast.ExprStmt{
161 expr: ast.CallExpr{
162 lhs: ast.SelectorExpr{
163 lhs: ast.Ident{
164 name: 'w'
165 pos: pos(12)
166 }
167 rhs: ast.Ident{
168 name: 'ping'
169 pos: pos(13)
170 }
171 pos: pos(13)
172 }
173 pos: pos(13)
174 }
175 }),
176 ]
177 }),
178 ast.Stmt(ast.FnDecl{
179 is_method: true
180 receiver: ast.Parameter{
181 name: 'w'
182 typ: ast.Ident{
183 name: 'Widget'
184 pos: pos(14)
185 }
186 pos: pos(14)
187 }
188 name: 'ping'
189 typ: ast.FnType{}
190 pos: pos(15)
191 }),
192 ast.Stmt(ast.FnDecl{
193 is_method: true
194 receiver: ast.Parameter{
195 name: 'w'
196 typ: ast.Ident{
197 name: 'Widget'
198 pos: pos(16)
199 }
200 pos: pos(16)
201 }
202 name: 'unused'
203 typ: ast.FnType{}
204 pos: pos(17)
205 }),
206 ]
207 },
208 ]
209 used := mark_used(files, env)
210 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
211 ping_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
212 unused_key := decl_key('main', files[0].stmts[2] as ast.FnDecl, env)
213 assert used[main_key]
214 assert used[ping_key]
215 assert !used[unused_key]
216}
217
218fn test_mark_used_tracks_current_receiver_method_calls() {
219 mut env := types.Environment.new()
220 env.set_expr_type(32, types.Struct{
221 name: 'Builder'
222 })
223 builder_ptr := ast.Type(ast.PointerType{
224 base_type: ast.Ident{
225 name: 'Builder'
226 pos: pos(33)
227 }
228 })
229 files := [
230 ast.File{
231 mod: 'main'
232 name: 'main.v'
233 stmts: [
234 ast.Stmt(ast.FnDecl{
235 name: 'main'
236 typ: ast.FnType{}
237 pos: pos(30)
238 stmts: [
239 ast.Stmt(ast.ExprStmt{
240 expr: ast.CallExpr{
241 lhs: ast.SelectorExpr{
242 lhs: ast.Ident{
243 name: 'b'
244 pos: pos(32)
245 }
246 rhs: ast.Ident{
247 name: 'build'
248 pos: pos(34)
249 }
250 pos: pos(34)
251 }
252 pos: pos(34)
253 }
254 }),
255 ]
256 }),
257 ast.Stmt(ast.FnDecl{
258 is_method: true
259 receiver: ast.Parameter{
260 name: 'b'
261 typ: builder_ptr
262 pos: pos(35)
263 }
264 name: 'build'
265 typ: ast.FnType{}
266 pos: pos(36)
267 stmts: [
268 ast.Stmt(ast.ExprStmt{
269 expr: ast.CallExpr{
270 lhs: ast.SelectorExpr{
271 lhs: ast.Ident{
272 name: 'b'
273 pos: pos(37)
274 }
275 rhs: ast.Ident{
276 name: 'helper'
277 pos: pos(38)
278 }
279 pos: pos(38)
280 }
281 pos: pos(38)
282 }
283 }),
284 ]
285 }),
286 ast.Stmt(ast.FnDecl{
287 is_method: true
288 receiver: ast.Parameter{
289 name: 'b'
290 typ: builder_ptr
291 pos: pos(39)
292 }
293 name: 'helper'
294 typ: ast.FnType{}
295 pos: pos(40)
296 }),
297 ast.Stmt(ast.FnDecl{
298 is_method: true
299 receiver: ast.Parameter{
300 name: 'b'
301 typ: builder_ptr
302 pos: pos(41)
303 }
304 name: 'unused'
305 typ: ast.FnType{}
306 pos: pos(42)
307 }),
308 ]
309 },
310 ]
311 used := mark_used(files, env)
312 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
313 build_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
314 helper_key := decl_key('main', files[0].stmts[2] as ast.FnDecl, env)
315 unused_key := decl_key('main', files[0].stmts[3] as ast.FnDecl, env)
316 assert used[main_key]
317 assert used[build_key]
318 assert used[helper_key]
319 assert !used[unused_key]
320}
321
322fn test_mark_used_keeps_methods_for_live_comptime_method_loop() {
323 mut env := types.Environment.new()
324 app_type := ast.Expr(ast.Ident{
325 name: 'App'
326 pos: pos(90)
327 })
328 app_receiver := ast.Parameter{
329 name: 'app'
330 typ: app_type
331 pos: pos(91)
332 }
333 files := [
334 ast.File{
335 mod: 'main'
336 name: 'main.v'
337 stmts: [
338 ast.Stmt(ast.FnDecl{
339 name: 'main'
340 typ: ast.FnType{}
341 pos: pos(92)
342 stmts: [
343 ast.Stmt(ast.ExprStmt{
344 expr: ast.CallExpr{
345 lhs: ast.Ident{
346 name: 'dispatch'
347 pos: pos(93)
348 }
349 pos: pos(93)
350 }
351 }),
352 ]
353 }),
354 ast.Stmt(ast.FnDecl{
355 name: 'dispatch'
356 typ: ast.FnType{}
357 pos: pos(94)
358 stmts: [
359 ast.Stmt(ast.ComptimeStmt{
360 stmt: ast.Stmt(ast.ForStmt{
361 init: ast.Stmt(ast.ForInStmt{
362 value: ast.Expr(ast.Ident{
363 name: 'method'
364 pos: pos(95)
365 })
366 expr: ast.Expr(ast.SelectorExpr{
367 lhs: app_type
368 rhs: ast.Ident{
369 name: 'methods'
370 pos: pos(96)
371 }
372 pos: pos(96)
373 })
374 })
375 stmts: [
376 ast.Stmt(ast.ExprStmt{
377 expr: ast.CallExpr{
378 lhs: ast.SelectorExpr{
379 lhs: ast.Ident{
380 name: 'app'
381 pos: pos(97)
382 }
383 rhs: ast.Ident{
384 name: '__comptime_selector__'
385 pos: pos(98)
386 }
387 pos: pos(98)
388 }
389 pos: pos(98)
390 }
391 }),
392 ]
393 })
394 }),
395 ]
396 }),
397 ast.Stmt(ast.FnDecl{
398 is_method: true
399 receiver: app_receiver
400 name: 'index'
401 typ: ast.FnType{}
402 pos: pos(99)
403 }),
404 ast.Stmt(ast.FnDecl{
405 is_method: true
406 receiver: app_receiver
407 name: 'settings'
408 typ: ast.FnType{}
409 pos: pos(100)
410 }),
411 ast.Stmt(ast.FnDecl{
412 is_method: true
413 receiver: ast.Parameter{
414 name: 'other'
415 typ: ast.Expr(ast.Ident{
416 name: 'Other'
417 pos: pos(101)
418 })
419 pos: pos(101)
420 }
421 name: 'unrelated'
422 typ: ast.FnType{}
423 pos: pos(102)
424 }),
425 ]
426 },
427 ]
428 used := mark_used(files, env)
429 flat := ast.flatten_files(files)
430 flat_used := mark_used_flat(&flat, env)
431 dispatch_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
432 index_key := decl_key('main', files[0].stmts[2] as ast.FnDecl, env)
433 settings_key := decl_key('main', files[0].stmts[3] as ast.FnDecl, env)
434 unrelated_key := decl_key('main', files[0].stmts[4] as ast.FnDecl, env)
435 assert used[dispatch_key]
436 assert used[index_key]
437 assert used[settings_key]
438 assert !used[unrelated_key]
439 assert flat_used[dispatch_key]
440 assert flat_used[index_key]
441 assert flat_used[settings_key]
442 assert !flat_used[unrelated_key]
443}
444
445fn test_mark_used_tracks_bound_method_values_in_struct_init() {
446 mut env := types.Environment.new()
447 env.set_expr_type(62, types.Pointer{
448 base_type: types.Type(types.Struct{
449 name: 'Game'
450 })
451 })
452 files := [
453 ast.File{
454 mod: 'main'
455 name: 'main.v'
456 stmts: [
457 ast.Stmt(ast.FnDecl{
458 name: 'main'
459 typ: ast.FnType{}
460 pos: pos(60)
461 stmts: [
462 ast.Stmt(ast.ExprStmt{
463 expr: ast.InitExpr{
464 typ: ast.Ident{
465 name: 'Config'
466 pos: pos(61)
467 }
468 fields: [
469 ast.FieldInit{
470 name: 'draw_fn'
471 value: ast.SelectorExpr{
472 lhs: ast.Ident{
473 name: 'game'
474 pos: pos(62)
475 }
476 rhs: ast.Ident{
477 name: 'draw'
478 pos: pos(63)
479 }
480 pos: pos(63)
481 }
482 },
483 ]
484 pos: pos(64)
485 }
486 }),
487 ]
488 }),
489 ast.Stmt(ast.FnDecl{
490 is_method: true
491 receiver: ast.Parameter{
492 name: 'g'
493 typ: ast.Ident{
494 name: 'Game'
495 pos: pos(65)
496 }
497 pos: pos(65)
498 }
499 name: 'draw'
500 typ: ast.FnType{}
501 pos: pos(66)
502 }),
503 ast.Stmt(ast.FnDecl{
504 is_method: true
505 receiver: ast.Parameter{
506 name: 'g'
507 typ: ast.Ident{
508 name: 'Game'
509 pos: pos(67)
510 }
511 pos: pos(67)
512 }
513 name: 'unused'
514 typ: ast.FnType{}
515 pos: pos(68)
516 }),
517 ]
518 },
519 ]
520 used := mark_used(files, env)
521 flat := ast.flatten_files(files)
522 flat_used := mark_used_flat(&flat, env)
523 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
524 draw_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
525 unused_key := decl_key('main', files[0].stmts[2] as ast.FnDecl, env)
526 assert used[main_key]
527 assert used[draw_key]
528 assert !used[unused_key]
529 assert flat_used[main_key]
530 assert flat_used[draw_key]
531 assert !flat_used[unused_key]
532}
533
534fn test_mark_used_tracks_typed_bound_method_values_without_receiver_type() {
535 mut env := types.Environment.new()
536 env.set_expr_type(73, types.Type(types.Alias{
537 name: 'DrawFn'
538 base_type: types.Type(types.FnType{})
539 }))
540 files := [
541 ast.File{
542 mod: 'main'
543 name: 'main.v'
544 stmts: [
545 ast.Stmt(ast.FnDecl{
546 name: 'main'
547 typ: ast.FnType{}
548 pos: pos(70)
549 stmts: [
550 ast.Stmt(ast.ExprStmt{
551 expr: ast.InitExpr{
552 typ: ast.Ident{
553 name: 'Config'
554 pos: pos(71)
555 }
556 fields: [
557 ast.FieldInit{
558 name: 'draw_fn'
559 value: ast.SelectorExpr{
560 lhs: ast.Ident{
561 name: 'game'
562 pos: pos(72)
563 }
564 rhs: ast.Ident{
565 name: 'draw'
566 pos: pos(73)
567 }
568 pos: pos(73)
569 }
570 },
571 ]
572 pos: pos(74)
573 }
574 }),
575 ]
576 }),
577 ast.Stmt(ast.FnDecl{
578 is_method: true
579 receiver: ast.Parameter{
580 name: 'g'
581 typ: ast.Ident{
582 name: 'Game'
583 pos: pos(75)
584 }
585 pos: pos(75)
586 }
587 name: 'draw'
588 typ: ast.FnType{}
589 pos: pos(76)
590 }),
591 ast.Stmt(ast.FnDecl{
592 is_method: true
593 receiver: ast.Parameter{
594 name: 'g'
595 typ: ast.Ident{
596 name: 'Game'
597 pos: pos(77)
598 }
599 pos: pos(77)
600 }
601 name: 'unused'
602 typ: ast.FnType{}
603 pos: pos(78)
604 }),
605 ]
606 },
607 ]
608 used := mark_used(files, env)
609 flat := ast.flatten_files(files)
610 flat_used := mark_used_flat(&flat, env)
611 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
612 draw_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
613 unused_key := decl_key('main', files[0].stmts[2] as ast.FnDecl, env)
614 assert used[main_key]
615 assert used[draw_key]
616 assert !used[unused_key]
617 assert flat_used[main_key]
618 assert flat_used[draw_key]
619 assert !flat_used[unused_key]
620}
621
622fn test_mark_used_tracks_bound_method_values_in_function_typed_struct_fields() {
623 mut env := types.Environment.new()
624 mut main_scope := types.new_scope(unsafe { nil })
625 main_scope.insert_type('DrawFn', types.Type(types.Alias{
626 name: 'DrawFn'
627 base_type: types.Type(types.FnType{})
628 }))
629 lock env.scopes {
630 env.scopes['main'] = main_scope
631 }
632 files := [
633 ast.File{
634 mod: 'main'
635 name: 'main.v'
636 stmts: [
637 ast.Stmt(ast.StructDecl{
638 name: 'Config'
639 fields: [
640 ast.FieldDecl{
641 name: 'draw_fn'
642 typ: ast.Ident{
643 name: 'DrawFn'
644 pos: pos(80)
645 }
646 },
647 ]
648 }),
649 ast.Stmt(ast.FnDecl{
650 name: 'main'
651 typ: ast.FnType{}
652 pos: pos(81)
653 stmts: [
654 ast.Stmt(ast.ExprStmt{
655 expr: ast.InitExpr{
656 typ: ast.Ident{
657 name: 'Config'
658 pos: pos(82)
659 }
660 fields: [
661 ast.FieldInit{
662 name: 'draw_fn'
663 value: ast.SelectorExpr{
664 lhs: ast.Ident{
665 name: 'game'
666 pos: pos(83)
667 }
668 rhs: ast.Ident{
669 name: 'draw'
670 pos: pos(84)
671 }
672 pos: pos(84)
673 }
674 },
675 ]
676 pos: pos(85)
677 }
678 }),
679 ]
680 }),
681 ast.Stmt(ast.FnDecl{
682 is_method: true
683 receiver: ast.Parameter{
684 name: 'g'
685 typ: ast.Ident{
686 name: 'Game'
687 pos: pos(86)
688 }
689 pos: pos(86)
690 }
691 name: 'draw'
692 typ: ast.FnType{}
693 pos: pos(87)
694 }),
695 ast.Stmt(ast.FnDecl{
696 is_method: true
697 receiver: ast.Parameter{
698 name: 'g'
699 typ: ast.Ident{
700 name: 'Game'
701 pos: pos(88)
702 }
703 pos: pos(88)
704 }
705 name: 'unused'
706 typ: ast.FnType{}
707 pos: pos(89)
708 }),
709 ]
710 },
711 ]
712 used := mark_used(files, env)
713 flat := ast.flatten_files(files)
714 flat_used := mark_used_flat(&flat, env)
715 main_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
716 draw_key := decl_key('main', files[0].stmts[2] as ast.FnDecl, env)
717 unused_key := decl_key('main', files[0].stmts[3] as ast.FnDecl, env)
718 assert used[main_key]
719 assert used[draw_key]
720 assert !used[unused_key]
721 assert flat_used[main_key]
722 assert flat_used[draw_key]
723 assert !flat_used[unused_key]
724}
725
726fn test_mark_used_walks_codegen_required_str_methods() {
727 mut env := types.Environment.new()
728 files := [
729 ast.File{
730 mod: 'main'
731 name: 'main.v'
732 stmts: [
733 ast.Stmt(ast.FnDecl{
734 name: 'main'
735 typ: ast.FnType{}
736 pos: pos(90)
737 }),
738 ]
739 },
740 ast.File{
741 mod: 'time'
742 name: 'time.v'
743 stmts: [
744 ast.Stmt(ast.StructDecl{
745 name: 'Time'
746 }),
747 ast.Stmt(ast.FnDecl{
748 is_method: true
749 receiver: ast.Parameter{
750 name: 't'
751 typ: ast.Ident{
752 name: 'Time'
753 pos: pos(91)
754 }
755 pos: pos(91)
756 }
757 name: 'str'
758 typ: ast.FnType{}
759 pos: pos(92)
760 stmts: [
761 ast.Stmt(ast.ReturnStmt{
762 exprs: [
763 ast.Expr(ast.CallExpr{
764 lhs: ast.SelectorExpr{
765 lhs: ast.Ident{
766 name: 't'
767 pos: pos(93)
768 }
769 rhs: ast.Ident{
770 name: 'format_ss'
771 pos: pos(94)
772 }
773 pos: pos(94)
774 }
775 pos: pos(94)
776 }),
777 ]
778 }),
779 ]
780 }),
781 ast.Stmt(ast.FnDecl{
782 is_method: true
783 receiver: ast.Parameter{
784 name: 't'
785 typ: ast.Ident{
786 name: 'Time'
787 pos: pos(95)
788 }
789 pos: pos(95)
790 }
791 name: 'format_ss'
792 typ: ast.FnType{}
793 pos: pos(96)
794 }),
795 ]
796 },
797 ]
798 used := mark_used(files, env)
799 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
800 str_key := decl_key('time', files[1].stmts[1] as ast.FnDecl, env)
801 format_key := decl_key('time', files[1].stmts[2] as ast.FnDecl, env)
802 assert used[main_key]
803 assert used[str_key]
804 assert used[format_key]
805}
806
807fn test_mark_used_walks_codegen_required_sync_method_roots() {
808 mut env := types.Environment.new()
809 spin_lock_ptr := ast.Expr(ast.PrefixExpr{
810 pos: pos(110)
811 op: .amp
812 expr: ast.Ident{
813 name: 'SpinLock'
814 pos: pos(111)
815 }
816 })
817 files := [
818 ast.File{
819 mod: 'main'
820 name: 'main.v'
821 stmts: [
822 ast.Stmt(ast.FnDecl{
823 name: 'main'
824 typ: ast.FnType{}
825 pos: pos(112)
826 }),
827 ]
828 },
829 ast.File{
830 mod: 'sync'
831 name: 'channels.c.v'
832 stmts: [
833 ast.Stmt(ast.StructDecl{
834 name: 'SpinLock'
835 }),
836 ast.Stmt(ast.StructDecl{
837 name: 'Channel'
838 fields: [
839 ast.FieldDecl{
840 name: 'read_sub_mtx'
841 typ: spin_lock_ptr
842 },
843 ]
844 }),
845 ast.Stmt(ast.FnDecl{
846 is_method: true
847 receiver: ast.Parameter{
848 name: 'ch'
849 typ: ast.Ident{
850 name: 'Channel'
851 pos: pos(113)
852 }
853 pos: pos(113)
854 }
855 name: 'try_wait'
856 typ: ast.FnType{}
857 pos: pos(114)
858 stmts: [
859 ast.Stmt(ast.ExprStmt{
860 expr: ast.CallExpr{
861 lhs: ast.SelectorExpr{
862 lhs: ast.SelectorExpr{
863 lhs: ast.Ident{
864 name: 'ch'
865 pos: pos(115)
866 }
867 rhs: ast.Ident{
868 name: 'read_sub_mtx'
869 pos: pos(116)
870 }
871 pos: pos(116)
872 }
873 rhs: ast.Ident{
874 name: 'lock'
875 pos: pos(117)
876 }
877 pos: pos(117)
878 }
879 pos: pos(117)
880 }
881 }),
882 ]
883 }),
884 ast.Stmt(ast.FnDecl{
885 is_method: true
886 receiver: ast.Parameter{
887 name: 's'
888 typ: spin_lock_ptr
889 pos: pos(118)
890 }
891 name: 'lock'
892 typ: ast.FnType{}
893 pos: pos(119)
894 }),
895 ]
896 },
897 ]
898 used := mark_used(files, env)
899 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
900 try_wait_key := decl_key('sync', files[1].stmts[2] as ast.FnDecl, env)
901 lock_key := decl_key('sync', files[1].stmts[3] as ast.FnDecl, env)
902 assert used[main_key]
903 assert used[try_wait_key]
904 assert used[lock_key]
905}
906
907fn test_mark_used_tracks_embedded_methods_for_interface_conversions() {
908 mut env := types.Environment.new()
909 files := [
910 ast.File{
911 mod: 'main'
912 name: 'main.v'
913 stmts: [
914 ast.Stmt(ast.InterfaceDecl{
915 name: 'Logger'
916 fields: [
917 ast.FieldDecl{
918 name: 'get_level'
919 typ: ast.Type(ast.FnType{})
920 is_interface_method: true
921 },
922 ]
923 }),
924 ast.Stmt(ast.GlobalDecl{
925 fields: [
926 ast.FieldDecl{
927 name: 'logger'
928 typ: ast.Type(ast.PointerType{
929 base_type: ast.Ident{
930 name: 'Logger'
931 pos: pos(60)
932 }
933 })
934 },
935 ]
936 }),
937 ast.Stmt(ast.StructDecl{
938 name: 'Log'
939 }),
940 ast.Stmt(ast.StructDecl{
941 name: 'ThreadSafeLog'
942 embedded: [
943 ast.Expr(ast.Ident{
944 name: 'Log'
945 pos: pos(61)
946 }),
947 ]
948 }),
949 ast.Stmt(ast.FnDecl{
950 is_method: true
951 receiver: ast.Parameter{
952 name: 'l'
953 typ: ast.Ident{
954 name: 'Log'
955 pos: pos(62)
956 }
957 pos: pos(62)
958 }
959 name: 'get_level'
960 typ: ast.FnType{}
961 pos: pos(63)
962 }),
963 ast.Stmt(ast.FnDecl{
964 is_method: true
965 receiver: ast.Parameter{
966 name: 'l'
967 typ: ast.Ident{
968 name: 'Log'
969 pos: pos(64)
970 }
971 pos: pos(64)
972 }
973 name: 'unused'
974 typ: ast.FnType{}
975 pos: pos(65)
976 }),
977 ast.Stmt(ast.FnDecl{
978 name: 'main'
979 typ: ast.FnType{}
980 pos: pos(66)
981 stmts: [
982 ast.Stmt(ast.AssignStmt{
983 lhs: [
984 ast.Expr(ast.Ident{
985 name: 'logger'
986 pos: pos(67)
987 }),
988 ]
989 rhs: [
990 ast.Expr(ast.InitExpr{
991 typ: ast.Ident{
992 name: 'ThreadSafeLog'
993 pos: pos(68)
994 }
995 pos: pos(68)
996 }),
997 ]
998 }),
999 ]
1000 }),
1001 ]
1002 },
1003 ]
1004 used := mark_used(files, env)
1005 main_key := decl_key('main', files[0].stmts[6] as ast.FnDecl, env)
1006 get_level_key := decl_key('main', files[0].stmts[4] as ast.FnDecl, env)
1007 unused_key := decl_key('main', files[0].stmts[5] as ast.FnDecl, env)
1008 assert used[main_key]
1009 assert used[get_level_key]
1010 assert !used[unused_key]
1011}
1012
1013fn test_mark_used_tracks_embedded_interface_methods_for_interface_conversions() {
1014 mut env := types.Environment.new()
1015 files := [
1016 ast.File{
1017 mod: 'main'
1018 name: 'main.v'
1019 stmts: [
1020 ast.Stmt(ast.InterfaceDecl{
1021 name: 'Base'
1022 fields: [
1023 ast.FieldDecl{
1024 name: 'base_required'
1025 typ: ast.Type(ast.FnType{})
1026 is_interface_method: true
1027 },
1028 ]
1029 }),
1030 ast.Stmt(ast.InterfaceDecl{
1031 name: 'Child'
1032 embedded: [
1033 ast.Expr(ast.Ident{
1034 name: 'Base'
1035 pos: pos(90)
1036 }),
1037 ]
1038 }),
1039 ast.Stmt(ast.GlobalDecl{
1040 fields: [
1041 ast.FieldDecl{
1042 name: 'child'
1043 typ: ast.Ident{
1044 name: 'Child'
1045 pos: pos(91)
1046 }
1047 },
1048 ]
1049 }),
1050 ast.Stmt(ast.StructDecl{
1051 name: 'ConcreteChild'
1052 }),
1053 ast.Stmt(ast.FnDecl{
1054 is_method: true
1055 receiver: ast.Parameter{
1056 name: 'c'
1057 typ: ast.Ident{
1058 name: 'ConcreteChild'
1059 pos: pos(92)
1060 }
1061 pos: pos(92)
1062 }
1063 name: 'base_required'
1064 typ: ast.FnType{}
1065 pos: pos(93)
1066 }),
1067 ast.Stmt(ast.FnDecl{
1068 is_method: true
1069 receiver: ast.Parameter{
1070 name: 'c'
1071 typ: ast.Ident{
1072 name: 'ConcreteChild'
1073 pos: pos(94)
1074 }
1075 pos: pos(94)
1076 }
1077 name: 'unused'
1078 typ: ast.FnType{}
1079 pos: pos(95)
1080 }),
1081 ast.Stmt(ast.FnDecl{
1082 name: 'main'
1083 typ: ast.FnType{}
1084 pos: pos(96)
1085 stmts: [
1086 ast.Stmt(ast.AssignStmt{
1087 lhs: [
1088 ast.Expr(ast.Ident{
1089 name: 'child'
1090 pos: pos(97)
1091 }),
1092 ]
1093 rhs: [
1094 ast.Expr(ast.InitExpr{
1095 typ: ast.Ident{
1096 name: 'ConcreteChild'
1097 pos: pos(98)
1098 }
1099 pos: pos(98)
1100 }),
1101 ]
1102 }),
1103 ]
1104 }),
1105 ]
1106 },
1107 ]
1108 used := mark_used(files, env)
1109 main_key := decl_key('main', files[0].stmts[6] as ast.FnDecl, env)
1110 base_required_key := decl_key('main', files[0].stmts[4] as ast.FnDecl, env)
1111 unused_key := decl_key('main', files[0].stmts[5] as ast.FnDecl, env)
1112 assert used[main_key]
1113 assert used[base_required_key]
1114 assert !used[unused_key]
1115}
1116
1117fn test_mark_used_tracks_direct_and_embedded_interface_methods_for_interface_conversions() {
1118 mut env := types.Environment.new()
1119 files := [
1120 ast.File{
1121 mod: 'main'
1122 name: 'main.v'
1123 stmts: [
1124 ast.Stmt(ast.InterfaceDecl{
1125 name: 'Base'
1126 fields: [
1127 ast.FieldDecl{
1128 name: 'base_required'
1129 typ: ast.Type(ast.FnType{})
1130 is_interface_method: true
1131 },
1132 ]
1133 }),
1134 ast.Stmt(ast.InterfaceDecl{
1135 name: 'Child'
1136 embedded: [
1137 ast.Expr(ast.Ident{
1138 name: 'Base'
1139 pos: pos(100)
1140 }),
1141 ]
1142 fields: [
1143 ast.FieldDecl{
1144 name: 'child_required'
1145 typ: ast.Type(ast.FnType{})
1146 is_interface_method: true
1147 },
1148 ]
1149 }),
1150 ast.Stmt(ast.GlobalDecl{
1151 fields: [
1152 ast.FieldDecl{
1153 name: 'child'
1154 typ: ast.Ident{
1155 name: 'Child'
1156 pos: pos(101)
1157 }
1158 },
1159 ]
1160 }),
1161 ast.Stmt(ast.StructDecl{
1162 name: 'ConcreteUnion'
1163 }),
1164 ast.Stmt(ast.FnDecl{
1165 is_method: true
1166 receiver: ast.Parameter{
1167 name: 'c'
1168 typ: ast.Ident{
1169 name: 'ConcreteUnion'
1170 pos: pos(102)
1171 }
1172 pos: pos(102)
1173 }
1174 name: 'base_required'
1175 typ: ast.FnType{}
1176 pos: pos(103)
1177 }),
1178 ast.Stmt(ast.FnDecl{
1179 is_method: true
1180 receiver: ast.Parameter{
1181 name: 'c'
1182 typ: ast.Ident{
1183 name: 'ConcreteUnion'
1184 pos: pos(104)
1185 }
1186 pos: pos(104)
1187 }
1188 name: 'child_required'
1189 typ: ast.FnType{}
1190 pos: pos(105)
1191 }),
1192 ast.Stmt(ast.FnDecl{
1193 is_method: true
1194 receiver: ast.Parameter{
1195 name: 'c'
1196 typ: ast.Ident{
1197 name: 'ConcreteUnion'
1198 pos: pos(106)
1199 }
1200 pos: pos(106)
1201 }
1202 name: 'unused'
1203 typ: ast.FnType{}
1204 pos: pos(107)
1205 }),
1206 ast.Stmt(ast.FnDecl{
1207 name: 'main'
1208 typ: ast.FnType{}
1209 pos: pos(108)
1210 stmts: [
1211 ast.Stmt(ast.AssignStmt{
1212 lhs: [
1213 ast.Expr(ast.Ident{
1214 name: 'child'
1215 pos: pos(109)
1216 }),
1217 ]
1218 rhs: [
1219 ast.Expr(ast.InitExpr{
1220 typ: ast.Ident{
1221 name: 'ConcreteUnion'
1222 pos: pos(110)
1223 }
1224 pos: pos(110)
1225 }),
1226 ]
1227 }),
1228 ]
1229 }),
1230 ]
1231 },
1232 ]
1233 used := mark_used(files, env)
1234 main_key := decl_key('main', files[0].stmts[7] as ast.FnDecl, env)
1235 base_required_key := decl_key('main', files[0].stmts[4] as ast.FnDecl, env)
1236 child_required_key := decl_key('main', files[0].stmts[5] as ast.FnDecl, env)
1237 unused_key := decl_key('main', files[0].stmts[6] as ast.FnDecl, env)
1238 assert used[main_key]
1239 assert used[base_required_key]
1240 assert used[child_required_key]
1241 assert !used[unused_key]
1242}
1243
1244fn test_mark_used_does_not_treat_interface_fn_field_as_method() {
1245 mut env := types.Environment.new()
1246 files := [
1247 ast.File{
1248 mod: 'main'
1249 name: 'main.v'
1250 stmts: [
1251 ast.Stmt(ast.InterfaceDecl{
1252 name: 'HandlerBox'
1253 fields: [
1254 ast.FieldDecl{
1255 name: 'handler'
1256 typ: ast.Type(ast.FnType{
1257 params: [
1258 ast.Parameter{
1259 name: 'value'
1260 typ: ast.Ident{
1261 name: 'int'
1262 pos: pos(70)
1263 }
1264 pos: pos(70)
1265 },
1266 ]
1267 })
1268 },
1269 ]
1270 }),
1271 ast.Stmt(ast.GlobalDecl{
1272 fields: [
1273 ast.FieldDecl{
1274 name: 'box'
1275 typ: ast.Ident{
1276 name: 'HandlerBox'
1277 pos: pos(71)
1278 }
1279 },
1280 ]
1281 }),
1282 ast.Stmt(ast.StructDecl{
1283 name: 'ConcreteHandler'
1284 }),
1285 ast.Stmt(ast.FnDecl{
1286 is_method: true
1287 receiver: ast.Parameter{
1288 name: 'h'
1289 typ: ast.Ident{
1290 name: 'ConcreteHandler'
1291 pos: pos(72)
1292 }
1293 pos: pos(72)
1294 }
1295 name: 'handler'
1296 typ: ast.FnType{}
1297 pos: pos(73)
1298 }),
1299 ast.Stmt(ast.FnDecl{
1300 name: 'main'
1301 typ: ast.FnType{}
1302 pos: pos(74)
1303 stmts: [
1304 ast.Stmt(ast.AssignStmt{
1305 lhs: [
1306 ast.Expr(ast.Ident{
1307 name: 'box'
1308 pos: pos(75)
1309 }),
1310 ]
1311 rhs: [
1312 ast.Expr(ast.InitExpr{
1313 typ: ast.Ident{
1314 name: 'ConcreteHandler'
1315 pos: pos(76)
1316 }
1317 pos: pos(76)
1318 }),
1319 ]
1320 }),
1321 ]
1322 }),
1323 ]
1324 },
1325 ]
1326 used := mark_used(files, env)
1327 handler_key := decl_key('main', files[0].stmts[3] as ast.FnDecl, env)
1328 main_key := decl_key('main', files[0].stmts[4] as ast.FnDecl, env)
1329 assert used[main_key]
1330 assert !used[handler_key]
1331}
1332
1333fn test_mark_used_tracks_function_pointers_in_top_level_const_arrays() {
1334 mut env := types.Environment.new()
1335 files := [
1336 ast.File{
1337 mod: 'main'
1338 name: 'main.v'
1339 stmts: [
1340 ast.Stmt(ast.ConstDecl{
1341 fields: [
1342 ast.FieldInit{
1343 name: 'validators'
1344 value: ast.ArrayInitExpr{
1345 exprs: [
1346 ast.Expr(ast.Ident{
1347 name: 'accept'
1348 pos: pos(51)
1349 }),
1350 ]
1351 pos: pos(50)
1352 }
1353 },
1354 ]
1355 }),
1356 ast.Stmt(ast.FnDecl{
1357 name: 'main'
1358 typ: ast.FnType{}
1359 pos: pos(52)
1360 stmts: []ast.Stmt{}
1361 }),
1362 ast.Stmt(ast.FnDecl{
1363 name: 'accept'
1364 typ: ast.FnType{}
1365 pos: pos(53)
1366 }),
1367 ast.Stmt(ast.FnDecl{
1368 name: 'dead'
1369 typ: ast.FnType{}
1370 pos: pos(54)
1371 }),
1372 ]
1373 },
1374 ]
1375 used := mark_used(files, env)
1376 main_key := decl_key('main', files[0].stmts[1] as ast.FnDecl, env)
1377 accept_key := decl_key('main', files[0].stmts[2] as ast.FnDecl, env)
1378 dead_key := decl_key('main', files[0].stmts[3] as ast.FnDecl, env)
1379 assert used[main_key]
1380 assert used[accept_key]
1381 assert !used[dead_key]
1382}
1383
1384fn test_mark_used_tracks_module_function_values_as_call_args() {
1385 mut env := types.Environment.new()
1386 callback_type := ast.Type(ast.FnType{
1387 params: [
1388 ast.Parameter{
1389 typ: ast.Ident{
1390 name: 'string'
1391 pos: pos(70)
1392 }
1393 },
1394 ]
1395 return_type: ast.Ident{
1396 name: 'string'
1397 pos: pos(71)
1398 }
1399 })
1400 files := [
1401 ast.File{
1402 mod: 'main'
1403 name: 'main.v'
1404 imports: [
1405 ast.ImportStmt{
1406 name: 'term'
1407 },
1408 ]
1409 stmts: [
1410 ast.Stmt(ast.FnDecl{
1411 name: 'main'
1412 typ: ast.FnType{}
1413 pos: pos(72)
1414 stmts: [
1415 ast.Stmt(ast.ExprStmt{
1416 expr: ast.CallExpr{
1417 lhs: ast.SelectorExpr{
1418 lhs: ast.Ident{
1419 name: 'term'
1420 pos: pos(73)
1421 }
1422 rhs: ast.Ident{
1423 name: 'colorize'
1424 pos: pos(74)
1425 }
1426 pos: pos(74)
1427 }
1428 args: [
1429 ast.Expr(ast.SelectorExpr{
1430 lhs: ast.Ident{
1431 name: 'term'
1432 pos: pos(75)
1433 }
1434 rhs: ast.Ident{
1435 name: 'green'
1436 pos: pos(76)
1437 }
1438 pos: pos(76)
1439 }),
1440 ast.Expr(ast.BasicLiteral{
1441 kind: .string
1442 value: 'ok'
1443 pos: pos(77)
1444 }),
1445 ]
1446 pos: pos(74)
1447 }
1448 }),
1449 ]
1450 }),
1451 ]
1452 },
1453 ast.File{
1454 mod: 'term'
1455 name: 'term.v'
1456 stmts: [
1457 ast.Stmt(ast.FnDecl{
1458 name: 'colorize'
1459 typ: ast.FnType{
1460 params: [
1461 ast.Parameter{
1462 name: 'cfn'
1463 typ: callback_type
1464 pos: pos(78)
1465 },
1466 ast.Parameter{
1467 name: 's'
1468 typ: ast.Ident{
1469 name: 'string'
1470 pos: pos(79)
1471 }
1472 pos: pos(79)
1473 },
1474 ]
1475 return_type: ast.Ident{
1476 name: 'string'
1477 pos: pos(80)
1478 }
1479 }
1480 pos: pos(81)
1481 }),
1482 ast.Stmt(ast.FnDecl{
1483 name: 'green'
1484 typ: ast.FnType{}
1485 pos: pos(82)
1486 }),
1487 ast.Stmt(ast.FnDecl{
1488 name: 'red'
1489 typ: ast.FnType{}
1490 pos: pos(83)
1491 }),
1492 ]
1493 },
1494 ]
1495 used := mark_used(files, env)
1496 main_key := decl_key('main', files[0].stmts[0] as ast.FnDecl, env)
1497 colorize_key := decl_key('term', files[1].stmts[0] as ast.FnDecl, env)
1498 green_key := decl_key('term', files[1].stmts[1] as ast.FnDecl, env)
1499 red_key := decl_key('term', files[1].stmts[2] as ast.FnDecl, env)
1500 assert used[main_key]
1501 assert used[colorize_key]
1502 assert used[green_key]
1503 assert !used[red_key]
1504}
1505
1506fn test_mark_used_keeps_all_functions_when_no_entry_root_exists() {
1507 mut env := types.Environment.new()
1508 files := [
1509 ast.File{
1510 mod: 'mylib'
1511 name: 'lib.v'
1512 stmts: [
1513 ast.Stmt(ast.FnDecl{
1514 name: 'a'
1515 typ: ast.FnType{}
1516 pos: pos(21)
1517 }),
1518 ast.Stmt(ast.FnDecl{
1519 name: 'b'
1520 typ: ast.FnType{}
1521 pos: pos(22)
1522 }),
1523 ]
1524 },
1525 ]
1526 used := mark_used(files, env)
1527 a_key := decl_key('mylib', files[0].stmts[0] as ast.FnDecl, env)
1528 b_key := decl_key('mylib', files[0].stmts[1] as ast.FnDecl, env)
1529 assert used[a_key]
1530 assert used[b_key]
1531}
1532