use std::collections::HashSet;
use std::path::Path;
use tree_sitter::{Node, Tree};
use crate::fs::tree::{collect_files, get_file_tree};
use crate::types::{CodeStructure, DefinitionInfo, FileStructure, IgnoreSpec, Language};
use crate::TldrResult;
use super::extract::is_upper_case_name;
use super::imports::extract_imports_from_tree;
use super::parser::parse_file;
pub fn get_code_structure(
root: &Path,
language: Language,
max_results: usize,
ignore_spec: Option<&IgnoreSpec>,
) -> TldrResult<CodeStructure> {
if root.is_file() {
let parent = root.parent().unwrap_or(root);
match extract_file_structure(root, parent, language) {
Ok(structure) => {
return Ok(CodeStructure {
root: root.to_path_buf(),
language,
files: vec![structure],
});
}
Err(e) => {
return Err(e);
}
}
}
let extensions: HashSet<String> = language
.extensions()
.iter()
.map(|s| s.to_string())
.collect();
let tree = get_file_tree(root, Some(&extensions), true, ignore_spec)?;
let files = collect_files(&tree, root);
let mut file_structures = Vec::new();
for file_path in files {
if max_results > 0 && file_structures.len() >= max_results {
break;
}
match extract_file_structure(&file_path, root, language) {
Ok(structure) => file_structures.push(structure),
Err(e) => {
if e.is_recoverable() {
eprintln!("Warning: Skipping {} - {}", file_path.display(), e);
} else {
return Err(e);
}
}
}
}
Ok(CodeStructure {
root: root.to_path_buf(),
language,
files: file_structures,
})
}
fn extract_file_structure(
path: &Path,
root: &Path,
language: Language,
) -> TldrResult<FileStructure> {
let (tree, source, _) = parse_file(path)?;
let relative_path = path.strip_prefix(root).unwrap_or(path).to_path_buf();
let functions = extract_functions(&tree, &source, language);
let classes = extract_classes(&tree, &source, language);
let methods = extract_methods(&tree, &source, language);
let imports = extract_imports_from_tree(&tree, &source, language)?;
let definitions = extract_definitions(&tree, &source, language);
Ok(FileStructure {
path: relative_path,
functions,
classes,
methods,
imports,
definitions,
})
}
pub fn extract_functions(tree: &Tree, source: &str, language: Language) -> Vec<String> {
let mut functions = Vec::new();
let root = tree.root_node();
match language {
Language::Python => extract_python_functions(&root, source, &mut functions, false),
Language::TypeScript | Language::JavaScript => {
extract_ts_functions(&root, source, &mut functions, false)
}
Language::Go => extract_go_functions(&root, source, &mut functions),
Language::Rust => extract_rust_functions(&root, source, &mut functions),
Language::Java => extract_java_functions(&root, source, &mut functions, false),
Language::C => extract_c_functions(&root, source, &mut functions),
Language::Cpp => extract_cpp_functions(&root, source, &mut functions),
Language::Ruby => extract_ruby_functions(&root, source, &mut functions, false),
Language::Scala => extract_scala_functions(&root, source, &mut functions),
Language::Kotlin => extract_kotlin_functions(&root, source, &mut functions, false),
Language::Ocaml => extract_ocaml_functions(&root, source, &mut functions),
Language::Php => extract_php_functions(&root, source, &mut functions, false),
Language::Swift => extract_swift_functions(&root, source, &mut functions),
Language::CSharp => {} Language::Elixir => extract_elixir_functions(&root, source, &mut functions),
Language::Lua => extract_lua_functions(&root, source, &mut functions),
Language::Luau => extract_luau_functions(&root, source, &mut functions),
}
functions
}
pub fn extract_classes(tree: &Tree, source: &str, language: Language) -> Vec<String> {
let mut classes = Vec::new();
let root = tree.root_node();
match language {
Language::Python => extract_python_classes(&root, source, &mut classes),
Language::TypeScript | Language::JavaScript => {
extract_ts_classes(&root, source, &mut classes)
}
Language::Go => extract_go_structs(&root, source, &mut classes),
Language::Rust => extract_rust_structs(&root, source, &mut classes),
Language::Java => extract_java_classes(&root, source, &mut classes),
Language::C => extract_c_structs(&root, source, &mut classes),
Language::Cpp => extract_cpp_classes(&root, source, &mut classes),
Language::Ruby => extract_ruby_classes(&root, source, &mut classes),
Language::Scala => extract_scala_classes(&root, source, &mut classes),
Language::Kotlin => extract_kotlin_classes(&root, source, &mut classes),
Language::Php => extract_php_classes(&root, source, &mut classes),
Language::Swift => extract_swift_classes(&root, source, &mut classes),
Language::CSharp => extract_csharp_classes(&root, source, &mut classes),
Language::Elixir => extract_elixir_classes(&root, source, &mut classes),
Language::Lua => {} Language::Luau => {} _ => {}
}
classes
}
pub fn extract_methods(tree: &Tree, source: &str, language: Language) -> Vec<String> {
let mut methods = Vec::new();
let root = tree.root_node();
match language {
Language::Python => extract_python_functions(&root, source, &mut methods, true),
Language::TypeScript | Language::JavaScript => {
extract_ts_functions(&root, source, &mut methods, true)
}
Language::Go => {} Language::Rust => extract_rust_impl_methods(&root, source, &mut methods),
Language::Java => extract_java_functions(&root, source, &mut methods, true),
Language::C => {} Language::Cpp => extract_cpp_methods(&root, source, &mut methods),
Language::Ruby => extract_ruby_functions(&root, source, &mut methods, true),
Language::Scala => extract_scala_methods(&root, source, &mut methods),
Language::Kotlin => extract_kotlin_functions(&root, source, &mut methods, true),
Language::Php => extract_php_functions(&root, source, &mut methods, true),
Language::Swift => extract_swift_methods(&root, source, &mut methods),
Language::CSharp => extract_csharp_methods(&root, source, &mut methods),
Language::Elixir => {} Language::Lua => {} Language::Luau => {} _ => {}
}
methods
}
fn extract_python_functions(
node: &Node,
source: &str,
functions: &mut Vec<String>,
methods_only: bool,
) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"function_definition" => {
let is_method = is_inside_class(&child);
if methods_only == is_method {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
}
}
}
"class_definition" => {
if let Some(body) = child.child_by_field_name("body") {
extract_python_functions(&body, source, functions, methods_only);
}
}
_ => {
extract_python_functions(&child, source, functions, methods_only);
}
}
}
}
fn extract_python_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "class_definition" {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
classes.push(name);
}
}
extract_python_classes(&child, source, classes);
}
}
fn extract_ts_functions(
node: &Node,
source: &str,
functions: &mut Vec<String>,
methods_only: bool,
) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"function_declaration"
| "function"
| "generator_function_declaration"
| "generator_function" => {
if !methods_only {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
}
}
}
"function_expression" => {
if !methods_only {
let mut extracted = false;
if let Some(parent) = child.parent() {
if parent.kind() == "variable_declarator" {
if let Some(name_node) = parent.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
extracted = true;
}
}
}
if !extracted {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
}
}
}
}
"method_definition" => {
if methods_only {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
}
}
}
"arrow_function" => {
if !methods_only {
if let Some(parent) = child.parent() {
if parent.kind() == "variable_declarator" {
if let Some(name_node) = parent.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
}
}
}
}
}
"class_declaration" | "class" => {
if let Some(body) = child.child_by_field_name("body") {
extract_ts_functions(&body, source, functions, methods_only);
}
}
_ => {
extract_ts_functions(&child, source, functions, methods_only);
}
}
}
}
fn extract_ts_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "class_declaration" || child.kind() == "class" {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
classes.push(name);
}
}
extract_ts_classes(&child, source, classes);
}
}
fn extract_go_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "function_declaration" || child.kind() == "method_declaration" {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
}
}
extract_go_functions(&child, source, functions);
}
}
fn extract_go_structs(node: &Node, source: &str, structs: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "type_declaration" {
let mut inner_cursor = child.walk();
for inner in child.children(&mut inner_cursor) {
if inner.kind() == "type_spec" {
let mut has_struct = false;
let mut type_name = None;
let mut spec_cursor = inner.walk();
for spec_child in inner.children(&mut spec_cursor) {
if spec_child.kind() == "type_identifier" {
type_name = Some(get_node_text(&spec_child, source));
}
if spec_child.kind() == "struct_type"
|| spec_child.kind() == "interface_type"
{
has_struct = true;
}
}
if has_struct {
if let Some(name) = type_name {
structs.push(name);
}
}
}
}
}
extract_go_structs(&child, source, structs);
}
}
fn extract_rust_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "function_item" {
if !is_inside_impl(&child) {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
}
}
}
extract_rust_functions(&child, source, functions);
}
}
fn extract_rust_structs(node: &Node, source: &str, structs: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "struct_item"
|| child.kind() == "enum_item"
|| child.kind() == "trait_item"
{
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
structs.push(name);
}
}
extract_rust_structs(&child, source, structs);
}
}
fn extract_rust_impl_methods(node: &Node, source: &str, methods: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "impl_item" {
if let Some(body) = child.child_by_field_name("body") {
let mut body_cursor = body.walk();
for item in body.children(&mut body_cursor) {
if item.kind() == "function_item" {
if let Some(name_node) = item.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
methods.push(name);
}
}
}
}
} else if child.kind() == "trait_item" {
let mut trait_cursor = child.walk();
for trait_child in child.children(&mut trait_cursor) {
if trait_child.kind() == "declaration_list" {
let mut body_cursor = trait_child.walk();
for item in trait_child.children(&mut body_cursor) {
if item.kind() == "function_item"
|| item.kind() == "function_signature_item"
{
if let Some(name_node) = item.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
methods.push(name);
}
}
}
}
}
}
extract_rust_impl_methods(&child, source, methods);
}
}
fn extract_java_functions(
node: &Node,
source: &str,
functions: &mut Vec<String>,
methods_only: bool,
) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"method_declaration" => {
let is_in_class = is_inside_class(&child);
if methods_only == is_in_class {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
}
}
}
"constructor_declaration" => {
if methods_only {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
}
}
}
_ => {}
}
extract_java_functions(&child, source, functions, methods_only);
}
}
fn extract_java_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "class_declaration" || child.kind() == "interface_declaration" {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
classes.push(name);
}
}
extract_java_classes(&child, source, classes);
}
}
fn extract_c_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "function_definition" {
if let Some(declarator) = child.child_by_field_name("declarator") {
if let Some(name) = extract_c_function_name(&declarator, source) {
functions.push(name);
}
}
}
extract_c_functions(&child, source, functions);
}
}
fn extract_c_function_name(node: &Node, source: &str) -> Option<String> {
match node.kind() {
"function_declarator" => {
if let Some(declarator) = node.child_by_field_name("declarator") {
if declarator.kind() == "identifier" {
return Some(get_node_text(&declarator, source));
} else if declarator.kind() == "parenthesized_declarator" {
return extract_c_function_name(&declarator, source);
}
}
}
"pointer_declarator" => {
if let Some(declarator) = node.child_by_field_name("declarator") {
return extract_c_function_name(&declarator, source);
}
}
"identifier" => {
return Some(get_node_text(node, source));
}
_ => {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if let Some(name) = extract_c_function_name(&child, source) {
return Some(name);
}
}
}
}
None
}
fn extract_c_structs(node: &Node, source: &str, structs: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"struct_specifier" | "enum_specifier" => {
if child.child_by_field_name("body").is_some() {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
structs.push(name);
}
}
}
"type_definition" => {
let mut has_struct_or_enum = false;
let mut typedef_name = None;
let mut inner_cursor = child.walk();
for inner in child.children(&mut inner_cursor) {
if (inner.kind() == "struct_specifier" || inner.kind() == "enum_specifier")
&& inner.child_by_field_name("body").is_some()
{
has_struct_or_enum = true;
}
if inner.kind() == "type_identifier" {
typedef_name = Some(get_node_text(&inner, source));
}
}
if has_struct_or_enum {
if let Some(name) = typedef_name {
structs.push(name);
}
}
}
_ => {}
}
extract_c_structs(&child, source, structs);
}
}
fn extract_cpp_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "function_definition" {
if !is_inside_cpp_class(&child) {
if let Some(declarator) = child.child_by_field_name("declarator") {
if let Some(name) = extract_cpp_function_name(&declarator, source) {
functions.push(name);
}
}
}
}
extract_cpp_functions(&child, source, functions);
}
}
fn extract_cpp_function_name(node: &Node, source: &str) -> Option<String> {
match node.kind() {
"function_declarator" => {
if let Some(declarator) = node.child_by_field_name("declarator") {
return extract_cpp_function_name(&declarator, source);
}
}
"qualified_identifier" | "scoped_identifier" => {
if let Some(name) = node.child_by_field_name("name") {
return Some(get_node_text(&name, source));
}
return Some(get_node_text(node, source));
}
"pointer_declarator" | "reference_declarator" => {
if let Some(declarator) = node.child_by_field_name("declarator") {
return extract_cpp_function_name(&declarator, source);
}
}
"identifier" | "field_identifier" | "destructor_name" => {
return Some(get_node_text(node, source));
}
"operator_name" => {
return Some(get_node_text(node, source));
}
_ => {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if let Some(name) = extract_cpp_function_name(&child, source) {
return Some(name);
}
}
}
}
None
}
fn extract_cpp_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"class_specifier" | "struct_specifier" | "enum_specifier" => {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
classes.push(name);
}
}
_ => {}
}
extract_cpp_classes(&child, source, classes);
}
}
fn extract_cpp_methods(node: &Node, source: &str, methods: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"class_specifier" | "struct_specifier" => {
if let Some(body) = child.child_by_field_name("body") {
let mut body_cursor = body.walk();
for body_child in body.children(&mut body_cursor) {
if body_child.kind() == "function_definition" {
if cpp_has_default_or_delete(&body_child) {
continue;
}
if let Some(declarator) = body_child.child_by_field_name("declarator") {
if let Some(name) = extract_cpp_function_name(&declarator, source) {
methods.push(name);
}
}
}
}
}
}
_ => {}
}
extract_cpp_methods(&child, source, methods);
}
}
fn cpp_has_default_or_delete(node: &Node) -> bool {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "default_method_clause" || child.kind() == "delete_method_clause" {
return true;
}
}
false
}
fn is_inside_cpp_class(node: &Node) -> bool {
let mut current = node.parent();
while let Some(parent) = current {
match parent.kind() {
"class_specifier" | "struct_specifier" => return true,
"field_declaration_list" => {
if let Some(grandparent) = parent.parent() {
if matches!(grandparent.kind(), "class_specifier" | "struct_specifier") {
return true;
}
}
}
"translation_unit" => return false, _ => {}
}
current = parent.parent();
}
false
}
fn extract_ruby_functions(
node: &Node,
source: &str,
functions: &mut Vec<String>,
methods_only: bool,
) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"method" => {
let is_method = is_inside_ruby_class_or_module(&child);
if methods_only == is_method {
let mut method_cursor = child.walk();
for method_child in child.children(&mut method_cursor) {
if method_child.kind() == "identifier" {
let name = get_node_text(&method_child, source);
functions.push(name);
break;
}
}
}
}
"singleton_method" => {
if methods_only {
let mut method_cursor = child.walk();
for method_child in child.children(&mut method_cursor) {
if method_child.kind() == "identifier" {
let name = get_node_text(&method_child, source);
if name != "self" {
functions.push(name);
break;
}
}
}
}
}
"class" | "module" => {
let body_found = child.child_by_field_name("body");
if let Some(body) = body_found {
extract_ruby_functions(&body, source, functions, methods_only);
} else {
let mut class_cursor = child.walk();
for class_child in child.children(&mut class_cursor) {
if class_child.kind() == "body_statement" {
extract_ruby_functions(&class_child, source, functions, methods_only);
}
}
}
}
_ => {
extract_ruby_functions(&child, source, functions, methods_only);
}
}
}
}
fn extract_ruby_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"class" | "module" => {
let mut class_cursor = child.walk();
for class_child in child.children(&mut class_cursor) {
if class_child.kind() == "constant" || class_child.kind() == "scope_resolution"
{
let name = get_node_text(&class_child, source);
classes.push(name);
break;
}
}
}
_ => {}
}
extract_ruby_classes(&child, source, classes);
}
}
fn is_inside_ruby_class_or_module(node: &Node) -> bool {
let mut current = node.parent();
while let Some(parent) = current {
match parent.kind() {
"class" | "module" | "body_statement" => {
if parent.kind() == "body_statement" {
if let Some(grandparent) = parent.parent() {
if grandparent.kind() == "class" || grandparent.kind() == "module" {
return true;
}
}
} else {
return true;
}
}
"program" => return false, _ => {}
}
current = parent.parent();
}
false
}
fn extract_scala_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"function_definition" => {
if !is_inside_scala_class_or_trait(&child) {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
}
}
}
"object_definition" => {
if let Some(body) = child.child_by_field_name("body") {
extract_scala_functions(&body, source, functions);
}
}
"template_body" => {
extract_scala_functions(&child, source, functions);
}
_ => {
extract_scala_functions(&child, source, functions);
}
}
}
}
fn extract_scala_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"class_definition" | "trait_definition" => {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
classes.push(name);
}
}
_ => {}
}
extract_scala_classes(&child, source, classes);
}
}
fn extract_scala_methods(node: &Node, source: &str, methods: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"function_definition" => {
if is_inside_scala_class_or_trait(&child) {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
methods.push(name);
}
}
}
"class_definition" | "trait_definition" => {
if let Some(body) = child.child_by_field_name("body") {
extract_scala_methods(&body, source, methods);
}
}
"template_body" => {
extract_scala_methods(&child, source, methods);
}
_ => {
extract_scala_methods(&child, source, methods);
}
}
}
}
fn is_inside_scala_class_or_trait(node: &Node) -> bool {
let mut current = node.parent();
while let Some(parent) = current {
match parent.kind() {
"class_definition" | "trait_definition" => return true,
"object_definition" => return false, "compilation_unit" => return false, _ => {}
}
current = parent.parent();
}
false
}
fn extract_kotlin_functions(
node: &Node,
source: &str,
functions: &mut Vec<String>,
methods_only: bool,
) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"function_declaration" => {
let is_method = is_inside_kotlin_class_or_object(&child);
if methods_only == is_method {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
}
}
}
"class_declaration" | "object_declaration" | "companion_object" => {
let mut class_cursor = child.walk();
for class_child in child.children(&mut class_cursor) {
if class_child.kind() == "class_body" {
extract_kotlin_functions(&class_child, source, functions, methods_only);
}
}
}
_ => {
extract_kotlin_functions(&child, source, functions, methods_only);
}
}
}
}
fn extract_kotlin_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"class_declaration" | "object_declaration" => {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
classes.push(name);
}
}
_ => {}
}
extract_kotlin_classes(&child, source, classes);
}
}
fn is_inside_kotlin_class_or_object(node: &Node) -> bool {
let mut current = node.parent();
while let Some(parent) = current {
match parent.kind() {
"class_declaration" | "object_declaration" | "companion_object" => return true,
"class_body" => {
if let Some(grandparent) = parent.parent() {
if matches!(
grandparent.kind(),
"class_declaration" | "object_declaration" | "companion_object"
) {
return true;
}
}
}
"source_file" => return false, _ => {}
}
current = parent.parent();
}
false
}
fn extract_ocaml_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "value_definition" {
let mut inner_cursor = child.walk();
for inner in child.children(&mut inner_cursor) {
if inner.kind() == "let_binding" {
if ocaml_binding_has_params_simple(&inner) {
if let Some(pattern_node) = inner.child_by_field_name("pattern") {
let name = get_node_text(&pattern_node, source);
if name != "()" && !name.is_empty() {
functions.push(name);
}
}
}
}
}
}
extract_ocaml_functions(&child, source, functions);
}
}
fn ocaml_binding_has_params_simple(node: &Node) -> bool {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "parameter" {
return true;
}
}
false
}
fn extract_php_functions(
node: &Node,
source: &str,
functions: &mut Vec<String>,
methods_only: bool,
) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"function_definition" => {
let is_method = is_inside_php_class(&child);
if methods_only == is_method {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
}
}
}
"method_declaration" => {
if methods_only {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
}
}
}
"class_declaration" | "interface_declaration" | "trait_declaration" => {
if let Some(body) = child.child_by_field_name("body") {
extract_php_functions(&body, source, functions, methods_only);
} else {
let mut class_cursor = child.walk();
for class_child in child.children(&mut class_cursor) {
if class_child.kind() == "declaration_list" {
extract_php_functions(&class_child, source, functions, methods_only);
}
}
}
}
_ => {
extract_php_functions(&child, source, functions, methods_only);
}
}
}
}
fn extract_php_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"class_declaration" | "interface_declaration" | "trait_declaration" => {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
classes.push(name);
}
}
_ => {}
}
extract_php_classes(&child, source, classes);
}
}
fn is_inside_php_class(node: &Node) -> bool {
let mut current = node.parent();
while let Some(parent) = current {
match parent.kind() {
"class_declaration"
| "interface_declaration"
| "trait_declaration"
| "declaration_list" => {
if parent.kind() == "declaration_list" {
if let Some(grandparent) = parent.parent() {
if matches!(
grandparent.kind(),
"class_declaration" | "interface_declaration" | "trait_declaration"
) {
return true;
}
}
} else {
return true;
}
}
"program" => return false, _ => {}
}
current = parent.parent();
}
false
}
fn extract_swift_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "function_declaration" && !is_inside_class(&child) {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
}
}
extract_swift_functions(&child, source, functions);
}
}
fn extract_swift_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "class_declaration" || child.kind() == "protocol_declaration" {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
classes.push(name);
}
}
extract_swift_classes(&child, source, classes);
}
}
fn extract_swift_methods(node: &Node, source: &str, methods: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"function_declaration" => {
if is_inside_class(&child) {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
methods.push(name);
}
}
}
"init_declaration" => {
if is_inside_class(&child) {
methods.push("init".to_string());
}
}
_ => {}
}
extract_swift_methods(&child, source, methods);
}
}
fn extract_csharp_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"class_declaration" | "interface_declaration" | "struct_declaration" => {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
classes.push(name);
}
}
_ => {}
}
extract_csharp_classes(&child, source, classes);
}
}
fn extract_csharp_methods(node: &Node, source: &str, methods: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"method_declaration" => {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
methods.push(name);
}
}
"constructor_declaration" => {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
methods.push(name);
} else {
let mut inner_cursor = child.walk();
for inner in child.children(&mut inner_cursor) {
if inner.kind() == "identifier" {
let name = get_node_text(&inner, source);
methods.push(name);
break;
}
}
}
}
_ => {}
}
extract_csharp_methods(&child, source, methods);
}
}
fn extract_elixir_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "call" {
let mut inner_cursor = child.walk();
for inner in child.children(&mut inner_cursor) {
if inner.kind() == "identifier" {
let text = get_node_text(&inner, source);
if text == "def" || text == "defp" {
if let Some(args) = inner.next_sibling() {
if args.kind() == "arguments" || args.kind() == "call" {
if let Some(name_node) = args.child(0) {
if name_node.kind() == "identifier"
|| name_node.kind() == "call"
{
let fname = if name_node.kind() == "call" {
if let Some(n) = name_node.child(0) {
get_node_text(&n, source)
} else {
get_node_text(&name_node, source)
}
} else {
get_node_text(&name_node, source)
};
if !functions.contains(&fname) {
functions.push(fname);
}
}
}
}
}
}
}
}
}
extract_elixir_functions(&child, source, functions);
}
}
fn extract_elixir_classes(node: &Node, source: &str, classes: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "call" {
let mut inner_cursor = child.walk();
for inner in child.children(&mut inner_cursor) {
if inner.kind() == "identifier" {
let text = get_node_text(&inner, source);
if text == "defmodule" {
if let Some(args) = inner.next_sibling() {
if let Some(name_node) = args.child(0) {
let name = get_node_text(&name_node, source);
classes.push(name);
}
}
}
}
}
}
extract_elixir_classes(&child, source, classes);
}
}
fn extract_lua_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"function_declaration" => {
if let Some(name) = extract_lua_function_name(&child, source) {
functions.push(name);
}
continue;
}
"variable_declaration" => {
extract_lua_variable_function(&child, source, functions);
continue;
}
"assignment_statement" => {
extract_lua_assignment_function(&child, source, functions);
continue;
}
_ => {}
}
extract_lua_functions(&child, source, functions);
}
}
fn extract_lua_function_name(node: &Node, source: &str) -> Option<String> {
if let Some(name_node) = node.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
return Some(name);
}
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"identifier" => {
let name = get_node_text(&child, source);
if name != "function" && name != "local" && name != "end" {
return Some(name);
}
}
"dot_index_expression" | "method_index_expression" => {
if let Some(field) = child.child_by_field_name("field") {
return Some(get_node_text(&field, source));
}
let mut inner_cursor = child.walk();
let mut last_ident = None;
for inner in child.children(&mut inner_cursor) {
if inner.kind() == "identifier" {
last_ident = Some(get_node_text(&inner, source));
}
}
return last_ident;
}
_ => {}
}
}
None
}
fn extract_lua_variable_function(node: &Node, source: &str, functions: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "assignment_statement" {
extract_lua_assignment_function(&child, source, functions);
}
}
}
fn extract_lua_assignment_function(node: &Node, source: &str, functions: &mut Vec<String>) {
let mut has_function_def = false;
let mut inner_cursor = node.walk();
for inner in node.children(&mut inner_cursor) {
if inner.kind() == "expression_list" {
let mut expr_cursor = inner.walk();
for expr in inner.children(&mut expr_cursor) {
if expr.kind() == "function_definition" {
has_function_def = true;
break;
}
}
}
}
if !has_function_def {
return;
}
let mut inner_cursor2 = node.walk();
for inner in node.children(&mut inner_cursor2) {
if inner.kind() == "variable_list" {
if let Some(name_node) = inner.child(0) {
if name_node.kind() == "identifier" {
functions.push(get_node_text(&name_node, source));
return;
}
}
}
}
}
fn extract_luau_functions(node: &Node, source: &str, functions: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"function_declaration" | "local_function" => {
if let Some(name_node) = child.child_by_field_name("name") {
let name = get_node_text(&name_node, source);
functions.push(name);
}
}
_ => {}
}
extract_luau_functions(&child, source, functions);
}
}
fn extract_definitions(tree: &Tree, source: &str, language: Language) -> Vec<DefinitionInfo> {
let mut definitions = Vec::new();
let root = tree.root_node();
collect_definitions(root, source, language, &mut definitions);
definitions
}
fn collect_definitions(
node: Node,
source: &str,
language: Language,
definitions: &mut Vec<DefinitionInfo>,
) {
let kind = node.kind();
if language == Language::Elixir && kind == "call" {
if let Some(def_info) = try_elixir_call_definition(node, source) {
definitions.push(def_info);
}
}
if let Some(const_def) = try_constant_definition(node, source, language) {
definitions.push(const_def);
}
let (is_func, is_class) = classify_definition_node(kind, language);
let is_bodyless_c_specifier = is_class
&& matches!(kind, "struct_specifier" | "enum_specifier")
&& node.child_by_field_name("body").is_none();
if (is_func || is_class) && !is_bodyless_c_specifier {
if let Some(name) = get_definition_node_name(node, source) {
let line_start = node.start_position().row as u32 + 1; let line_end = node.end_position().row as u32 + 1;
let signature = extract_def_signature(node, source);
let entry_kind = if is_class {
match kind {
"struct_item" | "struct_definition" | "struct_specifier" => "struct",
"enum_item" => "enum",
"trait_item" => "trait",
"interface_declaration" => "interface",
"module" => "module",
_ => "class",
}
} else {
if is_inside_class_or_impl(&node, language) {
"method"
} else {
"function"
}
};
definitions.push(DefinitionInfo {
name,
kind: entry_kind.to_string(),
line_start,
line_end,
signature,
});
}
}
if let Some(field_defs) = try_field_definition(node, source, language) {
definitions.extend(field_defs);
}
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
collect_definitions(child, source, language, definitions);
}
}
fn try_constant_definition(
node: Node,
source: &str,
language: Language,
) -> Option<DefinitionInfo> {
let kind = node.kind();
match language {
Language::Python => {
if kind != "expression_statement" {
return None;
}
let inner = node.child(0)?;
if inner.kind() != "assignment" {
return None;
}
let left = inner.child_by_field_name("left")?;
if left.kind() != "identifier" {
return None;
}
let name = get_node_text(&left, source);
if !is_upper_case_name(&name) {
return None;
}
Some(make_constant_def(node, name, source))
}
Language::Rust => {
if kind != "const_item" && kind != "static_item" {
return None;
}
let name = node
.child_by_field_name("name")
.map(|n| get_node_text(&n, source))?;
Some(make_constant_def(node, name, source))
}
Language::Go => {
if kind != "const_spec" {
return None;
}
let name = node
.child_by_field_name("name")
.map(|n| get_node_text(&n, source))?;
Some(make_constant_def(node, name, source))
}
Language::TypeScript | Language::JavaScript => {
if kind != "lexical_declaration" {
return None;
}
let decl_text = get_node_text(&node, source);
if !decl_text.starts_with("const ") {
return None;
}
let mut cursor = node.walk();
let declarator = node
.children(&mut cursor)
.find(|c| c.kind() == "variable_declarator")?;
let name = declarator
.child_by_field_name("name")
.map(|n| get_node_text(&n, source))?;
if !is_upper_case_name(&name) {
return None;
}
Some(make_constant_def(node, name, source))
}
Language::C | Language::Cpp => {
match kind {
"preproc_def" => {
let mut cursor = node.walk();
let ident = node
.children(&mut cursor)
.find(|c| c.kind() == "identifier")?;
let name = get_node_text(&ident, source);
if !is_upper_case_name(&name) {
return None;
}
Some(make_constant_def(node, name, source))
}
"declaration" => {
let mut cursor = node.walk();
let has_const = node.children(&mut cursor).any(|c| {
if c.kind() != "type_qualifier" {
return false;
}
let text = get_node_text(&c, source);
text == "const"
|| (language == Language::Cpp && text == "constexpr")
});
if !has_const {
return None;
}
let mut cursor2 = node.walk();
let init_decl = node
.children(&mut cursor2)
.find(|c| c.kind() == "init_declarator")?;
let decl = init_decl.child_by_field_name("declarator")?;
let name = get_node_text(&decl, source);
if !is_upper_case_name(&name) {
return None;
}
Some(make_constant_def(node, name, source))
}
_ => None,
}
}
Language::Ruby => {
if kind != "assignment" {
return None;
}
let mut cursor = node.walk();
let const_node = node
.children(&mut cursor)
.find(|c| c.kind() == "constant")?;
let name = get_node_text(&const_node, source);
Some(make_constant_def(node, name, source))
}
Language::Java => {
if kind != "field_declaration" {
return None;
}
let mut cursor = node.walk();
let modifiers = node
.children(&mut cursor)
.find(|c| c.kind() == "modifiers")?;
let mod_text = get_node_text(&modifiers, source);
if !mod_text.contains("static") || !mod_text.contains("final") {
return None;
}
let mut cursor2 = node.walk();
let declarator = node
.children(&mut cursor2)
.find(|c| c.kind() == "variable_declarator")?;
let name = declarator
.child_by_field_name("name")
.map(|n| get_node_text(&n, source))?;
Some(make_constant_def(node, name, source))
}
Language::Kotlin => {
if kind != "property_declaration" {
return None;
}
let text = get_node_text(&node, source);
if !text.starts_with("val ") && !text.starts_with("const val ") {
return None;
}
let mut cursor = node.walk();
let var_decl = node
.children(&mut cursor)
.find(|c| c.kind() == "variable_declaration")?;
let name_node = if let Some(n) = var_decl.child_by_field_name("name") {
Some(n)
} else {
let mut vc = var_decl.walk();
let found = var_decl
.children(&mut vc)
.find(|c| c.kind() == "simple_identifier" || c.kind() == "identifier");
found
};
let name = name_node.map(|n| get_node_text(&n, source))?;
if !text.starts_with("const val ") && !is_upper_case_name(&name) {
return None;
}
Some(make_constant_def(node, name, source))
}
Language::Swift => {
if kind != "property_declaration" {
return None;
}
if let Some(parent) = node.parent() {
if matches!(
parent.kind(),
"class_body" | "enum_body" | "protocol_body" | "struct_body"
) {
return None;
}
}
let text = get_node_text(&node, source);
if !text.starts_with("let ") {
return None;
}
let name_node = if let Some(n) = node.child_by_field_name("name") {
Some(n)
} else {
let mut cursor = node.walk();
let found = node.children(&mut cursor).find(|c| {
c.kind() == "pattern"
|| c.kind() == "simple_identifier"
|| c.kind() == "identifier"
});
found
};
let name_node = name_node?;
let name = get_node_text(&name_node, source);
if name.contains('(') || name.contains('{') {
return None;
}
Some(make_constant_def(node, name, source))
}
Language::CSharp => {
if kind != "field_declaration" {
return None;
}
let mut cursor = node.walk();
let has_const = node.children(&mut cursor).any(|c| {
c.kind() == "modifier" && get_node_text(&c, source) == "const"
});
if !has_const {
return None;
}
let mut cursor2 = node.walk();
let var_decl = node
.children(&mut cursor2)
.find(|c| c.kind() == "variable_declaration")?;
let mut cursor3 = var_decl.walk();
let declarator = var_decl
.children(&mut cursor3)
.find(|c| c.kind() == "variable_declarator")?;
let name = declarator
.child_by_field_name("name")
.or_else(|| declarator.child(0))
.map(|n| get_node_text(&n, source))?;
Some(make_constant_def(node, name, source))
}
Language::Scala => {
if kind != "val_definition" {
return None;
}
let name_node = if let Some(n) = node.child_by_field_name("pattern") {
Some(n)
} else {
let mut cursor = node.walk();
let found = node
.children(&mut cursor)
.find(|c| c.kind() == "identifier");
found
};
let name_node = name_node?;
let name = get_node_text(&name_node, source);
if !is_upper_case_name(&name) {
return None;
}
Some(make_constant_def(node, name, source))
}
Language::Php => {
if kind != "const_declaration" {
return None;
}
let mut cursor = node.walk();
let element = node
.children(&mut cursor)
.find(|c| c.kind() == "const_element")?;
let name = element
.child_by_field_name("name")
.or_else(|| element.child(0))
.map(|n| get_node_text(&n, source))?;
Some(make_constant_def(node, name, source))
}
Language::Elixir => {
if kind != "unary_operator" {
return None;
}
let text = get_node_text(&node, source);
if !text.starts_with('@') {
return None;
}
let mut cursor = node.walk();
let ident = node
.children(&mut cursor)
.find(|c| c.kind() == "identifier")?;
let name = format!("@{}", get_node_text(&ident, source));
if name == "@doc" || name == "@moduledoc" || name == "@spec" || name == "@type" {
return None;
}
Some(make_constant_def(node, name, source))
}
Language::Lua | Language::Luau | Language::Ocaml => None,
}
}
fn make_constant_def(node: Node, name: String, source: &str) -> DefinitionInfo {
let line_start = node.start_position().row as u32 + 1;
let line_end = node.end_position().row as u32 + 1;
let sig_start = node.start_byte();
let signature = source[sig_start..]
.lines()
.next()
.unwrap_or("")
.trim()
.to_string();
DefinitionInfo {
name,
kind: "constant".to_string(),
line_start,
line_end,
signature,
}
}
fn try_field_definition(
node: Node,
source: &str,
language: Language,
) -> Option<Vec<DefinitionInfo>> {
let kind = node.kind();
let kind_matches = match language {
Language::Java => matches!(kind, "field_declaration"),
Language::Kotlin => matches!(kind, "property_declaration"),
Language::Swift => matches!(kind, "property_declaration"),
Language::TypeScript | Language::JavaScript => {
matches!(kind, "public_field_definition" | "field_definition")
}
_ => false,
};
if !kind_matches {
return None;
}
let parent = node.parent()?;
let parent_kind = parent.kind();
let parent_is_class_body = matches!(
parent_kind,
"class_body" | "interface_body" | "enum_body" | "annotation_type_body" | "protocol_body" | "struct_body" );
if !parent_is_class_body {
return None;
}
let mut defs: Vec<DefinitionInfo> = Vec::new();
let line_start = node.start_position().row as u32 + 1;
let line_end = node.end_position().row as u32 + 1;
let signature = extract_def_signature(node, source);
match language {
Language::Java => {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "variable_declarator" {
let name_opt = child
.child_by_field_name("name")
.and_then(|n| n.utf8_text(source.as_bytes()).ok().map(|s| s.to_string()))
.or_else(|| {
let mut c2 = child.walk();
for inner in child.children(&mut c2) {
if inner.kind() == "identifier" {
return inner
.utf8_text(source.as_bytes())
.ok()
.map(|s| s.to_string());
}
}
None
});
if let Some(name) = name_opt {
defs.push(DefinitionInfo {
name,
kind: "field".to_string(),
line_start,
line_end,
signature: signature.clone(),
});
}
}
}
}
Language::Kotlin => {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "variable_declaration" {
let mut c2 = child.walk();
for inner in child.children(&mut c2) {
if inner.kind() == "identifier" {
if let Ok(name) = inner.utf8_text(source.as_bytes()) {
defs.push(DefinitionInfo {
name: name.to_string(),
kind: "field".to_string(),
line_start,
line_end,
signature: signature.clone(),
});
}
break;
}
}
}
}
}
Language::Swift => {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "pattern" {
let mut c2 = child.walk();
for inner in child.children(&mut c2) {
if inner.kind() == "simple_identifier" {
if let Ok(name) = inner.utf8_text(source.as_bytes()) {
defs.push(DefinitionInfo {
name: name.to_string(),
kind: "field".to_string(),
line_start,
line_end,
signature: signature.clone(),
});
}
break;
}
}
}
}
}
Language::TypeScript | Language::JavaScript => {
let name_opt = node
.child_by_field_name("name")
.and_then(|n| n.utf8_text(source.as_bytes()).ok().map(|s| s.to_string()))
.or_else(|| {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "property_identifier"
|| child.kind() == "private_property_identifier"
{
return child
.utf8_text(source.as_bytes())
.ok()
.map(|s| s.to_string());
}
}
None
});
if let Some(name) = name_opt {
defs.push(DefinitionInfo {
name,
kind: "field".to_string(),
line_start,
line_end,
signature,
});
}
}
_ => {}
}
if defs.is_empty() {
None
} else {
Some(defs)
}
}
fn try_elixir_call_definition(node: Node, source: &str) -> Option<DefinitionInfo> {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() != "identifier" {
continue;
}
let keyword = child.utf8_text(source.as_bytes()).ok()?;
let args = child.next_sibling()?;
match keyword {
"def" | "defp" => {
let first_arg = args.child(0)?;
let name = if first_arg.kind() == "call" {
first_arg.child(0)?.utf8_text(source.as_bytes()).ok()?
} else {
first_arg.utf8_text(source.as_bytes()).ok()?
};
let line_start = node.start_position().row as u32 + 1;
let line_end = node.end_position().row as u32 + 1;
let signature = extract_def_signature(node, source);
return Some(DefinitionInfo {
name: name.to_string(),
kind: "function".to_string(),
line_start,
line_end,
signature,
});
}
"defmodule" => {
let first_arg = args.child(0)?;
let name = first_arg.utf8_text(source.as_bytes()).ok()?;
let line_start = node.start_position().row as u32 + 1;
let line_end = node.end_position().row as u32 + 1;
let signature = extract_def_signature(node, source);
return Some(DefinitionInfo {
name: name.to_string(),
kind: "module".to_string(),
line_start,
line_end,
signature,
});
}
_ => {}
}
}
None
}
fn classify_definition_node(kind: &str, _language: Language) -> (bool, bool) {
let is_func = matches!(
kind,
"function_definition"
| "function_declaration"
| "function_item" | "method_definition"
| "method_declaration"
| "method" | "singleton_method" | "arrow_function"
| "function_expression"
| "function" | "func_literal" | "function_type"
| "value_definition" | "init_declaration" | "constructor_declaration" );
let is_class = matches!(
kind,
"class_definition"
| "class_declaration"
| "class_specifier" | "class" | "module" | "struct_item" | "struct_definition" | "struct_specifier" | "enum_item" | "trait_item" | "type_spec" | "interface_declaration"
| "type_definition" | "module_definition" | "companion_object" );
(is_func, is_class)
}
fn get_definition_node_name(node: Node, source: &str) -> Option<String> {
if node.kind() == "init_declaration" {
return Some("init".to_string());
}
if let Some(name_node) = node.child_by_field_name("name") {
let text = name_node.utf8_text(source.as_bytes()).ok()?;
return Some(text.to_string());
}
if node.kind() == "constructor_declaration" {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "identifier" {
let text = child.utf8_text(source.as_bytes()).ok()?;
return Some(text.to_string());
}
}
}
if node.kind() == "function_definition" {
if let Some(declarator) = node.child_by_field_name("declarator") {
return extract_name_from_declarator(declarator, source);
}
}
if node.kind() == "arrow_function" || node.kind() == "function_expression" {
if let Some(parent) = node.parent() {
if parent.kind() == "variable_declarator" {
if let Some(name_node) = parent.child_by_field_name("name") {
let text = name_node.utf8_text(source.as_bytes()).ok()?;
return Some(text.to_string());
}
}
}
}
if node.kind() == "value_definition" {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "let_binding" {
if let Some(pattern_node) = child.child_by_field_name("pattern") {
let text = pattern_node.utf8_text(source.as_bytes()).ok()?;
if text != "()" && !text.is_empty() {
return Some(text.to_string());
}
}
}
}
return None;
}
if node.kind() == "companion_object" {
return Some("Companion".to_string());
}
None
}
fn extract_name_from_declarator(node: Node, source: &str) -> Option<String> {
match node.kind() {
"identifier" | "field_identifier" | "destructor_name" => {
Some(get_node_text(&node, source))
}
"function_declarator" | "pointer_declarator" | "reference_declarator" => {
let inner = node.child_by_field_name("declarator")?;
extract_name_from_declarator(inner, source)
}
"qualified_identifier" | "scoped_identifier" => {
if let Some(name) = node.child_by_field_name("name") {
return Some(get_node_text(&name, source));
}
Some(get_node_text(&node, source))
}
_ => {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if let Some(name) = extract_name_from_declarator(child, source) {
return Some(name);
}
}
None
}
}
}
fn is_inside_class_or_impl(node: &Node, language: Language) -> bool {
let mut current = node.parent();
while let Some(parent) = current {
let kind = parent.kind();
let module_is_class = !matches!(language, Language::Python);
if matches!(
kind,
"class_definition"
| "class_declaration"
| "class_specifier" | "class" | "class_body"
| "impl_item"
| "struct_item"
| "trait_item"
| "companion_object" | "object_declaration" ) || (kind == "module" && module_is_class) {
return true;
}
current = parent.parent();
}
false
}
fn extract_def_signature(node: Node, source: &str) -> String {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
let ckind = child.kind();
if ckind == "line_comment"
|| ckind == "block_comment"
|| ckind == "comment"
|| ckind == "attribute_item" || ckind == "attribute" || ckind == "decorator" || ckind == "decorator_list"
{
continue;
}
let start_byte = child.start_byte();
let line_from_start = &source[start_byte..];
let sig = line_from_start
.lines()
.next()
.unwrap_or("")
.trim()
.to_string();
if !sig.is_empty() {
return sig;
}
}
let node_text = &source[node.start_byte()..node.end_byte()];
for line in node_text.lines() {
let trimmed = line.trim();
if !trimmed.is_empty()
&& !trimmed.starts_with("///")
&& !trimmed.starts_with("//!")
&& !trimmed.starts_with("//")
&& !trimmed.starts_with("/*")
&& !trimmed.starts_with("*")
&& !trimmed.starts_with("#[")
&& !trimmed.starts_with("@")
&& !trimmed.starts_with("#")
{
return trimmed.to_string();
}
}
source[node.start_byte()..]
.lines()
.next()
.unwrap_or("")
.trim()
.to_string()
}
fn get_node_text(node: &Node, source: &str) -> String {
source[node.byte_range()].to_string()
}
fn is_inside_class(node: &Node) -> bool {
let mut current = node.parent();
while let Some(parent) = current {
match parent.kind() {
"class_definition" | "class_declaration" | "class" | "class_body" => return true,
_ => current = parent.parent(),
}
}
false
}
fn is_inside_impl(node: &Node) -> bool {
let mut current = node.parent();
while let Some(parent) = current {
if parent.kind() == "impl_item" || parent.kind() == "trait_item" {
return true;
}
current = parent.parent();
}
false
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::parser::parse;
#[test]
fn test_extract_python_functions() {
let source = r#"
def foo():
pass
def bar(x):
return x
class MyClass:
def method(self):
pass
"#;
let tree = parse(source, Language::Python).unwrap();
let functions = extract_functions(&tree, source, Language::Python);
assert!(functions.contains(&"foo".to_string()));
assert!(functions.contains(&"bar".to_string()));
assert!(!functions.contains(&"method".to_string()));
}
#[test]
fn test_extract_python_classes() {
let source = r#"
class MyClass:
pass
class AnotherClass:
def method(self):
pass
"#;
let tree = parse(source, Language::Python).unwrap();
let classes = extract_classes(&tree, source, Language::Python);
assert!(classes.contains(&"MyClass".to_string()));
assert!(classes.contains(&"AnotherClass".to_string()));
}
#[test]
fn test_extract_python_methods() {
let source = r#"
class MyClass:
def method1(self):
pass
def method2(self, x):
return x
"#;
let tree = parse(source, Language::Python).unwrap();
let methods = extract_methods(&tree, source, Language::Python);
assert!(methods.contains(&"method1".to_string()));
assert!(methods.contains(&"method2".to_string()));
}
#[test]
fn test_extract_typescript_functions() {
let source = r#"
function foo() {}
const bar = () => {};
class MyClass {
method() {}
}
"#;
let tree = parse(source, Language::TypeScript).unwrap();
let functions = extract_functions(&tree, source, Language::TypeScript);
assert!(functions.contains(&"foo".to_string()));
}
#[test]
fn test_extract_go_functions() {
let source = r#"
package main
func foo() {}
func (r *Receiver) bar() {}
"#;
let tree = parse(source, Language::Go).unwrap();
let functions = extract_functions(&tree, source, Language::Go);
assert!(functions.contains(&"foo".to_string()));
assert!(functions.contains(&"bar".to_string()));
}
#[test]
fn test_extract_c_functions() {
let source = r#"
void hello(void) {
}
int main(int argc, char** argv) {
return 0;
}
static void helper(int x) {
}
"#;
let tree = parse(source, Language::C).unwrap();
let functions = extract_functions(&tree, source, Language::C);
assert!(
functions.contains(&"hello".to_string()),
"Should find hello function"
);
assert!(
functions.contains(&"main".to_string()),
"Should find main function"
);
assert!(
functions.contains(&"helper".to_string()),
"Should find helper function"
);
}
#[test]
fn test_extract_c_structs() {
let source = r#"
struct Point {
int x;
int y;
};
enum Color {
RED,
GREEN,
BLUE
};
"#;
let tree = parse(source, Language::C).unwrap();
let classes = extract_classes(&tree, source, Language::C);
assert!(
classes.contains(&"Point".to_string()),
"Should find Point struct"
);
assert!(
classes.contains(&"Color".to_string()),
"Should find Color enum"
);
}
#[test]
fn test_extract_c_structs_requires_body_val_001() {
let source = r#"
struct Foo { int x; };
void use_bar(struct Bar *b);
struct Forward;
typedef struct { int y; } Anon;
typedef struct Existing OtherName;
enum E { A };
enum F;
void use_enum(enum G *e);
"#;
let tree = parse(source, Language::C).unwrap();
let classes = extract_classes(&tree, source, Language::C);
let expected: std::collections::HashSet<String> =
["Foo", "Anon", "E"].iter().map(|s| s.to_string()).collect();
let got: std::collections::HashSet<String> = classes.iter().cloned().collect();
assert_eq!(
got, expected,
"VAL-001: extract_c_structs must only emit bodied struct/enum \
definitions. Expected {:?}, got {:?}",
expected, got
);
for forbidden in ["Bar", "Forward", "Existing", "OtherName", "F", "G"] {
assert!(
!classes.contains(&forbidden.to_string()),
"VAL-001: must NOT emit `{}` (no body / alias / param type), \
got classes = {:?}",
forbidden,
classes
);
}
}
#[test]
fn test_extract_cpp_functions() {
let source = r#"
void hello() {
}
int main() {
return 0;
}
namespace greeting {
void greet(const std::string& name) {
}
}
"#;
let tree = parse(source, Language::Cpp).unwrap();
let functions = extract_functions(&tree, source, Language::Cpp);
assert!(
functions.contains(&"hello".to_string()),
"Should find hello function"
);
assert!(
functions.contains(&"main".to_string()),
"Should find main function"
);
assert!(
functions.contains(&"greet".to_string()),
"Should find greet function"
);
}
#[test]
fn test_extract_cpp_classes() {
let source = r#"
class Greeter {
public:
void greet();
};
struct Point {
int x, y;
};
"#;
let tree = parse(source, Language::Cpp).unwrap();
let classes = extract_classes(&tree, source, Language::Cpp);
assert!(
classes.contains(&"Greeter".to_string()),
"Should find Greeter class"
);
assert!(
classes.contains(&"Point".to_string()),
"Should find Point struct"
);
}
#[test]
fn test_extract_ruby_functions() {
let source = r##"
def top_level_function
puts "I'm a function"
end
def another_function(x)
x * 2
end
class MyClass
def method_in_class
puts "method"
end
end
"##;
let tree = parse(source, Language::Ruby).unwrap();
let functions = extract_functions(&tree, source, Language::Ruby);
assert!(
functions.contains(&"top_level_function".to_string()),
"Should find top_level_function"
);
assert!(
functions.contains(&"another_function".to_string()),
"Should find another_function"
);
assert!(
!functions.contains(&"method_in_class".to_string()),
"Should not find method_in_class in functions"
);
}
#[test]
fn test_extract_ruby_methods() {
let source = r##"
def top_level_function
puts "I'm a function"
end
class MyClass
def initialize(name)
@name = name
end
def greet
puts "Hello"
end
end
module MyModule
def module_method
puts "module method"
end
end
"##;
let tree = parse(source, Language::Ruby).unwrap();
let methods = extract_methods(&tree, source, Language::Ruby);
assert!(
methods.contains(&"initialize".to_string()),
"Should find initialize method"
);
assert!(
methods.contains(&"greet".to_string()),
"Should find greet method"
);
assert!(
methods.contains(&"module_method".to_string()),
"Should find module_method"
);
assert!(
!methods.contains(&"top_level_function".to_string()),
"Should not find top_level_function in methods"
);
}
#[test]
fn test_extract_ruby_classes() {
let source = r##"
class MyClass
def initialize
end
end
class AnotherClass < BaseClass
def method
end
end
module MyModule
class NestedClass
end
end
"##;
let tree = parse(source, Language::Ruby).unwrap();
let classes = extract_classes(&tree, source, Language::Ruby);
assert!(
classes.contains(&"MyClass".to_string()),
"Should find MyClass"
);
assert!(
classes.contains(&"AnotherClass".to_string()),
"Should find AnotherClass"
);
assert!(
classes.contains(&"MyModule".to_string()),
"Should find MyModule"
);
assert!(
classes.contains(&"NestedClass".to_string()),
"Should find NestedClass"
);
}
#[test]
fn test_extract_kotlin_functions() {
let source = r#"
fun topLevel() {
println("hello")
}
fun anotherTopLevel(x: Int): Int {
return x * 2
}
class MyClass {
fun classMethod() {}
}
"#;
let tree = parse(source, Language::Kotlin).unwrap();
let functions = extract_functions(&tree, source, Language::Kotlin);
assert!(
functions.contains(&"topLevel".to_string()),
"Should find topLevel function"
);
assert!(
functions.contains(&"anotherTopLevel".to_string()),
"Should find anotherTopLevel function"
);
assert!(
!functions.contains(&"classMethod".to_string()),
"Should not find classMethod in functions"
);
}
#[test]
fn test_extract_kotlin_methods() {
let source = r#"
fun topLevel() {}
class MyClass {
fun method1() {}
fun method2(x: Int): String { return x.toString() }
}
object Singleton {
fun singletonMethod() {}
}
"#;
let tree = parse(source, Language::Kotlin).unwrap();
let methods = extract_methods(&tree, source, Language::Kotlin);
assert!(
methods.contains(&"method1".to_string()),
"Should find method1"
);
assert!(
methods.contains(&"method2".to_string()),
"Should find method2"
);
assert!(
!methods.contains(&"topLevel".to_string()),
"Should not find topLevel in methods"
);
}
#[test]
fn test_extract_kotlin_classes() {
let source = r#"
class HttpClient(val engine: Engine) {
fun config() {}
}
object Singleton {
fun method() {}
}
interface MyInterface {
fun abstractMethod()
}
"#;
let tree = parse(source, Language::Kotlin).unwrap();
let classes = extract_classes(&tree, source, Language::Kotlin);
assert!(
classes.contains(&"HttpClient".to_string()),
"Should find HttpClient class"
);
assert!(
classes.contains(&"Singleton".to_string()),
"Should find Singleton object"
);
assert!(
classes.contains(&"MyInterface".to_string()),
"Should find MyInterface interface"
);
}
#[test]
fn test_extract_ocaml_functions() {
let source = r#"
let greet name =
Printf.printf "Hello, %s!\n" name
let add x y = x + y
let value = 42
let rec factorial n =
if n <= 1 then 1
else n * factorial (n - 1)
let () = greet "world"
"#;
let tree = parse(source, Language::Ocaml).unwrap();
let functions = extract_functions(&tree, source, Language::Ocaml);
assert!(
functions.contains(&"greet".to_string()),
"Should find greet function"
);
assert!(
functions.contains(&"add".to_string()),
"Should find add function"
);
assert!(
functions.contains(&"factorial".to_string()),
"Should find factorial function"
);
assert!(
!functions.contains(&"value".to_string()),
"Should not find value binding as function"
);
assert!(
!functions.contains(&"()".to_string()),
"Should not find anonymous let () binding"
);
}
#[test]
fn test_extract_rust_classes_includes_traits() {
let source = r#"
pub struct Config {
pub name: String,
}
pub enum Color {
Red,
Green,
Blue,
}
pub trait Serialize {
fn serialize(&self) -> String;
}
trait Deserialize {
fn deserialize(input: &str) -> Self;
}
"#;
let tree = parse(source, Language::Rust).unwrap();
let classes = extract_classes(&tree, source, Language::Rust);
assert!(
classes.contains(&"Config".to_string()),
"Should find Config struct"
);
assert!(
classes.contains(&"Color".to_string()),
"Should find Color enum"
);
assert!(
classes.contains(&"Serialize".to_string()),
"Should find Serialize trait"
);
assert!(
classes.contains(&"Deserialize".to_string()),
"Should find Deserialize trait"
);
}
#[test]
fn test_extract_rust_functions_excludes_trait_methods() {
let source = r#"
pub fn top_level() -> bool {
true
}
pub trait Visitor {
fn visit_bool(&self, v: bool) {}
fn visit_i32(&self, v: i32) {}
}
impl Config {
pub fn new() -> Self {
Config {}
}
}
"#;
let tree = parse(source, Language::Rust).unwrap();
let functions = extract_functions(&tree, source, Language::Rust);
assert!(
functions.contains(&"top_level".to_string()),
"Should find top_level function"
);
assert!(
!functions.contains(&"visit_bool".to_string()),
"Trait method visit_bool should not be a top-level function"
);
assert!(
!functions.contains(&"visit_i32".to_string()),
"Trait method visit_i32 should not be a top-level function"
);
assert!(
!functions.contains(&"new".to_string()),
"Impl method new should not be a top-level function"
);
}
#[test]
fn test_extract_rust_methods_includes_trait_methods() {
let source = r#"
pub trait Visitor {
fn visit_bool(&self, v: bool) {}
fn visit_i32(&self, v: i32) {}
}
impl Config {
pub fn new() -> Self {
Config {}
}
pub fn name(&self) -> &str {
&self.name
}
}
fn top_level() {}
"#;
let tree = parse(source, Language::Rust).unwrap();
let methods = extract_methods(&tree, source, Language::Rust);
assert!(
methods.contains(&"visit_bool".to_string()),
"Should find visit_bool trait method"
);
assert!(
methods.contains(&"visit_i32".to_string()),
"Should find visit_i32 trait method"
);
assert!(
methods.contains(&"new".to_string()),
"Should find new impl method"
);
assert!(
methods.contains(&"name".to_string()),
"Should find name impl method"
);
assert!(
!methods.contains(&"top_level".to_string()),
"top_level should not be in methods"
);
}
fn extract_counts(source: &str, lang: Language) -> (usize, usize, usize) {
let tree = parse(source, lang).expect("parsing should succeed");
let functions = extract_functions(&tree, source, lang);
let classes = extract_classes(&tree, source, lang);
let methods = extract_methods(&tree, source, lang);
(functions.len(), classes.len(), methods.len())
}
#[test]
fn test_extractor_python() {
let source = include_str!("../../tests/fixtures/extractor/test_python.py");
let (f, c, m) = extract_counts(source, Language::Python);
assert_eq!(f, 3, "Python: expected 3 functions, got {}", f);
assert_eq!(c, 2, "Python: expected 2 classes, got {}", c);
assert_eq!(m, 5, "Python: expected 5 methods, got {}", m);
}
#[test]
fn test_extractor_go() {
let source = include_str!("../../tests/fixtures/extractor/test_go.go");
let (f, c, m) = extract_counts(source, Language::Go);
assert_eq!(f, 4, "Go: expected 4 functions, got {}", f);
assert_eq!(c, 2, "Go: expected 2 structs, got {}", c);
assert_eq!(m, 0, "Go: expected 0 methods, got {}", m);
}
#[test]
fn test_extractor_rust() {
let source = include_str!("../../tests/fixtures/extractor/test_rust.rs");
let (f, c, m) = extract_counts(source, Language::Rust);
assert_eq!(f, 3, "Rust: expected 3 functions, got {}", f);
assert_eq!(c, 2, "Rust: expected 2 structs, got {}", c);
assert_eq!(m, 4, "Rust: expected 4 methods, got {}", m);
}
#[test]
fn test_extractor_java() {
let source = include_str!("../../tests/fixtures/extractor/test_java.java");
let (f, c, m) = extract_counts(source, Language::Java);
assert_eq!(f, 0, "Java: expected 0 functions, got {}", f);
assert_eq!(c, 3, "Java: expected 3 classes, got {}", c);
assert_eq!(m, 6, "Java: expected 6 methods, got {}", m);
}
#[test]
fn test_extractor_c() {
let source = include_str!("../../tests/fixtures/extractor/test_c.c");
let (f, c, m) = extract_counts(source, Language::C);
assert_eq!(f, 4, "C: expected 4 functions, got {}", f);
assert_eq!(c, 2, "C: expected 2 structs, got {}", c);
assert_eq!(m, 0, "C: expected 0 methods, got {}", m);
}
#[test]
fn test_extractor_cpp() {
let source = include_str!("../../tests/fixtures/extractor/test_cpp.cpp");
let (f, c, m) = extract_counts(source, Language::Cpp);
assert_eq!(f, 2, "C++: expected 2 functions, got {}", f);
assert_eq!(c, 2, "C++: expected 2 classes, got {}", c);
assert_eq!(m, 5, "C++: expected 5 methods, got {}", m);
}
#[test]
fn test_extractor_typescript() {
let source = include_str!("../../tests/fixtures/extractor/test_typescript.ts");
let (f, c, m) = extract_counts(source, Language::TypeScript);
assert_eq!(f, 3, "TypeScript: expected 3 functions, got {}", f);
assert_eq!(c, 2, "TypeScript: expected 2 classes, got {}", c);
assert_eq!(m, 4, "TypeScript: expected 4 methods, got {}", m);
}
#[test]
fn test_extractor_javascript() {
let source = include_str!("../../tests/fixtures/extractor/test_javascript.js");
let (f, c, m) = extract_counts(source, Language::JavaScript);
assert_eq!(f, 5, "JavaScript: expected 5 functions, got {}", f);
assert_eq!(c, 2, "JavaScript: expected 2 classes, got {}", c);
assert_eq!(m, 4, "JavaScript: expected 4 methods, got {}", m);
}
#[test]
fn test_extractor_ruby() {
let source = include_str!("../../tests/fixtures/extractor/test_ruby.rb");
let (f, c, m) = extract_counts(source, Language::Ruby);
assert_eq!(f, 2, "Ruby: expected 2 functions, got {}", f);
assert_eq!(c, 2, "Ruby: expected 2 classes, got {}", c);
assert_eq!(m, 5, "Ruby: expected 5 methods, got {}", m);
}
#[test]
fn test_extractor_php() {
let source = include_str!("../../tests/fixtures/extractor/test_php.php");
let (f, c, m) = extract_counts(source, Language::Php);
assert_eq!(f, 2, "PHP: expected 2 functions, got {}", f);
assert_eq!(c, 2, "PHP: expected 2 classes, got {}", c);
assert_eq!(m, 5, "PHP: expected 5 methods, got {}", m);
}
#[test]
fn test_extractor_kotlin() {
let source = include_str!("../../tests/fixtures/extractor/test_kotlin.kt");
let (f, c, m) = extract_counts(source, Language::Kotlin);
assert_eq!(f, 2, "Kotlin: expected 2 functions, got {}", f);
assert_eq!(c, 2, "Kotlin: expected 2 classes, got {}", c);
assert_eq!(m, 5, "Kotlin: expected 5 methods, got {}", m);
}
#[test]
fn test_extractor_swift() {
let source = include_str!("../../tests/fixtures/extractor/test_swift.swift");
let (f, c, m) = extract_counts(source, Language::Swift);
assert_eq!(f, 3, "Swift: expected 3 functions, got {}", f);
assert_eq!(c, 2, "Swift: expected 2 classes, got {}", c);
assert_eq!(m, 5, "Swift: expected 5 methods, got {}", m);
}
#[test]
fn test_extractor_csharp() {
let source = include_str!("../../tests/fixtures/extractor/test_csharp.cs");
let (f, c, m) = extract_counts(source, Language::CSharp);
assert_eq!(f, 0, "C#: expected 0 functions, got {}", f);
assert_eq!(c, 3, "C#: expected 3 classes, got {}", c);
assert_eq!(
m, 7,
"C#: expected 7 methods (including constructors), got {}",
m
);
}
#[test]
fn test_extractor_scala() {
let source = include_str!("../../tests/fixtures/extractor/test_scala.scala");
let (f, c, m) = extract_counts(source, Language::Scala);
assert_eq!(f, 2, "Scala: expected 2 functions, got {}", f);
assert_eq!(c, 2, "Scala: expected 2 classes, got {}", c);
assert_eq!(m, 4, "Scala: expected 4 methods, got {}", m);
}
#[test]
fn test_extractor_ocaml() {
let source = include_str!("../../tests/fixtures/extractor/test_ocaml.ml");
let (f, c, m) = extract_counts(source, Language::Ocaml);
assert_eq!(f, 3, "OCaml: expected 3 functions, got {}", f);
assert_eq!(c, 0, "OCaml: expected 0 classes, got {}", c);
assert_eq!(m, 0, "OCaml: expected 0 methods, got {}", m);
}
#[test]
fn test_extractor_elixir() {
let source = include_str!("../../tests/fixtures/extractor/test_elixir.ex");
let (f, c, m) = extract_counts(source, Language::Elixir);
assert_eq!(f, 3, "Elixir: expected 3 functions, got {}", f);
assert_eq!(c, 1, "Elixir: expected 1 class (module), got {}", c);
assert_eq!(m, 0, "Elixir: expected 0 methods, got {}", m);
}
#[test]
fn test_extractor_lua() {
let source = include_str!("../../tests/fixtures/extractor/test_lua.lua");
let (f, c, m) = extract_counts(source, Language::Lua);
assert_eq!(f, 5, "Lua: expected 5 functions, got {}", f);
assert_eq!(c, 0, "Lua: expected 0 classes, got {}", c);
assert_eq!(m, 0, "Lua: expected 0 methods, got {}", m);
}
#[test]
fn test_extractor_luau() {
let source = include_str!("../../tests/fixtures/extractor/test_luau.luau");
let (f, c, m) = extract_counts(source, Language::Luau);
assert_eq!(f, 3, "Luau: expected 3 functions, got {}", f);
assert_eq!(c, 0, "Luau: expected 0 classes, got {}", c);
assert_eq!(m, 0, "Luau: expected 0 methods, got {}", m);
}
fn get_constants(source: &str, language: Language) -> Vec<DefinitionInfo> {
let tree = parse(source, language).unwrap();
let defs = extract_definitions(&tree, source, language);
defs.into_iter().filter(|d| d.kind == "constant").collect()
}
#[test]
fn test_python_constant_definitions() {
let source = "MAX_RETRIES = 3\n\nEXTERNAL_FUNCTIONS = {\n \"foo\": bar,\n \"baz\": qux,\n}\n\nlower_case = 42\n";
let consts = get_constants(source, Language::Python);
assert_eq!(consts.len(), 2);
assert_eq!(consts[0].name, "MAX_RETRIES");
assert_eq!(consts[0].line_start, 1);
assert_eq!(consts[0].line_end, 1);
assert_eq!(consts[0].signature, "MAX_RETRIES = 3");
assert_eq!(consts[1].name, "EXTERNAL_FUNCTIONS");
assert_eq!(consts[1].line_start, 3);
assert_eq!(consts[1].line_end, 6);
assert_eq!(consts[1].signature, "EXTERNAL_FUNCTIONS = {");
}
#[test]
fn test_rust_constant_definitions() {
let source = "const MAX_SIZE: usize = 100;\npub static GLOBAL: &str = \"hello\";\nlet x = 5;\n";
let consts = get_constants(source, Language::Rust);
assert_eq!(consts.len(), 2);
assert_eq!(consts[0].name, "MAX_SIZE");
assert_eq!(consts[0].kind, "constant");
assert_eq!(consts[1].name, "GLOBAL");
}
#[test]
fn test_go_constant_definitions() {
let source = "package main\n\nconst MaxRetries = 3\n\nconst (\n\tA = 1\n\tB = 2\n)\n";
let consts = get_constants(source, Language::Go);
assert_eq!(consts.len(), 3);
let names: Vec<&str> = consts.iter().map(|c| c.name.as_str()).collect();
assert!(names.contains(&"MaxRetries"));
assert!(names.contains(&"A"));
assert!(names.contains(&"B"));
}
#[test]
fn test_typescript_constant_definitions() {
let source = "const MAX_RETRIES = 3;\nexport const API_URL = \"https://example.com\";\nconst lower = 42;\n";
let consts = get_constants(source, Language::TypeScript);
assert_eq!(consts.len(), 2);
assert_eq!(consts[0].name, "MAX_RETRIES");
assert_eq!(consts[1].name, "API_URL");
}
#[test]
fn test_javascript_multiline_constant() {
let source = "const EXTERNAL_FUNCTIONS = {\n foo: 1,\n bar: 2,\n};\n";
let consts = get_constants(source, Language::JavaScript);
assert_eq!(consts.len(), 1);
assert_eq!(consts[0].name, "EXTERNAL_FUNCTIONS");
assert_eq!(consts[0].line_start, 1);
assert_eq!(consts[0].line_end, 4);
assert_eq!(consts[0].signature, "const EXTERNAL_FUNCTIONS = {");
}
#[test]
fn test_c_constant_definitions() {
let source = "#define MAX_SIZE 100\nconst int BUFFER_LEN = 256;\nint x = 5;\n";
let consts = get_constants(source, Language::C);
assert_eq!(consts.len(), 2);
assert_eq!(consts[0].name, "MAX_SIZE");
assert_eq!(consts[1].name, "BUFFER_LEN");
}
#[test]
fn test_java_constant_definitions() {
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";
let consts = get_constants(source, Language::Java);
assert_eq!(consts.len(), 2);
assert_eq!(consts[0].name, "MAX_RETRIES");
assert_eq!(consts[1].name, "API_URL");
}
#[test]
fn test_ruby_constant_definitions() {
let source = "MAX_RETRIES = 3\nAPI_URL = \"https://example.com\"\nlower = 42\n";
let consts = get_constants(source, Language::Ruby);
assert_eq!(consts.len(), 2);
assert_eq!(consts[0].name, "MAX_RETRIES");
assert_eq!(consts[1].name, "API_URL");
}
#[test]
fn test_python_toplevel_function_kind_is_function() {
let source = r#"
def top_level():
pass
class MyClass:
def method(self):
pass
"#;
let tree = parse(source, Language::Python).unwrap();
let defs = extract_definitions(&tree, source, Language::Python);
let top_level = defs.iter().find(|d| d.name == "top_level");
assert!(top_level.is_some(), "top_level definition not found");
assert_eq!(
top_level.unwrap().kind,
"function",
"top-level Python def must have kind 'function', not 'method'"
);
let method = defs.iter().find(|d| d.name == "method");
assert!(method.is_some(), "method definition not found");
assert_eq!(
method.unwrap().kind,
"method",
"Python def inside class must have kind 'method'"
);
}
#[test]
fn test_ocaml_definitions_non_empty() {
let source = r#"
let top_level x = x * 2
let another_func x y = x + y
let rec factorial n =
if n <= 1 then 1
else n * factorial (n - 1)
let () =
let result = factorial 5 in
Printf.printf "%d\n" result
"#;
let tree = parse(source, Language::Ocaml).unwrap();
let defs = extract_definitions(&tree, source, Language::Ocaml);
assert!(
!defs.is_empty(),
"OCaml definitions array must be non-empty; got 0"
);
let names: Vec<&str> = defs.iter().map(|d| d.name.as_str()).collect();
assert!(
names.contains(&"top_level"),
"OCaml: expected 'top_level' in definitions, got {:?}",
names
);
assert!(
names.contains(&"another_func"),
"OCaml: expected 'another_func' in definitions, got {:?}",
names
);
assert!(
names.contains(&"factorial"),
"OCaml: expected 'factorial' in definitions, got {:?}",
names
);
assert!(
!names.contains(&"()"),
"OCaml: '()' binding must not appear in definitions"
);
}
#[test]
fn test_kotlin_companion_object_definition() {
let source = r#"
class Animal(val name: String) {
fun speak(): String = "..."
companion object {
fun create(name: String): Animal = Animal(name)
}
}
"#;
let tree = parse(source, Language::Kotlin).unwrap();
let defs = extract_definitions(&tree, source, Language::Kotlin);
let companion = defs.iter().find(|d| d.name == "Companion");
assert!(
companion.is_some(),
"Kotlin: companion object must produce a 'Companion' definition; definitions: {:?}",
defs.iter().map(|d| (&d.name, &d.kind)).collect::<Vec<_>>()
);
assert_eq!(
companion.unwrap().kind,
"class",
"Kotlin companion object kind must be 'class'"
);
}
#[test]
fn test_c_struct_ref_not_emitted_as_definition_val_001() {
let source = r#"
int open_connection(struct sockaddr *addr, struct sockaddr_in *sin) {
return 0;
}
"#;
let tree = parse(source, Language::C).unwrap();
let defs = extract_definitions(&tree, source, Language::C);
let names: Vec<String> = defs.iter().map(|d| d.name.clone()).collect();
assert!(
names.contains(&"open_connection".to_string()),
"VAL-001: open_connection must be in definitions; got {:?}",
names
);
assert!(
!names.contains(&"sockaddr".to_string()),
"VAL-001: bare struct_specifier `struct sockaddr` (no body) \
must NOT appear in definitions; got {:?}",
names
);
assert!(
!names.contains(&"sockaddr_in".to_string()),
"VAL-001: bare struct_specifier `struct sockaddr_in` (no body) \
must NOT appear in definitions; got {:?}",
names
);
}
#[test]
fn test_swift_init_emitted_as_method_definition_val_002() {
let source = r#"
class Foo {
var x: Int = 0
init(x: Int) { self.x = x }
func bar() {}
}
"#;
let tree = parse(source, Language::Swift).unwrap();
let defs = extract_definitions(&tree, source, Language::Swift);
let named: Vec<(String, String)> = defs
.iter()
.map(|d| (d.name.clone(), d.kind.clone()))
.collect();
let init = defs.iter().find(|d| d.name == "init");
assert!(
init.is_some(),
"VAL-002: Swift init must be in definitions; got {:?}",
named
);
assert_eq!(
init.unwrap().kind,
"method",
"VAL-002: Swift init inside class must have kind='method'; got {:?}",
named
);
}
#[test]
fn test_java_constructor_emitted_as_method_definition_val_003() {
let source = r#"
public class Store {
public Store() {}
public void get() {}
}
"#;
let tree = parse(source, Language::Java).unwrap();
let defs = extract_definitions(&tree, source, Language::Java);
let named: Vec<(String, String)> = defs
.iter()
.map(|d| (d.name.clone(), d.kind.clone()))
.collect();
let ctor = defs.iter().find(|d| d.name == "Store" && d.kind == "method");
assert!(
ctor.is_some(),
"VAL-003: Java constructor `Store` must be in definitions with \
kind='method'; got {:?}",
named
);
assert!(
defs.iter().any(|d| d.name == "get" && d.kind == "method"),
"VAL-003: Java method `get` must remain in definitions; got {:?}",
named
);
}
#[test]
fn test_class_fields_emitted_as_definitions_val_004() {
{
let source = r#"
public class Store {
private int count = 0;
public String name;
int x, y;
public void get() {}
}
"#;
let tree = parse(source, Language::Java).unwrap();
let defs = extract_definitions(&tree, source, Language::Java);
let fields: Vec<String> = defs
.iter()
.filter(|d| d.kind == "field")
.map(|d| d.name.clone())
.collect();
for expected in ["count", "name", "x", "y"] {
assert!(
fields.contains(&expected.to_string()),
"VAL-004 (Java): field `{}` must appear in definitions; \
got fields={:?}, all defs={:?}",
expected,
fields,
defs.iter()
.map(|d| (&d.name, &d.kind))
.collect::<Vec<_>>()
);
}
}
{
let source = r#"
class Foo {
val x: Int = 0
var y: String = "hi"
fun bar() {}
}
val topLevelX = 1
"#;
let tree = parse(source, Language::Kotlin).unwrap();
let defs = extract_definitions(&tree, source, Language::Kotlin);
let fields: Vec<String> = defs
.iter()
.filter(|d| d.kind == "field")
.map(|d| d.name.clone())
.collect();
assert!(
fields.contains(&"x".to_string()),
"VAL-004 (Kotlin): class-scope `val x` must be a field; \
got fields={:?}",
fields
);
assert!(
fields.contains(&"y".to_string()),
"VAL-004 (Kotlin): class-scope `var y` must be a field; \
got fields={:?}",
fields
);
assert!(
!fields.contains(&"topLevelX".to_string()),
"VAL-004 (Kotlin): top-level `val topLevelX` must NOT be a \
field (only class-scope properties are fields); got \
fields={:?}",
fields
);
}
{
let source = r#"
class Foo {
var x: Int = 0
let y: String = "hi"
func bar() {}
}
"#;
let tree = parse(source, Language::Swift).unwrap();
let defs = extract_definitions(&tree, source, Language::Swift);
let fields: Vec<String> = defs
.iter()
.filter(|d| d.kind == "field")
.map(|d| d.name.clone())
.collect();
for expected in ["x", "y"] {
assert!(
fields.contains(&expected.to_string()),
"VAL-004 (Swift): class-scope property `{}` must be a \
field; got fields={:?}",
expected,
fields
);
}
}
{
let source = r#"
class Foo {
public count: number = 0;
name: string = "hi";
bar() {}
}
"#;
let tree = parse(source, Language::TypeScript).unwrap();
let defs = extract_definitions(&tree, source, Language::TypeScript);
let fields: Vec<String> = defs
.iter()
.filter(|d| d.kind == "field")
.map(|d| d.name.clone())
.collect();
for expected in ["count", "name"] {
assert!(
fields.contains(&expected.to_string()),
"VAL-004 (TypeScript): class field `{}` must be in \
definitions; got fields={:?}",
expected,
fields
);
}
}
}
}