v4 / vlib / v3 / markused / markused.v
2801 lines · 2668 sloc · 82.8 KB · 1498e02bf64c6e33255fc0d3055b6e72900af9a5
Raw
1module markused
2
3import v3.flat
4import v3.types
5
6const trace_markused = false
7
8// mark_used updates mark used state for markused.
9pub fn mark_used(a &flat.FlatAst, tc &types.TypeChecker) map[string]bool {
10 return mark_used_with_test_files(a, tc, map[string]bool{})
11}
12
13pub fn mark_used_for_tests(a &flat.FlatAst, tc &types.TypeChecker, test_files []string) map[string]bool {
14 mut file_map := map[string]bool{}
15 for file in test_files {
16 file_map[file] = true
17 }
18 return mark_used_with_test_files(a, tc, file_map)
19}
20
21fn mark_used_with_test_files(a &flat.FlatAst, tc &types.TypeChecker, test_files map[string]bool) map[string]bool {
22 mut cur_module := ''
23 mut imports := map[string]string{}
24 mut fn_decls := map[string]FnDeclInfo{}
25 mut fn_decl_lists := map[string][]FnDeclInfo{}
26 mut struct_decls := map[string]StructDeclInfo{}
27 mut const_decls := map[string]ConstDeclInfo{}
28
29 // Reverse index: short name (after last '.') -> list of full qualified names
30 mut suffix_map := map[string][]string{}
31
32 mut fn_count := 0
33 mut fn_with_dot := 0
34 mut contains2_total := 0
35 for node_idx, node in a.nodes {
36 if node.kind == .file {
37 cur_module = ''
38 continue
39 }
40 if node.kind == .module_decl {
41 cur_module = node.value
42 continue
43 }
44 if node.kind == .import_decl {
45 imports[node.typ] = node.value
46 continue
47 }
48 if node.kind == .struct_decl {
49 full_name := qualify_fn(cur_module, node.value)
50 info := StructDeclInfo{
51 node_id: flat.NodeId(node_idx)
52 module: cur_module
53 }
54 struct_decls[full_name] = info
55 if node.value !in struct_decls {
56 struct_decls[node.value] = info
57 }
58 continue
59 }
60 if node.kind == .const_decl {
61 for i in 0 .. node.children_count {
62 field_id := a.child(&node, i)
63 field := a.node(field_id)
64 if field.kind != .const_field || field.children_count == 0 {
65 continue
66 }
67 info := ConstDeclInfo{
68 expr_id: a.child(field, 0)
69 module: cur_module
70 }
71 const_decls[field.value] = info
72 full_name := qualify_fn(cur_module, field.value)
73 if full_name != field.value {
74 const_decls[full_name] = info
75 }
76 }
77 continue
78 }
79 if node.kind == .fn_decl {
80 has_dot := node.value.contains('.')
81 if trace_markused {
82 fn_count++
83 if has_dot {
84 fn_with_dot++
85 if fn_with_dot <= 5 {
86 eprintln(' fn with dot: "${node.value}"')
87 }
88 }
89 }
90 info := FnDeclInfo{
91 node_id: flat.NodeId(node_idx)
92 module: cur_module
93 }
94 add_fn_decl_info(mut fn_decls, mut fn_decl_lists, node.value, info)
95 lowered_name := markused_c_name(node.value)
96 if lowered_name != node.value {
97 add_fn_decl_info(mut fn_decls, mut fn_decl_lists, lowered_name, info)
98 }
99 qname := qualify_fn(cur_module, node.value)
100 if qname != node.value {
101 add_fn_decl_info(mut fn_decls, mut fn_decl_lists, qname, info)
102 lowered_qname := markused_c_name(qname)
103 if lowered_qname != qname {
104 add_fn_decl_info(mut fn_decls, mut fn_decl_lists, lowered_qname, info)
105 }
106 }
107 // Build suffix_map entries
108 if has_dot {
109 if trace_markused {
110 contains2_total++
111 }
112 short := node.value.all_after_last('.')
113 add_suffix_candidate(mut suffix_map, short, node.value)
114 if qname != node.value {
115 add_suffix_candidate(mut suffix_map, short, qname)
116 }
117 }
118 if qname != node.value && qname.contains('.') {
119 short := qname.all_after_last('.')
120 add_suffix_candidate(mut suffix_map, short, qname)
121 }
122 }
123 }
124
125 // BFS from main
126 mut used := map[string]bool{}
127 mut queue := []string{}
128 queue << 'main'
129 used['main'] = true
130 enqueue_main_module_roots(fn_decls, mut used, mut queue)
131 enqueue_auto_roots(fn_decls, mut used, mut queue)
132 enqueue_export_roots(a, mut used, mut queue)
133 enqueue_veb_handler_roots(a, tc, mut used, mut queue)
134 enqueue_test_file_roots(a, test_files, mut used, mut queue)
135 queue << 'time.Time.new'
136 used['time.Time.new'] = true
137 used['Time.new'] = true
138 queue << 'gen_expr_lvalue'
139 used['gen_expr_lvalue'] = true
140 queue << 'c.gen_expr_lvalue'
141 used['c.gen_expr_lvalue'] = true
142 queue << 'gen_assign'
143 used['gen_assign'] = true
144 queue << 'c.gen_assign'
145 used['c.gen_assign'] = true
146 for seed in ['__new_array', 'new_array_from_c_array', 'array.get', 'array.set', 'array.push',
147 'array.push_many', 'array.slice', 'array.clone', 'array.delete', 'array.ensure_cap',
148 'string.==', 'string.<', 'string.free', 'string.all_before', 'string.all_before_last',
149 'string.all_after', 'string.all_after_last', 'string.substr', 'string__substr', 'u8.vstring',
150 '[]rune.string', 'map.set', 'map.exists', 'map.get', 'map.get_check', 'map.get_and_set',
151 'map.delete', 'map.clone', 'map.clear', 'memdup', 'strings.Builder.write_ptr',
152 'strings.Builder.write_runes', 'strings.Builder.free', 'strconv.format_int',
153 'strconv.format_uint', 'bool.str', 'ptr_str', 'strconv__f32_to_str_l',
154 'strconv__f64_to_str_l', 'sync.new_channel_st', 'sync.Channel.push', 'sync.Channel.pop',
155 'sync.Channel.close', 'sync.Channel.len', 'sync.Channel.closed', 'new_channel_st',
156 'Channel.push', 'Channel.pop', 'Channel.close', 'Channel.len', 'Channel.closed', 'panic',
157 'u8.is_letter', 'u8.is_capital', 'string.is_capital', 'string.to_lower_ascii',
158 'rune.to_lower', 'Array_u8__bytestr', 'Array_u8__hex', 'data_to_hex_string',
159 'map_hash_string', 'map_hash_int_1', 'map_hash_int_2', 'map_hash_int_4', 'map_hash_int_8',
160 'map_eq_string', 'map_eq_int_1', 'map_eq_int_2', 'map_eq_int_4', 'map_eq_int_8',
161 'map_clone_string', 'map_clone_int_1', 'map_clone_int_2', 'map_clone_int_4',
162 'map_clone_int_8', 'map_free_string', 'map_free_nop', '[]string.join', 'Array_string__join',
163 'exit', 'v_exit'] {
164 queue << seed
165 used[seed] = true
166 }
167
168 if trace_markused {
169 eprintln('markused: fn_count:')
170 eprintln(fn_count.str())
171 eprintln('fn_with_dot:')
172 eprintln(fn_with_dot.str())
173 eprintln('contains2_total:')
174 eprintln(contains2_total.str())
175 eprintln('markused: main in fn_decls: ${'main' in fn_decls}')
176 eprintln('markused: fn_decls count: ${fn_decls.len}')
177 eprintln('markused: suffix_map count: ${suffix_map.len}')
178 mut total_suffix_entries := 0
179 for _, vals in suffix_map {
180 total_suffix_entries += vals.len
181 }
182 eprintln('total suffix_map entries (sum of array lens):')
183 eprintln(total_suffix_entries.str())
184 }
185
186 mut suffix_hits := 0
187 // mut suffix_misses := 0
188 mut in_cg := 0
189 mut not_in_cg := 0
190 mut total_callees := 0
191 collector := CallCollector{
192 a: a
193 tc: tc
194 fn_decls: fn_decls
195 struct_decls: struct_decls
196 const_decls: const_decls
197 }
198 enqueue_detected_runtime_helpers(a, tc, mut used, mut queue)
199 enqueue_function_value_selectors(a, collector, fn_decls, mut used, mut queue)
200 // Methods used as values (`recv.method` passed as a callback) are reachable only
201 // through a wrapper cgen generates later. The checker records them per enclosing
202 // function in `method_values_by_fn`; they are seeded inside the BFS below (only when
203 // that function is reached), so an unreachable function's method value never forces an
204 // otherwise-unused specialization to be transformed/emitted.
205 enqueue_initializer_calls(a, collector, imports, fn_decls, mut used, mut queue)
206 enqueue_top_level_calls(a, collector, fn_decls, mut used, mut queue)
207 // Interface dispatch reachability: calling an interface method `Foo.m` may
208 // dispatch to any concrete `T.m` for a type `T` that implements `Foo`. Those
209 // concrete methods are only referenced from the generated dispatch switch, so
210 // without this they would be pruned and produce undefined-symbol errors.
211 mut iface_impls := map[string][]string{}
212 mut checked_iface_impls := map[string]bool{}
213 mut processed_nodes := []bool{len: a.nodes.len}
214 mut calls := []string{cap: 128}
215 mut qi := 0
216 for qi < queue.len {
217 name := queue[qi]
218 qi++
219 prev_len := queue.len
220 fn_infos := fn_decl_infos_for_queue_name(name, fn_decl_lists, a)
221 if fn_infos.len == 0 {
222 not_in_cg++
223 if trace_markused && qi <= 10 {
224 eprintln('BFS qi=${qi.str()} name="${name}" in_cg=false')
225 }
226 continue
227 }
228 for fn_info in fn_infos {
229 if trace_markused && qi <= 10 {
230 eprintln('BFS qi=${qi.str()} name="${name}" in_cg=true')
231 }
232 in_cg++
233 node_key := int(fn_info.node_id)
234 if node_key < 0 || node_key >= processed_nodes.len {
235 continue
236 }
237 if processed_nodes[node_key] {
238 continue
239 }
240 processed_nodes[node_key] = true
241 // This function is reachable, so any methods it uses as *values* (recorded by
242 // the checker per enclosing function) are reachable too -- mark them so they
243 // survive pruning (cgen emits a wrapper that calls them).
244 if mvs := tc.method_values_by_fn[node_key] {
245 for mkey in mvs {
246 enqueue(mkey, mut used, mut queue)
247 lowered_mv := markused_c_name(mkey)
248 if lowered_mv != mkey {
249 enqueue(lowered_mv, mut used, mut queue)
250 }
251 mv_short := mkey.all_after_last('.')
252 if cands := suffix_map[mv_short] {
253 for cand in cands {
254 if cand == mkey || cand.ends_with('.${mkey}') {
255 enqueue(cand, mut used, mut queue)
256 }
257 }
258 }
259 }
260 }
261 calls.clear()
262 node := a.node(fn_info.node_id)
263 receiver_name, receiver_struct := receiver_info(a, node)
264 collector.collect_calls(node, fn_info.module, imports, receiver_name, receiver_struct, mut
265 calls)
266 total_callees += calls.len
267 for callee in calls {
268 if !valid_symbol_name(callee) {
269 continue
270 }
271 mut found_direct := false
272 if callee_info := fn_decls[callee] {
273 found_direct = true
274 if enqueue(callee, mut used, mut queue) {
275 if trace_markused && qi == 1 {
276 eprintln('main: all_fns hit: "${callee}"')
277 }
278 }
279 add_safe_decl_alias(callee, callee_info, a, mut used)
280 } else if callee in tc.fn_ret_types {
281 found_direct = true
282 if enqueue(callee, mut used, mut queue) {
283 if trace_markused && qi == 1 {
284 eprintln('main: resolved hit: "${callee}"')
285 }
286 }
287 }
288 if !found_direct || !callee.contains('.') {
289 short := callee.all_after_last('.')
290 if suffix_candidates := suffix_map[short] {
291 for candidate in suffix_candidates {
292 if candidate in fn_decls || candidate in tc.fn_ret_types {
293 if enqueue(candidate, mut used, mut queue) {
294 suffix_hits++
295 }
296 }
297 }
298 }
299 }
300 if callee.contains('.') {
301 recv := callee.all_before_last('.')
302 method := callee.all_after_last('.')
303 ensure_iface_impls(recv, fn_info.module, tc, mut iface_impls, mut
304 checked_iface_impls)
305 if impls := iface_impls[recv] {
306 for impl in impls {
307 impl_method := '${impl}.${method}'
308 enqueue(impl_method, mut used, mut queue)
309 short_impl := '${impl.all_after_last('.')}.${method}'
310 if short_impl != impl_method {
311 enqueue(short_impl, mut used, mut queue)
312 }
313 }
314 }
315 }
316 }
317 }
318 new_added := queue.len - prev_len
319 if trace_markused && qi <= 10 {
320 eprintln(' -> added ${new_added.str()} new entries, queue now ${queue.len.str()}')
321 }
322 }
323 if trace_markused {
324 eprintln('total_callees:')
325 eprintln(total_callees.str())
326 eprintln('markused: in_cg:')
327 eprintln(in_cg.str())
328 eprintln('not_in_cg:')
329 eprintln(not_in_cg.str())
330 eprintln('queue.len:')
331 eprintln(queue.len.str())
332 eprintln('markused: suffix_hits:')
333 eprintln(suffix_hits.str())
334 eprintln('markused: total used: ${used.len}')
335 }
336 return used
337}
338
339// ensure_iface_impls supports ensure iface impls handling for markused.
340fn ensure_iface_impls(recv string, cur_module string, tc &types.TypeChecker, mut iface_impls map[string][]string, mut checked map[string]bool) {
341 if recv.len == 0 {
342 return
343 }
344 iface_name := interface_name_for_receiver(recv, cur_module, tc) or { return }
345 if iface_name in checked {
346 return
347 }
348 checked[iface_name] = true
349 if recv != iface_name {
350 checked[recv] = true
351 }
352 mut impls := []string{}
353 for struct_name, _ in tc.structs {
354 if tc.named_type_implements_interface(struct_name, iface_name) {
355 impls << struct_name
356 }
357 }
358 iface_impls[recv] = impls
359 if iface_name != recv {
360 iface_impls[iface_name] = impls
361 checked[iface_name] = true
362 }
363}
364
365fn interface_name_for_receiver(recv string, cur_module string, tc &types.TypeChecker) ?string {
366 if recv in tc.interface_names {
367 return recv
368 }
369 if recv.contains('.') {
370 return none
371 }
372 if cur_module.len > 0 && cur_module != 'main' && cur_module != 'builtin' {
373 qname := '${cur_module}.${recv}'
374 if qname in tc.interface_names {
375 return qname
376 }
377 }
378 return none
379}
380
381// add_suffix_candidate updates add suffix candidate state for markused.
382fn add_suffix_candidate(mut suffix_map map[string][]string, short string, name string) {
383 if !valid_symbol_name(short) || !valid_symbol_name(name) {
384 return
385 }
386 mut candidates := suffix_map[short] or { []string{} }
387 candidates << name
388 suffix_map[short] = candidates
389}
390
391fn add_fn_decl_info(mut fn_decls map[string]FnDeclInfo, mut fn_decl_lists map[string][]FnDeclInfo, name string, info FnDeclInfo) {
392 fn_decls[name] = info
393 mut infos := fn_decl_lists[name] or { []FnDeclInfo{} }
394 infos << info
395 fn_decl_lists[name] = infos
396}
397
398fn fn_decl_infos_for_queue_name(name string, fn_decl_lists map[string][]FnDeclInfo, a &flat.FlatAst) []FnDeclInfo {
399 infos := fn_decl_lists[name] or { return []FnDeclInfo{} }
400 if infos.len <= 1 {
401 return infos
402 }
403 mut exact_infos := []FnDeclInfo{}
404 for info in infos {
405 node := a.node(info.node_id)
406 if fn_decl_key_is_exact_for_info(name, node.value, info.module) {
407 exact_infos << info
408 }
409 }
410 return exact_infos
411}
412
413fn fn_decl_key_is_exact_for_info(name string, decl_name string, module_name string) bool {
414 qname := qualify_fn(module_name, decl_name)
415 if name == qname {
416 return true
417 }
418 lowered := markused_c_name(qname)
419 return lowered != qname && name == lowered
420}
421
422fn add_safe_decl_alias(callee string, callee_info FnDeclInfo, a &flat.FlatAst, mut used map[string]bool) {
423 alias := a.node(callee_info.node_id).value
424 if alias == callee || alias in used {
425 return
426 }
427 if fn_decl_key_is_exact_for_info(callee, alias, callee_info.module) {
428 return
429 }
430 used[alias] = true
431}
432
433// valid_symbol_name supports valid symbol name handling for markused.
434fn valid_symbol_name(name string) bool {
435 return name.len > 0 && name.len < 512
436}
437
438// markused_clone_bool_map returns a value clone even when the source is passed
439// from a `mut map` parameter. v3 self-host cgen otherwise infers a `map*` local
440// for `param.clone()` in a few recursive markused scanners.
441fn markused_clone_bool_map(src map[string]bool) map[string]bool {
442 return src.clone()
443}
444
445fn markused_clone_string_map(src map[string]string) map[string]string {
446 return src.clone()
447}
448
449// enqueue_initializer_calls supports enqueue initializer calls handling for markused.
450fn enqueue_initializer_calls(a &flat.FlatAst, collector CallCollector, imports map[string]string, fn_decls map[string]FnDeclInfo, mut used map[string]bool, mut queue []string) {
451 mut cur_module := ''
452 mut calls := []string{cap: 32}
453 for node in a.nodes {
454 match node.kind {
455 .module_decl {
456 cur_module = node.value
457 }
458 .const_decl, .global_decl {
459 for i in 0 .. node.children_count {
460 field := a.child_node(&node, i)
461 if field.children_count == 0 {
462 continue
463 }
464 calls.clear()
465 collector.collect_calls(field, cur_module, imports, '', '', mut calls)
466 for callee in calls {
467 if callee_info := fn_decls[callee] {
468 enqueue(callee, mut used, mut queue)
469 add_safe_decl_alias(callee, callee_info, a, mut used)
470 } else {
471 enqueue(callee, mut used, mut queue)
472 }
473 }
474 }
475 }
476 else {}
477 }
478 }
479}
480
481fn enqueue_top_level_calls(a &flat.FlatAst, collector CallCollector, fn_decls map[string]FnDeclInfo, mut used map[string]bool, mut queue []string) {
482 if markused_has_entry_main(a) {
483 return
484 }
485 mut calls := []string{cap: 32}
486 for file_idx, file_node in a.nodes {
487 if !markused_should_scan_top_level_file(a, file_idx, file_node) {
488 continue
489 }
490 module_name := markused_top_level_file_module_name(a, file_node)
491 file_imports := markused_top_level_file_imports(a, file_node)
492 mut local_values := map[string]bool{}
493 mut local_types := map[string]string{}
494 for i in 0 .. file_node.children_count {
495 child_id := a.child(&file_node, i)
496 if int(child_id) < a.user_code_start {
497 continue
498 }
499 child := a.node(child_id)
500 if !markused_is_top_level_stmt(child) {
501 continue
502 }
503 calls.clear()
504 collector.collect_top_level_stmt_calls(child_id, module_name, file_imports, mut
505 local_values, mut local_types, mut calls)
506 for callee in calls {
507 if callee_info := fn_decls[callee] {
508 enqueue(callee, mut used, mut queue)
509 add_safe_decl_alias(callee, callee_info, a, mut used)
510 } else {
511 enqueue(callee, mut used, mut queue)
512 }
513 }
514 }
515 }
516}
517
518fn markused_has_entry_main(a &flat.FlatAst) bool {
519 mut cur_module := ''
520 for node in a.nodes {
521 match node.kind {
522 .file {
523 cur_module = ''
524 }
525 .module_decl {
526 cur_module = node.value
527 }
528 .fn_decl {
529 if node.value == 'main' && (cur_module.len == 0 || cur_module == 'main') {
530 return true
531 }
532 }
533 else {}
534 }
535 }
536 return false
537}
538
539fn markused_should_scan_top_level_file(a &flat.FlatAst, file_idx int, file_node flat.Node) bool {
540 if file_idx < a.user_code_start || file_node.kind != .file || file_node.children_count == 0 {
541 return false
542 }
543 module_name := markused_top_level_file_module_name(a, file_node)
544 return module_name.len == 0 || module_name == 'main'
545}
546
547fn markused_top_level_file_module_name(a &flat.FlatAst, file_node flat.Node) string {
548 for i in 0 .. file_node.children_count {
549 child := a.child_node(&file_node, i)
550 if child.kind == .module_decl {
551 return child.value
552 }
553 }
554 return ''
555}
556
557fn markused_top_level_file_imports(a &flat.FlatAst, file_node flat.Node) map[string]string {
558 mut imports := map[string]string{}
559 for i in 0 .. file_node.children_count {
560 child := a.child_node(&file_node, i)
561 if child.kind == .import_decl {
562 imports[child.typ] = child.value
563 }
564 }
565 return imports
566}
567
568fn markused_is_top_level_stmt(node flat.Node) bool {
569 return match node.kind {
570 .expr_stmt, .assign, .decl_assign, .global_decl, .selector_assign, .index_assign,
571 .for_stmt, .for_in_stmt, .if_expr, .match_stmt, .assert_stmt, .defer_stmt, .block {
572 true
573 }
574 else {
575 false
576 }
577 }
578}
579
580// enqueue supports enqueue handling for markused.
581fn enqueue(name string, mut used map[string]bool, mut queue []string) bool {
582 if !valid_symbol_name(name) {
583 return false
584 }
585 if name in used {
586 return false
587 }
588 used[name] = true
589 queue << name
590 return true
591}
592
593// FnDeclInfo stores fn decl info metadata used by markused.
594struct FnDeclInfo {
595 node_id flat.NodeId
596 module string
597}
598
599// StructDeclInfo stores struct decl info metadata used by markused.
600struct StructDeclInfo {
601 node_id flat.NodeId
602 module string
603}
604
605// ConstDeclInfo stores const decl info metadata used by markused.
606struct ConstDeclInfo {
607 expr_id flat.NodeId
608 module string
609}
610
611// CallCollector represents call collector data used by markused.
612struct CallCollector {
613 a &flat.FlatAst = unsafe { nil }
614 tc &types.TypeChecker = unsafe { nil }
615 fn_decls map[string]FnDeclInfo
616 struct_decls map[string]StructDeclInfo
617 const_decls map[string]ConstDeclInfo
618}
619
620// enqueue_auto_roots supports enqueue auto roots handling for markused.
621fn enqueue_auto_roots(fn_decls map[string]FnDeclInfo, mut used map[string]bool, mut queue []string) {
622 for name, info in fn_decls {
623 if !is_auto_root_fn(name) {
624 continue
625 }
626 enqueue(name, mut used, mut queue)
627 short_name := name.all_after_last('.')
628 if short_name != name {
629 enqueue(short_name, mut used, mut queue)
630 }
631 qualified_name := qualify_fn(info.module, short_name)
632 if qualified_name != name {
633 enqueue(qualified_name, mut used, mut queue)
634 }
635 }
636}
637
638fn enqueue_export_roots(a &flat.FlatAst, mut used map[string]bool, mut queue []string) {
639 if a.export_fn_names.len == 0 {
640 return
641 }
642 mut cur_module := ''
643 for node in a.nodes {
644 match node.kind {
645 .file {
646 cur_module = ''
647 }
648 .module_decl {
649 cur_module = node.value
650 }
651 .fn_decl {
652 qname := qualify_fn(cur_module, node.value)
653 if qname in a.export_fn_names {
654 enqueue(qname, mut used, mut queue)
655 }
656 }
657 else {}
658 }
659 }
660}
661
662fn enqueue_veb_handler_roots(a &flat.FlatAst, tc &types.TypeChecker, mut used map[string]bool, mut queue []string) {
663 mut cur_module := ''
664 for node in a.nodes {
665 match node.kind {
666 .file {
667 cur_module = ''
668 }
669 .module_decl {
670 cur_module = node.value
671 }
672 .fn_decl {
673 if markused_fn_needs_implicit_veb_ctx(a, tc, cur_module, node) {
674 enqueue(qualify_fn(cur_module, node.value), mut used, mut queue)
675 }
676 }
677 else {}
678 }
679 }
680}
681
682fn markused_fn_needs_implicit_veb_ctx(a &flat.FlatAst, tc &types.TypeChecker, cur_module string, node flat.Node) bool {
683 return markused_fn_returns_veb_result(tc, node) && markused_fn_has_receiver_param(a, node)
684 && !markused_fn_receiver_type_is_context(a, node) && !markused_fn_has_param(a, node, 'ctx')
685 && markused_type_name_known_in_module(tc, cur_module, 'Context')
686}
687
688fn markused_fn_returns_veb_result(tc &types.TypeChecker, node flat.Node) bool {
689 if node.typ == 'veb.Result' {
690 return true
691 }
692 ret := tc.parse_type(node.typ)
693 return ret.name() == 'veb.Result'
694}
695
696fn markused_fn_has_receiver_param(a &flat.FlatAst, node flat.Node) bool {
697 if !node.value.contains('.') || node.children_count == 0 {
698 return false
699 }
700 first := a.child_node(&node, 0)
701 if first.kind != .param || first.typ.len == 0 {
702 return false
703 }
704 receiver := node.value.all_before_last('.').all_after_last('.')
705 param_type := first.typ.trim_left('&').all_after_last('.')
706 return receiver == param_type
707}
708
709fn markused_fn_receiver_type_is_context(a &flat.FlatAst, node flat.Node) bool {
710 if !markused_fn_has_receiver_param(a, node) {
711 return false
712 }
713 first := a.child_node(&node, 0)
714 return first.typ.trim_left('&').all_after_last('.') == 'Context'
715}
716
717fn markused_fn_has_param(a &flat.FlatAst, node flat.Node, name string) bool {
718 for i in 0 .. node.children_count {
719 param := a.child_node(&node, i)
720 if param.kind == .param && param.value == name {
721 return true
722 }
723 }
724 return false
725}
726
727fn markused_type_name_known_in_module(tc &types.TypeChecker, module_name string, typ string) bool {
728 qtyp := qualify_fn(module_name, typ)
729 return qtyp in tc.type_aliases || qtyp in tc.structs || qtyp in tc.interface_names
730 || qtyp in tc.enum_names || qtyp in tc.sum_types
731}
732
733fn enqueue_test_file_roots(a &flat.FlatAst, test_files map[string]bool, mut used map[string]bool, mut queue []string) {
734 if test_files.len == 0 {
735 return
736 }
737 for file_idx, file_node in a.nodes {
738 if !is_user_test_file_node(a, file_idx, file_node, test_files) {
739 continue
740 }
741 module_name := test_file_module_name(a, file_node)
742 if module_name.len > 0 && module_name != 'main' {
743 continue
744 }
745 mut decl_ids := []flat.NodeId{}
746 markused_collect_test_harness_decl_ids(a, file_node, mut decl_ids)
747 for child_id in decl_ids {
748 child := a.node(child_id)
749 if is_test_harness_root_name(child.value) {
750 enqueue(qualify_fn(module_name, child.value), mut used, mut queue)
751 }
752 }
753 }
754}
755
756fn markused_collect_test_harness_decl_ids(a &flat.FlatAst, node flat.Node, mut ids []flat.NodeId) {
757 if node.kind != .file && node.kind != .block {
758 return
759 }
760 for i in 0 .. node.children_count {
761 child_id := a.child(&node, i)
762 if int(child_id) < a.user_code_start {
763 continue
764 }
765 child := a.node(child_id)
766 if child.kind == .fn_decl {
767 ids << child_id
768 } else if child.kind == .block {
769 markused_collect_test_harness_decl_ids(a, child, mut ids)
770 }
771 }
772}
773
774fn is_user_test_file_node(a &flat.FlatAst, file_idx int, file_node flat.Node, test_files map[string]bool) bool {
775 if file_idx < a.user_code_start || file_node.kind != .file || file_node.children_count == 0 {
776 return false
777 }
778 return test_files[file_node.value]
779}
780
781fn test_file_module_name(a &flat.FlatAst, file_node flat.Node) string {
782 for i in 0 .. file_node.children_count {
783 child := a.child_node(&file_node, i)
784 if child.kind == .module_decl {
785 return child.value
786 }
787 }
788 return ''
789}
790
791fn is_test_harness_root_name(name string) bool {
792 return name.starts_with('test_')
793 || name in ['testsuite_begin', 'testsuite_end', 'before_each', 'after_each']
794}
795
796// enqueue_main_module_roots supports enqueue main module roots handling for markused.
797fn enqueue_main_module_roots(fn_decls map[string]FnDeclInfo, mut used map[string]bool, mut queue []string) {
798 for name, info in fn_decls {
799 if info.module != 'main' || name != 'main' {
800 continue
801 }
802 enqueue(name, mut used, mut queue)
803 }
804}
805
806// is_auto_root_fn reports whether is auto root fn applies in markused.
807fn is_auto_root_fn(name string) bool {
808 short_name := name.all_after_last('.')
809 return short_name == 'init'
810}
811
812// enqueue_detected_runtime_helpers supports enqueue detected runtime helpers handling for markused.
813fn enqueue_detected_runtime_helpers(a &flat.FlatAst, tc &types.TypeChecker, mut used map[string]bool, mut queue []string) {
814 mut needs_optional_helpers := false
815 mut needs_string_interp_helpers := false
816 mut needs_string_plus_helper := false
817 mut needs_string_membership_helpers := false
818 mut needs_new_map := false
819 mut cur_module := ''
820 mut imports := map[string]string{}
821 for node in a.nodes {
822 match node.kind {
823 .file {
824 cur_module = ''
825 imports = map[string]string{}
826 }
827 .module_decl {
828 cur_module = node.value
829 }
830 .import_decl {
831 imports[node.typ] = node.value
832 }
833 .fn_decl {
834 ret_type := tc.parse_type(node.typ)
835 if ret_type is types.OptionType || ret_type is types.ResultType {
836 needs_optional_helpers = true
837 }
838 }
839 .param, .field_decl, .field_init, .const_field {
840 if type_string_needs_optional_helpers(node.typ) {
841 needs_optional_helpers = true
842 }
843 }
844 .none_expr {
845 needs_optional_helpers = true
846 }
847 .or_expr {
848 needs_optional_helpers = true
849 if node.children_count > 0 {
850 expr_id := a.child(&node, 0)
851 expr_type := tc.expr_type(expr_id) or { tc.resolve_type(expr_id) }
852 if type_needs_zero_map(expr_type) {
853 needs_new_map = true
854 }
855 }
856 }
857 .call {
858 if node.children_count > 0 {
859 fn_node := a.child_node(&node, 0)
860 if fn_node.kind == .ident
861 && (fn_node.value == 'error' || fn_node.value == 'error_with_code') {
862 needs_optional_helpers = true
863 }
864 if fn_node.kind == .ident && fn_node.value == 'flag_default_value' {
865 enqueue('escape_default_string', mut used, mut queue)
866 }
867 if fn_node.kind == .selector
868 && fn_node.value in ['trim_space', 'trim_space_left', 'trim_space_right', 'to_upper', 'to_upper_ascii', 'to_lower', 'to_lower_ascii'] {
869 enqueue('string.${fn_node.value}', mut used, mut queue)
870 }
871 if markused_call_lowers_to_join_path_single(a, fn_node, imports) {
872 enqueue('join_path_single', mut used, mut queue)
873 enqueue('os.join_path_single', mut used, mut queue)
874 }
875 if fn_node.kind == .ident
876 && fn_node.value in ['print', 'println', 'eprint', 'eprintln']
877 && node.children_count >= 2 {
878 enqueue_stringified_custom_str_method(a.child(&node, 1), cur_module, tc, mut
879 used, mut queue)
880 }
881 }
882 }
883 .string_interp {
884 needs_string_interp_helpers = true
885 needs_string_plus_helper = true
886 for i in 0 .. node.children_count {
887 enqueue_stringified_custom_str_method(a.child(&node, i), cur_module, tc, mut
888 used, mut queue)
889 }
890 }
891 .assign {
892 if node.op == .plus_assign && node.children_count == 2 {
893 lhs_id := a.child(&node, 0)
894 rhs_id := a.child(&node, 1)
895 rhs := a.node(rhs_id)
896 lhs_type := markused_membership_container_type(tc, tc.resolve_type(lhs_id))
897 rhs_type := markused_membership_container_type(tc, tc.resolve_type(rhs_id))
898 if lhs_type == 'string' || rhs_type == 'string'
899 || rhs.kind in [.string_literal, .string_interp] {
900 needs_string_plus_helper = true
901 }
902 }
903 }
904 .in_expr {
905 if node.children_count >= 2 {
906 rhs_id := a.child(&node, 1)
907 rhs_type := markused_membership_container_type(tc, tc.resolve_type(rhs_id))
908 if rhs_type == 'string' {
909 needs_string_membership_helpers = true
910 }
911 }
912 }
913 .map_init {
914 needs_new_map = true
915 }
916 .enum_decl {
917 // A `[flag]` enum's synthesized `<Enum>__autostr` helper (emitted
918 // unconditionally in cgen) builds its `Enum{.a | .b}` string with
919 // `string__plus`. markused never walks that generated body, so without
920 // seeding the helper here it can be pruned when the program has no other
921 // string concatenation, leaving the autostr calling an undefined
922 // `string__plus`.
923 if node.typ == 'flag' {
924 needs_string_plus_helper = true
925 }
926 }
927 else {}
928 }
929 }
930 if needs_optional_helpers {
931 for helper in ['IError.str', 'error', 'error_with_code'] {
932 enqueue(helper, mut used, mut queue)
933 }
934 }
935 if needs_string_interp_helpers {
936 for helper in ['strings.new_builder', 'strings.Builder.write_string', 'strings.Builder.str',
937 'string_plus_many'] {
938 enqueue(helper, mut used, mut queue)
939 }
940 }
941 if needs_string_plus_helper {
942 enqueue('string__plus', mut used, mut queue)
943 }
944 if needs_string_membership_helpers {
945 for helper in ['string__contains', 'string__contains_u8'] {
946 enqueue(helper, mut used, mut queue)
947 }
948 }
949 if needs_new_map {
950 enqueue('new_map', mut used, mut queue)
951 }
952}
953
954fn markused_call_lowers_to_join_path_single(a &flat.FlatAst, fn_node flat.Node, imports map[string]string) bool {
955 if fn_node.kind == .ident {
956 return fn_node.value == 'join_path'
957 }
958 if fn_node.kind != .selector || fn_node.value != 'join_path' || fn_node.children_count == 0 {
959 return false
960 }
961 base_id := a.child(&fn_node, 0)
962 if int(base_id) < 0 {
963 return false
964 }
965 base := a.node(base_id)
966 if base.kind != .ident {
967 return false
968 }
969 return base.value == 'os' || imports[base.value] == 'os'
970}
971
972// enqueue_stringified_custom_str_method supports enqueue_stringified_custom_str_method handling.
973fn enqueue_stringified_custom_str_method(expr_id flat.NodeId, cur_module string, tc &types.TypeChecker, mut used map[string]bool, mut queue []string) {
974 mut typ := tc.expr_type(expr_id) or { tc.resolve_type(expr_id) }
975 for _ in 0 .. 8 {
976 if typ is types.Alias {
977 typ = typ.base_type
978 continue
979 }
980 if typ is types.OptionType {
981 typ = typ.base_type
982 continue
983 }
984 if typ is types.ResultType {
985 typ = typ.base_type
986 continue
987 }
988 break
989 }
990 match typ {
991 types.Enum {
992 enqueue_enum_str_method(typ.name, cur_module, tc, mut used, mut queue)
993 }
994 types.Struct {
995 enqueue_structlike_str_method(typ.name, cur_module, tc, mut used, mut queue)
996 }
997 types.SumType {
998 enqueue_structlike_str_method(typ.name, cur_module, tc, mut used, mut queue)
999 }
1000 else {}
1001 }
1002}
1003
1004// enqueue_enum_str_method supports enqueue enum str method handling for markused.
1005fn enqueue_enum_str_method(type_name string, cur_module string, tc &types.TypeChecker, mut used map[string]bool, mut queue []string) {
1006 for candidate in stringification_type_candidates(type_name, cur_module) {
1007 method := '${candidate}.str'
1008 if method in tc.fn_ret_types {
1009 enqueue(method, mut used, mut queue)
1010 lowered := markused_c_name(method)
1011 if lowered != method {
1012 enqueue(lowered, mut used, mut queue)
1013 }
1014 }
1015 }
1016}
1017
1018// enqueue_structlike_str_method supports enqueue structlike str method handling for markused.
1019fn enqueue_structlike_str_method(type_name string, cur_module string, tc &types.TypeChecker, mut used map[string]bool, mut queue []string) {
1020 for candidate in stringification_type_candidates(type_name, cur_module) {
1021 lowered := '${markused_c_name(candidate)}__str'
1022 if lowered in tc.fn_ret_types {
1023 enqueue(lowered, mut used, mut queue)
1024 }
1025 method := '${candidate}.str'
1026 if method in tc.fn_ret_types {
1027 enqueue(method, mut used, mut queue)
1028 }
1029 }
1030}
1031
1032// stringification_type_candidates supports stringification type candidates handling for markused.
1033fn stringification_type_candidates(type_name string, cur_module string) []string {
1034 if type_name.len == 0 {
1035 return []string{}
1036 }
1037 mut candidates := []string{cap: 2}
1038 candidates << type_name
1039 if !type_name.contains('.') && cur_module.len > 0 && cur_module != 'main'
1040 && cur_module != 'builtin' {
1041 candidates << '${cur_module}.${type_name}'
1042 }
1043 return candidates
1044}
1045
1046// enqueue_function_value_selectors supports enqueue function value selectors handling for markused.
1047fn enqueue_function_value_selectors(a &flat.FlatAst, collector CallCollector, fn_decls map[string]FnDeclInfo, mut used map[string]bool, mut queue []string) {
1048 ignored_top_level_nodes := markused_ignored_top_level_nodes(a)
1049 ignored_fn_decl_nodes := markused_ignored_fn_decl_nodes(a)
1050 shadowed_value_idents := markused_shadowed_value_idents(collector)
1051 for node_idx, node in a.nodes {
1052 if node_idx < ignored_fn_decl_nodes.len && ignored_fn_decl_nodes[node_idx] {
1053 continue
1054 }
1055 if node_idx < ignored_top_level_nodes.len && ignored_top_level_nodes[node_idx] {
1056 continue
1057 }
1058 if node.kind == .ident && node.value.len > 0 {
1059 node_id := flat.NodeId(node_idx)
1060 is_shadowed := node_idx < shadowed_value_idents.len && shadowed_value_idents[node_idx]
1061 if is_shadowed {
1062 continue
1063 }
1064 if resolved := collector.tc.resolved_fn_value_name(node_id) {
1065 enqueue(resolved, mut used, mut queue)
1066 continue
1067 }
1068 if node.value in fn_decls && collector.node_is_fn_value(node_id) {
1069 enqueue(node.value, mut used, mut queue)
1070 }
1071 continue
1072 }
1073 if node.kind == .selector && node.children_count > 0 && node.value.len > 0 {
1074 base_id := a.child(&node, 0)
1075 if int(base_id) >= 0 && int(base_id) < shadowed_value_idents.len
1076 && shadowed_value_idents[int(base_id)] {
1077 continue
1078 }
1079 base := a.node(base_id)
1080 if base.kind == .ident && base.value.len > 0 {
1081 name := '${base.value}.${node.value}'
1082 if name in fn_decls {
1083 enqueue(name, mut used, mut queue)
1084 }
1085 }
1086 }
1087 }
1088}
1089
1090fn markused_ignored_fn_decl_nodes(a &flat.FlatAst) []bool {
1091 mut ignored := []bool{len: a.nodes.len}
1092 for node_idx, node in a.nodes {
1093 if node.kind == .fn_decl {
1094 markused_mark_node_subtree(a, flat.NodeId(node_idx), mut ignored)
1095 }
1096 }
1097 return ignored
1098}
1099
1100fn markused_ignored_top_level_nodes(a &flat.FlatAst) []bool {
1101 if !markused_has_entry_main(a) {
1102 return []bool{}
1103 }
1104 mut ignored := []bool{len: a.nodes.len}
1105 for file_idx, file_node in a.nodes {
1106 if !markused_should_scan_top_level_file(a, file_idx, file_node) {
1107 continue
1108 }
1109 for i in 0 .. file_node.children_count {
1110 child_id := a.child(&file_node, i)
1111 if int(child_id) < a.user_code_start {
1112 continue
1113 }
1114 child := a.node(child_id)
1115 if !markused_is_top_level_stmt(child) {
1116 continue
1117 }
1118 markused_mark_node_subtree(a, child_id, mut ignored)
1119 }
1120 }
1121 return ignored
1122}
1123
1124fn markused_shadowed_value_idents(collector CallCollector) []bool {
1125 a := collector.a
1126 mut shadowed := []bool{len: a.nodes.len}
1127 for node in a.nodes {
1128 if node.kind != .fn_decl {
1129 continue
1130 }
1131 local_values := markused_local_value_names(a, &node)
1132 if local_values.len == 0 {
1133 continue
1134 }
1135 markused_mark_shadowed_idents(a, &node, local_values, mut shadowed)
1136 }
1137 for file_idx, file_node in a.nodes {
1138 if !markused_should_scan_top_level_file(a, file_idx, file_node) {
1139 continue
1140 }
1141 mut local_values := map[string]bool{}
1142 for i in 0 .. file_node.children_count {
1143 child_id := a.child(&file_node, i)
1144 if int(child_id) < a.user_code_start {
1145 continue
1146 }
1147 child := a.node(child_id)
1148 if !markused_is_top_level_stmt(child) {
1149 continue
1150 }
1151 if local_values.len > 0 {
1152 markused_mark_shadowed_idents(a, child, local_values, mut shadowed)
1153 }
1154 markused_mark_top_level_lhs_idents(a, child, mut shadowed)
1155 markused_add_top_level_lhs_names(a, child, mut local_values)
1156 }
1157 }
1158 return shadowed
1159}
1160
1161fn markused_mark_shadowed_idents(a &flat.FlatAst, node &flat.Node, local_values map[string]bool, mut shadowed []bool) {
1162 if local_values.len == 0 {
1163 return
1164 }
1165 mut stack := []flat.NodeId{cap: int(node.children_count)}
1166 for i in 0 .. node.children_count {
1167 child_id := a.child(node, i)
1168 if int(child_id) >= 0 {
1169 stack << child_id
1170 }
1171 }
1172 for stack.len > 0 {
1173 id := stack.pop()
1174 idx := int(id)
1175 if idx < 0 || idx >= shadowed.len {
1176 continue
1177 }
1178 child := a.node(id)
1179 if child.kind == .ident && child.value in local_values {
1180 shadowed[idx] = true
1181 }
1182 for i in 0 .. child.children_count {
1183 next_id := a.child(child, i)
1184 if int(next_id) >= 0 {
1185 stack << next_id
1186 }
1187 }
1188 }
1189}
1190
1191fn markused_mark_top_level_lhs_idents(a &flat.FlatAst, node &flat.Node, mut shadowed []bool) {
1192 if node.kind != .decl_assign {
1193 return
1194 }
1195 mut i := 0
1196 for i + 1 < node.children_count {
1197 lhs_id := a.child(node, i)
1198 idx := int(lhs_id)
1199 if idx >= 0 && idx < shadowed.len {
1200 lhs := a.node(lhs_id)
1201 if lhs.kind == .ident {
1202 shadowed[idx] = true
1203 }
1204 }
1205 i += 2
1206 }
1207}
1208
1209fn markused_add_top_level_lhs_names(a &flat.FlatAst, node &flat.Node, mut local_values map[string]bool) {
1210 match node.kind {
1211 .decl_assign, .assign {
1212 mut i := 0
1213 for i + 1 < node.children_count {
1214 lhs_id := a.child(node, i)
1215 if int(lhs_id) >= 0 {
1216 lhs := a.node(lhs_id)
1217 if lhs.kind == .ident && lhs.value.len > 0 {
1218 local_values[lhs.value] = true
1219 }
1220 }
1221 i += 2
1222 }
1223 }
1224 .global_decl {
1225 for i in 0 .. node.children_count {
1226 field := a.child_node(node, i)
1227 if field.value.len > 0 {
1228 local_values[field.value] = true
1229 }
1230 }
1231 }
1232 else {}
1233 }
1234}
1235
1236fn markused_mark_node_subtree(a &flat.FlatAst, root flat.NodeId, mut marked []bool) {
1237 mut stack := [root]
1238 for stack.len > 0 {
1239 id := stack.pop()
1240 idx := int(id)
1241 if idx < 0 || idx >= marked.len || marked[idx] {
1242 continue
1243 }
1244 marked[idx] = true
1245 node := a.node(id)
1246 for i in 0 .. node.children_count {
1247 child_id := a.child(node, i)
1248 if int(child_id) >= 0 {
1249 stack << child_id
1250 }
1251 }
1252 }
1253}
1254
1255// type_string_needs_optional_helpers returns type string needs optional helpers data for markused.
1256fn type_string_needs_optional_helpers(typ string) bool {
1257 return typ.len > 0 && (typ[0] == `?` || typ[0] == `!`)
1258}
1259
1260// type_needs_zero_map returns type needs zero map data for markused.
1261fn type_needs_zero_map(typ types.Type) bool {
1262 mut clean := typ
1263 for _ in 0 .. 8 {
1264 if clean is types.Alias {
1265 clean = clean.base_type
1266 continue
1267 }
1268 if clean is types.OptionType {
1269 clean = clean.base_type
1270 continue
1271 }
1272 if clean is types.ResultType {
1273 clean = clean.base_type
1274 continue
1275 }
1276 break
1277 }
1278 return clean is types.Map
1279}
1280
1281// markused_membership_container_type
1282// supports helper handling in markused.
1283fn markused_membership_container_type(tc &types.TypeChecker, typ types.Type) string {
1284 mut clean := typ.name().trim_space()
1285 for {
1286 if clean.starts_with('shared ') {
1287 clean = clean[7..].trim_space()
1288 continue
1289 }
1290 if clean.starts_with('&') {
1291 clean = clean[1..].trim_space()
1292 continue
1293 }
1294 if clean in tc.type_aliases {
1295 alias := tc.type_aliases[clean].trim_space()
1296 if alias == clean {
1297 break
1298 }
1299 clean = alias
1300 continue
1301 }
1302 break
1303 }
1304 return clean
1305}
1306
1307// qualify_fn supports qualify fn handling for markused.
1308fn qualify_fn(mod string, name string) string {
1309 if mod.len == 0 || mod == 'main' || mod == 'builtin' {
1310 return name
1311 }
1312 return '${mod}.${name}'
1313}
1314
1315// receiver_info supports receiver info handling for markused.
1316fn receiver_info(a &flat.FlatAst, node &flat.Node) (string, string) {
1317 mut receiver_struct := ''
1318 if node.value.contains('.') {
1319 receiver_struct = node.value.all_before_last('.')
1320 }
1321 for pi in 0 .. node.children_count {
1322 pc := a.child_node(node, pi)
1323 if pc.kind == .param {
1324 if receiver_struct.len > 0 {
1325 return pc.value, receiver_struct
1326 }
1327 clean_type := pc.typ.trim_left('&')
1328 if pc.value.len > 0 && clean_type.len > 0 && clean_type[0] >= `A`
1329 && clean_type[0] <= `Z` {
1330 return pc.value, clean_type
1331 }
1332 }
1333 }
1334 if receiver_struct.len == 0 {
1335 return '', ''
1336 }
1337 return '', receiver_struct
1338}
1339
1340// collect_calls updates collect calls state for markused.
1341fn (c &CallCollector) collect_calls(node &flat.Node, cur_module string, imports map[string]string, receiver_name string, receiver_struct string, mut calls []string) {
1342 local_values := c.local_value_names(node)
1343 local_types := c.local_value_type_names(node, cur_module, imports)
1344 visible_local_idents := markused_visible_local_idents(c.a, node, local_values)
1345 mut stack := []flat.NodeId{cap: int(node.children_count)}
1346 for i in 0 .. node.children_count {
1347 child_id := c.a.child(node, i)
1348 if int(child_id) >= 0 {
1349 stack << child_id
1350 }
1351 }
1352 for stack.len > 0 {
1353 child_id := stack.pop()
1354 child := &c.a.nodes[int(child_id)]
1355 match child.kind {
1356 .ident {
1357 name_is_local := markused_ident_is_visible_local(child_id, child.value,
1358 local_values, visible_local_idents)
1359 c.collect_fn_value_ident(child_id, child.value, cur_module, imports, name_is_local, mut
1360 calls)
1361 }
1362 .selector {
1363 if !c.selector_base_is_local(child, local_values) {
1364 c.collect_fn_value_selector(child_id, child, cur_module, imports, mut calls)
1365 } else if resolved := c.tc.resolved_fn_value_name(child_id) {
1366 calls << resolved
1367 }
1368 }
1369 .call {
1370 mut resolved_call := ''
1371 if resolved := c.tc.resolved_call_name(child_id) {
1372 resolved_call = resolved
1373 }
1374 if child.children_count > 0 {
1375 callee_id := c.a.child(child, 0)
1376 if int(callee_id) >= 0 {
1377 callee := c.a.nodes[int(callee_id)]
1378 if callee.kind == .ident && callee.value.len > 0 {
1379 if resolved_call.len > 0 {
1380 calls << resolved_call
1381 }
1382 if callee.value !in local_values {
1383 calls << callee.value
1384 qcallee := qualify_fn(cur_module, callee.value)
1385 if qcallee != callee.value {
1386 calls << qcallee
1387 }
1388 }
1389 } else if callee.kind == .selector && callee.value.len > 0 {
1390 mut has_exact_selector_call := false
1391 if callee.children_count > 0 {
1392 base_id := c.a.child(&callee, 0)
1393 if int(base_id) >= 0 {
1394 base := c.a.nodes[int(base_id)]
1395 has_exact_selector_call = c.collect_typed_receiver_method(base_id,
1396 callee.value, cur_module, imports, local_values,
1397 local_types, mut calls)
1398 if !has_exact_selector_call && base.kind == .ident
1399 && base.value in imports && base.value !in local_values {
1400 calls << imports[base.value] + '.' + callee.value
1401 has_exact_selector_call = true
1402 }
1403 if !has_exact_selector_call && resolved_call.len > 0 {
1404 calls << resolved_call
1405 has_exact_selector_call = true
1406 }
1407 if !has_exact_selector_call && base.kind == .ident
1408 && base.value in local_values && !(receiver_name.len > 0
1409 && base.value == receiver_name) {
1410 has_exact_selector_call = true
1411 }
1412 if !has_exact_selector_call {
1413 if base.kind == .ident && base.value.len > 0 {
1414 if receiver_name.len > 0 && base.value == receiver_name {
1415 calls << receiver_struct + '.' + callee.value
1416 qrecv := qualify_fn(cur_module, receiver_struct +
1417 '.' + callee.value)
1418 if qrecv != receiver_struct + '.' + callee.value {
1419 calls << qrecv
1420 }
1421 }
1422 mod_name := if base.value in imports {
1423 imports[base.value]
1424 } else {
1425 base.value
1426 }
1427 calls << mod_name + '.' + callee.value
1428 calls << qualify_fn(cur_module, base.value + '.' +
1429 callee.value)
1430 if base.value.len > 0 && base.value[0] >= `A`
1431 && base.value[0] <= `Z` {
1432 named_type := c.tc.parse_type(base.value)
1433 named_type_name := resolve_type_name(named_type)
1434 if named_type_name.len > 0 {
1435 calls << named_type_name + '.' + callee.value
1436 }
1437 }
1438 } else if base.kind == .selector && base.children_count > 0 {
1439 inner_id := c.a.child(&base, 0)
1440 if int(inner_id) >= 0 {
1441 inner := c.a.nodes[int(inner_id)]
1442 if inner.kind == .ident && inner.value.len > 0 {
1443 mod_name := if inner.value in imports {
1444 imports[inner.value]
1445 } else {
1446 inner.value
1447 }
1448 calls << mod_name + '.' + base.value + '.' +
1449 callee.value
1450 }
1451 }
1452 }
1453 }
1454 }
1455 }
1456 if !has_exact_selector_call {
1457 calls << callee.value
1458 }
1459 } else if resolved_call.len > 0 {
1460 calls << resolved_call
1461 }
1462 }
1463 } else if resolved_call.len > 0 {
1464 calls << resolved_call
1465 }
1466 for ci in 1 .. child.children_count {
1467 arg_id := c.a.child(child, ci)
1468 if int(arg_id) >= 0 {
1469 arg := c.a.nodes[int(arg_id)]
1470 if arg.kind == .ident && arg.value.len > 0 {
1471 arg_is_local := markused_ident_is_visible_local(arg_id, arg.value,
1472 local_values, visible_local_idents)
1473 c.collect_fn_value_ident(arg_id, arg.value, cur_module, imports,
1474 arg_is_local, mut calls)
1475 }
1476 }
1477 }
1478 }
1479 .prefix {
1480 if child.op == .amp && child.children_count > 0 {
1481 inner_id := c.a.child(child, 0)
1482 if int(inner_id) >= 0 {
1483 inner := c.a.nodes[int(inner_id)]
1484 if inner.kind == .struct_init {
1485 calls << 'memdup'
1486 }
1487 }
1488 }
1489 }
1490 .string_interp {
1491 calls << 'string_plus_many'
1492 }
1493 .infix {
1494 if child.op == .plus {
1495 calls << 'string__plus'
1496 }
1497 if child.children_count >= 2 {
1498 lhs_id := c.a.child(child, 0)
1499 rhs_id := c.a.child(child, 1)
1500 if child.op in [.dot, .amp] {
1501 lhs_name := c.qualified_expr_name(lhs_id)
1502 if lhs_name.len > 0 {
1503 rhs := c.a.nodes[int(rhs_id)]
1504 if rhs.kind == .call && rhs.children_count > 0 {
1505 fn_node := c.a.child_node(&rhs, 0)
1506 if fn_node.kind == .ident && fn_node.value.len > 0 {
1507 mod_name := if lhs_name in imports {
1508 imports[lhs_name]
1509 } else {
1510 lhs_name
1511 }
1512 calls << mod_name + '.' + fn_node.value
1513 calls << qualify_fn(cur_module, lhs_name + '.' + fn_node.value)
1514 }
1515 }
1516 }
1517 }
1518 if int(lhs_id) >= 0 {
1519 c.collect_struct_operator_call(lhs_id, child.op, cur_module, mut calls)
1520 }
1521 }
1522 }
1523 .or_expr {
1524 if child.children_count > 0 {
1525 expr_id := c.a.child(child, 0)
1526 c.collect_zero_struct_default_calls(c.node_type(expr_id), cur_module, imports, mut
1527 calls)
1528 }
1529 }
1530 .struct_init {
1531 c.collect_struct_default_calls(child, cur_module, imports, mut calls)
1532 }
1533 else {}
1534 }
1535
1536 if child.children_count > 0 {
1537 mut j := int(child.children_count) - 1
1538 for j >= 0 {
1539 if child.kind == .decl_assign && j % 2 == 0 {
1540 j--
1541 continue
1542 }
1543 next_id := c.a.child(child, j)
1544 if int(next_id) >= 0 {
1545 stack << next_id
1546 }
1547 j--
1548 }
1549 }
1550 }
1551}
1552
1553fn (c &CallCollector) local_value_names(node &flat.Node) map[string]bool {
1554 return markused_local_value_names(c.a, node)
1555}
1556
1557fn (c &CallCollector) local_value_type_names(node &flat.Node, cur_module string, imports map[string]string) map[string]string {
1558 mut result := map[string]string{}
1559 mut stack := []flat.NodeId{cap: int(node.children_count)}
1560 for i in 0 .. node.children_count {
1561 child_id := c.a.child(node, i)
1562 if int(child_id) >= 0 {
1563 stack << child_id
1564 }
1565 }
1566 for stack.len > 0 {
1567 id := stack.pop()
1568 child := c.a.node(id)
1569 if child.kind == .param && child.value.len > 0 && child.typ.len > 0 {
1570 result[child.value] = markused_resolve_imported_type_name(child.typ, imports)
1571 } else if child.kind == .decl_assign {
1572 mut i := 0
1573 for i + 1 < child.children_count {
1574 lhs_id := c.a.child(child, i)
1575 rhs_id := c.a.child(child, i + 1)
1576 if int(lhs_id) >= 0 && int(rhs_id) >= 0 {
1577 lhs := c.a.node(lhs_id)
1578 if lhs.kind == .ident && lhs.value.len > 0 {
1579 type_name := if child.children_count == 2 && child.typ.len > 0 {
1580 child.typ
1581 } else {
1582 c.top_level_decl_rhs_type_name(rhs_id, cur_module, imports)
1583 }
1584 if type_name.len > 0 {
1585 result[lhs.value] = type_name
1586 }
1587 }
1588 }
1589 i += 2
1590 }
1591 }
1592 for i in 0 .. child.children_count {
1593 next_id := c.a.child(child, i)
1594 if int(next_id) >= 0 {
1595 stack << next_id
1596 }
1597 }
1598 }
1599 return result
1600}
1601
1602fn markused_local_value_names(a &flat.FlatAst, node &flat.Node) map[string]bool {
1603 mut names := map[string]bool{}
1604 mut stack := []flat.NodeId{cap: int(node.children_count)}
1605 for i in 0 .. node.children_count {
1606 child_id := a.child(node, i)
1607 if int(child_id) >= 0 {
1608 stack << child_id
1609 }
1610 }
1611 for stack.len > 0 {
1612 id := stack.pop()
1613 child := a.node(id)
1614 if child.kind == .param && child.value.len > 0 {
1615 names[child.value] = true
1616 } else if child.kind == .decl_assign {
1617 mut i := 0
1618 for i < child.children_count {
1619 lhs_id := a.child(child, i)
1620 if int(lhs_id) >= 0 {
1621 lhs := a.node(lhs_id)
1622 if lhs.kind == .ident && lhs.value.len > 0 {
1623 names[lhs.value] = true
1624 }
1625 }
1626 i += 2
1627 }
1628 }
1629 for i in 0 .. child.children_count {
1630 next_id := a.child(child, i)
1631 if int(next_id) >= 0 {
1632 stack << next_id
1633 }
1634 }
1635 }
1636 return names
1637}
1638
1639fn markused_visible_local_idents(a &flat.FlatAst, root &flat.Node, local_values map[string]bool) map[int]bool {
1640 mut visible_ids := map[int]bool{}
1641 if local_values.len == 0 {
1642 return visible_ids
1643 }
1644 mut locals := map[string]bool{}
1645 if root.kind == .fn_decl {
1646 for i in 0 .. root.children_count {
1647 child := a.child_node(root, i)
1648 if child.kind == .param && child.value.len > 0 {
1649 locals[child.value] = true
1650 }
1651 }
1652 }
1653 markused_collect_visible_local_idents(a, root, local_values, mut locals, mut visible_ids)
1654 return visible_ids
1655}
1656
1657fn markused_ident_is_visible_local(id flat.NodeId, name string, local_values map[string]bool, visible_local_idents map[int]bool) bool {
1658 return name.len > 0 && name in local_values && int(id) in visible_local_idents
1659}
1660
1661fn markused_collect_visible_local_idents(a &flat.FlatAst, node &flat.Node, local_values map[string]bool, mut locals map[string]bool, mut visible_ids map[int]bool) {
1662 for i in 0 .. node.children_count {
1663 child_id := a.child(node, i)
1664 if int(child_id) < 0 {
1665 continue
1666 }
1667 child := a.node(child_id)
1668 match child.kind {
1669 .fn_decl, .c_fn_decl, .fn_literal {
1670 continue
1671 }
1672 .block, .if_expr, .match_stmt, .for_stmt, .for_in_stmt {
1673 mut scoped := markused_clone_bool_map(locals)
1674 markused_collect_visible_local_idents(a, child, local_values, mut scoped, mut
1675 visible_ids)
1676 }
1677 .decl_assign {
1678 mut i2 := 1
1679 for i2 < child.children_count {
1680 rhs_id := a.child(child, i2)
1681 if int(rhs_id) >= 0 {
1682 markused_mark_visible_local_ident(a, rhs_id, local_values, locals, mut
1683 visible_ids)
1684 rhs := a.node(rhs_id)
1685 markused_collect_visible_local_idents(a, rhs, local_values, mut locals, mut
1686 visible_ids)
1687 }
1688 i2 += 2
1689 }
1690 i2 = 0
1691 for i2 + 1 < child.children_count {
1692 lhs_id := a.child(child, i2)
1693 lhs := a.node(lhs_id)
1694 if lhs.kind == .ident && lhs.value in local_values && lhs.value in locals {
1695 visible_ids[int(lhs_id)] = true
1696 }
1697 if lhs.kind == .ident && lhs.value.len > 0 {
1698 locals[lhs.value] = true
1699 }
1700 i2 += 2
1701 }
1702 }
1703 else {
1704 if child.kind == .ident && child.value in local_values && child.value in locals {
1705 visible_ids[int(child_id)] = true
1706 }
1707 markused_collect_visible_local_idents(a, child, local_values, mut locals, mut
1708 visible_ids)
1709 }
1710 }
1711 }
1712}
1713
1714fn markused_mark_visible_local_ident(a &flat.FlatAst, id flat.NodeId, local_values map[string]bool, locals map[string]bool, mut visible_ids map[int]bool) {
1715 node := a.node(id)
1716 if node.kind == .ident && node.value in local_values && node.value in locals {
1717 visible_ids[int(id)] = true
1718 }
1719}
1720
1721fn (c &CallCollector) top_level_decl_rhs_type_name(rhs_id flat.NodeId, cur_module string, imports map[string]string) string {
1722 rhs := c.a.node(rhs_id)
1723 if rhs.kind == .struct_init && rhs.value.len > 0 {
1724 return c.struct_lookup_name(markused_resolve_imported_type_name(rhs.value, imports),
1725 cur_module)
1726 }
1727 type_name := resolve_type_name(c.node_type(rhs_id))
1728 if type_name.len > 0 {
1729 struct_type := c.struct_lookup_name(type_name, cur_module)
1730 if struct_type.len > 0 {
1731 return struct_type
1732 }
1733 return type_name
1734 }
1735 return ''
1736}
1737
1738fn (c &CallCollector) collect_top_level_stmt_calls(id flat.NodeId, cur_module string, imports map[string]string, mut local_values map[string]bool, mut local_types map[string]string, mut calls []string) {
1739 if int(id) < 0 {
1740 return
1741 }
1742 node := c.a.node(id)
1743 match node.kind {
1744 .decl_assign, .assign {
1745 c.collect_top_level_assign_calls(node, cur_module, imports, mut local_values, mut
1746 local_types, mut calls)
1747 }
1748 .global_decl {
1749 c.collect_top_level_global_decl_calls(node, cur_module, imports, mut local_values, mut
1750 local_types, mut calls)
1751 }
1752 .block, .for_stmt, .for_in_stmt {
1753 mut nested_values := markused_clone_bool_map(local_values)
1754 mut nested_types := markused_clone_string_map(local_types)
1755 c.collect_top_level_child_calls(node, cur_module, imports, mut nested_values, mut
1756 nested_types, mut calls)
1757 }
1758 .if_expr, .match_stmt {
1759 c.collect_top_level_branch_calls(node, cur_module, imports, local_values, local_types, mut
1760 calls)
1761 }
1762 else {
1763 c.collect_top_level_expr_calls(id, cur_module, imports, local_values, local_types, mut
1764 calls)
1765 }
1766 }
1767}
1768
1769fn (c &CallCollector) collect_top_level_child_calls(node &flat.Node, cur_module string, imports map[string]string, mut local_values map[string]bool, mut local_types map[string]string, mut calls []string) {
1770 for i in 0 .. node.children_count {
1771 child_id := c.a.child(node, i)
1772 if int(child_id) >= 0 {
1773 c.collect_top_level_stmt_calls(child_id, cur_module, imports, mut local_values, mut
1774 local_types, mut calls)
1775 }
1776 }
1777}
1778
1779fn (c &CallCollector) collect_top_level_branch_calls(node &flat.Node, cur_module string, imports map[string]string, local_values map[string]bool, local_types map[string]string, mut calls []string) {
1780 for i in 0 .. node.children_count {
1781 child_id := c.a.child(node, i)
1782 if int(child_id) < 0 {
1783 continue
1784 }
1785 mut branch_values := local_values.clone()
1786 mut branch_types := local_types.clone()
1787 c.collect_top_level_stmt_calls(child_id, cur_module, imports, mut branch_values, mut
1788 branch_types, mut calls)
1789 }
1790}
1791
1792fn (c &CallCollector) collect_top_level_assign_calls(node &flat.Node, cur_module string, imports map[string]string, mut local_values map[string]bool, mut local_types map[string]string, mut calls []string) {
1793 if node.kind !in [.decl_assign, .assign] {
1794 return
1795 }
1796 mut i := 0
1797 pre_values := markused_clone_bool_map(local_values)
1798 pre_types := markused_clone_string_map(local_types)
1799 for i + 1 < node.children_count {
1800 rhs_id := c.a.child(node, i + 1)
1801 if int(rhs_id) >= 0 {
1802 mut rhs_values := markused_clone_bool_map(pre_values)
1803 mut rhs_types := markused_clone_string_map(pre_types)
1804 c.collect_top_level_stmt_calls(rhs_id, cur_module, imports, mut rhs_values, mut
1805 rhs_types, mut calls)
1806 }
1807 i += 2
1808 }
1809 i = 0
1810 for i + 1 < node.children_count {
1811 lhs_id := c.a.child(node, i)
1812 rhs_id := c.a.child(node, i + 1)
1813 if int(lhs_id) >= 0 && int(rhs_id) >= 0 {
1814 lhs := c.a.node(lhs_id)
1815 if lhs.kind == .ident && lhs.value.len > 0 {
1816 local_values[lhs.value] = true
1817 type_name := c.top_level_decl_rhs_type_name(rhs_id, cur_module, imports)
1818 if type_name.len > 0 {
1819 local_types[lhs.value] = type_name
1820 }
1821 }
1822 }
1823 i += 2
1824 }
1825}
1826
1827fn (c &CallCollector) collect_top_level_global_decl_calls(node &flat.Node, cur_module string, imports map[string]string, mut local_values map[string]bool, mut local_types map[string]string, mut calls []string) {
1828 if node.kind != .global_decl {
1829 return
1830 }
1831 pre_values := markused_clone_bool_map(local_values)
1832 pre_types := markused_clone_string_map(local_types)
1833 for i in 0 .. node.children_count {
1834 field := c.a.child_node(node, i)
1835 if field.children_count == 0 {
1836 continue
1837 }
1838 expr_id := c.a.child(field, 0)
1839 if int(expr_id) >= 0 {
1840 mut rhs_values := markused_clone_bool_map(pre_values)
1841 mut rhs_types := markused_clone_string_map(pre_types)
1842 c.collect_top_level_stmt_calls(expr_id, cur_module, imports, mut rhs_values, mut
1843 rhs_types, mut calls)
1844 }
1845 }
1846 for i in 0 .. node.children_count {
1847 field := c.a.child_node(node, i)
1848 if field.value.len == 0 {
1849 continue
1850 }
1851 local_values[field.value] = true
1852 if field.children_count > 0 {
1853 expr_id := c.a.child(field, 0)
1854 type_name := c.top_level_decl_rhs_type_name(expr_id, cur_module, imports)
1855 if type_name.len > 0 {
1856 local_types[field.value] = type_name
1857 }
1858 }
1859 }
1860}
1861
1862fn (c &CallCollector) collect_top_level_expr_calls(id flat.NodeId, cur_module string, imports map[string]string, local_values map[string]bool, local_types map[string]string, mut calls []string) {
1863 if int(id) < 0 {
1864 return
1865 }
1866 mut stack := [id]
1867 for stack.len > 0 {
1868 child_id := stack.pop()
1869 child := c.a.node(child_id)
1870 match child.kind {
1871 .ident {
1872 if child.value !in local_values {
1873 c.collect_fn_value_ident(child_id, child.value, cur_module, imports, false, mut
1874 calls)
1875 }
1876 }
1877 .selector {
1878 if !c.top_level_selector_base_is_local(child, local_values) {
1879 c.collect_fn_value_selector(child_id, child, cur_module, imports, mut calls)
1880 }
1881 }
1882 .call {
1883 c.collect_top_level_call(child_id, child, cur_module, imports, local_values,
1884 local_types, mut calls)
1885 }
1886 .prefix {
1887 if child.op == .amp && child.children_count > 0 {
1888 inner_id := c.a.child(child, 0)
1889 if int(inner_id) >= 0 {
1890 inner := c.a.node(inner_id)
1891 if inner.kind == .struct_init {
1892 calls << 'memdup'
1893 }
1894 }
1895 }
1896 }
1897 .string_interp {
1898 calls << 'string_plus_many'
1899 }
1900 .infix {
1901 if child.op == .plus {
1902 calls << 'string__plus'
1903 }
1904 if child.children_count >= 2 {
1905 lhs_id := c.a.child(child, 0)
1906 rhs_id := c.a.child(child, 1)
1907 if child.op in [.dot, .amp] {
1908 lhs_name := c.qualified_expr_name(lhs_id)
1909 if lhs_name.len > 0 {
1910 rhs := c.a.node(rhs_id)
1911 if rhs.kind == .call && rhs.children_count > 0 {
1912 fn_node := c.a.child_node(rhs, 0)
1913 if fn_node.kind == .ident && fn_node.value.len > 0 {
1914 mod_name := if lhs_name in imports {
1915 imports[lhs_name]
1916 } else {
1917 lhs_name
1918 }
1919 calls << mod_name + '.' + fn_node.value
1920 calls << qualify_fn(cur_module, lhs_name + '.' + fn_node.value)
1921 }
1922 }
1923 }
1924 }
1925 if int(lhs_id) >= 0 {
1926 c.collect_struct_operator_call(lhs_id, child.op, cur_module, mut calls)
1927 }
1928 }
1929 }
1930 .or_expr {
1931 if child.children_count > 0 {
1932 expr_id := c.a.child(child, 0)
1933 c.collect_zero_struct_default_calls(c.node_type(expr_id), cur_module, imports, mut
1934 calls)
1935 }
1936 }
1937 .struct_init {
1938 c.collect_struct_default_calls(child, cur_module, imports, mut calls)
1939 }
1940 else {}
1941 }
1942
1943 if child.children_count > 0 {
1944 mut j := int(child.children_count) - 1
1945 for j >= 0 {
1946 next_id := c.a.child(child, j)
1947 if int(next_id) >= 0 {
1948 stack << next_id
1949 }
1950 j--
1951 }
1952 }
1953 }
1954}
1955
1956fn (c &CallCollector) top_level_selector_base_is_local(node flat.Node, local_values map[string]bool) bool {
1957 return c.selector_base_is_local(&node, local_values)
1958}
1959
1960fn (c &CallCollector) selector_base_is_local(node &flat.Node, local_values map[string]bool) bool {
1961 if node.children_count == 0 {
1962 return false
1963 }
1964 base_id := c.a.child(node, 0)
1965 if int(base_id) < 0 {
1966 return false
1967 }
1968 base := c.a.node(base_id)
1969 return base.kind == .ident && base.value in local_values
1970}
1971
1972fn (c &CallCollector) collect_top_level_call(call_id flat.NodeId, call &flat.Node, cur_module string, imports map[string]string, local_values map[string]bool, local_types map[string]string, mut calls []string) {
1973 mut resolved_call := ''
1974 if resolved := c.tc.resolved_call_name(call_id) {
1975 resolved_call = resolved
1976 }
1977 if call.children_count == 0 {
1978 if resolved_call.len > 0 {
1979 calls << resolved_call
1980 }
1981 return
1982 }
1983 callee_id := c.a.child(call, 0)
1984 if int(callee_id) < 0 {
1985 return
1986 }
1987 callee := c.a.node(callee_id)
1988 if callee.kind == .ident && callee.value.len > 0 {
1989 if resolved_call.len > 0 {
1990 calls << resolved_call
1991 }
1992 if callee.value !in local_values {
1993 calls << callee.value
1994 qcallee := qualify_fn(cur_module, callee.value)
1995 if qcallee != callee.value {
1996 calls << qcallee
1997 }
1998 }
1999 } else if callee.kind == .selector && callee.value.len > 0 {
2000 c.collect_top_level_selector_call(callee, callee.value, resolved_call, cur_module, imports,
2001 local_values, local_types, mut calls)
2002 } else if resolved_call.len > 0 {
2003 calls << resolved_call
2004 }
2005 for ci in 1 .. call.children_count {
2006 arg_id := c.a.child(call, ci)
2007 if int(arg_id) >= 0 {
2008 arg := c.a.node(arg_id)
2009 if arg.kind == .ident && arg.value.len > 0 {
2010 if arg.value !in local_values {
2011 c.collect_fn_value_ident(arg_id, arg.value, cur_module, imports, false, mut
2012 calls)
2013 }
2014 }
2015 }
2016 }
2017}
2018
2019fn (c &CallCollector) collect_top_level_selector_call(callee &flat.Node, method string, resolved_call string, cur_module string, imports map[string]string, local_values map[string]bool, local_types map[string]string, mut calls []string) {
2020 mut has_exact_selector_call := false
2021 if callee.children_count > 0 {
2022 base_id := c.a.child(callee, 0)
2023 if int(base_id) >= 0 {
2024 base := c.a.node(base_id)
2025 has_exact_selector_call = c.collect_top_level_typed_receiver_method(base_id, method,
2026 cur_module, imports, local_values, local_types, mut calls)
2027 if !has_exact_selector_call && base.kind == .ident && base.value in imports
2028 && base.value !in local_values {
2029 calls << imports[base.value] + '.' + method
2030 has_exact_selector_call = true
2031 }
2032 if !has_exact_selector_call && resolved_call.len > 0 {
2033 calls << resolved_call
2034 has_exact_selector_call = true
2035 }
2036 if !has_exact_selector_call {
2037 c.collect_top_level_selector_fallback(base, method, cur_module, imports,
2038 local_values, mut calls)
2039 }
2040 }
2041 }
2042}
2043
2044fn (c &CallCollector) collect_top_level_selector_fallback(base &flat.Node, method string, cur_module string, imports map[string]string, local_values map[string]bool, mut calls []string) {
2045 if base.kind == .ident && base.value.len > 0 {
2046 if base.value in local_values {
2047 return
2048 }
2049 if base.value in imports {
2050 return
2051 }
2052 mod_name := base.value
2053 calls << mod_name + '.' + method
2054 calls << qualify_fn(cur_module, base.value + '.' + method)
2055 if base.value.len > 0 && base.value[0] >= `A` && base.value[0] <= `Z` {
2056 named_type := c.tc.parse_type(base.value)
2057 named_type_name := resolve_type_name(named_type)
2058 if named_type_name.len > 0 {
2059 calls << named_type_name + '.' + method
2060 }
2061 }
2062 } else if base.kind == .selector && base.children_count > 0 {
2063 inner_id := c.a.child(base, 0)
2064 if int(inner_id) >= 0 {
2065 inner := c.a.node(inner_id)
2066 if inner.kind == .ident && inner.value.len > 0 {
2067 mod_name := if inner.value in imports {
2068 imports[inner.value]
2069 } else {
2070 inner.value
2071 }
2072 calls << mod_name + '.' + base.value + '.' + method
2073 }
2074 }
2075 }
2076}
2077
2078fn (c &CallCollector) collect_top_level_typed_receiver_method(base_id flat.NodeId, method string, cur_module string, imports map[string]string, local_values map[string]bool, local_types map[string]string, mut calls []string) bool {
2079 type_name := c.top_level_receiver_type_name(base_id, cur_module, imports, local_values,
2080 local_types)
2081 if type_name.len == 0 {
2082 return false
2083 }
2084 method_name := c.typed_receiver_method_name(type_name, method, cur_module) or { return false }
2085 c.add_typed_receiver_method_name(method_name, mut calls)
2086 return true
2087}
2088
2089fn (c &CallCollector) top_level_receiver_type_name(base_id flat.NodeId, cur_module string, imports map[string]string, local_values map[string]bool, local_types map[string]string) string {
2090 base := c.a.node(base_id)
2091 type_name := resolve_type_name(c.node_type(base_id))
2092 if type_name.len > 0 {
2093 struct_type := c.struct_lookup_name(type_name, cur_module)
2094 if struct_type.len > 0 {
2095 return struct_type
2096 }
2097 return type_name
2098 }
2099 if base.kind == .ident && base.value.len > 0 {
2100 if local_type := local_types[base.value] {
2101 return local_type
2102 }
2103 if base.value in local_values {
2104 return ''
2105 }
2106 if base.value in imports {
2107 return ''
2108 }
2109 }
2110 if base.kind == .ident && base.value.len > 0 {
2111 return c.value_type_name(base.value, cur_module, imports)
2112 }
2113 if base.kind == .selector {
2114 name := c.qualified_expr_name(base_id)
2115 if name.len > 0 {
2116 return c.value_type_name(name, cur_module, imports)
2117 }
2118 }
2119 return ''
2120}
2121
2122// collect_struct_operator_call updates collect struct operator call state for markused.
2123fn (c &CallCollector) collect_struct_operator_call(lhs_id flat.NodeId, op flat.Op, cur_module string, mut calls []string) {
2124 lhs_type := c.node_type(lhs_id)
2125 lhs_name := resolve_type_name(lhs_type)
2126 struct_type := c.struct_lookup_name(lhs_name, cur_module)
2127 if struct_type.len == 0 {
2128 return
2129 }
2130 method_name := c.struct_operator_call_name(struct_type, op) or { return }
2131 c.add_operator_call_name(method_name, mut calls)
2132}
2133
2134// struct_operator_call_name supports struct operator call name handling for CallCollector.
2135fn (c &CallCollector) struct_operator_call_name(struct_type string, op flat.Op) ?string {
2136 if op_name := markused_struct_operator_symbol(op) {
2137 if method_name := c.struct_operator_fn_name(struct_type, op_name) {
2138 return method_name
2139 }
2140 }
2141 match op {
2142 .gt, .ge, .le {
2143 if method_name := c.struct_operator_fn_name(struct_type, '<') {
2144 return method_name
2145 }
2146 }
2147 .ne {
2148 if method_name := c.struct_operator_fn_name(struct_type, '==') {
2149 return method_name
2150 }
2151 }
2152 else {}
2153 }
2154
2155 return none
2156}
2157
2158// markused_struct_operator_symbol supports markused struct operator symbol handling for markused.
2159fn markused_struct_operator_symbol(op flat.Op) ?string {
2160 match op {
2161 .plus { return '+' }
2162 .minus { return '-' }
2163 .mul { return '*' }
2164 .div { return '/' }
2165 .mod { return '%' }
2166 .eq { return '==' }
2167 .ne { return '!=' }
2168 .lt { return '<' }
2169 .gt { return '>' }
2170 .le { return '<=' }
2171 .ge { return '>=' }
2172 else {}
2173 }
2174
2175 return none
2176}
2177
2178// struct_operator_fn_name supports struct operator fn name handling for CallCollector.
2179fn (c &CallCollector) struct_operator_fn_name(struct_type string, op_name string) ?string {
2180 method_name := '${struct_type}.${op_name}'
2181 if c.is_known_fn_name(method_name) {
2182 return method_name
2183 }
2184 cmethod_name := markused_c_name(method_name)
2185 if c.is_known_fn_name(cmethod_name) {
2186 return cmethod_name
2187 }
2188 return none
2189}
2190
2191// is_known_fn_name reports whether is known fn name applies in markused.
2192fn (c &CallCollector) is_known_fn_name(name string) bool {
2193 return name in c.fn_decls || name in c.tc.fn_ret_types || name in c.tc.fn_param_types
2194}
2195
2196// struct_lookup_name supports struct lookup name handling for CallCollector.
2197fn (c &CallCollector) struct_lookup_name(type_name string, cur_module string) string {
2198 if type_name.len == 0 {
2199 return ''
2200 }
2201 if type_name.contains('.') {
2202 if type_name in c.struct_decls {
2203 return type_name
2204 }
2205 short_type := type_name.all_after_last('.')
2206 type_mod := type_name.all_before_last('.')
2207 if info := c.struct_decls[short_type] {
2208 if info.module == type_mod {
2209 return short_type
2210 }
2211 }
2212 if info := c.struct_decls[type_name] {
2213 if info.module == type_mod {
2214 return type_name
2215 }
2216 }
2217 return ''
2218 }
2219 if info := c.struct_decls[type_name] {
2220 if info.module.len == 0 || info.module == cur_module {
2221 return type_name
2222 }
2223 }
2224 if cur_module.len > 0 && cur_module != 'main' && cur_module != 'builtin' {
2225 qtype := '${cur_module}.${type_name}'
2226 if qtype in c.struct_decls {
2227 return qtype
2228 }
2229 }
2230 return ''
2231}
2232
2233// add_operator_call_name updates add operator call name state for CallCollector.
2234fn (c &CallCollector) add_operator_call_name(method_name string, mut calls []string) {
2235 calls << method_name
2236 lowered := markused_c_name(method_name)
2237 if lowered != method_name {
2238 calls << lowered
2239 }
2240}
2241
2242// qualified_expr_name supports qualified expr name handling for CallCollector.
2243fn (c &CallCollector) qualified_expr_name(id flat.NodeId) string {
2244 if int(id) < 0 {
2245 return ''
2246 }
2247 node := c.a.nodes[int(id)]
2248 match node.kind {
2249 .ident {
2250 return node.value
2251 }
2252 .selector {
2253 if node.children_count == 0 {
2254 return node.value
2255 }
2256 base := c.qualified_expr_name(c.a.child(&node, 0))
2257 if base.len == 0 {
2258 return node.value
2259 }
2260 return base + '.' + node.value
2261 }
2262 else {
2263 return ''
2264 }
2265 }
2266}
2267
2268// collect_fn_value_ident updates collect fn value ident state for markused.
2269fn (c &CallCollector) collect_fn_value_ident(id flat.NodeId, name string, cur_module string, imports map[string]string, is_local_value bool, mut calls []string) {
2270 if is_local_value {
2271 return
2272 }
2273 if resolved := c.tc.resolved_fn_value_name(id) {
2274 calls << resolved
2275 return
2276 }
2277 if name.len == 0 || !c.name_may_reference_fn(name, cur_module, imports) {
2278 return
2279 }
2280 has_fn_decl := c.name_has_fn_decl(name, cur_module, imports)
2281 if !has_fn_decl && !c.node_is_fn_value(id) {
2282 return
2283 }
2284 c.add_fn_value_candidates(name, cur_module, imports, mut calls)
2285 c.add_const_alias_candidates(name, cur_module, imports, mut calls)
2286}
2287
2288// collect_fn_value_selector updates collect fn value selector state for markused.
2289fn (c &CallCollector) collect_fn_value_selector(id flat.NodeId, node &flat.Node, cur_module string, imports map[string]string, mut calls []string) {
2290 if resolved := c.tc.resolved_fn_value_name(id) {
2291 calls << resolved
2292 return
2293 }
2294 if node.children_count == 0 {
2295 return
2296 }
2297 base := c.a.child_node(node, 0)
2298 if base.kind != .ident || base.value.len == 0 || node.value.len == 0 {
2299 return
2300 }
2301 name := '${base.value}.${node.value}'
2302 if !c.name_may_reference_fn(name, cur_module, imports) {
2303 return
2304 }
2305 has_fn_decl := c.name_has_fn_decl(name, cur_module, imports)
2306 if !has_fn_decl && !c.node_is_fn_value(id) {
2307 return
2308 }
2309 c.add_fn_value_candidates(name, cur_module, imports, mut calls)
2310 c.add_const_alias_candidates(name, cur_module, imports, mut calls)
2311 if base.value in imports {
2312 mod_name := imports[base.value]
2313 resolved_name := '${mod_name}.${node.value}'
2314 c.add_fn_value_candidates(resolved_name, cur_module, imports, mut calls)
2315 c.add_const_alias_candidates(resolved_name, cur_module, imports, mut calls)
2316 }
2317}
2318
2319// name_may_reference_fn returns name may reference fn data for CallCollector.
2320fn (c &CallCollector) name_may_reference_fn(name string, cur_module string, imports map[string]string) bool {
2321 return c.name_has_candidate_decl(name, cur_module, imports, true)
2322}
2323
2324// name_has_fn_decl returns name has fn decl data for CallCollector.
2325fn (c &CallCollector) name_has_fn_decl(name string, cur_module string, imports map[string]string) bool {
2326 return c.name_has_candidate_decl(name, cur_module, imports, false)
2327}
2328
2329// name_has_candidate_decl returns name has candidate decl data for CallCollector.
2330fn (c &CallCollector) name_has_candidate_decl(name string, cur_module string, imports map[string]string, include_consts bool) bool {
2331 if c.candidate_matches_decl(name, include_consts) {
2332 return true
2333 }
2334 qname := qualify_fn(cur_module, name)
2335 if qname != name && c.candidate_matches_decl(qname, include_consts) {
2336 return true
2337 }
2338 if name.contains('.') {
2339 base := name.all_before_last('.')
2340 member := name.all_after_last('.')
2341 if base in imports {
2342 imported_name := imports[base] + '.' + member
2343 if c.candidate_matches_decl(imported_name, include_consts) {
2344 return true
2345 }
2346 }
2347 }
2348 return false
2349}
2350
2351// candidate_matches_decl supports candidate matches decl handling for CallCollector.
2352fn (c &CallCollector) candidate_matches_decl(candidate string, include_consts bool) bool {
2353 if candidate in c.fn_decls {
2354 return true
2355 }
2356 return include_consts && candidate in c.const_decls
2357}
2358
2359// node_is_fn_value supports node is fn value handling for CallCollector.
2360fn (c &CallCollector) node_is_fn_value(id flat.NodeId) bool {
2361 expr_type := c.node_type(id)
2362 return expr_type is types.FnType
2363}
2364
2365// add_fn_value_candidates updates add fn value candidates state for CallCollector.
2366fn (c &CallCollector) add_fn_value_candidates(name string, cur_module string, imports map[string]string, mut calls []string) {
2367 if name.len == 0 {
2368 return
2369 }
2370 calls << name
2371 qname := qualify_fn(cur_module, name)
2372 if qname != name {
2373 calls << qname
2374 }
2375 if name.contains('.') {
2376 base := name.all_before_last('.')
2377 member := name.all_after_last('.')
2378 if base in imports {
2379 calls << imports[base] + '.' + member
2380 }
2381 }
2382}
2383
2384// add_const_alias_candidates converts add const alias candidates data for markused.
2385fn (c &CallCollector) add_const_alias_candidates(name string, cur_module string, imports map[string]string, mut calls []string) {
2386 c.add_const_alias_candidate(name, imports, mut calls)
2387 qname := qualify_fn(cur_module, name)
2388 if qname != name {
2389 c.add_const_alias_candidate(qname, imports, mut calls)
2390 }
2391 if name.contains('.') {
2392 base := name.all_before_last('.')
2393 member := name.all_after_last('.')
2394 if base in imports {
2395 c.add_const_alias_candidate(imports[base] + '.' + member, imports, mut calls)
2396 }
2397 }
2398}
2399
2400// add_const_alias_candidate converts add const alias candidate data for markused.
2401fn (c &CallCollector) add_const_alias_candidate(const_name string, imports map[string]string, mut calls []string) {
2402 info := c.const_decls[const_name] or { return }
2403 expr := c.a.node(info.expr_id)
2404 match expr.kind {
2405 .ident {
2406 c.add_fn_value_candidates(expr.value, info.module, imports, mut calls)
2407 }
2408 .selector {
2409 if expr.children_count > 0 {
2410 base := c.a.child_node(expr, 0)
2411 if base.kind == .ident && base.value.len > 0 && expr.value.len > 0 {
2412 c.add_fn_value_candidates('${base.value}.${expr.value}', info.module, imports, mut
2413 calls)
2414 if base.value in imports {
2415 c.add_fn_value_candidates('${imports[base.value]}.${expr.value}',
2416 info.module, imports, mut calls)
2417 }
2418 }
2419 }
2420 }
2421 else {}
2422 }
2423}
2424
2425// node_type supports node type handling for CallCollector.
2426fn (c &CallCollector) node_type(id flat.NodeId) types.Type {
2427 if t := c.tc.expr_type(id) {
2428 return t
2429 }
2430 return c.tc.resolve_type(id)
2431}
2432
2433fn (c &CallCollector) collect_typed_receiver_method(base_id flat.NodeId, method string, cur_module string, imports map[string]string, local_values map[string]bool, local_types map[string]string, mut calls []string) bool {
2434 type_name := c.receiver_type_name(base_id, cur_module, imports, local_values, local_types)
2435 if type_name.len == 0 {
2436 return false
2437 }
2438 method_name := c.typed_receiver_method_name(type_name, method, cur_module) or { return false }
2439 c.add_typed_receiver_method_name(method_name, mut calls)
2440 return true
2441}
2442
2443fn (c &CallCollector) receiver_type_name(base_id flat.NodeId, cur_module string, imports map[string]string, local_values map[string]bool, local_types map[string]string) string {
2444 base := c.a.node(base_id)
2445 base_type := c.node_type(base_id)
2446 type_name := resolve_type_name(base_type)
2447 if type_name.len > 0 {
2448 return type_name
2449 }
2450 if base.kind == .ident && base.value.len > 0 {
2451 if local_type := local_types[base.value] {
2452 return local_type
2453 }
2454 }
2455 if base.kind == .ident && base.value.len > 0 {
2456 if base.value in local_values {
2457 return ''
2458 }
2459 return c.value_type_name(base.value, cur_module, imports)
2460 }
2461 if base.kind == .selector {
2462 name := c.qualified_expr_name(base_id)
2463 if name.len > 0 {
2464 return c.value_type_name(name, cur_module, imports)
2465 }
2466 }
2467 return ''
2468}
2469
2470fn (c &CallCollector) typed_receiver_method_name(type_name string, method string, cur_module string) ?string {
2471 if type_name.len == 0 || method.len == 0 {
2472 return none
2473 }
2474 mut candidates := []string{cap: 4}
2475 if type_name.starts_with('map[') {
2476 candidates << markused_map_receiver_method_candidates(type_name, method, cur_module)
2477 } else if type_name.contains('.') {
2478 candidates << '${type_name}.${method}'
2479 } else {
2480 if cur_module.len > 0 && cur_module != 'main' && cur_module != 'builtin' {
2481 candidates << '${cur_module}.${type_name}.${method}'
2482 }
2483 candidates << '${type_name}.${method}'
2484 }
2485 for candidate in candidates {
2486 if c.is_known_fn_name(candidate) {
2487 return candidate
2488 }
2489 lowered := markused_c_name(candidate)
2490 if lowered != candidate && c.is_known_fn_name(lowered) {
2491 return lowered
2492 }
2493 }
2494 return none
2495}
2496
2497fn markused_map_receiver_method_candidates(receiver_type string, method string, cur_module string) []string {
2498 clean_type := markused_clean_map_type(receiver_type)
2499 key_type := markused_map_key_type(clean_type)
2500 value_type := markused_map_value_type(clean_type)
2501 mut candidates := []string{}
2502 candidates << '${clean_type}.${method}'
2503 if key_type.len == 0 || value_type.len == 0 {
2504 return candidates
2505 }
2506 short_value := if value_type.contains('.') {
2507 value_type.all_after_last('.')
2508 } else {
2509 value_type
2510 }
2511 short_map := 'map[${key_type}]${short_value}'
2512 if short_map != clean_type {
2513 candidates << '${short_map}.${method}'
2514 }
2515 if value_type.contains('.') {
2516 mod_name := value_type.all_before_last('.')
2517 candidates << '${mod_name}.${short_map}.${method}'
2518 } else if cur_module.len > 0 && cur_module != 'main' && cur_module != 'builtin' {
2519 candidates << '${cur_module}.${short_map}.${method}'
2520 }
2521 return candidates
2522}
2523
2524fn markused_clean_map_type(receiver_type string) string {
2525 mut clean := receiver_type.trim_space()
2526 for {
2527 if clean.starts_with('shared ') {
2528 clean = clean[7..].trim_space()
2529 continue
2530 }
2531 if clean.starts_with('&') {
2532 clean = clean[1..].trim_space()
2533 continue
2534 }
2535 break
2536 }
2537 return clean
2538}
2539
2540fn markused_map_key_type(type_str string) string {
2541 if !type_str.starts_with('map[') {
2542 return ''
2543 }
2544 bracket_end := type_str.index(']') or { return '' }
2545 if bracket_end > 4 {
2546 return type_str[4..bracket_end]
2547 }
2548 return ''
2549}
2550
2551fn markused_map_value_type(type_str string) string {
2552 if !type_str.starts_with('map[') {
2553 return ''
2554 }
2555 bracket_end := type_str.index(']') or { return '' }
2556 if bracket_end + 1 < type_str.len {
2557 return type_str[bracket_end + 1..]
2558 }
2559 return ''
2560}
2561
2562fn (c &CallCollector) add_typed_receiver_method_name(method_name string, mut calls []string) {
2563 calls << method_name
2564 lowered := markused_c_name(method_name)
2565 if lowered != method_name {
2566 calls << lowered
2567 }
2568}
2569
2570fn (c &CallCollector) value_type_name(name string, cur_module string, imports map[string]string) string {
2571 for candidate in c.value_name_candidates(name, cur_module, imports) {
2572 if typ := c.tc.file_scope.lookup(candidate) {
2573 type_name := resolve_type_name(typ)
2574 if type_name.len > 0 {
2575 return type_name
2576 }
2577 }
2578 if typ := c.tc.const_types[candidate] {
2579 type_name := resolve_type_name(typ)
2580 if type_name.len > 0 {
2581 return type_name
2582 }
2583 }
2584 if info := c.const_decls[candidate] {
2585 type_name := c.const_initializer_type_name(info)
2586 if type_name.len > 0 {
2587 return type_name
2588 }
2589 }
2590 }
2591 return ''
2592}
2593
2594fn (c &CallCollector) const_initializer_type_name(info ConstDeclInfo) string {
2595 expr := c.a.node(info.expr_id)
2596 if expr.kind == .struct_init && expr.value.len > 0 {
2597 return c.struct_lookup_name(expr.value, info.module)
2598 }
2599 type_name := resolve_type_name(c.node_type(info.expr_id))
2600 if type_name.len > 0 {
2601 return type_name
2602 }
2603 return ''
2604}
2605
2606fn (c &CallCollector) value_name_candidates(name string, cur_module string, imports map[string]string) []string {
2607 mut candidates := []string{cap: 3}
2608 qname := qualify_fn(cur_module, name)
2609 candidates << qname
2610 if qname != name {
2611 candidates << name
2612 }
2613 if name.contains('.') {
2614 base := name.all_before_last('.')
2615 member := name.all_after_last('.')
2616 if base in imports {
2617 candidates << '${imports[base]}.${member}'
2618 }
2619 }
2620 return candidates
2621}
2622
2623fn markused_resolve_imported_type_name(name string, imports map[string]string) string {
2624 if !name.contains('.') {
2625 return name
2626 }
2627 base := name.all_before_last('.')
2628 member := name.all_after_last('.')
2629 if base in imports {
2630 return '${imports[base]}.${member}'
2631 }
2632 return name
2633}
2634
2635// collect_zero_struct_default_calls updates collect zero struct default calls state for markused.
2636fn (c &CallCollector) collect_zero_struct_default_calls(typ types.Type, cur_module string, imports map[string]string, mut calls []string) {
2637 type_name := zero_value_struct_type_name(typ)
2638 if type_name.len == 0 {
2639 return
2640 }
2641 c.collect_struct_default_calls_for_type(type_name, cur_module, imports, mut calls)
2642}
2643
2644// zero_value_struct_type_name supports zero value struct type name handling for markused.
2645fn zero_value_struct_type_name(typ types.Type) string {
2646 mut clean := typ
2647 for _ in 0 .. 8 {
2648 if clean is types.Alias {
2649 clean = clean.base_type
2650 continue
2651 }
2652 if clean is types.OptionType {
2653 clean = clean.base_type
2654 continue
2655 }
2656 if clean is types.ResultType {
2657 clean = clean.base_type
2658 continue
2659 }
2660 break
2661 }
2662 if clean is types.Struct {
2663 return clean.name
2664 }
2665 return ''
2666}
2667
2668// collect_struct_default_calls updates collect struct default calls state for markused.
2669fn (c &CallCollector) collect_struct_default_calls(init &flat.Node, cur_module string, imports map[string]string, mut calls []string) {
2670 info := c.struct_decl_info(init.value, cur_module) or { return }
2671 mut set_fields := map[string]bool{}
2672 for i in 0 .. init.children_count {
2673 field := c.a.child_node(init, i)
2674 if field.kind == .field_init {
2675 set_fields[field.value] = true
2676 }
2677 }
2678 c.collect_struct_default_calls_from_info(info, set_fields, imports, mut calls)
2679}
2680
2681// collect_struct_default_calls_for_type supports collect_struct_default_calls_for_type handling.
2682fn (c &CallCollector) collect_struct_default_calls_for_type(type_name string, cur_module string, imports map[string]string, mut calls []string) {
2683 info := c.struct_decl_info(type_name, cur_module) or { return }
2684 c.collect_struct_default_calls_from_info(info, map[string]bool{}, imports, mut calls)
2685}
2686
2687// struct_decl_info supports struct decl info handling for CallCollector.
2688fn (c &CallCollector) struct_decl_info(type_name string, cur_module string) ?StructDeclInfo {
2689 struct_name := c.struct_lookup_name(type_name, cur_module)
2690 if struct_name.len == 0 {
2691 return none
2692 }
2693 return c.struct_decls[struct_name] or { none }
2694}
2695
2696// collect_struct_default_calls_from_info supports collect_struct_default_calls_from_info handling.
2697fn (c &CallCollector) collect_struct_default_calls_from_info(info StructDeclInfo, provided map[string]bool, imports map[string]string, mut calls []string) {
2698 node := c.a.node(info.node_id)
2699 for i in 0 .. node.children_count {
2700 field := c.a.child_node(node, i)
2701 if field.kind != .field_decl || field.children_count == 0 || field.value in provided {
2702 continue
2703 }
2704 c.collect_calls(field, info.module, imports, '', '', mut calls)
2705 }
2706}
2707
2708// resolve_type_name resolves resolve type name information for markused.
2709fn resolve_type_name(t types.Type) string {
2710 if t is types.Alias {
2711 return t.name
2712 } else if t is types.Struct {
2713 return t.name
2714 } else if t is types.Interface {
2715 return t.name
2716 } else if t is types.SumType {
2717 return t.name
2718 } else if t is types.Enum {
2719 return t.name
2720 } else if t is types.String {
2721 return 'string'
2722 } else if t is types.Array {
2723 return 'Array'
2724 } else if t is types.Map {
2725 return 'map[${t.key_type.name()}]${t.value_type.name()}'
2726 } else if t is types.Pointer {
2727 return resolve_type_name(t.base_type)
2728 } else if t is types.Primitive {
2729 props := int(t.props)
2730 sz := int(t.size)
2731 if props & 1 > 0 {
2732 return 'bool'
2733 }
2734 if props & 4 > 0 {
2735 if props & 8 > 0 {
2736 return match sz {
2737 8 { 'u8' }
2738 16 { 'u16' }
2739 32 { 'u32' }
2740 64 { 'u64' }
2741 else { 'int' }
2742 }
2743 }
2744 return match sz {
2745 0 { 'int' }
2746 8 { 'i8' }
2747 16 { 'i16' }
2748 32 { 'i32' }
2749 64 { 'i64' }
2750 else { 'int' }
2751 }
2752 }
2753 if props & 2 > 0 {
2754 return match sz {
2755 32 { 'f32' }
2756 64 { 'f64' }
2757 else { 'f64' }
2758 }
2759 }
2760 return 'int'
2761 } else if t is types.ISize {
2762 return 'isize'
2763 } else if t is types.USize {
2764 return 'usize'
2765 } else if t is types.Rune {
2766 return 'rune'
2767 }
2768 return ''
2769}
2770
2771// markused_c_name converts markused c name data for markused.
2772fn markused_c_name(name string) string {
2773 if name.starts_with('C.') {
2774 return name[2..]
2775 }
2776 if name == 'malloc' {
2777 return 'v_malloc'
2778 }
2779 if name == 'exit' {
2780 return 'v_exit'
2781 }
2782 if markused_c_name_is_plain(name) {
2783 return name
2784 }
2785 return name.replace('[]', 'Array_').replace('.-', '__minus').replace('.+', '__plus').replace('.==',
2786 '__eq').replace('.!=', '__ne').replace('.<=', '__le').replace('.>=', '__ge').replace('.<',
2787 '__lt').replace('.>', '__gt').replace('&', 'ptr').replace('[', '_').replace(']', '').replace(',',
2788 '_').replace(' ', '_').replace('.', '__')
2789}
2790
2791// markused_c_name_is_plain converts markused c name is plain data for markused.
2792fn markused_c_name_is_plain(name string) bool {
2793 for i in 0 .. name.len {
2794 c := name[i]
2795 if (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) || (c >= `0` && c <= `9`) || c == `_` {
2796 continue
2797 }
2798 return false
2799 }
2800 return true
2801}
2802