1use crate::declarations::{
4 cpp_callable_identity_suffix, cpp_callable_is_structural_constructor,
5 cpp_comparable_parameter_shapes, cpp_declarator_adds_indirection,
6 cpp_macro_swallowed_access_label, extract_function_declarator, node_text,
7};
8use crate::graph::resolver::{
9 cpp_declared_field_type_fact, cpp_type_name_components, declarator_name_node,
10 declarator_names_function_type, parse_cpp_integer_literal as parse_integer_literal,
11 structured_alias_type_target,
12};
13use brokk_bifrost_core::analyzer::cpp_facts::*;
14use brokk_bifrost_core::analyzer::source_facts::{
15 PrimarySourceFactCollector, SourceDeclarationId, SourceOccurrenceId,
16};
17use brokk_bifrost_core::analyzer::tree_walk::{ParentIndex, node_range};
18use brokk_bifrost_core::analyzer::{CodeUnit, Range};
19use brokk_bifrost_core::hash::HashMap;
20use tree_sitter::{Node, Parser};
21
22pub fn parse_cpp_expression_source_facts(
26 source: &str,
27) -> brokk_bifrost_core::analyzer::cpp_facts::CppExpressionSourceFacts {
28 let mut parser = Parser::new();
29 parser
30 .set_language(&tree_sitter_cpp::LANGUAGE.into())
31 .expect("the linked tree-sitter-cpp grammar matches this tree-sitter version");
32 let tree = parser.parse(source, None).expect("uncancelled C++ parse");
33 let mut collector = PrimarySourceFactCollector::new(source);
34 let named_record_sizes = named_record_size_index(tree.root_node(), source);
35 let mut expressions = Vec::new();
36 let mut stack = vec![tree.root_node()];
37 while let Some(node) = stack.pop() {
38 if node.is_named() {
39 let occurrence = collector.intern_node(node);
40 if let Some(fact) =
41 capture_expression_fact(node, source, occurrence, &named_record_sizes)
42 {
43 expressions.push(fact);
44 }
45 }
46 let mut cursor = node.walk();
47 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
48 stack.extend(children.into_iter().rev());
49 }
50 brokk_bifrost_core::analyzer::cpp_facts::CppExpressionSourceFacts {
51 source: collector.finish(),
52 expressions,
53 }
54}
55
56pub(crate) fn capture_expression_fact(
60 node: Node<'_>,
61 source: &str,
62 occurrence: SourceOccurrenceId,
63 named_record_sizes: &HashMap<String, Option<u64>>,
64) -> Option<CppExpressionFact> {
65 match node.kind() {
66 "number_literal" => {
67 let literal_text = node_text(node, source).trim().to_owned();
68 Some(
69 CppExpressionFact::new(occurrence, CppExpressionKind::IntegerLiteral)
70 .with_literal(literal_text),
71 )
72 }
73 "string_literal" | "raw_string_literal" => {
74 let literal_text = node_text(node, source).trim().to_owned();
75 Some(
76 CppExpressionFact::new(occurrence, CppExpressionKind::StringLiteral)
77 .with_literal(literal_text),
78 )
79 }
80 "parameter_declaration" => capture_parameter(node, source, occurrence, named_record_sizes),
81 "field_declaration" => capture_bit_field(node, source, occurrence)
82 .or_else(|| capture_function_signature(node, source, occurrence)),
83 "enum_specifier" => capture_enum_definition(node, source, occurrence),
84 "enumerator" => capture_enumerator(node, source, occurrence),
85 "function_definition" | "declaration" => {
86 capture_function_signature(node, source, occurrence)
87 }
88 "for_statement" | "for_range_loop" => capture_loop_variable(node, source, occurrence),
89 "sizeof_expression" => capture_sizeof(node, source, occurrence),
90 _ => None,
91 }
92}
93
94trait CppExpressionFactBuilder {
95 fn with_literal(self, literal: String) -> Self;
96}
97
98impl CppExpressionFactBuilder for CppExpressionFact {
99 fn with_literal(mut self, literal: String) -> Self {
100 self.integer_value = parse_integer_literal(&literal);
101 self.literal_text = Some(literal);
102 self.complete()
103 }
104}
105
106fn capture_parameter(
107 node: Node<'_>,
108 source: &str,
109 occurrence: SourceOccurrenceId,
110 named_record_sizes: &HashMap<String, Option<u64>>,
111) -> Option<CppExpressionFact> {
112 let declarator = node.child_by_field_name("declarator")?;
113 let shape = declarator_shape(declarator, source);
114 let (type_text, _type_form, signedness, width_bits) =
115 type_properties(node.child_by_field_name("type"), source);
116 let name = crate::structural::declarator_name_node(declarator)
117 .map(|name| node_text(name, source).to_owned());
118 let kind = if shape.has_array {
119 CppExpressionKind::ArrayParameter
120 } else {
121 CppExpressionKind::FunctionParameter
122 };
123 let mut fact = CppExpressionFact::new(occurrence, kind);
124 fact.name = name;
125 fact.type_text = type_text;
126 fact.type_form = shape.top;
127 fact.signedness = signedness;
128 fact.width_bits = width_bits;
129 fact.array_extent = shape.array_extent;
130 fact.array_is_reference = Some(shape.has_reference);
131 fact.parameter_by_value = Some(!shape.has_reference);
132 fact.member_function = Some(enclosing_member_function(node, declarator));
133 let type_size = exact_parameter_type_size(node, source, named_record_sizes);
134 fact.type_size_bytes = type_size;
135 fact.type_size_exact = Some(type_size.is_some());
136 if shape.has_array {
137 Some(if shape.array_extent.is_some() {
138 fact.complete()
139 } else {
140 fact.open("array extent is not a literal")
141 })
142 } else {
143 Some(match type_size {
144 Some(_) => fact.complete(),
145 None => fact.open("parameter type size is not exact"),
146 })
147 }
148}
149
150fn capture_bit_field(
151 node: Node<'_>,
152 source: &str,
153 occurrence: SourceOccurrenceId,
154) -> Option<CppExpressionFact> {
155 let clause = named_child(node, "bitfield_clause")?;
156 let width_node = clause
157 .child_by_field_name("value")
158 .or_else(|| clause.child_by_field_name("width"))
159 .or_else(|| {
160 let mut cursor = clause.walk();
161 clause
162 .named_children(&mut cursor)
163 .find(|child| child.kind() == "number_literal")
164 });
165 let declarator = node.child_by_field_name("declarator");
166 let name = declarator
167 .and_then(crate::structural::declarator_name_node)
168 .map(|name| node_text(name, source).to_owned());
169 let name = name.filter(|name| !name.is_empty());
170 let (type_text, type_form, signedness, width_bits) =
171 type_properties(node.child_by_field_name("type"), source);
172 let mut fact = CppExpressionFact::new(occurrence, CppExpressionKind::BitField);
173 fact.name = name;
174 fact.type_text = type_text;
175 fact.type_form = type_form;
176 fact.signedness = signedness;
177 fact.width_bits = width_bits;
178 fact.member_function =
179 Some(declarator.is_some_and(|declarator| enclosing_member_function(node, declarator)));
180 fact.bit_field_anonymous = Some(fact.name.is_none());
181 fact.bit_field_width = width_node
182 .map(|width| node_text(width, source))
183 .and_then(parse_integer_literal)
184 .and_then(|width| u64::try_from(width).ok());
185 Some(match fact.bit_field_width {
186 Some(_) => fact.complete(),
187 None => fact.open("bit-field width is not a literal"),
188 })
189}
190
191fn capture_enum_definition(
192 node: Node<'_>,
193 source: &str,
194 occurrence: SourceOccurrenceId,
195) -> Option<CppExpressionFact> {
196 let mut fact = CppExpressionFact::new(occurrence, CppExpressionKind::EnumDefinition);
197 fact.name = node
198 .child_by_field_name("name")
199 .map(|name| node_text(name, source).to_owned());
200 fact.enum_scoped = Some({
201 let mut cursor = node.walk();
202 node.children(&mut cursor)
203 .any(|child| matches!(child.kind(), "class" | "struct"))
204 });
205 Some(fact.complete())
206}
207
208fn capture_enumerator(
209 node: Node<'_>,
210 source: &str,
211 occurrence: SourceOccurrenceId,
212) -> Option<CppExpressionFact> {
213 let mut fact = CppExpressionFact::new(occurrence, CppExpressionKind::Enumerator);
214 fact.name = node
215 .child_by_field_name("name")
216 .map(|name| node_text(name, source).to_owned())
217 .or_else(|| {
218 let mut cursor = node.walk();
219 node.named_children(&mut cursor)
220 .find(|child| child.kind() == "identifier")
221 .map(|name| node_text(name, source).to_owned())
222 });
223 let value = node.child_by_field_name("value");
224 fact.has_initializer = Some(value.is_some());
225 fact.enum_value =
226 value.and_then(|value| parse_integer_literal(node_text(value, source).trim()));
227 fact.enum_has_implicit_before_nonreference = enum_has_implicit_before_nonreference(node);
228 fact.enum_ordinal = node.parent().and_then(|parent| {
229 let mut ordinal = 0u32;
230 let mut cursor = parent.walk();
231 for sibling in parent.named_children(&mut cursor) {
232 if sibling.id() == node.id() {
233 return Some(ordinal);
234 }
235 if sibling.kind() == "enumerator" {
236 ordinal = ordinal.saturating_add(1);
237 }
238 }
239 None
240 });
241 Some(if fact.name.is_some() {
242 fact.complete()
243 } else {
244 fact.open("enumerator name is unavailable")
245 })
246}
247
248fn capture_function_signature(
249 node: Node<'_>,
250 source: &str,
251 occurrence: SourceOccurrenceId,
252) -> Option<CppExpressionFact> {
253 let declarator = first_descendant_kind(node, "function_declarator")?;
254 let (type_text, type_form, signedness, width_bits) =
255 type_properties(node.child_by_field_name("type"), source);
256 let mut fact = CppExpressionFact::new(occurrence, CppExpressionKind::FunctionSignature);
257 fact.name = crate::structural::declarator_name_node(declarator)
258 .map(|name| node_text(name, source).to_owned());
259 fact.type_text = type_text;
260 fact.type_form = type_form;
261 fact.signedness = signedness;
262 fact.width_bits = width_bits;
263 fact.member_function = Some(enclosing_member_function(node, declarator));
264 fact.return_top_level_const = Some(declaration_has_direct_const_qualifier(node, source));
265 fact.member_function_const = Some(declaration_has_direct_const_qualifier(declarator, source));
266 Some(fact.complete())
267}
268
269fn capture_loop_variable(
270 node: Node<'_>,
271 source: &str,
272 occurrence: SourceOccurrenceId,
273) -> Option<CppExpressionFact> {
274 let name = loop_binding_name(node, source);
275 let type_node = node.child_by_field_name("type").or_else(|| {
276 node.child_by_field_name("initializer")
277 .and_then(|init| init.child_by_field_name("type"))
278 });
279 let (type_text, type_form, signedness, width_bits) = type_properties(type_node, source);
280 let mut fact = CppExpressionFact::new(occurrence, CppExpressionKind::LoopVariable);
281 fact.name = name;
282 fact.type_text = type_text;
283 fact.type_form = type_form;
284 fact.signedness = signedness;
285 fact.width_bits = width_bits;
286 fact.loop_has_short_ancestor = fact
287 .name
288 .as_deref()
289 .map(|_| has_short_loop_ancestor(node, source));
290 Some(if fact.name.is_some() {
291 fact.complete()
292 } else {
293 fact.open("loop binding name is unavailable")
294 })
295}
296
297fn capture_sizeof(
298 node: Node<'_>,
299 source: &str,
300 occurrence: SourceOccurrenceId,
301) -> Option<CppExpressionFact> {
302 let operand_kind = if node.child_by_field_name("type").is_some() {
303 CppSizeofOperandKind::Type
304 } else if node.child_by_field_name("value").is_some() {
305 CppSizeofOperandKind::Expression
306 } else {
307 return Some(
308 CppExpressionFact::new(occurrence, CppExpressionKind::SizeofExpression)
309 .open("sizeof operand is unavailable"),
310 );
311 };
312 let operand = node
313 .child_by_field_name("value")
314 .or_else(|| node.child_by_field_name("type"));
315 let mut side_effect = false;
316 let mut stack = operand.into_iter().collect::<Vec<_>>();
317 while let Some(current) = stack.pop() {
318 if matches!(
319 current.kind(),
320 "call_expression"
321 | "assignment_expression"
322 | "update_expression"
323 | "new_expression"
324 | "delete_expression"
325 | "comma_expression"
326 ) {
327 side_effect = true;
328 break;
329 }
330 let mut cursor = current.walk();
331 stack.extend(current.named_children(&mut cursor));
332 }
333 let mut fact = CppExpressionFact::new(occurrence, CppExpressionKind::SizeofExpression);
334 fact.sizeof_operand_kind = Some(operand_kind);
335 fact.sizeof_operand_is_parameter = sizeof_operand_is_parameter(node, operand, source);
336 fact.sizeof_has_side_effect = Some(side_effect);
337 Some(fact.complete())
338}
339
340fn enclosing_member_function(node: Node<'_>, declarator: Node<'_>) -> bool {
341 let mut ancestor = node.parent();
342 while let Some(current) = ancestor {
343 if matches!(
344 current.kind(),
345 "class_specifier" | "struct_specifier" | "union_specifier"
346 ) {
347 return true;
348 }
349 ancestor = current.parent();
350 }
351 first_descendant_kind(declarator, "qualified_identifier")
352 .and_then(|qualified| qualified.child_by_field_name("scope"))
353 .is_some_and(|scope| first_descendant_kind(scope, "type_identifier").is_some())
354}
355
356fn enum_has_implicit_before_nonreference(node: Node<'_>) -> Option<bool> {
357 let mut ancestor = node.parent();
358 while let Some(current) = ancestor {
359 if current.kind() == "enum_specifier" {
360 let list = first_descendant_kind(current, "enumerator_list")?;
361 let mut cursor = list.walk();
362 let enumerators = list
363 .named_children(&mut cursor)
364 .filter(|child| child.kind() == "enumerator")
365 .collect::<Vec<_>>();
366 let current_index = enumerators
367 .iter()
368 .position(|enumerator| enumerator.id() == node.id())?;
369 if node.child_by_field_name("value").is_none()
370 || current_index == 0
371 || current_index + 1 >= enumerators.len()
372 {
373 return Some(false);
374 }
375 let mut implicit_before_nonreference = false;
376 for (index, enumerator) in enumerators.iter().enumerate() {
377 if enumerator.child_by_field_name("value").is_none()
378 && enumerators[index + 1..]
379 .iter()
380 .any(|later| later.child_by_field_name("value").is_some())
381 {
382 implicit_before_nonreference = true;
383 break;
384 }
385 }
386 return Some(implicit_before_nonreference);
387 }
388 ancestor = current.parent();
389 }
390 None
391}
392
393fn loop_binding_name(node: Node<'_>, source: &str) -> Option<String> {
394 let binding = node.child_by_field_name("declarator").or_else(|| {
395 node.child_by_field_name("initializer")
396 .and_then(first_declaration_or_name)
397 })?;
398 let name = crate::structural::declarator_name_node(binding)?;
399 Some(node_text(name, source).to_owned())
400}
401
402fn has_short_loop_ancestor(node: Node<'_>, source: &str) -> bool {
403 let mut ancestor = node.parent();
404 while let Some(current) = ancestor {
405 if matches!(current.kind(), "for_statement" | "for_range_loop")
406 && loop_binding_name(current, source).is_some_and(|name| name.chars().count() == 1)
407 {
408 return true;
409 }
410 ancestor = current.parent();
411 }
412 false
413}
414
415fn sizeof_operand_is_parameter(
416 node: Node<'_>,
417 operand: Option<Node<'_>>,
418 source: &str,
419) -> Option<bool> {
420 let operand = operand?;
421 let name_node = if matches!(operand.kind(), "identifier" | "field_identifier") {
422 Some(operand)
423 } else {
424 first_descendant_kind(operand, "identifier")
425 }?;
426 let name = node_text(name_node, source);
427 let mut ancestor = node.parent();
428 while let Some(current) = ancestor {
429 if current.kind() == "function_definition" {
430 let mut stack = vec![current];
431 while let Some(candidate) = stack.pop() {
432 if candidate.kind() == "parameter_declaration"
433 && candidate
434 .child_by_field_name("declarator")
435 .and_then(crate::structural::declarator_name_node)
436 .is_some_and(|candidate_name| node_text(candidate_name, source) == name)
437 {
438 return Some(true);
439 }
440 let mut cursor = candidate.walk();
441 let children = candidate.named_children(&mut cursor).collect::<Vec<_>>();
442 stack.extend(children.into_iter().rev());
443 }
444 return Some(false);
445 }
446 ancestor = current.parent();
447 }
448 None
449}
450
451fn exact_parameter_type_size(
452 node: Node<'_>,
453 source: &str,
454 named_record_sizes: &HashMap<String, Option<u64>>,
455) -> Option<u64> {
456 if node
460 .child_by_field_name("declarator")
461 .is_some_and(cpp_declarator_adds_indirection)
462 {
463 return None;
464 }
465 let type_node = node.child_by_field_name("type")?;
466 let type_text = node_text(type_node, source).trim();
467 match type_text {
468 "char" | "signed char" | "unsigned char" => Some(1),
469 _ if matches!(
470 type_node.kind(),
471 "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
472 ) =>
473 {
474 named_record_sizes.get(type_text).copied().flatten()
475 }
476 _ => None,
477 }
478}
479
480pub(crate) fn named_record_size_index(
481 root: Node<'_>,
482 source: &str,
483) -> HashMap<String, Option<u64>> {
484 let mut sizes = HashMap::default();
485 let mut stack = vec![root];
486 while let Some(node) = stack.pop() {
487 if matches!(
488 node.kind(),
489 "class_specifier" | "struct_specifier" | "union_specifier"
490 ) && node.child_by_field_name("name").is_some()
491 {
492 let name = node
493 .child_by_field_name("name")
494 .expect("record name was checked above");
495 let name = node_text(name, source).trim();
496 if !name.is_empty() {
497 sizes
498 .entry(name.to_owned())
499 .or_insert_with(|| exact_char_record_size(node, source));
500 }
501 }
502 let mut cursor = node.walk();
503 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
504 stack.extend(children.into_iter().rev());
505 }
506 sizes
507}
508
509fn exact_char_record_size(record: Node<'_>, source: &str) -> Option<u64> {
510 let fields = first_descendant_kind(record, "field_declaration_list")?;
511 let mut cursor = fields.walk();
512 let declarations = fields
513 .named_children(&mut cursor)
514 .filter(|child| child.kind() == "field_declaration")
515 .collect::<Vec<_>>();
516 if declarations.is_empty() {
517 return None;
518 }
519 let mut size = 0u64;
520 for field in declarations {
521 if named_child(field, "bitfield_clause").is_some() {
522 return None;
523 }
524 let type_node = field.child_by_field_name("type")?;
525 let type_text = node_text(type_node, source).trim();
526 if !matches!(type_text, "char" | "signed char" | "unsigned char") {
527 return None;
528 }
529 let mut cursor = field.walk();
530 let mut declarators = field
531 .children_by_field_name("declarator", &mut cursor)
532 .peekable();
533 declarators.peek()?;
534 for declarator in declarators {
535 let shape = declarator_shape(declarator, source);
536 let field_size = if shape.has_array {
537 shape.array_extent?
538 } else if shape.top == CppTypeForm::Unknown {
539 1
540 } else {
541 return None;
542 };
543 size = if record.kind() == "union_specifier" {
544 size.max(field_size)
545 } else {
546 size.checked_add(field_size)?
547 };
548 }
549 }
550 Some(size)
551}
552
553fn named_child<'tree>(node: Node<'tree>, kind: &str) -> Option<Node<'tree>> {
554 let mut cursor = node.walk();
555 node.named_children(&mut cursor)
556 .find(|child| child.kind() == kind)
557}
558
559fn first_descendant_kind<'tree>(node: Node<'tree>, kind: &str) -> Option<Node<'tree>> {
560 let mut stack = vec![node];
561 while let Some(current) = stack.pop() {
562 if current.kind() == kind {
563 return Some(current);
564 }
565 let mut cursor = current.walk();
566 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
567 stack.extend(children.into_iter().rev());
568 }
569 None
570}
571
572fn first_declaration_or_name(node: Node<'_>) -> Option<Node<'_>> {
573 if matches!(node.kind(), "declaration" | "parameter_declaration") {
574 return node.child_by_field_name("declarator");
575 }
576 if matches!(
577 node.kind(),
578 "identifier" | "field_identifier" | "reference_declarator" | "array_declarator"
579 ) {
580 return Some(node);
581 }
582 first_descendant_kind(node, "declaration")
583 .and_then(|declaration| declaration.child_by_field_name("declarator"))
584 .or_else(|| first_descendant_kind(node, "identifier"))
585}
586
587struct DeclaratorShape {
588 top: CppTypeForm,
589 has_array: bool,
590 has_reference: bool,
591 array_extent: Option<u64>,
592}
593
594fn declarator_shape(declarator: Node<'_>, source: &str) -> DeclaratorShape {
595 let mut current = Some(declarator);
596 let mut top = CppTypeForm::Unknown;
597 let mut has_array = false;
598 let mut has_reference = false;
599 let mut array_extent = None;
600 while let Some(node) = current {
601 match node.kind() {
602 "pointer_declarator" => {
603 if top == CppTypeForm::Unknown {
604 top = CppTypeForm::Pointer;
605 }
606 current = node.child_by_field_name("declarator");
607 }
608 "reference_declarator" => {
609 has_reference = true;
610 if top == CppTypeForm::Unknown {
611 top = CppTypeForm::Reference;
612 }
613 current = node
614 .child_by_field_name("declarator")
615 .or_else(|| node.named_child(0));
616 }
617 "array_declarator" => {
618 has_array = true;
619 if top == CppTypeForm::Unknown {
620 top = CppTypeForm::Array;
621 }
622 array_extent = node
623 .child_by_field_name("size")
624 .or_else(|| node.child_by_field_name("value"))
625 .and_then(|size| parse_integer_literal(node_text(size, source)))
626 .and_then(|size| u64::try_from(size).ok());
627 current = node.child_by_field_name("declarator");
628 }
629 "function_declarator" => {
630 if top == CppTypeForm::Unknown {
631 top = CppTypeForm::Function;
632 }
633 current = node.child_by_field_name("declarator");
634 }
635 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {
636 current = node
637 .child_by_field_name("declarator")
638 .or_else(|| node.named_child(0));
639 }
640 _ => current = None,
641 }
642 }
643 DeclaratorShape {
644 top,
645 has_array,
646 has_reference,
647 array_extent,
648 }
649}
650
651fn type_properties(
652 type_node: Option<Node<'_>>,
653 source: &str,
654) -> (Option<String>, CppTypeForm, CppSignedness, Option<u16>) {
655 let Some(type_node) = type_node else {
656 return (None, CppTypeForm::Unknown, CppSignedness::Unknown, None);
657 };
658 let text = node_text(type_node, source).trim().to_owned();
659 let kind = type_node.kind();
660 let builtin = matches!(kind, "primitive_type" | "sized_type_specifier")
661 || matches!(
662 text.as_str(),
663 "bool"
664 | "char"
665 | "signed char"
666 | "unsigned char"
667 | "short"
668 | "short int"
669 | "unsigned short"
670 | "unsigned short int"
671 | "int"
672 | "unsigned"
673 | "unsigned int"
674 | "long"
675 | "long int"
676 | "unsigned long"
677 | "unsigned long int"
678 | "long long"
679 | "long long int"
680 | "unsigned long long"
681 | "unsigned long long int"
682 );
683 let type_form = if builtin {
684 CppTypeForm::Builtin
685 } else if matches!(
686 kind,
687 "type_identifier"
688 | "qualified_identifier"
689 | "scoped_type_identifier"
690 | "template_type"
691 | "enum_specifier"
692 ) {
693 CppTypeForm::Named
694 } else {
695 CppTypeForm::Unknown
696 };
697 let signedness = if text.split_whitespace().any(|part| part == "unsigned") {
698 CppSignedness::Unsigned
699 } else if text.split_whitespace().any(|part| part == "signed") {
700 CppSignedness::Signed
701 } else if builtin
702 && matches!(
703 text.as_str(),
704 "char" | "int" | "short" | "long" | "long long"
705 )
706 {
707 CppSignedness::Ambiguous
708 } else {
709 CppSignedness::Unknown
710 };
711 let width_bits = match text.as_str() {
712 "bool" => Some(1),
713 "char" | "signed char" | "unsigned char" => Some(8),
714 "short" | "short int" | "signed short" | "signed short int" | "unsigned short"
715 | "unsigned short int" => Some(16),
716 "int" | "signed int" | "unsigned" | "unsigned int" => Some(32),
717 "long long"
718 | "long long int"
719 | "signed long long"
720 | "signed long long int"
721 | "unsigned long long"
722 | "unsigned long long int" => Some(64),
723 _ => None,
724 };
725 (Some(text), type_form, signedness, width_bits)
726}
727
728fn declaration_has_direct_const_qualifier(node: Node<'_>, source: &str) -> bool {
729 let mut cursor = node.walk();
730 node.named_children(&mut cursor)
731 .any(|child| child.kind() == "type_qualifier" && node_text(child, source).trim() == "const")
732}
733
734pub fn parse_statement_fact_rows(source: &str) -> StatementFactRows {
737 let mut parser = Parser::new();
738 parser
739 .set_language(&tree_sitter_cpp::LANGUAGE.into())
740 .expect("the linked tree-sitter-cpp grammar matches this tree-sitter version");
741 let tree = parser.parse(source, None).expect("uncancelled C++ parse");
742 brokk_bifrost_core::analyzer::statement_facts::producer::capture_statement_facts(
743 &crate::structural::CPP_STRUCTURAL_SPEC,
744 tree.root_node(),
745 source,
746 &Default::default(),
747 )
748}
749
750pub fn parse_cpp_class_fact_rows(source: &str) -> CppClassFactRows {
753 let mut parser = Parser::new();
754 parser
755 .set_language(&tree_sitter_cpp::LANGUAGE.into())
756 .expect("the linked tree-sitter-cpp grammar matches this tree-sitter version");
757 let tree = parser.parse(source, None).expect("uncancelled C++ parse");
758 let root = tree.root_node();
759 let ancestry = ParentIndex::new(root);
760 let mut rows = CppClassFactRows::default();
761 let mut stack = vec![root];
762 while let Some(node) = stack.pop() {
763 if node.is_named() {
764 capture_class_fact_rows(node, source, &ancestry, &mut rows);
765 }
766 let mut cursor = node.walk();
767 let children: Vec<_> = node.named_children(&mut cursor).collect();
768 stack.extend(children.into_iter().rev());
769 }
770 rows
771}
772
773pub(crate) fn base_specifier_from_node(
774 node: Node<'_>,
775 source: &str,
776 is_virtual: bool,
777 access: CppAccessSpecifier,
778) -> Option<CppBaseSpecifierFact> {
779 let components = cpp_type_name_components(node, source)?;
780 base_specifier_fact(node, components, is_virtual, access)
781}
782
783pub(crate) fn base_specifier_fact(
784 node: Node<'_>,
785 components: Vec<String>,
786 is_virtual: bool,
787 access: CppAccessSpecifier,
788) -> Option<CppBaseSpecifierFact> {
789 (!components.is_empty()).then_some(CppBaseSpecifierFact {
790 absolute: node.child(0).is_some_and(|token| token.kind() == "::"),
791 components,
792 is_virtual,
793 access,
794 })
795}
796
797pub(crate) fn declaration_name<'tree>(
798 node: Node<'tree>,
799 unit: &CodeUnit,
800 source: &str,
801) -> Option<Node<'tree>> {
802 if let Some(name) = node.child_by_field_name("name") {
803 return crate::structural::declarator_name_node(name);
804 }
805 let mut cursor = node.walk();
806 node.children_by_field_name("declarator", &mut cursor)
807 .find_map(|declarator| {
808 let name = crate::structural::declarator_name_node(declarator)?;
809 (node_text(name, source) == unit.identifier()).then_some(name)
810 })
811}
812
813pub(crate) fn capture_field_type(
816 node: Node<'_>,
817 field_name: &str,
818 source: &str,
819) -> (Option<CppDeclaredFieldTypeFact>, bool) {
820 if let Some(recovered) = crate::declarations::recovered_pyobject_head_field(node, source)
821 && node_text(recovered.name, source) == field_name
822 {
823 return (
824 Some(CppDeclaredFieldTypeFact {
825 type_text: node_text(recovered.type_node, source).to_owned(),
826 indirection: recovered.pointer_depth(),
827 binds_indirectly: recovered.pointer_depth() > 0,
828 template_arguments: None,
829 }),
830 false,
831 );
832 }
833 let Some(type_node) = node.child_by_field_name("type") else {
834 return (None, false);
835 };
836 let mut cursor = node.walk();
837 let declarator = node
838 .children_by_field_name("declarator", &mut cursor)
839 .find(|declarator| {
840 crate::structural::declarator_name_node(*declarator)
841 .is_some_and(|name| node_text(name, source) == field_name)
842 });
843 let names_function_type = declarator.is_some_and(declarator_names_function_type);
844 let field_type = cpp_declared_field_type_fact(node, type_node, field_name, source);
845 (field_type, names_function_type)
846}
847
848pub(crate) fn capture_namespace_alias(
852 node: Node<'_>,
853 source: &str,
854 ancestry: &ParentIndex<'_>,
855 namespaces: &crate::graph::resolver::OrphanedNamespaceScopeIndex,
856 collector: &mut PrimarySourceFactCollector<'_>,
857) -> Option<CppNamespaceAliasFact> {
858 if node.kind() != "namespace_alias_definition" {
859 return None;
860 }
861 let name = node.child_by_field_name("name")?;
862 let mut cursor = node.walk();
863 let target = node.named_children(&mut cursor).find(|child| {
864 matches!(
865 child.kind(),
866 "namespace_identifier" | "nested_namespace_specifier" | "splice_specifier"
867 ) && child.id() != name.id()
868 })?;
869 let block = std::iter::successors(ancestry.parent(node), |current| ancestry.parent(*current))
870 .take_while(|current| {
871 !matches!(current.kind(), "namespace_definition" | "translation_unit")
872 })
873 .find(|current| current.kind() == "compound_statement");
874 Some(CppNamespaceAliasFact {
875 occurrence: collector.intern_node(node),
876 name: node_text(name, source).to_owned(),
877 target: crate::graph::resolver::cpp_name_component_nodes(target)
878 .filter(|components| !components.is_empty())
879 .map(|components| {
880 components
881 .into_iter()
882 .map(|component| node_text(component, source).to_owned())
883 .collect()
884 }),
885 target_global: target.child(0).is_some_and(|child| child.kind() == "::"),
886 namespace: namespaces.enclosing_namespace_components(node, source),
887 block: block.map(|block| collector.intern_node(block)),
888 guards: crate::graph::resolver::binding_site_guards(node, source)
889 .as_ref()
890 .map(crate::source_context::cpp_guard_set_from_runtime),
891 })
892}
893
894pub(crate) fn capture_declaration(
895 declaration: SourceDeclarationId,
896 node: Node<'_>,
897 unit: &CodeUnit,
898 source: &str,
899 ancestry: &ParentIndex<'_>,
900) -> CppDeclarationSourceFact {
901 let mut fact = CppDeclarationSourceFact::new(declaration);
902 if matches!(node.kind(), "alias_declaration" | "type_definition")
903 && crate::graph::resolver::alias_has_visible_file_scope(node)
904 && let Some(name) = declaration_name(node, unit, source)
905 && let Some(name) =
906 crate::graph::resolver::normalize_reference_name(node_text(name, source))
907 && let Some(target) = node.child_by_field_name("type")
908 && let Some(target) =
909 crate::graph::resolver::normalize_reference_name(node_text(target, source))
910 {
911 fact.file_scope_alias = Some(CppFileScopeAliasFact {
912 name,
913 target,
914 namespace: crate::graph::resolver::enclosing_namespace_context(node, source),
915 });
916 }
917 let mut owners = Vec::new();
918 let mut current = ancestry.parent(node);
919 while let Some(parent) = current {
920 if matches!(
921 parent.kind(),
922 "namespace_definition"
923 | "class_specifier"
924 | "struct_specifier"
925 | "union_specifier"
926 | "enum_specifier"
927 ) && let Some(name) = parent.child_by_field_name("name")
928 {
929 owners.push(node_text(name, source).to_owned());
930 }
931 current = ancestry.parent(parent);
932 }
933 owners.reverse();
934 owners.push(unit.identifier().to_owned());
935 fact.lexical_path = owners;
936 if crate::graph::resolver::is_function_local_unit(unit) {
941 let interner = brokk_bifrost_core::analyzer::fq_name::segment_interner();
942 fact.lexical_path = unit
943 .fq()
944 .segments()
945 .iter()
946 .map(|&segment| interner.resolve(segment).0.to_owned())
947 .collect();
948 }
949 if matches!(
950 node.kind(),
951 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
952 ) {
953 fact.class_strength = if node.child_by_field_name("body").is_some() {
954 CppClassDeclarationStrength::Full
955 } else {
956 CppClassDeclarationStrength::Forward
957 };
958 if node.kind() == "enum_specifier" {
959 let mut cursor = node.walk();
960 fact.enum_kind = if node
961 .children(&mut cursor)
962 .any(|child| matches!(child.kind(), "class" | "struct"))
963 {
964 CppEnumOwnerKind::Scoped
965 } else {
966 CppEnumOwnerKind::Unscoped
967 };
968 }
969 let mut cursor = node.walk();
970 for base_clause in node
971 .named_children(&mut cursor)
972 .filter(|child| child.kind() == "base_class_clause")
973 {
974 let mut is_virtual = false;
975 let mut cursor = base_clause.walk();
976 for child in base_clause.children(&mut cursor) {
977 match child.kind() {
978 "," => is_virtual = false,
979 "virtual" => is_virtual = true,
980 "type_identifier"
981 | "qualified_identifier"
982 | "scoped_type_identifier"
983 | "template_type" => {
984 if let Some(base) = base_specifier_from_node(
985 child,
986 source,
987 is_virtual,
988 base_access_specifier(base_clause, child, node.kind()),
989 ) {
990 fact.bases.push(base);
991 }
992 is_virtual = false;
993 }
994 _ => {}
995 }
996 }
997 }
998 }
999 let mut cursor = node.walk();
1000 let declarator = node
1001 .children_by_field_name("declarator", &mut cursor)
1002 .find(|declarator| {
1003 crate::structural::declarator_name_node(*declarator)
1004 .is_some_and(|name| node_text(name, source) == unit.identifier())
1005 });
1006 if unit.is_function() {
1007 let callable = if node.kind() == "function_declarator" {
1008 Some(node)
1009 } else {
1010 node.child_by_field_name("declarator")
1011 };
1012 if let Some(declarator) = callable {
1013 if let Some(name) = declarator_name_node(declarator)
1014 && let Some(mut components) = cpp_type_name_components(name, source)
1015 {
1016 components.pop();
1017 fact.written_owner = components;
1018 }
1019 let mut chain = Some(declarator);
1020 while let Some(current) = chain {
1021 if current.kind() == "function_declarator" {
1022 fact.callable_comparable_shapes =
1023 Some(cpp_comparable_parameter_shapes(current, source, ancestry));
1024 fact.callable_identity_suffix = cpp_callable_identity_suffix(current, source);
1025 fact.callable_is_constructor =
1026 cpp_callable_is_structural_constructor(current, source, ancestry);
1027 fact.callable_is_deduction_guide =
1028 cpp_callable_is_deduction_guide(node, current, unit, source);
1029 fact.callable_is_template = ancestry.parent(node).is_some_and(|parent| {
1030 parent.kind() == "template_declaration"
1031 && parent
1032 .named_child(parent.named_child_count().saturating_sub(1))
1033 .is_some_and(|declaration| declaration.id() == node.id())
1034 });
1035 fact.trailing_qualifiers =
1038 fact.callable_identity_suffix.clone().unwrap_or_default();
1039 break;
1040 }
1041 chain = current.child_by_field_name("declarator");
1042 }
1043 }
1044 }
1045 if unit.is_field() {
1046 let (field_type, names_function_type) = capture_field_type(node, unit.identifier(), source);
1047 fact.field_type = field_type;
1048 fact.names_function_type = names_function_type;
1049 }
1050 let alias_type = if matches!(node.kind(), "alias_declaration" | "type_definition") {
1051 node.child_by_field_name("type")
1052 } else if unit.is_class() {
1053 crate::declarations::recovered_alias_type_node(node, unit.identifier(), source)
1054 } else {
1055 None
1056 };
1057 if let Some(alias_type) = alias_type {
1058 let alias_declarator = if node.kind() == "type_definition" {
1059 declarator
1060 } else {
1061 alias_type.child_by_field_name("declarator")
1062 };
1063 fact.names_function_type = alias_declarator.is_some_and(declarator_names_function_type);
1064 fact.adds_indirection = alias_declarator.is_some_and(cpp_declarator_adds_indirection);
1065 if !fact.names_function_type {
1066 fact.alias_target = structured_alias_type_target(alias_type, source);
1067 let mut target = alias_type;
1068 while target.kind() == "type_descriptor" {
1069 let Some(inner) = target.child_by_field_name("type") else {
1070 break;
1071 };
1072 target = inner;
1073 }
1074 if matches!(
1075 target.kind(),
1076 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
1077 ) && let Some(name) = target.child_by_field_name("name")
1078 {
1079 target = name;
1080 }
1081 fact.alias_target_text = Some(node_text(target, source).to_owned());
1082 }
1083 }
1084 fact
1085}
1086
1087pub(crate) fn capture_class_fact_rows(
1091 node: Node<'_>,
1092 source: &str,
1093 ancestry: &ParentIndex<'_>,
1094 rows: &mut CppClassFactRows,
1095) {
1096 match node.kind() {
1097 "class_specifier" | "struct_specifier" | "union_specifier" => {
1098 let mut path = enclosing_owner_path(node, ancestry, source);
1099 if let Some(name) = node.child_by_field_name("name") {
1100 path.push(node_text(name, source).to_owned());
1101 }
1102 let is_interface = node
1103 .child_by_field_name("body")
1104 .is_some_and(|body| tree_contains_pure_virtual(body, source));
1105 rows.classes.push(CppClassFact {
1106 range: node_range(node),
1107 path,
1108 is_interface,
1109 });
1110 }
1111 "function_definition" | "declaration" | "field_declaration" => {
1112 if let Some(member) = capture_member_fact(node, source, ancestry) {
1113 rows.members.push(member);
1114 }
1115 }
1116 "catch_clause" => {
1117 rows.catches.push(capture_catch_fact(node, source));
1118 }
1119 "throw_statement" => {
1120 rows.throws.push(capture_throw_fact(node, source));
1121 }
1122 _ => {}
1123 }
1124}
1125
1126fn capture_member_fact(
1127 node: Node<'_>,
1128 source: &str,
1129 ancestry: &ParentIndex<'_>,
1130) -> Option<CppMemberFact> {
1131 let function_declarator = extract_function_declarator(node)?;
1132 let name_node = declarator_name_node(function_declarator)?;
1133 let name = node_text(name_node, source).to_owned();
1134 let mut owner = enclosing_owner_path(node, ancestry, source);
1135 let written_owner = cpp_type_name_components(name_node, source)
1136 .map(|mut components| {
1137 components.pop();
1138 components
1139 })
1140 .unwrap_or_default();
1141 append_owner_components(&mut owner, &written_owner);
1142 let owner_name = owner.last().map(String::as_str);
1143 let role = if name == "operator=" {
1144 CppMemberRole::AssignmentOperator
1145 } else if name.strip_prefix('~').is_some() {
1146 CppMemberRole::Destructor
1147 } else if owner_name == Some(name.as_str()) {
1148 CppMemberRole::Constructor
1149 } else {
1150 CppMemberRole::Method
1151 };
1152 let (copy_parameter, move_parameter, remaining_parameters_defaulted) =
1153 callable_parameter_shape(function_declarator, source, ancestry, owner_name);
1154 let special_member = match role {
1155 CppMemberRole::Destructor => CppSpecialMemberKind::Destructor,
1156 CppMemberRole::Constructor if copy_parameter => CppSpecialMemberKind::CopyConstructor,
1157 CppMemberRole::Constructor if move_parameter => CppSpecialMemberKind::MoveConstructor,
1158 CppMemberRole::Constructor if function_parameter_count(function_declarator) == 0 => {
1159 CppSpecialMemberKind::DefaultConstructor
1160 }
1161 CppMemberRole::AssignmentOperator if copy_parameter => CppSpecialMemberKind::CopyAssignment,
1162 CppMemberRole::AssignmentOperator if move_parameter => CppSpecialMemberKind::MoveAssignment,
1163 _ => CppSpecialMemberKind::None,
1164 };
1165 let state = if tree_contains_kind(node, "delete_method_clause") {
1166 CppSpecialMemberState::Deleted
1167 } else if tree_contains_kind(node, "default_method_clause") {
1168 CppSpecialMemberState::Defaulted
1169 } else {
1170 CppSpecialMemberState::Declared
1171 };
1172 let access = member_access_specifier(node, source, ancestry);
1173 let assignment_return = if role == CppMemberRole::AssignmentOperator {
1174 assignment_return_shape(node, source, owner_name)
1175 } else {
1176 CppAssignmentReturnShape::Unknown
1177 };
1178 let initializer_target = if role == CppMemberRole::Constructor {
1179 constructor_initializer_target(node, source, owner_name)
1180 } else {
1181 None
1182 };
1183 let delegation_calls = if role == CppMemberRole::Constructor {
1184 constructor_delegation_calls(node, source, ancestry, owner_name)
1185 } else {
1186 Vec::new()
1187 };
1188 Some(CppMemberFact {
1189 range: node_range(node),
1190 owner,
1191 name,
1192 role,
1193 special_member,
1194 state,
1195 access,
1196 copy_parameter,
1197 remaining_parameters_defaulted,
1198 assignment_return,
1199 initializer_target,
1200 delegation_calls,
1201 pure_virtual: tree_contains_pure_virtual(node, source),
1202 })
1203}
1204
1205fn capture_catch_fact(node: Node<'_>, source: &str) -> CppCatchFact {
1206 let parameter = node
1207 .child_by_field_name("parameters")
1208 .and_then(|parameters| parameters.named_child(0));
1209 let type_node = parameter.and_then(|parameter| parameter.child_by_field_name("type"));
1210 let declarator = parameter.and_then(|parameter| parameter.child_by_field_name("declarator"));
1211 let value_category = declarator
1212 .map(classify_parameter_declarator)
1213 .unwrap_or(CppParameterValueCategory::Value);
1214 let type_name = type_node
1215 .and_then(|type_node| cpp_type_name_components(type_node, source))
1216 .unwrap_or_default();
1217 let class_type = type_node.is_some_and(|type_node| {
1218 !matches!(
1219 type_node.kind(),
1220 "primitive_type" | "sized_type_specifier" | "auto" | "decltype"
1221 )
1222 });
1223 CppCatchFact {
1224 range: node_range(node),
1225 value_category,
1226 type_name,
1227 class_type,
1228 }
1229}
1230
1231fn capture_throw_fact(node: Node<'_>, source: &str) -> CppThrowFact {
1232 let expression = node.named_child(0);
1233 let (operand_shape, thrown_type) = match expression {
1234 None => (CppThrowOperandShape::Rethrow, Vec::new()),
1235 Some(expression) if expression.kind() == "new_expression" => (
1236 CppThrowOperandShape::Pointer,
1237 expression
1238 .child_by_field_name("type")
1239 .and_then(|type_node| cpp_type_name_components(type_node, source))
1240 .unwrap_or_default(),
1241 ),
1242 Some(_) => (CppThrowOperandShape::Value, Vec::new()),
1243 };
1244 CppThrowFact {
1245 range: node_range(node),
1246 operand_shape,
1247 thrown_type,
1248 }
1249}
1250
1251fn function_parameter_count(function: Node<'_>) -> usize {
1252 function
1253 .child_by_field_name("parameters")
1254 .map_or(0, |parameters| parameters.named_child_count())
1255}
1256
1257fn callable_parameter_shape(
1258 function: Node<'_>,
1259 source: &str,
1260 ancestry: &ParentIndex<'_>,
1261 owner_name: Option<&str>,
1262) -> (bool, bool, bool) {
1263 let Some(parameters) = function.child_by_field_name("parameters") else {
1264 return (false, false, true);
1265 };
1266 let parameter_nodes: Vec<_> = parameters.named_children(&mut parameters.walk()).collect();
1267 let remaining_parameters_defaulted = parameter_nodes.len() > 1
1268 && parameter_nodes
1269 .iter()
1270 .skip(1)
1271 .all(|parameter| parameter.child_by_field_name("default_value").is_some());
1272 let Some(first) = parameter_nodes.first().copied() else {
1273 return (false, false, remaining_parameters_defaulted);
1274 };
1275 let Some(_type_node) = first.child_by_field_name("type") else {
1276 return (false, false, remaining_parameters_defaulted);
1277 };
1278 let Some(owner_name) = owner_name else {
1279 return (false, false, remaining_parameters_defaulted);
1280 };
1281 let comparable = cpp_comparable_parameter_shapes(function, source, ancestry);
1282 let Some(CppComparableSlot::Shape(parameter)) = comparable.first() else {
1283 return (false, false, remaining_parameters_defaulted);
1284 };
1285 let reference = match parameter.node(parameter.root()) {
1286 CppComparableNode::Reference { inner } => Some(*inner),
1287 _ => None,
1288 };
1289 let Some(inner) = reference else {
1290 return (false, false, remaining_parameters_defaulted);
1291 };
1292 let CppComparableNode::Named { name, .. } = parameter.node(inner) else {
1293 return (false, false, remaining_parameters_defaulted);
1294 };
1295 let same_owner = name.path().last().is_some_and(|name| name == owner_name);
1296 if !same_owner {
1297 return (false, false, remaining_parameters_defaulted);
1298 }
1299 let rvalue = first
1300 .child_by_field_name("declarator")
1301 .is_some_and(|declarator| declarator_contains_rvalue_reference(declarator));
1302 let copy = !rvalue;
1303 let move_parameter = rvalue;
1304 (copy, move_parameter, remaining_parameters_defaulted)
1305}
1306
1307fn declarator_contains_rvalue_reference(node: Node<'_>) -> bool {
1308 let mut stack = vec![node];
1309 while let Some(current) = stack.pop() {
1310 if crate::declarations::reference_declarator_is_rvalue(current) {
1311 return true;
1312 }
1313 let mut cursor = current.walk();
1314 stack.extend(current.named_children(&mut cursor));
1315 }
1316 false
1317}
1318
1319fn classify_parameter_declarator(node: Node<'_>) -> CppParameterValueCategory {
1320 if declarator_contains_rvalue_reference(node) {
1321 CppParameterValueCategory::RValueReference
1322 } else {
1323 let mut stack = vec![node];
1324 while let Some(current) = stack.pop() {
1325 if current.kind() == "reference_declarator" {
1326 return CppParameterValueCategory::LValueReference;
1327 }
1328 if current.kind() == "pointer_declarator" {
1329 return CppParameterValueCategory::Pointer;
1330 }
1331 let mut cursor = current.walk();
1332 stack.extend(current.named_children(&mut cursor));
1333 }
1334 CppParameterValueCategory::Value
1335 }
1336}
1337
1338fn assignment_return_shape(
1339 node: Node<'_>,
1340 source: &str,
1341 owner_name: Option<&str>,
1342) -> CppAssignmentReturnShape {
1343 let mut current = node.child_by_field_name("declarator");
1344 let mut reference = false;
1345 let mut pointer = false;
1346 while let Some(declarator) = current {
1347 reference |= declarator.kind() == "reference_declarator";
1348 pointer |= declarator.kind() == "pointer_declarator";
1349 current = declarator.child_by_field_name("declarator");
1350 }
1351 if !reference {
1352 return if pointer {
1353 CppAssignmentReturnShape::Unknown
1354 } else {
1355 CppAssignmentReturnShape::Value
1356 };
1357 }
1358 let return_type = node.child_by_field_name("type");
1359 if owner_name.is_some_and(|owner| {
1360 return_type
1361 .and_then(|type_node| cpp_type_name_components(type_node, source))
1362 .and_then(|components| components.last().cloned())
1363 .is_some_and(|name| name == owner)
1364 }) {
1365 CppAssignmentReturnShape::ReferenceToThis
1366 } else {
1367 CppAssignmentReturnShape::ReferenceOther
1368 }
1369}
1370
1371fn constructor_initializer_target(
1372 node: Node<'_>,
1373 source: &str,
1374 owner_name: Option<&str>,
1375) -> Option<Vec<String>> {
1376 let owner_name = owner_name?;
1377 let mut cursor = node.walk();
1378 let initializer_list = node
1379 .named_children(&mut cursor)
1380 .find(|child| child.kind() == "field_initializer_list")?;
1381 let mut cursor = initializer_list.walk();
1382 initializer_list
1383 .named_children(&mut cursor)
1384 .filter(|child| child.kind() == "field_initializer")
1385 .find_map(|initializer| {
1386 let name = initializer
1387 .child_by_field_name("name")
1388 .or_else(|| initializer.child_by_field_name("field"))
1389 .or_else(|| {
1390 let mut cursor = initializer.walk();
1391 initializer.named_children(&mut cursor).find(|child| {
1392 matches!(
1393 child.kind(),
1394 "field_identifier"
1395 | "type_identifier"
1396 | "qualified_identifier"
1397 | "scoped_type_identifier"
1398 )
1399 })
1400 })?;
1401 let components = cpp_type_name_components(name, source)?;
1402 components
1403 .last()
1404 .is_some_and(|component| component == owner_name)
1405 .then_some(components)
1406 })
1407}
1408
1409fn constructor_delegation_calls(
1410 node: Node<'_>,
1411 source: &str,
1412 ancestry: &ParentIndex<'_>,
1413 owner_name: Option<&str>,
1414) -> Vec<Range> {
1415 let Some(owner_name) = owner_name else {
1416 return Vec::new();
1417 };
1418 let Some(body) = node.child_by_field_name("body") else {
1419 return Vec::new();
1420 };
1421 let mut stack = vec![body];
1422 let mut calls = Vec::new();
1423 while let Some(current) = stack.pop() {
1424 if current.kind() == "call_expression"
1425 && ancestry
1426 .parent(current)
1427 .is_some_and(|parent| parent.kind() == "expression_statement")
1428 && let Some(function) = current.child_by_field_name("function")
1429 && function.kind() == "identifier"
1430 && node_text(function, source) == owner_name
1431 {
1432 calls.push(node_range(current));
1433 }
1434 let mut cursor = current.walk();
1435 stack.extend(current.named_children(&mut cursor));
1436 }
1437 calls
1438}
1439
1440fn enclosing_owner_path(node: Node<'_>, ancestry: &ParentIndex<'_>, source: &str) -> Vec<String> {
1441 let mut owners = Vec::new();
1442 let mut current = ancestry.parent(node);
1443 while let Some(parent) = current {
1444 if matches!(
1445 parent.kind(),
1446 "namespace_definition"
1447 | "class_specifier"
1448 | "struct_specifier"
1449 | "union_specifier"
1450 | "enum_specifier"
1451 ) && let Some(name) = parent.child_by_field_name("name")
1452 {
1453 owners.push(node_text(name, source).to_owned());
1454 }
1455 current = ancestry.parent(parent);
1456 }
1457 owners.reverse();
1458 owners
1459}
1460
1461fn append_owner_components(owner: &mut Vec<String>, written: &[String]) {
1462 let overlap = (0..=owner.len().min(written.len()))
1463 .rev()
1464 .find(|length| owner[owner.len().saturating_sub(*length)..] == written[..*length])
1465 .unwrap_or(0);
1466 owner.extend(written[overlap..].iter().cloned());
1467}
1468
1469fn member_access_specifier(
1470 node: Node<'_>,
1471 source: &str,
1472 ancestry: &ParentIndex<'_>,
1473) -> CppAccessSpecifier {
1474 let mut current = ancestry.parent(node);
1475 while let Some(parent) = current {
1476 if matches!(
1477 parent.kind(),
1478 "class_specifier" | "struct_specifier" | "union_specifier"
1479 ) {
1480 let default = if parent.kind() == "class_specifier" {
1481 CppAccessSpecifier::Private
1482 } else {
1483 CppAccessSpecifier::Public
1484 };
1485 let Some(body) = parent.child_by_field_name("body") else {
1486 return CppAccessSpecifier::Unknown;
1487 };
1488 let mut access = default;
1489 let mut cursor = body.walk();
1490 for child in body.named_children(&mut cursor) {
1491 if child.start_byte() >= node.start_byte() {
1492 break;
1493 }
1494 if child.kind() == "access_specifier" {
1495 access = match child.child(0).map(|keyword| keyword.kind()) {
1496 Some("public") => CppAccessSpecifier::Public,
1497 Some("protected") => CppAccessSpecifier::Protected,
1498 Some("private") => CppAccessSpecifier::Private,
1499 _ => CppAccessSpecifier::Unknown,
1500 };
1501 } else if let Some(label) = cpp_macro_swallowed_access_label(child, source) {
1502 access = match label.access {
1503 "public" => CppAccessSpecifier::Public,
1504 "protected" => CppAccessSpecifier::Protected,
1505 _ => CppAccessSpecifier::Private,
1506 };
1507 }
1508 }
1509 return access;
1510 }
1511 current = ancestry.parent(parent);
1512 }
1513 CppAccessSpecifier::Unknown
1514}
1515
1516pub(crate) fn base_access_specifier(
1517 clause: Node<'_>,
1518 base: Node<'_>,
1519 class_kind: &str,
1520) -> CppAccessSpecifier {
1521 let default = if matches!(class_kind, "struct_specifier" | "union_specifier") {
1522 CppAccessSpecifier::Public
1523 } else {
1524 CppAccessSpecifier::Private
1525 };
1526 let mut access = default;
1527 let mut cursor = clause.walk();
1528 for child in clause.children(&mut cursor) {
1529 if child.start_byte() >= base.start_byte() {
1530 break;
1531 }
1532 if child.kind() == "," {
1533 access = default;
1534 } else if child.kind() == "access_specifier" {
1535 access = match child.child(0).map(|keyword| keyword.kind()) {
1536 Some("public") => CppAccessSpecifier::Public,
1537 Some("protected") => CppAccessSpecifier::Protected,
1538 Some("private") => CppAccessSpecifier::Private,
1539 _ => CppAccessSpecifier::Unknown,
1540 };
1541 }
1542 }
1543 access
1544}
1545
1546fn tree_contains_kind(node: Node<'_>, kind: &str) -> bool {
1547 let mut stack = vec![node];
1548 while let Some(current) = stack.pop() {
1549 if current.kind() == kind {
1550 return true;
1551 }
1552 let mut cursor = current.walk();
1553 stack.extend(current.named_children(&mut cursor));
1554 }
1555 false
1556}
1557
1558fn tree_contains_pure_virtual(node: Node<'_>, source: &str) -> bool {
1559 let mut stack = vec![node];
1560 while let Some(current) = stack.pop() {
1561 if current.kind() == "field_declaration"
1562 && current
1563 .child_by_field_name("default_value")
1564 .is_some_and(|value| {
1565 value.kind() == "number_literal" && node_text(value, source).trim() == "0"
1566 })
1567 && current
1568 .child_by_field_name("declarator")
1569 .and_then(extract_function_declarator)
1570 .is_some()
1571 {
1572 return true;
1573 }
1574 let mut cursor = current.walk();
1575 stack.extend(current.named_children(&mut cursor));
1576 }
1577 tree_contains_kind(node, "pure_virtual_clause")
1578}
1579
1580fn cpp_callable_is_deduction_guide(
1581 declaration: Node<'_>,
1582 function_declarator: Node<'_>,
1583 unit: &CodeUnit,
1584 source: &str,
1585) -> bool {
1586 if declaration.kind() != "declaration"
1587 || declaration.child_by_field_name("type").is_some()
1588 || function_declarator.kind() != "function_declarator"
1589 {
1590 return false;
1591 }
1592 let mut cursor = function_declarator.walk();
1593 function_declarator
1594 .named_children(&mut cursor)
1595 .any(|child| child.kind() == "trailing_return_type")
1596 && declarator_name_node(function_declarator)
1597 .is_some_and(|name| node_text(name, source) == unit.identifier())
1598}
1599
1600#[derive(Debug, Clone)]
1602pub struct CppFileSourceFacts {
1603 pub source: brokk_bifrost_core::analyzer::source_facts::SourceFactRows,
1604 pub facts: CppSourceFacts,
1605 pub declaration_units: brokk_bifrost_core::hash::HashMap<SourceDeclarationId, Vec<CodeUnit>>,
1606 by_unit: brokk_bifrost_core::hash::HashMap<CodeUnit, Vec<usize>>,
1607}
1608
1609impl CppFileSourceFacts {
1610 pub fn new(
1611 source: brokk_bifrost_core::analyzer::source_facts::SourceFactRows,
1612 facts: CppSourceFacts,
1613 declaration_units: brokk_bifrost_core::hash::HashMap<SourceDeclarationId, Vec<CodeUnit>>,
1614 ) -> Self {
1615 assert!(
1616 facts.valid_links(&source),
1617 "invalid C++ canonical source links"
1618 );
1619 let mut by_unit: brokk_bifrost_core::hash::HashMap<_, Vec<_>> = Default::default();
1620 for (index, fact) in facts.declarations.iter().enumerate() {
1621 if let Some(units) = declaration_units.get(&fact.declaration) {
1622 for unit in units {
1623 by_unit.entry(unit.clone()).or_default().push(index);
1624 }
1625 }
1626 }
1627 Self {
1628 source,
1629 facts,
1630 declaration_units,
1631 by_unit,
1632 }
1633 }
1634
1635 pub fn for_unit(&self, unit: &CodeUnit) -> impl Iterator<Item = &CppDeclarationSourceFact> {
1636 self.by_unit
1637 .get(unit)
1638 .into_iter()
1639 .flatten()
1640 .map(|index| &self.facts.declarations[*index])
1641 }
1642
1643 pub fn estimated_retained_bytes(&self) -> usize {
1644 self.source.estimated_bytes()
1645 + self.facts.estimated_retained_bytes()
1646 + self
1647 .declaration_units
1648 .values()
1649 .map(|units| units.capacity() * std::mem::size_of::<CodeUnit>())
1650 .sum::<usize>()
1651 + self
1652 .by_unit
1653 .values()
1654 .map(|indices| indices.capacity() * std::mem::size_of::<usize>())
1655 .sum::<usize>()
1656 }
1657}
1658
1659#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1661pub struct CppNavigationOccurrence {
1662 pub range: brokk_bifrost_core::analyzer::Range,
1663 pub role: CppOccurrenceRole,
1664 pub conditional_family: Option<(usize, usize)>,
1665}
1666
1667#[derive(Debug, Clone, PartialEq, Eq)]
1669pub struct CppDeclarationOccurrence {
1670 pub fact: CppDeclarationSourceFact,
1671 pub range: brokk_bifrost_core::analyzer::Range,
1672 pub name_range: Option<brokk_bifrost_core::analyzer::Range>,
1673 pub exhaustive_family: Option<(usize, usize)>,
1674 pub displaced_namespace_closing_brace: Option<usize>,
1675}
1676
1677impl CppFileSourceFacts {
1678 pub fn declaration_occurrences(&self, unit: &CodeUnit) -> Vec<CppDeclarationOccurrence> {
1679 self.for_unit(unit)
1680 .map(|fact| {
1681 let declaration = self.source.declaration(fact.declaration);
1682 CppDeclarationOccurrence {
1683 range: self.source.occurrence(declaration.occurrence).range,
1684 name_range: declaration.name.map(|id| self.source.occurrence(id).range),
1685 exhaustive_family: fact.exhaustive_conditional_family.map(|id| {
1686 let range = self.source.occurrence(id).range;
1687 (range.start_byte, range.end_byte)
1688 }),
1689 displaced_namespace_closing_brace: fact
1690 .displaced_namespace_closing_brace
1691 .map(|id| self.source.occurrence(id).range.start_byte),
1692 fact: fact.clone(),
1693 }
1694 })
1695 .collect()
1696 }
1697
1698 pub fn navigation_occurrence(
1699 &self,
1700 fact: &CppDeclarationSourceFact,
1701 ) -> CppNavigationOccurrence {
1702 let declaration = self.source.declaration(fact.declaration);
1703 let range = self.source.occurrence(declaration.occurrence).range;
1704 let conditional_family = fact.conditional_family.map(|id| {
1705 let range = self.source.occurrence(id).range;
1706 (range.start_byte, range.end_byte)
1707 });
1708 CppNavigationOccurrence {
1709 range,
1710 role: fact.occurrence_role,
1711 conditional_family,
1712 }
1713 }
1714}
1715
1716#[cfg(test)]
1717mod tests {
1718 use super::*;
1719
1720 #[test]
1721 #[brokk_bifrost_test_macros::hardwon(
1722 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: range bindings, member const and numeric values used wrong syntax"
1723 )]
1724 fn source_fact_grammar_boundary() {
1725 let source = "void f() { for (char i : xs) { for (char item : xs) {} } }\nstruct C { void f(const int x); void g() const; };\nint n[] = {0X10, 0B10, 1uLL, 2Lu, 010};\n";
1726 let mut parser = Parser::new();
1727 parser
1728 .set_language(&tree_sitter_cpp::LANGUAGE.into())
1729 .unwrap();
1730 let tree = parser.parse(source, None).unwrap();
1731 assert!(!tree.root_node().has_error());
1732 let mut stack = vec![tree.root_node()];
1734 let mut loops = Vec::new();
1735 let mut qualifiers = Vec::new();
1736 let mut literals = Vec::new();
1737 while let Some(node) = stack.pop() {
1738 match node.kind() {
1739 "for_range_loop" => {
1740 assert!(node.child_by_field_name("left").is_none());
1741 loops.push((
1742 node.child_by_field_name("type").unwrap().byte_range(),
1743 node.child_by_field_name("declarator").unwrap().byte_range(),
1744 ));
1745 }
1746 "type_qualifier" => {
1747 qualifiers.push((node.parent().unwrap().kind(), node.byte_range()))
1748 }
1749 "number_literal" => literals.push(node.byte_range()),
1750 _ => {}
1751 }
1752 let children: Vec<_> = node.named_children(&mut node.walk()).collect();
1753 stack.extend(children.into_iter().rev());
1754 }
1755 assert_eq!(loops, [(16..20, 21..22), (36..40, 41..45)]);
1756 assert_eq!(
1757 qualifiers,
1758 [
1759 ("parameter_declaration", 77..82),
1760 ("function_declarator", 100..105)
1761 ]
1762 );
1763 assert_eq!(literals, [121..125, 127..131, 133..137, 139..142, 144..147]);
1764 let rows = parse_cpp_expression_source_facts(source);
1765 let bindings: Vec<_> = rows
1766 .expressions
1767 .iter()
1768 .filter(|f| f.kind == CppExpressionKind::LoopVariable)
1769 .map(|f| {
1770 (
1771 f.name.as_deref(),
1772 f.type_text.as_deref(),
1773 f.loop_has_short_ancestor,
1774 )
1775 })
1776 .collect();
1777 let methods: Vec<_> = rows
1778 .expressions
1779 .iter()
1780 .filter(|f| {
1781 f.kind == CppExpressionKind::FunctionSignature && f.member_function == Some(true)
1782 })
1783 .map(|f| (f.name.as_deref(), f.member_function_const))
1784 .collect();
1785 let values: Vec<_> = rows
1786 .expressions
1787 .iter()
1788 .filter(|f| f.kind == CppExpressionKind::IntegerLiteral)
1789 .map(|f| f.integer_value)
1790 .collect();
1791 assert_eq!(
1792 (bindings, methods, values),
1793 (
1794 vec![
1795 (Some("i"), Some("char"), Some(false)),
1796 (Some("item"), Some("char"), Some(true))
1797 ],
1798 vec![(Some("f"), Some(false)), (Some("g"), Some(true))],
1799 vec![Some(16), Some(2), Some(1), Some(2), Some(8)]
1800 )
1801 );
1802 let rows = parse_cpp_expression_source_facts(
1805 "void f() { for (int setup = 0; char i : xs) { for (int j = 0; j < 3; ++j) {} } }",
1806 );
1807 let bindings: Vec<_> = rows
1808 .expressions
1809 .iter()
1810 .filter(|f| f.kind == CppExpressionKind::LoopVariable)
1811 .map(|f| {
1812 (
1813 f.name.as_deref(),
1814 f.type_text.as_deref(),
1815 f.loop_has_short_ancestor,
1816 )
1817 })
1818 .collect();
1819 assert_eq!(
1820 bindings,
1821 [
1822 (Some("i"), Some("char"), Some(false)),
1823 (Some("j"), Some("int"), Some(true))
1824 ]
1825 );
1826 }
1827
1828 #[test]
1829 #[brokk_bifrost_test_macros::hardwon(
1830 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: base access leaked across commas"
1831 )]
1832 fn source_fact_base_access_defaults() {
1833 let source = "struct A {}; struct B {}; struct S : private A, B {}; class C : public A, B {}; struct P : protected A {};";
1835 let mut parser = Parser::new();
1836 parser
1837 .set_language(&tree_sitter_cpp::LANGUAGE.into())
1838 .unwrap();
1839 let tree = parser.parse(source, None).unwrap();
1840 assert!(!tree.root_node().has_error());
1841 let mut stack = vec![tree.root_node()];
1842 let mut access = Vec::new();
1843 while let Some(node) = stack.pop() {
1844 if node.kind() == "base_class_clause" {
1845 for base in node
1846 .named_children(&mut node.walk())
1847 .filter(|n| n.kind() == "type_identifier")
1848 {
1849 access.push(base_access_specifier(
1850 node,
1851 base,
1852 node.parent().unwrap().kind(),
1853 ));
1854 }
1855 }
1856 let children: Vec<_> = node.named_children(&mut node.walk()).collect();
1857 stack.extend(children.into_iter().rev());
1858 }
1859 assert_eq!(
1860 access,
1861 [
1862 CppAccessSpecifier::Private,
1863 CppAccessSpecifier::Public,
1864 CppAccessSpecifier::Public,
1865 CppAccessSpecifier::Private,
1866 CppAccessSpecifier::Protected
1867 ]
1868 );
1869 }
1870
1871 #[test]
1872 #[brokk_bifrost_test_macros::hardwon(
1873 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: parameter indirection reused pointee size"
1874 )]
1875 fn source_fact_parameter_sizes_respect_indirection() {
1876 let rows = parse_cpp_expression_source_facts(
1877 "void f(char a, signed char b, unsigned char c, char *p, char &r, char xs[3]);",
1878 );
1879 let sizes: Vec<_> = rows
1880 .expressions
1881 .iter()
1882 .filter(|f| {
1883 f.name.is_some()
1884 && matches!(
1885 f.kind,
1886 CppExpressionKind::FunctionParameter | CppExpressionKind::ArrayParameter
1887 )
1888 })
1889 .map(|f| (f.name.as_deref(), f.type_size_bytes, f.type_size_exact))
1890 .collect();
1891 assert_eq!(
1892 sizes,
1893 [
1894 (Some("a"), Some(1), Some(true)),
1895 (Some("b"), Some(1), Some(true)),
1896 (Some("c"), Some(1), Some(true)),
1897 (Some("p"), None, Some(false)),
1898 (Some("r"), None, Some(false)),
1899 (Some("xs"), None, Some(false))
1900 ]
1901 );
1902 }
1903
1904 #[test]
1905 #[brokk_bifrost_test_macros::hardwon(
1906 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: union sizes summed and later field declarators vanished"
1907 )]
1908 fn source_fact_record_sizes_follow_member_layout() {
1909 let rows = parse_cpp_expression_source_facts(
1910 "union U { char a[2]; char b[3]; }; struct S { char a, b; }; void f(U u, S s);",
1911 );
1912 let sizes: Vec<_> = rows
1913 .expressions
1914 .iter()
1915 .filter(|f| f.kind == CppExpressionKind::FunctionParameter)
1916 .map(|f| (f.name.as_deref(), f.type_size_bytes))
1917 .collect();
1918 assert_eq!(sizes, [(Some("u"), Some(3)), (Some("s"), Some(2))]);
1920 }
1921
1922 const REFERENCES: &str = "struct C {\n C(C& a); C(const C& a); C(C&& a); C(const C&& a);\n C(C&& a, int n = 0); C(C* a);\n C& operator=(const C& a); C& operator=(C&& a);\n operator C&(); operator C&&();\n};\nC& l = c; C&& r = f();\nvoid f() { try {} catch(C& e) {} try {} catch(const C& e) {} try {} catch(C&& e) {} }\nstruct V {\n V(V& a);\n V& operator=(V& a);\n V(const V& a);\n V& operator=(const V& a);\n V(volatile V& a);\n V& operator=(volatile V& a);\n V(const volatile V& a);\n V& operator=(const volatile V& a);\n V(V& a, int n = 0);\n V(volatile V& a, int n = 0);\n};\n";
1923
1924 #[test]
1925 #[brokk_bifrost_test_macros::hardwon(
1926 "d4f00f8567e4: quicksilver round 4 on brokk-bifrost-cpp: unnamed && tokens hid rvalue references"
1927 )]
1928 #[brokk_bifrost_test_macros::hardwon(
1929 "d4f00f8567e4: quicksilver round 4 on brokk-bifrost-cpp: a mutable C& copy constructor was not classified as copy"
1930 )]
1931 fn cpp_reference_token_grammar() {
1932 let mut parser = Parser::new();
1933 parser
1934 .set_language(&tree_sitter_cpp::LANGUAGE.into())
1935 .unwrap();
1936 let tree = parser.parse(REFERENCES, None).unwrap();
1937 assert!(!tree.root_node().has_error());
1938 let expected = "reference_declarator 15..18 & 15..16 identifier 17..18
1941reference_declarator 30..33 & 30..31 identifier 32..33
1942reference_declarator 39..43 && 39..41 identifier 42..43
1943reference_declarator 55..59 && 55..57 identifier 58..59
1944reference_declarator 66..70 && 66..68 identifier 69..70
1945reference_declarator 95..118 & 95..96 function_declarator 97..118
1946reference_declarator 114..117 & 114..115 identifier 116..117
1947reference_declarator 121..139 & 121..122 function_declarator 123..139
1948reference_declarator 134..138 && 134..136 identifier 137..138
1949abstract_reference_declarator 152..155 & 152..153 abstract_function_declarator 153..155
1950abstract_reference_declarator 167..171 && 167..169 abstract_function_declarator 169..171
1951reference_declarator 177..180 & 177..178 identifier 179..180
1952reference_declarator 187..191 && 187..189 identifier 190..191
1953reference_declarator 224..227 & 224..225 identifier 226..227
1954reference_declarator 252..255 & 252..253 identifier 254..255
1955reference_declarator 274..278 && 274..276 identifier 277..278
1956reference_declarator 300..303 & 300..301 identifier 302..303
1957reference_declarator 308..325 & 308..309 function_declarator 310..325
1958reference_declarator 321..324 & 321..322 identifier 323..324
1959reference_declarator 337..340 & 337..338 identifier 339..340
1960reference_declarator 345..368 & 345..346 function_declarator 347..368
1961reference_declarator 364..367 & 364..365 identifier 366..367
1962reference_declarator 383..386 & 383..384 identifier 385..386
1963reference_declarator 391..417 & 391..392 function_declarator 393..417
1964reference_declarator 413..416 & 413..414 identifier 415..416
1965reference_declarator 438..441 & 438..439 identifier 440..441
1966reference_declarator 446..478 & 446..447 function_declarator 448..478
1967reference_declarator 474..477 & 474..475 identifier 476..477
1968reference_declarator 484..487 & 484..485 identifier 486..487
1969reference_declarator 514..517 & 514..515 identifier 516..517";
1970 let mut stack = vec![tree.root_node()];
1971 let mut actual = Vec::new();
1972 let mut references = Vec::new();
1973 let mut qualifiers = Vec::new();
1974 while let Some(node) = stack.pop() {
1975 if node.kind() == "type_qualifier" && node.start_byte() >= 285 {
1976 assert_eq!(node.parent().unwrap().kind(), "parameter_declaration");
1977 qualifiers.push((
1978 node.utf8_text(REFERENCES.as_bytes()).unwrap(),
1979 node.byte_range(),
1980 ));
1981 }
1982 if matches!(
1983 node.kind(),
1984 "reference_declarator" | "abstract_reference_declarator"
1985 ) {
1986 let token = node.child(0).unwrap();
1987 let wrapped = node.named_child(0).unwrap();
1988 assert!(!token.is_named());
1989 assert!(node.child_by_field_name("declarator").is_none());
1990 assert_eq!(node.field_name_for_child(0), None);
1991 assert_eq!(node.field_name_for_child(1), None);
1992 if node.parent().unwrap().kind() == "parameter_declaration" {
1993 assert_eq!(
1994 node.parent().unwrap().child_by_field_name("declarator"),
1995 Some(node)
1996 );
1997 }
1998 actual.push(format!(
1999 "{} {:?} {} {:?} {} {:?}",
2000 node.kind(),
2001 node.byte_range(),
2002 token.kind(),
2003 token.byte_range(),
2004 wrapped.kind(),
2005 wrapped.byte_range()
2006 ));
2007 references.push(node);
2008 }
2009 let children: Vec<_> = node.named_children(&mut node.walk()).collect();
2010 stack.extend(children.into_iter().rev());
2011 }
2012 assert_eq!(actual.join("\n"), expected);
2013 assert_eq!(
2014 qualifiers,
2015 [
2016 ("const", 330..335),
2017 ("const", 357..362),
2018 ("volatile", 373..381),
2019 ("volatile", 403..411),
2020 ("const", 422..427),
2021 ("volatile", 428..436),
2022 ("const", 458..463),
2023 ("volatile", 464..472),
2024 ("volatile", 504..512),
2025 ]
2026 );
2027 for node in references {
2028 let token = node.child(0).unwrap();
2029 let wrapped = node.named_child(0).unwrap();
2030 if wrapped.kind() == "function_declarator" {
2033 continue;
2034 }
2035 assert_eq!(
2036 declarator_contains_rvalue_reference(node),
2037 token.kind() == "&&",
2038 "reference at {:?}",
2039 node.byte_range()
2040 );
2041 }
2042 let rows = parse_cpp_class_fact_rows(REFERENCES);
2043 use CppSpecialMemberKind::{
2044 CopyAssignment, CopyConstructor, MoveAssignment, MoveConstructor, None as NotSpecial,
2045 };
2046 let members: Vec<_> = rows
2049 .members
2050 .iter()
2051 .take(8)
2052 .map(|member| member.special_member)
2053 .collect();
2054 assert_eq!(
2055 members,
2056 [
2057 CopyConstructor,
2058 CopyConstructor,
2059 MoveConstructor,
2060 MoveConstructor,
2061 MoveConstructor,
2062 NotSpecial,
2063 CopyAssignment,
2064 MoveAssignment
2065 ]
2066 );
2067 assert!(rows.members[4].remaining_parameters_defaulted);
2068 let cv_members: Vec<_> = rows
2069 .members
2070 .iter()
2071 .filter(|member| member.owner == ["V"])
2072 .collect();
2073 assert_eq!(
2074 cv_members
2075 .iter()
2076 .map(|member| member.special_member)
2077 .collect::<Vec<_>>(),
2078 [
2079 CopyConstructor,
2080 CopyAssignment,
2081 CopyConstructor,
2082 CopyAssignment,
2083 CopyConstructor,
2084 CopyAssignment,
2085 CopyConstructor,
2086 CopyAssignment,
2087 CopyConstructor,
2088 CopyConstructor
2089 ]
2090 );
2091 assert!(cv_members.iter().all(|member| member.copy_parameter));
2092 assert!(
2093 cv_members[8..]
2094 .iter()
2095 .all(|member| member.remaining_parameters_defaulted)
2096 );
2097 assert_eq!(
2098 rows.catches
2099 .iter()
2100 .map(|catch| catch.value_category)
2101 .collect::<Vec<_>>(),
2102 [
2103 CppParameterValueCategory::LValueReference,
2104 CppParameterValueCategory::LValueReference,
2105 CppParameterValueCategory::RValueReference
2106 ]
2107 );
2108 }
2109
2110 #[test]
2111 fn specialized_declaration_name_grammar_boundary() {
2112 for (source, field_kind, expected, offset) in [
2113 ("struct Plain {};", "type_identifier", "Plain", 7),
2114 ("template<> struct Box<int> {};", "template_type", "Box", 18),
2115 (
2116 "struct ns::Qualified {};",
2117 "qualified_identifier",
2118 "Qualified",
2119 11,
2120 ),
2121 ] {
2122 let tree = crate::diagnostics::parse_cpp_tree(source);
2123 assert!(!tree.root_node().has_error());
2124 let mut stack = vec![tree.root_node()];
2125 let mut names = Vec::new();
2126 while let Some(node) = stack.pop() {
2127 if node.kind() == "struct_specifier" {
2128 let field = node.child_by_field_name("name").unwrap();
2129 assert_eq!(field.kind(), field_kind);
2130 let unit = CodeUnit::new(
2131 brokk_bifrost_core::analyzer::ProjectFile::new(
2132 std::env::temp_dir(),
2133 "names.cpp",
2134 ),
2135 brokk_bifrost_core::analyzer::model::CodeUnitType::Class,
2136 "",
2137 expected,
2138 );
2139 let name = declaration_name(node, &unit, source).unwrap();
2140 names.push((node_text(name, source), name.byte_range()));
2141 }
2142 stack.extend(node.named_children(&mut node.walk()));
2143 }
2144 assert_eq!(names, [(expected, offset..offset + expected.len())]);
2145 }
2146 }
2147}