1use brokk_bifrost_core::analyzer::Language;
4use brokk_bifrost_core::analyzer::structural::adapter_helpers::{
5 attach_positional_argument_roles, attach_role_with_derived_name, attach_terminal_callee,
6 first_named_child,
7};
8use brokk_bifrost_core::analyzer::structural::edges::{
9 INVERSE_REFERENCE_EDGE_SUPPORT, ReferenceEdgeSupport,
10};
11use brokk_bifrost_core::analyzer::structural::facts::Span;
12use brokk_bifrost_core::analyzer::structural::kinds::{NormalizedKind, Role};
13use brokk_bifrost_core::analyzer::structural::materialization::{
14 CPP_MATERIALIZATION_SUPPORT, DeclarationMaterializationSupport,
15};
16use brokk_bifrost_core::analyzer::structural::occurrences::{
17 NO_OCCURRENCE_ROLE_SUPPORT, OccurrenceRoleSupport,
18};
19use brokk_bifrost_core::analyzer::structural::resolution::{
20 LexicalEnvironmentSupport, NO_LEXICAL_ENVIRONMENT_SUPPORT,
21};
22use brokk_bifrost_core::analyzer::structural::routes::{
23 IdentityAxis, IdentityRouteSupport, RouteHopKind,
24};
25use brokk_bifrost_core::analyzer::structural::spec::{RoleSink, StructuralSpec};
26use tree_sitter::Node;
27
28#[derive(Debug, Default)]
29pub struct CppStructuralSpec;
30
31pub static CPP_STRUCTURAL_SPEC: CppStructuralSpec = CppStructuralSpec;
32
33pub const CPP_KIND_TABLE: &[(&str, NormalizedKind)] = &[
34 ("call_expression", NormalizedKind::Call),
35 ("new_expression", NormalizedKind::Call),
36 ("field_expression", NormalizedKind::FieldAccess),
37 ("function_definition", NormalizedKind::Function),
38 ("lambda_expression", NormalizedKind::Lambda),
39 ("class_specifier", NormalizedKind::Class),
40 ("struct_specifier", NormalizedKind::Class),
41 ("union_specifier", NormalizedKind::Class),
42 ("alias_declaration", NormalizedKind::Declaration),
43 ("assignment_expression", NormalizedKind::Assignment),
44 ("init_declarator", NormalizedKind::Assignment),
45 ("preproc_include", NormalizedKind::Import),
46 ("identifier", NormalizedKind::Identifier),
47 ("field_identifier", NormalizedKind::Identifier),
48 ("namespace_identifier", NormalizedKind::Identifier),
49 ("qualified_identifier", NormalizedKind::Identifier),
50 ("type_identifier", NormalizedKind::Identifier),
51 ("template_function", NormalizedKind::Identifier),
52 ("template_method", NormalizedKind::Identifier),
53 ("template_type", NormalizedKind::Identifier),
54 ("dependent_name", NormalizedKind::Identifier),
55 ("destructor_name", NormalizedKind::Identifier),
56 ("operator_name", NormalizedKind::Identifier),
57 ("primitive_type", NormalizedKind::Identifier),
58 ("char_literal", NormalizedKind::StringLiteral),
59 ("string_literal", NormalizedKind::StringLiteral),
60 ("raw_string_literal", NormalizedKind::StringLiteral),
61 ("number_literal", NormalizedKind::NumericLiteral),
62 ("true", NormalizedKind::BooleanLiteral),
63 ("false", NormalizedKind::BooleanLiteral),
64 ("null", NormalizedKind::NullLiteral),
65 ("return_statement", NormalizedKind::Return),
66 ("throw_statement", NormalizedKind::Throw),
67 ("catch_clause", NormalizedKind::Catch),
68 ("if_statement", NormalizedKind::If),
69 ("for_statement", NormalizedKind::Loop),
70 ("while_statement", NormalizedKind::WhileLoop),
71 ("do_statement", NormalizedKind::WhileLoop),
72];
73
74pub fn is_recovered_designator_init_declarator(node: Node<'_>) -> bool {
82 if node.kind() != "init_declarator" {
83 return false;
84 }
85 let Some(identifier) = node.child_by_field_name("declarator") else {
86 return false;
87 };
88 if identifier.kind() != "identifier" || identifier.is_missing() {
89 return false;
90 }
91 let Some(previous) = node.prev_named_sibling() else {
92 return false;
93 };
94 if previous.kind() != "ERROR"
95 || previous.named_child_count() != 0
96 || previous.child_count() != 1
97 || previous.end_byte() != node.start_byte()
98 {
99 return false;
100 }
101 previous.child(0).is_some_and(|child| {
102 child.kind() == "."
103 && !child.is_named()
104 && child.start_byte() == previous.start_byte()
105 && child.end_byte() == previous.end_byte()
106 })
107}
108
109fn last_named_field_child<'tree>(node: Node<'tree>, field: &str) -> Option<Node<'tree>> {
110 let mut cursor = node.walk();
111 node.children_by_field_name(field, &mut cursor)
112 .filter(|child| child.is_named())
113 .last()
114}
115
116fn declarator_name_node<'tree>(declarator: Node<'tree>) -> Option<Node<'tree>> {
117 let mut current = declarator;
118 loop {
119 match current.kind() {
120 "identifier"
121 | "field_identifier"
122 | "namespace_identifier"
123 | "type_identifier"
124 | "destructor_name"
125 | "operator_name"
126 | "primitive_type" => return Some(current),
127 "qualified_identifier" => current = last_named_field_child(current, "name")?,
128 "dependent_name" | "template_function" | "template_method" | "template_type" => {
129 current = current.child_by_field_name("name")?;
130 }
131 "function_declarator"
132 | "pointer_declarator"
133 | "array_declarator"
134 | "init_declarator" => current = current.child_by_field_name("declarator")?,
135 "reference_declarator" | "parenthesized_declarator" => {
136 current = first_named_child(current)?;
137 }
138 _ => return None,
139 }
140 }
141}
142
143fn expression_name_node<'tree>(expression: Node<'tree>) -> Option<Node<'tree>> {
144 let mut current = expression;
145 loop {
146 match current.kind() {
147 "identifier"
148 | "field_identifier"
149 | "namespace_identifier"
150 | "type_identifier"
151 | "destructor_name"
152 | "operator_name"
153 | "primitive_type"
154 | "this" => return Some(current),
155 "qualified_identifier" => current = last_named_field_child(current, "name")?,
156 "dependent_name" | "template_function" | "template_method" | "template_type" => {
157 current = current.child_by_field_name("name")?;
158 }
159 "field_expression" => current = current.child_by_field_name("field")?,
160 "call_expression" => current = current.child_by_field_name("function")?,
161 "new_expression" => current = current.child_by_field_name("type")?,
162 "parenthesized_expression" => current = first_named_child(current)?,
163 _ => return declarator_name_node(current),
164 }
165 }
166}
167
168fn attach_qualified_scope_receiver(sink: &mut RoleSink<'_>, function: Node<'_>) {
169 if function.kind() != "qualified_identifier" {
170 return;
171 }
172 if let Some(scope) = function.child_by_field_name("scope") {
173 attach_role_with_derived_name(sink, Role::Receiver, scope, expression_name_node);
174 }
175}
176
177fn qualified_declarator_node(mut node: Node<'_>) -> Option<Node<'_>> {
178 loop {
179 if node.kind() == "qualified_identifier" {
180 return Some(node);
181 }
182 node = node
183 .child_by_field_name("declarator")
184 .or_else(|| node.child_by_field_name("name"))
185 .or_else(|| first_named_child(node))?;
186 }
187}
188
189fn node_text<'source>(node: Node<'_>, source: &'source str) -> Option<&'source str> {
190 node.utf8_text(source.as_bytes()).ok()
191}
192
193fn scoped_function_definition(node: Node<'_>) -> Option<Node<'_>> {
194 node.child_by_field_name("declarator")
195 .and_then(qualified_declarator_node)
196 .and_then(|qualified| qualified.child_by_field_name("scope"))
197}
198
199fn is_constructor_definition(node: Node<'_>, source: &str) -> bool {
200 node.child_by_field_name("declarator")
201 .and_then(qualified_declarator_node)
202 .and_then(|qualified| {
203 Some((
204 expression_name_node(qualified.child_by_field_name("scope")?)?,
205 expression_name_node(last_named_field_child(qualified, "name")?)?,
206 ))
207 })
208 .is_some_and(|(scope, name)| node_text(scope, source) == node_text(name, source))
209}
210
211fn unquoted_include_span(node: Node<'_>) -> Option<Span> {
212 if !matches!(node.kind(), "string_literal" | "system_lib_string") {
213 return None;
214 }
215 let start = node.start_byte().checked_add(1)?;
216 let end = node.end_byte().checked_sub(1)?;
217 (start <= end).then_some(Span {
218 start_byte: start,
219 end_byte: end,
220 })
221}
222
223impl StructuralSpec for CppStructuralSpec {
224 fn language(&self) -> Language {
225 Language::Cpp
226 }
227
228 fn kind_table(&self) -> &'static [(&'static str, NormalizedKind)] {
229 CPP_KIND_TABLE
230 }
231
232 fn refine_kind(
233 &self,
234 node: Node<'_>,
235 kind: NormalizedKind,
236 enclosing: Option<NormalizedKind>,
237 source: &str,
238 ) -> NormalizedKind {
239 if kind == NormalizedKind::Function
240 && (enclosing == Some(NormalizedKind::Class)
241 || scoped_function_definition(node).is_some())
242 {
243 if is_constructor_definition(node, source) {
244 NormalizedKind::Constructor
245 } else {
246 NormalizedKind::Method
247 }
248 } else {
249 kind
250 }
251 }
252
253 fn supports_kind(&self, kind: NormalizedKind) -> bool {
254 matches!(kind, NormalizedKind::Method | NormalizedKind::Constructor)
255 || self
256 .kind_table()
257 .iter()
258 .any(|(_, fact_kind)| fact_kind.satisfies(kind))
259 }
260
261 fn supports_role(&self, role: Role) -> bool {
262 !matches!(role, Role::Kwarg | Role::Decorator)
263 }
264
265 fn occurrence_role_support(&self) -> &OccurrenceRoleSupport {
269 &NO_OCCURRENCE_ROLE_SUPPORT
270 }
271
272 fn lexical_environment_support(&self) -> &LexicalEnvironmentSupport {
273 &NO_LEXICAL_ENVIRONMENT_SUPPORT
274 }
275
276 fn materialization_support(&self) -> &DeclarationMaterializationSupport {
277 &CPP_MATERIALIZATION_SUPPORT
278 }
279
280 fn reference_edge_support(&self) -> &ReferenceEdgeSupport {
281 &INVERSE_REFERENCE_EDGE_SUPPORT
282 }
283
284 fn identity_route_support(&self) -> &IdentityRouteSupport {
285 static SUPPORT: IdentityRouteSupport = IdentityRouteSupport::NONE
292 .supported_axis(IdentityAxis::CanonicalIdentity)
293 .supported_axis(IdentityAxis::PhysicalGrouping)
294 .supported_relation(RouteHopKind::NestedOwner);
295 &SUPPORT
296 }
297
298 fn extract(&self, node: Node<'_>, kind: NormalizedKind, sink: &mut RoleSink<'_>) {
299 match kind {
300 NormalizedKind::Call => {
301 let function_field = if node.kind() == "new_expression" {
302 "type"
303 } else {
304 "function"
305 };
306 if let Some(function) = node.child_by_field_name(function_field) {
307 attach_terminal_callee(sink, function, expression_name_node(function));
308 if function.kind() == "field_expression"
309 && let Some(argument) = function.child_by_field_name("argument")
310 {
311 attach_role_with_derived_name(
312 sink,
313 Role::Receiver,
314 argument,
315 expression_name_node,
316 );
317 }
318 attach_qualified_scope_receiver(sink, function);
319 }
320 if let Some(arguments) = node.child_by_field_name("arguments") {
321 attach_positional_argument_roles(sink, arguments, expression_name_node);
322 }
323 }
324 NormalizedKind::FieldAccess => {
325 if let Some(field) = node.child_by_field_name("field") {
326 attach_role_with_derived_name(sink, Role::Field, field, expression_name_node);
327 if let Some(name) = expression_name_node(field) {
328 sink.set_name(name);
329 }
330 }
331 if let Some(argument) = node.child_by_field_name("argument") {
332 attach_role_with_derived_name(
333 sink,
334 Role::Object,
335 argument,
336 expression_name_node,
337 );
338 }
339 }
340 NormalizedKind::Function | NormalizedKind::Method | NormalizedKind::Constructor => {
341 if let Some(name) = node
342 .child_by_field_name("declarator")
343 .and_then(declarator_name_node)
344 {
345 sink.set_name(name);
346 }
347 }
348 NormalizedKind::Class | NormalizedKind::Declaration => {
349 if let Some(name) = node
350 .child_by_field_name("name")
351 .and_then(declarator_name_node)
352 .or_else(|| node.child_by_field_name("name"))
353 {
354 sink.set_name(name);
355 }
356 }
357 NormalizedKind::Assignment => match node.kind() {
358 "init_declarator" => {
359 if let Some(declarator) = node.child_by_field_name("declarator") {
360 attach_role_with_derived_name(
361 sink,
362 Role::Left,
363 declarator,
364 declarator_name_node,
365 );
366 if let Some(name) = declarator_name_node(declarator) {
367 sink.set_name(name);
368 }
369 }
370 if let Some(value) = node.child_by_field_name("value") {
371 attach_role_with_derived_name(
372 sink,
373 Role::Right,
374 value,
375 expression_name_node,
376 );
377 }
378 }
379 "assignment_expression" => {
380 if let Some(left) = node.child_by_field_name("left") {
381 attach_role_with_derived_name(sink, Role::Left, left, expression_name_node);
382 }
383 if let Some(right) = node.child_by_field_name("right") {
384 attach_role_with_derived_name(
385 sink,
386 Role::Right,
387 right,
388 expression_name_node,
389 );
390 }
391 }
392 _ => {}
393 },
394 NormalizedKind::Import => {
395 if let Some(path) = node.child_by_field_name("path") {
396 if let Some(name) = unquoted_include_span(path) {
397 sink.role_named_span(Role::Module, path, name);
398 } else {
399 attach_role_with_derived_name(
400 sink,
401 Role::Module,
402 path,
403 expression_name_node,
404 );
405 }
406 }
407 }
408 NormalizedKind::Identifier => match expression_name_node(node) {
409 Some(name) => sink.set_name(name),
410 None => sink.set_name(node),
411 },
412 _ => {}
413 }
414 }
415}