Skip to main content

brokk_bifrost_cpp/
source_facts.rs

1//! Declaration properties interpreted while the coordinated producer owns syntax.
2
3use 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
22/// Parse one source snapshot into the expression-only projection used by RQL.
23/// This helper is intentionally query-scoped: it owns no cross-file cache and
24/// retains only the source arena and rows for this one file.
25pub 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
56/// Capture one expression-shaped row while the primary tree is live. The
57/// caller has already selected the occurrence; this function only interprets
58/// the selected AST node and never searches source text for syntax.
59pub(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    // Only a scalar/by-value declarator has the size of its written type.
457    // Pointer ABI size and adjusted array parameters are not known here;
458    // references likewise carry no exact-size evidence in this projection.
459    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
734/// Parse the bounded C++ statement rows used by the statement-fact RQL
735/// relation. This is query-lifetime data; it owns no cross-file state.
736pub 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
750/// Parse one source snapshot into the class and exception projection used by
751/// RQL. The rows are query-lifetime and own no cross-file state.
752pub 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
813/// Capture the structured type properties of one field declaration while its
814/// declaration AST is still available.
815pub(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
848/// Capture one `namespace name = target;` definition while the primary tree is
849/// live. The target path, the enclosing namespaces and the guard environment
850/// come from the same structured helpers that using declarations use.
851pub(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    // A function-local class and its members are scoped by their callable
937    // and block, which the written class chain alone cannot spell: it would
938    // give a local `Widget` the path of a global one. Their structured
939    // identity is that scope, so the path follows it.
940    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                        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                    // Only identity qualifiers participate in overload hiding.
1036                    // The declarator can also contain override/final specifiers.
1037                    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
1087/// Add the class and exception rows whose meaning lives below a declaration
1088/// node. This is called by the primary iterative walk, while the tree and its
1089/// parent index are still owned by the producer.
1090pub(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 comparable = cpp_comparable_parameter_shapes(function_declarator, source, ancestry);
1153    let parameter_shape = comparable
1154        .as_deref()
1155        .and_then(|shapes| callable_parameter_shape(function_declarator, owner_name, shapes));
1156    // An unavailable list proves no special-member parameter shape. Keep the
1157    // other source facts, but never infer a default constructor from it.
1158    let (copy_parameter, move_parameter, remaining_parameters_defaulted) =
1159        parameter_shape.unwrap_or((false, false, false));
1160    let special_member = match role {
1161        CppMemberRole::Destructor => CppSpecialMemberKind::Destructor,
1162        CppMemberRole::Constructor if copy_parameter => CppSpecialMemberKind::CopyConstructor,
1163        CppMemberRole::Constructor if move_parameter => CppSpecialMemberKind::MoveConstructor,
1164        CppMemberRole::Constructor if comparable.as_ref().is_some_and(Vec::is_empty) => {
1165            CppSpecialMemberKind::DefaultConstructor
1166        }
1167        CppMemberRole::AssignmentOperator if copy_parameter => CppSpecialMemberKind::CopyAssignment,
1168        CppMemberRole::AssignmentOperator if move_parameter => CppSpecialMemberKind::MoveAssignment,
1169        _ => CppSpecialMemberKind::None,
1170    };
1171    let state = if tree_contains_kind(node, "delete_method_clause") {
1172        CppSpecialMemberState::Deleted
1173    } else if tree_contains_kind(node, "default_method_clause") {
1174        CppSpecialMemberState::Defaulted
1175    } else {
1176        CppSpecialMemberState::Declared
1177    };
1178    let access = member_access_specifier(node, source, ancestry);
1179    let assignment_return = if role == CppMemberRole::AssignmentOperator {
1180        assignment_return_shape(node, source, owner_name)
1181    } else {
1182        CppAssignmentReturnShape::Unknown
1183    };
1184    let initializer_target = if role == CppMemberRole::Constructor {
1185        constructor_initializer_target(node, source, owner_name)
1186    } else {
1187        None
1188    };
1189    let delegation_calls = if role == CppMemberRole::Constructor {
1190        constructor_delegation_calls(node, source, ancestry, owner_name)
1191    } else {
1192        Vec::new()
1193    };
1194    Some(CppMemberFact {
1195        range: node_range(node),
1196        owner,
1197        name,
1198        role,
1199        special_member,
1200        state,
1201        access,
1202        copy_parameter,
1203        remaining_parameters_defaulted,
1204        assignment_return,
1205        initializer_target,
1206        delegation_calls,
1207        pure_virtual: tree_contains_pure_virtual(node, source),
1208    })
1209}
1210
1211fn capture_catch_fact(node: Node<'_>, source: &str) -> CppCatchFact {
1212    let parameter = node
1213        .child_by_field_name("parameters")
1214        .and_then(|parameters| parameters.named_child(0));
1215    let type_node = parameter.and_then(|parameter| parameter.child_by_field_name("type"));
1216    let declarator = parameter.and_then(|parameter| parameter.child_by_field_name("declarator"));
1217    let value_category = declarator
1218        .map(classify_parameter_declarator)
1219        .unwrap_or(CppParameterValueCategory::Value);
1220    let type_name = type_node
1221        .and_then(|type_node| cpp_type_name_components(type_node, source))
1222        .unwrap_or_default();
1223    let class_type = type_node.is_some_and(|type_node| {
1224        !matches!(
1225            type_node.kind(),
1226            "primitive_type" | "sized_type_specifier" | "auto" | "decltype"
1227        )
1228    });
1229    CppCatchFact {
1230        range: node_range(node),
1231        value_category,
1232        type_name,
1233        class_type,
1234    }
1235}
1236
1237fn capture_throw_fact(node: Node<'_>, source: &str) -> CppThrowFact {
1238    let expression = node.named_child(0);
1239    let (operand_shape, thrown_type) = match expression {
1240        None => (CppThrowOperandShape::Rethrow, Vec::new()),
1241        Some(expression) if expression.kind() == "new_expression" => (
1242            CppThrowOperandShape::Pointer,
1243            expression
1244                .child_by_field_name("type")
1245                .and_then(|type_node| cpp_type_name_components(type_node, source))
1246                .unwrap_or_default(),
1247        ),
1248        Some(_) => (CppThrowOperandShape::Value, Vec::new()),
1249    };
1250    CppThrowFact {
1251        range: node_range(node),
1252        operand_shape,
1253        thrown_type,
1254    }
1255}
1256
1257fn callable_parameter_shape(
1258    function: Node<'_>,
1259    owner_name: Option<&str>,
1260    comparable: &[CppComparableSlot],
1261) -> Option<(bool, bool, bool)> {
1262    let parameters = function.child_by_field_name("parameters")?;
1263    let parameter_nodes: Vec<_> = parameters.named_children(&mut parameters.walk()).collect();
1264    let remaining_parameters_defaulted = parameter_nodes.len() > 1
1265        && parameter_nodes
1266            .iter()
1267            .skip(1)
1268            .all(|parameter| parameter.child_by_field_name("default_value").is_some());
1269    let Some(first) = parameter_nodes.first().copied() else {
1270        return Some((false, false, remaining_parameters_defaulted));
1271    };
1272    let Some(_type_node) = first.child_by_field_name("type") else {
1273        return Some((false, false, remaining_parameters_defaulted));
1274    };
1275    let Some(owner_name) = owner_name else {
1276        return Some((false, false, remaining_parameters_defaulted));
1277    };
1278    let Some(CppComparableSlot::Shape(parameter)) = comparable.first() else {
1279        return Some((false, false, remaining_parameters_defaulted));
1280    };
1281    let reference = match parameter.node(parameter.root()) {
1282        CppComparableNode::Reference { inner } => Some(*inner),
1283        _ => None,
1284    };
1285    let Some(inner) = reference else {
1286        return Some((false, false, remaining_parameters_defaulted));
1287    };
1288    let CppComparableNode::Named { name, .. } = parameter.node(inner) else {
1289        return Some((false, false, remaining_parameters_defaulted));
1290    };
1291    let same_owner = name.path().last().is_some_and(|name| name == owner_name);
1292    if !same_owner {
1293        return Some((false, false, remaining_parameters_defaulted));
1294    }
1295    let rvalue = first
1296        .child_by_field_name("declarator")
1297        .is_some_and(|declarator| declarator_contains_rvalue_reference(declarator));
1298    let copy = !rvalue;
1299    let move_parameter = rvalue;
1300    Some((copy, move_parameter, remaining_parameters_defaulted))
1301}
1302
1303fn declarator_contains_rvalue_reference(node: Node<'_>) -> bool {
1304    let mut stack = vec![node];
1305    while let Some(current) = stack.pop() {
1306        if crate::declarations::reference_declarator_is_rvalue(current) {
1307            return true;
1308        }
1309        let mut cursor = current.walk();
1310        stack.extend(current.named_children(&mut cursor));
1311    }
1312    false
1313}
1314
1315fn classify_parameter_declarator(node: Node<'_>) -> CppParameterValueCategory {
1316    if declarator_contains_rvalue_reference(node) {
1317        CppParameterValueCategory::RValueReference
1318    } else {
1319        let mut stack = vec![node];
1320        while let Some(current) = stack.pop() {
1321            if current.kind() == "reference_declarator" {
1322                return CppParameterValueCategory::LValueReference;
1323            }
1324            if current.kind() == "pointer_declarator" {
1325                return CppParameterValueCategory::Pointer;
1326            }
1327            let mut cursor = current.walk();
1328            stack.extend(current.named_children(&mut cursor));
1329        }
1330        CppParameterValueCategory::Value
1331    }
1332}
1333
1334fn assignment_return_shape(
1335    node: Node<'_>,
1336    source: &str,
1337    owner_name: Option<&str>,
1338) -> CppAssignmentReturnShape {
1339    let mut current = node.child_by_field_name("declarator");
1340    let mut reference = false;
1341    let mut pointer = false;
1342    while let Some(declarator) = current {
1343        reference |= declarator.kind() == "reference_declarator";
1344        pointer |= declarator.kind() == "pointer_declarator";
1345        current = declarator.child_by_field_name("declarator");
1346    }
1347    if !reference {
1348        return if pointer {
1349            CppAssignmentReturnShape::Unknown
1350        } else {
1351            CppAssignmentReturnShape::Value
1352        };
1353    }
1354    let return_type = node.child_by_field_name("type");
1355    if owner_name.is_some_and(|owner| {
1356        return_type
1357            .and_then(|type_node| cpp_type_name_components(type_node, source))
1358            .and_then(|components| components.last().cloned())
1359            .is_some_and(|name| name == owner)
1360    }) {
1361        CppAssignmentReturnShape::ReferenceToThis
1362    } else {
1363        CppAssignmentReturnShape::ReferenceOther
1364    }
1365}
1366
1367fn constructor_initializer_target(
1368    node: Node<'_>,
1369    source: &str,
1370    owner_name: Option<&str>,
1371) -> Option<Vec<String>> {
1372    let owner_name = owner_name?;
1373    let mut cursor = node.walk();
1374    let initializer_list = node
1375        .named_children(&mut cursor)
1376        .find(|child| child.kind() == "field_initializer_list")?;
1377    let mut cursor = initializer_list.walk();
1378    initializer_list
1379        .named_children(&mut cursor)
1380        .filter(|child| child.kind() == "field_initializer")
1381        .find_map(|initializer| {
1382            let name = initializer
1383                .child_by_field_name("name")
1384                .or_else(|| initializer.child_by_field_name("field"))
1385                .or_else(|| {
1386                    let mut cursor = initializer.walk();
1387                    initializer.named_children(&mut cursor).find(|child| {
1388                        matches!(
1389                            child.kind(),
1390                            "field_identifier"
1391                                | "type_identifier"
1392                                | "qualified_identifier"
1393                                | "scoped_type_identifier"
1394                        )
1395                    })
1396                })?;
1397            let components = cpp_type_name_components(name, source)?;
1398            components
1399                .last()
1400                .is_some_and(|component| component == owner_name)
1401                .then_some(components)
1402        })
1403}
1404
1405fn constructor_delegation_calls(
1406    node: Node<'_>,
1407    source: &str,
1408    ancestry: &ParentIndex<'_>,
1409    owner_name: Option<&str>,
1410) -> Vec<Range> {
1411    let Some(owner_name) = owner_name else {
1412        return Vec::new();
1413    };
1414    let Some(body) = node.child_by_field_name("body") else {
1415        return Vec::new();
1416    };
1417    let mut stack = vec![body];
1418    let mut calls = Vec::new();
1419    while let Some(current) = stack.pop() {
1420        if current.kind() == "call_expression"
1421            && ancestry
1422                .parent(current)
1423                .is_some_and(|parent| parent.kind() == "expression_statement")
1424            && let Some(function) = current.child_by_field_name("function")
1425            && function.kind() == "identifier"
1426            && node_text(function, source) == owner_name
1427        {
1428            calls.push(node_range(current));
1429        }
1430        let mut cursor = current.walk();
1431        stack.extend(current.named_children(&mut cursor));
1432    }
1433    calls
1434}
1435
1436fn enclosing_owner_path(node: Node<'_>, ancestry: &ParentIndex<'_>, source: &str) -> Vec<String> {
1437    let mut owners = Vec::new();
1438    let mut current = ancestry.parent(node);
1439    while let Some(parent) = current {
1440        if matches!(
1441            parent.kind(),
1442            "namespace_definition"
1443                | "class_specifier"
1444                | "struct_specifier"
1445                | "union_specifier"
1446                | "enum_specifier"
1447        ) && let Some(name) = parent.child_by_field_name("name")
1448        {
1449            owners.push(node_text(name, source).to_owned());
1450        }
1451        current = ancestry.parent(parent);
1452    }
1453    owners.reverse();
1454    owners
1455}
1456
1457fn append_owner_components(owner: &mut Vec<String>, written: &[String]) {
1458    let overlap = (0..=owner.len().min(written.len()))
1459        .rev()
1460        .find(|length| owner[owner.len().saturating_sub(*length)..] == written[..*length])
1461        .unwrap_or(0);
1462    owner.extend(written[overlap..].iter().cloned());
1463}
1464
1465fn member_access_specifier(
1466    node: Node<'_>,
1467    source: &str,
1468    ancestry: &ParentIndex<'_>,
1469) -> CppAccessSpecifier {
1470    let mut current = ancestry.parent(node);
1471    while let Some(parent) = current {
1472        if matches!(
1473            parent.kind(),
1474            "class_specifier" | "struct_specifier" | "union_specifier"
1475        ) {
1476            let default = if parent.kind() == "class_specifier" {
1477                CppAccessSpecifier::Private
1478            } else {
1479                CppAccessSpecifier::Public
1480            };
1481            let Some(body) = parent.child_by_field_name("body") else {
1482                return CppAccessSpecifier::Unknown;
1483            };
1484            let mut access = default;
1485            let mut cursor = body.walk();
1486            for child in body.named_children(&mut cursor) {
1487                if child.start_byte() >= node.start_byte() {
1488                    break;
1489                }
1490                if child.kind() == "access_specifier" {
1491                    access = match child.child(0).map(|keyword| keyword.kind()) {
1492                        Some("public") => CppAccessSpecifier::Public,
1493                        Some("protected") => CppAccessSpecifier::Protected,
1494                        Some("private") => CppAccessSpecifier::Private,
1495                        _ => CppAccessSpecifier::Unknown,
1496                    };
1497                } else if let Some(label) = cpp_macro_swallowed_access_label(child, source) {
1498                    access = match label.access {
1499                        "public" => CppAccessSpecifier::Public,
1500                        "protected" => CppAccessSpecifier::Protected,
1501                        _ => CppAccessSpecifier::Private,
1502                    };
1503                }
1504            }
1505            return access;
1506        }
1507        current = ancestry.parent(parent);
1508    }
1509    CppAccessSpecifier::Unknown
1510}
1511
1512pub(crate) fn base_access_specifier(
1513    clause: Node<'_>,
1514    base: Node<'_>,
1515    class_kind: &str,
1516) -> CppAccessSpecifier {
1517    let default = if matches!(class_kind, "struct_specifier" | "union_specifier") {
1518        CppAccessSpecifier::Public
1519    } else {
1520        CppAccessSpecifier::Private
1521    };
1522    let mut access = default;
1523    let mut cursor = clause.walk();
1524    for child in clause.children(&mut cursor) {
1525        if child.start_byte() >= base.start_byte() {
1526            break;
1527        }
1528        if child.kind() == "," {
1529            access = default;
1530        } else if child.kind() == "access_specifier" {
1531            access = match child.child(0).map(|keyword| keyword.kind()) {
1532                Some("public") => CppAccessSpecifier::Public,
1533                Some("protected") => CppAccessSpecifier::Protected,
1534                Some("private") => CppAccessSpecifier::Private,
1535                _ => CppAccessSpecifier::Unknown,
1536            };
1537        }
1538    }
1539    access
1540}
1541
1542fn tree_contains_kind(node: Node<'_>, kind: &str) -> bool {
1543    let mut stack = vec![node];
1544    while let Some(current) = stack.pop() {
1545        if current.kind() == kind {
1546            return true;
1547        }
1548        let mut cursor = current.walk();
1549        stack.extend(current.named_children(&mut cursor));
1550    }
1551    false
1552}
1553
1554fn tree_contains_pure_virtual(node: Node<'_>, source: &str) -> bool {
1555    let mut stack = vec![node];
1556    while let Some(current) = stack.pop() {
1557        if current.kind() == "field_declaration"
1558            && current
1559                .child_by_field_name("default_value")
1560                .is_some_and(|value| {
1561                    value.kind() == "number_literal" && node_text(value, source).trim() == "0"
1562                })
1563            && current
1564                .child_by_field_name("declarator")
1565                .and_then(extract_function_declarator)
1566                .is_some()
1567        {
1568            return true;
1569        }
1570        let mut cursor = current.walk();
1571        stack.extend(current.named_children(&mut cursor));
1572    }
1573    tree_contains_kind(node, "pure_virtual_clause")
1574}
1575
1576fn cpp_callable_is_deduction_guide(
1577    declaration: Node<'_>,
1578    function_declarator: Node<'_>,
1579    unit: &CodeUnit,
1580    source: &str,
1581) -> bool {
1582    if declaration.kind() != "declaration"
1583        || declaration.child_by_field_name("type").is_some()
1584        || function_declarator.kind() != "function_declarator"
1585    {
1586        return false;
1587    }
1588    let mut cursor = function_declarator.walk();
1589    function_declarator
1590        .named_children(&mut cursor)
1591        .any(|child| child.kind() == "trailing_return_type")
1592        && declarator_name_node(function_declarator)
1593            .is_some_and(|name| node_text(name, source) == unit.identifier())
1594}
1595
1596/// One mounted, generation-selected canonical declaration family.
1597#[derive(Debug, Clone)]
1598pub struct CppFileSourceFacts {
1599    pub source: brokk_bifrost_core::analyzer::source_facts::SourceFactRows,
1600    pub facts: CppSourceFacts,
1601    pub declaration_units: brokk_bifrost_core::hash::HashMap<SourceDeclarationId, Vec<CodeUnit>>,
1602    by_unit: brokk_bifrost_core::hash::HashMap<CodeUnit, Vec<usize>>,
1603}
1604
1605impl CppFileSourceFacts {
1606    pub fn new(
1607        source: brokk_bifrost_core::analyzer::source_facts::SourceFactRows,
1608        facts: CppSourceFacts,
1609        declaration_units: brokk_bifrost_core::hash::HashMap<SourceDeclarationId, Vec<CodeUnit>>,
1610    ) -> Self {
1611        assert!(
1612            facts.valid_links(&source),
1613            "invalid C++ canonical source links"
1614        );
1615        let mut by_unit: brokk_bifrost_core::hash::HashMap<_, Vec<_>> = Default::default();
1616        for (index, fact) in facts.declarations.iter().enumerate() {
1617            if let Some(units) = declaration_units.get(&fact.declaration) {
1618                for unit in units {
1619                    by_unit.entry(unit.clone()).or_default().push(index);
1620                }
1621            }
1622        }
1623        Self {
1624            source,
1625            facts,
1626            declaration_units,
1627            by_unit,
1628        }
1629    }
1630
1631    pub fn for_unit(&self, unit: &CodeUnit) -> impl Iterator<Item = &CppDeclarationSourceFact> {
1632        self.by_unit
1633            .get(unit)
1634            .into_iter()
1635            .flatten()
1636            .map(|index| &self.facts.declarations[*index])
1637    }
1638
1639    pub fn estimated_retained_bytes(&self) -> usize {
1640        self.source.estimated_bytes()
1641            + self.facts.estimated_retained_bytes()
1642            + self
1643                .declaration_units
1644                .values()
1645                .map(|units| units.capacity() * std::mem::size_of::<CodeUnit>())
1646                .sum::<usize>()
1647            + self
1648                .by_unit
1649                .values()
1650                .map(|indices| indices.capacity() * std::mem::size_of::<usize>())
1651                .sum::<usize>()
1652    }
1653}
1654
1655/// One source-backed navigation alternative for a mounted declaration.
1656#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1657pub struct CppNavigationOccurrence {
1658    pub range: brokk_bifrost_core::analyzer::Range,
1659    pub role: CppOccurrenceRole,
1660    pub conditional_family: Option<(usize, usize)>,
1661}
1662
1663/// Canonical declaration context with IDs resolved in its own dialect arena.
1664#[derive(Debug, Clone, PartialEq, Eq)]
1665pub struct CppDeclarationOccurrence {
1666    pub fact: CppDeclarationSourceFact,
1667    pub range: brokk_bifrost_core::analyzer::Range,
1668    pub name_range: Option<brokk_bifrost_core::analyzer::Range>,
1669    pub exhaustive_family: Option<(usize, usize)>,
1670    pub displaced_namespace_closing_brace: Option<usize>,
1671}
1672
1673impl CppFileSourceFacts {
1674    pub fn declaration_occurrences(&self, unit: &CodeUnit) -> Vec<CppDeclarationOccurrence> {
1675        self.for_unit(unit)
1676            .map(|fact| {
1677                let declaration = self.source.declaration(fact.declaration);
1678                CppDeclarationOccurrence {
1679                    range: self.source.occurrence(declaration.occurrence).range,
1680                    name_range: declaration.name.map(|id| self.source.occurrence(id).range),
1681                    exhaustive_family: fact.exhaustive_conditional_family.map(|id| {
1682                        let range = self.source.occurrence(id).range;
1683                        (range.start_byte, range.end_byte)
1684                    }),
1685                    displaced_namespace_closing_brace: fact
1686                        .displaced_namespace_closing_brace
1687                        .map(|id| self.source.occurrence(id).range.start_byte),
1688                    fact: fact.clone(),
1689                }
1690            })
1691            .collect()
1692    }
1693
1694    pub fn navigation_occurrence(
1695        &self,
1696        fact: &CppDeclarationSourceFact,
1697    ) -> CppNavigationOccurrence {
1698        let declaration = self.source.declaration(fact.declaration);
1699        let range = self.source.occurrence(declaration.occurrence).range;
1700        let conditional_family = fact.conditional_family.map(|id| {
1701            let range = self.source.occurrence(id).range;
1702            (range.start_byte, range.end_byte)
1703        });
1704        CppNavigationOccurrence {
1705            range,
1706            role: fact.occurrence_role,
1707            conditional_family,
1708        }
1709    }
1710}
1711
1712#[cfg(test)]
1713mod tests {
1714    use super::*;
1715
1716    #[test]
1717    #[brokk_bifrost_test_macros::hardwon(
1718        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: range bindings, member const and numeric values used wrong syntax"
1719    )]
1720    fn source_fact_grammar_boundary() {
1721        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";
1722        let mut parser = Parser::new();
1723        parser
1724            .set_language(&tree_sitter_cpp::LANGUAGE.into())
1725            .unwrap();
1726        let tree = parser.parse(source, None).unwrap();
1727        assert!(!tree.root_node().has_error());
1728        // Hand-written half-open spans pin which grammar fields carry these facts.
1729        let mut stack = vec![tree.root_node()];
1730        let mut loops = Vec::new();
1731        let mut qualifiers = Vec::new();
1732        let mut literals = Vec::new();
1733        while let Some(node) = stack.pop() {
1734            match node.kind() {
1735                "for_range_loop" => {
1736                    assert!(node.child_by_field_name("left").is_none());
1737                    loops.push((
1738                        node.child_by_field_name("type").unwrap().byte_range(),
1739                        node.child_by_field_name("declarator").unwrap().byte_range(),
1740                    ));
1741                }
1742                "type_qualifier" => {
1743                    qualifiers.push((node.parent().unwrap().kind(), node.byte_range()))
1744                }
1745                "number_literal" => literals.push(node.byte_range()),
1746                _ => {}
1747            }
1748            let children: Vec<_> = node.named_children(&mut node.walk()).collect();
1749            stack.extend(children.into_iter().rev());
1750        }
1751        assert_eq!(loops, [(16..20, 21..22), (36..40, 41..45)]);
1752        assert_eq!(
1753            qualifiers,
1754            [
1755                ("parameter_declaration", 77..82),
1756                ("function_declarator", 100..105)
1757            ]
1758        );
1759        assert_eq!(literals, [121..125, 127..131, 133..137, 139..142, 144..147]);
1760        let rows = parse_cpp_expression_source_facts(source);
1761        let bindings: Vec<_> = rows
1762            .expressions
1763            .iter()
1764            .filter(|f| f.kind == CppExpressionKind::LoopVariable)
1765            .map(|f| {
1766                (
1767                    f.name.as_deref(),
1768                    f.type_text.as_deref(),
1769                    f.loop_has_short_ancestor,
1770                )
1771            })
1772            .collect();
1773        let methods: Vec<_> = rows
1774            .expressions
1775            .iter()
1776            .filter(|f| {
1777                f.kind == CppExpressionKind::FunctionSignature && f.member_function == Some(true)
1778            })
1779            .map(|f| (f.name.as_deref(), f.member_function_const))
1780            .collect();
1781        let values: Vec<_> = rows
1782            .expressions
1783            .iter()
1784            .filter(|f| f.kind == CppExpressionKind::IntegerLiteral)
1785            .map(|f| f.integer_value)
1786            .collect();
1787        assert_eq!(
1788            (bindings, methods, values),
1789            (
1790                vec![
1791                    (Some("i"), Some("char"), Some(false)),
1792                    (Some("item"), Some("char"), Some(true))
1793                ],
1794                vec![(Some("f"), Some(false)), (Some("g"), Some(true))],
1795                vec![Some(16), Some(2), Some(1), Some(2), Some(8)]
1796            )
1797        );
1798        // The C++20 init-statement is not the range binding. A traditional
1799        // declaration-initialized inner loop still sees the short range name.
1800        let rows = parse_cpp_expression_source_facts(
1801            "void f() { for (int setup = 0; char i : xs) { for (int j = 0; j < 3; ++j) {} } }",
1802        );
1803        let bindings: Vec<_> = rows
1804            .expressions
1805            .iter()
1806            .filter(|f| f.kind == CppExpressionKind::LoopVariable)
1807            .map(|f| {
1808                (
1809                    f.name.as_deref(),
1810                    f.type_text.as_deref(),
1811                    f.loop_has_short_ancestor,
1812                )
1813            })
1814            .collect();
1815        assert_eq!(
1816            bindings,
1817            [
1818                (Some("i"), Some("char"), Some(false)),
1819                (Some("j"), Some("int"), Some(true))
1820            ]
1821        );
1822    }
1823
1824    #[test]
1825    #[brokk_bifrost_test_macros::hardwon(
1826        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: base access leaked across commas"
1827    )]
1828    fn source_fact_base_access_defaults() {
1829        // [class.access.base]: omitted access is decided independently per base.
1830        let source = "struct A {}; struct B {}; struct S : private A, B {}; class C : public A, B {}; struct P : protected A {};";
1831        let mut parser = Parser::new();
1832        parser
1833            .set_language(&tree_sitter_cpp::LANGUAGE.into())
1834            .unwrap();
1835        let tree = parser.parse(source, None).unwrap();
1836        assert!(!tree.root_node().has_error());
1837        let mut stack = vec![tree.root_node()];
1838        let mut access = Vec::new();
1839        while let Some(node) = stack.pop() {
1840            if node.kind() == "base_class_clause" {
1841                for base in node
1842                    .named_children(&mut node.walk())
1843                    .filter(|n| n.kind() == "type_identifier")
1844                {
1845                    access.push(base_access_specifier(
1846                        node,
1847                        base,
1848                        node.parent().unwrap().kind(),
1849                    ));
1850                }
1851            }
1852            let children: Vec<_> = node.named_children(&mut node.walk()).collect();
1853            stack.extend(children.into_iter().rev());
1854        }
1855        assert_eq!(
1856            access,
1857            [
1858                CppAccessSpecifier::Private,
1859                CppAccessSpecifier::Public,
1860                CppAccessSpecifier::Public,
1861                CppAccessSpecifier::Private,
1862                CppAccessSpecifier::Protected
1863            ]
1864        );
1865    }
1866
1867    #[test]
1868    #[brokk_bifrost_test_macros::hardwon(
1869        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: parameter indirection reused pointee size"
1870    )]
1871    fn source_fact_parameter_sizes_respect_indirection() {
1872        let rows = parse_cpp_expression_source_facts(
1873            "void f(char a, signed char b, unsigned char c, char *p, char &r, char xs[3]);",
1874        );
1875        let sizes: Vec<_> = rows
1876            .expressions
1877            .iter()
1878            .filter(|f| {
1879                f.name.is_some()
1880                    && matches!(
1881                        f.kind,
1882                        CppExpressionKind::FunctionParameter | CppExpressionKind::ArrayParameter
1883                    )
1884            })
1885            .map(|f| (f.name.as_deref(), f.type_size_bytes, f.type_size_exact))
1886            .collect();
1887        assert_eq!(
1888            sizes,
1889            [
1890                (Some("a"), Some(1), Some(true)),
1891                (Some("b"), Some(1), Some(true)),
1892                (Some("c"), Some(1), Some(true)),
1893                (Some("p"), None, Some(false)),
1894                (Some("r"), None, Some(false)),
1895                (Some("xs"), None, Some(false))
1896            ]
1897        );
1898    }
1899
1900    #[test]
1901    #[brokk_bifrost_test_macros::hardwon(
1902        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: union sizes summed and later field declarators vanished"
1903    )]
1904    fn source_fact_record_sizes_follow_member_layout() {
1905        let rows = parse_cpp_expression_source_facts(
1906            "union U { char a[2]; char b[3]; }; struct S { char a, b; }; void f(U u, S s);",
1907        );
1908        let sizes: Vec<_> = rows
1909            .expressions
1910            .iter()
1911            .filter(|f| f.kind == CppExpressionKind::FunctionParameter)
1912            .map(|f| (f.name.as_deref(), f.type_size_bytes))
1913            .collect();
1914        // [class.union] requires room for the largest member; char has size one.
1915        assert_eq!(sizes, [(Some("u"), Some(3)), (Some("s"), Some(2))]);
1916    }
1917
1918    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";
1919
1920    #[test]
1921    #[brokk_bifrost_test_macros::hardwon(
1922        "d4f00f8567e4: quicksilver round 4 on brokk-bifrost-cpp: unnamed && tokens hid rvalue references"
1923    )]
1924    #[brokk_bifrost_test_macros::hardwon(
1925        "d4f00f8567e4: quicksilver round 4 on brokk-bifrost-cpp: a mutable C& copy constructor was not classified as copy"
1926    )]
1927    fn cpp_reference_token_grammar() {
1928        let mut parser = Parser::new();
1929        parser
1930            .set_language(&tree_sitter_cpp::LANGUAGE.into())
1931            .unwrap();
1932        let tree = parser.parse(REFERENCES, None).unwrap();
1933        assert!(!tree.root_node().has_error());
1934        // Half-open byte spans from the pinned grammar boundary table. The
1935        // reference wrapper has no fields; the parameter owns `declarator`.
1936        let expected = "reference_declarator 15..18 & 15..16 identifier 17..18
1937reference_declarator 30..33 & 30..31 identifier 32..33
1938reference_declarator 39..43 && 39..41 identifier 42..43
1939reference_declarator 55..59 && 55..57 identifier 58..59
1940reference_declarator 66..70 && 66..68 identifier 69..70
1941reference_declarator 95..118 & 95..96 function_declarator 97..118
1942reference_declarator 114..117 & 114..115 identifier 116..117
1943reference_declarator 121..139 & 121..122 function_declarator 123..139
1944reference_declarator 134..138 && 134..136 identifier 137..138
1945abstract_reference_declarator 152..155 & 152..153 abstract_function_declarator 153..155
1946abstract_reference_declarator 167..171 && 167..169 abstract_function_declarator 169..171
1947reference_declarator 177..180 & 177..178 identifier 179..180
1948reference_declarator 187..191 && 187..189 identifier 190..191
1949reference_declarator 224..227 & 224..225 identifier 226..227
1950reference_declarator 252..255 & 252..253 identifier 254..255
1951reference_declarator 274..278 && 274..276 identifier 277..278
1952reference_declarator 300..303 & 300..301 identifier 302..303
1953reference_declarator 308..325 & 308..309 function_declarator 310..325
1954reference_declarator 321..324 & 321..322 identifier 323..324
1955reference_declarator 337..340 & 337..338 identifier 339..340
1956reference_declarator 345..368 & 345..346 function_declarator 347..368
1957reference_declarator 364..367 & 364..365 identifier 366..367
1958reference_declarator 383..386 & 383..384 identifier 385..386
1959reference_declarator 391..417 & 391..392 function_declarator 393..417
1960reference_declarator 413..416 & 413..414 identifier 415..416
1961reference_declarator 438..441 & 438..439 identifier 440..441
1962reference_declarator 446..478 & 446..447 function_declarator 448..478
1963reference_declarator 474..477 & 474..475 identifier 476..477
1964reference_declarator 484..487 & 484..485 identifier 486..487
1965reference_declarator 514..517 & 514..515 identifier 516..517";
1966        let mut stack = vec![tree.root_node()];
1967        let mut actual = Vec::new();
1968        let mut references = Vec::new();
1969        let mut qualifiers = Vec::new();
1970        while let Some(node) = stack.pop() {
1971            if node.kind() == "type_qualifier" && node.start_byte() >= 285 {
1972                assert_eq!(node.parent().unwrap().kind(), "parameter_declaration");
1973                qualifiers.push((
1974                    node.utf8_text(REFERENCES.as_bytes()).unwrap(),
1975                    node.byte_range(),
1976                ));
1977            }
1978            if matches!(
1979                node.kind(),
1980                "reference_declarator" | "abstract_reference_declarator"
1981            ) {
1982                let token = node.child(0).unwrap();
1983                let wrapped = node.named_child(0).unwrap();
1984                assert!(!token.is_named());
1985                assert!(node.child_by_field_name("declarator").is_none());
1986                assert_eq!(node.field_name_for_child(0), None);
1987                assert_eq!(node.field_name_for_child(1), None);
1988                if node.parent().unwrap().kind() == "parameter_declaration" {
1989                    assert_eq!(
1990                        node.parent().unwrap().child_by_field_name("declarator"),
1991                        Some(node)
1992                    );
1993                }
1994                actual.push(format!(
1995                    "{} {:?} {} {:?} {} {:?}",
1996                    node.kind(),
1997                    node.byte_range(),
1998                    token.kind(),
1999                    token.byte_range(),
2000                    wrapped.kind(),
2001                    wrapped.byte_range()
2002                ));
2003                references.push(node);
2004            }
2005            let children: Vec<_> = node.named_children(&mut node.walk()).collect();
2006            stack.extend(children.into_iter().rev());
2007        }
2008        assert_eq!(actual.join("\n"), expected);
2009        assert_eq!(
2010            qualifiers,
2011            [
2012                ("const", 330..335),
2013                ("const", 357..362),
2014                ("volatile", 373..381),
2015                ("volatile", 403..411),
2016                ("const", 422..427),
2017                ("volatile", 428..436),
2018                ("const", 458..463),
2019                ("volatile", 464..472),
2020                ("volatile", 504..512),
2021            ]
2022        );
2023        for node in references {
2024            let token = node.child(0).unwrap();
2025            let wrapped = node.named_child(0).unwrap();
2026            // A return wrapper also contains the function's parameters. The
2027            // producer calls this helper on parameter declarators only.
2028            if wrapped.kind() == "function_declarator" {
2029                continue;
2030            }
2031            assert_eq!(
2032                declarator_contains_rvalue_reference(node),
2033                token.kind() == "&&",
2034                "reference at {:?}",
2035                node.byte_range()
2036            );
2037        }
2038        let rows = parse_cpp_class_fact_rows(REFERENCES);
2039        use CppSpecialMemberKind::{
2040            CopyAssignment, CopyConstructor, MoveAssignment, MoveConstructor, None as NotSpecial,
2041        };
2042        // Pin the defect's public class/catch consequences as well as its
2043        // grammar boundary, including all cv-qualified lvalue references.
2044        let members: Vec<_> = rows
2045            .members
2046            .iter()
2047            .take(8)
2048            .map(|member| member.special_member)
2049            .collect();
2050        assert_eq!(
2051            members,
2052            [
2053                CopyConstructor,
2054                CopyConstructor,
2055                MoveConstructor,
2056                MoveConstructor,
2057                MoveConstructor,
2058                NotSpecial,
2059                CopyAssignment,
2060                MoveAssignment
2061            ]
2062        );
2063        assert!(rows.members[4].remaining_parameters_defaulted);
2064        let cv_members: Vec<_> = rows
2065            .members
2066            .iter()
2067            .filter(|member| member.owner == ["V"])
2068            .collect();
2069        assert_eq!(
2070            cv_members
2071                .iter()
2072                .map(|member| member.special_member)
2073                .collect::<Vec<_>>(),
2074            [
2075                CopyConstructor,
2076                CopyAssignment,
2077                CopyConstructor,
2078                CopyAssignment,
2079                CopyConstructor,
2080                CopyAssignment,
2081                CopyConstructor,
2082                CopyAssignment,
2083                CopyConstructor,
2084                CopyConstructor
2085            ]
2086        );
2087        assert!(cv_members.iter().all(|member| member.copy_parameter));
2088        assert!(
2089            cv_members[8..]
2090                .iter()
2091                .all(|member| member.remaining_parameters_defaulted)
2092        );
2093        assert_eq!(
2094            rows.catches
2095                .iter()
2096                .map(|catch| catch.value_category)
2097                .collect::<Vec<_>>(),
2098            [
2099                CppParameterValueCategory::LValueReference,
2100                CppParameterValueCategory::LValueReference,
2101                CppParameterValueCategory::RValueReference
2102            ]
2103        );
2104    }
2105
2106    #[test]
2107    fn specialized_declaration_name_grammar_boundary() {
2108        for (source, field_kind, expected, offset) in [
2109            ("struct Plain {};", "type_identifier", "Plain", 7),
2110            ("template<> struct Box<int> {};", "template_type", "Box", 18),
2111            (
2112                "struct ns::Qualified {};",
2113                "qualified_identifier",
2114                "Qualified",
2115                11,
2116            ),
2117        ] {
2118            let tree = crate::diagnostics::parse_cpp_tree(source);
2119            assert!(!tree.root_node().has_error());
2120            let mut stack = vec![tree.root_node()];
2121            let mut names = Vec::new();
2122            while let Some(node) = stack.pop() {
2123                if node.kind() == "struct_specifier" {
2124                    let field = node.child_by_field_name("name").unwrap();
2125                    assert_eq!(field.kind(), field_kind);
2126                    let unit = CodeUnit::new(
2127                        brokk_bifrost_core::analyzer::ProjectFile::new(
2128                            std::env::temp_dir(),
2129                            "names.cpp",
2130                        ),
2131                        brokk_bifrost_core::analyzer::model::CodeUnitType::Class,
2132                        "",
2133                        expected,
2134                    );
2135                    let name = declaration_name(node, &unit, source).unwrap();
2136                    names.push((node_text(name, source), name.byte_range()));
2137                }
2138                stack.extend(node.named_children(&mut node.walk()));
2139            }
2140            assert_eq!(names, [(expected, offset..offset + expected.len())]);
2141        }
2142    }
2143}