Skip to main content

normalize_facts/
symbols.rs

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
9// Re-export for use by other modules in this crate
10pub use normalize_facts_core::{FlatImport, FlatSymbol};
11
12pub struct SymbolParser {
13    extractor: Extractor,
14    // Keep for import parsing and call graph analysis
15}
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, // symbols.rs includes all symbols for indexing
28            }),
29        }
30    }
31
32    /// Parse symbols from a file.
33    ///
34    /// Returns `None` when the language is recognized but the grammar shared library
35    /// could not be loaded (grammar unavailable at runtime). This is distinct from
36    /// returning `Some(vec![])` which means the grammar loaded successfully but the
37    /// file contains no indexable symbols.
38    ///
39    /// Returns `Some(vec![])` (empty) for files whose language is not recognized.
40    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()), // unknown language → skip silently
44        };
45
46        // Check that the grammar .so is actually loadable before attempting extraction.
47        // If not, return None so callers can warn and skip rather than caching empty results.
48        // `try_get_grammar` emits a one-shot stderr warning and records the failure in
49        // the missing-grammar tracker (so `normalize structure rebuild` can summarise).
50        normalize_languages::parsers::try_get_grammar(support.grammar_name())?;
51
52        // Use shared extractor for symbol extraction
53        let result = self.extractor.extract(path, content);
54
55        // Flatten nested symbols
56        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    /// Flatten a nested symbol into the flat list with parent references
64    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        // Recurse into children with current symbol as parent
79        for child in &sym.children {
80            Self::flatten_symbol(child, Some(&sym.name), symbols);
81        }
82    }
83
84    /// Parse imports from any supported language file.
85    /// Tries query-based extraction first; falls back to trait-based extraction.
86    /// Returns a flattened list where each imported name gets its own FlatImport entry.
87    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        // Check if this language has import support (either via query or trait)
96        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        // Query-based extraction (compiled query is cached in the loader)
108        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        // Fallback: trait-based extraction via Language::extract_imports
116        Self::collect_imports_with_trait(root, content, support)
117    }
118
119    /// Query-based import extraction using `@import`, `@import.path`, `@import.name`,
120    /// `@import.alias`, `@import.glob`, and `@import.reexport` captures.
121    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        // Dedup: when both a plain and a re-export pattern match the same use_declaration
194        // (e.g. `pub use` matches both the generic pattern and the reexport pattern),
195        // keep one entry per (module, name, line) and prefer is_reexport=true.
196        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                // Prefer the reexport variant
203                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    /// Trait-based import extraction fallback.
216    /// Walks all nodes and calls `Language::extract_imports` on each.
217    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                    // Wildcard import: store name="*" with module
228                    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                    // Single import: module is the full path
237                    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                    // Named imports: one entry per name
246                    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            // Push children in reverse order for DFS
258            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    /// Find a symbol by name in a file
268    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    /// Extract the source code for a symbol
274    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    /// Find callees (functions/methods called) within a symbol
288    #[allow(dead_code)] // Call graph API - used by index
289    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    /// Find callees with line numbers (for call graph indexing)
304    /// Returns: (callee_name, line, Option<qualifier>)
305    /// For foo.bar(), returns ("bar", line, Some("foo"), access)
306    /// For bar(), returns ("bar", line, None, access)
307    /// `access` is `Some("write")` when the call result is assigned; `None` otherwise.
308    #[allow(dead_code)] // Call graph API - used by index
309    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    /// Find callees for a pre-parsed symbol (avoids re-parsing the file)
323    /// Use this when you already have the FlatSymbol from parse_file()
324    /// Returns `(callee_name, line, qualifier, access)` where `access` is
325    /// `Some("write")` when the call result is assigned, `None` otherwise.
326    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    /// Generic query-based call extraction using `@call`, `@call.write`, and
364    /// `@call.qualifier` captures.
365    ///
366    /// - `@call` — call in read context (access = None)
367    /// - `@call.write` — call whose result is assigned (access = Some("write"))
368    /// - `@call.qualifier` — qualifier/receiver for method calls
369    ///
370    /// When both `@call` and `@call.write` match the same node (same byte offset +
371    /// line), the "write" tag wins.  This happens because write-context patterns use
372    /// `@call.write` while the generic patterns still use `@call`; deduplication via
373    /// a HashMap ensures a single entry per (name, line) pair with the most specific
374    /// access tag.
375    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        // Map (name, line) -> (qualifier, access) — "write" beats None
397        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                // "write" wins over None
435                if access.is_some() {
436                    entry.1 = access;
437                }
438                // Update qualifier if present in this match
439                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    /// Extract type references from a source file.
452    /// Returns references to types found in struct fields, function params/returns,
453    /// inheritance, trait bounds, type aliases, etc.
454    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    /// Extract type references from Rust source code.
475    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                // struct Foo { field: BarType }
498                "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                // fn foo(x: BarType) -> BazType
512                "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(&params, 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 Trait for Type / impl Type
532                "impl_item" => {
533                    // Get the type being implemented
534                    let impl_type = node
535                        .child_by_field_name("type")
536                        .map(|n| content[n.byte_range()].to_string())
537                        .unwrap_or_default();
538                    // Check for trait
539                    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 Foo: Bar + Baz (supertraits)
551                "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 Foo = Bar
568                "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 T: Foo + Bar (type_bound_list in where clauses)
585                "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    /// Extract parameter types from a Rust function's parameter list.
604    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    /// Extract type bounds from a Rust where clause.
634    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    /// Extract type references from TypeScript source code.
664    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 Foo extends Bar implements Baz
688                "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                    // Find extends and implements clauses
694                    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 Foo extends Bar
730                "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 Foo = Bar
758                "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 foo(x: Bar): Baz  / method_definition / arrow functions
775                "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(&params, 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                // Property with type annotation in interface/class
795                "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    /// Extract parameter types from TypeScript function parameters.
823    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                // required_parameter, optional_parameter
834                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    /// Extract type references from Go source code.
854    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 Foo struct { Bar Baz }
877                // type MyInterface interface { OtherInterface }
878                // type Alias = Original
879                "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                    // Don't recurse into type_declaration children below
899                    if !cursor.goto_next_sibling() {
900                        break;
901                    }
902                    continue;
903                }
904                // func (r *Recv) Method(x Bar) Baz
905                "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                    // Receiver type → field_type edge from recv_type → fn_name (skip, not typical)
911                    // Params
912                    if let Some(params) = node.child_by_field_name("parameters") {
913                        Self::collect_go_param_types(&params, content, &fn_name, refs);
914                    }
915                    // Return type(s)
916                    if let Some(result) = node.child_by_field_name("result") {
917                        Self::collect_go_result_types(&result, content, &fn_name, refs);
918                    }
919                }
920                // func Foo(x Bar) Baz
921                "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(&params, 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    /// Collect type refs from a Go `type_spec` node (struct or interface).
948    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 fields
964            "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                                        // qualified_type (io.Reader) or type_identifier
978                                        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 embedded types
1004            "interface_type" => {
1005                let mut cur = type_body.walk();
1006                if cur.goto_first_child() {
1007                    loop {
1008                        let child = cur.node();
1009                        // type_elem = embedded interface constraint
1010                        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    /// Collect type refs from a Go `type_alias` node (`type Alias = Original`).
1045    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    /// Extract a readable type name from a Go type node.
1070    /// For `qualified_type` (io.Reader), returns just the name part.
1071    /// For `type_identifier`, returns the identifier directly.
1072    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                // package.Name — return just Name
1077                node.child_by_field_name("name")
1078                    .map(|n| content[n.byte_range()].to_string())
1079                    .unwrap_or_default()
1080            }
1081            "pointer_type" => {
1082                // *Foo — look through the pointer
1083                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                // []Foo or [N]Foo — look at element type
1099                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    /// Extract parameter types from a Go parameter list.
1108    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    /// Extract return types from a Go result field (single type or parameter_list).
1142    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            // Multiple return values: (Foo, Bar, error)
1164            "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    /// Go-specific primitive/builtin types to skip.
1196    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    /// Extract type references from Java source code.
1223    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 Foo extends Bar implements Baz, Qux { ... }
1246                "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                    // extends
1252                    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                    // implements
1263                    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 MyInterface extends OtherInterface { ... }
1275                "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                    // extends_interfaces child (not a named field)
1281                    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                // private Bar field;
1302                "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                // public Bar method(Baz param) { ... }
1316                "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                    // Return type
1322                    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                    // Parameters
1333                    if let Some(params) = node.child_by_field_name("parameters") {
1334                        Self::collect_java_param_types(&params, content, &fn_name, refs);
1335                    }
1336                    // Generic bounds: <T extends Bound>
1337                    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    /// Extract parameter types from Java formal_parameters.
1356    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    /// Extract generic bounds from Java type_parameters (<T extends Bound>).
1386    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                    // type_bound child: extends SomeType
1398                    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    /// Extract type references from Python source code.
1427    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 Foo(Bar, Baz):
1450                "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                        // argument_list containing identifiers
1457                        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                // def foo(x: Bar) -> Baz:
1478                "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(&params, 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                // x: int = 5 (variable type annotations at class level)
1498                "typed_parameter" | "typed_default_parameter" => {
1499                    // Handled in collect_python_param_types
1500                }
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    /// Extract parameter types from Python function parameters.
1516    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                // typed_parameter: name: type, typed_default_parameter: name: type = default
1527                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                        // Skip 'self' parameter type
1532                        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    // --- C# ---
1550
1551    /// Extract type references from C# source code.
1552    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 Foo : Bar, IBaz { ... }
1575                "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                    // base_list contains comma-separated base types
1584                    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                                        // Each entry is identifier, generic_name, or qualified_name
1594                                        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                                            // Heuristic: interface names conventionally start with I
1609                                            // but we emit Implements for all base types since C# has
1610                                            // both extends and implements via base_list
1611                                            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                // private Bar _field;
1636                // C# grammar: field_declaration > variable_declaration > identifier (type) + variable_declarator
1637                "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                                // First identifier child is the type
1645                                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                // public Response Method(Request param) { ... }
1676                // C# grammar: method_declaration has identifier children in order:
1677                //   [modifier] [return_type_identifier] [method_name_identifier] [parameter_list]
1678                // There are no named "type" or "name" fields — positional.
1679                "method_declaration" => {
1680                    // Collect identifiers in order; first is return type, second is name
1681                    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                    // identifiers[0] = return type, identifiers[1] = method name
1703                    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                    // Parameters
1720                    if let Some(params) = param_list {
1721                        Self::collect_csharp_param_types(&params, content, &fn_name, refs);
1722                    }
1723                    // Generic type constraints
1724                    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                    // C# parameter: first identifier/generic_name = type, second = name
1754                    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    /// Extract type constraints from a C# method's type_parameter_constraints_clauses.
1786    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                    // Contains: "where" TypeParam ":" constraint_list
1798                    let mut cc = child.walk();
1799                    if cc.goto_first_child() {
1800                        loop {
1801                            let cc_child = cc.node();
1802                            // type_constraint contains the bound type
1803                            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    // --- Kotlin ---
1856
1857    /// Extract type references from Kotlin source code.
1858    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 Foo : Bar(), IBaz { ... }
1881                "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                            // Kotlin class_declaration uses first type_identifier when no "name" field
1887                            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                    // delegation_specifiers are direct children (not a named field)
1903                    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                                // delegation_specifier contains a constructor_invocation or
1909                                // user_type — extract the type_identifier
1910                                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                // val foo: Bar = ...
1930                // Kotlin grammar: property_declaration > variable_declaration > user_type > type_identifier
1931                "property_declaration" => {
1932                    let container = Self::ancestor_name(&node, content);
1933                    // Walk into variable_declaration to find the user_type
1934                    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                                // user_type is a child of variable_declaration
1940                                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                // fun foo(x: Bar): Baz { ... }
1975                // Kotlin grammar: function_declaration > simple_identifier (name) >
1976                //   function_value_parameters > parameter > user_type >
1977                //   : > user_type (return type)
1978                "function_declaration" => {
1979                    // Find the function name (first simple_identifier) and collect
1980                    // function_value_parameters and return type (user_type after params)
1981                    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                                // Return type comes after function_value_parameters as user_type
1998                                "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(&params, 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        // Kotlin function_value_parameters contains parameter nodes directly
2047        // Each parameter: simple_identifier : user_type
2048        let mut cursor = params.walk();
2049        if cursor.goto_first_child() {
2050            loop {
2051                let child = cursor.node();
2052                if child.kind() == "parameter" {
2053                    // Find user_type or nullable_type child (after simple_identifier and :)
2054                    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(&param_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    /// Find the first type_identifier in a Kotlin delegation_specifier subtree.
2089    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    // --- Swift ---
2135
2136    /// Extract type references from Swift source code.
2137    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 Foo: Bar, Proto { ... } or struct/enum/actor
2160                "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                    // inheritance_specifier children contain the inherited types
2167                    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 Foo: Bar { ... }
2189                "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                // var foo: Bar = ...
2216                "property_declaration" => {
2217                    let container = Self::ancestor_name(&node, content);
2218                    // type_annotation child contains the type
2219                    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                // func foo(req: Bar) -> Baz { ... }
2243                // Swift grammar: function_declaration has direct parameter children (not in a
2244                // parameters node). Parameters come between ( and ).
2245                // Return type is user_type after the -> token.
2246                "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                    // Return type: "return_type" field, or user_type after ->
2267                    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                        // Find user_type that appears after the arrow (->)
2280                        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                    // Parameters: collect parameter children of function_declaration
2313                    // (they are direct children, not in a parameters field)
2314                    if let Some(params) = node.child_by_field_name("parameters") {
2315                        Self::collect_swift_param_types(&params, content, &fn_name, refs);
2316                    } else {
2317                        // Parameters are direct children between ( and )
2318                        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    /// Extract param type from a single Swift `parameter` node.
2369    /// Swift parameter structure: simple_identifier : user_type
2370    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(&param_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    /// Find the first type_identifier in a Swift inheritance_specifier subtree.
2404    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    // --- C++ ---
2455
2456    /// Extract type references from C++ source code.
2457    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 Derived : public Base { ... }
2480                "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                    // base_class_clause contains the base types
2486                    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                    // Recurse into body for field declarations
2528                    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                // int field_name;  or  MyType field_name;
2538                "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                // ReturnType funcName(Params) { ... }
2556                "function_definition" => {
2557                    // Get return type from "type" field
2558                    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                        // Parameters: find parameter_list in declarator
2578                        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        // function_declarator has a "parameters" field
2609        if declarator.kind() == "function_declarator" {
2610            if let Some(params) = declarator.child_by_field_name("parameters") {
2611                Self::collect_cpp_param_types(&params, content, fn_name, refs);
2612            }
2613        } else {
2614            // recurse into nested declarators (pointer_declarator, etc.)
2615            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(&params, content, fn_name, refs);
2622                        }
2623                        break;
2624                    }
2625                    // recurse one more level
2626                    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    /// Extract a usable type name from a C++ type node.
2669    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/reference type: recurse into the inner type
2681            "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    /// Extract a function name from a C++ declarator node.
2707    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    // --- Ruby ---
2758
2759    /// Extract type references from Ruby source code.
2760    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 Foo < Bar
2783                "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                    // "superclass" child node
2789                    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                                // superclass contains a constant or scope_resolution
2795                                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                // include Foo or prepend Bar
2831                "call" => {
2832                    // Check if this is a bare include/prepend call
2833                    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    // --- Helpers ---
2883
2884    /// Walk up the AST to find the nearest named ancestor (struct, impl, trait, class, function).
2885    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                // Java/C#/Kotlin/Swift
2895                | "class_declaration"
2896                | "interface_declaration"
2897                | "struct_declaration"
2898                | "record_declaration"
2899                | "protocol_declaration"
2900                | "actor_declaration"
2901                | "method_declaration"
2902                // Python
2903                | "class_definition"
2904                | "function_definition"
2905                // C++
2906                | "class_specifier"
2907                | "struct_specifier"
2908                // Ruby
2909                | "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                    // impl_item uses "type" field for the implemented type
2915                    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                    // C++ class_specifier/struct_specifier: name is a direct child type_identifier
2921                    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                    // Kotlin class_declaration: name may be first type_identifier
2935                    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    /// Extract all type_identifier nodes from a type expression.
2958    /// Handles generics (Vec<Foo>), references (&Foo), tuples, etc.
2959    /// Filters out primitive/builtin types.
2960    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        // Rust: type_identifier, TypeScript: type_identifier, Python: identifier/attribute
2974        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        // Scoped type: path::to::Type — take the last segment
2983        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        // Python attribute access: module.Type
2994        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        // Recurse into children
3005        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    /// Check if a type name is a primitive/builtin that we shouldn't track.
3017    fn is_primitive_type(name: &str) -> bool {
3018        matches!(
3019            name,
3020            // Rust primitives
3021            "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                // Rust common containers (keep the type params, skip the container)
3040                | "Option"
3041                | "Result"
3042                | "Vec"
3043                | "Box"
3044                | "Rc"
3045                | "Arc"
3046                | "Cell"
3047                | "RefCell"
3048                | "Cow"
3049                | "Pin"
3050                // TypeScript/JavaScript primitives
3051                | "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                // Python primitives
3074                | "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                // Java boxed primitives and root types (not user-defined)
3094                | "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    /// Find callers (symbols that call a given function) across all files
3109    #[allow(dead_code)] // Call graph API - used by index
3110    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            // Skip files without language support or calls query
3125            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, // grammar unavailable — caller logged elsewhere
3136            };
3137            for symbol in symbols {
3138                let callees = self.find_callees_for_symbol(&full_path, &content, &symbol);
3139                // Check if any callee matches, considering qualifiers
3140                let is_caller = callees.iter().any(|(name, _, qualifier, _)| {
3141                    if name != symbol_name {
3142                        return false;
3143                    }
3144                    // Match if: no qualifier, or qualifier is self/Self
3145                    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
3160/// Strip surrounding quotes from import path strings (", ', or `).
3161fn 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    /// Probe whether the grammar for `path`'s extension is loadable. Used by
3173    /// per-language type-ref tests to skip cleanly when the developer hasn't
3174    /// run `cargo xtask build-grammars` (only the 6 default grammars in
3175    /// `~/.config/normalize/grammars/` are loadable otherwise). Without these
3176    /// guards a missing grammar would panic the test, which in turn poisons
3177    /// the libsql global init and cascades into spurious failures in
3178    /// unrelated `index::tests::*`.
3179    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    // --- C# type refs ---
3479
3480    #[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    // --- Kotlin type refs ---
3543
3544    #[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    // --- Swift type refs ---
3607
3608    #[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    // --- C++ type refs ---
3664
3665    #[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    // --- Ruby type refs ---
3717
3718    #[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}