1use std::collections::HashSet;
6use std::path::Path;
7
8use tree_sitter::{Node, Tree};
9
10use crate::fs::tree::{collect_files, get_file_tree};
11use crate::types::{CodeStructure, DefinitionInfo, FileStructure, IgnoreSpec, Language};
12use crate::TldrResult;
13
14use super::extract::is_upper_case_name;
15use super::imports::extract_imports_from_tree;
16use super::parser::parse_file;
17
18pub fn get_code_structure(
32 root: &Path,
33 language: Language,
34 max_results: usize,
35 ignore_spec: Option<&IgnoreSpec>,
36) -> TldrResult<CodeStructure> {
37 if root.is_file() {
39 let parent = root.parent().unwrap_or(root);
40 match extract_file_structure(root, parent, language) {
41 Ok(structure) => {
42 return Ok(CodeStructure {
43 root: root.to_path_buf(),
44 language,
45 files: vec![structure],
46 });
47 }
48 Err(e) => {
49 return Err(e);
50 }
51 }
52 }
53
54 let extensions: HashSet<String> = language
56 .extensions()
57 .iter()
58 .map(|s| s.to_string())
59 .collect();
60
61 let tree = get_file_tree(root, Some(&extensions), true, ignore_spec)?;
62 let files = collect_files(&tree, root);
63
64 let mut file_structures = Vec::new();
65
66 for file_path in files {
67 if max_results > 0 && file_structures.len() >= max_results {
69 break;
70 }
71
72 match extract_file_structure(&file_path, root, language) {
74 Ok(structure) => file_structures.push(structure),
75 Err(e) => {
76 if e.is_recoverable() {
78 eprintln!("Warning: Skipping {} - {}", file_path.display(), e);
79 } else {
80 return Err(e);
81 }
82 }
83 }
84 }
85
86 Ok(CodeStructure {
87 root: root.to_path_buf(),
88 language,
89 files: file_structures,
90 })
91}
92
93fn extract_file_structure(
95 path: &Path,
96 root: &Path,
97 language: Language,
98) -> TldrResult<FileStructure> {
99 let (tree, source, _) = parse_file(path)?;
100
101 let relative_path = path.strip_prefix(root).unwrap_or(path).to_path_buf();
102
103 let functions = extract_functions(&tree, &source, language);
104 let classes = extract_classes(&tree, &source, language);
105 let methods = extract_methods(&tree, &source, language);
106 let imports = extract_imports_from_tree(&tree, &source, language)?;
107 let definitions = extract_definitions(&tree, &source, language);
108
109 Ok(FileStructure {
110 path: relative_path,
111 functions,
112 classes,
113 methods,
114 imports,
115 definitions,
116 })
117}
118
119pub fn extract_functions(tree: &Tree, source: &str, language: Language) -> Vec<String> {
121 let mut functions = Vec::new();
122 let root = tree.root_node();
123
124 match language {
125 Language::Python => extract_python_functions(&root, source, &mut functions, false),
126 Language::TypeScript | Language::JavaScript => {
127 extract_ts_functions(&root, source, &mut functions, false)
128 }
129 Language::Go => extract_go_functions(&root, source, &mut functions),
130 Language::Rust => extract_rust_functions(&root, source, &mut functions),
131 Language::Java => extract_java_functions(&root, source, &mut functions, false),
132 Language::C => extract_c_functions(&root, source, &mut functions),
133 Language::Cpp => extract_cpp_functions(&root, source, &mut functions),
134 Language::Ruby => extract_ruby_functions(&root, source, &mut functions, false),
135 Language::Scala => extract_scala_functions(&root, source, &mut functions),
136 Language::Kotlin => extract_kotlin_functions(&root, source, &mut functions, false),
137 Language::Ocaml => extract_ocaml_functions(&root, source, &mut functions),
138 Language::Php => extract_php_functions(&root, source, &mut functions, false),
139 Language::Swift => extract_swift_functions(&root, source, &mut functions),
140 Language::CSharp => {} Language::Elixir => extract_elixir_functions(&root, source, &mut functions),
142 Language::Lua => extract_lua_functions(&root, source, &mut functions),
143 Language::Luau => extract_luau_functions(&root, source, &mut functions),
144 }
145
146 functions
147}
148
149pub fn extract_classes(tree: &Tree, source: &str, language: Language) -> Vec<String> {
151 let mut classes = Vec::new();
152 let root = tree.root_node();
153
154 match language {
155 Language::Python => extract_python_classes(&root, source, &mut classes),
156 Language::TypeScript | Language::JavaScript => {
157 extract_ts_classes(&root, source, &mut classes)
158 }
159 Language::Go => extract_go_structs(&root, source, &mut classes),
160 Language::Rust => extract_rust_structs(&root, source, &mut classes),
161 Language::Java => extract_java_classes(&root, source, &mut classes),
162 Language::C => extract_c_structs(&root, source, &mut classes),
163 Language::Cpp => extract_cpp_classes(&root, source, &mut classes),
164 Language::Ruby => extract_ruby_classes(&root, source, &mut classes),
165 Language::Scala => extract_scala_classes(&root, source, &mut classes),
166 Language::Kotlin => extract_kotlin_classes(&root, source, &mut classes),
167 Language::Php => extract_php_classes(&root, source, &mut classes),
168 Language::Swift => extract_swift_classes(&root, source, &mut classes),
169 Language::CSharp => extract_csharp_classes(&root, source, &mut classes),
170 Language::Elixir => extract_elixir_classes(&root, source, &mut classes),
171 Language::Lua => {} Language::Luau => {} _ => {}
174 }
175
176 classes
177}
178
179pub fn extract_methods(tree: &Tree, source: &str, language: Language) -> Vec<String> {
181 let mut methods = Vec::new();
182 let root = tree.root_node();
183
184 match language {
185 Language::Python => extract_python_functions(&root, source, &mut methods, true),
186 Language::TypeScript | Language::JavaScript => {
187 extract_ts_functions(&root, source, &mut methods, true)
188 }
189 Language::Go => {} Language::Rust => extract_rust_impl_methods(&root, source, &mut methods),
191 Language::Java => extract_java_functions(&root, source, &mut methods, true),
192 Language::C => {} Language::Cpp => extract_cpp_methods(&root, source, &mut methods),
194 Language::Ruby => extract_ruby_functions(&root, source, &mut methods, true),
195 Language::Scala => extract_scala_methods(&root, source, &mut methods),
196 Language::Kotlin => extract_kotlin_functions(&root, source, &mut methods, true),
197 Language::Php => extract_php_functions(&root, source, &mut methods, true),
198 Language::Swift => extract_swift_methods(&root, source, &mut methods),
199 Language::CSharp => extract_csharp_methods(&root, source, &mut methods),
200 Language::Elixir => {} Language::Lua => {} Language::Luau => {} _ => {}
204 }
205
206 methods
207}
208
209fn extract_python_functions(
214 node: &Node,
215 source: &str,
216 functions: &mut Vec<String>,
217 methods_only: bool,
218) {
219 let mut cursor = node.walk();
220
221 for child in node.children(&mut cursor) {
222 match child.kind() {
223 "function_definition" => {
224 let is_method = is_inside_class(&child);
226
227 if methods_only == is_method {
228 if let Some(name_node) = child.child_by_field_name("name") {
229 let name = get_node_text(&name_node, source);
230 functions.push(name);
231 }
232 }
233 }
234 "class_definition" => {
235 if let Some(body) = child.child_by_field_name("body") {
237 extract_python_functions(&body, source, functions, methods_only);
238 }
239 }
240 _ => {
241 extract_python_functions(&child, source, functions, methods_only);
243 }
244 }
245 }
246}
247
248fn extract_python_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
249 let mut cursor = node.walk();
250
251 for child in node.children(&mut cursor) {
252 if child.kind() == "class_definition" {
253 if let Some(name_node) = child.child_by_field_name("name") {
254 let name = get_node_text(&name_node, source);
255 classes.push(name);
256 }
257 }
258 extract_python_classes(&child, source, classes);
259 }
260}
261
262fn extract_ts_functions(
267 node: &Node,
268 source: &str,
269 functions: &mut Vec<String>,
270 methods_only: bool,
271) {
272 let mut cursor = node.walk();
273
274 for child in node.children(&mut cursor) {
275 match child.kind() {
276 "function_declaration"
277 | "function"
278 | "generator_function_declaration"
279 | "generator_function" => {
280 if !methods_only {
281 if let Some(name_node) = child.child_by_field_name("name") {
282 let name = get_node_text(&name_node, source);
283 functions.push(name);
284 }
285 }
286 }
287 "function_expression" => {
288 if !methods_only {
291 let mut extracted = false;
292 if let Some(parent) = child.parent() {
293 if parent.kind() == "variable_declarator" {
294 if let Some(name_node) = parent.child_by_field_name("name") {
295 let name = get_node_text(&name_node, source);
296 functions.push(name);
297 extracted = true;
298 }
299 }
300 }
301 if !extracted {
302 if let Some(name_node) = child.child_by_field_name("name") {
303 let name = get_node_text(&name_node, source);
304 functions.push(name);
305 }
306 }
307 }
308 }
309 "method_definition" | "method_signature" | "abstract_method_signature" => {
310 if methods_only {
316 if let Some(name_node) = child.child_by_field_name("name") {
317 let name = get_node_text(&name_node, source);
318 functions.push(name);
319 }
320 }
321 }
322 "arrow_function" => {
323 if !methods_only {
325 if let Some(parent) = child.parent() {
326 if parent.kind() == "variable_declarator" {
327 if let Some(name_node) = parent.child_by_field_name("name") {
328 let name = get_node_text(&name_node, source);
329 functions.push(name);
330 }
331 }
332 }
333 }
334 }
335 "class_declaration" | "class" => {
336 if let Some(body) = child.child_by_field_name("body") {
338 extract_ts_functions(&body, source, functions, methods_only);
339 }
340 }
341 _ => {
342 extract_ts_functions(&child, source, functions, methods_only);
343 }
344 }
345 }
346}
347
348fn extract_ts_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
349 let mut cursor = node.walk();
350
351 for child in node.children(&mut cursor) {
352 if child.kind() == "class_declaration" || child.kind() == "class" {
353 if let Some(name_node) = child.child_by_field_name("name") {
354 let name = get_node_text(&name_node, source);
355 classes.push(name);
356 }
357 }
358 extract_ts_classes(&child, source, classes);
359 }
360}
361
362fn extract_go_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
367 let mut cursor = node.walk();
368
369 for child in node.children(&mut cursor) {
370 if child.kind() == "function_declaration" || child.kind() == "method_declaration" {
371 if let Some(name_node) = child.child_by_field_name("name") {
372 let name = get_node_text(&name_node, source);
373 functions.push(name);
374 }
375 }
376 extract_go_functions(&child, source, functions);
377 }
378}
379
380fn extract_go_structs(node: &Node, source: &str, structs: &mut Vec<String>) {
383 let mut cursor = node.walk();
384
385 for child in node.children(&mut cursor) {
386 if child.kind() == "type_declaration" {
387 let mut inner_cursor = child.walk();
389 for inner in child.children(&mut inner_cursor) {
390 if inner.kind() == "type_spec" {
391 let mut has_struct = false;
393 let mut type_name = None;
394 let mut spec_cursor = inner.walk();
395 for spec_child in inner.children(&mut spec_cursor) {
396 if spec_child.kind() == "type_identifier" {
397 type_name = Some(get_node_text(&spec_child, source));
398 }
399 if spec_child.kind() == "struct_type"
400 || spec_child.kind() == "interface_type"
401 {
402 has_struct = true;
403 }
404 }
405 if has_struct {
406 if let Some(name) = type_name {
407 structs.push(name);
408 }
409 }
410 }
411 }
412 }
413 extract_go_structs(&child, source, structs);
414 }
415}
416
417fn extract_rust_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
422 let mut cursor = node.walk();
423
424 for child in node.children(&mut cursor) {
425 if child.kind() == "function_item" {
426 if !is_inside_impl(&child) {
428 if let Some(name_node) = child.child_by_field_name("name") {
429 let name = get_node_text(&name_node, source);
430 functions.push(name);
431 }
432 }
433 }
434 extract_rust_functions(&child, source, functions);
435 }
436}
437
438fn extract_rust_structs(node: &Node, source: &str, structs: &mut Vec<String>) {
439 let mut cursor = node.walk();
440
441 for child in node.children(&mut cursor) {
442 if child.kind() == "struct_item"
443 || child.kind() == "enum_item"
444 || child.kind() == "trait_item"
445 {
446 if let Some(name_node) = child.child_by_field_name("name") {
447 let name = get_node_text(&name_node, source);
448 structs.push(name);
449 }
450 }
451 extract_rust_structs(&child, source, structs);
452 }
453}
454
455fn extract_rust_impl_methods(node: &Node, source: &str, methods: &mut Vec<String>) {
456 let mut cursor = node.walk();
457
458 for child in node.children(&mut cursor) {
459 if child.kind() == "impl_item" {
460 if let Some(body) = child.child_by_field_name("body") {
462 let mut body_cursor = body.walk();
463 for item in body.children(&mut body_cursor) {
464 if item.kind() == "function_item" {
465 if let Some(name_node) = item.child_by_field_name("name") {
466 let name = get_node_text(&name_node, source);
467 methods.push(name);
468 }
469 }
470 }
471 }
472 } else if child.kind() == "trait_item" {
473 let mut trait_cursor = child.walk();
475 for trait_child in child.children(&mut trait_cursor) {
476 if trait_child.kind() == "declaration_list" {
477 let mut body_cursor = trait_child.walk();
478 for item in trait_child.children(&mut body_cursor) {
479 if item.kind() == "function_item"
480 || item.kind() == "function_signature_item"
481 {
482 if let Some(name_node) = item.child_by_field_name("name") {
483 let name = get_node_text(&name_node, source);
484 methods.push(name);
485 }
486 }
487 }
488 }
489 }
490 }
491 extract_rust_impl_methods(&child, source, methods);
492 }
493}
494
495fn extract_java_functions(
500 node: &Node,
501 source: &str,
502 functions: &mut Vec<String>,
503 methods_only: bool,
504) {
505 let mut cursor = node.walk();
506
507 for child in node.children(&mut cursor) {
508 match child.kind() {
509 "method_declaration" => {
510 let is_in_class = is_inside_class(&child);
511 if methods_only == is_in_class {
512 if let Some(name_node) = child.child_by_field_name("name") {
513 let name = get_node_text(&name_node, source);
514 functions.push(name);
515 }
516 }
517 }
518 "constructor_declaration" => {
519 if methods_only {
521 if let Some(name_node) = child.child_by_field_name("name") {
522 let name = get_node_text(&name_node, source);
523 functions.push(name);
524 }
525 }
526 }
527 _ => {}
528 }
529 extract_java_functions(&child, source, functions, methods_only);
530 }
531}
532
533fn extract_java_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
534 let mut cursor = node.walk();
535
536 for child in node.children(&mut cursor) {
537 if child.kind() == "class_declaration" || child.kind() == "interface_declaration" {
538 if let Some(name_node) = child.child_by_field_name("name") {
539 let name = get_node_text(&name_node, source);
540 classes.push(name);
541 }
542 }
543 extract_java_classes(&child, source, classes);
544 }
545}
546
547fn extract_c_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
552 let mut cursor = node.walk();
553
554 for child in node.children(&mut cursor) {
555 if child.kind() == "function_definition" {
556 if let Some(declarator) = child.child_by_field_name("declarator") {
558 if let Some(name) = extract_c_function_name(&declarator, source) {
559 functions.push(name);
560 }
561 }
562 }
563 extract_c_functions(&child, source, functions);
564 }
565}
566
567fn extract_c_function_name(node: &Node, source: &str) -> Option<String> {
570 match node.kind() {
571 "function_declarator" => {
572 if let Some(declarator) = node.child_by_field_name("declarator") {
574 if declarator.kind() == "identifier" {
575 return Some(get_node_text(&declarator, source));
576 } else if declarator.kind() == "parenthesized_declarator" {
577 return extract_c_function_name(&declarator, source);
579 }
580 }
581 }
582 "pointer_declarator" => {
583 if let Some(declarator) = node.child_by_field_name("declarator") {
585 return extract_c_function_name(&declarator, source);
586 }
587 }
588 "identifier" => {
589 return Some(get_node_text(node, source));
590 }
591 _ => {
592 let mut cursor = node.walk();
594 for child in node.children(&mut cursor) {
595 if let Some(name) = extract_c_function_name(&child, source) {
596 return Some(name);
597 }
598 }
599 }
600 }
601 None
602}
603
604fn extract_c_structs(node: &Node, source: &str, structs: &mut Vec<String>) {
605 let mut cursor = node.walk();
606
607 for child in node.children(&mut cursor) {
608 match child.kind() {
609 "struct_specifier" | "enum_specifier" => {
610 if child.child_by_field_name("body").is_some() {
615 if let Some(name_node) = child.child_by_field_name("name") {
616 let name = get_node_text(&name_node, source);
617 structs.push(name);
618 }
619 }
620 }
621 "type_definition" => {
622 let mut has_struct_or_enum = false;
625 let mut typedef_name = None;
626 let mut inner_cursor = child.walk();
627 for inner in child.children(&mut inner_cursor) {
628 if (inner.kind() == "struct_specifier" || inner.kind() == "enum_specifier")
633 && inner.child_by_field_name("body").is_some()
634 {
635 has_struct_or_enum = true;
636 }
637 if inner.kind() == "type_identifier" {
638 typedef_name = Some(get_node_text(&inner, source));
639 }
640 }
641 if has_struct_or_enum {
642 if let Some(name) = typedef_name {
643 structs.push(name);
644 }
645 }
646 }
647 _ => {}
648 }
649 extract_c_structs(&child, source, structs);
650 }
651}
652
653fn extract_cpp_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
658 let mut cursor = node.walk();
659
660 for child in node.children(&mut cursor) {
661 if child.kind() == "function_definition" {
662 if !is_inside_cpp_class(&child) {
664 if let Some(declarator) = child.child_by_field_name("declarator") {
665 if let Some(name) = extract_cpp_function_name(&declarator, source) {
666 functions.push(name);
667 }
668 }
669 }
670 }
671 extract_cpp_functions(&child, source, functions);
672 }
673}
674
675fn extract_cpp_function_name(node: &Node, source: &str) -> Option<String> {
678 match node.kind() {
679 "function_declarator" => {
680 if let Some(declarator) = node.child_by_field_name("declarator") {
681 return extract_cpp_function_name(&declarator, source);
682 }
683 }
684 "qualified_identifier" | "scoped_identifier" => {
685 if let Some(name) = node.child_by_field_name("name") {
687 return Some(get_node_text(&name, source));
688 }
689 return Some(get_node_text(node, source));
691 }
692 "pointer_declarator" | "reference_declarator" => {
693 if let Some(declarator) = node.child_by_field_name("declarator") {
694 return extract_cpp_function_name(&declarator, source);
695 }
696 }
697 "identifier" | "field_identifier" | "destructor_name" => {
698 return Some(get_node_text(node, source));
699 }
700 "operator_name" => {
701 return Some(get_node_text(node, source));
703 }
704 _ => {
705 let mut cursor = node.walk();
707 for child in node.children(&mut cursor) {
708 if let Some(name) = extract_cpp_function_name(&child, source) {
709 return Some(name);
710 }
711 }
712 }
713 }
714 None
715}
716
717fn extract_cpp_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
718 let mut cursor = node.walk();
719
720 for child in node.children(&mut cursor) {
721 match child.kind() {
722 "class_specifier" | "struct_specifier" | "enum_specifier" => {
723 if let Some(name_node) = child.child_by_field_name("name") {
725 let name = get_node_text(&name_node, source);
726 classes.push(name);
727 }
728 }
729 _ => {}
730 }
731 extract_cpp_classes(&child, source, classes);
732 }
733}
734
735fn extract_cpp_methods(node: &Node, source: &str, methods: &mut Vec<String>) {
739 let mut cursor = node.walk();
740
741 for child in node.children(&mut cursor) {
742 match child.kind() {
743 "class_specifier" | "struct_specifier" => {
744 if let Some(body) = child.child_by_field_name("body") {
746 let mut body_cursor = body.walk();
747 for body_child in body.children(&mut body_cursor) {
748 if body_child.kind() == "function_definition" {
749 if cpp_has_default_or_delete(&body_child) {
751 continue;
752 }
753 if let Some(declarator) = body_child.child_by_field_name("declarator") {
755 if let Some(name) = extract_cpp_function_name(&declarator, source) {
756 methods.push(name);
757 }
758 }
759 }
760 }
761 }
762 }
763 _ => {}
764 }
765 extract_cpp_methods(&child, source, methods);
766 }
767}
768
769fn cpp_has_default_or_delete(node: &Node) -> bool {
771 let mut cursor = node.walk();
772 for child in node.children(&mut cursor) {
773 if child.kind() == "default_method_clause" || child.kind() == "delete_method_clause" {
774 return true;
775 }
776 }
777 false
778}
779
780fn is_inside_cpp_class(node: &Node) -> bool {
782 let mut current = node.parent();
783 while let Some(parent) = current {
784 match parent.kind() {
785 "class_specifier" | "struct_specifier" => return true,
786 "field_declaration_list" => {
787 if let Some(grandparent) = parent.parent() {
789 if matches!(grandparent.kind(), "class_specifier" | "struct_specifier") {
790 return true;
791 }
792 }
793 }
794 "translation_unit" => return false, _ => {}
796 }
797 current = parent.parent();
798 }
799 false
800}
801
802fn extract_ruby_functions(
811 node: &Node,
812 source: &str,
813 functions: &mut Vec<String>,
814 methods_only: bool,
815) {
816 let mut cursor = node.walk();
817
818 for child in node.children(&mut cursor) {
819 match child.kind() {
820 "method" => {
821 let is_method = is_inside_ruby_class_or_module(&child);
823
824 if methods_only == is_method {
825 let mut method_cursor = child.walk();
827 for method_child in child.children(&mut method_cursor) {
828 if method_child.kind() == "identifier" {
829 let name = get_node_text(&method_child, source);
830 functions.push(name);
831 break;
832 }
833 }
834 }
835 }
836 "singleton_method" => {
837 if methods_only {
840 let mut method_cursor = child.walk();
841 for method_child in child.children(&mut method_cursor) {
842 if method_child.kind() == "identifier" {
844 let name = get_node_text(&method_child, source);
845 if name != "self" {
847 functions.push(name);
848 break;
849 }
850 }
851 }
852 }
853 }
854 "class" | "module" => {
855 let body_found = child.child_by_field_name("body");
860 if let Some(body) = body_found {
861 extract_ruby_functions(&body, source, functions, methods_only);
862 } else {
863 let mut class_cursor = child.walk();
865 for class_child in child.children(&mut class_cursor) {
866 if class_child.kind() == "body_statement" {
867 extract_ruby_functions(&class_child, source, functions, methods_only);
868 }
869 }
870 }
871 }
872 _ => {
873 extract_ruby_functions(&child, source, functions, methods_only);
875 }
876 }
877 }
878}
879
880fn extract_ruby_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
882 let mut cursor = node.walk();
883
884 for child in node.children(&mut cursor) {
885 match child.kind() {
886 "class" | "module" => {
887 let mut class_cursor = child.walk();
889 for class_child in child.children(&mut class_cursor) {
890 if class_child.kind() == "constant" || class_child.kind() == "scope_resolution"
891 {
892 let name = get_node_text(&class_child, source);
893 classes.push(name);
894 break;
895 }
896 }
897 }
898 _ => {}
899 }
900 extract_ruby_classes(&child, source, classes);
902 }
903}
904
905fn is_inside_ruby_class_or_module(node: &Node) -> bool {
907 let mut current = node.parent();
908 while let Some(parent) = current {
909 match parent.kind() {
910 "class" | "module" | "body_statement" => {
911 if parent.kind() == "body_statement" {
913 if let Some(grandparent) = parent.parent() {
914 if grandparent.kind() == "class" || grandparent.kind() == "module" {
915 return true;
916 }
917 }
918 } else {
919 return true;
920 }
921 }
922 "program" => return false, _ => {}
924 }
925 current = parent.parent();
926 }
927 false
928}
929
930fn extract_scala_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
937 let mut cursor = node.walk();
938
939 for child in node.children(&mut cursor) {
940 match child.kind() {
941 "function_definition" => {
942 if !is_inside_scala_class_or_trait(&child) {
945 if let Some(name_node) = child.child_by_field_name("name") {
946 let name = get_node_text(&name_node, source);
947 functions.push(name);
948 }
949 }
950 }
951 "object_definition" => {
952 if let Some(body) = child.child_by_field_name("body") {
954 extract_scala_functions(&body, source, functions);
955 }
956 }
957 "template_body" => {
958 extract_scala_functions(&child, source, functions);
960 }
961 _ => {
962 extract_scala_functions(&child, source, functions);
964 }
965 }
966 }
967}
968
969fn extract_scala_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
971 let mut cursor = node.walk();
972
973 for child in node.children(&mut cursor) {
974 match child.kind() {
975 "class_definition" | "trait_definition" => {
976 if let Some(name_node) = child.child_by_field_name("name") {
977 let name = get_node_text(&name_node, source);
978 classes.push(name);
979 }
980 }
981 _ => {}
982 }
983 extract_scala_classes(&child, source, classes);
985 }
986}
987
988fn extract_scala_methods(node: &Node, source: &str, methods: &mut Vec<String>) {
990 let mut cursor = node.walk();
991
992 for child in node.children(&mut cursor) {
993 match child.kind() {
994 "function_definition" => {
995 if is_inside_scala_class_or_trait(&child) {
997 if let Some(name_node) = child.child_by_field_name("name") {
998 let name = get_node_text(&name_node, source);
999 methods.push(name);
1000 }
1001 }
1002 }
1003 "class_definition" | "trait_definition" => {
1004 if let Some(body) = child.child_by_field_name("body") {
1006 extract_scala_methods(&body, source, methods);
1007 }
1008 }
1009 "template_body" => {
1010 extract_scala_methods(&child, source, methods);
1012 }
1013 _ => {
1014 extract_scala_methods(&child, source, methods);
1016 }
1017 }
1018 }
1019}
1020
1021fn is_inside_scala_class_or_trait(node: &Node) -> bool {
1023 let mut current = node.parent();
1024 while let Some(parent) = current {
1025 match parent.kind() {
1026 "class_definition" | "trait_definition" => return true,
1027 "object_definition" => return false, "compilation_unit" => return false, _ => {}
1030 }
1031 current = parent.parent();
1032 }
1033 false
1034}
1035
1036fn extract_kotlin_functions(
1045 node: &Node,
1046 source: &str,
1047 functions: &mut Vec<String>,
1048 methods_only: bool,
1049) {
1050 let mut cursor = node.walk();
1051
1052 for child in node.children(&mut cursor) {
1053 match child.kind() {
1054 "function_declaration" => {
1055 let is_method = is_inside_kotlin_class_or_object(&child);
1057
1058 if methods_only == is_method {
1059 if let Some(name_node) = child.child_by_field_name("name") {
1060 let name = get_node_text(&name_node, source);
1061 functions.push(name);
1062 }
1063 }
1064 }
1065 "class_declaration" | "object_declaration" | "companion_object" => {
1066 let mut class_cursor = child.walk();
1068 for class_child in child.children(&mut class_cursor) {
1069 if class_child.kind() == "class_body" {
1070 extract_kotlin_functions(&class_child, source, functions, methods_only);
1071 }
1072 }
1073 }
1074 _ => {
1075 extract_kotlin_functions(&child, source, functions, methods_only);
1076 }
1077 }
1078 }
1079}
1080
1081fn extract_kotlin_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
1083 let mut cursor = node.walk();
1084
1085 for child in node.children(&mut cursor) {
1086 match child.kind() {
1087 "class_declaration" | "object_declaration" => {
1088 if let Some(name_node) = child.child_by_field_name("name") {
1089 let name = get_node_text(&name_node, source);
1090 classes.push(name);
1091 }
1092 }
1093 _ => {}
1094 }
1095 extract_kotlin_classes(&child, source, classes);
1096 }
1097}
1098
1099fn is_inside_kotlin_class_or_object(node: &Node) -> bool {
1101 let mut current = node.parent();
1102 while let Some(parent) = current {
1103 match parent.kind() {
1104 "class_declaration" | "object_declaration" | "companion_object" => return true,
1105 "class_body" => {
1106 if let Some(grandparent) = parent.parent() {
1108 if matches!(
1109 grandparent.kind(),
1110 "class_declaration" | "object_declaration" | "companion_object"
1111 ) {
1112 return true;
1113 }
1114 }
1115 }
1116 "source_file" => return false, _ => {}
1118 }
1119 current = parent.parent();
1120 }
1121 false
1122}
1123
1124fn extract_ocaml_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
1133 let mut cursor = node.walk();
1134
1135 for child in node.children(&mut cursor) {
1136 if child.kind() == "value_definition" {
1137 let mut inner_cursor = child.walk();
1138 for inner in child.children(&mut inner_cursor) {
1139 if inner.kind() == "let_binding" {
1140 if ocaml_binding_has_params_simple(&inner) {
1142 if let Some(pattern_node) = inner.child_by_field_name("pattern") {
1143 let name = get_node_text(&pattern_node, source);
1144 if name != "()" && !name.is_empty() {
1146 functions.push(name);
1147 }
1148 }
1149 }
1150 }
1151 }
1152 }
1153 extract_ocaml_functions(&child, source, functions);
1154 }
1155}
1156
1157fn ocaml_binding_has_params_simple(node: &Node) -> bool {
1159 let mut cursor = node.walk();
1160 for child in node.children(&mut cursor) {
1161 if child.kind() == "parameter" {
1162 return true;
1163 }
1164 }
1165 false
1166}
1167
1168fn extract_php_functions(
1177 node: &Node,
1178 source: &str,
1179 functions: &mut Vec<String>,
1180 methods_only: bool,
1181) {
1182 let mut cursor = node.walk();
1183
1184 for child in node.children(&mut cursor) {
1185 match child.kind() {
1186 "function_definition" => {
1187 let is_method = is_inside_php_class(&child);
1190
1191 if methods_only == is_method {
1192 if let Some(name_node) = child.child_by_field_name("name") {
1194 let name = get_node_text(&name_node, source);
1195 functions.push(name);
1196 }
1197 }
1198 }
1199 "method_declaration" => {
1200 if methods_only {
1202 if let Some(name_node) = child.child_by_field_name("name") {
1203 let name = get_node_text(&name_node, source);
1204 functions.push(name);
1205 }
1206 }
1207 }
1208 "class_declaration" | "interface_declaration" | "trait_declaration" => {
1209 if let Some(body) = child.child_by_field_name("body") {
1213 extract_php_functions(&body, source, functions, methods_only);
1214 } else {
1215 let mut class_cursor = child.walk();
1217 for class_child in child.children(&mut class_cursor) {
1218 if class_child.kind() == "declaration_list" {
1219 extract_php_functions(&class_child, source, functions, methods_only);
1220 }
1221 }
1222 }
1223 }
1224 _ => {
1225 extract_php_functions(&child, source, functions, methods_only);
1227 }
1228 }
1229 }
1230}
1231
1232fn extract_php_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
1234 let mut cursor = node.walk();
1235
1236 for child in node.children(&mut cursor) {
1237 match child.kind() {
1238 "class_declaration" | "interface_declaration" | "trait_declaration" => {
1239 if let Some(name_node) = child.child_by_field_name("name") {
1241 let name = get_node_text(&name_node, source);
1242 classes.push(name);
1243 }
1244 }
1245 _ => {}
1246 }
1247 extract_php_classes(&child, source, classes);
1250 }
1251}
1252
1253fn is_inside_php_class(node: &Node) -> bool {
1255 let mut current = node.parent();
1256 while let Some(parent) = current {
1257 match parent.kind() {
1258 "class_declaration"
1259 | "interface_declaration"
1260 | "trait_declaration"
1261 | "declaration_list" => {
1262 if parent.kind() == "declaration_list" {
1264 if let Some(grandparent) = parent.parent() {
1265 if matches!(
1266 grandparent.kind(),
1267 "class_declaration" | "interface_declaration" | "trait_declaration"
1268 ) {
1269 return true;
1270 }
1271 }
1272 } else {
1273 return true;
1274 }
1275 }
1276 "program" => return false, _ => {}
1278 }
1279 current = parent.parent();
1280 }
1281 false
1282}
1283
1284fn extract_swift_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
1289 let mut cursor = node.walk();
1290 for child in node.children(&mut cursor) {
1291 if child.kind() == "function_declaration" && !is_inside_class(&child) {
1292 if let Some(name_node) = child.child_by_field_name("name") {
1293 let name = get_node_text(&name_node, source);
1294 functions.push(name);
1295 }
1296 }
1297 extract_swift_functions(&child, source, functions);
1298 }
1299}
1300
1301fn extract_swift_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
1302 let mut cursor = node.walk();
1303 for child in node.children(&mut cursor) {
1304 if child.kind() == "class_declaration" || child.kind() == "protocol_declaration" {
1305 if let Some(name_node) = child.child_by_field_name("name") {
1306 let name = get_node_text(&name_node, source);
1307 classes.push(name);
1308 }
1309 }
1310 extract_swift_classes(&child, source, classes);
1311 }
1312}
1313
1314fn extract_swift_methods(node: &Node, source: &str, methods: &mut Vec<String>) {
1315 let mut cursor = node.walk();
1316 for child in node.children(&mut cursor) {
1317 match child.kind() {
1318 "function_declaration" => {
1319 if is_inside_class(&child) {
1320 if let Some(name_node) = child.child_by_field_name("name") {
1321 let name = get_node_text(&name_node, source);
1322 methods.push(name);
1323 }
1324 }
1325 }
1326 "init_declaration" => {
1327 if is_inside_class(&child) {
1329 methods.push("init".to_string());
1330 }
1331 }
1332 _ => {}
1333 }
1334 extract_swift_methods(&child, source, methods);
1335 }
1336}
1337
1338fn extract_csharp_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
1343 let mut cursor = node.walk();
1344 for child in node.children(&mut cursor) {
1345 match child.kind() {
1346 "class_declaration" | "interface_declaration" | "struct_declaration" => {
1347 if let Some(name_node) = child.child_by_field_name("name") {
1348 let name = get_node_text(&name_node, source);
1349 classes.push(name);
1350 }
1351 }
1352 _ => {}
1353 }
1354 extract_csharp_classes(&child, source, classes);
1355 }
1356}
1357
1358fn extract_csharp_methods(node: &Node, source: &str, methods: &mut Vec<String>) {
1359 let mut cursor = node.walk();
1360 for child in node.children(&mut cursor) {
1361 match child.kind() {
1362 "method_declaration" => {
1363 if let Some(name_node) = child.child_by_field_name("name") {
1364 let name = get_node_text(&name_node, source);
1365 methods.push(name);
1366 }
1367 }
1368 "constructor_declaration" => {
1369 if let Some(name_node) = child.child_by_field_name("name") {
1372 let name = get_node_text(&name_node, source);
1373 methods.push(name);
1374 } else {
1375 let mut inner_cursor = child.walk();
1377 for inner in child.children(&mut inner_cursor) {
1378 if inner.kind() == "identifier" {
1379 let name = get_node_text(&inner, source);
1380 methods.push(name);
1381 break;
1382 }
1383 }
1384 }
1385 }
1386 _ => {}
1387 }
1388 extract_csharp_methods(&child, source, methods);
1389 }
1390}
1391
1392fn extract_elixir_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
1397 let mut cursor = node.walk();
1398 for child in node.children(&mut cursor) {
1399 if child.kind() == "call" {
1400 let mut inner_cursor = child.walk();
1402 for inner in child.children(&mut inner_cursor) {
1403 if inner.kind() == "identifier" {
1404 let text = get_node_text(&inner, source);
1405 if text == "def" || text == "defp" {
1406 if let Some(args) = inner.next_sibling() {
1408 if args.kind() == "arguments" || args.kind() == "call" {
1409 if let Some(name_node) = args.child(0) {
1410 if name_node.kind() == "identifier"
1411 || name_node.kind() == "call"
1412 {
1413 let fname = if name_node.kind() == "call" {
1414 if let Some(n) = name_node.child(0) {
1415 get_node_text(&n, source)
1416 } else {
1417 get_node_text(&name_node, source)
1418 }
1419 } else {
1420 get_node_text(&name_node, source)
1421 };
1422 if !functions.contains(&fname) {
1423 functions.push(fname);
1424 }
1425 }
1426 }
1427 }
1428 }
1429 }
1430 }
1431 }
1432 }
1433 extract_elixir_functions(&child, source, functions);
1434 }
1435}
1436
1437fn extract_elixir_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
1438 let mut cursor = node.walk();
1439 for child in node.children(&mut cursor) {
1440 if child.kind() == "call" {
1441 let mut inner_cursor = child.walk();
1442 for inner in child.children(&mut inner_cursor) {
1443 if inner.kind() == "identifier" {
1444 let text = get_node_text(&inner, source);
1445 if text == "defmodule" {
1446 if let Some(args) = inner.next_sibling() {
1447 if let Some(name_node) = args.child(0) {
1448 let name = get_node_text(&name_node, source);
1449 classes.push(name);
1450 }
1451 }
1452 }
1453 }
1454 }
1455 }
1456 extract_elixir_classes(&child, source, classes);
1457 }
1458}
1459
1460fn extract_lua_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
1465 let mut cursor = node.walk();
1466 for child in node.children(&mut cursor) {
1467 match child.kind() {
1468 "function_declaration" => {
1469 if let Some(name) = extract_lua_function_name(&child, source) {
1472 functions.push(name);
1473 }
1474 continue;
1476 }
1477 "variable_declaration" => {
1478 extract_lua_variable_function(&child, source, functions);
1482 continue;
1484 }
1485 "assignment_statement" => {
1486 extract_lua_assignment_function(&child, source, functions);
1488 continue;
1489 }
1490 _ => {}
1491 }
1492 extract_lua_functions(&child, source, functions);
1493 }
1494}
1495
1496fn extract_lua_function_name(node: &Node, source: &str) -> Option<String> {
1500 if let Some(name_node) = node.child_by_field_name("name") {
1502 let name = get_node_text(&name_node, source);
1503 return Some(name);
1504 }
1505 let mut cursor = node.walk();
1507 for child in node.children(&mut cursor) {
1508 match child.kind() {
1509 "identifier" => {
1510 let name = get_node_text(&child, source);
1511 if name != "function" && name != "local" && name != "end" {
1513 return Some(name);
1514 }
1515 }
1516 "dot_index_expression" | "method_index_expression" => {
1517 if let Some(field) = child.child_by_field_name("field") {
1519 return Some(get_node_text(&field, source));
1520 }
1521 let mut inner_cursor = child.walk();
1523 let mut last_ident = None;
1524 for inner in child.children(&mut inner_cursor) {
1525 if inner.kind() == "identifier" {
1526 last_ident = Some(get_node_text(&inner, source));
1527 }
1528 }
1529 return last_ident;
1530 }
1531 _ => {}
1532 }
1533 }
1534 None
1535}
1536
1537fn extract_lua_variable_function(node: &Node, source: &str, functions: &mut Vec<String>) {
1539 let mut cursor = node.walk();
1540 for child in node.children(&mut cursor) {
1541 if child.kind() == "assignment_statement" {
1542 extract_lua_assignment_function(&child, source, functions);
1543 }
1544 }
1545}
1546
1547fn extract_lua_assignment_function(node: &Node, source: &str, functions: &mut Vec<String>) {
1549 let mut has_function_def = false;
1551 let mut inner_cursor = node.walk();
1552 for inner in node.children(&mut inner_cursor) {
1553 if inner.kind() == "expression_list" {
1554 let mut expr_cursor = inner.walk();
1555 for expr in inner.children(&mut expr_cursor) {
1556 if expr.kind() == "function_definition" {
1557 has_function_def = true;
1558 break;
1559 }
1560 }
1561 }
1562 }
1563 if !has_function_def {
1564 return;
1565 }
1566 let mut inner_cursor2 = node.walk();
1568 for inner in node.children(&mut inner_cursor2) {
1569 if inner.kind() == "variable_list" {
1570 if let Some(name_node) = inner.child(0) {
1571 if name_node.kind() == "identifier" {
1572 functions.push(get_node_text(&name_node, source));
1573 return;
1574 }
1575 }
1576 }
1577 }
1578}
1579
1580fn extract_luau_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
1585 let mut cursor = node.walk();
1586 for child in node.children(&mut cursor) {
1587 match child.kind() {
1588 "function_declaration" | "local_function" => {
1589 if let Some(name_node) = child.child_by_field_name("name") {
1590 let name = get_node_text(&name_node, source);
1591 functions.push(name);
1592 }
1593 }
1594 _ => {}
1595 }
1596 extract_luau_functions(&child, source, functions);
1597 }
1598}
1599
1600fn extract_definitions(tree: &Tree, source: &str, language: Language) -> Vec<DefinitionInfo> {
1610 let mut definitions = Vec::new();
1611 let root = tree.root_node();
1612 collect_definitions(root, source, language, &mut definitions);
1613 definitions
1614}
1615
1616fn collect_definitions(
1618 node: Node,
1619 source: &str,
1620 language: Language,
1621 definitions: &mut Vec<DefinitionInfo>,
1622) {
1623 let kind = node.kind();
1624
1625 if language == Language::Elixir && kind == "call" {
1628 if let Some(def_info) = try_elixir_call_definition(node, source) {
1629 definitions.push(def_info);
1630 }
1631 }
1632
1633 if let Some(const_def) = try_constant_definition(node, source, language) {
1635 definitions.push(const_def);
1636 }
1637
1638 let (is_func, is_class) = classify_definition_node(kind, language);
1639
1640 let is_bodyless_c_specifier = is_class
1646 && matches!(kind, "struct_specifier" | "enum_specifier")
1647 && node.child_by_field_name("body").is_none();
1648
1649 if (is_func || is_class) && !is_bodyless_c_specifier {
1650 if let Some(name) = get_definition_node_name(node, source) {
1651 let line_start = node.start_position().row as u32 + 1; let line_end = node.end_position().row as u32 + 1;
1653
1654 let signature = extract_def_signature(node, source);
1656
1657 let entry_kind = if is_class {
1658 match kind {
1659 "struct_item" | "struct_definition" | "struct_specifier" => "struct",
1660 "enum_item" => "enum",
1661 "trait_item" => "trait",
1662 "interface_declaration" => "interface",
1663 "module" => "module",
1664 _ => "class",
1665 }
1666 } else {
1667 if is_inside_class_or_impl(&node, language) {
1669 "method"
1670 } else {
1671 "function"
1672 }
1673 };
1674
1675 definitions.push(DefinitionInfo {
1676 name,
1677 kind: entry_kind.to_string(),
1678 line_start,
1679 line_end,
1680 signature,
1681 });
1682 }
1683 }
1684
1685 if let Some(field_defs) = try_field_definition(node, source, language) {
1687 definitions.extend(field_defs);
1688 }
1689
1690 let mut cursor = node.walk();
1692 for child in node.children(&mut cursor) {
1693 collect_definitions(child, source, language, definitions);
1694 }
1695}
1696
1697fn try_constant_definition(
1703 node: Node,
1704 source: &str,
1705 language: Language,
1706) -> Option<DefinitionInfo> {
1707 let kind = node.kind();
1708
1709 match language {
1710 Language::Python => {
1711 if kind != "expression_statement" {
1713 return None;
1714 }
1715 let inner = node.child(0)?;
1716 if inner.kind() != "assignment" {
1717 return None;
1718 }
1719 let left = inner.child_by_field_name("left")?;
1720 if left.kind() != "identifier" {
1721 return None;
1722 }
1723 let name = get_node_text(&left, source);
1724 if !is_upper_case_name(&name) {
1725 return None;
1726 }
1727 Some(make_constant_def(node, name, source))
1728 }
1729
1730 Language::Rust => {
1731 if kind != "const_item" && kind != "static_item" {
1734 return None;
1735 }
1736 let name = node
1737 .child_by_field_name("name")
1738 .map(|n| get_node_text(&n, source))?;
1739 Some(make_constant_def(node, name, source))
1740 }
1741
1742 Language::Go => {
1743 if kind != "const_spec" {
1745 return None;
1746 }
1747 let name = node
1748 .child_by_field_name("name")
1749 .map(|n| get_node_text(&n, source))?;
1750 Some(make_constant_def(node, name, source))
1751 }
1752
1753 Language::TypeScript | Language::JavaScript => {
1754 if kind != "lexical_declaration" {
1756 return None;
1757 }
1758 let decl_text = get_node_text(&node, source);
1759 if !decl_text.starts_with("const ") {
1760 return None;
1761 }
1762 let mut cursor = node.walk();
1763 let declarator = node
1764 .children(&mut cursor)
1765 .find(|c| c.kind() == "variable_declarator")?;
1766 let name = declarator
1767 .child_by_field_name("name")
1768 .map(|n| get_node_text(&n, source))?;
1769 if !is_upper_case_name(&name) {
1770 return None;
1771 }
1772 Some(make_constant_def(node, name, source))
1773 }
1774
1775 Language::C | Language::Cpp => {
1776 match kind {
1777 "preproc_def" => {
1778 let mut cursor = node.walk();
1780 let ident = node
1781 .children(&mut cursor)
1782 .find(|c| c.kind() == "identifier")?;
1783 let name = get_node_text(&ident, source);
1784 if !is_upper_case_name(&name) {
1785 return None;
1786 }
1787 Some(make_constant_def(node, name, source))
1788 }
1789 "declaration" => {
1790 let mut cursor = node.walk();
1792 let has_const = node.children(&mut cursor).any(|c| {
1793 if c.kind() != "type_qualifier" {
1794 return false;
1795 }
1796 let text = get_node_text(&c, source);
1797 text == "const"
1798 || (language == Language::Cpp && text == "constexpr")
1799 });
1800 if !has_const {
1801 return None;
1802 }
1803 let mut cursor2 = node.walk();
1804 let init_decl = node
1805 .children(&mut cursor2)
1806 .find(|c| c.kind() == "init_declarator")?;
1807 let decl = init_decl.child_by_field_name("declarator")?;
1808 let name = get_node_text(&decl, source);
1809 if !is_upper_case_name(&name) {
1810 return None;
1811 }
1812 Some(make_constant_def(node, name, source))
1813 }
1814 _ => None,
1815 }
1816 }
1817
1818 Language::Ruby => {
1819 if kind != "assignment" {
1821 return None;
1822 }
1823 let mut cursor = node.walk();
1824 let const_node = node
1825 .children(&mut cursor)
1826 .find(|c| c.kind() == "constant")?;
1827 let name = get_node_text(&const_node, source);
1828 Some(make_constant_def(node, name, source))
1829 }
1830
1831 Language::Java => {
1832 if kind != "field_declaration" {
1834 return None;
1835 }
1836 let mut cursor = node.walk();
1837 let modifiers = node
1838 .children(&mut cursor)
1839 .find(|c| c.kind() == "modifiers")?;
1840 let mod_text = get_node_text(&modifiers, source);
1841 if !mod_text.contains("static") || !mod_text.contains("final") {
1842 return None;
1843 }
1844 let mut cursor2 = node.walk();
1845 let declarator = node
1846 .children(&mut cursor2)
1847 .find(|c| c.kind() == "variable_declarator")?;
1848 let name = declarator
1849 .child_by_field_name("name")
1850 .map(|n| get_node_text(&n, source))?;
1851 Some(make_constant_def(node, name, source))
1852 }
1853
1854 Language::Kotlin => {
1855 if kind != "property_declaration" {
1858 return None;
1859 }
1860 let text = get_node_text(&node, source);
1861 if !text.starts_with("val ") && !text.starts_with("const val ") {
1862 return None;
1863 }
1864 let mut cursor = node.walk();
1866 let var_decl = node
1867 .children(&mut cursor)
1868 .find(|c| c.kind() == "variable_declaration")?;
1869 let name_node = if let Some(n) = var_decl.child_by_field_name("name") {
1870 Some(n)
1871 } else {
1872 let mut vc = var_decl.walk();
1873 let found = var_decl
1874 .children(&mut vc)
1875 .find(|c| c.kind() == "simple_identifier" || c.kind() == "identifier");
1876 found
1877 };
1878 let name = name_node.map(|n| get_node_text(&n, source))?;
1879 if !text.starts_with("const val ") && !is_upper_case_name(&name) {
1880 return None;
1881 }
1882 Some(make_constant_def(node, name, source))
1883 }
1884
1885 Language::Swift => {
1886 if kind != "property_declaration" {
1888 return None;
1889 }
1890 if let Some(parent) = node.parent() {
1893 if matches!(
1894 parent.kind(),
1895 "class_body" | "enum_body" | "protocol_body" | "struct_body"
1896 ) {
1897 return None;
1898 }
1899 }
1900 let text = get_node_text(&node, source);
1901 if !text.starts_with("let ") {
1902 return None;
1903 }
1904 let name_node = if let Some(n) = node.child_by_field_name("name") {
1905 Some(n)
1906 } else {
1907 let mut cursor = node.walk();
1908 let found = node.children(&mut cursor).find(|c| {
1909 c.kind() == "pattern"
1910 || c.kind() == "simple_identifier"
1911 || c.kind() == "identifier"
1912 });
1913 found
1914 };
1915 let name_node = name_node?;
1916 let name = get_node_text(&name_node, source);
1917 if name.contains('(') || name.contains('{') {
1919 return None;
1920 }
1921 Some(make_constant_def(node, name, source))
1922 }
1923
1924 Language::CSharp => {
1925 if kind != "field_declaration" {
1927 return None;
1928 }
1929 let mut cursor = node.walk();
1930 let has_const = node.children(&mut cursor).any(|c| {
1931 c.kind() == "modifier" && get_node_text(&c, source) == "const"
1932 });
1933 if !has_const {
1934 return None;
1935 }
1936 let mut cursor2 = node.walk();
1937 let var_decl = node
1938 .children(&mut cursor2)
1939 .find(|c| c.kind() == "variable_declaration")?;
1940 let mut cursor3 = var_decl.walk();
1941 let declarator = var_decl
1942 .children(&mut cursor3)
1943 .find(|c| c.kind() == "variable_declarator")?;
1944 let name = declarator
1945 .child_by_field_name("name")
1946 .or_else(|| declarator.child(0))
1947 .map(|n| get_node_text(&n, source))?;
1948 Some(make_constant_def(node, name, source))
1949 }
1950
1951 Language::Scala => {
1952 if kind != "val_definition" {
1954 return None;
1955 }
1956 let name_node = if let Some(n) = node.child_by_field_name("pattern") {
1957 Some(n)
1958 } else {
1959 let mut cursor = node.walk();
1960 let found = node
1961 .children(&mut cursor)
1962 .find(|c| c.kind() == "identifier");
1963 found
1964 };
1965 let name_node = name_node?;
1966 let name = get_node_text(&name_node, source);
1967 if !is_upper_case_name(&name) {
1968 return None;
1969 }
1970 Some(make_constant_def(node, name, source))
1971 }
1972
1973 Language::Php => {
1974 if kind != "const_declaration" {
1976 return None;
1977 }
1978 let mut cursor = node.walk();
1979 let element = node
1980 .children(&mut cursor)
1981 .find(|c| c.kind() == "const_element")?;
1982 let name = element
1983 .child_by_field_name("name")
1984 .or_else(|| element.child(0))
1985 .map(|n| get_node_text(&n, source))?;
1986 Some(make_constant_def(node, name, source))
1987 }
1988
1989 Language::Elixir => {
1990 if kind != "unary_operator" {
1992 return None;
1993 }
1994 let text = get_node_text(&node, source);
1995 if !text.starts_with('@') {
1996 return None;
1997 }
1998 let mut cursor = node.walk();
1999 let ident = node
2000 .children(&mut cursor)
2001 .find(|c| c.kind() == "identifier")?;
2002 let name = format!("@{}", get_node_text(&ident, source));
2003 if name == "@doc" || name == "@moduledoc" || name == "@spec" || name == "@type" {
2005 return None;
2006 }
2007 Some(make_constant_def(node, name, source))
2008 }
2009
2010 Language::Lua | Language::Luau | Language::Ocaml => None,
2011 }
2012}
2013
2014fn make_constant_def(node: Node, name: String, source: &str) -> DefinitionInfo {
2017 let line_start = node.start_position().row as u32 + 1;
2018 let line_end = node.end_position().row as u32 + 1;
2019 let sig_start = node.start_byte();
2020 let signature = source[sig_start..]
2021 .lines()
2022 .next()
2023 .unwrap_or("")
2024 .trim()
2025 .to_string();
2026 DefinitionInfo {
2027 name,
2028 kind: "constant".to_string(),
2029 line_start,
2030 line_end,
2031 signature,
2032 }
2033}
2034
2035fn try_field_definition(
2044 node: Node,
2045 source: &str,
2046 language: Language,
2047) -> Option<Vec<DefinitionInfo>> {
2048 let kind = node.kind();
2049
2050 let kind_matches = match language {
2052 Language::Java => matches!(kind, "field_declaration"),
2053 Language::Kotlin => matches!(kind, "property_declaration"),
2054 Language::Swift => matches!(kind, "property_declaration"),
2055 Language::TypeScript | Language::JavaScript => {
2056 matches!(kind, "public_field_definition" | "field_definition")
2057 }
2058 _ => false,
2059 };
2060 if !kind_matches {
2061 return None;
2062 }
2063
2064 let parent = node.parent()?;
2067 let parent_kind = parent.kind();
2068 let parent_is_class_body = matches!(
2069 parent_kind,
2070 "class_body" | "interface_body" | "enum_body" | "annotation_type_body" | "protocol_body" | "struct_body" );
2077 if !parent_is_class_body {
2078 return None;
2079 }
2080
2081 let mut defs: Vec<DefinitionInfo> = Vec::new();
2083 let line_start = node.start_position().row as u32 + 1;
2084 let line_end = node.end_position().row as u32 + 1;
2085 let signature = extract_def_signature(node, source);
2086
2087 match language {
2088 Language::Java => {
2089 let mut cursor = node.walk();
2091 for child in node.children(&mut cursor) {
2092 if child.kind() == "variable_declarator" {
2093 let name_opt = child
2094 .child_by_field_name("name")
2095 .and_then(|n| n.utf8_text(source.as_bytes()).ok().map(|s| s.to_string()))
2096 .or_else(|| {
2097 let mut c2 = child.walk();
2098 for inner in child.children(&mut c2) {
2099 if inner.kind() == "identifier" {
2100 return inner
2101 .utf8_text(source.as_bytes())
2102 .ok()
2103 .map(|s| s.to_string());
2104 }
2105 }
2106 None
2107 });
2108 if let Some(name) = name_opt {
2109 defs.push(DefinitionInfo {
2110 name,
2111 kind: "field".to_string(),
2112 line_start,
2113 line_end,
2114 signature: signature.clone(),
2115 });
2116 }
2117 }
2118 }
2119 }
2120 Language::Kotlin => {
2121 let mut cursor = node.walk();
2123 for child in node.children(&mut cursor) {
2124 if child.kind() == "variable_declaration" {
2125 let mut c2 = child.walk();
2126 for inner in child.children(&mut c2) {
2127 if inner.kind() == "identifier" {
2128 if let Ok(name) = inner.utf8_text(source.as_bytes()) {
2129 defs.push(DefinitionInfo {
2130 name: name.to_string(),
2131 kind: "field".to_string(),
2132 line_start,
2133 line_end,
2134 signature: signature.clone(),
2135 });
2136 }
2137 break;
2138 }
2139 }
2140 }
2141 }
2142 }
2143 Language::Swift => {
2144 let mut cursor = node.walk();
2146 for child in node.children(&mut cursor) {
2147 if child.kind() == "pattern" {
2148 let mut c2 = child.walk();
2149 for inner in child.children(&mut c2) {
2150 if inner.kind() == "simple_identifier" {
2151 if let Ok(name) = inner.utf8_text(source.as_bytes()) {
2152 defs.push(DefinitionInfo {
2153 name: name.to_string(),
2154 kind: "field".to_string(),
2155 line_start,
2156 line_end,
2157 signature: signature.clone(),
2158 });
2159 }
2160 break;
2161 }
2162 }
2163 }
2164 }
2165 }
2166 Language::TypeScript | Language::JavaScript => {
2167 let name_opt = node
2170 .child_by_field_name("name")
2171 .and_then(|n| n.utf8_text(source.as_bytes()).ok().map(|s| s.to_string()))
2172 .or_else(|| {
2173 let mut cursor = node.walk();
2174 for child in node.children(&mut cursor) {
2175 if child.kind() == "property_identifier"
2176 || child.kind() == "private_property_identifier"
2177 {
2178 return child
2179 .utf8_text(source.as_bytes())
2180 .ok()
2181 .map(|s| s.to_string());
2182 }
2183 }
2184 None
2185 });
2186 if let Some(name) = name_opt {
2187 defs.push(DefinitionInfo {
2188 name,
2189 kind: "field".to_string(),
2190 line_start,
2191 line_end,
2192 signature,
2193 });
2194 }
2195 }
2196 _ => {}
2197 }
2198
2199 if defs.is_empty() {
2200 None
2201 } else {
2202 Some(defs)
2203 }
2204}
2205
2206fn try_elixir_call_definition(node: Node, source: &str) -> Option<DefinitionInfo> {
2209 let mut cursor = node.walk();
2210 for child in node.children(&mut cursor) {
2211 if child.kind() != "identifier" {
2212 continue;
2213 }
2214 let keyword = child.utf8_text(source.as_bytes()).ok()?;
2215 let args = child.next_sibling()?;
2216
2217 match keyword {
2218 "def" | "defp" => {
2219 let first_arg = args.child(0)?;
2220 let name = if first_arg.kind() == "call" {
2221 first_arg.child(0)?.utf8_text(source.as_bytes()).ok()?
2223 } else {
2224 first_arg.utf8_text(source.as_bytes()).ok()?
2225 };
2226 let line_start = node.start_position().row as u32 + 1;
2227 let line_end = node.end_position().row as u32 + 1;
2228 let signature = extract_def_signature(node, source);
2229 return Some(DefinitionInfo {
2230 name: name.to_string(),
2231 kind: "function".to_string(),
2232 line_start,
2233 line_end,
2234 signature,
2235 });
2236 }
2237 "defmodule" => {
2238 let first_arg = args.child(0)?;
2239 let name = first_arg.utf8_text(source.as_bytes()).ok()?;
2240 let line_start = node.start_position().row as u32 + 1;
2241 let line_end = node.end_position().row as u32 + 1;
2242 let signature = extract_def_signature(node, source);
2243 return Some(DefinitionInfo {
2244 name: name.to_string(),
2245 kind: "module".to_string(),
2246 line_start,
2247 line_end,
2248 signature,
2249 });
2250 }
2251 _ => {}
2252 }
2253 }
2254 None
2255}
2256
2257fn classify_definition_node(kind: &str, _language: Language) -> (bool, bool) {
2260 let is_func = matches!(
2261 kind,
2262 "function_definition"
2263 | "function_declaration"
2264 | "function_item" | "method_definition"
2266 | "method_signature" | "abstract_method_signature" | "method_declaration"
2269 | "method" | "singleton_method" | "arrow_function"
2272 | "function_expression"
2273 | "function" | "func_literal" | "function_type"
2276 | "value_definition" | "init_declaration" | "constructor_declaration" );
2280
2281 let is_class = matches!(
2282 kind,
2283 "class_definition"
2284 | "class_declaration"
2285 | "abstract_class_declaration" | "class_specifier" | "class" | "module" | "struct_item" | "struct_definition" | "struct_specifier" | "enum_item" | "trait_item" | "type_spec" | "interface_declaration"
2296 | "type_definition" | "module_definition" | "companion_object" );
2300
2301 (is_func, is_class)
2302}
2303
2304fn get_definition_node_name(node: Node, source: &str) -> Option<String> {
2307 if node.kind() == "init_declaration" {
2311 return Some("init".to_string());
2312 }
2313
2314 if let Some(name_node) = node.child_by_field_name("name") {
2316 let text = name_node.utf8_text(source.as_bytes()).ok()?;
2317 return Some(text.to_string());
2318 }
2319
2320 if node.kind() == "constructor_declaration" {
2324 let mut cursor = node.walk();
2325 for child in node.children(&mut cursor) {
2326 if child.kind() == "identifier" {
2327 let text = child.utf8_text(source.as_bytes()).ok()?;
2328 return Some(text.to_string());
2329 }
2330 }
2331 }
2332
2333 if node.kind() == "function_definition" {
2335 if let Some(declarator) = node.child_by_field_name("declarator") {
2336 return extract_name_from_declarator(declarator, source);
2337 }
2338 }
2339
2340 if node.kind() == "arrow_function" || node.kind() == "function_expression" {
2342 if let Some(parent) = node.parent() {
2343 if parent.kind() == "variable_declarator" {
2344 if let Some(name_node) = parent.child_by_field_name("name") {
2345 let text = name_node.utf8_text(source.as_bytes()).ok()?;
2346 return Some(text.to_string());
2347 }
2348 }
2349 }
2350 }
2351
2352 if node.kind() == "value_definition" {
2356 let mut cursor = node.walk();
2357 for child in node.children(&mut cursor) {
2358 if child.kind() == "let_binding" {
2359 if let Some(pattern_node) = child.child_by_field_name("pattern") {
2360 let text = pattern_node.utf8_text(source.as_bytes()).ok()?;
2361 if text != "()" && !text.is_empty() {
2362 return Some(text.to_string());
2363 }
2364 }
2365 }
2366 }
2367 return None;
2368 }
2369
2370 if node.kind() == "companion_object" {
2372 return Some("Companion".to_string());
2373 }
2374
2375 None
2376}
2377
2378fn extract_name_from_declarator(node: Node, source: &str) -> Option<String> {
2381 match node.kind() {
2382 "identifier" | "field_identifier" | "destructor_name" => {
2383 Some(get_node_text(&node, source))
2384 }
2385 "function_declarator" | "pointer_declarator" | "reference_declarator" => {
2386 let inner = node.child_by_field_name("declarator")?;
2387 extract_name_from_declarator(inner, source)
2388 }
2389 "qualified_identifier" | "scoped_identifier" => {
2390 if let Some(name) = node.child_by_field_name("name") {
2391 return Some(get_node_text(&name, source));
2392 }
2393 Some(get_node_text(&node, source))
2394 }
2395 _ => {
2396 let mut cursor = node.walk();
2398 for child in node.children(&mut cursor) {
2399 if let Some(name) = extract_name_from_declarator(child, source) {
2400 return Some(name);
2401 }
2402 }
2403 None
2404 }
2405 }
2406}
2407
2408fn is_inside_class_or_impl(node: &Node, language: Language) -> bool {
2415 let mut current = node.parent();
2416 while let Some(parent) = current {
2417 let kind = parent.kind();
2418 let module_is_class = !matches!(language, Language::Python);
2421 if matches!(
2422 kind,
2423 "class_definition"
2424 | "class_declaration"
2425 | "abstract_class_declaration" | "class_specifier" | "class" | "class_body"
2429 | "impl_item"
2430 | "struct_item"
2431 | "trait_item"
2432 | "interface_declaration" | "interface_body" | "companion_object" | "object_declaration" ) || (kind == "module" && module_is_class) {
2438 return true;
2439 }
2440 current = parent.parent();
2441 }
2442 false
2443}
2444
2445fn extract_def_signature(node: Node, source: &str) -> String {
2449 let mut cursor = node.walk();
2452 for child in node.children(&mut cursor) {
2453 let ckind = child.kind();
2454 if ckind == "line_comment"
2456 || ckind == "block_comment"
2457 || ckind == "comment"
2458 || ckind == "attribute_item" || ckind == "attribute" || ckind == "decorator" || ckind == "decorator_list"
2462 {
2464 continue;
2465 }
2466 let start_byte = child.start_byte();
2468 let line_from_start = &source[start_byte..];
2469 let sig = line_from_start
2470 .lines()
2471 .next()
2472 .unwrap_or("")
2473 .trim()
2474 .to_string();
2475 if !sig.is_empty() {
2476 return sig;
2477 }
2478 }
2479
2480 let node_text = &source[node.start_byte()..node.end_byte()];
2482 for line in node_text.lines() {
2483 let trimmed = line.trim();
2484 if !trimmed.is_empty()
2485 && !trimmed.starts_with("///")
2486 && !trimmed.starts_with("//!")
2487 && !trimmed.starts_with("//")
2488 && !trimmed.starts_with("/*")
2489 && !trimmed.starts_with("*")
2490 && !trimmed.starts_with("#[")
2491 && !trimmed.starts_with("@")
2492 && !trimmed.starts_with("#")
2493 {
2494 return trimmed.to_string();
2495 }
2496 }
2497
2498 source[node.start_byte()..]
2500 .lines()
2501 .next()
2502 .unwrap_or("")
2503 .trim()
2504 .to_string()
2505}
2506
2507fn get_node_text(node: &Node, source: &str) -> String {
2513 source[node.byte_range()].to_string()
2514}
2515
2516fn is_inside_class(node: &Node) -> bool {
2518 let mut current = node.parent();
2519 while let Some(parent) = current {
2520 match parent.kind() {
2521 "class_definition" | "class_declaration" | "class" | "class_body" => return true,
2522 _ => current = parent.parent(),
2523 }
2524 }
2525 false
2526}
2527
2528fn is_inside_impl(node: &Node) -> bool {
2530 let mut current = node.parent();
2531 while let Some(parent) = current {
2532 if parent.kind() == "impl_item" || parent.kind() == "trait_item" {
2533 return true;
2534 }
2535 current = parent.parent();
2536 }
2537 false
2538}
2539
2540#[cfg(test)]
2541mod tests {
2542 use super::*;
2543 use crate::ast::parser::parse;
2544
2545 #[test]
2546 fn test_extract_python_functions() {
2547 let source = r#"
2548def foo():
2549 pass
2550
2551def bar(x):
2552 return x
2553
2554class MyClass:
2555 def method(self):
2556 pass
2557"#;
2558 let tree = parse(source, Language::Python).unwrap();
2559 let functions = extract_functions(&tree, source, Language::Python);
2560
2561 assert!(functions.contains(&"foo".to_string()));
2562 assert!(functions.contains(&"bar".to_string()));
2563 assert!(!functions.contains(&"method".to_string()));
2565 }
2566
2567 #[test]
2568 fn test_extract_python_classes() {
2569 let source = r#"
2570class MyClass:
2571 pass
2572
2573class AnotherClass:
2574 def method(self):
2575 pass
2576"#;
2577 let tree = parse(source, Language::Python).unwrap();
2578 let classes = extract_classes(&tree, source, Language::Python);
2579
2580 assert!(classes.contains(&"MyClass".to_string()));
2581 assert!(classes.contains(&"AnotherClass".to_string()));
2582 }
2583
2584 #[test]
2585 fn test_extract_python_methods() {
2586 let source = r#"
2587class MyClass:
2588 def method1(self):
2589 pass
2590
2591 def method2(self, x):
2592 return x
2593"#;
2594 let tree = parse(source, Language::Python).unwrap();
2595 let methods = extract_methods(&tree, source, Language::Python);
2596
2597 assert!(methods.contains(&"method1".to_string()));
2598 assert!(methods.contains(&"method2".to_string()));
2599 }
2600
2601 #[test]
2602 fn test_extract_typescript_functions() {
2603 let source = r#"
2604function foo() {}
2605
2606const bar = () => {};
2607
2608class MyClass {
2609 method() {}
2610}
2611"#;
2612 let tree = parse(source, Language::TypeScript).unwrap();
2613 let functions = extract_functions(&tree, source, Language::TypeScript);
2614
2615 assert!(functions.contains(&"foo".to_string()));
2616 }
2618
2619 #[test]
2620 fn test_extract_go_functions() {
2621 let source = r#"
2622package main
2623
2624func foo() {}
2625
2626func (r *Receiver) bar() {}
2627"#;
2628 let tree = parse(source, Language::Go).unwrap();
2629 let functions = extract_functions(&tree, source, Language::Go);
2630
2631 assert!(functions.contains(&"foo".to_string()));
2632 assert!(functions.contains(&"bar".to_string()));
2633 }
2634
2635 #[test]
2636 fn test_extract_c_functions() {
2637 let source = r#"
2638void hello(void) {
2639}
2640
2641int main(int argc, char** argv) {
2642 return 0;
2643}
2644
2645static void helper(int x) {
2646}
2647"#;
2648 let tree = parse(source, Language::C).unwrap();
2649 let functions = extract_functions(&tree, source, Language::C);
2650
2651 assert!(
2652 functions.contains(&"hello".to_string()),
2653 "Should find hello function"
2654 );
2655 assert!(
2656 functions.contains(&"main".to_string()),
2657 "Should find main function"
2658 );
2659 assert!(
2660 functions.contains(&"helper".to_string()),
2661 "Should find helper function"
2662 );
2663 }
2664
2665 #[test]
2666 fn test_extract_c_structs() {
2667 let source = r#"
2668struct Point {
2669 int x;
2670 int y;
2671};
2672
2673enum Color {
2674 RED,
2675 GREEN,
2676 BLUE
2677};
2678"#;
2679 let tree = parse(source, Language::C).unwrap();
2680 let classes = extract_classes(&tree, source, Language::C);
2681
2682 assert!(
2683 classes.contains(&"Point".to_string()),
2684 "Should find Point struct"
2685 );
2686 assert!(
2687 classes.contains(&"Color".to_string()),
2688 "Should find Color enum"
2689 );
2690 }
2691
2692 #[test]
2693 fn test_extract_c_structs_requires_body_val_001() {
2694 let source = r#"
2699struct Foo { int x; };
2700void use_bar(struct Bar *b);
2701struct Forward;
2702typedef struct { int y; } Anon;
2703typedef struct Existing OtherName;
2704enum E { A };
2705enum F;
2706void use_enum(enum G *e);
2707"#;
2708 let tree = parse(source, Language::C).unwrap();
2709 let classes = extract_classes(&tree, source, Language::C);
2710
2711 let expected: std::collections::HashSet<String> =
2712 ["Foo", "Anon", "E"].iter().map(|s| s.to_string()).collect();
2713 let got: std::collections::HashSet<String> = classes.iter().cloned().collect();
2714
2715 assert_eq!(
2716 got, expected,
2717 "VAL-001: extract_c_structs must only emit bodied struct/enum \
2718 definitions. Expected {:?}, got {:?}",
2719 expected, got
2720 );
2721
2722 for forbidden in ["Bar", "Forward", "Existing", "OtherName", "F", "G"] {
2724 assert!(
2725 !classes.contains(&forbidden.to_string()),
2726 "VAL-001: must NOT emit `{}` (no body / alias / param type), \
2727 got classes = {:?}",
2728 forbidden,
2729 classes
2730 );
2731 }
2732 }
2733
2734 #[test]
2735 fn test_extract_cpp_functions() {
2736 let source = r#"
2737void hello() {
2738}
2739
2740int main() {
2741 return 0;
2742}
2743
2744namespace greeting {
2745 void greet(const std::string& name) {
2746 }
2747}
2748"#;
2749 let tree = parse(source, Language::Cpp).unwrap();
2750 let functions = extract_functions(&tree, source, Language::Cpp);
2751
2752 assert!(
2753 functions.contains(&"hello".to_string()),
2754 "Should find hello function"
2755 );
2756 assert!(
2757 functions.contains(&"main".to_string()),
2758 "Should find main function"
2759 );
2760 assert!(
2762 functions.contains(&"greet".to_string()),
2763 "Should find greet function"
2764 );
2765 }
2766
2767 #[test]
2768 fn test_extract_cpp_classes() {
2769 let source = r#"
2770class Greeter {
2771public:
2772 void greet();
2773};
2774
2775struct Point {
2776 int x, y;
2777};
2778"#;
2779 let tree = parse(source, Language::Cpp).unwrap();
2780 let classes = extract_classes(&tree, source, Language::Cpp);
2781
2782 assert!(
2783 classes.contains(&"Greeter".to_string()),
2784 "Should find Greeter class"
2785 );
2786 assert!(
2787 classes.contains(&"Point".to_string()),
2788 "Should find Point struct"
2789 );
2790 }
2791
2792 #[test]
2793 fn test_extract_ruby_functions() {
2794 let source = r##"
2795def top_level_function
2796 puts "I'm a function"
2797end
2798
2799def another_function(x)
2800 x * 2
2801end
2802
2803class MyClass
2804 def method_in_class
2805 puts "method"
2806 end
2807end
2808"##;
2809 let tree = parse(source, Language::Ruby).unwrap();
2810 let functions = extract_functions(&tree, source, Language::Ruby);
2811
2812 assert!(
2813 functions.contains(&"top_level_function".to_string()),
2814 "Should find top_level_function"
2815 );
2816 assert!(
2817 functions.contains(&"another_function".to_string()),
2818 "Should find another_function"
2819 );
2820 assert!(
2822 !functions.contains(&"method_in_class".to_string()),
2823 "Should not find method_in_class in functions"
2824 );
2825 }
2826
2827 #[test]
2828 fn test_extract_ruby_methods() {
2829 let source = r##"
2830def top_level_function
2831 puts "I'm a function"
2832end
2833
2834class MyClass
2835 def initialize(name)
2836 @name = name
2837 end
2838
2839 def greet
2840 puts "Hello"
2841 end
2842end
2843
2844module MyModule
2845 def module_method
2846 puts "module method"
2847 end
2848end
2849"##;
2850 let tree = parse(source, Language::Ruby).unwrap();
2851 let methods = extract_methods(&tree, source, Language::Ruby);
2852
2853 assert!(
2855 methods.contains(&"initialize".to_string()),
2856 "Should find initialize method"
2857 );
2858 assert!(
2859 methods.contains(&"greet".to_string()),
2860 "Should find greet method"
2861 );
2862 assert!(
2863 methods.contains(&"module_method".to_string()),
2864 "Should find module_method"
2865 );
2866 assert!(
2868 !methods.contains(&"top_level_function".to_string()),
2869 "Should not find top_level_function in methods"
2870 );
2871 }
2872
2873 #[test]
2874 fn test_extract_ruby_classes() {
2875 let source = r##"
2876class MyClass
2877 def initialize
2878 end
2879end
2880
2881class AnotherClass < BaseClass
2882 def method
2883 end
2884end
2885
2886module MyModule
2887 class NestedClass
2888 end
2889end
2890"##;
2891 let tree = parse(source, Language::Ruby).unwrap();
2892 let classes = extract_classes(&tree, source, Language::Ruby);
2893
2894 assert!(
2895 classes.contains(&"MyClass".to_string()),
2896 "Should find MyClass"
2897 );
2898 assert!(
2899 classes.contains(&"AnotherClass".to_string()),
2900 "Should find AnotherClass"
2901 );
2902 assert!(
2903 classes.contains(&"MyModule".to_string()),
2904 "Should find MyModule"
2905 );
2906 assert!(
2907 classes.contains(&"NestedClass".to_string()),
2908 "Should find NestedClass"
2909 );
2910 }
2911
2912 #[test]
2913 fn test_extract_kotlin_functions() {
2914 let source = r#"
2915fun topLevel() {
2916 println("hello")
2917}
2918
2919fun anotherTopLevel(x: Int): Int {
2920 return x * 2
2921}
2922
2923class MyClass {
2924 fun classMethod() {}
2925}
2926"#;
2927 let tree = parse(source, Language::Kotlin).unwrap();
2928 let functions = extract_functions(&tree, source, Language::Kotlin);
2929
2930 assert!(
2931 functions.contains(&"topLevel".to_string()),
2932 "Should find topLevel function"
2933 );
2934 assert!(
2935 functions.contains(&"anotherTopLevel".to_string()),
2936 "Should find anotherTopLevel function"
2937 );
2938 assert!(
2940 !functions.contains(&"classMethod".to_string()),
2941 "Should not find classMethod in functions"
2942 );
2943 }
2944
2945 #[test]
2946 fn test_extract_kotlin_methods() {
2947 let source = r#"
2948fun topLevel() {}
2949
2950class MyClass {
2951 fun method1() {}
2952 fun method2(x: Int): String { return x.toString() }
2953}
2954
2955object Singleton {
2956 fun singletonMethod() {}
2957}
2958"#;
2959 let tree = parse(source, Language::Kotlin).unwrap();
2960 let methods = extract_methods(&tree, source, Language::Kotlin);
2961
2962 assert!(
2963 methods.contains(&"method1".to_string()),
2964 "Should find method1"
2965 );
2966 assert!(
2967 methods.contains(&"method2".to_string()),
2968 "Should find method2"
2969 );
2970 assert!(
2972 !methods.contains(&"topLevel".to_string()),
2973 "Should not find topLevel in methods"
2974 );
2975 }
2976
2977 #[test]
2978 fn test_extract_kotlin_classes() {
2979 let source = r#"
2980class HttpClient(val engine: Engine) {
2981 fun config() {}
2982}
2983
2984object Singleton {
2985 fun method() {}
2986}
2987
2988interface MyInterface {
2989 fun abstractMethod()
2990}
2991"#;
2992 let tree = parse(source, Language::Kotlin).unwrap();
2993 let classes = extract_classes(&tree, source, Language::Kotlin);
2994
2995 assert!(
2996 classes.contains(&"HttpClient".to_string()),
2997 "Should find HttpClient class"
2998 );
2999 assert!(
3000 classes.contains(&"Singleton".to_string()),
3001 "Should find Singleton object"
3002 );
3003 assert!(
3004 classes.contains(&"MyInterface".to_string()),
3005 "Should find MyInterface interface"
3006 );
3007 }
3008
3009 #[test]
3010 fn test_extract_ocaml_functions() {
3011 let source = r#"
3012let greet name =
3013 Printf.printf "Hello, %s!\n" name
3014
3015let add x y = x + y
3016
3017let value = 42
3018
3019let rec factorial n =
3020 if n <= 1 then 1
3021 else n * factorial (n - 1)
3022
3023let () = greet "world"
3024"#;
3025 let tree = parse(source, Language::Ocaml).unwrap();
3026 let functions = extract_functions(&tree, source, Language::Ocaml);
3027
3028 assert!(
3029 functions.contains(&"greet".to_string()),
3030 "Should find greet function"
3031 );
3032 assert!(
3033 functions.contains(&"add".to_string()),
3034 "Should find add function"
3035 );
3036 assert!(
3037 functions.contains(&"factorial".to_string()),
3038 "Should find factorial function"
3039 );
3040 assert!(
3042 !functions.contains(&"value".to_string()),
3043 "Should not find value binding as function"
3044 );
3045 assert!(
3047 !functions.contains(&"()".to_string()),
3048 "Should not find anonymous let () binding"
3049 );
3050 }
3051
3052 #[test]
3057 fn test_extract_rust_classes_includes_traits() {
3058 let source = r#"
3059pub struct Config {
3060 pub name: String,
3061}
3062
3063pub enum Color {
3064 Red,
3065 Green,
3066 Blue,
3067}
3068
3069pub trait Serialize {
3070 fn serialize(&self) -> String;
3071}
3072
3073trait Deserialize {
3074 fn deserialize(input: &str) -> Self;
3075}
3076"#;
3077 let tree = parse(source, Language::Rust).unwrap();
3078 let classes = extract_classes(&tree, source, Language::Rust);
3079
3080 assert!(
3081 classes.contains(&"Config".to_string()),
3082 "Should find Config struct"
3083 );
3084 assert!(
3085 classes.contains(&"Color".to_string()),
3086 "Should find Color enum"
3087 );
3088 assert!(
3089 classes.contains(&"Serialize".to_string()),
3090 "Should find Serialize trait"
3091 );
3092 assert!(
3093 classes.contains(&"Deserialize".to_string()),
3094 "Should find Deserialize trait"
3095 );
3096 }
3097
3098 #[test]
3099 fn test_extract_rust_functions_excludes_trait_methods() {
3100 let source = r#"
3101pub fn top_level() -> bool {
3102 true
3103}
3104
3105pub trait Visitor {
3106 fn visit_bool(&self, v: bool) {}
3107 fn visit_i32(&self, v: i32) {}
3108}
3109
3110impl Config {
3111 pub fn new() -> Self {
3112 Config {}
3113 }
3114}
3115"#;
3116 let tree = parse(source, Language::Rust).unwrap();
3117 let functions = extract_functions(&tree, source, Language::Rust);
3118
3119 assert!(
3120 functions.contains(&"top_level".to_string()),
3121 "Should find top_level function"
3122 );
3123 assert!(
3125 !functions.contains(&"visit_bool".to_string()),
3126 "Trait method visit_bool should not be a top-level function"
3127 );
3128 assert!(
3129 !functions.contains(&"visit_i32".to_string()),
3130 "Trait method visit_i32 should not be a top-level function"
3131 );
3132 assert!(
3134 !functions.contains(&"new".to_string()),
3135 "Impl method new should not be a top-level function"
3136 );
3137 }
3138
3139 #[test]
3140 fn test_extract_rust_methods_includes_trait_methods() {
3141 let source = r#"
3142pub trait Visitor {
3143 fn visit_bool(&self, v: bool) {}
3144 fn visit_i32(&self, v: i32) {}
3145}
3146
3147impl Config {
3148 pub fn new() -> Self {
3149 Config {}
3150 }
3151 pub fn name(&self) -> &str {
3152 &self.name
3153 }
3154}
3155
3156fn top_level() {}
3157"#;
3158 let tree = parse(source, Language::Rust).unwrap();
3159 let methods = extract_methods(&tree, source, Language::Rust);
3160
3161 assert!(
3163 methods.contains(&"visit_bool".to_string()),
3164 "Should find visit_bool trait method"
3165 );
3166 assert!(
3167 methods.contains(&"visit_i32".to_string()),
3168 "Should find visit_i32 trait method"
3169 );
3170 assert!(
3172 methods.contains(&"new".to_string()),
3173 "Should find new impl method"
3174 );
3175 assert!(
3176 methods.contains(&"name".to_string()),
3177 "Should find name impl method"
3178 );
3179 assert!(
3181 !methods.contains(&"top_level".to_string()),
3182 "top_level should not be in methods"
3183 );
3184 }
3185
3186 fn extract_counts(source: &str, lang: Language) -> (usize, usize, usize) {
3197 let tree = parse(source, lang).expect("parsing should succeed");
3198 let functions = extract_functions(&tree, source, lang);
3199 let classes = extract_classes(&tree, source, lang);
3200 let methods = extract_methods(&tree, source, lang);
3201 (functions.len(), classes.len(), methods.len())
3202 }
3203
3204 #[test]
3205 fn test_extractor_python() {
3206 let source = include_str!("../../tests/fixtures/extractor/test_python.py");
3207 let (f, c, m) = extract_counts(source, Language::Python);
3208 assert_eq!(f, 3, "Python: expected 3 functions, got {}", f);
3209 assert_eq!(c, 2, "Python: expected 2 classes, got {}", c);
3210 assert_eq!(m, 5, "Python: expected 5 methods, got {}", m);
3211 }
3212
3213 #[test]
3214 fn test_extractor_go() {
3215 let source = include_str!("../../tests/fixtures/extractor/test_go.go");
3216 let (f, c, m) = extract_counts(source, Language::Go);
3217 assert_eq!(f, 4, "Go: expected 4 functions, got {}", f);
3218 assert_eq!(c, 2, "Go: expected 2 structs, got {}", c);
3219 assert_eq!(m, 0, "Go: expected 0 methods, got {}", m);
3220 }
3221
3222 #[test]
3223 fn test_extractor_rust() {
3224 let source = include_str!("../../tests/fixtures/extractor/test_rust.rs");
3225 let (f, c, m) = extract_counts(source, Language::Rust);
3226 assert_eq!(f, 3, "Rust: expected 3 functions, got {}", f);
3227 assert_eq!(c, 2, "Rust: expected 2 structs, got {}", c);
3228 assert_eq!(m, 4, "Rust: expected 4 methods, got {}", m);
3229 }
3230
3231 #[test]
3232 fn test_extractor_java() {
3233 let source = include_str!("../../tests/fixtures/extractor/test_java.java");
3234 let (f, c, m) = extract_counts(source, Language::Java);
3235 assert_eq!(f, 0, "Java: expected 0 functions, got {}", f);
3236 assert_eq!(c, 3, "Java: expected 3 classes, got {}", c);
3237 assert_eq!(m, 6, "Java: expected 6 methods, got {}", m);
3238 }
3239
3240 #[test]
3241 fn test_extractor_c() {
3242 let source = include_str!("../../tests/fixtures/extractor/test_c.c");
3243 let (f, c, m) = extract_counts(source, Language::C);
3244 assert_eq!(f, 4, "C: expected 4 functions, got {}", f);
3245 assert_eq!(c, 2, "C: expected 2 structs, got {}", c);
3246 assert_eq!(m, 0, "C: expected 0 methods, got {}", m);
3247 }
3248
3249 #[test]
3250 fn test_extractor_cpp() {
3251 let source = include_str!("../../tests/fixtures/extractor/test_cpp.cpp");
3252 let (f, c, m) = extract_counts(source, Language::Cpp);
3253 assert_eq!(f, 2, "C++: expected 2 functions, got {}", f);
3254 assert_eq!(c, 2, "C++: expected 2 classes, got {}", c);
3255 assert_eq!(m, 5, "C++: expected 5 methods, got {}", m);
3256 }
3257
3258 #[test]
3259 fn test_extractor_typescript() {
3260 let source = include_str!("../../tests/fixtures/extractor/test_typescript.ts");
3261 let (f, c, m) = extract_counts(source, Language::TypeScript);
3262 assert_eq!(f, 3, "TypeScript: expected 3 functions, got {}", f);
3263 assert_eq!(c, 2, "TypeScript: expected 2 classes, got {}", c);
3264 assert_eq!(m, 4, "TypeScript: expected 4 methods, got {}", m);
3265 }
3266
3267 #[test]
3268 fn test_extractor_javascript() {
3269 let source = include_str!("../../tests/fixtures/extractor/test_javascript.js");
3270 let (f, c, m) = extract_counts(source, Language::JavaScript);
3272 assert_eq!(f, 5, "JavaScript: expected 5 functions, got {}", f);
3273 assert_eq!(c, 2, "JavaScript: expected 2 classes, got {}", c);
3274 assert_eq!(m, 4, "JavaScript: expected 4 methods, got {}", m);
3275 }
3276
3277 #[test]
3278 fn test_extractor_ruby() {
3279 let source = include_str!("../../tests/fixtures/extractor/test_ruby.rb");
3280 let (f, c, m) = extract_counts(source, Language::Ruby);
3281 assert_eq!(f, 2, "Ruby: expected 2 functions, got {}", f);
3282 assert_eq!(c, 2, "Ruby: expected 2 classes, got {}", c);
3283 assert_eq!(m, 5, "Ruby: expected 5 methods, got {}", m);
3284 }
3285
3286 #[test]
3287 fn test_extractor_php() {
3288 let source = include_str!("../../tests/fixtures/extractor/test_php.php");
3289 let (f, c, m) = extract_counts(source, Language::Php);
3290 assert_eq!(f, 2, "PHP: expected 2 functions, got {}", f);
3291 assert_eq!(c, 2, "PHP: expected 2 classes, got {}", c);
3292 assert_eq!(m, 5, "PHP: expected 5 methods, got {}", m);
3293 }
3294
3295 #[test]
3296 fn test_extractor_kotlin() {
3297 let source = include_str!("../../tests/fixtures/extractor/test_kotlin.kt");
3298 let (f, c, m) = extract_counts(source, Language::Kotlin);
3299 assert_eq!(f, 2, "Kotlin: expected 2 functions, got {}", f);
3300 assert_eq!(c, 2, "Kotlin: expected 2 classes, got {}", c);
3301 assert_eq!(m, 5, "Kotlin: expected 5 methods, got {}", m);
3302 }
3303
3304 #[test]
3305 fn test_extractor_swift() {
3306 let source = include_str!("../../tests/fixtures/extractor/test_swift.swift");
3307 let (f, c, m) = extract_counts(source, Language::Swift);
3308 assert_eq!(f, 3, "Swift: expected 3 functions, got {}", f);
3309 assert_eq!(c, 2, "Swift: expected 2 classes, got {}", c);
3310 assert_eq!(m, 5, "Swift: expected 5 methods, got {}", m);
3311 }
3312
3313 #[test]
3314 fn test_extractor_csharp() {
3315 let source = include_str!("../../tests/fixtures/extractor/test_csharp.cs");
3316 let (f, c, m) = extract_counts(source, Language::CSharp);
3317 assert_eq!(f, 0, "C#: expected 0 functions, got {}", f);
3318 assert_eq!(c, 3, "C#: expected 3 classes, got {}", c);
3319 assert_eq!(
3320 m, 7,
3321 "C#: expected 7 methods (including constructors), got {}",
3322 m
3323 );
3324 }
3325
3326 #[test]
3327 fn test_extractor_scala() {
3328 let source = include_str!("../../tests/fixtures/extractor/test_scala.scala");
3329 let (f, c, m) = extract_counts(source, Language::Scala);
3330 assert_eq!(f, 2, "Scala: expected 2 functions, got {}", f);
3331 assert_eq!(c, 2, "Scala: expected 2 classes, got {}", c);
3332 assert_eq!(m, 4, "Scala: expected 4 methods, got {}", m);
3333 }
3334
3335 #[test]
3336 fn test_extractor_ocaml() {
3337 let source = include_str!("../../tests/fixtures/extractor/test_ocaml.ml");
3338 let (f, c, m) = extract_counts(source, Language::Ocaml);
3339 assert_eq!(f, 3, "OCaml: expected 3 functions, got {}", f);
3340 assert_eq!(c, 0, "OCaml: expected 0 classes, got {}", c);
3341 assert_eq!(m, 0, "OCaml: expected 0 methods, got {}", m);
3342 }
3343
3344 #[test]
3345 fn test_extractor_elixir() {
3346 let source = include_str!("../../tests/fixtures/extractor/test_elixir.ex");
3347 let (f, c, m) = extract_counts(source, Language::Elixir);
3348 assert_eq!(f, 3, "Elixir: expected 3 functions, got {}", f);
3349 assert_eq!(c, 1, "Elixir: expected 1 class (module), got {}", c);
3350 assert_eq!(m, 0, "Elixir: expected 0 methods, got {}", m);
3351 }
3352
3353 #[test]
3354 fn test_extractor_lua() {
3355 let source = include_str!("../../tests/fixtures/extractor/test_lua.lua");
3356 let (f, c, m) = extract_counts(source, Language::Lua);
3357 assert_eq!(f, 5, "Lua: expected 5 functions, got {}", f);
3358 assert_eq!(c, 0, "Lua: expected 0 classes, got {}", c);
3359 assert_eq!(m, 0, "Lua: expected 0 methods, got {}", m);
3360 }
3361
3362 #[test]
3363 fn test_extractor_luau() {
3364 let source = include_str!("../../tests/fixtures/extractor/test_luau.luau");
3365 let (f, c, m) = extract_counts(source, Language::Luau);
3366 assert_eq!(f, 3, "Luau: expected 3 functions, got {}", f);
3367 assert_eq!(c, 0, "Luau: expected 0 classes, got {}", c);
3368 assert_eq!(m, 0, "Luau: expected 0 methods, got {}", m);
3369 }
3370
3371 fn get_constants(source: &str, language: Language) -> Vec<DefinitionInfo> {
3374 let tree = parse(source, language).unwrap();
3375 let defs = extract_definitions(&tree, source, language);
3376 defs.into_iter().filter(|d| d.kind == "constant").collect()
3377 }
3378
3379 #[test]
3380 fn test_python_constant_definitions() {
3381 let source = "MAX_RETRIES = 3\n\nEXTERNAL_FUNCTIONS = {\n \"foo\": bar,\n \"baz\": qux,\n}\n\nlower_case = 42\n";
3382 let consts = get_constants(source, Language::Python);
3383 assert_eq!(consts.len(), 2);
3384 assert_eq!(consts[0].name, "MAX_RETRIES");
3385 assert_eq!(consts[0].line_start, 1);
3386 assert_eq!(consts[0].line_end, 1);
3387 assert_eq!(consts[0].signature, "MAX_RETRIES = 3");
3388 assert_eq!(consts[1].name, "EXTERNAL_FUNCTIONS");
3389 assert_eq!(consts[1].line_start, 3);
3390 assert_eq!(consts[1].line_end, 6);
3391 assert_eq!(consts[1].signature, "EXTERNAL_FUNCTIONS = {");
3392 }
3393
3394 #[test]
3395 fn test_rust_constant_definitions() {
3396 let source = "const MAX_SIZE: usize = 100;\npub static GLOBAL: &str = \"hello\";\nlet x = 5;\n";
3397 let consts = get_constants(source, Language::Rust);
3398 assert_eq!(consts.len(), 2);
3399 assert_eq!(consts[0].name, "MAX_SIZE");
3400 assert_eq!(consts[0].kind, "constant");
3401 assert_eq!(consts[1].name, "GLOBAL");
3402 }
3403
3404 #[test]
3405 fn test_go_constant_definitions() {
3406 let source = "package main\n\nconst MaxRetries = 3\n\nconst (\n\tA = 1\n\tB = 2\n)\n";
3407 let consts = get_constants(source, Language::Go);
3408 assert_eq!(consts.len(), 3);
3409 let names: Vec<&str> = consts.iter().map(|c| c.name.as_str()).collect();
3410 assert!(names.contains(&"MaxRetries"));
3411 assert!(names.contains(&"A"));
3412 assert!(names.contains(&"B"));
3413 }
3414
3415 #[test]
3416 fn test_typescript_constant_definitions() {
3417 let source = "const MAX_RETRIES = 3;\nexport const API_URL = \"https://example.com\";\nconst lower = 42;\n";
3418 let consts = get_constants(source, Language::TypeScript);
3419 assert_eq!(consts.len(), 2);
3420 assert_eq!(consts[0].name, "MAX_RETRIES");
3421 assert_eq!(consts[1].name, "API_URL");
3422 }
3423
3424 #[test]
3425 fn test_javascript_multiline_constant() {
3426 let source = "const EXTERNAL_FUNCTIONS = {\n foo: 1,\n bar: 2,\n};\n";
3427 let consts = get_constants(source, Language::JavaScript);
3428 assert_eq!(consts.len(), 1);
3429 assert_eq!(consts[0].name, "EXTERNAL_FUNCTIONS");
3430 assert_eq!(consts[0].line_start, 1);
3431 assert_eq!(consts[0].line_end, 4);
3432 assert_eq!(consts[0].signature, "const EXTERNAL_FUNCTIONS = {");
3433 }
3434
3435 #[test]
3436 fn test_c_constant_definitions() {
3437 let source = "#define MAX_SIZE 100\nconst int BUFFER_LEN = 256;\nint x = 5;\n";
3438 let consts = get_constants(source, Language::C);
3439 assert_eq!(consts.len(), 2);
3440 assert_eq!(consts[0].name, "MAX_SIZE");
3441 assert_eq!(consts[1].name, "BUFFER_LEN");
3442 }
3443
3444 #[test]
3445 fn test_java_constant_definitions() {
3446 let source = "class Config {\n public static final int MAX_RETRIES = 3;\n public static final String API_URL = \"https://example.com\";\n private int x = 5;\n}\n";
3447 let consts = get_constants(source, Language::Java);
3448 assert_eq!(consts.len(), 2);
3449 assert_eq!(consts[0].name, "MAX_RETRIES");
3450 assert_eq!(consts[1].name, "API_URL");
3451 }
3452
3453 #[test]
3454 fn test_ruby_constant_definitions() {
3455 let source = "MAX_RETRIES = 3\nAPI_URL = \"https://example.com\"\nlower = 42\n";
3456 let consts = get_constants(source, Language::Ruby);
3457 assert_eq!(consts.len(), 2);
3458 assert_eq!(consts[0].name, "MAX_RETRIES");
3459 assert_eq!(consts[1].name, "API_URL");
3460 }
3461
3462 #[test]
3465 fn test_python_toplevel_function_kind_is_function() {
3466 let source = r#"
3467def top_level():
3468 pass
3469
3470class MyClass:
3471 def method(self):
3472 pass
3473"#;
3474 let tree = parse(source, Language::Python).unwrap();
3475 let defs = extract_definitions(&tree, source, Language::Python);
3476 let top_level = defs.iter().find(|d| d.name == "top_level");
3477 assert!(top_level.is_some(), "top_level definition not found");
3478 assert_eq!(
3479 top_level.unwrap().kind,
3480 "function",
3481 "top-level Python def must have kind 'function', not 'method'"
3482 );
3483 let method = defs.iter().find(|d| d.name == "method");
3484 assert!(method.is_some(), "method definition not found");
3485 assert_eq!(
3486 method.unwrap().kind,
3487 "method",
3488 "Python def inside class must have kind 'method'"
3489 );
3490 }
3491
3492 #[test]
3495 fn test_ocaml_definitions_non_empty() {
3496 let source = r#"
3497let top_level x = x * 2
3498
3499let another_func x y = x + y
3500
3501let rec factorial n =
3502 if n <= 1 then 1
3503 else n * factorial (n - 1)
3504
3505let () =
3506 let result = factorial 5 in
3507 Printf.printf "%d\n" result
3508"#;
3509 let tree = parse(source, Language::Ocaml).unwrap();
3510 let defs = extract_definitions(&tree, source, Language::Ocaml);
3511 assert!(
3512 !defs.is_empty(),
3513 "OCaml definitions array must be non-empty; got 0"
3514 );
3515 let names: Vec<&str> = defs.iter().map(|d| d.name.as_str()).collect();
3516 assert!(
3517 names.contains(&"top_level"),
3518 "OCaml: expected 'top_level' in definitions, got {:?}",
3519 names
3520 );
3521 assert!(
3522 names.contains(&"another_func"),
3523 "OCaml: expected 'another_func' in definitions, got {:?}",
3524 names
3525 );
3526 assert!(
3527 names.contains(&"factorial"),
3528 "OCaml: expected 'factorial' in definitions, got {:?}",
3529 names
3530 );
3531 assert!(
3533 !names.contains(&"()"),
3534 "OCaml: '()' binding must not appear in definitions"
3535 );
3536 }
3537
3538 #[test]
3541 fn test_kotlin_companion_object_definition() {
3542 let source = r#"
3543class Animal(val name: String) {
3544 fun speak(): String = "..."
3545
3546 companion object {
3547 fun create(name: String): Animal = Animal(name)
3548 }
3549}
3550"#;
3551 let tree = parse(source, Language::Kotlin).unwrap();
3552 let defs = extract_definitions(&tree, source, Language::Kotlin);
3553 let companion = defs.iter().find(|d| d.name == "Companion");
3554 assert!(
3555 companion.is_some(),
3556 "Kotlin: companion object must produce a 'Companion' definition; definitions: {:?}",
3557 defs.iter().map(|d| (&d.name, &d.kind)).collect::<Vec<_>>()
3558 );
3559 assert_eq!(
3560 companion.unwrap().kind,
3561 "class",
3562 "Kotlin companion object kind must be 'class'"
3563 );
3564 }
3565
3566 #[test]
3569 fn test_c_struct_ref_not_emitted_as_definition_val_001() {
3570 let source = r#"
3574int open_connection(struct sockaddr *addr, struct sockaddr_in *sin) {
3575 return 0;
3576}
3577"#;
3578 let tree = parse(source, Language::C).unwrap();
3579 let defs = extract_definitions(&tree, source, Language::C);
3580 let names: Vec<String> = defs.iter().map(|d| d.name.clone()).collect();
3581
3582 assert!(
3583 names.contains(&"open_connection".to_string()),
3584 "VAL-001: open_connection must be in definitions; got {:?}",
3585 names
3586 );
3587 assert!(
3588 !names.contains(&"sockaddr".to_string()),
3589 "VAL-001: bare struct_specifier `struct sockaddr` (no body) \
3590 must NOT appear in definitions; got {:?}",
3591 names
3592 );
3593 assert!(
3594 !names.contains(&"sockaddr_in".to_string()),
3595 "VAL-001: bare struct_specifier `struct sockaddr_in` (no body) \
3596 must NOT appear in definitions; got {:?}",
3597 names
3598 );
3599 }
3600
3601 #[test]
3604 fn test_swift_init_emitted_as_method_definition_val_002() {
3605 let source = r#"
3609class Foo {
3610 var x: Int = 0
3611 init(x: Int) { self.x = x }
3612 func bar() {}
3613}
3614"#;
3615 let tree = parse(source, Language::Swift).unwrap();
3616 let defs = extract_definitions(&tree, source, Language::Swift);
3617 let named: Vec<(String, String)> = defs
3618 .iter()
3619 .map(|d| (d.name.clone(), d.kind.clone()))
3620 .collect();
3621
3622 let init = defs.iter().find(|d| d.name == "init");
3623 assert!(
3624 init.is_some(),
3625 "VAL-002: Swift init must be in definitions; got {:?}",
3626 named
3627 );
3628 assert_eq!(
3629 init.unwrap().kind,
3630 "method",
3631 "VAL-002: Swift init inside class must have kind='method'; got {:?}",
3632 named
3633 );
3634 }
3635
3636 #[test]
3639 fn test_java_constructor_emitted_as_method_definition_val_003() {
3640 let source = r#"
3643public class Store {
3644 public Store() {}
3645 public void get() {}
3646}
3647"#;
3648 let tree = parse(source, Language::Java).unwrap();
3649 let defs = extract_definitions(&tree, source, Language::Java);
3650 let named: Vec<(String, String)> = defs
3651 .iter()
3652 .map(|d| (d.name.clone(), d.kind.clone()))
3653 .collect();
3654
3655 let ctor = defs.iter().find(|d| d.name == "Store" && d.kind == "method");
3656 assert!(
3657 ctor.is_some(),
3658 "VAL-003: Java constructor `Store` must be in definitions with \
3659 kind='method'; got {:?}",
3660 named
3661 );
3662 assert!(
3664 defs.iter().any(|d| d.name == "get" && d.kind == "method"),
3665 "VAL-003: Java method `get` must remain in definitions; got {:?}",
3666 named
3667 );
3668 }
3669
3670 #[test]
3673 fn test_class_fields_emitted_as_definitions_val_004() {
3674 {
3683 let source = r#"
3684public class Store {
3685 private int count = 0;
3686 public String name;
3687 int x, y;
3688 public void get() {}
3689}
3690"#;
3691 let tree = parse(source, Language::Java).unwrap();
3692 let defs = extract_definitions(&tree, source, Language::Java);
3693 let fields: Vec<String> = defs
3694 .iter()
3695 .filter(|d| d.kind == "field")
3696 .map(|d| d.name.clone())
3697 .collect();
3698 for expected in ["count", "name", "x", "y"] {
3699 assert!(
3700 fields.contains(&expected.to_string()),
3701 "VAL-004 (Java): field `{}` must appear in definitions; \
3702 got fields={:?}, all defs={:?}",
3703 expected,
3704 fields,
3705 defs.iter()
3706 .map(|d| (&d.name, &d.kind))
3707 .collect::<Vec<_>>()
3708 );
3709 }
3710 }
3711
3712 {
3714 let source = r#"
3715class Foo {
3716 val x: Int = 0
3717 var y: String = "hi"
3718 fun bar() {}
3719}
3720
3721val topLevelX = 1
3722"#;
3723 let tree = parse(source, Language::Kotlin).unwrap();
3724 let defs = extract_definitions(&tree, source, Language::Kotlin);
3725 let fields: Vec<String> = defs
3726 .iter()
3727 .filter(|d| d.kind == "field")
3728 .map(|d| d.name.clone())
3729 .collect();
3730 assert!(
3731 fields.contains(&"x".to_string()),
3732 "VAL-004 (Kotlin): class-scope `val x` must be a field; \
3733 got fields={:?}",
3734 fields
3735 );
3736 assert!(
3737 fields.contains(&"y".to_string()),
3738 "VAL-004 (Kotlin): class-scope `var y` must be a field; \
3739 got fields={:?}",
3740 fields
3741 );
3742 assert!(
3743 !fields.contains(&"topLevelX".to_string()),
3744 "VAL-004 (Kotlin): top-level `val topLevelX` must NOT be a \
3745 field (only class-scope properties are fields); got \
3746 fields={:?}",
3747 fields
3748 );
3749 }
3750
3751 {
3753 let source = r#"
3754class Foo {
3755 var x: Int = 0
3756 let y: String = "hi"
3757 func bar() {}
3758}
3759"#;
3760 let tree = parse(source, Language::Swift).unwrap();
3761 let defs = extract_definitions(&tree, source, Language::Swift);
3762 let fields: Vec<String> = defs
3763 .iter()
3764 .filter(|d| d.kind == "field")
3765 .map(|d| d.name.clone())
3766 .collect();
3767 for expected in ["x", "y"] {
3768 assert!(
3769 fields.contains(&expected.to_string()),
3770 "VAL-004 (Swift): class-scope property `{}` must be a \
3771 field; got fields={:?}",
3772 expected,
3773 fields
3774 );
3775 }
3776 }
3777
3778 {
3780 let source = r#"
3781class Foo {
3782 public count: number = 0;
3783 name: string = "hi";
3784 bar() {}
3785}
3786"#;
3787 let tree = parse(source, Language::TypeScript).unwrap();
3788 let defs = extract_definitions(&tree, source, Language::TypeScript);
3789 let fields: Vec<String> = defs
3790 .iter()
3791 .filter(|d| d.kind == "field")
3792 .map(|d| d.name.clone())
3793 .collect();
3794 for expected in ["count", "name"] {
3795 assert!(
3796 fields.contains(&expected.to_string()),
3797 "VAL-004 (TypeScript): class field `{}` must be in \
3798 definitions; got fields={:?}",
3799 expected,
3800 fields
3801 );
3802 }
3803 }
3804 }
3805
3806 #[test]
3812 fn test_typescript_abstract_and_interface_methods_emitted_val_001() {
3813 let source = r#"
3814export abstract class Repo<T> {
3815 abstract save(item: T): Promise<void>;
3816 find(id: string): T | null { return null; }
3817}
3818interface IFace {
3819 greet(name: string): void;
3820 defaulted(): number;
3821}
3822"#;
3823 let tree = parse(source, Language::TypeScript).unwrap();
3824
3825 let defs = extract_definitions(&tree, source, Language::TypeScript);
3827 let by_name: std::collections::BTreeMap<String, String> = defs
3828 .iter()
3829 .map(|d| (d.name.clone(), d.kind.clone()))
3830 .collect();
3831
3832 assert_eq!(
3833 by_name.get("Repo").map(String::as_str),
3834 Some("class"),
3835 "VAL-001: `Repo` must be kind=class; got defs={:?}",
3836 defs
3837 );
3838 assert_eq!(
3839 by_name.get("IFace").map(String::as_str),
3840 Some("interface"),
3841 "VAL-001: `IFace` must be kind=interface; got defs={:?}",
3842 defs
3843 );
3844 for expected in ["save", "find", "greet", "defaulted"] {
3845 assert_eq!(
3846 by_name.get(expected).map(String::as_str),
3847 Some("method"),
3848 "VAL-001: `{}` must appear in definitions with kind=method; got defs={:?}",
3849 expected,
3850 defs
3851 );
3852 }
3853
3854 let methods = extract_methods(&tree, source, Language::TypeScript);
3856 for expected in ["save", "find", "greet", "defaulted"] {
3857 assert!(
3858 methods.contains(&expected.to_string()),
3859 "VAL-001: `{}` must appear in methods[]; got methods={:?}",
3860 expected,
3861 methods
3862 );
3863 }
3864 }
3865}