use crate::language::c::{C_FAMILY_IMPORT_QUERY_STR, C_FAMILY_REFERENCE_QUERY_STR};
use crate::language::{DefaultVisibility, LanguageSpec};
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
fn resolve_cpp_import(raw: &str, source_dir: &Path, _project_root: &Path) -> Option<PathBuf> {
crate::language::import_resolver::resolve_c_family_import(raw, source_dir)
}
static CPP_QUERY: LazyLock<tree_sitter::Query> = LazyLock::new(|| {
crate::language::common::compile_query(
&tree_sitter_cpp::LANGUAGE.into(),
r#"
(function_definition
declarator: (function_declarator
declarator: (_) @name
parameters: (parameter_list) @signature
)
) @kind.function
(class_specifier
name: (type_identifier) @name) @kind.class
(struct_specifier
name: (type_identifier) @name) @kind.struct
(enum_specifier
name: (type_identifier) @name) @kind.enum
(namespace_definition
name: (namespace_identifier) @name) @kind.namespace
(type_definition
declarator: (type_identifier) @name) @kind.type_alias
"#,
"C++",
)
});
fn cpp_query() -> &'static tree_sitter::Query {
&CPP_QUERY
}
static CPP_IMPORT_REF_QUERY: LazyLock<tree_sitter::Query> = LazyLock::new(|| {
crate::language::common::compile_query(
&tree_sitter_cpp::LANGUAGE.into(),
&format!(
"{}\n{}",
C_FAMILY_IMPORT_QUERY_STR, C_FAMILY_REFERENCE_QUERY_STR
),
"C++ combined import+ref",
)
});
fn cpp_import_ref_query() -> &'static tree_sitter::Query {
&CPP_IMPORT_REF_QUERY
}
pub(crate) const CPP_SPEC: LanguageSpec = LanguageSpec {
extensions: &["cc", "cpp", "cxx"],
grammar_fn: || tree_sitter_cpp::LANGUAGE.into(),
query_fn: cpp_query,
import_path_resolver: resolve_cpp_import,
import_ref_query_fn: cpp_import_ref_query,
class_like_parents: &["class_specifier", "struct_specifier"],
ancestor_visibility_rules: &[],
visibility_from_name: None,
import_statement_kinds: &["preproc_include"],
default_visibility: DefaultVisibility::PrivateByDefault,
doc_comment_config: Some(crate::language::C_LIKE_DOC_COMMENT),
};
#[cfg(test)]
mod tests {
use crate::language::{LangId, extract_symbols_for, grammar_for};
use crate::model::SymbolKind;
fn parse(source: &[u8]) -> tree_sitter::Tree {
let mut parser = tree_sitter::Parser::new();
parser.set_language(&grammar_for(LangId::Cpp)).unwrap();
parser.parse(source, None).unwrap()
}
#[test]
fn extract_class() {
let src = b"class Foo { public: void bar() {} };";
let tree = parse(src);
let symbols = extract_symbols_for(LangId::Cpp, &tree, src);
let foo = symbols.iter().find(|s| s.name == "Foo").unwrap();
assert!(matches!(foo.kind, SymbolKind::Class));
}
#[test]
fn extract_method() {
let src = b"class Foo { public: void bar() {} };";
let tree = parse(src);
let symbols = extract_symbols_for(LangId::Cpp, &tree, src);
let bar = symbols.iter().find(|s| s.name == "bar").unwrap();
assert!(matches!(bar.kind, SymbolKind::Method));
}
#[test]
fn extract_namespace() {
let src = b"namespace math { int add(int a, int b) { return a + b; } }";
let tree = parse(src);
let symbols = extract_symbols_for(LangId::Cpp, &tree, src);
let ns = symbols.iter().find(|s| s.name == "math").unwrap();
assert!(matches!(ns.kind, SymbolKind::Namespace));
}
#[test]
fn cpp_docstring_extraction() {
let src = b"/** C++ doc comment. */\nint documented() {}";
let tree = parse(src);
let symbols = extract_symbols_for(LangId::Cpp, &tree, src);
let func = symbols.iter().find(|s| s.name == "documented").unwrap();
assert!(func.docstring.is_some(), "documented should have docstring");
assert!(func.docstring.as_ref().unwrap().contains("C++ doc comment"));
}
#[test]
fn cpp_insta_snapshot() {
let src = std::fs::read_to_string(
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/cpp/classes.cpp"),
)
.unwrap();
let tree = parse(src.as_bytes());
let symbols = extract_symbols_for(LangId::Cpp, &tree, src.as_bytes());
insta::assert_json_snapshot!(symbols);
}
}