1use crate::extract::{ExtractOptions, Extractor};
2use crate::parsers;
3use normalize_facts_core::TypeRef;
4use normalize_facts_core::TypeRefKind;
5use normalize_languages::{Symbol as LangSymbol, support_for_path};
6use std::path::Path;
7use streaming_iterator::StreamingIterator;
8
9pub use normalize_facts_core::{FlatImport, FlatSymbol};
11
12pub struct SymbolParser {
13 extractor: Extractor,
14 }
16
17impl Default for SymbolParser {
18 fn default() -> Self {
19 Self::new()
20 }
21}
22
23impl SymbolParser {
24 pub fn new() -> Self {
25 Self {
26 extractor: Extractor::with_options(ExtractOptions {
27 include_private: true, }),
29 }
30 }
31
32 pub fn parse_file(&self, path: &Path, content: &str) -> Option<Vec<FlatSymbol>> {
41 let support = match support_for_path(path) {
42 Some(s) => s,
43 None => return Some(Vec::new()), };
45
46 normalize_languages::parsers::try_get_grammar(support.grammar_name())?;
51
52 let result = self.extractor.extract(path, content);
54
55 let mut symbols = Vec::new();
57 for sym in &result.symbols {
58 Self::flatten_symbol(sym, None, &mut symbols);
59 }
60 Some(symbols)
61 }
62
63 fn flatten_symbol(sym: &LangSymbol, parent: Option<&str>, symbols: &mut Vec<FlatSymbol>) {
65 symbols.push(FlatSymbol {
66 name: sym.name.clone(),
67 kind: sym.kind,
68 start_line: sym.start_line,
69 end_line: sym.end_line,
70 parent: parent.map(String::from),
71 visibility: sym.visibility,
72 attributes: sym.attributes.clone(),
73 is_interface_impl: sym.is_interface_impl,
74 implements: sym.implements.clone(),
75 docstring: sym.docstring.clone(),
76 });
77
78 for child in &sym.children {
80 Self::flatten_symbol(child, Some(&sym.name), symbols);
81 }
82 }
83
84 pub fn parse_imports(&self, path: &Path, content: &str) -> Vec<FlatImport> {
88 let support = match support_for_path(path) {
89 Some(s) => s,
90 None => return Vec::new(),
91 };
92
93 let grammar_name = support.grammar_name();
94
95 let loader = normalize_languages::parsers::grammar_loader();
97 if loader.get_imports(grammar_name).is_none() {
98 return Vec::new();
99 }
100 let tree = match parsers::parse_with_grammar(grammar_name, content) {
101 Some(t) => t,
102 None => return Vec::new(),
103 };
104
105 let root = tree.root_node();
106
107 if let Some(query_str) = loader.get_imports(grammar_name)
109 && let Some(query) = loader.get_compiled_query(grammar_name, "imports", &query_str)
110 && let Some(imports) = Self::collect_imports_with_compiled_query(root, content, &query)
111 {
112 return imports;
113 }
114
115 Self::collect_imports_with_trait(root, content, support)
117 }
118
119 fn collect_imports_with_compiled_query(
122 root: tree_sitter::Node,
123 source: &str,
124 query: &tree_sitter::Query,
125 ) -> Option<Vec<FlatImport>> {
126 let path_idx = query.capture_index_for_name("import.path");
127 let name_idx = query.capture_index_for_name("import.name");
128 let alias_idx = query.capture_index_for_name("import.alias");
129 let glob_idx = query.capture_index_for_name("import.glob");
130 let reexport_idx = query.capture_index_for_name("import.reexport");
131 let stmt_idx = query.capture_index_for_name("import");
132
133 let mut qcursor = tree_sitter::QueryCursor::new();
134 let mut results = Vec::new();
135
136 let mut matches = qcursor.matches(query, root, source.as_bytes());
137 while let Some(m) = matches.next() {
138 let mut stmt_line = 0usize;
139 let mut path: Option<String> = None;
140 let mut name: Option<String> = None;
141 let mut alias: Option<String> = None;
142 let mut is_glob = false;
143 let mut is_reexport = false;
144
145 for cap in m.captures {
146 let text = &source[cap.node.byte_range()];
147 let idx = cap.index;
148 if stmt_idx == Some(idx) {
149 stmt_line = cap.node.start_position().row + 1;
150 } else if path_idx == Some(idx) {
151 path = Some(strip_import_quotes(text));
152 } else if name_idx == Some(idx) {
153 name = Some(text.to_string());
154 } else if alias_idx == Some(idx) {
155 alias = Some(text.to_string());
156 } else if glob_idx == Some(idx) {
157 is_glob = true;
158 } else if reexport_idx == Some(idx) {
159 is_reexport = true;
160 }
161 }
162
163 if is_glob {
164 results.push(FlatImport {
165 module: path,
166 name: "*".to_string(),
167 alias,
168 line: stmt_line,
169 is_reexport,
170 });
171 } else if let Some(n) = name {
172 results.push(FlatImport {
173 module: path,
174 name: n,
175 alias,
176 line: stmt_line,
177 is_reexport,
178 });
179 } else if let Some(p) = path {
180 results.push(FlatImport {
181 module: None,
182 name: p,
183 alias,
184 line: stmt_line,
185 is_reexport,
186 });
187 }
188 }
189 if results.is_empty() {
190 return None;
191 }
192
193 let mut seen: std::collections::HashMap<(Option<String>, String, usize), usize> =
197 std::collections::HashMap::new();
198 let mut deduped: Vec<FlatImport> = Vec::with_capacity(results.len());
199 for imp in results {
200 let key = (imp.module.clone(), imp.name.clone(), imp.line);
201 if let Some(&existing_idx) = seen.get(&key) {
202 if imp.is_reexport && !deduped[existing_idx].is_reexport {
204 deduped[existing_idx].is_reexport = true;
205 }
206 } else {
207 seen.insert(key, deduped.len());
208 deduped.push(imp);
209 }
210 }
211
212 Some(deduped)
213 }
214
215 fn collect_imports_with_trait(
218 root: tree_sitter::Node,
219 source: &str,
220 support: &dyn normalize_languages::Language,
221 ) -> Vec<FlatImport> {
222 let mut results = Vec::new();
223 let mut stack = vec![root];
224 while let Some(node) = stack.pop() {
225 for import in support.extract_imports(&node, source) {
226 if import.is_wildcard {
227 results.push(FlatImport {
229 module: Some(import.module.clone()),
230 name: "*".to_string(),
231 alias: None,
232 line: import.line,
233 is_reexport: false,
234 });
235 } else if import.names.is_empty() {
236 results.push(FlatImport {
238 module: None,
239 name: import.module.clone(),
240 alias: import.alias.clone(),
241 line: import.line,
242 is_reexport: false,
243 });
244 } else {
245 for n in &import.names {
247 results.push(FlatImport {
248 module: Some(import.module.clone()),
249 name: n.clone(),
250 alias: import.alias.clone(),
251 line: import.line,
252 is_reexport: false,
253 });
254 }
255 }
256 }
257 let mut cursor = node.walk();
259 let children: Vec<_> = node.children(&mut cursor).collect();
260 for child in children.into_iter().rev() {
261 stack.push(child);
262 }
263 }
264 results
265 }
266
267 pub fn find_symbol(&mut self, path: &Path, content: &str, name: &str) -> Option<FlatSymbol> {
269 let symbols = self.parse_file(path, content)?;
270 symbols.into_iter().find(|s| s.name == name)
271 }
272
273 pub fn extract_symbol_source(
275 &mut self,
276 path: &Path,
277 content: &str,
278 name: &str,
279 ) -> Option<String> {
280 let symbol = self.find_symbol(path, content, name)?;
281 let lines: Vec<&str> = content.lines().collect();
282 let start = symbol.start_line.saturating_sub(1);
283 let end = symbol.end_line.min(lines.len());
284 Some(lines[start..end].join("\n"))
285 }
286
287 #[allow(dead_code)] pub fn find_callees(&mut self, path: &Path, content: &str, symbol_name: &str) -> Vec<String> {
290 let symbol = match self.find_symbol(path, content, symbol_name) {
291 Some(s) => s,
292 None => return Vec::new(),
293 };
294
295 let calls = self.find_callees_for_symbol(path, content, &symbol);
296 let mut unique: std::collections::HashSet<String> =
297 calls.into_iter().map(|(name, _, _, _)| name).collect();
298 let mut result: Vec<_> = unique.drain().collect();
299 result.sort();
300 result
301 }
302
303 #[allow(dead_code)] pub fn find_callees_with_lines(
310 &mut self,
311 path: &Path,
312 content: &str,
313 symbol_name: &str,
314 ) -> Vec<(String, usize, Option<String>, Option<String>)> {
315 let symbol = match self.find_symbol(path, content, symbol_name) {
316 Some(s) => s,
317 None => return Vec::new(),
318 };
319 self.find_callees_for_symbol(path, content, &symbol)
320 }
321
322 pub fn find_callees_for_symbol(
327 &mut self,
328 path: &Path,
329 content: &str,
330 symbol: &FlatSymbol,
331 ) -> Vec<(String, usize, Option<String>, Option<String>)> {
332 let support = match support_for_path(path) {
333 Some(s) => s,
334 None => return Vec::new(),
335 };
336
337 let grammar_name = support.grammar_name();
338 let loader = normalize_languages::parsers::grammar_loader();
339
340 let calls_query = match loader.get_calls(grammar_name) {
341 Some(scm) => scm,
342 None => return Vec::new(),
343 };
344
345 let query = match loader.get_compiled_query(grammar_name, "calls", &calls_query) {
346 Some(q) => q,
347 None => return Vec::new(),
348 };
349
350 let lines: Vec<&str> = content.lines().collect();
351 let start = symbol.start_line.saturating_sub(1);
352 let end = symbol.end_line.min(lines.len());
353 let source = lines[start..end].join("\n");
354
355 let tree = match parsers::parse_with_grammar(grammar_name, &source) {
356 Some(t) => t,
357 None => return Vec::new(),
358 };
359
360 Self::collect_calls_with_query(&tree.root_node(), &source, &query, symbol.start_line)
361 }
362
363 fn collect_calls_with_query(
376 root: &tree_sitter::Node,
377 source: &str,
378 query: &tree_sitter::Query,
379 base_line: usize,
380 ) -> Vec<(String, usize, Option<String>, Option<String>)> {
381 let call_idx = query.capture_names().iter().position(|n| *n == "call");
382 let call_write_idx = query
383 .capture_names()
384 .iter()
385 .position(|n| *n == "call.write");
386 let qualifier_idx = query
387 .capture_names()
388 .iter()
389 .position(|n| *n == "call.qualifier");
390
391 if call_idx.is_none() && call_write_idx.is_none() {
392 return Vec::new();
393 }
394
395 let mut qcursor = tree_sitter::QueryCursor::new();
396 let mut call_map: std::collections::HashMap<
398 (String, usize),
399 (Option<String>, Option<String>),
400 > = std::collections::HashMap::new();
401
402 let mut matches = qcursor.matches(query, *root, source.as_bytes());
403 while let Some(m) = matches.next() {
404 let mut name: Option<(&str, usize)> = None;
405 let mut qualifier: Option<&str> = None;
406 let mut is_write = false;
407
408 for capture in m.captures {
409 let idx = capture.index as usize;
410 if Some(idx) == call_idx {
411 let text = &source[capture.node.byte_range()];
412 let line = capture.node.start_position().row + base_line;
413 name = Some((text, line));
414 } else if Some(idx) == call_write_idx {
415 let text = &source[capture.node.byte_range()];
416 let line = capture.node.start_position().row + base_line;
417 name = Some((text, line));
418 is_write = true;
419 } else if Some(idx) == qualifier_idx {
420 qualifier = Some(&source[capture.node.byte_range()]);
421 }
422 }
423
424 if let Some((call_name, line)) = name {
425 let access = if is_write {
426 Some("write".to_string())
427 } else {
428 None
429 };
430 let key = (call_name.to_string(), line);
431 let entry = call_map
432 .entry(key)
433 .or_insert((qualifier.map(|q| q.to_string()), None));
434 if access.is_some() {
436 entry.1 = access;
437 }
438 if let Some(q) = qualifier {
440 entry.0 = Some(q.to_string());
441 }
442 }
443 }
444
445 call_map
446 .into_iter()
447 .map(|((name, line), (qualifier, access))| (name, line, qualifier, access))
448 .collect()
449 }
450
451 pub fn find_type_refs(&mut self, path: &Path, content: &str) -> Vec<TypeRef> {
455 let lang = match normalize_languages::support_for_path(path) {
456 Some(l) => l,
457 None => return Vec::new(),
458 };
459 match lang.name() {
460 "Rust" => Self::find_rust_type_refs(content),
461 "TypeScript" | "TSX" => Self::find_typescript_type_refs(content, lang.name() == "TSX"),
462 "Python" => Self::find_python_type_refs(content),
463 "Go" => Self::find_go_type_refs(content),
464 "Java" => Self::find_java_type_refs(content),
465 "C#" => Self::find_csharp_type_refs(content),
466 "Kotlin" => Self::find_kotlin_type_refs(content),
467 "Swift" => Self::find_swift_type_refs(content),
468 "C++" => Self::find_cpp_type_refs(content),
469 "Ruby" => Self::find_ruby_type_refs(content),
470 _ => Vec::new(),
471 }
472 }
473
474 fn find_rust_type_refs(content: &str) -> Vec<TypeRef> {
476 let tree = match parsers::parse_with_grammar("rust", content) {
477 Some(t) => t,
478 None => return Vec::new(),
479 };
480
481 let mut refs = Vec::new();
482 let mut cursor = tree.root_node().walk();
483 Self::collect_rust_type_refs(&mut cursor, content, &mut refs);
484 refs
485 }
486
487 fn collect_rust_type_refs(
488 cursor: &mut tree_sitter::TreeCursor,
489 content: &str,
490 refs: &mut Vec<TypeRef>,
491 ) {
492 loop {
493 let node = cursor.node();
494 let kind = node.kind();
495
496 match kind {
497 "field_declaration" => {
499 let container = Self::ancestor_name(&node, content);
500 if let Some(type_node) = node.child_by_field_name("type") {
501 for type_name in Self::extract_type_identifiers(&type_node, content) {
502 refs.push(TypeRef {
503 source_symbol: container.clone(),
504 target_type: type_name,
505 kind: TypeRefKind::FieldType,
506 line: type_node.start_position().row + 1,
507 });
508 }
509 }
510 }
511 "function_item" | "function_signature_item" => {
513 let fn_name = node
514 .child_by_field_name("name")
515 .map(|n| content[n.byte_range()].to_string())
516 .unwrap_or_default();
517 if let Some(params) = node.child_by_field_name("parameters") {
518 Self::collect_rust_param_types(¶ms, content, &fn_name, refs);
519 }
520 if let Some(ret) = node.child_by_field_name("return_type") {
521 for type_name in Self::extract_type_identifiers(&ret, content) {
522 refs.push(TypeRef {
523 source_symbol: fn_name.clone(),
524 target_type: type_name,
525 kind: TypeRefKind::ReturnType,
526 line: ret.start_position().row + 1,
527 });
528 }
529 }
530 }
531 "impl_item" => {
533 let impl_type = node
535 .child_by_field_name("type")
536 .map(|n| content[n.byte_range()].to_string())
537 .unwrap_or_default();
538 if let Some(trait_node) = node.child_by_field_name("trait") {
540 for type_name in Self::extract_type_identifiers(&trait_node, content) {
541 refs.push(TypeRef {
542 source_symbol: impl_type.clone(),
543 target_type: type_name,
544 kind: TypeRefKind::Implements,
545 line: trait_node.start_position().row + 1,
546 });
547 }
548 }
549 }
550 "trait_item" => {
552 let trait_name = node
553 .child_by_field_name("name")
554 .map(|n| content[n.byte_range()].to_string())
555 .unwrap_or_default();
556 if let Some(bounds) = node.child_by_field_name("bounds") {
557 for type_name in Self::extract_type_identifiers(&bounds, content) {
558 refs.push(TypeRef {
559 source_symbol: trait_name.clone(),
560 target_type: type_name,
561 kind: TypeRefKind::Extends,
562 line: bounds.start_position().row + 1,
563 });
564 }
565 }
566 }
567 "type_item" => {
569 let alias_name = node
570 .child_by_field_name("name")
571 .map(|n| content[n.byte_range()].to_string())
572 .unwrap_or_default();
573 if let Some(value) = node.child_by_field_name("type") {
574 for type_name in Self::extract_type_identifiers(&value, content) {
575 refs.push(TypeRef {
576 source_symbol: alias_name.clone(),
577 target_type: type_name,
578 kind: TypeRefKind::TypeAlias,
579 line: value.start_position().row + 1,
580 });
581 }
582 }
583 }
584 "where_clause" => {
586 let fn_name = Self::ancestor_name(&node, content);
587 Self::collect_rust_where_bounds(&node, content, &fn_name, refs);
588 }
589 _ => {}
590 }
591
592 if cursor.goto_first_child() {
593 Self::collect_rust_type_refs(cursor, content, refs);
594 cursor.goto_parent();
595 }
596
597 if !cursor.goto_next_sibling() {
598 break;
599 }
600 }
601 }
602
603 fn collect_rust_param_types(
605 params: &tree_sitter::Node,
606 content: &str,
607 fn_name: &str,
608 refs: &mut Vec<TypeRef>,
609 ) {
610 let mut cursor = params.walk();
611 if cursor.goto_first_child() {
612 loop {
613 let child = cursor.node();
614 if (child.kind() == "parameter" || child.kind() == "self_parameter")
615 && let Some(type_node) = child.child_by_field_name("type")
616 {
617 for type_name in Self::extract_type_identifiers(&type_node, content) {
618 refs.push(TypeRef {
619 source_symbol: fn_name.to_string(),
620 target_type: type_name,
621 kind: TypeRefKind::ParamType,
622 line: type_node.start_position().row + 1,
623 });
624 }
625 }
626 if !cursor.goto_next_sibling() {
627 break;
628 }
629 }
630 }
631 }
632
633 fn collect_rust_where_bounds(
635 where_node: &tree_sitter::Node,
636 content: &str,
637 fn_name: &str,
638 refs: &mut Vec<TypeRef>,
639 ) {
640 let mut cursor = where_node.walk();
641 if cursor.goto_first_child() {
642 loop {
643 let child = cursor.node();
644 if child.kind() == "where_predicate"
645 && let Some(bounds) = child.child_by_field_name("bounds")
646 {
647 for type_name in Self::extract_type_identifiers(&bounds, content) {
648 refs.push(TypeRef {
649 source_symbol: fn_name.to_string(),
650 target_type: type_name,
651 kind: TypeRefKind::GenericBound,
652 line: bounds.start_position().row + 1,
653 });
654 }
655 }
656 if !cursor.goto_next_sibling() {
657 break;
658 }
659 }
660 }
661 }
662
663 fn find_typescript_type_refs(content: &str, is_tsx: bool) -> Vec<TypeRef> {
665 let grammar = if is_tsx { "tsx" } else { "typescript" };
666 let tree = match parsers::parse_with_grammar(grammar, content) {
667 Some(t) => t,
668 None => return Vec::new(),
669 };
670
671 let mut refs = Vec::new();
672 let mut cursor = tree.root_node().walk();
673 Self::collect_typescript_type_refs(&mut cursor, content, &mut refs);
674 refs
675 }
676
677 fn collect_typescript_type_refs(
678 cursor: &mut tree_sitter::TreeCursor,
679 content: &str,
680 refs: &mut Vec<TypeRef>,
681 ) {
682 loop {
683 let node = cursor.node();
684 let kind = node.kind();
685
686 match kind {
687 "class_declaration" => {
689 let class_name = node
690 .child_by_field_name("name")
691 .map(|n| content[n.byte_range()].to_string())
692 .unwrap_or_default();
693 let mut child_cursor = node.walk();
695 if child_cursor.goto_first_child() {
696 loop {
697 let child = child_cursor.node();
698 match child.kind() {
699 "extends_clause" => {
700 for type_name in Self::extract_type_identifiers(&child, content)
701 {
702 refs.push(TypeRef {
703 source_symbol: class_name.clone(),
704 target_type: type_name,
705 kind: TypeRefKind::Extends,
706 line: child.start_position().row + 1,
707 });
708 }
709 }
710 "implements_clause" => {
711 for type_name in Self::extract_type_identifiers(&child, content)
712 {
713 refs.push(TypeRef {
714 source_symbol: class_name.clone(),
715 target_type: type_name,
716 kind: TypeRefKind::Implements,
717 line: child.start_position().row + 1,
718 });
719 }
720 }
721 _ => {}
722 }
723 if !child_cursor.goto_next_sibling() {
724 break;
725 }
726 }
727 }
728 }
729 "interface_declaration" => {
731 let iface_name = node
732 .child_by_field_name("name")
733 .map(|n| content[n.byte_range()].to_string())
734 .unwrap_or_default();
735 let mut child_cursor = node.walk();
736 if child_cursor.goto_first_child() {
737 loop {
738 let child = child_cursor.node();
739 if child.kind() == "extends_type_clause"
740 || child.kind() == "extends_clause"
741 {
742 for type_name in Self::extract_type_identifiers(&child, content) {
743 refs.push(TypeRef {
744 source_symbol: iface_name.clone(),
745 target_type: type_name,
746 kind: TypeRefKind::Extends,
747 line: child.start_position().row + 1,
748 });
749 }
750 }
751 if !child_cursor.goto_next_sibling() {
752 break;
753 }
754 }
755 }
756 }
757 "type_alias_declaration" => {
759 let alias_name = node
760 .child_by_field_name("name")
761 .map(|n| content[n.byte_range()].to_string())
762 .unwrap_or_default();
763 if let Some(value) = node.child_by_field_name("value") {
764 for type_name in Self::extract_type_identifiers(&value, content) {
765 refs.push(TypeRef {
766 source_symbol: alias_name.clone(),
767 target_type: type_name,
768 kind: TypeRefKind::TypeAlias,
769 line: value.start_position().row + 1,
770 });
771 }
772 }
773 }
774 "function_declaration" | "method_definition" => {
776 let fn_name = node
777 .child_by_field_name("name")
778 .map(|n| content[n.byte_range()].to_string())
779 .unwrap_or_default();
780 if let Some(params) = node.child_by_field_name("parameters") {
781 Self::collect_ts_param_types(¶ms, content, &fn_name, refs);
782 }
783 if let Some(ret) = node.child_by_field_name("return_type") {
784 for type_name in Self::extract_type_identifiers(&ret, content) {
785 refs.push(TypeRef {
786 source_symbol: fn_name.clone(),
787 target_type: type_name,
788 kind: TypeRefKind::ReturnType,
789 line: ret.start_position().row + 1,
790 });
791 }
792 }
793 }
794 "public_field_definition" | "property_signature" => {
796 let container = Self::ancestor_name(&node, content);
797 if let Some(type_ann) = node.child_by_field_name("type") {
798 for type_name in Self::extract_type_identifiers(&type_ann, content) {
799 refs.push(TypeRef {
800 source_symbol: container.clone(),
801 target_type: type_name,
802 kind: TypeRefKind::FieldType,
803 line: type_ann.start_position().row + 1,
804 });
805 }
806 }
807 }
808 _ => {}
809 }
810
811 if cursor.goto_first_child() {
812 Self::collect_typescript_type_refs(cursor, content, refs);
813 cursor.goto_parent();
814 }
815
816 if !cursor.goto_next_sibling() {
817 break;
818 }
819 }
820 }
821
822 fn collect_ts_param_types(
824 params: &tree_sitter::Node,
825 content: &str,
826 fn_name: &str,
827 refs: &mut Vec<TypeRef>,
828 ) {
829 let mut cursor = params.walk();
830 if cursor.goto_first_child() {
831 loop {
832 let child = cursor.node();
833 if child.kind().contains("parameter")
835 && let Some(type_ann) = child.child_by_field_name("type")
836 {
837 for type_name in Self::extract_type_identifiers(&type_ann, content) {
838 refs.push(TypeRef {
839 source_symbol: fn_name.to_string(),
840 target_type: type_name,
841 kind: TypeRefKind::ParamType,
842 line: type_ann.start_position().row + 1,
843 });
844 }
845 }
846 if !cursor.goto_next_sibling() {
847 break;
848 }
849 }
850 }
851 }
852
853 fn find_go_type_refs(content: &str) -> Vec<TypeRef> {
855 let tree = match parsers::parse_with_grammar("go", content) {
856 Some(t) => t,
857 None => return Vec::new(),
858 };
859
860 let mut refs = Vec::new();
861 let mut cursor = tree.root_node().walk();
862 Self::collect_go_type_refs(&mut cursor, content, &mut refs);
863 refs
864 }
865
866 fn collect_go_type_refs(
867 cursor: &mut tree_sitter::TreeCursor,
868 content: &str,
869 refs: &mut Vec<TypeRef>,
870 ) {
871 loop {
872 let node = cursor.node();
873 let kind = node.kind();
874
875 match kind {
876 "type_declaration" => {
880 let mut child_cursor = node.walk();
881 if child_cursor.goto_first_child() {
882 loop {
883 let child = child_cursor.node();
884 match child.kind() {
885 "type_spec" => {
886 Self::collect_go_type_spec(&child, content, refs);
887 }
888 "type_alias" => {
889 Self::collect_go_type_alias(&child, content, refs);
890 }
891 _ => {}
892 }
893 if !child_cursor.goto_next_sibling() {
894 break;
895 }
896 }
897 }
898 if !cursor.goto_next_sibling() {
900 break;
901 }
902 continue;
903 }
904 "method_declaration" => {
906 let fn_name = node
907 .child_by_field_name("name")
908 .map(|n| content[n.byte_range()].to_string())
909 .unwrap_or_default();
910 if let Some(params) = node.child_by_field_name("parameters") {
913 Self::collect_go_param_types(¶ms, content, &fn_name, refs);
914 }
915 if let Some(result) = node.child_by_field_name("result") {
917 Self::collect_go_result_types(&result, content, &fn_name, refs);
918 }
919 }
920 "function_declaration" => {
922 let fn_name = node
923 .child_by_field_name("name")
924 .map(|n| content[n.byte_range()].to_string())
925 .unwrap_or_default();
926 if let Some(params) = node.child_by_field_name("parameters") {
927 Self::collect_go_param_types(¶ms, content, &fn_name, refs);
928 }
929 if let Some(result) = node.child_by_field_name("result") {
930 Self::collect_go_result_types(&result, content, &fn_name, refs);
931 }
932 }
933 _ => {}
934 }
935
936 if cursor.goto_first_child() {
937 Self::collect_go_type_refs(cursor, content, refs);
938 cursor.goto_parent();
939 }
940
941 if !cursor.goto_next_sibling() {
942 break;
943 }
944 }
945 }
946
947 fn collect_go_type_spec(node: &tree_sitter::Node, content: &str, refs: &mut Vec<TypeRef>) {
949 let type_name = node
950 .child_by_field_name("name")
951 .map(|n| content[n.byte_range()].to_string())
952 .unwrap_or_default();
953 if type_name.is_empty() {
954 return;
955 }
956
957 let type_body = match node.child_by_field_name("type") {
958 Some(t) => t,
959 None => return,
960 };
961
962 match type_body.kind() {
963 "struct_type" => {
965 let mut cur = type_body.walk();
966 if cur.goto_first_child() {
967 loop {
968 let child = cur.node();
969 if child.kind() == "field_declaration_list" {
970 let mut fc = child.walk();
971 if fc.goto_first_child() {
972 loop {
973 let field = fc.node();
974 if field.kind() == "field_declaration"
975 && let Some(ft) = field.child_by_field_name("type")
976 {
977 let type_name_str = Self::go_type_name(&ft, content);
979 if !type_name_str.is_empty()
980 && !Self::is_primitive_type(&type_name_str)
981 && !Self::is_go_primitive(&type_name_str)
982 {
983 refs.push(TypeRef {
984 source_symbol: type_name.clone(),
985 target_type: type_name_str,
986 kind: TypeRefKind::FieldType,
987 line: ft.start_position().row + 1,
988 });
989 }
990 }
991 if !fc.goto_next_sibling() {
992 break;
993 }
994 }
995 }
996 }
997 if !cur.goto_next_sibling() {
998 break;
999 }
1000 }
1001 }
1002 }
1003 "interface_type" => {
1005 let mut cur = type_body.walk();
1006 if cur.goto_first_child() {
1007 loop {
1008 let child = cur.node();
1009 if child.kind() == "type_elem" {
1011 let mut ec = child.walk();
1012 if ec.goto_first_child() {
1013 loop {
1014 let elem = ec.node();
1015 if elem.kind() == "type_identifier" {
1016 let embedded = content[elem.byte_range()].to_string();
1017 if !Self::is_primitive_type(&embedded)
1018 && !Self::is_go_primitive(&embedded)
1019 {
1020 refs.push(TypeRef {
1021 source_symbol: type_name.clone(),
1022 target_type: embedded,
1023 kind: TypeRefKind::Implements,
1024 line: elem.start_position().row + 1,
1025 });
1026 }
1027 }
1028 if !ec.goto_next_sibling() {
1029 break;
1030 }
1031 }
1032 }
1033 }
1034 if !cur.goto_next_sibling() {
1035 break;
1036 }
1037 }
1038 }
1039 }
1040 _ => {}
1041 }
1042 }
1043
1044 fn collect_go_type_alias(node: &tree_sitter::Node, content: &str, refs: &mut Vec<TypeRef>) {
1046 let alias_name = node
1047 .child_by_field_name("name")
1048 .map(|n| content[n.byte_range()].to_string())
1049 .unwrap_or_default();
1050 if alias_name.is_empty() {
1051 return;
1052 }
1053 if let Some(type_node) = node.child_by_field_name("type") {
1054 let target = Self::go_type_name(&type_node, content);
1055 if !target.is_empty()
1056 && !Self::is_primitive_type(&target)
1057 && !Self::is_go_primitive(&target)
1058 {
1059 refs.push(TypeRef {
1060 source_symbol: alias_name,
1061 target_type: target,
1062 kind: TypeRefKind::TypeAlias,
1063 line: type_node.start_position().row + 1,
1064 });
1065 }
1066 }
1067 }
1068
1069 fn go_type_name(node: &tree_sitter::Node, content: &str) -> String {
1073 match node.kind() {
1074 "type_identifier" => content[node.byte_range()].to_string(),
1075 "qualified_type" => {
1076 node.child_by_field_name("name")
1078 .map(|n| content[n.byte_range()].to_string())
1079 .unwrap_or_default()
1080 }
1081 "pointer_type" => {
1082 let mut c = node.walk();
1084 if c.goto_first_child() {
1085 loop {
1086 let child = c.node();
1087 if child.kind() == "type_identifier" || child.kind() == "qualified_type" {
1088 return Self::go_type_name(&child, content);
1089 }
1090 if !c.goto_next_sibling() {
1091 break;
1092 }
1093 }
1094 }
1095 String::new()
1096 }
1097 "slice_type" | "array_type" => {
1098 node.child_by_field_name("element")
1100 .map(|n| Self::go_type_name(&n, content))
1101 .unwrap_or_default()
1102 }
1103 _ => String::new(),
1104 }
1105 }
1106
1107 fn collect_go_param_types(
1109 params: &tree_sitter::Node,
1110 content: &str,
1111 fn_name: &str,
1112 refs: &mut Vec<TypeRef>,
1113 ) {
1114 let mut cursor = params.walk();
1115 if cursor.goto_first_child() {
1116 loop {
1117 let child = cursor.node();
1118 if child.kind() == "parameter_declaration"
1119 && let Some(type_node) = child.child_by_field_name("type")
1120 {
1121 let type_name = Self::go_type_name(&type_node, content);
1122 if !type_name.is_empty()
1123 && !Self::is_primitive_type(&type_name)
1124 && !Self::is_go_primitive(&type_name)
1125 {
1126 refs.push(TypeRef {
1127 source_symbol: fn_name.to_string(),
1128 target_type: type_name,
1129 kind: TypeRefKind::ParamType,
1130 line: type_node.start_position().row + 1,
1131 });
1132 }
1133 }
1134 if !cursor.goto_next_sibling() {
1135 break;
1136 }
1137 }
1138 }
1139 }
1140
1141 fn collect_go_result_types(
1143 result: &tree_sitter::Node,
1144 content: &str,
1145 fn_name: &str,
1146 refs: &mut Vec<TypeRef>,
1147 ) {
1148 match result.kind() {
1149 "type_identifier" | "qualified_type" | "pointer_type" | "slice_type" | "array_type" => {
1150 let type_name = Self::go_type_name(result, content);
1151 if !type_name.is_empty()
1152 && !Self::is_primitive_type(&type_name)
1153 && !Self::is_go_primitive(&type_name)
1154 {
1155 refs.push(TypeRef {
1156 source_symbol: fn_name.to_string(),
1157 target_type: type_name,
1158 kind: TypeRefKind::ReturnType,
1159 line: result.start_position().row + 1,
1160 });
1161 }
1162 }
1163 "parameter_list" => {
1165 let mut cursor = result.walk();
1166 if cursor.goto_first_child() {
1167 loop {
1168 let child = cursor.node();
1169 if child.kind() == "parameter_declaration"
1170 && let Some(type_node) = child.child_by_field_name("type")
1171 {
1172 let type_name = Self::go_type_name(&type_node, content);
1173 if !type_name.is_empty()
1174 && !Self::is_primitive_type(&type_name)
1175 && !Self::is_go_primitive(&type_name)
1176 {
1177 refs.push(TypeRef {
1178 source_symbol: fn_name.to_string(),
1179 target_type: type_name,
1180 kind: TypeRefKind::ReturnType,
1181 line: type_node.start_position().row + 1,
1182 });
1183 }
1184 }
1185 if !cursor.goto_next_sibling() {
1186 break;
1187 }
1188 }
1189 }
1190 }
1191 _ => {}
1192 }
1193 }
1194
1195 fn is_go_primitive(name: &str) -> bool {
1197 matches!(
1198 name,
1199 "int"
1200 | "int8"
1201 | "int16"
1202 | "int32"
1203 | "int64"
1204 | "uint"
1205 | "uint8"
1206 | "uint16"
1207 | "uint32"
1208 | "uint64"
1209 | "uintptr"
1210 | "float32"
1211 | "float64"
1212 | "complex64"
1213 | "complex128"
1214 | "bool"
1215 | "string"
1216 | "byte"
1217 | "rune"
1218 | "error"
1219 )
1220 }
1221
1222 fn find_java_type_refs(content: &str) -> Vec<TypeRef> {
1224 let tree = match parsers::parse_with_grammar("java", content) {
1225 Some(t) => t,
1226 None => return Vec::new(),
1227 };
1228
1229 let mut refs = Vec::new();
1230 let mut cursor = tree.root_node().walk();
1231 Self::collect_java_type_refs(&mut cursor, content, &mut refs);
1232 refs
1233 }
1234
1235 fn collect_java_type_refs(
1236 cursor: &mut tree_sitter::TreeCursor,
1237 content: &str,
1238 refs: &mut Vec<TypeRef>,
1239 ) {
1240 loop {
1241 let node = cursor.node();
1242 let kind = node.kind();
1243
1244 match kind {
1245 "class_declaration" => {
1247 let class_name = node
1248 .child_by_field_name("name")
1249 .map(|n| content[n.byte_range()].to_string())
1250 .unwrap_or_default();
1251 if let Some(superclass) = node.child_by_field_name("superclass") {
1253 for type_name in Self::extract_type_identifiers(&superclass, content) {
1254 refs.push(TypeRef {
1255 source_symbol: class_name.clone(),
1256 target_type: type_name,
1257 kind: TypeRefKind::Extends,
1258 line: superclass.start_position().row + 1,
1259 });
1260 }
1261 }
1262 if let Some(interfaces) = node.child_by_field_name("interfaces") {
1264 for type_name in Self::extract_type_identifiers(&interfaces, content) {
1265 refs.push(TypeRef {
1266 source_symbol: class_name.clone(),
1267 target_type: type_name,
1268 kind: TypeRefKind::Implements,
1269 line: interfaces.start_position().row + 1,
1270 });
1271 }
1272 }
1273 }
1274 "interface_declaration" => {
1276 let iface_name = node
1277 .child_by_field_name("name")
1278 .map(|n| content[n.byte_range()].to_string())
1279 .unwrap_or_default();
1280 let mut child_cursor = node.walk();
1282 if child_cursor.goto_first_child() {
1283 loop {
1284 let child = child_cursor.node();
1285 if child.kind() == "extends_interfaces" {
1286 for type_name in Self::extract_type_identifiers(&child, content) {
1287 refs.push(TypeRef {
1288 source_symbol: iface_name.clone(),
1289 target_type: type_name,
1290 kind: TypeRefKind::Extends,
1291 line: child.start_position().row + 1,
1292 });
1293 }
1294 }
1295 if !child_cursor.goto_next_sibling() {
1296 break;
1297 }
1298 }
1299 }
1300 }
1301 "field_declaration" => {
1303 let container = Self::ancestor_name(&node, content);
1304 if let Some(type_node) = node.child_by_field_name("type") {
1305 for type_name in Self::extract_type_identifiers(&type_node, content) {
1306 refs.push(TypeRef {
1307 source_symbol: container.clone(),
1308 target_type: type_name,
1309 kind: TypeRefKind::FieldType,
1310 line: type_node.start_position().row + 1,
1311 });
1312 }
1313 }
1314 }
1315 "method_declaration" => {
1317 let fn_name = node
1318 .child_by_field_name("name")
1319 .map(|n| content[n.byte_range()].to_string())
1320 .unwrap_or_default();
1321 if let Some(ret) = node.child_by_field_name("type") {
1323 for type_name in Self::extract_type_identifiers(&ret, content) {
1324 refs.push(TypeRef {
1325 source_symbol: fn_name.clone(),
1326 target_type: type_name,
1327 kind: TypeRefKind::ReturnType,
1328 line: ret.start_position().row + 1,
1329 });
1330 }
1331 }
1332 if let Some(params) = node.child_by_field_name("parameters") {
1334 Self::collect_java_param_types(¶ms, content, &fn_name, refs);
1335 }
1336 if let Some(type_params) = node.child_by_field_name("type_parameters") {
1338 Self::collect_java_generic_bounds(&type_params, content, &fn_name, refs);
1339 }
1340 }
1341 _ => {}
1342 }
1343
1344 if cursor.goto_first_child() {
1345 Self::collect_java_type_refs(cursor, content, refs);
1346 cursor.goto_parent();
1347 }
1348
1349 if !cursor.goto_next_sibling() {
1350 break;
1351 }
1352 }
1353 }
1354
1355 fn collect_java_param_types(
1357 params: &tree_sitter::Node,
1358 content: &str,
1359 fn_name: &str,
1360 refs: &mut Vec<TypeRef>,
1361 ) {
1362 let mut cursor = params.walk();
1363 if cursor.goto_first_child() {
1364 loop {
1365 let child = cursor.node();
1366 if child.kind() == "formal_parameter"
1367 && let Some(type_node) = child.child_by_field_name("type")
1368 {
1369 for type_name in Self::extract_type_identifiers(&type_node, content) {
1370 refs.push(TypeRef {
1371 source_symbol: fn_name.to_string(),
1372 target_type: type_name,
1373 kind: TypeRefKind::ParamType,
1374 line: type_node.start_position().row + 1,
1375 });
1376 }
1377 }
1378 if !cursor.goto_next_sibling() {
1379 break;
1380 }
1381 }
1382 }
1383 }
1384
1385 fn collect_java_generic_bounds(
1387 type_params: &tree_sitter::Node,
1388 content: &str,
1389 fn_name: &str,
1390 refs: &mut Vec<TypeRef>,
1391 ) {
1392 let mut cursor = type_params.walk();
1393 if cursor.goto_first_child() {
1394 loop {
1395 let child = cursor.node();
1396 if child.kind() == "type_parameter" {
1397 let mut tc = child.walk();
1399 if tc.goto_first_child() {
1400 loop {
1401 let tc_child = tc.node();
1402 if tc_child.kind() == "type_bound" {
1403 for type_name in Self::extract_type_identifiers(&tc_child, content)
1404 {
1405 refs.push(TypeRef {
1406 source_symbol: fn_name.to_string(),
1407 target_type: type_name,
1408 kind: TypeRefKind::GenericBound,
1409 line: tc_child.start_position().row + 1,
1410 });
1411 }
1412 }
1413 if !tc.goto_next_sibling() {
1414 break;
1415 }
1416 }
1417 }
1418 }
1419 if !cursor.goto_next_sibling() {
1420 break;
1421 }
1422 }
1423 }
1424 }
1425
1426 fn find_python_type_refs(content: &str) -> Vec<TypeRef> {
1428 let tree = match parsers::parse_with_grammar("python", content) {
1429 Some(t) => t,
1430 None => return Vec::new(),
1431 };
1432
1433 let mut refs = Vec::new();
1434 let mut cursor = tree.root_node().walk();
1435 Self::collect_python_type_refs(&mut cursor, content, &mut refs);
1436 refs
1437 }
1438
1439 fn collect_python_type_refs(
1440 cursor: &mut tree_sitter::TreeCursor,
1441 content: &str,
1442 refs: &mut Vec<TypeRef>,
1443 ) {
1444 loop {
1445 let node = cursor.node();
1446 let kind = node.kind();
1447
1448 match kind {
1449 "class_definition" => {
1451 let class_name = node
1452 .child_by_field_name("name")
1453 .map(|n| content[n.byte_range()].to_string())
1454 .unwrap_or_default();
1455 if let Some(bases) = node.child_by_field_name("superclasses") {
1456 let mut base_cursor = bases.walk();
1458 if base_cursor.goto_first_child() {
1459 loop {
1460 let base = base_cursor.node();
1461 if base.kind() == "identifier" || base.kind() == "attribute" {
1462 let base_name = content[base.byte_range()].to_string();
1463 refs.push(TypeRef {
1464 source_symbol: class_name.clone(),
1465 target_type: base_name,
1466 kind: TypeRefKind::Extends,
1467 line: base.start_position().row + 1,
1468 });
1469 }
1470 if !base_cursor.goto_next_sibling() {
1471 break;
1472 }
1473 }
1474 }
1475 }
1476 }
1477 "function_definition" => {
1479 let fn_name = node
1480 .child_by_field_name("name")
1481 .map(|n| content[n.byte_range()].to_string())
1482 .unwrap_or_default();
1483 if let Some(params) = node.child_by_field_name("parameters") {
1484 Self::collect_python_param_types(¶ms, content, &fn_name, refs);
1485 }
1486 if let Some(ret) = node.child_by_field_name("return_type") {
1487 for type_name in Self::extract_type_identifiers(&ret, content) {
1488 refs.push(TypeRef {
1489 source_symbol: fn_name.clone(),
1490 target_type: type_name,
1491 kind: TypeRefKind::ReturnType,
1492 line: ret.start_position().row + 1,
1493 });
1494 }
1495 }
1496 }
1497 "typed_parameter" | "typed_default_parameter" => {
1499 }
1501 _ => {}
1502 }
1503
1504 if cursor.goto_first_child() {
1505 Self::collect_python_type_refs(cursor, content, refs);
1506 cursor.goto_parent();
1507 }
1508
1509 if !cursor.goto_next_sibling() {
1510 break;
1511 }
1512 }
1513 }
1514
1515 fn collect_python_param_types(
1517 params: &tree_sitter::Node,
1518 content: &str,
1519 fn_name: &str,
1520 refs: &mut Vec<TypeRef>,
1521 ) {
1522 let mut cursor = params.walk();
1523 if cursor.goto_first_child() {
1524 loop {
1525 let child = cursor.node();
1526 if (child.kind() == "typed_parameter" || child.kind() == "typed_default_parameter")
1528 && let Some(type_node) = child.child_by_field_name("type")
1529 {
1530 for type_name in Self::extract_type_identifiers(&type_node, content) {
1531 if type_name != "self" {
1533 refs.push(TypeRef {
1534 source_symbol: fn_name.to_string(),
1535 target_type: type_name,
1536 kind: TypeRefKind::ParamType,
1537 line: type_node.start_position().row + 1,
1538 });
1539 }
1540 }
1541 }
1542 if !cursor.goto_next_sibling() {
1543 break;
1544 }
1545 }
1546 }
1547 }
1548
1549 fn find_csharp_type_refs(content: &str) -> Vec<TypeRef> {
1553 let tree = match parsers::parse_with_grammar("c-sharp", content) {
1554 Some(t) => t,
1555 None => return Vec::new(),
1556 };
1557
1558 let mut refs = Vec::new();
1559 let mut cursor = tree.root_node().walk();
1560 Self::collect_csharp_type_refs(&mut cursor, content, &mut refs);
1561 refs
1562 }
1563
1564 fn collect_csharp_type_refs(
1565 cursor: &mut tree_sitter::TreeCursor,
1566 content: &str,
1567 refs: &mut Vec<TypeRef>,
1568 ) {
1569 loop {
1570 let node = cursor.node();
1571 let kind = node.kind();
1572
1573 match kind {
1574 "class_declaration"
1576 | "interface_declaration"
1577 | "struct_declaration"
1578 | "record_declaration" => {
1579 let class_name = node
1580 .child_by_field_name("name")
1581 .map(|n| content[n.byte_range()].to_string())
1582 .unwrap_or_default();
1583 let mut child_cursor = node.walk();
1585 if child_cursor.goto_first_child() {
1586 loop {
1587 let child = child_cursor.node();
1588 if child.kind() == "base_list" {
1589 let mut bl = child.walk();
1590 if bl.goto_first_child() {
1591 loop {
1592 let base = bl.node();
1593 let type_name = match base.kind() {
1595 "identifier" | "qualified_name" => {
1596 content[base.byte_range()].to_string()
1597 }
1598 "generic_name" => base
1599 .child_by_field_name("name")
1600 .map(|n| content[n.byte_range()].to_string())
1601 .unwrap_or_default(),
1602 _ => String::new(),
1603 };
1604 if !type_name.is_empty()
1605 && !Self::is_primitive_type(&type_name)
1606 && !Self::is_csharp_primitive(&type_name)
1607 {
1608 let ref_kind = if kind == "interface_declaration" {
1612 TypeRefKind::Extends
1613 } else {
1614 TypeRefKind::Implements
1615 };
1616 refs.push(TypeRef {
1617 source_symbol: class_name.clone(),
1618 target_type: type_name,
1619 kind: ref_kind,
1620 line: base.start_position().row + 1,
1621 });
1622 }
1623 if !bl.goto_next_sibling() {
1624 break;
1625 }
1626 }
1627 }
1628 }
1629 if !child_cursor.goto_next_sibling() {
1630 break;
1631 }
1632 }
1633 }
1634 }
1635 "field_declaration" => {
1638 let container = Self::ancestor_name(&node, content);
1639 let mut fc = node.walk();
1640 if fc.goto_first_child() {
1641 loop {
1642 let child = fc.node();
1643 if child.kind() == "variable_declaration" {
1644 let mut vc = child.walk();
1646 if vc.goto_first_child() {
1647 let type_node = vc.node();
1648 if type_node.kind() == "identifier"
1649 || type_node.kind() == "generic_name"
1650 || type_node.kind() == "qualified_name"
1651 || type_node.kind() == "nullable_type"
1652 {
1653 for type_name in
1654 Self::extract_type_identifiers(&type_node, content)
1655 {
1656 if !Self::is_csharp_primitive(&type_name) {
1657 refs.push(TypeRef {
1658 source_symbol: container.clone(),
1659 target_type: type_name,
1660 kind: TypeRefKind::FieldType,
1661 line: type_node.start_position().row + 1,
1662 });
1663 }
1664 }
1665 }
1666 }
1667 break;
1668 }
1669 if !fc.goto_next_sibling() {
1670 break;
1671 }
1672 }
1673 }
1674 }
1675 "method_declaration" => {
1680 let mut identifiers: Vec<tree_sitter::Node> = Vec::new();
1682 let mut param_list: Option<tree_sitter::Node> = None;
1683 let mut mc = node.walk();
1684 if mc.goto_first_child() {
1685 loop {
1686 let child = mc.node();
1687 match child.kind() {
1688 "identifier" | "generic_name" | "qualified_name"
1689 | "nullable_type" | "predefined_type" => {
1690 identifiers.push(child);
1691 }
1692 "parameter_list" => {
1693 param_list = Some(child);
1694 }
1695 _ => {}
1696 }
1697 if !mc.goto_next_sibling() {
1698 break;
1699 }
1700 }
1701 }
1702 let fn_name = identifiers
1704 .get(1)
1705 .map(|n| content[n.byte_range()].to_string())
1706 .unwrap_or_default();
1707 if let Some(ret_node) = identifiers.first() {
1708 for type_name in Self::extract_type_identifiers(ret_node, content) {
1709 if !Self::is_csharp_primitive(&type_name) {
1710 refs.push(TypeRef {
1711 source_symbol: fn_name.clone(),
1712 target_type: type_name,
1713 kind: TypeRefKind::ReturnType,
1714 line: ret_node.start_position().row + 1,
1715 });
1716 }
1717 }
1718 }
1719 if let Some(params) = param_list {
1721 Self::collect_csharp_param_types(¶ms, content, &fn_name, refs);
1722 }
1723 if !fn_name.is_empty() {
1725 Self::collect_csharp_generic_bounds(&node, content, &fn_name, refs);
1726 }
1727 }
1728 _ => {}
1729 }
1730
1731 if cursor.goto_first_child() {
1732 Self::collect_csharp_type_refs(cursor, content, refs);
1733 cursor.goto_parent();
1734 }
1735
1736 if !cursor.goto_next_sibling() {
1737 break;
1738 }
1739 }
1740 }
1741
1742 fn collect_csharp_param_types(
1743 params: &tree_sitter::Node,
1744 content: &str,
1745 fn_name: &str,
1746 refs: &mut Vec<TypeRef>,
1747 ) {
1748 let mut cursor = params.walk();
1749 if cursor.goto_first_child() {
1750 loop {
1751 let child = cursor.node();
1752 if child.kind() == "parameter" {
1753 let mut pc = child.walk();
1755 if pc.goto_first_child() {
1756 let type_node = pc.node();
1757 if matches!(
1758 type_node.kind(),
1759 "identifier"
1760 | "generic_name"
1761 | "qualified_name"
1762 | "nullable_type"
1763 | "predefined_type"
1764 ) {
1765 for type_name in Self::extract_type_identifiers(&type_node, content) {
1766 if !Self::is_csharp_primitive(&type_name) {
1767 refs.push(TypeRef {
1768 source_symbol: fn_name.to_string(),
1769 target_type: type_name,
1770 kind: TypeRefKind::ParamType,
1771 line: type_node.start_position().row + 1,
1772 });
1773 }
1774 }
1775 }
1776 }
1777 }
1778 if !cursor.goto_next_sibling() {
1779 break;
1780 }
1781 }
1782 }
1783 }
1784
1785 fn collect_csharp_generic_bounds(
1787 method_node: &tree_sitter::Node,
1788 content: &str,
1789 fn_name: &str,
1790 refs: &mut Vec<TypeRef>,
1791 ) {
1792 let mut cursor = method_node.walk();
1793 if cursor.goto_first_child() {
1794 loop {
1795 let child = cursor.node();
1796 if child.kind() == "type_parameter_constraints_clause" {
1797 let mut cc = child.walk();
1799 if cc.goto_first_child() {
1800 loop {
1801 let cc_child = cc.node();
1802 if cc_child.kind() == "type_constraint"
1804 || cc_child.kind() == "constructor_constraint"
1805 {
1806 for type_name in Self::extract_type_identifiers(&cc_child, content)
1807 {
1808 if !Self::is_csharp_primitive(&type_name) {
1809 refs.push(TypeRef {
1810 source_symbol: fn_name.to_string(),
1811 target_type: type_name,
1812 kind: TypeRefKind::GenericBound,
1813 line: cc_child.start_position().row + 1,
1814 });
1815 }
1816 }
1817 }
1818 if !cc.goto_next_sibling() {
1819 break;
1820 }
1821 }
1822 }
1823 }
1824 if !cursor.goto_next_sibling() {
1825 break;
1826 }
1827 }
1828 }
1829 }
1830
1831 fn is_csharp_primitive(name: &str) -> bool {
1832 matches!(
1833 name,
1834 "int"
1835 | "uint"
1836 | "long"
1837 | "ulong"
1838 | "short"
1839 | "ushort"
1840 | "byte"
1841 | "sbyte"
1842 | "float"
1843 | "double"
1844 | "decimal"
1845 | "bool"
1846 | "char"
1847 | "string"
1848 | "object"
1849 | "void"
1850 | "dynamic"
1851 | "var"
1852 )
1853 }
1854
1855 fn find_kotlin_type_refs(content: &str) -> Vec<TypeRef> {
1859 let tree = match parsers::parse_with_grammar("kotlin", content) {
1860 Some(t) => t,
1861 None => return Vec::new(),
1862 };
1863
1864 let mut refs = Vec::new();
1865 let mut cursor = tree.root_node().walk();
1866 Self::collect_kotlin_type_refs(&mut cursor, content, &mut refs);
1867 refs
1868 }
1869
1870 fn collect_kotlin_type_refs(
1871 cursor: &mut tree_sitter::TreeCursor,
1872 content: &str,
1873 refs: &mut Vec<TypeRef>,
1874 ) {
1875 loop {
1876 let node = cursor.node();
1877 let kind = node.kind();
1878
1879 match kind {
1880 "class_declaration" => {
1882 let class_name = node
1883 .child_by_field_name("name")
1884 .map(|n| content[n.byte_range()].to_string())
1885 .unwrap_or_else(|| {
1886 let mut cur = node.walk();
1888 let mut name = String::new();
1889 if cur.goto_first_child() {
1890 loop {
1891 if cur.node().kind() == "type_identifier" {
1892 name = content[cur.node().byte_range()].to_string();
1893 break;
1894 }
1895 if !cur.goto_next_sibling() {
1896 break;
1897 }
1898 }
1899 }
1900 name
1901 });
1902 let mut child_cursor = node.walk();
1904 if child_cursor.goto_first_child() {
1905 loop {
1906 let child = child_cursor.node();
1907 if child.kind() == "delegation_specifier" {
1908 let type_name = Self::kotlin_first_type_identifier(&child, content);
1911 if !type_name.is_empty()
1912 && !Self::is_primitive_type(&type_name)
1913 && !Self::is_kotlin_primitive(&type_name)
1914 {
1915 refs.push(TypeRef {
1916 source_symbol: class_name.clone(),
1917 target_type: type_name,
1918 kind: TypeRefKind::Implements,
1919 line: child.start_position().row + 1,
1920 });
1921 }
1922 }
1923 if !child_cursor.goto_next_sibling() {
1924 break;
1925 }
1926 }
1927 }
1928 }
1929 "property_declaration" => {
1932 let container = Self::ancestor_name(&node, content);
1933 let mut pc = node.walk();
1935 if pc.goto_first_child() {
1936 loop {
1937 let child = pc.node();
1938 if child.kind() == "variable_declaration" {
1939 let mut vc = child.walk();
1941 if vc.goto_first_child() {
1942 loop {
1943 let vc_child = vc.node();
1944 if vc_child.kind() == "user_type"
1945 || vc_child.kind() == "nullable_type"
1946 || vc_child.kind() == "type_identifier"
1947 {
1948 for type_name in
1949 Self::extract_type_identifiers(&vc_child, content)
1950 {
1951 if !Self::is_kotlin_primitive(&type_name) {
1952 refs.push(TypeRef {
1953 source_symbol: container.clone(),
1954 target_type: type_name,
1955 kind: TypeRefKind::FieldType,
1956 line: vc_child.start_position().row + 1,
1957 });
1958 }
1959 }
1960 }
1961 if !vc.goto_next_sibling() {
1962 break;
1963 }
1964 }
1965 }
1966 break;
1967 }
1968 if !pc.goto_next_sibling() {
1969 break;
1970 }
1971 }
1972 }
1973 }
1974 "function_declaration" => {
1979 let mut fn_name = String::new();
1982 let mut params_node: Option<tree_sitter::Node> = None;
1983 let mut return_type_node: Option<tree_sitter::Node> = None;
1984 let mut found_params = false;
1985 let mut cur = node.walk();
1986 if cur.goto_first_child() {
1987 loop {
1988 let child = cur.node();
1989 match child.kind() {
1990 "simple_identifier" if fn_name.is_empty() => {
1991 fn_name = content[child.byte_range()].to_string();
1992 }
1993 "function_value_parameters" => {
1994 params_node = Some(child);
1995 found_params = true;
1996 }
1997 "user_type" | "nullable_type" | "type_identifier"
1999 if found_params =>
2000 {
2001 return_type_node = Some(child);
2002 }
2003 _ => {}
2004 }
2005 if !cur.goto_next_sibling() {
2006 break;
2007 }
2008 }
2009 }
2010 if let Some(ret) = return_type_node {
2011 for type_name in Self::extract_type_identifiers(&ret, content) {
2012 if !Self::is_kotlin_primitive(&type_name) {
2013 refs.push(TypeRef {
2014 source_symbol: fn_name.clone(),
2015 target_type: type_name,
2016 kind: TypeRefKind::ReturnType,
2017 line: ret.start_position().row + 1,
2018 });
2019 }
2020 }
2021 }
2022 if let Some(params) = params_node {
2023 Self::collect_kotlin_param_types(¶ms, content, &fn_name, refs);
2024 }
2025 }
2026 _ => {}
2027 }
2028
2029 if cursor.goto_first_child() {
2030 Self::collect_kotlin_type_refs(cursor, content, refs);
2031 cursor.goto_parent();
2032 }
2033
2034 if !cursor.goto_next_sibling() {
2035 break;
2036 }
2037 }
2038 }
2039
2040 fn collect_kotlin_param_types(
2041 params: &tree_sitter::Node,
2042 content: &str,
2043 fn_name: &str,
2044 refs: &mut Vec<TypeRef>,
2045 ) {
2046 let mut cursor = params.walk();
2049 if cursor.goto_first_child() {
2050 loop {
2051 let child = cursor.node();
2052 if child.kind() == "parameter" {
2053 let mut pc = child.walk();
2055 if pc.goto_first_child() {
2056 loop {
2057 let param_child = pc.node();
2058 if matches!(
2059 param_child.kind(),
2060 "user_type" | "nullable_type" | "type_identifier"
2061 ) {
2062 for type_name in
2063 Self::extract_type_identifiers(¶m_child, content)
2064 {
2065 if !Self::is_kotlin_primitive(&type_name) {
2066 refs.push(TypeRef {
2067 source_symbol: fn_name.to_string(),
2068 target_type: type_name,
2069 kind: TypeRefKind::ParamType,
2070 line: param_child.start_position().row + 1,
2071 });
2072 }
2073 }
2074 }
2075 if !pc.goto_next_sibling() {
2076 break;
2077 }
2078 }
2079 }
2080 }
2081 if !cursor.goto_next_sibling() {
2082 break;
2083 }
2084 }
2085 }
2086 }
2087
2088 fn kotlin_first_type_identifier(node: &tree_sitter::Node, content: &str) -> String {
2090 if node.kind() == "type_identifier" {
2091 return content[node.byte_range()].to_string();
2092 }
2093 let mut cursor = node.walk();
2094 if cursor.goto_first_child() {
2095 loop {
2096 let result = Self::kotlin_first_type_identifier(&cursor.node(), content);
2097 if !result.is_empty() {
2098 return result;
2099 }
2100 if !cursor.goto_next_sibling() {
2101 break;
2102 }
2103 }
2104 }
2105 String::new()
2106 }
2107
2108 fn is_kotlin_primitive(name: &str) -> bool {
2109 matches!(
2110 name,
2111 "Int"
2112 | "Long"
2113 | "Short"
2114 | "Byte"
2115 | "Float"
2116 | "Double"
2117 | "Boolean"
2118 | "Char"
2119 | "String"
2120 | "Unit"
2121 | "Nothing"
2122 | "Any"
2123 | "Number"
2124 | "Array"
2125 | "List"
2126 | "MutableList"
2127 | "Map"
2128 | "MutableMap"
2129 | "Set"
2130 | "MutableSet"
2131 )
2132 }
2133
2134 fn find_swift_type_refs(content: &str) -> Vec<TypeRef> {
2138 let tree = match parsers::parse_with_grammar("swift", content) {
2139 Some(t) => t,
2140 None => return Vec::new(),
2141 };
2142
2143 let mut refs = Vec::new();
2144 let mut cursor = tree.root_node().walk();
2145 Self::collect_swift_type_refs(&mut cursor, content, &mut refs);
2146 refs
2147 }
2148
2149 fn collect_swift_type_refs(
2150 cursor: &mut tree_sitter::TreeCursor,
2151 content: &str,
2152 refs: &mut Vec<TypeRef>,
2153 ) {
2154 loop {
2155 let node = cursor.node();
2156 let kind = node.kind();
2157
2158 match kind {
2159 "class_declaration" | "struct_declaration" | "enum_declaration"
2161 | "actor_declaration" => {
2162 let class_name = node
2163 .child_by_field_name("name")
2164 .map(|n| content[n.byte_range()].to_string())
2165 .unwrap_or_default();
2166 let mut child_cursor = node.walk();
2168 if child_cursor.goto_first_child() {
2169 loop {
2170 let child = child_cursor.node();
2171 if child.kind() == "inheritance_specifier" {
2172 let type_name = Self::swift_first_type_identifier(&child, content);
2173 if !type_name.is_empty() && !Self::is_swift_primitive(&type_name) {
2174 refs.push(TypeRef {
2175 source_symbol: class_name.clone(),
2176 target_type: type_name,
2177 kind: TypeRefKind::Implements,
2178 line: child.start_position().row + 1,
2179 });
2180 }
2181 }
2182 if !child_cursor.goto_next_sibling() {
2183 break;
2184 }
2185 }
2186 }
2187 }
2188 "protocol_declaration" => {
2190 let proto_name = node
2191 .child_by_field_name("name")
2192 .map(|n| content[n.byte_range()].to_string())
2193 .unwrap_or_default();
2194 let mut child_cursor = node.walk();
2195 if child_cursor.goto_first_child() {
2196 loop {
2197 let child = child_cursor.node();
2198 if child.kind() == "inheritance_specifier" {
2199 let type_name = Self::swift_first_type_identifier(&child, content);
2200 if !type_name.is_empty() && !Self::is_swift_primitive(&type_name) {
2201 refs.push(TypeRef {
2202 source_symbol: proto_name.clone(),
2203 target_type: type_name,
2204 kind: TypeRefKind::Extends,
2205 line: child.start_position().row + 1,
2206 });
2207 }
2208 }
2209 if !child_cursor.goto_next_sibling() {
2210 break;
2211 }
2212 }
2213 }
2214 }
2215 "property_declaration" => {
2217 let container = Self::ancestor_name(&node, content);
2218 let mut child_cursor = node.walk();
2220 if child_cursor.goto_first_child() {
2221 loop {
2222 let child = child_cursor.node();
2223 if child.kind() == "type_annotation" {
2224 for type_name in Self::extract_type_identifiers(&child, content) {
2225 if !Self::is_swift_primitive(&type_name) {
2226 refs.push(TypeRef {
2227 source_symbol: container.clone(),
2228 target_type: type_name,
2229 kind: TypeRefKind::FieldType,
2230 line: child.start_position().row + 1,
2231 });
2232 }
2233 }
2234 break;
2235 }
2236 if !child_cursor.goto_next_sibling() {
2237 break;
2238 }
2239 }
2240 }
2241 }
2242 "function_declaration" => {
2247 let fn_name = node
2248 .child_by_field_name("name")
2249 .map(|n| content[n.byte_range()].to_string())
2250 .unwrap_or_else(|| {
2251 let mut cur = node.walk();
2252 let mut name = String::new();
2253 if cur.goto_first_child() {
2254 loop {
2255 if cur.node().kind() == "simple_identifier" {
2256 name = content[cur.node().byte_range()].to_string();
2257 break;
2258 }
2259 if !cur.goto_next_sibling() {
2260 break;
2261 }
2262 }
2263 }
2264 name
2265 });
2266 if let Some(ret) = node.child_by_field_name("return_type") {
2268 for type_name in Self::extract_type_identifiers(&ret, content) {
2269 if !Self::is_swift_primitive(&type_name) {
2270 refs.push(TypeRef {
2271 source_symbol: fn_name.clone(),
2272 target_type: type_name,
2273 kind: TypeRefKind::ReturnType,
2274 line: ret.start_position().row + 1,
2275 });
2276 }
2277 }
2278 } else {
2279 let mut cur = node.walk();
2281 let mut after_arrow = false;
2282 if cur.goto_first_child() {
2283 loop {
2284 let child = cur.node();
2285 if child.kind() == "->" {
2286 after_arrow = true;
2287 } else if after_arrow
2288 && matches!(
2289 child.kind(),
2290 "user_type" | "optional_type" | "type_identifier"
2291 )
2292 {
2293 for type_name in Self::extract_type_identifiers(&child, content)
2294 {
2295 if !Self::is_swift_primitive(&type_name) {
2296 refs.push(TypeRef {
2297 source_symbol: fn_name.clone(),
2298 target_type: type_name,
2299 kind: TypeRefKind::ReturnType,
2300 line: child.start_position().row + 1,
2301 });
2302 }
2303 }
2304 break;
2305 }
2306 if !cur.goto_next_sibling() {
2307 break;
2308 }
2309 }
2310 }
2311 }
2312 if let Some(params) = node.child_by_field_name("parameters") {
2315 Self::collect_swift_param_types(¶ms, content, &fn_name, refs);
2316 } else {
2317 let mut cur = node.walk();
2319 if cur.goto_first_child() {
2320 loop {
2321 let child = cur.node();
2322 if child.kind() == "parameter" {
2323 Self::collect_swift_param_type_from_node(
2324 &child, content, &fn_name, refs,
2325 );
2326 }
2327 if !cur.goto_next_sibling() {
2328 break;
2329 }
2330 }
2331 }
2332 }
2333 }
2334 _ => {}
2335 }
2336
2337 if cursor.goto_first_child() {
2338 Self::collect_swift_type_refs(cursor, content, refs);
2339 cursor.goto_parent();
2340 }
2341
2342 if !cursor.goto_next_sibling() {
2343 break;
2344 }
2345 }
2346 }
2347
2348 fn collect_swift_param_types(
2349 params: &tree_sitter::Node,
2350 content: &str,
2351 fn_name: &str,
2352 refs: &mut Vec<TypeRef>,
2353 ) {
2354 let mut cursor = params.walk();
2355 if cursor.goto_first_child() {
2356 loop {
2357 let child = cursor.node();
2358 if child.kind() == "parameter" {
2359 Self::collect_swift_param_type_from_node(&child, content, fn_name, refs);
2360 }
2361 if !cursor.goto_next_sibling() {
2362 break;
2363 }
2364 }
2365 }
2366 }
2367
2368 fn collect_swift_param_type_from_node(
2371 param: &tree_sitter::Node,
2372 content: &str,
2373 fn_name: &str,
2374 refs: &mut Vec<TypeRef>,
2375 ) {
2376 let mut pc = param.walk();
2377 if pc.goto_first_child() {
2378 loop {
2379 let param_child = pc.node();
2380 if matches!(
2381 param_child.kind(),
2382 "user_type" | "optional_type" | "type_identifier" | "type_annotation"
2383 ) {
2384 for type_name in Self::extract_type_identifiers(¶m_child, content) {
2385 if !Self::is_swift_primitive(&type_name) {
2386 refs.push(TypeRef {
2387 source_symbol: fn_name.to_string(),
2388 target_type: type_name,
2389 kind: TypeRefKind::ParamType,
2390 line: param_child.start_position().row + 1,
2391 });
2392 }
2393 }
2394 break;
2395 }
2396 if !pc.goto_next_sibling() {
2397 break;
2398 }
2399 }
2400 }
2401 }
2402
2403 fn swift_first_type_identifier(node: &tree_sitter::Node, content: &str) -> String {
2405 if node.kind() == "type_identifier" {
2406 return content[node.byte_range()].to_string();
2407 }
2408 let mut cursor = node.walk();
2409 if cursor.goto_first_child() {
2410 loop {
2411 let result = Self::swift_first_type_identifier(&cursor.node(), content);
2412 if !result.is_empty() {
2413 return result;
2414 }
2415 if !cursor.goto_next_sibling() {
2416 break;
2417 }
2418 }
2419 }
2420 String::new()
2421 }
2422
2423 fn is_swift_primitive(name: &str) -> bool {
2424 matches!(
2425 name,
2426 "Int"
2427 | "Int8"
2428 | "Int16"
2429 | "Int32"
2430 | "Int64"
2431 | "UInt"
2432 | "UInt8"
2433 | "UInt16"
2434 | "UInt32"
2435 | "UInt64"
2436 | "Float"
2437 | "Double"
2438 | "Float16"
2439 | "Float80"
2440 | "Bool"
2441 | "Character"
2442 | "String"
2443 | "Void"
2444 | "Never"
2445 | "Any"
2446 | "AnyObject"
2447 | "Optional"
2448 | "Array"
2449 | "Dictionary"
2450 | "Set"
2451 )
2452 }
2453
2454 fn find_cpp_type_refs(content: &str) -> Vec<TypeRef> {
2458 let tree = match parsers::parse_with_grammar("cpp", content) {
2459 Some(t) => t,
2460 None => return Vec::new(),
2461 };
2462
2463 let mut refs = Vec::new();
2464 let mut cursor = tree.root_node().walk();
2465 Self::collect_cpp_type_refs(&mut cursor, content, &mut refs);
2466 refs
2467 }
2468
2469 fn collect_cpp_type_refs(
2470 cursor: &mut tree_sitter::TreeCursor,
2471 content: &str,
2472 refs: &mut Vec<TypeRef>,
2473 ) {
2474 loop {
2475 let node = cursor.node();
2476 let kind = node.kind();
2477
2478 match kind {
2479 "class_specifier" | "struct_specifier" => {
2481 let class_name = node
2482 .child_by_field_name("name")
2483 .map(|n| content[n.byte_range()].to_string())
2484 .unwrap_or_default();
2485 let mut child_cursor = node.walk();
2487 if child_cursor.goto_first_child() {
2488 loop {
2489 let child = child_cursor.node();
2490 if child.kind() == "base_class_clause" {
2491 let mut bc = child.walk();
2492 if bc.goto_first_child() {
2493 loop {
2494 let base = bc.node();
2495 let type_name = match base.kind() {
2496 "type_identifier" => {
2497 content[base.byte_range()].to_string()
2498 }
2499 "qualified_identifier" => base
2500 .child_by_field_name("name")
2501 .map(|n| content[n.byte_range()].to_string())
2502 .unwrap_or_default(),
2503 _ => String::new(),
2504 };
2505 if !type_name.is_empty()
2506 && !Self::is_primitive_type(&type_name)
2507 && !Self::is_cpp_primitive(&type_name)
2508 {
2509 refs.push(TypeRef {
2510 source_symbol: class_name.clone(),
2511 target_type: type_name,
2512 kind: TypeRefKind::Extends,
2513 line: base.start_position().row + 1,
2514 });
2515 }
2516 if !bc.goto_next_sibling() {
2517 break;
2518 }
2519 }
2520 }
2521 }
2522 if !child_cursor.goto_next_sibling() {
2523 break;
2524 }
2525 }
2526 }
2527 if cursor.goto_first_child() {
2529 Self::collect_cpp_type_refs(cursor, content, refs);
2530 cursor.goto_parent();
2531 }
2532 if !cursor.goto_next_sibling() {
2533 break;
2534 }
2535 continue;
2536 }
2537 "field_declaration" => {
2539 let container = Self::ancestor_name(&node, content);
2540 if let Some(type_node) = node.child_by_field_name("type") {
2541 let type_name = Self::cpp_type_name(&type_node, content);
2542 if !type_name.is_empty()
2543 && !Self::is_primitive_type(&type_name)
2544 && !Self::is_cpp_primitive(&type_name)
2545 {
2546 refs.push(TypeRef {
2547 source_symbol: container,
2548 target_type: type_name,
2549 kind: TypeRefKind::FieldType,
2550 line: type_node.start_position().row + 1,
2551 });
2552 }
2553 }
2554 }
2555 "function_definition" => {
2557 let fn_name = node
2559 .child_by_field_name("declarator")
2560 .and_then(|d| Self::cpp_function_name(&d, content))
2561 .unwrap_or_default();
2562 if !fn_name.is_empty() {
2563 if let Some(ret_node) = node.child_by_field_name("type") {
2564 let type_name = Self::cpp_type_name(&ret_node, content);
2565 if !type_name.is_empty()
2566 && !Self::is_primitive_type(&type_name)
2567 && !Self::is_cpp_primitive(&type_name)
2568 {
2569 refs.push(TypeRef {
2570 source_symbol: fn_name.clone(),
2571 target_type: type_name,
2572 kind: TypeRefKind::ReturnType,
2573 line: ret_node.start_position().row + 1,
2574 });
2575 }
2576 }
2577 if let Some(declarator) = node.child_by_field_name("declarator") {
2579 Self::collect_cpp_param_types_from_declarator(
2580 &declarator,
2581 content,
2582 &fn_name,
2583 refs,
2584 );
2585 }
2586 }
2587 }
2588 _ => {}
2589 }
2590
2591 if cursor.goto_first_child() {
2592 Self::collect_cpp_type_refs(cursor, content, refs);
2593 cursor.goto_parent();
2594 }
2595
2596 if !cursor.goto_next_sibling() {
2597 break;
2598 }
2599 }
2600 }
2601
2602 fn collect_cpp_param_types_from_declarator(
2603 declarator: &tree_sitter::Node,
2604 content: &str,
2605 fn_name: &str,
2606 refs: &mut Vec<TypeRef>,
2607 ) {
2608 if declarator.kind() == "function_declarator" {
2610 if let Some(params) = declarator.child_by_field_name("parameters") {
2611 Self::collect_cpp_param_types(¶ms, content, fn_name, refs);
2612 }
2613 } else {
2614 let mut cursor = declarator.walk();
2616 if cursor.goto_first_child() {
2617 loop {
2618 let child = cursor.node();
2619 if child.kind() == "function_declarator" {
2620 if let Some(params) = child.child_by_field_name("parameters") {
2621 Self::collect_cpp_param_types(¶ms, content, fn_name, refs);
2622 }
2623 break;
2624 }
2625 Self::collect_cpp_param_types_from_declarator(&child, content, fn_name, refs);
2627 if !cursor.goto_next_sibling() {
2628 break;
2629 }
2630 }
2631 }
2632 }
2633 }
2634
2635 fn collect_cpp_param_types(
2636 params: &tree_sitter::Node,
2637 content: &str,
2638 fn_name: &str,
2639 refs: &mut Vec<TypeRef>,
2640 ) {
2641 let mut cursor = params.walk();
2642 if cursor.goto_first_child() {
2643 loop {
2644 let child = cursor.node();
2645 if child.kind() == "parameter_declaration"
2646 && let Some(type_node) = child.child_by_field_name("type")
2647 {
2648 let type_name = Self::cpp_type_name(&type_node, content);
2649 if !type_name.is_empty()
2650 && !Self::is_primitive_type(&type_name)
2651 && !Self::is_cpp_primitive(&type_name)
2652 {
2653 refs.push(TypeRef {
2654 source_symbol: fn_name.to_string(),
2655 target_type: type_name,
2656 kind: TypeRefKind::ParamType,
2657 line: type_node.start_position().row + 1,
2658 });
2659 }
2660 }
2661 if !cursor.goto_next_sibling() {
2662 break;
2663 }
2664 }
2665 }
2666 }
2667
2668 fn cpp_type_name(node: &tree_sitter::Node, content: &str) -> String {
2670 match node.kind() {
2671 "type_identifier" => content[node.byte_range()].to_string(),
2672 "qualified_identifier" => node
2673 .child_by_field_name("name")
2674 .map(|n| content[n.byte_range()].to_string())
2675 .unwrap_or_default(),
2676 "template_type" => node
2677 .child_by_field_name("name")
2678 .map(|n| content[n.byte_range()].to_string())
2679 .unwrap_or_default(),
2680 "pointer_declarator"
2682 | "reference_declarator"
2683 | "abstract_pointer_declarator"
2684 | "abstract_reference_declarator" => {
2685 let mut cursor = node.walk();
2686 if cursor.goto_first_child() {
2687 loop {
2688 let child = cursor.node();
2689 if matches!(
2690 child.kind(),
2691 "type_identifier" | "qualified_identifier" | "template_type"
2692 ) {
2693 return Self::cpp_type_name(&child, content);
2694 }
2695 if !cursor.goto_next_sibling() {
2696 break;
2697 }
2698 }
2699 }
2700 String::new()
2701 }
2702 _ => String::new(),
2703 }
2704 }
2705
2706 fn cpp_function_name(declarator: &tree_sitter::Node, content: &str) -> Option<String> {
2708 match declarator.kind() {
2709 "function_declarator" => declarator
2710 .child_by_field_name("declarator")
2711 .and_then(|d| Self::cpp_function_name(&d, content)),
2712 "identifier" | "field_identifier" => Some(content[declarator.byte_range()].to_string()),
2713 "qualified_identifier" => declarator
2714 .child_by_field_name("name")
2715 .map(|n| content[n.byte_range()].to_string()),
2716 "pointer_declarator" | "reference_declarator" => {
2717 let mut cursor = declarator.walk();
2718 if cursor.goto_first_child() {
2719 loop {
2720 let child = cursor.node();
2721 if let Some(name) = Self::cpp_function_name(&child, content) {
2722 return Some(name);
2723 }
2724 if !cursor.goto_next_sibling() {
2725 break;
2726 }
2727 }
2728 }
2729 None
2730 }
2731 _ => None,
2732 }
2733 }
2734
2735 fn is_cpp_primitive(name: &str) -> bool {
2736 matches!(
2737 name,
2738 "int"
2739 | "long"
2740 | "short"
2741 | "char"
2742 | "float"
2743 | "double"
2744 | "void"
2745 | "bool"
2746 | "auto"
2747 | "size_t"
2748 | "ptrdiff_t"
2749 | "nullptr_t"
2750 | "wchar_t"
2751 | "char8_t"
2752 | "char16_t"
2753 | "char32_t"
2754 )
2755 }
2756
2757 fn find_ruby_type_refs(content: &str) -> Vec<TypeRef> {
2761 let tree = match parsers::parse_with_grammar("ruby", content) {
2762 Some(t) => t,
2763 None => return Vec::new(),
2764 };
2765
2766 let mut refs = Vec::new();
2767 let mut cursor = tree.root_node().walk();
2768 Self::collect_ruby_type_refs(&mut cursor, content, &mut refs);
2769 refs
2770 }
2771
2772 fn collect_ruby_type_refs(
2773 cursor: &mut tree_sitter::TreeCursor,
2774 content: &str,
2775 refs: &mut Vec<TypeRef>,
2776 ) {
2777 loop {
2778 let node = cursor.node();
2779 let kind = node.kind();
2780
2781 match kind {
2782 "class" => {
2784 let class_name = node
2785 .child_by_field_name("name")
2786 .map(|n| content[n.byte_range()].to_string())
2787 .unwrap_or_default();
2788 let mut child_cursor = node.walk();
2790 if child_cursor.goto_first_child() {
2791 loop {
2792 let child = child_cursor.node();
2793 if child.kind() == "superclass" {
2794 let mut sc = child.walk();
2796 if sc.goto_first_child() {
2797 loop {
2798 let sc_child = sc.node();
2799 let type_name = match sc_child.kind() {
2800 "constant" => {
2801 content[sc_child.byte_range()].to_string()
2802 }
2803 "scope_resolution" => sc_child
2804 .child_by_field_name("name")
2805 .map(|n| content[n.byte_range()].to_string())
2806 .unwrap_or_default(),
2807 _ => String::new(),
2808 };
2809 if !type_name.is_empty() {
2810 refs.push(TypeRef {
2811 source_symbol: class_name.clone(),
2812 target_type: type_name,
2813 kind: TypeRefKind::Extends,
2814 line: sc_child.start_position().row + 1,
2815 });
2816 }
2817 if !sc.goto_next_sibling() {
2818 break;
2819 }
2820 }
2821 }
2822 break;
2823 }
2824 if !child_cursor.goto_next_sibling() {
2825 break;
2826 }
2827 }
2828 }
2829 }
2830 "call" => {
2832 let method_name = node
2834 .child_by_field_name("method")
2835 .map(|n| content[n.byte_range()].to_string())
2836 .unwrap_or_default();
2837 if method_name == "include" || method_name == "prepend" {
2838 let container = Self::ancestor_name(&node, content);
2839 if let Some(args) = node.child_by_field_name("arguments") {
2840 let mut arg_cursor = args.walk();
2841 if arg_cursor.goto_first_child() {
2842 loop {
2843 let arg = arg_cursor.node();
2844 let type_name = match arg.kind() {
2845 "constant" => content[arg.byte_range()].to_string(),
2846 "scope_resolution" => arg
2847 .child_by_field_name("name")
2848 .map(|n| content[n.byte_range()].to_string())
2849 .unwrap_or_default(),
2850 _ => String::new(),
2851 };
2852 if !type_name.is_empty() {
2853 refs.push(TypeRef {
2854 source_symbol: container.clone(),
2855 target_type: type_name,
2856 kind: TypeRefKind::Implements,
2857 line: arg.start_position().row + 1,
2858 });
2859 }
2860 if !arg_cursor.goto_next_sibling() {
2861 break;
2862 }
2863 }
2864 }
2865 }
2866 }
2867 }
2868 _ => {}
2869 }
2870
2871 if cursor.goto_first_child() {
2872 Self::collect_ruby_type_refs(cursor, content, refs);
2873 cursor.goto_parent();
2874 }
2875
2876 if !cursor.goto_next_sibling() {
2877 break;
2878 }
2879 }
2880 }
2881
2882 fn ancestor_name(node: &tree_sitter::Node, content: &str) -> String {
2886 let mut current = node.parent();
2887 while let Some(parent) = current {
2888 match parent.kind() {
2889 "struct_item"
2890 | "enum_item"
2891 | "impl_item"
2892 | "trait_item"
2893 | "function_item"
2894 | "class_declaration"
2896 | "interface_declaration"
2897 | "struct_declaration"
2898 | "record_declaration"
2899 | "protocol_declaration"
2900 | "actor_declaration"
2901 | "method_declaration"
2902 | "class_definition"
2904 | "function_definition"
2905 | "class_specifier"
2907 | "struct_specifier"
2908 | "class"
2910 | "module" => {
2911 if let Some(name_node) = parent.child_by_field_name("name") {
2912 return content[name_node.byte_range()].to_string();
2913 }
2914 if parent.kind() == "impl_item"
2916 && let Some(type_node) = parent.child_by_field_name("type")
2917 {
2918 return content[type_node.byte_range()].to_string();
2919 }
2920 if matches!(parent.kind(), "class_specifier" | "struct_specifier") {
2922 let mut cur = parent.walk();
2923 if cur.goto_first_child() {
2924 loop {
2925 if cur.node().kind() == "type_identifier" {
2926 return content[cur.node().byte_range()].to_string();
2927 }
2928 if !cur.goto_next_sibling() {
2929 break;
2930 }
2931 }
2932 }
2933 }
2934 if parent.kind() == "class_declaration" {
2936 let mut cur = parent.walk();
2937 if cur.goto_first_child() {
2938 loop {
2939 let k = cur.node().kind();
2940 if k == "type_identifier" || k == "simple_identifier" {
2941 return content[cur.node().byte_range()].to_string();
2942 }
2943 if !cur.goto_next_sibling() {
2944 break;
2945 }
2946 }
2947 }
2948 }
2949 }
2950 _ => {}
2951 }
2952 current = parent.parent();
2953 }
2954 "<module>".to_string()
2955 }
2956
2957 fn extract_type_identifiers(node: &tree_sitter::Node, content: &str) -> Vec<String> {
2961 let mut types = Vec::new();
2962 Self::collect_type_identifiers_recursive(node, content, &mut types);
2963 types
2964 }
2965
2966 fn collect_type_identifiers_recursive(
2967 node: &tree_sitter::Node,
2968 content: &str,
2969 types: &mut Vec<String>,
2970 ) {
2971 let kind = node.kind();
2972
2973 if kind == "type_identifier" || kind == "identifier" {
2975 let name = content[node.byte_range()].to_string();
2976 if !Self::is_primitive_type(&name) {
2977 types.push(name);
2978 }
2979 return;
2980 }
2981
2982 if kind == "scoped_type_identifier" || kind == "scoped_identifier" {
2984 if let Some(name_node) = node.child_by_field_name("name") {
2985 let name = content[name_node.byte_range()].to_string();
2986 if !Self::is_primitive_type(&name) {
2987 types.push(name);
2988 }
2989 }
2990 return;
2991 }
2992
2993 if kind == "attribute" {
2995 let text = content[node.byte_range()].to_string();
2996 if let Some(last) = text.rsplit('.').next()
2997 && !Self::is_primitive_type(last)
2998 {
2999 types.push(last.to_string());
3000 }
3001 return;
3002 }
3003
3004 let mut cursor = node.walk();
3006 if cursor.goto_first_child() {
3007 loop {
3008 Self::collect_type_identifiers_recursive(&cursor.node(), content, types);
3009 if !cursor.goto_next_sibling() {
3010 break;
3011 }
3012 }
3013 }
3014 }
3015
3016 fn is_primitive_type(name: &str) -> bool {
3018 matches!(
3019 name,
3020 "bool"
3022 | "char"
3023 | "str"
3024 | "String"
3025 | "i8"
3026 | "i16"
3027 | "i32"
3028 | "i64"
3029 | "i128"
3030 | "isize"
3031 | "u8"
3032 | "u16"
3033 | "u32"
3034 | "u64"
3035 | "u128"
3036 | "usize"
3037 | "f32"
3038 | "f64"
3039 | "Option"
3041 | "Result"
3042 | "Vec"
3043 | "Box"
3044 | "Rc"
3045 | "Arc"
3046 | "Cell"
3047 | "RefCell"
3048 | "Cow"
3049 | "Pin"
3050 | "string"
3052 | "number"
3053 | "boolean"
3054 | "void"
3055 | "null"
3056 | "undefined"
3057 | "never"
3058 | "any"
3059 | "unknown"
3060 | "object"
3061 | "symbol"
3062 | "bigint"
3063 | "Array"
3064 | "Promise"
3065 | "Record"
3066 | "Map"
3067 | "Set"
3068 | "Partial"
3069 | "Required"
3070 | "Readonly"
3071 | "Pick"
3072 | "Omit"
3073 | "int"
3075 | "float"
3076 | "complex"
3077 | "list"
3078 | "dict"
3079 | "set"
3080 | "tuple"
3081 | "bytes"
3082 | "bytearray"
3083 | "memoryview"
3084 | "range"
3085 | "frozenset"
3086 | "type"
3087 | "None"
3088 | "True"
3089 | "False"
3090 | "self"
3091 | "Self"
3092 | "cls"
3093 | "Integer"
3095 | "Long"
3096 | "Double"
3097 | "Float"
3098 | "Short"
3099 | "Byte"
3100 | "Character"
3101 | "Boolean"
3102 | "Void"
3103 | "Number"
3104 | "Object"
3105 )
3106 }
3107
3108 #[allow(dead_code)] pub fn find_callers(
3111 &mut self,
3112 root: &Path,
3113 files: &[(String, bool)],
3114 symbol_name: &str,
3115 ) -> Vec<(String, String)> {
3116 let mut callers = Vec::new();
3117
3118 for (path, is_dir) in files {
3119 if *is_dir {
3120 continue;
3121 }
3122
3123 let full_path = root.join(path);
3124 if support_for_path(&full_path).is_none() {
3126 continue;
3127 }
3128 let content = match std::fs::read_to_string(&full_path) {
3129 Ok(c) => c,
3130 Err(_) => continue,
3131 };
3132
3133 let symbols = match self.parse_file(&full_path, &content) {
3134 Some(s) => s,
3135 None => continue, };
3137 for symbol in symbols {
3138 let callees = self.find_callees_for_symbol(&full_path, &content, &symbol);
3139 let is_caller = callees.iter().any(|(name, _, qualifier, _)| {
3141 if name != symbol_name {
3142 return false;
3143 }
3144 match qualifier {
3146 None => true,
3147 Some(q) => q == "self" || q == "Self",
3148 }
3149 });
3150 if is_caller {
3151 callers.push((path.clone(), symbol.name.clone()));
3152 }
3153 }
3154 }
3155
3156 callers
3157 }
3158}
3159
3160fn strip_import_quotes(s: &str) -> String {
3162 s.trim_matches(|c| c == '"' || c == '\'' || c == '`')
3163 .to_string()
3164}
3165
3166#[cfg(test)]
3167mod tests {
3168 use super::*;
3169 use crate::SymbolKind;
3170 use std::path::PathBuf;
3171
3172 fn grammar_for_path_loaded(path: &std::path::Path, sample: &str) -> bool {
3180 let Some(support) = normalize_languages::support_for_path(path) else {
3181 return false;
3182 };
3183 crate::parsers::parse_with_grammar(support.grammar_name(), sample).is_some()
3184 }
3185
3186 #[test]
3187 fn test_parse_python_function() {
3188 let parser = SymbolParser::new();
3189 let content = r#"
3190def foo():
3191 pass
3192
3193def bar(x):
3194 return x
3195"#;
3196 let symbols = parser
3197 .parse_file(&PathBuf::from("test.py"), content)
3198 .unwrap();
3199 assert_eq!(symbols.len(), 2);
3200 assert_eq!(symbols[0].name, "foo");
3201 assert_eq!(symbols[0].kind, SymbolKind::Function);
3202 assert_eq!(symbols[1].name, "bar");
3203 }
3204
3205 #[test]
3206 fn test_parse_python_class() {
3207 let parser = SymbolParser::new();
3208 let content = r#"
3209class Foo:
3210 def method(self):
3211 pass
3212"#;
3213 let symbols = parser
3214 .parse_file(&PathBuf::from("test.py"), content)
3215 .unwrap();
3216 assert_eq!(symbols.len(), 2);
3217 assert_eq!(symbols[0].name, "Foo");
3218 assert_eq!(symbols[0].kind, SymbolKind::Class);
3219 assert_eq!(symbols[1].name, "method");
3220 assert_eq!(symbols[1].kind, SymbolKind::Method);
3221 assert_eq!(symbols[1].parent, Some("Foo".to_string()));
3222 }
3223
3224 #[test]
3225 fn test_parse_rust_function() {
3226 let parser = SymbolParser::new();
3227 let content = r#"
3228fn foo() {}
3229
3230fn bar(x: i32) -> i32 {
3231 x
3232}
3233"#;
3234 let symbols = parser
3235 .parse_file(&PathBuf::from("test.rs"), content)
3236 .unwrap();
3237 assert_eq!(symbols.len(), 2);
3238 assert_eq!(symbols[0].name, "foo");
3239 assert_eq!(symbols[0].kind, SymbolKind::Function);
3240 }
3241
3242 #[test]
3243 fn test_extract_symbol_source() {
3244 let mut parser = SymbolParser::new();
3245 let content = r#"def foo():
3246 return 42
3247
3248def bar():
3249 pass"#;
3250 let source = parser.extract_symbol_source(&PathBuf::from("test.py"), content, "foo");
3251 assert!(source.is_some());
3252 assert!(source.unwrap().contains("return 42"));
3253 }
3254
3255 #[test]
3256 fn test_go_type_refs_struct_fields() {
3257 let mut parser = SymbolParser::new();
3258 let content = r#"package main
3259
3260type Server struct {
3261 Handler RequestHandler
3262 Logger Logger
3263}
3264"#;
3265 let __probe_path = PathBuf::from("main.go");
3266 if !grammar_for_path_loaded(&__probe_path, content) {
3267 return;
3268 }
3269 let refs = parser.find_type_refs(&__probe_path, content);
3270 let field_refs: Vec<_> = refs
3271 .iter()
3272 .filter(|r| r.kind == TypeRefKind::FieldType)
3273 .collect();
3274 assert!(
3275 field_refs
3276 .iter()
3277 .any(|r| r.source_symbol == "Server" && r.target_type == "RequestHandler"),
3278 "expected Server→RequestHandler field_type"
3279 );
3280 assert!(
3281 field_refs
3282 .iter()
3283 .any(|r| r.source_symbol == "Server" && r.target_type == "Logger"),
3284 "expected Server→Logger field_type"
3285 );
3286 }
3287
3288 #[test]
3289 fn test_go_type_refs_interface_embed() {
3290 let mut parser = SymbolParser::new();
3291 let content = r#"package main
3292
3293type ReadWriter interface {
3294 Reader
3295 Writer
3296}
3297"#;
3298 let __probe_path = PathBuf::from("main.go");
3299 if !grammar_for_path_loaded(&__probe_path, content) {
3300 return;
3301 }
3302 let refs = parser.find_type_refs(&__probe_path, content);
3303 let impl_refs: Vec<_> = refs
3304 .iter()
3305 .filter(|r| r.kind == TypeRefKind::Implements)
3306 .collect();
3307 assert!(
3308 impl_refs
3309 .iter()
3310 .any(|r| r.source_symbol == "ReadWriter" && r.target_type == "Reader"),
3311 "expected ReadWriter→Reader implements"
3312 );
3313 assert!(
3314 impl_refs
3315 .iter()
3316 .any(|r| r.source_symbol == "ReadWriter" && r.target_type == "Writer"),
3317 "expected ReadWriter→Writer implements"
3318 );
3319 }
3320
3321 #[test]
3322 fn test_go_type_refs_func_params_return() {
3323 let mut parser = SymbolParser::new();
3324 let content = r#"package main
3325
3326func Process(req Request) Response {
3327 return Response{}
3328}
3329"#;
3330 let __probe_path = PathBuf::from("main.go");
3331 if !grammar_for_path_loaded(&__probe_path, content) {
3332 return;
3333 }
3334 let refs = parser.find_type_refs(&__probe_path, content);
3335 assert!(
3336 refs.iter().any(|r| r.kind == TypeRefKind::ParamType
3337 && r.source_symbol == "Process"
3338 && r.target_type == "Request"),
3339 "expected Process→Request param_type"
3340 );
3341 assert!(
3342 refs.iter().any(|r| r.kind == TypeRefKind::ReturnType
3343 && r.source_symbol == "Process"
3344 && r.target_type == "Response"),
3345 "expected Process→Response return_type"
3346 );
3347 }
3348
3349 #[test]
3350 fn test_go_type_refs_alias() {
3351 let mut parser = SymbolParser::new();
3352 let content = r#"package main
3353
3354type MyHandler = http.Handler
3355"#;
3356 let __probe_path = PathBuf::from("main.go");
3357 if !grammar_for_path_loaded(&__probe_path, content) {
3358 return;
3359 }
3360 let refs = parser.find_type_refs(&__probe_path, content);
3361 assert!(
3362 refs.iter().any(|r| r.kind == TypeRefKind::TypeAlias
3363 && r.source_symbol == "MyHandler"
3364 && r.target_type == "Handler"),
3365 "expected MyHandler→Handler type_alias (qualified type, leaf name)"
3366 );
3367 }
3368
3369 #[test]
3370 fn test_java_type_refs_class_hierarchy() {
3371 let mut parser = SymbolParser::new();
3372 let content = r#"public class Foo extends Bar implements Baz, Qux {
3373}
3374"#;
3375 let __probe_path = PathBuf::from("Foo.java");
3376 if !grammar_for_path_loaded(&__probe_path, content) {
3377 return;
3378 }
3379 let refs = parser.find_type_refs(&__probe_path, content);
3380 assert!(
3381 refs.iter()
3382 .any(|r| r.kind == TypeRefKind::Extends && r.target_type == "Bar"),
3383 "expected Foo extends Bar"
3384 );
3385 assert!(
3386 refs.iter()
3387 .any(|r| r.kind == TypeRefKind::Implements && r.target_type == "Baz"),
3388 "expected Foo implements Baz"
3389 );
3390 assert!(
3391 refs.iter()
3392 .any(|r| r.kind == TypeRefKind::Implements && r.target_type == "Qux"),
3393 "expected Foo implements Qux"
3394 );
3395 }
3396
3397 #[test]
3398 fn test_java_type_refs_field_and_method() {
3399 let mut parser = SymbolParser::new();
3400 let content = r#"public class Service {
3401 private Repository repo;
3402
3403 public Response handle(Request req) {
3404 return null;
3405 }
3406}
3407"#;
3408 let __probe_path = PathBuf::from("Service.java");
3409 if !grammar_for_path_loaded(&__probe_path, content) {
3410 return;
3411 }
3412 let refs = parser.find_type_refs(&__probe_path, content);
3413 assert!(
3414 refs.iter().any(|r| r.kind == TypeRefKind::FieldType
3415 && r.source_symbol == "Service"
3416 && r.target_type == "Repository"),
3417 "expected Service→Repository field_type"
3418 );
3419 assert!(
3420 refs.iter().any(|r| r.kind == TypeRefKind::ReturnType
3421 && r.source_symbol == "handle"
3422 && r.target_type == "Response"),
3423 "expected handle→Response return_type"
3424 );
3425 assert!(
3426 refs.iter().any(|r| r.kind == TypeRefKind::ParamType
3427 && r.source_symbol == "handle"
3428 && r.target_type == "Request"),
3429 "expected handle→Request param_type"
3430 );
3431 }
3432
3433 #[test]
3434 fn test_java_type_refs_generic_bound() {
3435 let mut parser = SymbolParser::new();
3436 let content = r#"public class Sorter {
3437 public <T extends Comparable> void sort(T[] arr) {}
3438}
3439"#;
3440 let __probe_path = PathBuf::from("Sorter.java");
3441 if !grammar_for_path_loaded(&__probe_path, content) {
3442 return;
3443 }
3444 let refs = parser.find_type_refs(&__probe_path, content);
3445 assert!(
3446 refs.iter().any(|r| r.kind == TypeRefKind::GenericBound
3447 && r.source_symbol == "sort"
3448 && r.target_type == "Comparable"),
3449 "expected sort→Comparable generic_bound"
3450 );
3451 }
3452
3453 #[test]
3454 fn test_java_type_refs_interface_extends() {
3455 let mut parser = SymbolParser::new();
3456 let content = r#"interface ReadWriter extends Reader, Writer {
3457}
3458"#;
3459 let __probe_path = PathBuf::from("ReadWriter.java");
3460 if !grammar_for_path_loaded(&__probe_path, content) {
3461 return;
3462 }
3463 let refs = parser.find_type_refs(&__probe_path, content);
3464 assert!(
3465 refs.iter().any(|r| r.kind == TypeRefKind::Extends
3466 && r.source_symbol == "ReadWriter"
3467 && r.target_type == "Reader"),
3468 "expected ReadWriter extends Reader"
3469 );
3470 assert!(
3471 refs.iter().any(|r| r.kind == TypeRefKind::Extends
3472 && r.source_symbol == "ReadWriter"
3473 && r.target_type == "Writer"),
3474 "expected ReadWriter extends Writer"
3475 );
3476 }
3477
3478 #[test]
3481 fn test_csharp_type_refs_class_hierarchy() {
3482 let mut parser = SymbolParser::new();
3483 let content = r#"class Service : BaseService, IService {
3484}
3485"#;
3486 let __probe_path = PathBuf::from("Service.cs");
3487 if !grammar_for_path_loaded(&__probe_path, content) {
3488 return;
3489 }
3490 let refs = parser.find_type_refs(&__probe_path, content);
3491 assert!(
3492 refs.iter()
3493 .any(|r| r.source_symbol == "Service" && r.target_type == "BaseService"),
3494 "expected Service : BaseService, got: {:?}",
3495 refs
3496 );
3497 assert!(
3498 refs.iter()
3499 .any(|r| r.source_symbol == "Service" && r.target_type == "IService"),
3500 "expected Service : IService, got: {:?}",
3501 refs
3502 );
3503 }
3504
3505 #[test]
3506 fn test_csharp_type_refs_method_return_and_param() {
3507 let mut parser = SymbolParser::new();
3508 let content = r#"class Processor {
3509 private Repository _repo;
3510 public Response Process(Request req) {
3511 return null;
3512 }
3513}
3514"#;
3515 let __probe_path = PathBuf::from("Processor.cs");
3516 if !grammar_for_path_loaded(&__probe_path, content) {
3517 return;
3518 }
3519 let refs = parser.find_type_refs(&__probe_path, content);
3520 assert!(
3521 refs.iter()
3522 .any(|r| r.kind == TypeRefKind::FieldType && r.target_type == "Repository"),
3523 "expected field type Repository, got: {:?}",
3524 refs
3525 );
3526 assert!(
3527 refs.iter().any(|r| r.kind == TypeRefKind::ReturnType
3528 && r.source_symbol == "Process"
3529 && r.target_type == "Response"),
3530 "expected return type Response, got: {:?}",
3531 refs
3532 );
3533 assert!(
3534 refs.iter().any(|r| r.kind == TypeRefKind::ParamType
3535 && r.source_symbol == "Process"
3536 && r.target_type == "Request"),
3537 "expected param type Request, got: {:?}",
3538 refs
3539 );
3540 }
3541
3542 #[test]
3545 fn test_kotlin_type_refs_class_hierarchy() {
3546 let mut parser = SymbolParser::new();
3547 let content = r#"class Service : BaseService(), IService {
3548}
3549"#;
3550 let __probe_path = PathBuf::from("Service.kt");
3551 if !grammar_for_path_loaded(&__probe_path, content) {
3552 return;
3553 }
3554 let refs = parser.find_type_refs(&__probe_path, content);
3555 assert!(
3556 refs.iter()
3557 .any(|r| r.source_symbol == "Service" && r.target_type == "BaseService"),
3558 "expected Service : BaseService, got: {:?}",
3559 refs
3560 );
3561 assert!(
3562 refs.iter()
3563 .any(|r| r.source_symbol == "Service" && r.target_type == "IService"),
3564 "expected Service : IService, got: {:?}",
3565 refs
3566 );
3567 }
3568
3569 #[test]
3570 fn test_kotlin_type_refs_function_types() {
3571 let mut parser = SymbolParser::new();
3572 let content = r#"class Repo {
3573 val handler: Handler = Handler()
3574 fun process(req: Request): Response {
3575 return Response()
3576 }
3577}
3578"#;
3579 let __probe_path = PathBuf::from("Repo.kt");
3580 if !grammar_for_path_loaded(&__probe_path, content) {
3581 return;
3582 }
3583 let refs = parser.find_type_refs(&__probe_path, content);
3584 assert!(
3585 refs.iter()
3586 .any(|r| r.kind == TypeRefKind::FieldType && r.target_type == "Handler"),
3587 "expected field type Handler, got: {:?}",
3588 refs
3589 );
3590 assert!(
3591 refs.iter().any(|r| r.kind == TypeRefKind::ParamType
3592 && r.source_symbol == "process"
3593 && r.target_type == "Request"),
3594 "expected param type Request, got: {:?}",
3595 refs
3596 );
3597 assert!(
3598 refs.iter().any(|r| r.kind == TypeRefKind::ReturnType
3599 && r.source_symbol == "process"
3600 && r.target_type == "Response"),
3601 "expected return type Response, got: {:?}",
3602 refs
3603 );
3604 }
3605
3606 #[test]
3609 fn test_swift_type_refs_class_hierarchy() {
3610 let mut parser = SymbolParser::new();
3611 let content = r#"class Service: BaseService, IService {
3612}
3613"#;
3614 let __probe_path = PathBuf::from("Service.swift");
3615 if !grammar_for_path_loaded(&__probe_path, content) {
3616 return;
3617 }
3618 let refs = parser.find_type_refs(&__probe_path, content);
3619 assert!(
3620 refs.iter()
3621 .any(|r| r.source_symbol == "Service" && r.target_type == "BaseService"),
3622 "expected Service: BaseService, got: {:?}",
3623 refs
3624 );
3625 assert!(
3626 refs.iter()
3627 .any(|r| r.source_symbol == "Service" && r.target_type == "IService"),
3628 "expected Service: IService, got: {:?}",
3629 refs
3630 );
3631 }
3632
3633 #[test]
3634 fn test_swift_type_refs_function_types() {
3635 let mut parser = SymbolParser::new();
3636 let content = r#"class Processor {
3637 func process(req: Request) -> Response {
3638 return Response()
3639 }
3640}
3641"#;
3642 let __probe_path = PathBuf::from("Processor.swift");
3643 if !grammar_for_path_loaded(&__probe_path, content) {
3644 return;
3645 }
3646 let refs = parser.find_type_refs(&__probe_path, content);
3647 assert!(
3648 refs.iter().any(|r| r.kind == TypeRefKind::ParamType
3649 && r.source_symbol == "process"
3650 && r.target_type == "Request"),
3651 "expected param type Request, got: {:?}",
3652 refs
3653 );
3654 assert!(
3655 refs.iter().any(|r| r.kind == TypeRefKind::ReturnType
3656 && r.source_symbol == "process"
3657 && r.target_type == "Response"),
3658 "expected return type Response, got: {:?}",
3659 refs
3660 );
3661 }
3662
3663 #[test]
3666 fn test_cpp_type_refs_class_hierarchy() {
3667 let mut parser = SymbolParser::new();
3668 let content = r#"class Derived : public Base, public IFoo {
3669};
3670"#;
3671 let __probe_path = PathBuf::from("derived.cpp");
3672 if !grammar_for_path_loaded(&__probe_path, content) {
3673 return;
3674 }
3675 let refs = parser.find_type_refs(&__probe_path, content);
3676 assert!(
3677 refs.iter()
3678 .any(|r| r.source_symbol == "Derived" && r.target_type == "Base"),
3679 "expected Derived extends Base, got: {:?}",
3680 refs
3681 );
3682 assert!(
3683 refs.iter()
3684 .any(|r| r.source_symbol == "Derived" && r.target_type == "IFoo"),
3685 "expected Derived extends IFoo, got: {:?}",
3686 refs
3687 );
3688 }
3689
3690 #[test]
3691 fn test_cpp_type_refs_function_return_param() {
3692 let mut parser = SymbolParser::new();
3693 let content = r#"Response process(Request req) {
3694 return Response();
3695}
3696"#;
3697 let __probe_path = PathBuf::from("proc.cpp");
3698 if !grammar_for_path_loaded(&__probe_path, content) {
3699 return;
3700 }
3701 let refs = parser.find_type_refs(&__probe_path, content);
3702 assert!(
3703 refs.iter()
3704 .any(|r| r.kind == TypeRefKind::ReturnType && r.target_type == "Response"),
3705 "expected return type Response, got: {:?}",
3706 refs
3707 );
3708 assert!(
3709 refs.iter()
3710 .any(|r| r.kind == TypeRefKind::ParamType && r.target_type == "Request"),
3711 "expected param type Request, got: {:?}",
3712 refs
3713 );
3714 }
3715
3716 #[test]
3719 fn test_ruby_type_refs_inheritance() {
3720 let mut parser = SymbolParser::new();
3721 let content = r#"class Service < BaseService
3722end
3723"#;
3724 let __probe_path = PathBuf::from("service.rb");
3725 if !grammar_for_path_loaded(&__probe_path, content) {
3726 return;
3727 }
3728 let refs = parser.find_type_refs(&__probe_path, content);
3729 assert!(
3730 refs.iter().any(|r| r.kind == TypeRefKind::Extends
3731 && r.source_symbol == "Service"
3732 && r.target_type == "BaseService"),
3733 "expected Service < BaseService, got: {:?}",
3734 refs
3735 );
3736 }
3737
3738 #[test]
3739 fn test_ruby_type_refs_include() {
3740 let mut parser = SymbolParser::new();
3741 let content = r#"class Worker
3742 include Serializable
3743 prepend Auditable
3744end
3745"#;
3746 let __probe_path = PathBuf::from("worker.rb");
3747 if !grammar_for_path_loaded(&__probe_path, content) {
3748 return;
3749 }
3750 let refs = parser.find_type_refs(&__probe_path, content);
3751 assert!(
3752 refs.iter().any(|r| r.kind == TypeRefKind::Implements
3753 && r.source_symbol == "Worker"
3754 && r.target_type == "Serializable"),
3755 "expected Worker include Serializable, got: {:?}",
3756 refs
3757 );
3758 assert!(
3759 refs.iter().any(|r| r.kind == TypeRefKind::Implements
3760 && r.source_symbol == "Worker"
3761 && r.target_type == "Auditable"),
3762 "expected Worker prepend Auditable, got: {:?}",
3763 refs
3764 );
3765 }
3766}