brokk_bifrost_cpp/
adapter.rs1use 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::model::{Language, LanguageDialect};
11use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
12use brokk_bifrost_core::hash::HashMap;
13use std::sync::LazyLock;
14use tree_sitter::{Node, Tree};
15
16pub const CPP_FILE_EXTENSION: &str = "cpp";
19
20pub static CPP_COGNITIVE_CONFIG: LazyLock<cognitive_complexity::Config> =
23 LazyLock::new(|| cognitive_complexity::Config {
24 if_types: &["if_statement"],
25 loop_types: &["for_statement", "while_statement", "do_statement"],
26 catch_types: &["catch_clause"],
27 conditional_types: &["conditional_expression"],
28 case_types: &["case_statement"],
29 binary_types: &["binary_expression"],
30 logical_operators: &["&&", "||", "and", "or"],
31 jump_types: &["break_statement", "continue_statement"],
32 named_function_boundary_types: &["function_definition"],
33 anonymous_function_types: &["lambda_expression"],
34 else_clause_types: &["else_clause"],
35 default_case_predicate: Some(cpp_is_default_case),
36 ..cognitive_complexity::Config::empty()
37 });
38
39fn cpp_is_default_case(node: Node<'_>, _source: &str) -> bool {
40 node.child_by_field_name("value").is_none()
41}
42
43pub fn parse_cpp_file(file: &ProjectFile, source: &str, tree: &Tree) -> ParsedFile {
45 parse_cpp_file_in_dialect(
46 file,
47 source,
48 tree,
49 LanguageDialect::for_path(Language::Cpp, file.rel_path()),
50 )
51}
52
53pub fn parse_cpp_file_in_dialect(
63 file: &ProjectFile,
64 source: &str,
65 tree: &Tree,
66 dialect: LanguageDialect,
67) -> ParsedFile {
68 let mut parsed = ParsedFile::new(String::new());
69 let root = tree.root_node();
70
71 collect_cpp_identifiers(root, source, &mut parsed.type_identifiers);
72
73 let mut visitor = CppVisitor {
74 file,
75 source,
76 parsed: &mut parsed,
77 c_tag_semantics: dialect == LanguageDialect::CppC,
78 recovered_class_sibling_scopes: HashMap::default(),
79 consumed_fragment_regions: Vec::new(),
80 };
81 visitor.visit_container(root, "", None, None, None, Vec::new());
82 recover_quoted_includes(source, &mut parsed);
83 parsed.finalize_deferred_replacements();
84
85 parsed
86}
87
88pub fn cpp_projections_differ(left: &ParsedFile, right: &ParsedFile) -> bool {
98 left.declarations() != right.declarations()
99 || left.top_level_declarations != right.top_level_declarations
100 || left.definition_lookup_units != right.definition_lookup_units
101 || left.children != right.children
102 || left.ranges != right.ranges
103 || left.signatures != right.signatures
104 || left.type_aliases != right.type_aliases
105}
106
107pub fn cpp_extract_call_receiver(reference: &str) -> Option<String> {
108 let trimmed = reference.trim();
109 let before_args = trimmed
110 .split_once('(')
111 .map(|(head, _)| head)
112 .unwrap_or(trimmed);
113 before_args
114 .rsplit_once("::")
115 .or_else(|| before_args.rsplit_once('.'))
116 .map(|(receiver, _)| receiver.to_string())
117}