1use crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::{MacroReplacementField, ObjectMacroReplacement};
9use crate::structural::CPP_STRUCTURAL_SPEC;
10use brokk_bifrost_core::analyzer::common::{
11 node_source_text, parse_source_ranges_with_cancellation, parse_source_region,
12};
13pub use brokk_bifrost_core::analyzer::cpp_facts::{
14 CppBaseSpecifierFact, CppComparableNode, CppComparableParameter, CppComparableSlot,
15};
16use brokk_bifrost_core::analyzer::fq_name::{
17 FqName, SegmentId, SegmentKind, joined_segments, normalize_joined, segment_interner,
18};
19use brokk_bifrost_core::analyzer::model::LanguageDialect;
20use brokk_bifrost_core::analyzer::model::{
21 CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
22 CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
23 CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
24 ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
25 StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
26};
27use brokk_bifrost_core::analyzer::parsed_file::{ParsedFile, ParsedSourceFacts, SourceImportFact};
28use brokk_bifrost_core::analyzer::source_facts::{
29 PrimarySourceFactCollector, SourceDeclarationId, SourceImportId, SourceOccurrenceId,
30 SourceOccurrenceProvenance,
31};
32use brokk_bifrost_core::analyzer::structural::adapter_helpers::node_range;
33use brokk_bifrost_core::analyzer::structural::collector::StructuralFactCollector;
34use brokk_bifrost_core::analyzer::structural::materialization::{
35 GenerationKind, MaterializationRecord,
36};
37use brokk_bifrost_core::analyzer::structural::resolution::DeclaredVisibility;
38use brokk_bifrost_core::analyzer::structural::spec::{CompiledKinds, StructuralSpec};
39use brokk_bifrost_core::analyzer::symbol_path::parse_symbol_path;
40use brokk_bifrost_core::analyzer::tree_walk::{
41 NodeKindIds, ParentIndex, WalkControl, children_iter, named_children_iter,
42 push_children_reversed, push_named_children_reversed, walk_named_tree_preorder,
43};
44use brokk_bifrost_core::analyzer::{CodeUnit, Language, ProjectFile};
45use brokk_bifrost_core::hash::{HashMap, HashSet};
46use regex::Regex;
47use std::cell::RefCell;
48#[cfg(any(debug_assertions, test))]
49use tree_sitter::Parser;
50use tree_sitter::{Node, Tree};
51
52fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
54 segment_interner().intern(text, kind)
55}
56
57fn cpp_push_package(fq: &mut FqName, package_name: &str) {
65 for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
66 fq.push(cpp_segment(component, SegmentKind::Package));
67 }
68}
69
70const CPP_PACKAGE_SEPARATOR: &str = "::";
72
73fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
80 let mut first = true;
81 for component in chain.split('$').filter(|c| !c.is_empty()) {
85 let kind = if first {
86 SegmentKind::Type
87 } else {
88 SegmentKind::Nested
89 };
90 fq.push(cpp_segment(component, kind));
91 first = false;
92 }
93}
94
95fn cpp_namespace_fq(full_name: &str) -> FqName {
99 let mut fq = FqName::new();
100 cpp_push_package(&mut fq, full_name);
101 fq
102}
103
104fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
120 let mut components = Vec::new();
121 let mut stack = vec![node];
122 while let Some(current) = stack.pop() {
123 match current.kind() {
124 "namespace_identifier" | "identifier" => {
125 components.push(normalize_cpp_whitespace(node_text(current, source)));
126 }
127 "nested_namespace_specifier" => {
128 for index in (0..current.named_child_count()).rev() {
129 stack.push(
130 current
131 .named_child(index)
132 .expect("index below the node's own named child count"),
133 );
134 }
135 }
136 _ => return cpp_raw_namespace_name_components(node, source),
137 }
138 }
139 if components.iter().any(String::is_empty) {
140 return cpp_raw_namespace_name_components(node, source);
141 }
142 components
143}
144
145fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
159 let start = node
160 .child(0)
161 .filter(|child| !child.is_named() && child.kind() == "::")
162 .map_or(node.start_byte(), |marker| marker.end_byte());
163 let text = normalize_cpp_whitespace(
164 source
165 .get(start..node.end_byte())
166 .expect("namespace name node covers one source range"),
167 );
168 let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
169 if text.is_empty() {
170 return Vec::new();
171 }
172 vec![text]
173}
174
175fn cpp_lexical_namespace_name<'tree>(
181 node: Node<'tree>,
182 source: &str,
183 ancestry: &ParentIndex<'tree>,
184) -> Option<String> {
185 let mut components = Vec::new();
186 let mut ancestor = ancestry.parent(node);
187 while let Some(current) = ancestor {
188 if current.kind() == "namespace_definition" {
189 let name_node = current.child_by_field_name("name")?;
190 let name = normalize_cpp_whitespace(node_text(name_node, source));
191 if name.is_empty() {
192 return None;
193 }
194 components.push(name);
195 }
196 ancestor = ancestry.parent(current);
197 }
198 if components.is_empty() {
199 return None;
200 }
201 components.reverse();
202 Some(
206 normalize_joined(
207 &components.join(CPP_PACKAGE_SEPARATOR),
208 CPP_PACKAGE_SEPARATOR,
209 )
210 .into_owned(),
211 )
212}
213
214fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
220 if parent_short.is_empty() {
221 name.to_string()
222 } else {
223 format!("{parent_short}${name}")
224 }
225}
226
227fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
230 if parent_short.is_empty() {
231 name.to_string()
232 } else {
233 format!("{parent_short}.{name}")
234 }
235}
236
237fn cpp_leaf_fq(
244 package_name: &str,
245 parent: Option<&CodeUnit>,
246 name: &str,
247 kind_if_nested: SegmentKind,
248 kind_if_top: SegmentKind,
249) -> FqName {
250 if let Some(parent) = parent {
251 parent
252 .fq()
253 .clone()
254 .with_pushed(cpp_segment(name, kind_if_nested))
255 } else {
256 let mut fq = FqName::new();
257 cpp_push_package(&mut fq, package_name);
258 fq.push(cpp_segment(name, kind_if_top));
259 fq
260 }
261}
262
263pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
270 let mut fq = FqName::new();
271 cpp_push_package(&mut fq, package_name);
272 match short_name.rsplit_once('.') {
273 Some((owner_chain, member)) => {
274 cpp_push_type_chain(&mut fq, owner_chain);
275 fq.push(cpp_segment(member, SegmentKind::Member));
276 }
277 None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
278 }
279 fq
280}
281
282pub fn cpp_rendered_name_fq(rendered: &str) -> FqName {
297 let mut fq = FqName::new();
298 for component in parse_symbol_path(Language::Cpp, rendered) {
299 let mut first = true;
303 for part in component.split('$').filter(|part| !part.is_empty()) {
304 let kind = if first {
305 SegmentKind::Unknown
308 } else {
309 SegmentKind::Nested
310 };
311 fq.push(cpp_segment(part, kind));
312 first = false;
313 }
314 }
315 fq
316}
317
318pub fn cpp_colon_source_aliases(rendered: &str) -> Vec<FqName> {
332 if !rendered.contains("::") || rendered.contains('.') || rendered.contains('$') {
333 return Vec::new();
334 }
335 let texts = parse_symbol_path(Language::Cpp, rendered);
336 if texts.len() < 2 {
337 return Vec::new();
338 }
339 cpp_qualified_kind_interpretations(&texts)
340}
341
342fn cpp_qualified_kind_interpretations(texts: &[String]) -> Vec<FqName> {
346 let len = texts.len();
347 let mut names = Vec::new();
348 names.push(cpp_fq_with_kinds(texts, vec![SegmentKind::Package; len]));
349 for packages in 0..len {
350 let rest = len - packages;
351 let mut type_chain = Vec::with_capacity(len);
352 type_chain.extend(std::iter::repeat_n(SegmentKind::Package, packages));
353 type_chain.push(SegmentKind::Type);
354 type_chain.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 1));
355 names.push(cpp_fq_with_kinds(texts, type_chain));
356 if rest >= 2 {
357 let mut member = Vec::with_capacity(len);
358 member.extend(std::iter::repeat_n(SegmentKind::Package, packages));
359 member.push(SegmentKind::Type);
360 member.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 2));
361 member.push(SegmentKind::Member);
362 names.push(cpp_fq_with_kinds(texts, member));
363 } else if packages >= 1 {
364 let mut function = vec![SegmentKind::Package; packages];
365 function.push(SegmentKind::Member);
366 names.push(cpp_fq_with_kinds(texts, function));
367 }
368 }
369 names
370}
371
372fn cpp_fq_with_kinds(texts: &[String], kinds: Vec<SegmentKind>) -> FqName {
373 assert_eq!(
374 texts.len(),
375 kinds.len(),
376 "a qualified interpretation names every segment"
377 );
378 let mut fq = FqName::new();
379 for (text, kind) in texts.iter().zip(kinds) {
380 fq.push(cpp_segment(text, kind));
381 }
382 fq
383}
384
385#[derive(Clone)]
386pub struct ScopeInfo {
387 package_name: String,
388 module: Option<CodeUnit>,
389 class_unit: Option<CodeUnit>,
390 template_signature: Option<String>,
391 template_metadata: Option<CppTemplateMetadata>,
392 declarations_are_fields: bool,
393 recovered_specialization_member_scope: bool,
394 visible_using_namespaces: Vec<String>,
406}
407
408struct CppContainer<'tree> {
409 node: Node<'tree>,
410 scope: ScopeInfo,
411}
412
413struct CppNodeWork<'tree> {
414 node: Node<'tree>,
415 scope: ScopeInfo,
416}
417
418struct CppSiblingsWork<'tree> {
425 children: std::vec::IntoIter<Node<'tree>>,
426 scope: ScopeInfo,
427}
428
429enum CppWork<'tree> {
430 Container(CppContainer<'tree>),
431 Node(CppNodeWork<'tree>),
432 Siblings(CppSiblingsWork<'tree>),
433}
434
435fn class_like_name<'tree>(
436 node: Node<'tree>,
437 source: &str,
438 ancestry: &ParentIndex<'tree>,
439) -> Option<String> {
440 let best = class_like_name_from_children(node, source);
441 if let Some(parent) = ancestry.parent(node)
442 && matches!(
443 parent.kind(),
444 "declaration" | "field_declaration" | "function_definition"
445 )
446 && cpp_body_node(node).is_none()
455 && node
456 .child_by_field_name("name")
457 .map(|name_node| {
458 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
459 })
460 .unwrap_or(false)
461 && let Some(recovered) = exported_class_name_from_node(parent, source)
462 && best.as_deref() != Some(recovered.as_str())
463 {
464 return Some(recovered);
465 }
466 best.or_else(|| {
467 node.child_by_field_name("name")
468 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
469 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
470 })
471}
472
473fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
474 let mut grammar_name = None;
475 if let Some(name_node) = node.child_by_field_name("name") {
476 let name = normalize_cpp_whitespace(node_text(name_node, source));
477 if name.is_empty() {
478 return None;
479 }
480 if !cpp_export_macro_token(&name) {
481 return Some(name);
482 }
483 grammar_name = Some(name);
484 }
485
486 let mut best = None;
487 let mut cursor = node.walk();
488 let mut stack = Vec::new();
489 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
490 if matches!(
491 child.kind(),
492 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
493 ) {
494 break;
495 }
496 stack.push(child);
497 }
498
499 while let Some(current) = stack.pop() {
500 if matches!(current.kind(), "type_identifier" | "identifier") {
501 let name = normalize_cpp_whitespace(node_text(current, source));
502 if !name.is_empty() && !cpp_export_macro_token(&name) {
503 best = Some(name);
504 }
505 continue;
506 }
507
508 push_named_children_reversed(current, &mut stack);
509 }
510 best.or(grammar_name)
511}
512
513pub fn cpp_export_macro_token(token: &str) -> bool {
514 token
515 .chars()
516 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
517}
518
519struct RecoveredExportedClass<'tree> {
520 declaration_node: Node<'tree>,
521 name: String,
522 body: Option<Node<'tree>>,
523 raw_supertypes: Option<Vec<String>>,
524 base_facts: Option<Vec<CppBaseSpecifierFact>>,
525 uses_initializer_body: bool,
526 fragmented_body: Option<FragmentedExportBody>,
531}
532
533struct RecoveredFunctionLikeExportClassPair {
534 name: String,
535 range: Range,
536 raw_supertypes: Option<Vec<String>>,
537 base_facts: Option<Vec<CppBaseSpecifierFact>>,
538 fragmented_body: FragmentedExportBody,
539}
540
541struct RecoveredEmbeddedFunctionLikeExportClass {
542 name: String,
543 range: Range,
544 raw_supertypes: Vec<String>,
545 base_facts: Vec<CppBaseSpecifierFact>,
546 fragmented_body: FragmentedExportBody,
547}
548
549struct FragmentedExportBody {
555 reparse_start: usize,
556 reparse_end: usize,
557 class_range: Range,
558}
559
560fn recovered_fragmented_export_body(
561 body: Node<'_>,
562 class_range: Range,
563) -> Option<FragmentedExportBody> {
564 let open = body.child(0).filter(|child| child.kind() == "{")?;
565 let close = body
566 .child(body.child_count().saturating_sub(1))
567 .filter(|child| child.kind() == "}" && !child.is_missing());
568 Some(FragmentedExportBody {
569 reparse_start: open.end_byte(),
570 reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
575 class_range,
576 })
577}
578
579struct DisplacedFragmentNamespaceBoundary<'tree> {
580 class_close: Node<'tree>,
581 class_semicolon: Node<'tree>,
582 namespace_items: Vec<Node<'tree>>,
583}
584
585enum FragmentedExportMembers {
590 Complete(Tree),
591 ConditionalConstructor(Tree),
592}
593
594#[derive(Clone, Copy)]
595struct DisplacedMacroClassTail {
596 split_index: usize,
597 class_range: Range,
598}
599
600fn recover_exported_class_declaration<'tree>(
601 node: Node<'tree>,
602 source: &str,
603) -> Option<RecoveredExportedClass<'tree>> {
604 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
605 return Some(recovered);
606 }
607
608 let class_node = first_class_like_child(node)?;
609 if let Some(name_node) = class_node.child_by_field_name("name") {
610 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
611 if cpp_export_macro_token(&class_name) {
612 let mut cursor = node.walk();
616 if node
617 .children_by_field_name("declarator", &mut cursor)
618 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
619 {
620 return None;
621 }
622 } else if has_direct_cpp_declarator(node) {
623 return None;
624 }
625 }
626 let name = exported_class_name_from_node(class_node, source)?;
627 Some(RecoveredExportedClass {
628 declaration_node: class_node,
629 name,
630 body: cpp_body_node(class_node),
631 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
632 .then(|| extract_cpp_supertypes(class_node, source)),
633 base_facts: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
634 .then(|| extract_cpp_base_facts(class_node, source)),
635 uses_initializer_body: false,
636 fragmented_body: None,
637 })
638}
639
640fn recover_malformed_exported_base_class<'tree>(
641 node: Node<'tree>,
642 source: &str,
643) -> Option<RecoveredExportedClass<'tree>> {
644 if node.kind() != "declaration" {
645 return None;
646 }
647 let class_node = node.child_by_field_name("type")?;
648 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
649 return None;
650 }
651 let macro_name = class_node
652 .child_by_field_name("name")
653 .and_then(|name| direct_identifier_name(name, source))?;
654 if !cpp_export_macro_token(¯o_name) {
655 return None;
656 }
657
658 let mut named_cursor = node.walk();
659 let mut named = node.named_children(&mut named_cursor);
660 if named
661 .next()
662 .is_none_or(|child| !same_node(child, class_node))
663 {
664 return None;
665 }
666 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
667 if displaced.kind() != "ERROR" {
668 return None;
669 }
670 let name = displaced_exported_class_name(displaced, source)?;
671
672 let remaining = named.collect::<Vec<_>>();
673 let init = *remaining.last()?;
674 if init.kind() != "init_declarator" {
675 return None;
676 }
677 let final_base = init
678 .child_by_field_name("declarator")
679 .and_then(|base| recovered_malformed_base_name(base, source))?;
680 let body = init.child_by_field_name("value")?;
681 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
685 return None;
686 }
687
688 if remaining[..remaining.len() - 1]
689 .iter()
690 .any(|child| match child.kind() {
691 "qualified_identifier"
692 | "scoped_type_identifier"
693 | "type_identifier"
694 | "identifier" => false,
695 "ERROR" => !is_malformed_inheritance_access(*child, source),
696 _ => true,
697 })
698 {
699 return None;
700 }
701
702 let mut raw_supertypes = Vec::new();
703 let mut base_facts = Vec::new();
704 for base in &remaining[..remaining.len() - 1] {
705 if base.kind() == "ERROR" {
706 continue;
707 }
708 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
709 base_facts.push(recovered_base_specifier(
710 *base,
711 source,
712 recovered_base_is_virtual(node, *base, source),
713 )?);
714 }
715 raw_supertypes.push(final_base);
716 let final_base_node = init.child_by_field_name("declarator")?;
717 base_facts.push(recovered_base_specifier(
718 final_base_node,
719 source,
720 recovered_base_is_virtual(init, final_base_node, source),
721 )?);
722
723 Some(RecoveredExportedClass {
724 declaration_node: node,
725 name,
726 body: Some(body),
727 raw_supertypes: Some(raw_supertypes),
728 base_facts: Some(base_facts),
729 uses_initializer_body: true,
730 fragmented_body: fragmented_export_body_region(node, body, source),
731 })
732}
733
734fn fragmented_export_body_region(
750 node: Node<'_>,
751 body: Node<'_>,
752 source: &str,
753) -> Option<FragmentedExportBody> {
754 let reparse_start = body.start_byte() + 1;
755 let close = direct_close_brace(body)?;
756 if close.end_byte() > close.start_byte() {
757 return Some(FragmentedExportBody {
758 reparse_start,
759 reparse_end: close.start_byte(),
760 class_range: cpp_declaration_range(node),
761 });
762 }
763 let mut sibling = node.next_named_sibling();
766 let displaced_close = loop {
767 let Some(current) = sibling else {
768 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
769 };
770 if cpp_is_stray_close_brace(current, source) {
771 break current;
772 }
773 sibling = current.next_named_sibling();
774 };
775 Some(FragmentedExportBody {
776 reparse_start,
777 reparse_end: displaced_close.start_byte(),
778 class_range: Range {
779 start_byte: node.start_byte(),
780 end_byte: displaced_close.end_byte(),
781 start_line: node.start_position().row + 1,
782 end_line: displaced_close.end_position().row + 1,
783 },
784 })
785}
786
787fn fragmented_export_function_body_region(
795 node: Node<'_>,
796 body: Node<'_>,
797 source: &str,
798 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
799) -> Option<FragmentedExportBody> {
800 let reparse_start = body.start_byte().checked_add(1)?;
801 if let Some(boundary) = displaced_namespace {
802 return Some(FragmentedExportBody {
803 reparse_start,
804 reparse_end: boundary.class_close.start_byte(),
805 class_range: Range {
806 start_byte: node.start_byte(),
807 end_byte: boundary.class_semicolon.end_byte(),
808 start_line: node.start_position().row + 1,
809 end_line: boundary.class_semicolon.end_position().row + 1,
810 },
811 });
812 }
813 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
814 let boundary =
815 fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
816 let boundary_index = boundary.and_then(|boundary| {
817 siblings
818 .iter()
819 .position(|candidate| same_node(*candidate, boundary))
820 });
821 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
822 let mut sibling_index = 0;
823 while let Some(current) = siblings.get(sibling_index).copied() {
836 if current.kind() == "comment" {
837 sibling_index += 1;
838 continue;
839 }
840 if is_trailing_attribute_macro_sibling(current) {
841 sibling_index += 1;
842 continue;
843 }
844 if cpp_is_stray_semicolon(current, source) {
845 return None;
846 }
847 break;
848 }
849 while let Some(current) = siblings.get(sibling_index).copied() {
850 let next = siblings.get(sibling_index + 1).copied();
851 if cpp_is_stray_close_brace(current, source)
852 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
853 {
854 let semicolon = next.expect("checked above");
855 return Some(FragmentedExportBody {
856 reparse_start,
857 reparse_end: current.start_byte(),
858 class_range: Range {
859 start_byte: node.start_byte(),
860 end_byte: semicolon.end_byte(),
861 start_line: node.start_position().row + 1,
862 end_line: semicolon.end_position().row + 1,
863 },
864 });
865 }
866 if current.start_byte() >= body.end_byte()
873 && let Some(close) = cpp_nested_stray_close_brace(current, source)
874 {
875 return Some(FragmentedExportBody {
876 reparse_start,
877 reparse_end: close.start_byte(),
878 class_range: Range {
879 start_byte: node.start_byte(),
880 end_byte: current.end_byte(),
881 start_line: node.start_position().row + 1,
882 end_line: current.end_position().row + 1,
883 },
884 });
885 }
886 sibling_index += 1;
887 }
888 boundary.map(|boundary| FragmentedExportBody {
889 reparse_start,
890 reparse_end: boundary.start_byte(),
891 class_range: Range {
892 start_byte: node.start_byte(),
893 end_byte: boundary.start_byte(),
894 start_line: node.start_position().row + 1,
895 end_line: boundary.start_position().row + 1,
896 },
897 })
898}
899
900fn fragmented_export_sibling_class_boundary<'tree>(
905 node: Node<'tree>,
906 source: &str,
907 ancestry: &ParentIndex<'tree>,
908) -> Option<Node<'tree>> {
909 let node_parent = ancestry.parent(node)?;
910 cpp_following_named_siblings(node, source, ancestry)
911 .into_iter()
912 .find(|candidate| {
913 recover_exported_class_function_definition(*candidate, source).is_some()
914 && ancestry
915 .parent(*candidate)
916 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
917 })
918}
919
920fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
925 if node.kind() != "expression_statement" {
926 return false;
927 }
928 let mut cursor = node.walk();
929 let mut children = node.named_children(&mut cursor);
930 children
931 .next()
932 .is_some_and(|child| child.kind() == "identifier")
933 && children.next().is_none()
934}
935
936fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
942 let mut stack = vec![node];
943 while let Some(current) = stack.pop() {
944 if cpp_is_stray_close_brace(current, source) {
945 return Some(current);
946 }
947 let mut cursor = current.walk();
948 stack.extend(current.named_children(&mut cursor));
949 }
950 None
951}
952
953fn cpp_following_named_siblings<'tree>(
967 node: Node<'tree>,
968 source: &str,
969 ancestry: &ParentIndex<'tree>,
970) -> Vec<Node<'tree>> {
971 let mut siblings = Vec::new();
972 let mut anchor = node;
973 while let Some(parent) = ancestry.parent(anchor) {
974 let at_translation_unit = parent.kind() == "translation_unit";
975 let mut cursor = parent.walk();
976 assert!(
977 cursor.goto_first_child(),
978 "ParentIndex returned a parent without children"
979 );
980 while !same_node(cursor.node(), anchor) {
981 assert!(
982 cursor.goto_next_sibling(),
983 "ParentIndex parent does not contain its child anchor"
984 );
985 }
986 while cursor.goto_next_sibling() {
987 let current = cursor.node();
988 if !current.is_named() {
989 continue;
990 }
991 if at_translation_unit
992 && (current.kind() == "namespace_definition"
993 || (current.kind() == "function_definition"
994 && first_class_like_child(current).is_some()))
995 {
996 return siblings;
997 }
998 siblings.push(current);
999 if cpp_is_stray_close_brace(current, source) {
1000 if let Some(semicolon) = current
1001 .next_named_sibling()
1002 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
1003 {
1004 siblings.push(semicolon);
1005 }
1006 return siblings;
1007 }
1008 if current.start_byte() >= node.end_byte()
1009 && matches!(current.kind(), "ERROR" | "labeled_statement")
1010 && cpp_nested_stray_close_brace(current, source).is_some()
1011 {
1012 return siblings;
1013 }
1014 }
1015 anchor = parent;
1016 }
1017 siblings
1018}
1019
1020fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
1021 if node.start_byte() >= class_end {
1022 return false;
1023 }
1024 node.end_byte() <= class_end
1025 || cpp_nested_stray_close_brace(node, source)
1026 .is_some_and(|close| close.start_byte() == class_end)
1027}
1028
1029struct FragmentedClassRecovery<'tree> {
1033 declaration_node: Node<'tree>,
1034 name: String,
1035 raw_supertypes: Vec<String>,
1036 body: FragmentedExportBody,
1037}
1038
1039fn fragmented_class_body<'tree>(
1051 node: Node<'tree>,
1052 source: &str,
1053) -> Option<FragmentedClassRecovery<'tree>> {
1054 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
1055 return Some(recovered);
1056 }
1057 let supported_container = node.kind() == "ERROR"
1058 || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
1059 if !supported_container {
1060 return None;
1061 }
1062 let mut cursor = node.walk();
1063 let children = node.children(&mut cursor).collect::<Vec<_>>();
1064 if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
1065 return Some(recovered);
1066 }
1067 let keyword = children.first()?;
1068 if !matches!(keyword.kind(), "class" | "struct" | "union") {
1069 return None;
1070 }
1071 let name_node = children
1072 .iter()
1073 .copied()
1074 .skip(1)
1075 .find(|child| child.is_named())?;
1076 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
1077 return None;
1078 }
1079 let name = normalize_cpp_whitespace(node_text(name_node, source));
1080 if name.is_empty() || cpp_export_macro_token(&name) {
1081 return None;
1082 }
1083 let open_index = children.iter().position(|child| child.kind() == "{")?;
1084 Some(FragmentedClassRecovery {
1085 declaration_node: node,
1086 name,
1087 raw_supertypes: extract_cpp_supertypes(node, source),
1088 body: fragmented_displaced_class_body(node, &children, open_index, source)?,
1089 })
1090}
1091
1092fn fragmented_export_macro_class_body<'tree>(
1103 node: Node<'tree>,
1104 children: &[Node<'tree>],
1105 source: &str,
1106) -> Option<FragmentedClassRecovery<'tree>> {
1107 let class_node = *children.first()?;
1108 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1109 return None;
1110 }
1111 class_node
1115 .child_by_field_name("name")
1116 .and_then(|name| direct_identifier_name(name, source))?;
1117 let invocation = *children.get(1)?;
1118 if invocation.is_named() || invocation.kind() != "(" {
1119 return None;
1120 }
1121 let open_index = children
1122 .iter()
1123 .position(|child| !child.is_named() && child.kind() == "{")?;
1124 let open = children[open_index];
1125 let name_node = recovered_export_head_name(node, open, source)?;
1126 let name = normalize_cpp_whitespace(node_text(name_node, source));
1127 if name.is_empty() || cpp_export_macro_token(&name) {
1128 return None;
1129 }
1130 Some(FragmentedClassRecovery {
1131 declaration_node: node,
1132 name,
1133 raw_supertypes: recovered_export_head_bases(
1134 node,
1135 name_node.end_byte(),
1136 open.start_byte(),
1137 source,
1138 ),
1139 body: fragmented_displaced_class_body(node, children, open_index, source)
1140 .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1141 })
1142}
1143
1144fn fragmented_displaced_class_body(
1150 node: Node<'_>,
1151 children: &[Node<'_>],
1152 open_index: usize,
1153 source: &str,
1154) -> Option<FragmentedExportBody> {
1155 let open = children[open_index];
1156 let nested_class_opens = children[open_index + 1..]
1157 .iter()
1158 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1159 .count();
1160 let mut closes_remaining = 1 + nested_class_opens;
1161 let mut sibling = node.next_named_sibling();
1162 while let Some(candidate) = sibling {
1163 let next = candidate.next_named_sibling();
1164 if cpp_is_stray_close_brace(candidate, source) {
1165 closes_remaining -= 1;
1166 if closes_remaining == 0 {
1167 let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
1168 if open.end_byte() >= candidate.start_byte() {
1169 return None;
1170 }
1171 return Some(FragmentedExportBody {
1172 reparse_start: open.end_byte(),
1173 reparse_end: candidate.start_byte(),
1174 class_range: Range {
1175 start_byte: node.start_byte(),
1176 end_byte: semicolon.end_byte(),
1177 start_line: node.start_position().row + 1,
1178 end_line: semicolon.end_position().row + 1,
1179 },
1180 });
1181 }
1182 }
1183 sibling = next;
1184 }
1185 None
1186}
1187
1188fn fragmented_container_close_class_body(
1196 node: Node<'_>,
1197 open: Node<'_>,
1198 source: &str,
1199) -> Option<FragmentedExportBody> {
1200 let parent = node.parent()?;
1201 if !matches!(
1202 parent.kind(),
1203 "declaration_list" | "field_declaration_list" | "compound_statement"
1204 ) {
1205 return None;
1206 }
1207 let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1208 if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1213 || open.end_byte() >= close.start_byte()
1214 {
1215 return None;
1216 }
1217 Some(FragmentedExportBody {
1218 reparse_start: open.end_byte(),
1219 reparse_end: close.start_byte(),
1220 class_range: Range {
1221 start_byte: node.start_byte(),
1222 end_byte: close.end_byte(),
1223 start_line: node.start_position().row + 1,
1224 end_line: close.end_position().row + 1,
1225 },
1226 })
1227}
1228
1229pub(crate) fn recovered_fragmented_class_has_body(
1230 node: Node<'_>,
1231 source: &str,
1232 expected_name: &str,
1233 expected_range: &Range,
1234) -> bool {
1235 fragmented_class_body(node, source).is_some_and(|recovered| {
1236 recovered.name == expected_name
1237 && recovered.body.class_range.start_byte == expected_range.start_byte
1238 && recovered.body.class_range.end_byte == expected_range.end_byte
1239 })
1240}
1241
1242fn fragmented_plain_class_declaration_body<'tree>(
1249 node: Node<'tree>,
1250 source: &str,
1251) -> Option<FragmentedClassRecovery<'tree>> {
1252 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1253 return None;
1254 }
1255 let class_node = node.child_by_field_name("type")?;
1256 if !matches!(
1257 class_node.kind(),
1258 "class_specifier" | "struct_specifier" | "union_specifier"
1259 ) {
1260 return None;
1261 }
1262 let name_node = class_node.child_by_field_name("name")?;
1263 let name = normalize_cpp_whitespace(node_text(name_node, source));
1264 if name.is_empty() || cpp_export_macro_token(&name) {
1265 return None;
1266 }
1267 let body = cpp_body_node(class_node)?;
1268 if body.kind() != "field_declaration_list" {
1269 return None;
1270 }
1271 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1272 if declarator.start_byte() < class_node.end_byte() {
1273 return None;
1274 }
1275 let mut cursor = node.walk();
1276 node.named_children(&mut cursor).any(|child| {
1277 if child.kind() != "ERROR"
1278 || child.start_byte() < class_node.end_byte()
1279 || child.end_byte() > declarator.start_byte()
1280 {
1281 return false;
1282 }
1283 let mut cursor = child.walk();
1284 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1285 let Some((return_type, attributes)) = components.split_last() else {
1286 return false;
1287 };
1288 matches!(
1289 return_type.kind(),
1290 "identifier"
1291 | "type_identifier"
1292 | "primitive_type"
1293 | "decltype"
1294 | "placeholder_type_specifier"
1295 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1296 && attributes.iter().all(|attribute| {
1297 matches!(attribute.kind(), "identifier" | "type_identifier")
1298 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1299 *attribute, source,
1300 )))
1301 })
1302 })
1303 } else {
1304 let mut cursor = node.walk();
1305 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1306 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1307 if same_node(*candidate_class, class_node)
1308 && continuation.kind() == "identifier"
1309 && node_text(*continuation, source) == "else"
1310 && continuation_body.kind() == "compound_statement"
1311 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1312 && continuation_body
1313 .child(continuation_body.child_count().saturating_sub(1))
1314 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1315 };
1316 if !displaced_member {
1317 return None;
1318 }
1319 let open = body
1320 .children(&mut body.walk())
1321 .find(|child| child.kind() == "{")?;
1322 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1323 let ordinary_boundary =
1324 siblings
1325 .iter()
1326 .copied()
1327 .enumerate()
1328 .find_map(|(close_index, close)| {
1329 cpp_is_stray_close_brace(close, source)
1330 .then(|| {
1331 siblings
1332 .get(close_index + 1)
1333 .copied()
1334 .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1335 .map(|semicolon| (close, semicolon))
1336 })
1337 .flatten()
1338 });
1339 let (close, semicolon) =
1340 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1341 (boundary.class_close, boundary.class_semicolon)
1342 } else {
1343 ordinary_boundary?
1344 };
1345 if open.end_byte() >= close.start_byte() {
1346 return None;
1347 }
1348 Some(FragmentedClassRecovery {
1349 declaration_node: class_node,
1350 name,
1351 raw_supertypes: extract_cpp_supertypes(class_node, source),
1352 body: FragmentedExportBody {
1353 reparse_start: open.end_byte(),
1354 reparse_end: close.start_byte(),
1355 class_range: Range {
1356 start_byte: class_node.start_byte(),
1357 end_byte: semicolon.end_byte(),
1358 start_line: class_node.start_position().row + 1,
1359 end_line: semicolon.end_position().row + 1,
1360 },
1361 },
1362 })
1363}
1364
1365fn displaced_export_function_namespace_shape<'tree>(
1366 declaration: Node<'tree>,
1367 source: &str,
1368) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1369 let mut nested = Vec::new();
1370 for index in (0..declaration.named_child_count()).rev() {
1371 nested.push(declaration.named_child(index)?);
1372 }
1373 while let Some(current) = nested.pop() {
1374 if recover_exported_class_function_definition(current, source).is_some() {
1380 return None;
1381 }
1382 for index in (0..current.named_child_count()).rev() {
1383 nested.push(current.named_child(index)?);
1384 }
1385 }
1386 let mut same_envelope_sibling = declaration.next_named_sibling();
1387 while let Some(current) = same_envelope_sibling {
1388 if recover_exported_class_function_definition(current, source).is_some() {
1389 return None;
1390 }
1391 same_envelope_sibling = current.next_named_sibling();
1392 }
1393 let declaration_list = declaration.parent()?;
1394 if declaration_list.kind() != "declaration_list" {
1395 return None;
1396 }
1397 let namespace = declaration_list.parent()?;
1398 if namespace.kind() != "namespace_definition"
1399 || namespace.child_by_field_name("body") != Some(declaration_list)
1400 {
1401 return None;
1402 }
1403 let class_close = direct_close_brace(declaration_list)?;
1404 let trailing_semicolon = namespace.next_named_sibling()?;
1405 if trailing_semicolon.kind() != "expression_statement"
1406 || trailing_semicolon.named_child_count() != 0
1407 {
1408 return None;
1409 }
1410 let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1416 let trailing_index = siblings
1417 .iter()
1418 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1419 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1420 recover_exported_class_function_definition(*candidate, source).is_some()
1421 }) {
1422 return None;
1427 }
1428 let mut namespace_items = Vec::new();
1429 let mut nested_fragment_end = 0;
1430 for current in siblings.into_iter().skip(trailing_index + 1) {
1431 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1432 {
1433 return Some(DisplacedFragmentNamespaceBoundary {
1434 class_close,
1435 class_semicolon: trailing_semicolon,
1436 namespace_items,
1437 });
1438 }
1439 if current.start_byte() >= nested_fragment_end
1440 && let Some(recovered) = fragmented_class_body(current, source)
1441 {
1442 nested_fragment_end = recovered.body.class_range.end_byte;
1443 } else if current.start_byte() >= nested_fragment_end
1444 && recover_exported_class_function_definition(current, source).is_some()
1445 && let Some(body) = cpp_body_node(current)
1446 && let Some(fragmented) =
1447 fragmented_export_function_body_region(current, body, source, None)
1448 {
1449 nested_fragment_end = fragmented.class_range.end_byte;
1450 }
1451 namespace_items.push(current);
1452 }
1453 None
1454}
1455
1456fn displaced_fragment_namespace_boundary<'tree>(
1457 declaration: Node<'tree>,
1458 body: Node<'tree>,
1459 source: &str,
1460) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1461 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1462 let reparse_start = body.start_byte() + 1;
1463 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1464 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1465}
1466
1467fn displaced_fragment_namespace_geometry<'tree>(
1473 declaration: Node<'tree>,
1474 source: &str,
1475) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1476 let envelope = declaration
1480 .parent()
1481 .filter(|parent| {
1482 parent.kind() == "template_declaration"
1483 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1484 })
1485 .unwrap_or(declaration);
1486 let declaration_list = envelope.parent()?;
1487 if declaration_list.kind() != "declaration_list" {
1488 return None;
1489 }
1490 let namespace = declaration_list.parent()?;
1491 if namespace.kind() != "namespace_definition"
1492 || namespace.child_by_field_name("body") != Some(declaration_list)
1493 {
1494 return None;
1495 }
1496 let class_close = direct_close_brace(declaration_list)?;
1497 let trailing_semicolon = namespace.next_named_sibling()?;
1498 if trailing_semicolon.kind() != "expression_statement"
1499 || trailing_semicolon.named_child_count() != 0
1500 {
1501 return None;
1502 }
1503 let mut namespace_items = Vec::new();
1504 let mut sibling = trailing_semicolon.next_named_sibling();
1505 let mut nested_fragment_end = 0;
1506 loop {
1507 let current = sibling?;
1508 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1509 {
1510 break;
1511 }
1512 if current.start_byte() >= nested_fragment_end
1513 && let Some(recovered) = fragmented_class_body(current, source)
1514 {
1515 nested_fragment_end = recovered.body.class_range.end_byte;
1516 }
1517 namespace_items.push(current);
1518 sibling = current.next_named_sibling();
1519 }
1520 Some(DisplacedFragmentNamespaceBoundary {
1521 class_close,
1522 class_semicolon: trailing_semicolon,
1523 namespace_items,
1524 })
1525}
1526
1527fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1529 (0..node.child_count())
1530 .filter_map(|index| node.child(index))
1531 .find(|child| !child.is_named() && child.kind() == "}")
1532}
1533
1534fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1537 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1538}
1539
1540fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1551 let bytes = source.as_bytes();
1552 if bytes.get(open_byte) != Some(&b'{') {
1553 return None;
1554 }
1555 let mut depth = 0usize;
1556 let mut i = open_byte;
1557 while i < bytes.len() {
1558 match bytes[i] {
1559 b'{' => depth += 1,
1560 b'}' => {
1561 depth = depth.checked_sub(1)?;
1562 if depth == 0 {
1563 return Some(i);
1564 }
1565 }
1566 b'/' if bytes.get(i + 1) == Some(&b'/') => {
1567 while i < bytes.len() && bytes[i] != b'\n' {
1568 i += 1;
1569 }
1570 continue;
1571 }
1572 b'/' if bytes.get(i + 1) == Some(&b'*') => {
1573 i += 2;
1574 while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1575 i += 1;
1576 }
1577 i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1578 continue;
1579 }
1580 quote @ (b'"' | b'\'') => {
1581 if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1584 return None;
1585 }
1586 i += 1;
1587 while i < bytes.len() && bytes[i] != quote {
1588 i += if bytes[i] == b'\\' { 2 } else { 1 };
1589 }
1590 if i >= bytes.len() {
1591 return None;
1592 }
1593 }
1594 _ => {}
1595 }
1596 i += 1;
1597 }
1598 None
1599}
1600
1601fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1602 let mut name = None;
1603 let mut colon_count = 0;
1604 let mut access_count = 0;
1605 for index in 0..node.child_count() {
1606 let child = node.child(index)?;
1607 match child.kind() {
1608 "identifier" | "type_identifier" if child.is_named() => {
1609 if name.is_some() {
1610 return None;
1611 }
1612 let candidate = normalize_cpp_whitespace(node_text(child, source));
1613 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1614 return None;
1615 }
1616 name = Some(candidate);
1617 }
1618 "template_function" | "template_type" if child.is_named() => {
1619 if name.is_some() {
1620 return None;
1621 }
1622 let candidate = child
1623 .child_by_field_name("name")
1624 .and_then(|name| direct_identifier_name(name, source))?;
1625 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1626 return None;
1627 }
1628 name = Some(candidate);
1629 }
1630 ":" if !child.is_named() => colon_count += 1,
1631 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1632 _ => return None,
1633 }
1634 }
1635 (colon_count == 1 && access_count == 1)
1636 .then_some(name)
1637 .flatten()
1638}
1639
1640fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1641 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1642 return false;
1643 }
1644 node.named_child(0)
1645 .and_then(|child| direct_identifier_name(child, source))
1646 .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1647}
1648
1649fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1650 (0..node.child_count()).any(|index| {
1651 node.child(index)
1652 .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1653 })
1654}
1655
1656fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1657 match node.kind() {
1658 "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1659 recovered_base_atom(node, source)
1660 }
1661 "template_type" | "template_function" => node
1662 .child_by_field_name("name")
1663 .and_then(|name| recovered_malformed_base_name(name, source)),
1664 "ERROR" => None,
1665 "qualified_identifier" | "scoped_type_identifier" => {
1666 let suffix = node
1667 .child_by_field_name("name")
1668 .and_then(|name| recovered_malformed_base_name(name, source))?;
1669 let scope = node
1670 .child_by_field_name("scope")
1671 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1672 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1673 malformed_qualified_prefix(node, source)?
1674 } else {
1675 if malformed_qualified_prefix(node, source).is_some() {
1676 return None;
1677 }
1678 scope
1679 };
1680 Some(format!("{prefix}::{suffix}"))
1681 }
1682 _ => None,
1683 }
1684}
1685
1686fn recovered_base_components(node: Node<'_>, source: &str) -> Option<Vec<String>> {
1687 let mut pending = vec![node];
1688 let mut components = Vec::new();
1689 while let Some(current) = pending.pop() {
1690 match current.kind() {
1691 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier" => {
1692 components.push(recovered_base_atom(current, source)?);
1693 }
1694 "template_type" | "template_function" => {
1695 pending.push(current.child_by_field_name("name")?);
1696 }
1697 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
1698 pending.push(current.child_by_field_name("name")?);
1699 if let Some(scope) = current.child_by_field_name("scope") {
1700 if recovered_base_atom(scope, source).is_some_and(|name| {
1704 matches!(name.as_str(), "public" | "protected" | "private")
1705 }) {
1706 pending.push(malformed_qualified_prefix_node(current, source)?);
1707 } else {
1708 if malformed_qualified_prefix_node(current, source).is_some() {
1709 return None;
1710 }
1711 pending.push(scope);
1712 }
1713 }
1714 }
1715 _ => return None,
1716 }
1717 }
1718 Some(components)
1719}
1720
1721fn recovered_base_specifier(
1722 node: Node<'_>,
1723 source: &str,
1724 is_virtual: bool,
1725) -> Option<CppBaseSpecifierFact> {
1726 let components = recovered_base_components(node, source)?;
1727 (!components.is_empty()).then_some(CppBaseSpecifierFact {
1728 absolute: node.child(0).is_some_and(|token| token.kind() == "::"),
1729 components,
1730 is_virtual,
1731 })
1732}
1733
1734fn recovered_base_is_virtual(container: Node<'_>, base: Node<'_>, source: &str) -> bool {
1735 let mut pending = vec![container];
1739 let mut is_virtual = false;
1740 while let Some(node) = pending.pop() {
1741 if node.start_byte() >= base.end_byte() {
1742 break;
1743 }
1744 if node.child_count() != 0 {
1745 let mut cursor = node.walk();
1746 let children = node
1747 .children(&mut cursor)
1748 .take_while(|child| child.start_byte() < base.end_byte())
1749 .collect::<Vec<_>>();
1750 pending.extend(children.into_iter().rev());
1751 continue;
1752 }
1753 if node.start_byte() < base.start_byte() && matches!(node.kind(), "," | ":") {
1754 is_virtual = false;
1755 } else if node.kind() == "virtual"
1756 || recovered_base_atom(node, source).is_some_and(|name| name == "virtual")
1757 {
1758 is_virtual = true;
1759 }
1760 }
1761 is_virtual
1762}
1763
1764fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1765 if !matches!(
1766 node.kind(),
1767 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1768 ) {
1769 return None;
1770 }
1771 let name = normalize_cpp_whitespace(node_text(node, source));
1772 (!name.is_empty()).then_some(name)
1773}
1774
1775fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1776 malformed_qualified_prefix_node(node, source)
1777 .and_then(|prefix| recovered_base_atom(prefix, source))
1778}
1779
1780fn malformed_qualified_prefix_node<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1781 let mut prefix = None;
1782 let mut cursor = node.walk();
1783 for error in node
1784 .named_children(&mut cursor)
1785 .filter(|child| child.kind() == "ERROR")
1786 {
1787 if prefix.is_some() {
1788 return None;
1789 }
1790 let mut error_cursor = error.walk();
1795 let atoms = error
1796 .named_children(&mut error_cursor)
1797 .map(|child| recovered_base_atom(child, source).map(|name| (child, name)))
1798 .collect::<Option<Vec<_>>>()?;
1799 let [atom] = atoms
1800 .iter()
1801 .filter(|(_, name)| name != "virtual")
1802 .collect::<Vec<_>>()[..]
1803 else {
1804 return None;
1805 };
1806 prefix = Some(atom.0);
1807 }
1808 prefix
1809}
1810
1811type RecoveredFunctionDefinitionClass<'tree> = (
1812 Node<'tree>,
1813 String,
1814 Option<Vec<String>>,
1815 Option<Vec<CppBaseSpecifierFact>>,
1816);
1817struct StrandedRun<'tree> {
1821 declarations: Vec<MacroWrappedDeclaration<'tree>>,
1822 complete: bool,
1828}
1829
1830struct MacroWrappedDeclaration<'tree> {
1831 declarator: Node<'tree>,
1832 range: Range,
1833 is_static: bool,
1838}
1839
1840fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1854 declaration_scope_container(node, ancestry).is_some()
1855}
1856
1857fn declaration_scope_container<'tree>(
1862 node: Node<'tree>,
1863 ancestry: &ParentIndex<'tree>,
1864) -> Option<Node<'tree>> {
1865 let mut parent = ancestry.parent(node)?;
1866 loop {
1867 match parent.kind() {
1868 "translation_unit" => return Some(parent),
1869 "declaration_list" => {
1870 return ancestry
1871 .parent(parent)
1872 .is_some_and(|grandparent| {
1873 matches!(
1874 grandparent.kind(),
1875 "namespace_definition" | "linkage_specification"
1876 )
1877 })
1878 .then_some(parent);
1879 }
1880 "ERROR" => match ancestry.parent(parent) {
1881 Some(grandparent) => parent = grandparent,
1882 None => return Some(parent),
1885 },
1886 _ => return None,
1887 }
1888 }
1889}
1890
1891fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1893 node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
1894}
1895
1896fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1901 matches!(
1902 node.kind(),
1903 "identifier"
1904 | "type_identifier"
1905 | "primitive_type"
1906 | "sized_type_specifier"
1907 | "struct_specifier"
1908 | "union_specifier"
1909 | "enum_specifier"
1910 | "type_qualifier"
1911 | "storage_class_specifier"
1912 | "explicit_function_specifier"
1913 | "virtual_function_specifier"
1914 | "qualified_identifier"
1915 | "template_type"
1916 | "dependent_type"
1917 | "placeholder_type_specifier"
1918 )
1919}
1920
1921fn is_macro_argument_error(node: Node<'_>) -> bool {
1927 if node.kind() != "ERROR" {
1928 return false;
1929 }
1930 let mut cursor = node.walk();
1931 node.named_children(&mut cursor).all(|child| {
1932 matches!(
1933 child.kind(),
1934 "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1935 )
1936 })
1937}
1938
1939fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1948 declarator
1949 .next_sibling()
1950 .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1951 .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1952}
1953
1954fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1976 let mut parts = Vec::new();
1977 let mut cursor = node.walk();
1978 for child in node.named_children(&mut cursor) {
1979 if child.kind() == "ERROR" {
1980 let mut error_cursor = child.walk();
1981 parts.extend(child.named_children(&mut error_cursor));
1982 } else {
1983 parts.push(child);
1984 }
1985 }
1986
1987 let mut declarations = Vec::new();
1988 let mut start = None;
1989 let mut is_static = false;
1990 let mut complete = true;
1991 for part in parts {
1992 if part.kind() == "comment" {
1993 continue;
1994 }
1995 if let Some(declarator) = extract_function_declarator(part) {
1996 let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1997 declarations.push(MacroWrappedDeclaration {
1998 declarator,
1999 range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
2000 is_static,
2001 });
2002 is_static = false;
2003 continue;
2004 }
2005 if !is_recovered_declaration_type_part(part) {
2006 complete = false;
2007 break;
2008 }
2009 is_static |= part.kind() == "storage_class_specifier"
2010 && normalize_cpp_whitespace(node_text(part, source)) == "static";
2011 start.get_or_insert(part.start_byte());
2012 }
2013 StrandedRun {
2014 declarations,
2015 complete: complete && start.is_none(),
2016 }
2017}
2018
2019fn macro_wrapped_declarations<'tree>(
2045 envelope: Node<'tree>,
2046 source: &str,
2047 ancestry: &ParentIndex<'tree>,
2048) -> Vec<MacroWrappedDeclaration<'tree>> {
2049 let mut declarations = Vec::new();
2050 if !is_declaration_scope_error(envelope, ancestry) {
2051 return declarations;
2052 }
2053 let mut cursor = envelope.walk();
2054 let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
2055 let [macro_name, arguments @ ..] = children.as_slice() else {
2056 return declarations;
2057 };
2058 if macro_name.kind() != "identifier" {
2059 return declarations;
2060 }
2061 let mut wrapped_declaration_seen = false;
2062 for argument in arguments {
2063 match argument.kind() {
2064 "comment" => {}
2065 "parameter_declaration" => {
2066 let recovered = stranded_declaration_run(*argument, source).declarations;
2067 if recovered.is_empty() {
2068 break;
2069 }
2070 wrapped_declaration_seen = true;
2071 declarations.extend(recovered);
2072 }
2073 "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
2078 _ => break,
2079 }
2080 }
2081 declarations
2082}
2083
2084struct CollapsedMacroDeclarationRun {
2087 invocation_end: usize,
2090 region_end: usize,
2095}
2096
2097struct MacroInvocationTokens<'tree> {
2109 stack: Vec<Node<'tree>>,
2110 frontier: Option<Node<'tree>>,
2120}
2121
2122impl<'tree> MacroInvocationTokens<'tree> {
2123 fn new(node: Node<'tree>) -> Self {
2124 Self {
2125 stack: vec![node],
2126 frontier: Some(node),
2127 }
2128 }
2129}
2130
2131impl<'tree> Iterator for MacroInvocationTokens<'tree> {
2132 type Item = Node<'tree>;
2133
2134 fn next(&mut self) -> Option<Node<'tree>> {
2135 loop {
2136 let Some(node) = self.stack.pop() else {
2137 let sibling = self.frontier?.next_sibling()?;
2138 self.frontier = Some(sibling);
2139 self.stack.push(sibling);
2140 continue;
2141 };
2142 if node.child_count() == 0 {
2143 if node.kind() == "comment" || node.is_missing() {
2144 continue;
2145 }
2146 return Some(node);
2147 }
2148 let mut cursor = node.walk();
2149 let children = node.children(&mut cursor).collect::<Vec<_>>();
2150 self.stack.extend(children.into_iter().rev());
2151 }
2152 }
2153}
2154
2155fn collapsed_macro_declaration_run<'tree>(
2202 node: Node<'tree>,
2203 source: &str,
2204 ancestry: &ParentIndex<'tree>,
2205) -> Option<CollapsedMacroDeclarationRun> {
2206 let container_end = declaration_scope_container(node, ancestry)?.end_byte();
2207 let mut tokens = MacroInvocationTokens::new(node);
2208 let name = tokens.next()?;
2209 if !matches!(name.kind(), "identifier" | "type_identifier")
2210 || !cpp_export_macro_token(node_text(name, source))
2211 {
2212 return None;
2213 }
2214 if tokens.next()?.kind() != "(" {
2215 return None;
2216 }
2217 let mut depth = 1usize;
2218 let invocation_end = loop {
2219 let token = tokens.next()?;
2220 match token.kind() {
2221 "(" => depth += 1,
2222 ";" => return None,
2223 ")" => {
2224 depth -= 1;
2225 if depth == 0 {
2226 let semicolon = tokens.next()?;
2227 if semicolon.kind() != ";" {
2228 return None;
2229 }
2230 break semicolon.end_byte();
2231 }
2232 }
2233 _ => {}
2234 }
2235 };
2236 if invocation_end >= container_end {
2242 return None;
2243 }
2244 let region_end = if node.end_byte() > invocation_end {
2253 container_end
2254 } else {
2255 invocation_end
2256 };
2257 Some(CollapsedMacroDeclarationRun {
2258 invocation_end,
2259 region_end,
2260 })
2261}
2262
2263fn string_attribute_macro_member_declarators<'tree>(
2284 field: Node<'tree>,
2285 source: &str,
2286) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2287 if field.kind() != "field_declaration"
2288 || field
2289 .child_by_field_name("type")
2290 .is_none_or(|type_node| type_node.kind() != "type_identifier")
2291 {
2292 return None;
2293 }
2294 let declarator = field.child_by_field_name("declarator")?;
2295 if declarator.kind() != "parenthesized_declarator" {
2296 return None;
2297 }
2298 let opening = declarator.named_child(0)?;
2299 if opening.kind() != "ERROR"
2300 || opening
2301 .named_child(0)
2302 .is_none_or(|word| word.kind() != "identifier")
2303 {
2304 return None;
2305 }
2306 let declarations = stranded_declaration_run(declarator, source).declarations;
2307 (!declarations.is_empty()).then_some(declarations)
2308}
2309
2310fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2317 let Some(call) = (node.kind() == "expression_statement")
2318 .then(|| node.named_child(0))
2319 .flatten()
2320 .filter(|child| child.kind() == "call_expression")
2321 else {
2322 return false;
2323 };
2324 call.child_by_field_name("function")
2325 .is_some_and(|function| function.kind() == "identifier")
2326 && call
2327 .child_by_field_name("arguments")
2328 .is_some_and(|arguments| {
2329 let mut cursor = arguments.walk();
2330 arguments.named_child_count() > 0
2331 && arguments
2332 .named_children(&mut cursor)
2333 .all(|argument| argument.kind() == "string_literal")
2334 })
2335}
2336
2337fn cpp_access_label_constructor_call_start(
2350 node: Node<'_>,
2351 class_name: &str,
2352 source: &str,
2353) -> Option<usize> {
2354 if node.kind() != "labeled_statement" {
2355 return None;
2356 }
2357 let label = node.named_child(0)?;
2358 if label.kind() != "statement_identifier"
2359 || !matches!(
2360 node_text(label, source).trim(),
2361 "public" | "private" | "protected"
2362 )
2363 {
2364 return None;
2365 }
2366 let mut starts = Vec::new();
2367 let mut stack = vec![node];
2368 while let Some(current) = stack.pop() {
2369 if current.kind() == "call_expression"
2370 && current
2371 .child_by_field_name("function")
2372 .is_some_and(|function| {
2373 function.kind() == "identifier"
2374 && node_text(function, source).trim() == class_name
2375 })
2376 {
2377 starts.push(current.start_byte());
2378 }
2379 let mut cursor = current.walk();
2380 stack.extend(current.named_children(&mut cursor));
2381 }
2382 let [start] = starts.as_slice() else {
2383 return None;
2384 };
2385 Some(*start)
2386}
2387
2388fn cpp_declarator_function_definition<'tree>(
2392 declarator: Node<'tree>,
2393 ancestry: &ParentIndex<'tree>,
2394) -> Option<Node<'tree>> {
2395 let mut current = declarator;
2396 while let Some(parent) = ancestry.parent(current) {
2397 match parent.kind() {
2398 "function_definition" if parent.child_by_field_name("body").is_some() => {
2399 return Some(parent);
2400 }
2401 "pointer_declarator"
2402 | "reference_declarator"
2403 | "parenthesized_declarator"
2404 | "array_declarator" => current = parent,
2405 _ => return None,
2406 }
2407 }
2408 None
2409}
2410
2411fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2415 let mut current = node;
2416 while let Some(parent) = ancestry.parent(current) {
2417 if parent.kind() == "namespace_definition"
2418 && parent.child_by_field_name("body") == Some(current)
2419 {
2420 return true;
2421 }
2422 current = parent;
2423 }
2424 false
2425}
2426
2427pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2441 let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2442 let root = tree.root_node();
2443 let mut cursor = root.walk();
2444 let items = root
2445 .named_children(&mut cursor)
2446 .filter(|child| child.kind() != "comment")
2447 .collect::<Vec<_>>();
2448 let [item] = items.as_slice() else {
2449 return None;
2450 };
2451 if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2452 return None;
2453 }
2454 match item.kind() {
2455 "function_definition" => Some(item.child_by_field_name("body").is_some()),
2456 "declaration" | "field_declaration" => Some(false),
2457 _ => None,
2458 }
2459}
2460
2461pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2473 let ancestry = ParentIndex::unindexed();
2474 !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2475 || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2476}
2477
2478fn recover_exported_class_function_definition<'tree>(
2479 node: Node<'tree>,
2480 source: &str,
2481) -> Option<RecoveredFunctionDefinitionClass<'tree>> {
2482 if node.kind() != "function_definition" {
2483 return None;
2484 }
2485 if let Some(prefix) = node.prev_named_sibling()
2486 && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2487 && recovered.range.end_byte == node.end_byte()
2488 {
2489 return Some((
2490 node,
2491 recovered.name,
2492 recovered.raw_supertypes,
2493 recovered.base_facts,
2494 ));
2495 }
2496 let type_node = node.child_by_field_name("type")?;
2497 let declarator = node.child_by_field_name("declarator")?;
2498
2499 if matches!(
2500 type_node.kind(),
2501 "class_specifier" | "struct_specifier" | "union_specifier"
2502 ) {
2503 let type_name = type_node
2504 .child_by_field_name("name")
2505 .and_then(|name| direct_identifier_name(name, source));
2506 let exported_macro_type = type_name
2507 .as_ref()
2508 .is_some_and(|name| cpp_export_macro_token(name));
2509 if exported_macro_type {
2510 let mut cursor = node.walk();
2511 let errors_before_declarator = node
2512 .named_children(&mut cursor)
2513 .filter(|child| {
2514 child.kind() == "ERROR"
2515 && child.start_byte() >= type_node.end_byte()
2516 && child.end_byte() <= declarator.start_byte()
2517 })
2518 .collect::<Vec<_>>();
2519 if let Some(name) = errors_before_declarator
2520 .iter()
2521 .find_map(|error| displaced_exported_class_name(*error, source))
2522 {
2523 let base_node = errors_before_declarator
2524 .iter()
2525 .any(|error| malformed_inheritance_syntax(*error))
2526 .then_some(declarator);
2527 let raw_supertypes = base_node
2528 .and_then(|base| recovered_malformed_base_name(base, source))
2529 .map(|base| vec![base]);
2530 let base_facts = base_node
2531 .and_then(|base| {
2532 recovered_base_specifier(
2533 base,
2534 source,
2535 recovered_base_is_virtual(node, base, source),
2536 )
2537 })
2538 .map(|base| vec![base]);
2539 return Some((node, name, raw_supertypes, base_facts));
2540 }
2541 if errors_before_declarator
2542 .iter()
2543 .any(|error| malformed_inheritance_syntax(*error))
2544 {
2545 return None;
2546 }
2547 }
2548 if !exported_macro_type
2549 && let Some(name) = type_name
2550 && !cpp_export_macro_token(&name)
2551 && let Some(base) =
2552 recovered_postfix_export_macro_base(node, type_node, declarator, source)
2553 {
2554 let base_node =
2555 recovered_postfix_export_macro_base_node(node, type_node, declarator, source)?;
2556 let base_facts = Some(vec![recovered_base_specifier(
2557 base_node,
2558 source,
2559 recovered_base_is_virtual(node, base_node, source),
2560 )?]);
2561 return Some((node, name, Some(vec![base]), base_facts));
2562 }
2563 if let Some(name) = direct_identifier_name(declarator, source)
2564 && exported_macro_type
2565 && !cpp_export_macro_token(&name)
2566 {
2567 let base_node = exported_macro_type
2568 .then(|| recovered_single_base_after_declarator_node(node, declarator, source))
2569 .flatten();
2570 let raw_supertypes = base_node
2571 .and_then(|base| recovered_malformed_base_name(base, source))
2572 .map(|base| vec![base]);
2573 let base_facts = base_node
2574 .and_then(|base| {
2575 recovered_base_specifier(
2576 base,
2577 source,
2578 recovered_base_is_virtual(node, base, source),
2579 )
2580 })
2581 .map(|base| vec![base]);
2582 return Some((node, name, raw_supertypes, base_facts));
2583 }
2584 if declarator.kind() == "parenthesized_declarator"
2585 && type_node
2586 .child_by_field_name("name")
2587 .and_then(|name| direct_identifier_name(name, source))
2588 .is_some_and(|name| cpp_export_macro_token(&name))
2589 {
2590 if let Some((name, base_node)) =
2591 recovered_function_like_export_class_owner_node(declarator, source)
2592 {
2593 return Some((
2594 node,
2595 name,
2596 Some(vec![recovered_malformed_base_name(base_node, source)?]),
2597 Some(vec![recovered_base_specifier(
2598 base_node,
2599 source,
2600 recovered_base_is_virtual(node, base_node, source),
2601 )?]),
2602 ));
2603 }
2604 let body_start = node
2605 .child_by_field_name("body")
2606 .map(|body| body.start_byte())
2607 .unwrap_or(node.end_byte());
2608 let mut cursor = node.walk();
2609 if let Some(name) = node
2610 .named_children(&mut cursor)
2611 .filter(|child| {
2612 child.kind() == "ERROR"
2613 && child.start_byte() >= declarator.end_byte()
2614 && child.end_byte() <= body_start
2615 })
2616 .find_map(|error| declarator_name_from_node(error, source))
2617 {
2618 return Some((node, name, None, None));
2619 }
2620 }
2621 }
2622
2623 let declarator_text = direct_identifier_name(declarator, source)?;
2624 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2625 return None;
2626 }
2627 class_identifier_before_body(node, source).map(|name| (node, name, None, None))
2628}
2629
2630fn recovered_function_like_export_class_owner_node<'tree>(
2631 declarator: Node<'tree>,
2632 source: &str,
2633) -> Option<(String, Node<'tree>)> {
2634 if declarator.kind() != "parenthesized_declarator" {
2635 return None;
2636 }
2637 let mut cursor = declarator.walk();
2638 let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2639 let [prefix, base] = children.as_slice() else {
2640 return None;
2641 };
2642 if prefix.kind() != "ERROR"
2643 || !matches!(
2644 base.kind(),
2645 "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2646 )
2647 {
2648 return None;
2649 }
2650 let mut identifiers = Vec::new();
2651 let mut prefix_cursor = prefix.walk();
2652 for child in prefix.named_children(&mut prefix_cursor) {
2653 match child.kind() {
2654 "number_literal" | "string_literal" | "char_literal" => {}
2655 "identifier" | "type_identifier" => {
2656 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2657 }
2658 _ => return None,
2659 }
2660 }
2661 let name = match identifiers.as_slice() {
2662 [name] => name.clone(),
2663 [name, final_token] if final_token == "final" => name.clone(),
2664 _ => return None,
2665 };
2666 if name.is_empty() || cpp_export_macro_token(&name) {
2667 return None;
2668 }
2669 Some((name, *base))
2670}
2671
2672fn recovered_export_head_base_data(
2686 node: Node<'_>,
2687 after: usize,
2688 before: usize,
2689 source: &str,
2690) -> (Vec<String>, Vec<CppBaseSpecifierFact>) {
2691 let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2692 let mut base_nodes = Vec::new();
2693 let mut cursor = node.walk();
2694 for child in node.named_children(&mut cursor) {
2695 if child.kind() == "ERROR" {
2696 let mut error_cursor = child.walk();
2697 base_nodes.extend(
2698 child
2699 .named_children(&mut error_cursor)
2700 .filter(within)
2701 .filter_map(|part| {
2702 recovered_malformed_base_name(part, source).map(|_| (part, child))
2703 }),
2704 );
2705 } else if within(&child) && recovered_malformed_base_name(child, source).is_some() {
2706 base_nodes.push((child, node));
2707 }
2708 }
2709 let mut bases = Vec::new();
2710 let mut facts = Vec::new();
2711 for (base, container) in base_nodes {
2712 let Some(name) = recovered_malformed_base_name(base, source) else {
2713 continue;
2714 };
2715 if matches!(
2716 name.as_str(),
2717 "final" | "public" | "protected" | "private" | "virtual"
2718 ) {
2719 continue;
2720 }
2721 bases.push(name);
2722 if let Some(fact) = recovered_base_specifier(
2723 base,
2724 source,
2725 recovered_base_is_virtual(container, base, source),
2726 ) {
2727 facts.push(fact);
2728 }
2729 }
2730 (bases, facts)
2731}
2732
2733fn recovered_export_head_bases(
2734 node: Node<'_>,
2735 after: usize,
2736 before: usize,
2737 source: &str,
2738) -> Vec<String> {
2739 recovered_export_head_base_data(node, after, before, source).0
2740}
2741
2742fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2747 if token.is_named() {
2748 token.kind() == "identifier" && node_text(token, source) == "final"
2749 } else {
2750 token.kind() == "final"
2751 }
2752}
2753
2754fn recovered_export_head_name<'tree>(
2767 node: Node<'tree>,
2768 tail: Node<'tree>,
2769 source: &str,
2770) -> Option<Node<'tree>> {
2771 export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
2772}
2773
2774fn recovered_export_pair_head_name<'tree>(
2781 prefix: Node<'tree>,
2782 sibling: Node<'tree>,
2783 tail: Node<'tree>,
2784 source: &str,
2785) -> Option<Node<'tree>> {
2786 let mut tokens = export_head_tokens(prefix, None);
2787 tokens.extend(export_head_tokens(sibling, Some(tail)));
2788 export_head_name_from_tokens(&tokens, source)
2789}
2790
2791fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
2795 let mut tokens = Vec::new();
2796 let mut cursor = node.walk();
2797 for child in node.children(&mut cursor) {
2798 if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
2799 break;
2800 }
2801 if child.kind() == "ERROR" {
2802 let mut fragment_cursor = child.walk();
2803 tokens.extend(child.children(&mut fragment_cursor));
2804 } else {
2805 tokens.push(child);
2806 }
2807 }
2808 tokens
2809}
2810
2811fn export_head_name_from_tokens<'tree>(
2813 tokens: &[Node<'tree>],
2814 source: &str,
2815) -> Option<Node<'tree>> {
2816 let mut name = None;
2817 for token in tokens.iter().copied() {
2818 if recovered_export_head_final(token, source) || (!token.is_named() && token.kind() == ":")
2819 {
2820 break;
2821 }
2822 if token.is_named()
2823 && !token.is_missing()
2824 && matches!(
2825 token.kind(),
2826 "identifier" | "type_identifier" | "field_identifier"
2827 )
2828 {
2829 name = Some(token);
2830 }
2831 }
2832 name
2833}
2834
2835fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2838 let mut cursor = declaration.walk();
2839 declaration
2840 .named_children(&mut cursor)
2841 .find(|child| child.kind() == "init_declarator")
2842}
2843
2844fn recovered_export_declaration_tail<'tree>(
2852 declaration: Node<'tree>,
2853 head_end: usize,
2854 source: &str,
2855) -> Option<(Vec<String>, Option<Vec<CppBaseSpecifierFact>>, Node<'tree>)> {
2856 let init = recovered_export_init_declarator(declaration)?;
2857 let body = init.child_by_field_name("value")?;
2858 if body.kind() != "initializer_list" {
2859 return None;
2860 }
2861 let (mut bases, mut facts) =
2862 recovered_export_head_base_data(declaration, head_end, init.start_byte(), source);
2863 let (more_bases, more_facts) =
2864 recovered_export_head_base_data(init, init.start_byte(), body.start_byte(), source);
2865 bases.extend(more_bases);
2866 facts.extend(more_facts);
2867 Some((bases, (!facts.is_empty()).then_some(facts), body))
2868}
2869
2870fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
2884 match node.kind() {
2885 "ERROR" => {
2886 let Some(class_node) = first_class_like_child(node) else {
2887 return false;
2888 };
2889 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2890 return false;
2891 }
2892 if class_node
2893 .child_by_field_name("name")
2894 .and_then(|name| direct_identifier_name(name, source))
2895 .is_none()
2896 {
2897 return false;
2898 }
2899 class_node
2900 .next_sibling()
2901 .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
2902 }
2903 "declaration" => {
2904 let mut cursor = node.walk();
2905 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
2906 let Some(keyword) = children
2912 .iter()
2913 .copied()
2914 .flat_map(|child| {
2915 let mut cursor = child.walk();
2916 if child.kind() == "ERROR" {
2917 child.named_children(&mut cursor).collect::<Vec<_>>()
2918 } else {
2919 vec![child]
2920 }
2921 })
2922 .find(|child| {
2923 child.kind() == "identifier"
2924 && matches!(node_text(*child, source), "class" | "struct" | "union")
2925 })
2926 else {
2927 return false;
2928 };
2929 children.last().is_some_and(|init| {
2930 init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
2931 let mut cursor = init.walk();
2932 let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
2933 matches!(parts.as_slice(), [macro_name, arguments]
2934 if matches!(macro_name.kind(), "identifier" | "type_identifier")
2935 && arguments.kind() == "argument_list")
2936 }
2937 })
2938 }
2939 _ => false,
2940 }
2941}
2942
2943fn recover_function_like_export_class_pair(
2944 node: Node<'_>,
2945 source: &str,
2946) -> Option<RecoveredFunctionLikeExportClassPair> {
2947 if !is_function_like_export_class_head(node, source) {
2948 return None;
2949 }
2950 let sibling = node.next_named_sibling()?;
2951 let (name, raw_supertypes, base_facts, body) = match sibling.kind() {
2952 "compound_statement" => (
2958 recovered_export_pair_head_name(node, sibling, sibling, source)
2959 .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2960 None,
2961 None,
2962 sibling,
2963 ),
2964 "expression_statement" => {
2965 let compound = sibling.named_child(0)?;
2966 if compound.kind() != "compound_literal_expression" {
2967 return None;
2968 }
2969 let body = compound.child_by_field_name("value")?;
2970 if body.kind() != "initializer_list" {
2971 return None;
2972 }
2973 (
2974 compound
2975 .child_by_field_name("type")
2976 .and_then(|name| direct_identifier_name(name, source))?,
2977 None,
2978 None,
2979 body,
2980 )
2981 }
2982 "labeled_statement" => {
2983 let label = sibling.child_by_field_name("label")?;
2984 if label.kind() != "statement_identifier" {
2985 return None;
2986 }
2987 let name = normalize_cpp_whitespace(node_text(label, source));
2988 let declaration = sibling
2989 .named_children(&mut sibling.walk())
2990 .find(|child| child.kind() == "declaration")?;
2991 let access = declaration.child_by_field_name("type")?;
2992 if !matches!(
2993 node_text(access, source),
2994 "public" | "protected" | "private"
2995 ) {
2996 return None;
2997 }
2998 let (bases, base_facts, body) =
2999 recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
3000 (name, (!bases.is_empty()).then_some(bases), base_facts, body)
3001 }
3002 "function_definition" => {
3008 let body = sibling.child_by_field_name("body")?;
3009 if body.kind() != "compound_statement" {
3010 return None;
3011 }
3012 let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
3013 let (bases, base_facts) = recovered_export_head_base_data(
3014 sibling,
3015 name_node.end_byte(),
3016 body.start_byte(),
3017 source,
3018 );
3019 (
3020 normalize_cpp_whitespace(node_text(name_node, source)),
3021 (!bases.is_empty()).then_some(bases),
3022 (!base_facts.is_empty()).then_some(base_facts),
3023 body,
3024 )
3025 }
3026 "declaration" => {
3030 let init = recovered_export_init_declarator(sibling)?;
3031 let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
3032 let (bases, base_facts, body) =
3033 recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
3034 (
3035 normalize_cpp_whitespace(node_text(name_node, source)),
3036 (!bases.is_empty()).then_some(bases),
3037 base_facts,
3038 body,
3039 )
3040 }
3041 _ => return None,
3042 };
3043 debug_assert!(
3044 !name.is_empty(),
3045 "a recovered class head names its class by an identifier token"
3046 );
3047 let range = Range {
3048 start_byte: node.start_byte(),
3049 end_byte: sibling.end_byte(),
3050 start_line: node.start_position().row + 1,
3051 end_line: sibling.end_position().row + 1,
3052 };
3053 Some(RecoveredFunctionLikeExportClassPair {
3054 name,
3055 raw_supertypes,
3056 base_facts,
3057 range,
3058 fragmented_body: recovered_fragmented_export_body(body, range)?,
3059 })
3060}
3061
3062fn recover_embedded_function_like_export_classes(
3069 node: Node<'_>,
3070 source: &str,
3071) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
3072 if !node.is_error() {
3073 return Vec::new();
3074 }
3075
3076 let mut nodes = Vec::new();
3077 let mut class_tokens = Vec::new();
3078 let mut stack = vec![node];
3079 while let Some(current) = stack.pop() {
3080 nodes.push(current);
3081 if !current.is_named() && current.kind() == "class" {
3085 class_tokens.push(current);
3086 }
3087 push_children_reversed(current, &mut stack);
3088 }
3089 if class_tokens.is_empty() {
3090 return Vec::new();
3091 }
3092 class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3093 nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3094
3095 let language = node.language();
3098 let class_kind = NodeKindIds::new(&language, "class");
3099 let identifier_kinds = [
3100 NodeKindIds::new(&language, "identifier"),
3101 NodeKindIds::new(&language, "type_identifier"),
3102 NodeKindIds::new(&language, "field_identifier"),
3103 ];
3104 let argument_list_kind = NodeKindIds::new(&language, "argument_list");
3105 let colon_kind = NodeKindIds::new(&language, ":");
3106 let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
3107
3108 let mut recovered = Vec::new();
3109 for class_token in class_tokens {
3110 debug_assert!(class_kind.matches(class_token));
3111 let row = class_token.start_position().row;
3112 let is_identifier = |candidate: &Node<'_>| {
3118 !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
3119 };
3120 let Some(macro_name) = nodes.iter().copied().find(|candidate| {
3121 candidate.start_byte() >= class_token.end_byte()
3122 && candidate.start_position().row == row
3123 && is_identifier(candidate)
3124 }) else {
3125 continue;
3126 };
3127 let Some(arguments) = nodes.iter().copied().find(|candidate| {
3128 argument_list_kind.matches(*candidate)
3129 && candidate.start_byte() >= macro_name.end_byte()
3130 && candidate.start_position().row == row
3131 }) else {
3132 continue;
3133 };
3134 if nodes.iter().any(|candidate| {
3135 is_identifier(candidate)
3136 && candidate.start_byte() >= macro_name.end_byte()
3137 && candidate.end_byte() <= arguments.start_byte()
3138 }) {
3139 continue;
3140 }
3141 let Some(head_end) = nodes.iter().copied().find(|candidate| {
3142 candidate.start_byte() >= arguments.end_byte()
3143 && (recovered_export_head_final(*candidate, source)
3144 || (!candidate.is_named() && colon_kind.matches(*candidate)))
3145 }) else {
3146 continue;
3147 };
3148 let Some(name_node) = nodes.iter().copied().rfind(|candidate| {
3149 is_identifier(candidate)
3150 && candidate.start_byte() >= arguments.end_byte()
3151 && candidate.end_byte() <= head_end.start_byte()
3152 && candidate.start_position().row == row
3153 }) else {
3154 continue;
3155 };
3156 let name = normalize_cpp_whitespace(node_text(name_node, source));
3157 let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
3158 field_initializer_kind.matches(*candidate)
3159 && candidate.start_byte() >= name_node.end_byte()
3160 && candidate
3161 .child_by_field_name("field")
3162 .or_else(|| candidate.named_child(0))
3163 .is_some()
3164 && candidate
3165 .child_by_field_name("value")
3166 .or_else(|| {
3167 let mut cursor = candidate.walk();
3168 candidate
3169 .named_children(&mut cursor)
3170 .find(|child| child.kind() == "initializer_list")
3171 })
3172 .is_some_and(|value| value.kind() == "initializer_list")
3173 }) else {
3174 continue;
3175 };
3176 let has_access = nodes.iter().copied().any(|candidate| {
3177 candidate.start_byte() >= name_node.end_byte()
3178 && candidate.end_byte() <= base_initializer.start_byte()
3179 && matches!(
3180 normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
3181 "public" | "protected" | "private"
3182 )
3183 });
3184 if !has_access {
3185 continue;
3186 }
3187 let Some(base_node) = base_initializer
3188 .child_by_field_name("field")
3189 .or_else(|| base_initializer.named_child(0))
3190 else {
3191 continue;
3192 };
3193 let Some(base) = recovered_malformed_base_name(base_node, source) else {
3194 continue;
3195 };
3196 let body = base_initializer
3197 .child_by_field_name("value")
3198 .or_else(|| {
3199 let mut cursor = base_initializer.walk();
3200 base_initializer
3201 .named_children(&mut cursor)
3202 .find(|child| child.kind() == "initializer_list")
3203 })
3204 .expect("initializer-list value checked above");
3205 let range = Range {
3206 start_byte: class_token.start_byte(),
3207 end_byte: body.end_byte(),
3208 start_line: class_token.start_position().row + 1,
3209 end_line: body.end_position().row + 1,
3210 };
3211 if recovered
3212 .iter()
3213 .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
3214 existing.name == name && existing.range == range
3215 })
3216 {
3217 continue;
3218 }
3219 let Some(base_fact) = recovered_base_specifier(base_node, source, false) else {
3220 continue;
3221 };
3222 recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
3223 name,
3224 range,
3225 raw_supertypes: vec![base],
3226 base_facts: vec![base_fact],
3227 fragmented_body: match recovered_fragmented_export_body(body, range) {
3228 Some(fragmented) => fragmented,
3229 None => continue,
3230 },
3231 });
3232 }
3233 recovered
3234}
3235
3236fn lifted_function_like_export_class_namespace<'tree>(
3237 node: Node<'tree>,
3238 source: &str,
3239 ancestry: &ParentIndex<'tree>,
3240) -> Option<String> {
3241 let mut anchor = node;
3247 let parent = loop {
3248 let parent = ancestry.parent(anchor)?;
3249 if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
3250 break parent;
3251 }
3252 anchor = parent;
3253 };
3254 let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
3255 sibling.start_byte() > anchor.end_byte()
3256 && sibling.kind() == "ERROR"
3257 && sibling.named_child_count() == 0
3258 && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
3259 });
3260 if !has_later_close {
3261 return None;
3262 }
3263 let candidates = parent
3264 .named_children(&mut parent.walk())
3265 .filter(|sibling| {
3266 sibling.kind() == "namespace_definition"
3267 && sibling.has_error()
3268 && sibling.end_byte() < anchor.start_byte()
3269 })
3270 .filter_map(|namespace| {
3271 namespace
3272 .child_by_field_name("name")
3273 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
3274 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
3275 })
3276 .collect::<Vec<_>>();
3277 let [namespace] = candidates.as_slice() else {
3278 return None;
3279 };
3280 Some(namespace.clone())
3281}
3282
3283pub(crate) fn recovered_function_like_export_class_pair_has_body(
3284 node: Node<'_>,
3285 source: &str,
3286 identifier: &str,
3287 range: &Range,
3288) -> bool {
3289 recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
3290 recovered.name == identifier
3291 && recovered.range.start_byte == range.start_byte
3292 && recovered.range.end_byte == range.end_byte
3293 })
3294}
3295
3296fn recovered_exported_class_has_body(
3297 node: Node<'_>,
3298 source: &str,
3299 expected_name: &str,
3300) -> Option<bool> {
3301 match node.kind() {
3302 "function_definition" => {
3303 let (class_node, name, _, _) =
3304 recover_exported_class_function_definition(node, source)?;
3305 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3306 }
3307 "declaration" | "field_declaration" => {
3308 let recovered = recover_exported_class_declaration(node, source)?;
3309 (recovered.name == expected_name).then(|| recovered.body.is_some())
3310 }
3311 _ => None,
3312 }
3313}
3314
3315#[derive(Default)]
3331pub struct CppRecoveredExportClassIndex {
3332 by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3333}
3334
3335impl CppRecoveredExportClassIndex {
3336 pub fn build(root: Node<'_>, source: &str) -> Self {
3337 let mut by_error_node: HashMap<
3338 (usize, usize),
3339 Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3340 > = HashMap::default();
3341 let mut cursor = root.walk();
3344 let mut stack = vec![root];
3345 while let Some(node) = stack.pop() {
3346 if node.is_error() {
3347 let recovered = recover_embedded_function_like_export_classes(node, source);
3348 if !recovered.is_empty() {
3349 by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3350 }
3351 }
3352 stack.extend(node.named_children(&mut cursor));
3353 }
3354 Self { by_error_node }
3355 }
3356
3357 pub fn approximate_size(&self) -> usize {
3359 self.by_error_node
3360 .values()
3361 .fold(0usize, |total, recovered| {
3362 recovered.iter().fold(
3363 total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3364 |acc, class| {
3365 acc.saturating_add(std::mem::size_of::<
3366 RecoveredEmbeddedFunctionLikeExportClass,
3367 >())
3368 .saturating_add(class.name.len())
3369 .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3370 },
3371 )
3372 })
3373 }
3374
3375 fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3376 self.by_error_node
3377 .get(&(node.start_byte(), node.end_byte()))
3378 .is_some_and(|recovered| {
3379 recovered.iter().any(|class| {
3380 class.name == identifier
3381 && class.range.start_byte == range.start_byte
3382 && class.range.end_byte == range.end_byte
3383 })
3384 })
3385 }
3386}
3387
3388#[cfg(any(test, feature = "test-support"))]
3395thread_local! {
3396 static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3397 const { std::cell::Cell::new(0) };
3398}
3399
3400#[cfg(any(test, feature = "test-support"))]
3404#[doc(hidden)]
3405pub fn recovered_class_body_node_visits_for_test() -> usize {
3406 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3407}
3408
3409#[cfg(any(test, feature = "test-support"))]
3411#[doc(hidden)]
3412pub fn reset_recovered_class_body_node_visits_for_test() {
3413 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3414}
3415
3416#[cfg(any(test, feature = "test-support"))]
3417fn record_recovered_class_body_visit() {
3418 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3419}
3420
3421#[cfg(not(any(test, feature = "test-support")))]
3422fn record_recovered_class_body_visit() {}
3423
3424pub(crate) fn recovered_class_body_at(
3447 recovered_export_classes: &CppRecoveredExportClassIndex,
3448 root: Node<'_>,
3449 source: &str,
3450 identifier: &str,
3451 range: &Range,
3452) -> Option<bool> {
3453 let covers_range_start = |node: &Node<'_>| {
3454 node.start_byte() <= range.start_byte
3455 && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3456 };
3457 let mut stack = vec![root];
3458 let mut saw_forward = false;
3459 while let Some(node) = stack.pop() {
3460 record_recovered_class_body_visit();
3461 if (node.start_byte() == range.start_byte
3465 && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3466 || recovered_export_classes.claims(node, identifier, range)
3467 || (node.start_byte() == range.start_byte
3468 && recovered_fragmented_class_has_body(node, source, identifier, range))
3469 {
3470 return Some(true);
3471 }
3472 if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3473 return Some(true);
3474 }
3475 if node.start_byte() <= range.start_byte
3482 && range.start_byte < node.end_byte()
3483 && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3484 {
3485 if has_body {
3486 return Some(true);
3487 }
3488 saw_forward = true;
3489 continue;
3490 }
3491 let mut cursor = node.walk();
3492 stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3493 }
3494 saw_forward.then_some(false)
3495}
3496
3497fn recovered_collapsed_aggregate_has_body(
3510 node: Node<'_>,
3511 source: &str,
3512 identifier: &str,
3513 range: &Range,
3514) -> bool {
3515 let claims = |head: &CppCollapsedAggregateHead<'_>| {
3516 head.key.start_byte() == range.start_byte
3517 && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3518 };
3519 if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3520 return true;
3521 }
3522 if !node.is_error() {
3523 return false;
3524 }
3525 let mut cursor = node.walk();
3526 let children = node.children(&mut cursor).collect::<Vec<_>>();
3527 children
3528 .iter()
3529 .enumerate()
3530 .filter(|(_, child)| {
3531 child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3532 })
3533 .any(|(index, _)| {
3534 cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3535 })
3536}
3537
3538pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3546 if !matches!(
3547 node.kind(),
3548 "qualified_identifier" | "scoped_type_identifier" | "template_type"
3549 ) {
3550 return false;
3551 }
3552 if let Some(function) = node.parent().filter(|parent| {
3553 parent.kind() == "function_definition"
3554 && parent
3555 .child_by_field_name("declarator")
3556 .is_some_and(|declarator| same_node(declarator, node))
3557 }) {
3558 return recover_exported_class_function_definition(function, source)
3559 .is_some_and(|(_, _, raw_supertypes, _)| raw_supertypes.is_some());
3560 }
3561 let Some(initializer) = node.parent().filter(|parent| {
3562 parent.kind() == "init_declarator"
3563 && parent
3564 .child_by_field_name("declarator")
3565 .is_some_and(|declarator| same_node(declarator, node))
3566 }) else {
3567 return false;
3568 };
3569 initializer
3570 .parent()
3571 .filter(|parent| parent.kind() == "declaration")
3572 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3573 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3574}
3575
3576struct CppSentinelReparsedClass<'tree> {
3582 declaration_node: Node<'tree>,
3583 name: String,
3584 body: Node<'tree>,
3585 raw_supertypes: Option<Vec<String>>,
3586 base_facts: Option<Vec<CppBaseSpecifierFact>>,
3587}
3588
3589fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3590 let mut cursor = root.walk();
3591 root.named_children(&mut cursor)
3592 .find(|child| child.kind() != "comment")
3593 .filter(|child| child.kind() == "template_declaration")
3594}
3595
3596fn cpp_sentinel_reparsed_class<'tree>(
3597 root: Node<'tree>,
3598 template_node: Option<Node<'tree>>,
3599 source: &str,
3600 ancestry: &ParentIndex<'tree>,
3601) -> Option<CppSentinelReparsedClass<'tree>> {
3602 let container = template_node.unwrap_or(root);
3603 let mut cursor = container.walk();
3604 for child in container.named_children(&mut cursor) {
3605 if matches!(
3606 child.kind(),
3607 "class_specifier" | "struct_specifier" | "union_specifier"
3608 ) {
3609 let name = class_like_name(child, source, ancestry)?;
3610 let body = cpp_body_node(child)?;
3611 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
3612 .then(|| extract_cpp_supertypes(child, source));
3613 let base_facts = matches!(child.kind(), "class_specifier" | "struct_specifier")
3614 .then(|| extract_cpp_base_facts(child, source));
3615 return Some(CppSentinelReparsedClass {
3616 declaration_node: child,
3617 name,
3618 body,
3619 raw_supertypes,
3620 base_facts,
3621 });
3622 }
3623 if child.kind() == "declaration"
3624 && let Some(class_node) = first_class_like_child(child)
3625 {
3626 let name = class_like_name(class_node, source, ancestry)?;
3627 let body = cpp_body_node(class_node)?;
3628 let raw_supertypes =
3629 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3630 .then(|| extract_cpp_supertypes(class_node, source));
3631 let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3632 .then(|| extract_cpp_base_facts(class_node, source));
3633 return Some(CppSentinelReparsedClass {
3634 declaration_node: class_node,
3635 name,
3636 body,
3637 raw_supertypes,
3638 base_facts,
3639 });
3640 }
3641 if child.kind() == "function_definition"
3646 && let Some(class_node) = first_class_like_child(child)
3647 && let Some(body) = cpp_body_node(class_node)
3648 && let Some(name) = class_like_name(class_node, source, ancestry)
3649 {
3650 let raw_supertypes =
3651 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3652 .then(|| extract_cpp_supertypes(class_node, source));
3653 let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3654 .then(|| extract_cpp_base_facts(class_node, source));
3655 return Some(CppSentinelReparsedClass {
3656 declaration_node: class_node,
3657 name,
3658 body,
3659 raw_supertypes,
3660 base_facts,
3661 });
3662 }
3663 if child.kind() == "function_definition"
3664 && let Some((_, name, raw_supertypes, base_facts)) =
3665 recover_exported_class_function_definition(child, source)
3666 {
3667 let body = cpp_body_node(child)?;
3668 return Some(CppSentinelReparsedClass {
3669 declaration_node: child,
3670 name,
3671 body,
3672 raw_supertypes,
3673 base_facts,
3674 });
3675 }
3676 }
3677 None
3678}
3679
3680fn recovered_postfix_export_macro_base(
3681 node: Node<'_>,
3682 type_node: Node<'_>,
3683 declarator: Node<'_>,
3684 source: &str,
3685) -> Option<String> {
3686 recovered_postfix_export_macro_base_node(node, type_node, declarator, source)
3687 .and_then(|base| recovered_malformed_base_name(base, source))
3688}
3689
3690fn recovered_postfix_export_macro_base_node<'tree>(
3691 node: Node<'tree>,
3692 type_node: Node<'tree>,
3693 declarator: Node<'tree>,
3694 source: &str,
3695) -> Option<Node<'tree>> {
3696 let mut cursor = node.walk();
3697 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
3698 child.kind() == "ERROR"
3699 && child.start_byte() >= type_node.end_byte()
3700 && child.end_byte() <= declarator.start_byte()
3701 && postfix_export_macro_inheritance(*child, source)
3702 });
3703 malformed_clauses.next()?;
3704 if malformed_clauses.next().is_some() {
3705 return None;
3706 }
3707 Some(declarator)
3708}
3709
3710fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
3711 let mut macro_count = 0;
3712 let mut colon_count = 0;
3713 let mut access_count = 0;
3714 for index in 0..node.child_count() {
3715 let Some(child) = node.child(index) else {
3716 return false;
3717 };
3718 match child.kind() {
3719 "identifier" | "type_identifier" if child.is_named() => {
3720 let candidate = normalize_cpp_whitespace(node_text(child, source));
3721 if !cpp_export_macro_token(&candidate) {
3722 return false;
3723 }
3724 macro_count += 1;
3725 }
3726 ":" if !child.is_named() => colon_count += 1,
3727 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
3728 _ => return false,
3729 }
3730 }
3731 macro_count == 1 && colon_count == 1 && access_count == 1
3732}
3733
3734fn recovered_single_base_after_declarator_node<'tree>(
3735 node: Node<'tree>,
3736 declarator: Node<'tree>,
3737 source: &str,
3738) -> Option<Node<'tree>> {
3739 let body_start = node
3740 .child_by_field_name("body")
3741 .map(|body| body.start_byte())
3742 .unwrap_or(node.end_byte());
3743 let mut cursor = node.walk();
3744 let mut bases = node
3745 .named_children(&mut cursor)
3746 .filter(|child| {
3747 child.kind() == "ERROR"
3748 && child.start_byte() >= declarator.end_byte()
3749 && child.end_byte() <= body_start
3750 })
3751 .filter_map(|error| {
3752 displaced_exported_class_name(error, source)?;
3753 let mut error_cursor = error.walk();
3754 error
3755 .named_children(&mut error_cursor)
3756 .find(|child| recovered_malformed_base_name(*child, source).is_some())
3757 });
3758 let base = bases.next()?;
3759 bases.next().is_none().then_some(base)
3760}
3761
3762fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
3763 (0..node.child_count()).any(|index| {
3764 node.child(index)
3765 .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
3766 })
3767}
3768
3769pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3770 recover_exported_class_function_definition(node, source).is_some()
3771}
3772
3773fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3774 matches!(
3775 kind,
3776 "ERROR"
3777 | "preproc_if"
3778 | "preproc_ifdef"
3779 | "preproc_ifndef"
3780 | "preproc_else"
3781 | "preproc_elif"
3782 ) || (kind == "labeled_statement" && in_class_scope)
3783}
3784
3785pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3786 if node.kind() != "declaration" {
3787 return false;
3788 }
3789 let mut ancestor = node.parent();
3790 while let Some(container) = ancestor {
3791 match container.kind() {
3792 "compound_statement" => {
3793 return container.parent().is_some_and(|class_container| {
3794 is_recovered_exported_class_container(class_container, source)
3795 });
3796 }
3797 "template_declaration" | "linkage_specification" | "declaration_list" => {}
3800 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3801 _ => return false,
3802 }
3803 ancestor = container.parent();
3804 }
3805 false
3806}
3807
3808fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3809 let body_start = node
3810 .child_by_field_name("body")
3811 .map(|body| body.start_byte())
3812 .unwrap_or(node.end_byte());
3813 let mut stack = Vec::new();
3814 for index in (0..node.named_child_count()).rev() {
3815 let Some(child) = node.named_child(index) else {
3816 continue;
3817 };
3818 if child.start_byte() >= body_start {
3819 continue;
3820 }
3821 stack.push(child);
3822 }
3823
3824 let mut best = None;
3825 while let Some(current) = stack.pop() {
3826 if matches!(current.kind(), "identifier" | "type_identifier") {
3827 let name = normalize_cpp_whitespace(node_text(current, source));
3828 if !name.is_empty()
3829 && !cpp_export_macro_token(&name)
3830 && !matches!(name.as_str(), "class" | "struct" | "union")
3831 {
3832 best = Some(name);
3833 }
3834 continue;
3835 }
3836
3837 for index in (0..current.named_child_count()).rev() {
3838 if let Some(child) = current.named_child(index)
3839 && child.start_byte() < body_start
3840 {
3841 stack.push(child);
3842 }
3843 }
3844 }
3845 best
3846}
3847
3848fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3849 if node.kind() == "declaration"
3850 && node
3851 .child_by_field_name("type")
3852 .or_else(|| first_class_like_child(node))
3853 .is_some_and(|type_node| {
3854 matches!(
3855 type_node.kind(),
3856 "class_specifier" | "struct_specifier" | "union_specifier"
3857 )
3858 })
3859 && let Some(name) = node
3860 .child_by_field_name("declarator")
3861 .and_then(|declarator| declarator_name_from_node(declarator, source))
3862 && !cpp_export_macro_token(&name)
3863 {
3864 return Some(name);
3865 }
3866
3867 if node.kind() == "function_definition"
3868 && node.child_by_field_name("type").is_some_and(|type_node| {
3869 matches!(
3870 type_node.kind(),
3871 "class_specifier" | "struct_specifier" | "union_specifier"
3872 )
3873 })
3874 && let Some(name) = node
3875 .child_by_field_name("declarator")
3876 .and_then(|declarator| direct_identifier_name(declarator, source))
3877 && !cpp_export_macro_token(&name)
3878 {
3879 return Some(name);
3880 }
3881
3882 let class_node = if matches!(
3883 node.kind(),
3884 "class_specifier" | "struct_specifier" | "union_specifier"
3885 ) {
3886 node
3887 } else {
3888 first_class_like_child(node)?
3889 };
3890 class_like_name_from_children(class_node, source)
3891}
3892
3893fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3894 if !matches!(
3895 node.kind(),
3896 "identifier" | "field_identifier" | "type_identifier"
3897 ) {
3898 return None;
3899 }
3900 let name = normalize_cpp_whitespace(node_text(node, source));
3901 (!name.is_empty()).then_some(name)
3902}
3903
3904fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3905 match node.kind() {
3906 "identifier" | "field_identifier" | "type_identifier" => {
3907 let name = normalize_cpp_whitespace(node_text(node, source));
3908 (!name.is_empty()).then_some(name)
3909 }
3910 _ => {
3911 let mut cursor = node.walk();
3912 node.named_children(&mut cursor)
3913 .find_map(|child| declarator_name_from_node(child, source))
3914 }
3915 }
3916}
3917
3918fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3919 let mut cursor = node.walk();
3920 node.named_children(&mut cursor).find(|child| {
3921 matches!(
3922 child.kind(),
3923 "class_specifier" | "struct_specifier" | "union_specifier"
3924 )
3925 })
3926}
3927
3928fn push_cpp_container_work<'tree>(
3933 node: Node<'tree>,
3934 scope: ScopeInfo,
3935 stack: &mut Vec<CppWork<'tree>>,
3936) {
3937 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3938}
3939
3940fn push_cpp_sibling_range<'tree>(
3944 parent: Node<'tree>,
3945 start_index: usize,
3946 end_index: usize,
3947 scope: ScopeInfo,
3948 stack: &mut Vec<CppWork<'tree>>,
3949) {
3950 let mut cursor = parent.walk();
3951 let children = parent
3952 .named_children(&mut cursor)
3953 .skip(start_index)
3954 .take(end_index.saturating_sub(start_index))
3955 .collect::<Vec<_>>()
3956 .into_iter();
3957 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3958}
3959
3960fn advance_cpp_siblings<'tree>(
3968 mut siblings: CppSiblingsWork<'tree>,
3969 source: &str,
3970 stack: &mut Vec<CppWork<'tree>>,
3971) {
3972 let Some(child) = siblings.children.next() else {
3973 return;
3974 };
3975 let current_scope = siblings.scope.clone();
3976 if let Some(namespace) = cpp_using_namespace_target(child, source) {
3977 siblings.scope.visible_using_namespaces.push(namespace);
3978 }
3979 if !siblings.children.as_slice().is_empty() {
3980 stack.push(CppWork::Siblings(siblings));
3981 }
3982 stack.push(CppWork::Node(CppNodeWork {
3983 node: child,
3984 scope: current_scope,
3985 }));
3986}
3987
3988fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3995 if node.kind() != "using_declaration" {
3996 return None;
3997 }
3998 let mut cursor = node.walk();
3999 let is_namespace_directive = node
4000 .children(&mut cursor)
4001 .any(|child| child.kind() == "namespace");
4002 if !is_namespace_directive {
4003 return None;
4004 }
4005 let target = node.named_child(0)?;
4006 let start = target
4014 .child(0)
4015 .filter(|child| !child.is_named() && child.kind() == "::")
4016 .map_or(target.start_byte(), |marker| marker.end_byte());
4017 let text = normalize_cpp_whitespace(
4018 source
4019 .get(start..target.end_byte())
4020 .expect("using-directive target covers one source range"),
4021 );
4022 (!text.is_empty()).then_some(text)
4023}
4024
4025pub(crate) fn parse_cpp_readings<'tree>(
4029 file: &ProjectFile,
4030 source: &str,
4031 root: Node<'tree>,
4032 ancestry: &ParentIndex<'tree>,
4033 dialects: &[LanguageDialect],
4034) -> Vec<ParsedFile> {
4035 parse_cpp_readings_with_object_macro_fields(
4036 file,
4037 source,
4038 root,
4039 ancestry,
4040 dialects,
4041 HashMap::default(),
4042 )
4043}
4044
4045pub(crate) fn parse_cpp_readings_with_object_macro_fields<'tree>(
4046 file: &ProjectFile,
4047 source: &str,
4048 root: Node<'tree>,
4049 ancestry: &ParentIndex<'tree>,
4050 dialects: &[LanguageDialect],
4051 object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4052) -> Vec<ParsedFile> {
4053 assert!(
4054 dialects.len() == 1
4055 || (dialects.len() == 2
4056 && dialects[0] != LanguageDialect::CppC
4057 && dialects[1] == LanguageDialect::CppC),
4058 "one primary dialect, optionally followed by its C alternative"
4059 );
4060 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
4061 let properties = RefCell::new(HashMap::default());
4062 let dependencies = RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
4063 let orphaned_namespaces = OrphanedNamespaceScopeIndex::build(root, source);
4064 let mut using_namespaces = Vec::new();
4065 let mut readings: Vec<_> = dialects
4066 .iter()
4067 .map(|_| ParsedFile::new(String::new()))
4068 .collect();
4069 let mut visitors: Vec<_> = readings
4070 .iter_mut()
4071 .zip(dialects)
4072 .map(|(parsed, dialect)| CppVisitor {
4073 file,
4074 source,
4075 parsed,
4076 source_collector: &collector,
4077 source_properties: &properties,
4078 source_dependencies: &dependencies,
4079 primary_ancestry: ancestry,
4080 primary_root_id: root.id(),
4081 source_links: HashMap::default(),
4082 source_declarations: HashSet::default(),
4083 c_tag_semantics: *dialect == LanguageDialect::CppC,
4084 c_tag_scope_witness: false,
4085 recovered_class_sibling_scopes: HashMap::default(),
4086 consumed_fragment_regions: Vec::new(),
4087 orphaned_namespaces: &orphaned_namespaces,
4088 partitioned_regions: Vec::new(),
4089 namespace_forward_scans: HashMap::default(),
4090 field_owners: None,
4091 recovery_captures: Vec::new(),
4092 active_source_context: None,
4093 object_macro_fields: object_macro_fields.clone(),
4094 ambiguous_object_macro_fields: HashSet::default(),
4095 })
4096 .collect();
4097 let mut declaration_stacks: Vec<_> = dialects
4098 .iter()
4099 .map(|_| {
4100 vec![CppWork::Container(CppContainer {
4101 node: root,
4102 scope: ScopeInfo {
4103 package_name: String::new(),
4104 module: None,
4105 class_unit: None,
4106 template_signature: None,
4107 template_metadata: None,
4108 declarations_are_fields: false,
4109 recovered_specialization_member_scope: false,
4110 visible_using_namespaces: Vec::new(),
4111 },
4112 })]
4113 })
4114 .collect();
4115 let kinds = CompiledKinds::compile(
4116 &tree_sitter_cpp::LANGUAGE.into(),
4117 CPP_STRUCTURAL_SPEC.kind_table(),
4118 );
4119 let context = CPP_STRUCTURAL_SPEC.call_site_context(root, source);
4122 let mut structural = StructuralFactCollector::new(
4123 &CPP_STRUCTURAL_SPEC,
4124 source,
4125 &context,
4126 brokk_bifrost_core::analyzer::tree_walk::ParentIndex::new(root),
4127 usize::MAX,
4128 None,
4129 );
4130 let mut imports = Vec::new();
4131 let mut includes = Vec::new();
4132 let mut identifiers = HashSet::default();
4133 let mut entered = HashSet::default();
4134 let mut primary_ordinals = HashMap::default();
4135 let mut stack = vec![(root, None)];
4136 while let Some((node, parent)) = stack.pop() {
4137 assert!(entered.insert(node.id()), "one primary event per AST node");
4138 primary_ordinals.insert(node.id(), primary_ordinals.len());
4139 dependencies.borrow_mut().primary.enter(node, source);
4140 let mut child_parent = parent;
4141 if node.is_named() {
4142 if let Some(namespace) = cpp_using_namespace_target(node, source) {
4143 using_namespaces.push((collector.borrow_mut().intern_node(node), namespace));
4144 }
4145 if let Some(raw_kind) = kinds.kind_of(&node)
4146 && CPP_STRUCTURAL_SPEC.should_extract(node, raw_kind)
4147 {
4148 let kind = CPP_STRUCTURAL_SPEC.refine_kind(
4149 node,
4150 raw_kind,
4151 parent.map(|id| structural.normalized_kind(id)),
4152 source,
4153 &context,
4154 );
4155 let mut source_facts = collector.borrow_mut();
4156 let id = structural
4157 .enter(node, kind, parent, &mut source_facts)
4158 .expect("unbounded C++ structural collector");
4159 let mut sink = structural.role_sink(&mut source_facts);
4160 CPP_STRUCTURAL_SPEC.extract(node, kind, &mut sink);
4161 structural
4162 .accept_roles(id, sink.into_parts())
4163 .expect("unbounded C++ structural roles");
4164 child_parent = Some(id);
4165 }
4166 if node.kind() == "preproc_include" {
4167 let raw = normalize_cpp_whitespace(node_text(node, source));
4168 if !raw.is_empty() {
4169 let occurrence = collector.borrow_mut().intern_node(node);
4170 let target = node.child_by_field_name("path").and_then(|path| {
4171 let (value, quoted) = crate::imports::include_path_from_node(node, source)?;
4172 let target = collector.borrow_mut().intern_node(path);
4173 includes.push(brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
4174 declaration: occurrence,
4175 target,
4176 path: value.to_owned(),
4177 quoted,
4178 });
4179 Some(target)
4180 });
4181 imports.push(SourceImportFact::from_import(
4182 cpp_include_info(raw),
4183 occurrence,
4184 target,
4185 None,
4186 Vec::new(),
4187 ));
4188 }
4189 }
4190 if matches!(
4191 node.kind(),
4192 "type_identifier" | "identifier" | "qualified_identifier"
4193 ) {
4194 let text = node_text(node, source).trim();
4195 if !text.is_empty() {
4196 identifiers.insert(text.to_owned());
4197 }
4198 }
4199 }
4200 for index in 0..visitors.len() {
4201 if index != 0 && !visitors[0].c_tag_scope_witness {
4206 continue;
4207 }
4208 let visitor = &mut visitors[index];
4209 advance_primary_declarations(
4210 visitor,
4211 &mut declaration_stacks[index],
4212 &entered,
4213 ancestry,
4214 );
4215 visitor.drain_partitioned_regions();
4216 }
4217 let mut cursor = node.walk();
4218 let children: Vec<_> = node
4219 .children(&mut cursor)
4220 .map(|child| (child, child_parent))
4221 .collect();
4222 stack.extend(children.into_iter().rev());
4223 }
4224 let reading_count = if visitors[0].c_tag_scope_witness {
4225 visitors.len()
4226 } else {
4227 1
4228 };
4229 visitors.truncate(reading_count);
4230 declaration_stacks.truncate(reading_count);
4231 assert!(
4232 declaration_stacks.iter().all(Vec::is_empty),
4233 "all primary declaration events were consumed"
4234 );
4235 for visitor in &mut visitors {
4236 visitor.parsed.source_declaration_units = std::mem::take(&mut visitor.source_links)
4237 .into_iter()
4238 .flat_map(|(unit, declarations)| {
4239 declarations
4240 .into_iter()
4241 .map(move |declaration| (declaration, unit.clone()))
4242 })
4243 .collect();
4244 }
4245 let dialect_declarations: Vec<_> = visitors
4246 .iter()
4247 .map(|visitor| visitor.source_declarations.clone())
4248 .collect();
4249 drop(visitors);
4250 readings.truncate(reading_count);
4251 dependencies
4252 .borrow_mut()
4253 .finish_primary(&mut properties.borrow_mut());
4254 recover_quoted_source_imports(source, &mut imports, &mut includes, &collector);
4257 let primary_occurrence_ordinals: HashMap<_, _> = collector
4258 .borrow()
4259 .primary_node_occurrences()
4260 .map(|(node, occurrence)| (occurrence, primary_ordinals[&node]))
4261 .collect();
4262 let occurrences = collector.into_inner().finish();
4263 let generic_imports: Vec<_> = (0..imports.len())
4264 .map(|i| SourceImportId::try_from_index(i).expect("C++ import id"))
4265 .collect();
4266 let source_facts = ParsedSourceFacts {
4267 cpp: Some(brokk_bifrost_core::analyzer::cpp_facts::CppSourceFacts {
4268 includes,
4269 declarations: {
4270 let mut facts: Vec<_> = properties.into_inner().into_values().collect();
4271 facts.sort_by_key(|fact| fact.declaration.get());
4272 facts
4273 },
4274 using_namespaces,
4275 }),
4276 go: None,
4277 java: None,
4278 js_ts: None,
4279 php: None,
4280 scala: None,
4281 ruby: None,
4282 python: None,
4283 source_bytes: source.len(),
4284 occurrences,
4285 structural: structural
4286 .finish()
4287 .expect("C++ structural collection finishes"),
4288 native_site_occurrences: Vec::new(),
4289 native_declaration_sources: Vec::new(),
4290 declaration_visibilities: None,
4291 rust_declaration_properties: Vec::new(),
4292 rust_modules: None,
4293 rust_types: Vec::new(),
4294 rust_items: Default::default(),
4295 imports,
4296 generic_imports,
4297 rust_import_contexts: Vec::new(),
4298 };
4299 for (parsed, declarations) in readings.iter_mut().zip(dialect_declarations) {
4300 parsed.finalize_deferred_replacements();
4301 let links = std::mem::take(&mut parsed.source_declaration_units);
4302 parsed.source_declaration_units = links
4303 .into_iter()
4304 .filter(|(_, unit)| parsed.contains_declaration(unit))
4305 .collect();
4306 parsed.imports = source_facts
4307 .imports
4308 .iter()
4309 .map(|import| import.import_info(&source_facts.occurrences))
4310 .collect();
4311 parsed.type_identifiers = identifiers.clone();
4312 parsed.source_facts = Some(crate::source_projection::finalize_dialect(
4313 &source_facts,
4314 &declarations,
4315 &primary_occurrence_ordinals,
4316 &mut parsed.source_declaration_units,
4317 &mut parsed.source_declaration_metadata,
4318 ));
4319 }
4320 readings
4321}
4322
4323fn advance_primary_declarations<'tree>(
4324 visitor: &mut CppVisitor<'_, '_>,
4325 stack: &mut Vec<CppWork<'tree>>,
4326 entered: &HashSet<usize>,
4327 ancestry: &ParentIndex<'tree>,
4328) {
4329 while let Some(work) = stack.pop() {
4330 match work {
4331 CppWork::Container(container) => {
4332 push_cpp_container_work(container.node, container.scope, stack)
4333 }
4334 CppWork::Siblings(siblings) => advance_cpp_siblings(siblings, visitor.source, stack),
4335 CppWork::Node(work) => {
4336 if !entered.contains(&work.node.id()) {
4337 stack.push(CppWork::Node(work));
4338 return;
4339 }
4340 if !visitor.node_is_inside_consumed_fragment(work.node) {
4341 visitor.visit_node(work.node, &work.scope, stack, ancestry);
4342 }
4343 }
4344 }
4345 }
4346}
4347
4348#[derive(Clone)]
4349struct CppActiveSourceContext {
4350 guards: Option<HashSet<crate::graph::resolver::PreprocessorGuard>>,
4351 namespace: Option<String>,
4352 child_namespace: Option<String>,
4353 outer_lexical_scope: Vec<String>,
4354 child_lexical_scope: Vec<String>,
4355 family: Option<SourceOccurrenceId>,
4356 exhaustive_family: Option<SourceOccurrenceId>,
4357 callable_guard_completion_byte: Option<usize>,
4358}
4359
4360pub struct CppPartitionedRegion {
4364 tree: Tree,
4365 range: std::ops::Range<usize>,
4366 scope: ScopeInfo,
4367 recovery: Range,
4368 source_context: Option<CppActiveSourceContext>,
4369}
4370
4371pub struct CppVisitor<'a, 'source> {
4372 pub file: &'a ProjectFile,
4373 pub source: &'source str,
4374 pub parsed: &'a mut ParsedFile,
4375 pub source_collector: &'a RefCell<PrimarySourceFactCollector<'source>>,
4376 source_properties: &'a RefCell<
4377 HashMap<
4378 SourceDeclarationId,
4379 brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4380 >,
4381 >,
4382 source_dependencies: &'a RefCell<crate::source_dependencies::DeclarationDependencies>,
4383 pub primary_ancestry: &'a ParentIndex<'a>,
4384 pub primary_root_id: usize,
4385 source_links: HashMap<CodeUnit, Vec<SourceDeclarationId>>,
4386 source_declarations: HashSet<SourceDeclarationId>,
4387 pub c_tag_semantics: bool,
4398 pub(crate) c_tag_scope_witness: bool,
4401 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
4402 pub consumed_fragment_regions: Vec<(usize, usize)>,
4411 pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
4416 pub partitioned_regions: Vec<CppPartitionedRegion>,
4419 pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
4424 pub field_owners: Option<CppFieldOwnerIndex>,
4428 pub recovery_captures: Vec<CppRecoveryCapture>,
4432 active_source_context: Option<CppActiveSourceContext>,
4433 pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4437 pub ambiguous_object_macro_fields: HashSet<String>,
4440}
4441
4442#[derive(Clone, Debug, PartialEq, Eq)]
4449pub enum ObjectMacroFieldEvent {
4450 Define {
4451 name: String,
4452 replacement: ObjectMacroReplacement,
4453 conditional: bool,
4454 },
4455 Undef {
4456 name: String,
4457 conditional: bool,
4458 },
4459}
4460
4461pub fn collect_cpp_object_macro_fields<'tree>(
4465 root: Node<'tree>,
4466 source: &str,
4467) -> HashMap<String, ObjectMacroReplacement> {
4468 let mut fields = HashMap::default();
4469 let mut ambiguous = HashSet::default();
4470 for event in collect_cpp_object_macro_field_events(root, source) {
4471 match event {
4472 ObjectMacroFieldEvent::Define {
4473 name,
4474 replacement: value,
4475 conditional,
4476 } => {
4477 if value.is_empty() {
4478 fields.remove(&name);
4479 if conditional {
4480 ambiguous.insert(name);
4481 } else {
4482 ambiguous.remove(&name);
4483 }
4484 } else if ambiguous.contains(&name) {
4485 } else if let Some(previous) = fields.get(&name) {
4487 if previous != &value {
4488 fields.remove(&name);
4489 ambiguous.insert(name);
4490 }
4491 } else {
4492 fields.insert(name, value);
4493 }
4494 }
4495 ObjectMacroFieldEvent::Undef { name, conditional } => {
4496 fields.remove(&name);
4497 if conditional {
4498 ambiguous.insert(name);
4499 } else {
4500 ambiguous.remove(&name);
4501 }
4502 }
4503 }
4504 }
4505 fields
4506}
4507
4508pub fn collect_cpp_object_macro_field_events<'tree>(
4513 root: Node<'tree>,
4514 source: &str,
4515) -> Vec<ObjectMacroFieldEvent> {
4516 let mut events = Vec::new();
4517 let mut stack = vec![root];
4518 while let Some(node) = stack.pop() {
4519 if node.kind() == "preproc_def"
4520 && let Some(name) = extract_macro_name(node, source)
4521 {
4522 let replacement = object_macro_replacement_of(node, source);
4523 events.push(ObjectMacroFieldEvent::Define {
4524 name,
4525 replacement,
4526 conditional: inside_preprocessor_conditional(node),
4527 });
4528 } else if is_cpp_undef_directive(node, source)
4529 && let Some(argument) = node.child_by_field_name("argument")
4530 {
4531 events.push(ObjectMacroFieldEvent::Undef {
4532 name: node_text(argument, source).trim().to_string(),
4533 conditional: inside_preprocessor_conditional(node),
4534 });
4535 }
4536 let mut cursor = node.walk();
4537 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
4538 stack.extend(children.into_iter().rev());
4539 }
4540 events
4541}
4542
4543fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
4544 let mut current = node.parent();
4545 while let Some(parent) = current {
4546 if matches!(
4547 parent.kind(),
4548 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
4549 ) {
4550 return true;
4551 }
4552 current = parent.parent();
4553 }
4554 false
4555}
4556
4557fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
4558 node.kind() == "preproc_call"
4559 && node
4560 .child_by_field_name("directive")
4561 .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
4562}
4563
4564impl<'a, 'source> CppVisitor<'a, 'source> {
4565 fn apply_active_source_context(
4566 &self,
4567 fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4568 ) {
4569 let Some(context) = self.active_source_context.as_ref() else {
4570 return;
4571 };
4572 fact.guard_requirements = fact
4573 .guard_requirements
4574 .as_ref()
4575 .and_then(crate::source_context::cpp_guard_set_to_runtime)
4576 .zip(context.guards.as_ref())
4577 .and_then(|(local, outer)| {
4578 crate::graph::resolver::merge_preprocessor_guards(&local, outer)
4579 })
4580 .as_ref()
4581 .map(crate::source_context::cpp_guard_set_from_runtime);
4582 if fact.callable_guards.is_some() {
4583 fact.callable_guards = fact
4584 .callable_guards
4585 .as_ref()
4586 .and_then(crate::source_context::cpp_guard_set_to_runtime)
4587 .zip(context.guards.as_ref())
4588 .and_then(|(local, outer)| {
4589 crate::graph::resolver::merge_preprocessor_guards(&local, outer)
4590 })
4591 .as_ref()
4592 .map(crate::source_context::cpp_guard_set_from_runtime);
4593 }
4594 fact.callable_guard_completion_byte = fact
4595 .callable_guard_completion_byte
4596 .zip(context.callable_guard_completion_byte)
4597 .map(|(local, outer)| local.max(outer));
4598 fact.conditional_family = fact.conditional_family.or(context.family);
4599 fact.exhaustive_conditional_family = fact
4600 .exhaustive_conditional_family
4601 .or(context.exhaustive_family);
4602 if let Some(alias) = fact.file_scope_alias.as_mut() {
4603 alias.namespace = match (context.namespace.as_ref(), alias.namespace.take()) {
4604 (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
4605 (Some(outer), None) => Some(outer.clone()),
4606 (None, local) => local,
4607 };
4608 }
4609 if !context.outer_lexical_scope.is_empty() {
4610 let mut lexical = context.outer_lexical_scope.clone();
4611 lexical.append(&mut fact.lexical_path);
4612 fact.lexical_path = lexical;
4613 }
4614 }
4615
4616 fn recovery_ancestry<'tree>(
4617 &self,
4618 root: Node<'tree>,
4619 ) -> crate::source_dependencies::RecoveryAncestry<'a, 'tree> {
4620 crate::source_dependencies::RecoveryAncestry::new(
4621 root,
4622 self.source,
4623 self.source_dependencies,
4624 )
4625 }
4626
4627 fn capture_source_dependencies(
4628 &self,
4629 fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4630 node: Node<'_>,
4631 unit: &CodeUnit,
4632 ) {
4633 if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
4634 self.source_dependencies.borrow_mut().register_primary(
4635 fact.declaration,
4636 node,
4637 unit.is_class(),
4638 );
4639 } else {
4640 self.source_dependencies
4641 .borrow()
4642 .apply_recovered(node, unit.is_class(), fact);
4643 }
4644 }
4645
4646 fn link_source_declaration(
4647 &mut self,
4648 ancestry: &ParentIndex<'_>,
4649 unit: &CodeUnit,
4650 node: Node<'_>,
4651 ) {
4652 let (declaration, family) = {
4653 let mut collector = self.source_collector.borrow_mut();
4654 let occurrence = if node.id() == self.primary_root_id
4655 || self.primary_ancestry.contains(node)
4656 {
4657 collector.intern_node(node)
4658 } else {
4659 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
4662 };
4663 let name = crate::source_facts::declaration_name(node, unit, self.source).map(|name| {
4664 if self.primary_ancestry.contains(name) {
4665 collector.intern_node(name)
4666 } else {
4667 collector
4668 .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(name))
4669 }
4670 });
4671 let declaration = collector.declare(occurrence, name);
4672 let family =
4673 crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
4674 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
4675 {
4676 collector.intern_node(family)
4677 } else {
4678 collector.intern_embedded(
4679 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
4680 )
4681 }
4682 });
4683 (declaration, family)
4684 };
4685 self.source_declarations.insert(declaration);
4686 if !self.source_properties.borrow().contains_key(&declaration) {
4687 let context =
4688 crate::source_context::capture_declaration_context(node, unit, self.source);
4689 let (exhaustive_family, displaced_namespace_closing_brace) = {
4690 let mut collector = self.source_collector.borrow_mut();
4691 let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
4692 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
4693 {
4694 collector.intern_node(family)
4695 } else {
4696 collector.intern_embedded(
4697 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
4698 )
4699 }
4700 });
4701 let displaced_namespace_closing_brace =
4702 context.displaced_namespace_closing_brace.map(|brace| {
4703 if brace.id() == self.primary_root_id
4704 || self.primary_ancestry.contains(brace)
4705 {
4706 collector.intern_node(brace)
4707 } else {
4708 collector.intern_embedded(
4709 brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
4710 )
4711 }
4712 });
4713 (exhaustive_family, displaced_namespace_closing_brace)
4714 };
4715 let guard_requirements = context.guard_requirements;
4716 let callable_guards = context.callable_guards;
4717 let callable_activation = context.callable_activation;
4718 let callable_guard_completion_byte = context.callable_guard_completion_byte;
4719 let flattened_macro_namespace = context.flattened_macro_namespace;
4720 self.source_properties
4721 .borrow_mut()
4722 .entry(declaration)
4723 .or_insert_with(|| {
4724 let mut fact = crate::source_facts::capture_declaration(
4725 declaration,
4726 node,
4727 unit,
4728 self.source,
4729 ancestry,
4730 );
4731 self.capture_source_dependencies(&mut fact, node, unit);
4732 fact.occurrence_role =
4733 crate::identity::cpp_occurrence_role_for_node(node, unit);
4734 fact.conditional_family = family;
4735 fact.guard_requirements = guard_requirements;
4736 fact.callable_guards = callable_guards;
4737 fact.callable_activation = callable_activation;
4738 fact.callable_guard_completion_byte = callable_guard_completion_byte;
4739 fact.exhaustive_conditional_family = exhaustive_family;
4740 fact.flattened_macro_namespace = flattened_macro_namespace;
4741 fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
4742 self.apply_active_source_context(&mut fact);
4743 fact
4744 });
4745 }
4746 let links = self.source_links.entry(unit.clone()).or_default();
4747 if !links.contains(&declaration) {
4748 links.push(declaration);
4749 }
4750 }
4751
4752 fn link_source_range(
4753 &mut self,
4754 ancestry: &ParentIndex<'_>,
4755 unit: &CodeUnit,
4756 range: Range,
4757 evidence: Option<Node<'_>>,
4758 ) {
4759 let (declaration, family) = {
4760 let mut collector = self.source_collector.borrow_mut();
4761 let occurrence =
4762 collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
4763 let name = evidence
4764 .and_then(|node| crate::source_facts::declaration_name(node, unit, self.source))
4765 .map(|name| {
4766 if self.primary_ancestry.contains(name) {
4767 collector.intern_node(name)
4768 } else {
4769 collector.intern_embedded(
4770 brokk_bifrost_core::analyzer::tree_walk::node_range(name),
4771 )
4772 }
4773 });
4774 let declaration = collector.declare(occurrence, name);
4775 let family = evidence.and_then(|node| {
4776 crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
4777 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
4778 {
4779 collector.intern_node(family)
4780 } else {
4781 collector.intern_embedded(
4782 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
4783 )
4784 }
4785 })
4786 });
4787 (declaration, family)
4788 };
4789 self.source_declarations.insert(declaration);
4790 if let Some(node) = evidence
4791 && !self.source_properties.borrow().contains_key(&declaration)
4792 {
4793 let context =
4794 crate::source_context::capture_declaration_context(node, unit, self.source);
4795 let (exhaustive_family, displaced_namespace_closing_brace) = {
4796 let mut collector = self.source_collector.borrow_mut();
4797 let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
4798 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
4799 {
4800 collector.intern_node(family)
4801 } else {
4802 collector.intern_embedded(
4803 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
4804 )
4805 }
4806 });
4807 let displaced_namespace_closing_brace =
4808 context.displaced_namespace_closing_brace.map(|brace| {
4809 if brace.id() == self.primary_root_id
4810 || self.primary_ancestry.contains(brace)
4811 {
4812 collector.intern_node(brace)
4813 } else {
4814 collector.intern_embedded(
4815 brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
4816 )
4817 }
4818 });
4819 (exhaustive_family, displaced_namespace_closing_brace)
4820 };
4821 let guard_requirements = context.guard_requirements;
4822 let callable_guards = context.callable_guards;
4823 let callable_activation = context.callable_activation;
4824 let callable_guard_completion_byte = context.callable_guard_completion_byte;
4825 let flattened_macro_namespace = context.flattened_macro_namespace;
4826 let mut fact = crate::source_facts::capture_declaration(
4827 declaration,
4828 node,
4829 unit,
4830 self.source,
4831 ancestry,
4832 );
4833 self.capture_source_dependencies(&mut fact, node, unit);
4834 fact.occurrence_role = crate::identity::cpp_occurrence_role_for_node(node, unit);
4835 fact.conditional_family = family;
4836 fact.guard_requirements = guard_requirements;
4837 fact.callable_guards = callable_guards;
4838 fact.callable_activation = callable_activation;
4839 fact.callable_guard_completion_byte = callable_guard_completion_byte;
4840 fact.exhaustive_conditional_family = exhaustive_family;
4841 fact.flattened_macro_namespace = flattened_macro_namespace;
4842 fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
4843 self.apply_active_source_context(&mut fact);
4844 self.source_properties
4845 .borrow_mut()
4846 .insert(declaration, fact);
4847 }
4848 let links = self.source_links.entry(unit.clone()).or_default();
4849 if !links.contains(&declaration) {
4850 links.push(declaration);
4851 }
4852 }
4853
4854 fn add_signature_with_metadata(&mut self, unit: CodeUnit, metadata: SignatureMetadata) {
4855 let declaration = *self
4856 .source_links
4857 .get(&unit)
4858 .and_then(|links| links.last())
4859 .expect("signature metadata follows its source declaration");
4860 let metadata_ordinal = self
4861 .parsed
4862 .add_signature_with_metadata(unit.clone(), metadata);
4863 let link = brokk_bifrost_core::analyzer::parsed_file::SourceDeclarationMetadataLink {
4864 declaration,
4865 unit,
4866 metadata_ordinal,
4867 };
4868 if !self.parsed.source_declaration_metadata.contains(&link) {
4869 self.parsed.source_declaration_metadata.push(link);
4870 }
4871 }
4872
4873 fn add_declaration(
4881 &mut self,
4882 ancestry: &ParentIndex<'_>,
4883 code_unit: CodeUnit,
4884 node: Node<'_>,
4885 parent: Option<CodeUnit>,
4886 top_level: Option<CodeUnit>,
4887 ) {
4888 self.note_declaration(&code_unit);
4889 self.link_source_declaration(ancestry, &code_unit, node);
4890 let source = self.source;
4891 self.parsed
4892 .add_code_unit(code_unit, node, source, parent, top_level);
4893 }
4894
4895 fn add_declaration_with_range(
4897 &mut self,
4898 ancestry: &ParentIndex<'_>,
4899 code_unit: CodeUnit,
4900 range: Range,
4901 evidence: Option<Node<'_>>,
4902 parent: Option<CodeUnit>,
4903 top_level: Option<CodeUnit>,
4904 ) {
4905 self.note_declaration(&code_unit);
4906 self.link_source_range(ancestry, &code_unit, range, evidence);
4907 self.parsed
4908 .add_code_unit_with_range(code_unit, range, parent, top_level);
4909 }
4910
4911 fn replace_declaration_deferred(
4913 &mut self,
4914 ancestry: &ParentIndex<'_>,
4915 code_unit: CodeUnit,
4916 node: Node<'_>,
4917 parent: Option<CodeUnit>,
4918 top_level: Option<CodeUnit>,
4919 ) {
4920 self.note_replaced_declaration(&code_unit);
4921 self.link_source_declaration(ancestry, &code_unit, node);
4922 let source = self.source;
4923 self.parsed
4924 .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
4925 }
4926
4927 fn replace_declaration_with_range_deferred(
4929 &mut self,
4930 ancestry: &ParentIndex<'_>,
4931 code_unit: CodeUnit,
4932 range: Range,
4933 evidence: Option<Node<'_>>,
4934 parent: Option<CodeUnit>,
4935 top_level: Option<CodeUnit>,
4936 ) {
4937 self.note_replaced_declaration(&code_unit);
4938 self.link_source_range(ancestry, &code_unit, range, evidence);
4939 self.parsed
4940 .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
4941 }
4942
4943 fn note_declaration(&mut self, code_unit: &CodeUnit) {
4950 if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
4951 for capture in &mut self.recovery_captures {
4952 if capture.removed_pre_existing.contains(code_unit) {
4953 continue;
4954 }
4955 if capture.created_units.insert(code_unit.clone()) {
4956 capture.created.push(code_unit.clone());
4957 }
4958 }
4959 }
4960 if let Some(field_owners) = self.field_owners.as_mut() {
4961 field_owners.record(code_unit, self.file);
4962 }
4963 }
4964
4965 fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
4974 for removed in self.declarations_a_replacement_removes(code_unit) {
4977 self.source_links.remove(&removed);
4978 }
4979 let removes_children = self.parsed.contains_declaration(code_unit)
4980 && self
4981 .parsed
4982 .children
4983 .get(code_unit)
4984 .is_some_and(|children| !children.is_empty());
4985 if removes_children {
4986 if !self.recovery_captures.is_empty() {
4987 let removed = self.declarations_a_replacement_removes(code_unit);
4988 for capture in &mut self.recovery_captures {
4989 for unit in &removed {
4990 if !capture.created_units.contains(unit) {
4995 capture.removed_pre_existing.insert(unit.clone());
4996 }
4997 }
4998 }
4999 }
5000 self.field_owners = None;
5001 }
5002 self.note_declaration(code_unit);
5003 }
5004
5005 fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
5008 let mut removed = Vec::new();
5009 let mut seen = HashSet::default();
5010 let mut pending: Vec<CodeUnit> = self
5011 .parsed
5012 .children
5013 .get(code_unit)
5014 .cloned()
5015 .unwrap_or_default();
5016 while let Some(unit) = pending.pop() {
5017 if !seen.insert(unit.clone()) {
5018 continue;
5019 }
5020 if let Some(children) = self.parsed.children.get(&unit) {
5021 pending.extend(children.iter().cloned());
5022 }
5023 removed.push(unit);
5024 }
5025 removed
5026 }
5027
5028 fn visit_function_like_export_class_pair<'tree>(
5029 &mut self,
5030 node: Node<'tree>,
5031 scope: &ScopeInfo,
5032 stack: &mut Vec<CppWork<'tree>>,
5033 ancestry: &ParentIndex<'tree>,
5034 ) -> bool {
5035 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
5036 return false;
5037 };
5038 let member_outcome = self
5039 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
5040 let mut displaced = node.next_named_sibling();
5046 while let Some(candidate) = displaced {
5047 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
5048 break;
5049 }
5050 displaced = candidate.next_named_sibling();
5051 }
5052 let class_unit = self.visit_named_class_like_shape(
5053 node,
5054 recovered.name,
5055 None,
5060 true,
5061 Some(recovered.range),
5062 recovered.raw_supertypes,
5063 recovered.base_facts,
5064 scope,
5065 stack,
5066 ancestry,
5067 );
5068 self.parsed
5069 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5070 recovery: recovered.range,
5071 unit: class_unit.clone(),
5072 });
5073 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5074 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
5075 tree.root_node(),
5076 class_unit.identifier(),
5077 self.source,
5078 )
5079 {
5080 self.visit_recovered_fragment_constructor(
5081 range,
5082 body,
5083 node,
5084 &class_unit,
5085 scope,
5086 ancestry,
5087 );
5088 }
5089 if let Some(outcome) = member_outcome {
5090 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5091 }
5092 self.consumed_fragment_regions
5093 .push((node.start_byte(), recovered.range.end_byte));
5094 true
5095 }
5096
5097 fn visit_embedded_function_like_export_classes<'tree>(
5098 &mut self,
5099 node: Node<'tree>,
5100 scope: &ScopeInfo,
5101 stack: &mut Vec<CppWork<'tree>>,
5102 ancestry: &ParentIndex<'tree>,
5103 ) -> bool {
5104 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
5105 let found = !recovered_classes.is_empty();
5106 for recovered in recovered_classes {
5107 let member_outcome = self.reparse_fragmented_export_class_members(
5108 &recovered.fragmented_body,
5109 &recovered.name,
5110 );
5111 let class_unit = self.visit_named_class_like_shape(
5112 node,
5113 recovered.name,
5114 None,
5115 true,
5116 Some(recovered.range),
5117 Some(recovered.raw_supertypes),
5118 Some(recovered.base_facts),
5119 scope,
5120 stack,
5121 ancestry,
5122 );
5123 self.parsed
5124 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5125 recovery: recovered.range,
5126 unit: class_unit.clone(),
5127 });
5128 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5129 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
5130 tree.root_node(),
5131 class_unit.identifier(),
5132 self.source,
5133 )
5134 {
5135 self.visit_recovered_fragment_constructor(
5136 range,
5137 body,
5138 node,
5139 &class_unit,
5140 scope,
5141 ancestry,
5142 );
5143 }
5144 if let Some(outcome) = member_outcome {
5145 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5146 }
5147 }
5148 found
5149 }
5150
5151 #[allow(clippy::too_many_arguments)]
5159 pub fn visit_container<'tree>(
5160 &mut self,
5161 node: Node<'tree>,
5162 ancestry: &ParentIndex<'tree>,
5163 package_name: &str,
5164 module: Option<CodeUnit>,
5165 class_unit: Option<CodeUnit>,
5166 template_signature: Option<String>,
5167 visible_using_namespaces: Vec<String>,
5168 ) {
5169 let scope = ScopeInfo {
5170 package_name: package_name.to_string(),
5171 module,
5172 class_unit,
5173 template_signature,
5174 template_metadata: None,
5175 declarations_are_fields: false,
5176 recovered_specialization_member_scope: false,
5177 visible_using_namespaces,
5178 };
5179 if node.is_error() {
5186 self.visit_object_macro_error_classes(node, &scope);
5187 }
5188 self.run_container_work(node, scope, ancestry);
5189 self.drain_partitioned_regions();
5190 }
5191
5192 fn drain_partitioned_regions(&mut self) {
5197 let mut consumed = Vec::new();
5198 while let Some(CppPartitionedRegion {
5199 tree,
5200 range,
5201 scope,
5202 recovery,
5203 source_context,
5204 }) = self.partitioned_regions.pop()
5205 {
5206 let root = tree.root_node();
5207 let container = root
5208 .descendant_for_byte_range(range.start, range.end)
5209 .expect("the queued container belongs to this tree");
5210 assert_eq!(container.byte_range(), range);
5211 let ancestry = self.recovery_ancestry(root);
5212 self.record_recovered_declarations_with_context(recovery, source_context, |visitor| {
5213 visitor.run_container_work(container, scope, &ancestry)
5214 });
5215 consumed.push((range.start, range.end));
5219 }
5220 self.consumed_fragment_regions.extend(consumed);
5221 }
5222
5223 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
5227 self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
5228 }
5229
5230 fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
5235 self.consumed_fragment_regions
5236 .iter()
5237 .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
5238 }
5239
5240 fn run_container_work<'tree>(
5252 &mut self,
5253 node: Node<'tree>,
5254 scope: ScopeInfo,
5255 ancestry: &ParentIndex<'tree>,
5256 ) {
5257 self.drain_cpp_work(
5258 vec![CppWork::Container(CppContainer { node, scope })],
5259 ancestry,
5260 );
5261 }
5262
5263 fn drain_cpp_work<'tree>(
5270 &mut self,
5271 mut stack: Vec<CppWork<'tree>>,
5272 ancestry: &ParentIndex<'tree>,
5273 ) {
5274 while let Some(work) = stack.pop() {
5275 match work {
5276 CppWork::Container(container) => {
5277 push_cpp_container_work(container.node, container.scope, &mut stack);
5278 }
5279 CppWork::Siblings(siblings) => {
5280 advance_cpp_siblings(siblings, self.source, &mut stack);
5281 }
5282 CppWork::Node(work) => {
5283 if self.node_is_inside_consumed_fragment(work.node) {
5284 continue;
5285 }
5286 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5287 }
5288 }
5289 }
5290 }
5291
5292 fn reparse_fragmented_export_class_members(
5297 &self,
5298 fragmented: &FragmentedExportBody,
5299 class_name: &str,
5300 ) -> Option<FragmentedExportMembers> {
5301 if fragmented.reparse_start >= fragmented.reparse_end {
5302 return None;
5303 }
5304 let tree = cpp_reparse_fragmented_class_body(
5305 self.source,
5306 fragmented.reparse_start,
5307 fragmented.reparse_end,
5308 )?;
5309 if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
5310 return Some(FragmentedExportMembers::Complete(tree));
5311 }
5312 let has_conditional_constructor = {
5313 let root = tree.root_node();
5314 let mut cursor = root.walk();
5315 root.named_children(&mut cursor).any(|child| {
5316 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
5317 })
5318 };
5319 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
5320 }
5321
5322 fn visit_fragmented_export_class_members(
5325 &mut self,
5326 origin: Node<'_>,
5327 outcome: FragmentedExportMembers,
5328 class_unit: CodeUnit,
5329 scope: &ScopeInfo,
5330 ) -> bool {
5331 self.with_recovery_source_context(origin, |visitor| {
5332 visitor.visit_fragmented_export_class_members_in_context(outcome, class_unit, scope)
5333 })
5334 }
5335
5336 fn visit_fragmented_export_class_members_in_context(
5337 &mut self,
5338 outcome: FragmentedExportMembers,
5339 class_unit: CodeUnit,
5340 scope: &ScopeInfo,
5341 ) -> bool {
5342 let (tree, complete) = match outcome {
5343 FragmentedExportMembers::Complete(tree) => (tree, true),
5344 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
5345 };
5346 let root = tree.root_node();
5347 let class_name = class_unit.identifier().to_string();
5348 let member_scope = ScopeInfo {
5349 package_name: class_unit.package_name().to_string(),
5354 module: scope.module.clone(),
5355 class_unit: Some(class_unit),
5356 template_signature: scope.template_signature.clone(),
5357 template_metadata: None,
5358 declarations_are_fields: true,
5359 recovered_specialization_member_scope: false,
5360 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5361 };
5362 if !complete {
5363 let mut cursor = root.walk();
5369 let constructors = root
5370 .named_children(&mut cursor)
5371 .filter_map(|child| {
5372 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
5373 })
5374 .collect::<Vec<_>>();
5375 let reparsed_ancestry = self.recovery_ancestry(root);
5378 for constructor in constructors {
5379 let mut stack = Vec::new();
5380 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
5381 while let Some(work) = stack.pop() {
5382 match work {
5383 CppWork::Container(container) => {
5384 push_cpp_container_work(container.node, container.scope, &mut stack);
5385 }
5386 CppWork::Siblings(siblings) => {
5387 advance_cpp_siblings(siblings, self.source, &mut stack);
5388 }
5389 CppWork::Node(work) => {
5390 self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
5391 }
5392 }
5393 }
5394 }
5395 return false;
5396 }
5397 self.run_container_work(root, member_scope, &self.recovery_ancestry(root));
5399 true
5400 }
5401
5402 fn visit_recovered_fragment_constructor<'tree>(
5403 &mut self,
5404 range: std::ops::Range<usize>,
5405 constructor_body: Node<'tree>,
5406 class_declaration: Node<'tree>,
5407 class_unit: &CodeUnit,
5408 scope: &ScopeInfo,
5409 ancestry: &ParentIndex<'tree>,
5410 ) {
5411 self.with_recovery_source_context(class_declaration, |visitor| {
5412 visitor.visit_recovered_fragment_constructor_in_context(
5413 range,
5414 constructor_body,
5415 class_declaration,
5416 class_unit,
5417 scope,
5418 ancestry,
5419 );
5420 });
5421 }
5422
5423 fn visit_recovered_fragment_constructor_in_context<'tree>(
5424 &mut self,
5425 range: std::ops::Range<usize>,
5426 constructor_body: Node<'tree>,
5427 class_declaration: Node<'tree>,
5428 class_unit: &CodeUnit,
5429 scope: &ScopeInfo,
5430 ancestry: &ParentIndex<'tree>,
5431 ) {
5432 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
5433 return;
5434 };
5435 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
5436 tree.root_node(),
5437 range.start,
5438 class_unit.identifier(),
5439 self.source,
5440 ) else {
5441 return;
5442 };
5443 let member_scope = ScopeInfo {
5444 package_name: class_unit.package_name().to_string(),
5445 module: scope.module.clone(),
5446 class_unit: Some(class_unit.clone()),
5447 template_signature: scope.template_signature.clone(),
5448 template_metadata: None,
5449 declarations_are_fields: true,
5450 recovered_specialization_member_scope: false,
5451 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5452 };
5453 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
5454 else {
5455 return;
5456 };
5457 debug_assert_eq!(function.name, class_unit.identifier());
5458 let code_unit = function.code_unit(self.file.clone());
5459 let recovered_ancestry = self.recovery_ancestry(tree.root_node());
5460 self.add_declaration_with_range(
5461 &recovered_ancestry,
5462 code_unit.clone(),
5463 Range {
5464 start_byte: function_declarator.start_byte(),
5465 end_byte: constructor_body.end_byte(),
5466 start_line: function_declarator.start_position().row + 1,
5467 end_line: constructor_body.end_position().row + 1,
5468 },
5469 Some(function_declarator),
5470 None,
5471 None,
5472 );
5473 let declaration = *self.source_links[&code_unit]
5474 .last()
5475 .expect("recovered constructor source");
5476 {
5477 let mut properties = self.source_properties.borrow_mut();
5478 let fact = properties
5479 .get_mut(&declaration)
5480 .expect("recovered constructor source fact");
5481 fact.occurrence_role =
5482 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
5483 fact.callable_is_constructor = true;
5484 }
5485 self.add_signature_with_metadata(
5486 code_unit.clone(),
5487 cpp_signature_metadata(
5488 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
5489 function_declarator,
5490 cpp_callable_linkage(class_declaration, self.source, ancestry),
5491 self.source,
5492 &recovered_ancestry,
5493 )
5494 .with_declaration_only(false),
5495 );
5496 self.parsed.add_child(class_unit.clone(), code_unit);
5497 }
5498
5499 fn visit_recovered_fragment_prefix_members<'tree>(
5500 &mut self,
5501 origin: Node<'_>,
5502 root: Node<'tree>,
5503 constructor_start: usize,
5504 class_unit: &CodeUnit,
5505 scope: &ScopeInfo,
5506 ancestry: &ParentIndex<'tree>,
5507 ) {
5508 self.with_recovery_source_context(origin, |visitor| {
5509 visitor.visit_recovered_fragment_prefix_members_in_context(
5510 root,
5511 constructor_start,
5512 class_unit,
5513 scope,
5514 ancestry,
5515 );
5516 });
5517 }
5518
5519 fn visit_recovered_fragment_prefix_members_in_context<'tree>(
5520 &mut self,
5521 root: Node<'tree>,
5522 constructor_start: usize,
5523 class_unit: &CodeUnit,
5524 scope: &ScopeInfo,
5525 ancestry: &ParentIndex<'tree>,
5526 ) {
5527 let member_scope = ScopeInfo {
5528 package_name: class_unit.package_name().to_string(),
5529 module: scope.module.clone(),
5530 class_unit: Some(class_unit.clone()),
5531 template_signature: scope.template_signature.clone(),
5532 template_metadata: None,
5533 declarations_are_fields: true,
5534 recovered_specialization_member_scope: false,
5535 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5536 };
5537 let mut stack = vec![root];
5538 while let Some(current) = stack.pop() {
5539 if current.kind() == "comment" || current.start_byte() >= constructor_start {
5540 continue;
5541 }
5542 if current.end_byte() <= constructor_start
5543 && current.kind() != "translation_unit"
5544 && current.kind() != "labeled_statement"
5545 && current.kind() != "ERROR"
5546 {
5547 let mut work_stack = Vec::new();
5548 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
5549 while let Some(work) = work_stack.pop() {
5550 match work {
5551 CppWork::Container(container) => {
5552 push_cpp_container_work(
5553 container.node,
5554 container.scope,
5555 &mut work_stack,
5556 );
5557 }
5558 CppWork::Siblings(siblings) => {
5559 advance_cpp_siblings(siblings, self.source, &mut work_stack);
5560 }
5561 CppWork::Node(work) => {
5562 self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
5563 }
5564 }
5565 }
5566 continue;
5567 }
5568 if matches!(
5569 current.kind(),
5570 "translation_unit" | "labeled_statement" | "ERROR"
5571 ) {
5572 let mut cursor = current.walk();
5573 stack.extend(current.named_children(&mut cursor));
5574 }
5575 }
5576 }
5577
5578 fn with_recovery_source_context<T>(
5581 &mut self,
5582 node: Node<'_>,
5583 walk: impl FnOnce(&mut Self) -> T,
5584 ) -> T {
5585 use crate::graph::resolver::{
5586 enclosing_namespace_context, merge_preprocessor_guards,
5587 preprocessor_conditional_family_for_declaration, preprocessor_conditional_family_node,
5588 preprocessor_guard_environment,
5589 };
5590 let previous = self.active_source_context.take();
5591 let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
5592 let inherited = (!primary).then_some(previous.as_ref()).flatten();
5593 let local_guards = preprocessor_guard_environment(node, self.source);
5594 let guards = match inherited {
5595 Some(outer) => outer
5596 .guards
5597 .as_ref()
5598 .zip(local_guards.as_ref())
5599 .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
5600 None => local_guards,
5601 };
5602 let local_namespace = enclosing_namespace_context(node, self.source);
5603 let namespace = match (
5604 inherited.and_then(|outer| outer.child_namespace.as_ref()),
5605 local_namespace,
5606 ) {
5607 (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
5608 (Some(outer), None) => Some(outer.clone()),
5609 (None, local) => local,
5610 };
5611 let outer_lexical_scope =
5612 inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
5613 let mut local_owners = Vec::new();
5614 let mut parent = node.parent();
5615 while let Some(ancestor) = parent {
5616 if matches!(
5617 ancestor.kind(),
5618 "namespace_definition"
5619 | "class_specifier"
5620 | "struct_specifier"
5621 | "union_specifier"
5622 | "enum_specifier"
5623 ) && let Some(name) = ancestor.child_by_field_name("name")
5624 {
5625 local_owners.push(node_text(name, self.source).to_owned());
5626 }
5627 parent = ancestor.parent();
5628 }
5629 local_owners.reverse();
5630 let mut child_lexical_scope = outer_lexical_scope.clone();
5631 child_lexical_scope.extend(local_owners);
5632 if matches!(
5633 node.kind(),
5634 "namespace_definition"
5635 | "class_specifier"
5636 | "struct_specifier"
5637 | "union_specifier"
5638 | "enum_specifier"
5639 ) && let Some(name) = node.child_by_field_name("name")
5640 {
5641 child_lexical_scope.push(node_text(name, self.source).to_owned());
5642 }
5643 let child_namespace = if node.kind() == "namespace_definition" {
5644 node.child_by_field_name("name")
5645 .map(|name| {
5646 let local = node_text(name, self.source);
5647 namespace
5648 .as_ref()
5649 .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
5650 })
5651 .or_else(|| namespace.clone())
5652 } else {
5653 namespace.clone()
5654 };
5655 let mut intern = |family: Node<'_>| {
5656 let mut collector = self.source_collector.borrow_mut();
5657 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
5658 collector.intern_node(family)
5659 } else {
5660 collector
5661 .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
5662 }
5663 };
5664 let family = preprocessor_conditional_family_node(node)
5665 .map(&mut intern)
5666 .or_else(|| inherited.and_then(|outer| outer.family));
5667 let exhaustive_family = preprocessor_conditional_family_for_declaration(node)
5668 .map(&mut intern)
5669 .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
5670 let local_completion =
5671 crate::graph::resolver::callable_guard_completion_byte(node, self.source);
5672 let callable_guard_completion_byte = match inherited {
5673 Some(outer) => local_completion
5674 .zip(outer.callable_guard_completion_byte)
5675 .map(|(local, outer)| local.max(outer)),
5676 None => local_completion,
5677 };
5678 self.active_source_context = Some(CppActiveSourceContext {
5679 guards,
5680 namespace: child_namespace.clone(),
5681 child_namespace,
5682 outer_lexical_scope: child_lexical_scope.clone(),
5683 child_lexical_scope,
5684 family,
5685 exhaustive_family,
5686 callable_guard_completion_byte,
5687 });
5688 let result = walk(self);
5689 self.active_source_context = previous;
5690 result
5691 }
5692
5693 fn visit_node<'tree>(
5694 &mut self,
5695 node: Node<'tree>,
5696 scope: &ScopeInfo,
5697 stack: &mut Vec<CppWork<'tree>>,
5698 ancestry: &ParentIndex<'tree>,
5699 ) {
5700 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
5701 self.visit_node(node, &recovered_scope, stack, ancestry);
5702 return;
5703 }
5704 if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
5705 self.visit_node(node, &recovered_scope, stack, ancestry);
5706 return;
5707 }
5708 if node.kind() == "function_definition" && node.has_error() {
5714 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
5715 }
5716 if let Some(FragmentedClassRecovery {
5717 declaration_node: class_node,
5718 name,
5719 raw_supertypes,
5720 body: fragmented,
5721 }) = fragmented_class_body(node, self.source)
5722 {
5723 let displaced_namespace_items =
5724 displaced_fragment_namespace_geometry(node, self.source)
5725 .map(|boundary| boundary.namespace_items)
5726 .unwrap_or_default();
5727 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
5728 let mut class_stack = Vec::new();
5729 let parser_visible_body =
5732 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
5733 .then(|| cpp_body_node(class_node))
5734 .flatten();
5735 let class_unit = self.visit_named_class_like_shape(
5736 class_node,
5737 name,
5738 parser_visible_body,
5739 true,
5740 Some(fragmented.class_range),
5741 Some(raw_supertypes),
5742 Some(extract_cpp_base_facts(class_node, self.source)),
5743 scope,
5744 &mut class_stack,
5745 ancestry,
5746 );
5747 let member_scope = ScopeInfo {
5748 package_name: class_unit.package_name().to_string(),
5749 module: scope.module.clone(),
5750 class_unit: Some(class_unit.clone()),
5751 template_signature: scope.template_signature.clone(),
5752 template_metadata: None,
5753 declarations_are_fields: true,
5754 recovered_specialization_member_scope: false,
5755 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5756 };
5757 let complete = outcome.is_some_and(|outcome| {
5758 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
5759 });
5760 if complete {
5761 self.consumed_fragment_regions
5762 .push((node.start_byte(), fragmented.class_range.end_byte));
5763 } else {
5764 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
5774 if candidate.start_byte() >= fragmented.reparse_end {
5775 break;
5776 }
5777 if cpp_fragment_sibling_is_class_member(
5778 candidate,
5779 fragmented.reparse_end,
5780 self.source,
5781 ) {
5782 self.recovered_class_sibling_scopes
5783 .insert(candidate.id(), member_scope.clone());
5784 }
5785 }
5786 }
5787 for item in displaced_namespace_items {
5788 self.recovered_class_sibling_scopes
5789 .insert(item.id(), scope.clone());
5790 }
5791 stack.extend(class_stack);
5792 return;
5793 }
5794 if self.visit_folded_aggregate(node, scope) {
5795 return;
5796 }
5797 match node.kind() {
5798 "template_declaration" => {
5799 if let Some(recovered) =
5800 recover_fragmented_preprocessor_class(node, self.source, ancestry)
5801 {
5802 let mut template_scope = scope.clone();
5803 template_scope.template_signature =
5804 cpp_template_signature(node, recovered.declaration_node, self.source);
5805 template_scope.template_metadata =
5806 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
5807 let raw_supertypes =
5808 Some(extract_cpp_supertypes(recovered.class_node, self.source));
5809 let mut class_stack = Vec::new();
5810 let class_unit = self.visit_named_class_like_shape(
5811 recovered.class_node,
5812 recovered.name,
5813 Some(recovered.body),
5814 true,
5815 Some(recovered.range),
5816 raw_supertypes,
5817 Some(extract_cpp_base_facts(recovered.class_node, self.source)),
5818 &template_scope,
5819 &mut class_stack,
5820 ancestry,
5821 );
5822 self.parsed.record_materialization(
5823 MaterializationRecord::RecoveredDeclaration {
5824 recovery: recovered.range,
5825 unit: class_unit.clone(),
5826 },
5827 );
5828 let member_scope = ScopeInfo {
5829 package_name: template_scope.package_name.clone(),
5830 module: template_scope.module.clone(),
5831 class_unit: Some(class_unit.clone()),
5832 template_signature: template_scope.template_signature.clone(),
5833 template_metadata: None,
5834 declarations_are_fields: true,
5835 recovered_specialization_member_scope: recovered
5836 .class_node
5837 .child_by_field_name("name")
5838 .is_some_and(|name| name.kind() == "template_type"),
5839 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
5840 };
5841 for tail_member in recovered.tail_members.into_iter().rev() {
5842 stack.push(CppWork::Node(CppNodeWork {
5843 node: tail_member,
5844 scope: member_scope.clone(),
5845 }));
5846 }
5847 stack.extend(class_stack);
5848 for sibling in recovered.member_siblings {
5849 self.recovered_class_sibling_scopes
5850 .insert(sibling.id(), member_scope.clone());
5851 }
5852 return;
5853 }
5854 for index in (0..node.named_child_count()).rev() {
5855 let Some(child) = node.named_child(index) else {
5856 continue;
5857 };
5858 if matches!(
5859 child.kind(),
5860 "class_specifier"
5861 | "struct_specifier"
5862 | "union_specifier"
5863 | "enum_specifier"
5864 | "function_definition"
5865 | "declaration"
5866 | "field_declaration"
5867 | "alias_declaration"
5868 | "namespace_definition"
5869 ) {
5870 let mut template_scope = scope.clone();
5871 template_scope.template_signature =
5872 cpp_template_signature(node, child, self.source);
5873 template_scope.template_metadata =
5874 cpp_template_metadata(node, child, self.source, ancestry);
5875 if let Some(recovered) = recover_fragmented_partial_specialization(
5876 node,
5877 child,
5878 self.source,
5879 ancestry,
5880 ) {
5881 let code_unit = self.visit_named_class_like_shape(
5882 recovered.declaration_node,
5883 recovered.name,
5884 None,
5885 true,
5886 Some(recovered.range),
5887 None,
5888 None,
5889 &template_scope,
5890 stack,
5891 ancestry,
5892 );
5893 self.parsed.record_materialization(
5894 MaterializationRecord::RecoveredDeclaration {
5895 recovery: recovered.range,
5896 unit: code_unit.clone(),
5897 },
5898 );
5899 let mut member_scope = template_scope.clone();
5900 member_scope.class_unit = Some(code_unit);
5901 member_scope.declarations_are_fields = true;
5902 member_scope.recovered_specialization_member_scope = true;
5903 for prefix_member in recovered.prefix_members.into_iter().rev() {
5904 stack.push(CppWork::Node(CppNodeWork {
5905 node: prefix_member,
5906 scope: member_scope.clone(),
5907 }));
5908 }
5909 for sibling in recovered.member_siblings {
5910 self.recovered_class_sibling_scopes
5911 .insert(sibling.id(), member_scope.clone());
5912 }
5913 for following in recovered.following_declarations.into_iter().rev() {
5914 stack.push(CppWork::Node(CppNodeWork {
5915 node: following,
5916 scope: scope.clone(),
5917 }));
5918 }
5919 return;
5920 }
5921 stack.push(CppWork::Node(CppNodeWork {
5922 node: child,
5923 scope: template_scope,
5924 }));
5925 }
5926 }
5927 }
5928 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
5929 "linkage_specification" => {
5930 if let Some(body) = cpp_body_node(node) {
5931 stack.push(CppWork::Container(CppContainer {
5932 node: body,
5933 scope: scope.clone(),
5934 }));
5935 } else {
5936 stack.push(CppWork::Container(CppContainer {
5937 node,
5938 scope: scope.clone(),
5939 }));
5940 }
5941 }
5942 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
5943 self.visit_class_like(node, scope, stack, ancestry)
5944 }
5945 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
5946 "ERROR" => {
5953 self.visit_object_macro_error_classes(node, scope);
5954 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
5955 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
5956 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
5957 return;
5958 }
5959 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
5960 return;
5961 }
5962 self.visit_macro_swallowed_function_declarations(node, scope);
5963 self.visit_macro_wrapped_declarations(node, scope, ancestry);
5964 self.visit_stranded_class_members(node, scope, ancestry);
5965 stack.push(CppWork::Container(CppContainer {
5966 node,
5967 scope: scope.clone(),
5968 }));
5969 }
5970 }
5971 "declaration" => {
5972 if node.has_error() {
5973 if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
5980 return;
5981 }
5982 self.visit_prototype_macro_declarations(node, scope);
5983 if self.node_is_inside_consumed_fragment(node) {
5984 return;
5991 }
5992 }
5993 if scope.class_unit.is_some()
5994 && scope.declarations_are_fields
5995 && scope.recovered_specialization_member_scope
5996 && let Some(alias_name) =
5997 recovered_using_declaration_alias_name(node, self.source)
5998 {
5999 self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
6000 } else {
6001 self.visit_declaration(
6002 node,
6003 scope,
6004 scope.declarations_are_fields,
6005 stack,
6006 ancestry,
6007 )
6008 }
6009 }
6010 "expression_statement" => {
6024 if node.has_error() {
6025 self.visit_prototype_macro_declarations(node, scope);
6026 }
6027 }
6028 "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
6029 "preproc_call" => self.visit_preproc_call(node, scope),
6030 "type_definition" | "alias_declaration" => {
6031 self.visit_type_declaration(node, scope, stack, ancestry)
6032 }
6033 "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
6034 "preproc_include" => {}
6040 kind if preserves_declaration_scope_through_wrapper(
6041 kind,
6042 scope.class_unit.is_some(),
6043 ) =>
6044 {
6045 if kind == "labeled_statement" {
6051 self.visit_access_label_constructor(node, scope);
6052 }
6053 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
6054 let mut range = cpp_declaration_range(node);
6055 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
6056 range.end_byte = boundary.end_byte;
6057 range.end_line = boundary.end_line;
6058 }
6059 self.parsed.record_materialization(
6060 MaterializationRecord::ConfigurationConditional { range },
6061 );
6062 if node.has_error() {
6063 let mut candidates = vec![node];
6072 while let Some(candidate) = candidates.pop() {
6073 if candidate.kind() == "ERROR"
6084 && !cpp_is_inside_namespace_body(candidate, ancestry)
6085 && self.visit_function_like_export_class_pair(
6086 candidate, scope, stack, ancestry,
6087 )
6088 {
6089 continue;
6090 }
6091 for index in (0..candidate.named_child_count()).rev() {
6092 candidates.push(
6093 candidate
6094 .named_child(index)
6095 .expect("index below the node's own named child count"),
6096 );
6097 }
6098 }
6099 }
6100 }
6101 stack.push(CppWork::Container(CppContainer {
6102 node,
6103 scope: scope.clone(),
6104 }))
6105 }
6106 _ => {
6112 self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
6113 }
6114 }
6115 }
6116
6117 fn visit_macro_swallowed_function_declarations<'tree>(
6118 &mut self,
6119 envelope: Node<'tree>,
6120 scope: &ScopeInfo,
6121 ) {
6122 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
6123 || envelope.kind() == "ERROR"
6124 && envelope
6125 .parent()
6126 .is_some_and(|parent| parent.kind() == "ERROR")
6127 {
6128 return;
6129 }
6130 let mut stack = (0..envelope.named_child_count())
6131 .filter_map(|index| envelope.named_child(index))
6132 .collect::<Vec<_>>();
6133 while let Some(node) = stack.pop() {
6134 if node.kind() == "function_declarator" {
6135 self.visit_error_swallowed_function_declaration(node, scope);
6136 }
6137 for child in named_children_iter(node) {
6138 stack.push(child);
6139 }
6140 }
6141 }
6142
6143 fn visit_macro_wrapped_declarations<'tree>(
6147 &mut self,
6148 envelope: Node<'tree>,
6149 scope: &ScopeInfo,
6150 ancestry: &ParentIndex<'tree>,
6151 ) {
6152 let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
6153 if recovered.is_empty() {
6154 return;
6155 }
6156 let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
6157 self.record_recovered_declarations(recovery, envelope, |visitor| {
6158 for declaration in recovered {
6159 visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
6160 }
6161 });
6162 }
6163
6164 fn visit_collapsed_macro_declaration_run<'tree>(
6191 &mut self,
6192 envelope: Node<'tree>,
6193 scope: &ScopeInfo,
6194 ancestry: &ParentIndex<'tree>,
6195 ) -> bool {
6196 let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
6197 return false;
6198 };
6199 let start = envelope.start_byte();
6200 let end = run.region_end;
6201 let recovery = cpp_recovery_window(self.source, start, end);
6202 self.record_recovered_declarations(recovery, envelope, |visitor| {
6203 let mut position = start;
6204 while position < end {
6205 let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
6206 return;
6207 };
6208 let root = tree.root_node();
6209 let ancestry = visitor.recovery_ancestry(root);
6213 let mut cursor = root.walk();
6214 let collapsed =
6215 root.named_children(&mut cursor)
6216 .enumerate()
6217 .find_map(|(index, item)| {
6218 collapsed_macro_declaration_run(item, visitor.source, &ancestry)
6219 .map(|run| (index, item, run))
6220 });
6221 let mut stack = Vec::new();
6226 push_cpp_sibling_range(
6227 root,
6228 0,
6229 collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
6230 scope.clone(),
6231 &mut stack,
6232 );
6233 visitor.drain_cpp_work(stack, &ancestry);
6234 let Some((_, item, run)) = collapsed else {
6235 return;
6236 };
6237 if let Some(head) =
6241 cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
6242 {
6243 let head_root = head.root_node();
6244 let head_ancestry = visitor.recovery_ancestry(head_root);
6245 visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
6246 }
6247 assert!(
6248 run.invocation_end > position,
6249 "a collapsed run at {position} must end after the byte the scan resumed \
6250 from, but ended at {}",
6251 run.invocation_end
6252 );
6253 position = run.invocation_end;
6254 }
6255 });
6256 self.consumed_fragment_regions.push((start, end));
6257 true
6258 }
6259
6260 fn visit_stranded_class_members<'tree>(
6270 &mut self,
6271 node: Node<'tree>,
6272 scope: &ScopeInfo,
6273 ancestry: &ParentIndex<'tree>,
6274 ) {
6275 if scope.class_unit.is_none() || !scope.declarations_are_fields {
6276 return;
6277 }
6278 for member in stranded_declaration_run(node, self.source).declarations {
6279 self.add_macro_wrapped_declaration(member, scope, ancestry);
6280 }
6281 }
6282
6283 fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
6291 let Some(class_unit) = scope.class_unit.clone() else {
6292 return;
6293 };
6294 if !scope.declarations_are_fields {
6295 return;
6296 }
6297 let class_name = class_unit.identifier().to_string();
6298 let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
6299 else {
6300 return;
6301 };
6302 let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
6303 return;
6304 };
6305 let root = tree.root_node();
6306 let Some(declarator) =
6307 cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
6308 else {
6309 return;
6310 };
6311 let reparsed_ancestry = self.recovery_ancestry(root);
6312 let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
6313 let range = cpp_declaration_range(definition.unwrap_or(declarator));
6314 let recovery = cpp_recovery_window(self.source, start, node.end_byte());
6315 self.record_recovered_declarations(recovery, node, |visitor| {
6316 visitor.add_macro_wrapped_declaration(
6317 MacroWrappedDeclaration {
6318 declarator,
6319 range,
6320 is_static: false,
6321 },
6322 scope,
6323 &reparsed_ancestry,
6324 );
6325 });
6326 }
6327
6328 fn add_macro_wrapped_declaration<'tree>(
6329 &mut self,
6330 declaration: MacroWrappedDeclaration<'tree>,
6331 scope: &ScopeInfo,
6332 ancestry: &ParentIndex<'tree>,
6333 ) {
6334 let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
6335 else {
6336 return;
6337 };
6338 let code_unit =
6339 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
6340 let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
6341 let is_constructor = scope
6342 .class_unit
6343 .as_ref()
6344 .is_some_and(|class| function.name == class.identifier())
6345 || cpp_callable_is_structural_constructor(
6346 declaration.declarator,
6347 self.source,
6348 ancestry,
6349 );
6350 let already_declared = self.parsed.contains_declaration(&code_unit);
6351 if already_declared {
6352 self.parsed
6353 .record_navigation_range(code_unit.clone(), declaration.range);
6354 if definition.is_none() {
6355 return;
6356 }
6357 }
6360 self.add_declaration_with_range(
6361 ancestry,
6362 code_unit.clone(),
6363 declaration.range,
6364 Some(declaration.declarator),
6365 None,
6366 None,
6367 );
6368 if definition.is_some() {
6369 let recovered_declaration = *self
6370 .source_links
6371 .get(&code_unit)
6372 .and_then(|links| links.last())
6373 .expect("recovered definition source");
6374 let mut properties = self.source_properties.borrow_mut();
6375 let fact = properties
6376 .get_mut(&recovered_declaration)
6377 .expect("recovered definition source fact");
6378 fact.occurrence_role =
6379 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
6380 fact.callable_is_constructor |= is_constructor;
6381 }
6382 let signature = normalize_cpp_whitespace(
6383 self.source
6384 .get(declaration.range.start_byte..declaration.range.end_byte)
6385 .expect("a recovered declaration range covers one source range"),
6386 );
6387 let linkage = if declaration.is_static {
6388 CallableLinkage::Internal
6389 } else {
6390 cpp_callable_linkage(declaration.declarator, self.source, ancestry)
6391 };
6392 let declaration_only = definition.is_none();
6396 self.add_signature_with_metadata(
6397 code_unit.clone(),
6398 cpp_signature_metadata(
6399 signature,
6400 declaration.declarator,
6401 linkage,
6402 self.source,
6403 ancestry,
6404 )
6405 .with_declaration_only(declaration_only),
6406 );
6407 if already_declared {
6408 return;
6409 }
6410 if let Some(parent) = &scope.class_unit {
6411 self.parsed.add_child(parent.clone(), code_unit);
6412 } else if let Some(module) = &scope.module {
6413 self.parsed.add_child(module.clone(), code_unit);
6414 }
6415 }
6416
6417 fn visit_anonymous_local_aggregates_in_function<'tree>(
6422 &mut self,
6423 function: Node<'tree>,
6424 scope: &ScopeInfo,
6425 stack: &mut Vec<CppWork<'tree>>,
6426 ancestry: &ParentIndex<'tree>,
6427 ) {
6428 let Some(body) = cpp_body_node(function) else {
6429 return;
6430 };
6431 let block_scope;
6438 let scope = if scope.class_unit.is_some() {
6439 block_scope = ScopeInfo {
6440 class_unit: None,
6441 declarations_are_fields: false,
6442 ..scope.clone()
6443 };
6444 &block_scope
6445 } else {
6446 scope
6447 };
6448 let mut pending = vec![body];
6449 while let Some(node) = pending.pop() {
6450 if matches!(node.kind(), "function_definition" | "lambda_expression") {
6451 continue;
6452 }
6453 if node.kind() == "declaration"
6454 && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
6455 {
6456 continue;
6457 }
6458 if matches!(
6459 node.kind(),
6460 "class_specifier" | "struct_specifier" | "union_specifier"
6461 ) {
6462 continue;
6463 }
6464 let mut cursor = node.walk();
6465 let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
6466 children.reverse();
6467 pending.extend(children);
6468 }
6469 }
6470
6471 fn visit_error_swallowed_function_declaration<'tree>(
6472 &mut self,
6473 node: Node<'tree>,
6474 scope: &ScopeInfo,
6475 ) -> bool {
6476 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
6477 return false;
6478 };
6479 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
6480 return false;
6481 };
6482 let root = tree.root_node();
6483 let mut cursor = root.walk();
6484 let declarations = root
6485 .named_children(&mut cursor)
6486 .filter(|child| child.kind() != "comment")
6487 .collect::<Vec<_>>();
6488 let [declaration] = declarations.as_slice() else {
6489 return false;
6490 };
6491 if declaration.kind() != "declaration"
6492 || declaration.has_error()
6493 || declaration.start_byte() != start
6494 || declaration.end_byte() != end
6495 {
6496 return false;
6497 }
6498 let recovery = cpp_recovery_window(self.source, start, end);
6499 let reparsed_ancestry = self.recovery_ancestry(root);
6501 self.record_recovered_declarations(recovery, node, |visitor| {
6502 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
6503 });
6504 true
6505 }
6506
6507 fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
6530 for candidate in cpp_prototype_macro_candidates(node, self.source) {
6531 let start = candidate.run_start;
6532 let end = candidate.semicolon_end;
6533 if self.byte_range_is_inside_consumed_fragment(start, end) {
6534 continue;
6535 }
6536 let Some(tree) = parse_source_ranges_with_cancellation(
6537 &tree_sitter_cpp::LANGUAGE.into(),
6538 self.source,
6539 &candidate.ranges(),
6540 None,
6541 ) else {
6542 continue;
6543 };
6544 let root = tree.root_node();
6545 let mut cursor = root.walk();
6546 let declarations = root
6547 .named_children(&mut cursor)
6548 .filter(|child| child.kind() != "comment")
6549 .collect::<Vec<_>>();
6550 let [declaration] = declarations.as_slice() else {
6551 continue;
6552 };
6553 if declaration.kind() != "declaration"
6554 || declaration.has_error()
6555 || declaration.start_byte() != start
6556 || declaration.end_byte() != end
6557 || declaration
6558 .child_by_field_name("declarator")
6559 .and_then(extract_function_declarator)
6560 .is_none()
6561 {
6562 continue;
6563 }
6564 let recovery = cpp_recovery_window(self.source, start, end);
6565 let reparsed_ancestry = self.recovery_ancestry(root);
6568 self.record_recovered_declarations(recovery, node, |visitor| {
6569 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
6570 });
6571 self.consumed_fragment_regions.push((start, end));
6572 }
6573 }
6574
6575 fn declare_namespace_levels(
6581 &mut self,
6582 mut package_name: String,
6583 levels: Vec<(String, Range)>,
6584 node: Node<'_>,
6585 ancestry: &ParentIndex<'_>,
6586 ) -> (String, Option<CodeUnit>) {
6587 let mut module = None;
6588 for (component, range) in levels {
6589 let full_name = if package_name.is_empty() {
6590 component
6591 } else {
6592 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
6593 };
6594 let level = CodeUnit::new_fq(
6595 self.file.clone(),
6596 CodeUnitType::Module,
6597 "",
6598 full_name.clone(),
6599 cpp_namespace_fq(&full_name),
6600 );
6601 if !self.parsed.contains_declaration(&level) {
6602 self.add_declaration_with_range(
6603 ancestry,
6604 level.clone(),
6605 range,
6606 Some(node),
6607 None,
6608 None,
6609 );
6610 }
6611 package_name = full_name;
6612 module = Some(level);
6613 }
6614 (package_name, module)
6615 }
6616
6617 fn recovered_namespace_scope(
6622 &mut self,
6623 node: Node<'_>,
6624 scope: &ScopeInfo,
6625 ) -> Option<ScopeInfo> {
6626 self.orphaned_namespaces.region_at(node.start_byte())?;
6627 let levels = self
6633 .orphaned_namespaces
6634 .enclosing_namespace_levels(node, self.source);
6635 let package_name = levels
6636 .iter()
6637 .map(|(component, _)| component.as_str())
6638 .collect::<Vec<_>>()
6639 .join(CPP_PACKAGE_SEPARATOR);
6640 if package_name == scope.package_name {
6641 return None;
6642 }
6643 let (package_name, module) =
6644 self.declare_namespace_levels(String::new(), levels, node, self.primary_ancestry);
6645 Some(ScopeInfo {
6646 package_name,
6647 module,
6648 class_unit: None,
6653 template_signature: None,
6654 template_metadata: None,
6655 declarations_are_fields: false,
6656 recovered_specialization_member_scope: false,
6657 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6658 })
6659 }
6660
6661 fn visit_namespace<'tree>(
6662 &mut self,
6663 node: Node<'tree>,
6664 scope: &ScopeInfo,
6665 stack: &mut Vec<CppWork<'tree>>,
6666 ancestry: &ParentIndex<'tree>,
6667 ) {
6668 let name_node = node.child_by_field_name("name");
6669 let Some(name_node) = name_node else {
6670 if let Some(body) = cpp_body_node(node) {
6671 stack.push(CppWork::Container(CppContainer {
6672 node: body,
6673 scope: scope.clone(),
6674 }));
6675 }
6676 return;
6677 };
6678 let explicitly_global = name_node
6685 .child(0)
6686 .is_some_and(|child| !child.is_named() && child.kind() == "::");
6687 let components = cpp_namespace_name_components(name_node, self.source);
6688 if components.is_empty() {
6689 return;
6690 }
6691 let package_name = if explicitly_global {
6697 String::new()
6698 } else {
6699 scope.package_name.clone()
6700 };
6701 let definition = node_range(node);
6702 let (package_name, module) = self.declare_namespace_levels(
6703 package_name,
6704 components
6705 .into_iter()
6706 .map(|component| (component, definition))
6707 .collect(),
6708 node,
6709 ancestry,
6710 );
6711
6712 let namespace_scope = ScopeInfo {
6713 package_name,
6714 module,
6715 class_unit: None,
6723 template_signature: scope.template_signature.clone(),
6724 template_metadata: scope.template_metadata.clone(),
6725 declarations_are_fields: false,
6726 recovered_specialization_member_scope: false,
6727 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6728 };
6729 let container = cpp_body_node(node).unwrap_or(node);
6730 let mut candidates = vec![container];
6738 while let Some(candidate) = candidates.pop() {
6739 if matches!(
6740 candidate.kind(),
6741 "ERROR" | "function_definition" | "labeled_statement"
6742 ) && self.visit_embedded_function_like_export_classes(
6743 candidate,
6744 &namespace_scope,
6745 stack,
6746 ancestry,
6747 ) {
6748 continue;
6749 }
6750 for index in (0..candidate.named_child_count()).rev() {
6751 candidates.push(
6752 candidate
6753 .named_child(index)
6754 .expect("index below the node's own named child count"),
6755 );
6756 }
6757 }
6758 stack.push(CppWork::Container(CppContainer {
6759 node: container,
6760 scope: namespace_scope,
6761 }));
6762 }
6763
6764 fn visit_class_like<'tree>(
6765 &mut self,
6766 node: Node<'tree>,
6767 scope: &ScopeInfo,
6768 stack: &mut Vec<CppWork<'tree>>,
6769 ancestry: &ParentIndex<'tree>,
6770 ) {
6771 let Some(name) = class_like_name(node, self.source, ancestry) else {
6772 return;
6773 };
6774 let name = qualified_class_name_chain(node, self.source, scope)
6775 .map(|chain| chain.join("$"))
6776 .unwrap_or(name);
6777 self.visit_named_class_like(node, name, scope, stack, ancestry);
6778 }
6779
6780 fn visit_named_class_like<'tree>(
6781 &mut self,
6782 node: Node<'tree>,
6783 name: String,
6784 scope: &ScopeInfo,
6785 stack: &mut Vec<CppWork<'tree>>,
6786 ancestry: &ParentIndex<'tree>,
6787 ) {
6788 let body = cpp_body_node(node);
6789 let definition_body_present = body.is_some();
6790 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
6791 .then(|| extract_cpp_supertypes(node, self.source));
6792 self.visit_named_class_like_shape(
6793 node,
6794 name,
6795 body,
6796 definition_body_present,
6797 None,
6798 raw_supertypes,
6799 Some(extract_cpp_base_facts(node, self.source)),
6800 scope,
6801 stack,
6802 ancestry,
6803 );
6804 }
6805
6806 fn mints_tag_at_enclosing_c_scope(
6814 &self,
6815 declaration_node: Node<'_>,
6816 scope: &ScopeInfo,
6817 ancestry: &ParentIndex<'_>,
6818 ) -> bool {
6819 scope.class_unit.is_some()
6820 && matches!(
6821 declaration_node.kind(),
6822 "struct_specifier" | "union_specifier" | "enum_specifier"
6823 )
6824 && class_like_name(declaration_node, self.source, ancestry).is_some()
6825 }
6826
6827 #[allow(clippy::too_many_arguments)]
6828 fn visit_named_class_like_shape<'tree>(
6829 &mut self,
6830 declaration_node: Node<'tree>,
6831 name: String,
6832 body: Option<Node<'tree>>,
6833 definition_body_present: bool,
6834 explicit_range: Option<Range>,
6835 raw_supertypes: Option<Vec<String>>,
6836 base_facts: Option<Vec<CppBaseSpecifierFact>>,
6837 scope: &ScopeInfo,
6838 stack: &mut Vec<CppWork<'tree>>,
6839 ancestry: &ParentIndex<'tree>,
6840 ) -> CodeUnit {
6841 let displaced_macro_tail = if explicit_range.is_none() {
6842 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
6843 } else {
6844 None
6845 };
6846 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
6847 let recovered_scope = self.scope_for_recovered_exported_class(
6848 declaration_node,
6849 &name,
6850 definition_body_present,
6851 scope,
6852 ancestry,
6853 );
6854 let c_tag_scope;
6864 let c_tag_scope_applies =
6865 self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
6866 if !self.c_tag_semantics && c_tag_scope_applies {
6867 self.c_tag_scope_witness = true;
6868 }
6869 let scope = if self.c_tag_semantics && c_tag_scope_applies {
6870 c_tag_scope = ScopeInfo {
6871 class_unit: None,
6872 ..recovered_scope.clone()
6873 };
6874 &c_tag_scope
6875 } else {
6876 &recovered_scope
6877 };
6878 let short_name = if let Some(parent) = &scope.class_unit {
6879 cpp_join_nested_short(parent.short_name(), &name)
6880 } else {
6881 name.clone()
6882 };
6883 let qualified_chain = if scope.class_unit.is_none() {
6890 qualified_class_name_chain(declaration_node, self.source, scope)
6891 .filter(|chain| chain.join("$") == name)
6892 } else {
6893 None
6894 };
6895 let fq = if let Some(chain) = qualified_chain {
6896 let mut fq = FqName::new();
6897 cpp_push_package(&mut fq, &scope.package_name);
6898 let mut first = true;
6899 for component in chain {
6900 let kind = if first {
6901 SegmentKind::Type
6902 } else {
6903 SegmentKind::Nested
6904 };
6905 fq.push(cpp_segment(&component, kind));
6906 first = false;
6907 }
6908 fq
6909 } else {
6910 cpp_leaf_fq(
6911 &scope.package_name,
6912 scope.class_unit.as_ref(),
6913 &name,
6914 SegmentKind::Nested,
6915 SegmentKind::Type,
6916 )
6917 };
6918 let code_unit = CodeUnit::with_signature_and_fq(
6919 self.file.clone(),
6920 CodeUnitType::Class,
6921 scope.package_name.clone(),
6922 short_name,
6923 scope.template_signature.clone(),
6924 false,
6925 fq,
6926 );
6927 let has_body = definition_body_present;
6928 if !has_body && self.parsed.contains_declaration(&code_unit) {
6929 if let Some(range) = explicit_range {
6930 self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
6931 } else {
6932 self.link_source_declaration(ancestry, &code_unit, declaration_node);
6933 }
6934 self.parsed.record_navigation_range(
6935 code_unit.clone(),
6936 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
6937 );
6938 return code_unit;
6939 }
6940 if has_body {
6941 let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
6948 if let Some(range) = explicit_range {
6949 self.replace_declaration_with_range_deferred(
6950 ancestry,
6951 code_unit.clone(),
6952 range,
6953 Some(declaration_node),
6954 None,
6955 None,
6956 );
6957 } else {
6958 self.replace_declaration_deferred(
6959 ancestry,
6960 code_unit.clone(),
6961 declaration_node,
6962 None,
6963 None,
6964 );
6965 }
6966 for range in earlier_ranges {
6967 self.parsed.add_declaration_range(&code_unit, range);
6968 }
6969 } else {
6970 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
6971 }
6972 if let Some(declaration) = self
6973 .source_links
6974 .get(&code_unit)
6975 .and_then(|links| links.last())
6976 .copied()
6977 {
6978 let mut properties = self.source_properties.borrow_mut();
6979 let fact = properties
6980 .get_mut(&declaration)
6981 .expect("linked class has source properties");
6982 fact.occurrence_role = if has_body {
6983 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
6984 } else {
6985 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
6986 };
6987 fact.class_strength = if has_body {
6988 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
6989 } else {
6990 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
6991 };
6992 if let Some(base_facts) = base_facts.as_ref() {
6993 fact.bases = base_facts.clone();
6994 }
6995 }
6996 if let Some(raw_supertypes) = raw_supertypes {
6997 self.parsed
6998 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
6999 }
7000 self.parsed.add_signature(
7001 code_unit.clone(),
7002 render_cpp_type_signature(
7003 declaration_node,
7004 self.source,
7005 scope.template_signature.as_deref(),
7006 ),
7007 );
7008 if let Some(metadata) = &scope.template_metadata {
7009 let primary_short_name = if let Some(parent) = &scope.class_unit {
7010 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
7011 } else {
7012 metadata.primary_name.clone()
7013 };
7014 let primary_fq_name = CodeUnit::new(
7015 self.file.clone(),
7016 CodeUnitType::Class,
7017 scope.package_name.clone(),
7018 primary_short_name,
7019 )
7020 .fq_name();
7021 let mut metadata = metadata.clone();
7022 metadata.primary_fq_name = primary_fq_name;
7023 self.parsed
7024 .set_cpp_template_metadata(code_unit.clone(), metadata);
7025 }
7026 if let Some(parent) = &scope.class_unit {
7027 self.parsed.add_child(parent.clone(), code_unit.clone());
7028 } else if let Some(module) = &scope.module {
7029 self.parsed.add_child(module.clone(), code_unit.clone());
7030 }
7031
7032 if let Some(body) = body {
7033 let mut nested_scope = scope.clone();
7034 nested_scope.class_unit = Some(code_unit.clone());
7035 nested_scope.template_signature = scope.template_signature.clone();
7036 nested_scope.template_metadata = None;
7041 nested_scope.recovered_specialization_member_scope =
7044 scope.template_metadata.as_ref().is_some_and(|metadata| {
7045 declaration_node.kind() == "function_definition" && metadata.is_specialization()
7046 });
7047 nested_scope.declarations_are_fields =
7048 is_recovered_exported_class_container(declaration_node, self.source)
7049 || nested_scope.recovered_specialization_member_scope;
7050 if let Some(displaced) = displaced_macro_tail {
7051 push_cpp_sibling_range(
7059 body,
7060 displaced.split_index,
7061 usize::MAX,
7062 scope.clone(),
7063 stack,
7064 );
7065 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
7066 } else {
7067 stack.push(CppWork::Container(CppContainer {
7068 node: body,
7069 scope: nested_scope,
7070 }));
7071 }
7072 }
7073 if declaration_node.kind() == "enum_specifier" {
7074 self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
7075 if !self.has_enum_enumerator_units(&code_unit) {
7076 self.visit_enum_enumerators_from_text(
7077 ancestry,
7078 declaration_node,
7079 scope,
7080 &code_unit,
7081 );
7082 }
7083 }
7084 code_unit
7085 }
7086
7087 fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
7095 if self.field_owners.is_none() {
7096 self.field_owners = Some(CppFieldOwnerIndex::of(
7097 self.parsed.declarations().iter(),
7098 self.file,
7099 ));
7100 }
7101 debug_assert_eq!(
7102 parent.source(),
7103 self.file,
7104 "the walk's declarations are declarations of the file it is walking"
7105 );
7106 let carried = self
7107 .field_owners
7108 .as_ref()
7109 .expect("the index was just ensured")
7110 .owns_fields(parent.package_name(), parent.short_name());
7111
7112 #[cfg(debug_assertions)]
7113 assert_eq!(
7114 carried,
7115 cpp_declarations_hold_owned_fields(
7116 self.parsed.declarations(),
7117 self.file,
7118 parent.package_name(),
7119 parent.short_name()
7120 ),
7121 "the carried-forward field index must answer what a fresh declaration scan \
7122 answers for {}",
7123 parent.fq_name()
7124 );
7125
7126 carried
7127 }
7128
7129 fn visit_enum_enumerators(
7130 &mut self,
7131 ancestry: &ParentIndex<'_>,
7132 node: Node<'_>,
7133 scope: &ScopeInfo,
7134 parent: &CodeUnit,
7135 ) {
7136 walk_named_tree_preorder(node, false, |child| {
7137 if child.kind() != "enumerator" {
7138 return WalkControl::Continue;
7139 }
7140 let Some(name_node) = child.child_by_field_name("name") else {
7141 return WalkControl::Continue;
7142 };
7143 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
7144 if name.is_empty() {
7145 return WalkControl::Continue;
7146 }
7147 let code_unit = CodeUnit::new_fq(
7148 self.file.clone(),
7149 CodeUnitType::Field,
7150 scope.package_name.clone(),
7151 cpp_join_member_short(parent.short_name(), &name),
7152 parent
7153 .fq()
7154 .clone()
7155 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
7156 );
7157 if self.parsed.contains_declaration(&code_unit) {
7158 return WalkControl::Continue;
7159 }
7160 self.add_declaration(
7161 ancestry,
7162 code_unit.clone(),
7163 child,
7164 Some(parent.clone()),
7165 None,
7166 );
7167 self.parsed.add_signature(
7168 code_unit,
7169 normalize_cpp_whitespace(node_text(child, self.source)),
7170 );
7171 WalkControl::Continue
7172 });
7173 }
7174
7175 fn visit_enum_enumerators_from_text(
7176 &mut self,
7177 ancestry: &ParentIndex<'_>,
7178 node: Node<'_>,
7179 scope: &ScopeInfo,
7180 parent: &CodeUnit,
7181 ) {
7182 let text = node_text(node, self.source);
7183 let Some((_, body)) = text.split_once('{') else {
7184 return;
7185 };
7186 let Some((body, _)) = body.rsplit_once('}') else {
7187 return;
7188 };
7189 for entry in body.split(',') {
7190 let trimmed = entry.trim();
7191 let name = trimmed
7192 .split('=')
7193 .next()
7194 .unwrap_or("")
7195 .split_whitespace()
7196 .next()
7197 .unwrap_or("");
7198 if name.is_empty() {
7199 continue;
7200 }
7201 let code_unit = CodeUnit::new_fq(
7202 self.file.clone(),
7203 CodeUnitType::Field,
7204 scope.package_name.clone(),
7205 cpp_join_member_short(parent.short_name(), name),
7206 parent
7207 .fq()
7208 .clone()
7209 .with_pushed(cpp_segment(name, SegmentKind::Member)),
7210 );
7211 if self.parsed.contains_declaration(&code_unit) {
7212 continue;
7213 }
7214 self.add_declaration(
7215 ancestry,
7216 code_unit.clone(),
7217 node,
7218 Some(parent.clone()),
7219 None,
7220 );
7221 self.parsed.add_signature(code_unit, trimmed.to_string());
7222 }
7223 }
7224
7225 fn visit_function_definition<'tree>(
7226 &mut self,
7227 node: Node<'tree>,
7228 scope: &ScopeInfo,
7229 stack: &mut Vec<CppWork<'tree>>,
7230 ancestry: &ParentIndex<'tree>,
7231 ) {
7232 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
7239 return;
7240 }
7241 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
7247 return;
7248 }
7249 if node.has_error() {
7250 self.visit_macro_swallowed_function_declarations(node, scope);
7251 }
7252 if let Some((class_node, name, raw_supertypes, base_facts)) =
7253 recover_exported_class_function_definition(node, self.source)
7254 {
7255 if let Some(body) = cpp_body_node(node)
7256 && let Some(close) = self
7257 .orphaned_namespaces
7258 .matching_close_brace(body.start_byte())
7259 && close.end_byte < body.end_byte()
7260 {
7261 let class_range = Range {
7262 start_byte: node.start_byte(),
7263 end_byte: close.end_byte,
7264 start_line: node.start_position().row + 1,
7265 end_line: close.end_line,
7266 };
7267 let mut head = vec![node];
7272 let mut keyword = None;
7273 let mut name_node = None;
7274 while let Some(part) = head.pop() {
7275 if part.start_byte() >= body.start_byte() || part.is_missing() {
7276 continue;
7277 }
7278 if matches!(part.kind(), "class" | "struct" | "union") {
7279 keyword = Some(part);
7280 }
7281 if matches!(part.kind(), "identifier" | "type_identifier")
7282 && node_text(part, self.source) == name
7283 {
7284 name_node = Some(part);
7285 }
7286 let mut cursor = part.walk();
7287 head.extend(part.children(&mut cursor));
7288 }
7289 if let (Some(keyword), Some(name_node)) = (keyword, name_node)
7290 && let Some(type_name) = keyword
7291 .parent()
7292 .and_then(|parent| parent.child_by_field_name("name"))
7293 && let Some(tree) = parse_source_ranges_with_cancellation(
7294 &tree_sitter_cpp::LANGUAGE.into(),
7295 self.source,
7296 &[
7297 (keyword.start_byte(), type_name.start_byte()),
7298 (name_node.start_byte(), name_node.end_byte()),
7299 (body.start_byte(), close.end_byte),
7300 ],
7301 None,
7302 )
7303 && let Some(reparsed_class) = tree.root_node().named_child(0)
7304 && let Some(class_body) = cpp_body_node(reparsed_class)
7305 && class_body.start_byte() == body.start_byte()
7306 && class_body.end_byte() == close.end_byte
7307 && let Some(tail) =
7308 cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
7309 {
7310 let class_body_range = class_body.byte_range();
7311 self.with_recovery_source_context(node, |visitor| {
7312 let source_context = visitor.active_source_context.clone();
7313 let class_unit = visitor.visit_named_class_like_shape(
7314 class_node,
7315 name,
7316 None,
7317 true,
7318 Some(class_range),
7319 raw_supertypes,
7320 base_facts,
7321 scope,
7322 stack,
7323 ancestry,
7324 );
7325 visitor.parsed.record_materialization(
7326 MaterializationRecord::RecoveredDeclaration {
7327 recovery: class_range,
7328 unit: class_unit.clone(),
7329 },
7330 );
7331 let member_scope = ScopeInfo {
7332 package_name: class_unit.package_name().to_string(),
7333 class_unit: Some(class_unit),
7334 declarations_are_fields: true,
7335 template_metadata: None,
7336 recovered_specialization_member_scope: false,
7337 ..scope.clone()
7338 };
7339 let tail_range = tail.root_node().byte_range();
7340 let class_body_recovery = cpp_recovery_window(
7341 visitor.source,
7342 class_body_range.start,
7343 class_body_range.end,
7344 );
7345 let tail_recovery =
7346 cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
7347 visitor.partitioned_regions.push(CppPartitionedRegion {
7348 tree: tail,
7349 range: tail_range,
7350 scope: scope.clone(),
7351 recovery: tail_recovery,
7352 source_context: source_context.clone(),
7353 });
7354 visitor.partitioned_regions.push(CppPartitionedRegion {
7355 tree,
7356 range: class_body_range,
7357 scope: member_scope,
7358 recovery: class_body_recovery,
7359 source_context: source_context.clone(),
7360 });
7361 });
7362 return;
7363 }
7364 }
7365 let body = cpp_body_node(class_node);
7366 let displaced_namespace = cpp_body_node(node)
7367 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
7368 let fragmented = cpp_body_node(node).and_then(|body| {
7369 fragmented_export_function_body_region(
7370 node,
7371 body,
7372 self.source,
7373 displaced_namespace.as_ref(),
7374 )
7375 });
7376 if let Some(fragmented) = fragmented {
7382 if let Some(boundary) =
7386 fragmented_export_sibling_class_boundary(node, self.source, ancestry)
7387 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
7388 {
7389 let mut boundary_scope = scope.clone();
7390 for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
7391 if sibling.start_byte() >= boundary.start_byte() {
7392 break;
7393 }
7394 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
7395 boundary_scope.visible_using_namespaces.push(namespace);
7396 }
7397 }
7398 self.recovered_class_sibling_scopes
7399 .insert(boundary.id(), boundary_scope);
7400 }
7401 let mut recovered_constructor = None;
7402 let mut recovered_prefix_tree = None;
7403 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
7404 {
7405 Some(FragmentedExportMembers::Complete(tree)) => {
7406 if let Some(body) = body
7407 && let Some(range) =
7408 cpp_reparsed_synthetic_initializer_constructor_range(
7409 tree.root_node(),
7410 &name,
7411 self.source,
7412 body.end_byte(),
7413 )
7414 {
7415 recovered_constructor = Some(range);
7416 recovered_prefix_tree = Some(tree);
7417 None
7418 } else {
7419 Some(FragmentedExportMembers::Complete(tree))
7420 }
7421 }
7422 outcome => outcome,
7423 };
7424 let mut class_stack = Vec::new();
7425 let class_unit = self.visit_named_class_like_shape(
7426 class_node,
7427 name,
7428 None,
7429 true,
7430 Some(fragmented.class_range),
7431 raw_supertypes,
7432 base_facts.clone(),
7433 scope,
7434 &mut class_stack,
7435 ancestry,
7436 );
7437 self.parsed
7438 .record_materialization(MaterializationRecord::RecoveredDeclaration {
7439 recovery: fragmented.class_range,
7440 unit: class_unit.clone(),
7441 });
7442 let complete = outcome.is_some_and(|outcome| {
7443 self.visit_fragmented_export_class_members(
7444 node,
7445 outcome,
7446 class_unit.clone(),
7447 scope,
7448 )
7449 });
7450 if complete {
7451 self.consumed_fragment_regions
7452 .push((node.start_byte(), fragmented.class_range.end_byte));
7453 } else {
7454 let member_scope = ScopeInfo {
7463 package_name: class_unit.package_name().to_string(),
7464 module: scope.module.clone(),
7465 class_unit: Some(class_unit.clone()),
7466 template_signature: scope.template_signature.clone(),
7467 template_metadata: None,
7468 declarations_are_fields: true,
7469 recovered_specialization_member_scope: false,
7470 visible_using_namespaces: scope.visible_using_namespaces.clone(),
7471 };
7472 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
7473 if candidate.start_byte() >= fragmented.reparse_end {
7474 break;
7475 }
7476 if cpp_fragment_sibling_is_class_member(
7477 candidate,
7478 fragmented.reparse_end,
7479 self.source,
7480 ) {
7481 self.recovered_class_sibling_scopes
7482 .insert(candidate.id(), member_scope.clone());
7483 }
7484 }
7485 if let Some(range) = recovered_constructor
7486 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
7487 {
7488 let prefix_root = prefix_tree.root_node();
7489 let prefix_ancestry = self.recovery_ancestry(prefix_root);
7490 self.visit_recovered_fragment_prefix_members(
7491 node,
7492 prefix_root,
7493 range.start,
7494 &class_unit,
7495 scope,
7496 &prefix_ancestry,
7497 );
7498 self.visit_recovered_fragment_constructor(
7499 range,
7500 body,
7501 class_node,
7502 &class_unit,
7503 scope,
7504 ancestry,
7505 );
7506 }
7507 }
7508 if let Some(boundary) = displaced_namespace {
7509 for item in boundary.namespace_items {
7510 self.recovered_class_sibling_scopes
7511 .insert(item.id(), scope.clone());
7512 }
7513 }
7514 stack.extend(class_stack);
7515 return;
7516 }
7517 let mut stack = Vec::new();
7518 let class_unit = self.visit_named_class_like_shape(
7519 class_node,
7520 name,
7521 body,
7522 body.is_some(),
7523 None,
7524 raw_supertypes,
7525 base_facts,
7526 scope,
7527 &mut stack,
7528 ancestry,
7529 );
7530 self.parsed
7531 .record_materialization(MaterializationRecord::RecoveredDeclaration {
7532 recovery: cpp_declaration_range(node),
7533 unit: class_unit,
7534 });
7535 if let Some(body) = body
7542 && let Some(class_close) = self
7543 .orphaned_namespaces
7544 .matching_close_brace(body.start_byte())
7545 && class_close.start_byte < body.end_byte()
7546 {
7547 let split = {
7548 let mut cursor = body.walk();
7549 body.named_children(&mut cursor)
7550 .position(|child| child.start_byte() > class_close.start_byte)
7551 };
7552 if let Some(split) = split {
7553 let seeded = stack.pop();
7558 match seeded {
7559 Some(CppWork::Container(container)) => {
7560 push_cpp_sibling_range(
7561 body,
7562 split,
7563 usize::MAX,
7564 scope.clone(),
7565 &mut stack,
7566 );
7567 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
7568 }
7569 _ => unreachable!("exported-class seed is always one Container"),
7572 }
7573 }
7574 }
7575 while let Some(work) = stack.pop() {
7576 match work {
7577 CppWork::Container(container) => {
7578 push_cpp_container_work(container.node, container.scope, &mut stack);
7579 }
7580 CppWork::Siblings(siblings) => {
7581 advance_cpp_siblings(siblings, self.source, &mut stack);
7582 }
7583 CppWork::Node(work) => {
7584 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
7585 }
7586 }
7587 }
7588 return;
7589 }
7590 let recovered_constraint_constructor =
7591 cpp_recovered_template_macro_constructor(node, self.source);
7592 let declarator = recovered_constraint_constructor
7593 .map(|(declarator, _)| declarator)
7594 .or_else(|| node.child_by_field_name("declarator"));
7595 let Some(declarator) = declarator else {
7596 self.visit_malformed_function_definition_container(node, scope, stack);
7597 return;
7598 };
7599 let Some(function_declarator) = extract_function_declarator(declarator) else {
7600 self.visit_malformed_function_definition_container(node, scope, stack);
7601 return;
7602 };
7603 let function = if let Some((_, callable_name)) =
7604 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
7605 {
7606 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
7607 } else {
7608 extract_function_info(function_declarator, self.source, scope)
7609 };
7610 let Some(mut function) = function else {
7611 self.visit_malformed_function_definition_container(node, scope, stack);
7612 return;
7613 };
7614 if let Some((_, template_parameter)) = recovered_constraint_constructor {
7615 function.signature = format!(
7616 "template <{}>{}",
7617 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
7618 function.signature
7619 );
7620 }
7621 let code_unit = function.code_unit(self.file.clone());
7622 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
7627 let signature = if recovered_constraint_constructor.is_some() {
7628 normalize_cpp_whitespace(node_text(function_declarator, self.source))
7629 } else {
7630 render_cpp_function_display_signature_from_node(
7631 node,
7632 self.source,
7633 scope.template_signature.as_deref(),
7634 true,
7635 ancestry,
7636 )
7637 };
7638 self.add_signature_with_metadata(
7639 code_unit.clone(),
7640 cpp_signature_metadata(
7641 signature,
7642 function_declarator,
7643 cpp_callable_linkage(node, self.source, ancestry),
7644 self.source,
7645 ancestry,
7646 )
7647 .with_declaration_only(false),
7648 );
7649 if let Some(parent) = &scope.class_unit {
7650 self.parsed.add_child(parent.clone(), code_unit);
7651 } else if let Some(module) = &scope.module {
7652 self.parsed.add_child(module.clone(), code_unit);
7653 }
7654 self.visit_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
7655 }
7656
7657 fn scope_for_recovered_exported_class<'tree>(
7662 &mut self,
7663 node: Node<'tree>,
7664 name: &str,
7665 definition_body_present: bool,
7666 scope: &ScopeInfo,
7667 ancestry: &ParentIndex<'tree>,
7668 ) -> ScopeInfo {
7669 if !definition_body_present
7670 || !scope.package_name.is_empty()
7671 || scope.class_unit.is_some()
7672 || !(is_recovered_exported_class_container(node, self.source)
7673 || recover_function_like_export_class_pair(node, self.source).is_some()
7674 || recover_embedded_function_like_export_classes(node, self.source)
7675 .iter()
7676 .any(|recovered| recovered.name == name)
7677 || matches!(node.kind(), "declaration" | "field_declaration")
7678 && recover_exported_class_declaration(node, self.source).is_some()
7679 || matches!(
7680 node.kind(),
7681 "class_specifier" | "struct_specifier" | "union_specifier"
7682 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
7683 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
7684 name_node,
7685 self.source,
7686 )))
7687 }) || ancestry.parent(node).is_some_and(|parent| {
7688 matches!(parent.kind(), "declaration" | "field_declaration")
7689 && recover_exported_class_declaration(parent, self.source).is_some()
7690 || is_recovered_exported_class_container(parent, self.source)
7691 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
7692 {
7693 return scope.clone();
7694 }
7695 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
7696 let Some(package_name) = borrowed_namespace
7697 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
7698 else {
7699 return scope.clone();
7700 };
7701
7702 let module = CodeUnit::new_fq(
7703 self.file.clone(),
7704 CodeUnitType::Module,
7705 "",
7706 package_name.clone(),
7707 cpp_namespace_fq(&package_name),
7708 );
7709 let mut recovered = scope.clone();
7710 recovered.package_name = package_name;
7711 recovered.module = Some(module);
7712 recovered
7713 }
7714
7715 fn unique_earlier_namespace_forward<'tree>(
7724 &mut self,
7725 recovered_node: Node<'tree>,
7726 name: &str,
7727 ancestry: &ParentIndex<'tree>,
7728 ) -> Option<String> {
7729 let mut root = recovered_node;
7730 while let Some(parent) = ancestry.parent(root) {
7731 root = parent;
7732 }
7733 let source = self.source;
7734 let scan = self
7735 .namespace_forward_scans
7736 .entry(CppTreeIdentity::of(root))
7737 .or_default();
7738 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
7739 let borrowed = scan.unique_earlier_forward(name, recovered_node);
7740
7741 #[cfg(debug_assertions)]
7742 assert_eq!(
7743 borrowed,
7744 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
7745 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
7746 for {name} at byte {}",
7747 recovered_node.start_byte()
7748 );
7749
7750 borrowed
7751 }
7752
7753 fn visit_malformed_function_definition_container<'tree>(
7754 &mut self,
7755 node: Node<'tree>,
7756 scope: &ScopeInfo,
7757 stack: &mut Vec<CppWork<'tree>>,
7758 ) {
7759 let Some(body) = cpp_body_node(node) else {
7760 return;
7761 };
7762 if !cpp_contains_namespace_definition(body) {
7763 return;
7764 }
7765 stack.push(CppWork::Container(CppContainer {
7766 node: body,
7767 scope: scope.clone(),
7768 }));
7769 }
7770
7771 fn record_recovered_declarations(
7789 &mut self,
7790 recovery: Range,
7791 origin: Node<'_>,
7792 reparse_walk: impl FnOnce(&mut Self),
7793 ) {
7794 self.with_recovery_source_context(origin, |visitor| {
7795 let source_context = visitor.active_source_context.clone();
7796 visitor.record_recovered_declarations_with_context(
7797 recovery,
7798 source_context,
7799 reparse_walk,
7800 );
7801 });
7802 }
7803
7804 fn record_recovered_declarations_with_context(
7809 &mut self,
7810 recovery: Range,
7811 source_context: Option<CppActiveSourceContext>,
7812 reparse_walk: impl FnOnce(&mut Self),
7813 ) {
7814 #[cfg(any(debug_assertions, test))]
7817 let before = self.parsed.declarations().clone();
7818
7819 self.recovery_captures.push(CppRecoveryCapture::default());
7820 let previous = self.active_source_context.take();
7821 self.active_source_context = source_context;
7822 reparse_walk(self);
7823 self.active_source_context = previous;
7824 let captured = self
7825 .recovery_captures
7826 .pop()
7827 .expect("the capture this call pushed is the one it pops");
7828
7829 let mut minted: Vec<CodeUnit> = captured
7835 .created
7836 .into_iter()
7837 .filter(|unit| self.parsed.contains_declaration(unit))
7838 .collect();
7839 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
7840
7841 #[cfg(any(debug_assertions, test))]
7842 {
7843 let mut rediscovered: Vec<CodeUnit> = self
7844 .parsed
7845 .declarations()
7846 .iter()
7847 .filter(|unit| !before.contains(*unit))
7848 .cloned()
7849 .collect();
7850 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
7851 assert_eq!(
7852 minted, rediscovered,
7853 "the captured recovered set must be the declaration delta of the reparse \
7854 walk over {recovery:?}"
7855 );
7856 }
7857
7858 for unit in minted {
7859 self.parsed
7860 .record_materialization(MaterializationRecord::RecoveredDeclaration {
7861 recovery,
7862 unit,
7863 });
7864 }
7865 }
7866
7867 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
7870 let start = self
7871 .parsed
7872 .declaration_ranges(unit)
7873 .first()
7874 .map(|range| range.start_byte)
7875 .unwrap_or(usize::MAX);
7876 (start, unit.fq_name().to_string())
7877 }
7878
7879 fn visit_sentinel_macro_region<'tree>(
7880 &mut self,
7881 node: Node<'tree>,
7882 scope: &ScopeInfo,
7883 stack: &mut Vec<CppWork<'tree>>,
7884 ancestry: &ParentIndex<'tree>,
7885 ) -> bool {
7886 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
7887 return true;
7888 }
7889 if let Some((
7890 reparse_start,
7891 class_start,
7892 body_start,
7893 class_close_start,
7894 class_close_end,
7895 class_close_line,
7896 )) = cpp_sentinel_macro_class_region(node, self.source)
7897 {
7898 let Some(class_tree) =
7899 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
7900 else {
7901 return false;
7902 };
7903 let class_root = class_tree.root_node();
7904 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
7905 let class_ancestry = self.recovery_ancestry(class_root);
7907 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
7908 class_root,
7909 template_node,
7910 self.source,
7911 &class_ancestry,
7912 ) else {
7913 return false;
7914 };
7915 let class_node = reparsed_class.declaration_node;
7916 let name = reparsed_class.name;
7917 let mut class_scope = scope.clone();
7918 if let Some(template_node) = template_node {
7919 class_scope.template_signature =
7920 cpp_template_signature(template_node, class_node, self.source);
7921 class_scope.template_metadata =
7922 cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
7923 }
7924 let Some(body_tree) =
7925 cpp_reparse_region_items(self.source, body_start, class_close_start)
7926 else {
7927 return false;
7928 };
7929 let raw_supertypes = reparsed_class.raw_supertypes;
7930 let base_facts = reparsed_class.base_facts;
7931 let class_range = Range {
7932 start_byte: class_start,
7933 end_byte: class_close_end,
7934 start_line: class_node.start_position().row + 1,
7935 end_line: class_close_line,
7936 };
7937 let class_scope = self.scope_for_recovered_exported_class(
7938 class_node,
7939 &name,
7940 true,
7941 &class_scope,
7942 &class_ancestry,
7943 );
7944 let mut class_stack = Vec::new();
7945 self.with_recovery_source_context(node, |visitor| {
7946 let class_unit = visitor.visit_named_class_like_shape(
7947 class_node,
7948 name,
7949 None,
7950 true,
7951 Some(class_range),
7952 raw_supertypes,
7953 base_facts,
7954 &class_scope,
7955 &mut class_stack,
7956 &class_ancestry,
7957 );
7958 visitor.parsed.record_materialization(
7959 MaterializationRecord::RecoveredDeclaration {
7960 recovery: class_range,
7961 unit: class_unit.clone(),
7962 },
7963 );
7964 let member_scope = ScopeInfo {
7965 package_name: class_scope.package_name.clone(),
7966 module: class_scope.module.clone(),
7967 class_unit: Some(class_unit),
7968 template_signature: class_scope.template_signature.clone(),
7969 template_metadata: None,
7970 declarations_are_fields: true,
7971 recovered_specialization_member_scope: false,
7972 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
7973 };
7974 let body_root = body_tree.root_node();
7976 visitor.run_container_work(
7977 body_root,
7978 member_scope,
7979 &visitor.recovery_ancestry(body_root),
7980 );
7981 });
7982 self.consumed_fragment_regions
7985 .push((node.start_byte(), class_close_end));
7986 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
7993 stack.push(CppWork::Container(CppContainer {
7994 node,
7995 scope: scope.clone(),
7996 }));
7997 }
7998 return true;
7999 }
8000 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
8001 return false;
8002 };
8003 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
8004 return false;
8005 };
8006 let root = tree.root_node();
8007 if !cpp_reparsed_items_are_indexable(root, self.source) {
8008 return false;
8009 }
8010 let recovery = cpp_recovery_window(self.source, start, end);
8011 let reparsed_ancestry = self.recovery_ancestry(root);
8013 self.record_recovered_declarations(recovery, node, |visitor| {
8014 visitor.visit_container(
8015 root,
8016 &reparsed_ancestry,
8017 &scope.package_name,
8018 scope.module.clone(),
8019 scope.class_unit.clone(),
8020 scope.template_signature.clone(),
8021 scope.visible_using_namespaces.clone(),
8022 );
8023 });
8024 if end > node.end_byte() {
8025 self.consumed_fragment_regions
8026 .push((node.start_byte(), end));
8027 } else if node.kind() == "ERROR" && node.end_byte() > end {
8028 self.consumed_fragment_regions
8035 .push((node.start_byte(), end));
8036 stack.push(CppWork::Container(CppContainer {
8037 node,
8038 scope: scope.clone(),
8039 }));
8040 }
8041 true
8042 }
8043
8044 fn visit_nested_namespace_sentinel<'tree>(
8050 &mut self,
8051 node: Node<'tree>,
8052 scope: &ScopeInfo,
8053 ancestry: &ParentIndex<'tree>,
8054 ) -> bool {
8055 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
8056 return false;
8057 };
8058
8059 let mut package_name = scope.package_name.clone();
8060 let mut module = scope.module.clone();
8061 for component in recovered.namespace_components {
8062 package_name = if package_name.is_empty() {
8063 component
8064 } else {
8065 format!("{package_name}::{component}")
8066 };
8067 let namespace_module = CodeUnit::new_fq(
8068 self.file.clone(),
8069 CodeUnitType::Module,
8070 "",
8071 package_name.clone(),
8072 cpp_namespace_fq(&package_name),
8073 );
8074 if !self.parsed.contains_declaration(&namespace_module) {
8075 self.add_declaration(
8076 ancestry,
8077 namespace_module.clone(),
8078 recovered.function,
8079 None,
8080 None,
8081 );
8082 }
8083 module = Some(namespace_module);
8084 }
8085
8086 let recovered_scope = ScopeInfo {
8087 package_name,
8088 module,
8089 class_unit: None,
8100 template_signature: scope.template_signature.clone(),
8101 template_metadata: scope.template_metadata.clone(),
8102 declarations_are_fields: false,
8103 recovered_specialization_member_scope: false,
8104 visible_using_namespaces: scope.visible_using_namespaces.clone(),
8105 };
8106 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
8107 recovered.function,
8108 recovered.body,
8109 self.source,
8110 ancestry,
8111 ) {
8112 let mut class_scope = recovered_scope.clone();
8113 if let Some(template_node) = fragmented.template_node {
8114 class_scope.template_signature =
8115 cpp_template_signature(template_node, fragmented.class_node, self.source);
8116 class_scope.template_metadata = cpp_template_metadata(
8117 template_node,
8118 fragmented.class_node,
8119 self.source,
8120 ancestry,
8121 );
8122 }
8123 if let Some(outcome) = self
8124 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
8125 {
8126 let mut class_stack = Vec::new();
8127 let class_unit = self.visit_named_class_like_shape(
8128 fragmented.class_node,
8129 fragmented.name.clone(),
8130 None,
8131 true,
8132 Some(fragmented.fragmented.class_range),
8133 fragmented.raw_supertypes.clone(),
8134 Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
8135 &class_scope,
8136 &mut class_stack,
8137 ancestry,
8138 );
8139 self.parsed
8140 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8141 recovery: fragmented.fragmented.class_range,
8142 unit: class_unit.clone(),
8143 });
8144 if self.visit_fragmented_export_class_members(
8145 node,
8146 outcome,
8147 class_unit,
8148 &class_scope,
8149 ) {
8150 self.consumed_fragment_regions.push((
8151 fragmented.consumed_start,
8152 fragmented.fragmented.class_range.end_byte,
8153 ));
8154 }
8155 }
8156 }
8157 self.run_container_work(recovered.body, recovered_scope, ancestry);
8162 true
8163 }
8164
8165 fn visit_declaration<'tree>(
8166 &mut self,
8167 node: Node<'tree>,
8168 scope: &ScopeInfo,
8169 in_class_body: bool,
8170 stack: &mut Vec<CppWork<'tree>>,
8171 ancestry: &ParentIndex<'tree>,
8172 ) {
8173 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8174 return;
8175 }
8176 if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
8177 return;
8178 }
8179 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
8180 !cpp_active_template_type_parameter(
8181 node,
8182 node_text(declarator, self.source),
8183 self.source,
8184 ancestry,
8185 )
8186 }) {
8187 return;
8188 }
8189 if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
8190 self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
8196 return;
8197 }
8198 if in_class_body
8199 && let Some(parent) = scope.class_unit.as_ref()
8200 && let Some(call) =
8201 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
8202 {
8203 self.visit_recovered_macro_qualified_constructor_definition(
8204 node, call, scope, ancestry,
8205 );
8206 return;
8207 }
8208 if in_class_body
8209 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
8210 {
8211 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
8212 return;
8213 }
8214 if in_class_body
8215 && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
8216 {
8217 for member in members {
8218 self.add_macro_wrapped_declaration(member, scope, ancestry);
8219 }
8220 return;
8221 }
8222 if in_class_body
8223 && let Some(declarators) =
8224 recovered_macro_qualified_field_declarators(node, self.source)
8225 {
8226 for declarator in declarators {
8227 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
8228 }
8229 return;
8230 }
8231 let recovered_alias_names = recovered_type_alias_names(node, self.source);
8232 if !recovered_alias_names.is_empty() {
8233 self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
8234 return;
8235 }
8236 if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
8237 return;
8238 }
8239 if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
8240 return;
8241 }
8242
8243 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
8244 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
8245 if let Some(outcome) =
8250 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
8251 {
8252 let consumed_region = (
8253 recovered.declaration_node.end_byte(),
8254 fragmented.class_range.end_byte,
8255 );
8256 let code_unit = self.visit_named_class_like_shape(
8257 recovered.declaration_node,
8258 recovered.name,
8259 None,
8260 true,
8261 Some(fragmented.class_range),
8262 recovered.raw_supertypes,
8263 recovered.base_facts,
8264 scope,
8265 stack,
8266 ancestry,
8267 );
8268 self.parsed.record_materialization(
8269 MaterializationRecord::RecoveredDeclaration {
8270 recovery: fragmented.class_range,
8271 unit: code_unit.clone(),
8272 },
8273 );
8274 let consume_fragment =
8275 self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
8276 if consume_fragment {
8282 self.consumed_fragment_regions.push(consumed_region);
8283 }
8284 return;
8285 }
8286 }
8287 let uses_initializer_body = recovered.uses_initializer_body;
8288 let definition_body_present = recovered.body.is_some();
8289 let class_unit = self.visit_named_class_like_shape(
8290 recovered.declaration_node,
8291 recovered.name,
8292 recovered.body,
8293 definition_body_present,
8294 None,
8295 recovered.raw_supertypes,
8296 recovered.base_facts,
8297 scope,
8298 stack,
8299 ancestry,
8300 );
8301 self.parsed
8302 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8303 recovery: cpp_declaration_range(node),
8304 unit: class_unit,
8305 });
8306 if uses_initializer_body {
8307 return;
8308 }
8309 }
8310
8311 if in_class_body && cpp_unterminated_macro_invocation_field(node) {
8322 return;
8323 }
8324
8325 let mut handled_function = false;
8326 let mut handled_declarator = false;
8327 let mut cursor = node.walk();
8328 for child in node.named_children(&mut cursor) {
8329 if matches!(
8330 child.kind(),
8331 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
8332 ) {
8333 if cpp_body_node(child).is_some() {
8342 self.visit_class_like(child, scope, stack, ancestry);
8343 }
8344 continue;
8345 }
8346 }
8347
8348 let mut cursor = node.walk();
8349 for child in node.children_by_field_name("declarator", &mut cursor) {
8350 if crate::structural::is_recovered_designator_init_declarator(child) {
8351 handled_declarator = true;
8352 continue;
8353 }
8354 if in_class_body
8355 && let Some(field) = recovered_function_like_field_declarator(node, self.source)
8356 {
8357 handled_declarator = true;
8358 self.visit_variable_declaration(node, field.name, scope, true, ancestry);
8359 continue;
8360 }
8361 if let Some(kind) = classify_declarator(child) {
8362 handled_declarator = true;
8363 match kind {
8364 DeclaratorKind::Function(function_declarator) => {
8365 handled_function = true;
8366 self.visit_function_declaration(node, function_declarator, scope, ancestry);
8367 }
8368 DeclaratorKind::Variable(variable_declarator) => {
8369 self.visit_variable_declaration(
8370 node,
8371 variable_declarator,
8372 scope,
8373 in_class_body,
8374 ancestry,
8375 );
8376 }
8377 }
8378 }
8379 }
8380
8381 if !handled_declarator {
8382 let mut cursor = node.walk();
8383 for child in node.named_children(&mut cursor) {
8384 if crate::structural::is_recovered_designator_init_declarator(child) {
8385 handled_declarator = true;
8386 continue;
8387 }
8388 if !is_unfielded_declarator_candidate(child) {
8389 continue;
8390 }
8391 let Some(kind) = classify_declarator(child) else {
8392 continue;
8393 };
8394 handled_declarator = true;
8395 match kind {
8396 DeclaratorKind::Function(function_declarator) => {
8397 handled_function = true;
8398 self.visit_function_declaration(node, function_declarator, scope, ancestry);
8399 }
8400 DeclaratorKind::Variable(variable_declarator) => {
8401 self.visit_variable_declaration(
8402 node,
8403 variable_declarator,
8404 scope,
8405 in_class_body,
8406 ancestry,
8407 );
8408 }
8409 }
8410 }
8411 }
8412
8413 if handled_function {
8414 return;
8415 }
8416
8417 if !handled_declarator {
8418 if in_class_body {
8419 self.visit_class_members_from_declaration(node, scope, ancestry);
8420 } else {
8421 self.visit_global_variables_from_declaration(node, scope, ancestry);
8422 }
8423 }
8424 }
8425
8426 fn visit_anonymous_aggregate_declaration<'tree>(
8440 &mut self,
8441 node: Node<'tree>,
8442 scope: &ScopeInfo,
8443 in_class_body: bool,
8444 stack: &mut Vec<CppWork<'tree>>,
8445 ancestry: &ParentIndex<'tree>,
8446 ) -> bool {
8447 if !in_class_body || scope.class_unit.is_none() {
8448 return false;
8449 }
8450 let Some(aggregate) = node.child_by_field_name("type") else {
8451 return false;
8452 };
8453 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
8454 || aggregate.child_by_field_name("name").is_some()
8455 {
8456 return false;
8457 }
8458 let Some(body) = cpp_body_node(aggregate) else {
8459 return false;
8460 };
8461
8462 let mut cursor = node.walk();
8463 let declarators = node
8464 .children_by_field_name("declarator", &mut cursor)
8465 .filter_map(|declarator| match classify_declarator(declarator) {
8466 Some(DeclaratorKind::Variable(variable)) => Some(variable),
8467 Some(DeclaratorKind::Function(_)) | None => None,
8468 })
8469 .collect::<Vec<_>>();
8470 if declarators.is_empty() {
8471 stack.push(CppWork::Container(CppContainer {
8472 node: body,
8473 scope: scope.clone(),
8474 }));
8475 return true;
8476 }
8477
8478 for declarator in declarators {
8479 let Some(name) = extract_variable_name(declarator, self.source) else {
8480 continue;
8481 };
8482 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
8483 self.visit_named_class_like_shape(
8488 aggregate,
8489 name,
8490 Some(body),
8491 true,
8492 Some(cpp_declaration_range(node)),
8493 None,
8494 None,
8495 scope,
8496 stack,
8497 ancestry,
8498 );
8499 }
8500 true
8501 }
8502
8503 fn visit_anonymous_local_aggregate_declaration<'tree>(
8512 &mut self,
8513 node: Node<'tree>,
8514 scope: &ScopeInfo,
8515 stack: &mut Vec<CppWork<'tree>>,
8516 ancestry: &ParentIndex<'tree>,
8517 ) -> bool {
8518 if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
8519 return false;
8520 }
8521 let Some(aggregate) = node.child_by_field_name("type") else {
8522 return false;
8523 };
8524 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
8525 || aggregate.child_by_field_name("name").is_some()
8526 {
8527 return false;
8528 }
8529 let Some(body) = cpp_body_node(aggregate) else {
8530 return false;
8531 };
8532 let mut cursor = node.walk();
8533 let declarators = node
8534 .children_by_field_name("declarator", &mut cursor)
8535 .filter_map(|declarator| match classify_declarator(declarator) {
8536 Some(DeclaratorKind::Variable(variable)) => Some(variable),
8537 Some(DeclaratorKind::Function(_)) | None => None,
8538 })
8539 .collect::<Vec<_>>();
8540 if declarators.is_empty() {
8541 return false;
8542 }
8543
8544 for declarator in &declarators {
8545 self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
8546 }
8547 let name = format!("<anonymous:{}>", aggregate.start_byte());
8548 self.visit_named_class_like_shape(
8549 aggregate,
8550 name,
8551 Some(body),
8552 true,
8553 None,
8554 None,
8555 None,
8556 scope,
8557 stack,
8558 ancestry,
8559 );
8560 true
8561 }
8562
8563 fn visit_function_declaration<'tree>(
8564 &mut self,
8565 declaration_node: Node<'tree>,
8566 declarator: Node<'tree>,
8567 scope: &ScopeInfo,
8568 ancestry: &ParentIndex<'tree>,
8569 ) {
8570 let Some(function) = extract_function_info(declarator, self.source, scope) else {
8571 return;
8572 };
8573 let code_unit = function.code_unit(self.file.clone());
8579 if self.parsed.contains_declaration(&code_unit) {
8580 self.link_source_declaration(ancestry, &code_unit, declaration_node);
8581 self.parsed
8582 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
8583 return;
8584 }
8585 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8586 let signature = render_cpp_function_display_signature_from_node(
8587 declaration_node,
8588 self.source,
8589 scope.template_signature.as_deref(),
8590 false,
8591 ancestry,
8592 );
8593 self.add_signature_with_metadata(
8594 code_unit.clone(),
8595 cpp_signature_metadata(
8596 signature,
8597 declarator,
8598 cpp_callable_linkage(declaration_node, self.source, ancestry),
8599 self.source,
8600 ancestry,
8601 )
8602 .with_declaration_only(true),
8603 );
8604 if let Some(parent) = &scope.class_unit {
8605 self.parsed.add_child(parent.clone(), code_unit);
8606 } else if let Some(module) = &scope.module {
8607 self.parsed.add_child(module.clone(), code_unit);
8608 }
8609 }
8610
8611 fn visit_recovered_macro_qualified_function_declaration<'tree>(
8612 &mut self,
8613 declaration_node: Node<'tree>,
8614 call: Node<'tree>,
8615 scope: &ScopeInfo,
8616 ancestry: &ParentIndex<'tree>,
8617 ) {
8618 let Some(parent) = &scope.class_unit else {
8619 return;
8620 };
8621 let Some(name_node) = call.child_by_field_name("function") else {
8622 return;
8623 };
8624 let Some(arguments) = call.child_by_field_name("arguments") else {
8625 return;
8626 };
8627 let Some((signature, parameter_labels)) =
8628 recovered_macro_qualified_function_parameters(arguments, self.source)
8629 else {
8630 return;
8631 };
8632 let arity = parameter_labels.len();
8633 let function = FunctionInfo {
8634 package_name: scope.package_name.clone(),
8635 owner: Some(CppMemberOwner::Unit(parent.clone())),
8636 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
8637 signature,
8638 };
8639 if function.name.is_empty() {
8640 return;
8641 }
8642 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
8643 if self.parsed.contains_declaration(&code_unit) {
8644 self.link_source_declaration(ancestry, &code_unit, declaration_node);
8645 self.parsed
8646 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
8647 return;
8648 }
8649 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8650 let signature_label = render_cpp_function_display_signature_from_node(
8651 declaration_node,
8652 self.source,
8653 scope.template_signature.as_deref(),
8654 false,
8655 ancestry,
8656 );
8657 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
8658 let modifiers =
8663 cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
8664 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
8665 .with_declaration_only(true)
8666 .with_callable_arity(CallableArity::exact(arity))
8667 .with_callable_linkage(linkage)
8668 .with_callable_modifiers(
8669 modifiers.is_static,
8670 modifiers.is_constructor,
8671 modifiers.visibility,
8672 );
8673 self.add_signature_with_metadata(code_unit.clone(), metadata);
8674 self.parsed.add_child(parent.clone(), code_unit);
8675 }
8676
8677 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
8678 &mut self,
8679 declaration_node: Node<'tree>,
8680 call: Node<'tree>,
8681 scope: &ScopeInfo,
8682 ancestry: &ParentIndex<'tree>,
8683 ) {
8684 let Some(parent) = &scope.class_unit else {
8685 return;
8686 };
8687 let Some(arguments) = call.child_by_field_name("arguments") else {
8688 return;
8689 };
8690 let Some((mut signature, parameter_labels)) =
8691 recovered_macro_qualified_function_parameters(arguments, self.source)
8692 else {
8693 return;
8694 };
8695 if let Some(template_signature) = &scope.template_signature {
8696 signature = format!("{template_signature}{signature}");
8697 }
8698 let arity = parameter_labels.len();
8699 let function = FunctionInfo {
8700 package_name: scope.package_name.clone(),
8701 owner: Some(CppMemberOwner::Unit(parent.clone())),
8702 name: parent.identifier().to_string(),
8703 signature,
8704 };
8705 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
8706 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8707 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
8708 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
8709 let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
8713 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
8714 .with_declaration_only(false)
8715 .with_callable_arity(CallableArity::exact(arity))
8716 .with_callable_linkage(linkage)
8717 .with_callable_modifiers(
8718 modifiers.is_static,
8719 modifiers.is_constructor,
8720 modifiers.visibility,
8721 );
8722 self.add_signature_with_metadata(code_unit.clone(), metadata);
8723 self.parsed.add_child(parent.clone(), code_unit);
8724 }
8725
8726 fn visit_variable_declaration<'tree>(
8727 &mut self,
8728 declaration_node: Node<'tree>,
8729 declarator: Node<'tree>,
8730 scope: &ScopeInfo,
8731 in_class_body: bool,
8732 ancestry: &ParentIndex<'tree>,
8733 ) {
8734 let Some(name) = extract_variable_name(declarator, self.source) else {
8735 return;
8736 };
8737 let parent = if in_class_body {
8738 let Some(parent) = &scope.class_unit else {
8739 return;
8740 };
8741 Some(parent)
8742 } else {
8743 None
8744 };
8745 let short_name = match parent {
8746 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
8747 None => name.clone(),
8748 };
8749 let fq = cpp_leaf_fq(
8750 &scope.package_name,
8751 parent,
8752 &name,
8753 SegmentKind::Member,
8754 SegmentKind::Member,
8755 );
8756 let code_unit = CodeUnit::new_fq(
8757 self.file.clone(),
8758 CodeUnitType::Field,
8759 scope.package_name.clone(),
8760 short_name,
8761 fq,
8762 );
8763 if self.parsed.contains_declaration(&code_unit) {
8764 return;
8765 }
8766 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8767 self.add_signature_with_metadata(
8768 code_unit.clone(),
8769 SignatureMetadata::new(
8770 render_cpp_field_signature(declaration_node, declarator, self.source),
8771 Vec::new(),
8772 )
8773 .with_cpp_field_linkage(cpp_field_declaration_linkage(
8774 declaration_node,
8775 self.source,
8776 ancestry,
8777 )),
8778 );
8779 if let Some(parent) = &scope.class_unit {
8780 self.parsed.add_child(parent.clone(), code_unit);
8781 } else if let Some(module) = &scope.module {
8782 self.parsed.add_child(module.clone(), code_unit);
8783 }
8784 }
8785
8786 fn visit_class_members_from_declaration<'tree>(
8787 &mut self,
8788 node: Node<'tree>,
8789 scope: &ScopeInfo,
8790 ancestry: &ParentIndex<'tree>,
8791 ) {
8792 let mut cursor = node.walk();
8793 for child in node.named_children(&mut cursor) {
8794 if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
8795 self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
8796 } else if child.kind() == "init_declarator"
8797 && let Some(inner) = child.child_by_field_name("declarator")
8798 {
8799 self.visit_variable_declaration(node, inner, scope, true, ancestry);
8800 } else if matches!(
8801 child.kind(),
8802 "identifier"
8803 | "field_identifier"
8804 | "pointer_declarator"
8805 | "reference_declarator"
8806 | "array_declarator"
8807 | "parenthesized_declarator"
8808 ) {
8809 self.visit_variable_declaration(node, child, scope, true, ancestry);
8810 }
8811 }
8812 }
8813
8814 fn visit_global_variables_from_declaration<'tree>(
8815 &mut self,
8816 node: Node<'tree>,
8817 scope: &ScopeInfo,
8818 ancestry: &ParentIndex<'tree>,
8819 ) {
8820 let mut cursor = node.walk();
8821 for child in node.named_children(&mut cursor) {
8822 if child.kind() == "init_declarator"
8823 && let Some(inner) = child.child_by_field_name("declarator")
8824 {
8825 self.visit_variable_declaration(node, inner, scope, false, ancestry);
8826 } else if matches!(
8827 child.kind(),
8828 "identifier"
8829 | "field_identifier"
8830 | "pointer_declarator"
8831 | "reference_declarator"
8832 | "array_declarator"
8833 | "parenthesized_declarator"
8834 ) {
8835 self.visit_variable_declaration(node, child, scope, false, ancestry);
8836 }
8837 }
8838 }
8839
8840 fn visit_type_declaration<'tree>(
8841 &mut self,
8842 node: Node<'tree>,
8843 scope: &ScopeInfo,
8844 stack: &mut Vec<CppWork<'tree>>,
8845 ancestry: &ParentIndex<'tree>,
8846 ) {
8847 let type_node = node.child_by_field_name("type");
8848 if let Some(type_node) = type_node
8849 && matches!(
8850 type_node.kind(),
8851 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
8852 )
8853 {
8854 self.visit_class_like(type_node, scope, stack, ancestry);
8855 }
8856
8857 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
8858 let range = Range {
8859 start_byte: node.start_byte(),
8860 end_byte: recovered.end_node.end_byte(),
8861 start_line: node.start_position().row + 1,
8862 end_line: recovered.end_node.end_position().row + 1,
8863 };
8864 let signature = self
8865 .source
8866 .get(range.start_byte..range.end_byte)
8867 .map(normalize_cpp_whitespace)
8868 .unwrap_or_default();
8869 self.record_type_aliases(
8870 ancestry,
8871 node,
8872 scope,
8873 vec![recovered.name],
8874 signature,
8875 range,
8876 );
8877 return;
8878 }
8879
8880 let alias_names = match node.kind() {
8881 "alias_declaration" => extract_alias_declaration_name(node, self.source)
8882 .into_iter()
8883 .collect::<Vec<_>>(),
8884 "type_definition" => extract_typedef_alias_names(node, self.source),
8885 _ => Vec::new(),
8886 };
8887 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
8888 (type_node, alias_names.as_slice())
8889 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
8890 && type_node.child_by_field_name("name").is_none()
8891 {
8892 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
8893 } else {
8894 None
8895 };
8896 self.add_type_aliases(ancestry, node, scope, alias_names);
8897 if let Some((body, alias_name)) = anonymous_aggregate {
8898 let signature = normalize_cpp_whitespace(node_text(node, self.source));
8904 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
8905 debug_assert!(self.parsed.contains_declaration(&alias_unit));
8906 let mut nested_scope = scope.clone();
8907 nested_scope.class_unit = Some(alias_unit);
8908 nested_scope.template_signature = scope.template_signature.clone();
8909 nested_scope.template_metadata = None;
8910 nested_scope.declarations_are_fields = false;
8911 nested_scope.recovered_specialization_member_scope = false;
8912 stack.push(CppWork::Container(CppContainer {
8913 node: body,
8914 scope: nested_scope,
8915 }));
8916 }
8917 }
8918
8919 fn add_type_aliases(
8920 &mut self,
8921 ancestry: &ParentIndex<'_>,
8922 node: Node<'_>,
8923 scope: &ScopeInfo,
8924 alias_names: Vec<String>,
8925 ) {
8926 let signature = normalize_cpp_whitespace(node_text(node, self.source));
8927 self.record_type_aliases(
8928 ancestry,
8929 node,
8930 scope,
8931 alias_names,
8932 signature,
8933 cpp_declaration_range(node),
8934 );
8935 }
8936
8937 fn record_type_aliases(
8938 &mut self,
8939 ancestry: &ParentIndex<'_>,
8940 node: Node<'_>,
8941 scope: &ScopeInfo,
8942 alias_names: Vec<String>,
8943 signature: String,
8944 range: Range,
8945 ) {
8946 if signature.is_empty() {
8947 return;
8948 }
8949 let type_name = node
8950 .child_by_field_name("type")
8951 .and_then(|type_node| type_node.child_by_field_name("name"))
8952 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
8953 for alias_name in alias_names {
8954 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
8955 continue;
8956 }
8957 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
8958 self.note_declaration(&code_unit);
8961 self.link_source_declaration(ancestry, &code_unit, node);
8962 self.parsed
8963 .add_code_unit_with_range(code_unit.clone(), range, None, None);
8964 let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
8965 let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
8966 cpp_structured_type_identity(type_node, self.source, &lexical_scope)
8967 });
8968 self.add_signature_with_metadata(
8969 code_unit.clone(),
8970 SignatureMetadata::new(signature.clone(), Vec::new())
8971 .with_underlying_type_identity(underlying_type_identity),
8972 );
8973 if let Some(metadata) = &scope.template_metadata {
8974 let mut metadata = metadata.clone();
8975 metadata.primary_fq_name = code_unit.fq_name();
8976 self.parsed
8977 .set_cpp_template_metadata(code_unit.clone(), metadata);
8978 }
8979 if let Some(parent) = &scope.class_unit {
8980 self.parsed.add_child(parent.clone(), code_unit.clone());
8981 } else if let Some(module) = &scope.module {
8982 self.parsed.add_child(module.clone(), code_unit.clone());
8983 }
8984 self.parsed.mark_type_alias(code_unit);
8985 }
8986 }
8987
8988 fn type_alias_unit(
8989 &self,
8990 scope: &ScopeInfo,
8991 alias_name: String,
8992 signature: String,
8993 ) -> CodeUnit {
8994 let short_name = if let Some(parent) = &scope.class_unit {
8995 cpp_join_nested_short(parent.short_name(), &alias_name)
8996 } else {
8997 alias_name.clone()
8998 };
8999 let fq = cpp_leaf_fq(
9000 &scope.package_name,
9001 scope.class_unit.as_ref(),
9002 &alias_name,
9003 SegmentKind::Nested,
9004 SegmentKind::Type,
9005 );
9006 CodeUnit::with_signature_and_fq(
9007 self.file.clone(),
9008 CodeUnitType::Class,
9009 scope.package_name.clone(),
9010 short_name,
9011 Some(signature),
9012 false,
9013 fq,
9014 )
9015 }
9016
9017 fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
9018 if let Some(replacement) =
9022 crate::graph::syntax::function_macro_replacement_span(node, self.source)
9023 {
9024 self.consumed_fragment_regions
9025 .push((replacement.start, replacement.end));
9026 }
9027 let Some(name) = extract_macro_name(node, self.source) else {
9028 return;
9029 };
9030 let signature = node_text(node, self.source).trim_end().to_string();
9031 if signature.is_empty() {
9032 return;
9033 }
9034 let fq = cpp_member_fq("", &name);
9035 let code_unit = CodeUnit::with_signature_and_fq(
9042 self.file.clone(),
9043 CodeUnitType::Macro,
9044 "",
9045 name.clone(),
9046 Some(signature.clone()),
9047 false,
9048 fq,
9049 );
9050 if !self.parsed.contains_declaration(&code_unit) {
9051 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
9052 let name_range = node
9053 .child_by_field_name("name")
9054 .map(cpp_declaration_range)
9055 .unwrap_or_else(|| cpp_declaration_range(node));
9056 self.parsed
9057 .record_materialization(MaterializationRecord::GeneratedDeclaration {
9058 site: cpp_declaration_range(node),
9059 argument: name_range,
9060 kind: GenerationKind::PreprocessorDefinition,
9061 unit: code_unit.clone(),
9062 });
9063 self.parsed.add_signature(code_unit, signature);
9064 }
9065 if node.kind() == "preproc_def" {
9066 update_object_macro_field_environment(
9067 node,
9068 self.source,
9069 &mut self.object_macro_fields,
9070 &mut self.ambiguous_object_macro_fields,
9071 );
9072 } else {
9073 self.object_macro_fields.remove(&name);
9074 self.ambiguous_object_macro_fields.remove(&name);
9075 }
9076 }
9077
9078 fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
9079 let Some(directive) = node.child_by_field_name("directive") else {
9080 return;
9081 };
9082 let name = node_text(directive, self.source).trim();
9083 let range = cpp_declaration_range(node);
9084 let fields = object_macro_field_closure(&self.object_macro_fields, name);
9085 self.materialize_object_macro_fields(fields, range, node, scope);
9086 }
9087
9088 fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
9093 if !matches!(node.kind(), "declaration" | "field_declaration") {
9094 return false;
9095 }
9096 if scope.class_unit.is_none() {
9104 return false;
9105 }
9106 let macro_nodes =
9107 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
9108 for macro_node in ¯o_nodes {
9109 let name = node_text(*macro_node, self.source).trim();
9110 let fields = object_macro_field_closure(&self.object_macro_fields, name);
9111 self.materialize_object_macro_fields(
9112 fields,
9113 cpp_declaration_range(*macro_node),
9114 *macro_node,
9115 scope,
9116 );
9117 }
9118 let Some(last) = macro_nodes.last() else {
9119 return false;
9120 };
9121 self.record_collapsed_aggregate_fields(
9127 last.end_byte()..node.end_byte(),
9128 node.start_position().row
9129 + 1
9130 + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
9131 node,
9132 scope,
9133 );
9134 true
9135 }
9136
9137 fn capture_recovered_field_facts(
9141 &mut self,
9142 unit: &CodeUnit,
9143 range: Range,
9144 context: Node<'_>,
9145 field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
9146 names_function_type: bool,
9147 ) {
9148 let declaration = *self.source_links[unit]
9149 .last()
9150 .expect("recovered field follows its source occurrence");
9151 let mut fact = crate::source_facts::capture_declaration(
9152 declaration,
9153 context,
9154 unit,
9155 self.source,
9156 self.primary_ancestry,
9157 );
9158 fact.field_type = field_type;
9159 fact.names_function_type = names_function_type;
9160 fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
9161 let captured =
9162 crate::source_context::capture_declaration_context(context, unit, self.source);
9163 fact.guard_requirements = captured.guard_requirements;
9164 fact.callable_guards = fact.guard_requirements.clone();
9165 fact.callable_activation = Some(range.end_byte);
9166 fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
9167 fact.flattened_macro_namespace = captured.flattened_macro_namespace;
9168 let mut intern = |node: Node<'_>| {
9169 let mut collector = self.source_collector.borrow_mut();
9170 if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
9171 collector.intern_node(node)
9172 } else {
9173 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
9174 }
9175 };
9176 fact.conditional_family =
9177 crate::graph::resolver::preprocessor_conditional_family_node(context).map(&mut intern);
9178 fact.exhaustive_conditional_family =
9179 captured.exhaustive_conditional_family.map(&mut intern);
9180 fact.displaced_namespace_closing_brace =
9181 captured.displaced_namespace_closing_brace.map(&mut intern);
9182 self.capture_source_dependencies(&mut fact, context, unit);
9183 self.apply_active_source_context(&mut fact);
9184 assert!(
9185 self.source_properties
9186 .borrow_mut()
9187 .insert(declaration, fact)
9188 .is_none()
9189 );
9190 }
9191
9192 fn materialize_object_macro_fields(
9193 &mut self,
9194 fields: Vec<MacroReplacementField>,
9195 range: Range,
9196 context: Node<'_>,
9197 scope: &ScopeInfo,
9198 ) {
9199 let Some(owner) = scope.class_unit.as_ref() else {
9200 return;
9201 };
9202 for field in fields {
9203 let signature = field.declaration.clone();
9204 let mut fq = owner.fq().clone();
9205 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
9206 let short_name = if owner.short_name().is_empty() {
9207 field.name.clone()
9208 } else {
9209 format!("{}.{}", owner.short_name(), field.name)
9210 };
9211 let code_unit = CodeUnit::with_signature_and_fq(
9212 self.file.clone(),
9213 CodeUnitType::Field,
9214 owner.package_name().to_string(),
9215 short_name,
9216 Some(field.declaration),
9217 true,
9218 fq,
9219 );
9220 if self.parsed.contains_declaration(&code_unit) {
9221 continue;
9222 }
9223 self.add_declaration_with_range(
9224 self.primary_ancestry,
9225 code_unit.clone(),
9226 range,
9227 None,
9228 Some(owner.clone()),
9229 None,
9230 );
9231 self.capture_recovered_field_facts(
9232 &code_unit,
9233 range,
9234 context,
9235 field.field_type,
9236 field.names_function_type,
9237 );
9238 self.parsed.add_signature(code_unit, signature);
9239 }
9240 }
9241
9242 fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
9249 let mut cursor = node.walk();
9250 let children = node.children(&mut cursor).collect::<Vec<_>>();
9251 let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
9252 let mut object_macro_fields = self.object_macro_fields.clone();
9253 let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
9254 let mut open = Vec::<usize>::new();
9255 let mut index = 0;
9256 while index < children.len() {
9257 let keyword = children[index];
9258 if update_object_macro_field_environment(
9259 keyword,
9260 self.source,
9261 &mut object_macro_fields,
9262 &mut ambiguous_object_macro_fields,
9263 ) {
9264 index += 1;
9265 continue;
9266 }
9267 if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
9268 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
9269 if !name.is_empty() {
9270 let parent = open
9271 .last()
9272 .and_then(|class| recovered.get(*class))
9273 .map(|(owner, _, _, _)| owner.clone())
9274 .or_else(|| scope.class_unit.clone());
9275 let short_name = parent.as_ref().map_or_else(
9276 || name.clone(),
9277 |parent| cpp_join_nested_short(parent.short_name(), &name),
9278 );
9279 let fq = cpp_leaf_fq(
9280 &scope.package_name,
9281 parent.as_ref(),
9282 &name,
9283 SegmentKind::Nested,
9284 SegmentKind::Type,
9285 );
9286 let owner = CodeUnit::with_signature_and_fq(
9287 self.file.clone(),
9288 CodeUnitType::Class,
9289 scope.package_name.clone(),
9290 short_name,
9291 None,
9292 false,
9293 fq,
9294 );
9295 recovered.push((
9296 owner,
9297 head.key.start_byte(),
9298 head.opening.end_byte(),
9299 Vec::new(),
9300 ));
9301 let class_index = recovered.len() - 1;
9302 match head.folded_members {
9303 None => open.push(class_index),
9307 Some(members) => {
9313 let macro_nodes = object_macro_identifier_nodes_with_environment(
9314 children[index],
9315 self.source,
9316 &mut object_macro_fields,
9317 &mut ambiguous_object_macro_fields,
9318 );
9319 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
9320 .iter()
9321 .partition(|node| node.start_byte() < head.key.start_byte());
9322 if let Some(&enclosing) = open.last() {
9323 for macro_node in preceding {
9324 recovered[enclosing]
9325 .3
9326 .push(CppCollapsedMember::MacroFields {
9327 context: macro_node,
9328 range: cpp_declaration_range(macro_node),
9329 fields: object_macro_field_closure(
9330 &object_macro_fields,
9331 &normalize_cpp_whitespace(node_text(
9332 macro_node,
9333 self.source,
9334 )),
9335 ),
9336 });
9337 }
9338 }
9339 let closing = cpp_collapsed_aggregate_closing_brace(members);
9340 recovered[class_index]
9341 .3
9342 .extend(cpp_collapsed_aggregate_members(
9343 members,
9344 &inner,
9345 head.opening.end_byte()..closing,
9346 head.opening.end_position().row + 1,
9347 self.source,
9348 &object_macro_fields,
9349 ));
9350 recovered[class_index].2 = members.end_byte();
9351 for &open_class in &open {
9352 recovered[open_class].2 =
9353 recovered[open_class].2.max(members.end_byte());
9354 }
9355 }
9356 }
9357 index += head.width;
9358 continue;
9359 }
9360 }
9361 if let Some(&class_index) = open.last()
9362 && children[index].kind() == "field_declaration"
9363 {
9364 let field = children[index];
9365 let macro_nodes = object_macro_identifier_nodes_with_environment(
9366 field,
9367 self.source,
9368 &mut object_macro_fields,
9369 &mut ambiguous_object_macro_fields,
9370 );
9371 recovered[class_index]
9372 .3
9373 .extend(cpp_collapsed_aggregate_members(
9374 field,
9375 ¯o_nodes,
9376 field.start_byte()..field.end_byte(),
9377 field.start_position().row + 1,
9378 self.source,
9379 &object_macro_fields,
9380 ));
9381 let end = field.end_byte();
9382 for &open_class in &open {
9383 recovered[open_class].2 = recovered[open_class].2.max(end);
9384 }
9385 let closes = count_close_brace_nodes(field);
9386 for _ in 0..closes {
9387 if let Some(closed) = open.pop() {
9388 recovered[closed].2 = end;
9389 }
9390 }
9391 }
9392 index += 1;
9393 }
9394
9395 let mut owners = Vec::with_capacity(recovered.len());
9396 for (owner, start, end, members) in recovered {
9397 let parent = owners
9398 .iter()
9399 .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
9400 .cloned()
9401 .or_else(|| scope.class_unit.clone());
9402 self.declare_collapsed_aggregate(
9403 owner.clone(),
9404 start..end,
9405 Some(node),
9406 parent,
9407 members,
9408 scope,
9409 );
9410 owners.push(owner);
9411 }
9412 }
9413
9414 fn declare_collapsed_aggregate(
9416 &mut self,
9417 owner: CodeUnit,
9418 span: std::ops::Range<usize>,
9419 evidence: Option<Node<'_>>,
9420 parent: Option<CodeUnit>,
9421 members: Vec<CppCollapsedMember<'_>>,
9422 scope: &ScopeInfo,
9423 ) {
9424 let range = Range {
9425 start_byte: span.start,
9426 end_byte: span.end,
9427 start_line: self.source.get(..span.start).map_or(1, |source| {
9428 source.bytes().filter(|byte| *byte == b'\n').count() + 1
9429 }),
9430 end_line: self.source.get(..span.end).map_or(1, |source| {
9431 source.bytes().filter(|byte| *byte == b'\n').count() + 1
9432 }),
9433 };
9434 self.add_declaration_with_range(
9441 self.primary_ancestry,
9442 owner.clone(),
9443 range,
9444 evidence,
9445 parent,
9446 None,
9447 );
9448 let declaration = *self.source_links[&owner]
9452 .last()
9453 .expect("collapsed aggregate has a source occurrence");
9454 {
9455 let mut properties = self.source_properties.borrow_mut();
9456 let fact = properties
9457 .get_mut(&declaration)
9458 .expect("collapsed aggregate retains its original context");
9459 fact.class_strength =
9460 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
9461 fact.occurrence_role =
9462 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
9463 }
9464 let owner_scope = ScopeInfo {
9465 class_unit: Some(owner),
9466 declarations_are_fields: true,
9467 ..scope.clone()
9468 };
9469 for member in members {
9470 match member {
9471 CppCollapsedMember::MacroFields {
9472 range,
9473 fields,
9474 context,
9475 } => {
9476 self.materialize_object_macro_fields(fields, range, context, &owner_scope);
9477 }
9478 CppCollapsedMember::Declarations {
9479 span,
9480 start_line,
9481 context,
9482 } => {
9483 self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
9484 }
9485 }
9486 }
9487 }
9488
9489 fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
9500 if node.parent().is_some_and(|parent| parent.is_error()) {
9501 return false;
9504 }
9505 let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
9506 return false;
9507 };
9508 let members = head
9509 .folded_members
9510 .expect("a folded aggregate head carries its member list");
9511 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
9512 if name.is_empty() {
9513 return false;
9514 }
9515 let macro_nodes =
9516 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
9517 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
9518 .iter()
9519 .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
9520 for macro_node in preceding {
9521 let fields = object_macro_field_closure(
9522 &self.object_macro_fields,
9523 &normalize_cpp_whitespace(node_text(macro_node, self.source)),
9524 );
9525 self.materialize_object_macro_fields(
9526 fields,
9527 cpp_declaration_range(macro_node),
9528 macro_node,
9529 scope,
9530 );
9531 }
9532 let parent = scope.class_unit.clone();
9533 let short_name = parent.as_ref().map_or_else(
9534 || name.clone(),
9535 |parent| cpp_join_nested_short(parent.short_name(), &name),
9536 );
9537 let fq = cpp_leaf_fq(
9538 &scope.package_name,
9539 parent.as_ref(),
9540 &name,
9541 SegmentKind::Nested,
9542 SegmentKind::Type,
9543 );
9544 let owner = CodeUnit::with_signature_and_fq(
9545 self.file.clone(),
9546 CodeUnitType::Class,
9547 scope.package_name.clone(),
9548 short_name,
9549 None,
9550 false,
9551 fq,
9552 );
9553 let recovered = cpp_collapsed_aggregate_members(
9554 members,
9555 &inner,
9556 head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
9557 head.opening.end_position().row + 1,
9558 self.source,
9559 &self.object_macro_fields,
9560 );
9561 self.declare_collapsed_aggregate(
9562 owner,
9563 head.key.start_byte()..members.end_byte(),
9564 Some(node),
9565 parent,
9566 recovered,
9567 scope,
9568 );
9569 true
9570 }
9571
9572 fn record_collapsed_aggregate_fields(
9577 &mut self,
9578 span: std::ops::Range<usize>,
9579 start_line: usize,
9580 context: Node<'_>,
9581 scope: &ScopeInfo,
9582 ) {
9583 let Some(owner) = scope.class_unit.as_ref() else {
9584 return;
9585 };
9586 for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
9587 let range = Range {
9588 start_byte: field.range.start,
9589 end_byte: field.range.end,
9590 start_line: start_line
9591 + cpp_line_breaks_between(self.source, span.start, field.range.start),
9592 end_line: start_line
9593 + cpp_line_breaks_between(self.source, span.start, field.range.end),
9594 };
9595 let mut fq = owner.fq().clone();
9596 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
9597 let short_name = if owner.short_name().is_empty() {
9598 field.name.clone()
9599 } else {
9600 format!("{}.{}", owner.short_name(), field.name)
9601 };
9602 let signature = normalize_cpp_whitespace(&field.declaration);
9603 let code_unit = CodeUnit::with_signature_and_fq(
9604 self.file.clone(),
9605 CodeUnitType::Field,
9606 owner.package_name().to_string(),
9607 short_name,
9608 Some(signature.clone()),
9609 false,
9610 fq,
9611 );
9612 if self.parsed.contains_declaration(&code_unit) {
9613 continue;
9614 }
9615 self.add_declaration_with_range(
9616 self.primary_ancestry,
9617 code_unit.clone(),
9618 range,
9619 None,
9620 Some(owner.clone()),
9621 None,
9622 );
9623 self.capture_recovered_field_facts(
9624 &code_unit,
9625 range,
9626 context,
9627 field.field_type,
9628 field.names_function_type,
9629 );
9630 self.parsed.add_signature(code_unit, signature);
9631 }
9632 }
9633
9634 fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
9635 let Some(_directive) = node.child_by_field_name("directive") else {
9636 return;
9637 };
9638 if is_cpp_undef_directive(node, self.source) {
9639 update_object_macro_field_environment(
9640 node,
9641 self.source,
9642 &mut self.object_macro_fields,
9643 &mut self.ambiguous_object_macro_fields,
9644 );
9645 return;
9646 }
9647 let directly_in_field_list = node
9648 .parent()
9649 .is_some_and(|parent| parent.kind() == "field_declaration_list");
9650 if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
9651 self.visit_object_macro_fields(node, scope);
9652 }
9653 }
9654}
9655
9656fn object_macro_field_closure(
9664 environment: &HashMap<String, ObjectMacroReplacement>,
9665 name: &str,
9666) -> Vec<MacroReplacementField> {
9667 let mut fields = Vec::new();
9668 let mut visited = HashSet::default();
9669 let mut stack = vec![name.to_string()];
9670 while let Some(current) = stack.pop() {
9671 if !visited.insert(current.clone()) {
9672 continue;
9673 }
9674 let Some(replacement) = environment.get(¤t) else {
9675 continue;
9676 };
9677 fields.extend(replacement.fields.iter().cloned());
9678 stack.extend(replacement.nested.iter().rev().cloned());
9679 }
9680 fields
9681}
9682
9683fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
9689 crate::graph::syntax::object_macro_replacement_span(node, source)
9690 .and_then(|span| source.get(span))
9691 .map(crate::graph::syntax::object_macro_replacement)
9692 .unwrap_or_default()
9693}
9694
9695fn update_object_macro_field_environment(
9700 node: Node<'_>,
9701 source: &str,
9702 fields: &mut HashMap<String, ObjectMacroReplacement>,
9703 ambiguous: &mut HashSet<String>,
9704) -> bool {
9705 match node.kind() {
9706 "preproc_def" => {
9707 let Some(name) = extract_macro_name(node, source) else {
9708 return false;
9709 };
9710 let replacement = object_macro_replacement_of(node, source);
9711 if replacement.is_empty() || ambiguous.contains(&name) {
9712 fields.remove(&name);
9713 ambiguous.insert(name);
9714 } else if let Some(previous) = fields.get(&name) {
9715 if previous != &replacement {
9716 fields.remove(&name);
9717 ambiguous.insert(name);
9718 }
9719 } else {
9720 fields.insert(name, replacement);
9721 }
9722 true
9723 }
9724 "preproc_call" if is_cpp_undef_directive(node, source) => {
9725 if let Some(argument) = node.child_by_field_name("argument") {
9726 let name = node_text(argument, source).trim();
9727 fields.remove(name);
9728 if inside_preprocessor_conditional(node) {
9729 ambiguous.insert(name.to_string());
9730 } else {
9731 ambiguous.remove(name);
9732 }
9733 }
9734 true
9735 }
9736 _ => false,
9737 }
9738}
9739
9740fn object_macro_identifier_nodes<'tree>(
9741 node: Node<'tree>,
9742 source: &str,
9743 fields: &HashMap<String, ObjectMacroReplacement>,
9744) -> Vec<Node<'tree>> {
9745 let mut result = Vec::new();
9746 let mut stack = vec![node];
9747 while let Some(current) = stack.pop() {
9748 if matches!(
9749 current.kind(),
9750 "identifier" | "field_identifier" | "type_identifier"
9751 ) && fields.contains_key(node_text(current, source).trim())
9752 {
9753 result.push(current);
9754 }
9755 let mut cursor = current.walk();
9756 let children = current.children(&mut cursor).collect::<Vec<_>>();
9757 stack.extend(children.into_iter().rev());
9758 }
9759 result.sort_by_key(|node| node.start_byte());
9760 result
9761}
9762
9763fn object_macro_identifier_nodes_with_environment<'tree>(
9768 node: Node<'tree>,
9769 source: &str,
9770 fields: &mut HashMap<String, ObjectMacroReplacement>,
9771 ambiguous: &mut HashSet<String>,
9772) -> Vec<Node<'tree>> {
9773 let mut result = Vec::new();
9774 let mut stack = vec![node];
9775 while let Some(current) = stack.pop() {
9776 if update_object_macro_field_environment(current, source, fields, ambiguous) {
9777 continue;
9778 }
9779 if matches!(
9780 current.kind(),
9781 "identifier" | "field_identifier" | "type_identifier"
9782 ) && fields.contains_key(node_text(current, source).trim())
9783 {
9784 result.push(current);
9785 }
9786 let mut cursor = current.walk();
9787 let children = current.children(&mut cursor).collect::<Vec<_>>();
9788 stack.extend(children.into_iter().rev());
9789 }
9790 result.sort_by_key(|node| node.start_byte());
9791 result
9792}
9793
9794enum CppCollapsedMember<'tree> {
9797 MacroFields {
9800 context: Node<'tree>,
9801 range: Range,
9802 fields: Vec<MacroReplacementField>,
9803 },
9804 Declarations {
9807 context: Node<'tree>,
9808 span: std::ops::Range<usize>,
9809 start_line: usize,
9810 },
9811}
9812
9813struct CppCollapsedAggregateHead<'tree> {
9828 key: Node<'tree>,
9829 name: Node<'tree>,
9830 opening: Node<'tree>,
9831 folded_members: Option<Node<'tree>>,
9835 width: usize,
9837}
9838
9839fn cpp_collapsed_aggregate_head<'tree>(
9841 children: &[Node<'tree>],
9842 index: usize,
9843 source: &str,
9844) -> Option<CppCollapsedAggregateHead<'tree>> {
9845 let key = *children.get(index)?;
9846 if matches!(key.kind(), "struct" | "class" | "union") {
9847 let name = *children.get(index + 1)?;
9848 let opening = *children.get(index + 2)?;
9849 if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
9850 return None;
9851 }
9852 return Some(CppCollapsedAggregateHead {
9853 key,
9854 name,
9855 opening,
9856 folded_members: None,
9857 width: 3,
9858 });
9859 }
9860 cpp_folded_aggregate_head(key, source)
9861}
9862
9863fn cpp_folded_aggregate_head<'tree>(
9866 node: Node<'tree>,
9867 source: &str,
9868) -> Option<CppCollapsedAggregateHead<'tree>> {
9869 if !matches!(node.kind(), "declaration" | "field_declaration") {
9870 return None;
9871 }
9872 let mut cursor = node.walk();
9873 let children = node.children(&mut cursor).collect::<Vec<_>>();
9874 let key_index = children.iter().position(|child| {
9875 child.is_error()
9876 && matches!(
9877 node_text(*child, source).trim(),
9878 "struct" | "class" | "union"
9879 )
9880 })?;
9881 let declarator = *children.get(key_index + 1)?;
9882 let (name, members) = if declarator.kind() == "init_declarator" {
9885 (
9886 declarator.child_by_field_name("declarator")?,
9887 declarator.child_by_field_name("value")?,
9888 )
9889 } else {
9890 (declarator, *children.get(key_index + 2)?)
9891 };
9892 if !matches!(
9893 name.kind(),
9894 "field_identifier" | "type_identifier" | "identifier"
9895 ) {
9896 return None;
9897 }
9898 if members.kind() != "initializer_list" {
9899 return None;
9900 }
9901 let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
9902 Some(CppCollapsedAggregateHead {
9903 key: children[key_index],
9904 name,
9905 opening,
9906 folded_members: Some(members),
9907 width: 1,
9908 })
9909}
9910
9911fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
9915 let mut cursor = members.walk();
9916 members
9917 .children(&mut cursor)
9918 .filter(|child| child.kind() == "}" && !child.is_missing())
9919 .last()
9920 .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
9921}
9922
9923fn cpp_collapsed_aggregate_members<'tree>(
9927 context: Node<'tree>,
9928 macro_nodes: &[Node<'tree>],
9929 region: std::ops::Range<usize>,
9930 region_start_line: usize,
9931 source: &str,
9932 environment: &HashMap<String, ObjectMacroReplacement>,
9933) -> Vec<CppCollapsedMember<'tree>> {
9934 let mut members = macro_nodes
9935 .iter()
9936 .map(|macro_node| CppCollapsedMember::MacroFields {
9937 context: *macro_node,
9938 range: cpp_declaration_range(*macro_node),
9939 fields: object_macro_field_closure(
9940 environment,
9941 &normalize_cpp_whitespace(node_text(*macro_node, source)),
9942 ),
9943 })
9944 .collect::<Vec<_>>();
9945 let declarations_start = macro_nodes
9949 .last()
9950 .map_or(region.start, |macro_node| macro_node.end_byte());
9951 members.push(CppCollapsedMember::Declarations {
9952 context,
9953 span: declarations_start..region.end,
9954 start_line: region_start_line
9955 + cpp_line_breaks_between(source, region.start, declarations_start),
9956 });
9957 members
9958}
9959
9960fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
9963 source.get(from..to).map_or(0, |slice| {
9964 slice.bytes().filter(|byte| *byte == b'\n').count()
9965 })
9966}
9967
9968fn count_close_brace_nodes(node: Node<'_>) -> usize {
9974 let mut opened = 0usize;
9975 let mut closed = 0usize;
9976 let mut stack = vec![node];
9977 while let Some(current) = stack.pop() {
9978 if !current.is_missing() {
9979 match current.kind() {
9980 "{" => opened += 1,
9981 "}" => closed += 1,
9982 _ => {}
9983 }
9984 }
9985 let mut cursor = current.walk();
9986 stack.extend(current.children(&mut cursor));
9987 }
9988 closed.saturating_sub(opened)
9989}
9990
9991pub fn cpp_field_declaration_linkage<'tree>(
9996 declaration: Node<'tree>,
9997 source: &str,
9998 ancestry: &ParentIndex<'tree>,
9999) -> CppFieldLinkage {
10000 let mut current = ancestry.parent(declaration);
10001 let mut enclosed_by_class = false;
10002 while let Some(node) = current {
10003 if node.kind() == "namespace_definition"
10004 && node
10005 .child_by_field_name("name")
10006 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
10007 {
10008 return CppFieldLinkage::Internal;
10009 }
10010 if matches!(
10011 node.kind(),
10012 "class_specifier" | "struct_specifier" | "union_specifier"
10013 ) && node
10014 .child_by_field_name("name")
10015 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
10016 {
10017 return CppFieldLinkage::Internal;
10018 }
10019 if matches!(
10020 node.kind(),
10021 "class_specifier" | "struct_specifier" | "union_specifier"
10022 ) {
10023 enclosed_by_class = true;
10024 }
10025 if matches!(node.kind(), "function_definition" | "lambda_expression") {
10026 return CppFieldLinkage::Internal;
10027 }
10028 current = ancestry.parent(node);
10029 }
10030 if enclosed_by_class {
10031 return CppFieldLinkage::External;
10032 }
10033 let mut cursor = declaration.walk();
10034 let mut has_static = false;
10035 let mut has_extern = false;
10036 let mut has_inline = false;
10037 let mut has_const = false;
10038 let mut has_constexpr = false;
10039 for child in declaration.named_children(&mut cursor) {
10040 let text = normalize_cpp_whitespace(node_text(child, source));
10041 match (child.kind(), text.as_str()) {
10042 ("storage_class_specifier", "static") => has_static = true,
10043 ("storage_class_specifier", "extern") => has_extern = true,
10044 ("storage_class_specifier", "inline") => has_inline = true,
10045 ("storage_class_specifier", "constexpr") => has_constexpr = true,
10046 ("type_qualifier", "const") => has_const = true,
10047 ("type_qualifier", "constexpr") => has_constexpr = true,
10048 _ => {}
10049 }
10050 }
10051 if has_static {
10052 CppFieldLinkage::Internal
10053 } else if has_extern || has_inline {
10054 CppFieldLinkage::External
10055 } else if has_const || has_constexpr {
10056 CppFieldLinkage::InternalUnlessExternalPeer
10057 } else {
10058 CppFieldLinkage::External
10059 }
10060}
10061
10062fn cpp_declaration_range(node: Node<'_>) -> Range {
10063 Range {
10064 start_byte: node.start_byte(),
10065 end_byte: node.end_byte(),
10066 start_line: node.start_position().row + 1,
10067 end_line: node.end_position().row + 1,
10068 }
10069}
10070
10071fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
10075 let line_at = |byte: usize| {
10076 source.as_bytes()[..byte]
10077 .iter()
10078 .filter(|&&b| b == b'\n')
10079 .count()
10080 + 1
10081 };
10082 Range {
10083 start_byte,
10084 end_byte,
10085 start_line: line_at(start_byte),
10086 end_line: line_at(end_byte),
10087 }
10088}
10089
10090fn cpp_include_info(raw_snippet: String) -> ImportInfo {
10091 ImportInfo {
10092 raw_snippet,
10093 is_wildcard: false,
10094 is_global: false,
10095 identifier: None,
10096 alias: None,
10097 path: None,
10098 binder_span: None,
10099 }
10100}
10101
10102fn recover_quoted_source_imports(
10103 source: &str,
10104 imports: &mut Vec<SourceImportFact>,
10105 includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
10106 collector: &RefCell<PrimarySourceFactCollector<'_>>,
10107) {
10108 let mut in_block_comment = false;
10109 let mut start = 0;
10110 for line in source.split_inclusive('\n') {
10111 let end = start + line.len();
10112 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
10113 let trimmed = stripped.trim();
10114 if looks_like_quoted_include_line(trimmed) {
10115 let raw = normalize_cpp_whitespace(trimmed);
10116 if !imports.iter().any(|import| import.statement == raw) {
10117 let occurrence = collector.borrow_mut().intern_subspan_bytes(
10118 start,
10119 end,
10120 SourceOccurrenceProvenance::ExplicitSubspan,
10121 );
10122 let region =
10126 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
10127 let target = region.as_ref().and_then(|tree| {
10128 let mut pending = vec![tree.root_node()];
10129 while let Some(node) = pending.pop() {
10130 if node.kind() == "preproc_include"
10131 && let Some(path) = node.child_by_field_name("path")
10132 && path.kind() == "string_literal"
10133 {
10134 let (value, _) = crate::imports::include_path_from_node(node, source)?;
10135 let target = collector.borrow_mut().intern_embedded(
10136 brokk_bifrost_core::analyzer::tree_walk::node_range(path),
10137 );
10138 includes.push(
10139 brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
10140 declaration: occurrence,
10141 target,
10142 path: value.to_owned(),
10143 quoted: true,
10144 },
10145 );
10146 return Some(target);
10147 }
10148 let mut cursor = node.walk();
10149 let children: Vec<_> = node.named_children(&mut cursor).collect();
10150 pending.extend(children.into_iter().rev());
10151 }
10152 None
10153 });
10154 imports.push(SourceImportFact::from_import(
10155 cpp_include_info(raw),
10156 occurrence,
10157 target,
10158 None,
10159 Vec::new(),
10160 ));
10161 }
10162 }
10163 start = end;
10164 }
10165}
10166
10167fn looks_like_quoted_include_line(line: &str) -> bool {
10168 let Some(rest) = line.trim_start().strip_prefix('#') else {
10169 return false;
10170 };
10171 let Some(rest) = rest.trim_start().strip_prefix("include") else {
10172 return false;
10173 };
10174 rest.trim_start().starts_with('"')
10175}
10176
10177fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
10178 let mut raw = Vec::new();
10179 if let Some(bases) = cpp_class_like_base_clause(node) {
10180 collect_cpp_base_nodes(bases, source, &mut raw);
10181 }
10182 raw
10183}
10184
10185fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
10186 let mut facts = Vec::new();
10187 let mut cursor = node.walk();
10188 for child in node.named_children(&mut cursor) {
10189 if child.kind() != "base_class_clause" {
10190 continue;
10191 }
10192 let mut is_virtual = false;
10193 let mut child_cursor = child.walk();
10194 for base in child.children(&mut child_cursor) {
10195 match base.kind() {
10196 "virtual" => is_virtual = true,
10197 "," => is_virtual = false,
10198 "type_identifier"
10199 | "qualified_identifier"
10200 | "scoped_type_identifier"
10201 | "template_type"
10202 | "template_function" => {
10203 if let Some(fact) =
10204 crate::source_facts::base_specifier_from_node(base, source, is_virtual)
10205 {
10206 facts.push(fact);
10207 }
10208 is_virtual = false;
10209 }
10210 _ => {}
10211 }
10212 }
10213 }
10214 facts
10215}
10216
10217fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
10224 let mut cursor = class_like.walk();
10225 class_like
10226 .named_children(&mut cursor)
10227 .find(|child| child.kind() == "base_class_clause")
10228}
10229
10230fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
10231 walk_named_tree_preorder(node, false, |child| match child.kind() {
10232 "type_identifier" | "qualified_identifier" | "template_type" => {
10233 let text = normalize_cpp_whitespace(node_text(child, source));
10234 if !text.is_empty() {
10235 raw.push(text);
10236 }
10237 WalkControl::SkipChildren
10238 }
10239 _ => WalkControl::Continue,
10240 });
10241}
10242
10243fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
10244 let mut out = String::new();
10245 let chars: Vec<char> = line.chars().collect();
10246 let mut index = 0;
10247 let mut in_string = false;
10248 let mut in_char = false;
10249 let mut escape = false;
10250
10251 while index < chars.len() {
10252 let ch = chars[index];
10253 let next = chars.get(index + 1).copied();
10254
10255 if *in_block_comment {
10256 if ch == '*' && next == Some('/') {
10257 *in_block_comment = false;
10258 index += 2;
10259 } else {
10260 index += 1;
10261 }
10262 continue;
10263 }
10264
10265 if in_string {
10266 out.push(ch);
10267 if escape {
10268 escape = false;
10269 } else if ch == '\\' {
10270 escape = true;
10271 } else if ch == '"' {
10272 in_string = false;
10273 }
10274 index += 1;
10275 continue;
10276 }
10277
10278 if in_char {
10279 out.push(ch);
10280 if escape {
10281 escape = false;
10282 } else if ch == '\\' {
10283 escape = true;
10284 } else if ch == '\'' {
10285 in_char = false;
10286 }
10287 index += 1;
10288 continue;
10289 }
10290
10291 if ch == '/' && next == Some('/') {
10292 break;
10293 }
10294 if ch == '/' && next == Some('*') {
10295 *in_block_comment = true;
10296 index += 2;
10297 continue;
10298 }
10299 if ch == '"' {
10300 in_string = true;
10301 out.push(ch);
10302 index += 1;
10303 continue;
10304 }
10305 if ch == '\'' {
10306 in_char = true;
10307 out.push(ch);
10308 index += 1;
10309 continue;
10310 }
10311
10312 out.push(ch);
10313 index += 1;
10314 }
10315
10316 out
10317}
10318
10319#[derive(Clone)]
10320struct FunctionInfo {
10321 package_name: String,
10322 owner: Option<CppMemberOwner>,
10323 name: String,
10324 signature: String,
10325}
10326
10327#[derive(Clone)]
10334enum CppMemberOwner {
10335 Chain(Vec<String>),
10339 Unit(CodeUnit),
10342}
10343
10344impl CppMemberOwner {
10345 fn short_chain(&self) -> String {
10347 match self {
10348 Self::Chain(chain) => chain.join("$"),
10349 Self::Unit(parent) => parent.short_name().to_string(),
10350 }
10351 }
10352}
10353
10354enum DeclaratorKind<'a> {
10355 Function(Node<'a>),
10356 Variable(Node<'a>),
10357}
10358
10359impl FunctionInfo {
10360 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
10361 self.code_unit_with_synthetic(file, false)
10362 }
10363
10364 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
10365 let short_name = match &self.owner {
10366 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
10367 None => self.name.clone(),
10368 };
10369 let fq = match &self.owner {
10370 Some(CppMemberOwner::Chain(chain)) => {
10371 debug_assert!(
10372 !chain.is_empty(),
10373 "an empty owner chain is no owner; producers return None instead"
10374 );
10375 let mut fq = FqName::new();
10376 cpp_push_package(&mut fq, &self.package_name);
10377 let mut first = true;
10378 for component in chain {
10379 let kind = if first {
10380 SegmentKind::Type
10381 } else {
10382 SegmentKind::Nested
10383 };
10384 fq.push(cpp_segment(component, kind));
10385 first = false;
10386 }
10387 fq.push(cpp_segment(&self.name, SegmentKind::Member));
10388 fq
10389 }
10390 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
10391 .fq()
10392 .clone()
10393 .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
10394 Some(CppMemberOwner::Unit(_)) | None => {
10397 let mut fq = FqName::new();
10398 cpp_push_package(&mut fq, &self.package_name);
10399 fq.push(cpp_segment(&self.name, SegmentKind::Member));
10400 fq
10401 }
10402 };
10403 CodeUnit::with_signature_and_fq(
10404 file,
10405 CodeUnitType::Function,
10406 self.package_name.clone(),
10407 short_name,
10408 Some(self.signature.clone()),
10409 synthetic,
10410 fq,
10411 )
10412 }
10413}
10414
10415fn extract_function_info(
10416 declarator: Node<'_>,
10417 source: &str,
10418 scope: &ScopeInfo,
10419) -> Option<FunctionInfo> {
10420 let parameters_node = declarator.child_by_field_name("parameters")?;
10421 let declarator_name_node = declarator
10422 .child_by_field_name("declarator")
10423 .or_else(|| parameters_node.prev_named_sibling())?;
10424 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
10425}
10426
10427fn extract_function_info_from_name(
10428 declarator: Node<'_>,
10429 declarator_name_node: Node<'_>,
10430 source: &str,
10431 scope: &ScopeInfo,
10432) -> Option<FunctionInfo> {
10433 let parameters_node = declarator.child_by_field_name("parameters")?;
10434 let parameters_text = cpp_parameter_signature(parameters_node, source);
10435 let recovered_specialization_member = scope
10436 .recovered_specialization_member_scope
10437 .then(|| {
10438 let terminal = declarator_name_node
10439 .child_by_field_name("name")
10440 .unwrap_or(declarator_name_node);
10441 let name = canonical_cpp_qualified_component(terminal, source)?.name;
10442 let owner = scope.class_unit.as_ref()?;
10443 Some((
10444 Some(CppMemberOwner::Unit(owner.clone())),
10445 name,
10446 scope.package_name.clone(),
10447 ))
10448 })
10449 .flatten();
10450 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
10451 parts
10452 } else if let Some(parts) =
10453 split_structured_templated_cpp_name(declarator_name_node, source, scope)
10454 {
10455 parts
10456 } else {
10457 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
10458 declarator_name_node,
10459 source,
10460 )?);
10461 if raw_name.is_empty() {
10462 return None;
10463 }
10464 split_cpp_name(&raw_name, scope)
10465 };
10466 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
10467 let mut signature = if suffix.is_empty() {
10468 parameters_text
10469 } else {
10470 format!("{parameters_text} {suffix}")
10471 };
10472 if let Some(template_signature) = &scope.template_signature {
10473 signature = format!("{template_signature}{signature}");
10474 }
10475
10476 Some(FunctionInfo {
10477 package_name,
10478 owner,
10479 name,
10480 signature,
10481 })
10482}
10483
10484fn cpp_macro_displaced_callable_parts<'tree>(
10491 function_declarator: Node<'tree>,
10492 source: &str,
10493 ancestry: &ParentIndex<'tree>,
10494) -> Option<(Node<'tree>, Node<'tree>)> {
10495 let definition = ancestry.parent(function_declarator)?;
10496 if definition.kind() != "function_definition"
10497 || definition.child_by_field_name("declarator") != Some(function_declarator)
10498 || definition
10499 .child_by_field_name("body")
10500 .is_none_or(|body| body.kind() != "compound_statement")
10501 {
10502 return None;
10503 }
10504 let macro_type = definition.child_by_field_name("type")?;
10505 if macro_type.kind() != "type_identifier"
10506 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10507 {
10508 return None;
10509 }
10510
10511 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
10512 if apparent_return_type.kind() == "qualified_identifier"
10513 && let (Some(return_type), Some(callable_name)) = (
10514 apparent_return_type.child_by_field_name("scope"),
10515 apparent_return_type.child_by_field_name("name"),
10516 )
10517 && return_type.kind() == "template_type"
10518 && matches!(callable_name.kind(), "identifier" | "field_identifier")
10519 && (0..apparent_return_type.child_count())
10520 .filter_map(|index| apparent_return_type.child(index))
10521 .any(|child| child.kind() == "::" && child.is_missing())
10522 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
10523 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
10524 {
10525 return Some((return_type, callable_name));
10526 }
10527 if !matches!(
10528 apparent_return_type.kind(),
10529 "identifier" | "field_identifier" | "type_identifier"
10530 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
10531 {
10532 return None;
10533 }
10534 let parameters = function_declarator.child_by_field_name("parameters")?;
10535 let mut cursor = function_declarator.walk();
10536 let between = function_declarator
10537 .named_children(&mut cursor)
10538 .filter(|child| child.kind() != "comment")
10539 .filter(|child| {
10540 child.start_byte() >= apparent_return_type.end_byte()
10541 && child.end_byte() <= parameters.start_byte()
10542 && !same_node(*child, apparent_return_type)
10543 && !same_node(*child, parameters)
10544 })
10545 .collect::<Vec<_>>();
10546 let [name_error] = between.as_slice() else {
10547 return None;
10548 };
10549 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
10550 return None;
10551 }
10552 let callable_name = name_error.named_child(0)?;
10553 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
10554 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
10555 {
10556 return None;
10557 }
10558 Some((apparent_return_type, callable_name))
10559}
10560
10561fn cpp_declarator_identity_suffix(
10577 declarator: Node<'_>,
10578 parameters_node: Node<'_>,
10579 source: &str,
10580) -> String {
10581 let mut cursor = declarator.walk();
10582 let parts = declarator
10583 .named_children(&mut cursor)
10584 .filter(|child| child.start_byte() >= parameters_node.end_byte())
10585 .filter(|child| {
10586 matches!(
10587 child.kind(),
10588 "type_qualifier"
10589 | "ref_qualifier"
10590 | "noexcept"
10591 | "throw_specifier"
10592 | "trailing_return_type"
10593 | "requires_clause"
10594 )
10595 })
10596 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
10597 .filter(|text| !text.is_empty())
10598 .collect::<Vec<_>>();
10599 normalize_cpp_qualifier_suffix(&parts.join(" "))
10600}
10601
10602pub(crate) fn cpp_callable_identity_suffix(
10609 function_declarator: Node<'_>,
10610 source: &str,
10611) -> Option<String> {
10612 let parameters_node = function_declarator.child_by_field_name("parameters")?;
10613 Some(cpp_declarator_identity_suffix(
10614 function_declarator,
10615 parameters_node,
10616 source,
10617 ))
10618}
10619
10620pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
10621 match classify_declarator(node)? {
10622 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
10623 DeclaratorKind::Variable(_) => None,
10624 }
10625}
10626
10627fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
10628 match node.kind() {
10629 "function_declarator" => {
10630 let inner = node
10631 .child_by_field_name("declarator")
10632 .or_else(|| node.child_by_field_name("name"))
10633 .or_else(|| last_named_child(node));
10634 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
10635 Some(DeclaratorKind::Variable(node))
10636 } else {
10637 Some(DeclaratorKind::Function(node))
10638 }
10639 }
10640 "init_declarator"
10641 | "pointer_declarator"
10642 | "reference_declarator"
10643 | "parenthesized_declarator"
10644 | "array_declarator"
10645 | "attributed_declarator"
10646 | "template_function" => node
10647 .child_by_field_name("declarator")
10648 .or_else(|| node.child_by_field_name("name"))
10649 .or_else(|| last_named_child(node))
10650 .and_then(classify_declarator),
10651 "identifier" | "field_identifier" | "qualified_identifier" => {
10652 Some(DeclaratorKind::Variable(node))
10653 }
10654 _ => node
10655 .child_by_field_name("declarator")
10656 .or_else(|| node.child_by_field_name("name"))
10657 .or_else(|| last_named_child(node))
10658 .and_then(classify_declarator),
10659 }
10660}
10661
10662fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
10663 matches!(
10664 node.kind(),
10665 "function_declarator"
10666 | "init_declarator"
10667 | "pointer_declarator"
10668 | "reference_declarator"
10669 | "parenthesized_declarator"
10670 | "array_declarator"
10671 | "attributed_declarator"
10672 | "template_function"
10673 | "identifier"
10674 | "field_identifier"
10675 | "qualified_identifier"
10676 )
10677}
10678
10679fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
10680 let class_like = first_class_like_child(node);
10681 let mut cursor = node.walk();
10682 node.named_children(&mut cursor).any(|child| {
10683 matches!(
10684 child.kind(),
10685 "init_declarator"
10686 | "pointer_declarator"
10687 | "reference_declarator"
10688 | "array_declarator"
10689 | "function_declarator"
10690 | "parenthesized_declarator"
10691 | "attributed_declarator"
10692 ) || matches!(
10693 child.kind(),
10694 "identifier" | "field_identifier" | "qualified_identifier"
10695 ) && class_like.is_none_or(|class_node| {
10696 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
10697 })
10698 })
10699}
10700
10701struct CppNamespaceForward {
10714 name: String,
10715 start_byte: usize,
10716 namespace_end_byte: usize,
10719 package_name: String,
10720}
10721
10722fn cpp_namespace_forward_entry<'tree>(
10728 node: Node<'tree>,
10729 source: &str,
10730 ancestry: &ParentIndex<'tree>,
10731) -> Option<CppNamespaceForward> {
10732 if !matches!(
10733 node.kind(),
10734 "class_specifier" | "struct_specifier" | "union_specifier"
10735 ) || cpp_body_node(node).is_some()
10736 {
10737 return None;
10738 }
10739 let parent = node.parent()?;
10740 if !(parent.kind() == "declaration_list"
10741 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
10742 {
10743 return None;
10744 }
10745 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
10746 if !namespace.has_error() {
10751 return None;
10752 }
10753 Some(CppNamespaceForward {
10754 name: class_like_name(node, source, ancestry)?,
10755 start_byte: node.start_byte(),
10756 namespace_end_byte: namespace.end_byte(),
10757 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
10758 })
10759}
10760
10761fn cpp_namespace_forward_matches_recovery(
10765 forward: &CppNamespaceForward,
10766 recovered_node: Node<'_>,
10767) -> bool {
10768 forward.start_byte < recovered_node.start_byte()
10769 && forward.namespace_end_byte < recovered_node.start_byte()
10770 && malformed_namespace_is_nearest_recovery_region(
10771 forward.namespace_end_byte,
10772 recovered_node,
10773 )
10774}
10775
10776#[derive(Debug, Default)]
10792pub struct CppRecoveryCapture {
10793 created: Vec<CodeUnit>,
10795 created_units: HashSet<CodeUnit>,
10797 removed_pre_existing: HashSet<CodeUnit>,
10799}
10800
10801#[derive(Debug, Default)]
10813pub struct CppFieldOwnerIndex {
10814 owners: HashMap<String, HashSet<String>>,
10816 ownerless_packages: HashSet<String>,
10818}
10819
10820impl CppFieldOwnerIndex {
10821 fn of<'unit>(
10824 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
10825 file: &ProjectFile,
10826 ) -> Self {
10827 let mut index = Self::default();
10828 for declaration in declarations {
10829 index.record(declaration, file);
10830 }
10831 index
10832 }
10833
10834 fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
10835 if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
10836 return;
10837 }
10838 let short_name = code_unit.short_name();
10839 let package_name = code_unit.package_name();
10840 if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
10841 self.ownerless_packages.insert(package_name.to_string());
10842 }
10843 if !short_name.contains('.') {
10844 return;
10845 }
10846 if !self.owners.contains_key(package_name) {
10847 self.owners
10848 .insert(package_name.to_string(), HashSet::default());
10849 }
10850 let owners = self
10851 .owners
10852 .get_mut(package_name)
10853 .expect("the package entry was just ensured");
10854 for (offset, _) in short_name.match_indices('.') {
10855 let owner = &short_name[..offset];
10856 if !owners.contains(owner) {
10857 owners.insert(owner.to_string());
10858 }
10859 }
10860 }
10861
10862 fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
10865 if owner_short_name.is_empty() {
10866 self.ownerless_packages.contains(package_name)
10867 } else {
10868 self.owners
10869 .get(package_name)
10870 .is_some_and(|owners| owners.contains(owner_short_name))
10871 }
10872 }
10873}
10874
10875#[cfg(any(debug_assertions, test))]
10879fn cpp_declarations_hold_owned_fields<'unit>(
10880 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
10881 file: &ProjectFile,
10882 package_name: &str,
10883 owner_short_name: &str,
10884) -> bool {
10885 let prefix = format!("{owner_short_name}.");
10886 declarations.into_iter().any(|unit| {
10887 unit.kind() == CodeUnitType::Field
10888 && unit.source() == file
10889 && unit.package_name() == package_name
10890 && if owner_short_name.is_empty() {
10891 !unit.short_name().contains(['.', '$'])
10895 } else {
10896 unit.short_name().starts_with(&prefix)
10897 }
10898 })
10899}
10900
10901#[derive(PartialEq, Eq, Hash)]
10909pub struct CppTreeIdentity {
10910 root_id: usize,
10911 start_byte: usize,
10912 end_byte: usize,
10913 kind_id: u16,
10914 child_count: usize,
10915}
10916
10917impl CppTreeIdentity {
10918 fn of(root: Node<'_>) -> Self {
10919 Self {
10920 root_id: root.id(),
10921 start_byte: root.start_byte(),
10922 end_byte: root.end_byte(),
10923 kind_id: root.kind_id(),
10924 child_count: root.child_count(),
10925 }
10926 }
10927}
10928
10929#[derive(Default)]
10943pub struct CppNamespaceForwardScan {
10944 scanned_through: usize,
10946 forwards: HashMap<String, Vec<CppNamespaceForward>>,
10947}
10948
10949impl CppNamespaceForwardScan {
10950 fn advance_to<'tree>(
10957 &mut self,
10958 root: Node<'tree>,
10959 cutoff: usize,
10960 source: &str,
10961 ancestry: &ParentIndex<'tree>,
10962 ) {
10963 if cutoff <= self.scanned_through {
10964 return;
10965 }
10966 let folded_through = self.scanned_through;
10967 let mut cursor = root.walk();
10968 let mut stack = vec![root];
10969 while let Some(current) = stack.pop() {
10970 if (folded_through..cutoff).contains(¤t.start_byte())
10971 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
10972 {
10973 self.forwards
10974 .entry(forward.name.clone())
10975 .or_default()
10976 .push(forward);
10977 }
10978 for child in current.named_children(&mut cursor) {
10983 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
10984 stack.push(child);
10985 }
10986 }
10987 }
10988 self.scanned_through = cutoff;
10989 }
10990
10991 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
10995 let mut matching = self
10996 .forwards
10997 .get(name)
10998 .into_iter()
10999 .flatten()
11000 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
11001 let first = matching.next()?;
11002 matching
11003 .next()
11004 .is_none()
11005 .then(|| first.package_name.clone())
11006 }
11007}
11008
11009#[cfg(any(debug_assertions, test))]
11014fn unique_earlier_cpp_namespace_forward<'tree>(
11015 recovered_node: Node<'tree>,
11016 name: &str,
11017 source: &str,
11018 ancestry: &ParentIndex<'tree>,
11019) -> Option<String> {
11020 let mut root = recovered_node;
11021 while let Some(parent) = ancestry.parent(root) {
11022 root = parent;
11023 }
11024
11025 let mut candidates = Vec::new();
11026 let mut stack = vec![root];
11027 while let Some(current) = stack.pop() {
11028 if current.start_byte() < recovered_node.start_byte()
11029 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
11030 && forward.name == name
11031 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
11032 {
11033 candidates.push(forward.package_name);
11034 }
11035
11036 let mut cursor = current.walk();
11037 for child in current.named_children(&mut cursor) {
11038 if child.start_byte() < recovered_node.start_byte() {
11039 stack.push(child);
11040 }
11041 }
11042 }
11043
11044 if candidates.len() == 1 {
11045 candidates.pop()
11046 } else {
11047 None
11048 }
11049}
11050
11051fn malformed_namespace_is_nearest_recovery_region(
11052 namespace_end_byte: usize,
11053 recovered_node: Node<'_>,
11054) -> bool {
11055 let mut root = recovered_node;
11056 while let Some(parent) = root.parent() {
11057 root = parent;
11058 }
11059 let mut cursor = root.walk();
11060 root.named_children(&mut cursor)
11061 .filter(|sibling| {
11062 namespace_end_byte <= sibling.start_byte()
11063 && sibling.end_byte() <= recovered_node.start_byte()
11064 })
11065 .all(is_malformed_namespace_recovery_trivia)
11066}
11067
11068fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
11069 matches!(node.kind(), "ERROR" | "comment")
11070 || node.kind().starts_with("preproc_")
11071 || node.kind() == "expression_statement" && node.named_child_count() == 0
11072}
11073
11074fn cpp_namespace_name_for_forward<'tree>(
11078 node: Node<'tree>,
11079 source: &str,
11080 ancestry: &ParentIndex<'tree>,
11081) -> Option<String> {
11082 cpp_namespace_definition_for_forward(node, ancestry)?;
11083 cpp_lexical_namespace_name(node, source, ancestry)
11084}
11085
11086fn cpp_namespace_definition_for_forward<'tree>(
11087 node: Node<'tree>,
11088 ancestry: &ParentIndex<'tree>,
11089) -> Option<Node<'tree>> {
11090 let declaration = ancestry.parent(node)?;
11091 let mut ancestor = ancestry.parent(declaration);
11092 while let Some(current) = ancestor {
11093 if matches!(
11094 current.kind(),
11095 "compound_statement"
11096 | "field_declaration_list"
11097 | "class_specifier"
11098 | "struct_specifier"
11099 | "union_specifier"
11100 | "function_definition"
11101 | "lambda_expression"
11102 ) {
11103 return None;
11104 }
11105 if current.kind() == "namespace_definition" {
11106 return Some(current);
11107 }
11108 ancestor = ancestry.parent(current);
11109 }
11110 None
11111}
11112
11113fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
11114 match node.kind() {
11115 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
11116 "parenthesized_declarator" => node
11117 .child_by_field_name("declarator")
11118 .or_else(|| last_named_child(node))
11119 .is_some_and(is_pointer_wrapper_declarator),
11120 "template_function" => node
11121 .child_by_field_name("name")
11122 .is_some_and(is_function_pointer_like_inner_declarator),
11123 _ => false,
11124 }
11125}
11126
11127fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
11128 match node.kind() {
11129 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
11130 "parenthesized_declarator" => node
11131 .child_by_field_name("declarator")
11132 .or_else(|| last_named_child(node))
11133 .is_some_and(is_pointer_wrapper_declarator),
11134 _ => false,
11135 }
11136}
11137
11138fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
11139 let cleaned = raw_name.trim_start_matches("template ").trim();
11140 let cleaned = cleaned.trim_start_matches("::");
11147 let parts: Vec<_> = cleaned
11156 .split("::")
11157 .filter(|component| !component.is_empty())
11158 .collect();
11159 if parts.is_empty() {
11160 return (None, cleaned.to_string(), scope.package_name.clone());
11161 }
11162 if parts.len() > 1 {
11163 let name = parts.last().unwrap_or(&cleaned).to_string();
11164 let owner_parts = &parts[..parts.len() - 1];
11165 if let Some(class_unit) = &scope.class_unit {
11166 return (
11169 Some(CppMemberOwner::Unit(class_unit.clone())),
11170 name,
11171 scope.package_name.clone(),
11172 );
11173 }
11174 if !scope.package_name.is_empty() {
11175 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
11190 let owner = (!nested.is_empty()).then(|| {
11191 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
11192 });
11193 return (owner, name, scope.package_name.clone());
11194 }
11195 let (owner, package_name) = if owner_parts.len() > 1 {
11197 (
11209 Some(CppMemberOwner::Chain(vec![
11210 owner_parts.last().unwrap_or(&"").to_string(),
11211 ])),
11212 owner_parts[..owner_parts.len() - 1].join("::"),
11213 )
11214 } else {
11215 (
11227 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
11228 cpp_using_directive_namespace_for_bare_owner(scope),
11229 )
11230 };
11231 return (owner, name, package_name);
11232 }
11233
11234 let package_name = scope.package_name.clone();
11235 let owner = scope
11236 .class_unit
11237 .as_ref()
11238 .map(|parent| CppMemberOwner::Unit(parent.clone()));
11239 (owner, cleaned.to_string(), package_name)
11240}
11241
11242fn strip_redundant_namespace_prefix<'a>(
11256 owner_parts: &'a [&'a str],
11257 package_name: &str,
11258) -> &'a [&'a str] {
11259 if package_name.is_empty() {
11260 return owner_parts;
11261 }
11262 let package_segments: Vec<&str> = package_name.split("::").collect();
11263 let max_prefix = owner_parts.len().min(package_segments.len());
11264 for prefix_len in (1..=max_prefix).rev() {
11265 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
11266 if &owner_parts[..prefix_len] == package_suffix {
11267 return &owner_parts[prefix_len..];
11268 }
11269 }
11270 owner_parts
11271}
11272
11273fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
11284 scope
11285 .visible_using_namespaces
11286 .iter()
11287 .min_by_key(|namespace| namespace.split("::").count())
11288 .cloned()
11289 .unwrap_or_default()
11290}
11291
11292struct CppQualifiedNameComponent {
11293 name: String,
11294 is_template_id: bool,
11295}
11296
11297fn qualified_class_name_chain(
11310 class_node: Node<'_>,
11311 source: &str,
11312 scope: &ScopeInfo,
11313) -> Option<Vec<String>> {
11314 if scope.package_name.is_empty() || scope.class_unit.is_some() {
11315 return None;
11316 }
11317 let name = class_node.child_by_field_name("name")?;
11318 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
11319 if explicitly_global
11320 || components.len() < 2
11321 || components.iter().any(|component| component.is_template_id)
11322 {
11323 return None;
11324 }
11325 let names = components
11326 .iter()
11327 .map(|component| component.name.as_str())
11328 .collect::<Vec<_>>();
11329 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
11330 if class_chain.is_empty() {
11331 return None;
11332 }
11333 Some(class_chain.iter().map(|name| name.to_string()).collect())
11334}
11335
11336fn structured_cpp_qualified_components(
11337 qualified_name: Node<'_>,
11338 source: &str,
11339) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
11340 if qualified_name.kind() != "qualified_identifier" {
11341 return None;
11342 }
11343
11344 let mut components = Vec::new();
11345 let mut current = qualified_name;
11346 let mut explicitly_global = false;
11347 loop {
11348 if current.kind() == "qualified_identifier" {
11349 if let Some(component) = current.child_by_field_name("scope") {
11350 components.push(canonical_cpp_qualified_component(component, source)?);
11351 } else if components.is_empty() {
11352 explicitly_global = true;
11353 } else {
11354 return None;
11355 }
11356 current = current.child_by_field_name("name")?;
11357 } else {
11358 components.push(canonical_cpp_qualified_component(current, source)?);
11359 break;
11360 }
11361 }
11362 Some((components, explicitly_global))
11363}
11364
11365fn split_structured_templated_cpp_name(
11366 declarator_name: Node<'_>,
11367 source: &str,
11368 scope: &ScopeInfo,
11369) -> Option<(Option<CppMemberOwner>, String, String)> {
11370 let (mut components, explicitly_global) =
11371 structured_cpp_qualified_components(declarator_name, source)?;
11372
11373 let terminal = components.pop()?;
11374 let owner_start = components
11375 .iter()
11376 .position(|component| component.is_template_id)?;
11377 let explicit_package = components[..owner_start]
11378 .iter()
11379 .map(|component| component.name.as_str())
11380 .collect::<Vec<_>>()
11381 .join("::");
11382 let explicit_package_is_empty = explicit_package.is_empty();
11383 let package_name = match (
11384 explicitly_global,
11385 scope.package_name.is_empty(),
11386 explicit_package_is_empty,
11387 ) {
11388 (true, _, _) => explicit_package,
11389 (false, _, true) => scope.package_name.clone(),
11390 (false, true, false) => explicit_package,
11391 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
11392 };
11393 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
11399 {
11400 cpp_using_directive_namespace_for_bare_owner(scope)
11401 } else {
11402 package_name
11403 };
11404 let owner_chain = components[owner_start..]
11405 .iter()
11406 .map(|component| component.name.clone())
11407 .collect::<Vec<_>>();
11408 if owner_chain.is_empty() || terminal.name.is_empty() {
11409 return None;
11410 }
11411
11412 Some((
11413 Some(CppMemberOwner::Chain(owner_chain)),
11414 terminal.name,
11415 package_name,
11416 ))
11417}
11418
11419fn canonical_cpp_qualified_component(
11420 mut component: Node<'_>,
11421 source: &str,
11422) -> Option<CppQualifiedNameComponent> {
11423 let mut is_template_id = false;
11424 loop {
11425 match component.kind() {
11426 "template_type" => {
11427 is_template_id = true;
11428 component = component.child_by_field_name("name")?;
11429 }
11430 "dependent_name" => component = component.named_child(0)?,
11431 "identifier"
11432 | "field_identifier"
11433 | "namespace_identifier"
11434 | "type_identifier"
11435 | "operator_name"
11436 | "destructor_name" => {
11437 let name = normalize_cpp_whitespace(node_text(component, source));
11438 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
11439 name,
11440 is_template_id,
11441 });
11442 }
11443 _ => component = component.child_by_field_name("name")?,
11444 }
11445 }
11446}
11447
11448fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
11449 if let Some(name) = macro_decorated_unqualified_name(node) {
11450 return extract_declarator_name(name, source);
11451 }
11452 match node.kind() {
11453 "identifier"
11454 | "field_identifier"
11455 | "type_identifier"
11456 | "operator_name"
11457 | "destructor_name"
11458 | "qualified_identifier" => node_text(node, source).to_string(),
11459 "function_declarator"
11460 | "pointer_declarator"
11461 | "reference_declarator"
11462 | "parenthesized_declarator"
11463 | "array_declarator"
11464 | "template_function" => node
11465 .child_by_field_name("declarator")
11466 .or_else(|| node.child_by_field_name("name"))
11467 .or_else(|| last_named_child(node))
11468 .map(|child| extract_declarator_name(child, source))
11469 .unwrap_or_else(|| node_text(node, source).to_string()),
11470 _ => node
11471 .child_by_field_name("name")
11472 .map(|child| extract_declarator_name(child, source))
11473 .unwrap_or_else(|| node_text(node, source).to_string()),
11474 }
11475}
11476
11477fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
11482 if let Some(name) = macro_decorated_unqualified_name(node) {
11483 return extract_callable_declarator_name(name, source);
11484 }
11485 match node.kind() {
11486 "identifier"
11487 | "field_identifier"
11488 | "type_identifier"
11489 | "operator_name"
11490 | "destructor_name"
11491 | "qualified_identifier" => Some(node_text(node, source).to_string()),
11492 "function_declarator"
11493 | "pointer_declarator"
11494 | "reference_declarator"
11495 | "parenthesized_declarator"
11496 | "array_declarator"
11497 | "template_function" => node
11498 .child_by_field_name("declarator")
11499 .or_else(|| node.child_by_field_name("name"))
11500 .and_then(|child| extract_callable_declarator_name(child, source)),
11501 _ => None,
11502 }
11503}
11504
11505fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
11506 match node.kind() {
11507 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
11508 let name = node_text(node, source).trim().to_string();
11509 (!name.is_empty()).then_some(name)
11510 }
11511 _ => node
11512 .child_by_field_name("declarator")
11513 .or_else(|| node.child_by_field_name("name"))
11514 .or_else(|| last_named_child(node))
11515 .and_then(|child| extract_variable_name(child, source)),
11516 }
11517}
11518
11519#[derive(Clone, Copy)]
11525pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
11526 pub(crate) name: Node<'tree>,
11527 pub(crate) declarator: Node<'tree>,
11528}
11529
11530impl RecoveredFunctionLikeFieldDeclarator<'_> {
11531 pub(crate) fn pointer_depth(self) -> i32 {
11532 let mut depth = 0;
11533 let mut current = self.declarator;
11534 while current.kind() != "function_declarator" {
11535 if current.kind() == "pointer_declarator" {
11536 depth += 1;
11537 }
11538 current = current
11539 .child_by_field_name("declarator")
11540 .expect("recovered field wrapper has an inner declarator");
11541 }
11542 depth
11543 }
11544}
11545
11546fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
11557 if node.kind() != "field_declaration" {
11558 return false;
11559 }
11560 let Some(type_node) = node.child_by_field_name("type") else {
11561 return false;
11562 };
11563 if !matches!(type_node.kind(), "identifier" | "type_identifier") {
11564 return false;
11565 }
11566 let Some(declarator) = node.child_by_field_name("declarator") else {
11567 return false;
11568 };
11569 if declarator.kind() != "parenthesized_declarator" {
11570 return false;
11571 }
11572 let mut invented_terminator = false;
11573 for child in children_iter(node) {
11574 if child.kind() != ";" {
11575 continue;
11576 }
11577 if !child.is_missing() {
11578 return false;
11579 }
11580 invented_terminator = true;
11581 }
11582 invented_terminator
11583}
11584
11585pub(crate) fn recovered_function_like_field_declarator<'tree>(
11586 node: Node<'tree>,
11587 source: &str,
11588) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
11589 if node.kind() != "field_declaration" {
11590 return None;
11591 }
11592 let outer_declarator = node.child_by_field_name("declarator")?;
11593 let mut declarator = outer_declarator;
11594 while matches!(
11595 declarator.kind(),
11596 "pointer_declarator"
11597 | "reference_declarator"
11598 | "array_declarator"
11599 | "parenthesized_declarator"
11600 ) {
11601 declarator = declarator.child_by_field_name("declarator")?;
11602 }
11603 if declarator.kind() != "function_declarator" {
11604 return None;
11605 }
11606 let macro_name = declarator.child_by_field_name("declarator")?;
11607 if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
11608 {
11609 return None;
11610 }
11611 let parameters = declarator.child_by_field_name("parameters")?;
11612 let mut cursor = parameters.walk();
11613 let mut arguments = parameters.named_children(&mut cursor);
11614 let parameter = arguments.next()?;
11615 if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
11616 return None;
11617 }
11618 let name = parameter.child_by_field_name("type").filter(|argument| {
11619 matches!(
11620 argument.kind(),
11621 "identifier" | "field_identifier" | "type_identifier"
11622 )
11623 })?;
11624 Some(RecoveredFunctionLikeFieldDeclarator {
11625 name,
11626 declarator: outer_declarator,
11627 })
11628}
11629
11630fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
11631 let count = node.named_child_count();
11632 if count == 0 {
11633 None
11634 } else {
11635 node.named_child(count - 1)
11636 }
11637}
11638
11639fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
11640 let name_node = node.child_by_field_name("name")?;
11641 let name = normalize_cpp_whitespace(node_text(name_node, source));
11642 (!name.is_empty()).then_some(name)
11643}
11644
11645fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
11646 if node.kind() != "declaration" {
11647 return Vec::new();
11648 }
11649 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
11650 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
11651 }) else {
11652 return Vec::new();
11653 };
11654 let Some(declarator) = node.child_by_field_name("declarator") else {
11655 return Vec::new();
11656 };
11657 if node_text(keyword, source) == "using"
11658 && (declarator.kind() != "init_declarator"
11659 || declarator.child_by_field_name("value").is_none())
11660 {
11661 return Vec::new();
11662 }
11663 if node_text(keyword, source) == "typedef"
11664 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
11665 {
11666 return vec![alias_name];
11667 }
11668 extract_typedef_declarator_name(declarator, source)
11669 .into_iter()
11670 .collect()
11671}
11672
11673pub(crate) fn recovered_alias_type_node<'tree>(
11676 node: Node<'tree>,
11677 alias_name: &str,
11678 source: &str,
11679) -> Option<Node<'tree>> {
11680 if node.kind() != "declaration" {
11681 return None;
11682 }
11683 let declarator = node.child_by_field_name("declarator")?;
11684 let admitted = recovered_type_alias_names(node, source)
11685 .iter()
11686 .any(|name| name == alias_name)
11687 || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
11688 if !admitted {
11689 return None;
11690 }
11691 if declarator.kind() == "init_declarator" {
11692 return declarator.child_by_field_name("value");
11693 }
11694 (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
11695 .then_some(declarator)
11696}
11697
11698fn recovered_typedef_error_alias_name(
11699 declaration: Node<'_>,
11700 declarator: Node<'_>,
11701 source: &str,
11702) -> Option<String> {
11703 if declarator.kind() != "qualified_identifier" {
11713 return None;
11714 }
11715 let mut cursor = declaration.walk();
11716 let mut errors = declaration
11717 .named_children(&mut cursor)
11718 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
11719 let error = errors.next()?;
11720 if errors.next().is_some() || error.named_child_count() != 1 {
11721 return None;
11722 }
11723 let name = error.named_child(0)?;
11724 if !matches!(
11725 name.kind(),
11726 "identifier" | "field_identifier" | "type_identifier"
11727 ) {
11728 return None;
11729 }
11730 let name = normalize_cpp_whitespace(node_text(name, source));
11731 (!name.is_empty()).then_some(name)
11732}
11733
11734fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
11735 if fragmented_parenthesized_typedef_type(node).is_some() {
11739 return Vec::new();
11740 }
11741 let has_function_like_macro_type = node
11742 .child_by_field_name("type")
11743 .filter(|type_node| type_node.kind() == "type_identifier")
11744 .is_some_and(|type_node| {
11745 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
11746 });
11747 let mut names = Vec::new();
11748 let mut cursor = node.walk();
11749 for declarator in node.children_by_field_name("declarator", &mut cursor) {
11750 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
11751 continue;
11752 }
11753 if let Some(name) = extract_typedef_declarator_name(declarator, source)
11754 && !names.contains(&name)
11755 {
11756 names.push(name);
11757 }
11758 }
11759 names
11760}
11761
11762struct RecoveredMacroTypedefAlias<'tree> {
11763 name: String,
11764 end_node: Node<'tree>,
11765}
11766
11767fn recovered_macro_typedef_alias<'tree>(
11771 node: Node<'tree>,
11772 source: &str,
11773) -> Option<RecoveredMacroTypedefAlias<'tree>> {
11774 let type_node = fragmented_parenthesized_typedef_type(node)?;
11775 if type_node.kind() != "type_identifier"
11776 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
11777 {
11778 return None;
11779 }
11780
11781 let end_node = node.next_named_sibling()?;
11782 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
11783 return None;
11784 }
11785 let name_node = end_node.named_child(0)?;
11786 if name_node.kind() != "identifier" {
11787 return None;
11788 }
11789 let has_terminator = (0..end_node.child_count()).any(|index| {
11790 end_node
11791 .child(index)
11792 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
11793 });
11794 if !has_terminator {
11795 return None;
11796 }
11797 let name = normalize_cpp_whitespace(node_text(name_node, source));
11798 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
11799}
11800
11801fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
11802 if node.kind() != "type_definition" {
11803 return None;
11804 }
11805 let mut declarator_cursor = node.walk();
11806 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
11807 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
11808 return None;
11809 }
11810 let has_missing_terminator = (0..node.child_count()).any(|index| {
11811 node.child(index)
11812 .is_some_and(|child| child.kind() == ";" && child.is_missing())
11813 });
11814 if !has_missing_terminator {
11815 return None;
11816 }
11817 node.child_by_field_name("type")
11818}
11819
11820fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
11821 match node.kind() {
11822 "identifier" | "field_identifier" | "type_identifier" => {
11823 let name = normalize_cpp_whitespace(node_text(node, source));
11824 (!name.is_empty()).then_some(name)
11825 }
11826 "qualified_identifier" => node
11827 .child_by_field_name("name")
11828 .and_then(|name| extract_typedef_declarator_name(name, source)),
11829 _ => node
11830 .child_by_field_name("declarator")
11831 .or_else(|| node.child_by_field_name("name"))
11832 .or_else(|| last_named_child(node))
11833 .and_then(|child| extract_typedef_declarator_name(child, source)),
11834 }
11835}
11836
11837fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
11838 let name = node
11839 .child_by_field_name("name")
11840 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
11841 .or_else(|| {
11842 let mut cursor = node.walk();
11843 node.named_children(&mut cursor)
11844 .find(|child| {
11845 matches!(
11846 child.kind(),
11847 "identifier" | "field_identifier" | "type_identifier"
11848 )
11849 })
11850 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
11851 })?;
11852 (!name.is_empty()).then_some(name)
11853}
11854
11855fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
11856 left.id() == right.id()
11857}
11858
11859fn render_cpp_type_signature(
11860 node: Node<'_>,
11861 source: &str,
11862 template_signature: Option<&str>,
11863) -> String {
11864 let text = normalize_cpp_whitespace(node_text(node, source));
11865 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
11866 let rendered = if head.ends_with(';') {
11867 head.to_string()
11868 } else {
11869 format!("{head} {{")
11870 };
11871 if let Some(template_signature) = template_signature {
11872 format!("template {template_signature} {rendered}")
11873 } else {
11874 rendered
11875 }
11876}
11877
11878fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
11879 if let Some(recovered) = recovered_pyobject_head_field(node, source)
11880 && recovered.declarator == declarator
11881 {
11882 let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
11883 let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
11884 return format!("{type_text} {declarator};");
11885 }
11886 if let Some(recovered) = recovered_function_like_field_declarator(node, source)
11887 && recovered.name == declarator
11888 {
11889 let type_text = node
11890 .child_by_field_name("type")
11891 .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
11892 .unwrap_or_default();
11893 let name = normalize_cpp_whitespace(node_text(recovered.name, source));
11894 let mut prefix = String::new();
11895 let mut suffix = String::new();
11896 let mut current = recovered.declarator;
11897 while current.kind() != "function_declarator" {
11898 match current.kind() {
11899 "pointer_declarator" => prefix.push('*'),
11900 "reference_declarator" => prefix.push('&'),
11901 "array_declarator" => {
11902 let size = current
11903 .child_by_field_name("size")
11904 .map(|size| normalize_cpp_whitespace(node_text(size, source)))
11905 .unwrap_or_default();
11906 suffix.push('[');
11907 suffix.push_str(&size);
11908 suffix.push(']');
11909 }
11910 "parenthesized_declarator" => {}
11911 _ => unreachable!("validated recovered field declarator wrapper"),
11912 }
11913 current = current
11914 .child_by_field_name("declarator")
11915 .expect("recovered field wrapper has an inner declarator");
11916 }
11917 let separator = if prefix.is_empty() { "" } else { " " };
11918 return format!("{type_text} {prefix}{separator}{name}{suffix};");
11919 }
11920 if let Some(signature) =
11921 render_recovered_macro_qualified_field_signature(node, declarator, source)
11922 {
11923 return signature;
11924 }
11925 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
11926 let prefix = cpp_declaration_prefix(node, source);
11927 let name = extract_variable_name(declarator, source).unwrap_or_default();
11928 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
11929 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
11930 || (prefix.ends_with('&') && raw_suffix == "&")
11931 {
11932 String::new()
11933 } else {
11934 raw_suffix
11935 };
11936
11937 let mut rendered = if suffix.is_empty() {
11938 format!("{prefix} {name}")
11939 } else if suffix.starts_with('*') || suffix.starts_with('&') {
11940 format!("{prefix}{suffix} {name}")
11941 } else if suffix.starts_with('[') || suffix.starts_with('(') {
11942 format!("{prefix} {name}{suffix}")
11943 } else {
11944 format!("{prefix} {suffix}{name}")
11945 };
11946 rendered = collapse_cpp_whitespace(&rendered);
11947
11948 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
11949 format!("{rendered} = {initializer};")
11950 } else if declaration_text.ends_with(';') {
11951 format!("{rendered};")
11952 } else {
11953 rendered
11954 }
11955}
11956
11957fn render_recovered_macro_qualified_field_signature(
11958 node: Node<'_>,
11959 declarator: Node<'_>,
11960 source: &str,
11961) -> Option<String> {
11962 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
11963 if !recovered
11964 .iter()
11965 .any(|candidate| same_node(*candidate, declarator))
11966 {
11967 return None;
11968 }
11969 let pseudo_declarator = node.child_by_field_name("declarator")?;
11970 let mut cursor = node.walk();
11971 let clause = node
11972 .named_children(&mut cursor)
11973 .find(|child| child.kind() == "bitfield_clause")?;
11974 let mut cursor = clause.walk();
11975 let error = clause
11976 .named_children(&mut cursor)
11977 .find(|child| child.kind() == "ERROR")?;
11978 let qualified_type =
11979 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
11980 let prefix = cpp_declaration_prefix(node, source);
11981 let name = extract_variable_name(declarator, source)?;
11982 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
11983 let mut rendered = if suffix.is_empty() {
11984 format!("{prefix} {qualified_type} {name}")
11985 } else {
11986 format!("{prefix} {qualified_type} {suffix} {name}")
11987 };
11988 rendered = collapse_cpp_whitespace(&rendered);
11989
11990 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
11991 Some(format!(
11992 "{rendered} = {};",
11993 normalize_cpp_whitespace(node_text(initializer, source))
11994 ))
11995 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
11996 Some(format!("{rendered} = {initializer};"))
11997 } else {
11998 Some(format!("{rendered};"))
11999 }
12000}
12001
12002fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
12003 match node.kind() {
12004 "pointer_expression" => {
12005 let operator = node
12006 .child_by_field_name("operator")
12007 .or_else(|| node.child(0))
12008 .map(|operator| node_text(operator, source))
12009 .unwrap_or("*");
12010 let argument = node
12011 .child_by_field_name("argument")
12012 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
12013 .unwrap_or_default();
12014 format!("{operator}{argument}")
12015 }
12016 "unary_expression" => {
12017 let operator = node
12018 .child_by_field_name("operator")
12019 .or_else(|| node.child(0))
12020 .map(|operator| node_text(operator, source))
12021 .unwrap_or_default();
12022 let argument = node
12023 .child_by_field_name("argument")
12024 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
12025 .unwrap_or_default();
12026 format!("{operator}{argument}")
12027 }
12028 "identifier" | "field_identifier" => String::new(),
12029 _ => cpp_declarator_suffix_without_name(node, source),
12030 }
12031}
12032
12033fn recovered_macro_qualified_field_initializer<'tree>(
12034 clause: Node<'tree>,
12035 declarator: Node<'tree>,
12036) -> Option<Node<'tree>> {
12037 let mut stack = vec![clause];
12038 while let Some(current) = stack.pop() {
12039 if current.kind() == "assignment_expression"
12040 && current
12041 .child_by_field_name("left")
12042 .is_some_and(|left| same_node(left, declarator))
12043 {
12044 return current.child_by_field_name("right");
12045 }
12046 let mut cursor = current.walk();
12047 stack.extend(current.named_children(&mut cursor));
12048 }
12049 None
12050}
12051
12052fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
12053 let text = node_text(node, source);
12054 let mut cursor = node.walk();
12055 let first_declarator = node.named_children(&mut cursor).find(|child| {
12056 matches!(
12057 child.kind(),
12058 "init_declarator"
12059 | "identifier"
12060 | "field_identifier"
12061 | "pointer_declarator"
12062 | "reference_declarator"
12063 | "array_declarator"
12064 | "function_declarator"
12065 )
12066 });
12067 let prefix = if let Some(first_declarator) = first_declarator {
12068 let end = first_declarator
12069 .start_byte()
12070 .saturating_sub(node.start_byte());
12071 let mut prefix = text.get(..end).unwrap_or(text).to_string();
12072 let declarator_suffix = match first_declarator.kind() {
12073 "init_declarator" => first_declarator
12074 .child_by_field_name("declarator")
12075 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
12076 .unwrap_or_default(),
12077 _ => cpp_declarator_suffix_without_name(first_declarator, source),
12078 };
12079 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
12080 prefix.push_str(&declarator_suffix);
12081 }
12082 return collapse_cpp_whitespace(&prefix)
12083 .trim_end_matches(',')
12084 .trim_end_matches(';')
12085 .trim()
12086 .to_string();
12087 } else {
12088 text
12089 };
12090 collapse_cpp_whitespace(prefix)
12091 .trim_end_matches(',')
12092 .trim_end_matches(';')
12093 .trim()
12094 .to_string()
12095}
12096
12097fn cpp_preserved_initializer(
12098 declaration_node: Node<'_>,
12099 declarator: Node<'_>,
12100 source: &str,
12101) -> Option<String> {
12102 let name = extract_variable_name(declarator, source)?;
12103 let mut cursor = declaration_node.walk();
12104 for child in declaration_node.named_children(&mut cursor) {
12105 if child.kind() != "init_declarator" {
12106 continue;
12107 }
12108 let Some(inner) = child.child_by_field_name("declarator") else {
12109 continue;
12110 };
12111 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
12112 continue;
12113 }
12114 let value = child.child_by_field_name("value")?;
12115 let kind = value.kind();
12116 if matches!(
12117 kind,
12118 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
12119 ) {
12120 return Some(normalize_cpp_whitespace(node_text(value, source)));
12121 }
12122 break;
12123 }
12124 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
12125 let pattern = format!(
12126 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
12127 regex::escape(&name)
12128 );
12129 Regex::new(&pattern)
12130 .ok()
12131 .and_then(|regex| regex.captures(&declaration_text))
12132 .and_then(|captures| captures.get(1))
12133 .map(|value| value.as_str().to_string())
12134}
12135
12136fn render_cpp_function_display_signature_from_node<'tree>(
12137 node: Node<'tree>,
12138 source: &str,
12139 template_signature: Option<&str>,
12140 has_body: bool,
12141 ancestry: &ParentIndex<'tree>,
12142) -> String {
12143 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
12144 let parent_text = node_text(root, source);
12145 let body_local_start = root
12146 .child_by_field_name("body")
12147 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
12148 .unwrap_or(parent_text.len());
12149 let display = parent_text
12150 .get(..body_local_start)
12151 .unwrap_or(parent_text)
12152 .trim()
12153 .trim();
12154 let display = if let Some(template_signature) = template_signature {
12155 if display.starts_with("template ") {
12156 display.to_string()
12157 } else {
12158 format!("template {template_signature} {display}")
12159 }
12160 } else {
12161 display.to_string()
12162 };
12163 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
12164 if has_body {
12165 format!("{display} {{...}}")
12166 } else {
12167 format!("{display};")
12168 }
12169}
12170
12171fn cpp_template_signature(
12172 template_node: Node<'_>,
12173 declaration_child: Node<'_>,
12174 source: &str,
12175) -> Option<String> {
12176 let text = source
12177 .get(template_node.start_byte()..declaration_child.start_byte())
12178 .unwrap_or("");
12179 let text = normalize_cpp_whitespace(text);
12180 let start = text.find('<')?;
12181 let end = text.rfind('>')?;
12182 if end < start {
12183 return None;
12184 }
12185 Some(text[start..=end].to_string())
12186}
12187
12188struct RecoveredFragmentedPartialSpecialization<'tree> {
12189 declaration_node: Node<'tree>,
12190 name: String,
12191 range: Range,
12192 prefix_members: Vec<Node<'tree>>,
12193 member_siblings: Vec<Node<'tree>>,
12194 following_declarations: Vec<Node<'tree>>,
12195}
12196
12197struct RecoveredFragmentedPreprocessorClass<'tree> {
12198 declaration_node: Node<'tree>,
12199 class_node: Node<'tree>,
12200 body: Node<'tree>,
12201 name: String,
12202 range: Range,
12203 tail_members: Vec<Node<'tree>>,
12204 member_siblings: Vec<Node<'tree>>,
12205}
12206
12207fn recover_fragmented_preprocessor_class<'tree>(
12216 template_node: Node<'tree>,
12217 source: &str,
12218 ancestry: &ParentIndex<'tree>,
12219) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
12220 let alternative = ancestry.parent(template_node)?;
12221 if alternative.kind() != "preproc_else" {
12222 return None;
12223 }
12224 let conditional = alternative.parent()?;
12225 if conditional.kind() != "preproc_if" {
12226 return None;
12227 }
12228 let declaration_node = template_node
12229 .named_children(&mut template_node.walk())
12230 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
12231 let class_node = declaration_node
12232 .named_children(&mut declaration_node.walk())
12233 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
12234 let body = cpp_body_node(class_node)?;
12235 if class_node.end_byte() >= declaration_node.end_byte() {
12236 return None;
12237 }
12238 let name = class_like_name(class_node, source, ancestry)?;
12239 let is_partial_specialization = class_node
12240 .child_by_field_name("name")
12241 .is_some_and(|class_name| class_name.kind() == "template_type");
12242 if is_partial_specialization {
12243 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
12244 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
12245 return None;
12246 }
12247 } else {
12248 if !class_has_displaced_preprocessor_terminator(class_node) {
12249 return None;
12250 }
12251 let matching_other_branch = conditional
12252 .named_children(&mut conditional.walk())
12253 .take_while(|child| !same_node(*child, alternative))
12254 .filter(|child| child.kind() == "template_declaration")
12255 .filter_map(first_class_like_child)
12256 .any(|candidate| {
12257 cpp_body_node(candidate).is_none()
12258 && class_like_name(candidate, source, ancestry).as_deref()
12259 == Some(name.as_str())
12260 });
12261 if !matching_other_branch {
12262 return None;
12263 }
12264 }
12265
12266 let mut tail_members = Vec::new();
12267 let mut saw_class = false;
12268 let mut declaration_cursor = declaration_node.walk();
12269 for child in declaration_node.named_children(&mut declaration_cursor) {
12270 if same_node(child, class_node) {
12271 saw_class = true;
12272 } else if saw_class {
12273 tail_members.push(child);
12274 }
12275 }
12276
12277 let mut member_siblings = Vec::new();
12278 let mut saw_template = false;
12279 let mut terminator = None;
12280 for index in 0..alternative.child_count() {
12281 let Some(child) = alternative.child(index) else {
12282 continue;
12283 };
12284 if same_node(child, template_node) {
12285 saw_template = true;
12286 continue;
12287 }
12288 if !saw_template {
12289 continue;
12290 }
12291 if displaced_fragmented_class_terminator(alternative, index) {
12292 terminator = alternative.child(index + 1);
12293 break;
12294 }
12295 if child.is_named() {
12296 member_siblings.push(child);
12297 }
12298 }
12299 let terminator = terminator?;
12300 Some(RecoveredFragmentedPreprocessorClass {
12301 declaration_node,
12302 class_node,
12303 body,
12304 name,
12305 range: Range {
12306 start_byte: class_node.start_byte(),
12307 end_byte: terminator.end_byte(),
12308 start_line: class_node.start_position().row + 1,
12309 end_line: terminator.end_position().row + 1,
12310 },
12311 tail_members,
12312 member_siblings,
12313 })
12314}
12315
12316fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
12317 (0..class_node.child_count()).any(|index| {
12318 class_node.child(index).is_some_and(|child| {
12319 child.kind() == "ERROR"
12320 && (0..child.child_count()).any(|error_index| {
12321 child
12322 .child(error_index)
12323 .is_some_and(|token| token.kind() == "#endif")
12324 })
12325 })
12326 })
12327}
12328
12329pub fn cpp_displaced_preprocessor_terminator<'tree>(
12338 conditional: Node<'tree>,
12339) -> Option<Node<'tree>> {
12340 if !conditional.has_error() {
12341 return None;
12342 }
12343 let has_concrete_direct_terminator = conditional
12344 .child_count()
12345 .checked_sub(1)
12346 .and_then(|index| conditional.child(index))
12347 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
12348 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
12349 return None;
12353 }
12354 let mut displaced = None;
12360 let mut conditional_depth = 0usize;
12361 let mut stack = children_iter(conditional)
12362 .map(|child| (child, false))
12363 .collect::<Vec<_>>();
12364 stack.reverse();
12365 while let Some((node, inside_error)) = stack.pop() {
12366 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
12367 continue;
12368 }
12369 if node != conditional
12370 && matches!(
12371 node.kind(),
12372 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
12373 )
12374 {
12375 continue;
12376 }
12377 let inside_error = inside_error || node.kind() == "ERROR";
12378 match node.kind() {
12379 "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
12380 conditional_depth += 1;
12381 }
12382 "#endif" if !node.is_missing() => {
12383 if conditional_depth == 0
12384 && inside_error
12385 && displaced
12386 .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
12387 {
12388 displaced = Some(node);
12389 }
12390 conditional_depth = conditional_depth.saturating_sub(1);
12391 }
12392 _ => {}
12393 }
12394 let first_pushed = stack.len();
12395 stack.extend(children_iter(node).map(|child| (child, inside_error)));
12396 stack[first_pushed..].reverse();
12397 }
12398 displaced
12399}
12400
12401#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12414pub struct CppDisplacedPreprocessorBoundary {
12415 pub end_byte: usize,
12416 pub end_line: usize,
12417}
12418
12419pub fn cpp_displaced_preprocessor_boundary(
12420 conditional: Node<'_>,
12421) -> Option<CppDisplacedPreprocessorBoundary> {
12422 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
12423 return Some(CppDisplacedPreprocessorBoundary {
12424 end_byte: terminator.end_byte(),
12425 end_line: terminator.end_position().row + 1,
12426 });
12427 }
12428 if let Some(declaration) = displaced_split_declaration(conditional) {
12429 return Some(CppDisplacedPreprocessorBoundary {
12430 end_byte: declaration.end_byte(),
12431 end_line: declaration.end_position().row + 1,
12432 });
12433 }
12434 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
12435 return Some(CppDisplacedPreprocessorBoundary {
12436 end_byte: terminator.end_byte(),
12437 end_line: terminator.end_position().row + 1,
12438 });
12439 }
12440 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
12441 return Some(CppDisplacedPreprocessorBoundary {
12442 end_byte: terminator.end_byte(),
12443 end_line: terminator.end_position().row + 1,
12444 });
12445 }
12446 None
12447}
12448
12449fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
12455 if !conditional.has_error()
12456 || conditional.child_by_field_name("alternative").is_some()
12457 || conditional
12458 .child(conditional.child_count().saturating_sub(1))
12459 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
12460 {
12461 return None;
12462 }
12463 let mut recovered = None;
12464 for nested in named_children_iter(conditional) {
12465 if !matches!(
12466 nested.kind(),
12467 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
12468 ) || nested.child_by_field_name("alternative").is_some()
12469 {
12470 continue;
12471 }
12472 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
12473 continue;
12474 };
12475 if direct.kind() != "#endif" || direct.is_missing() {
12476 continue;
12477 }
12478 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
12479 continue;
12480 };
12481 if displaced.end_byte() >= direct.start_byte() {
12482 continue;
12483 }
12484 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
12485 recovered = Some(direct);
12486 }
12487 }
12488 recovered
12489}
12490
12491fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
12492 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
12493 return None;
12494 }
12495 let mut cursor = conditional.walk();
12496 let declarations = conditional
12497 .named_children(&mut cursor)
12498 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
12499 .collect::<Vec<_>>();
12500 let declaration = *declarations.first()?;
12501 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
12502 return None;
12503 }
12504 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
12505 let mut terminator = None;
12506 let mut stack = (0..declaration.child_count())
12507 .filter_map(|index| declaration.child(index))
12508 .filter(|child| child.start_byte() < declarator_start)
12509 .map(|child| (child, false))
12510 .collect::<Vec<_>>();
12511 while let Some((node, inside_error)) = stack.pop() {
12512 let inside_error = inside_error || node.kind() == "ERROR";
12513 if inside_error && node.kind() == "#endif" && !node.is_missing() {
12514 terminator = Some(node);
12515 continue;
12516 }
12517 for index in 0..node.child_count() {
12518 if let Some(child) = node.child(index)
12519 && child.start_byte() < declarator_start
12520 {
12521 stack.push((child, inside_error));
12522 }
12523 }
12524 }
12525 terminator
12526}
12527
12528fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
12529 if !conditional.has_error()
12530 || conditional.child_by_field_name("alternative").is_some()
12531 || conditional
12532 .prev_named_sibling()
12533 .filter(|sibling| {
12534 sibling.kind() == "ERROR"
12535 && sibling.child_count() == 1
12536 && sibling
12537 .child(0)
12538 .is_some_and(|child| child.kind() == "typedef")
12539 })
12540 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
12541 .is_none()
12542 {
12543 return None;
12544 }
12545 let mut cursor = conditional.walk();
12546 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
12547 let declaration_index = children
12548 .iter()
12549 .position(|child| child.kind() == "declaration" && child.has_error())?;
12550 let declaration = children[declaration_index];
12551 if !children
12552 .iter()
12553 .skip(declaration_index + 1)
12554 .any(|child| child.end_byte() > declaration.end_byte())
12555 {
12556 return None;
12557 }
12558 let declarator = declaration.child_by_field_name("declarator")?;
12559 let mut error_end = None;
12560 let mut names = Vec::new();
12561 let mut stack = vec![declarator];
12562 while let Some(node) = stack.pop() {
12563 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
12564 error_end =
12565 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
12566 continue;
12567 }
12568 if matches!(node.kind(), "identifier" | "type_identifier") {
12569 names.push(node.start_byte());
12570 }
12571 push_named_children_reversed(node, &mut stack);
12572 }
12573 let error_end = error_end?;
12574 names
12575 .into_iter()
12576 .any(|start| start >= error_end)
12577 .then_some(declaration)
12578}
12579
12580fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
12581 let Some(error) = parent.child(error_index) else {
12582 return false;
12583 };
12584 if error.kind() != "ERROR"
12585 || error.child_count() != 1
12586 || error.child(0).is_none_or(|child| child.kind() != "}")
12587 {
12588 return false;
12589 }
12590 let Some(semicolon) = parent.child(error_index + 1) else {
12591 return false;
12592 };
12593 semicolon.kind() == "expression_statement"
12594 && semicolon.child_count() == 1
12595 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
12596}
12597
12598fn displaced_macro_class_tail(
12604 declaration_node: Node<'_>,
12605 body: Node<'_>,
12606 source: &str,
12607) -> Option<DisplacedMacroClassTail> {
12608 if !matches!(
12609 declaration_node.kind(),
12610 "class_specifier" | "struct_specifier" | "union_specifier"
12611 ) || body.kind() != "field_declaration_list"
12612 {
12613 return None;
12614 }
12615
12616 let child_count = body.named_child_count();
12617 for index in 0..child_count {
12618 let child = body.named_child(index)?;
12619 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
12620 continue;
12621 };
12622 let split_index = index + 1;
12623 if split_index >= child_count {
12624 return None;
12625 }
12626 let mut cursor = body.walk();
12627 if !body
12628 .named_children(&mut cursor)
12629 .skip(split_index)
12630 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
12631 {
12632 return None;
12633 }
12634 return Some(DisplacedMacroClassTail {
12635 split_index,
12636 class_range: Range {
12637 start_byte: declaration_node.start_byte(),
12638 end_byte: terminator.end_byte(),
12639 start_line: declaration_node.start_position().row + 1,
12640 end_line: terminator.end_position().row + 1,
12641 },
12642 });
12643 }
12644 None
12645}
12646
12647fn displaced_macro_field_terminator<'tree>(
12648 field: Node<'tree>,
12649 source: &str,
12650) -> Option<Node<'tree>> {
12651 if field.kind() != "field_declaration" {
12652 return None;
12653 }
12654 let macro_type = field.child_by_field_name("type")?;
12655 if macro_type.kind() != "type_identifier"
12656 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12657 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
12658 {
12659 return None;
12660 }
12661 for index in 0..field.child_count() {
12662 let error = field.child(index)?;
12663 if error.kind() != "ERROR"
12664 || error.child_count() != 1
12665 || error.child(0).is_none_or(|child| child.kind() != "}")
12666 {
12667 continue;
12668 }
12669 let semicolon = field.child(index + 1)?;
12670 if semicolon.kind() == ";" {
12671 return Some(semicolon);
12672 }
12673 }
12674 None
12675}
12676
12677fn recover_fragmented_partial_specialization<'tree>(
12678 template_node: Node<'tree>,
12679 declaration_child: Node<'tree>,
12680 source: &str,
12681 ancestry: &ParentIndex<'tree>,
12682) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
12683 if declaration_child.kind() != "function_definition" {
12684 return None;
12685 }
12686 let class_node = declaration_child.child_by_field_name("type")?;
12687 if !matches!(
12688 class_node.kind(),
12689 "class_specifier" | "struct_specifier" | "union_specifier"
12690 ) || !class_node
12691 .child_by_field_name("name")
12692 .and_then(|name| direct_identifier_name(name, source))
12693 .is_some_and(|name| cpp_export_macro_token(&name))
12694 {
12695 return None;
12696 }
12697 let declarator = declaration_child.child_by_field_name("declarator")?;
12698 if declarator.kind() != "template_function" {
12699 return None;
12700 }
12701 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
12702 if metadata.specialization_arguments.is_empty() {
12703 return None;
12704 }
12705 let body = declaration_child.child_by_field_name("body")?;
12706 if body.kind() != "compound_statement" {
12707 return None;
12708 }
12709 let complete_prefix = body.named_child(0).filter(|first| {
12710 first.kind() == "labeled_statement"
12711 && first.has_error()
12712 && first
12713 .named_child(first.named_child_count().saturating_sub(1))
12714 .is_some_and(recovered_declaration_has_class_terminator)
12715 });
12716 let complete_body = complete_prefix.is_some();
12717 let mut prefix_members = Vec::new();
12718 if let Some(prefix) = complete_prefix {
12719 prefix_members.push(prefix);
12720 } else {
12721 let mut body_cursor = body.walk();
12722 for child in body.named_children(&mut body_cursor) {
12723 if !is_structurally_valid_fragmented_class_prefix_member(child) {
12724 break;
12725 }
12726 prefix_members.push(child);
12727 }
12728 }
12729 let containing_declarations = template_node.parent()?;
12730 if !matches!(
12731 containing_declarations.kind(),
12732 "declaration_list" | "compound_statement"
12733 ) {
12734 return None;
12735 }
12736 let mut member_siblings = Vec::new();
12737 let mut following_declarations = Vec::new();
12738 let terminator;
12739 if complete_body {
12740 terminator = complete_prefix?;
12741 let mut cursor = body.walk();
12742 let mut after_prefix = false;
12743 for child in body.named_children(&mut cursor) {
12744 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
12745 after_prefix = true;
12746 } else if after_prefix {
12747 following_declarations.push(child);
12748 }
12749 }
12750 } else {
12751 let mut found_template = false;
12752 let mut cursor = containing_declarations.walk();
12753 let mut class_terminator = None;
12754 for child in containing_declarations.children(&mut cursor) {
12755 if same_node(child, template_node) {
12756 found_template = true;
12757 continue;
12758 }
12759 if found_template && child.kind() == "}" {
12760 class_terminator = Some(child);
12761 break;
12762 }
12763 if found_template && child.kind() == "namespace_definition" {
12770 return None;
12771 }
12772 if found_template && child.is_named() {
12773 member_siblings.push(child);
12774 }
12775 }
12776 terminator = class_terminator?;
12777 }
12778 let name = format!(
12779 "{}<{}>",
12780 metadata.primary_name,
12781 metadata
12782 .specialization_arguments
12783 .iter()
12784 .map(|argument| argument.text.as_str())
12785 .collect::<Vec<_>>()
12786 .join(", ")
12787 );
12788 Some(RecoveredFragmentedPartialSpecialization {
12789 declaration_node: declaration_child,
12790 name,
12791 range: Range {
12792 start_byte: declaration_child.start_byte(),
12793 end_byte: terminator.end_byte(),
12794 start_line: declaration_child.start_position().row + 1,
12795 end_line: terminator.end_position().row + 1,
12796 },
12797 prefix_members,
12798 member_siblings,
12799 following_declarations,
12800 })
12801}
12802
12803pub fn is_recovered_fragmented_partial_specialization_container(
12810 node: Node<'_>,
12811 source: &str,
12812) -> bool {
12813 let Some(template) = node
12814 .parent()
12815 .filter(|parent| parent.kind() == "template_declaration")
12816 else {
12817 return false;
12818 };
12819 let mut root = template;
12820 while let Some(parent) = root.parent() {
12821 root = parent;
12822 }
12823 recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
12824 .is_some()
12825}
12826
12827fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
12828 if declaration.kind() != "declaration" {
12829 return false;
12830 }
12831 (0..declaration.child_count().saturating_sub(1)).any(|index| {
12836 let Some(error) = declaration.child(index) else {
12837 return false;
12838 };
12839 error.kind() == "ERROR"
12840 && error.child_count() == 1
12841 && error.child(0).is_some_and(|child| child.kind() == "}")
12842 && declaration
12843 .child(index + 1)
12844 .is_some_and(|child| child.kind() == ";")
12845 })
12846}
12847
12848fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
12849 if node.has_error() {
12850 return false;
12851 }
12852 match node.kind() {
12853 "declaration"
12854 | "field_declaration"
12855 | "alias_declaration"
12856 | "type_definition"
12857 | "static_assert_declaration" => true,
12858 "labeled_statement" => node
12859 .named_child(node.named_child_count().saturating_sub(1))
12860 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
12861 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
12862 matches!(
12863 child.kind(),
12864 "declaration"
12865 | "field_declaration"
12866 | "alias_declaration"
12867 | "type_definition"
12868 | "function_definition"
12869 )
12870 }),
12871 _ => false,
12872 }
12873}
12874
12875fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
12876 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
12877 .then(|| node.child_by_field_name("declarator"))
12878 .flatten()
12879 .and_then(|declarator| extract_variable_name(declarator, source))
12880}
12881
12882fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
12883 while let Some(parent) = node.parent() {
12884 if matches!(parent.kind(), "function_definition" | "lambda_expression") {
12885 return true;
12886 }
12887 node = parent;
12888 }
12889 false
12890}
12891
12892fn cpp_template_metadata<'tree>(
12893 template_node: Node<'tree>,
12894 declaration_child: Node<'tree>,
12895 source: &str,
12896 ancestry: &ParentIndex<'tree>,
12897) -> Option<CppTemplateMetadata> {
12898 let parameters_node = template_node.child_by_field_name("parameters")?;
12899 let name_node = cpp_templated_class_name_node(declaration_child)?;
12900 let primary_node = match name_node.kind() {
12901 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
12902 _ => name_node,
12903 };
12904 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
12905 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
12906 return None;
12907 }
12908
12909 let mut parameter_nodes = Vec::new();
12910 let mut parameter_names = Vec::new();
12911 let mut cursor = parameters_node.walk();
12912 for parameter in parameters_node.named_children(&mut cursor) {
12913 if !matches!(
12914 parameter.kind(),
12915 "type_parameter_declaration"
12916 | "optional_type_parameter_declaration"
12917 | "variadic_type_parameter_declaration"
12918 | "template_template_parameter_declaration"
12919 | "parameter_declaration"
12920 | "optional_parameter_declaration"
12921 | "variadic_parameter_declaration"
12922 ) {
12923 continue;
12924 }
12925 let index = parameter_nodes.len();
12926 let name = cpp_template_parameter_name(parameter, source)
12931 .unwrap_or_else(|| format!("<anonymous:{index}>"));
12932 parameter_names.push(name);
12933 parameter_nodes.push(parameter);
12934 }
12935 let parameters = parameter_nodes
12936 .into_iter()
12937 .zip(parameter_names.iter().cloned())
12938 .map(|(parameter, name)| CppTemplateParameterMetadata {
12939 name,
12940 kind: cpp_template_parameter_kind(parameter),
12941 variadic: matches!(
12942 parameter.kind(),
12943 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
12944 ),
12945 default: cpp_template_parameter_default_expression(
12946 parameter,
12947 source,
12948 ¶meter_names,
12949 ancestry,
12950 ),
12951 })
12952 .collect();
12953 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
12954 Vec::new()
12955 } else {
12956 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
12957 .unwrap_or_default()
12958 };
12959 let alias_target = (declaration_child.kind() == "alias_declaration")
12960 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
12961 .flatten();
12962 Some(CppTemplateMetadata {
12963 primary_name,
12964 primary_fq_name: String::new(),
12965 parameters,
12966 specialization_arguments,
12967 alias_target,
12968 })
12969}
12970
12971fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
12972 match node.kind() {
12973 "class_specifier" | "struct_specifier" | "union_specifier" => {
12974 node.child_by_field_name("name")
12975 }
12976 "function_definition" => {
12977 let declarator = node.child_by_field_name("declarator")?;
12978 if matches!(declarator.kind(), "identifier" | "template_function") {
12979 Some(declarator)
12980 } else {
12981 None
12982 }
12983 }
12984 "alias_declaration" => node.child_by_field_name("name"),
12985 _ => None,
12986 }
12987}
12988
12989fn cpp_template_alias_target<'tree>(
12990 alias: Node<'tree>,
12991 source: &str,
12992 parameter_names: &[String],
12993 ancestry: &ParentIndex<'tree>,
12994) -> Option<CppTemplateAliasTargetMetadata> {
12995 let mut type_node = alias.child_by_field_name("type")?;
12996 while type_node.kind() == "type_descriptor" {
12997 type_node = type_node.child_by_field_name("type")?;
12998 }
12999 let global = type_node.child_by_field_name("scope").is_none()
13000 && type_node.child(0).is_some_and(|child| child.kind() == "::");
13001 let mut components = Vec::new();
13002 cpp_template_target_components(type_node, source, &mut components)?;
13003 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
13004 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
13005 components,
13006 global,
13007 arguments,
13008 })
13009}
13010
13011fn cpp_template_target_components(
13012 node: Node<'_>,
13013 source: &str,
13014 out: &mut Vec<String>,
13015) -> Option<()> {
13016 match node.kind() {
13017 "identifier" | "namespace_identifier" | "type_identifier" => {
13018 out.push(node_text(node, source).to_string());
13019 Some(())
13020 }
13021 "template_type" => {
13022 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
13023 }
13024 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
13025 if let Some(scope) = node.child_by_field_name("scope") {
13026 cpp_template_target_components(scope, source, out)?;
13027 }
13028 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
13029 }
13030 _ => None,
13031 }
13032}
13033
13034fn cpp_template_argument_expressions<'tree>(
13035 mut node: Node<'tree>,
13036 source: &str,
13037 parameter_names: &[String],
13038 ancestry: &ParentIndex<'tree>,
13039) -> Option<Vec<CppTemplateExpression>> {
13040 loop {
13041 match node.kind() {
13042 "template_type" | "template_function" => {
13043 let arguments = node.child_by_field_name("arguments")?;
13044 let mut cursor = arguments.walk();
13045 return Some(
13046 arguments
13047 .named_children(&mut cursor)
13048 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
13049 .map(|argument| {
13050 cpp_template_expression(argument, source, parameter_names, ancestry)
13051 })
13052 .collect(),
13053 );
13054 }
13055 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
13056 node = node
13057 .child_by_field_name("name")
13058 .or_else(|| node.child_by_field_name("type"))?;
13059 }
13060 _ => return None,
13061 }
13062 }
13063}
13064
13065fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
13066 let candidate = node
13067 .child_by_field_name("name")
13068 .or_else(|| node.child_by_field_name("declarator"))
13069 .or_else(|| {
13070 let mut cursor = node.walk();
13071 node.named_children(&mut cursor).find(|child| {
13072 matches!(
13073 child.kind(),
13074 "identifier" | "type_identifier" | "field_identifier"
13075 )
13076 })
13077 })?;
13078 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
13079 (!name.is_empty()).then_some(name)
13080}
13081
13082fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
13083 match node.kind() {
13084 "type_parameter_declaration"
13085 | "optional_type_parameter_declaration"
13086 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
13087 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
13088 _ => CppTemplateParameterKind::Value,
13089 }
13090}
13091
13092fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
13093 node.child_by_field_name("default_type")
13094 .or_else(|| node.child_by_field_name("default_value"))
13095}
13096
13097fn cpp_template_parameter_default_expression<'tree>(
13098 parameter: Node<'tree>,
13099 source: &str,
13100 parameter_names: &[String],
13101 ancestry: &ParentIndex<'tree>,
13102) -> Option<CppTemplateExpression> {
13103 let default = cpp_template_parameter_default(parameter)?;
13104 let base = cpp_template_expression(default, source, parameter_names, ancestry);
13105 let Some(pointer_error) = parameter.next_named_sibling() else {
13106 return Some(base);
13107 };
13108 let Some(pointer_declarator) =
13109 recovered_abstract_pointer_declarator_term(pointer_error, source)
13110 else {
13111 return Some(base);
13112 };
13113 Some(CppTemplateExpression {
13114 text: format!(
13115 "{}{}",
13116 base.text,
13117 normalize_cpp_whitespace(node_text(pointer_error, source))
13118 ),
13119 term: CppTemplateTerm::Node {
13120 kind: "type_descriptor".to_string(),
13121 children: vec![base.term, pointer_declarator],
13122 },
13123 })
13124}
13125
13126fn recovered_abstract_pointer_declarator_term(
13127 node: Node<'_>,
13128 source: &str,
13129) -> Option<CppTemplateTerm> {
13130 if node.kind() != "ERROR" || node.child_count() == 0 {
13131 return None;
13132 }
13133 let mut children = Vec::new();
13134 for index in 0..node.child_count() {
13135 let child = node.child(index)?;
13136 if child.kind() != "*" {
13137 return None;
13138 }
13139 children.push(CppTemplateTerm::Atom {
13140 kind: "*".to_string(),
13141 text: normalize_cpp_whitespace(node_text(child, source)),
13142 });
13143 }
13144 Some(CppTemplateTerm::Node {
13145 kind: "abstract_pointer_declarator".to_string(),
13146 children,
13147 })
13148}
13149
13150fn cpp_template_expression<'tree>(
13151 node: Node<'tree>,
13152 source: &str,
13153 parameter_names: &[String],
13154 ancestry: &ParentIndex<'tree>,
13155) -> CppTemplateExpression {
13156 let text = normalize_cpp_whitespace(node_text(node, source));
13157 CppTemplateExpression {
13158 text,
13159 term: cpp_template_term(node, source, parameter_names, ancestry),
13160 }
13161}
13162
13163pub fn cpp_template_term<'tree>(
13164 node: Node<'tree>,
13165 source: &str,
13166 parameter_names: &[String],
13167 ancestry: &ParentIndex<'tree>,
13168) -> CppTemplateTerm {
13169 enum Work<'tree> {
13170 Visit(Node<'tree>),
13171 Build { kind: String, child_count: usize },
13172 }
13173
13174 let mut work = vec![Work::Visit(node)];
13175 let mut terms = Vec::new();
13176 while let Some(next) = work.pop() {
13177 match next {
13178 Work::Visit(current) => {
13179 let text = normalize_cpp_whitespace(node_text(current, source));
13180 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
13181 terms.push(CppTemplateTerm::Parameter(text));
13182 continue;
13183 }
13184 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
13185 let mut cursor = current.walk();
13186 let named = current
13187 .named_children(&mut cursor)
13188 .filter(|child| !child.is_extra() && child.kind() != "comment")
13189 .collect::<Vec<_>>();
13190 if let [child] = named.as_slice() {
13191 work.push(Work::Visit(*child));
13192 continue;
13193 }
13194 }
13195 if current.child_count() == 0 {
13196 terms.push(CppTemplateTerm::Atom {
13197 kind: if matches!(
13198 current.kind(),
13199 "identifier"
13200 | "type_identifier"
13201 | "field_identifier"
13202 | "namespace_identifier"
13203 ) {
13204 "identifier".to_string()
13205 } else {
13206 current.kind().to_string()
13207 },
13208 text,
13209 });
13210 continue;
13211 }
13212 let children = (0..current.child_count())
13213 .filter_map(|index| current.child(index))
13214 .filter(|child| !child.is_extra() && child.kind() != "comment")
13215 .collect::<Vec<_>>();
13216 work.push(Work::Build {
13217 kind: current.kind().to_string(),
13218 child_count: children.len(),
13219 });
13220 work.extend(children.into_iter().rev().map(Work::Visit));
13221 }
13222 Work::Build { kind, child_count } => {
13223 let children = terms.split_off(terms.len() - child_count);
13224 terms.push(CppTemplateTerm::Node { kind, children });
13225 }
13226 }
13227 }
13228 terms.pop().expect("template term traversal emits one root")
13229}
13230
13231fn cpp_template_term_leaf_is_parameter<'tree>(
13232 node: Node<'tree>,
13233 text: &str,
13234 parameter_names: &[String],
13235 ancestry: &ParentIndex<'tree>,
13236) -> bool {
13237 if !parameter_names.iter().any(|parameter| parameter == text) {
13238 return false;
13239 }
13240 !ancestry.parent(node).is_some_and(|parent| {
13241 matches!(
13242 parent.kind(),
13243 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
13244 ) && parent.child_by_field_name("scope").is_some()
13245 && parent.child_by_field_name("name") == Some(node)
13246 })
13247}
13248
13249fn enclosing_cpp_declaration_node<'tree>(
13250 mut node: Node<'tree>,
13251 ancestry: &ParentIndex<'tree>,
13252) -> Option<Node<'tree>> {
13253 loop {
13254 match node.kind() {
13255 "declaration"
13256 | "function_declaration"
13257 | "field_declaration"
13258 | "function_definition" => return Some(node),
13259 _ => node = ancestry.parent(node)?,
13260 }
13261 }
13262}
13263
13264fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
13265 let mut params = Vec::new();
13266 let mut cursor = parameters_node.walk();
13267 for child in parameters_node.children(&mut cursor) {
13268 match child.kind() {
13269 "parameter_declaration" | "optional_parameter_declaration" => {
13270 params.push(cpp_parameter_type(child, source));
13271 }
13272 "variadic_parameter_declaration" => {
13273 params.push(cpp_parameter_type(child, source));
13274 }
13275 "variadic_parameter" | "..." => params.push("...".to_string()),
13276 _ => {}
13277 }
13278 }
13279
13280 if params.is_empty() {
13281 "()".to_string()
13282 } else {
13283 format!("({})", params.join(", "))
13284 }
13285}
13286
13287enum CppCallableScope<'tree> {
13290 ClassBody {
13294 body: Node<'tree>,
13295 default_visibility: DeclaredVisibility,
13296 },
13297 Qualified,
13303 Unqualified,
13306}
13307
13308fn cpp_callable_scope<'tree>(
13324 declaration: Node<'tree>,
13325 function_declarator: Option<Node<'tree>>,
13326 ancestry: &ParentIndex<'tree>,
13327) -> CppCallableScope<'tree> {
13328 let mut current = Some(declaration);
13329 while let Some(node) = current {
13330 match node.kind() {
13331 "field_declaration_list" => {
13332 let default_visibility =
13333 ancestry
13334 .parent(node)
13335 .map_or(DeclaredVisibility::Unknown, |class_like| {
13336 match class_like.kind() {
13339 "struct_specifier" | "union_specifier" => {
13340 DeclaredVisibility::Public
13341 }
13342 _ => DeclaredVisibility::Private,
13343 }
13344 });
13345 return CppCallableScope::ClassBody {
13346 body: node,
13347 default_visibility,
13348 };
13349 }
13350 "namespace_definition" | "compound_statement" | "translation_unit" => break,
13351 _ => {}
13352 }
13353 current = ancestry.parent(node);
13354 }
13355 if cpp_callable_declarator_is_qualified(function_declarator) {
13356 CppCallableScope::Qualified
13357 } else {
13358 CppCallableScope::Unqualified
13359 }
13360}
13361
13362fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
13369 function_declarator
13370 .and_then(cpp_function_declarator_name_node)
13371 .is_some_and(|name| name.kind() == "qualified_identifier")
13372}
13373
13374fn cpp_member_declared_visibility<'tree>(
13386 declaration: Node<'tree>,
13387 body: Node<'tree>,
13388 default_visibility: DeclaredVisibility,
13389 ancestry: &ParentIndex<'tree>,
13390) -> DeclaredVisibility {
13391 let mut list_level = declaration;
13392 while ancestry.parent(list_level) != Some(body) {
13393 let Some(parent) = ancestry.parent(list_level) else {
13394 return default_visibility;
13395 };
13396 list_level = parent;
13397 }
13398 let mut visibility = default_visibility;
13399 let mut cursor = body.walk();
13400 for child in body.children(&mut cursor) {
13401 if child == list_level {
13402 break;
13403 }
13404 if child.kind() == "access_specifier" {
13405 visibility = match child.child(0).map(|keyword| keyword.kind()) {
13406 Some("public") => DeclaredVisibility::Public,
13407 Some("protected") => DeclaredVisibility::Protected,
13408 Some("private") => DeclaredVisibility::Private,
13409 _ => visibility,
13410 };
13411 }
13412 }
13413 visibility
13414}
13415
13416fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
13423 let mut cursor = declaration.walk();
13424 declaration.named_children(&mut cursor).any(|child| {
13425 child.kind() == "storage_class_specifier"
13426 && normalize_cpp_whitespace(node_text(child, source)) == "static"
13427 })
13428}
13429
13430struct CppCallableModifiers {
13433 is_static: bool,
13434 is_constructor: bool,
13435 visibility: DeclaredVisibility,
13436}
13437
13438fn cpp_callable_modifiers<'tree>(
13462 declaration: Node<'tree>,
13463 function_declarator: Option<Node<'tree>>,
13464 is_constructor: bool,
13465 source: &str,
13466 ancestry: &ParentIndex<'tree>,
13467) -> CppCallableModifiers {
13468 match cpp_callable_scope(declaration, function_declarator, ancestry) {
13469 CppCallableScope::ClassBody {
13470 body,
13471 default_visibility,
13472 } => CppCallableModifiers {
13473 is_static: cpp_declares_static_storage_class(declaration, source),
13474 is_constructor,
13475 visibility: cpp_member_declared_visibility(
13476 declaration,
13477 body,
13478 default_visibility,
13479 ancestry,
13480 ),
13481 },
13482 CppCallableScope::Qualified => CppCallableModifiers {
13483 is_static: false,
13484 is_constructor,
13485 visibility: DeclaredVisibility::Unknown,
13486 },
13487 CppCallableScope::Unqualified => CppCallableModifiers {
13488 is_static: false,
13489 is_constructor,
13490 visibility: DeclaredVisibility::Unknown,
13491 },
13492 }
13493}
13494
13495fn cpp_signature_metadata<'tree>(
13504 signature: String,
13505 function_declarator: Node<'tree>,
13506 linkage: CallableLinkage,
13507 source: &str,
13508 ancestry: &ParentIndex<'tree>,
13509) -> SignatureMetadata {
13510 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
13511 let is_constructor =
13512 cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
13513 let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
13514 Some(declaration) => cpp_callable_modifiers(
13515 declaration,
13516 Some(function_declarator),
13517 is_constructor,
13518 source,
13519 ancestry,
13520 ),
13521 None => CppCallableModifiers {
13524 is_static: false,
13525 is_constructor,
13526 visibility: DeclaredVisibility::Unknown,
13527 },
13528 };
13529 let enrich = |mut metadata: SignatureMetadata| {
13530 if let Some(dispatch) = dispatch {
13533 metadata = metadata.with_dispatch_extensibility(dispatch);
13534 }
13535 metadata
13536 .with_callable_linkage(linkage)
13537 .with_callable_modifiers(
13538 modifiers.is_static,
13539 modifiers.is_constructor,
13540 modifiers.visibility,
13541 )
13542 };
13543 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
13544 let return_type_identity =
13545 cpp_callable_return_type_identity(function_declarator, source, ancestry);
13546 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
13547 return enrich(
13548 SignatureMetadata::new(signature, Vec::new())
13549 .with_return_type_text(return_type_text)
13550 .with_return_type_identity(return_type_identity),
13551 );
13552 };
13553 let callable_arity = cpp_callable_arity(parameters_node, source);
13554 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
13555 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
13556 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
13557 let Some(relative_start) = signature
13558 .get(search_from..)
13559 .and_then(|suffix| suffix.find(¶meter_text))
13560 else {
13561 return enrich(
13562 SignatureMetadata::new(signature, Vec::new())
13563 .with_callable_arity(callable_arity)
13564 .with_callable_parameter_types(callable_parameter_types)
13565 .with_return_type_text(return_type_text)
13566 .with_return_type_identity(return_type_identity),
13567 );
13568 };
13569 let parameters_start = search_from + relative_start;
13570 let parameters_end = parameters_start + parameter_text.len();
13571 let mut search_start = parameters_start;
13572 let parameters = cpp_parameter_label_nodes(parameters_node)
13573 .into_iter()
13574 .filter_map(|label_node| {
13575 let label = normalize_cpp_whitespace(node_text(label_node, source));
13576 if label.is_empty() || search_start > parameters_end {
13577 return None;
13578 }
13579 let haystack = signature.get(search_start..parameters_end)?;
13580 let relative_start = haystack.find(&label)?;
13581 let start_byte = search_start + relative_start;
13582 let end_byte = start_byte + label.len();
13583 search_start = end_byte;
13584 Some(ParameterMetadata::new(label, start_byte, end_byte))
13585 })
13586 .collect();
13587 enrich(
13588 SignatureMetadata::new(signature, parameters)
13589 .with_callable_arity(callable_arity)
13590 .with_callable_parameter_types(callable_parameter_types)
13591 .with_return_type_text(return_type_text)
13592 .with_return_type_identity(return_type_identity),
13593 )
13594}
13595
13596pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
13597 function_declarator: Node<'tree>,
13598 source: &str,
13599 ancestry: &ParentIndex<'tree>,
13600) -> bool {
13601 let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
13602 .or_else(|| function_declarator.child_by_field_name("name"))
13603 .or_else(|| last_named_child(function_declarator))
13604 else {
13605 return false;
13606 };
13607 if name_node.kind() == "qualified_identifier" {
13611 let owner = name_node
13612 .child_by_field_name("scope")
13613 .and_then(|scope| canonical_cpp_qualified_component(scope, source));
13614 let member = name_node
13615 .child_by_field_name("name")
13616 .and_then(|name| canonical_cpp_qualified_component(name, source));
13617 return match (owner, member) {
13618 (Some(owner), Some(member)) => owner.name == member.name,
13619 _ => false,
13620 };
13621 }
13622 let Some(callable_name) = direct_identifier_name(name_node, source) else {
13623 return false;
13624 };
13625
13626 let mut current = ancestry.parent(function_declarator);
13627 while let Some(ancestor) = current {
13628 let owner_name = match ancestor.kind() {
13629 "class_specifier" | "struct_specifier" | "union_specifier" => {
13630 class_like_name(ancestor, source, ancestry)
13631 }
13632 "ERROR" => malformed_class_error_owner_name(ancestor, source),
13633 _ => None,
13634 };
13635 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
13636 return true;
13637 }
13638 current = ancestry.parent(ancestor);
13639 }
13640 false
13641}
13642
13643fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
13653 if node.kind() != "ERROR" {
13654 return None;
13655 }
13656 let keyword = node.child(0)?;
13657 if !matches!(keyword.kind(), "class" | "struct" | "union") {
13658 return None;
13659 }
13660 let name_node = node.child(1)?;
13661 let name = direct_identifier_name(name_node, source)?;
13662 let has_body = (2..node.child_count())
13663 .filter_map(|index| node.child(index))
13664 .any(|child| child.kind() == "{");
13665 has_body.then_some(name)
13666}
13667
13668pub fn cpp_callable_declaration_return_type_identity<'tree>(
13674 callable: Node<'tree>,
13675 source: &str,
13676 ancestry: &ParentIndex<'tree>,
13677) -> Option<StructuredTypeIdentity> {
13678 let declarator = callable
13679 .child_by_field_name("declarator")
13680 .and_then(extract_function_declarator)?;
13681 cpp_callable_return_type_identity(declarator, source, ancestry)
13682}
13683
13684pub(crate) fn cpp_callable_return_type_identity<'tree>(
13685 function_declarator: Node<'tree>,
13686 source: &str,
13687 ancestry: &ParentIndex<'tree>,
13688) -> Option<StructuredTypeIdentity> {
13689 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
13690 return None;
13691 }
13692 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
13693 if let Some((return_type, _)) =
13694 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
13695 {
13696 return cpp_structured_type_identity(return_type, source, &lexical_scope);
13697 }
13698 let mut cursor = function_declarator.walk();
13699 if let Some(trailing) = function_declarator
13700 .named_children(&mut cursor)
13701 .find(|child| child.kind() == "trailing_return_type")
13702 && let Some(type_descriptor) = trailing.named_child(0)
13703 {
13704 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
13705 }
13706
13707 let mut current = function_declarator;
13708 let mut wrappers = Vec::new();
13709 while let Some(parent) = ancestry.parent(current) {
13710 if matches!(
13711 parent.kind(),
13712 "function_definition" | "declaration" | "field_declaration"
13713 ) {
13714 let type_node = parent.child_by_field_name("type")?;
13715 if cpp_export_macro_token(node_text(type_node, source))
13716 && (0..parent.named_child_count()).any(|index| {
13717 parent
13718 .named_child(index)
13719 .is_some_and(|child| child.kind() == "ERROR")
13720 })
13721 {
13722 return None;
13723 }
13724 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
13725 for wrapper in wrappers.into_iter().rev() {
13726 identity = cpp_wrap_structured_type(identity, wrapper)?;
13727 }
13728 return Some(identity);
13729 }
13730 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
13731 || (matches!(
13732 parent.kind(),
13733 "pointer_declarator"
13734 | "reference_declarator"
13735 | "array_declarator"
13736 | "parenthesized_declarator"
13737 ) && parent.named_child_count() == 1
13738 && parent.named_child(0) == Some(current));
13739 if !wraps_current_declarator {
13740 return None;
13741 }
13742 match parent.kind() {
13743 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
13744 "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
13745 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
13746 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
13747 _ => return None,
13748 }
13749 current = parent;
13750 }
13751 None
13752}
13753
13754fn cpp_structured_type_identity(
13755 node: Node<'_>,
13756 source: &str,
13757 lexical_scope: &[String],
13758) -> Option<StructuredTypeIdentity> {
13759 enum Work<'tree> {
13760 Visit(Node<'tree>),
13761 Wrap(CppStructuredTypeWrapper),
13762 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
13763 BuildGeneric { argument_count: usize },
13764 }
13765
13766 let mut work = vec![Work::Visit(node)];
13767 let mut values = Vec::new();
13768 let mut builder = StructuredTypeIdentityBuilder::default();
13769 while let Some(next) = work.pop() {
13770 match next {
13771 Work::Visit(current) => match current.kind() {
13772 "type_descriptor" => {
13773 let type_node = current
13774 .child_by_field_name("type")
13775 .or_else(|| current.named_child(0))?;
13776 let mut wrappers = Vec::new();
13777 let mut cursor = current.walk();
13778 for child in current.named_children(&mut cursor) {
13779 if child.id() != type_node.id() {
13780 wrappers.extend(cpp_structured_declarator_wrappers(child)?);
13781 }
13782 }
13783 work.push(Work::ApplyWrappers(wrappers));
13784 work.push(Work::Visit(type_node));
13785 }
13786 "pointer_declarator" | "abstract_pointer_declarator" => {
13787 let child = current
13788 .child_by_field_name("declarator")
13789 .or_else(|| current.named_child(0))?;
13790 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
13791 work.push(Work::Visit(child));
13792 }
13793 "reference_declarator" => {
13794 let child = current
13795 .child_by_field_name("declarator")
13796 .or_else(|| current.named_child(0))?;
13797 work.push(Work::Wrap(cpp_reference_wrapper(current)?));
13798 work.push(Work::Visit(child));
13799 }
13800 "array_declarator" | "abstract_array_declarator" => {
13801 let child = current
13802 .child_by_field_name("declarator")
13803 .or_else(|| current.named_child(0))?;
13804 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
13805 work.push(Work::Visit(child));
13806 }
13807 "template_type" => {
13808 let name_node = current.child_by_field_name("name")?;
13809 let arguments = current
13810 .child_by_field_name("arguments")
13811 .map(|arguments_node| {
13812 let mut cursor = arguments_node.walk();
13813 arguments_node
13814 .named_children(&mut cursor)
13815 .filter(|child| !child.is_extra() && child.kind() != "comment")
13816 .collect::<Vec<_>>()
13817 })
13818 .unwrap_or_default();
13819 work.push(Work::BuildGeneric {
13820 argument_count: arguments.len(),
13821 });
13822 work.extend(arguments.into_iter().rev().map(Work::Visit));
13823 work.push(Work::Visit(name_node));
13824 }
13825 "qualified_identifier"
13826 | "scoped_identifier"
13827 | "scoped_type_identifier"
13828 | "type_identifier"
13829 | "field_identifier"
13830 | "identifier"
13831 | "namespace_identifier"
13832 | "primitive_type" => {
13833 values.push(builder.named(cpp_structured_named_type(
13834 current,
13835 source,
13836 lexical_scope,
13837 )?)?);
13838 }
13839 _ => {
13840 let child = current.child_by_field_name("type").or_else(|| {
13841 (current.named_child_count() == 1)
13842 .then(|| current.named_child(0))
13843 .flatten()
13844 })?;
13845 work.push(Work::Visit(child));
13846 }
13847 },
13848 Work::Wrap(wrapper) => {
13849 let root = values.pop()?;
13850 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
13851 }
13852 Work::ApplyWrappers(wrappers) => {
13853 let mut root = values.pop()?;
13854 for wrapper in wrappers.into_iter().rev() {
13855 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
13856 }
13857 values.push(root);
13858 }
13859 Work::BuildGeneric { argument_count } => {
13860 let value_count = argument_count.checked_add(1)?;
13861 let start = values.len().checked_sub(value_count)?;
13862 let mut built = values.split_off(start);
13863 let base = built.remove(0);
13864 values.push(builder.generic(base, built)?);
13865 }
13866 }
13867 }
13868 (values.len() == 1)
13869 .then(|| values.pop())
13870 .flatten()
13871 .and_then(|root| builder.finish(root))
13872}
13873
13874fn cpp_structured_named_type(
13875 node: Node<'_>,
13876 source: &str,
13877 lexical_scope: &[String],
13878) -> Option<StructuredTypeName> {
13879 let path = cpp_structured_type_path(node, source)?;
13880 let absolute = node.child_by_field_name("scope").is_none()
13881 && node.child(0).is_some_and(|child| child.kind() == "::");
13882 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
13883}
13884
13885#[derive(Clone, Copy)]
13886enum CppStructuredTypeWrapper {
13887 Pointer,
13888 LvalueReference,
13889 RvalueReference,
13890 Array,
13891}
13892
13893fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
13894 let mut wrappers = Vec::new();
13895 let mut current = node;
13896 loop {
13897 match current.kind() {
13898 "pointer_declarator" | "abstract_pointer_declarator" => {
13899 wrappers.push(CppStructuredTypeWrapper::Pointer)
13900 }
13901 "reference_declarator" | "abstract_reference_declarator" => {
13902 wrappers.push(cpp_reference_wrapper(current)?);
13903 }
13904 "array_declarator" | "abstract_array_declarator" => {
13905 wrappers.push(CppStructuredTypeWrapper::Array)
13906 }
13907 "init_declarator" | "parenthesized_declarator" => {}
13914 _ => break,
13915 }
13916 let Some(child) = current
13917 .child_by_field_name("declarator")
13918 .or_else(|| current.named_child(0))
13919 else {
13920 break;
13921 };
13922 current = child;
13923 }
13924 Some(wrappers)
13925}
13926
13927fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
13928 node.children(&mut node.walk())
13929 .find_map(|child| match child.kind() {
13930 "&" => Some(CppStructuredTypeWrapper::LvalueReference),
13931 "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
13932 _ => None,
13933 })
13934}
13935
13936fn cpp_wrap_structured_type(
13937 identity: StructuredTypeIdentity,
13938 wrapper: CppStructuredTypeWrapper,
13939) -> Option<StructuredTypeIdentity> {
13940 match wrapper {
13941 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
13942 CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
13943 CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
13944 CppStructuredTypeWrapper::Array => identity.wrap_array(),
13945 }
13946}
13947
13948fn cpp_wrap_structured_type_node(
13949 builder: &mut StructuredTypeIdentityBuilder,
13950 inner: StructuredTypeNodeId,
13951 wrapper: CppStructuredTypeWrapper,
13952) -> Option<StructuredTypeNodeId> {
13953 match wrapper {
13954 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
13955 CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
13956 CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
13957 CppStructuredTypeWrapper::Array => builder.array(inner),
13958 }
13959}
13960
13961pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
13968 let mut path = Vec::new();
13969 let mut stack = vec![node];
13970 while let Some(current) = stack.pop() {
13971 match current.kind() {
13972 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
13973 let component = node_text(current, source).to_string();
13974 if component.is_empty() {
13975 return None;
13976 }
13977 path.push(component);
13978 }
13979 "template_type" | "dependent_type" => {
13980 stack.push(current.child_by_field_name("name")?);
13981 }
13982 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
13983 stack.push(current.child_by_field_name("name")?);
13984 if let Some(scope) = current.child_by_field_name("scope") {
13985 stack.push(scope);
13986 }
13987 }
13988 _ => return None,
13989 }
13990 }
13991 (!path.is_empty()).then_some(path)
13992}
13993
13994fn cpp_callable_lexical_scope<'tree>(
13995 node: Node<'tree>,
13996 source: &str,
13997 ancestry: &ParentIndex<'tree>,
13998) -> Vec<String> {
13999 let mut groups = Vec::new();
14000 let mut current = ancestry.parent(node);
14001 while let Some(parent) = current {
14002 if matches!(
14003 parent.kind(),
14004 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
14005 ) && let Some(name_node) = parent.child_by_field_name("name")
14006 && let Some(components) = cpp_structured_type_path(name_node, source)
14007 && !components.is_empty()
14008 {
14009 groups.push(components);
14010 }
14011 current = ancestry.parent(parent);
14012 }
14013 groups.reverse();
14014 groups.into_iter().flatten().collect()
14015}
14016
14017fn cpp_callable_dispatch_extensibility<'tree>(
14041 function_declarator: Node<'tree>,
14042 ancestry: &ParentIndex<'tree>,
14043) -> Option<DispatchExtensibility> {
14044 let mut declaration = None;
14045 let mut class_body = None;
14049 let mut current = Some(function_declarator);
14050 while let Some(node) = current {
14051 match node.kind() {
14052 "template_declaration"
14053 | "preproc_if"
14054 | "preproc_ifdef"
14055 | "preproc_else"
14056 | "preproc_elif"
14057 | "preproc_call"
14058 | "ERROR" => return Some(DispatchExtensibility::Open),
14059 "declaration" | "field_declaration" | "function_definition" => {
14060 declaration.get_or_insert(node);
14061 }
14062 "field_declaration_list" => {
14063 class_body.get_or_insert(node);
14064 }
14065 "translation_unit" => break,
14066 _ => {}
14067 }
14068 current = ancestry.parent(node);
14069 }
14070 let Some(declaration) = declaration else {
14071 return Some(DispatchExtensibility::Open);
14072 };
14073
14074 let mut saw_virtual_boundary = false;
14075 let mut stack = vec![declaration];
14076 while let Some(node) = stack.pop() {
14077 match node.kind() {
14078 "compound_statement" | "field_declaration_list" => continue,
14079 "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
14080 "virtual"
14081 | "override"
14082 | "virtual_specifier"
14083 | "pure_virtual_clause"
14084 | "template_parameter_list"
14085 | "template_method"
14086 | "template_function"
14087 | "ERROR" => saw_virtual_boundary = true,
14088 _ => {}
14089 }
14090 let mut cursor = node.walk();
14091 stack.extend(node.children(&mut cursor));
14092 }
14093
14094 if saw_virtual_boundary {
14095 return Some(DispatchExtensibility::Open);
14096 }
14097 if let Some(body) = class_body {
14098 let has_base_clause = ancestry
14102 .parent(body)
14103 .and_then(cpp_class_like_base_clause)
14104 .is_some();
14105 return Some(if has_base_clause {
14106 DispatchExtensibility::Open
14107 } else {
14108 DispatchExtensibility::Closed
14109 });
14110 }
14111 if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
14112 return None;
14113 }
14114 Some(DispatchExtensibility::Closed)
14117}
14118
14119fn cpp_callable_linkage<'tree>(
14120 declaration: Node<'tree>,
14121 source: &str,
14122 ancestry: &ParentIndex<'tree>,
14123) -> CallableLinkage {
14124 let mut enclosed_by_class = false;
14125 let mut current = ancestry.parent(declaration);
14126 while let Some(node) = current {
14127 if node.kind() == "namespace_definition"
14128 && node
14129 .child_by_field_name("name")
14130 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
14131 {
14132 return CallableLinkage::Internal;
14133 }
14134 if matches!(
14135 node.kind(),
14136 "class_specifier" | "struct_specifier" | "union_specifier"
14137 ) {
14138 if node
14139 .child_by_field_name("name")
14140 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
14141 {
14142 return CallableLinkage::Internal;
14143 }
14144 enclosed_by_class = true;
14145 }
14146 if node.kind() == "lambda_expression"
14151 || node.kind() == "function_definition"
14152 && !is_recovered_exported_class_container(node, source)
14153 {
14154 return CallableLinkage::Internal;
14155 }
14156 current = ancestry.parent(node);
14157 }
14158
14159 if enclosed_by_class {
14160 return CallableLinkage::External;
14161 }
14162
14163 if cpp_declares_static_storage_class(declaration, source) {
14164 CallableLinkage::Internal
14165 } else {
14166 CallableLinkage::External
14167 }
14168}
14169
14170fn cpp_callable_return_type_text<'tree>(
14171 function_declarator: Node<'tree>,
14172 source: &str,
14173 ancestry: &ParentIndex<'tree>,
14174) -> Option<String> {
14175 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
14176 return None;
14177 }
14178 if let Some((return_type, _)) =
14179 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
14180 {
14181 let text = normalize_cpp_whitespace(node_text(return_type, source));
14182 return (!text.is_empty()).then_some(text);
14183 }
14184 let mut cursor = function_declarator.walk();
14185 if let Some(trailing) = function_declarator
14186 .named_children(&mut cursor)
14187 .find(|child| child.kind() == "trailing_return_type")
14188 && let Some(type_descriptor) = trailing.named_child(0)
14189 {
14190 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
14191 if !text.is_empty() {
14192 return Some(text);
14193 }
14194 }
14195
14196 let mut current = function_declarator;
14197 let mut indirection = String::new();
14198 while let Some(parent) = ancestry.parent(current) {
14199 if matches!(
14200 parent.kind(),
14201 "function_definition" | "declaration" | "field_declaration"
14202 ) {
14203 let type_node = parent.child_by_field_name("type")?;
14204 if cpp_export_macro_token(node_text(type_node, source))
14205 && (0..parent.named_child_count()).any(|index| {
14206 parent
14207 .named_child(index)
14208 .is_some_and(|child| child.kind() == "ERROR")
14209 })
14210 {
14211 return None;
14216 }
14217 let base = normalize_cpp_whitespace(node_text(type_node, source));
14218 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
14219 }
14220 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
14221 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
14222 && parent.named_child_count() == 1
14223 && parent.named_child(0) == Some(current));
14224 if wraps_current_declarator {
14225 match parent.kind() {
14226 "pointer_declarator" => indirection.push('*'),
14227 "reference_declarator" => {
14228 let reference = parent
14229 .children(&mut parent.walk())
14230 .find(|child| !child.is_named())
14231 .map(|child| node_text(child, source))
14232 .unwrap_or("&");
14233 indirection.push_str(reference);
14234 }
14235 "init_declarator" | "parenthesized_declarator" => {}
14236 _ => return None,
14237 }
14238 current = parent;
14239 continue;
14240 }
14241 return None;
14242 }
14243 None
14244}
14245
14246pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
14247 let mut required = 0;
14248 let mut total = 0;
14249 let mut repeated = false;
14250 let mut cursor = parameters_node.walk();
14251 for child in parameters_node.children(&mut cursor) {
14252 match child.kind() {
14253 "parameter_declaration" => {
14254 if cpp_parameter_is_explicit_object(child, source) {
14255 continue;
14256 }
14257 if child.child_by_field_name("declarator").is_none()
14258 && child
14259 .child_by_field_name("type")
14260 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
14261 {
14262 continue;
14263 }
14264 required += 1;
14265 total += 1;
14266 }
14267 "optional_parameter_declaration" => total += 1,
14268 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
14269 repeated = true;
14270 }
14271 _ => {}
14272 }
14273 }
14274 CallableArity::new(required, total, repeated)
14275}
14276
14277fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
14278 parameter
14279 .child_by_field_name("type")
14280 .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
14281 .and_then(|type_node| type_node.child_by_field_name("constraint"))
14282 .is_some_and(|constraint| {
14283 constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
14284 })
14285}
14286
14287#[derive(Clone, Copy)]
14295enum CppParameterSlot<'tree> {
14296 Declared(Node<'tree>),
14297 Ellipsis,
14298}
14299
14300fn cpp_callable_parameter_slots<'tree>(
14301 parameters_node: Node<'tree>,
14302 source: &str,
14303) -> Vec<CppParameterSlot<'tree>> {
14304 let mut slots = Vec::new();
14305 let mut cursor = parameters_node.walk();
14306 for parameter in parameters_node.children(&mut cursor) {
14307 match parameter.kind() {
14308 "parameter_declaration" | "optional_parameter_declaration" => {
14309 if cpp_parameter_is_explicit_object(parameter, source)
14310 || (parameter.child_by_field_name("declarator").is_none()
14311 && parameter
14312 .child_by_field_name("type")
14313 .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
14314 {
14315 continue;
14316 }
14317 slots.push(CppParameterSlot::Declared(parameter));
14318 }
14319 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
14320 slots.push(CppParameterSlot::Ellipsis);
14321 }
14322 _ => {}
14323 }
14324 }
14325 slots
14326}
14327
14328pub(crate) fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
14329 cpp_callable_parameter_slots(parameters_node, source)
14330 .into_iter()
14331 .map(|slot| match slot {
14332 CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
14333 CppParameterSlot::Ellipsis => "...".to_string(),
14334 })
14335 .collect()
14336}
14337
14338#[derive(Debug, Clone, PartialEq, Eq)]
14344pub enum CppParameterType {
14345 Structured(StructuredTypeIdentity),
14348 Ellipsis,
14350 Unstructured,
14353}
14354
14355pub fn cpp_callable_parameter_type_identities<'tree>(
14361 function_declarator: Node<'tree>,
14362 source: &str,
14363 ancestry: &ParentIndex<'tree>,
14364) -> Vec<CppParameterType> {
14365 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
14366 return Vec::new();
14367 };
14368 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
14369 cpp_callable_parameter_slots(parameters_node, source)
14370 .into_iter()
14371 .map(|slot| match slot {
14372 CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
14373 CppParameterSlot::Declared(parameter) => {
14374 cpp_parameter_type_identity(parameter, source, &lexical_scope)
14375 .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
14376 }
14377 })
14378 .collect()
14379}
14380
14381pub fn cpp_declaration_type_identity<'tree>(
14388 declaration: Node<'tree>,
14389 declarator: Node<'tree>,
14390 source: &str,
14391 ancestry: &ParentIndex<'tree>,
14392) -> Option<StructuredTypeIdentity> {
14393 let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
14394 cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
14395}
14396
14397fn cpp_parameter_type_identity(
14398 parameter: Node<'_>,
14399 source: &str,
14400 lexical_scope: &[String],
14401) -> Option<StructuredTypeIdentity> {
14402 cpp_declaration_type_identity_in_scope(
14403 parameter,
14404 cpp_parameter_declarator(parameter),
14405 source,
14406 lexical_scope,
14407 )
14408}
14409
14410fn cpp_declaration_type_identity_in_scope(
14411 declaration: Node<'_>,
14412 declarator: Option<Node<'_>>,
14413 source: &str,
14414 lexical_scope: &[String],
14415) -> Option<StructuredTypeIdentity> {
14416 let type_node = declaration.child_by_field_name("type")?;
14417 let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
14418 if let Some(declarator) = declarator {
14419 for wrapper in cpp_structured_declarator_wrappers(declarator)?
14420 .into_iter()
14421 .rev()
14422 {
14423 identity = cpp_wrap_structured_type(identity, wrapper)?;
14424 }
14425 }
14426 Some(identity)
14427}
14428
14429pub fn cpp_comparable_parameter_shapes<'tree>(
14436 function_declarator: Node<'tree>,
14437 source: &str,
14438 ancestry: &ParentIndex<'tree>,
14439) -> Vec<CppComparableSlot> {
14440 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
14441 return Vec::new();
14442 };
14443 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
14444 cpp_callable_parameter_slots(parameters_node, source)
14445 .into_iter()
14446 .map(|slot| match slot {
14447 CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
14448 CppParameterSlot::Declared(parameter) => {
14449 cpp_comparable_parameter(parameter, source, &lexical_scope)
14450 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
14451 }
14452 })
14453 .collect()
14454}
14455
14456fn cpp_comparable_parameter(
14457 parameter: Node<'_>,
14458 source: &str,
14459 lexical_scope: &[String],
14460) -> Option<CppComparableParameter> {
14461 let type_node = parameter.child_by_field_name("type")?;
14462 let levels = match cpp_parameter_declarator(parameter) {
14463 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
14464 None => Vec::new(),
14465 };
14466 let mut shape = cpp_comparable_type_shape(
14467 type_node,
14468 cpp_cv_qualifiers(parameter, source),
14469 levels,
14470 source,
14471 lexical_scope,
14472 )?;
14473 shape.adjust_parameter_top_level();
14474 Some(shape)
14475}
14476
14477fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
14488 let mut qualifiers = CppCvQualifiers::default();
14489 let mut cursor = node.walk();
14490 for child in node.named_children(&mut cursor) {
14491 if child.kind() != "type_qualifier" {
14492 continue;
14493 }
14494 match node_text(child, source) {
14495 "const" => qualifiers.konst = true,
14496 "volatile" => qualifiers.volatil = true,
14497 _ => {}
14498 }
14499 }
14500 qualifiers
14501}
14502
14503#[derive(Clone, Copy, Default)]
14504struct CppCvQualifiers {
14505 konst: bool,
14506 volatil: bool,
14507}
14508
14509impl CppCvQualifiers {
14510 fn union(self, other: Self) -> Self {
14511 Self {
14512 konst: self.konst || other.konst,
14513 volatil: self.volatil || other.volatil,
14514 }
14515 }
14516}
14517
14518#[derive(Clone, Copy)]
14520enum CppComparableLevel {
14521 Pointer { konst: bool, volatil: bool },
14522 Reference,
14523 Array,
14524}
14525
14526fn cpp_comparable_declarator_levels(
14539 declarator: Node<'_>,
14540 source: &str,
14541) -> Option<Vec<CppComparableLevel>> {
14542 let mut levels = Vec::new();
14543 let mut current = declarator;
14544 loop {
14545 match current.kind() {
14546 "pointer_declarator" | "abstract_pointer_declarator" => {
14547 let qualifiers = cpp_cv_qualifiers(current, source);
14548 levels.push(CppComparableLevel::Pointer {
14549 konst: qualifiers.konst,
14550 volatil: qualifiers.volatil,
14551 });
14552 }
14553 "reference_declarator" | "abstract_reference_declarator" => {
14554 levels.push(CppComparableLevel::Reference);
14555 }
14556 "array_declarator" | "abstract_array_declarator" => {
14557 levels.push(CppComparableLevel::Array);
14558 }
14559 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
14560 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
14561 _ => return None,
14562 }
14563 let Some(next) = cpp_nested_declarator(current) else {
14564 return Some(levels);
14565 };
14566 current = next;
14567 }
14568}
14569
14570fn cpp_comparable_type_shape(
14577 type_node: Node<'_>,
14578 qualifiers: CppCvQualifiers,
14579 levels: Vec<CppComparableLevel>,
14580 source: &str,
14581 lexical_scope: &[String],
14582) -> Option<CppComparableParameter> {
14583 enum Work<'tree> {
14584 Visit {
14585 node: Node<'tree>,
14586 qualifiers: CppCvQualifiers,
14587 },
14588 ApplyLevels(Vec<CppComparableLevel>),
14589 BuildGeneric {
14590 argument_count: usize,
14591 },
14592 }
14593
14594 let mut nodes: Vec<CppComparableNode> = Vec::new();
14595 let mut values: Vec<usize> = Vec::new();
14596 let mut work = vec![
14597 Work::ApplyLevels(levels),
14598 Work::Visit {
14599 node: type_node,
14600 qualifiers,
14601 },
14602 ];
14603 while let Some(next) = work.pop() {
14604 match next {
14605 Work::Visit { node, qualifiers } => match node.kind() {
14606 "type_descriptor" => {
14607 let inner_type = node
14608 .child_by_field_name("type")
14609 .or_else(|| node.named_child(0))?;
14610 let mut cursor = node.walk();
14611 let declarator = node.child_by_field_name("declarator").or_else(|| {
14612 node.named_children(&mut cursor).find(|child| {
14613 child.id() != inner_type.id() && child.kind() != "type_qualifier"
14614 })
14615 });
14616 let levels = match declarator {
14617 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
14618 None => Vec::new(),
14619 };
14620 work.push(Work::ApplyLevels(levels));
14621 work.push(Work::Visit {
14622 node: inner_type,
14623 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
14624 });
14625 }
14626 "sized_type_specifier" => {
14627 let name = StructuredTypeName::new(
14632 vec![normalize_cpp_whitespace(node_text(node, source))],
14633 lexical_scope.to_vec(),
14634 false,
14635 )?;
14636 values.push(cpp_push_comparable_node(
14637 &mut nodes,
14638 CppComparableNode::Named {
14639 name,
14640 primitive: true,
14641 konst: qualifiers.konst,
14642 volatil: qualifiers.volatil,
14643 },
14644 ));
14645 }
14646 "qualified_identifier"
14647 | "scoped_identifier"
14648 | "scoped_type_identifier"
14649 | "type_identifier"
14650 | "field_identifier"
14651 | "identifier"
14652 | "namespace_identifier"
14653 | "primitive_type"
14654 | "template_type" => {
14655 let name = cpp_structured_named_type(node, source, lexical_scope)?;
14656 values.push(cpp_push_comparable_node(
14657 &mut nodes,
14658 CppComparableNode::Named {
14659 name,
14660 primitive: node.kind() == "primitive_type",
14661 konst: qualifiers.konst,
14662 volatil: qualifiers.volatil,
14663 },
14664 ));
14665 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
14666 let mut cursor = arguments_node.walk();
14667 let arguments = arguments_node
14668 .named_children(&mut cursor)
14669 .filter(|child| !child.is_extra() && child.kind() != "comment")
14670 .collect::<Vec<_>>();
14671 work.push(Work::BuildGeneric {
14672 argument_count: arguments.len(),
14673 });
14674 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
14675 node: argument,
14676 qualifiers: CppCvQualifiers::default(),
14677 }));
14678 }
14679 }
14680 _ => {
14681 let inner = node.child_by_field_name("type").or_else(|| {
14682 (node.named_child_count() == 1)
14683 .then(|| node.named_child(0))
14684 .flatten()
14685 })?;
14686 work.push(Work::Visit {
14687 node: inner,
14688 qualifiers,
14689 });
14690 }
14691 },
14692 Work::ApplyLevels(levels) => {
14693 let mut root = values.pop()?;
14694 for level in levels {
14695 let node = match level {
14696 CppComparableLevel::Pointer { konst, volatil } => {
14697 CppComparableNode::Pointer {
14698 inner: root,
14699 konst,
14700 volatil,
14701 }
14702 }
14703 CppComparableLevel::Reference => {
14704 CppComparableNode::Reference { inner: root }
14705 }
14706 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
14707 };
14708 root = cpp_push_comparable_node(&mut nodes, node);
14709 }
14710 values.push(root);
14711 }
14712 Work::BuildGeneric { argument_count } => {
14713 let value_count = argument_count.checked_add(1)?;
14714 let start = values.len().checked_sub(value_count)?;
14715 let mut built = values.split_off(start);
14716 let base = built.remove(0);
14717 values.push(cpp_push_comparable_node(
14718 &mut nodes,
14719 CppComparableNode::Generic {
14720 base,
14721 arguments: built,
14722 },
14723 ));
14724 }
14725 }
14726 }
14727 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
14728 debug_assert_eq!(
14729 root,
14730 nodes.len().saturating_sub(1),
14731 "comparable nodes are appended in post-order, so the root is the last one"
14732 );
14733 Some(CppComparableParameter::new(nodes, root))
14734}
14735
14736fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
14737 nodes.push(node);
14738 nodes.len() - 1
14739}
14740
14741fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
14747 let mut current = node;
14748 loop {
14749 match current.kind() {
14750 "template_type" => return current.child_by_field_name("arguments"),
14751 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14752 current = current.child_by_field_name("name")?;
14753 }
14754 _ => return None,
14755 }
14756 }
14757}
14758
14759pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
14765 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
14766 loop {
14767 if matches!(
14768 current.kind(),
14769 "declaration" | "field_declaration" | "function_definition"
14770 ) && let Some(declarator) = current
14771 .child_by_field_name("declarator")
14772 .and_then(extract_function_declarator)
14773 {
14774 return Some(declarator);
14775 }
14776 current = current.parent()?;
14777 }
14778}
14779
14780fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
14781 let mut labels = Vec::new();
14782 let mut cursor = parameters_node.walk();
14783 for child in parameters_node.children(&mut cursor) {
14784 match child.kind() {
14785 "parameter_declaration" | "optional_parameter_declaration" => {
14786 if let Some(name_node) = child
14787 .child_by_field_name("declarator")
14788 .and_then(cpp_declarator_label_node)
14789 {
14790 labels.push(name_node);
14791 } else {
14792 labels.push(child);
14793 }
14794 }
14795 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
14796 labels.push(child);
14797 }
14798 _ => {}
14799 }
14800 }
14801 labels
14802}
14803
14804fn cpp_signature_search_start<'tree>(
14805 signature: &str,
14806 function_declarator: Node<'tree>,
14807 source: &str,
14808 ancestry: &ParentIndex<'tree>,
14809) -> usize {
14810 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
14811 return 0;
14812 };
14813 let raw = node_text(enclosing, source);
14814 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
14815 let offset = function_declarator
14816 .start_byte()
14817 .saturating_sub(enclosing.start_byte())
14818 .saturating_sub(leading_trim_bytes);
14819 offset.min(signature.len())
14820}
14821
14822fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
14823 match node.kind() {
14824 "identifier" | "field_identifier" => Some(node),
14825 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
14826 .child_by_field_name("declarator")
14827 .or_else(|| last_named_child(node))
14828 .and_then(cpp_declarator_label_node),
14829 "array_declarator" => node
14830 .child_by_field_name("declarator")
14831 .and_then(cpp_declarator_label_node),
14832 "function_declarator" => node
14833 .child_by_field_name("declarator")
14834 .or_else(|| node.child_by_field_name("name"))
14835 .or_else(|| last_named_child(node))
14836 .and_then(cpp_declarator_label_node),
14837 _ => None,
14838 }
14839}
14840
14841fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
14842 let base_type = parameter
14843 .child_by_field_name("type")
14844 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
14845 .unwrap_or_default();
14846 let declarator = cpp_parameter_declarator(parameter);
14847 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
14854 let mut cursor = parameter.walk();
14855 let qualifiers = parameter
14856 .named_children(&mut cursor)
14857 .filter(|child| child.kind() == "type_qualifier")
14858 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
14859 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
14860 .collect::<Vec<_>>()
14861 .join(" ");
14862 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
14863 (true, _) => base_type,
14864 (_, true) => qualifiers,
14865 (false, false) => format!("{qualifiers} {base_type}"),
14866 };
14867 let declarator_suffix = declarator
14868 .map(|node| cpp_declarator_suffix_without_name(node, source))
14869 .unwrap_or_default();
14870
14871 let combined = if type_text.is_empty() {
14872 declarator_suffix
14873 } else if declarator_suffix.is_empty() {
14874 type_text
14875 } else {
14876 format!("{type_text} {declarator_suffix}")
14877 };
14878 normalize_cpp_type_text(&combined)
14879}
14880
14881fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
14882 parameter.child_by_field_name("declarator").or_else(|| {
14883 let mut cursor = parameter.walk();
14889 parameter
14890 .named_children(&mut cursor)
14891 .find(|child| is_cpp_abstract_declarator(child.kind()))
14892 })
14893}
14894
14895pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
14898 let mut current = Some(declarator);
14899 while let Some(node) = current {
14900 if matches!(
14901 node.kind(),
14902 "pointer_declarator"
14903 | "abstract_pointer_declarator"
14904 | "reference_declarator"
14905 | "abstract_reference_declarator"
14906 | "array_declarator"
14907 | "abstract_array_declarator"
14908 | "function_declarator"
14909 | "abstract_function_declarator"
14910 ) {
14911 return true;
14912 }
14913 current = cpp_nested_declarator(node);
14914 }
14915 false
14916}
14917
14918fn is_cpp_abstract_declarator(kind: &str) -> bool {
14919 matches!(
14920 kind,
14921 "abstract_pointer_declarator"
14922 | "abstract_reference_declarator"
14923 | "abstract_array_declarator"
14924 | "abstract_function_declarator"
14925 | "abstract_parenthesized_declarator"
14926 )
14927}
14928
14929fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
14930 node.child_by_field_name("declarator").or_else(|| {
14931 if is_cpp_abstract_declarator(node.kind()) {
14932 let mut cursor = node.walk();
14933 node.named_children(&mut cursor)
14934 .find(|child| is_cpp_abstract_declarator(child.kind()))
14935 } else {
14936 last_named_child(node)
14940 }
14941 })
14942}
14943
14944fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
14945 match node.kind() {
14946 "identifier" | "field_identifier" => String::new(),
14947 "pointer_declarator" | "abstract_pointer_declarator" => {
14948 let inner = cpp_nested_declarator(node)
14949 .map(|child| cpp_declarator_suffix_without_name(child, source))
14950 .unwrap_or_default();
14951 format!("*{inner}")
14952 }
14953 "reference_declarator" | "abstract_reference_declarator" => {
14954 let inner = cpp_nested_declarator(node)
14955 .map(|child| cpp_declarator_suffix_without_name(child, source))
14956 .unwrap_or_default();
14957 let reference = node
14958 .children(&mut node.walk())
14959 .find(|child| matches!(child.kind(), "&" | "&&"))
14960 .map(|child| node_text(child, source))
14961 .unwrap_or("&");
14962 format!("{reference}{inner}")
14963 }
14964 "array_declarator" | "abstract_array_declarator" => {
14965 let inner = cpp_nested_declarator(node)
14966 .map(|child| cpp_declarator_suffix_without_name(child, source))
14967 .unwrap_or_default();
14968 let size = node
14969 .child_by_field_name("size")
14970 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
14971 .unwrap_or_default();
14972 format!("{inner}[{size}]")
14973 }
14974 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
14975 let inner = cpp_nested_declarator(node);
14976 inner
14977 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
14978 .unwrap_or_default()
14979 }
14980 "function_declarator" | "abstract_function_declarator" => {
14981 let inner = cpp_nested_declarator(node)
14982 .map(|child| cpp_declarator_suffix_without_name(child, source))
14983 .unwrap_or_default();
14984 let params = node
14985 .child_by_field_name("parameters")
14986 .map(|child| cpp_parameter_signature(child, source))
14987 .unwrap_or_else(|| "()".to_string());
14988 format!("{inner}{params}")
14989 }
14990 _ => {
14991 let text = normalize_cpp_whitespace(node_text(node, source));
14992 let name = extract_declarator_name(node, source);
14993 if name.is_empty() {
14994 text
14995 } else {
14996 text.replace(&name, "").trim().to_string()
14997 }
14998 }
14999 }
15000}
15001
15002fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
15003 collapse_cpp_whitespace(
15004 suffix
15005 .trim()
15006 .trim_start_matches("->")
15007 .trim_start_matches('{')
15008 .trim_end_matches(';'),
15009 )
15010}
15011
15012pub fn normalize_cpp_whitespace(value: &str) -> String {
15013 collapse_cpp_whitespace(value)
15014}
15015
15016fn normalize_cpp_type_text(value: &str) -> String {
15017 collapse_cpp_whitespace(value)
15018 .replace(", ", ",")
15019 .replace(" <", "<")
15020 .replace("< ", "<")
15021 .replace(" >", ">")
15022}
15023
15024fn collapse_cpp_whitespace(value: &str) -> String {
15025 let mut result = String::new();
15026 let mut prev_space = false;
15027 for ch in value.chars() {
15028 if ch.is_whitespace() {
15029 if !prev_space {
15030 result.push(' ');
15031 }
15032 prev_space = true;
15033 } else {
15034 result.push(ch);
15035 prev_space = false;
15036 }
15037 }
15038 result.trim().to_string()
15039}
15040
15041pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
15042 node_source_text(node, source)
15043}
15044
15045fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
15046 node.child_by_field_name("body").or_else(|| {
15047 let mut cursor = node.walk();
15048 node.named_children(&mut cursor).find(|child| {
15049 matches!(
15050 child.kind(),
15051 "declaration_list" | "field_declaration_list" | "enumerator_list"
15052 )
15053 })
15054 })
15055}
15056
15057fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
15068 if !matches!(
15069 node.kind(),
15070 "class_specifier" | "struct_specifier" | "union_specifier"
15071 ) {
15072 return None;
15073 }
15074 let body = cpp_body_node(node)?;
15075 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
15076 return None;
15077 }
15078 let open = body.child(0)?;
15079 let close = body.child(body.child_count().checked_sub(1)?)?;
15080 if open.kind() != "{"
15081 || open.is_missing()
15082 || close.kind() != "}"
15083 || close.is_missing()
15084 || close.end_byte() != body.end_byte()
15085 || body.end_byte() > node.end_byte()
15086 || node
15087 .parent()
15088 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
15089 {
15090 return None;
15091 }
15092 Some(close)
15093}
15094
15095fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
15096 if node.kind() == "namespace_definition" {
15097 return true;
15098 }
15099 let mut cursor = node.walk();
15100 node.named_children(&mut cursor)
15101 .any(cpp_contains_namespace_definition)
15102}
15103
15104struct CppNestedNamespaceSentinel<'tree> {
15105 function: Node<'tree>,
15106 body: Node<'tree>,
15107 namespace_components: Vec<String>,
15108}
15109
15110#[derive(Debug, Clone)]
15120pub struct CppSentinelRecoveredOwner {
15121 pub range: Range,
15122 pub owner_name_start_byte: usize,
15126 pub namespace_component_count: usize,
15130 pub scope_components: Vec<String>,
15131}
15132
15133#[derive(Debug, Clone)]
15134pub struct CppSentinelRecoveredClass {
15135 pub namespace_range: Range,
15136 pub namespace_scope_components: Vec<String>,
15137 pub class_range: Range,
15138 pub scope_components: Vec<String>,
15140 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
15144}
15145
15146pub fn cpp_sentinel_recovered_scope_for_node(
15152 node: Node<'_>,
15153 source: &str,
15154 recovered_classes: &[CppSentinelRecoveredClass],
15155) -> Option<Vec<String>> {
15156 let contains =
15157 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
15158 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
15159 for recovered in recovered_classes {
15160 for owner in recovered
15161 .owner_ranges
15162 .iter()
15163 .filter(|owner| contains(owner.range))
15164 {
15165 let replace = best_owner.is_none_or(|existing| {
15166 owner.range.end_byte.saturating_sub(owner.range.start_byte)
15167 < existing
15168 .range
15169 .end_byte
15170 .saturating_sub(existing.range.start_byte)
15171 });
15172 if replace {
15173 best_owner = Some(owner);
15174 }
15175 }
15176 }
15177 if let Some(owner) = best_owner {
15178 let mut scope = owner.scope_components.clone();
15179 if node.start_byte() < owner.owner_name_start_byte {
15180 scope.truncate(owner.namespace_component_count);
15181 }
15182 return Some(scope);
15183 }
15184
15185 let class = recovered_classes
15186 .iter()
15187 .filter(|recovered| contains(recovered.class_range))
15188 .min_by_key(|recovered| {
15189 recovered
15190 .class_range
15191 .end_byte
15192 .saturating_sub(recovered.class_range.start_byte)
15193 });
15194 let class_scope = class.is_some();
15195 let mut scope = if let Some(class) = class {
15196 class.scope_components.clone()
15197 } else {
15198 let namespace = recovered_classes
15199 .iter()
15200 .filter(|recovered| contains(recovered.namespace_range))
15201 .min_by_key(|recovered| {
15202 recovered
15203 .namespace_range
15204 .end_byte
15205 .saturating_sub(recovered.namespace_range.start_byte)
15206 })?;
15207 let mut scope = namespace.namespace_scope_components.clone();
15208 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
15209 let common_prefix = scope
15210 .iter()
15211 .zip(&parser_namespace)
15212 .take_while(|(recovered, parser)| recovered == parser)
15213 .count();
15214 scope.extend(parser_namespace.into_iter().skip(common_prefix));
15215 scope
15216 };
15217 if class_scope {
15218 let mut ancestor_components = Vec::new();
15219 let mut ancestor = node.parent();
15220 while let Some(current) = ancestor {
15221 if matches!(
15222 current.kind(),
15223 "class_specifier" | "struct_specifier" | "union_specifier"
15224 ) && let Some(name) = current.child_by_field_name("name")
15225 && let Some(name_components) = cpp_name_components(name, source)
15226 {
15227 ancestor_components.push(
15228 name_components
15229 .into_iter()
15230 .map(|component| component.name)
15231 .collect::<Vec<_>>(),
15232 );
15233 }
15234 ancestor = current.parent();
15235 }
15236 ancestor_components.reverse();
15237 let base_len = scope.len();
15238 for component in ancestor_components.into_iter().flatten() {
15239 if scope.len() >= base_len && scope.last() == Some(&component) {
15240 continue;
15241 }
15242 scope.push(component);
15243 }
15244 }
15245 Some(scope)
15246}
15247
15248struct CppSentinelFragmentedClassTail<'tree> {
15249 class_node: Node<'tree>,
15250 template_node: Option<Node<'tree>>,
15251 name: String,
15252 raw_supertypes: Option<Vec<String>>,
15253 fragmented: FragmentedExportBody,
15254 consumed_start: usize,
15255}
15256
15257struct CppSentinelFragmentedClassErrorPrefix<'tree> {
15258 name: String,
15259 open: Node<'tree>,
15260 raw_supertypes: Option<Vec<String>>,
15261}
15262
15263struct CppSentinelDirectBodyClassRegion {
15264 namespace_components: Vec<String>,
15265 class_start: usize,
15266 class_start_line: usize,
15267 class_close_end: usize,
15268 class_close_line: usize,
15269 name: String,
15270}
15271
15272fn cpp_sentinel_body_class_candidate<'tree>(
15273 child: Node<'tree>,
15274) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
15275 if matches!(
15276 child.kind(),
15277 "class_specifier" | "struct_specifier" | "union_specifier"
15278 ) {
15279 return Some((child, None));
15280 }
15281 if child.kind() != "template_declaration" {
15282 if child.kind() == "declaration" {
15283 return Some((first_class_like_child(child)?, None));
15284 }
15285 return None;
15286 }
15287 let mut cursor = child.walk();
15288 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
15289 if matches!(
15290 candidate.kind(),
15291 "class_specifier" | "struct_specifier" | "union_specifier"
15292 ) {
15293 Some(candidate)
15294 } else if candidate.kind() == "declaration" {
15295 first_class_like_child(candidate)
15296 } else {
15297 None
15298 }
15299 })?;
15300 Some((class_node, Some(child)))
15301}
15302
15303fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
15309 node: Node<'tree>,
15310 source: &str,
15311) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
15312 let name = malformed_class_error_owner_name(node, source)?;
15313 let mut cursor = node.walk();
15314 let children = node.children(&mut cursor).collect::<Vec<_>>();
15315 let keyword = children.first()?;
15316 let open_index = children.iter().position(|child| child.kind() == "{")?;
15317 if children[open_index + 1..]
15318 .iter()
15319 .any(|child| child.kind() == "}")
15320 {
15321 return None;
15322 }
15323 let raw_supertypes =
15324 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
15325 Some(CppSentinelFragmentedClassErrorPrefix {
15326 name,
15327 open: children[open_index],
15328 raw_supertypes,
15329 })
15330}
15331
15332fn cpp_sentinel_direct_body_class_candidate<'tree>(
15333 child: Node<'tree>,
15334) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
15335 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
15336 return Some(candidate);
15337 }
15338 if child.kind() != "template_declaration" {
15339 return None;
15340 }
15341 let mut cursor = child.walk();
15342 let wrapper = child
15343 .named_children(&mut cursor)
15344 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
15345 Some((first_class_like_child(wrapper)?, Some(child)))
15346}
15347
15348fn cpp_sentinel_direct_namespace_components(
15349 function: Node<'_>,
15350 body: Node<'_>,
15351 source: &str,
15352) -> Option<Vec<String>> {
15353 let mut cursor = function.walk();
15354 let children = function
15355 .named_children(&mut cursor)
15356 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
15357 .collect::<Vec<_>>();
15358 let sentinel_index = children.iter().rposition(|child| {
15359 direct_identifier_name(*child, source)
15360 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
15361 })?;
15362 let mut identifiers = Vec::new();
15363 let mut stack = children[sentinel_index + 1..]
15364 .iter()
15365 .rev()
15366 .copied()
15367 .collect::<Vec<_>>();
15368 while let Some(current) = stack.pop() {
15369 if let Some(name) = direct_identifier_name(current, source) {
15370 identifiers.push(name);
15371 continue;
15372 }
15373 let mut cursor = current.walk();
15374 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
15375 stack.extend(children.into_iter().rev());
15376 }
15377 let [keyword, namespace] = identifiers.as_slice() else {
15378 return None;
15379 };
15380 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
15381 .then(|| vec![namespace.clone()])
15382}
15383
15384fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
15385 let mut sibling = class_semicolon.next_named_sibling();
15386 let namespace_close = loop {
15387 let Some(current) = sibling else {
15388 return false;
15389 };
15390 sibling = current.next_named_sibling();
15391 if current.kind() != "comment" {
15392 break current;
15393 }
15394 };
15395 if !cpp_is_stray_close_brace(namespace_close, source) {
15396 return false;
15397 }
15398 loop {
15399 let Some(current) = sibling else {
15400 return false;
15401 };
15402 sibling = current.next_named_sibling();
15403 if current.kind() == "comment" {
15404 continue;
15405 }
15406 return direct_identifier_name(current, source)
15407 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
15408 }
15409}
15410
15411fn cpp_sentinel_macro_body_class_region<'tree>(
15412 node: Node<'tree>,
15413 source: &str,
15414 ancestry: &ParentIndex<'tree>,
15415) -> Option<CppSentinelDirectBodyClassRegion> {
15416 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
15417 return None;
15418 };
15419 if node.kind() != "function_definition" || !node.has_error() {
15420 return None;
15421 }
15422 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
15423 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
15424 let mut cursor = body.walk();
15425 let candidates = body
15426 .named_children(&mut cursor)
15427 .filter_map(cpp_sentinel_direct_body_class_candidate)
15428 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
15429 .collect::<Vec<_>>();
15430 let [(class_node, template_node)] = candidates.as_slice() else {
15431 return None;
15432 };
15433 let original_body = cpp_body_node(*class_node)?;
15434 let name = class_like_name(*class_node, source, ancestry)?;
15435 if name.is_empty() || cpp_export_macro_token(&name) {
15436 return None;
15437 }
15438
15439 let mut sibling = node.next_named_sibling();
15440 let (class_close_start, class_close_end, class_close_line) = loop {
15441 let current = sibling?;
15442 let next = current.next_named_sibling();
15443 if cpp_is_stray_close_brace(current, source)
15444 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
15445 {
15446 let semicolon = next.expect("checked above");
15447 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
15448 return None;
15449 }
15450 break (
15451 current.start_byte(),
15452 semicolon.end_byte(),
15453 semicolon.end_position().row + 1,
15454 );
15455 }
15456 sibling = next;
15457 };
15458 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
15459 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
15460 let root = tree.root_node();
15461 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
15462 let reparsed_ancestry = ParentIndex::new(root);
15465 let reparsed =
15466 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
15467 if reparsed.name != name
15468 || reparsed.declaration_node.start_byte() != class_node.start_byte()
15469 || reparsed.body.start_byte() != original_body.start_byte()
15470 || class_close_start <= reparsed.body.end_byte()
15471 || class_close_end <= class_node.end_byte()
15472 {
15473 return None;
15474 }
15475 Some(CppSentinelDirectBodyClassRegion {
15476 namespace_components,
15477 class_start: reparse_start,
15478 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
15479 node.start_position().row + 1
15480 }),
15481 class_close_end,
15482 class_close_line,
15483 name,
15484 })
15485}
15486
15487fn cpp_nested_namespace_sentinel<'tree>(
15499 node: Node<'tree>,
15500 source: &str,
15501 ancestry: &ParentIndex<'tree>,
15502) -> Option<CppNestedNamespaceSentinel<'tree>> {
15503 if !node.has_error() {
15504 return None;
15505 }
15506
15507 let (function, mut namespace_components) = if node.kind() == "ERROR" {
15508 let mut cursor = node.walk();
15509 let functions = node
15510 .named_children(&mut cursor)
15511 .filter(|child| child.kind() == "function_definition")
15512 .collect::<Vec<_>>();
15513 let [function] = functions.as_slice() else {
15514 return None;
15515 };
15516 if !function.has_error() {
15517 return None;
15518 }
15519 let mut cursor = node.walk();
15520 let children = node.children(&mut cursor).collect::<Vec<_>>();
15521 let function_index = children
15522 .iter()
15523 .position(|child| same_node(*child, *function))?;
15524 let [outer_keyword, outer_name, outer_open] =
15525 children.get(function_index.checked_sub(3)?..function_index)?
15526 else {
15527 return None;
15528 };
15529 if outer_keyword.kind() != "namespace"
15530 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
15531 || outer_open.kind() != "{"
15532 {
15533 return None;
15534 }
15535 (
15536 *function,
15537 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
15538 )
15539 } else if node.kind() == "function_definition" {
15540 let declaration_list = node.parent()?;
15541 let namespace = declaration_list.parent()?;
15542 if declaration_list.kind() != "declaration_list"
15543 || namespace.kind() != "namespace_definition"
15544 || namespace.child_by_field_name("body") != Some(declaration_list)
15545 {
15546 return None;
15547 }
15548 (node, Vec::new())
15549 } else {
15550 return None;
15551 };
15552
15553 let mut cursor = function.walk();
15554 let named = function
15555 .named_children(&mut cursor)
15556 .filter(|child| child.kind() != "comment")
15557 .collect::<Vec<_>>();
15558 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
15559 return None;
15560 };
15561 if first_type.kind() != "type_identifier" {
15562 return None;
15563 }
15564 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
15565 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
15566 return None;
15567 }
15568 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
15569 return None;
15570 }
15571 let inner_keyword = inner_error.named_child(0)?;
15572 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
15573 return None;
15574 }
15575 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
15576 return None;
15577 }
15578 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
15579 if inner_name.is_empty() || body.kind() != "compound_statement" {
15580 return None;
15581 }
15582 namespace_components.push(inner_name);
15583
15584 let mut cursor = body.walk();
15585 let has_complete_class = body.named_children(&mut cursor).any(|child| {
15586 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
15587 cpp_body_node(class_node).is_some()
15588 && class_like_name(class_node, source, ancestry)
15589 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
15590 })
15591 });
15592 if !has_complete_class
15593 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
15594 {
15595 return None;
15596 }
15597
15598 Some(CppNestedNamespaceSentinel {
15599 function,
15600 body: *body,
15601 namespace_components,
15602 })
15603}
15604
15605fn cpp_root_namespace_sentinel<'tree>(
15614 node: Node<'tree>,
15615 source: &str,
15616 ancestry: &ParentIndex<'tree>,
15617) -> Option<CppNestedNamespaceSentinel<'tree>> {
15618 if node.kind() != "function_definition"
15619 || !node.has_error()
15620 || node.parent()?.kind() != "translation_unit"
15621 {
15622 return None;
15623 }
15624 let first_type = node.child_by_field_name("type")?;
15625 let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
15626 if first_type.kind() != "type_identifier"
15627 || sentinel.is_empty()
15628 || !cpp_export_macro_token(&sentinel)
15629 {
15630 return None;
15631 }
15632 let declarator = node.child_by_field_name("declarator")?;
15633 let body = node.child_by_field_name("body")?;
15634 if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
15635 return None;
15636 }
15637 let mut cursor = node.walk();
15638 let named = node
15639 .named_children(&mut cursor)
15640 .filter(|child| child.kind() != "comment")
15641 .collect::<Vec<_>>();
15642 let [named_type, named_declarator, named_body] = named.as_slice() else {
15643 return None;
15644 };
15645 if !same_node(*named_type, first_type)
15646 || !same_node(*named_declarator, declarator)
15647 || !same_node(*named_body, body)
15648 {
15649 return None;
15650 }
15651 let mut declarator_components = Vec::new();
15652 let mut valid_components = true;
15653 walk_named_tree_preorder(declarator, true, |component| {
15654 if !matches!(
15655 component.kind(),
15656 "identifier" | "namespace_identifier" | "type_identifier"
15657 ) {
15658 return WalkControl::Continue;
15659 }
15660 let Some(component) = canonical_cpp_qualified_component(component, source) else {
15661 valid_components = false;
15662 return WalkControl::Break;
15663 };
15664 declarator_components.push(component.name);
15665 WalkControl::SkipChildren
15666 });
15667 if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
15668 return None;
15669 }
15670 declarator_components.remove(0);
15671 let namespace_components = declarator_components;
15672 if namespace_components.is_empty()
15673 || namespace_components
15674 .iter()
15675 .any(|component| component.is_empty() || cpp_export_macro_token(component))
15676 {
15677 return None;
15678 }
15679
15680 let mut cursor = body.walk();
15681 let has_complete_class = body.named_children(&mut cursor).any(|child| {
15682 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
15683 cpp_body_node(class_node).is_some()
15684 && class_like_name(class_node, source, ancestry)
15685 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
15686 })
15687 });
15688 if !has_complete_class
15689 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
15690 {
15691 return None;
15692 }
15693
15694 Some(CppNestedNamespaceSentinel {
15695 function: node,
15696 body,
15697 namespace_components,
15698 })
15699}
15700
15701fn cpp_sentinel_fragmented_class_tail<'tree>(
15710 function: Node<'tree>,
15711 body: Node<'tree>,
15712 source: &str,
15713 ancestry: &ParentIndex<'tree>,
15714) -> Option<CppSentinelFragmentedClassTail<'tree>> {
15715 let mut cursor = body.walk();
15716 let candidates = body
15717 .named_children(&mut cursor)
15718 .filter_map(|child| {
15719 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
15720 let class_body = cpp_body_node(class_node)?;
15721 if !class_node.has_error() {
15722 return None;
15723 }
15724 let name = class_like_name(class_node, source, ancestry)?;
15725 let raw_supertypes =
15726 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
15727 .then(|| extract_cpp_supertypes(class_node, source));
15728 return Some((
15729 class_node,
15730 template_node,
15731 name,
15732 class_body,
15733 class_body.start_byte().checked_add(1)?,
15734 raw_supertypes,
15735 ));
15736 }
15737 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
15738 Some((
15739 child,
15740 None,
15741 prefix.name,
15742 prefix.open,
15743 prefix.open.end_byte(),
15744 prefix.raw_supertypes,
15745 ))
15746 })
15747 .collect::<Vec<_>>();
15748 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
15749 candidates.as_slice()
15750 else {
15751 return None;
15752 };
15753 if name.is_empty() || cpp_export_macro_token(name) {
15754 return None;
15755 }
15756
15757 let (close, semicolon) =
15758 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
15759
15760 let reparse_end = close.start_byte();
15761 if *reparse_start >= reparse_end {
15762 return None;
15763 }
15764 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
15765 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
15766 return None;
15767 }
15768 let class_range = Range {
15769 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
15770 end_byte: semicolon.end_byte(),
15771 start_line: template_node.map_or(class_node.start_position().row, |node| {
15772 node.start_position().row
15773 }) + 1,
15774 end_line: semicolon.end_position().row + 1,
15775 };
15776 Some(CppSentinelFragmentedClassTail {
15777 class_node: *class_node,
15778 template_node: *template_node,
15779 name: name.clone(),
15780 raw_supertypes: raw_supertypes.clone(),
15781 fragmented: FragmentedExportBody {
15782 reparse_start: *reparse_start,
15783 reparse_end,
15784 class_range,
15785 },
15786 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
15787 })
15788}
15789
15790pub fn cpp_sentinel_recovered_classes(
15799 root: Node<'_>,
15800 source: &str,
15801) -> Vec<CppSentinelRecoveredClass> {
15802 if !root.has_error() {
15803 return Vec::new();
15804 }
15805 let ancestry = ParentIndex::new(root);
15809 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
15810 let mut stack = vec![root];
15811 while let Some(current) = stack.pop() {
15812 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
15813 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
15814 {
15815 let namespace_components = cpp_sentinel_recovered_namespace_components(
15816 recovered.function,
15817 &recovered.namespace_components,
15818 source,
15819 );
15820 let fragmented = cpp_sentinel_fragmented_class_tail(
15821 recovered.function,
15822 recovered.body,
15823 source,
15824 &ancestry,
15825 );
15826 let mut class_candidates = Vec::new();
15827 let mut cursor = recovered.body.walk();
15828 for (class_node, template_node) in recovered
15829 .body
15830 .named_children(&mut cursor)
15831 .filter_map(cpp_sentinel_body_class_candidate)
15832 {
15833 let Some(name) = class_like_name(class_node, source, &ancestry) else {
15834 continue;
15835 };
15836 if name.is_empty() || cpp_export_macro_token(&name) {
15837 continue;
15838 }
15839 let is_fragmented = fragmented
15840 .as_ref()
15841 .is_some_and(|tail| same_node(tail.class_node, class_node));
15842 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
15843 continue;
15844 }
15845 let class_range = if is_fragmented {
15846 fragmented
15847 .as_ref()
15848 .map(|tail| tail.fragmented.class_range)
15849 .expect("fragmented class range is present when class matches")
15850 } else {
15851 cpp_declaration_range(template_node.unwrap_or(class_node))
15852 };
15853 class_candidates.push((class_range, name));
15854 }
15855 if let Some(fragmented) = fragmented
15856 .as_ref()
15857 .filter(|tail| tail.class_node.kind() == "ERROR")
15858 {
15859 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
15860 }
15861
15862 let mut owner_ranges =
15863 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
15864 cpp_sentinel_extend_unique_owner_ranges(
15865 &mut owner_ranges,
15866 cpp_sentinel_recovered_sibling_owner_ranges(
15867 recovered.function,
15868 &namespace_components,
15869 source,
15870 ),
15871 );
15872 for (class_range, name) in class_candidates {
15873 push_cpp_sentinel_recovered_class(
15874 &mut recovered_classes,
15875 cpp_declaration_range(recovered.body),
15876 &namespace_components,
15877 class_range,
15878 name,
15879 &owner_ranges,
15880 );
15881 }
15882
15883 if let Some(declaration_list) = recovered
15884 .function
15885 .parent()
15886 .filter(|parent| parent.kind() == "declaration_list")
15887 {
15888 let outer_namespace =
15889 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
15890 push_cpp_sentinel_sibling_classes(
15891 &mut recovered_classes,
15892 declaration_list,
15893 recovered.function,
15894 &outer_namespace,
15895 source,
15896 &ancestry,
15897 );
15898 }
15899 } else if let Some(region) =
15900 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
15901 {
15902 let namespace_components = cpp_sentinel_recovered_namespace_components(
15903 current,
15904 ®ion.namespace_components,
15905 source,
15906 );
15907 let owner_container = current
15908 .parent()
15909 .filter(|parent| parent.kind() == "declaration_list")
15910 .unwrap_or(current);
15911 let owner_ranges =
15912 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
15913 push_cpp_sentinel_recovered_class(
15914 &mut recovered_classes,
15915 cpp_declaration_range(owner_container),
15916 &namespace_components,
15917 Range {
15918 start_byte: region.class_start,
15919 end_byte: region.class_close_end,
15920 start_line: region.class_start_line,
15921 end_line: region.class_close_line,
15922 },
15923 region.name,
15924 &owner_ranges,
15925 );
15926 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
15927 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
15932 region;
15933 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
15934 continue;
15935 };
15936 let root = tree.root_node();
15937 let template_node = cpp_sentinel_reparsed_leading_template(root);
15938 let reparsed_ancestry = ParentIndex::new(root);
15940 let Some(reparsed_class) =
15941 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
15942 else {
15943 continue;
15944 };
15945 let class_node = reparsed_class.declaration_node;
15946 let name = reparsed_class.name;
15947 let namespace_components =
15948 cpp_sentinel_recovered_namespace_components(current, &[], source);
15949 let owner_container = current
15950 .parent()
15951 .filter(|parent| parent.kind() == "declaration_list")
15952 .unwrap_or(current);
15953 let mut owner_ranges =
15954 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
15955 cpp_sentinel_extend_unique_owner_ranges(
15956 &mut owner_ranges,
15957 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
15958 );
15959 push_cpp_sentinel_recovered_class(
15960 &mut recovered_classes,
15961 cpp_declaration_range(owner_container),
15962 &namespace_components,
15963 Range {
15964 start_byte: class_start,
15965 end_byte: close_end,
15966 start_line: class_node.start_position().row + 1,
15967 end_line: class_node.end_position().row + 1,
15968 },
15969 name,
15970 &owner_ranges,
15971 );
15972 if owner_container.kind() == "declaration_list" {
15973 push_cpp_sentinel_sibling_classes(
15974 &mut recovered_classes,
15975 owner_container,
15976 current,
15977 &namespace_components,
15978 source,
15979 &ancestry,
15980 );
15981 }
15982 }
15983
15984 let mut cursor = current.walk();
15985 stack.extend(current.named_children(&mut cursor));
15986 }
15987 let shadowed = recovered_classes
15993 .iter()
15994 .map(|candidate| {
15995 recovered_classes.iter().any(|container| {
15996 container.class_range.start_byte <= candidate.class_range.start_byte
15997 && container.class_range.end_byte >= candidate.class_range.end_byte
15998 && container.class_range != candidate.class_range
15999 && container.namespace_scope_components.len()
16000 > candidate.namespace_scope_components.len()
16001 && container
16002 .namespace_scope_components
16003 .starts_with(&candidate.namespace_scope_components)
16004 })
16005 })
16006 .collect::<Vec<_>>();
16007 let mut index = 0usize;
16008 recovered_classes.retain(|_| {
16009 let keep = !shadowed[index];
16010 index += 1;
16011 keep
16012 });
16013 recovered_classes
16014}
16015
16016fn push_cpp_sentinel_sibling_classes<'tree>(
16022 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
16023 declaration_list: Node<'tree>,
16024 sentinel_node: Node<'tree>,
16025 namespace_components: &[String],
16026 source: &str,
16027 ancestry: &ParentIndex<'tree>,
16028) {
16029 let owner_ranges =
16030 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
16031 let namespace_range = cpp_declaration_range(declaration_list);
16032 let mut cursor = declaration_list.walk();
16033 for (class_node, template_node) in declaration_list
16034 .named_children(&mut cursor)
16035 .filter(|child| !same_node(*child, sentinel_node))
16036 .filter_map(cpp_sentinel_body_class_candidate)
16037 {
16038 let Some(name) = class_like_name(class_node, source, ancestry) else {
16039 continue;
16040 };
16041 if name.is_empty()
16042 || cpp_export_macro_token(&name)
16043 || cpp_complete_class_body_close(class_node).is_none()
16044 {
16045 continue;
16046 }
16047 push_cpp_sentinel_recovered_class(
16048 recovered_classes,
16049 namespace_range,
16050 namespace_components,
16051 cpp_declaration_range(template_node.unwrap_or(class_node)),
16052 name,
16053 &owner_ranges,
16054 );
16055 }
16056}
16057
16058fn push_cpp_sentinel_recovered_class(
16059 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
16060 namespace_range: Range,
16061 namespace_components: &[String],
16062 class_range: Range,
16063 name: String,
16064 owner_ranges: &[CppSentinelRecoveredOwner],
16065) {
16066 let mut scope_components = namespace_components.to_vec();
16067 scope_components.push(name);
16068 let owner_ranges = owner_ranges
16069 .iter()
16070 .filter(|owner| owner.scope_components.starts_with(&scope_components))
16071 .cloned()
16072 .collect::<Vec<_>>();
16073 if recovered_classes.iter().any(|existing| {
16074 existing.class_range == class_range && existing.scope_components == scope_components
16075 }) {
16076 return;
16077 }
16078 recovered_classes.push(CppSentinelRecoveredClass {
16079 namespace_range,
16080 namespace_scope_components: namespace_components.to_vec(),
16081 class_range,
16082 scope_components,
16083 owner_ranges,
16084 });
16085}
16086
16087fn cpp_sentinel_recovered_namespace_components(
16088 function: Node<'_>,
16089 recovered_components: &[String],
16090 source: &str,
16091) -> Vec<String> {
16092 let mut ancestor_components = Vec::new();
16093 let mut ancestor = function.parent();
16094 while let Some(current) = ancestor {
16095 if current.kind() == "namespace_definition"
16096 && let Some(name_node) = current.child_by_field_name("name")
16097 && let Some(components) = cpp_name_components(name_node, source)
16098 {
16099 ancestor_components.push(
16100 components
16101 .into_iter()
16102 .map(|component| component.name)
16103 .collect::<Vec<_>>(),
16104 );
16105 }
16106 ancestor = current.parent();
16107 }
16108 ancestor_components.reverse();
16109 let mut ancestors = ancestor_components
16110 .into_iter()
16111 .flatten()
16112 .collect::<Vec<_>>();
16113
16114 let overlap = (0..=ancestors.len().min(recovered_components.len()))
16115 .rev()
16116 .find(|length| {
16117 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
16118 })
16119 .unwrap_or(0);
16120 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
16121 ancestors
16122}
16123
16124fn cpp_sentinel_recovered_owner_ranges(
16125 body: Node<'_>,
16126 namespace_components: &[String],
16127 source: &str,
16128) -> Vec<CppSentinelRecoveredOwner> {
16129 let mut owners = Vec::new();
16130 walk_named_tree_preorder(body, true, |node| {
16131 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
16132 });
16133 owners
16134}
16135
16136fn cpp_sentinel_collect_owner_range(
16137 node: Node<'_>,
16138 namespace_components: &[String],
16139 source: &str,
16140 owners: &mut Vec<CppSentinelRecoveredOwner>,
16141) -> WalkControl {
16142 if node.kind() != "function_definition" {
16143 return WalkControl::Continue;
16144 }
16145 let Some(function_declarator) = extract_function_declarator(node) else {
16146 return WalkControl::Continue;
16147 };
16148 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
16149 return WalkControl::Continue;
16150 };
16151 let Some(mut components) = cpp_name_components(name_node, source) else {
16152 return WalkControl::Continue;
16153 };
16154 if components.len() <= 1 {
16155 return WalkControl::Continue;
16156 }
16157 components.pop();
16158 let mut owner_components = components
16159 .into_iter()
16160 .map(|component| component.name)
16161 .collect::<Vec<_>>();
16162 let overlap = (0..=namespace_components.len().min(owner_components.len()))
16163 .rev()
16164 .find(|length| {
16165 owner_components[..*length]
16166 == namespace_components[namespace_components.len().saturating_sub(*length)..]
16167 })
16168 .unwrap_or(0);
16169 let mut scope_components = namespace_components.to_vec();
16170 scope_components.extend(owner_components.drain(overlap..));
16171 if scope_components.len() <= namespace_components.len() {
16172 return WalkControl::Continue;
16173 }
16174 let range = cpp_declaration_range(node);
16175 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
16176 existing.range == range && existing.scope_components == scope_components
16177 }) {
16178 owners.push(CppSentinelRecoveredOwner {
16179 range,
16180 owner_name_start_byte: name_node.start_byte(),
16181 namespace_component_count: namespace_components.len(),
16182 scope_components,
16183 });
16184 }
16185 WalkControl::Continue
16186}
16187
16188fn cpp_sentinel_extend_unique_owner_ranges(
16189 owners: &mut Vec<CppSentinelRecoveredOwner>,
16190 additional: Vec<CppSentinelRecoveredOwner>,
16191) {
16192 for owner in additional {
16193 if !owners.iter().any(|existing| {
16194 existing.range == owner.range && existing.scope_components == owner.scope_components
16195 }) {
16196 owners.push(owner);
16197 }
16198 }
16199}
16200
16201fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
16202 if node.kind() != "ERROR" || node.named_child_count() != 1 {
16203 return false;
16204 }
16205 let Some(end_name) = node.named_child(0) else {
16206 return false;
16207 };
16208 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
16209 return false;
16210 }
16211 let mut cursor = node.walk();
16212 node.children(&mut cursor)
16213 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
16214}
16215
16216fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
16220 parent: Node<'_>,
16221 sentinel_node: Node<'_>,
16222 namespace_components: &[String],
16223 source: &str,
16224) -> Vec<CppSentinelRecoveredOwner> {
16225 let mut owners = Vec::new();
16226 let mut after_sentinel = false;
16227 let mut cursor = parent.walk();
16228 for child in parent.named_children(&mut cursor) {
16229 if !after_sentinel {
16230 if same_node(child, sentinel_node) {
16231 after_sentinel = true;
16232 }
16233 continue;
16234 }
16235 walk_named_tree_preorder(child, true, |node| {
16236 if node.kind() == "namespace_definition" {
16237 return WalkControl::SkipChildren;
16238 }
16239 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
16240 });
16241 }
16242 owners
16243}
16244
16245fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
16249 parent: Node<'_>,
16250 sentinel_node: Node<'_>,
16251 namespace_components: &[String],
16252 source: &str,
16253) -> Option<Vec<CppSentinelRecoveredOwner>> {
16254 let mut owners = Vec::new();
16255 let mut after_namespace = false;
16256 let mut cursor = parent.walk();
16257 for child in parent.named_children(&mut cursor) {
16258 if !after_namespace {
16259 if same_node(child, sentinel_node) {
16260 after_namespace = true;
16261 }
16262 continue;
16263 }
16264 if cpp_sentinel_namespace_end(child, source) {
16265 return Some(owners);
16266 }
16267 walk_named_tree_preorder(child, true, |node| {
16268 if node.kind() == "namespace_definition" {
16269 return WalkControl::SkipChildren;
16270 }
16271 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
16272 });
16273 }
16274 None
16275}
16276
16277fn cpp_sentinel_recovered_sibling_owner_ranges(
16278 sentinel_node: Node<'_>,
16279 namespace_components: &[String],
16280 source: &str,
16281) -> Vec<CppSentinelRecoveredOwner> {
16282 let Some(declaration_list) = sentinel_node
16283 .parent()
16284 .filter(|parent| parent.kind() == "declaration_list")
16285 else {
16286 return Vec::new();
16287 };
16288 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
16289 declaration_list,
16290 sentinel_node,
16291 namespace_components,
16292 source,
16293 );
16294
16295 let Some(namespace) = declaration_list
16296 .parent()
16297 .filter(|parent| parent.kind() == "namespace_definition")
16298 else {
16299 return owners;
16300 };
16301 let Some(outer_parent) = namespace.parent() else {
16302 return owners;
16303 };
16304 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
16305 outer_parent,
16306 namespace,
16307 namespace_components,
16308 source,
16309 ) {
16310 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
16311 }
16312 owners
16313}
16314
16315fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
16316 let mut current = function_declarator.child_by_field_name("declarator")?;
16317 loop {
16318 if let Some(name) = macro_decorated_unqualified_name(current) {
16319 current = name;
16320 continue;
16321 }
16322 if matches!(
16323 current.kind(),
16324 "qualified_identifier"
16325 | "scoped_identifier"
16326 | "scoped_type_identifier"
16327 | "identifier"
16328 | "field_identifier"
16329 | "operator_name"
16330 | "destructor_name"
16331 | "literal_operator_name"
16332 ) {
16333 return Some(current);
16334 }
16335 current = current
16336 .child_by_field_name("declarator")
16337 .or_else(|| current.child_by_field_name("name"))
16338 .or_else(|| last_named_child(current))?;
16339 }
16340}
16341
16342fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
16355 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
16356 return None;
16357 }
16358 let mut cursor = node.walk();
16359 if node
16360 .children(&mut cursor)
16361 .any(|child| child.kind() == "::" && !child.is_missing())
16362 {
16363 return None;
16364 }
16365 node.child_by_field_name("name")
16366}
16367
16368fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
16369 if let Some(name) = macro_decorated_unqualified_name(node) {
16370 return cpp_name_components(name, source);
16371 }
16372 match node.kind() {
16373 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16374 let mut components = match node.child_by_field_name("scope") {
16375 Some(scope) => cpp_name_components(scope, source)?,
16376 None => Vec::new(),
16377 };
16378 let name = node.child_by_field_name("name")?;
16379 components.push(canonical_cpp_qualified_component(name, source)?);
16380 Some(components)
16381 }
16382 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
16383 }
16384}
16385
16386fn cpp_sentinel_fragment_boundary<'tree>(
16387 function: Node<'tree>,
16388 class_node: Node<'tree>,
16389 class_body: Node<'tree>,
16390 source: &str,
16391) -> Option<(Node<'tree>, Node<'tree>)> {
16392 let declaration_list = function.parent()?;
16393 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
16394 return None;
16395 }
16396 let namespace = declaration_list.parent()?;
16397 if namespace.kind() != "namespace_definition"
16398 || namespace.child_by_field_name("body") != Some(declaration_list)
16399 {
16400 return None;
16401 }
16402 let mut cursor = declaration_list.walk();
16403 let closes = declaration_list
16404 .children(&mut cursor)
16405 .filter(|child| {
16406 !child.is_named()
16407 && child.kind() == "}"
16408 && child.start_byte() >= function.end_byte()
16409 && child.start_byte() > class_node.end_byte()
16410 && child.start_byte() > class_body.start_byte()
16411 })
16412 .collect::<Vec<_>>();
16413 let [close] = closes.as_slice() else {
16414 return None;
16415 };
16416 let semicolon = namespace.next_named_sibling()?;
16417 if !cpp_is_stray_semicolon(semicolon, source)
16418 || close.end_byte() != namespace.end_byte()
16419 || semicolon.start_byte() < namespace.end_byte()
16420 {
16421 return None;
16422 }
16423 Some((*close, semicolon))
16424}
16425
16426fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
16452 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
16453 {
16454 return None;
16455 }
16456 let mut declarator_cursor = node.walk();
16462 let preserved_callable = node
16463 .children_by_field_name("declarator", &mut declarator_cursor)
16464 .find_map(extract_function_declarator);
16465 let mut cursor = node.walk();
16473 let first = node
16474 .named_children(&mut cursor)
16475 .find(|child| child.kind() != "comment")?;
16476 if first.kind() != "type_identifier" {
16477 return None;
16478 }
16479 let sentinel = normalize_cpp_whitespace(node_text(first, source));
16480 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
16481 return None;
16482 }
16483 let mut start = first.end_byte();
16490 let mut after_first = false;
16491 let mut cursor = node.walk();
16492 for child in node.named_children(&mut cursor) {
16493 if !after_first {
16494 if same_node(child, first) {
16495 after_first = true;
16496 }
16497 continue;
16498 }
16499 if matches!(child.kind(), "identifier" | "type_identifier")
16500 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
16501 {
16502 start = child.end_byte();
16503 } else {
16504 break;
16505 }
16506 }
16507 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
16516 let mut class_start = None;
16517 let mut template_start = None;
16518 let mut stack = vec![node];
16519 while let Some(current) = stack.pop() {
16520 if current.start_byte() >= prefix_end {
16521 continue;
16522 }
16523 if matches!(
16524 current.kind(),
16525 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
16526 ) {
16527 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
16528 "class" | "struct" | "union" | "enum" => {
16529 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
16530 seen.min(current.start_byte())
16531 }));
16532 }
16533 "template" => {
16534 template_start =
16535 Some(template_start.map_or(current.start_byte(), |seen: usize| {
16536 seen.min(current.start_byte())
16537 }));
16538 }
16539 _ => {}
16540 }
16541 }
16542 let mut cursor = current.walk();
16543 stack.extend(current.children(&mut cursor));
16544 }
16545 if preserved_callable.is_some_and(|callable| {
16546 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
16547 }) {
16548 return None;
16549 }
16550 if let Some(class_start) = class_start {
16551 start = template_start
16552 .filter(|template_start| *template_start < class_start)
16553 .unwrap_or(class_start);
16554 }
16555 Some((start, class_start))
16556}
16557
16558fn cpp_sentinel_macro_class_region<'tree>(
16564 node: Node<'tree>,
16565 source: &str,
16566) -> Option<(usize, usize, usize, usize, usize, usize)> {
16567 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
16568 return None;
16569 };
16570 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
16571 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
16572 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
16573 if class_start >= body_open_start {
16574 return None;
16575 }
16576 let sibling_close = {
16577 let mut sibling = node.next_named_sibling();
16578 let mut found = None;
16579 while let Some(current) = sibling {
16580 let next = current.next_named_sibling();
16581 if cpp_is_stray_close_brace(current, source)
16582 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
16583 {
16584 let semicolon = next.expect("checked above");
16585 found = Some((
16586 current.start_byte(),
16587 semicolon.end_byte(),
16588 semicolon.end_position().row + 1,
16589 ));
16590 break;
16591 }
16592 sibling = next;
16593 }
16594 found
16595 };
16596 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
16609 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
16610 return false;
16611 };
16612 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
16613 let reparsed_ancestry = ParentIndex::new(tree.root_node());
16615 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
16616 tree.root_node(),
16617 template_node,
16618 source,
16619 &reparsed_ancestry,
16620 ) else {
16621 return false;
16622 };
16623 let body = reparsed_class.body;
16624 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
16625 });
16626 let (class_close_start, class_close_end, class_close_line) =
16627 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
16628 (class_close_start, class_close_end, class_close_line)
16629 } else {
16630 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
16637 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
16638 let reparsed_ancestry = ParentIndex::new(tree.root_node());
16640 let reparsed_class = cpp_sentinel_reparsed_class(
16641 tree.root_node(),
16642 template_node,
16643 source,
16644 &reparsed_ancestry,
16645 )?;
16646 let body = reparsed_class.body;
16647 let class_close_end = body.end_byte();
16648 let class_close_start = class_close_end.checked_sub(1)?;
16649 let class_close_line = body.end_position().row + 1;
16650 (class_close_start, class_close_end, class_close_line)
16651 };
16652 if class_close_start <= class_start {
16653 return None;
16654 }
16655
16656 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
16660 let class_root = tree.root_node();
16661 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
16662 let reparsed_ancestry = ParentIndex::new(class_root);
16664 let reparsed_class =
16665 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
16666 let body = reparsed_class.body;
16667 if body.start_byte() != body_open_start {
16671 return None;
16672 }
16673 let body_start = body.start_byte().checked_add(1)?;
16674 (body_start < class_close_start).then_some((
16675 reparse_start,
16676 class_start,
16677 body_start,
16678 class_close_start,
16679 class_close_end,
16680 class_close_line,
16681 ))
16682}
16683
16684fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
16689 let mut stack = vec![node];
16690 while let Some(current) = stack.pop() {
16691 if current.start_byte() == class_start
16692 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
16693 {
16694 let mut sibling = current.next_sibling();
16695 while let Some(candidate) = sibling {
16696 if candidate.kind() == "{" {
16697 return Some(candidate.start_byte());
16698 }
16699 sibling = candidate.next_sibling();
16700 }
16701 }
16702 let mut cursor = current.walk();
16703 stack.extend(current.children(&mut cursor));
16704 }
16705 None
16706}
16707
16708fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
16719 node.next_named_sibling()
16720 .filter(|sibling| sibling.kind() == "compound_statement")
16721}
16722
16723fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
16724 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
16725 let mut end = if class_start.is_some() {
16726 cpp_macro_prefixed_class_end(source, start)?
16727 } else {
16728 node.end_byte()
16729 };
16730 if class_start.is_none()
16731 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
16732 {
16733 end = end.max(namespace_end);
16734 }
16735 let mut sibling = node.next_named_sibling();
16736 while let Some(current) = sibling {
16737 if !cpp_is_stray_semicolon(current, source) {
16738 break;
16739 }
16740 end = current.end_byte();
16741 sibling = current.next_named_sibling();
16742 }
16743 (start < end).then_some((start, end))
16744}
16745
16746fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
16757 let mut sibling = node.next_sibling();
16758 let keyword = loop {
16759 let candidate = sibling?;
16760 sibling = candidate.next_sibling();
16761 if candidate.kind() != "comment" {
16762 break candidate;
16763 }
16764 };
16765 if keyword.kind() != "namespace" {
16766 return None;
16767 }
16768 let name = loop {
16769 let candidate = sibling?;
16770 sibling = candidate.next_sibling();
16771 if candidate.kind() != "comment" {
16772 break candidate;
16773 }
16774 };
16775 if cpp_namespace_name_components(name, source).is_empty() {
16776 return None;
16777 }
16778 let open = loop {
16779 let candidate = sibling?;
16780 sibling = candidate.next_sibling();
16781 if candidate.kind() != "comment" {
16782 break candidate;
16783 }
16784 };
16785 if open.kind() != "{" {
16786 return None;
16787 }
16788
16789 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
16790 let root = tree.root_node();
16791 let mut cursor = root.walk();
16792 let namespace = root
16793 .named_children(&mut cursor)
16794 .find(|candidate| candidate.kind() != "comment")?;
16795 (namespace.kind() == "namespace_definition"
16796 && namespace.start_byte() == keyword.start_byte()
16797 && namespace.child_by_field_name("body").is_some())
16798 .then_some(namespace.end_byte())
16799}
16800
16801fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
16807 let tree = cpp_reparse_region_items(source, start, source.len())?;
16808 let root = tree.root_node();
16809 let mut cursor = root.walk();
16810 for item in root.named_children(&mut cursor) {
16811 if item.end_byte() <= start || item.kind() == "comment" {
16812 continue;
16813 }
16814 let mut stack = vec![item];
16815 while let Some(current) = stack.pop() {
16816 if matches!(
16817 current.kind(),
16818 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
16819 ) && cpp_body_node(current).is_some()
16820 {
16821 return Some(current.end_byte());
16822 }
16823 let mut cursor = current.walk();
16824 stack.extend(current.named_children(&mut cursor));
16825 }
16826 return None;
16830 }
16831 None
16832}
16833
16834fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
16837 node.kind() == "expression_statement"
16838 && node.named_child_count() == 0
16839 && node_text(node, source).trim() == ";"
16840}
16841
16842#[derive(Clone, Copy)]
16852pub(crate) struct RecoveredPyObjectHeadField<'tree> {
16853 pub(crate) type_node: Node<'tree>,
16854 pub(crate) name: Node<'tree>,
16855 pub(crate) declarator: Node<'tree>,
16856}
16857
16858impl RecoveredPyObjectHeadField<'_> {
16859 pub(crate) fn pointer_depth(self) -> i32 {
16860 let mut depth = 0;
16861 let mut current = self.declarator;
16862 while current != self.name {
16863 debug_assert_eq!(current.kind(), "pointer_declarator");
16864 depth += 1;
16865 current = current
16866 .child_by_field_name("declarator")
16867 .expect("recovered PyObject field pointer has an inner declarator");
16868 }
16869 depth
16870 }
16871}
16872
16873pub(crate) fn recovered_pyobject_head_field<'tree>(
16874 node: Node<'tree>,
16875 source: &str,
16876) -> Option<RecoveredPyObjectHeadField<'tree>> {
16877 if node.kind() != "field_declaration" {
16878 return None;
16879 }
16880 let type_node = node.child_by_field_name("type")?;
16881 if type_node.kind() != "type_identifier"
16882 || node_text(type_node, source).trim() != "PyObject_HEAD"
16883 {
16884 return None;
16885 }
16886 let pseudo_declarator = node.child_by_field_name("declarator")?;
16887 let mut cursor = node.walk();
16888 let errors = node
16889 .named_children(&mut cursor)
16890 .filter(|child| child.kind() == "ERROR")
16891 .collect::<Vec<_>>();
16892 let [error] = errors.as_slice() else {
16893 return None;
16894 };
16895 if error.named_child_count() != 1 {
16896 return None;
16897 }
16898 let error_child = error.named_child(0)?;
16899 if pseudo_declarator.kind() == "field_identifier"
16900 && error.start_byte() >= pseudo_declarator.end_byte()
16901 && error_child.kind() == "identifier"
16902 {
16903 return Some(RecoveredPyObjectHeadField {
16904 type_node: pseudo_declarator,
16905 name: error_child,
16906 declarator: error_child,
16907 });
16908 }
16909 if pseudo_declarator.kind() != "pointer_declarator"
16910 || error.end_byte() > pseudo_declarator.start_byte()
16911 || error_child.kind() != "identifier"
16912 {
16913 return None;
16914 }
16915 let mut name = pseudo_declarator;
16916 while name.kind() == "pointer_declarator" {
16917 name = name.child_by_field_name("declarator")?;
16918 }
16919 (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
16920 type_node: error_child,
16921 name,
16922 declarator: pseudo_declarator,
16923 })
16924}
16925
16926fn recovered_macro_qualified_field_declarators<'tree>(
16935 node: Node<'tree>,
16936 source: &str,
16937) -> Option<Vec<Node<'tree>>> {
16938 if node.kind() != "field_declaration" {
16939 return None;
16940 }
16941 let macro_type = node.child_by_field_name("type")?;
16942 if macro_type.kind() != "type_identifier"
16943 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
16944 {
16945 return None;
16946 }
16947 let pseudo_declarator = node.child_by_field_name("declarator")?;
16948 if pseudo_declarator.kind() != "field_identifier" {
16949 return None;
16950 }
16951 let mut cursor = node.walk();
16952 let clause = node
16953 .named_children(&mut cursor)
16954 .find(|child| child.kind() == "bitfield_clause")?;
16955 if !(0..clause.named_child_count()).any(|index| {
16956 clause
16957 .named_child(index)
16958 .is_some_and(|child| child.kind() == "ERROR")
16959 }) {
16960 return None;
16961 }
16962 let mut recovered = Vec::new();
16963 let mut stack = vec![clause];
16964 while let Some(current) = stack.pop() {
16965 if current.kind() == "assignment_expression"
16966 && let Some(left) = current.child_by_field_name("left")
16967 && extract_variable_name(left, source).is_some()
16968 {
16969 recovered.push(left);
16970 break;
16971 }
16972 let mut cursor = current.walk();
16973 stack.extend(current.named_children(&mut cursor));
16974 }
16975 if recovered.is_empty() {
16976 return None;
16977 }
16978 let mut cursor = node.walk();
16979 recovered.extend(
16980 node.children_by_field_name("declarator", &mut cursor)
16981 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
16982 );
16983 Some(recovered)
16984}
16985
16986fn recovered_macro_qualified_constructor_call<'tree>(
16992 node: Node<'tree>,
16993 class_name: &str,
16994 source: &str,
16995) -> Option<Node<'tree>> {
16996 if node.kind() != "field_declaration" {
16997 return None;
16998 }
16999 let macro_type = node.child_by_field_name("type")?;
17000 if macro_type.kind() != "type_identifier"
17001 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
17002 {
17003 return None;
17004 }
17005 let mut cursor = node.walk();
17006 let bitfield = node
17007 .named_children(&mut cursor)
17008 .find(|child| child.kind() == "bitfield_clause")?;
17009 let error = bitfield
17010 .named_child(0)
17011 .filter(|child| child.kind() == "ERROR")?;
17012 let mut stack = vec![error];
17013 while let Some(current) = stack.pop() {
17014 if current.kind() == "call_expression"
17015 && current
17016 .child_by_field_name("function")
17017 .is_some_and(|function| node_text(function, source) == class_name)
17018 && current
17019 .child_by_field_name("arguments")
17020 .is_some_and(|arguments| arguments.kind() == "argument_list")
17021 {
17022 return Some(current);
17023 }
17024 let mut cursor = current.walk();
17025 stack.extend(current.named_children(&mut cursor));
17026 }
17027 None
17028}
17029
17030fn recovered_macro_qualified_function_call<'tree>(
17038 node: Node<'tree>,
17039 source: &str,
17040) -> Option<Node<'tree>> {
17041 if node.kind() != "field_declaration" {
17042 return None;
17043 }
17044 let macro_type = node.child_by_field_name("type")?;
17045 if macro_type.kind() != "type_identifier"
17046 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
17047 {
17048 return None;
17049 }
17050 let declarator = node.child_by_field_name("declarator")?;
17051 if declarator.kind() != "field_identifier" {
17052 return None;
17053 }
17054 let mut cursor = node.walk();
17055 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
17056 if !named.iter().any(|child| {
17057 child.kind() == "storage_class_specifier"
17058 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
17059 }) {
17060 return None;
17061 }
17062 let bitfield = named
17063 .iter()
17064 .find(|child| child.kind() == "bitfield_clause")?;
17065 let mut bitfield_cursor = bitfield.walk();
17066 let payload = bitfield
17067 .named_children(&mut bitfield_cursor)
17068 .collect::<Vec<_>>();
17069 let [displaced_error, call] = payload.as_slice() else {
17070 return None;
17071 };
17072 if displaced_error.kind() != "ERROR"
17073 || displaced_error.named_child_count() != 1
17074 || displaced_error
17075 .named_child(0)
17076 .is_none_or(|child| child.kind() != "identifier")
17077 || call.kind() != "call_expression"
17078 || call
17079 .child_by_field_name("function")
17080 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
17081 || call
17082 .child_by_field_name("arguments")
17083 .is_none_or(|arguments| arguments.kind() != "argument_list")
17084 {
17085 return None;
17086 }
17087 Some(*call)
17088}
17089
17090fn recovered_macro_qualified_function_parameters(
17091 arguments: Node<'_>,
17092 source: &str,
17093) -> Option<(String, Vec<String>)> {
17094 if arguments.kind() != "argument_list" {
17095 return None;
17096 }
17097 let mut cursor = arguments.walk();
17098 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
17099 if named.is_empty() {
17100 return Some(("()".to_string(), Vec::new()));
17101 }
17102 let mut types = Vec::new();
17103 let mut labels = Vec::new();
17104 let mut index = 0;
17105 while index < named.len() {
17106 let parameter_type = named[index];
17107 let parameter_name = named.get(index + 1).copied()?;
17108 if !matches!(
17109 parameter_type.kind(),
17110 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
17111 ) || parameter_name.kind() != "ERROR"
17112 || parameter_name.named_child_count() != 1
17113 || parameter_name
17114 .named_child(0)
17115 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
17116 {
17117 return None;
17118 }
17119 let parameter_name = parameter_name.named_child(0)?;
17120 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
17121 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
17122 index += 2;
17123 }
17124 Some((format!("({})", types.join(", ")), labels))
17125}
17126
17127pub fn recovered_macro_return_type_node<'tree>(
17139 node: Node<'tree>,
17140 source: &str,
17141) -> Option<Node<'tree>> {
17142 if node.kind() != "field_declaration" {
17143 return None;
17144 }
17145 let macro_type = node.child_by_field_name("type")?;
17146 if macro_type.kind() != "type_identifier"
17147 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
17148 {
17149 return None;
17150 }
17151 let declarator = node.child_by_field_name("declarator")?;
17152 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
17153 return None;
17154 }
17155 let mut has_missing_semicolon = false;
17156 let mut has_real_semicolon = false;
17157 for child in children_iter(node) {
17158 if child.kind() != ";" {
17159 continue;
17160 }
17161 if child.is_missing() {
17162 has_missing_semicolon = true;
17163 } else {
17164 has_real_semicolon = true;
17165 }
17166 }
17167 if !has_missing_semicolon || has_real_semicolon {
17168 return None;
17169 }
17170 let mut next = node.next_named_sibling();
17171 while next.is_some_and(|sibling| sibling.kind() == "comment") {
17172 next = next.and_then(|sibling| sibling.next_named_sibling());
17173 }
17174 let next = next?;
17175 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
17176 return None;
17177 }
17178 let function_declarator = next.child_by_field_name("declarator")?;
17179 extract_function_declarator(function_declarator).map(|_| declarator)
17180}
17181
17182pub(crate) fn cpp_active_template_type_parameter<'tree>(
17189 node: Node<'tree>,
17190 name: &str,
17191 source: &str,
17192 ancestry: &ParentIndex<'tree>,
17193) -> bool {
17194 let mut ancestor = ancestry.parent(node);
17195 while let Some(current) = ancestor {
17196 if current.kind() == "template_declaration"
17197 && let Some(parameters) = current.child_by_field_name("parameters")
17198 {
17199 let mut cursor = parameters.walk();
17200 if parameters.named_children(&mut cursor).any(|parameter| {
17201 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
17202 && cpp_template_parameter_name(parameter, source)
17203 .is_some_and(|parameter_name| parameter_name == name)
17204 }) {
17205 return true;
17206 }
17207 }
17208 ancestor = ancestry.parent(current);
17209 }
17210 false
17211}
17212
17213fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
17219 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
17220}
17221
17222fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
17223 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
17224 return None;
17225 }
17226 let semicolon = node.next_sibling()?;
17227 if semicolon.kind() != ";" || semicolon.is_missing() {
17228 return None;
17229 }
17230 let row = node.start_position().row;
17231 let mut start = node.start_byte();
17232 let mut sibling = node.prev_sibling();
17233 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
17234 if previous.kind() == ";" {
17235 break;
17236 }
17237 start = previous.start_byte();
17238 sibling = previous.prev_sibling();
17239 }
17240 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
17241}
17242
17243struct PrototypeMacroCandidate {
17247 run_start: usize,
17249 identifier_start: usize,
17252 inner_open_start: usize,
17256 inner_close_end: usize,
17258 outer_close_end: usize,
17260 semicolon_end: usize,
17263}
17264
17265impl PrototypeMacroCandidate {
17266 fn ranges(&self) -> [(usize, usize); 3] {
17272 [
17273 (self.run_start, self.identifier_start),
17274 (self.inner_open_start, self.inner_close_end),
17275 (self.outer_close_end, self.semicolon_end),
17276 ]
17277 }
17278}
17279
17280fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
17282 node.child(node.child_count().checked_sub(1)?)
17283 .filter(|child| child.kind() == ";" && !child.is_missing())
17284}
17285
17286fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
17291 matches!(
17292 node.kind(),
17293 "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
17294 ) && matches!(
17295 normalize_cpp_whitespace(node_text(node, source)).as_str(),
17296 "_" | "__P" | "OF" | "PROTO"
17297 )
17298}
17299
17300fn cpp_prototype_macro_qualified_parts<'tree>(
17304 node: Node<'tree>,
17305 source: &str,
17306) -> Option<(Node<'tree>, Node<'tree>)> {
17307 if node.kind() != "qualified_identifier" {
17308 return None;
17309 }
17310 let declared_name = node
17311 .child_by_field_name("scope")
17312 .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
17313 let macro_name = macro_decorated_unqualified_name(node)?;
17314 cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
17315}
17316
17317fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
17323 if arguments.kind() != "argument_list"
17324 || arguments.named_child_count() != 1
17325 || arguments.child_count() != 3
17326 || arguments
17327 .child(0)
17328 .is_none_or(|open| open.kind() != "(" || open.is_missing())
17329 || arguments
17330 .child(2)
17331 .is_none_or(|close| close.kind() != ")" || close.is_missing())
17332 {
17333 return None;
17334 }
17335 let inner = arguments.named_child(0)?;
17336 let close_index = match inner.kind() {
17337 "parenthesized_expression" => inner.child_count().checked_sub(1)?,
17338 "cast_expression" => inner.child_count().checked_sub(2)?,
17341 _ => return None,
17342 };
17343 (inner
17344 .child(0)
17345 .is_some_and(|open| open.kind() == "(" && !open.is_missing())
17346 && inner
17347 .child(close_index)
17348 .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
17349 .then_some(inner)
17350}
17351
17352fn cpp_prototype_macro_candidate_from_init_declaration(
17353 declaration: Node<'_>,
17354 source: &str,
17355) -> Option<PrototypeMacroCandidate> {
17356 let init = declaration
17357 .child_by_field_name("declarator")
17358 .filter(|declarator| declarator.kind() == "init_declarator")?;
17359 let malformed_declarator = init.child_by_field_name("declarator")?;
17360 let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
17361 cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
17362 } else {
17363 if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
17364 return None;
17365 }
17366 let declared_name_error = init
17367 .prev_named_sibling()
17368 .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
17369 let declared_name = declared_name_error
17370 .named_child(0)
17371 .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
17372 (declared_name, malformed_declarator)
17373 };
17374 let arguments = init
17375 .child_by_field_name("value")
17376 .filter(|value| value.kind() == "argument_list")?;
17377 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
17378 let semicolon = cpp_direct_semicolon(declaration)?;
17379 let return_type = declaration.child_by_field_name("type")?;
17380 if return_type.end_byte() > declared_name.start_byte()
17381 || declared_name.end_byte() > macro_name.start_byte()
17382 || macro_name.end_byte() > arguments.start_byte()
17383 || arguments.end_byte() > semicolon.start_byte()
17384 {
17385 return None;
17386 }
17387 Some(PrototypeMacroCandidate {
17388 run_start: declaration.start_byte(),
17389 identifier_start: macro_name.start_byte(),
17390 inner_open_start: inner.start_byte(),
17391 inner_close_end: inner.end_byte(),
17392 outer_close_end: arguments.end_byte(),
17393 semicolon_end: semicolon.end_byte(),
17394 })
17395}
17396
17397fn cpp_prototype_macro_candidate_from_qualified_declaration(
17398 declaration: Node<'_>,
17399 source: &str,
17400) -> Option<PrototypeMacroCandidate> {
17401 let qualified = declaration
17402 .child_by_field_name("declarator")
17403 .filter(|declarator| declarator.kind() == "qualified_identifier")?;
17404 let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
17405 let open_error = qualified
17406 .next_named_sibling()
17407 .filter(|next| next.kind() == "ERROR")?;
17408 let close_error = last_named_child(declaration)
17409 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
17410 if open_error.child_count() < 3
17411 || open_error
17412 .child(0)
17413 .is_none_or(|open| open.kind() != "(" || open.is_missing())
17414 || open_error
17415 .child(1)
17416 .is_none_or(|open| open.kind() != "(" || open.is_missing())
17417 || close_error.child_count() != 2
17418 || close_error
17419 .child(0)
17420 .is_none_or(|close| close.kind() != ")" || close.is_missing())
17421 || close_error
17422 .child(1)
17423 .is_none_or(|close| close.kind() != ")" || close.is_missing())
17424 {
17425 return None;
17426 }
17427 let inner_open = open_error.child(1)?;
17428 let inner_close = close_error.child(0)?;
17429 let outer_close = close_error.child(1)?;
17430 let semicolon = cpp_direct_semicolon(declaration)?;
17431 let return_type = declaration.child_by_field_name("type")?;
17432 if return_type.end_byte() > qualified.start_byte()
17433 || macro_name.end_byte() > open_error.start_byte()
17434 || inner_open.start_byte() > inner_close.end_byte()
17435 || inner_close.end_byte() > outer_close.start_byte()
17436 || outer_close.end_byte() > semicolon.start_byte()
17437 {
17438 return None;
17439 }
17440 Some(PrototypeMacroCandidate {
17441 run_start: declaration.start_byte(),
17442 identifier_start: macro_name.start_byte(),
17443 inner_open_start: inner_open.start_byte(),
17444 inner_close_end: inner_close.end_byte(),
17445 outer_close_end: outer_close.end_byte(),
17446 semicolon_end: semicolon.end_byte(),
17447 })
17448}
17449
17450fn cpp_prototype_macro_candidate_from_pointer_expression(
17451 statement: Node<'_>,
17452 source: &str,
17453) -> Option<PrototypeMacroCandidate> {
17454 if statement.kind() != "expression_statement"
17455 || statement.named_child_count() != 1
17456 || !statement.has_error()
17457 {
17458 return None;
17459 }
17460 let expansion = statement
17461 .named_child(0)
17462 .filter(|child| child.kind() == "parameter_pack_expansion")?;
17463 let binary = expansion
17464 .child_by_field_name("pattern")
17465 .filter(|pattern| pattern.kind() == "binary_expression")?;
17466 if binary.child_count() != 3
17467 || binary
17468 .child(1)
17469 .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
17470 || expansion
17471 .child(expansion.child_count().checked_sub(1)?)
17472 .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
17473 {
17474 return None;
17475 }
17476 let return_type = binary.child_by_field_name("left")?;
17477 let call = binary
17478 .child_by_field_name("right")
17479 .filter(|right| right.kind() == "call_expression")?;
17480 let qualified = call.child_by_field_name("function")?;
17481 let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
17482 let arguments = call
17483 .child_by_field_name("arguments")
17484 .filter(|arguments| arguments.kind() == "argument_list")?;
17485 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
17486 let semicolon = cpp_direct_semicolon(statement)?;
17487 if return_type.end_byte() > declared_name.start_byte()
17488 || macro_name.end_byte() > arguments.start_byte()
17489 || arguments.end_byte() > semicolon.start_byte()
17490 {
17491 return None;
17492 }
17493 Some(PrototypeMacroCandidate {
17494 run_start: statement.start_byte(),
17495 identifier_start: macro_name.start_byte(),
17496 inner_open_start: inner.start_byte(),
17497 inner_close_end: inner.end_byte(),
17498 outer_close_end: arguments.end_byte(),
17499 semicolon_end: semicolon.end_byte(),
17500 })
17501}
17502
17503fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
17508 let candidate = match node.kind() {
17509 "declaration" if node.has_error() => {
17510 cpp_prototype_macro_candidate_from_init_declaration(node, source)
17511 .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
17512 }
17513 "expression_statement" => {
17514 cpp_prototype_macro_candidate_from_pointer_expression(node, source)
17515 }
17516 _ => None,
17517 };
17518 candidate.into_iter().collect()
17519}
17520
17521fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
17522 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
17523 return false;
17524 }
17525 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
17526 return false;
17527 }
17528 let Some(declarator) = (if node.kind() == "function_definition" {
17529 node.child_by_field_name("declarator")
17530 .and_then(extract_function_declarator)
17531 } else {
17532 node.named_child(0)
17533 .filter(|child| child.kind() == "function_declarator")
17534 }) else {
17535 return false;
17536 };
17537 let Some(name) = cpp_function_declarator_name_node(declarator) else {
17538 return false;
17539 };
17540 declarator.start_byte() == node.start_byte()
17541 && name.kind() == "identifier"
17542 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
17543}
17544
17545fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
17558 let region = cpp_reparse_region_items(source, start, end);
17559
17560 #[cfg(debug_assertions)]
17561 assert_eq!(
17562 region.as_ref().map(cpp_tree_shape),
17563 cpp_reparse_padded_class_body(source, start, end)
17564 .as_ref()
17565 .map(cpp_tree_shape),
17566 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
17567 prefix produces"
17568 );
17569
17570 region
17571}
17572
17573#[cfg(any(debug_assertions, test))]
17577fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
17578 if start >= end {
17579 return None;
17583 }
17584 let bytes = source.as_bytes();
17585 let prefix = bytes.get(..start)?;
17586 let interior = bytes.get(start..end)?;
17587 let mut padded = Vec::with_capacity(end);
17588 padded.extend(
17589 prefix
17590 .iter()
17591 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
17592 );
17593 padded.extend_from_slice(interior);
17594 let padded = String::from_utf8(padded).ok()?;
17595 let mut parser = Parser::new();
17596 parser
17597 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17598 .ok()?;
17599 parser.parse(&padded, None)
17600}
17601
17602#[cfg(any(debug_assertions, test))]
17606fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
17607 let mut shape = Vec::new();
17608 let mut cursor = tree.root_node().walk();
17609 let mut stack = vec![tree.root_node()];
17610 while let Some(node) = stack.pop() {
17611 shape.push((
17612 node.kind(),
17613 node.start_byte(),
17614 node.end_byte(),
17615 node.start_position().row,
17616 node.start_position().column,
17617 node.is_named(),
17618 node.is_missing(),
17619 ));
17620 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
17621 stack.extend(children.into_iter().rev());
17622 }
17623 shape
17624}
17625
17626fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
17647 let mut cursor = root.walk();
17648 let mut saw_item = false;
17649 for child in root.named_children(&mut cursor) {
17650 match child.kind() {
17651 "comment" => {}
17652 "function_definition" => {
17653 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
17654 return false;
17655 }
17656 saw_item = true;
17657 }
17658 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
17659 _ => return false,
17660 }
17661 }
17662 saw_item
17663}
17664
17665fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
17675 if node.kind() != "ERROR" {
17676 return false;
17677 }
17678 let mut stack = Vec::new();
17679 let mut saw_function_declarator = false;
17680 let mut cursor = node.walk();
17681 for child in node.named_children(&mut cursor) {
17682 stack.push(child);
17683 }
17684 while let Some(current) = stack.pop() {
17685 match current.kind() {
17686 "ERROR" => {
17690 let mut cursor = current.walk();
17691 stack.extend(current.named_children(&mut cursor));
17692 }
17693 "function_declarator" => saw_function_declarator = true,
17694 _ => return false,
17695 }
17696 }
17697 saw_function_declarator
17698}
17699
17700fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
17701 if node.kind() != "ERROR" {
17702 return false;
17703 }
17704 let mut cursor = node.walk();
17705 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
17706 let [explicit, constructor_error, destructor] = named.as_slice() else {
17707 return false;
17708 };
17709 let Some(constructor) = constructor_error.named_child(0) else {
17710 return false;
17711 };
17712 let Some(constructor_name) =
17713 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
17714 else {
17715 return false;
17716 };
17717 let Some(destructor_name) =
17718 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
17719 else {
17720 return false;
17721 };
17722 let Some(destroyed_type) = destructor_name.named_child(0) else {
17723 return false;
17724 };
17725 explicit.kind() == "explicit_function_specifier"
17726 && constructor_error.kind() == "ERROR"
17727 && constructor_error.named_child_count() == 1
17728 && constructor.kind() == "function_declarator"
17729 && constructor_name.kind() == "identifier"
17730 && destructor.kind() == "function_declarator"
17731 && destructor_name.kind() == "destructor_name"
17732 && destroyed_type.kind() == "identifier"
17733 && node_text(constructor_name, source) == node_text(destroyed_type, source)
17734}
17735
17736fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
17737 if node.kind() != "compound_statement" {
17738 return false;
17739 }
17740 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
17741 return false;
17742 };
17743 if prefix.kind() == "labeled_statement"
17744 && prefix.named_child(0).is_some_and(|label| {
17745 matches!(
17746 node_text(label, source).trim(),
17747 "public" | "private" | "protected"
17748 )
17749 })
17750 {
17751 return prefix.named_children(&mut prefix.walk()).any(|child| {
17752 child.kind() == "declaration"
17753 && child.has_error()
17754 && child
17755 .named_children(&mut child.walk())
17756 .any(cpp_reparsed_member_error_is_indexable)
17757 });
17758 }
17759 prefix.kind() == "declaration"
17764 && prefix.has_error()
17765 && prefix
17766 .named_children(&mut prefix.walk())
17767 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
17768}
17769
17770fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
17771 if !cpp_reparsed_member_error_is_indexable(node) {
17772 return false;
17773 }
17774 let Some(preproc) = node.next_named_sibling() else {
17775 return false;
17776 };
17777 preproc.kind() == "preproc_if"
17778 && preproc.has_error()
17779 && preproc
17780 .named_children(&mut preproc.walk())
17781 .any(|child| child.kind() == "expression_statement" && child.has_error())
17782 && preproc
17783 .next_named_sibling()
17784 .is_some_and(|body| body.kind() == "compound_statement")
17785}
17786
17787fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
17792 if node.kind() != "function_definition" {
17793 return None;
17794 }
17795 let body = node.child_by_field_name("body")?;
17796 if body.kind() != "compound_statement" {
17797 return None;
17798 }
17799 let open = body.child(0)?;
17800 let close = body.child(body.child_count().checked_sub(1)?)?;
17801 if open.kind() != "{"
17802 || open.is_missing()
17803 || close.kind() != "}"
17804 || close.is_missing()
17805 || close.end_byte() != body.end_byte()
17806 || body.end_byte() != node.end_byte()
17807 {
17808 return None;
17809 }
17810 Some(body)
17811}
17812
17813fn cpp_reparsed_member_function_errors_are_in_body(
17814 node: Node<'_>,
17815 body: Node<'_>,
17816 source: &str,
17817) -> bool {
17818 let mut cursor = node.walk();
17819 node.children(&mut cursor).all(|child| {
17820 same_node(child, body)
17821 || cpp_reparsed_member_attribute_error(child, source)
17822 || cpp_reparsed_member_signature_identifier_errors(child)
17823 || (!child.has_error() && !child.is_error() && !child.is_missing())
17824 })
17825}
17826
17827fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
17835 if !node.has_error() && !node.is_error() && !node.is_missing() {
17836 return false;
17837 }
17838 let mut stack = vec![node];
17839 let mut saw_error = false;
17840 while let Some(current) = stack.pop() {
17841 if current.is_missing() {
17842 return false;
17843 }
17844 if current.kind() == "ERROR" {
17845 saw_error = true;
17846 let mut cursor = current.walk();
17847 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
17848 if children
17849 .iter()
17850 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
17851 {
17852 return false;
17853 }
17854 stack.extend(children);
17855 continue;
17856 }
17857 let mut cursor = current.walk();
17858 stack.extend(current.children(&mut cursor));
17859 }
17860 saw_error
17861}
17862
17863fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
17864 node.kind() == "ERROR"
17865 && node.named_child_count() == 1
17866 && node.named_child(0).is_some_and(|attribute| {
17867 attribute.kind() == "identifier"
17868 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
17869 })
17870}
17871
17872fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
17878 let Some(body) = cpp_reparsed_member_function_body(node) else {
17879 return false;
17880 };
17881 let mut cursor = node.walk();
17882 let named = node
17883 .named_children(&mut cursor)
17884 .filter(|child| child.kind() != "comment")
17885 .collect::<Vec<_>>();
17886 let [type_node, error, attribute, body_node] = named.as_slice() else {
17887 return false;
17888 };
17889 if !same_node(*body_node, body)
17890 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
17891 || attribute.kind() != "identifier"
17892 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
17893 || error.kind() != "ERROR"
17894 || error.named_child_count() != 1
17895 {
17896 return false;
17897 }
17898 error
17899 .named_child(0)
17900 .is_some_and(cpp_reparsed_attribute_callable_declarator)
17901}
17902
17903fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
17904 cpp_structured_type_path(node, source).is_some()
17905 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
17906}
17907
17908fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
17909 let Some(body) = cpp_reparsed_member_function_body(node) else {
17910 return false;
17911 };
17912 let mut cursor = node.walk();
17913 let named = node
17914 .named_children(&mut cursor)
17915 .filter(|child| child.kind() != "comment")
17916 .collect::<Vec<_>>();
17917 let [friend, return_error, declarator, body_node] = named.as_slice() else {
17918 return false;
17919 };
17920 let Some(return_type) = return_error.named_child(0) else {
17921 return false;
17922 };
17923 same_node(*body_node, body)
17924 && friend.kind() == "type_identifier"
17925 && node_text(*friend, source) == "friend"
17926 && return_error.kind() == "ERROR"
17927 && return_error.named_child_count() == 1
17928 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
17929 && extract_function_declarator(*declarator)
17930 .and_then(cpp_function_declarator_name_node)
17931 .is_some()
17932}
17933
17934fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
17935 let Some(body) = cpp_reparsed_member_function_body(node) else {
17936 return false;
17937 };
17938 let mut cursor = node.walk();
17939 let named = node
17940 .named_children(&mut cursor)
17941 .filter(|child| child.kind() != "comment")
17942 .collect::<Vec<_>>();
17943 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
17944 return false;
17945 };
17946 let Some(return_type) = return_error.named_child(0) else {
17947 return false;
17948 };
17949 same_node(*body_node, body)
17950 && prefix
17951 .iter()
17952 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
17953 && attribute.kind() == "type_identifier"
17954 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
17955 && return_error.kind() == "ERROR"
17956 && return_error.named_child_count() == 1
17957 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
17958 && extract_function_declarator(*declarator)
17959 .and_then(cpp_function_declarator_name_node)
17960 .is_some()
17961}
17962
17963fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
17969 let Some(body) = cpp_reparsed_member_function_body(node) else {
17970 return false;
17971 };
17972 let mut cursor = node.walk();
17973 let named = node
17974 .named_children(&mut cursor)
17975 .filter(|child| child.kind() != "comment")
17976 .collect::<Vec<_>>();
17977 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
17978 return false;
17979 };
17980 let Some(template_name) = template_type.child_by_field_name("name") else {
17981 return false;
17982 };
17983 let Some(arguments) = template_type.child_by_field_name("arguments") else {
17984 return false;
17985 };
17986 let Some(return_type) = return_error.named_child(0) else {
17987 return false;
17988 };
17989 let mut cursor = template_type.walk();
17990 let template_errors = template_type
17991 .named_children(&mut cursor)
17992 .filter(|child| child.kind() == "ERROR")
17993 .collect::<Vec<_>>();
17994 let [comment_error] = template_errors.as_slice() else {
17995 return false;
17996 };
17997 let mut cursor = comment_error.walk();
17998 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
17999 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
18000 return false;
18001 };
18002 same_node(*body_node, body)
18003 && template_type.kind() == "template_type"
18004 && template_name.kind() == "type_identifier"
18005 && matches!(
18006 node_text(template_name, source).trim(),
18007 "public" | "private" | "protected"
18008 )
18009 && arguments.kind() == "template_argument_list"
18010 && arguments.named_child_count() > 0
18011 && !arguments.has_error()
18012 && !colon.is_named()
18013 && colon.kind() == ":"
18014 && comments.iter().all(|child| child.kind() == "comment")
18015 && !template_keyword.is_named()
18016 && template_keyword.kind() == "template"
18017 && return_error.kind() == "ERROR"
18018 && return_error.named_child_count() == 1
18019 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
18020 && extract_function_declarator(*declarator)
18021 .and_then(cpp_function_declarator_name_node)
18022 .is_some()
18023}
18024
18025fn cpp_reparsed_preprocessor_constructor<'tree>(
18031 node: Node<'tree>,
18032 class_name: &str,
18033 source: &str,
18034) -> Option<Node<'tree>> {
18035 if node.kind() != "labeled_statement" {
18036 return None;
18037 }
18038 let mut cursor = node.walk();
18039 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18040 let [label, directive_error, declaration] = named.as_slice() else {
18041 return None;
18042 };
18043 if label.kind() != "statement_identifier"
18044 || !matches!(
18045 node_text(*label, source),
18046 "public" | "private" | "protected"
18047 )
18048 || directive_error.kind() != "ERROR"
18049 || directive_error.child_count() != 1
18050 || directive_error
18051 .child(0)
18052 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
18053 || declaration.kind() != "declaration"
18054 || declaration.named_child_count() != 2
18055 {
18056 return None;
18057 }
18058 let apparent_type = declaration.child_by_field_name("type")?;
18059 if apparent_type.kind() != "type_identifier"
18060 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
18061 {
18062 return None;
18063 }
18064 let declarator = declaration.child_by_field_name("declarator")?;
18065 let function = extract_function_declarator(declarator)?;
18066 let name = cpp_function_declarator_name_node(function)?;
18067 (node_text(name, source) == class_name).then_some(*declaration)
18068}
18069
18070fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
18071 if extract_function_declarator(node)
18072 .and_then(cpp_function_declarator_name_node)
18073 .is_some()
18074 {
18075 return true;
18076 }
18077 node.kind() == "init_declarator"
18078 && node
18079 .child_by_field_name("declarator")
18080 .is_some_and(|declarator| declarator.kind() == "identifier")
18081 && node
18082 .child_by_field_name("value")
18083 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
18084}
18085
18086fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
18091 if node.kind() != "ERROR" || node.named_child_count() != 3 {
18092 return false;
18093 }
18094 let mut cursor = node.walk();
18095 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18096 let [type_node, function_declarator, attribute] = named.as_slice() else {
18097 return false;
18098 };
18099 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
18100 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
18101 || attribute.kind() != "identifier"
18102 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
18103 {
18104 return false;
18105 }
18106 let Some(preproc) =
18107 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
18108 else {
18109 return false;
18110 };
18111 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
18112 .filter(|sibling| sibling.kind() == "compound_statement")
18113 else {
18114 return false;
18115 };
18116 let Some(open) = body.child(0) else {
18117 return false;
18118 };
18119 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
18120 return false;
18121 };
18122 let Some(condition) = preproc.child_by_field_name("condition") else {
18123 return false;
18124 };
18125 let mut cursor = preproc.walk();
18126 let payload = preproc
18127 .named_children(&mut cursor)
18128 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
18129 .collect::<Vec<_>>();
18130 let [requires_statement] = payload.as_slice() else {
18131 return false;
18132 };
18133 let requires_clause = requires_statement.named_child(0);
18134
18135 open.kind() == "{"
18136 && !open.is_missing()
18137 && close.kind() == "}"
18138 && !close.is_missing()
18139 && close.end_byte() == body.end_byte()
18140 && requires_statement.kind() == "expression_statement"
18141 && requires_statement.named_child_count() == 1
18142 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
18143}
18144
18145fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
18146 let mut sibling = node.next_named_sibling();
18147 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
18148 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
18149 }
18150 sibling
18151}
18152
18153fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
18154 let mut sibling = node.prev_named_sibling();
18155 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
18156 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
18157 }
18158 sibling
18159}
18160
18161fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
18162 let Some(preproc) =
18163 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
18164 else {
18165 return false;
18166 };
18167 let Some(error) =
18168 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
18169 else {
18170 return false;
18171 };
18172 cpp_reparsed_attribute_requires_error(error, source)
18173}
18174
18175fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
18176 node: Node<'tree>,
18177 source: &str,
18178) -> Option<Node<'tree>> {
18179 if node.kind() != "ERROR" {
18180 return None;
18181 }
18182 let mut cursor = node.walk();
18183 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18184 let [parameter, macro_name, message] = named.as_slice() else {
18185 return None;
18186 };
18187 let parameter_name = parameter.named_child(0)?;
18188 (parameter.kind() == "type_parameter_declaration"
18189 && parameter_name.kind() == "type_identifier"
18190 && macro_name.kind() == "type_identifier"
18191 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
18192 && message.kind() == "string_literal")
18193 .then_some(parameter_name)
18194}
18195
18196fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
18201 node: Node<'tree>,
18202 source: &str,
18203) -> Option<Node<'tree>> {
18204 if node.kind() != "ERROR" {
18205 return None;
18206 }
18207 let mut cursor = node.walk();
18208 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18209 let [parameter, macro_name, message, constraint] = named.as_slice() else {
18210 return None;
18211 };
18212 let parameter_name = parameter.named_child(0)?;
18213 let constraint_scope = constraint.child_by_field_name("scope")?;
18214 let constraint_template = constraint.child_by_field_name("name")?;
18215 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
18216 let mut argument_cursor = constraint_arguments.walk();
18217 let constraint_types = constraint_arguments
18218 .named_children(&mut argument_cursor)
18219 .collect::<Vec<_>>();
18220 if parameter.kind() != "type_parameter_declaration"
18221 || parameter_name.kind() != "type_identifier"
18222 || macro_name.kind() != "type_identifier"
18223 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
18224 || message.kind() != "string_literal"
18225 || constraint.kind() != "qualified_identifier"
18226 || constraint_scope.kind() != "namespace_identifier"
18227 || !matches!(
18228 constraint_template.kind(),
18229 "template_function" | "template_type"
18230 )
18231 || !matches!(constraint_types.as_slice(), [left, right]
18232 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
18233 || constraint_arguments.has_error()
18234 {
18235 return None;
18236 }
18237 let parameter_text = node_text(parameter_name, source);
18238 let mut stack = constraint_types;
18239 while let Some(current) = stack.pop() {
18240 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
18241 return Some(parameter_name);
18242 }
18243 let mut cursor = current.walk();
18244 stack.extend(current.named_children(&mut cursor));
18245 }
18246 None
18247}
18248
18249fn cpp_reparsed_template_macro_companion_is_indexable(
18250 node: Node<'_>,
18251 parameter_name: Node<'_>,
18252 source: &str,
18253) -> bool {
18254 let Some(body) = cpp_reparsed_member_function_body(node) else {
18255 return false;
18256 };
18257 let mut cursor = node.walk();
18258 let named = node
18259 .named_children(&mut cursor)
18260 .filter(|child| child.kind() != "comment")
18261 .collect::<Vec<_>>();
18262 let [
18263 constraint,
18264 close_error,
18265 storage,
18266 return_error,
18267 declarator,
18268 body_node,
18269 ] = named.as_slice()
18270 else {
18271 return false;
18272 };
18273 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
18274 return false;
18275 };
18276 let Some(constraint_template) = constraint.child_by_field_name("name") else {
18277 return false;
18278 };
18279 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
18280 return false;
18281 };
18282 let Some(return_type) = return_error.named_child(0) else {
18283 return false;
18284 };
18285 let mut cursor = constraint_arguments.walk();
18286 let constraint_types = constraint_arguments
18287 .named_children(&mut cursor)
18288 .collect::<Vec<_>>();
18289 same_node(*body_node, body)
18290 && constraint.kind() == "qualified_identifier"
18291 && constraint_scope.kind() == "namespace_identifier"
18292 && constraint_template.kind() == "template_type"
18293 && matches!(constraint_types.as_slice(), [left, right]
18294 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
18295 && !constraint_arguments.has_error()
18296 && close_error.kind() == "ERROR"
18297 && close_error.named_child_count() == 0
18298 && storage.kind() == "storage_class_specifier"
18299 && return_error.kind() == "ERROR"
18300 && return_error.named_child_count() == 1
18301 && return_type.kind() == "identifier"
18302 && node_text(return_type, source) == node_text(parameter_name, source)
18303 && extract_function_declarator(*declarator)
18304 .and_then(cpp_function_declarator_name_node)
18305 .is_some()
18306}
18307
18308fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
18309 node: Node<'tree>,
18310 parameter_name: Node<'_>,
18311 source: &str,
18312) -> Option<Node<'tree>> {
18313 let body = cpp_reparsed_member_function_body(node)?;
18314 let constraint = node.child_by_field_name("type")?;
18315 let constraint_template = constraint.child_by_field_name("name")?;
18316 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
18317 let mut argument_cursor = constraint_arguments.walk();
18318 let constraint_types = constraint_arguments
18319 .named_children(&mut argument_cursor)
18320 .collect::<Vec<_>>();
18321 if constraint.kind() != "qualified_identifier"
18322 || constraint_template.kind() != "template_type"
18323 || !matches!(constraint_types.as_slice(), [left, right]
18324 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
18325 || constraint_arguments.has_error()
18326 || node
18327 .child_by_field_name("body")
18328 .is_none_or(|candidate| !same_node(candidate, body))
18329 {
18330 return None;
18331 }
18332
18333 let mut cursor = node.walk();
18334 let recovery_errors = node
18335 .named_children(&mut cursor)
18336 .filter(|child| child.kind() == "ERROR")
18337 .collect::<Vec<_>>();
18338 if !recovery_errors
18339 .iter()
18340 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
18341 || !recovery_errors.iter().all(|error| {
18342 error.named_child_count() == 0
18343 || cpp_reparsed_constraint_macro_error(*error, source)
18344 || (error.named_child_count() == 1
18345 && error
18346 .named_child(0)
18347 .is_some_and(|child| child.kind() == "function_declarator"))
18348 })
18349 {
18350 return None;
18351 }
18352
18353 let parameter_text = node_text(parameter_name, source);
18354 let mut declarators = node
18355 .child_by_field_name("declarator")
18356 .and_then(extract_function_declarator)
18357 .into_iter()
18358 .collect::<Vec<_>>();
18359 for error in recovery_errors {
18360 let mut stack = vec![error];
18361 while let Some(current) = stack.pop() {
18362 if current.kind() == "function_declarator" {
18363 declarators.push(current);
18364 }
18365 let mut cursor = current.walk();
18366 stack.extend(current.named_children(&mut cursor));
18367 }
18368 }
18369 declarators.into_iter().find(|declarator| {
18370 cpp_function_declarator_name_node(*declarator)
18371 .is_some_and(|name| name.kind() == "identifier")
18372 && declarator
18373 .child_by_field_name("parameters")
18374 .is_some_and(|parameters| {
18375 parameters
18376 .named_children(&mut parameters.walk())
18377 .filter_map(|parameter| parameter.child_by_field_name("type"))
18378 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
18379 })
18380 })
18381}
18382
18383fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
18384 node: Node<'_>,
18385 parameter_name: Node<'_>,
18386 source: &str,
18387) -> bool {
18388 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
18389}
18390
18391fn cpp_reparsed_template_macro_function_companion_is_indexable(
18392 node: Node<'_>,
18393 parameter_name: Node<'_>,
18394 source: &str,
18395) -> bool {
18396 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
18397 return false;
18398 }
18399 let Some(return_type) = node.child_by_field_name("type") else {
18400 return false;
18401 };
18402 let Some(function_declarator) = node
18403 .child_by_field_name("declarator")
18404 .and_then(extract_function_declarator)
18405 else {
18406 return false;
18407 };
18408 if cpp_function_declarator_name_node(function_declarator).is_none()
18409 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
18410 {
18411 return false;
18412 }
18413 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
18414 return false;
18415 };
18416 let parameter_text = node_text(parameter_name, source);
18417 parameters
18418 .named_children(&mut parameters.walk())
18419 .any(|parameter| {
18420 parameter
18421 .child_by_field_name("type")
18422 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
18423 })
18424}
18425
18426fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
18427 if node.kind() != "ERROR" {
18428 return false;
18429 }
18430 let mut stack = vec![node];
18431 while let Some(current) = stack.pop() {
18432 let macro_shape = match current.kind() {
18433 "call_expression" => current
18434 .child_by_field_name("function")
18435 .zip(current.child_by_field_name("arguments")),
18436 "init_declarator" => current
18437 .child_by_field_name("declarator")
18438 .zip(current.child_by_field_name("value")),
18439 _ => None,
18440 };
18441 if let Some((name, arguments)) = macro_shape
18442 && name.kind() == "identifier"
18443 && arguments.kind() == "argument_list"
18444 && arguments.named_child_count() >= 2
18445 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
18446 {
18447 return true;
18448 }
18449 let mut cursor = current.walk();
18450 stack.extend(current.named_children(&mut cursor));
18451 }
18452 false
18453}
18454
18455fn cpp_recovered_template_macro_constructor<'tree>(
18456 node: Node<'tree>,
18457 source: &str,
18458) -> Option<(Node<'tree>, Node<'tree>)> {
18459 let mut prefix = node.prev_named_sibling()?;
18460 while prefix.kind() == "comment" {
18461 prefix = prefix.prev_named_sibling()?;
18462 }
18463 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
18464 let parameter = parameter_name
18465 .parent()
18466 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
18467 let declarator =
18468 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
18469 Some((declarator, parameter))
18470}
18471
18472fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
18473 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
18474 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
18475 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
18476 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
18477 function,
18478 parameter_name,
18479 source,
18480 )
18481 });
18482 }
18483 let Some(parameter_name) =
18484 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
18485 else {
18486 return false;
18487 };
18488 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
18489 cpp_reparsed_template_macro_function_companion_is_indexable(
18490 function,
18491 parameter_name,
18492 source,
18493 )
18494 })
18495}
18496
18497fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
18498 let function_name = node
18499 .child_by_field_name("declarator")
18500 .and_then(extract_function_declarator)
18501 .and_then(cpp_function_declarator_name_node);
18502 if let Some(body) = cpp_reparsed_member_function_body(node)
18503 && function_name.is_some()
18504 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
18505 {
18506 return true;
18507 }
18508 cpp_reparsed_attribute_member_function(node, source)
18509 || cpp_reparsed_friend_function_is_indexable(node, source)
18510 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
18511 || cpp_reparsed_access_template_function_is_indexable(node, source)
18512 || cpp_recovered_template_macro_constructor(node, source).is_some()
18513}
18514
18515fn cpp_reparsed_macro_attribute_member_sequence(
18522 children: &[Node<'_>],
18523 index: usize,
18524 source: &str,
18525) -> bool {
18526 let Some(prefix) = children.get(index).copied() else {
18527 return false;
18528 };
18529 let declaration = if prefix.kind() == "labeled_statement" {
18530 prefix
18531 .named_child(prefix.named_child_count().saturating_sub(1))
18532 .filter(|child| child.kind() == "declaration")
18533 } else {
18534 (prefix.kind() == "declaration").then_some(prefix)
18535 };
18536 let Some(declaration) = declaration else {
18537 return false;
18538 };
18539 if !declaration.has_error()
18540 || declaration
18541 .child_by_field_name("declarator")
18542 .and_then(extract_function_declarator)
18543 .and_then(cpp_function_declarator_name_node)
18544 .is_none()
18545 {
18546 return false;
18547 }
18548 let Some(attribute_statement) = children.get(index + 1).copied() else {
18549 return false;
18550 };
18551 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
18552 .then(|| attribute_statement.named_child(0))
18553 .flatten()
18554 .filter(|child| child.kind() == "call_expression")
18555 else {
18556 return false;
18557 };
18558 let Some(attribute_name) = attribute_call
18559 .child_by_field_name("function")
18560 .filter(|function| function.kind() == "identifier")
18561 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
18562 else {
18563 return false;
18564 };
18565 if !cpp_export_macro_token(&attribute_name) {
18566 return false;
18567 }
18568 let Some(body) = children.get(index + 2).copied() else {
18569 return false;
18570 };
18571 body.kind() == "compound_statement"
18572 && body.child(0).is_some_and(|open| open.kind() == "{")
18573 && body
18574 .child(body.child_count().saturating_sub(1))
18575 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
18576 && declaration.end_byte() <= attribute_statement.start_byte()
18577 && attribute_statement.end_byte() <= body.start_byte()
18578}
18579
18580fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
18588 if node.kind() != "ERROR" {
18589 return false;
18590 }
18591 let run = stranded_declaration_run(node, source);
18592 run.complete && !run.declarations.is_empty()
18593}
18594
18595fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
18596 let mut cursor = root.walk();
18597 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
18598 let mut saw_member = false;
18599 let mut index = 0;
18600 while index < children.len() {
18601 let child = children[index];
18602 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
18603 saw_member = true;
18604 index += 3;
18605 continue;
18606 }
18607 if let Some(recovered) = fragmented_class_body(child, source) {
18608 let Some(tree) = cpp_reparse_fragmented_class_body(
18609 source,
18610 recovered.body.reparse_start,
18611 recovered.body.reparse_end,
18612 ) else {
18613 return false;
18614 };
18615 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
18616 return false;
18617 }
18618 saw_member = true;
18619 index += 1;
18620 while index < children.len()
18621 && children[index].end_byte() <= recovered.body.class_range.end_byte
18622 {
18623 index += 1;
18624 }
18625 continue;
18626 }
18627 match child.kind() {
18628 "comment" => {}
18629 "labeled_statement" => saw_member = true,
18630 "function_definition" => {
18631 if child.has_error()
18632 && !cpp_reparsed_member_function_is_indexable(child, source)
18633 && cpp_sentinel_macro_region(child, source).is_none()
18634 {
18635 return false;
18636 }
18637 saw_member = true;
18638 }
18639 "expression_statement" if is_string_attribute_macro_statement(child) => {}
18643 "ERROR"
18644 if (cpp_reparsed_member_error_is_indexable(child)
18645 || cpp_reparsed_adjacent_copy_control_error(child, source)
18646 || cpp_reparsed_stranded_member_error(child, source))
18647 && (child
18648 .next_named_sibling()
18649 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
18650 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
18651 {
18652 saw_member = true;
18653 }
18654 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
18655 saw_member = true;
18656 }
18657 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
18658 saw_member = true;
18659 }
18660 "expression_statement"
18661 if cpp_is_stray_semicolon(child, source)
18662 && child.prev_named_sibling().is_some_and(|error| {
18663 cpp_reparsed_member_error_is_indexable(error)
18664 || cpp_reparsed_adjacent_copy_control_error(error, source)
18665 || cpp_reparsed_stranded_member_error(error, source)
18666 }) =>
18667 {
18668 saw_member = true;
18669 }
18670 "compound_statement"
18671 if cpp_reparsed_constructor_body_is_indexable(child, source)
18672 || cpp_reparsed_attribute_requires_body(child, source) =>
18673 {
18674 saw_member = true;
18675 }
18676 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
18677 _ => return false,
18678 }
18679 index += 1;
18680 }
18681 saw_member
18682}
18683
18684fn cpp_reparsed_synthetic_initializer_constructor_range(
18692 root: Node<'_>,
18693 class_name: &str,
18694 source: &str,
18695 constructor_end: usize,
18696) -> Option<std::ops::Range<usize>> {
18697 let mut stack = {
18698 let mut cursor = root.walk();
18699 root.named_children(&mut cursor).collect::<Vec<_>>()
18700 };
18701 while let Some(current) = stack.pop() {
18702 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
18703 current,
18704 class_name,
18705 source,
18706 constructor_end,
18707 ) {
18708 return Some(range);
18709 }
18710 if current.kind() == "ERROR" {
18711 let mut cursor = current.walk();
18712 stack.extend(current.named_children(&mut cursor));
18713 }
18714 }
18715 None
18716}
18717
18718fn cpp_reparsed_merged_inline_constructor<'tree>(
18725 root: Node<'tree>,
18726 class_name: &str,
18727 source: &str,
18728) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
18729 let mut stack = vec![root];
18730 while let Some(current) = stack.pop() {
18731 if current.kind() != "labeled_statement" {
18732 let mut cursor = current.walk();
18733 stack.extend(current.named_children(&mut cursor));
18734 continue;
18735 }
18736 let declaration = current
18737 .named_children(&mut current.walk())
18738 .find(|child| child.kind() == "declaration")?;
18739 if declaration
18740 .child_by_field_name("type")
18741 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
18742 {
18743 continue;
18744 }
18745 let following = declaration
18746 .child_by_field_name("declarator")
18747 .and_then(extract_function_declarator)
18748 .and_then(cpp_function_declarator_name_node);
18749 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
18750 continue;
18751 }
18752 let mut declaration_cursor = declaration.walk();
18753 let Some(error) = declaration
18754 .named_children(&mut declaration_cursor)
18755 .find(|child| child.kind() == "ERROR")
18756 else {
18757 continue;
18758 };
18759 let mut error_cursor = error.walk();
18760 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
18761 let Some(constructor) = error_children.iter().copied().find(|child| {
18762 child.kind() == "function_declarator"
18763 && cpp_function_declarator_name_node(*child)
18764 .is_some_and(|name| node_text(name, source).trim() == class_name)
18765 }) else {
18766 continue;
18767 };
18768 let Some(body) = error_children.iter().copied().find_map(|child| {
18769 (child.kind() == "init_declarator")
18770 .then(|| child.child_by_field_name("value"))
18771 .flatten()
18772 .filter(|value| value.kind() == "initializer_list")
18773 }) else {
18774 continue;
18775 };
18776 if constructor.end_byte() > body.start_byte() {
18777 continue;
18778 }
18779 return Some((constructor.start_byte()..body.end_byte(), body));
18780 }
18781 None
18782}
18783
18784fn cpp_reparsed_synthetic_initializer_constructor(
18785 node: Node<'_>,
18786 class_name: &str,
18787 source: &str,
18788 constructor_end: usize,
18789) -> Option<std::ops::Range<usize>> {
18790 if node.kind() != "labeled_statement" {
18791 return None;
18792 }
18793 let mut cursor = node.walk();
18794 let named = node
18795 .named_children(&mut cursor)
18796 .filter(|child| child.kind() != "comment")
18797 .collect::<Vec<_>>();
18798 let label = named.first()?;
18799 if label.kind() != "statement_identifier"
18800 || !matches!(
18801 node_text(*label, source).trim(),
18802 "public" | "private" | "protected"
18803 )
18804 {
18805 return None;
18806 }
18807 let call_error_index = named.iter().position(|child| {
18808 if child.kind() != "ERROR" {
18809 return false;
18810 }
18811 let mut stack = vec![*child];
18812 while let Some(current) = stack.pop() {
18813 if current.kind() == "call_expression"
18814 && current
18815 .child_by_field_name("function")
18816 .is_some_and(|function| {
18817 function.kind() == "identifier"
18818 && node_text(function, source).trim() == class_name
18819 })
18820 {
18821 return true;
18822 }
18823 let mut cursor = current.walk();
18824 stack.extend(current.named_children(&mut cursor));
18825 }
18826 false
18827 })?;
18828 let constructor_call = {
18829 let mut stack = vec![named[call_error_index]];
18830 let mut found = None;
18831 while let Some(current) = stack.pop() {
18832 if current.kind() == "call_expression"
18833 && current
18834 .child_by_field_name("function")
18835 .is_some_and(|function| {
18836 function.kind() == "identifier"
18837 && node_text(function, source).trim() == class_name
18838 })
18839 {
18840 found = Some(current);
18841 break;
18842 }
18843 let mut cursor = current.walk();
18844 stack.extend(current.named_children(&mut cursor));
18845 }
18846 found
18847 };
18848 let constructor_call = constructor_call?;
18849 named.iter().skip(call_error_index + 1).find(|child| {
18850 child.kind() == "declaration" && child.has_error() && {
18851 let mut cursor = child.walk();
18852 child.named_children(&mut cursor).any(|declarator| {
18853 declarator.kind() == "init_declarator"
18854 && declarator
18855 .child_by_field_name("declarator")
18856 .is_some_and(|declarator| declarator.kind() == "function_declarator")
18857 && declarator
18858 .child_by_field_name("value")
18859 .is_some_and(|value| value.kind() == "initializer_list")
18860 })
18861 }
18862 })?;
18863 Some(constructor_call.start_byte()..constructor_end)
18864}
18865
18866fn cpp_reparsed_exact_constructor_declarator<'tree>(
18867 root: Node<'tree>,
18868 start: usize,
18869 class_name: &str,
18870 source: &str,
18871) -> Option<Node<'tree>> {
18872 let mut candidate = None;
18873 let mut stack = vec![root];
18874 while let Some(current) = stack.pop() {
18875 if current.kind() == "function_declarator"
18876 && current.start_byte() == start
18877 && cpp_function_declarator_name_node(current)
18878 .is_some_and(|name| node_text(name, source).trim() == class_name)
18879 {
18880 if candidate.is_some() {
18881 return None;
18882 }
18883 candidate = Some(current);
18884 continue;
18885 }
18886 let mut cursor = current.walk();
18887 stack.extend(current.named_children(&mut cursor));
18888 }
18889 candidate
18890}
18891
18892fn cpp_is_indexable_item_kind(kind: &str) -> bool {
18893 matches!(
18894 kind,
18895 "namespace_definition"
18896 | "class_specifier"
18897 | "struct_specifier"
18898 | "union_specifier"
18899 | "enum_specifier"
18900 | "function_definition"
18901 | "template_declaration"
18902 | "declaration"
18903 | "field_declaration"
18904 | "alias_declaration"
18905 | "static_assert_declaration"
18906 | "type_definition"
18907 | "using_declaration"
18908 | "linkage_specification"
18909 | "preproc_def"
18910 | "preproc_function_def"
18911 | "preproc_include"
18912 | "preproc_if"
18913 | "preproc_ifdef"
18914 | "preproc_call"
18915 )
18916}
18917
18918#[cfg(test)]
18919mod tests {
18920 use super::*;
18921 use crate::adapter::parse_cpp_file;
18922 use brokk_bifrost_core::analyzer::parsed_file::{
18923 finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
18924 start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
18925 };
18926 use std::fmt::Write;
18927
18928 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
18929 let mut parser = tree_sitter::Parser::new();
18930 parser
18931 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18932 .unwrap();
18933 let tree = parser.parse(source, None).unwrap();
18934 let file = ProjectFile::new(std::env::temp_dir(), name);
18935 parse_cpp_file(&file, source, &tree)
18936 }
18937
18938 #[test]
18942 fn rendered_name_fq_round_trips_nested_class_spellings() {
18943 assert!(cpp_rendered_name_fq("").is_empty());
18944 for rendered in [
18945 "Only",
18946 "Top$Only",
18947 "ns.Outer$Inner",
18948 "Top$Only.b",
18949 "ns.Outer$Inner.a",
18950 "Buck$Stop",
18951 ] {
18952 let fq = cpp_rendered_name_fq(rendered);
18953 assert_eq!(
18954 fq.display_native(Language::Cpp, segment_interner()),
18955 rendered,
18956 "native round trip for {rendered}"
18957 );
18958 }
18959 let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
18960 .segments()
18961 .iter()
18962 .map(|segment| segment_interner().resolve(*segment).1)
18963 .collect();
18964 assert_eq!(
18965 kinds,
18966 [
18967 SegmentKind::Unknown,
18968 SegmentKind::Unknown,
18969 SegmentKind::Nested
18970 ]
18971 );
18972 }
18973
18974 #[test]
18978 fn colon_source_aliases_name_nested_and_namespace_identities() {
18979 let native = |rendered: &str| {
18980 cpp_colon_source_aliases(rendered)
18981 .into_iter()
18982 .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
18983 .collect::<Vec<_>>()
18984 };
18985 let top = native("Top::Only");
18986 assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
18987 assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
18988 assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
18989 let nested = native("ns::Outer::Inner");
18990 assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
18991 assert!(
18992 nested.contains(&"ns::Outer.Inner".to_string()),
18993 "{nested:?}"
18994 );
18995 let member = native("Top::Only::b");
18996 assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
18997 assert!(cpp_colon_source_aliases("Top$Only").is_empty());
18998 assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
18999 assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
19000 assert!(cpp_colon_source_aliases("Only").is_empty());
19001 assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
19002 fq.segments()
19003 .iter()
19004 .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
19005 }));
19006 }
19007
19008 #[test]
19009 fn displaced_terminator_ignores_nested_initializer_endif() {
19010 let source = "#ifdef ENABLE_ITEMS\n\
19011struct Item { int reg; };\n\
19012static const struct Item items[] = {{1}};\n\
19013static const struct Item extras[] = {\n\
19014 {0},\n\
19015#ifndef REDUCED\n\
19016 {1},\n\
19017#endif\n\
19018};\n\
19019int read_item(int i) { return items[i].reg; }\n\
19020#endif\n";
19021 let mut parser = tree_sitter::Parser::new();
19022 parser
19023 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19024 .expect("C++ grammar");
19025 let tree = parser.parse(source, None).expect("fixture tree");
19026 let conditional = tree
19027 .root_node()
19028 .named_child(0)
19029 .filter(|node| node.kind() == "preproc_ifdef")
19030 .expect("outer conditional");
19031
19032 assert!(conditional.has_error());
19033 assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
19034 assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
19035 }
19036
19037 #[test]
19038 fn pyobject_head_field_recovery_publishes_only_the_real_member() {
19039 let source = "struct Image { PyObject_HEAD Imaging image; };";
19040 let parsed = parse_cpp_declarations(source, "image.h");
19041 let names = parsed
19042 .declarations()
19043 .iter()
19044 .map(|unit| unit.fq_name())
19045 .collect::<Vec<_>>();
19046
19047 assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
19048 assert!(
19049 names.iter().all(|name| name != "Image.Imaging"),
19050 "the pseudo-declarator must not become a field: {names:#?}"
19051 );
19052
19053 let pointer = parse_cpp_declarations(
19054 "struct Image { PyObject_HEAD Imaging *image; };",
19055 "image-pointer.h",
19056 );
19057 let pointer_names = pointer
19058 .declarations()
19059 .iter()
19060 .map(|unit| unit.fq_name())
19061 .collect::<Vec<_>>();
19062 assert!(
19063 pointer_names.iter().any(|name| name == "Image.image"),
19064 "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
19065 );
19066 assert!(
19067 pointer_names.iter().all(|name| name != "Image.Imaging"),
19068 "the pointer recovery error must not become a field: {pointer_names:#?}"
19069 );
19070
19071 let unrelated_macro = parse_cpp_declarations(
19072 "struct Image { OTHER_HEAD Imaging other; };",
19073 "image-near-miss.h",
19074 );
19075 let unrelated_names = unrelated_macro
19076 .declarations()
19077 .iter()
19078 .map(|unit| unit.fq_name())
19079 .collect::<Vec<_>>();
19080 assert!(
19081 unrelated_names.iter().all(|name| name != "Image.other"),
19082 "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
19083 );
19084 }
19085
19086 #[test]
19087 fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
19088 let source = r#"namespace testing {
19089namespace internal {
19090 namespace detail {
19091 class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
19092 public:
19093 int value() const { return count_ + 1; }
19094 private:
19095 int count_;
19096 };
19097 class GTEST_API_ [[nodiscard]] OtherReporter {
19098 public:
19099 int value() const { return count_ + 2; }
19100 private:
19101 int count_;
19102 };
19103 }
19104
19105 template <typename T>
19106 void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
19107
19108 class TailReporter {};
19109}
19110}
19111"#;
19112 let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
19113 let declarations = parsed.declarations();
19114 let mut parser = tree_sitter::Parser::new();
19115 parser
19116 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19117 .unwrap();
19118 let tree = parser.parse(source, None).unwrap();
19119 let tail_start = source.find("TailReporter").expect("tail class");
19120 let tail_node = tree
19121 .root_node()
19122 .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
19123 .expect("tail class AST node");
19124 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
19125 assert!(
19126 tree.root_node().has_error(),
19127 "the malformed class must exercise recovery"
19128 );
19129 assert!(index.region_at(tail_start).is_some());
19130 assert_eq!(
19131 index.enclosing_namespace_components(tail_node, source),
19132 ["testing", "internal"]
19133 );
19134 let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
19135 let mut recovered_parsed = ParsedFile::new(String::new());
19136 let class_unit = CodeUnit::new_fq(
19137 file.clone(),
19138 CodeUnitType::Class,
19139 "testing",
19140 "ScopedFakeTestPartResultReporter",
19141 cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
19142 );
19143 let scope = ScopeInfo {
19144 package_name: "testing".to_string(),
19145 module: None,
19146 class_unit: Some(class_unit),
19147 template_signature: Some("<typename T>".to_string()),
19148 template_metadata: Some(CppTemplateMetadata {
19149 primary_name: "ScopedFakeTestPartResultReporter".to_string(),
19150 primary_fq_name: String::new(),
19151 parameters: Vec::new(),
19152 specialization_arguments: Vec::new(),
19153 alias_target: None,
19154 }),
19155 declarations_are_fields: true,
19156 recovered_specialization_member_scope: true,
19157 visible_using_namespaces: Vec::new(),
19158 };
19159 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
19160 let properties = RefCell::new(HashMap::default());
19161 let dependencies =
19162 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
19163 let ancestry = ParentIndex::new(tree.root_node());
19164 let mut visitor = CppVisitor {
19165 file: &file,
19166 source,
19167 parsed: &mut recovered_parsed,
19168 source_collector: &collector,
19169 source_properties: &properties,
19170 source_dependencies: &dependencies,
19171 primary_ancestry: &ancestry,
19172 primary_root_id: tree.root_node().id(),
19173 source_links: HashMap::default(),
19174 source_declarations: HashSet::default(),
19175 active_source_context: None,
19176 c_tag_semantics: false,
19177 c_tag_scope_witness: false,
19178 recovered_class_sibling_scopes: HashMap::default(),
19179 consumed_fragment_regions: Vec::new(),
19180 orphaned_namespaces: &index,
19181 partitioned_regions: Vec::new(),
19182 namespace_forward_scans: HashMap::default(),
19183 field_owners: None,
19184 recovery_captures: Vec::new(),
19185 object_macro_fields: HashMap::default(),
19186 ambiguous_object_macro_fields: HashSet::default(),
19187 };
19188 let recovered = visitor
19189 .recovered_namespace_scope(tail_node, &scope)
19190 .expect("the tail must use the stolen namespace scope");
19191 assert_eq!(recovered.package_name, "testing::internal");
19192 assert!(
19193 recovered.class_unit.is_none(),
19194 "recovered namespace declarations cannot retain the malformed class owner"
19195 );
19196 assert!(recovered.template_signature.is_none());
19197 assert!(recovered.template_metadata.is_none());
19198 assert!(!recovered.declarations_are_fields);
19199 assert!(!recovered.recovered_specialization_member_scope);
19200 assert!(
19201 declarations
19202 .iter()
19203 .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
19204 "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
19205 );
19206 assert!(
19207 declarations
19208 .iter()
19209 .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
19210 "the recovered free function remains in its namespace: {declarations:#?}"
19211 );
19212 assert!(
19213 declarations
19214 .iter()
19215 .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
19216 "the independent nested class keeps its ordinary class owner: {declarations:#?}"
19217 );
19218 assert!(
19219 declarations.iter().all(|unit| {
19220 !unit
19221 .short_name()
19222 .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
19223 }),
19224 "recovered namespace declarations must not retain a class owner: {declarations:#?}"
19225 );
19226 assert!(
19227 declarations
19228 .iter()
19229 .all(|unit| !unit.identifier().is_empty()),
19230 "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
19231 );
19232 }
19233
19234 #[test]
19235 fn object_like_field_macros_materialize_owner_specific_declarations() {
19236 let source = r#"#define PUBLIC_FIELDS int public_value;
19237#define PRIVATE_FIELDS int private_value;
19238#define NOT_A_FIELD_LIST not a declaration
19239
19240struct First {
19241 PUBLIC_FIELDS
19242 PRIVATE_FIELDS
19243};
19244struct Second {
19245 PUBLIC_FIELDS
19246 NOT_A_FIELD_LIST
19247};
19248#undef PUBLIC_FIELDS
19249struct Third {
19250 PUBLIC_FIELDS
19251};
19252"#;
19253 let parsed = parse_cpp_declarations(source, "macro-fields.c");
19254 let fields = parsed
19255 .declarations()
19256 .iter()
19257 .filter(|unit| unit.is_field())
19258 .map(|unit| unit.fq_name())
19259 .collect::<Vec<_>>();
19260
19261 assert!(
19262 fields.contains(&"First.public_value".to_string()),
19263 "{fields:?}"
19264 );
19265 assert!(
19266 fields.contains(&"First.private_value".to_string()),
19267 "{fields:?}"
19268 );
19269 assert!(
19270 fields.contains(&"Second.public_value".to_string()),
19271 "{fields:?}"
19272 );
19273 assert!(
19274 !fields.iter().any(|field| field.contains("not_a_field")),
19275 "malformed macro must fail closed: {fields:?}"
19276 );
19277 assert!(
19278 !fields.iter().any(|field| field.starts_with("Third.")),
19279 "undefined macro must fail closed: {fields:?}"
19280 );
19281 }
19282
19283 #[test]
19284 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
19285 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
19286 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
19287 let mut macros = parsed
19288 .declarations()
19289 .iter()
19290 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
19291 .collect::<Vec<_>>();
19292 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
19293
19294 assert_eq!(macros.len(), 2, "{macros:#?}");
19295 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
19296 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
19297 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
19298 assert_eq!(
19299 parsed.declaration_ranges(macros[1])[0].start_byte,
19300 source.rfind("#define VALUE 2").expect("second definition")
19301 );
19302 }
19303
19304 #[test]
19305 fn identifies_export_macro_class_base_displaced_into_declarator() {
19306 let source = r#"#define PROJECT_API_
19307namespace project {
19308namespace internal {
19309template <typename T>
19310class Base {};
19311}
19312template <typename T>
19313class Wrapper;
19314template <>
19315class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
19316}
19317"#;
19318 let mut parser = tree_sitter::Parser::new();
19319 parser
19320 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19321 .unwrap();
19322 let tree = parser.parse(source, None).unwrap();
19323 let start = source.find("internal::Base<int>").expect("base");
19324 let mut base = tree
19325 .root_node()
19326 .descendant_for_byte_range(start, start + 8)
19327 .expect("base syntax");
19328 while base.kind() != "qualified_identifier" {
19329 base = base.parent().expect("qualified base ancestor");
19330 }
19331 assert!(
19332 is_recovered_exported_class_base_type_node(base, source),
19333 "{}",
19334 tree.root_node().to_sexp()
19335 );
19336 }
19337
19338 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
19339 let mut identities = parsed
19340 .declarations()
19341 .iter()
19342 .filter(|unit| unit.is_function())
19343 .map(|unit| {
19344 (
19345 unit.fq_name(),
19346 unit.signature().unwrap_or_default().to_string(),
19347 )
19348 })
19349 .collect::<Vec<_>>();
19350 identities.sort();
19351 identities
19352 }
19353
19354 #[test]
19361 fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
19362 let cases = [
19363 (
19364 "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
19365 "pg_typemap_fit_to_result",
19366 "(VALUE, VALUE)",
19367 ),
19368 (
19369 "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
19370 "pg_typemap_result_value",
19371 "(t_typemap *, VALUE, int, int)",
19372 ),
19373 (
19374 "void pg_typemap_mark _(( void * ));",
19375 "pg_typemap_mark",
19376 "(void *)",
19377 ),
19378 (
19379 "void init_pg_type_map _(( void ));",
19380 "init_pg_type_map",
19381 "(void)",
19382 ),
19383 ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
19384 (
19385 "extern VALUE pg_extern _(( VALUE, VALUE ));",
19386 "pg_extern",
19387 "(VALUE, VALUE)",
19388 ),
19389 (
19390 "size_t pg_typemap_memsize _(( const void * ));",
19391 "pg_typemap_memsize",
19392 "(const void *)",
19393 ),
19394 (
19395 "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
19396 "pg_wrap_socket_io",
19397 "(int, VALUE, VALUE *, int *)",
19398 ),
19399 ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
19400 ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
19401 ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
19402 ];
19403
19404 for (index, (source, name, signature)) in cases.into_iter().enumerate() {
19405 let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
19406 assert_eq!(
19407 function_identities(&parsed),
19408 vec![(name.to_string(), signature.to_string())],
19409 "{source}: {:#?}",
19410 parsed.declarations()
19411 );
19412 assert!(
19413 parsed.declarations().iter().all(|unit| !unit.is_field()),
19414 "{source} must not retain the malformed field: {:#?}",
19415 parsed.declarations()
19416 );
19417 }
19418 }
19419
19420 #[test]
19429 fn c_prototype_macro_recovers_pointer_return_expression_statements() {
19430 let cases = [
19431 (
19432 "PGconn *pg_get_pgconn _(( VALUE ));",
19433 "pg_get_pgconn",
19434 "(VALUE)",
19435 ),
19436 (
19437 "PGresult* pgresult_get _(( VALUE ));",
19438 "pgresult_get",
19439 "(VALUE)",
19440 ),
19441 ];
19442 for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
19443 let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
19444 let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
19445 assert_eq!(
19446 function_identities(&parsed),
19447 vec![(name.to_string(), signature.to_string())],
19448 "{source}: {:#?}",
19449 parsed.declarations()
19450 );
19451 assert_eq!(
19452 parsed
19453 .declarations()
19454 .iter()
19455 .filter(|unit| unit.is_field())
19456 .map(|unit| unit.fq_name())
19457 .collect::<Vec<_>>(),
19458 vec!["rb_mPG".to_string()],
19459 "{source}: {:#?}",
19460 parsed.declarations()
19461 );
19462 }
19463 }
19464
19465 #[test]
19473 fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
19474 let source = r#"VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
19475VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
19476int pg_typemap_fit_to_copy_get _(( VALUE ));
19477VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
19478t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
19479VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
19480void pg_typemap_mark _(( void * ));
19481size_t pg_typemap_memsize _(( const void * ));
19482void pg_typemap_compact _(( void * ));
19483
19484PGconn *pg_get_pgconn _(( VALUE ));
19485t_pg_connection *pg_get_connection _(( VALUE ));
19486VALUE pgconn_block _(( int, VALUE *, VALUE ));
19487#ifdef __GNUC__
19488__attribute__((format(printf, 3, 4)))
19489#endif
19490NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
19491VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
19492void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
19493
19494
19495VALUE pg_new_result _(( PGresult *, VALUE ));
19496VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
19497PGresult* pgresult_get _(( VALUE ));
19498VALUE pg_result_check _(( VALUE ));
19499VALUE pg_result_clear _(( VALUE ));
19500VALUE pg_tuple_new _(( VALUE, int ));
19501
19502/*
19503 * Fetch the data pointer for the result object
19504 */
19505static inline t_pg_result *
19506pgresult_get_this( VALUE self )
19507{
19508 return RTYPEDDATA_DATA(self);
19509}
19510
19511
19512rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
19513const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
19514rb_encoding *pg_conn_enc_get _(( PGconn * ));
19515
19516"#;
19517 let parsed = parse_cpp_declarations(source, "pg.h");
19518 assert!(
19519 function_identities(&parsed)
19520 .iter()
19521 .any(|(name, signature)| name == "pg_typemap_result_value"
19522 && signature == "(t_typemap *, VALUE, int, int)"),
19523 "the real issue witness must be a Function with its C signature: {:#?}",
19524 parsed.declarations()
19525 );
19526 assert!(
19527 parsed
19528 .declarations()
19529 .iter()
19530 .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
19531 "the issue witness must not leave its return type as a Field name: {:#?}",
19532 parsed.declarations()
19533 );
19534 }
19535
19536 #[test]
19543 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
19544 let source = r#"class SIMD_4x26 final {
19545 public:
19546 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
19547 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
19548 int m_v;
19549};
19550"#;
19551 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
19552 assert_eq!(
19553 function_identities(&parsed),
19554 vec![
19555 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
19556 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
19557 ],
19558 "{:#?}",
19559 parsed.declarations()
19560 );
19561 }
19562
19563 #[test]
19568 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
19569 let source = r#"#include <cstdint>
19570struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
19571 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
19572 struct llama_model * model,
19573 struct llama_context_params params),
19574 "use llama_init_from_model instead");
19575 LLAMA_API int32_t llama_tokenize(
19576 const struct llama_vocab * vocab,
19577 const char * text,
19578 bool parse_special);
19579 LLAMA_API int32_t llama_other(int a);
19580"#;
19581 let parsed = parse_cpp_declarations(source, "llama.h");
19582 assert_eq!(
19583 function_identities(&parsed),
19584 vec![
19585 (
19586 "llama_new_context_with_model".to_string(),
19587 "(struct llama_model *, struct llama_context_params)".to_string()
19588 ),
19589 ("llama_other".to_string(), "(int)".to_string()),
19590 (
19591 "llama_tokenize".to_string(),
19592 "(const struct llama_vocab *, const char *, bool)".to_string()
19593 ),
19594 ],
19595 "{:#?}",
19596 parsed.declarations()
19597 );
19598
19599 for (name, expected) in [
19602 (
19603 "llama_new_context_with_model",
19604 "LLAMA_API struct llama_context * llama_new_context_with_model(",
19605 ),
19606 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
19607 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
19608 ] {
19609 let unit = parsed
19610 .declarations()
19611 .iter()
19612 .find(|unit| unit.is_function() && unit.fq_name() == name)
19613 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
19614 let [range] = parsed.declaration_ranges(unit) else {
19615 panic!("{name} must have exactly one range");
19616 };
19617 let text = &source[range.start_byte..range.end_byte];
19618 assert!(
19619 text.starts_with(expected),
19620 "{name} range is {text:?}, expected it to start with {expected:?}"
19621 );
19622 assert!(
19623 text.ends_with(')') || text.ends_with(';'),
19624 "{name}: {text:?}"
19625 );
19626 }
19627 }
19628
19629 #[test]
19643 fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
19644 let source = r#" LIBRARY_DEPRECATED(
19645 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
19646 "use library_init_from_file_with_params instead"
19647 );
19648 LIBRARY_DEPRECATED(
19649 LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
19650 "use library_init_from_buffer_with_params instead"
19651 );
19652 LIBRARY_DEPRECATED(
19653 LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
19654 "use library_init_with_params instead"
19655 );
19656"#;
19657 let mut parser = tree_sitter::Parser::new();
19658 parser
19659 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19660 .expect("C++ grammar");
19661 let tree = parser.parse(source, None).expect("C++ tree");
19662 let root = tree.root_node();
19663
19664 let mut cursor = root.walk();
19668 let items = root.children(&mut cursor).collect::<Vec<_>>();
19669 let [first, hint_statement, rest @ ..] = items.as_slice() else {
19670 panic!("{}", root.to_sexp());
19671 };
19672 assert_eq!(first.kind(), "ERROR");
19673 assert_eq!(hint_statement.kind(), "expression_statement");
19674 assert!(
19675 node_text(*first, source).ends_with(','),
19676 "the first invocation's own `)` and `;` are the statement's, not its own: {}",
19677 root.to_sexp()
19678 );
19679 let swallowing = rest
19680 .iter()
19681 .find(|item| item.kind() == "ERROR")
19682 .unwrap_or_else(|| panic!("{}", root.to_sexp()));
19683 let mut swallowing_cursor = swallowing.walk();
19684 let flattened = swallowing
19685 .children(&mut swallowing_cursor)
19686 .map(|child| child.kind())
19687 .collect::<Vec<_>>();
19688 assert_eq!(
19689 flattened,
19690 vec![
19691 "identifier",
19692 "(",
19693 "parameter_declaration",
19694 ",",
19695 "ERROR",
19696 "type_identifier",
19697 "(",
19698 "parameter_declaration",
19699 ",",
19700 "\"",
19701 "identifier",
19702 "identifier",
19703 "identifier",
19704 "\"",
19705 ")",
19706 ],
19707 "the third invocation must be flattened into the second one's node: {}",
19708 root.to_sexp()
19709 );
19710
19711 let ancestry = ParentIndex::new(root);
19713 let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
19714 .expect("the first invocation");
19715 assert_eq!(
19716 &source[..first_run.invocation_end],
19717 &source[..source.find("instead\"\n );").expect("first hint")
19718 + "instead\"\n );".len()]
19719 );
19720 assert_eq!(
19721 first_run.region_end, first_run.invocation_end,
19722 "the first invocation swallowed nothing, so the recovery owns only its own bytes"
19723 );
19724 let swallowing_run = collapsed_macro_declaration_run(*swallowing, source, &ancestry)
19725 .expect("the second invocation");
19726 assert!(
19727 swallowing_run.invocation_end < swallowing.end_byte(),
19728 "the second invocation swallowed the third"
19729 );
19730 assert_eq!(
19731 swallowing_run.region_end,
19732 root.end_byte(),
19733 "a swallowing invocation owns the region to the close of its declaration scope"
19734 );
19735
19736 let parsed = parse_cpp_declarations(source, "library.h");
19737 assert_eq!(
19738 function_identities(&parsed),
19739 vec![
19740 (
19741 "library_init".to_string(),
19742 "(struct library_model_loader *)".to_string()
19743 ),
19744 (
19745 "library_init_from_buffer".to_string(),
19746 "(void *, size_t)".to_string()
19747 ),
19748 (
19749 "library_init_from_file".to_string(),
19750 "(const char *)".to_string()
19751 ),
19752 ],
19753 "{:#?}",
19754 parsed.declarations()
19755 );
19756 }
19757
19758 #[test]
19764 fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
19765 let source = r#"LIBRARY_DEPRECATED(
19766 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
19767 "use library_init_from_file_with_params instead"
19768 );"#;
19769 let mut parser = tree_sitter::Parser::new();
19770 parser
19771 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19772 .expect("C++ grammar");
19773 let tree = parser.parse(source, None).expect("C++ tree");
19774 let root = tree.root_node();
19775 let head = root.named_child(0).expect("the invocation");
19776 let ancestry = ParentIndex::new(root);
19777 assert!(
19778 collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
19779 "{}",
19780 root.to_sexp()
19781 );
19782 assert!(
19783 !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
19784 "the ordinary reader must be the one that has it: {}",
19785 root.to_sexp()
19786 );
19787 let unindexed = ParentIndex::unindexed();
19790 assert!(
19791 collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
19792 "indexed and unindexed climbs must agree"
19793 );
19794 assert_eq!(
19795 macro_wrapped_declarations(head, source, &ancestry).len(),
19796 macro_wrapped_declarations(head, source, &unindexed).len(),
19797 "indexed and unindexed climbs must agree"
19798 );
19799 }
19800
19801 #[test]
19806 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
19807 for source in [
19808 "int before;\nFOO(1, 2);\nint after;\n",
19809 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
19810 "int before;\nMACRO(int a, int b);\nint after;\n",
19811 "DECLARE_HANDLE(HWND);\nint after;\n",
19812 ] {
19813 let parsed = parse_cpp_declarations(source, "macro-call.h");
19814 assert_eq!(
19815 function_identities(&parsed),
19816 Vec::new(),
19817 "{source:?} must declare no function: {:#?}",
19818 parsed.declarations()
19819 );
19820 }
19821 }
19822
19823 #[test]
19828 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
19829 let source = r#"#include <string_view>
19830namespace Botan {
19831class DL_Group final {
19832 public:
19833 DL_Group() = default;
19834 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
19835 DL_Group(std::string_view pem, int format);
19836 size_t get_p() const;
19837};
19838}
19839"#;
19840 let parsed = parse_cpp_declarations(source, "dl_group.h");
19841 assert_eq!(
19842 function_identities(&parsed),
19843 vec![
19844 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
19845 (
19846 "Botan.DL_Group.DL_Group".to_string(),
19847 "(std::string_view)".to_string()
19848 ),
19849 (
19850 "Botan.DL_Group.DL_Group".to_string(),
19851 "(std::string_view, int)".to_string()
19852 ),
19853 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
19854 ],
19855 "{:#?}",
19856 parsed.declarations()
19857 );
19858 }
19859
19860 #[test]
19865 fn an_export_macro_class_keeps_its_string_attribute_members() {
19866 let source = r#"#include <string_view>
19867namespace Botan {
19868class BOTAN_PUBLIC_API(2, 0) DL_Group final {
19869 public:
19870 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
19871 DL_Group(std::string_view pem, int format);
19872 size_t get_p() const;
19873};
19874}
19875"#;
19876 let parsed = parse_cpp_declarations(source, "dl_group.h");
19877 assert!(
19878 parsed
19879 .declarations()
19880 .iter()
19881 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
19882 "{:#?}",
19883 parsed.declarations()
19884 );
19885 assert_eq!(
19886 function_identities(&parsed),
19887 vec![
19888 (
19889 "Botan.DL_Group.DL_Group".to_string(),
19890 "(std::string_view)".to_string()
19891 ),
19892 (
19893 "Botan.DL_Group.DL_Group".to_string(),
19894 "(std::string_view, int)".to_string()
19895 ),
19896 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
19897 ],
19898 "{:#?}",
19899 parsed.declarations()
19900 );
19901 }
19902
19903 #[test]
19909 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
19910 let source = r#"namespace Botan {
19911class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
19912 public:
19913 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
19914 XMSS_Parameters(int oid, int len);
19915 size_t len() const;
19916
19917 private:
19918 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
19919 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
19920
19921 int m_oid;
19922 int m_wots_oid;
19923 size_t m_element_size;
19924 size_t m_tree_height;
19925};
19926}
19927"#;
19928 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
19929 let constructors = function_identities(&parsed)
19930 .into_iter()
19931 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
19932 .map(|(_, signature)| signature)
19933 .collect::<Vec<_>>();
19934 assert_eq!(
19935 constructors,
19936 vec![
19937 "()".to_string(),
19938 "(int, int)".to_string(),
19939 "(int, int, size_t, size_t)".to_string(),
19940 ],
19941 "{:#?}",
19942 parsed.declarations()
19943 );
19944 }
19945
19946 #[test]
19950 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
19951 let source = r#"namespace shell {
19952struct Outer {
19953 struct Inner {
19954 Inner(int v);
19955 void run(int v);
19956 };
19957};
19958Outer::Inner::Inner(int v) {}
19959void Outer::Inner::run(int v) {}
19960}
19961"#;
19962 let parsed = parse_cpp_declarations(source, "outer.cpp");
19963 let names = function_identities(&parsed)
19964 .into_iter()
19965 .map(|(fq_name, _)| fq_name)
19966 .collect::<Vec<_>>();
19967 assert!(
19968 names
19969 .iter()
19970 .all(|name| name.starts_with("shell.Outer$Inner.")),
19971 "{names:#?}"
19972 );
19973 }
19974
19975 #[test]
19976 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
19977 let source = r#"namespace control {
19978template <typename T>
19979class AnySpan;
19980template <typename T>
19981class ABSL_ATTRIBUTE_VIEW AnySpan {
19982 public:
19983 int begin() const;
19984};
19985}
19986
19987namespace absl {
19988ABSL_NAMESPACE_BEGIN
19989template <typename T>
19990class ABSL_ATTRIBUTE_VIEW Span {
19991 public:
19992 int begin() const;
19993 int back() const;
19994};
19995
19996int begin();
19997int back();
19998}
19999"#;
20000 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
20001 let declarations = parsed.declarations();
20002 assert!(
20003 declarations
20004 .iter()
20005 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
20006 );
20007 for method in ["begin", "back"] {
20008 assert!(declarations.iter().any(|unit| {
20009 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
20010 }));
20011 assert!(
20012 declarations.iter().any(|unit| {
20013 unit.is_function() && unit.fq_name() == format!("absl.{method}")
20014 })
20015 );
20016 }
20017 assert!(
20018 declarations
20019 .iter()
20020 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
20021 );
20022 assert!(
20023 declarations
20024 .iter()
20025 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
20026 );
20027 assert!(
20028 declarations
20029 .iter()
20030 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
20031 );
20032 }
20033
20034 #[test]
20035 fn explicit_global_member_definition_has_canonical_package_boundary() {
20036 let source = r#"
20037namespace arangodb::aql {
20038class ExecutionPlan {
20039 public:
20040 template<class... Args> Node* createNode(Args&&... args);
20041};
20042}
20043
20044template<class... Args>
20045Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
20046"#;
20047 let parsed = parse_cpp_declarations(source, "global-member.cpp");
20048
20049 assert!(parsed.declarations().iter().any(|unit| {
20050 unit.is_function()
20051 && unit.package_name() == "arangodb::aql"
20052 && unit.short_name() == "ExecutionPlan.createNode"
20053 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
20054 }));
20055 }
20056
20057 #[test]
20058 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
20059 let source = r#"
20060#ifndef TINYXML2_INCLUDED
20061#define TINYXML2_INCLUDED
20062namespace tinyxml2 {
20063class TINYXML2_LIB XMLUtil {
20064 public:
20065 static const char* SkipWhiteSpace(const char* p) {
20066 while (*p) {
20067 if (*p == ' ') {
20068 ++p;
20069 }
20070 }
20071 return p;
20072 }
20073 static bool StringEqual(const char* p, const char* q) {
20074 return p == q;
20075 }
20076 class TINYXML2_LIB Helper {
20077 public:
20078 void Touch();
20079 };
20080 static void ToStr(int value, char* buffer);
20081 private:
20082 static const char* writeBoolTrue;
20083};
20084
20085class TINYXML2_LIB XMLNode {
20086 public:
20087 virtual XMLNode* ShallowClone() const = 0;
20088 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
20089};
20090}
20091#endif
20092"#;
20093 let mut parser = tree_sitter::Parser::new();
20094 parser
20095 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20096 .unwrap();
20097 let tree = parser.parse(source, None).unwrap();
20098 let mut boundary_found = false;
20099 walk_named_tree_preorder(tree.root_node(), true, |node| {
20100 if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
20101 && name == "XMLUtil"
20102 {
20103 boundary_found = fragmented_export_sibling_class_boundary(
20104 node,
20105 source,
20106 &ParentIndex::unindexed(),
20107 )
20108 .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
20109 .is_some_and(|(_, name, _, _)| name == "XMLNode");
20110 }
20111 WalkControl::Continue
20112 });
20113 assert!(
20114 boundary_found,
20115 "fixture must exercise the recovered sibling boundary"
20116 );
20117
20118 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
20119 assert!(
20120 parsed
20121 .declarations()
20122 .iter()
20123 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
20124 "{:#?}",
20125 parsed.declarations()
20126 );
20127 assert!(
20128 parsed
20129 .declarations()
20130 .iter()
20131 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
20132 "{:#?}",
20133 parsed.declarations()
20134 );
20135 assert!(parsed.declarations().iter().any(|unit| {
20136 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
20137 }));
20138 assert!(
20139 parsed
20140 .declarations()
20141 .iter()
20142 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
20143 );
20144 assert!(
20145 parsed
20146 .declarations()
20147 .iter()
20148 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
20149 );
20150 }
20151
20152 #[test]
20153 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
20154 let parsed = parse_cpp_declarations(
20158 r#"
20159namespace cwg311 {
20160namespace X { namespace Y {} }
20161namespace ::cwg311::X {}
20162}
20163"#,
20164 "explicit-global-namespace.cpp",
20165 );
20166
20167 assert!(parsed.declarations().iter().any(|unit| {
20168 unit.kind() == CodeUnitType::Module
20169 && unit.short_name() == "cwg311::X"
20170 && unit.fq_name() == "cwg311::X"
20171 }));
20172 assert!(
20173 parsed
20174 .declarations()
20175 .iter()
20176 .all(|unit| !unit.short_name().contains("::::")),
20177 "recovered namespace names must not retain empty scope components: {:#?}",
20178 parsed.declarations()
20179 );
20180 }
20181
20182 #[test]
20183 fn repeated_scope_separator_does_not_create_empty_function_owner() {
20184 let scope = ScopeInfo {
20185 package_name: "X".to_string(),
20186 module: None,
20187 class_unit: None,
20188 template_signature: None,
20189 template_metadata: None,
20190 declarations_are_fields: false,
20191 recovered_specialization_member_scope: false,
20192 visible_using_namespaces: Vec::new(),
20193 };
20194
20195 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
20196
20197 assert!(owner.is_none());
20198 assert_eq!(name, "doit");
20199 assert_eq!(package, "X");
20200 }
20201
20202 #[test]
20203 fn trailing_decltype_expression_is_not_a_function_declarator() {
20204 let source = r#"
20205namespace boost { namespace detail {
20206#if ! defined(BOOST_NO_SFINAE_EXPR) && \
20207 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
20208 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
20209#define BOOST_THREAD_PROVIDES_INVOKE
20210#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
20211template <class Fp, class A0, class ...Args>
20212inline auto
20213invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
20214 BOOST_THREAD_RV_REF(Args) ...args)
20215 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
20216{
20217 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
20218}
20219#endif
20220#endif
20221}}
20222"#;
20223 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
20224
20225 assert!(
20226 parsed
20227 .declarations()
20228 .iter()
20229 .all(|unit| unit.short_name() != ".*f")
20230 );
20231 }
20232
20233 fn find_class_named<'tree>(
20234 root: Node<'tree>,
20235 source: &str,
20236 expected_name: &str,
20237 ) -> Option<Node<'tree>> {
20238 let mut stack = vec![root];
20239 while let Some(node) = stack.pop() {
20240 if node.kind() == "class_specifier"
20241 && node
20242 .child_by_field_name("name")
20243 .is_some_and(|name| node_text(name, source) == expected_name)
20244 {
20245 return Some(node);
20246 }
20247 let mut cursor = node.walk();
20248 stack.extend(node.named_children(&mut cursor));
20249 }
20250 None
20251 }
20252
20253 #[test]
20254 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
20255 let source = r#"EXPORT void definition(struct Value value) {}
20256EXPORT void prototype(struct Value value);
20257"#;
20258 let mut parser = tree_sitter::Parser::new();
20259 parser
20260 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20261 .unwrap();
20262 let tree = parser.parse(source, None).unwrap();
20263 let root = tree.root_node();
20264 let mut cursor = root.walk();
20265 let callables = root
20266 .named_children(&mut cursor)
20267 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
20268 .collect::<Vec<_>>();
20269
20270 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
20271 for callable in callables {
20272 assert!(callable.has_error(), "fixture must exercise error recovery");
20273 assert!(
20274 cpp_sentinel_macro_parts(callable, source).is_none(),
20275 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
20276 );
20277 }
20278 }
20279
20280 #[test]
20281 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
20282 let source = r#"namespace absl {
20283ABSL_NAMESPACE_BEGIN
20284// Generate a floating-point variate conforming to a Beta distribution:
20285template <typename RealType = double>
20286class beta_distribution {
20287 public:
20288 using result_type = RealType;
20289
20290
20291 beta_distribution() : beta_distribution(1) {}
20292
20293 explicit beta_distribution(result_type alpha, result_type beta = 1)
20294 : param_(alpha, beta) {}
20295
20296 explicit beta_distribution(const param_type& p) : param_(p) {}
20297
20298 void reset() {}
20299
20300 // Generating functions
20301 template <typename URBG>
20302 result_type operator()(URBG& g) { // NOLINT(runtime/references)
20303 return (*this)(g, param_);
20304 }
20305
20306};
20307ABSL_NAMESPACE_END
20308} // namespace absl
20309"#;
20310 let mut parser = tree_sitter::Parser::new();
20311 parser
20312 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20313 .unwrap();
20314 let tree = parser.parse(source, None).unwrap();
20315 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
20316 let body = namespace
20317 .child_by_field_name("body")
20318 .expect("fixture namespace body");
20319 let sentinel = body.named_child(0).expect("sentinel envelope");
20320 let callable = sentinel
20321 .child_by_field_name("declarator")
20322 .and_then(extract_function_declarator)
20323 .and_then(cpp_function_declarator_name_node)
20324 .expect("preserved callable name");
20325
20326 assert_eq!(sentinel.kind(), "function_definition");
20327 assert_eq!(callable.kind(), "operator_name");
20328 assert!(
20329 cpp_sentinel_macro_parts(sentinel, source).is_some(),
20330 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
20331 );
20332 }
20333
20334 #[test]
20335 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
20336 let source = r#"namespace absl {
20337ABSL_NAMESPACE_BEGIN
20338// absl::discrete_distribution
20339//
20340// A discrete distribution produces random integers i, where 0 <= i < n
20341template <typename IntType = int>
20342class discrete_distribution {
20343 public:
20344 using result_type = IntType;
20345 class param_type {
20346 public:
20347 param_type() { init(); }
20348 template <typename InputIterator>
20349 explicit param_type(InputIterator begin, InputIterator end)
20350 : p_(begin, end) {
20351 init();
20352 }
20353 };
20354 discrete_distribution() : param_() {}
20355 explicit discrete_distribution(const param_type& p) : param_(p) {}
20356};
20357ABSL_NAMESPACE_END
20358} // namespace absl
20359"#;
20360 let mut parser = tree_sitter::Parser::new();
20361 parser
20362 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20363 .unwrap();
20364 let tree = parser.parse(source, None).unwrap();
20365 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
20366 let body = namespace
20367 .child_by_field_name("body")
20368 .expect("fixture namespace body");
20369 let sentinel = body.named_child(0).expect("sentinel envelope");
20370 let callable = sentinel
20371 .child_by_field_name("declarator")
20372 .and_then(extract_function_declarator)
20373 .and_then(cpp_function_declarator_name_node)
20374 .expect("preserved callable name");
20375
20376 assert_eq!(sentinel.kind(), "function_definition");
20377 assert_eq!(callable.kind(), "identifier");
20378 assert!(
20379 cpp_sentinel_macro_parts(sentinel, source).is_some(),
20380 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
20381 );
20382 }
20383
20384 #[test]
20385 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
20386 let source = r#"
20387#define CPPCHECKLIB
20388class Library {
20389 struct Container {
20390 CPPCHECKLIB static std::string toString(Yield yield);
20391 CPPCHECKLIB static std::string toString(Action action);
20392 };
20393};
20394"#;
20395 let mut parser = tree_sitter::Parser::new();
20396 parser
20397 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20398 .unwrap();
20399 let tree = parser.parse(source, None).unwrap();
20400 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
20401 let parsed = parse_cpp_file(&file, source, &tree);
20402 assert!(
20403 parsed
20404 .declarations()
20405 .iter()
20406 .all(|unit| unit.fq_name() != "Library$Container.std"),
20407 "the qualified return-type namespace must not become a field: {:#?}",
20408 parsed.declarations()
20409 );
20410 for expected in ["(Yield)", "(Action)"] {
20411 assert!(
20412 parsed.declarations().iter().any(|unit| {
20413 unit.is_function()
20414 && unit.fq_name() == "Library$Container.toString"
20415 && unit.signature() == Some(expected)
20416 }),
20417 "recovered toString overload {expected} is missing: {:#?}",
20418 parsed.declarations()
20419 );
20420 }
20421 }
20422
20423 #[test]
20424 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
20425 let source = r#"
20426#define SIMPLECPP_LIB
20427namespace simplecpp {
20428using TokenString = std::string;
20429struct Location { int line{}; };
20430class SIMPLECPP_LIB Token {
20431 TokenString prefix;
20432 void prefix_method() {}
20433 public:
20434 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
20435 whitespaceahead(wsahead), location(loc), string(s)
20436 // The comment must not hide the constructor body from recovery.
20437 {
20438 flags();
20439 }
20440 TokenString string;
20441 bool whitespaceahead;
20442 Location location;
20443 Token *previous{};
20444 private:
20445 void flags() {
20446 whitespaceahead = true;
20447 }
20448};
20449}
20450"#;
20451 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
20452
20453 let location_fields = parsed
20454 .declarations()
20455 .iter()
20456 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
20457 .collect::<Vec<_>>();
20458 assert_eq!(
20459 location_fields.len(),
20460 1,
20461 "location should have one class-owned declaration: {:#?}",
20462 parsed.declarations()
20463 );
20464 assert!(
20465 location_fields[0].is_field(),
20466 "location has wrong kind: {:#?}",
20467 parsed.declarations()
20468 );
20469 assert!(
20470 parsed.declarations().iter().all(|unit| {
20471 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
20472 })
20473 );
20474 assert!(
20475 parsed.declarations().iter().all(|unit| {
20476 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
20477 })
20478 );
20479 assert!(
20480 parsed
20481 .declarations()
20482 .iter()
20483 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
20484 );
20485 assert!(
20486 parsed
20487 .declarations()
20488 .iter()
20489 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
20490 "the recovered class must retain its constructor: {:#?}",
20491 parsed.declarations()
20492 );
20493 assert!(
20494 parsed
20495 .declarations()
20496 .iter()
20497 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
20498 );
20499 assert!(parsed.declarations().iter().any(|unit| {
20500 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
20501 }));
20502 let constructor = parsed
20503 .declarations()
20504 .iter()
20505 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
20506 .expect("recovered constructor");
20507 let constructor_start = source.find("Token(const").expect("constructor start");
20508 let constructor_end = source
20509 .get(
20510 ..source
20511 .find(" TokenString string;")
20512 .expect("constructor end"),
20513 )
20514 .expect("constructor slice")
20515 .trim_end()
20516 .len();
20517 assert!(
20518 parsed
20519 .navigation_ranges
20520 .get(constructor)
20521 .is_some_and(|ranges| {
20522 ranges.iter().any(|range| {
20523 range.start_byte == constructor_start && range.end_byte == constructor_end
20524 })
20525 }),
20526 "constructor navigation must span the full body: {:#?}",
20527 parsed.navigation_ranges
20528 );
20529 assert_eq!(
20530 parsed
20531 .signature_metadata
20532 .get(constructor)
20533 .and_then(|metadata| metadata.first())
20534 .and_then(SignatureMetadata::callable_linkage),
20535 Some(CallableLinkage::External)
20536 );
20537 let token_class = parsed
20538 .declarations()
20539 .iter()
20540 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
20541 .expect("recovered Token class");
20542 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
20543 assert!(
20544 parsed
20545 .navigation_ranges
20546 .get(token_class)
20547 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
20548 "class navigation must include the terminating semicolon: {:#?}",
20549 parsed.navigation_ranges
20550 );
20551 }
20552
20553 #[test]
20554 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
20555 let source = r#"
20556#define SIMPLECPP_LIB
20557namespace simplecpp {
20558using TokenString = std::string;
20559class Macro;
20560struct Location {
20561 unsigned int fileIndex{};
20562 unsigned int line{};
20563 unsigned int col{};
20564};
20565struct Output {
20566 int type;
20567};
20568class SIMPLECPP_LIB Token {
20569 public:
20570 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
20571 whitespaceahead(wsahead), location(loc), string(s) {
20572 flags();
20573 }
20574 Token(const Token &tok) :
20575 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
20576 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
20577 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
20578 Token &operator=(const Token &tok) = delete;
20579 const TokenString& str() const { return string; }
20580 void setstr(const std::string &s) { string = s; flags(); }
20581 bool isOneOf(const char ops[]) const;
20582 TokenString macro;
20583 char op;
20584 bool comment;
20585 bool name;
20586 bool number;
20587 bool whitespaceahead;
20588 Location location;
20589 Token *previous{};
20590 Token *next{};
20591 private:
20592 void flags() {
20593 name = !string.empty();
20594 comment = false;
20595 number = false;
20596 op = 0;
20597 }
20598 TokenString string;
20599};
20600}
20601struct Following {
20602 int type;
20603};
20604class SIMPLECPP_LIB Later {
20605 public:
20606 Later(int value) : value(value) {}
20607 int value;
20608};
20609"#;
20610 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
20611 assert!(
20612 parsed
20613 .declarations()
20614 .iter()
20615 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
20616 );
20617 assert!(
20618 !parsed
20619 .declarations()
20620 .iter()
20621 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
20622 );
20623 assert!(
20624 parsed
20625 .declarations()
20626 .iter()
20627 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
20628 );
20629 assert!(
20630 !parsed
20631 .declarations()
20632 .iter()
20633 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
20634 );
20635 assert!(
20636 parsed
20637 .declarations()
20638 .iter()
20639 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
20640 );
20641 assert!(
20642 parsed
20643 .declarations()
20644 .iter()
20645 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
20646 );
20647 assert!(
20648 parsed
20649 .declarations()
20650 .iter()
20651 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
20652 );
20653 assert!(
20654 parsed
20655 .declarations()
20656 .iter()
20657 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
20658 );
20659 assert!(
20660 parsed
20661 .declarations()
20662 .iter()
20663 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
20664 );
20665 assert!(
20666 parsed
20667 .declarations()
20668 .iter()
20669 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
20670 );
20671 assert!(parsed.declarations().iter().all(|unit| {
20672 !matches!(
20673 unit.fq_name().as_str(),
20674 "simplecpp.Token.Following" | "simplecpp.Token.Later"
20675 )
20676 }));
20677 assert!(
20678 !parsed
20679 .declarations()
20680 .iter()
20681 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
20682 "the following struct must remain outside the recovered Token class"
20683 );
20684 }
20685
20686 #[test]
20687 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
20688 let source = r#"
20689#define SIMPLECPP_LIB
20690namespace {
20691namespace simplecpp {
20692using TokenString = std::string;
20693struct Location { int line{}; };
20694class SIMPLECPP_LIB HiddenToken {
20695 public:
20696 HiddenToken(const TokenString &s, const Location &loc) :
20697 location(loc), string(s) {
20698 flags();
20699 }
20700 TokenString string;
20701 Location location;
20702 HiddenToken *previous{};
20703 private:
20704 void flags() {}
20705};
20706}
20707}
20708"#;
20709 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
20710 let constructor = parsed
20711 .declarations()
20712 .iter()
20713 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
20714 .expect("recovered anonymous-namespace constructor");
20715 assert_eq!(
20716 parsed
20717 .signature_metadata
20718 .get(constructor)
20719 .and_then(|metadata| metadata.first())
20720 .and_then(SignatureMetadata::callable_linkage),
20721 Some(CallableLinkage::Internal)
20722 );
20723 }
20724
20725 #[test]
20726 fn macro_qualified_static_field_keeps_real_declarator() {
20727 let source = r#"#define JSON_INLINE_VARIABLE
20728struct Reader {
20729static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
20730static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
20731static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
20732};"#;
20733 let mut parser = tree_sitter::Parser::new();
20734 parser
20735 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20736 .unwrap();
20737 let tree = parser.parse(source, None).unwrap();
20738 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
20739 let parsed = parse_cpp_file(&file, source, &tree);
20740 for expected in [
20741 "Reader.npos",
20742 "Reader.other",
20743 "Reader.pointer",
20744 "Reader.reference",
20745 ] {
20746 assert!(
20747 parsed
20748 .declarations()
20749 .iter()
20750 .any(|unit| unit.is_field() && unit.fq_name() == expected),
20751 "real macro-decorated field {expected} is missing: {:#?}",
20752 parsed.declarations()
20753 );
20754 }
20755 assert!(
20756 parsed
20757 .declarations()
20758 .iter()
20759 .all(|unit| unit.fq_name() != "Reader.std"),
20760 "qualified type prefix became a pseudo-field: {:#?}",
20761 parsed.declarations()
20762 );
20763 let root = tree.root_node();
20764 let mut stack = vec![root];
20765 let mut signatures = Vec::new();
20766 while let Some(current) = stack.pop() {
20767 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
20768 {
20769 signatures.extend(
20770 declarators
20771 .into_iter()
20772 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
20773 );
20774 }
20775 let mut cursor = current.walk();
20776 stack.extend(current.named_children(&mut cursor));
20777 }
20778 signatures.sort();
20779 assert_eq!(
20780 signatures,
20781 [
20782 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
20783 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
20784 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
20785 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
20786 ]
20787 );
20788 }
20789
20790 fn member_function_linkage(source: &str) -> CallableLinkage {
20791 let mut parser = tree_sitter::Parser::new();
20792 parser
20793 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20794 .unwrap();
20795 let tree = parser.parse(source, None).unwrap();
20796 let ancestry = ParentIndex::new(tree.root_node());
20797 let mut stack = vec![tree.root_node()];
20798 while let Some(node) = stack.pop() {
20799 if node.kind() == "function_definition" {
20800 let mut current = node.parent();
20801 while let Some(parent) = current {
20802 if matches!(
20803 parent.kind(),
20804 "class_specifier" | "struct_specifier" | "union_specifier"
20805 ) {
20806 return cpp_callable_linkage(node, source, &ancestry);
20807 }
20808 current = parent.parent();
20809 }
20810 }
20811 let mut cursor = node.walk();
20812 stack.extend(node.named_children(&mut cursor));
20813 }
20814 panic!("fixture has no member function definition");
20815 }
20816
20817 #[test]
20818 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
20819 assert_eq!(
20820 member_function_linkage("struct Named { int method() { return 1; } };"),
20821 CallableLinkage::External
20822 );
20823 assert_eq!(
20824 member_function_linkage(
20825 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
20826 ),
20827 CallableLinkage::Internal
20828 );
20829 assert_eq!(
20830 member_function_linkage("struct { int method() { return 1; } } instance;"),
20831 CallableLinkage::Internal
20832 );
20833 assert_eq!(
20834 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
20835 CallableLinkage::Internal
20836 );
20837 }
20838
20839 #[test]
20840 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
20841 let source = r#"
20842#ifndef PROTON_VALUE_HPP
20843#define PROTON_VALUE_HPP
20844namespace proton {
20845namespace internal {
20846class value_base {
20847 protected:
20848 internal::data& data();
20849 internal::data data_;
20850 friend class codec::encoder;
20851 friend class codec::decoder;
20852};
20853}
20854class value : public internal::value_base, private internal::comparable<value> {
20855 private:
20856 template<class T, class U=void> struct assignable :
20857 public std::enable_if<codec::is_encodable<T>::value, U> {};
20858 template<class U> struct assignable<value, U> {};
20859 public:
20860 PN_CPP_EXTERN value();
20861 PN_CPP_EXTERN value(const value&);
20862 PN_CPP_EXTERN value& operator=(const value&);
20863 PN_CPP_EXTERN value(value&&);
20864 PN_CPP_EXTERN value& operator=(value&&);
20865 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
20866 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
20867 codec::encoder e(*this);
20868 e << x;
20869 return *this;
20870 }
20871 PN_CPP_EXTERN type_id type() const;
20872 PN_CPP_EXTERN bool empty() const;
20873 PN_CPP_EXTERN void clear();
20874 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
20875 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
20876 friend PN_CPP_EXTERN void swap(value&, value&);
20877 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
20878 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
20879 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
20880 value(pn_data_t* d);
20881 void reset(pn_data_t* d = 0);
20882};
20883}
20884#endif
20885"#;
20886 let mut parser = tree_sitter::Parser::new();
20887 parser
20888 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20889 .unwrap();
20890 let tree = parser.parse(source, None).unwrap();
20891 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
20892 let parsed = parse_cpp_file(&file, source, &tree);
20893 let macro_constructors = parsed
20894 .signature_metadata
20895 .iter()
20896 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
20897 .flat_map(|(_, metadata)| metadata)
20898 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
20899 .collect::<Vec<_>>();
20900
20901 assert_eq!(
20902 macro_constructors.len(),
20903 3,
20904 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
20905 parsed.declarations()
20906 );
20907 assert!(
20908 macro_constructors.iter().all(|metadata| {
20909 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
20910 }),
20911 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
20912 );
20913 }
20914
20915 #[test]
20916 fn recovered_export_class_typedef_uses_displaced_alias_name() {
20917 let source = r#"
20918namespace spi {
20919class Filter {
20920public:
20921 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
20922};
20923}
20924namespace filter {
20925class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
20926{
20927public:
20928 typedef spi::Filter BASE_CLASS;
20929 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
20930 BEGIN_LOG4CXX_CAST_MAP()
20931 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
20932 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
20933 END_LOG4CXX_CAST_MAP()
20934 FilterDecision decide() const;
20935};
20936}
20937"#;
20938 let mut parser = tree_sitter::Parser::new();
20939 parser
20940 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20941 .unwrap();
20942 let tree = parser.parse(source, None).unwrap();
20943 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
20944 let parsed = parse_cpp_file(&file, source, &tree);
20945 assert!(
20946 parsed.declarations().iter().any(|unit| {
20947 unit.is_class()
20948 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
20949 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
20950 }),
20951 "the displaced typedef alias must retain its declared name: {:#?}",
20952 parsed.declarations()
20953 );
20954 assert!(
20955 parsed
20956 .declarations()
20957 .iter()
20958 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
20959 "the qualified underlying type must not become a false nested alias: {:#?}",
20960 parsed.declarations()
20961 );
20962 }
20963
20964 #[test]
20965 fn exported_single_base_recovery_uses_displaced_class_name() {
20966 let source = r#"
20967class CORE_EXPORT QgsPoint : public AbstractGeometry
20968{
20969 Q_GADGET
20970
20971 Q_PROPERTY( double x READ x WRITE setX )
20972 Q_PROPERTY( double y READ y WRITE setY )
20973 Q_PROPERTY( double z READ z WRITE setZ )
20974 Q_PROPERTY( double m READ m WRITE setM )
20975
20976 public:
20977#ifndef SIP_RUN
20978 QgsPoint(
20979 double x = std::numeric_limits<double>::quiet_NaN(),
20980 double y = std::numeric_limits<double>::quiet_NaN(),
20981 double z = std::numeric_limits<double>::quiet_NaN(),
20982 double m = std::numeric_limits<double>::quiet_NaN(),
20983 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
20984 );
20985#else
20986 QgsPoint( SIP_PYOBJECT x SIP_TYPEHINT( Optional[Union[QgsPoint, QPointF, float]] ) = Py_None, SIP_PYOBJECT y SIP_TYPEHINT( Optional[float] ) = Py_None, SIP_PYOBJECT z SIP_TYPEHINT( Optional[float] ) = Py_None, SIP_PYOBJECT m SIP_TYPEHINT( Optional[float] ) = Py_None, SIP_PYOBJECT wkbType SIP_TYPEHINT( Optional[int] ) = Py_None ) [( double x = 0.0, double y = 0.0, double z = 0.0, double m = 0.0, Qgis::WkbType wkbType = Qgis::WkbType::Unknown )];
20987 % MethodCode
20988 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
20989 {
20990 int state;
20991 sipIsErr = 0;
20992 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
20993 if ( !sipIsErr )
20994 {
20995 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
20996 }
20997 sipReleaseType( p, sipType_QgsPointXY, state );
20998 }
20999 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
21000 {
21001 int state;
21002 sipIsErr = 0;
21003
21004 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
21005 if ( !sipIsErr )
21006 {
21007 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
21008 }
21009 sipReleaseType( p, sipType_QPointF, state );
21010 }
21011 else if (
21012 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
21013 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
21014 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
21015 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
21016 {
21017 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
21018 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
21019 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
21020 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
21021 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
21022 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
21023 }
21024 else // Invalid ctor arguments
21025 {
21026 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
21027 sipIsErr = 1;
21028 }
21029 % End
21030#endif
21031
21032 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
21033 explicit QgsPoint( QPointF p ) SIP_SKIP;
21034 explicit QgsPoint(
21035 Qgis::WkbType wkbType,
21036 double x = std::numeric_limits<double>::quiet_NaN(),
21037 double y = std::numeric_limits<double>::quiet_NaN(),
21038 double z = std::numeric_limits<double>::quiet_NaN(),
21039 double m = std::numeric_limits<double>::quiet_NaN()
21040 ) SIP_SKIP;
21041 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
21042 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
21043 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
21044#ifndef SIP_RUN
21045 private:
21046 bool fuzzyHelper(
21047 double epsilon,
21048 const AbstractGeometry &other,
21049 bool is3DFlag,
21050 bool isMeasureFlag
21051 ) const
21052 {
21053 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
21054 }
21055#endif
21056};
21057class Ordinary : public Base { public: Ordinary(); };
21058class API_EXPORT Plain { public: Plain(); };
21059class API_EXPORT : public Base {};
21060class
21061PN_CPP_CLASS_EXTERN Sender : public Link {
21062 Sender();
21063 struct impl;
21064 struct impl& get_impl() const;
21065};
21066class thread_ctx_t {};
21067class ctx_t ZMQ_FINAL : public thread_ctx_t {
21068 bool start();
21069};
21070"#;
21071 let mut parser = tree_sitter::Parser::new();
21072 parser
21073 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21074 .unwrap();
21075 let tree = parser.parse(source, None).unwrap();
21076 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
21077 let parsed = parse_cpp_file(&file, source, &tree);
21078 let declarations = parsed.declarations();
21079
21080 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
21081 assert!(
21082 declarations
21083 .iter()
21084 .any(|unit| unit.is_class() && unit.fq_name() == expected),
21085 "missing recovered class {expected}: {declarations:#?}"
21086 );
21087 }
21088 let qgs_point = declarations
21089 .iter()
21090 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
21091 .expect("recovered QgsPoint class");
21092 assert_eq!(
21093 parsed.raw_supertypes.get(qgs_point),
21094 Some(&vec!["AbstractGeometry".to_string()]),
21095 "single-base export recovery must retain its displaced base"
21096 );
21097 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
21098 assert!(
21099 parsed
21100 .navigation_ranges
21101 .get(qgs_point)
21102 .is_some_and(|ranges| {
21103 !ranges.is_empty()
21104 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
21105 }),
21106 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
21107 parsed.navigation_ranges.get(qgs_point)
21108 );
21109 let sender = declarations
21110 .iter()
21111 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
21112 .expect("recovered Sender class");
21113 assert_eq!(
21114 parsed.raw_supertypes.get(sender),
21115 Some(&vec!["Link".to_string()]),
21116 "post-declarator export recovery must retain its displaced base"
21117 );
21118 let recovered_member = declarations
21119 .iter()
21120 .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
21121 .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
21122 assert_eq!(
21123 parsed
21124 .signature_metadata
21125 .get(recovered_member)
21126 .and_then(|metadata| metadata.first())
21127 .and_then(SignatureMetadata::callable_linkage),
21128 Some(CallableLinkage::External),
21129 "a named recovered class's members have external linkage"
21130 );
21131 let ctx = declarations
21132 .iter()
21133 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
21134 .expect("recovered ctx_t class");
21135 assert_eq!(
21136 parsed.raw_supertypes.get(ctx),
21137 Some(&vec!["thread_ctx_t".to_string()]),
21138 "postfix export-macro recovery must retain its displaced base"
21139 );
21140 assert!(
21141 declarations.iter().any(|unit| {
21142 unit.is_function()
21143 && unit.fq_name() == "QgsPoint.QgsPoint"
21144 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
21145 }),
21146 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
21147 );
21148 assert!(
21149 declarations.iter().all(|unit| {
21150 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
21151 }),
21152 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
21153 );
21154 }
21155
21156 #[test]
21157 fn function_like_export_macro_classes_keep_names_and_base_edges() {
21158 let source = r#"
21162namespace api {
21163class PROJECT_PUBLIC_API(2, 0) Prelude {
21164 public:
21165 Prelude();
21166};
21167class PROJECT_PUBLIC_API(2, 0) Base {
21168 public:
21169 Base(int value);
21170};
21171class PROJECT_PUBLIC_API(2, 0) Mixin {
21172 public:
21173 Mixin();
21174};
21175class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
21176 public:
21177 Adopted(int value);
21178};
21179class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
21180 public:
21181 Derived(int value);
21182};
21183class PROJECT_PUBLIC_API(2, 0) Solo final {
21184 public:
21185 Solo();
21186};
21187class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
21188 public:
21189 Blended(int value);
21190};
21191class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
21192 public:
21193 Woven(int value);
21194};
21195} // namespace api
21196"#;
21197 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
21198 let declarations = parsed.declarations();
21199 let class_named = |name: &str| {
21200 declarations
21201 .iter()
21202 .find(|unit| unit.is_class() && unit.fq_name() == name)
21203 .unwrap_or_else(|| {
21204 panic!("missing function-like export macro class {name}: {declarations:#?}")
21205 })
21206 };
21207 let base = class_named("api.Base");
21208 class_named("api.Prelude");
21209 class_named("api.Mixin");
21210
21211 assert_eq!(
21212 parsed.raw_supertypes.get(class_named("api.Adopted")),
21213 Some(&vec!["Base".to_string()])
21214 );
21215 assert_eq!(
21216 parsed.raw_supertypes.get(class_named("api.Derived")),
21217 Some(&vec!["Base".to_string()])
21218 );
21219 assert_eq!(
21220 parsed.raw_supertypes.get(class_named("api.Solo")),
21221 None,
21222 "a final class without a base list must not invent a supertype"
21223 );
21224 assert_eq!(
21225 parsed.raw_supertypes.get(class_named("api.Blended")),
21226 Some(&vec!["Base".to_string(), "Mixin".to_string()])
21227 );
21228 assert_eq!(
21229 parsed.raw_supertypes.get(class_named("api.Woven")),
21230 Some(&vec!["Base".to_string(), "Mixin".to_string()])
21231 );
21232 assert!(
21233 declarations
21234 .iter()
21235 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
21236 "the export macro must not become a declaration: {declarations:#?}"
21237 );
21238 assert!(
21239 declarations.iter().all(|unit| !matches!(
21240 unit.identifier(),
21241 "final" | "public" | "protected" | "private"
21242 )),
21243 "the head specifiers must not become declarations: {declarations:#?}"
21244 );
21245 assert!(
21246 parsed
21247 .navigation_ranges
21248 .get(base)
21249 .is_some_and(|ranges| !ranges.is_empty()),
21250 "the recovered base must retain a navigable declaration range"
21251 );
21252 }
21253
21254 #[test]
21255 fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
21256 let source = r#"
21262namespace api {
21263class PROJECT_PUBLIC_API(2, 0) Base {
21264 public:
21265 Base();
21266};
21267class PROJECT_PUBLIC_API(2, 0) Mixin {
21268 public:
21269 Mixin();
21270};
21271class PROJECT_PUBLIC_API(2, 0) Name {
21272 public:
21273 Name();
21274};
21275class PROJECT_PUBLIC_API(2, 0) X509_CA final {
21276 public:
21277 X509_CA();
21278};
21279class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
21280 public:
21281 HSS_LMS_KEY();
21282};
21283class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
21284 public:
21285 GOST_3410();
21286};
21287class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
21288 public:
21289 PKCS11_RSA();
21290};
21291class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
21292 public:
21293 Plain();
21294};
21295class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
21296 public:
21297 Decorated();
21298};
21299class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
21300 public:
21301 Layered();
21302};
21303} // namespace api
21304"#;
21305 let parsed = parse_cpp_declarations(source, "positional-export.hpp");
21306 let declarations = parsed.declarations();
21307 let class_named = |name: &str| {
21308 declarations
21309 .iter()
21310 .find(|unit| unit.is_class() && unit.fq_name() == name)
21311 .unwrap_or_else(|| {
21312 panic!("missing function-like export macro class {name}: {declarations:#?}")
21313 })
21314 };
21315 for (name, bases) in [
21316 ("api.Name", None),
21317 ("api.X509_CA", None),
21318 ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
21319 ("api.GOST_3410", Some(vec!["Base"])),
21320 ("api.PKCS11_RSA", None),
21321 ("api.Plain", None),
21322 ("api.Decorated", Some(vec!["Base"])),
21323 ("api.Layered", Some(vec!["Base", "Mixin"])),
21324 ] {
21325 let expected =
21326 bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
21327 assert_eq!(
21328 parsed.raw_supertypes.get(class_named(name)),
21329 expected.as_ref(),
21330 "{name}"
21331 );
21332 }
21333 assert!(
21334 declarations.iter().all(|unit| !matches!(
21335 unit.identifier(),
21336 "PROJECT_PUBLIC_API"
21337 | "OTHER_MACRO"
21338 | "FIRST_MACRO"
21339 | "SECOND_MACRO"
21340 | "final"
21341 | "public"
21342 )),
21343 "macros and head specifiers must not become declarations: {declarations:#?}"
21344 );
21345 }
21346
21347 #[test]
21348 fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
21349 let source = r#"
21355namespace api {
21356
21357/**
21358* Doc comment
21359*/
21360class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
21361 public:
21362 /**
21363 * Construct from a point.
21364 */
21365 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
21366
21367#if defined(PROJECT_HAS_LEGACY_POINT)
21368 /**
21369 * Construct from a legacy point.
21370 */
21371 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
21372#endif
21373
21374 std::string algo_name() const override;
21375
21376 AlgorithmIdentifier algorithm_identifier() const override;
21377};
21378
21379}
21380"#;
21381 let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
21382 let declarations = parsed.declarations();
21383 let class = declarations
21384 .iter()
21385 .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
21386 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
21387 assert_eq!(
21388 parsed.raw_supertypes.get(class),
21389 Some(&vec!["BaseKey".to_string()]),
21390 "the virtual base is the class's base: {declarations:#?}"
21391 );
21392 for member in [
21393 "api.VirtualBased.algo_name",
21394 "api.VirtualBased.algorithm_identifier",
21395 ] {
21396 assert!(
21397 declarations
21398 .iter()
21399 .any(|unit| unit.is_function() && unit.fq_name() == member),
21400 "{member} must be owned by the recovered class: {declarations:#?}"
21401 );
21402 }
21403 assert!(
21404 declarations
21405 .iter()
21406 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
21407 "an unrecovered head must not mint a macro-named class: {declarations:#?}"
21408 );
21409 }
21410
21411 #[test]
21412 fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
21413 let source = r#"
21420namespace api {
21421
21422DIAGNOSTIC_PUSH
21423DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
21424
21425class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
21426 public virtual api::Inner::Key {
21427 public:
21428 std::string algo_name() const override;
21429};
21430
21431DIAGNOSTIC_POP
21432
21433}
21434"#;
21435 let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
21436 let declarations = parsed.declarations();
21437 let class = declarations
21438 .iter()
21439 .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
21440 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
21441 assert_eq!(
21442 parsed.raw_supertypes.get(class),
21443 Some(&vec![
21444 "api::Outer::Key".to_string(),
21445 "api::Inner::Key".to_string()
21446 ]),
21447 "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
21448 );
21449 assert!(
21450 declarations
21451 .iter()
21452 .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
21453 "the member is owned by the recovered class: {declarations:#?}"
21454 );
21455 assert!(
21456 declarations
21457 .iter()
21458 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
21459 "the macro invocation must not mint a declaration: {declarations:#?}"
21460 );
21461 }
21462
21463 #[test]
21464 fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
21465 let fixture = |head: &str, name: &str| {
21468 format!(
21469 r#"
21470namespace api {{
21471class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
21472 public:
21473 /** Return a descriptive string. */
21474 const char* what() const noexcept override {{ return m_msg.c_str(); }}
21475
21476 /** Return the type of error. */
21477 virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
21478
21479 /** Return an associated error code. */
21480 virtual int error_code() const noexcept {{ return 0; }}
21481
21482 /** Avoid throwing the base directly. */
21483 explicit Exception(std::string_view msg);
21484
21485 /** Avoid throwing the base directly. */
21486 Exception(const char* prefix, std::string_view msg);
21487
21488 /** Avoid throwing the base directly. */
21489 Exception(std::string_view msg, const std::exception& e);
21490
21491 private:
21492 std::string m_msg;
21493}};
21494
21495class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
21496 public:
21497 explicit {name}(std::string_view msg);
21498
21499 explicit {name}(std::string_view msg, std::string_view where);
21500
21501 {name}(std::string_view msg, const std::exception& e);
21502
21503 ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
21504}};
21505}} // namespace api
21506"#
21507 )
21508 };
21509 for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
21510 let source = fixture(head, name);
21511 let mut parser = Parser::new();
21512 parser
21513 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21514 .expect("set C++ grammar");
21515 let tree = parser.parse(&source, None).expect("parse fixture");
21516 let mut embedded = Vec::new();
21517 let mut stack = vec![tree.root_node()];
21518 while let Some(node) = stack.pop() {
21519 embedded.extend(
21520 recover_embedded_function_like_export_classes(node, &source)
21521 .into_iter()
21522 .map(|recovered| (recovered.name, recovered.raw_supertypes)),
21523 );
21524 let mut cursor = node.walk();
21525 stack.extend(node.named_children(&mut cursor));
21526 }
21527 assert!(
21528 embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
21529 "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
21530 tree.root_node().to_sexp()
21531 );
21532 assert!(
21533 embedded
21534 .iter()
21535 .all(|(recovered, _)| recovered != "OTHER_MACRO"),
21536 "{head}: the object-like macro is not a class: {embedded:#?}"
21537 );
21538
21539 let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
21540 let declarations = parsed.declarations();
21541 let class = declarations
21542 .iter()
21543 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
21544 .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
21545 assert_eq!(
21546 parsed.raw_supertypes.get(class),
21547 Some(&vec!["Exception".to_string()]),
21548 "{head}"
21549 );
21550 assert!(
21551 declarations
21552 .iter()
21553 .all(|unit| unit.identifier() != "OTHER_MACRO"),
21554 "{head}: the object-like macro must not become a declaration: {declarations:#?}"
21555 );
21556 }
21557 }
21558
21559 #[test]
21560 fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
21561 let source = r#"
21571namespace api {
21572class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
21573 public virtual Botan::EC_PublicKey {
21574 public:
21575 std::string algo_name() const override { return "ECDSA"; }
21576};
21577} // namespace api
21578"#;
21579 let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
21580 let declarations = parsed.declarations();
21581 assert!(
21582 declarations
21583 .iter()
21584 .all(|unit| !unit.identifier().is_empty()),
21585 "no declaration may carry an empty name: {declarations:#?}"
21586 );
21587 assert!(
21588 declarations
21589 .iter()
21590 .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
21591 "macros and head specifiers must not become declarations: {declarations:#?}"
21592 );
21593 let class = declarations
21594 .iter()
21595 .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
21596 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
21597 assert_eq!(
21598 parsed.raw_supertypes.get(class),
21599 Some(&vec![
21600 "Botan::TPM2::PublicKey".to_string(),
21601 "Botan::EC_PublicKey".to_string()
21602 ]),
21603 "both qualified virtual bases are bases: {declarations:#?}"
21604 );
21605 let source_facts = parsed.source_facts.as_ref().unwrap();
21606 let declaration = parsed
21607 .source_declaration_units
21608 .iter()
21609 .find(|(_, unit)| unit == class)
21610 .map(|(declaration, _)| *declaration)
21611 .expect("recovered class source bridge");
21612 let bases = source_facts
21613 .cpp
21614 .as_ref()
21615 .unwrap()
21616 .declarations
21617 .iter()
21618 .find(|fact| fact.declaration == declaration)
21619 .expect("recovered class source fact")
21620 .bases
21621 .clone();
21622 assert_eq!(
21623 bases
21624 .iter()
21625 .map(|base| base.components.clone())
21626 .collect::<Vec<_>>(),
21627 vec![
21628 vec!["Botan", "TPM2", "PublicKey"],
21629 vec!["Botan", "EC_PublicKey"],
21630 ],
21631 "generalized recovery must publish structured base components"
21632 );
21633 assert!(
21634 bases.iter().all(|base| base.is_virtual),
21635 "generalized recovery must preserve virtual base qualifiers"
21636 );
21637 assert!(
21638 declarations
21639 .iter()
21640 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
21641 "the head must not mint a macro-named class: {declarations:#?}"
21642 );
21643 let mixed_source = source.replace(
21644 "public virtual Botan::EC_PublicKey",
21645 "public Botan::EC_PublicKey",
21646 );
21647 let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
21648 let qualifiers = mixed
21649 .source_facts
21650 .as_ref()
21651 .unwrap()
21652 .cpp
21653 .as_ref()
21654 .unwrap()
21655 .declarations
21656 .iter()
21657 .flat_map(|declaration| &declaration.bases)
21658 .map(|base| base.is_virtual)
21659 .collect::<Vec<_>>();
21660 assert_eq!(
21661 qualifiers,
21662 [true, false],
21663 "base qualifiers must not cross the comma"
21664 );
21665 }
21666
21667 #[test]
21668 fn function_like_export_class_survives_a_preceding_malformed_body() {
21669 let source = r#"
21670namespace api {
21671class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
21672 public:
21673 /** Return a descriptive string. */
21674 const char* what() const noexcept override { return m_msg.c_str(); }
21675
21676 /** Return the type of error. */
21677 virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
21678
21679 /** Return an associated error code. */
21680 virtual int error_code() const noexcept { return 0; }
21681
21682 /** Avoid throwing the base directly. */
21683 explicit Exception(std::string_view msg);
21684
21685 /** Avoid throwing the base directly. */
21686 Exception(const char* prefix, std::string_view msg);
21687
21688 /** Avoid throwing the base directly. */
21689 Exception(std::string_view msg, const std::exception& e);
21690
21691 private:
21692 std::string m_msg;
21693};
21694
21695class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
21696 public:
21697 explicit Invalid_Argument(std::string_view msg);
21698
21699 explicit Invalid_Argument(std::string_view msg, std::string_view where);
21700
21701 Invalid_Argument(std::string_view msg, const std::exception& e);
21702
21703 ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
21704};
21705} // namespace api
21706"#;
21707 let mut parser = Parser::new();
21708 parser
21709 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21710 .expect("set C++ grammar");
21711 let tree = parser.parse(source, None).expect("parse fixture");
21712 let mut stack = vec![tree.root_node()];
21713 let mut saw_embedded_shape = false;
21714 while let Some(node) = stack.pop() {
21715 saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
21716 .iter()
21717 .any(|recovered| recovered.name == "Invalid_Argument");
21718 let mut cursor = node.walk();
21719 stack.extend(node.named_children(&mut cursor));
21720 }
21721 assert!(
21722 saw_embedded_shape,
21723 "fixture must retain the embedded error geometry: {}",
21724 tree.root_node().to_sexp()
21725 );
21726
21727 let parsed = parse_cpp_file(
21728 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
21729 source,
21730 &tree,
21731 );
21732 let declarations = parsed.declarations();
21733 let exception = declarations
21734 .iter()
21735 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
21736 .expect("qualified-base export class");
21737 let invalid = declarations
21738 .iter()
21739 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
21740 .expect("class embedded in the preceding malformed body");
21741
21742 assert_eq!(
21743 parsed.raw_supertypes.get(exception),
21744 Some(&vec!["std::exception".to_string()])
21745 );
21746 assert_eq!(
21747 parsed.raw_supertypes.get(invalid),
21748 Some(&vec!["Exception".to_string()])
21749 );
21750 assert!(
21751 parsed.materialization_records.iter().any(|record| matches!(
21752 record,
21753 MaterializationRecord::RecoveredDeclaration { unit, .. }
21754 if unit == invalid
21755 )),
21756 "the embedded class must retain recovery provenance: {:#?}",
21757 parsed.materialization_records
21758 );
21759 }
21760
21761 #[test]
21762 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
21763 let source = r#"
21764public:
21765 explicit Lookup_Error(std::string_view err) : Exception(err) {}
21766
21767 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
21768"#;
21769 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
21770 .expect("reparse merged constructor body");
21771 let (range, body) =
21772 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
21773 .unwrap_or_else(|| {
21774 panic!(
21775 "the merged constructor must retain its structured declarator/body: {}",
21776 tree.root_node().to_sexp()
21777 )
21778 });
21779 assert_eq!(
21780 source.get(range).expect("constructor range"),
21781 "Lookup_Error(std::string_view err) : Exception(err) {}"
21782 );
21783 assert_eq!(node_text(body, source), "{}");
21784 }
21785
21786 #[test]
21787 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
21788 let positive_source =
21789 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
21790 let mut parser = tree_sitter::Parser::new();
21791 parser
21792 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21793 .unwrap();
21794 let positive_tree = parser.parse(positive_source, None).unwrap();
21795 assert!(cpp_reparsed_members_are_indexable(
21796 positive_tree.root_node(),
21797 positive_source
21798 ));
21799
21800 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
21801 let negative_tree = parser.parse(negative_source, None).unwrap();
21802 assert!(!cpp_reparsed_members_are_indexable(
21803 negative_tree.root_node(),
21804 negative_source
21805 ));
21806 }
21807
21808 #[test]
21809 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
21810 let copy_control_source = r#"
21811public:
21812 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
21813 explicit Token(const Token* tok);
21814 ~Token();
21815 Token* astOperand1() { return nullptr; }
21816"#;
21817 let constraint_source = r#"
21818private:
21819 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
21820 static T *tokAtImpl(T *tok, int index) {
21821 return tok;
21822 }
21823
21824 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
21825 static T *linkAtImpl(T *tok, int index) {
21826 return tok;
21827 }
21828
21829public:
21830 int late() const { return 1; }
21831"#;
21832 let mut parser = tree_sitter::Parser::new();
21833 parser
21834 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21835 .unwrap();
21836 let copy_control_tree = parser
21837 .parse(copy_control_source, None)
21838 .expect("parse copy-control fixture");
21839 assert!(
21840 copy_control_tree.root_node().has_error(),
21841 "fixture must exercise adjacent copy-control recovery"
21842 );
21843 assert!(
21844 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
21845 "a complete late getter must remain recoverable after adjacent copy-control declarations"
21846 );
21847 let mut cursor = copy_control_tree.root_node().walk();
21848 assert!(
21849 copy_control_tree
21850 .root_node()
21851 .named_children(&mut cursor)
21852 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
21853 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
21854 copy_control_tree.root_node().to_sexp()
21855 );
21856 let constraint_tree = parser
21857 .parse(constraint_source, None)
21858 .expect("parse constraint-macro fixture");
21859 assert!(constraint_tree.root_node().has_error());
21860 assert!(
21861 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
21862 "complete constraint-macro members must not hide a later ordinary member"
21863 );
21864 let mut cursor = constraint_tree.root_node().walk();
21865 assert!(
21866 constraint_tree
21867 .root_node()
21868 .named_children(&mut cursor)
21869 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
21870 child,
21871 constraint_source
21872 )),
21873 "fixture must retain the split constraint-macro prefix/function geometry"
21874 );
21875 }
21876
21877 #[test]
21878 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
21879 let source = r#"
21880struct Analyzer {
21881 struct Action {
21882 Action() = default;
21883 Action(const Action&) = default;
21884 Action& operator=(const Action& rhs) & = default;
21885
21886 template<class T,
21887 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
21888 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
21889 // NOLINTNEXTLINE(google-explicit-constructor)
21890 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
21891 {}
21892
21893 enum : std::uint16_t { None = 0, Read = (1 << 0) };
21894 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
21895
21896 private:
21897 unsigned int mFlag{};
21898 };
21899
21900 enum class Direction : unsigned char { Forward, Reverse };
21901 virtual Action analyze(Direction d) const = 0;
21902};
21903"#;
21904 let mut parser = tree_sitter::Parser::new();
21905 parser
21906 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21907 .unwrap();
21908 let tree = parser.parse(source, None).unwrap();
21909 assert!(tree.root_node().has_error());
21910 let root = tree.root_node();
21911 let outer = root
21912 .named_children(&mut root.walk())
21913 .find(|child| child.kind() == "ERROR")
21914 .expect("fragmented Analyzer prefix");
21915 let outer_recovered =
21916 fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
21917 assert_eq!(outer_recovered.name, "Analyzer");
21918 let outer_tree = cpp_reparse_fragmented_class_body(
21919 source,
21920 outer_recovered.body.reparse_start,
21921 outer_recovered.body.reparse_end,
21922 )
21923 .expect("reparse Analyzer body");
21924 let outer_root = outer_tree.root_node();
21925 let action_prefix = outer_root
21926 .named_children(&mut outer_root.walk())
21927 .find(|child| child.kind() == "ERROR")
21928 .expect("fragmented Action prefix");
21929 let action_recovered = fragmented_class_body(action_prefix, source)
21930 .expect("structured Action fragment boundary");
21931 assert_eq!(action_recovered.name, "Action");
21932 let action_tree = cpp_reparse_fragmented_class_body(
21933 source,
21934 action_recovered.body.reparse_start,
21935 action_recovered.body.reparse_end,
21936 )
21937 .expect("reparse Action body");
21938 let action_root = action_tree.root_node();
21939 let macro_prefix = action_root
21940 .named_children(&mut action_root.walk())
21941 .find(|child| child.kind() == "ERROR")
21942 .expect("constraint macro prefix");
21943 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
21944 .expect("structured template macro prefix");
21945 let macro_companion =
21946 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
21947 assert!(
21948 cpp_reparsed_template_macro_constructor_companion_is_indexable(
21949 macro_companion,
21950 macro_parameter,
21951 source,
21952 ),
21953 "split constrained constructor must be admitted: {}",
21954 macro_companion.to_sexp()
21955 );
21956 assert!(
21957 cpp_reparsed_members_are_indexable(action_root, source),
21958 "complete Action body must pass the recovery gate: {}",
21959 action_tree.root_node().to_sexp()
21960 );
21961 assert!(
21962 cpp_reparsed_members_are_indexable(outer_root, source),
21963 "complete Analyzer body must pass the recovery gate: {}",
21964 outer_tree.root_node().to_sexp()
21965 );
21966 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
21967 let parsed = parse_cpp_file(&file, source, &tree);
21968 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
21969 assert!(
21970 parsed
21971 .declarations()
21972 .iter()
21973 .any(|unit| unit.fq_name() == expected),
21974 "missing recovered declaration {expected}: {:#?}",
21975 parsed.declarations()
21976 );
21977 }
21978 assert!(
21979 parsed
21980 .declarations()
21981 .iter()
21982 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
21983 "nested members must not remain flattened: {:#?}",
21984 parsed.declarations()
21985 );
21986 }
21987
21988 #[test]
21989 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
21990 let source = r#"
21991raw_hash_set& operator=(raw_hash_set&& that) {
21992 return move_assign(
21993 std::move(that),
21994 typename AllocTraits::propagate_on_container_move_assignment());
21995}
21996
21997iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
21998 return {};
21999}
22000
22001void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
22002
22003iterator insert(const_iterator hint, value_type&& value)
22004 ABSL_ATTRIBUTE_LIFETIME_BOUND {
22005 return {};
22006}
22007
22008friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
22009 return left.size() == right.size();
22010}
22011
22012static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
22013 return static_cast<slot_type*>(buffer);
22014}
22015
22016protected:
22017// Included-range recovery can attach this comment to the template prefix.
22018template <class K>
22019void AssertOnFind([[maybe_unused]] const K& key) {
22020 Check(key);
22021}
22022"#;
22023 let mut parser = tree_sitter::Parser::new();
22024 parser
22025 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22026 .unwrap();
22027 let tree = parser.parse(source, None).unwrap();
22028 assert!(
22029 tree.root_node().has_error(),
22030 "the fixture must exercise tree-sitter's errorful member shapes"
22031 );
22032 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
22033
22034 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
22035 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
22036 assert!(!cpp_reparsed_members_are_indexable(
22037 incomplete_tree.root_node(),
22038 incomplete_source
22039 ));
22040
22041 let outside_error_source = "int foo() stray_attribute {}\n";
22042 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
22043 assert!(outside_error_tree.root_node().has_error());
22044 assert!(!cpp_reparsed_members_are_indexable(
22045 outside_error_tree.root_node(),
22046 outside_error_source
22047 ));
22048
22049 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
22050 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
22051 assert!(!cpp_reparsed_members_are_indexable(
22052 variable_initializer_tree.root_node(),
22053 variable_initializer_source
22054 ));
22055 }
22056
22057 #[test]
22058 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
22059 let positive_source = r#"
22060std::pair<iterator, bool> insert(init_type&& value)
22061 ABSL_ATTRIBUTE_LIFETIME_BOUND
22062#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
22063 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
22064#endif
22065{
22066 return emplace(std::move(value));
22067}
22068"#;
22069 let mut parser = tree_sitter::Parser::new();
22070 parser
22071 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22072 .unwrap();
22073 let positive_tree = parser.parse(positive_source, None).unwrap();
22074 assert!(
22075 positive_tree.root_node().has_error(),
22076 "the fixture must exercise the split attribute/requires shape"
22077 );
22078 assert!(cpp_reparsed_members_are_indexable(
22079 positive_tree.root_node(),
22080 positive_source
22081 ));
22082
22083 let template_return_source = r#"
22084pair<int> insert(init_type&& value)
22085 ABSL_ATTRIBUTE_LIFETIME_BOUND
22086#if LANGUAGE_LEVEL >= 202002L
22087 requires(!Predicate<init_type>::value)
22088#endif
22089// Attributes and the function body may be separated by comments.
22090{
22091 return {};
22092}
22093"#;
22094 let template_return_tree = parser.parse(template_return_source, None).unwrap();
22095 assert!(
22096 cpp_reparsed_members_are_indexable(
22097 template_return_tree.root_node(),
22098 template_return_source
22099 ),
22100 "template-return attribute/requires tree: {}",
22101 template_return_tree.root_node().to_sexp()
22102 );
22103
22104 let no_body_source = r#"
22105std::pair<iterator, bool> insert(init_type&& value)
22106 ABSL_ATTRIBUTE_LIFETIME_BOUND
22107#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
22108 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
22109#endif
22110+ 0;
22111"#;
22112 let no_body_tree = parser.parse(no_body_source, None).unwrap();
22113 assert!(!cpp_reparsed_members_are_indexable(
22114 no_body_tree.root_node(),
22115 no_body_source
22116 ));
22117
22118 let extra_payload_source = r#"
22119pair<int> insert(init_type&& value)
22120 ABSL_ATTRIBUTE_LIFETIME_BOUND
22121#if LANGUAGE_LEVEL >= 202002L
22122 int unrelated;
22123 requires(Predicate<init_type>::value)
22124#endif
22125{
22126 return {};
22127}
22128"#;
22129 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
22130 assert!(!cpp_reparsed_members_are_indexable(
22131 extra_payload_tree.root_node(),
22132 extra_payload_source
22133 ));
22134
22135 let variable_initializer_source = r#"
22136int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
22137#if LANGUAGE_LEVEL >= 202002L
22138 requires(true)
22139#endif
22140{
22141 bad;
22142}
22143"#;
22144 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
22145 assert!(!cpp_reparsed_members_are_indexable(
22146 variable_initializer_tree.root_node(),
22147 variable_initializer_source
22148 ));
22149 }
22150
22151 #[test]
22152 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
22153 let source = r#"namespace absl {
22154ABSL_NAMESPACE_BEGIN namespace container_internal {
22155
22156class raw_hash_set : public Base {
22157 public:
22158 using value_type = int;
22159
22160 template <class U,
22161 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
22162 void insert(U value) { (void)value; }
22163
22164 struct InsertSlot {
22165 raw_hash_set& s;
22166 };
22167};
22168
22169}
22170ABSL_NAMESPACE_END
22171}"#;
22172 let mut parser = tree_sitter::Parser::new();
22173 parser
22174 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22175 .unwrap();
22176 let tree = parser.parse(source, None).unwrap();
22177 let root = tree.root_node();
22178 let outer_namespace = root
22179 .named_children(&mut root.walk())
22180 .find(|child| child.kind() == "namespace_definition")
22181 .expect("outer absl namespace");
22182 let declaration_list = outer_namespace
22183 .child_by_field_name("body")
22184 .expect("outer namespace body");
22185 let sentinel_function = declaration_list
22186 .named_children(&mut declaration_list.walk())
22187 .find(|child| child.kind() == "function_definition")
22188 .expect("malformed namespace sentinel function");
22189 let ancestry = ParentIndex::new(root);
22190 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
22191 .expect("structured nested namespace sentinel");
22192 let fragmented =
22193 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
22194 .expect("fragmented raw_hash_set class");
22195 assert_eq!(fragmented.class_node.kind(), "ERROR");
22196 assert_eq!(fragmented.name, "raw_hash_set");
22197 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
22198
22199 let outer_scope =
22200 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
22201 let mut outer_siblings = Vec::new();
22202 push_cpp_sentinel_sibling_classes(
22203 &mut outer_siblings,
22204 declaration_list,
22205 sentinel.function,
22206 &outer_scope,
22207 source,
22208 &ancestry,
22209 );
22210 let [outer_shadow] = outer_siblings.as_slice() else {
22211 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
22212 };
22213 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
22214 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
22215
22216 let field = " raw_hash_set& s;";
22217 let start = source.find(field).expect("InsertSlot field") + 4;
22218 let node = root
22219 .descendant_for_byte_range(start, start + "raw_hash_set".len())
22220 .expect("raw_hash_set type node");
22221 let recovered = cpp_sentinel_recovered_classes(root, source);
22222 let [deep_class] = recovered.as_slice() else {
22223 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
22224 };
22225 assert_eq!(
22226 deep_class.namespace_scope_components,
22227 vec!["absl", "container_internal"]
22228 );
22229 assert_eq!(
22230 deep_class.scope_components,
22231 vec!["absl", "container_internal", "raw_hash_set"]
22232 );
22233 assert!(
22234 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
22235 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
22236 );
22237
22238 assert_eq!(
22239 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
22240 Some(vec![
22241 "absl".to_string(),
22242 "container_internal".to_string(),
22243 "raw_hash_set".to_string(),
22244 "InsertSlot".to_string(),
22245 ])
22246 );
22247
22248 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
22249 let parsed = parse_cpp_file(&file, source, &tree);
22250 let raw_hash_set = parsed
22251 .declarations()
22252 .iter()
22253 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
22254 .expect("recovered raw_hash_set class");
22255 assert_eq!(
22256 raw_hash_set.fq_name(),
22257 "absl::container_internal.raw_hash_set",
22258 "the recovered declaration must publish under the deeper sentinel namespace"
22259 );
22260 assert_eq!(
22261 parsed.raw_supertypes.get(raw_hash_set),
22262 Some(&vec!["Base".to_string()]),
22263 "the structured base clause on the fragmented ERROR prefix must survive publication"
22264 );
22265 assert!(
22266 parsed.materialization_records.iter().any(|record| matches!(
22267 record,
22268 MaterializationRecord::RecoveredDeclaration { recovery, unit }
22269 if unit == raw_hash_set && *recovery == deep_class.class_range
22270 )),
22271 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
22272 parsed.materialization_records
22273 );
22274 }
22275
22276 #[test]
22303 fn the_parent_index_answers_what_tree_sitter_answers() {
22304 const SHAPES: [&str; 5] = [
22305 "namespace outer { namespace inner { struct Tag { int field; }; } }",
22306 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
22307 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
22308 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
22309 "class API Broken : public First, public Second {\n void member();\n",
22310 ];
22311 for source in SHAPES {
22312 let mut parser = tree_sitter::Parser::new();
22313 parser
22314 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22315 .unwrap();
22316 let tree = parser.parse(source, None).unwrap();
22317 let root = tree.root_node();
22318 let ancestry = ParentIndex::new(root);
22319 let mut nodes = 0usize;
22320 let mut stack = vec![root];
22321 while let Some(node) = stack.pop() {
22322 nodes += 1;
22323 assert_eq!(
22324 node.parent().map(|parent| parent.id()),
22325 ancestry.parent(node).map(|parent| parent.id()),
22326 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
22327 );
22328 let mut cursor = node.walk();
22329 stack.extend(node.children(&mut cursor));
22330 }
22331 assert!(nodes > 1, "{source:?} produced no tree to compare");
22332 }
22333 }
22334
22335 #[test]
22342 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
22343 const DEPTH: usize = 64;
22344 let mut source = String::new();
22345 for level in 0..DEPTH {
22346 writeln!(source, "namespace n{level} {{").unwrap();
22347 }
22348 source.push_str("int deepest(int value);\n");
22349 for _ in 0..DEPTH {
22350 source.push_str("}\n");
22351 }
22352
22353 let mut parser = tree_sitter::Parser::new();
22354 parser
22355 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22356 .unwrap();
22357 let tree = parser.parse(&source, None).unwrap();
22358 let root = tree.root_node();
22359 let ancestry = ParentIndex::new(root);
22360 let mut function_declarator = None;
22361 walk_named_tree_preorder(root, true, |node| {
22362 if node.kind() == "function_declarator" {
22363 function_declarator = Some(node);
22364 WalkControl::Break
22365 } else {
22366 WalkControl::Continue
22367 }
22368 });
22369 let function_declarator = function_declarator.expect("deepest function declarator");
22370 let ancestor_count =
22371 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
22372
22373 ancestry.reset_parent_query_count_for_test();
22374 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
22375 assert_eq!(DEPTH, lexical_scope.len());
22376 assert_eq!(
22377 ancestor_count + 1,
22378 ancestry.parent_query_count_for_test(),
22379 "lexical-scope ancestry bypassed the parent index"
22380 );
22381
22382 ancestry.reset_parent_query_count_for_test();
22383 assert_eq!(
22384 Some(DispatchExtensibility::Closed),
22385 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
22386 );
22387 assert_eq!(
22388 ancestor_count,
22389 ancestry.parent_query_count_for_test(),
22390 "dispatch ancestry bypassed the parent index"
22391 );
22392
22393 ancestry.reset_parent_query_count_for_test();
22394 assert_eq!(
22395 CallableLinkage::External,
22396 cpp_callable_linkage(function_declarator, &source, &ancestry)
22397 );
22398 assert_eq!(
22399 ancestor_count + 1,
22400 ancestry.parent_query_count_for_test(),
22401 "linkage ancestry bypassed the parent index"
22402 );
22403
22404 ancestry.reset_parent_query_count_for_test();
22405 assert!(!cpp_callable_is_structural_constructor(
22406 function_declarator,
22407 &source,
22408 &ancestry
22409 ));
22410 assert_eq!(
22411 ancestor_count + 1,
22412 ancestry.parent_query_count_for_test(),
22413 "constructor ancestry bypassed the parent index"
22414 );
22415 }
22416
22417 #[test]
22427 fn callable_dispatch_extensibility_comes_from_the_class_body() {
22428 const SOURCE: &str = "\
22429class Plain { public: long send(long order); };
22430class Derived : public Base { public: long send(long order); };
22431class Sealed : public Base { public: long send(long order) final; };
22432class Hiding : public Base { public: long send(long order) { return order; } };
22433class Virtual { public: virtual long send(long order); };
22434namespace shop { long free_call(long order); }
22435long Plain::send(long order) { return order; }
22436";
22437 let mut parser = tree_sitter::Parser::new();
22438 parser
22439 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22440 .unwrap();
22441 let tree = parser.parse(SOURCE, None).unwrap();
22442 let root = tree.root_node();
22443 let ancestry = ParentIndex::new(root);
22444 let mut answers = Vec::new();
22445 walk_named_tree_preorder(root, true, |node| {
22446 if node.kind() != "function_declarator" {
22447 return WalkControl::Continue;
22448 }
22449 let name = cpp_function_declarator_name_node(node).expect("declarator name");
22450 answers.push((
22451 node_text(name, SOURCE).to_string(),
22452 cpp_callable_dispatch_extensibility(node, &ancestry),
22453 ));
22454 WalkControl::Continue
22455 });
22456 assert_eq!(
22457 answers,
22458 vec![
22459 ("send".to_string(), Some(DispatchExtensibility::Closed)),
22460 ("send".to_string(), Some(DispatchExtensibility::Open)),
22461 ("send".to_string(), Some(DispatchExtensibility::Closed)),
22462 ("send".to_string(), Some(DispatchExtensibility::Open)),
22463 ("send".to_string(), Some(DispatchExtensibility::Open)),
22464 ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
22465 ("Plain::send".to_string(), None),
22466 ],
22467 "the class body states the fact; a qualified definition states none"
22468 );
22469 }
22470
22471 #[test]
22476 fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
22477 for aggregates in [64usize, 512] {
22478 let mut source =
22479 String::from("typedef unsigned long long u64;\nnamespace generated {\n");
22480 for index in 0..aggregates {
22481 writeln!(source, "struct tag{index};").unwrap();
22482 }
22483 for index in (0..aggregates).rev() {
22484 writeln!(
22485 source,
22486 "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
22487 )
22488 .unwrap();
22489 }
22490 source.push_str("}\n");
22491
22492 start_code_unit_removal_scan_probe();
22493 let parsed = parse_cpp_declarations(&source, "vmlinux.h");
22494 let scanned = finish_code_unit_removal_scan_probe();
22495
22496 let expected_names: Vec<String> = (0..aggregates)
22497 .rev()
22498 .map(|index| format!("tag{index}"))
22499 .collect();
22500 let top_level_names: Vec<String> = parsed
22501 .top_level_declarations
22502 .iter()
22503 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
22504 .map(|unit| unit.short_name().to_string())
22505 .collect();
22506 let namespace = parsed
22507 .declarations()
22508 .iter()
22509 .find(|unit| {
22510 unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
22511 })
22512 .expect("generated namespace should be declared");
22513 let child_names: Vec<String> = parsed.children[namespace]
22514 .iter()
22515 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
22516 .map(|unit| unit.short_name().to_string())
22517 .collect();
22518 assert_eq!(
22519 aggregates,
22520 parsed
22521 .declarations()
22522 .iter()
22523 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
22524 .count(),
22525 "every aggregate must still be declared at {aggregates} aggregates"
22526 );
22527 assert_eq!(expected_names, top_level_names);
22528 assert_eq!(expected_names, child_names);
22529 assert_eq!(
22530 0, scanned,
22531 "replacing {aggregates} forward declarations must compact their shared lists once"
22532 );
22533 }
22534 }
22535
22536 #[test]
22537 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
22538 const DISTINCT_PER_KIND: usize = 64;
22539 let mut source = String::new();
22540 for index in 0..DISTINCT_PER_KIND {
22541 writeln!(source, "typedef int Alias{index};").unwrap();
22542 }
22543 writeln!(source, "typedef long Alias0;").unwrap();
22544 for index in 0..DISTINCT_PER_KIND {
22545 writeln!(source, "#define MACRO_{index} {index}").unwrap();
22546 }
22547 writeln!(source, "#define MACRO_0 duplicate").unwrap();
22548 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
22549
22550 let mut parser = tree_sitter::Parser::new();
22551 parser
22552 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22553 .unwrap();
22554 let tree = parser.parse(&source, None).unwrap();
22555 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
22556
22557 start_declaration_identity_comparison_probe();
22558 let parsed = parse_cpp_file(&file, &source, &tree);
22559 let comparisons = finish_declaration_identity_comparison_probe();
22560
22561 assert_eq!(
22562 DISTINCT_PER_KIND + 1,
22563 parsed
22564 .declarations()
22565 .iter()
22566 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
22567 .count(),
22568 "every physical typedef alias declaration must be retained so \
22569 conditional branch guards stay available to the resolver"
22570 );
22571 assert_eq!(
22572 DISTINCT_PER_KIND + 1,
22573 parsed
22574 .declarations()
22575 .iter()
22576 .filter(|unit| {
22577 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
22578 })
22579 .count(),
22580 "distinct macro redefinitions must remain available to temporal lookup"
22581 );
22582 assert_eq!(
22583 2,
22584 parsed
22585 .declarations()
22586 .iter()
22587 .filter(|unit| {
22588 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
22589 })
22590 .count(),
22591 "function overloads must remain distinct"
22592 );
22593
22594 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
22595 assert!(
22596 comparisons <= dedup_inputs * 4,
22597 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
22598 );
22599 }
22600
22601 #[test]
22602 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
22603 let source = r#"namespace absl {
22604ABSL_NAMESPACE_BEGIN namespace container_internal {
22605template <typename T>
22606class broken {
22607 public:
22608 using value_type = T;
22609 T operator->() const { return &operator*(); }
22610 using alias = value_type;
22611};
22612}
22613}
22614"#;
22615 let mut parser = tree_sitter::Parser::new();
22616 parser
22617 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22618 .unwrap();
22619 let tree = parser.parse(source, None).unwrap();
22620 let broken = find_class_named(tree.root_node(), source, "broken")
22621 .expect("the positive fixture must expose the broken class node");
22622 assert!(
22623 broken.has_error(),
22624 "the positive fixture must retain an internal parser error"
22625 );
22626 assert!(
22627 cpp_complete_class_body_close(broken).is_some(),
22628 "the positive fixture must expose a real class body close"
22629 );
22630 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
22631 assert!(
22632 recovered.iter().any(|class| {
22633 class.scope_components == ["absl", "container_internal", "broken"]
22634 }),
22635 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
22636 );
22637 }
22638
22639 #[test]
22640 fn sentinel_recovery_keeps_members_after_nested_body_close() {
22641 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
22642NLOHMANN_BASIC_JSON_TPL_DECLARATION
22643class basic_json {
22644 private:
22645 union storage {
22646 int value;
22647 } data;
22648 public:
22649 using late_alias = int;
22650 late_alias value() const;
22651};
22652NLOHMANN_JSON_NAMESPACE_END
22653"#;
22654 let mut parser = tree_sitter::Parser::new();
22655 parser
22656 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22657 .unwrap();
22658 let tree = parser.parse(source, None).unwrap();
22659 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
22660 let basic_json = recovered
22661 .iter()
22662 .find(|class| {
22663 class
22664 .scope_components
22665 .last()
22666 .is_some_and(|name| name == "basic_json")
22667 })
22668 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
22669 let late_alias = source
22670 .find("late_alias value")
22671 .expect("late alias reference");
22672 assert!(
22673 basic_json.class_range.start_byte < late_alias
22674 && late_alias < basic_json.class_range.end_byte,
22675 "the recovered class range must include members after a nested close: {basic_json:#?}"
22676 );
22677 }
22678
22679 #[test]
22680 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
22681 let source = r#"namespace absl {
22682ABSL_NAMESPACE_BEGIN namespace container_internal {
22683template <typename T>
22684class broken {
22685 public:
22686 using value_type = T;
22687 T operator->() const { return &operator*(); }
22688}
22689}
22690"#;
22691 let mut parser = tree_sitter::Parser::new();
22692 parser
22693 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22694 .unwrap();
22695 let tree = parser.parse(source, None).unwrap();
22696 let broken = find_class_named(tree.root_node(), source, "broken")
22697 .expect("the negative fixture must expose the malformed class node");
22698 assert!(
22699 broken.has_error(),
22700 "the negative fixture must retain a parser error"
22701 );
22702 assert!(
22703 cpp_complete_class_body_close(broken).is_none(),
22704 "the malformed class must not expose a real body close"
22705 );
22706 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
22707 assert!(
22708 recovered
22709 .iter()
22710 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
22711 "an incomplete class must not borrow the namespace close: {recovered:#?}"
22712 );
22713 }
22714
22715 #[test]
22716 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
22717 let source = r#"namespace absl {
22718ABSL_NAMESPACE_BEGIN namespace container_internal {
22719template <typename T>
22720struct broken {
22721 using value_type = T;
22722};
22723}
22724
22725#ifdef OWNER_DEF
22726template <typename T>
22727typename broken<T>::value_type broken<T>::method() {
22728 value_type value{};
22729 return value;
22730}
22731#endif
22732
22733namespace sibling {
22734template <typename T>
22735typename broken<T>::value_type broken<T>::other() {
22736 value_type value{};
22737 return value;
22738}
22739}
22740
22741ABSL_NAMESPACE_END
22742}
22743"#;
22744 let mut parser = tree_sitter::Parser::new();
22745 parser
22746 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22747 .unwrap();
22748 let tree = parser.parse(source, None).unwrap();
22749 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
22750 let broken = recovered
22751 .iter()
22752 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
22753 .expect("the sentinel class must be recovered");
22754 let method_start = source
22755 .find("typename broken<T>::value_type broken<T>::method()")
22756 .expect("guarded sibling owner");
22757 let method_end = source[method_start..]
22758 .find("\n}")
22759 .map(|offset| method_start + offset + 2)
22760 .expect("guarded sibling owner close");
22761 assert!(
22762 broken
22763 .owner_ranges
22764 .iter()
22765 .any(|owner| owner.range.start_byte <= method_start
22766 && method_end <= owner.range.end_byte),
22767 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
22768 );
22769 let sibling_start = source
22770 .find("typename broken<T>::value_type broken<T>::other()")
22771 .expect("nested namespace sibling owner");
22772 assert!(
22773 broken
22774 .owner_ranges
22775 .iter()
22776 .all(|owner| owner.range.start_byte > sibling_start
22777 || owner.range.end_byte <= sibling_start),
22778 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
22779 );
22780 }
22781
22782 #[test]
22783 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
22784 let source = r#"#ifdef OUTER
22785namespace absl {
22786ABSL_NAMESPACE_BEGIN namespace container_internal {
22787template <typename T>
22788struct broken {
22789 using value_type = T;
22790};
22791}
22792}
22793
22794#ifdef OWNER_DEF
22795template <typename T>
22796typename broken<T>::value_type broken<T>::method() {
22797 value_type value{};
22798 return value;
22799}
22800#endif
22801#endif
22802"#;
22803 let mut parser = tree_sitter::Parser::new();
22804 parser
22805 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22806 .unwrap();
22807 let tree = parser.parse(source, None).unwrap();
22808 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
22809 let broken = recovered
22810 .iter()
22811 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
22812 .expect("the sentinel class must be recovered");
22813 let method_start = source
22814 .find("typename broken<T>::value_type broken<T>::method()")
22815 .expect("outer sibling owner");
22816 assert!(
22817 broken
22818 .owner_ranges
22819 .iter()
22820 .all(|owner| owner.range.start_byte > method_start
22821 || owner.range.end_byte <= method_start),
22822 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
22823 );
22824 }
22825
22826 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
22828 let mut signatures = parsed
22829 .declarations()
22830 .iter()
22831 .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
22832 .filter_map(|unit| unit.signature().map(str::to_string))
22833 .collect::<Vec<_>>();
22834 signatures.sort();
22835 signatures.dedup();
22836 signatures
22837 }
22838
22839 #[test]
22840 fn callable_parameter_types_come_from_the_ast_parameter_list() {
22841 let source = r#"
22842template <typename T, ENABLE_BYTES(T)>
22843Vec256<T> DupOdd(Vec256<T> value) { return value; }
22844
22845struct Visitor {
22846 void fail(this auto const& self) {}
22847};
22848"#;
22849 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
22850 let dup_odd = parsed
22851 .declarations()
22852 .iter()
22853 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
22854 .expect("DupOdd declaration");
22855 assert_eq!(
22856 dup_odd.signature(),
22857 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
22858 );
22859 assert_eq!(
22860 parsed
22861 .signature_metadata
22862 .get(dup_odd)
22863 .and_then(|metadata| metadata.first())
22864 .and_then(SignatureMetadata::callable_parameter_types),
22865 Some(["Vec256<T>".to_string()].as_slice())
22866 );
22867
22868 let fail = parsed
22869 .declarations()
22870 .iter()
22871 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
22872 .expect("explicit-object member");
22873 assert_eq!(fail.signature(), Some("(const this auto &)"));
22874 let metadata = parsed
22875 .signature_metadata
22876 .get(fail)
22877 .and_then(|metadata| metadata.first())
22878 .expect("explicit-object signature metadata");
22879 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
22880 assert!(
22881 metadata
22882 .callable_arity()
22883 .is_some_and(|arity| arity.accepts(0))
22884 );
22885 }
22886
22887 #[test]
22888 fn trailing_qualifiers_survive_parameter_list_whitespace() {
22889 let source = r#"
22894struct Widget {
22895 bool multiline(int settings, int supprs) const;
22896 bool doublespace(int settings, int supprs) const;
22897 bool noexcept_multiline(int settings, int supprs) noexcept;
22898 bool ref_multiline(int settings, int supprs) &&;
22899};
22900bool
22901Widget::multiline (int settings,
22902 int supprs) const
22903{ return settings + supprs > 0; }
22904bool Widget::doublespace(int settings, int supprs) const { return true; }
22905bool Widget::noexcept_multiline(int settings,
22906 int supprs) noexcept { return true; }
22907bool Widget::ref_multiline(int settings,
22908 int supprs) && { return true; }
22909"#;
22910 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
22911 assert_eq!(
22912 vec!["(int, int) const".to_string()],
22913 identity_signatures(&parsed, "Widget.multiline")
22914 );
22915 assert_eq!(
22916 vec!["(int, int) const".to_string()],
22917 identity_signatures(&parsed, "Widget.doublespace")
22918 );
22919 assert_eq!(
22920 vec!["(int, int) noexcept".to_string()],
22921 identity_signatures(&parsed, "Widget.noexcept_multiline")
22922 );
22923 assert_eq!(
22924 vec!["(int, int) &&".to_string()],
22925 identity_signatures(&parsed, "Widget.ref_multiline")
22926 );
22927 }
22928
22929 #[test]
22930 fn macro_fragmented_plain_class_keeps_following_member_signature() {
22931 let source = r#"
22932struct CString {};
22933class CMessage {
22934public:
22935 CString GetParams(unsigned int index, unsigned int length = -1) const
22936 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
22937 return GetParamsColon(index, length);
22938 }
22939 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
22940};
22941CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
22942 return {};
22943}
22944"#;
22945 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
22946 assert_eq!(
22947 vec!["(unsigned int, unsigned int) const".to_string()],
22948 identity_signatures(&parsed, "CMessage.GetParamsColon")
22949 );
22950 }
22951
22952 #[test]
22953 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
22954 let source = r#"
22955#pragma once
22956#define DEMO_DEPRECATED(message)
22957namespace demo {
22958struct Base {
22959 static int aligned(int value) { return value; }
22960 int legacy(int value) const
22961 DEMO_DEPRECATED("use replacement()") { return value; }
22962 int replacement() const;
22963 void run(int value);
22964};
22965struct OtherBase {
22966 void run(int value);
22967 static int aligned(int value) { return value; }
22968};
22969struct Derived : Base {};
22970struct Override : Base {
22971 void run(int value);
22972 static int aligned(int value) { return value; }
22973};
22974struct RecoveredOverride : Base {
22975 int legacy(int value) const
22976 DEMO_DEPRECATED("use replacement()") { return value; }
22977 void run(int value);
22978};
22979struct Hidden : Base {
22980 void run(int first, int second);
22981 static int aligned(int first, int second) { return first + second; }
22982};
22983struct Ambiguous : Base, OtherBase {};
22984}
22985struct Global {};
22986"#;
22987 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
22988 let declarations = parsed.declarations();
22989 let fq_names = declarations
22990 .iter()
22991 .map(|unit| unit.fq_name())
22992 .collect::<std::collections::BTreeSet<_>>();
22993
22994 for expected in [
22995 "demo.Base",
22996 "demo.Base.aligned",
22997 "demo.Base.legacy",
22998 "demo.Base.replacement",
22999 "demo.Base.run",
23000 "demo.Derived",
23001 "demo.OtherBase",
23002 "demo.Override",
23003 "demo.RecoveredOverride",
23004 "demo.Hidden",
23005 "demo.Ambiguous",
23006 "Global",
23007 ] {
23008 assert!(
23009 fq_names.contains(expected),
23010 "missing {expected} from namespaced macro fragment: {declarations:#?}"
23011 );
23012 }
23013 assert!(
23014 !fq_names.contains("Derived"),
23015 "following class escaped its namespace: {declarations:#?}"
23016 );
23017 assert!(
23018 !fq_names.contains("demo.Global"),
23019 "global class crossed the recovered namespace boundary: {declarations:#?}"
23020 );
23021 }
23022
23023 #[test]
23024 fn trailing_qualifiers_still_separate_genuine_overloads() {
23025 let source = r#"
23028struct Widget {
23029 int* slot(int index);
23030 const int* slot(int index) const;
23031 int log(int severity) &;
23032 int log(int severity) &&;
23033};
23034"#;
23035 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
23036 assert_eq!(
23037 vec!["(int)".to_string(), "(int) const".to_string()],
23038 identity_signatures(&parsed, "Widget.slot")
23039 );
23040 assert_eq!(
23041 vec!["(int) &".to_string(), "(int) &&".to_string()],
23042 identity_signatures(&parsed, "Widget.log")
23043 );
23044 }
23045
23046 #[test]
23047 fn virtual_specifier_is_not_part_of_the_identity_signature() {
23048 let source = r#"
23051struct Base {
23052 virtual void run(int value) const;
23053};
23054struct Widget : Base {
23055 void run(int value) const override;
23056};
23057void Widget::run(int value) const {}
23058"#;
23059 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
23060 assert_eq!(
23061 vec!["(int) const".to_string()],
23062 identity_signatures(&parsed, "Widget.run")
23063 );
23064 }
23065
23066 #[test]
23067 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
23068 let source = r#"
23072struct Widget {
23073 bool value_params(const int settings, const int supprs);
23074 void pointee_const(const int* p);
23075 void pointer_const(int* const p);
23076 void both_const(const int* const p);
23077 void reference_const(const int& p);
23078 void array_const(const int values[4]);
23079};
23080bool Widget::value_params(int settings, int supprs) { return true; }
23081void Widget::pointer_const(int* p) {}
23082void Widget::both_const(const int* p) {}
23083"#;
23084 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
23085 assert_eq!(
23086 vec!["(int, int)".to_string()],
23087 identity_signatures(&parsed, "Widget.value_params")
23088 );
23089 assert_eq!(
23090 vec!["(int *)".to_string()],
23091 identity_signatures(&parsed, "Widget.pointer_const")
23092 );
23093 assert_eq!(
23094 vec!["(const int *)".to_string()],
23095 identity_signatures(&parsed, "Widget.both_const")
23096 );
23097 assert_eq!(
23099 vec!["(const int *)".to_string()],
23100 identity_signatures(&parsed, "Widget.pointee_const")
23101 );
23102 assert_eq!(
23103 vec!["(const int &)".to_string()],
23104 identity_signatures(&parsed, "Widget.reference_const")
23105 );
23106 assert_eq!(
23107 vec!["(const int [4])".to_string()],
23108 identity_signatures(&parsed, "Widget.array_const")
23109 );
23110 }
23111
23112 #[test]
23113 fn top_level_parameter_const_still_separates_pointee_overloads() {
23114 let source = r#"
23115struct Widget {
23116 void take(const int* p);
23117 void take(int* p);
23118};
23119"#;
23120 let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
23121 assert_eq!(
23122 vec!["(const int *)".to_string(), "(int *)".to_string()],
23123 identity_signatures(&parsed, "Widget.take")
23124 );
23125 }
23126
23127 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
23128 let mut parser = tree_sitter::Parser::new();
23129 parser
23130 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23131 .unwrap();
23132 let tree = parser.parse(source, None).unwrap();
23133 let start = source.find(callable_name).expect("callable declaration");
23134 let declarator =
23135 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
23136 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
23137 }
23138
23139 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
23140 let mut shapes = comparable_shapes(source, callable_name);
23141 assert_eq!(1, shapes.len(), "{shapes:?}");
23142 match shapes.remove(0) {
23143 CppComparableSlot::Shape(shape) => shape,
23144 other => panic!("expected a comparable shape, got {other:?}"),
23145 }
23146 }
23147
23148 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
23149 let mut current = shape.root();
23150 loop {
23151 match shape.node(current) {
23152 CppComparableNode::Named { .. } => return shape.node(current),
23153 CppComparableNode::Pointer { inner, .. }
23154 | CppComparableNode::Reference { inner }
23155 | CppComparableNode::Array { inner } => current = *inner,
23156 CppComparableNode::Generic { base, .. } => current = *base,
23157 }
23158 }
23159 }
23160
23161 #[test]
23162 fn comparable_shape_keeps_pointee_const() {
23163 assert_ne!(
23164 sole_comparable_shape("void f(const char* p);", "f("),
23165 sole_comparable_shape("void f(char* p);", "f(")
23166 );
23167 }
23168
23169 #[test]
23170 fn comparable_shape_keeps_inner_pointer_const() {
23171 assert_ne!(
23172 sole_comparable_shape("void f(int** p);", "f("),
23173 sole_comparable_shape("void f(int* const* p);", "f(")
23174 );
23175 }
23176
23177 #[test]
23178 fn comparable_shape_drops_top_level_pointer_const() {
23179 assert_eq!(
23180 sole_comparable_shape("void f(int* const p);", "f("),
23181 sole_comparable_shape("void f(int* p);", "f(")
23182 );
23183 }
23184
23185 #[test]
23186 fn comparable_shape_drops_top_level_base_const() {
23187 assert_eq!(
23188 sole_comparable_shape("void f(const int p);", "f("),
23189 sole_comparable_shape("void f(int p);", "f(")
23190 );
23191 }
23192
23193 #[test]
23194 fn comparable_shape_decays_top_level_array_to_pointer() {
23195 assert_eq!(
23196 sole_comparable_shape("void f(int a[3]);", "f("),
23197 sole_comparable_shape("void f(int* a);", "f(")
23198 );
23199 assert_eq!(
23200 sole_comparable_shape("void f(int* a[3]);", "f("),
23201 sole_comparable_shape("void f(int** a);", "f(")
23202 );
23203 }
23204
23205 #[test]
23206 fn comparable_shape_keeps_array_behind_pointer() {
23207 assert_ne!(
23208 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
23209 sole_comparable_shape("struct S { void f(int** a); };", "f(")
23210 );
23211 }
23212
23213 #[test]
23214 fn comparable_shape_records_written_name_and_lexical_scope() {
23215 let declared =
23216 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
23217 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
23218 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
23219 panic!("named leaf");
23220 };
23221 assert_eq!(["Msg".to_string()].as_slice(), name.path());
23222 assert_eq!(
23223 ["ns".to_string(), "S".to_string()].as_slice(),
23224 name.lexical_scope()
23225 );
23226 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
23227 panic!("named leaf");
23228 };
23229 assert_eq!(
23230 ["ns".to_string(), "Msg".to_string()].as_slice(),
23231 name.path()
23232 );
23233 assert!(name.lexical_scope().is_empty());
23234 assert_ne!(declared, defined);
23235 }
23236
23237 #[test]
23238 fn comparable_shape_marks_sized_primitive_leaf() {
23239 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
23240 let CppComparableNode::Named {
23241 name, primitive, ..
23242 } = comparable_named_leaf(&shape)
23243 else {
23244 panic!("named leaf");
23245 };
23246 assert!(primitive);
23247 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
23248 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
23249 }
23250
23251 #[test]
23252 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
23253 assert_eq!(
23254 vec![CppComparableSlot::Unstructured],
23255 comparable_shapes("void f(void (*cb)(int));", "f(")
23256 );
23257 }
23258
23259 #[test]
23260 fn comparable_shape_reports_ellipsis_slot() {
23261 let shapes = comparable_shapes("void f(int a, ...);", "f(");
23262 assert_eq!(2, shapes.len(), "{shapes:?}");
23263 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
23264 }
23265
23266 #[test]
23267 fn comparable_shape_keeps_template_argument_const() {
23268 assert_ne!(
23269 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
23270 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
23271 );
23272 }
23273
23274 #[test]
23277 fn c_file_mints_aggregate_member_tag_at_file_scope() {
23278 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
23279 let parsed = parse_cpp_declarations(source, "x.c");
23280 let declarations = parsed.declarations();
23281
23282 assert!(
23283 declarations
23284 .iter()
23285 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
23286 "expected a file-scope inner tag, got {declarations:?}"
23287 );
23288 assert!(
23289 declarations
23290 .iter()
23291 .all(|unit| unit.fq_name() != "outer$inner"),
23292 "expected no nested identity, got {declarations:?}"
23293 );
23294 assert!(
23295 declarations
23296 .iter()
23297 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
23298 );
23299 assert!(
23301 declarations
23302 .iter()
23303 .any(|unit| unit.fq_name() == "inner.value")
23304 );
23305 assert!(
23306 declarations
23307 .iter()
23308 .any(|unit| unit.fq_name() == "outer.item")
23309 );
23310
23311 let outer = declarations
23312 .iter()
23313 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
23314 .expect("outer");
23315 assert!(
23316 parsed
23317 .children
23318 .get(outer)
23319 .into_iter()
23320 .flatten()
23321 .all(|child| child.fq_name() != "inner"),
23322 "the tag must not hang off the aggregate it is written inside: {:?}",
23323 parsed.children
23324 );
23325 }
23326
23327 #[test]
23331 fn header_and_cpp_files_keep_nested_tag_identity() {
23332 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
23333 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
23334 let parsed = parse_cpp_declarations(source, name);
23335 let declarations = parsed.declarations();
23336 assert!(
23337 declarations
23338 .iter()
23339 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
23340 "{name} must keep the nested identity, got {declarations:?}"
23341 );
23342 assert!(
23343 declarations.iter().all(|unit| unit.fq_name() != "inner"),
23344 "{name} must not mint a file-scope tag, got {declarations:?}"
23345 );
23346 assert!(
23347 declarations
23348 .iter()
23349 .any(|unit| unit.fq_name() == "outer$inner.value")
23350 );
23351 }
23352 }
23353
23354 #[test]
23356 fn uppercase_c_extension_keeps_cpp_tag_scope() {
23357 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
23358 let parsed = parse_cpp_declarations(source, "x.C");
23359 assert!(
23360 parsed
23361 .declarations()
23362 .iter()
23363 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
23364 );
23365 }
23366
23367 #[test]
23370 fn c_file_mints_every_nesting_level_at_file_scope() {
23371 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
23372 let parsed = parse_cpp_declarations(source, "z.c");
23373 let declarations = parsed.declarations();
23374
23375 for tag in ["a", "b", "c"] {
23376 assert!(
23377 declarations
23378 .iter()
23379 .any(|unit| unit.is_class() && unit.fq_name() == tag),
23380 "expected a file-scope {tag}, got {declarations:?}"
23381 );
23382 }
23383 assert!(
23384 declarations
23385 .iter()
23386 .all(|unit| !unit.fq_name().contains('$')),
23387 "no level may keep a nested identity, got {declarations:?}"
23388 );
23389 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
23391 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
23392 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
23393 }
23394
23395 #[test]
23398 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
23399 let source = "struct outer { enum color { RED, GREEN } c; };\n";
23400 let parsed = parse_cpp_declarations(source, "e.c");
23401 let declarations = parsed.declarations();
23402
23403 let color = declarations
23404 .iter()
23405 .find(|unit| unit.is_class() && unit.fq_name() == "color")
23406 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
23407 assert!(
23408 declarations
23409 .iter()
23410 .all(|unit| unit.fq_name() != "outer$color")
23411 );
23412 for enumerator in ["color.RED", "color.GREEN"] {
23413 assert!(
23414 declarations.iter().any(|unit| unit.fq_name() == enumerator),
23415 "expected {enumerator}, got {declarations:?}"
23416 );
23417 }
23418 let children = parsed
23419 .children
23420 .get(color)
23421 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
23422 assert!(
23423 ["color.RED", "color.GREEN"]
23424 .iter()
23425 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
23426 "enumerators must hang off their enum: {children:?}"
23427 );
23428 }
23429
23430 #[test]
23431 fn c_file_mints_member_list_union_at_file_scope() {
23432 let source = "struct outer { union inner { int a; float b; } item; };\n";
23433 let parsed = parse_cpp_declarations(source, "u.c");
23434 let declarations = parsed.declarations();
23435 assert!(
23436 declarations
23437 .iter()
23438 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
23439 "expected a file-scope inner union, got {declarations:?}"
23440 );
23441 assert!(
23442 declarations
23443 .iter()
23444 .all(|unit| unit.fq_name() != "outer$inner")
23445 );
23446 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
23447 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
23448 }
23449
23450 #[test]
23453 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
23454 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
23455 let parsed = parse_cpp_declarations(source, "n.c");
23456 let declarations = parsed.declarations();
23457 let inner = declarations
23458 .iter()
23459 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
23460 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
23461 assert_eq!(inner.package_name(), "ns");
23462 assert!(
23463 declarations
23464 .iter()
23465 .all(|unit| unit.fq_name() != "ns.outer$inner")
23466 );
23467 }
23468
23469 #[test]
23474 fn function_local_tags_are_unchanged_in_both_dialects() {
23475 let source =
23476 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
23477 for name in ["y.c", "y.cpp"] {
23478 let parsed = parse_cpp_declarations(source, name);
23479 let declarations = parsed.declarations();
23480 assert!(
23481 declarations
23482 .iter()
23483 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
23484 "{name}: {declarations:?}"
23485 );
23486 for tag in ["localtag", "deeper", "localtag$deeper"] {
23487 assert!(
23488 declarations.iter().all(|unit| unit.fq_name() != tag),
23489 "{name} must not mint {tag}, got {declarations:?}"
23490 );
23491 }
23492 }
23493 }
23494
23495 #[test]
23498 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
23499 let source = "typedef struct { int v; } T;\n";
23500 for name in ["t.c", "t.cpp"] {
23501 let parsed = parse_cpp_declarations(source, name);
23502 let declarations = parsed.declarations();
23503 assert!(
23504 declarations
23505 .iter()
23506 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
23507 "{name}: {declarations:?}"
23508 );
23509 }
23510 }
23511
23512 #[test]
23519 fn anonymous_aggregate_members_are_identical_in_both_dialects() {
23520 let sources = [
23521 "struct outer { union { int a; float b; }; };\n",
23522 "struct outer { struct { int *ops; } sock; };\n",
23523 "void run(void) { struct { int v; } item; }\n",
23524 "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
23525 ];
23526 for source in sources {
23527 let names = |name: &str| {
23528 parse_cpp_declarations(source, name)
23529 .declarations()
23530 .iter()
23531 .map(|unit| unit.fq_name())
23532 .collect::<std::collections::BTreeSet<_>>()
23533 };
23534 let c_names = names("a.c");
23535 let cpp_names = names("a.cpp");
23536 assert_eq!(
23537 c_names, cpp_names,
23538 "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
23539 );
23540 assert!(
23541 c_names
23542 .iter()
23543 .any(|name| name.contains('.') && name != "outer"),
23544 "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
23545 );
23546 }
23547 }
23548
23549 #[test]
23550 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
23551 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
23552 let parsed = parse_cpp_declarations(source, "socket.c");
23553 let declarations = parsed.declarations();
23554 assert_eq!(
23555 declarations
23556 .iter()
23557 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
23558 .count(),
23559 1,
23560 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
23561 );
23562 for expected in [
23563 "PAL_HANDLE",
23564 "PAL_HANDLE.sock",
23565 "PAL_HANDLE$sock",
23566 "PAL_HANDLE$sock.ops",
23567 ] {
23568 assert!(
23569 declarations.iter().any(|unit| unit.fq_name() == expected),
23570 "expected {expected}, got {declarations:?}"
23571 );
23572 }
23573 }
23574
23575 #[test]
23578 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
23579 let source = "class outer { class inner { int v; }; };\n";
23580 let c_parsed = parse_cpp_declarations(source, "k.c");
23581 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
23582 let c_declarations = c_parsed.declarations();
23583 let cpp_declarations = cpp_parsed.declarations();
23584 assert!(
23585 c_declarations
23586 .iter()
23587 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
23588 "{c_declarations:?}"
23589 );
23590 assert_eq!(
23591 c_declarations
23592 .iter()
23593 .map(|unit| unit.fq_name())
23594 .collect::<std::collections::BTreeSet<_>>(),
23595 cpp_declarations
23596 .iter()
23597 .map(|unit| unit.fq_name())
23598 .collect::<std::collections::BTreeSet<_>>()
23599 );
23600 }
23601
23602 fn namespace_forward_scan_agreement(source: &str) -> usize {
23616 let mut parser = tree_sitter::Parser::new();
23617 parser
23618 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23619 .unwrap();
23620 let tree = parser.parse(source, None).unwrap();
23621 let root = tree.root_node();
23622 let ancestry = ParentIndex::new(root);
23623
23624 let mut nodes = Vec::new();
23625 let mut names = std::collections::BTreeSet::new();
23626 let mut cursor = root.walk();
23627 let mut stack = vec![root];
23628 while let Some(node) = stack.pop() {
23629 if matches!(
23630 node.kind(),
23631 "class_specifier" | "struct_specifier" | "union_specifier"
23632 ) && let Some(name) = class_like_name(node, source, &ancestry)
23633 {
23634 names.insert(name);
23635 }
23636 nodes.push(node);
23637 stack.extend(node.named_children(&mut cursor));
23638 }
23639 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
23640 assert!(!names.is_empty(), "fixture declares no class-like name");
23641
23642 let mut answered = 0usize;
23643 for reversed in [false, true] {
23644 let mut scan = CppNamespaceForwardScan::default();
23645 let ordered: Vec<_> = if reversed {
23646 nodes.iter().rev().copied().collect()
23647 } else {
23648 nodes.clone()
23649 };
23650 answered = 0;
23651 for node in ordered {
23652 for name in &names {
23653 scan.advance_to(root, node.start_byte(), source, &ancestry);
23654 let carried = scan.unique_earlier_forward(name, node);
23655 assert_eq!(
23656 carried,
23657 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
23658 "carried-forward scan and prefix scan disagree about {name} at \
23659 {} node starting at byte {} (reversed order: {reversed})",
23660 node.kind(),
23661 node.start_byte()
23662 );
23663 answered += usize::from(carried.is_some());
23664 }
23665 }
23666 }
23667 answered
23668 }
23669
23670 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
23677namespace ns {
23678class Widget;
23679class Gadget;
23680int x = ;
23681}
23682class API Widget {
23683public:
23684 void first();
23685};
23686class API Gadget {
23687public:
23688 void second();
23689};
23690"#;
23691
23692 #[test]
23693 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
23694 assert!(
23695 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
23696 "the fixture must actually reach the namespace-borrow path"
23697 );
23698
23699 for source in [
23704 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
23705 r#"#define API
23706namespace ns {
23707class Widget;
23708class Widget;
23709int x = ;
23710}
23711class API Widget {
23712public:
23713 void method();
23714};
23715"#,
23716 r#"#define API
23717namespace ns {
23718void host() {
23719 class Widget;
23720}
23721int x = ;
23722}
23723class API Widget {
23724public:
23725 void method();
23726};
23727"#,
23728 ] {
23729 assert_eq!(
23730 namespace_forward_scan_agreement(source),
23731 0,
23732 "no borrow is justified here: {source}"
23733 );
23734 }
23735 }
23736
23737 #[test]
23741 fn carried_forward_namespace_scan_folds_each_node_once() {
23742 let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
23743 let mut parser = tree_sitter::Parser::new();
23744 parser
23745 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23746 .unwrap();
23747 let tree = parser.parse(source, None).unwrap();
23748 let root = tree.root_node();
23749 let ancestry = ParentIndex::new(root);
23750
23751 let mut incremental = CppNamespaceForwardScan::default();
23752 for cutoff in 0..=source.len() {
23753 incremental.advance_to(root, cutoff, source, &ancestry);
23754 }
23755 let mut whole = CppNamespaceForwardScan::default();
23756 whole.advance_to(root, source.len(), source, &ancestry);
23757
23758 let mut incremental_shape: Vec<_> = incremental
23759 .forwards
23760 .iter()
23761 .map(|(name, forwards)| {
23762 (
23763 name.clone(),
23764 forwards
23765 .iter()
23766 .map(|forward| (forward.start_byte, forward.package_name.clone()))
23767 .collect::<Vec<_>>(),
23768 )
23769 })
23770 .collect();
23771 let mut whole_shape: Vec<_> = whole
23772 .forwards
23773 .iter()
23774 .map(|(name, forwards)| {
23775 (
23776 name.clone(),
23777 forwards
23778 .iter()
23779 .map(|forward| (forward.start_byte, forward.package_name.clone()))
23780 .collect::<Vec<_>>(),
23781 )
23782 })
23783 .collect();
23784 incremental_shape.sort();
23785 whole_shape.sort();
23786 for (_, forwards) in &mut incremental_shape {
23787 forwards.sort();
23788 }
23789 for (_, forwards) in &mut whole_shape {
23790 forwards.sort();
23791 }
23792
23793 assert!(!whole_shape.is_empty(), "fixture folds no forward");
23794 assert_eq!(
23795 incremental_shape, whole_shape,
23796 "one byte at a time must fold exactly what one whole pass folds"
23797 );
23798 }
23799
23800 fn fragmented_class_reparse_agreement(body: &str) {
23810 for prefix in [
23811 String::new(),
23812 "// leading comment\n".to_string(),
23813 "class Widget : public Base { ".to_string(),
23818 "namespace filler {\n".to_string()
23819 + &"struct Filler { int member; };\n".repeat(200)
23820 + "}\n",
23821 "namespace filler {\n".to_string()
23822 + &"struct Filler { int member; };\n".repeat(200)
23823 + "}\nclass Widget : public Base { ",
23824 ] {
23825 let source = format!("{prefix}{body}");
23826 let start = prefix.len();
23827 let end = source.len();
23828 let region = cpp_reparse_fragmented_class_body(&source, start, end)
23829 .expect("the region reparse must produce a tree");
23830 let padded = cpp_reparse_padded_class_body(&source, start, end)
23831 .expect("the padded reparse must produce a tree");
23832 assert_eq!(
23833 cpp_tree_shape(®ion),
23834 cpp_tree_shape(&padded),
23835 "region and padded reparse disagree at offset {start} of {end} bytes"
23836 );
23837 assert_eq!(
23838 region.root_node().start_byte(),
23839 start,
23840 "the reparsed region keeps its original offsets"
23841 );
23842 }
23843 }
23844
23845 #[test]
23846 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
23847 fragmented_class_reparse_agreement(
23851 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
23852 );
23853 fragmented_class_reparse_agreement(
23854 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
23855 );
23856 fragmented_class_reparse_agreement(
23859 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
23860 );
23861 fragmented_class_reparse_agreement(
23862 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
23863 );
23864 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
23867 }
23868
23869 fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
23870 let mut siblings = Vec::new();
23871 let mut anchor = node;
23872 while let Some(parent) = anchor.parent() {
23873 let at_translation_unit = parent.kind() == "translation_unit";
23874 let mut sibling = anchor.next_named_sibling();
23875 while let Some(current) = sibling {
23876 if at_translation_unit
23877 && (current.kind() == "namespace_definition"
23878 || (current.kind() == "function_definition"
23879 && first_class_like_child(current).is_some()))
23880 {
23881 return siblings;
23882 }
23883 siblings.push(current);
23884 if cpp_is_stray_close_brace(current, source) {
23885 if let Some(semicolon) = current
23886 .next_named_sibling()
23887 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
23888 {
23889 siblings.push(semicolon);
23890 }
23891 return siblings;
23892 }
23893 if current.start_byte() >= node.end_byte()
23894 && matches!(current.kind(), "ERROR" | "labeled_statement")
23895 && cpp_nested_stray_close_brace(current, source).is_some()
23896 {
23897 return siblings;
23898 }
23899 sibling = current.next_named_sibling();
23900 }
23901 anchor = parent;
23902 }
23903 siblings
23904 }
23905
23906 #[test]
23907 fn following_named_siblings_cursor_matches_native_traversal() {
23908 let source = r#"
23912namespace api {
23913class PROJECT_API(2, 0) Widget : public virtual Base {
23914 public:
23915 Widget() {}
23916 void first();
23917};
23918enum After { Value };
23919}
23920"#;
23921 let mut parser = tree_sitter::Parser::new();
23922 parser
23923 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23924 .unwrap();
23925 let tree = parser.parse(source, None).unwrap();
23926 let root = tree.root_node();
23927 assert!(root.has_error(), "fixture must remain damaged");
23928
23929 let ancestry = ParentIndex::new(root);
23930 let mut stack = vec![root];
23931 let mut checked = 0;
23932 let mut damaged = 0;
23933 let mut anonymous = 0;
23934 let mut nonempty_sequences = 0;
23935 let mut named_then_anonymous = 0;
23936 while let Some(node) = stack.pop() {
23937 let expected = native_following_named_siblings(node, source)
23938 .iter()
23939 .map(Node::id)
23940 .collect::<Vec<_>>();
23941 let actual = cpp_following_named_siblings(node, source, &ancestry)
23942 .iter()
23943 .map(Node::id)
23944 .collect::<Vec<_>>();
23945 assert_eq!(
23946 actual,
23947 expected,
23948 "cursor traversal diverged at byte {}",
23949 node.start_byte()
23950 );
23951
23952 let mut cursor = node.walk();
23953 let children = node.children(&mut cursor).collect::<Vec<_>>();
23954 stack.extend(children.iter().copied());
23955 checked += 1;
23956 damaged += usize::from(node.is_error() || node.is_missing());
23957 anonymous += usize::from(!node.is_named());
23958 nonempty_sequences += usize::from(!actual.is_empty());
23959 named_then_anonymous += usize::from(
23960 children
23961 .windows(2)
23962 .any(|pair| pair[0].is_named() && !pair[1].is_named()),
23963 );
23964 }
23965 assert!(checked > 0, "fixture must provide nodes to compare");
23966 assert!(damaged > 0, "fixture must cover damaged nodes");
23967 assert!(anonymous > 0, "fixture must cover anonymous nodes");
23968 assert!(
23969 nonempty_sequences > 0,
23970 "fixture must exercise following named siblings"
23971 );
23972 assert!(
23973 named_then_anonymous > 0,
23974 "fixture must exercise anonymous nodes following a named anchor"
23975 );
23976 }
23977
23978 fn many_enums_and_mixed_declarations() -> String {
23982 let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
23983 for index in 0..40 {
23984 let _ = write!(
23985 source,
23986 "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
23987 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
23988 class Color{index}Like {{ public: int member{index}; }};\n"
23989 );
23990 }
23991 source.push_str("namespace outer {\n");
23992 for index in 0..20 {
23993 let _ = write!(
23994 source,
23995 "enum Shade{index} {{ DARK{index} }};\n\
23996 struct Shade{index}Holder {{ int field{index}; }};\n"
23997 );
23998 }
23999 source.push_str("}\n");
24000 source
24001 }
24002
24003 fn field_owner_index_agreement(source: &str, name: &str) -> usize {
24019 let parsed = parse_cpp_declarations(source, name);
24020 let file = ProjectFile::new(std::env::temp_dir(), name);
24021 let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
24022
24023 let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
24024 declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
24025
24026 let foreign: Vec<CodeUnit> = declarations
24027 .iter()
24028 .filter(|unit| unit.kind() == CodeUnitType::Field)
24029 .map(|unit| {
24030 CodeUnit::new_fq(
24031 elsewhere.clone(),
24032 unit.kind(),
24033 unit.package_name().to_string(),
24034 unit.short_name().to_string(),
24035 unit.fq().clone(),
24036 )
24037 })
24038 .collect();
24039
24040 let mut packages: Vec<String> = declarations
24046 .iter()
24047 .map(|unit| unit.package_name().to_string())
24048 .collect();
24049 packages.push(String::new());
24050 packages.sort();
24051 packages.dedup();
24052 let deeper: Vec<CodeUnit> = packages
24053 .iter()
24054 .map(|package_name| {
24055 CodeUnit::new_fq(
24056 file.clone(),
24057 CodeUnitType::Field,
24058 package_name.clone(),
24059 "SynthOwner.middle.leaf".to_string(),
24060 cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
24061 )
24062 })
24063 .collect();
24064
24065 let mut questions: Vec<(String, String)> = Vec::new();
24069 for unit in declarations.iter().chain(deeper.iter()) {
24070 let package_name = unit.package_name().to_string();
24071 let short_name = unit.short_name();
24072 questions.push((package_name.clone(), short_name.to_string()));
24073 questions.push((package_name.clone(), String::new()));
24074 for (offset, _) in short_name.match_indices('.') {
24075 questions.push((package_name.clone(), short_name[..offset].to_string()));
24076 }
24077 }
24078 questions.sort();
24079 questions.dedup();
24080
24081 let mut index = CppFieldOwnerIndex::default();
24082 let mut recorded: Vec<&CodeUnit> = Vec::new();
24083 let mut answered = 0usize;
24084 for unit in foreign
24085 .iter()
24086 .chain(declarations.iter())
24087 .chain(deeper.iter())
24088 {
24089 index.record(unit, &file);
24090 recorded.push(unit);
24091 for (package_name, owner_short_name) in &questions {
24092 let carried = index.owns_fields(package_name, owner_short_name);
24093 assert_eq!(
24094 carried,
24095 cpp_declarations_hold_owned_fields(
24096 recorded.iter().copied(),
24097 &file,
24098 package_name,
24099 owner_short_name
24100 ),
24101 "the carried field index and the declaration scan disagree about \
24102 {package_name:?}/{owner_short_name:?} after recording {}",
24103 unit.fq_name()
24104 );
24105 answered += usize::from(carried);
24106 }
24107 }
24108
24109 let rebuilt = CppFieldOwnerIndex::of(
24112 foreign
24113 .iter()
24114 .chain(declarations.iter())
24115 .chain(deeper.iter()),
24116 &file,
24117 );
24118 for (package_name, owner_short_name) in &questions {
24119 assert_eq!(
24120 rebuilt.owns_fields(package_name, owner_short_name),
24121 index.owns_fields(package_name, owner_short_name),
24122 "a rebuilt index must answer what the incremental one answers for \
24123 {package_name:?}/{owner_short_name:?}"
24124 );
24125 }
24126 answered
24127 }
24128
24129 #[test]
24138 fn a_replacement_that_removes_children_drops_the_field_index() {
24139 let source =
24140 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
24141 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
24142 let mut names: Vec<_> = parsed
24143 .declarations()
24144 .iter()
24145 .map(|unit| unit.fq_name())
24146 .collect();
24147 names.sort();
24148 assert_eq!(
24149 names,
24150 vec![
24151 "Color".to_string(),
24152 "First".to_string(),
24153 "First.A".to_string()
24154 ],
24155 "the replaced Color owns no field any more"
24156 );
24157 }
24158
24159 #[test]
24168 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
24169 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
24170 BEGIN_NS\n\
24171 namespace demo { struct Widget { void doWork(); }; }\n\
24172 END_NS\n";
24173 let parsed = parse_cpp_declarations(source, "restored.cpp");
24174 let recovered: Vec<String> = parsed
24175 .materialization_records
24176 .iter()
24177 .filter_map(|record| match record {
24178 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
24179 _ => None,
24180 })
24181 .collect();
24182 assert!(
24183 recovered.is_empty(),
24184 "the region declares nothing the file did not already declare: {recovered:?}"
24185 );
24186 let mut names: Vec<String> = parsed
24187 .declarations()
24188 .iter()
24189 .map(|unit| unit.fq_name())
24190 .collect();
24191 names.sort();
24192 assert_eq!(
24193 names,
24194 vec![
24195 "demo".to_string(),
24196 "demo.Widget".to_string(),
24197 "demo.Widget.doWork".to_string(),
24198 ]
24199 );
24200 }
24201
24202 #[test]
24207 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
24208 let mut source = String::new();
24209 for index in 0..4 {
24210 let _ = write!(
24211 source,
24212 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
24213 );
24214 }
24215 source.push_str("void outside() {}\n");
24216 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
24217
24218 let recovered: Vec<(String, (usize, usize))> = parsed
24219 .materialization_records
24220 .iter()
24221 .filter_map(|record| match record {
24222 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
24223 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
24224 }
24225 _ => None,
24226 })
24227 .collect();
24228
24229 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
24230 for index in 0..4 {
24231 let region = format!("namespace demo{index}");
24234 let region_start = source.find(®ion).expect("each region is in the source");
24235 let start = source[..region_start]
24236 .rfind("BEGIN_NS")
24237 .expect("each region opens with a sentinel")
24238 + "BEGIN_NS".len();
24239 let end = start
24240 + source[start..]
24241 .find("END_NS")
24242 .expect("each region closes with a sentinel")
24243 - 1;
24244 let window = (start, end);
24245 for name in [
24246 format!("demo{index}"),
24247 format!("demo{index}.Widget{index}"),
24248 format!("demo{index}.Widget{index}.doWork{index}"),
24249 ] {
24250 expected.push((name, window));
24251 }
24252 }
24253 assert_eq!(
24254 recovered, expected,
24255 "each recovery records its own minted declarations, in order"
24256 );
24257 assert!(
24258 parsed
24259 .declarations()
24260 .iter()
24261 .any(|unit| unit.fq_name() == "outside"),
24262 "the declaration outside every region stays parsed and unrecovered"
24263 );
24264 }
24265
24266 #[test]
24267 fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
24268 assert!(
24269 field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
24270 "the fixture must actually own fields"
24271 );
24272
24273 for (source, name) in [
24279 ("struct S { enum E { V }; };\n", "nested.hpp"),
24280 (
24281 "enum Color { RED };\nstruct Color { int RED; };\n",
24282 "class-like.c",
24283 ),
24284 ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
24285 (
24286 "enum E { V };\nnamespace ns { enum E { V }; }\n",
24287 "repeated.hpp",
24288 ),
24289 ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
24290 ] {
24291 field_owner_index_agreement(source, name);
24292 }
24293 }
24294
24295 fn repeated_class_blocks(blocks: usize) -> String {
24300 let mut source = String::from("namespace demo {\n");
24301 for index in 0..blocks {
24302 let _ = write!(
24303 source,
24304 "\nclass Widget{index} {{\npublic:\n int translate() const {{ return {index}; }}\n int helper(const Widget{index}& other) const {{ return other.translate(); }}\nprivate:\n int field = {index};\n}};\n"
24305 );
24306 }
24307 source.push_str("\n}\n");
24308 source
24309 }
24310
24311 #[test]
24319 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
24320 let mut answers = Vec::new();
24321 let mut visits = Vec::new();
24322 let mut node_counts = Vec::new();
24323 for blocks in [200usize, 400] {
24324 let source = repeated_class_blocks(blocks);
24325 let mut parser = tree_sitter::Parser::new();
24326 parser
24327 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24328 .unwrap();
24329 let tree = parser.parse(&source, None).unwrap();
24330 let start_byte = source.find("class Widget0 ").expect("first class");
24331 let end_byte = start_byte
24332 + source[start_byte..]
24333 .find("};")
24334 .expect("first class terminator")
24335 + "};".len();
24336 let range = Range {
24337 start_byte,
24338 end_byte,
24339 start_line: 0,
24340 end_line: 0,
24341 };
24342 reset_recovered_class_body_node_visits_for_test();
24343 let recovered_export_classes =
24344 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
24345 answers.push(recovered_class_body_at(
24346 &recovered_export_classes,
24347 tree.root_node(),
24348 &source,
24349 "Widget0",
24350 &range,
24351 ));
24352 visits.push(recovered_class_body_node_visits_for_test());
24353 let mut nodes = 0usize;
24354 let mut stack = vec![tree.root_node()];
24355 while let Some(node) = stack.pop() {
24356 nodes += 1;
24357 let mut cursor = node.walk();
24358 stack.extend(node.named_children(&mut cursor));
24359 }
24360 node_counts.push(nodes);
24361 }
24362
24363 assert_eq!(
24364 answers,
24365 vec![None, None],
24366 "no recovered shape claims a plain class"
24367 );
24368 assert_eq!(
24369 visits[0], visits[1],
24370 "the walk must follow the range's own path, so doubling the unrelated \
24371 classes must not change the node count: {visits:?} over trees of \
24372 {node_counts:?} nodes"
24373 );
24374 assert!(
24375 visits[1] * 20 < node_counts[1],
24376 "the walk must stay far below one pass over the tree: {visits:?} over \
24377 trees of {node_counts:?} nodes"
24378 );
24379 }
24380
24381 #[test]
24382 fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
24383 let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
24384 let mut parser = tree_sitter::Parser::new();
24385 parser
24386 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24387 .expect("C++ grammar");
24388 let tree = parser.parse(source, None).expect("fixture tree");
24389 let mut stack = vec![tree.root_node()];
24390 let mut recovered = None;
24391 while let Some(node) = stack.pop() {
24392 if node.kind() == "field_declaration"
24393 && let Some(field) = recovered_function_like_field_declarator(node, source)
24394 {
24395 recovered = Some((node, field.name));
24396 break;
24397 }
24398 let mut cursor = node.walk();
24399 stack.extend(node.named_children(&mut cursor));
24400 }
24401 let (declaration, name) = recovered.unwrap_or_else(|| {
24402 panic!(
24403 "pointer-wrapped macro field was not recovered: {}",
24404 tree.root_node().to_sexp()
24405 )
24406 });
24407 assert_eq!(node_text(name, source), "handshake");
24408 let recovered =
24409 recovered_function_like_field_declarator(declaration, source).expect("recovered field");
24410 assert_eq!(recovered.pointer_depth(), 1);
24411 assert_eq!(
24412 render_cpp_field_signature(declaration, name, source),
24413 "struct handshake * handshake;"
24414 );
24415 }
24416
24417 #[test]
24418 fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
24419 let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
24420 let mut parser = tree_sitter::Parser::new();
24421 parser
24422 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24423 .expect("C++ grammar");
24424 let tree = parser.parse(source, None).expect("fixture tree");
24425 let mut stack = vec![tree.root_node()];
24426 let mut recovered = None;
24427 while let Some(node) = stack.pop() {
24428 if let Some(field) = recovered_pyobject_head_field(node, source) {
24429 recovered = Some((node, field));
24430 break;
24431 }
24432 let mut cursor = node.walk();
24433 stack.extend(node.named_children(&mut cursor));
24434 }
24435 let (declaration, recovered) = recovered.unwrap_or_else(|| {
24436 panic!(
24437 "pointer field after PyObject_HEAD was not recovered: {}",
24438 tree.root_node().to_sexp()
24439 )
24440 });
24441 assert_eq!(node_text(recovered.name, source), "image");
24442 assert_eq!(recovered.pointer_depth(), 1);
24443 assert_eq!(
24444 render_cpp_field_signature(declaration, recovered.declarator, source),
24445 "ImagingObject *image;"
24446 );
24447 assert_eq!(
24448 crate::source_facts::capture_field_type(declaration, "image", source),
24449 (
24450 Some(
24451 brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
24452 type_text: "ImagingObject".to_owned(),
24453 indirection: 1,
24454 binds_indirectly: true,
24455 template_arguments: None,
24456 }
24457 ),
24458 false,
24459 ),
24460 "canonical field facts must use the recovered type, not the macro token"
24461 );
24462 }
24463
24464 #[test]
24469 fn callable_metadata_records_c_receiver_contracts_structurally() {
24470 const SOURCE: &str = concat!(
24471 "static long accumulate(const long *lines, int count) { return count; }\n",
24472 "\n",
24473 "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
24474 );
24475 let parsed = parse_cpp_declarations(SOURCE, "shop.c");
24476
24477 assert_eq!(
24478 recorded_callable_modifiers(&parsed),
24479 vec![
24480 "accumulate decl_only=false static=false constructor=false \
24481 visibility=Unknown"
24482 .to_string(),
24483 "total decl_only=false static=false constructor=false visibility=Unknown"
24484 .to_string(),
24485 ],
24486 );
24487 }
24488
24489 #[test]
24502 fn callable_metadata_records_cpp_receiver_contracts_structurally() {
24503 const SOURCE: &str = concat!(
24504 "namespace shop {\n",
24505 "\n",
24506 "class Client {\n",
24507 "public:\n",
24508 " Client();\n",
24509 " void send(int order);\n",
24510 " static int count();\n",
24511 "protected:\n",
24512 " virtual int audit(int n) const { return n; }\n",
24513 "private:\n",
24514 " int tally(int n) { return n; }\n",
24515 "};\n",
24516 "\n",
24517 "struct Ledger {\n",
24518 " void record(int n) { (void)n; }\n",
24519 "};\n",
24520 "\n",
24521 "Client::Client() {}\n",
24522 "void Client::send(int order) { (void)order; }\n",
24523 "int Client::count() { return 0; }\n",
24524 "\n",
24525 "int total(int order) { return order; }\n",
24526 "\n",
24527 "}\n",
24528 );
24529 let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
24530
24531 assert_eq!(
24532 recorded_callable_modifiers(&parsed),
24533 vec![
24534 "shop.Client.Client decl_only=false static=false constructor=true \
24535 visibility=Unknown"
24536 .to_string(),
24537 "shop.Client.Client decl_only=true static=false constructor=true \
24538 visibility=Public"
24539 .to_string(),
24540 "shop.Client.audit decl_only=false static=false constructor=false \
24541 visibility=Protected"
24542 .to_string(),
24543 "shop.Client.count decl_only=false static=false constructor=false \
24544 visibility=Unknown"
24545 .to_string(),
24546 "shop.Client.count decl_only=true static=true constructor=false \
24547 visibility=Public"
24548 .to_string(),
24549 "shop.Client.send decl_only=false static=false constructor=false \
24550 visibility=Unknown"
24551 .to_string(),
24552 "shop.Client.send decl_only=true static=false constructor=false \
24553 visibility=Public"
24554 .to_string(),
24555 "shop.Client.tally decl_only=false static=false constructor=false \
24556 visibility=Private"
24557 .to_string(),
24558 "shop.Ledger.record decl_only=false static=false constructor=false \
24559 visibility=Public"
24560 .to_string(),
24561 "shop.total decl_only=false static=false constructor=false visibility=Unknown"
24562 .to_string(),
24563 ],
24564 );
24565 }
24566
24567 fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
24574 let mut rows = parsed
24575 .signature_metadata
24576 .iter()
24577 .flat_map(|(unit, entries)| {
24578 entries.iter().map(move |metadata| {
24579 assert!(
24580 metadata.callable_modifiers_recorded(),
24581 "{} must record that the walk read its declaration shape",
24582 unit.fq_name()
24583 );
24584 format!(
24585 "{} decl_only={} static={} constructor={} visibility={:?}",
24586 unit.fq_name(),
24587 metadata.is_declaration_only(),
24588 metadata.callable_is_static(),
24589 metadata.callable_is_constructor(),
24590 metadata
24591 .callable_declared_visibility()
24592 .expect("a recorded modifier set states a visibility"),
24593 )
24594 })
24595 })
24596 .collect::<Vec<_>>();
24597 rows.sort();
24598 rows
24599 }
24600}