use crate::models::{EntityKind, ParsedEntity, ReferenceIntent};
use crate::pipeline::parser::utils::node_text;
use tree_sitter::Node;
pub(crate) fn reclassify_methods_in_impl_blocks(
root: Node<'_>,
source: &[u8],
entities: &mut [ParsedEntity],
) {
let mut method_lines = std::collections::HashSet::new();
collect_method_lines(&root, &mut method_lines);
if method_lines.is_empty() {
return;
}
let mut class_contexts: Vec<crate::pipeline::parser::context::ClassContext> = Vec::new();
collect_impl_class_contexts(&root, source, &mut class_contexts);
for entity in entities.iter_mut() {
if entity.kind == EntityKind::RustFunction && method_lines.contains(&entity.start_line) {
entity.kind = EntityKind::RustMethod;
let (new_fqn, new_enclosing) =
crate::pipeline::parser::context::compute_fqn_and_context(
&entity.name,
&EntityKind::RustMethod,
entity.start_line,
"rust",
&class_contexts,
);
entity.fqn = new_fqn;
if entity.enclosing_class.is_none() {
entity.enclosing_class = new_enclosing;
}
}
}
}
fn collect_impl_class_contexts(
node: &Node<'_>,
source: &[u8],
contexts: &mut Vec<crate::pipeline::parser::context::ClassContext>,
) {
if node.kind() == "impl_item"
&& let Some(self_type) = extract_impl_self_type(*node, source)
{
contexts.push(crate::pipeline::parser::context::ClassContext {
name: self_type,
start_line: node.start_position().row + 1,
end_line: node.end_position().row + 1,
});
}
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
collect_impl_class_contexts(&child, source, contexts);
}
}
fn collect_method_lines(node: &Node<'_>, method_lines: &mut std::collections::HashSet<usize>) {
if node.kind() == "impl_item" {
let mut child = node.child(0);
while let Some(c) = child {
if c.kind() == "function_item" {
let line = c.start_position().row + 1;
method_lines.insert(line);
} else {
collect_method_lines_in_scope(&c, method_lines);
}
child = c.next_sibling();
}
} else {
let mut child = node.child(0);
while let Some(c) = child {
collect_method_lines(&c, method_lines);
child = c.next_sibling();
}
}
}
fn collect_method_lines_in_scope(
node: &Node<'_>,
method_lines: &mut std::collections::HashSet<usize>,
) {
if node.kind() == "function_item" {
let line = node.start_position().row + 1;
method_lines.insert(line);
}
let mut child = node.child(0);
while let Some(c) = child {
collect_method_lines_in_scope(&c, method_lines);
child = c.next_sibling();
}
}
pub(crate) fn collect_rust_trait_implementations(
root: Node<'_>,
source: &[u8],
entities: &mut [ParsedEntity],
_file_path: &str,
_repo_name: &str,
) {
let mut implementations: Vec<(usize, String, String)> = Vec::new();
collect_impl_nodes(&root, source, &mut implementations);
for (line, target_type, trait_name) in implementations {
if let Some(target_entity) = entities.iter_mut().find(|e| {
e.name == target_type
&& matches!(
e.kind,
EntityKind::RustStruct | EntityKind::RustEnum | EntityKind::RustUnion
)
}) {
target_entity
.reference_intents
.push(ReferenceIntent::Implements {
interface: trait_name,
line,
});
}
}
}
pub(crate) fn extract_impl_self_type(node: Node<'_>, source: &[u8]) -> Option<String> {
if node.kind() != "impl_item" {
return None;
}
let mut saw_for = false;
let mut first_type: Option<String> = None;
let mut last_type: Option<String> = None;
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"for" => {
saw_for = true;
}
"lifetime" | "where_clause" | "declaration_list" | "attribute_item" | "token_tree" => {
continue;
}
_ => {
if let Some(name) = extract_type_base_name(child, source) {
if first_type.is_none() {
first_type = Some(name.clone());
}
last_type = Some(name);
}
}
}
}
if saw_for {
last_type.or(first_type)
} else {
first_type
}
}
fn extract_type_base_name(node: Node<'_>, source: &[u8]) -> Option<String> {
match node.kind() {
"type_identifier" => Some(node_text(node, source).to_string()),
"scoped_type_identifier" => {
let mut last: Option<String> = None;
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "type_identifier" {
last = Some(node_text(child, source).to_string());
}
}
last
}
"generic_type" => node
.child_by_field_name("type")
.and_then(|t| extract_type_base_name(t, source)),
"reference_type" | "pointer_type" | "array_type" | "slice_type" | "tuple_type" => {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if let Some(name) = extract_type_base_name(child, source)
&& !name.is_empty()
{
return Some(name);
}
}
None
}
_ => None,
}
}
fn collect_impl_nodes(
node: &Node<'_>,
source: &[u8],
implementations: &mut Vec<(usize, String, String)>,
) {
if node.kind() == "impl_item" {
let line = node.start_position().row + 1;
let impl_text = node_text(*node, source);
if impl_text.contains(" for ") {
let mut type_identifiers: Vec<String> = Vec::new();
let mut child = node.child(0);
while let Some(c) = child {
if c.kind() == "type_identifier" {
type_identifiers.push(node_text(c, source).to_string());
} else if c.kind() == "generic_type" {
if let Some(name_node) = c.child_by_field_name("type")
&& name_node.kind() == "type_identifier"
{
type_identifiers.push(node_text(name_node, source).to_string());
}
}
child = c.next_sibling();
}
if type_identifiers.len() >= 2 {
let trait_name = type_identifiers[0].clone();
let target_type = type_identifiers[1].clone();
implementations.push((line, target_type, trait_name));
}
}
}
let mut child = node.child(0);
while let Some(c) = child {
collect_impl_nodes(&c, source, implementations);
child = c.next_sibling();
}
}