Skip to main content

brokk_bifrost_cpp/
adapter.rs

1//! The C++ answers behind `CppAdapter`.
2//!
3//! `LanguageAdapter` is analysis-owned, so the trait impl itself stays in
4//! `analyzer/cpp/adapter.rs`; every answer it gives comes from here or from
5//! [`crate::test_detection`] and [`crate::queries`].
6
7use crate::declarations::{CppVisitor, collect_cpp_identifiers, recover_quoted_includes};
8use brokk_bifrost_core::analyzer::ProjectFile;
9use brokk_bifrost_core::analyzer::cognitive_complexity;
10use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
11use brokk_bifrost_core::hash::HashMap;
12use std::sync::LazyLock;
13use tree_sitter::{Node, Tree};
14
15/// The file extension `CppAdapter` reports. `Language::Cpp` also covers `.c`,
16/// `.cc`, `.cxx` and the header spellings; this is only the canonical one.
17pub const CPP_FILE_EXTENSION: &str = "cpp";
18
19/// Tree-sitter node-kind mapping used by the cognitive-complexity scorer for
20/// C++. Node names are from the tree-sitter-cpp grammar.
21pub static CPP_COGNITIVE_CONFIG: LazyLock<cognitive_complexity::Config> =
22    LazyLock::new(|| cognitive_complexity::Config {
23        if_types: &["if_statement"],
24        loop_types: &["for_statement", "while_statement", "do_statement"],
25        catch_types: &["catch_clause"],
26        conditional_types: &["conditional_expression"],
27        case_types: &["case_statement"],
28        binary_types: &["binary_expression"],
29        logical_operators: &["&&", "||", "and", "or"],
30        jump_types: &["break_statement", "continue_statement"],
31        named_function_boundary_types: &["function_definition"],
32        anonymous_function_types: &["lambda_expression"],
33        else_clause_types: &["else_clause"],
34        default_case_predicate: Some(cpp_is_default_case),
35        ..cognitive_complexity::Config::empty()
36    });
37
38fn cpp_is_default_case(node: Node<'_>, _source: &str) -> bool {
39    node.child_by_field_name("value").is_none()
40}
41
42pub fn parse_cpp_file(file: &ProjectFile, source: &str, tree: &Tree) -> ParsedFile {
43    let mut parsed = ParsedFile::new(String::new());
44    let root = tree.root_node();
45
46    collect_cpp_identifiers(root, source, &mut parsed.type_identifiers);
47
48    let mut visitor = CppVisitor {
49        file,
50        source,
51        parsed: &mut parsed,
52        recovered_class_sibling_scopes: HashMap::default(),
53        consumed_fragment_regions: Vec::new(),
54    };
55    visitor.visit_container(root, "", None, None, None, Vec::new());
56    recover_quoted_includes(source, &mut parsed);
57
58    parsed
59}
60
61pub fn cpp_extract_call_receiver(reference: &str) -> Option<String> {
62    let trimmed = reference.trim();
63    let before_args = trimmed
64        .split_once('(')
65        .map(|(head, _)| head)
66        .unwrap_or(trimmed);
67    before_args
68        .rsplit_once("::")
69        .or_else(|| before_args.rsplit_once('.'))
70        .map(|(receiver, _)| receiver.to_string())
71}