use std::rc::Rc;
use crate::symbol_resolver::ResolvedSymbol;
pub(crate) fn find_definitions(
node: &tree_sitter::Node,
source: &[u8],
target_name: &str,
) -> Vec<ResolvedSymbol> {
let mut results = Vec::new();
let mut stack: Vec<(tree_sitter::Node, Option<Rc<str>>)> = vec![(*node, None)];
while let Some((node, parent)) = stack.pop() {
let current_parent = parent.as_deref();
let kind = node.kind();
let mut descended_with_new_parent = false;
match kind {
"function_item" => {
if let Some(name_node) = node.child_by_field_name("name") {
let name = node_text(&name_node, source);
if name == target_name {
results.push(ResolvedSymbol {
name: name.to_string(),
start_line: node.start_position().row as u32 + 1,
end_line: node.end_position().row as u32 + 1,
parent_name: current_parent.map(String::from),
});
}
}
}
"impl_item" => {
let impl_type_name: Option<Rc<str>> =
extract_impl_type_name(&node, source).map(Rc::from);
let mut cursor = node.walk();
let children: Vec<_> = node.children(&mut cursor).collect();
for child in children.into_iter().rev() {
stack.push((child, impl_type_name.clone()));
}
descended_with_new_parent = true;
}
"struct_item" | "enum_item" | "type_item" | "trait_item" => {
if let Some(name_node) = node.child_by_field_name("name") {
let name = node_text(&name_node, source);
if name == target_name {
results.push(ResolvedSymbol {
name: name.to_string(),
start_line: node.start_position().row as u32 + 1,
end_line: node.end_position().row as u32 + 1,
parent_name: current_parent.map(String::from),
});
}
}
}
"function_definition" => {
if let Some(name_node) = node.child_by_field_name("name") {
let name = node_text(&name_node, source);
if name == target_name {
results.push(ResolvedSymbol {
name: name.to_string(),
start_line: node.start_position().row as u32 + 1,
end_line: node.end_position().row as u32 + 1,
parent_name: current_parent.map(String::from),
});
}
}
}
"class_definition" => {
let class_name: Option<Rc<str>> = node
.child_by_field_name("name")
.map(|n| Rc::from(node_text(&n, source)));
if let Some(ref name) = class_name
&& &**name == target_name
{
results.push(ResolvedSymbol {
name: name.to_string(),
start_line: node.start_position().row as u32 + 1,
end_line: node.end_position().row as u32 + 1,
parent_name: current_parent.map(String::from),
});
}
let mut cursor = node.walk();
let children: Vec<_> = node.children(&mut cursor).collect();
for child in children.into_iter().rev() {
stack.push((child, class_name.clone()));
}
descended_with_new_parent = true;
}
"function_declaration" => {
if let Some(name_node) = node.child_by_field_name("name") {
let name = node_text(&name_node, source);
if name == target_name {
results.push(ResolvedSymbol {
name: name.to_string(),
start_line: node.start_position().row as u32 + 1,
end_line: node.end_position().row as u32 + 1,
parent_name: current_parent.map(String::from),
});
}
}
}
"method_declaration" => {
if let Some(name_node) = node.child_by_field_name("name") {
let name = node_text(&name_node, source);
if name == target_name {
let receiver_type = extract_go_receiver_type(&node, source);
results.push(ResolvedSymbol {
name: name.to_string(),
start_line: node.start_position().row as u32 + 1,
end_line: node.end_position().row as u32 + 1,
parent_name: receiver_type.or_else(|| current_parent.map(String::from)),
});
}
}
}
"type_declaration" => {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "type_spec"
&& let Some(name_node) = child.child_by_field_name("name")
{
let name = node_text(&name_node, source);
if name == target_name {
results.push(ResolvedSymbol {
name: name.to_string(),
start_line: child.start_position().row as u32 + 1,
end_line: child.end_position().row as u32 + 1,
parent_name: current_parent.map(String::from),
});
}
}
}
}
"method_definition" => {
if let Some(name_node) = node.child_by_field_name("name") {
let name = node_text(&name_node, source);
if name == target_name {
results.push(ResolvedSymbol {
name: name.to_string(),
start_line: node.start_position().row as u32 + 1,
end_line: node.end_position().row as u32 + 1,
parent_name: current_parent.map(String::from),
});
}
}
}
"class_declaration" => {
let class_name: Option<Rc<str>> = node
.child_by_field_name("name")
.map(|n| Rc::from(node_text(&n, source)));
if let Some(ref name) = class_name
&& &**name == target_name
{
results.push(ResolvedSymbol {
name: name.to_string(),
start_line: node.start_position().row as u32 + 1,
end_line: node.end_position().row as u32 + 1,
parent_name: current_parent.map(String::from),
});
}
let mut cursor = node.walk();
let children: Vec<_> = node.children(&mut cursor).collect();
for child in children.into_iter().rev() {
stack.push((child, class_name.clone()));
}
descended_with_new_parent = true;
}
"lexical_declaration" | "variable_declaration" => {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "variable_declarator"
&& let Some(name_node) = child.child_by_field_name("name")
{
let name = node_text(&name_node, source);
if name == target_name {
if let Some(value_node) = child.child_by_field_name("value") {
let vkind = value_node.kind();
if vkind == "arrow_function"
|| vkind == "function"
|| vkind == "function_expression"
{
results.push(ResolvedSymbol {
name: name.to_string(),
start_line: node.start_position().row as u32 + 1,
end_line: node.end_position().row as u32 + 1,
parent_name: current_parent.map(String::from),
});
}
}
}
}
}
}
"pair" => {
if let Some(key_node) = node.child_by_field_name("key") {
let key = node_text(&key_node, source);
if key == target_name
&& let Some(value_node) = node.child_by_field_name("value")
{
let vkind = value_node.kind();
if vkind == "arrow_function"
|| vkind == "function"
|| vkind == "function_expression"
{
results.push(ResolvedSymbol {
name: key.to_string(),
start_line: node.start_position().row as u32 + 1,
end_line: node.end_position().row as u32 + 1,
parent_name: current_parent.map(String::from),
});
}
}
}
}
_ => {}
}
if !descended_with_new_parent {
let mut cursor = node.walk();
let children: Vec<_> = node.children(&mut cursor).collect();
for child in children.into_iter().rev() {
stack.push((child, parent.clone()));
}
}
}
results
}
fn extract_impl_type_name(node: &tree_sitter::Node, source: &[u8]) -> Option<String> {
if let Some(type_node) = node.child_by_field_name("type") {
return Some(extract_type_identifier(&type_node, source));
}
None
}
fn extract_type_identifier(node: &tree_sitter::Node, source: &[u8]) -> String {
match node.kind() {
"type_identifier" | "identifier" => node_text(node, source).to_string(),
"generic_type" | "scoped_type_identifier" => {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "type_identifier" || child.kind() == "identifier" {
return node_text(&child, source).to_string();
}
}
node_text(node, source).to_string()
}
_ => node_text(node, source).to_string(),
}
}
fn extract_go_receiver_type(node: &tree_sitter::Node, source: &[u8]) -> Option<String> {
let params = node.child_by_field_name("receiver")?;
let mut cursor = params.walk();
for child in params.children(&mut cursor) {
if child.kind() == "parameter_declaration" {
if let Some(type_node) = child.child_by_field_name("type") {
let text = node_text(&type_node, source);
let trimmed = text.trim_start_matches('*');
return Some(trimmed.to_string());
}
}
}
None
}
fn node_text<'a>(node: &tree_sitter::Node, source: &'a [u8]) -> &'a str {
let start = node.start_byte();
let end = node.end_byte();
std::str::from_utf8(&source[start..end]).unwrap_or("")
}
#[cfg(all(test, feature = "lang-rust"))]
mod tests {
use super::find_definitions;
#[test]
fn deeply_nested_rust_modules_does_not_stack_overflow() {
let depth = 2000usize;
let mut s = String::new();
for i in 0..depth {
s.push_str(&format!("mod m{i} {{\n"));
}
s.push_str("fn target() {}\n");
for _ in 0..depth {
s.push_str("}\n");
}
let mut parser = tree_sitter::Parser::new();
parser
.set_language(&tree_sitter_rust::LANGUAGE.into())
.expect("set rust language");
let tree = parser.parse(&s, None).expect("parse");
let source = s.into_bytes();
let handle = std::thread::Builder::new()
.stack_size(128 * 1024)
.spawn(move || find_definitions(&tree.root_node(), &source, "target"))
.expect("spawn");
let results = handle
.join()
.expect("find_definitions must not stack-overflow on deeply-nested input");
assert!(
results.iter().any(|r| r.name == "target"),
"deep target fn must be returned, not silently dropped by a depth cap; got {results:?}"
);
}
}