use crate::indexer::languages::Language;
use tree_sitter::Node;
pub struct Cpp {}
impl Language for Cpp {
fn name(&self) -> &'static str {
"cpp"
}
fn get_ts_language(&self) -> tree_sitter::Language {
tree_sitter_cpp::LANGUAGE.into()
}
fn get_meaningful_kinds(&self) -> Vec<&'static str> {
vec![
"function_definition",
"declaration", "class_specifier",
"struct_specifier",
"enum_specifier",
"namespace_definition",
"preproc_include", ]
}
fn extract_symbols(&self, node: Node, contents: &str) -> Vec<String> {
let mut symbols = Vec::new();
match node.kind() {
"function_definition" => {
for child in node.children(&mut node.walk()) {
if child.kind() == "function_declarator" {
for decl_child in child.children(&mut child.walk()) {
if decl_child.kind() == "identifier" {
if let Ok(name) = decl_child.utf8_text(contents.as_bytes()) {
symbols.push(name.to_string());
}
break;
}
}
break;
}
}
for child in node.children(&mut node.walk()) {
if child.kind() == "compound_statement" {
self.extract_cpp_variables(child, contents, &mut symbols);
break;
}
}
}
"declaration" => {
let mut found_function = false;
for child in node.children(&mut node.walk()) {
if child.kind() == "function_declarator" {
found_function = true;
for decl_child in child.children(&mut child.walk()) {
if decl_child.kind() == "identifier" {
if let Ok(name) = decl_child.utf8_text(contents.as_bytes()) {
symbols.push(name.to_string());
}
break;
}
}
break;
}
}
if !found_function {
for child in node.children(&mut node.walk()) {
if child.kind() == "init_declarator" || child.kind() == "declarator" {
for decl_child in child.children(&mut child.walk()) {
if decl_child.kind() == "identifier" {
if let Ok(name) = decl_child.utf8_text(contents.as_bytes()) {
symbols.push(name.to_string());
}
break;
}
}
}
}
}
}
"class_specifier" | "struct_specifier" | "enum_specifier" => {
for child in node.children(&mut node.walk()) {
if child.kind() == "name" || child.kind() == "type_identifier" {
if let Ok(name) = child.utf8_text(contents.as_bytes()) {
symbols.push(name.to_string());
}
break;
}
}
self.extract_cpp_members(node, contents, &mut symbols);
}
"namespace_definition" => {
for child in node.children(&mut node.walk()) {
if child.kind() == "identifier" || child.kind() == "namespace_identifier" {
if let Ok(name) = child.utf8_text(contents.as_bytes()) {
symbols.push(name.to_string());
}
break;
}
}
}
_ => self.extract_identifiers(node, contents, &mut symbols),
}
symbols.sort();
symbols.dedup();
symbols
}
fn extract_identifiers(&self, node: Node, contents: &str, symbols: &mut Vec<String>) {
let kind = node.kind();
if kind == "identifier" || kind == "type_identifier" || kind == "field_identifier" {
if let Ok(text) = node.utf8_text(contents.as_bytes()) {
let t = text.trim();
if !t.is_empty() && !symbols.contains(&t.to_string()) {
symbols.push(t.to_string());
}
}
}
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
self.extract_identifiers(cursor.node(), contents, symbols);
if !cursor.goto_next_sibling() {
break;
}
}
}
}
fn are_node_types_equivalent(&self, type1: &str, type2: &str) -> bool {
if type1 == type2 {
return true;
}
let semantic_groups = [
&["function_definition"] as &[&str],
&["class_specifier", "struct_specifier", "enum_specifier"],
&["namespace_definition"],
&["template_declaration"],
&["declaration"],
&[
"preproc_include",
"preproc_define",
"preproc_ifdef",
"preproc_ifndef",
],
];
for group in &semantic_groups {
let contains_type1 = group.contains(&type1);
let contains_type2 = group.contains(&type2);
if contains_type1 && contains_type2 {
return true;
}
}
false
}
fn get_node_type_description(&self, node_type: &str) -> &'static str {
match node_type {
"function_definition" => "function declarations",
"class_specifier" => "class declarations",
"struct_specifier" => "struct declarations",
"enum_specifier" => "enum declarations",
"namespace_definition" => "namespace declarations",
"template_declaration" => "template declarations",
"declaration" => "variable declarations",
"preproc_include" | "preproc_define" | "preproc_ifdef" | "preproc_ifndef" => {
"preprocessor directives"
}
_ => "declarations",
}
}
fn extract_imports_exports(&self, node: Node, contents: &str) -> (Vec<String>, Vec<String>) {
let mut imports = Vec::new();
let mut exports = Vec::new();
match node.kind() {
"preproc_include" => {
if let Ok(include_text) = node.utf8_text(contents.as_bytes()) {
if let Some(header) = Self::parse_cpp_include(include_text) {
imports.push(header);
}
}
}
"function_definition" | "declaration" => {
if node.kind() == "function_definition" {
if let Some(function_name) = self.extract_function_name(node, contents) {
exports.push(function_name);
}
} else {
let mut is_function_declaration = false;
for child in node.children(&mut node.walk()) {
if child.kind() == "function_declarator" {
is_function_declaration = true;
if let Some(function_name) = self.extract_function_name(node, contents)
{
exports.push(function_name);
}
break;
}
}
if !is_function_declaration {
if let Some(var_names) = self.extract_variable_names(node, contents) {
exports.extend(var_names);
}
}
}
}
"class_specifier" | "struct_specifier" | "enum_specifier" => {
if let Some(type_name) = self.extract_type_name(node, contents) {
exports.push(type_name);
}
}
"namespace_definition" => {
if let Some(namespace_name) = self.extract_namespace_name(node, contents) {
exports.push(namespace_name);
}
}
"template_declaration" => {
for child in node.children(&mut node.walk()) {
match child.kind() {
"function_definition" | "class_specifier" | "struct_specifier" => {
if let Some(template_name) = self.extract_template_name(child, contents)
{
exports.push(format!("template<{}>", template_name));
}
}
_ => {}
}
}
}
"preproc_define" => {
if let Some(macro_name) = self.extract_macro_name(node, contents) {
exports.push(macro_name);
}
}
"type_definition" => {
if let Some(typedef_name) = self.extract_typedef_name(node, contents) {
exports.push(typedef_name);
}
}
_ => {
}
}
(imports, exports)
}
fn resolve_import(
&self,
import_path: &str,
source_file: &str,
all_files: &[String],
) -> Option<String> {
use super::resolution_utils::FileRegistry;
let registry = FileRegistry::new(all_files);
if import_path.starts_with('"') && import_path.ends_with('"') {
let header_name = &import_path[1..import_path.len() - 1];
self.resolve_local_include(header_name, source_file, ®istry)
} else if import_path.starts_with('<') && import_path.ends_with('>') {
let header_name = &import_path[1..import_path.len() - 1];
registry.find_exact_file(header_name)
} else {
if let Some(local_result) =
self.resolve_local_include(import_path, source_file, ®istry)
{
Some(local_result)
} else if let Some(exact_result) = registry.find_exact_file(import_path) {
Some(exact_result)
} else {
let include_dirs = ["include", "inc", "headers", "lib"];
for include_dir in &include_dirs {
let full_path = std::path::Path::new(include_dir).join(import_path);
if let Some(result) = registry.find_exact_file(&full_path.to_string_lossy()) {
return Some(result);
}
}
None
}
}
}
fn get_file_extensions(&self) -> Vec<&'static str> {
vec!["cpp", "cc", "cxx", "c++", "c", "h", "hpp"]
}
}
impl Cpp {
#[allow(clippy::only_used_in_recursion)]
fn extract_cpp_variables(&self, node: Node, contents: &str, symbols: &mut Vec<String>) {
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
let child = cursor.node();
match child.kind() {
"declaration" => {
for decl_child in child.children(&mut child.walk()) {
if decl_child.kind() == "init_declarator"
|| decl_child.kind() == "declarator"
{
for init_child in decl_child.children(&mut decl_child.walk()) {
if init_child.kind() == "identifier" {
if let Ok(name) = init_child.utf8_text(contents.as_bytes())
{
if !symbols.contains(&name.to_string()) {
symbols.push(name.to_string());
}
}
break;
}
}
}
}
}
"compound_statement" => {
self.extract_cpp_variables(child, contents, symbols);
}
"if_statement" | "for_statement" | "while_statement" | "do_statement" => {
for stmt_child in child.children(&mut child.walk()) {
if stmt_child.kind() == "compound_statement" {
self.extract_cpp_variables(stmt_child, contents, symbols);
}
}
}
_ => {}
}
if !cursor.goto_next_sibling() {
break;
}
}
}
}
fn extract_cpp_members(&self, node: Node, contents: &str, symbols: &mut Vec<String>) {
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
let child = cursor.node();
match child.kind() {
"field_declaration" => {
for field_child in child.children(&mut child.walk()) {
if field_child.kind() == "field_identifier"
|| field_child.kind() == "identifier"
{
if let Ok(name) = field_child.utf8_text(contents.as_bytes()) {
if !symbols.contains(&name.to_string()) {
symbols.push(name.to_string());
}
}
}
}
}
"function_definition" => {
for fn_child in child.children(&mut child.walk()) {
if fn_child.kind() == "function_declarator" {
for decl_child in fn_child.children(&mut fn_child.walk()) {
if decl_child.kind() == "identifier" {
if let Ok(name) = decl_child.utf8_text(contents.as_bytes())
{
if !symbols.contains(&name.to_string()) {
symbols.push(name.to_string());
}
}
break;
}
}
break;
}
}
}
"enum_specifier" => {
for enum_child in child.children(&mut child.walk()) {
if enum_child.kind() == "enumerator_list" {
for enum_list_child in enum_child.children(&mut enum_child.walk()) {
if enum_list_child.kind() == "enumerator" {
for enumerator_child in
enum_list_child.children(&mut enum_list_child.walk())
{
if enumerator_child.kind() == "identifier" {
if let Ok(name) =
enumerator_child.utf8_text(contents.as_bytes())
{
if !symbols.contains(&name.to_string()) {
symbols.push(name.to_string());
}
}
break;
}
}
}
}
}
}
}
_ => {}
}
if !cursor.goto_next_sibling() {
break;
}
}
}
}
fn parse_cpp_include(include_text: &str) -> Option<String> {
let trimmed = include_text.trim();
if trimmed.starts_with("#include") {
let include_part = trimmed.strip_prefix("#include").unwrap().trim();
if include_part.starts_with('<') && include_part.ends_with('>') {
return Some(include_part[1..include_part.len() - 1].to_string());
}
if include_part.starts_with('"') && include_part.ends_with('"') {
return Some(include_part[1..include_part.len() - 1].to_string());
}
}
None
}
}
impl Cpp {
fn extract_function_name(&self, node: Node, contents: &str) -> Option<String> {
for child in node.children(&mut node.walk()) {
if child.kind() == "function_declarator" {
for decl_child in child.children(&mut child.walk()) {
if decl_child.kind() == "identifier" {
if let Ok(name) = decl_child.utf8_text(contents.as_bytes()) {
return Some(name.to_string());
}
}
}
}
}
None
}
fn extract_variable_names(&self, node: Node, contents: &str) -> Option<Vec<String>> {
let mut var_names = Vec::new();
for child in node.children(&mut node.walk()) {
if child.kind() == "init_declarator" || child.kind() == "declarator" {
for decl_child in child.children(&mut child.walk()) {
if decl_child.kind() == "identifier" {
if let Ok(name) = decl_child.utf8_text(contents.as_bytes()) {
var_names.push(name.to_string());
}
}
}
}
}
if var_names.is_empty() {
None
} else {
Some(var_names)
}
}
fn extract_type_name(&self, node: Node, contents: &str) -> Option<String> {
for child in node.children(&mut node.walk()) {
if child.kind() == "type_identifier" || child.kind() == "identifier" {
if let Ok(name) = child.utf8_text(contents.as_bytes()) {
return Some(name.to_string());
}
}
}
None
}
fn extract_namespace_name(&self, node: Node, contents: &str) -> Option<String> {
for child in node.children(&mut node.walk()) {
if child.kind() == "identifier" || child.kind() == "namespace_identifier" {
if let Ok(name) = child.utf8_text(contents.as_bytes()) {
return Some(name.to_string());
}
}
}
None
}
fn extract_template_name(&self, node: Node, contents: &str) -> Option<String> {
match node.kind() {
"function_definition" => self.extract_function_name(node, contents),
"class_specifier" | "struct_specifier" => self.extract_type_name(node, contents),
_ => None,
}
}
fn extract_macro_name(&self, node: Node, contents: &str) -> Option<String> {
for child in node.children(&mut node.walk()) {
if child.kind() == "identifier" {
if let Ok(name) = child.utf8_text(contents.as_bytes()) {
return Some(name.to_string());
}
}
}
None
}
fn extract_typedef_name(&self, node: Node, contents: &str) -> Option<String> {
for child in node.children(&mut node.walk()) {
if child.kind() == "type_identifier" {
if let Ok(name) = child.utf8_text(contents.as_bytes()) {
return Some(name.to_string());
}
}
}
None
}
fn resolve_local_include(
&self,
header_name: &str,
source_file: &str,
registry: &super::resolution_utils::FileRegistry,
) -> Option<String> {
let source_path = std::path::Path::new(source_file);
let source_dir = source_path.parent()?;
let header_path = source_dir.join(header_name);
let normalized_path =
crate::utils::path::PathNormalizer::normalize_path(&header_path.to_string_lossy());
registry.find_exact_file(&normalized_path)
}
}