1use crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::{MacroReplacementField, ObjectMacroReplacement};
9use crate::structural::{CPP_STRUCTURAL_SPEC, is_cpp_preprocessor_conditional};
10pub use brokk_bifrost_core::analyzer::common::node_source_text as node_text;
11use brokk_bifrost_core::analyzer::common::{
12 parse_source_ranges_with_cancellation, parse_source_region,
13};
14pub use brokk_bifrost_core::analyzer::cpp_facts::{
15 CppAccessSpecifier, CppBaseSpecifierFact, CppComparableNode, CppComparableParameter,
16 CppComparableSlot,
17};
18use brokk_bifrost_core::analyzer::fq_name::{
19 FqName, SegmentKind, joined_segments, normalize_joined, segment_interner,
20};
21use brokk_bifrost_core::analyzer::model::LanguageDialect;
22use brokk_bifrost_core::analyzer::model::{
23 CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
24 CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
25 CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
26 ParameterMetadata, ParseCompleteness, Range, SignatureMetadata, StructuredTypeIdentity,
27 StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
28};
29use brokk_bifrost_core::analyzer::parsed_file::{ParsedFile, ParsedSourceFacts, SourceImportFact};
30use brokk_bifrost_core::analyzer::source_facts::{
31 PrimarySourceFactCollector, SourceDeclarationId, SourceImportId, SourceOccurrenceId,
32 SourceOccurrenceProvenance,
33};
34use brokk_bifrost_core::analyzer::statement_facts::CppStatementFactRows;
35use brokk_bifrost_core::analyzer::structural::adapter_helpers::node_range;
36use brokk_bifrost_core::analyzer::structural::collector::StructuralFactCollector;
37use brokk_bifrost_core::analyzer::structural::materialization::{
38 GenerationKind, MaterializationRecord,
39};
40use brokk_bifrost_core::analyzer::structural::resolution::DeclaredVisibility;
41use brokk_bifrost_core::analyzer::structural::spec::{CompiledKinds, StructuralSpec};
42use brokk_bifrost_core::analyzer::symbol_path::parse_symbol_path;
43use brokk_bifrost_core::analyzer::tree_walk::{
44 NodeKindIds, ParentIndex, WalkControl, child_by_field_or_named_child_of_kinds, children_iter,
45 named_children_iter, push_children_reversed, push_named_children_reversed, subtree_contains,
46 walk_named_tree_preorder,
47};
48use brokk_bifrost_core::analyzer::usages::common::same_node;
49use brokk_bifrost_core::analyzer::{CodeUnit, Language, ProjectFile};
50use brokk_bifrost_core::hash::{HashMap, HashSet};
51use regex::Regex;
52use std::cell::RefCell;
53use tree_sitter::{Node, Tree};
54
55pub(crate) fn strip_tag_type_prefix(value: &str) -> &str {
57 let value = value.trim_start_matches("const ");
58 value
59 .strip_prefix("struct ")
60 .or_else(|| value.strip_prefix("class "))
61 .or_else(|| value.strip_prefix("enum "))
62 .unwrap_or(value)
63 .trim()
64}
65
66fn cpp_push_package(fq: &mut FqName, package_name: &str) {
74 for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
75 fq.push(segment_interner().intern(component, SegmentKind::Package));
76 }
77}
78
79const CPP_PACKAGE_SEPARATOR: &str = "::";
81
82fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
89 let mut first = true;
90 for component in chain.split('$').filter(|c| !c.is_empty()) {
94 let kind = if first {
95 SegmentKind::Type
96 } else {
97 SegmentKind::Nested
98 };
99 fq.push(segment_interner().intern(component, kind));
100 first = false;
101 }
102}
103
104fn cpp_namespace_fq(full_name: &str) -> FqName {
108 let mut fq = FqName::new();
109 cpp_push_package(&mut fq, full_name);
110 fq
111}
112
113fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
129 let mut components = Vec::new();
130 let mut stack = vec![node];
131 while let Some(current) = stack.pop() {
132 match current.kind() {
133 "namespace_identifier" | "identifier" => {
134 components.push(normalize_cpp_whitespace(node_text(current, source)));
135 }
136 "nested_namespace_specifier" => {
137 for index in (0..current.named_child_count()).rev() {
138 stack.push(
139 current
140 .named_child(index)
141 .expect("index below the node's own named child count"),
142 );
143 }
144 }
145 _ => return cpp_raw_namespace_name_components(node, source),
146 }
147 }
148 if components.iter().any(String::is_empty) {
149 return cpp_raw_namespace_name_components(node, source);
150 }
151 components
152}
153
154fn cpp_namespace_name_component_ranges(node: Node<'_>, source: &str) -> Vec<(String, Range)> {
159 let mut components = Vec::new();
160 let mut stack = vec![node];
161 while let Some(current) = stack.pop() {
162 match current.kind() {
163 "namespace_identifier" | "identifier" => {
164 components.push((
165 normalize_cpp_whitespace(node_text(current, source)),
166 cpp_declaration_range(current),
167 ));
168 }
169 "nested_namespace_specifier" => {
170 for index in (0..current.named_child_count()).rev() {
171 stack.push(
172 current
173 .named_child(index)
174 .expect("index below the node's own named child count"),
175 );
176 }
177 }
178 _ => return Vec::new(),
179 }
180 }
181 if components.iter().any(|(name, _)| name.is_empty()) {
182 return Vec::new();
183 }
184 components
185}
186
187fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
201 let start = node
202 .child(0)
203 .filter(|child| !child.is_named() && child.kind() == "::")
204 .map_or(node.start_byte(), |marker| marker.end_byte());
205 let text = normalize_cpp_whitespace(
206 source
207 .get(start..node.end_byte())
208 .expect("namespace name node covers one source range"),
209 );
210 let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
211 if text.is_empty() {
212 return Vec::new();
213 }
214 vec![text]
215}
216
217pub fn enclosing_cpp_namespace_nodes<'tree>(node: Node<'tree>) -> Vec<Node<'tree>> {
219 let mut namespaces = Vec::new();
220 let mut current = node.parent();
221 while let Some(parent) = current {
222 if parent.kind() == "namespace_definition" {
223 namespaces.push(parent);
224 }
225 current = parent.parent();
226 }
227 namespaces.reverse();
228 namespaces
229}
230
231fn cpp_lexical_namespace_name<'tree>(node: Node<'tree>, source: &str) -> Option<String> {
237 let mut components = Vec::new();
238 for current in enclosing_cpp_namespace_nodes(node) {
239 let name_node = current.child_by_field_name("name")?;
240 let name = normalize_cpp_whitespace(node_text(name_node, source));
241 if name.is_empty() {
242 return None;
243 }
244 components.push(name);
245 }
246 if components.is_empty() {
247 return None;
248 }
249 Some(
253 normalize_joined(
254 &components.join(CPP_PACKAGE_SEPARATOR),
255 CPP_PACKAGE_SEPARATOR,
256 )
257 .into_owned(),
258 )
259}
260
261fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
267 cpp_join_short_name(parent_short, name, '$')
268}
269
270fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
273 cpp_join_short_name(parent_short, name, '.')
274}
275
276fn cpp_join_short_name(parent_short: &str, name: &str, separator: char) -> String {
277 if parent_short.is_empty() {
278 name.to_string()
279 } else {
280 format!("{parent_short}{separator}{name}")
281 }
282}
283
284fn cpp_leaf_fq(
291 package_name: &str,
292 parent: Option<&CodeUnit>,
293 name: &str,
294 kind_if_nested: SegmentKind,
295 kind_if_top: SegmentKind,
296) -> FqName {
297 if let Some(parent) = parent {
298 parent
299 .fq()
300 .clone()
301 .with_pushed(segment_interner().intern(name, kind_if_nested))
302 } else {
303 let mut fq = FqName::new();
304 cpp_push_package(&mut fq, package_name);
305 fq.push(segment_interner().intern(name, kind_if_top));
306 fq
307 }
308}
309
310pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
317 let mut fq = FqName::new();
318 cpp_push_package(&mut fq, package_name);
319 match short_name.rsplit_once('.') {
320 Some((owner_chain, member)) => {
321 cpp_push_type_chain(&mut fq, owner_chain);
322 fq.push(segment_interner().intern(member, SegmentKind::Member));
323 }
324 None => fq.push(segment_interner().intern(short_name, SegmentKind::Member)),
325 }
326 fq
327}
328
329pub fn cpp_rendered_name_fq(rendered: &str) -> FqName {
344 let mut fq = FqName::new();
345 for component in parse_symbol_path(Language::Cpp, rendered) {
346 let mut first = true;
350 for part in component.split('$').filter(|part| !part.is_empty()) {
351 let kind = if first {
352 SegmentKind::Unknown
355 } else {
356 SegmentKind::Nested
357 };
358 fq.push(segment_interner().intern(part, kind));
359 first = false;
360 }
361 }
362 fq
363}
364
365pub fn cpp_colon_source_aliases(rendered: &str) -> Vec<FqName> {
379 if !rendered.contains("::") || rendered.contains('.') || rendered.contains('$') {
380 return Vec::new();
381 }
382 let texts = parse_symbol_path(Language::Cpp, rendered);
383 if texts.len() < 2 {
384 return Vec::new();
385 }
386 cpp_qualified_kind_interpretations(&texts)
387}
388
389fn cpp_qualified_kind_interpretations(texts: &[String]) -> Vec<FqName> {
393 let len = texts.len();
394 let mut names = Vec::new();
395 names.push(cpp_fq_with_kinds(texts, vec![SegmentKind::Package; len]));
396 for packages in 0..len {
397 let rest = len - packages;
398 let mut type_chain = Vec::with_capacity(len);
399 type_chain.extend(std::iter::repeat_n(SegmentKind::Package, packages));
400 type_chain.push(SegmentKind::Type);
401 type_chain.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 1));
402 names.push(cpp_fq_with_kinds(texts, type_chain));
403 if rest >= 2 {
404 let mut member = Vec::with_capacity(len);
405 member.extend(std::iter::repeat_n(SegmentKind::Package, packages));
406 member.push(SegmentKind::Type);
407 member.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 2));
408 member.push(SegmentKind::Member);
409 names.push(cpp_fq_with_kinds(texts, member));
410 } else if packages >= 1 {
411 let mut function = vec![SegmentKind::Package; packages];
412 function.push(SegmentKind::Member);
413 names.push(cpp_fq_with_kinds(texts, function));
414 }
415 }
416 names
417}
418
419fn cpp_fq_with_kinds(texts: &[String], kinds: Vec<SegmentKind>) -> FqName {
420 assert_eq!(
421 texts.len(),
422 kinds.len(),
423 "a qualified interpretation names every segment"
424 );
425 let mut fq = FqName::new();
426 for (text, kind) in texts.iter().zip(kinds) {
427 fq.push(segment_interner().intern(text, kind));
428 }
429 fq
430}
431
432#[derive(Clone)]
433pub struct ScopeInfo {
434 package_name: String,
435 module: Option<CodeUnit>,
436 class_unit: Option<CodeUnit>,
437 template_signature: Option<String>,
438 template_metadata: Option<CppTemplateMetadata>,
439 declarations_are_fields: bool,
440 recovered_specialization_member_scope: bool,
441 visible_using_namespaces: Vec<String>,
453}
454
455struct CppContainer<'tree> {
456 node: Node<'tree>,
457 scope: ScopeInfo,
458}
459
460struct CppNodeWork<'tree> {
461 node: Node<'tree>,
462 scope: ScopeInfo,
463}
464
465struct CppSiblingsWork<'tree> {
472 children: std::vec::IntoIter<Node<'tree>>,
473 scope: ScopeInfo,
474}
475
476enum CppWork<'tree> {
477 Container(CppContainer<'tree>),
478 Node(CppNodeWork<'tree>),
479 Siblings(CppSiblingsWork<'tree>),
480}
481
482fn class_like_name<'tree>(
483 node: Node<'tree>,
484 source: &str,
485 ancestry: &ParentIndex<'tree>,
486) -> Option<String> {
487 let best = class_like_name_from_children(node, source);
488 if let Some(parent) = ancestry.parent(node)
489 && matches!(
490 parent.kind(),
491 "declaration" | "field_declaration" | "function_definition"
492 )
493 && cpp_body_node(node).is_none()
502 && node
503 .child_by_field_name("name")
504 .map(|name_node| {
505 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
506 })
507 .unwrap_or(false)
508 && let Some(recovered) = exported_class_name_from_node(parent, source)
509 && best.as_deref() != Some(recovered.as_str())
510 {
511 return Some(recovered);
512 }
513 best.or_else(|| {
514 node.child_by_field_name("name")
515 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
516 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
517 })
518}
519
520fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
521 let mut grammar_name = None;
522 if let Some(name_node) = node.child_by_field_name("name") {
523 let name = normalize_cpp_whitespace(node_text(name_node, source));
524 if name.is_empty() {
525 return None;
526 }
527 if !cpp_export_macro_token(&name) {
528 return Some(name);
529 }
530 grammar_name = Some(name);
531 }
532
533 let mut best = None;
534 let mut cursor = node.walk();
535 let mut stack = Vec::new();
536 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
537 if matches!(
538 child.kind(),
539 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
540 ) {
541 break;
542 }
543 stack.push(child);
544 }
545
546 while let Some(current) = stack.pop() {
547 if matches!(current.kind(), "type_identifier" | "identifier") {
548 let name = normalize_cpp_whitespace(node_text(current, source));
549 if !name.is_empty() && !cpp_export_macro_token(&name) {
550 best = Some(name);
551 }
552 continue;
553 }
554
555 push_named_children_reversed(current, &mut stack);
556 }
557 best.or(grammar_name)
558}
559
560pub fn cpp_export_macro_token(token: &str) -> bool {
561 token
562 .chars()
563 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
564}
565
566struct RecoveredExportedClass<'tree> {
567 declaration_node: Node<'tree>,
568 name: String,
569 body: Option<Node<'tree>>,
570 raw_supertypes: Option<Vec<String>>,
571 base_facts: Option<Vec<CppBaseSpecifierFact>>,
572 uses_initializer_body: bool,
573 fragmented_body: Option<FragmentedExportBody>,
578}
579
580struct RecoveredFunctionLikeExportClassPair {
581 name: String,
582 range: Range,
583 raw_supertypes: Option<Vec<String>>,
584 base_facts: Option<Vec<CppBaseSpecifierFact>>,
585 fragmented_body: FragmentedExportBody,
586}
587
588struct RecoveredEmbeddedFunctionLikeExportClass {
589 name: String,
590 range: Range,
591 raw_supertypes: Vec<String>,
592 base_facts: Vec<CppBaseSpecifierFact>,
593 fragmented_body: FragmentedExportBody,
594}
595
596struct RecoveredFunctionLikeExportClass {
597 name: String,
598 range: Range,
599 raw_supertypes: Option<Vec<String>>,
600 base_facts: Option<Vec<CppBaseSpecifierFact>>,
601 member_outcome: Option<FragmentedExportMembers>,
602}
603
604struct FragmentedExportBody {
610 reparse_start: usize,
611 reparse_end: usize,
612 class_range: Range,
613}
614
615fn recovered_fragmented_export_body(
616 body: Node<'_>,
617 class_range: Range,
618) -> Option<FragmentedExportBody> {
619 let open = body.child(0).filter(|child| child.kind() == "{")?;
620 let close = body
621 .child(body.child_count().saturating_sub(1))
622 .filter(|child| child.kind() == "}" && !child.is_missing());
623 Some(FragmentedExportBody {
624 reparse_start: open.end_byte(),
625 reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
630 class_range,
631 })
632}
633
634struct DisplacedFragmentNamespaceBoundary<'tree> {
635 class_close: Node<'tree>,
636 class_semicolon: Node<'tree>,
637 namespace_items: Vec<Node<'tree>>,
638}
639
640enum FragmentedExportMembers {
645 Complete(Tree),
646 ConditionalConstructor(Tree),
647}
648
649#[derive(Clone, Copy)]
650struct DisplacedMacroClassTail {
651 split_index: usize,
652 class_range: Range,
653}
654
655fn recover_exported_class_declaration<'tree>(
656 node: Node<'tree>,
657 source: &str,
658) -> Option<RecoveredExportedClass<'tree>> {
659 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
660 return Some(recovered);
661 }
662
663 let class_node = first_class_like_child(node)?;
664 if let Some(name_node) = class_node.child_by_field_name("name") {
665 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
666 if cpp_export_macro_token(&class_name) {
667 let mut cursor = node.walk();
671 if node
672 .children_by_field_name("declarator", &mut cursor)
673 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
674 {
675 return None;
676 }
677 } else if has_direct_cpp_declarator(node) {
678 return None;
679 }
680 }
681 let name = exported_class_name_from_node(class_node, source)?;
682 Some(RecoveredExportedClass {
683 declaration_node: class_node,
684 name,
685 body: cpp_body_node(class_node),
686 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
687 .then(|| extract_cpp_supertypes(class_node, source)),
688 base_facts: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
689 .then(|| extract_cpp_base_facts(class_node, source)),
690 uses_initializer_body: false,
691 fragmented_body: None,
692 })
693}
694
695fn recover_malformed_exported_base_class<'tree>(
696 node: Node<'tree>,
697 source: &str,
698) -> Option<RecoveredExportedClass<'tree>> {
699 if node.kind() != "declaration" {
700 return None;
701 }
702 let class_node = node.child_by_field_name("type")?;
703 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
704 return None;
705 }
706 let macro_name = class_node
707 .child_by_field_name("name")
708 .and_then(|name| direct_identifier_name(name, source))?;
709 if !cpp_export_macro_token(¯o_name) {
710 return None;
711 }
712
713 let mut named_cursor = node.walk();
714 let mut named = node.named_children(&mut named_cursor);
715 let head_matches_class = loop {
718 let child = named.next()?;
719 if child.kind() == "macro_modifier" {
720 continue;
721 }
722 break same_node(child, class_node);
723 };
724 if !head_matches_class {
725 return None;
726 }
727 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
728 if displaced.kind() == "parenthesized_declarator" {
729 let (name, first_base_node) =
734 recovered_function_like_export_class_owner_node(displaced, source)?;
735 let remaining = named.collect::<Vec<_>>();
736 let [init] = remaining.as_slice() else {
737 return None;
738 };
739 if init.kind() != "init_declarator" {
740 return None;
741 }
742 let mut final_base_node = init.child_by_field_name("declarator")?;
743 while final_base_node.kind() == "macro_modifier_declarator" {
744 final_base_node = final_base_node.child_by_field_name("declarator")?;
745 }
746 let first_base = recovered_malformed_base_name(first_base_node, source)?;
747 let final_base = recovered_malformed_base_name(final_base_node, source)?;
748 let body = init.child_by_field_name("value")?;
749 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
750 return None;
751 }
752 return Some(RecoveredExportedClass {
753 declaration_node: node,
754 name,
755 body: Some(body),
756 raw_supertypes: Some(vec![first_base, final_base]),
757 base_facts: Some(vec![
758 recovered_base_specifier(
759 first_base_node,
760 source,
761 recovered_base_is_virtual(displaced, first_base_node, source),
762 )?,
763 recovered_base_specifier(
764 final_base_node,
765 source,
766 recovered_base_is_virtual(*init, final_base_node, source),
767 )?,
768 ]),
769 uses_initializer_body: true,
770 fragmented_body: fragmented_export_body_region(node, body, source),
771 });
772 }
773 if displaced.kind() != "ERROR" {
774 return None;
775 }
776 let name = displaced_exported_class_name(displaced, source)?;
777
778 let remaining = named.collect::<Vec<_>>();
779 let init = *remaining.last()?;
780 if init.kind() != "init_declarator" {
781 return None;
782 }
783 let final_base = init
784 .child_by_field_name("declarator")
785 .and_then(|base| recovered_malformed_base_name(base, source))?;
786 let body = init.child_by_field_name("value")?;
787 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
791 return None;
792 }
793
794 if remaining[..remaining.len() - 1]
795 .iter()
796 .any(|child| match child.kind() {
797 "qualified_identifier"
798 | "scoped_type_identifier"
799 | "type_identifier"
800 | "identifier"
801 | "macro_modifier_declarator" => false,
802 "ERROR" => !is_malformed_inheritance_access(*child, source),
803 _ => true,
804 })
805 {
806 return None;
807 }
808
809 let mut raw_supertypes = Vec::new();
810 let mut base_facts = Vec::new();
811 for base in &remaining[..remaining.len() - 1] {
812 if base.kind() == "ERROR" {
813 continue;
814 }
815 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
816 base_facts.push(recovered_base_specifier(
817 *base,
818 source,
819 recovered_base_is_virtual(node, *base, source),
820 )?);
821 }
822 raw_supertypes.push(final_base);
823 let final_base_node = init.child_by_field_name("declarator")?;
824 base_facts.push(recovered_base_specifier(
825 final_base_node,
826 source,
827 recovered_base_is_virtual(init, final_base_node, source),
828 )?);
829
830 Some(RecoveredExportedClass {
831 declaration_node: node,
832 name,
833 body: Some(body),
834 raw_supertypes: Some(raw_supertypes),
835 base_facts: Some(base_facts),
836 uses_initializer_body: true,
837 fragmented_body: fragmented_export_body_region(node, body, source),
838 })
839}
840
841fn fragmented_export_body_region(
857 node: Node<'_>,
858 body: Node<'_>,
859 source: &str,
860) -> Option<FragmentedExportBody> {
861 let reparse_start = body.start_byte() + 1;
862 let close = direct_close_brace(body)?;
863 if close.end_byte() > close.start_byte() {
864 return Some(FragmentedExportBody {
865 reparse_start,
866 reparse_end: close.start_byte(),
867 class_range: cpp_declaration_range(node),
868 });
869 }
870 let mut sibling = node.next_named_sibling();
873 let displaced_close = loop {
874 let Some(current) = sibling else {
875 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
876 };
877 if cpp_is_stray_close_brace(current, source) {
878 break current;
879 }
880 sibling = current.next_named_sibling();
881 };
882 Some(FragmentedExportBody {
883 reparse_start,
884 reparse_end: displaced_close.start_byte(),
885 class_range: Range {
886 start_byte: node.start_byte(),
887 end_byte: displaced_close.end_byte(),
888 start_line: node.start_position().row + 1,
889 end_line: displaced_close.end_position().row + 1,
890 },
891 })
892}
893
894fn fragmented_export_function_body_region(
902 node: Node<'_>,
903 body: Node<'_>,
904 source: &str,
905 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
906) -> Option<FragmentedExportBody> {
907 let reparse_start = body.start_byte().checked_add(1)?;
908 if let Some(boundary) = displaced_namespace {
909 return Some(FragmentedExportBody {
910 reparse_start,
911 reparse_end: boundary.class_close.start_byte(),
912 class_range: Range {
913 start_byte: node.start_byte(),
914 end_byte: boundary.class_semicolon.end_byte(),
915 start_line: node.start_position().row + 1,
916 end_line: boundary.class_semicolon.end_position().row + 1,
917 },
918 });
919 }
920 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
921 let boundary =
922 fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
923 let boundary_index = boundary.and_then(|boundary| {
924 siblings
925 .iter()
926 .position(|candidate| same_node(*candidate, boundary))
927 });
928 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
929 let mut sibling_index = 0;
930 while let Some(current) = siblings.get(sibling_index).copied() {
943 if current.kind() == "comment" {
944 sibling_index += 1;
945 continue;
946 }
947 if is_trailing_attribute_macro_sibling(current) {
948 sibling_index += 1;
949 continue;
950 }
951 if cpp_is_stray_semicolon(current, source) {
952 return None;
953 }
954 break;
955 }
956 while let Some(current) = siblings.get(sibling_index).copied() {
957 let next = siblings.get(sibling_index + 1).copied();
958 if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
959 return Some(FragmentedExportBody {
960 reparse_start,
961 reparse_end: current.start_byte(),
962 class_range: Range {
963 start_byte: node.start_byte(),
964 end_byte: semicolon.end_byte(),
965 start_line: node.start_position().row + 1,
966 end_line: semicolon.end_position().row + 1,
967 },
968 });
969 }
970 if current.start_byte() >= body.end_byte()
977 && let Some(close) = cpp_nested_stray_close_brace(current, source)
978 {
979 return Some(FragmentedExportBody {
980 reparse_start,
981 reparse_end: close.start_byte(),
982 class_range: Range {
983 start_byte: node.start_byte(),
984 end_byte: current.end_byte(),
985 start_line: node.start_position().row + 1,
986 end_line: current.end_position().row + 1,
987 },
988 });
989 }
990 sibling_index += 1;
991 }
992 boundary.map(|boundary| FragmentedExportBody {
993 reparse_start,
994 reparse_end: boundary.start_byte(),
995 class_range: Range {
996 start_byte: node.start_byte(),
997 end_byte: boundary.start_byte(),
998 start_line: node.start_position().row + 1,
999 end_line: boundary.start_position().row + 1,
1000 },
1001 })
1002}
1003
1004fn fragmented_export_sibling_class_boundary<'tree>(
1009 node: Node<'tree>,
1010 source: &str,
1011 ancestry: &ParentIndex<'tree>,
1012) -> Option<Node<'tree>> {
1013 let node_parent = ancestry.parent(node)?;
1014 cpp_following_named_siblings(node, source, ancestry)
1015 .into_iter()
1016 .find(|candidate| {
1017 recover_exported_class_function_definition(*candidate, source).is_some()
1018 && ancestry
1019 .parent(*candidate)
1020 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
1021 })
1022}
1023
1024fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
1029 if node.kind() != "expression_statement" {
1030 return false;
1031 }
1032 let mut cursor = node.walk();
1033 let mut children = node.named_children(&mut cursor);
1034 children
1035 .next()
1036 .is_some_and(|child| child.kind() == "identifier")
1037 && children.next().is_none()
1038}
1039
1040fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1046 let mut stack = vec![node];
1047 while let Some(current) = stack.pop() {
1048 if cpp_is_stray_close_brace(current, source) {
1049 return Some(current);
1050 }
1051 let mut cursor = current.walk();
1052 stack.extend(current.named_children(&mut cursor));
1053 }
1054 None
1055}
1056
1057fn cpp_following_named_siblings<'tree>(
1071 node: Node<'tree>,
1072 source: &str,
1073 ancestry: &ParentIndex<'tree>,
1074) -> Vec<Node<'tree>> {
1075 let mut siblings = Vec::new();
1076 let mut anchor = node;
1077 while let Some(parent) = ancestry.parent(anchor) {
1078 let at_translation_unit = parent.kind() == "translation_unit";
1079 let mut cursor = parent.walk();
1080 assert!(
1081 cursor.goto_first_child(),
1082 "ParentIndex returned a parent without children"
1083 );
1084 while !same_node(cursor.node(), anchor) {
1085 assert!(
1086 cursor.goto_next_sibling(),
1087 "ParentIndex parent does not contain its child anchor"
1088 );
1089 }
1090 while cursor.goto_next_sibling() {
1091 let current = cursor.node();
1092 if !current.is_named() {
1093 continue;
1094 }
1095 if at_translation_unit
1096 && (current.kind() == "namespace_definition"
1097 || (current.kind() == "function_definition"
1098 && first_class_like_child(current).is_some()))
1099 {
1100 return siblings;
1101 }
1102 siblings.push(current);
1103 if cpp_is_stray_close_brace(current, source) {
1104 if let Some(semicolon) = current
1105 .next_named_sibling()
1106 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
1107 {
1108 siblings.push(semicolon);
1109 }
1110 return siblings;
1111 }
1112 if current.start_byte() >= node.end_byte()
1113 && matches!(current.kind(), "ERROR" | "labeled_statement")
1114 && cpp_nested_stray_close_brace(current, source).is_some()
1115 {
1116 return siblings;
1117 }
1118 }
1119 anchor = parent;
1120 }
1121 siblings
1122}
1123
1124fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
1125 if node.start_byte() >= class_end {
1126 return false;
1127 }
1128 node.end_byte() <= class_end
1129 || cpp_nested_stray_close_brace(node, source)
1130 .is_some_and(|close| close.start_byte() == class_end)
1131}
1132
1133struct FragmentedClassRecovery<'tree> {
1137 declaration_node: Node<'tree>,
1138 name: String,
1139 raw_supertypes: Vec<String>,
1140 body: FragmentedExportBody,
1141}
1142
1143fn fragmented_class_body<'tree>(
1155 node: Node<'tree>,
1156 source: &str,
1157) -> Option<FragmentedClassRecovery<'tree>> {
1158 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
1159 return Some(recovered);
1160 }
1161 let supported_container = node.kind() == "ERROR"
1162 || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
1163 if !supported_container {
1164 return None;
1165 }
1166 let mut cursor = node.walk();
1167 let children = node.children(&mut cursor).collect::<Vec<_>>();
1168 if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
1169 return Some(recovered);
1170 }
1171 let keyword = children.first()?;
1172 if !matches!(keyword.kind(), "class" | "struct" | "union") {
1173 return None;
1174 }
1175 let name_node = children
1176 .iter()
1177 .copied()
1178 .skip(1)
1179 .find(|child| child.is_named())?;
1180 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
1181 return None;
1182 }
1183 let name = normalize_cpp_whitespace(node_text(name_node, source));
1184 if name.is_empty() || cpp_export_macro_token(&name) {
1185 return None;
1186 }
1187 let open_index = children.iter().position(|child| child.kind() == "{")?;
1188 let open = children[open_index];
1189 let body = displaced_fragment_namespace_geometry(node, source)
1190 .map(|boundary| FragmentedExportBody {
1191 reparse_start: open.end_byte(),
1192 reparse_end: boundary.class_close.start_byte(),
1193 class_range: Range {
1194 start_byte: node.start_byte(),
1195 end_byte: boundary.class_semicolon.end_byte(),
1196 start_line: node.start_position().row + 1,
1197 end_line: boundary.class_semicolon.end_position().row + 1,
1198 },
1199 })
1200 .or_else(|| fragmented_displaced_class_body(node, &children, open_index, source))?;
1201 Some(FragmentedClassRecovery {
1202 declaration_node: node,
1203 name,
1204 raw_supertypes: extract_cpp_supertypes(node, source),
1205 body,
1206 })
1207}
1208
1209fn fragmented_export_macro_class_body<'tree>(
1220 node: Node<'tree>,
1221 children: &[Node<'tree>],
1222 source: &str,
1223) -> Option<FragmentedClassRecovery<'tree>> {
1224 let class_node = *children.first()?;
1225 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1226 return None;
1227 }
1228 class_node
1232 .child_by_field_name("name")
1233 .and_then(|name| direct_identifier_name(name, source))?;
1234 let invocation = *children.get(1)?;
1235 if invocation.is_named() || invocation.kind() != "(" {
1236 return None;
1237 }
1238 let open_index = children
1239 .iter()
1240 .position(|child| !child.is_named() && child.kind() == "{")?;
1241 let open = children[open_index];
1242 let name_node = recovered_export_head_name(node, open, source)?;
1243 let name = normalize_cpp_whitespace(node_text(name_node, source));
1244 if name.is_empty() || cpp_export_macro_token(&name) {
1245 return None;
1246 }
1247 Some(FragmentedClassRecovery {
1248 declaration_node: node,
1249 name,
1250 raw_supertypes: recovered_export_head_bases(
1251 node,
1252 name_node.end_byte(),
1253 open.start_byte(),
1254 source,
1255 ),
1256 body: fragmented_displaced_class_body(node, children, open_index, source)
1257 .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1258 })
1259}
1260
1261fn fragmented_displaced_class_body(
1267 node: Node<'_>,
1268 children: &[Node<'_>],
1269 open_index: usize,
1270 source: &str,
1271) -> Option<FragmentedExportBody> {
1272 let open = children[open_index];
1273 let nested_class_opens = children[open_index + 1..]
1274 .iter()
1275 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1276 .count();
1277 let mut closes_remaining = 1 + nested_class_opens;
1278 let mut sibling = node.next_named_sibling();
1279 while let Some(candidate) = sibling {
1280 let next = candidate.next_named_sibling();
1281 let is_close = cpp_is_stray_close_brace(candidate, source)
1287 || (candidate.kind() == "}" && !candidate.is_named());
1288 if is_close {
1289 closes_remaining -= 1;
1290 if closes_remaining == 0 {
1291 let semicolon = next.filter(|next| cpp_is_stray_semicolon(*next, source))?;
1292 if open.end_byte() >= candidate.start_byte() {
1293 return None;
1294 }
1295 return Some(FragmentedExportBody {
1296 reparse_start: open.end_byte(),
1297 reparse_end: candidate.start_byte(),
1298 class_range: Range {
1299 start_byte: node.start_byte(),
1300 end_byte: semicolon.end_byte(),
1301 start_line: node.start_position().row + 1,
1302 end_line: semicolon.end_position().row + 1,
1303 },
1304 });
1305 }
1306 }
1307 sibling = next;
1308 }
1309 None
1310}
1311
1312fn fragmented_container_close_class_body(
1320 node: Node<'_>,
1321 open: Node<'_>,
1322 source: &str,
1323) -> Option<FragmentedExportBody> {
1324 let parent = node.parent()?;
1325 if !matches!(
1326 parent.kind(),
1327 "declaration_list" | "field_declaration_list" | "compound_statement"
1328 ) {
1329 return None;
1330 }
1331 let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1332 if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1337 || open.end_byte() >= close.start_byte()
1338 {
1339 return None;
1340 }
1341 Some(FragmentedExportBody {
1342 reparse_start: open.end_byte(),
1343 reparse_end: close.start_byte(),
1344 class_range: Range {
1345 start_byte: node.start_byte(),
1346 end_byte: close.end_byte(),
1347 start_line: node.start_position().row + 1,
1348 end_line: close.end_position().row + 1,
1349 },
1350 })
1351}
1352
1353pub(crate) fn recovered_fragmented_class_has_body(
1354 node: Node<'_>,
1355 source: &str,
1356 expected_name: &str,
1357 expected_range: &Range,
1358) -> bool {
1359 fragmented_class_body(node, source).is_some_and(|recovered| {
1360 recovered.name == expected_name
1361 && recovered.body.class_range.start_byte == expected_range.start_byte
1362 && recovered.body.class_range.end_byte == expected_range.end_byte
1363 })
1364}
1365
1366fn fragmented_plain_class_declaration_body<'tree>(
1373 node: Node<'tree>,
1374 source: &str,
1375) -> Option<FragmentedClassRecovery<'tree>> {
1376 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1377 return None;
1378 }
1379 let class_node = node.child_by_field_name("type")?;
1380 if !matches!(
1381 class_node.kind(),
1382 "class_specifier" | "struct_specifier" | "union_specifier"
1383 ) {
1384 return None;
1385 }
1386 let name_node = class_node.child_by_field_name("name")?;
1387 let name = normalize_cpp_whitespace(node_text(name_node, source));
1388 if name.is_empty() || cpp_export_macro_token(&name) {
1389 return None;
1390 }
1391 let body = cpp_body_node(class_node)?;
1392 if body.kind() != "field_declaration_list" {
1393 return None;
1394 }
1395 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1396 if declarator.start_byte() < class_node.end_byte() {
1397 return None;
1398 }
1399 let mut cursor = node.walk();
1400 node.named_children(&mut cursor).any(|child| {
1401 if child.kind() != "ERROR"
1402 || child.start_byte() < class_node.end_byte()
1403 || child.end_byte() > declarator.start_byte()
1404 {
1405 return false;
1406 }
1407 let mut cursor = child.walk();
1408 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1409 let Some((return_type, attributes)) = components.split_last() else {
1410 return false;
1411 };
1412 matches!(
1413 return_type.kind(),
1414 "identifier"
1415 | "type_identifier"
1416 | "primitive_type"
1417 | "decltype"
1418 | "placeholder_type_specifier"
1419 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1420 && attributes.iter().all(|attribute| {
1421 matches!(attribute.kind(), "identifier" | "type_identifier")
1422 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1423 *attribute, source,
1424 )))
1425 })
1426 })
1427 } else {
1428 let mut cursor = node.walk();
1429 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1430 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1431 if same_node(*candidate_class, class_node)
1432 && continuation.kind() == "identifier"
1433 && node_text(*continuation, source) == "else"
1434 && continuation_body.kind() == "compound_statement"
1435 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1436 && continuation_body
1437 .child(continuation_body.child_count().saturating_sub(1))
1438 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1439 };
1440 if !displaced_member {
1441 return None;
1442 }
1443 let open = body
1444 .children(&mut body.walk())
1445 .find(|child| child.kind() == "{")?;
1446 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1447 let ordinary_boundary =
1448 siblings
1449 .iter()
1450 .copied()
1451 .enumerate()
1452 .find_map(|(close_index, close)| {
1453 cpp_stray_close_followed_by_semicolon(
1454 close,
1455 siblings.get(close_index + 1).copied(),
1456 source,
1457 )
1458 .map(|semicolon| (close, semicolon))
1459 });
1460 let (close, semicolon) =
1461 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1462 (boundary.class_close, boundary.class_semicolon)
1463 } else {
1464 ordinary_boundary?
1465 };
1466 if open.end_byte() >= close.start_byte() {
1467 return None;
1468 }
1469 Some(FragmentedClassRecovery {
1470 declaration_node: class_node,
1471 name,
1472 raw_supertypes: extract_cpp_supertypes(class_node, source),
1473 body: FragmentedExportBody {
1474 reparse_start: open.end_byte(),
1475 reparse_end: close.start_byte(),
1476 class_range: Range {
1477 start_byte: class_node.start_byte(),
1478 end_byte: semicolon.end_byte(),
1479 start_line: class_node.start_position().row + 1,
1480 end_line: semicolon.end_position().row + 1,
1481 },
1482 },
1483 })
1484}
1485
1486fn displaced_export_function_namespace_shape<'tree>(
1487 declaration: Node<'tree>,
1488 source: &str,
1489) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1490 let mut nested = Vec::new();
1491 for index in (0..declaration.named_child_count()).rev() {
1492 nested.push(declaration.named_child(index)?);
1493 }
1494 while let Some(current) = nested.pop() {
1495 if recover_exported_class_function_definition(current, source).is_some() {
1501 return None;
1502 }
1503 for index in (0..current.named_child_count()).rev() {
1504 nested.push(current.named_child(index)?);
1505 }
1506 }
1507 let mut same_envelope_sibling = declaration.next_named_sibling();
1508 while let Some(current) = same_envelope_sibling {
1509 if recover_exported_class_function_definition(current, source).is_some() {
1510 return None;
1511 }
1512 same_envelope_sibling = current.next_named_sibling();
1513 }
1514 let declaration_list = declaration.parent()?;
1515 let (namespace, class_close, trailing_semicolon) =
1516 cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1517 let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1523 let trailing_index = siblings
1524 .iter()
1525 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1526 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1527 recover_exported_class_function_definition(*candidate, source).is_some()
1528 }) {
1529 return None;
1534 }
1535 let mut namespace_items = Vec::new();
1536 let mut nested_fragment_end = 0;
1537 for current in siblings.into_iter().skip(trailing_index + 1) {
1538 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1539 {
1540 return Some(DisplacedFragmentNamespaceBoundary {
1541 class_close,
1542 class_semicolon: trailing_semicolon,
1543 namespace_items,
1544 });
1545 }
1546 if current.start_byte() >= nested_fragment_end
1547 && let Some(recovered) = fragmented_class_body(current, source)
1548 {
1549 nested_fragment_end = recovered.body.class_range.end_byte;
1550 } else if current.start_byte() >= nested_fragment_end
1551 && recover_exported_class_function_definition(current, source).is_some()
1552 && let Some(body) = cpp_body_node(current)
1553 && let Some(fragmented) =
1554 fragmented_export_function_body_region(current, body, source, None)
1555 {
1556 nested_fragment_end = fragmented.class_range.end_byte;
1557 }
1558 namespace_items.push(current);
1559 }
1560 None
1561}
1562
1563fn displaced_fragment_namespace_boundary<'tree>(
1564 declaration: Node<'tree>,
1565 body: Node<'tree>,
1566 source: &str,
1567) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1568 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1569 let reparse_start = body.start_byte() + 1;
1570 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1571 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1572}
1573
1574fn displaced_fragment_namespace_geometry<'tree>(
1580 declaration: Node<'tree>,
1581 source: &str,
1582) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1583 let envelope = declaration
1587 .parent()
1588 .filter(|parent| {
1589 parent.kind() == "template_declaration"
1590 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1591 })
1592 .unwrap_or(declaration);
1593 let declaration_list = envelope.parent()?;
1594 let (_, class_close, trailing_semicolon) =
1595 cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1596 let mut namespace_items = Vec::new();
1597 let mut sibling = trailing_semicolon.next_named_sibling();
1598 let mut nested_fragment_end = 0;
1599 loop {
1600 let current = sibling?;
1601 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1602 {
1603 break;
1604 }
1605 if current.start_byte() >= nested_fragment_end
1606 && let Some(recovered) = fragmented_class_body(current, source)
1607 {
1608 nested_fragment_end = recovered.body.class_range.end_byte;
1609 }
1610 namespace_items.push(current);
1611 sibling = current.next_named_sibling();
1612 }
1613 Some(DisplacedFragmentNamespaceBoundary {
1614 class_close,
1615 class_semicolon: trailing_semicolon,
1616 namespace_items,
1617 })
1618}
1619
1620fn cpp_fragment_namespace_boundary_nodes<'tree>(
1621 declaration_list: Node<'tree>,
1622) -> Option<(Node<'tree>, Node<'tree>, Node<'tree>)> {
1623 if declaration_list.kind() != "declaration_list" {
1624 return None;
1625 }
1626 let namespace = declaration_list.parent()?;
1627 if namespace.kind() != "namespace_definition"
1628 || namespace.child_by_field_name("body") != Some(declaration_list)
1629 {
1630 return None;
1631 }
1632 let class_close = direct_close_brace(declaration_list)?;
1633 let trailing_semicolon = namespace.next_named_sibling()?;
1634 (trailing_semicolon.kind() == "expression_statement"
1635 && trailing_semicolon.named_child_count() == 0)
1636 .then_some((namespace, class_close, trailing_semicolon))
1637}
1638
1639fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1641 (0..node.child_count())
1642 .filter_map(|index| node.child(index))
1643 .find(|child| !child.is_named() && child.kind() == "}")
1644}
1645
1646fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1649 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1650}
1651
1652fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1663 let bytes = source.as_bytes();
1664 if bytes.get(open_byte) != Some(&b'{') {
1665 return None;
1666 }
1667 let mut depth = 0usize;
1668 let mut i = open_byte;
1669 while i < bytes.len() {
1670 match bytes[i] {
1671 b'{' => depth += 1,
1672 b'}' => {
1673 depth = depth.checked_sub(1)?;
1674 if depth == 0 {
1675 return Some(i);
1676 }
1677 }
1678 b'/' if bytes.get(i + 1) == Some(&b'/') => {
1679 while i < bytes.len() && bytes[i] != b'\n' {
1680 i += 1;
1681 }
1682 continue;
1683 }
1684 b'/' if bytes.get(i + 1) == Some(&b'*') => {
1685 i += 2;
1686 while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1687 i += 1;
1688 }
1689 i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1690 continue;
1691 }
1692 quote @ (b'"' | b'\'') => {
1693 if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1696 return None;
1697 }
1698 i += 1;
1699 while i < bytes.len() && bytes[i] != quote {
1700 i += if bytes[i] == b'\\' { 2 } else { 1 };
1701 }
1702 if i >= bytes.len() {
1703 return None;
1704 }
1705 }
1706 _ => {}
1707 }
1708 i += 1;
1709 }
1710 None
1711}
1712
1713fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1714 let mut name = None;
1715 let mut colon_count = 0;
1716 let mut access_count = 0;
1717 for index in 0..node.child_count() {
1718 let child = node.child(index)?;
1719 match child.kind() {
1720 "identifier" | "type_identifier" if child.is_named() => {
1721 if name.is_some() {
1722 return None;
1723 }
1724 let candidate = normalize_cpp_whitespace(node_text(child, source));
1725 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1726 return None;
1727 }
1728 name = Some(candidate);
1729 }
1730 "template_function" | "template_type" if child.is_named() => {
1731 if name.is_some() {
1732 return None;
1733 }
1734 let candidate = child
1735 .child_by_field_name("name")
1736 .and_then(|name| direct_identifier_name(name, source))?;
1737 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1738 return None;
1739 }
1740 name = Some(candidate);
1741 }
1742 ":" if !child.is_named() => colon_count += 1,
1743 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1744 _ => return None,
1745 }
1746 }
1747 (colon_count == 1 && access_count == 1)
1748 .then_some(name)
1749 .flatten()
1750}
1751
1752fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1753 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1754 return false;
1755 }
1756 node.named_child(0)
1757 .and_then(|child| direct_identifier_name(child, source))
1758 .is_some_and(|name| cpp_is_inheritance_access_specifier(&name))
1759}
1760
1761use brokk_bifrost_core::analyzer::tree_walk::has_token_child as has_direct_token;
1762
1763fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1764 match node.kind() {
1765 "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1766 recovered_base_atom(node, source)
1767 }
1768 "macro_modifier_declarator" => node
1771 .child_by_field_name("declarator")
1772 .and_then(|declarator| recovered_malformed_base_name(declarator, source)),
1773 "template_type" | "template_function" => node
1774 .child_by_field_name("name")
1775 .and_then(|name| recovered_malformed_base_name(name, source)),
1776 "ERROR" => None,
1777 "qualified_identifier" | "scoped_type_identifier" => {
1778 let suffix = node
1779 .child_by_field_name("name")
1780 .and_then(|name| recovered_malformed_base_name(name, source))?;
1781 let scope = node
1782 .child_by_field_name("scope")
1783 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1784 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1785 malformed_qualified_prefix(node, source)?
1786 } else {
1787 if malformed_qualified_prefix(node, source).is_some() {
1788 return None;
1789 }
1790 scope
1791 };
1792 Some(format!("{prefix}::{suffix}"))
1793 }
1794 _ => None,
1795 }
1796}
1797
1798fn recovered_base_components(node: Node<'_>, source: &str) -> Option<Vec<String>> {
1799 let mut pending = vec![node];
1800 let mut components = Vec::new();
1801 while let Some(current) = pending.pop() {
1802 match current.kind() {
1803 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier" => {
1804 components.push(recovered_base_atom(current, source)?);
1805 }
1806 "macro_modifier_declarator" => {
1807 pending.push(current.child_by_field_name("declarator")?);
1808 }
1809 "template_type" | "template_function" => {
1810 pending.push(current.child_by_field_name("name")?);
1811 }
1812 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
1813 pending.push(current.child_by_field_name("name")?);
1814 if let Some(scope) = current.child_by_field_name("scope") {
1815 if recovered_base_atom(scope, source).is_some_and(|name| {
1819 matches!(name.as_str(), "public" | "protected" | "private")
1820 }) {
1821 pending.push(malformed_qualified_prefix_node(current, source)?);
1822 } else {
1823 if malformed_qualified_prefix_node(current, source).is_some() {
1824 return None;
1825 }
1826 pending.push(scope);
1827 }
1828 }
1829 }
1830 _ => return None,
1831 }
1832 }
1833 Some(components)
1834}
1835
1836fn recovered_base_specifier(
1837 node: Node<'_>,
1838 source: &str,
1839 is_virtual: bool,
1840) -> Option<CppBaseSpecifierFact> {
1841 let components = recovered_base_components(node, source)?;
1842 crate::source_facts::base_specifier_fact(
1843 node,
1844 components,
1845 is_virtual,
1846 CppAccessSpecifier::Unknown,
1847 )
1848}
1849
1850fn recovered_base_is_virtual(container: Node<'_>, base: Node<'_>, source: &str) -> bool {
1851 let mut pending = vec![container];
1855 let mut is_virtual = false;
1856 while let Some(node) = pending.pop() {
1857 if node.start_byte() >= base.end_byte() {
1858 break;
1859 }
1860 if node.child_count() != 0 {
1861 let mut cursor = node.walk();
1862 let children = node
1863 .children(&mut cursor)
1864 .take_while(|child| child.start_byte() < base.end_byte())
1865 .collect::<Vec<_>>();
1866 pending.extend(children.into_iter().rev());
1867 continue;
1868 }
1869 if node.start_byte() < base.start_byte() && matches!(node.kind(), "," | ":") {
1870 is_virtual = false;
1871 } else if node.kind() == "virtual"
1872 || recovered_base_atom(node, source).is_some_and(|name| name == "virtual")
1873 {
1874 is_virtual = true;
1875 }
1876 }
1877 is_virtual
1878}
1879
1880fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1881 if !matches!(
1882 node.kind(),
1883 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1884 ) {
1885 return None;
1886 }
1887 let name = normalize_cpp_whitespace(node_text(node, source));
1888 (!name.is_empty()).then_some(name)
1889}
1890
1891fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1892 malformed_qualified_prefix_node(node, source)
1893 .and_then(|prefix| recovered_base_atom(prefix, source))
1894}
1895
1896fn malformed_qualified_prefix_node<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1897 let mut prefix = None;
1898 let mut cursor = node.walk();
1899 for error in node
1900 .named_children(&mut cursor)
1901 .filter(|child| child.kind() == "ERROR")
1902 {
1903 if prefix.is_some() {
1904 return None;
1905 }
1906 let mut error_cursor = error.walk();
1911 let atoms = error
1912 .named_children(&mut error_cursor)
1913 .map(|child| recovered_base_atom(child, source).map(|name| (child, name)))
1914 .collect::<Option<Vec<_>>>()?;
1915 let [atom] = atoms
1916 .iter()
1917 .filter(|(_, name)| name != "virtual")
1918 .collect::<Vec<_>>()[..]
1919 else {
1920 return None;
1921 };
1922 prefix = Some(atom.0);
1923 }
1924 prefix
1925}
1926
1927type RecoveredFunctionDefinitionClass<'tree> = (
1928 Node<'tree>,
1929 String,
1930 Option<Vec<String>>,
1931 Option<Vec<CppBaseSpecifierFact>>,
1932);
1933struct StrandedRun<'tree> {
1937 declarations: Vec<MacroWrappedDeclaration<'tree>>,
1938 complete: bool,
1944}
1945
1946struct MacroWrappedDeclaration<'tree> {
1947 declarator: Node<'tree>,
1948 range: Range,
1949 is_static: bool,
1954}
1955
1956fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1970 declaration_scope_container(node, ancestry).is_some()
1971}
1972
1973fn declaration_scope_container<'tree>(
1978 node: Node<'tree>,
1979 ancestry: &ParentIndex<'tree>,
1980) -> Option<Node<'tree>> {
1981 let mut parent = ancestry.parent(node)?;
1982 loop {
1983 match parent.kind() {
1984 "translation_unit" => return Some(parent),
1985 "declaration_list" => {
1986 return ancestry
1987 .parent(parent)
1988 .is_some_and(|grandparent| {
1989 matches!(
1990 grandparent.kind(),
1991 "namespace_definition" | "linkage_specification"
1992 )
1993 })
1994 .then_some(parent);
1995 }
1996 "ERROR" => match ancestry.parent(parent) {
1997 Some(grandparent) => parent = grandparent,
1998 None => return Some(parent),
2001 },
2002 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif" | "preproc_else" => {
2010 parent = ancestry.parent(parent)?;
2011 }
2012 _ => return None,
2013 }
2014 }
2015}
2016
2017fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2019 node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
2020}
2021
2022pub(crate) fn macro_modifier_identifier(node: Node<'_>) -> Option<Node<'_>> {
2029 if node.kind() != "macro_modifier" {
2030 return None;
2031 }
2032 node.named_child(0)
2033 .filter(|child| child.kind() == "identifier")
2034}
2035
2036pub(crate) fn macro_modifier_declarator_parts(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
2042 if node.kind() != "macro_modifier_declarator" {
2043 return None;
2044 }
2045 let mut cursor = node.walk();
2046 let modifier = node
2047 .named_children(&mut cursor)
2048 .find(|child| child.kind() == "macro_modifier")
2049 .and_then(macro_modifier_identifier)?;
2050 let declarator = node.child_by_field_name("declarator")?;
2051 Some((modifier, declarator))
2052}
2053
2054fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
2060 matches!(
2061 node.kind(),
2062 "identifier"
2063 | "type_identifier"
2064 | "primitive_type"
2065 | "sized_type_specifier"
2066 | "struct_specifier"
2067 | "union_specifier"
2068 | "enum_specifier"
2069 | "type_qualifier"
2070 | "storage_class_specifier"
2071 | "explicit_function_specifier"
2072 | "virtual_function_specifier"
2073 | "qualified_identifier"
2074 | "template_type"
2075 | "dependent_type"
2076 | "placeholder_type_specifier"
2077 | "macro_modifier"
2078 | "macro_type_specifier"
2079 )
2080}
2081
2082fn is_macro_argument_error(node: Node<'_>) -> bool {
2088 if node.kind() != "ERROR" {
2089 return false;
2090 }
2091 let mut cursor = node.walk();
2092 node.named_children(&mut cursor).all(|child| {
2093 matches!(
2094 child.kind(),
2095 "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
2096 )
2097 })
2098}
2099
2100fn recovered_declaration_end(declarator: Node<'_>) -> usize {
2109 declarator
2110 .next_sibling()
2111 .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
2112 .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
2113}
2114
2115fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
2137 let mut parts = Vec::new();
2138 let mut cursor = node.walk();
2139 for child in node.named_children(&mut cursor) {
2140 if child.kind() == "ERROR" {
2141 let mut error_cursor = child.walk();
2142 parts.extend(child.named_children(&mut error_cursor));
2143 } else {
2144 parts.push(child);
2145 }
2146 }
2147
2148 let mut declarations = Vec::new();
2149 let mut start = None;
2150 let mut is_static = false;
2151 let mut complete = true;
2152 for part in parts {
2153 if part.kind() == "comment" {
2154 continue;
2155 }
2156 if let Some(declarator) = extract_function_declarator(part) {
2157 let start_byte = start.take().unwrap_or_else(|| part.start_byte());
2158 declarations.push(MacroWrappedDeclaration {
2159 declarator,
2160 range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
2161 is_static,
2162 });
2163 is_static = false;
2164 continue;
2165 }
2166 if !is_recovered_declaration_type_part(part) {
2167 complete = false;
2168 break;
2169 }
2170 is_static |= part.kind() == "storage_class_specifier"
2171 && normalize_cpp_whitespace(node_text(part, source)) == "static";
2172 start.get_or_insert(part.start_byte());
2173 }
2174 StrandedRun {
2175 declarations,
2176 complete: complete && start.is_none(),
2177 }
2178}
2179
2180fn macro_wrapped_declarations<'tree>(
2216 envelope: Node<'tree>,
2217 source: &str,
2218 ancestry: &ParentIndex<'tree>,
2219) -> Vec<MacroWrappedDeclaration<'tree>> {
2220 let mut declarations = Vec::new();
2221 if !is_declaration_scope_error(envelope, ancestry) {
2222 return declarations;
2223 }
2224 let mut cursor = envelope.walk();
2225 let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
2226 let [head, rest @ ..] = children.as_slice() else {
2227 return declarations;
2228 };
2229 let arguments: Vec<Node<'tree>> = if head.kind() == "identifier" {
2230 rest.to_vec()
2231 } else if head.kind() == "function_declarator"
2232 && head
2233 .child_by_field_name("declarator")
2234 .is_some_and(|name| name.kind() == "identifier")
2235 {
2236 let Some(parameters) = head
2237 .child_by_field_name("parameters")
2238 .filter(|parameters| parameters.kind() == "parameter_list")
2239 else {
2240 return declarations;
2241 };
2242 let mut parameters_cursor = parameters.walk();
2243 parameters.named_children(&mut parameters_cursor).collect()
2244 } else {
2245 return declarations;
2246 };
2247 let mut wrapped_declaration_seen = false;
2248 let mut in_hint = false;
2252 let mut pending_start = None;
2257 let mut is_static = false;
2258 for argument in arguments {
2259 if argument.kind() == "comment" {
2260 continue;
2261 }
2262 if argument.kind() == "ERROR"
2263 && (0..argument.child_count())
2264 .filter(|index| {
2265 argument
2266 .child(*index)
2267 .is_some_and(|child| child.kind() == "\"")
2268 })
2269 .count()
2270 % 2
2271 == 1
2272 {
2273 in_hint = !in_hint;
2275 pending_start = None;
2276 continue;
2277 }
2278 if in_hint {
2279 continue;
2280 }
2281 match argument.kind() {
2282 "parameter_declaration" => {
2283 let recovered = stranded_declaration_run(argument, source).declarations;
2284 if recovered.is_empty() {
2285 break;
2286 }
2287 wrapped_declaration_seen = true;
2288 declarations.extend(recovered);
2289 }
2290 "macro_modifier" if wrapped_declaration_seen => {
2293 pending_start.get_or_insert(argument.start_byte());
2294 }
2295 "storage_class_specifier" if wrapped_declaration_seen => {
2296 pending_start.get_or_insert(argument.start_byte());
2297 is_static |= normalize_cpp_whitespace(node_text(argument, source)) == "static";
2298 }
2299 "macro_modifier_declarator" if wrapped_declaration_seen => {
2300 let Some(declarator) = extract_function_declarator(argument) else {
2301 break;
2302 };
2303 let start = pending_start
2304 .take()
2305 .unwrap_or_else(|| argument.start_byte());
2306 declarations.push(MacroWrappedDeclaration {
2307 declarator,
2308 range: cpp_recovery_window(source, start, recovered_declaration_end(argument)),
2309 is_static,
2310 });
2311 is_static = false;
2312 }
2313 "ERROR" if wrapped_declaration_seen && is_macro_argument_error(argument) => {}
2318 "ERROR" if wrapped_declaration_seen => {
2322 let mut error_cursor = argument.walk();
2323 let mut parts = argument.named_children(&mut error_cursor);
2324 let Some(declarator) = parts
2325 .next()
2326 .filter(|part| part.kind() == "function_declarator")
2327 .filter(|_| parts.next().is_none())
2328 else {
2329 break;
2330 };
2331 let start = pending_start
2332 .take()
2333 .unwrap_or_else(|| declarator.start_byte());
2334 declarations.push(MacroWrappedDeclaration {
2335 declarator,
2336 range: cpp_recovery_window(
2337 source,
2338 start,
2339 recovered_declaration_end(declarator),
2340 ),
2341 is_static,
2342 });
2343 is_static = false;
2344 }
2345 _ => break,
2346 }
2347 }
2348 declarations
2349}
2350
2351struct CollapsedMacroDeclarationRun {
2354 invocation_end: usize,
2357 region_end: usize,
2362}
2363
2364struct MacroInvocationTokens<'tree> {
2376 stack: Vec<Node<'tree>>,
2377 frontier: Option<Node<'tree>>,
2387}
2388
2389impl<'tree> MacroInvocationTokens<'tree> {
2390 fn new(node: Node<'tree>) -> Self {
2391 Self {
2392 stack: vec![node],
2393 frontier: Some(node),
2394 }
2395 }
2396}
2397
2398impl<'tree> Iterator for MacroInvocationTokens<'tree> {
2399 type Item = Node<'tree>;
2400
2401 fn next(&mut self) -> Option<Node<'tree>> {
2402 loop {
2403 let Some(node) = self.stack.pop() else {
2404 let sibling = self.frontier?.next_sibling()?;
2405 self.frontier = Some(sibling);
2406 self.stack.push(sibling);
2407 continue;
2408 };
2409 if node.child_count() == 0 {
2410 if node.kind() == "comment" || node.is_missing() {
2411 continue;
2412 }
2413 return Some(node);
2414 }
2415 let mut cursor = node.walk();
2416 let children = node.children(&mut cursor).collect::<Vec<_>>();
2417 self.stack.extend(children.into_iter().rev());
2418 }
2419 }
2420}
2421
2422fn collapsed_macro_declaration_run<'tree>(
2469 node: Node<'tree>,
2470 source: &str,
2471 ancestry: &ParentIndex<'tree>,
2472) -> Option<CollapsedMacroDeclarationRun> {
2473 let container_end = declaration_scope_container(node, ancestry)?.end_byte();
2474 let mut tokens = MacroInvocationTokens::new(node);
2475 let name = tokens.next()?;
2476 if !matches!(name.kind(), "identifier" | "type_identifier")
2477 || !cpp_export_macro_token(node_text(name, source))
2478 {
2479 return None;
2480 }
2481 if tokens.next()?.kind() != "(" {
2482 return None;
2483 }
2484 let mut depth = 1usize;
2485 let invocation_end = loop {
2486 let token = tokens.next()?;
2487 match token.kind() {
2488 "(" => depth += 1,
2489 ";" => return None,
2490 ")" => {
2491 depth -= 1;
2492 if depth == 0 {
2493 let semicolon = tokens.next()?;
2494 if semicolon.kind() != ";" {
2495 return None;
2496 }
2497 break semicolon.end_byte();
2498 }
2499 }
2500 _ => {}
2501 }
2502 };
2503 if invocation_end >= container_end {
2509 return None;
2510 }
2511 let region_end = if node.end_byte() > invocation_end {
2520 container_end
2521 } else {
2522 invocation_end
2523 };
2524 Some(CollapsedMacroDeclarationRun {
2525 invocation_end,
2526 region_end,
2527 })
2528}
2529
2530fn string_attribute_macro_member_declarators<'tree>(
2551 field: Node<'tree>,
2552 source: &str,
2553) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2554 if field.kind() != "field_declaration"
2555 || field
2556 .child_by_field_name("type")
2557 .is_none_or(|type_node| type_node.kind() != "type_identifier")
2558 {
2559 return None;
2560 }
2561 let declarator = field.child_by_field_name("declarator")?;
2562 if declarator.kind() != "parenthesized_declarator" {
2563 return None;
2564 }
2565 let opening = declarator.named_child(0)?;
2566 if opening.kind() != "ERROR"
2567 || opening
2568 .named_child(0)
2569 .is_none_or(|word| word.kind() != "identifier")
2570 {
2571 return None;
2572 }
2573 let declarations = stranded_declaration_run(declarator, source).declarations;
2574 (!declarations.is_empty()).then_some(declarations)
2575}
2576
2577fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2584 let Some(call) = (node.kind() == "expression_statement")
2585 .then(|| node.named_child(0))
2586 .flatten()
2587 .filter(|child| child.kind() == "call_expression")
2588 else {
2589 return false;
2590 };
2591 call.child_by_field_name("function")
2592 .is_some_and(|function| function.kind() == "identifier")
2593 && call
2594 .child_by_field_name("arguments")
2595 .is_some_and(|arguments| {
2596 let mut cursor = arguments.walk();
2597 arguments.named_child_count() > 0
2598 && arguments
2599 .named_children(&mut cursor)
2600 .all(|argument| argument.kind() == "string_literal")
2601 })
2602}
2603
2604fn cpp_access_label_constructor_call_start(
2617 node: Node<'_>,
2618 class_name: &str,
2619 source: &str,
2620) -> Option<usize> {
2621 if node.kind() != "labeled_statement" {
2622 return None;
2623 }
2624 let label = node.named_child(0)?;
2625 if label.kind() != "statement_identifier"
2626 || !matches!(
2627 node_text(label, source).trim(),
2628 "public" | "private" | "protected"
2629 )
2630 {
2631 return None;
2632 }
2633 let mut starts = Vec::new();
2634 let mut stack = vec![node];
2635 while let Some(current) = stack.pop() {
2636 if current.kind() == "call_expression"
2637 && current
2638 .child_by_field_name("function")
2639 .is_some_and(|function| {
2640 function.kind() == "identifier"
2641 && node_text(function, source).trim() == class_name
2642 })
2643 {
2644 starts.push(current.start_byte());
2645 }
2646 if current.kind() == "declaration"
2651 && current
2652 .child_by_field_name("type")
2653 .is_some_and(|type_node| {
2654 type_node.kind() == "type_identifier"
2655 && node_text(type_node, source).trim() == class_name
2656 })
2657 {
2658 starts.push(
2659 current
2660 .child_by_field_name("type")
2661 .expect("checked declaration type")
2662 .start_byte(),
2663 );
2664 }
2665 let mut cursor = current.walk();
2666 stack.extend(current.named_children(&mut cursor));
2667 }
2668 let [start] = starts.as_slice() else {
2669 return None;
2670 };
2671 Some(*start)
2672}
2673
2674fn cpp_declarator_function_definition<'tree>(
2678 declarator: Node<'tree>,
2679 ancestry: &ParentIndex<'tree>,
2680) -> Option<Node<'tree>> {
2681 let mut current = declarator;
2682 while let Some(parent) = ancestry.parent(current) {
2683 match parent.kind() {
2684 "function_definition" if parent.child_by_field_name("body").is_some() => {
2685 return Some(parent);
2686 }
2687 "pointer_declarator"
2688 | "reference_declarator"
2689 | "parenthesized_declarator"
2690 | "array_declarator" => current = parent,
2691 _ => return None,
2692 }
2693 }
2694 None
2695}
2696
2697fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2701 let mut current = node;
2702 while let Some(parent) = ancestry.parent(current) {
2703 if parent.kind() == "namespace_definition"
2704 && parent.child_by_field_name("body") == Some(current)
2705 {
2706 return true;
2707 }
2708 current = parent;
2709 }
2710 false
2711}
2712
2713pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2727 let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2728 let root = tree.root_node();
2729 let mut cursor = root.walk();
2730 let items = root
2731 .named_children(&mut cursor)
2732 .filter(|child| child.kind() != "comment")
2733 .collect::<Vec<_>>();
2734 let [item] = items.as_slice() else {
2735 return None;
2736 };
2737 if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2738 return None;
2739 }
2740 match item.kind() {
2741 "function_definition" => Some(item.child_by_field_name("body").is_some()),
2742 "declaration" | "field_declaration" => Some(false),
2743 _ => None,
2744 }
2745}
2746
2747pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2759 let ancestry = ParentIndex::unindexed();
2760 !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2761 || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2762}
2763
2764fn recover_exported_class_function_definition<'tree>(
2765 node: Node<'tree>,
2766 source: &str,
2767) -> Option<RecoveredFunctionDefinitionClass<'tree>> {
2768 if node.kind() != "function_definition" {
2769 return None;
2770 }
2771 if let Some(prefix) = node.prev_named_sibling()
2772 && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2773 && recovered.range.end_byte == node.end_byte()
2774 {
2775 return Some((
2776 node,
2777 recovered.name,
2778 recovered.raw_supertypes,
2779 recovered.base_facts,
2780 ));
2781 }
2782 let type_node = node.child_by_field_name("type")?;
2783 let declarator = node.child_by_field_name("declarator")?;
2784
2785 if matches!(
2786 type_node.kind(),
2787 "class_specifier" | "struct_specifier" | "union_specifier"
2788 ) {
2789 let type_name = type_node
2790 .child_by_field_name("name")
2791 .and_then(|name| direct_identifier_name(name, source));
2792 let exported_macro_type = type_name
2793 .as_ref()
2794 .is_some_and(|name| cpp_export_macro_token(name));
2795 if exported_macro_type {
2796 let mut cursor = node.walk();
2797 let errors_before_declarator = node
2798 .named_children(&mut cursor)
2799 .filter(|child| {
2800 child.kind() == "ERROR"
2801 && child.start_byte() >= type_node.end_byte()
2802 && child.end_byte() <= declarator.start_byte()
2803 })
2804 .collect::<Vec<_>>();
2805 if let Some(name) = errors_before_declarator
2806 .iter()
2807 .find_map(|error| displaced_exported_class_name(*error, source))
2808 {
2809 let base_node = errors_before_declarator
2810 .iter()
2811 .any(|error| malformed_inheritance_syntax(*error))
2812 .then_some(declarator);
2813 let raw_supertypes = base_node
2814 .and_then(|base| recovered_malformed_base_name(base, source))
2815 .map(|base| vec![base]);
2816 let base_facts = base_node
2817 .and_then(|base| {
2818 recovered_base_specifier(
2819 base,
2820 source,
2821 recovered_base_is_virtual(node, base, source),
2822 )
2823 })
2824 .map(|base| vec![base]);
2825 return Some((node, name, raw_supertypes, base_facts));
2826 }
2827 if errors_before_declarator
2828 .iter()
2829 .any(|error| malformed_inheritance_syntax(*error))
2830 {
2831 return None;
2832 }
2833 }
2834 if !exported_macro_type
2835 && let Some(name) = type_name
2836 && !cpp_export_macro_token(&name)
2837 && let Some(base) =
2838 recovered_postfix_export_macro_base(node, type_node, declarator, source)
2839 {
2840 let base_node =
2841 recovered_postfix_export_macro_base_node(node, type_node, declarator, source)?;
2842 let base_facts = Some(vec![recovered_base_specifier(
2843 base_node,
2844 source,
2845 recovered_base_is_virtual(node, base_node, source),
2846 )?]);
2847 return Some((node, name, Some(vec![base]), base_facts));
2848 }
2849 let declarator_name = direct_identifier_name(declarator, source).or_else(|| {
2854 (declarator.kind() == "macro_modifier_declarator")
2855 .then(|| node.child_by_field_name("body"))
2856 .flatten()
2857 .filter(|body| body.kind() == "compound_statement")
2858 .and_then(|body| recovered_export_head_name(node, body, source))
2859 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
2860 });
2861 if let Some(name) = declarator_name
2862 && exported_macro_type
2863 && !cpp_export_macro_token(&name)
2864 {
2865 let base_node = exported_macro_type
2866 .then(|| recovered_single_base_after_declarator_node(node, declarator, source))
2867 .flatten();
2868 let raw_supertypes = base_node
2869 .and_then(|base| recovered_malformed_base_name(base, source))
2870 .map(|base| vec![base]);
2871 let base_facts = base_node
2872 .and_then(|base| {
2873 recovered_base_specifier(
2874 base,
2875 source,
2876 recovered_base_is_virtual(node, base, source),
2877 )
2878 })
2879 .map(|base| vec![base]);
2880 return Some((node, name, raw_supertypes, base_facts));
2881 }
2882 if declarator.kind() == "parenthesized_declarator"
2883 && type_node
2884 .child_by_field_name("name")
2885 .and_then(|name| direct_identifier_name(name, source))
2886 .is_some_and(|name| cpp_export_macro_token(&name))
2887 {
2888 if let Some((name, base_node)) =
2889 recovered_function_like_export_class_owner_node(declarator, source)
2890 {
2891 return Some((
2892 node,
2893 name,
2894 Some(vec![recovered_malformed_base_name(base_node, source)?]),
2895 Some(vec![recovered_base_specifier(
2896 base_node,
2897 source,
2898 recovered_base_is_virtual(node, base_node, source),
2899 )?]),
2900 ));
2901 }
2902 let body_start = node
2903 .child_by_field_name("body")
2904 .map(|body| body.start_byte())
2905 .unwrap_or(node.end_byte());
2906 let mut cursor = node.walk();
2907 if let Some(name) = node
2908 .named_children(&mut cursor)
2909 .filter(|child| {
2910 child.kind() == "ERROR"
2911 && child.start_byte() >= declarator.end_byte()
2912 && child.end_byte() <= body_start
2913 })
2914 .find_map(|error| declarator_name_from_node(error, source))
2915 {
2916 return Some((node, name, None, None));
2917 }
2918 if let Some(body) = node
2925 .child_by_field_name("body")
2926 .filter(|body| body.kind() == "compound_statement")
2927 && let Some(name_node) =
2928 export_head_name_from_tokens(&export_head_tokens(declarator, None), source)
2929 {
2930 let name = normalize_cpp_whitespace(node_text(name_node, source));
2931 if !name.is_empty() {
2932 let (bases, base_facts) = recovered_export_head_base_data(
2933 declarator,
2934 name_node.end_byte(),
2935 body.start_byte(),
2936 source,
2937 );
2938 return Some((
2939 node,
2940 name,
2941 (!bases.is_empty()).then_some(bases),
2942 (!base_facts.is_empty()).then_some(base_facts),
2943 ));
2944 }
2945 }
2946 }
2947 }
2948
2949 if type_node.kind() == "macro_type_specifier"
2955 && declarator.kind() == "macro_modifier_declarator"
2956 && type_node
2957 .child_by_field_name("name")
2958 .and_then(|name| direct_identifier_name(name, source))
2959 .is_some_and(|name| cpp_export_macro_token(&name))
2960 {
2961 let body = node
2962 .child_by_field_name("body")
2963 .filter(|body| body.kind() == "compound_statement")?;
2964 let tokens = export_head_tokens(node, Some(body));
2965 let has_class_keyword = tokens.iter().any(|token| {
2966 matches!(token.kind(), "identifier" | "type_identifier")
2967 && matches!(node_text(*token, source), "class" | "struct" | "union")
2968 });
2969 if !has_class_keyword {
2970 return None;
2971 }
2972 let name_node = recovered_export_head_name(node, body, source)?;
2973 let name = normalize_cpp_whitespace(node_text(name_node, source));
2974 if name.is_empty() || cpp_export_macro_token(&name) {
2975 return None;
2976 }
2977 return Some((node, name, None, None));
2978 }
2979
2980 let declarator_text = direct_identifier_name(declarator, source)?;
2981 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2982 return None;
2983 }
2984 class_identifier_before_body(node, source).map(|name| (node, name, None, None))
2985}
2986
2987fn recovered_function_like_export_class_owner_node<'tree>(
2988 declarator: Node<'tree>,
2989 source: &str,
2990) -> Option<(String, Node<'tree>)> {
2991 if declarator.kind() != "parenthesized_declarator" {
2992 return None;
2993 }
2994 let mut cursor = declarator.walk();
2995 let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2996 let [prefix, base] = children.as_slice() else {
2997 return None;
2998 };
2999 if prefix.kind() != "ERROR"
3000 || !matches!(
3001 base.kind(),
3002 "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
3003 )
3004 {
3005 return None;
3006 }
3007 let mut identifiers = Vec::new();
3008 let mut prefix_cursor = prefix.walk();
3009 let mut parts = prefix
3010 .named_children(&mut prefix_cursor)
3011 .collect::<Vec<_>>();
3012 while let Some(child) = parts.pop() {
3016 match child.kind() {
3017 "number_literal" | "string_literal" | "char_literal" => {}
3018 "identifier" | "type_identifier" => {
3019 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3020 }
3021 "macro_modifier" => {
3022 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3023 }
3024 "macro_modifier_declarator" => {
3025 parts.extend(child.named_children(&mut child.walk()));
3026 }
3027 _ => return None,
3028 }
3029 }
3030 identifiers.reverse();
3031 let name = match identifiers.as_slice() {
3032 [name] => name.clone(),
3033 [name, final_token] if final_token == "final" => name.clone(),
3034 _ => return None,
3035 };
3036 if name.is_empty() || cpp_export_macro_token(&name) {
3037 return None;
3038 }
3039 Some((name, *base))
3040}
3041
3042fn recovered_export_head_base_data(
3056 node: Node<'_>,
3057 after: usize,
3058 before: usize,
3059 source: &str,
3060) -> (Vec<String>, Vec<CppBaseSpecifierFact>) {
3061 let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
3062 let mut base_nodes = Vec::new();
3063 let mut cursor = node.walk();
3064 for child in node.named_children(&mut cursor) {
3065 if child.kind() == "ERROR" {
3066 let mut error_cursor = child.walk();
3067 base_nodes.extend(
3068 child
3069 .named_children(&mut error_cursor)
3070 .filter(within)
3071 .filter_map(|part| {
3072 recovered_malformed_base_name(part, source).map(|_| (part, child))
3073 }),
3074 );
3075 } else if child.kind() == "macro_modifier_declarator" {
3076 let mut current = child;
3079 while let Some(inner) = current.child_by_field_name("declarator") {
3080 if inner.kind() == "macro_modifier_declarator" {
3081 current = inner;
3082 continue;
3083 }
3084 if within(&inner) && recovered_malformed_base_name(inner, source).is_some() {
3085 base_nodes.push((inner, child));
3086 }
3087 break;
3088 }
3089 } else if within(&child) && recovered_malformed_base_name(child, source).is_some() {
3090 base_nodes.push((child, node));
3091 }
3092 }
3093 let mut bases = Vec::new();
3094 let mut facts = Vec::new();
3095 for (base, container) in base_nodes {
3096 let Some(name) = recovered_malformed_base_name(base, source) else {
3097 continue;
3098 };
3099 if matches!(
3100 name.as_str(),
3101 "final" | "public" | "protected" | "private" | "virtual"
3102 ) {
3103 continue;
3104 }
3105 bases.push(name);
3106 if let Some(fact) = recovered_base_specifier(
3107 base,
3108 source,
3109 recovered_base_is_virtual(container, base, source),
3110 ) {
3111 facts.push(fact);
3112 }
3113 }
3114 (bases, facts)
3115}
3116
3117fn recovered_export_head_bases(
3118 node: Node<'_>,
3119 after: usize,
3120 before: usize,
3121 source: &str,
3122) -> Vec<String> {
3123 recovered_export_head_base_data(node, after, before, source).0
3124}
3125
3126fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
3131 if token.is_named() {
3132 token.kind() == "identifier" && node_text(token, source) == "final"
3133 } else {
3134 token.kind() == "final"
3135 }
3136}
3137
3138fn recovered_export_head_name<'tree>(
3151 node: Node<'tree>,
3152 tail: Node<'tree>,
3153 source: &str,
3154) -> Option<Node<'tree>> {
3155 export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
3156}
3157
3158fn recovered_export_pair_head_name<'tree>(
3165 prefix: Node<'tree>,
3166 sibling: Node<'tree>,
3167 tail: Node<'tree>,
3168 source: &str,
3169) -> Option<Node<'tree>> {
3170 let mut tokens = export_head_tokens(prefix, None);
3171 tokens.extend(export_head_tokens(sibling, Some(tail)));
3172 export_head_name_from_tokens(&tokens, source)
3173}
3174
3175fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
3182 let mut tokens = Vec::new();
3183 let mut stack = Vec::new();
3184 let mut cursor = node.walk();
3185 stack.extend(
3186 node.children(&mut cursor)
3187 .collect::<Vec<_>>()
3188 .into_iter()
3189 .rev(),
3190 );
3191 while let Some(child) = stack.pop() {
3192 if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
3193 break;
3194 }
3195 if matches!(
3196 child.kind(),
3197 "ERROR" | "macro_modifier" | "macro_modifier_declarator"
3198 ) {
3199 let mut fragment_cursor = child.walk();
3200 stack.extend(
3201 child
3202 .children(&mut fragment_cursor)
3203 .collect::<Vec<_>>()
3204 .into_iter()
3205 .rev(),
3206 );
3207 } else {
3208 tokens.push(child);
3209 }
3210 }
3211 tokens
3212}
3213
3214fn export_head_name_from_tokens<'tree>(
3216 tokens: &[Node<'tree>],
3217 source: &str,
3218) -> Option<Node<'tree>> {
3219 let end = tokens
3220 .iter()
3221 .position(|token| {
3222 recovered_export_head_final(*token, source)
3223 || (!token.is_named() && token.kind() == ":")
3224 })
3225 .unwrap_or(tokens.len());
3226 export_head_last_identifier(tokens[..end].iter().copied())
3227}
3228
3229fn export_head_last_identifier<'tree>(
3230 candidates: impl IntoIterator<Item = Node<'tree>>,
3231) -> Option<Node<'tree>> {
3232 candidates
3233 .into_iter()
3234 .filter(|candidate| {
3235 candidate.is_named()
3236 && !candidate.is_missing()
3237 && matches!(
3238 candidate.kind(),
3239 "identifier" | "type_identifier" | "field_identifier"
3240 )
3241 })
3242 .last()
3243}
3244
3245fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
3248 let mut cursor = declaration.walk();
3249 declaration
3250 .named_children(&mut cursor)
3251 .find(|child| child.kind() == "init_declarator")
3252}
3253
3254fn recovered_export_declaration_tail<'tree>(
3262 declaration: Node<'tree>,
3263 head_end: usize,
3264 source: &str,
3265) -> Option<(Vec<String>, Option<Vec<CppBaseSpecifierFact>>, Node<'tree>)> {
3266 let init = recovered_export_init_declarator(declaration)?;
3267 let body = init.child_by_field_name("value")?;
3268 if body.kind() != "initializer_list" {
3269 return None;
3270 }
3271 let (mut bases, mut facts) =
3272 recovered_export_head_base_data(declaration, head_end, init.start_byte(), source);
3273 let (more_bases, more_facts) =
3274 recovered_export_head_base_data(init, init.start_byte(), body.start_byte(), source);
3275 bases.extend(more_bases);
3276 facts.extend(more_facts);
3277 Some((bases, (!facts.is_empty()).then_some(facts), body))
3278}
3279
3280fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
3294 match node.kind() {
3295 "ERROR" => {
3296 let Some(class_node) = first_class_like_child(node) else {
3297 return false;
3298 };
3299 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
3300 return false;
3301 }
3302 if class_node
3303 .child_by_field_name("name")
3304 .and_then(|name| direct_identifier_name(name, source))
3305 .is_none()
3306 {
3307 return false;
3308 }
3309 class_node
3310 .next_sibling()
3311 .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
3312 }
3313 "declaration" => {
3314 let mut cursor = node.walk();
3315 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3316 let Some(keyword) = children
3322 .iter()
3323 .copied()
3324 .flat_map(|child| {
3325 let mut cursor = child.walk();
3326 if child.kind() == "ERROR" {
3327 child.named_children(&mut cursor).collect::<Vec<_>>()
3328 } else {
3329 vec![child]
3330 }
3331 })
3332 .find(|child| {
3333 child.kind() == "identifier"
3334 && matches!(node_text(*child, source), "class" | "struct" | "union")
3335 })
3336 else {
3337 return false;
3338 };
3339 children.last().is_some_and(|init| {
3340 init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
3341 let mut cursor = init.walk();
3342 let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
3343 matches!(parts.as_slice(), [macro_name, arguments]
3344 if matches!(macro_name.kind(), "identifier" | "type_identifier")
3345 && arguments.kind() == "argument_list")
3346 }
3347 })
3348 }
3349 _ => false,
3350 }
3351}
3352
3353fn recover_function_like_export_class_pair(
3354 node: Node<'_>,
3355 source: &str,
3356) -> Option<RecoveredFunctionLikeExportClassPair> {
3357 if !is_function_like_export_class_head(node, source) {
3358 return None;
3359 }
3360 let sibling = node.next_named_sibling()?;
3361 let (name, raw_supertypes, base_facts, body) = match sibling.kind() {
3362 "compound_statement" => (
3368 recovered_export_pair_head_name(node, sibling, sibling, source)
3369 .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
3370 None,
3371 None,
3372 sibling,
3373 ),
3374 "expression_statement" => {
3375 let compound = sibling.named_child(0)?;
3376 if compound.kind() != "compound_literal_expression" {
3377 return None;
3378 }
3379 let body = compound.child_by_field_name("value")?;
3380 if body.kind() != "initializer_list" {
3381 return None;
3382 }
3383 (
3384 compound
3385 .child_by_field_name("type")
3386 .and_then(|name| direct_identifier_name(name, source))?,
3387 None,
3388 None,
3389 body,
3390 )
3391 }
3392 "labeled_statement" => {
3393 let label = sibling.child_by_field_name("label")?;
3394 if label.kind() != "statement_identifier" {
3395 return None;
3396 }
3397 let name = normalize_cpp_whitespace(node_text(label, source));
3398 let declaration = sibling
3399 .named_children(&mut sibling.walk())
3400 .find(|child| child.kind() == "declaration")?;
3401 let access = declaration.child_by_field_name("type")?;
3402 if !matches!(
3403 node_text(access, source),
3404 "public" | "protected" | "private"
3405 ) {
3406 return None;
3407 }
3408 let (bases, base_facts, body) =
3409 recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
3410 (name, (!bases.is_empty()).then_some(bases), base_facts, body)
3411 }
3412 "function_definition" => {
3418 let body = sibling.child_by_field_name("body")?;
3419 if body.kind() != "compound_statement" {
3420 return None;
3421 }
3422 let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
3423 let (bases, base_facts) = recovered_export_head_base_data(
3424 sibling,
3425 name_node.end_byte(),
3426 body.start_byte(),
3427 source,
3428 );
3429 (
3430 normalize_cpp_whitespace(node_text(name_node, source)),
3431 (!bases.is_empty()).then_some(bases),
3432 (!base_facts.is_empty()).then_some(base_facts),
3433 body,
3434 )
3435 }
3436 "declaration" => {
3440 let init = recovered_export_init_declarator(sibling)?;
3441 let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
3442 let (bases, base_facts, body) =
3443 recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
3444 (
3445 normalize_cpp_whitespace(node_text(name_node, source)),
3446 (!bases.is_empty()).then_some(bases),
3447 base_facts,
3448 body,
3449 )
3450 }
3451 _ => return None,
3452 };
3453 debug_assert!(
3454 !name.is_empty(),
3455 "a recovered class head names its class by an identifier token"
3456 );
3457 let range = Range {
3458 start_byte: node.start_byte(),
3459 end_byte: sibling.end_byte(),
3460 start_line: node.start_position().row + 1,
3461 end_line: sibling.end_position().row + 1,
3462 };
3463 Some(RecoveredFunctionLikeExportClassPair {
3464 name,
3465 raw_supertypes,
3466 base_facts,
3467 range,
3468 fragmented_body: recovered_fragmented_export_body(body, range)?,
3469 })
3470}
3471
3472fn recover_embedded_function_like_export_classes(
3479 node: Node<'_>,
3480 source: &str,
3481) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
3482 if !node.is_error() {
3483 return Vec::new();
3484 }
3485
3486 let mut nodes = Vec::new();
3487 let mut class_tokens = Vec::new();
3488 let mut stack = vec![node];
3489 while let Some(current) = stack.pop() {
3490 nodes.push(current);
3491 if !current.is_named() && current.kind() == "class" {
3495 class_tokens.push(current);
3496 }
3497 push_children_reversed(current, &mut stack);
3498 }
3499 if class_tokens.is_empty() {
3500 return Vec::new();
3501 }
3502 class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3503 nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3504
3505 let language = node.language();
3508 let class_kind = NodeKindIds::new(&language, "class");
3509 let identifier_kinds = [
3510 NodeKindIds::new(&language, "identifier"),
3511 NodeKindIds::new(&language, "type_identifier"),
3512 NodeKindIds::new(&language, "field_identifier"),
3513 ];
3514 let argument_list_kind = NodeKindIds::new(&language, "argument_list");
3515 let colon_kind = NodeKindIds::new(&language, ":");
3516 let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
3517
3518 let mut recovered = Vec::new();
3519 for class_token in class_tokens {
3520 debug_assert!(class_kind.matches(class_token));
3521 let row = class_token.start_position().row;
3522 let is_identifier = |candidate: &Node<'_>| {
3528 !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
3529 };
3530 let Some(macro_name) = nodes.iter().copied().find(|candidate| {
3531 candidate.start_byte() >= class_token.end_byte()
3532 && candidate.start_position().row == row
3533 && is_identifier(candidate)
3534 }) else {
3535 continue;
3536 };
3537 let Some(arguments) = nodes.iter().copied().find(|candidate| {
3538 argument_list_kind.matches(*candidate)
3539 && candidate.start_byte() >= macro_name.end_byte()
3540 && candidate.start_position().row == row
3541 }) else {
3542 continue;
3543 };
3544 if nodes.iter().any(|candidate| {
3545 is_identifier(candidate)
3546 && candidate.start_byte() >= macro_name.end_byte()
3547 && candidate.end_byte() <= arguments.start_byte()
3548 }) {
3549 continue;
3550 }
3551 let Some(head_end) = nodes.iter().copied().find(|candidate| {
3552 candidate.start_byte() >= arguments.end_byte()
3553 && (recovered_export_head_final(*candidate, source)
3554 || (!candidate.is_named() && colon_kind.matches(*candidate)))
3555 }) else {
3556 continue;
3557 };
3558 let Some(name_node) =
3559 export_head_last_identifier(nodes.iter().copied().filter(|candidate| {
3560 is_identifier(candidate)
3561 && candidate.start_byte() >= arguments.end_byte()
3562 && candidate.end_byte() <= head_end.start_byte()
3563 && candidate.start_position().row == row
3564 }))
3565 else {
3566 continue;
3567 };
3568 let name = normalize_cpp_whitespace(node_text(name_node, source));
3569 let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
3570 field_initializer_kind.matches(*candidate)
3571 && candidate.start_byte() >= name_node.end_byte()
3572 && candidate
3573 .child_by_field_name("field")
3574 .or_else(|| candidate.named_child(0))
3575 .is_some()
3576 && candidate
3577 .child_by_field_name("value")
3578 .or_else(|| {
3579 let mut cursor = candidate.walk();
3580 candidate
3581 .named_children(&mut cursor)
3582 .find(|child| child.kind() == "initializer_list")
3583 })
3584 .is_some_and(|value| value.kind() == "initializer_list")
3585 }) else {
3586 continue;
3587 };
3588 let has_access = nodes.iter().copied().any(|candidate| {
3589 candidate.start_byte() >= name_node.end_byte()
3590 && candidate.end_byte() <= base_initializer.start_byte()
3591 && matches!(
3592 normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
3593 "public" | "protected" | "private"
3594 )
3595 });
3596 if !has_access {
3597 continue;
3598 }
3599 let Some(base_node) = base_initializer
3600 .child_by_field_name("field")
3601 .or_else(|| base_initializer.named_child(0))
3602 else {
3603 continue;
3604 };
3605 let Some(base) = recovered_malformed_base_name(base_node, source) else {
3606 continue;
3607 };
3608 let body = base_initializer
3609 .child_by_field_name("value")
3610 .or_else(|| {
3611 let mut cursor = base_initializer.walk();
3612 base_initializer
3613 .named_children(&mut cursor)
3614 .find(|child| child.kind() == "initializer_list")
3615 })
3616 .expect("initializer-list value checked above");
3617 let range = Range {
3618 start_byte: class_token.start_byte(),
3619 end_byte: body.end_byte(),
3620 start_line: class_token.start_position().row + 1,
3621 end_line: body.end_position().row + 1,
3622 };
3623 if recovered
3624 .iter()
3625 .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
3626 existing.name == name && existing.range == range
3627 })
3628 {
3629 continue;
3630 }
3631 let Some(base_fact) = recovered_base_specifier(base_node, source, false) else {
3632 continue;
3633 };
3634 recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
3635 name,
3636 range,
3637 raw_supertypes: vec![base],
3638 base_facts: vec![base_fact],
3639 fragmented_body: match recovered_fragmented_export_body(body, range) {
3640 Some(fragmented) => fragmented,
3641 None => continue,
3642 },
3643 });
3644 }
3645 recovered
3646}
3647
3648fn lifted_function_like_export_class_namespace<'tree>(
3649 node: Node<'tree>,
3650 source: &str,
3651 ancestry: &ParentIndex<'tree>,
3652) -> Option<String> {
3653 let mut anchor = node;
3659 let parent = loop {
3660 let parent = ancestry.parent(anchor)?;
3661 if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
3662 break parent;
3663 }
3664 anchor = parent;
3665 };
3666 let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
3667 sibling.start_byte() > anchor.end_byte()
3668 && sibling.kind() == "ERROR"
3669 && sibling.named_child_count() == 0
3670 && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
3671 });
3672 if !has_later_close {
3673 return None;
3674 }
3675 let candidates = parent
3676 .named_children(&mut parent.walk())
3677 .filter(|sibling| {
3678 sibling.kind() == "namespace_definition"
3679 && sibling.has_error()
3680 && sibling.end_byte() < anchor.start_byte()
3681 })
3682 .filter_map(|namespace| {
3683 namespace
3684 .child_by_field_name("name")
3685 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
3686 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
3687 })
3688 .collect::<Vec<_>>();
3689 let [namespace] = candidates.as_slice() else {
3690 return None;
3691 };
3692 Some(namespace.clone())
3693}
3694
3695pub(crate) fn recovered_function_like_export_class_pair_has_body(
3696 node: Node<'_>,
3697 source: &str,
3698 identifier: &str,
3699 range: &Range,
3700) -> bool {
3701 recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
3702 recovered.name == identifier
3703 && recovered.range.start_byte == range.start_byte
3704 && recovered.range.end_byte == range.end_byte
3705 })
3706}
3707
3708fn recovered_exported_class_has_body(
3709 node: Node<'_>,
3710 source: &str,
3711 expected_name: &str,
3712) -> Option<bool> {
3713 match node.kind() {
3714 "function_definition" => {
3715 let (class_node, name, _, _) =
3716 recover_exported_class_function_definition(node, source)?;
3717 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3718 }
3719 "declaration" | "field_declaration" => {
3720 let recovered = recover_exported_class_declaration(node, source)?;
3721 (recovered.name == expected_name).then(|| recovered.body.is_some())
3722 }
3723 _ => None,
3724 }
3725}
3726
3727#[derive(Default)]
3743pub struct CppRecoveredExportClassIndex {
3744 by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3745}
3746
3747impl CppRecoveredExportClassIndex {
3748 pub fn build(root: Node<'_>, source: &str) -> Self {
3749 let mut by_error_node: HashMap<
3750 (usize, usize),
3751 Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3752 > = HashMap::default();
3753 let mut cursor = root.walk();
3756 let mut stack = vec![root];
3757 while let Some(node) = stack.pop() {
3758 if node.is_error() {
3759 let recovered = recover_embedded_function_like_export_classes(node, source);
3760 if !recovered.is_empty() {
3761 by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3762 }
3763 }
3764 stack.extend(node.named_children(&mut cursor));
3765 }
3766 Self { by_error_node }
3767 }
3768
3769 pub fn approximate_size(&self) -> usize {
3771 self.by_error_node
3772 .values()
3773 .fold(0usize, |total, recovered| {
3774 recovered.iter().fold(
3775 total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3776 |acc, class| {
3777 acc.saturating_add(std::mem::size_of::<
3778 RecoveredEmbeddedFunctionLikeExportClass,
3779 >())
3780 .saturating_add(class.name.len())
3781 .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3782 },
3783 )
3784 })
3785 }
3786
3787 fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3788 self.by_error_node
3789 .get(&(node.start_byte(), node.end_byte()))
3790 .is_some_and(|recovered| {
3791 recovered.iter().any(|class| {
3792 class.name == identifier
3793 && class.range.start_byte == range.start_byte
3794 && class.range.end_byte == range.end_byte
3795 })
3796 })
3797 }
3798}
3799
3800#[cfg(any(test, feature = "test-support"))]
3807thread_local! {
3808 static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3809 const { std::cell::Cell::new(0) };
3810}
3811
3812#[cfg(any(test, feature = "test-support"))]
3816#[doc(hidden)]
3817pub fn recovered_class_body_node_visits_for_test() -> usize {
3818 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3819}
3820
3821#[cfg(any(test, feature = "test-support"))]
3823#[doc(hidden)]
3824pub fn reset_recovered_class_body_node_visits_for_test() {
3825 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3826}
3827
3828#[cfg(any(test, feature = "test-support"))]
3829fn record_recovered_class_body_visit() {
3830 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3831}
3832
3833#[cfg(not(any(test, feature = "test-support")))]
3834fn record_recovered_class_body_visit() {}
3835
3836pub(crate) fn recovered_class_body_at(
3859 recovered_export_classes: &CppRecoveredExportClassIndex,
3860 root: Node<'_>,
3861 source: &str,
3862 identifier: &str,
3863 range: &Range,
3864) -> Option<bool> {
3865 let covers_range_start = |node: &Node<'_>| {
3866 node.start_byte() <= range.start_byte
3867 && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3868 };
3869 let mut stack = vec![root];
3870 let mut saw_forward = false;
3871 while let Some(node) = stack.pop() {
3872 record_recovered_class_body_visit();
3873 if (node.start_byte() == range.start_byte
3877 && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3878 || recovered_export_classes.claims(node, identifier, range)
3879 || (node.start_byte() == range.start_byte
3880 && recovered_fragmented_class_has_body(node, source, identifier, range))
3881 {
3882 return Some(true);
3883 }
3884 if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3885 return Some(true);
3886 }
3887 if node.start_byte() <= range.start_byte
3894 && range.start_byte < node.end_byte()
3895 && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3896 {
3897 if has_body {
3898 return Some(true);
3899 }
3900 saw_forward = true;
3901 continue;
3902 }
3903 let mut cursor = node.walk();
3904 stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3905 }
3906 saw_forward.then_some(false)
3907}
3908
3909fn recovered_collapsed_aggregate_has_body(
3922 node: Node<'_>,
3923 source: &str,
3924 identifier: &str,
3925 range: &Range,
3926) -> bool {
3927 let claims = |head: &CppCollapsedAggregateHead<'_>| {
3928 head.key.start_byte() == range.start_byte
3929 && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3930 };
3931 if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3932 return true;
3933 }
3934 if !node.is_error() {
3935 return false;
3936 }
3937 let mut cursor = node.walk();
3938 let children = node.children(&mut cursor).collect::<Vec<_>>();
3939 children
3940 .iter()
3941 .enumerate()
3942 .filter(|(_, child)| {
3943 child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3944 })
3945 .any(|(index, _)| {
3946 cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3947 })
3948}
3949
3950pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3958 if !matches!(
3959 node.kind(),
3960 "qualified_identifier" | "scoped_type_identifier" | "template_type"
3961 ) {
3962 return false;
3963 }
3964 if let Some(function) = node.parent().filter(|parent| {
3965 parent.kind() == "function_definition"
3966 && parent
3967 .child_by_field_name("declarator")
3968 .is_some_and(|declarator| same_node(declarator, node))
3969 }) {
3970 return recover_exported_class_function_definition(function, source)
3971 .is_some_and(|(_, _, raw_supertypes, _)| raw_supertypes.is_some());
3972 }
3973 let Some(initializer) = node.parent().filter(|parent| {
3974 parent.kind() == "init_declarator"
3975 && parent
3976 .child_by_field_name("declarator")
3977 .is_some_and(|declarator| same_node(declarator, node))
3978 }) else {
3979 return false;
3980 };
3981 initializer
3982 .parent()
3983 .filter(|parent| parent.kind() == "declaration")
3984 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3985 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3986}
3987
3988struct CppSentinelReparsedClass<'tree> {
3994 declaration_node: Node<'tree>,
3995 name: String,
3996 body: Node<'tree>,
3997 raw_supertypes: Option<Vec<String>>,
3998 base_facts: Option<Vec<CppBaseSpecifierFact>>,
3999}
4000
4001fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
4002 let mut cursor = root.walk();
4003 root.named_children(&mut cursor)
4004 .find(|child| child.kind() != "comment")
4005 .filter(|child| child.kind() == "template_declaration")
4006}
4007
4008fn cpp_sentinel_reparsed_class<'tree>(
4009 root: Node<'tree>,
4010 template_node: Option<Node<'tree>>,
4011 source: &str,
4012 ancestry: &ParentIndex<'tree>,
4013) -> Option<CppSentinelReparsedClass<'tree>> {
4014 let container = template_node.unwrap_or(root);
4015 let mut cursor = container.walk();
4016 for child in container.named_children(&mut cursor) {
4017 if matches!(
4018 child.kind(),
4019 "class_specifier" | "struct_specifier" | "union_specifier"
4020 ) {
4021 let name = class_like_name(child, source, ancestry)?;
4022 let body = cpp_body_node(child)?;
4023 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
4024 .then(|| extract_cpp_supertypes(child, source));
4025 let base_facts = matches!(child.kind(), "class_specifier" | "struct_specifier")
4026 .then(|| extract_cpp_base_facts(child, source));
4027 return Some(CppSentinelReparsedClass {
4028 declaration_node: child,
4029 name,
4030 body,
4031 raw_supertypes,
4032 base_facts,
4033 });
4034 }
4035 if child.kind() == "declaration"
4036 && let Some(class_node) = first_class_like_child(child)
4037 {
4038 let name = class_like_name(class_node, source, ancestry)?;
4039 let body = cpp_body_node(class_node)?;
4040 let raw_supertypes =
4041 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4042 .then(|| extract_cpp_supertypes(class_node, source));
4043 let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4044 .then(|| extract_cpp_base_facts(class_node, source));
4045 return Some(CppSentinelReparsedClass {
4046 declaration_node: class_node,
4047 name,
4048 body,
4049 raw_supertypes,
4050 base_facts,
4051 });
4052 }
4053 if child.kind() == "function_definition"
4058 && let Some(class_node) = first_class_like_child(child)
4059 && let Some(body) = cpp_body_node(class_node)
4060 && let Some(name) = class_like_name(class_node, source, ancestry)
4061 {
4062 let raw_supertypes =
4063 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4064 .then(|| extract_cpp_supertypes(class_node, source));
4065 let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4066 .then(|| extract_cpp_base_facts(class_node, source));
4067 return Some(CppSentinelReparsedClass {
4068 declaration_node: class_node,
4069 name,
4070 body,
4071 raw_supertypes,
4072 base_facts,
4073 });
4074 }
4075 if child.kind() == "function_definition"
4076 && let Some((_, name, raw_supertypes, base_facts)) =
4077 recover_exported_class_function_definition(child, source)
4078 {
4079 let body = cpp_body_node(child)?;
4080 return Some(CppSentinelReparsedClass {
4081 declaration_node: child,
4082 name,
4083 body,
4084 raw_supertypes,
4085 base_facts,
4086 });
4087 }
4088 }
4089 None
4090}
4091
4092fn recovered_postfix_export_macro_base(
4093 node: Node<'_>,
4094 type_node: Node<'_>,
4095 declarator: Node<'_>,
4096 source: &str,
4097) -> Option<String> {
4098 recovered_postfix_export_macro_base_node(node, type_node, declarator, source)
4099 .and_then(|base| recovered_malformed_base_name(base, source))
4100}
4101
4102fn recovered_postfix_export_macro_base_node<'tree>(
4103 node: Node<'tree>,
4104 type_node: Node<'tree>,
4105 declarator: Node<'tree>,
4106 source: &str,
4107) -> Option<Node<'tree>> {
4108 let mut cursor = node.walk();
4109 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
4110 child.kind() == "ERROR"
4111 && child.start_byte() >= type_node.end_byte()
4112 && child.end_byte() <= declarator.start_byte()
4113 && postfix_export_macro_inheritance(*child, source)
4114 });
4115 malformed_clauses.next()?;
4116 if malformed_clauses.next().is_some() {
4117 return None;
4118 }
4119 Some(declarator)
4120}
4121
4122fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
4123 let mut macro_count = 0;
4124 let mut colon_count = 0;
4125 let mut access_count = 0;
4126 for index in 0..node.child_count() {
4127 let Some(child) = node.child(index) else {
4128 return false;
4129 };
4130 match child.kind() {
4131 "identifier" | "type_identifier" if child.is_named() => {
4132 let candidate = normalize_cpp_whitespace(node_text(child, source));
4133 if !cpp_export_macro_token(&candidate) {
4134 return false;
4135 }
4136 macro_count += 1;
4137 }
4138 ":" if !child.is_named() => colon_count += 1,
4139 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
4140 _ => return false,
4141 }
4142 }
4143 macro_count == 1 && colon_count == 1 && access_count == 1
4144}
4145
4146fn recovered_single_base_after_declarator_node<'tree>(
4147 node: Node<'tree>,
4148 declarator: Node<'tree>,
4149 source: &str,
4150) -> Option<Node<'tree>> {
4151 let body_start = node
4152 .child_by_field_name("body")
4153 .map(|body| body.start_byte())
4154 .unwrap_or(node.end_byte());
4155 let mut cursor = node.walk();
4156 let mut bases = node
4157 .named_children(&mut cursor)
4158 .filter(|child| {
4159 child.kind() == "ERROR"
4160 && child.start_byte() >= declarator.end_byte()
4161 && child.end_byte() <= body_start
4162 })
4163 .filter_map(|error| {
4164 displaced_exported_class_name(error, source)?;
4165 let mut error_cursor = error.walk();
4166 error
4167 .named_children(&mut error_cursor)
4168 .find(|child| recovered_malformed_base_name(*child, source).is_some())
4169 });
4170 let base = bases.next()?;
4171 bases.next().is_none().then_some(base)
4172}
4173
4174fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
4175 (0..node.child_count()).any(|index| {
4176 node.child(index).is_some_and(|child| {
4177 child.kind() == ":" || cpp_is_inheritance_access_specifier(child.kind())
4178 })
4179 })
4180}
4181
4182fn cpp_is_inheritance_access_specifier(name: &str) -> bool {
4183 matches!(name, "public" | "protected" | "private")
4184}
4185
4186pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
4187 recover_exported_class_function_definition(node, source).is_some()
4188}
4189
4190fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
4191 matches!(
4192 kind,
4193 "ERROR"
4194 | "preproc_if"
4195 | "preproc_ifdef"
4196 | "preproc_ifndef"
4197 | "preproc_else"
4198 | "preproc_elif"
4199 ) || (kind == "labeled_statement" && in_class_scope)
4200}
4201
4202pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
4203 if node.kind() != "declaration" {
4204 return false;
4205 }
4206 let mut ancestor = node.parent();
4207 while let Some(container) = ancestor {
4208 match container.kind() {
4209 "compound_statement" => {
4210 return container.parent().is_some_and(|class_container| {
4211 is_recovered_exported_class_container(class_container, source)
4212 });
4213 }
4214 "template_declaration" | "linkage_specification" | "declaration_list" => {}
4217 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
4218 _ => return false,
4219 }
4220 ancestor = container.parent();
4221 }
4222 false
4223}
4224
4225fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
4226 let body_start = node
4227 .child_by_field_name("body")
4228 .map(|body| body.start_byte())
4229 .unwrap_or(node.end_byte());
4230 let mut stack = Vec::new();
4231 for index in (0..node.named_child_count()).rev() {
4232 let Some(child) = node.named_child(index) else {
4233 continue;
4234 };
4235 if child.start_byte() >= body_start {
4236 continue;
4237 }
4238 stack.push(child);
4239 }
4240
4241 let mut best = None;
4242 while let Some(current) = stack.pop() {
4243 if matches!(current.kind(), "identifier" | "type_identifier") {
4244 let name = normalize_cpp_whitespace(node_text(current, source));
4245 if !name.is_empty()
4246 && !cpp_export_macro_token(&name)
4247 && !matches!(name.as_str(), "class" | "struct" | "union")
4248 {
4249 best = Some(name);
4250 }
4251 continue;
4252 }
4253
4254 for index in (0..current.named_child_count()).rev() {
4255 if let Some(child) = current.named_child(index)
4256 && child.start_byte() < body_start
4257 {
4258 stack.push(child);
4259 }
4260 }
4261 }
4262 best
4263}
4264
4265fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4266 if node.kind() == "declaration"
4267 && node
4268 .child_by_field_name("type")
4269 .or_else(|| first_class_like_child(node))
4270 .is_some_and(|type_node| {
4271 matches!(
4272 type_node.kind(),
4273 "class_specifier" | "struct_specifier" | "union_specifier"
4274 )
4275 })
4276 && let Some(name) = node
4277 .child_by_field_name("declarator")
4278 .and_then(|declarator| declarator_name_from_node(declarator, source))
4279 && !cpp_export_macro_token(&name)
4280 {
4281 return Some(name);
4282 }
4283
4284 if node.kind() == "function_definition"
4285 && node.child_by_field_name("type").is_some_and(|type_node| {
4286 matches!(
4287 type_node.kind(),
4288 "class_specifier" | "struct_specifier" | "union_specifier"
4289 )
4290 })
4291 && let Some(name) = node
4292 .child_by_field_name("declarator")
4293 .and_then(|declarator| direct_identifier_name(declarator, source))
4294 && !cpp_export_macro_token(&name)
4295 {
4296 return Some(name);
4297 }
4298
4299 let class_node = if matches!(
4300 node.kind(),
4301 "class_specifier" | "struct_specifier" | "union_specifier"
4302 ) {
4303 node
4304 } else {
4305 first_class_like_child(node)?
4306 };
4307 class_like_name_from_children(class_node, source)
4308}
4309
4310pub fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
4312 if !matches!(
4313 node.kind(),
4314 "identifier" | "field_identifier" | "type_identifier"
4315 ) {
4316 return None;
4317 }
4318 let name = normalize_cpp_whitespace(node_text(node, source));
4319 (!name.is_empty()).then_some(name)
4320}
4321
4322fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4323 if let Some(name) = direct_identifier_name(node, source) {
4324 return Some(name);
4325 }
4326 let mut cursor = node.walk();
4327 node.named_children(&mut cursor)
4328 .find_map(|child| declarator_name_from_node(child, source))
4329}
4330
4331fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
4332 let mut cursor = node.walk();
4333 node.named_children(&mut cursor).find(|child| {
4334 matches!(
4335 child.kind(),
4336 "class_specifier" | "struct_specifier" | "union_specifier"
4337 )
4338 })
4339}
4340
4341fn push_cpp_container_work<'tree>(
4346 node: Node<'tree>,
4347 scope: ScopeInfo,
4348 stack: &mut Vec<CppWork<'tree>>,
4349) {
4350 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
4351}
4352
4353fn push_cpp_sibling_range<'tree>(
4357 parent: Node<'tree>,
4358 start_index: usize,
4359 end_index: usize,
4360 scope: ScopeInfo,
4361 stack: &mut Vec<CppWork<'tree>>,
4362) {
4363 let mut cursor = parent.walk();
4364 let children = parent
4365 .named_children(&mut cursor)
4366 .skip(start_index)
4367 .take(end_index.saturating_sub(start_index))
4368 .collect::<Vec<_>>()
4369 .into_iter();
4370 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
4371}
4372
4373fn advance_cpp_siblings<'tree>(
4381 mut siblings: CppSiblingsWork<'tree>,
4382 source: &str,
4383 stack: &mut Vec<CppWork<'tree>>,
4384) {
4385 let Some(child) = siblings.children.next() else {
4386 return;
4387 };
4388 let current_scope = siblings.scope.clone();
4389 if let Some(namespace) = cpp_using_namespace_target(child, source) {
4390 siblings.scope.visible_using_namespaces.push(namespace);
4391 }
4392 if !siblings.children.as_slice().is_empty() {
4393 stack.push(CppWork::Siblings(siblings));
4394 }
4395 stack.push(CppWork::Node(CppNodeWork {
4396 node: child,
4397 scope: current_scope,
4398 }));
4399}
4400
4401fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
4408 if node.kind() != "using_declaration" {
4409 return None;
4410 }
4411 let mut cursor = node.walk();
4412 let is_namespace_directive = node
4413 .children(&mut cursor)
4414 .any(|child| child.kind() == "namespace");
4415 if !is_namespace_directive {
4416 return None;
4417 }
4418 let target = node.named_child(0)?;
4419 let start = target
4427 .child(0)
4428 .filter(|child| !child.is_named() && child.kind() == "::")
4429 .map_or(target.start_byte(), |marker| marker.end_byte());
4430 let text = normalize_cpp_whitespace(
4431 source
4432 .get(start..target.end_byte())
4433 .expect("using-directive target covers one source range"),
4434 );
4435 (!text.is_empty()).then_some(text)
4436}
4437
4438const CPP_CONSTRUCT_OPENERS: [&str; 6] = ["#if", "#ifdef", "#ifndef", "{", "(", "["];
4440
4441pub fn cpp_unclosed_construct_openers<'tree>(root: Node<'tree>) -> Vec<Node<'tree>> {
4460 assert!(
4461 root.parent().is_none(),
4462 "only a tree's root records what its parse left open"
4463 );
4464 if !root.is_error() {
4465 return Vec::new();
4466 }
4467 children_iter(root)
4468 .filter(|child| CPP_CONSTRUCT_OPENERS.contains(&child.kind()))
4469 .collect()
4470}
4471
4472fn record_declarations_left_open(parsed: &mut ParsedFile, recovery: Range) {
4483 let recorded: HashSet<CodeUnit> = parsed
4484 .materialization_records
4485 .iter()
4486 .filter_map(|record| match record {
4487 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.clone()),
4488 _ => None,
4489 })
4490 .collect();
4491 let mut left_open: Vec<(usize, CodeUnit)> = parsed
4492 .declarations()
4493 .iter()
4494 .filter_map(|unit| {
4495 let ranges = parsed.declaration_ranges(unit);
4496 let first = ranges.iter().map(|range| range.start_byte).min()?;
4497 let last = ranges.iter().map(|range| range.start_byte).max()?;
4498 (last >= recovery.start_byte).then(|| (first, unit.clone()))
4499 })
4500 .collect();
4501 if left_open.is_empty() {
4502 return;
4503 }
4504 parsed.completeness = ParseCompleteness::Partial;
4505 left_open.retain(|(_, unit)| !recorded.contains(unit));
4506 left_open.sort();
4507 for (_, unit) in left_open {
4508 parsed
4509 .record_materialization(MaterializationRecord::RecoveredDeclaration { recovery, unit });
4510 }
4511}
4512
4513pub(crate) fn parse_cpp_readings<'tree>(
4517 file: &ProjectFile,
4518 source: &str,
4519 root: Node<'tree>,
4520 ancestry: &ParentIndex<'tree>,
4521 dialects: &[LanguageDialect],
4522) -> Vec<ParsedFile> {
4523 parse_cpp_readings_with_object_macro_fields(
4524 file,
4525 source,
4526 root,
4527 ancestry,
4528 dialects,
4529 HashMap::default(),
4530 )
4531}
4532
4533pub(crate) fn parse_cpp_readings_with_object_macro_fields<'tree>(
4534 file: &ProjectFile,
4535 source: &str,
4536 root: Node<'tree>,
4537 ancestry: &ParentIndex<'tree>,
4538 dialects: &[LanguageDialect],
4539 object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4540) -> Vec<ParsedFile> {
4541 assert!(
4542 dialects.len() == 1
4543 || (dialects.len() == 2
4544 && dialects[0] != LanguageDialect::CppC
4545 && dialects[1] == LanguageDialect::CppC),
4546 "one primary dialect, optionally followed by its C alternative"
4547 );
4548 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
4549 let properties = RefCell::new(HashMap::default());
4550 let dependencies = RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
4551 let orphaned_namespaces = OrphanedNamespaceScopeIndex::build(root, source);
4552 let mut using_namespaces = Vec::new();
4553 let mut readings: Vec<_> = dialects
4554 .iter()
4555 .map(|_| ParsedFile::new(String::new()))
4556 .collect();
4557 let mut visitors: Vec<_> = readings
4558 .iter_mut()
4559 .zip(dialects)
4560 .map(|(parsed, dialect)| CppVisitor {
4561 file,
4562 source,
4563 parsed,
4564 source_collector: &collector,
4565 source_properties: &properties,
4566 source_dependencies: &dependencies,
4567 primary_ancestry: ancestry,
4568 primary_root_id: root.id(),
4569 source_links: HashMap::default(),
4570 source_declarations: HashSet::default(),
4571 preprocessor_context_cache: Default::default(),
4572 c_tag_semantics: *dialect == LanguageDialect::CppC,
4573 c_tag_scope_witness: false,
4574 recovered_class_sibling_scopes: HashMap::default(),
4575 consumed_fragment_regions: Vec::new(),
4576 orphaned_namespaces: &orphaned_namespaces,
4577 partitioned_regions: Vec::new(),
4578 namespace_forward_scans: HashMap::default(),
4579 field_owners: None,
4580 recovery_captures: Vec::new(),
4581 active_source_context: None,
4582 object_macro_fields: object_macro_fields.clone(),
4583 ambiguous_object_macro_fields: HashSet::default(),
4584 })
4585 .collect();
4586 let mut declaration_stacks: Vec<_> = dialects
4587 .iter()
4588 .map(|_| {
4589 vec![CppWork::Container(CppContainer {
4590 node: root,
4591 scope: ScopeInfo {
4592 package_name: String::new(),
4593 module: None,
4594 class_unit: None,
4595 template_signature: None,
4596 template_metadata: None,
4597 declarations_are_fields: false,
4598 recovered_specialization_member_scope: false,
4599 visible_using_namespaces: Vec::new(),
4600 },
4601 })]
4602 })
4603 .collect();
4604 let kinds = CompiledKinds::compile(
4605 &tree_sitter_cpp::LANGUAGE.into(),
4606 CPP_STRUCTURAL_SPEC.kind_table(),
4607 );
4608 let context = CPP_STRUCTURAL_SPEC.call_site_context(root, source);
4611 let mut structural = StructuralFactCollector::new(
4612 &CPP_STRUCTURAL_SPEC,
4613 source,
4614 &context,
4615 brokk_bifrost_core::analyzer::tree_walk::ParentIndex::new(root),
4616 usize::MAX,
4617 None,
4618 );
4619 let mut imports = Vec::new();
4620 let mut includes = Vec::new();
4621 let mut expressions = Vec::new();
4622 let named_record_sizes = crate::source_facts::named_record_size_index(root, source);
4623 let mut statement_facts = CppStatementFactRows::default();
4624 let mut identifiers = HashSet::default();
4625 let mut entered = HashSet::default();
4626 let mut primary_ordinals = HashMap::default();
4627 let mut stack = vec![(root, None)];
4628 while let Some((node, parent)) = stack.pop() {
4629 assert!(entered.insert(node.id()), "one primary event per AST node");
4630 primary_ordinals.insert(node.id(), primary_ordinals.len());
4631 dependencies.borrow_mut().primary.enter(node, source);
4632 let mut child_parent = parent;
4633 if node.is_named() {
4634 crate::structural::capture_statement_fact_rows(node, source, &mut statement_facts);
4635 if let Some(namespace) = cpp_using_namespace_target(node, source) {
4636 using_namespaces.push((collector.borrow_mut().intern_node(node), namespace));
4637 }
4638 if let Some(raw_kind) = kinds.kind_of(&node)
4639 && CPP_STRUCTURAL_SPEC.should_extract(node, raw_kind)
4640 {
4641 let kind = CPP_STRUCTURAL_SPEC.refine_kind(
4642 node,
4643 raw_kind,
4644 parent.map(|id| structural.normalized_kind(id)),
4645 source,
4646 &context,
4647 );
4648 let mut source_facts = collector.borrow_mut();
4649 let id = structural
4650 .enter(node, kind, parent, &mut source_facts)
4651 .expect("unbounded C++ structural collector");
4652 let mut sink = structural.role_sink(&mut source_facts);
4653 CPP_STRUCTURAL_SPEC.extract(node, kind, &mut sink);
4654 structural
4655 .accept_roles(id, sink.into_parts())
4656 .expect("unbounded C++ structural roles");
4657 child_parent = Some(id);
4658 }
4659 if let Some(fact) = crate::source_facts::capture_expression_fact(
4660 node,
4661 source,
4662 collector.borrow_mut().intern_node(node),
4663 &named_record_sizes,
4664 ) {
4665 expressions.push(fact);
4666 }
4667 if node.kind() == "preproc_include" {
4668 let raw = normalize_cpp_whitespace(node_text(node, source));
4669 if !raw.is_empty() {
4670 let occurrence = collector.borrow_mut().intern_node(node);
4671 let target = node.child_by_field_name("path").and_then(|path| {
4672 let (value, quoted) = crate::imports::include_path_from_node(node, source)?;
4673 let target = collector.borrow_mut().intern_node(path);
4674 includes.push(brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
4675 declaration: occurrence,
4676 target,
4677 path: value.to_owned(),
4678 quoted,
4679 });
4680 Some(target)
4681 });
4682 imports.push(SourceImportFact::from_import(
4683 cpp_include_info(raw),
4684 occurrence,
4685 target,
4686 None,
4687 Vec::new(),
4688 ));
4689 }
4690 }
4691 if matches!(
4692 node.kind(),
4693 "type_identifier" | "identifier" | "qualified_identifier"
4694 ) {
4695 let text = node_text(node, source).trim();
4696 if !text.is_empty() {
4697 identifiers.insert(text.to_owned());
4698 }
4699 }
4700 }
4701 for index in 0..visitors.len() {
4702 if index != 0 && !visitors[0].c_tag_scope_witness {
4707 continue;
4708 }
4709 let visitor = &mut visitors[index];
4710 advance_primary_declarations(
4711 visitor,
4712 &mut declaration_stacks[index],
4713 &entered,
4714 ancestry,
4715 );
4716 visitor.drain_partitioned_regions();
4717 }
4718 let mut cursor = node.walk();
4719 let children: Vec<_> = node
4720 .children(&mut cursor)
4721 .map(|child| (child, child_parent))
4722 .collect();
4723 stack.extend(children.into_iter().rev());
4724 }
4725 let reading_count = if visitors[0].c_tag_scope_witness {
4726 visitors.len()
4727 } else {
4728 1
4729 };
4730 visitors.truncate(reading_count);
4731 declaration_stacks.truncate(reading_count);
4732 assert!(
4733 declaration_stacks.iter().all(Vec::is_empty),
4734 "all primary declaration events were consumed"
4735 );
4736 for visitor in &mut visitors {
4737 visitor.parsed.source_declaration_units = std::mem::take(&mut visitor.source_links)
4738 .into_iter()
4739 .flat_map(|(unit, declarations)| {
4740 declarations
4741 .into_iter()
4742 .map(move |declaration| (declaration, unit.clone()))
4743 })
4744 .collect();
4745 }
4746 let dialect_declarations: Vec<_> = visitors
4747 .iter()
4748 .map(|visitor| visitor.source_declarations.clone())
4749 .collect();
4750 drop(visitors);
4751 readings.truncate(reading_count);
4752 dependencies
4753 .borrow_mut()
4754 .finish_primary(&mut properties.borrow_mut());
4755 recover_quoted_source_imports(source, &mut imports, &mut includes, &collector);
4758 let primary_occurrence_ordinals: HashMap<_, _> = collector
4759 .borrow()
4760 .primary_node_occurrences()
4761 .map(|(node, occurrence)| (occurrence, primary_ordinals[&node]))
4762 .collect();
4763 let occurrences = collector.into_inner().finish();
4764 let generic_imports: Vec<_> = (0..imports.len())
4765 .map(|i| SourceImportId::try_from_index(i).expect("C++ import id"))
4766 .collect();
4767 let source_facts = ParsedSourceFacts {
4768 cpp: Some(brokk_bifrost_core::analyzer::cpp_facts::CppSourceFacts {
4769 includes,
4770 declarations: {
4771 let mut facts: Vec<_> = properties.into_inner().into_values().collect();
4772 facts.sort_by_key(|fact| fact.declaration.get());
4773 facts
4774 },
4775 using_namespaces,
4776 expressions,
4777 statement_facts,
4778 }),
4779 go: None,
4780 java: None,
4781 js_ts: None,
4782 php: None,
4783 scala: None,
4784 ruby: None,
4785 python: None,
4786 source_bytes: source.len(),
4787 occurrences,
4788 structural: structural
4789 .finish(root)
4790 .expect("C++ structural collection finishes"),
4791 native_site_occurrences: Vec::new(),
4792 native_declaration_sources: Vec::new(),
4793 declaration_visibilities: None,
4794 rust_declaration_properties: Vec::new(),
4795 rust_modules: None,
4796 rust_types: Vec::new(),
4797 rust_items: Default::default(),
4798 imports,
4799 generic_imports,
4800 rust_import_contexts: Vec::new(),
4801 };
4802 for (parsed, declarations) in readings.iter_mut().zip(dialect_declarations) {
4803 parsed.finalize_deferred_replacements();
4804 let links = std::mem::take(&mut parsed.source_declaration_units);
4805 parsed.source_declaration_units = links
4806 .into_iter()
4807 .filter(|(_, unit)| parsed.contains_declaration(unit))
4808 .collect();
4809 parsed.imports = source_facts
4810 .imports
4811 .iter()
4812 .map(|import| import.import_info(&source_facts.occurrences))
4813 .collect();
4814 parsed.type_identifiers = identifiers.clone();
4815 parsed.source_facts = Some(crate::source_projection::finalize_dialect(
4816 &source_facts,
4817 &declarations,
4818 &primary_occurrence_ordinals,
4819 &mut parsed.source_declaration_units,
4820 &mut parsed.source_declaration_metadata,
4821 ));
4822 }
4823 if let Some(outermost) = cpp_unclosed_construct_openers(root).first() {
4824 let recovery = Range {
4827 start_byte: outermost.start_byte(),
4828 end_byte: source.len(),
4829 start_line: outermost.start_position().row + 1,
4830 end_line: root.end_position().row + 1,
4831 };
4832 for parsed in &mut readings {
4833 record_declarations_left_open(parsed, recovery);
4834 }
4835 }
4836 readings
4837}
4838
4839fn advance_primary_declarations<'tree>(
4840 visitor: &mut CppVisitor<'_, '_>,
4841 stack: &mut Vec<CppWork<'tree>>,
4842 entered: &HashSet<usize>,
4843 ancestry: &ParentIndex<'tree>,
4844) {
4845 while let Some(work) = stack.pop() {
4846 match work {
4847 CppWork::Container(container) => {
4848 push_cpp_container_work(container.node, container.scope, stack)
4849 }
4850 CppWork::Siblings(siblings) => advance_cpp_siblings(siblings, visitor.source, stack),
4851 CppWork::Node(work) => {
4852 if !entered.contains(&work.node.id()) {
4853 stack.push(CppWork::Node(work));
4854 return;
4855 }
4856 if !visitor.node_is_inside_consumed_fragment(work.node) {
4857 visitor.visit_node(work.node, &work.scope, stack, ancestry);
4858 }
4859 }
4860 }
4861 }
4862}
4863
4864#[derive(Clone)]
4865struct CppActiveSourceContext {
4866 guards: Option<HashSet<crate::graph::resolver::PreprocessorGuard>>,
4867 namespace: Option<String>,
4868 child_namespace: Option<String>,
4869 outer_lexical_scope: Vec<String>,
4870 child_lexical_scope: Vec<String>,
4871 family: Option<SourceOccurrenceId>,
4872 exhaustive_family: Option<SourceOccurrenceId>,
4873 callable_guard_completion_byte: Option<usize>,
4874}
4875
4876pub struct CppPartitionedRegion {
4880 tree: Tree,
4881 range: std::ops::Range<usize>,
4882 scope: ScopeInfo,
4883 recovery: Range,
4884 source_context: Option<CppActiveSourceContext>,
4885}
4886
4887pub struct CppVisitor<'a, 'source> {
4888 pub file: &'a ProjectFile,
4889 pub source: &'source str,
4890 pub parsed: &'a mut ParsedFile,
4891 pub source_collector: &'a RefCell<PrimarySourceFactCollector<'source>>,
4892 source_properties: &'a RefCell<
4893 HashMap<
4894 SourceDeclarationId,
4895 brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4896 >,
4897 >,
4898 source_dependencies: &'a RefCell<crate::source_dependencies::DeclarationDependencies>,
4899 pub primary_ancestry: &'a ParentIndex<'a>,
4900 pub primary_root_id: usize,
4901 source_links: HashMap<CodeUnit, Vec<SourceDeclarationId>>,
4902 source_declarations: HashSet<SourceDeclarationId>,
4903 preprocessor_context_cache: crate::graph::resolver::PreprocessorContextCache,
4904 pub c_tag_semantics: bool,
4915 pub(crate) c_tag_scope_witness: bool,
4918 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
4919 pub consumed_fragment_regions: Vec<(usize, usize)>,
4928 pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
4933 pub partitioned_regions: Vec<CppPartitionedRegion>,
4936 pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
4941 pub field_owners: Option<CppFieldOwnerIndex>,
4945 pub recovery_captures: Vec<CppRecoveryCapture>,
4949 active_source_context: Option<CppActiveSourceContext>,
4950 pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4954 pub ambiguous_object_macro_fields: HashSet<String>,
4957}
4958
4959#[derive(Clone, Debug, PartialEq, Eq)]
4966pub enum ObjectMacroFieldEvent {
4967 Define {
4968 name: String,
4969 replacement: ObjectMacroReplacement,
4970 conditional: bool,
4971 },
4972 Undef {
4973 name: String,
4974 conditional: bool,
4975 },
4976}
4977
4978pub fn collect_cpp_object_macro_fields<'tree>(
4982 root: Node<'tree>,
4983 source: &str,
4984) -> HashMap<String, ObjectMacroReplacement> {
4985 let mut fields = HashMap::default();
4986 let mut ambiguous = HashSet::default();
4987 for event in collect_cpp_object_macro_field_events(root, source) {
4988 match event {
4989 ObjectMacroFieldEvent::Define {
4990 name,
4991 replacement: value,
4992 conditional,
4993 } => {
4994 if value.is_empty() {
4995 fields.remove(&name);
4996 if conditional {
4997 ambiguous.insert(name);
4998 } else {
4999 ambiguous.remove(&name);
5000 }
5001 } else if ambiguous.contains(&name) {
5002 } else if let Some(previous) = fields.get(&name) {
5004 if previous != &value {
5005 fields.remove(&name);
5006 ambiguous.insert(name);
5007 }
5008 } else {
5009 fields.insert(name, value);
5010 }
5011 }
5012 ObjectMacroFieldEvent::Undef { name, conditional } => {
5013 fields.remove(&name);
5014 if conditional {
5015 ambiguous.insert(name);
5016 } else {
5017 ambiguous.remove(&name);
5018 }
5019 }
5020 }
5021 }
5022 fields
5023}
5024
5025pub fn collect_cpp_object_macro_field_events<'tree>(
5030 root: Node<'tree>,
5031 source: &str,
5032) -> Vec<ObjectMacroFieldEvent> {
5033 let mut events = Vec::new();
5034 let mut stack = vec![root];
5035 while let Some(node) = stack.pop() {
5036 if node.kind() == "preproc_def"
5037 && let Some(name) = extract_macro_name(node, source)
5038 {
5039 let replacement = object_macro_replacement_of(node, source);
5040 events.push(ObjectMacroFieldEvent::Define {
5041 name,
5042 replacement,
5043 conditional: inside_preprocessor_conditional(node),
5044 });
5045 } else if is_cpp_undef_directive(node, source)
5046 && let Some(argument) = node.child_by_field_name("argument")
5047 {
5048 events.push(ObjectMacroFieldEvent::Undef {
5049 name: node_text(argument, source).trim().to_string(),
5050 conditional: inside_preprocessor_conditional(node),
5051 });
5052 }
5053 let mut cursor = node.walk();
5054 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
5055 stack.extend(children.into_iter().rev());
5056 }
5057 events
5058}
5059
5060fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
5061 let mut current = node.parent();
5062 while let Some(parent) = current {
5063 if is_cpp_preprocessor_conditional(parent) {
5064 return true;
5065 }
5066 current = parent.parent();
5067 }
5068 false
5069}
5070
5071fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
5072 node.kind() == "preproc_call"
5073 && node
5074 .child_by_field_name("directive")
5075 .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
5076}
5077
5078impl<'a, 'source> CppVisitor<'a, 'source> {
5079 fn apply_active_source_context(
5080 &self,
5081 fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5082 ) {
5083 let Some(context) = self.active_source_context.as_ref() else {
5084 return;
5085 };
5086 fact.guard_requirements = fact
5087 .guard_requirements
5088 .as_ref()
5089 .and_then(crate::source_context::cpp_guard_set_to_runtime)
5090 .zip(context.guards.as_ref())
5091 .and_then(|(local, outer)| {
5092 crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5093 })
5094 .as_ref()
5095 .map(crate::source_context::cpp_guard_set_from_runtime);
5096 if fact.callable_guards.is_some() {
5097 fact.callable_guards = fact
5098 .callable_guards
5099 .as_ref()
5100 .and_then(crate::source_context::cpp_guard_set_to_runtime)
5101 .zip(context.guards.as_ref())
5102 .and_then(|(local, outer)| {
5103 crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5104 })
5105 .as_ref()
5106 .map(crate::source_context::cpp_guard_set_from_runtime);
5107 }
5108 fact.callable_guard_completion_byte = fact
5109 .callable_guard_completion_byte
5110 .zip(context.callable_guard_completion_byte)
5111 .map(|(local, outer)| local.max(outer));
5112 fact.conditional_family = fact.conditional_family.or(context.family);
5113 fact.exhaustive_conditional_family = fact
5114 .exhaustive_conditional_family
5115 .or(context.exhaustive_family);
5116 if let Some(alias) = fact.file_scope_alias.as_mut() {
5117 alias.namespace = match (context.namespace.as_ref(), alias.namespace.take()) {
5118 (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
5119 (Some(outer), None) => Some(outer.clone()),
5120 (None, local) => local,
5121 };
5122 }
5123 if !context.outer_lexical_scope.is_empty() {
5124 let mut lexical = context.outer_lexical_scope.clone();
5125 lexical.append(&mut fact.lexical_path);
5126 fact.lexical_path = lexical;
5127 }
5128 }
5129
5130 fn recovery_ancestry<'tree>(
5131 &self,
5132 root: Node<'tree>,
5133 ) -> crate::source_dependencies::RecoveryAncestry<'a, 'tree> {
5134 crate::source_dependencies::RecoveryAncestry::new(
5135 root,
5136 self.source,
5137 self.source_dependencies,
5138 )
5139 }
5140
5141 fn capture_source_dependencies(
5142 &self,
5143 fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5144 node: Node<'_>,
5145 unit: &CodeUnit,
5146 ) {
5147 if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
5148 self.source_dependencies.borrow_mut().register_primary(
5149 fact.declaration,
5150 node,
5151 unit.is_class(),
5152 );
5153 } else {
5154 self.source_dependencies
5155 .borrow()
5156 .apply_recovered(node, unit.is_class(), fact);
5157 }
5158 }
5159
5160 fn link_source_declaration(
5161 &mut self,
5162 ancestry: &ParentIndex<'_>,
5163 unit: &CodeUnit,
5164 node: Node<'_>,
5165 ) {
5166 let (declaration, family) = {
5167 let mut collector = self.source_collector.borrow_mut();
5168 let occurrence = if node.id() == self.primary_root_id
5169 || self.primary_ancestry.contains(node)
5170 {
5171 collector.intern_node(node)
5172 } else {
5173 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
5176 };
5177 let name = crate::source_facts::declaration_name(node, unit, self.source).map(|name| {
5178 if self.primary_ancestry.contains(name) {
5179 collector.intern_node(name)
5180 } else {
5181 collector
5182 .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(name))
5183 }
5184 });
5185 let declaration = collector.declare(occurrence, name);
5186 let family =
5187 crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5188 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5189 {
5190 collector.intern_node(family)
5191 } else {
5192 collector.intern_embedded(
5193 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5194 )
5195 }
5196 });
5197 (declaration, family)
5198 };
5199 self.source_declarations.insert(declaration);
5200 if !self.source_properties.borrow().contains_key(&declaration) {
5201 let context = crate::source_context::capture_declaration_context(
5202 node,
5203 unit,
5204 self.source,
5205 &mut self.preprocessor_context_cache,
5206 );
5207 let (exhaustive_family, displaced_namespace_closing_brace) = {
5208 let mut collector = self.source_collector.borrow_mut();
5209 let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5210 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5211 {
5212 collector.intern_node(family)
5213 } else {
5214 collector.intern_embedded(
5215 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5216 )
5217 }
5218 });
5219 let displaced_namespace_closing_brace =
5220 context.displaced_namespace_closing_brace.map(|brace| {
5221 if brace.id() == self.primary_root_id
5222 || self.primary_ancestry.contains(brace)
5223 {
5224 collector.intern_node(brace)
5225 } else {
5226 collector.intern_embedded(
5227 brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5228 )
5229 }
5230 });
5231 (exhaustive_family, displaced_namespace_closing_brace)
5232 };
5233 let guard_requirements = context.guard_requirements;
5234 let callable_guards = context.callable_guards;
5235 let callable_activation = context.callable_activation;
5236 let callable_guard_completion_byte = context.callable_guard_completion_byte;
5237 let flattened_macro_namespace = context.flattened_macro_namespace;
5238 self.source_properties
5239 .borrow_mut()
5240 .entry(declaration)
5241 .or_insert_with(|| {
5242 let mut fact = crate::source_facts::capture_declaration(
5243 declaration,
5244 node,
5245 unit,
5246 self.source,
5247 ancestry,
5248 );
5249 self.capture_source_dependencies(&mut fact, node, unit);
5250 fact.occurrence_role =
5251 crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5252 fact.conditional_family = family;
5253 fact.guard_requirements = guard_requirements;
5254 fact.callable_guards = callable_guards;
5255 fact.callable_activation = callable_activation;
5256 fact.callable_guard_completion_byte = callable_guard_completion_byte;
5257 fact.exhaustive_conditional_family = exhaustive_family;
5258 fact.flattened_macro_namespace = flattened_macro_namespace;
5259 fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5260 self.apply_active_source_context(&mut fact);
5261 fact
5262 });
5263 }
5264 let links = self.source_links.entry(unit.clone()).or_default();
5265 if !links.contains(&declaration) {
5266 links.push(declaration);
5267 }
5268 }
5269
5270 fn link_source_range(
5271 &mut self,
5272 ancestry: &ParentIndex<'_>,
5273 unit: &CodeUnit,
5274 range: Range,
5275 evidence: Option<Node<'_>>,
5276 ) {
5277 let declaration = {
5278 let mut collector = self.source_collector.borrow_mut();
5279 let occurrence =
5280 collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5281 let name = evidence
5282 .and_then(|node| crate::source_facts::declaration_name(node, unit, self.source))
5283 .map(|name| {
5284 if self.primary_ancestry.contains(name) {
5285 collector.intern_node(name)
5286 } else {
5287 collector.intern_embedded(
5288 brokk_bifrost_core::analyzer::tree_walk::node_range(name),
5289 )
5290 }
5291 });
5292 collector.declare(occurrence, name)
5293 };
5294 self.link_source_declaration_facts(
5295 unit,
5296 declaration,
5297 evidence.map(|node| (ancestry, node)),
5298 );
5299 }
5300
5301 fn link_source_name_range(
5305 &mut self,
5306 unit: &CodeUnit,
5307 range: Range,
5308 name_range: Range,
5309 evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5310 ) {
5311 let declaration = {
5312 let mut collector = self.source_collector.borrow_mut();
5313 let occurrence =
5314 collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5315 let name =
5316 collector.intern_subspan(name_range, SourceOccurrenceProvenance::ExplicitSubspan);
5317 collector.declare(occurrence, Some(name))
5318 };
5319 self.link_source_declaration_facts(unit, declaration, evidence);
5320 }
5321
5322 fn link_source_declaration_facts(
5327 &mut self,
5328 unit: &CodeUnit,
5329 declaration: SourceDeclarationId,
5330 evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5331 ) {
5332 let family = evidence.and_then(|(_, node)| {
5333 let mut collector = self.source_collector.borrow_mut();
5334 crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5335 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
5336 collector.intern_node(family)
5337 } else {
5338 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(
5339 family,
5340 ))
5341 }
5342 })
5343 });
5344 self.source_declarations.insert(declaration);
5345 if let Some((ancestry, node)) = evidence
5346 && !self.source_properties.borrow().contains_key(&declaration)
5347 {
5348 let context = crate::source_context::capture_declaration_context(
5349 node,
5350 unit,
5351 self.source,
5352 &mut self.preprocessor_context_cache,
5353 );
5354 let (exhaustive_family, displaced_namespace_closing_brace) = {
5355 let mut collector = self.source_collector.borrow_mut();
5356 let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5357 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5358 {
5359 collector.intern_node(family)
5360 } else {
5361 collector.intern_embedded(
5362 brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5363 )
5364 }
5365 });
5366 let displaced_namespace_closing_brace =
5367 context.displaced_namespace_closing_brace.map(|brace| {
5368 if brace.id() == self.primary_root_id
5369 || self.primary_ancestry.contains(brace)
5370 {
5371 collector.intern_node(brace)
5372 } else {
5373 collector.intern_embedded(
5374 brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5375 )
5376 }
5377 });
5378 (exhaustive_family, displaced_namespace_closing_brace)
5379 };
5380 let guard_requirements = context.guard_requirements;
5381 let callable_guards = context.callable_guards;
5382 let callable_activation = context.callable_activation;
5383 let callable_guard_completion_byte = context.callable_guard_completion_byte;
5384 let flattened_macro_namespace = context.flattened_macro_namespace;
5385 let mut fact = crate::source_facts::capture_declaration(
5386 declaration,
5387 node,
5388 unit,
5389 self.source,
5390 ancestry,
5391 );
5392 self.capture_source_dependencies(&mut fact, node, unit);
5393 fact.occurrence_role =
5394 crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5395 fact.conditional_family = family;
5396 fact.guard_requirements = guard_requirements;
5397 fact.callable_guards = callable_guards;
5398 fact.callable_activation = callable_activation;
5399 fact.callable_guard_completion_byte = callable_guard_completion_byte;
5400 fact.exhaustive_conditional_family = exhaustive_family;
5401 fact.flattened_macro_namespace = flattened_macro_namespace;
5402 fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5403 self.apply_active_source_context(&mut fact);
5404 self.source_properties
5405 .borrow_mut()
5406 .insert(declaration, fact);
5407 }
5408 let links = self.source_links.entry(unit.clone()).or_default();
5409 if !links.contains(&declaration) {
5410 links.push(declaration);
5411 }
5412 }
5413
5414 fn add_signature_with_metadata(&mut self, unit: CodeUnit, metadata: SignatureMetadata) {
5415 let declaration = *self
5416 .source_links
5417 .get(&unit)
5418 .and_then(|links| links.last())
5419 .expect("signature metadata follows its source declaration");
5420 let metadata_ordinal = self
5421 .parsed
5422 .add_signature_with_metadata(unit.clone(), metadata);
5423 let link = brokk_bifrost_core::analyzer::parsed_file::SourceDeclarationMetadataLink {
5424 declaration,
5425 unit,
5426 metadata_ordinal,
5427 };
5428 if !self.parsed.source_declaration_metadata.contains(&link) {
5429 self.parsed.source_declaration_metadata.push(link);
5430 }
5431 }
5432
5433 fn add_declaration(
5441 &mut self,
5442 ancestry: &ParentIndex<'_>,
5443 code_unit: CodeUnit,
5444 node: Node<'_>,
5445 parent: Option<CodeUnit>,
5446 top_level: Option<CodeUnit>,
5447 ) {
5448 self.note_declaration(&code_unit);
5449 self.link_source_declaration(ancestry, &code_unit, node);
5450 let source = self.source;
5451 self.parsed
5452 .add_code_unit(code_unit, node, source, parent, top_level);
5453 }
5454
5455 fn add_declaration_with_range(
5457 &mut self,
5458 ancestry: &ParentIndex<'_>,
5459 code_unit: CodeUnit,
5460 range: Range,
5461 evidence: Option<Node<'_>>,
5462 parent: Option<CodeUnit>,
5463 top_level: Option<CodeUnit>,
5464 ) {
5465 self.note_declaration(&code_unit);
5466 self.link_source_range(ancestry, &code_unit, range, evidence);
5467 self.parsed
5468 .add_code_unit_with_range(code_unit, range, parent, top_level);
5469 }
5470
5471 fn add_declaration_with_name_range(
5477 &mut self,
5478 code_unit: CodeUnit,
5479 range: Range,
5480 name_range: Range,
5481 evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5482 parent: Option<CodeUnit>,
5483 top_level: Option<CodeUnit>,
5484 ) {
5485 self.note_declaration(&code_unit);
5486 self.link_source_name_range(&code_unit, range, name_range, evidence);
5487 self.parsed
5488 .add_code_unit_with_range(code_unit, range, parent, top_level);
5489 }
5490
5491 fn replace_declaration_deferred(
5493 &mut self,
5494 ancestry: &ParentIndex<'_>,
5495 code_unit: CodeUnit,
5496 node: Node<'_>,
5497 parent: Option<CodeUnit>,
5498 top_level: Option<CodeUnit>,
5499 ) {
5500 self.note_replaced_declaration(&code_unit);
5501 self.link_source_declaration(ancestry, &code_unit, node);
5502 let source = self.source;
5503 self.parsed
5504 .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
5505 }
5506
5507 fn replace_declaration_with_range_deferred(
5509 &mut self,
5510 ancestry: &ParentIndex<'_>,
5511 code_unit: CodeUnit,
5512 range: Range,
5513 evidence: Option<Node<'_>>,
5514 parent: Option<CodeUnit>,
5515 top_level: Option<CodeUnit>,
5516 ) {
5517 self.note_replaced_declaration(&code_unit);
5518 self.link_source_range(ancestry, &code_unit, range, evidence);
5519 self.parsed
5520 .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
5521 }
5522
5523 fn note_declaration(&mut self, code_unit: &CodeUnit) {
5530 if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
5531 for capture in &mut self.recovery_captures {
5532 if capture.removed_pre_existing.contains(code_unit) {
5533 continue;
5534 }
5535 if capture.created_units.insert(code_unit.clone()) {
5536 capture.created.push(code_unit.clone());
5537 }
5538 }
5539 }
5540 if let Some(field_owners) = self.field_owners.as_mut() {
5541 field_owners.record(code_unit, self.file);
5542 }
5543 }
5544
5545 fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
5554 for removed in self.declarations_a_replacement_removes(code_unit) {
5557 self.source_links.remove(&removed);
5558 }
5559 let removes_children = self.parsed.contains_declaration(code_unit)
5560 && self
5561 .parsed
5562 .children
5563 .get(code_unit)
5564 .is_some_and(|children| !children.is_empty());
5565 if removes_children {
5566 if !self.recovery_captures.is_empty() {
5567 let removed = self.declarations_a_replacement_removes(code_unit);
5568 for capture in &mut self.recovery_captures {
5569 for unit in &removed {
5570 if !capture.created_units.contains(unit) {
5575 capture.removed_pre_existing.insert(unit.clone());
5576 }
5577 }
5578 }
5579 }
5580 self.field_owners = None;
5581 }
5582 self.note_declaration(code_unit);
5583 }
5584
5585 fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
5588 let mut removed = Vec::new();
5589 let mut seen = HashSet::default();
5590 let mut pending: Vec<CodeUnit> = self
5591 .parsed
5592 .children
5593 .get(code_unit)
5594 .cloned()
5595 .unwrap_or_default();
5596 while let Some(unit) = pending.pop() {
5597 if !seen.insert(unit.clone()) {
5598 continue;
5599 }
5600 if let Some(children) = self.parsed.children.get(&unit) {
5601 pending.extend(children.iter().cloned());
5602 }
5603 removed.push(unit);
5604 }
5605 removed
5606 }
5607
5608 fn visit_recovered_function_like_export_class<'tree>(
5609 &mut self,
5610 node: Node<'tree>,
5611 recovered: RecoveredFunctionLikeExportClass,
5612 scope: &ScopeInfo,
5613 stack: &mut Vec<CppWork<'tree>>,
5614 ancestry: &ParentIndex<'tree>,
5615 ) {
5616 let RecoveredFunctionLikeExportClass {
5617 name,
5618 range,
5619 raw_supertypes,
5620 base_facts,
5621 member_outcome,
5622 } = recovered;
5623 let class_unit = self.visit_named_class_like_shape(
5624 node,
5625 name,
5626 None,
5627 true,
5628 Some(range),
5629 raw_supertypes,
5630 base_facts,
5631 scope,
5632 stack,
5633 ancestry,
5634 );
5635 self.parsed
5636 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5637 recovery: range,
5638 unit: class_unit.clone(),
5639 });
5640 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5641 && let Some((constructor_range, body)) = cpp_reparsed_merged_inline_constructor(
5642 tree.root_node(),
5643 class_unit.identifier(),
5644 self.source,
5645 )
5646 {
5647 self.visit_recovered_fragment_constructor(
5648 constructor_range,
5649 body,
5650 node,
5651 &class_unit,
5652 scope,
5653 ancestry,
5654 );
5655 }
5656 if let Some(outcome) = member_outcome {
5657 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5658 }
5659 }
5660
5661 fn visit_function_like_export_class_pair<'tree>(
5662 &mut self,
5663 node: Node<'tree>,
5664 scope: &ScopeInfo,
5665 stack: &mut Vec<CppWork<'tree>>,
5666 ancestry: &ParentIndex<'tree>,
5667 ) -> bool {
5668 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
5669 return false;
5670 };
5671 let member_outcome = self
5672 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
5673 let mut displaced = node.next_named_sibling();
5679 while let Some(candidate) = displaced {
5680 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
5681 break;
5682 }
5683 displaced = candidate.next_named_sibling();
5684 }
5685 let range = recovered.range;
5690 self.visit_recovered_function_like_export_class(
5691 node,
5692 RecoveredFunctionLikeExportClass {
5693 name: recovered.name,
5694 range,
5695 raw_supertypes: recovered.raw_supertypes,
5696 base_facts: recovered.base_facts,
5697 member_outcome,
5698 },
5699 scope,
5700 stack,
5701 ancestry,
5702 );
5703 self.consumed_fragment_regions
5704 .push((node.start_byte(), range.end_byte));
5705 true
5706 }
5707
5708 fn visit_embedded_function_like_export_classes<'tree>(
5709 &mut self,
5710 node: Node<'tree>,
5711 scope: &ScopeInfo,
5712 stack: &mut Vec<CppWork<'tree>>,
5713 ancestry: &ParentIndex<'tree>,
5714 ) -> bool {
5715 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
5716 let found = !recovered_classes.is_empty();
5717 for recovered in recovered_classes {
5718 let member_outcome = self.reparse_fragmented_export_class_members(
5719 &recovered.fragmented_body,
5720 &recovered.name,
5721 );
5722 self.visit_recovered_function_like_export_class(
5723 node,
5724 RecoveredFunctionLikeExportClass {
5725 name: recovered.name,
5726 range: recovered.range,
5727 raw_supertypes: Some(recovered.raw_supertypes),
5728 base_facts: Some(recovered.base_facts),
5729 member_outcome,
5730 },
5731 scope,
5732 stack,
5733 ancestry,
5734 );
5735 }
5736 found
5737 }
5738
5739 #[allow(clippy::too_many_arguments)]
5747 pub fn visit_container<'tree>(
5748 &mut self,
5749 node: Node<'tree>,
5750 ancestry: &ParentIndex<'tree>,
5751 package_name: &str,
5752 module: Option<CodeUnit>,
5753 class_unit: Option<CodeUnit>,
5754 template_signature: Option<String>,
5755 visible_using_namespaces: Vec<String>,
5756 ) {
5757 let scope = ScopeInfo {
5758 package_name: package_name.to_string(),
5759 module,
5760 class_unit,
5761 template_signature,
5762 template_metadata: None,
5763 declarations_are_fields: false,
5764 recovered_specialization_member_scope: false,
5765 visible_using_namespaces,
5766 };
5767 if node.is_error() {
5774 self.visit_object_macro_error_classes(node, &scope);
5775 }
5776 self.run_container_work(node, scope, ancestry);
5777 self.drain_partitioned_regions();
5778 }
5779
5780 fn drain_partitioned_regions(&mut self) {
5785 let mut consumed = Vec::new();
5786 while let Some(CppPartitionedRegion {
5787 tree,
5788 range,
5789 scope,
5790 recovery,
5791 source_context,
5792 }) = self.partitioned_regions.pop()
5793 {
5794 let root = tree.root_node();
5795 let container = root
5796 .descendant_for_byte_range(range.start, range.end)
5797 .expect("the queued container belongs to this tree");
5798 assert_eq!(container.byte_range(), range);
5799 let ancestry = self.recovery_ancestry(root);
5800 self.record_recovered_declarations_with_context(recovery, source_context, |visitor| {
5801 visitor.run_container_work(container, scope, &ancestry)
5802 });
5803 consumed.push((range.start, range.end));
5807 }
5808 self.consumed_fragment_regions.extend(consumed);
5809 }
5810
5811 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
5815 self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
5816 }
5817
5818 fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
5823 self.consumed_fragment_regions
5824 .iter()
5825 .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
5826 }
5827
5828 fn run_container_work<'tree>(
5840 &mut self,
5841 node: Node<'tree>,
5842 scope: ScopeInfo,
5843 ancestry: &ParentIndex<'tree>,
5844 ) {
5845 self.drain_cpp_work(
5846 vec![CppWork::Container(CppContainer { node, scope })],
5847 ancestry,
5848 );
5849 }
5850
5851 fn drain_cpp_work<'tree>(
5858 &mut self,
5859 mut stack: Vec<CppWork<'tree>>,
5860 ancestry: &ParentIndex<'tree>,
5861 ) {
5862 while let Some(work) = stack.pop() {
5863 match work {
5864 CppWork::Container(container) => {
5865 push_cpp_container_work(container.node, container.scope, &mut stack);
5866 }
5867 CppWork::Siblings(siblings) => {
5868 advance_cpp_siblings(siblings, self.source, &mut stack);
5869 }
5870 CppWork::Node(work) => {
5871 if self.node_is_inside_consumed_fragment(work.node) {
5872 continue;
5873 }
5874 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5875 }
5876 }
5877 }
5878 }
5879
5880 fn drain_cpp_recovery_work<'tree>(
5883 &mut self,
5884 mut stack: Vec<CppWork<'tree>>,
5885 ancestry: &ParentIndex<'tree>,
5886 ) {
5887 while let Some(work) = stack.pop() {
5888 match work {
5889 CppWork::Container(container) => {
5890 push_cpp_container_work(container.node, container.scope, &mut stack);
5891 }
5892 CppWork::Siblings(siblings) => {
5893 advance_cpp_siblings(siblings, self.source, &mut stack);
5894 }
5895 CppWork::Node(work) => {
5896 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5897 }
5898 }
5899 }
5900 }
5901
5902 fn reparse_fragmented_export_class_members(
5907 &self,
5908 fragmented: &FragmentedExportBody,
5909 class_name: &str,
5910 ) -> Option<FragmentedExportMembers> {
5911 if fragmented.reparse_start >= fragmented.reparse_end {
5912 return None;
5913 }
5914 let tree = cpp_reparse_fragmented_class_body(
5915 self.source,
5916 fragmented.reparse_start,
5917 fragmented.reparse_end,
5918 )?;
5919 if cpp_reparsed_members_are_indexable_for_class(
5920 tree.root_node(),
5921 self.source,
5922 Some(class_name),
5923 ) {
5924 return Some(FragmentedExportMembers::Complete(tree));
5925 }
5926 let has_conditional_constructor = {
5927 let root = tree.root_node();
5928 let mut cursor = root.walk();
5929 root.named_children(&mut cursor).any(|child| {
5930 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
5931 })
5932 };
5933 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
5934 }
5935
5936 fn visit_fragmented_export_class_members(
5939 &mut self,
5940 origin: Node<'_>,
5941 outcome: FragmentedExportMembers,
5942 class_unit: CodeUnit,
5943 scope: &ScopeInfo,
5944 ) -> bool {
5945 self.with_recovery_source_context(origin, |visitor| {
5946 visitor.visit_fragmented_export_class_members_in_context(outcome, class_unit, scope)
5947 })
5948 }
5949
5950 fn visit_fragmented_export_class_members_in_context(
5951 &mut self,
5952 outcome: FragmentedExportMembers,
5953 class_unit: CodeUnit,
5954 scope: &ScopeInfo,
5955 ) -> bool {
5956 let (tree, complete) = match outcome {
5957 FragmentedExportMembers::Complete(tree) => (tree, true),
5958 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
5959 };
5960 let root = tree.root_node();
5961 let class_name = class_unit.identifier().to_string();
5962 let member_scope = ScopeInfo {
5963 package_name: class_unit.package_name().to_string(),
5968 module: scope.module.clone(),
5969 class_unit: Some(class_unit),
5970 template_signature: scope.template_signature.clone(),
5971 template_metadata: None,
5972 declarations_are_fields: true,
5973 recovered_specialization_member_scope: false,
5974 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5975 };
5976 let reparsed_ancestry = self.recovery_ancestry(root);
5977 if !complete {
5978 let mut cursor = root.walk();
5984 let constructors = root
5985 .named_children(&mut cursor)
5986 .filter_map(|child| {
5987 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
5988 })
5989 .collect::<Vec<_>>();
5990 for constructor in constructors {
5993 let mut stack = Vec::new();
5994 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
5995 self.drain_cpp_recovery_work(stack, &reparsed_ancestry);
5996 }
5997 return false;
5998 }
5999 let mut cursor = root.walk();
6000 let members = root.named_children(&mut cursor).collect::<Vec<_>>();
6001 for member in members.iter().copied().filter(|member| {
6002 cpp_reparsed_defaulted_constructor_is_indexable(
6003 *member,
6004 &members,
6005 Some(class_name.as_str()),
6006 self.source,
6007 )
6008 }) {
6009 self.visit_recovered_defaulted_constructor(member, &member_scope, &reparsed_ancestry);
6010 }
6011 for member in members.iter().copied().filter(|member| {
6012 cpp_reparsed_bare_constructor_call(*member, class_name.as_str(), self.source).is_some()
6013 }) {
6014 self.visit_bare_constructor_expression(member, &member_scope, &reparsed_ancestry);
6015 }
6016 self.run_container_work(root, member_scope, &reparsed_ancestry);
6018 true
6019 }
6020
6021 fn visit_recovered_fragment_constructor<'tree>(
6022 &mut self,
6023 range: std::ops::Range<usize>,
6024 constructor_body: Node<'tree>,
6025 class_declaration: Node<'tree>,
6026 class_unit: &CodeUnit,
6027 scope: &ScopeInfo,
6028 ancestry: &ParentIndex<'tree>,
6029 ) {
6030 self.with_recovery_source_context(class_declaration, |visitor| {
6031 visitor.visit_recovered_fragment_constructor_in_context(
6032 range,
6033 constructor_body,
6034 class_declaration,
6035 class_unit,
6036 scope,
6037 ancestry,
6038 );
6039 });
6040 }
6041
6042 fn visit_recovered_fragment_constructor_in_context<'tree>(
6043 &mut self,
6044 range: std::ops::Range<usize>,
6045 constructor_body: Node<'tree>,
6046 class_declaration: Node<'tree>,
6047 class_unit: &CodeUnit,
6048 scope: &ScopeInfo,
6049 ancestry: &ParentIndex<'tree>,
6050 ) {
6051 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
6052 return;
6053 };
6054 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
6055 tree.root_node(),
6056 range.start,
6057 class_unit.identifier(),
6058 self.source,
6059 ) else {
6060 return;
6061 };
6062 let member_scope = ScopeInfo {
6063 package_name: class_unit.package_name().to_string(),
6064 module: scope.module.clone(),
6065 class_unit: Some(class_unit.clone()),
6066 template_signature: scope.template_signature.clone(),
6067 template_metadata: None,
6068 declarations_are_fields: true,
6069 recovered_specialization_member_scope: false,
6070 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6071 };
6072 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
6073 else {
6074 return;
6075 };
6076 debug_assert_eq!(function.name, class_unit.identifier());
6077 let code_unit = function.code_unit(self.file.clone());
6078 let recovered_ancestry = self.recovery_ancestry(tree.root_node());
6079 self.add_declaration_with_range(
6080 &recovered_ancestry,
6081 code_unit.clone(),
6082 Range {
6083 start_byte: function_declarator.start_byte(),
6084 end_byte: constructor_body.end_byte(),
6085 start_line: function_declarator.start_position().row + 1,
6086 end_line: constructor_body.end_position().row + 1,
6087 },
6088 Some(function_declarator),
6089 None,
6090 None,
6091 );
6092 let declaration = *self.source_links[&code_unit]
6093 .last()
6094 .expect("recovered constructor source");
6095 {
6096 let mut properties = self.source_properties.borrow_mut();
6097 let fact = properties
6098 .get_mut(&declaration)
6099 .expect("recovered constructor source fact");
6100 fact.occurrence_role =
6101 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
6102 fact.callable_is_constructor = true;
6103 }
6104 self.add_signature_with_metadata(
6105 code_unit.clone(),
6106 cpp_signature_metadata(
6107 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
6108 function_declarator,
6109 cpp_callable_linkage(class_declaration, self.source, ancestry),
6110 self.source,
6111 &recovered_ancestry,
6112 )
6113 .with_declaration_only(false),
6114 );
6115 self.parsed.add_child(class_unit.clone(), code_unit);
6116 }
6117
6118 fn visit_recovered_fragment_prefix_members<'tree>(
6119 &mut self,
6120 origin: Node<'_>,
6121 root: Node<'tree>,
6122 constructor_start: usize,
6123 class_unit: &CodeUnit,
6124 scope: &ScopeInfo,
6125 ancestry: &ParentIndex<'tree>,
6126 ) {
6127 self.with_recovery_source_context(origin, |visitor| {
6128 visitor.visit_recovered_fragment_prefix_members_in_context(
6129 root,
6130 constructor_start,
6131 class_unit,
6132 scope,
6133 ancestry,
6134 );
6135 });
6136 }
6137
6138 fn visit_recovered_fragment_prefix_members_in_context<'tree>(
6139 &mut self,
6140 root: Node<'tree>,
6141 constructor_start: usize,
6142 class_unit: &CodeUnit,
6143 scope: &ScopeInfo,
6144 ancestry: &ParentIndex<'tree>,
6145 ) {
6146 let member_scope = ScopeInfo {
6147 package_name: class_unit.package_name().to_string(),
6148 module: scope.module.clone(),
6149 class_unit: Some(class_unit.clone()),
6150 template_signature: scope.template_signature.clone(),
6151 template_metadata: None,
6152 declarations_are_fields: true,
6153 recovered_specialization_member_scope: false,
6154 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6155 };
6156 let mut root_cursor = root.walk();
6157 let members = root.named_children(&mut root_cursor).collect::<Vec<_>>();
6158 let mut stack = vec![root];
6159 while let Some(current) = stack.pop() {
6160 if current.kind() == "comment" || current.start_byte() >= constructor_start {
6161 continue;
6162 }
6163 if current
6164 .parent()
6165 .is_some_and(|parent| same_node(parent, root))
6166 && cpp_reparsed_defaulted_constructor_is_indexable(
6167 current,
6168 &members,
6169 Some(class_unit.identifier()),
6170 self.source,
6171 )
6172 {
6173 self.visit_recovered_defaulted_constructor(current, &member_scope, ancestry);
6174 continue;
6175 }
6176 if current.end_byte() <= constructor_start
6177 && current.kind() != "translation_unit"
6178 && current.kind() != "labeled_statement"
6179 && current.kind() != "ERROR"
6180 {
6181 let mut work_stack = Vec::new();
6182 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
6183 self.drain_cpp_recovery_work(work_stack, ancestry);
6184 continue;
6185 }
6186 if matches!(
6187 current.kind(),
6188 "translation_unit" | "labeled_statement" | "ERROR"
6189 ) {
6190 let mut cursor = current.walk();
6191 stack.extend(current.named_children(&mut cursor));
6192 }
6193 }
6194 }
6195
6196 fn visit_recovered_defaulted_constructor(
6197 &mut self,
6198 statement: Node<'_>,
6199 scope: &ScopeInfo,
6200 ancestry: &ParentIndex<'_>,
6201 ) {
6202 let Some(parent) = scope.class_unit.clone() else {
6203 return;
6204 };
6205 let Some(assignment) = statement.named_child(0) else {
6206 return;
6207 };
6208 let Some(call) = assignment.child_by_field_name("left") else {
6209 return;
6210 };
6211 let Some(function_name) = call.child_by_field_name("function") else {
6212 return;
6213 };
6214 let Some(arguments) = call.child_by_field_name("arguments") else {
6215 return;
6216 };
6217 if function_name.kind() != "identifier"
6218 || node_text(function_name, self.source) != parent.identifier()
6219 || arguments.kind() != "argument_list"
6220 || arguments.named_child_count() != 0
6221 {
6222 return;
6223 }
6224 let function = FunctionInfo {
6225 package_name: scope.package_name.clone(),
6226 owner: Some(CppMemberOwner::Unit(parent.clone())),
6227 name: parent.identifier().to_string(),
6228 signature: normalize_cpp_whitespace(node_text(arguments, self.source)),
6229 };
6230 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6231 if self.parsed.contains_declaration(&code_unit) {
6232 return;
6233 }
6234 self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6235 let metadata = SignatureMetadata::new(
6236 normalize_cpp_whitespace(node_text(statement, self.source)),
6237 Vec::new(),
6238 )
6239 .with_declaration_only(true)
6240 .with_callable_arity(CallableArity::exact(0))
6241 .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6242 .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6245 self.add_signature_with_metadata(code_unit.clone(), metadata);
6246 if let Some(declaration) = self
6247 .source_links
6248 .get(&code_unit)
6249 .and_then(|links| links.last())
6250 .copied()
6251 {
6252 let mut properties = self.source_properties.borrow_mut();
6253 let fact = properties
6254 .get_mut(&declaration)
6255 .expect("recovered defaulted constructor source fact");
6256 fact.occurrence_role =
6257 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6258 fact.callable_is_constructor = true;
6259 }
6260 self.parsed.add_child(parent, code_unit);
6261 }
6262
6263 fn visit_bare_constructor_expression<'tree>(
6270 &mut self,
6271 statement: Node<'tree>,
6272 scope: &ScopeInfo,
6273 ancestry: &ParentIndex<'tree>,
6274 ) {
6275 let Some(parent) = scope.class_unit.clone() else {
6276 return;
6277 };
6278 const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
6279 let region = &self.source[statement.start_byte()..statement.end_byte()];
6280 let synthetic = format!("{PREFIX}{region} }};");
6281 let mut parser = tree_sitter::Parser::new();
6282 if parser
6283 .set_language(&tree_sitter_cpp::LANGUAGE.into())
6284 .is_err()
6285 {
6286 return;
6287 }
6288 let Some(tree) = parser.parse(&synthetic, None) else {
6289 return;
6290 };
6291 let root = tree.root_node();
6292 if root.has_error() {
6293 return;
6294 }
6295 let mut declarator = None;
6296 let mut stack = vec![root];
6297 while let Some(current) = stack.pop() {
6298 if current.kind() == "function_declarator" {
6299 if declarator.is_some() {
6300 return;
6301 }
6302 declarator = Some(current);
6303 continue;
6304 }
6305 let mut cursor = current.walk();
6306 stack.extend(current.named_children(&mut cursor));
6307 }
6308 let Some(declarator) = declarator else {
6309 return;
6310 };
6311 let Some(parameters_node) = declarator.child_by_field_name("parameters") else {
6312 return;
6313 };
6314 let signature = cpp_parameter_signature(parameters_node, &synthetic);
6315 let mut parameter_labels = Vec::new();
6316 let mut parameter_cursor = parameters_node.walk();
6317 for parameter in parameters_node.named_children(&mut parameter_cursor) {
6318 if matches!(
6319 parameter.kind(),
6320 "parameter_declaration" | "optional_parameter_declaration"
6321 ) && let Some(name) = parameter
6322 .child_by_field_name("declarator")
6323 .and_then(|declarator| extract_variable_name(declarator, &synthetic))
6324 {
6325 parameter_labels.push(name);
6326 }
6327 }
6328 let arity = parameter_labels.len();
6329 let function = FunctionInfo {
6330 package_name: scope.package_name.clone(),
6331 owner: Some(CppMemberOwner::Unit(parent.clone())),
6332 name: parent.identifier().to_string(),
6333 signature,
6334 };
6335 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6336 if self.parsed.contains_declaration(&code_unit) {
6337 return;
6338 }
6339 self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6340 let metadata = SignatureMetadata::with_parameter_labels(
6341 normalize_cpp_whitespace(node_text(statement, self.source)),
6342 parameter_labels,
6343 )
6344 .with_declaration_only(true)
6345 .with_callable_arity(CallableArity::exact(arity))
6346 .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6347 .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6351 self.add_signature_with_metadata(code_unit.clone(), metadata);
6352 if let Some(declaration) = self
6353 .source_links
6354 .get(&code_unit)
6355 .and_then(|links| links.last())
6356 .copied()
6357 {
6358 let mut properties = self.source_properties.borrow_mut();
6359 let fact = properties
6360 .get_mut(&declaration)
6361 .expect("recovered constructor source fact");
6362 fact.occurrence_role =
6363 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6364 fact.callable_is_constructor = true;
6365 }
6366 self.parsed.add_child(parent, code_unit);
6367 }
6368
6369 fn with_recovery_source_context<T>(
6372 &mut self,
6373 node: Node<'_>,
6374 walk: impl FnOnce(&mut Self) -> T,
6375 ) -> T {
6376 self.with_recovery_source_context_at(node, node, walk)
6377 }
6378
6379 fn with_recovery_source_context_at<T>(
6385 &mut self,
6386 node: Node<'_>,
6387 preprocessor_anchor: Node<'_>,
6388 walk: impl FnOnce(&mut Self) -> T,
6389 ) -> T {
6390 use crate::graph::resolver::{
6391 enclosing_namespace_context, merge_preprocessor_guards,
6392 preprocessor_conditional_family_for_declaration_with_cache,
6393 preprocessor_conditional_family_node, preprocessor_guard_environment_with_cache,
6394 };
6395 let previous = self.active_source_context.take();
6396 let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
6397 let inherited = (!primary).then_some(previous.as_ref()).flatten();
6398 let local_guards = preprocessor_guard_environment_with_cache(
6399 preprocessor_anchor,
6400 self.source,
6401 &mut self.preprocessor_context_cache,
6402 );
6403 let guards = match inherited {
6404 Some(outer) => outer
6405 .guards
6406 .as_ref()
6407 .zip(local_guards.as_ref())
6408 .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
6409 None => local_guards,
6410 };
6411 let local_namespace = enclosing_namespace_context(node, self.source);
6412 let namespace = match (
6413 inherited.and_then(|outer| outer.child_namespace.as_ref()),
6414 local_namespace,
6415 ) {
6416 (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
6417 (Some(outer), None) => Some(outer.clone()),
6418 (None, local) => local,
6419 };
6420 let outer_lexical_scope =
6421 inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
6422 let mut local_owners = Vec::new();
6423 let mut parent = node.parent();
6424 while let Some(ancestor) = parent {
6425 if matches!(
6426 ancestor.kind(),
6427 "namespace_definition"
6428 | "class_specifier"
6429 | "struct_specifier"
6430 | "union_specifier"
6431 | "enum_specifier"
6432 ) && let Some(name) = ancestor.child_by_field_name("name")
6433 {
6434 local_owners.push(node_text(name, self.source).to_owned());
6435 }
6436 parent = ancestor.parent();
6437 }
6438 local_owners.reverse();
6439 let mut child_lexical_scope = outer_lexical_scope.clone();
6440 child_lexical_scope.extend(local_owners);
6441 if matches!(
6442 node.kind(),
6443 "namespace_definition"
6444 | "class_specifier"
6445 | "struct_specifier"
6446 | "union_specifier"
6447 | "enum_specifier"
6448 ) && let Some(name) = node.child_by_field_name("name")
6449 {
6450 child_lexical_scope.push(node_text(name, self.source).to_owned());
6451 }
6452 let child_namespace = if node.kind() == "namespace_definition" {
6453 node.child_by_field_name("name")
6454 .map(|name| {
6455 let local = node_text(name, self.source);
6456 namespace
6457 .as_ref()
6458 .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
6459 })
6460 .or_else(|| namespace.clone())
6461 } else {
6462 namespace.clone()
6463 };
6464 let intern = |family: Node<'_>| {
6465 let mut collector = self.source_collector.borrow_mut();
6466 if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
6467 collector.intern_node(family)
6468 } else {
6469 collector
6470 .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
6471 }
6472 };
6473 let family = preprocessor_conditional_family_node(preprocessor_anchor)
6474 .map(intern)
6475 .or_else(|| inherited.and_then(|outer| outer.family));
6476 let exhaustive_family = preprocessor_conditional_family_for_declaration_with_cache(
6477 preprocessor_anchor,
6478 &mut self.preprocessor_context_cache,
6479 )
6480 .map(intern)
6481 .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
6482 let local_completion = crate::graph::resolver::callable_guard_completion_byte_with_cache(
6483 preprocessor_anchor,
6484 self.source,
6485 &mut self.preprocessor_context_cache,
6486 );
6487 let callable_guard_completion_byte = match inherited {
6488 Some(outer) => local_completion
6489 .zip(outer.callable_guard_completion_byte)
6490 .map(|(local, outer)| local.max(outer)),
6491 None => local_completion,
6492 };
6493 self.active_source_context = Some(CppActiveSourceContext {
6494 guards,
6495 namespace: child_namespace.clone(),
6496 child_namespace,
6497 outer_lexical_scope: child_lexical_scope.clone(),
6498 child_lexical_scope,
6499 family,
6500 exhaustive_family,
6501 callable_guard_completion_byte,
6502 });
6503 let result = walk(self);
6504 self.active_source_context = previous;
6505 result
6506 }
6507
6508 fn visit_node<'tree>(
6509 &mut self,
6510 node: Node<'tree>,
6511 scope: &ScopeInfo,
6512 stack: &mut Vec<CppWork<'tree>>,
6513 ancestry: &ParentIndex<'tree>,
6514 ) {
6515 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
6516 self.visit_node(node, &recovered_scope, stack, ancestry);
6517 return;
6518 }
6519 if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
6520 self.visit_node(node, &recovered_scope, stack, ancestry);
6521 return;
6522 }
6523 if scope.class_unit.is_some()
6524 && let Some(label) = cpp_macro_swallowed_access_label(node, self.source)
6525 {
6526 self.visit_macro_swallowed_access_label_members(node, &label, scope, ancestry);
6527 return;
6528 }
6529 if node.kind() == "function_definition" && node.has_error() {
6535 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6536 }
6537 if let Some(FragmentedClassRecovery {
6538 declaration_node: class_node,
6539 name,
6540 raw_supertypes,
6541 body: fragmented,
6542 }) = fragmented_class_body(node, self.source)
6543 {
6544 let displaced_namespace_items =
6545 displaced_fragment_namespace_geometry(node, self.source)
6546 .map(|boundary| boundary.namespace_items)
6547 .unwrap_or_default();
6548 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
6549 let mut class_stack = Vec::new();
6550 let parser_visible_body =
6553 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
6554 .then(|| cpp_body_node(class_node))
6555 .flatten();
6556 let class_unit = self.visit_named_class_like_shape(
6557 class_node,
6558 name,
6559 parser_visible_body,
6560 true,
6561 Some(fragmented.class_range),
6562 Some(raw_supertypes),
6563 Some(extract_cpp_base_facts(class_node, self.source)),
6564 scope,
6565 &mut class_stack,
6566 ancestry,
6567 );
6568 let member_scope = ScopeInfo {
6569 package_name: class_unit.package_name().to_string(),
6570 module: scope.module.clone(),
6571 class_unit: Some(class_unit.clone()),
6572 template_signature: scope.template_signature.clone(),
6573 template_metadata: None,
6574 declarations_are_fields: true,
6575 recovered_specialization_member_scope: false,
6576 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6577 };
6578 let complete = outcome.is_some_and(|outcome| {
6579 self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
6580 });
6581 if complete {
6582 self.consumed_fragment_regions
6583 .push((node.start_byte(), fragmented.class_range.end_byte));
6584 } else {
6585 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6595 if candidate.start_byte() >= fragmented.reparse_end {
6596 break;
6597 }
6598 if cpp_fragment_sibling_is_class_member(
6599 candidate,
6600 fragmented.reparse_end,
6601 self.source,
6602 ) {
6603 self.recovered_class_sibling_scopes
6604 .insert(candidate.id(), member_scope.clone());
6605 }
6606 }
6607 if class_node.kind() == "ERROR" && class_node.has_error() {
6614 stack.push(CppWork::Container(CppContainer {
6615 node: class_node,
6616 scope: member_scope,
6617 }));
6618 }
6619 }
6620 for item in displaced_namespace_items {
6621 self.recovered_class_sibling_scopes
6622 .insert(item.id(), scope.clone());
6623 }
6624 stack.extend(class_stack);
6625 return;
6626 }
6627 if self.visit_folded_aggregate(node, scope) {
6628 return;
6629 }
6630 match node.kind() {
6631 "template_declaration" => {
6632 if let Some(recovered) =
6633 recover_fragmented_preprocessor_class(node, self.source, ancestry)
6634 {
6635 let mut template_scope = scope.clone();
6636 template_scope.template_signature =
6637 cpp_template_signature(node, recovered.declaration_node, self.source);
6638 template_scope.template_metadata =
6639 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
6640 let raw_supertypes =
6641 Some(extract_cpp_supertypes(recovered.class_node, self.source));
6642 let mut class_stack = Vec::new();
6643 let class_unit = self.visit_named_class_like_shape(
6644 recovered.class_node,
6645 recovered.name,
6646 Some(recovered.body),
6647 true,
6648 Some(recovered.range),
6649 raw_supertypes,
6650 Some(extract_cpp_base_facts(recovered.class_node, self.source)),
6651 &template_scope,
6652 &mut class_stack,
6653 ancestry,
6654 );
6655 self.parsed.record_materialization(
6656 MaterializationRecord::RecoveredDeclaration {
6657 recovery: recovered.range,
6658 unit: class_unit.clone(),
6659 },
6660 );
6661 let member_scope = ScopeInfo {
6662 package_name: template_scope.package_name.clone(),
6663 module: template_scope.module.clone(),
6664 class_unit: Some(class_unit.clone()),
6665 template_signature: template_scope.template_signature.clone(),
6666 template_metadata: None,
6667 declarations_are_fields: true,
6668 recovered_specialization_member_scope: recovered
6669 .class_node
6670 .child_by_field_name("name")
6671 .is_some_and(|name| name.kind() == "template_type"),
6672 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
6673 };
6674 for tail_member in recovered.tail_members.into_iter().rev() {
6675 stack.push(CppWork::Node(CppNodeWork {
6676 node: tail_member,
6677 scope: member_scope.clone(),
6678 }));
6679 }
6680 stack.extend(class_stack);
6681 for sibling in recovered.member_siblings {
6682 self.recovered_class_sibling_scopes
6683 .insert(sibling.id(), member_scope.clone());
6684 }
6685 return;
6686 }
6687 for index in (0..node.named_child_count()).rev() {
6688 let Some(child) = node.named_child(index) else {
6689 continue;
6690 };
6691 if matches!(
6692 child.kind(),
6693 "class_specifier"
6694 | "struct_specifier"
6695 | "union_specifier"
6696 | "enum_specifier"
6697 | "function_definition"
6698 | "declaration"
6699 | "field_declaration"
6700 | "alias_declaration"
6701 | "namespace_definition"
6702 ) {
6703 let mut template_scope = scope.clone();
6704 template_scope.template_signature =
6705 cpp_template_signature(node, child, self.source);
6706 template_scope.template_metadata =
6707 cpp_template_metadata(node, child, self.source, ancestry);
6708 if let Some(recovered) = recover_fragmented_partial_specialization(
6709 node,
6710 child,
6711 self.source,
6712 ancestry,
6713 ) {
6714 let code_unit = self.visit_named_class_like_shape(
6715 recovered.declaration_node,
6716 recovered.name,
6717 None,
6718 true,
6719 Some(recovered.range),
6720 None,
6721 None,
6722 &template_scope,
6723 stack,
6724 ancestry,
6725 );
6726 self.parsed.record_materialization(
6727 MaterializationRecord::RecoveredDeclaration {
6728 recovery: recovered.range,
6729 unit: code_unit.clone(),
6730 },
6731 );
6732 let mut member_scope = template_scope.clone();
6733 member_scope.class_unit = Some(code_unit);
6734 member_scope.declarations_are_fields = true;
6735 member_scope.recovered_specialization_member_scope = true;
6736 for prefix_member in recovered.prefix_members.into_iter().rev() {
6737 stack.push(CppWork::Node(CppNodeWork {
6738 node: prefix_member,
6739 scope: member_scope.clone(),
6740 }));
6741 }
6742 for sibling in recovered.member_siblings {
6743 self.recovered_class_sibling_scopes
6744 .insert(sibling.id(), member_scope.clone());
6745 }
6746 for following in recovered.following_declarations.into_iter().rev() {
6747 stack.push(CppWork::Node(CppNodeWork {
6748 node: following,
6749 scope: scope.clone(),
6750 }));
6751 }
6752 return;
6753 }
6754 stack.push(CppWork::Node(CppNodeWork {
6755 node: child,
6756 scope: template_scope,
6757 }));
6758 }
6759 }
6760 }
6761 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
6762 "linkage_specification" => {
6763 if let Some(body) = cpp_body_node(node) {
6764 let work = if body.kind() == "declaration_list" {
6768 CppWork::Container(CppContainer {
6769 node: body,
6770 scope: scope.clone(),
6771 })
6772 } else {
6773 CppWork::Node(CppNodeWork {
6774 node: body,
6775 scope: scope.clone(),
6776 })
6777 };
6778 stack.push(work);
6779 } else {
6780 stack.push(CppWork::Container(CppContainer {
6781 node,
6782 scope: scope.clone(),
6783 }));
6784 }
6785 }
6786 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
6787 self.visit_class_like(node, scope, stack, ancestry)
6788 }
6789 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
6790 "ERROR" => {
6797 self.visit_object_macro_error_classes(node, scope);
6798 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6799 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6800 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6801 return;
6802 }
6803 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6804 return;
6805 }
6806 self.visit_macro_swallowed_function_declarations(node, scope);
6807 self.visit_macro_wrapped_declarations(node, scope, ancestry);
6808 self.visit_stranded_class_members(node, scope, ancestry);
6809 stack.push(CppWork::Container(CppContainer {
6810 node,
6811 scope: scope.clone(),
6812 }));
6813 }
6814 }
6815 "declaration" => {
6816 if node.has_error() {
6817 if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6824 return;
6825 }
6826 }
6827 self.visit_prototype_macro_declarations(node, scope);
6833 if self.node_is_inside_consumed_fragment(node) {
6834 return;
6841 }
6842 if scope.class_unit.is_some()
6843 && scope.declarations_are_fields
6844 && scope.recovered_specialization_member_scope
6845 && let Some(alias_name) =
6846 recovered_using_declaration_alias_name(node, self.source)
6847 {
6848 self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
6849 } else {
6850 self.visit_declaration(
6851 node,
6852 scope,
6853 scope.declarations_are_fields,
6854 stack,
6855 ancestry,
6856 )
6857 }
6858 }
6859 "expression_statement" => {
6873 if node.has_error() {
6874 self.visit_prototype_macro_declarations(node, scope);
6875 }
6876 }
6877 "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
6878 "preproc_call" => self.visit_preproc_call(node, scope),
6879 "type_definition" | "alias_declaration" => {
6880 self.visit_type_declaration(node, scope, stack, ancestry)
6881 }
6882 "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
6883 "preproc_include" => {}
6889 "preproc_linkage_open" | "preproc_linkage_close" => {}
6893 kind if preserves_declaration_scope_through_wrapper(
6894 kind,
6895 scope.class_unit.is_some(),
6896 ) =>
6897 {
6898 if kind == "labeled_statement" {
6904 self.visit_access_label_constructor(node, scope);
6905 }
6906 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
6907 let mut range = cpp_declaration_range(node);
6908 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
6909 range.end_byte = boundary.end_byte;
6910 range.end_line = boundary.end_line;
6911 }
6912 self.parsed.record_materialization(
6913 MaterializationRecord::ConfigurationConditional { range },
6914 );
6915 if node.has_error() {
6916 let mut candidates = vec![node];
6925 while let Some(candidate) = candidates.pop() {
6926 if candidate.kind() == "ERROR"
6937 && !cpp_is_inside_namespace_body(candidate, ancestry)
6938 && self.visit_function_like_export_class_pair(
6939 candidate, scope, stack, ancestry,
6940 )
6941 {
6942 continue;
6943 }
6944 for index in (0..candidate.named_child_count()).rev() {
6945 candidates.push(
6946 candidate
6947 .named_child(index)
6948 .expect("index below the node's own named child count"),
6949 );
6950 }
6951 }
6952 }
6953 }
6954 stack.push(CppWork::Container(CppContainer {
6955 node,
6956 scope: scope.clone(),
6957 }))
6958 }
6959 _ => {
6965 self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
6966 }
6967 }
6968 }
6969
6970 fn visit_macro_swallowed_function_declarations<'tree>(
6971 &mut self,
6972 envelope: Node<'tree>,
6973 scope: &ScopeInfo,
6974 ) {
6975 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
6976 || envelope.kind() == "ERROR"
6977 && envelope
6978 .parent()
6979 .is_some_and(|parent| parent.kind() == "ERROR")
6980 {
6981 return;
6982 }
6983 let mut stack = (0..envelope.named_child_count())
6984 .filter_map(|index| envelope.named_child(index))
6985 .collect::<Vec<_>>();
6986 while let Some(node) = stack.pop() {
6987 if node.kind() == "function_declarator" {
6988 self.visit_error_swallowed_function_declaration(node, scope);
6989 }
6990 for child in named_children_iter(node) {
6991 stack.push(child);
6992 }
6993 }
6994 }
6995
6996 fn visit_macro_wrapped_declarations<'tree>(
7000 &mut self,
7001 envelope: Node<'tree>,
7002 scope: &ScopeInfo,
7003 ancestry: &ParentIndex<'tree>,
7004 ) {
7005 let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
7006 if recovered.is_empty() {
7007 return;
7008 }
7009 let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
7010 self.record_recovered_declarations(recovery, envelope, |visitor| {
7011 for declaration in recovered {
7012 visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
7013 }
7014 });
7015 }
7016
7017 fn visit_collapsed_macro_declaration_run<'tree>(
7044 &mut self,
7045 envelope: Node<'tree>,
7046 scope: &ScopeInfo,
7047 ancestry: &ParentIndex<'tree>,
7048 ) -> bool {
7049 let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
7050 return false;
7051 };
7052 let start = envelope.start_byte();
7053 let end = run.region_end;
7054 let recovery = cpp_recovery_window(self.source, start, end);
7055 self.record_recovered_declarations(recovery, envelope, |visitor| {
7056 let mut position = start;
7057 while position < end {
7058 let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
7059 return;
7060 };
7061 let root = tree.root_node();
7062 let ancestry = visitor.recovery_ancestry(root);
7066 let mut cursor = root.walk();
7067 let collapsed =
7068 root.named_children(&mut cursor)
7069 .enumerate()
7070 .find_map(|(index, item)| {
7071 collapsed_macro_declaration_run(item, visitor.source, &ancestry)
7072 .map(|run| (index, item, run))
7073 });
7074 let mut stack = Vec::new();
7079 push_cpp_sibling_range(
7080 root,
7081 0,
7082 collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
7083 scope.clone(),
7084 &mut stack,
7085 );
7086 visitor.drain_cpp_work(stack, &ancestry);
7087 let Some((_, item, run)) = collapsed else {
7088 return;
7089 };
7090 if let Some(head) =
7094 cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
7095 {
7096 let head_root = head.root_node();
7097 let head_ancestry = visitor.recovery_ancestry(head_root);
7098 visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
7099 }
7100 assert!(
7101 run.invocation_end > position,
7102 "a collapsed run at {position} must end after the byte the scan resumed \
7103 from, but ended at {}",
7104 run.invocation_end
7105 );
7106 position = run.invocation_end;
7107 }
7108 });
7109 self.consumed_fragment_regions.push((start, end));
7110 true
7111 }
7112
7113 fn visit_macro_swallowed_access_label_members<'tree>(
7130 &mut self,
7131 node: Node<'tree>,
7132 label: &CppSwallowedAccessLabel<'tree>,
7133 scope: &ScopeInfo,
7134 ancestry: &ParentIndex<'tree>,
7135 ) {
7136 let Some(body) = ancestry.parent(node) else {
7137 return;
7138 };
7139 let Some(class_like) = ancestry.parent(body) else {
7140 return;
7141 };
7142 let Some(class_parent) = ancestry.parent(class_like) else {
7146 return;
7147 };
7148 let (Some(open), Some(close)) = (body.child(0), body.child(body.child_count() - 1)) else {
7149 return;
7150 };
7151 if body.kind() != "field_declaration_list"
7152 || open.kind() != "{"
7153 || close.kind() != "}"
7154 || close.is_missing()
7155 {
7156 return;
7157 }
7158 let start = label.keyword.start_byte();
7159 let end = node.end_byte();
7160 let Some(tree) = parse_source_ranges_with_cancellation(
7161 &tree_sitter_cpp::LANGUAGE.into(),
7162 self.source,
7163 &[
7164 (class_like.start_byte(), open.end_byte()),
7165 (start, end),
7166 (close.start_byte(), close.end_byte()),
7167 ],
7168 None,
7169 ) else {
7170 return;
7171 };
7172 let root = tree.root_node();
7173 let Some(reparsed_body) = root
7174 .descendant_for_byte_range(open.start_byte(), open.end_byte())
7175 .and_then(|brace| brace.parent())
7176 .filter(|parent| parent.kind() == "field_declaration_list")
7177 else {
7178 return;
7179 };
7180 let mut cursor = reparsed_body.walk();
7181 let members = reparsed_body
7182 .named_children(&mut cursor)
7183 .enumerate()
7184 .filter(|(_, member)| member.start_byte() >= start && member.end_byte() <= end)
7185 .map(|(index, member)| (index, member.kind(), member.start_byte()))
7186 .collect::<Vec<_>>();
7187 let Some(&(first, "access_specifier", label_start)) = members.first() else {
7188 return;
7189 };
7190 if label_start != start {
7191 return;
7192 }
7193 let last = members.last().expect("the label is a member").0;
7194 let reparsed_ancestry = self.recovery_ancestry(root);
7195 let recovery = cpp_recovery_window(self.source, start, end);
7196 self.record_recovered_declarations(recovery, class_parent, |visitor| {
7197 let mut stack = Vec::new();
7198 push_cpp_sibling_range(
7199 reparsed_body,
7200 first + 1,
7201 last + 1,
7202 scope.clone(),
7203 &mut stack,
7204 );
7205 visitor.drain_cpp_work(stack, &reparsed_ancestry);
7206 });
7207 }
7208
7209 fn visit_stranded_class_members<'tree>(
7219 &mut self,
7220 node: Node<'tree>,
7221 scope: &ScopeInfo,
7222 ancestry: &ParentIndex<'tree>,
7223 ) {
7224 if scope.class_unit.is_none() || !scope.declarations_are_fields {
7225 return;
7226 }
7227 for member in stranded_declaration_run(node, self.source).declarations {
7228 self.add_macro_wrapped_declaration(member, scope, ancestry);
7229 }
7230 }
7231
7232 fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7240 let Some(class_unit) = scope.class_unit.clone() else {
7241 return;
7242 };
7243 if !scope.declarations_are_fields {
7244 return;
7245 }
7246 let class_name = class_unit.identifier().to_string();
7247 let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
7248 else {
7249 return;
7250 };
7251 let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
7252 return;
7253 };
7254 let root = tree.root_node();
7255 let Some(declarator) =
7256 cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
7257 else {
7258 return;
7259 };
7260 let reparsed_ancestry = self.recovery_ancestry(root);
7261 let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
7262 let range = cpp_declaration_range(definition.unwrap_or(declarator));
7263 let recovery = cpp_recovery_window(self.source, start, node.end_byte());
7264 self.record_recovered_declarations(recovery, node, |visitor| {
7265 visitor.add_macro_wrapped_declaration(
7266 MacroWrappedDeclaration {
7267 declarator,
7268 range,
7269 is_static: false,
7270 },
7271 scope,
7272 &reparsed_ancestry,
7273 );
7274 });
7275 }
7276
7277 fn visit_string_attribute_macro_type_members<'tree>(
7292 &mut self,
7293 node: Node<'tree>,
7294 scope: &ScopeInfo,
7295 ) -> bool {
7296 if node.kind() != "field_declaration" {
7297 return false;
7298 }
7299 let Some(specifier) = node
7300 .child_by_field_name("type")
7301 .filter(|type_node| type_node.kind() == "macro_type_specifier")
7302 else {
7303 return false;
7304 };
7305 if specifier
7306 .child_by_field_name("name")
7307 .is_none_or(|name| name.kind() != "identifier")
7308 {
7309 return false;
7310 }
7311 let mut cursor = specifier.walk();
7315 let Some(argument_error) = specifier
7316 .named_children(&mut cursor)
7317 .find(|child| child.kind() == "ERROR")
7318 else {
7319 return false;
7320 };
7321 if argument_error
7322 .child(0)
7323 .is_none_or(|first| first.kind() != "\"" || first.is_missing())
7324 || argument_error
7325 .child(argument_error.child_count().saturating_sub(1))
7326 .is_none_or(|last| last.kind() != ";" || last.is_missing())
7327 {
7328 return false;
7329 }
7330 let mut members: Vec<((usize, usize), String)> = Vec::new();
7331 for index in 0..argument_error.child_count() {
7332 let Some(child) = argument_error.child(index) else {
7333 continue;
7334 };
7335 if child.kind() == "type_descriptor" {
7336 let start = (0..index)
7337 .rev()
7338 .find_map(|previous| {
7339 argument_error
7340 .child(previous)
7341 .filter(|sibling| sibling.kind() == "explicit")
7342 })
7343 .map_or_else(|| child.start_byte(), |explicit| explicit.start_byte());
7344 let Some(name) = child
7345 .child_by_field_name("type")
7346 .and_then(|type_node| direct_identifier_name(type_node, self.source))
7347 else {
7348 return false;
7349 };
7350 members.push(((start, argument_error.end_byte()), name));
7351 }
7352 }
7353 let [(first_range, first_name)] = members.as_slice() else {
7354 return false;
7355 };
7356 let Some(second) = specifier
7357 .child_by_field_name("type")
7358 .filter(|type_node| type_node.kind() == "type_descriptor")
7359 else {
7360 return false;
7361 };
7362 let Some(second_name) = second
7363 .child_by_field_name("type")
7364 .and_then(|type_node| direct_identifier_name(type_node, self.source))
7365 else {
7366 return false;
7367 };
7368 let members = [
7369 (*first_range, first_name.as_str()),
7370 ((second.start_byte(), node.end_byte()), second_name.as_str()),
7371 ];
7372 let recovery = cpp_recovery_window(self.source, node.start_byte(), node.end_byte());
7373 self.record_recovered_declarations(recovery, node, |visitor| {
7374 for ((start, end), member_name) in members {
7375 let Some(tree) = cpp_reparse_region_items(visitor.source, start, end) else {
7376 continue;
7377 };
7378 let root = tree.root_node();
7379 let mut candidate = None;
7384 let mut stack = vec![root];
7385 while let Some(current) = stack.pop() {
7386 if current.kind() == "function_declarator"
7387 && current.start_byte() >= start
7388 && current.end_byte() <= end
7389 && cpp_function_declarator_name_node(current)
7390 .is_some_and(|name| node_text(name, visitor.source) == member_name)
7391 {
7392 if candidate.is_some() {
7393 candidate = None;
7394 break;
7395 }
7396 candidate = Some(current);
7397 continue;
7398 }
7399 let mut cursor = current.walk();
7400 stack.extend(current.named_children(&mut cursor));
7401 }
7402 let Some(declarator) = candidate else {
7403 continue;
7404 };
7405 let reparsed_ancestry = visitor.recovery_ancestry(root);
7406 visitor.add_macro_wrapped_declaration(
7407 MacroWrappedDeclaration {
7408 declarator,
7409 range: cpp_recovery_window(visitor.source, start, end),
7410 is_static: false,
7411 },
7412 scope,
7413 &reparsed_ancestry,
7414 );
7415 }
7416 });
7417 true
7418 }
7419
7420 fn add_macro_wrapped_declaration<'tree>(
7421 &mut self,
7422 declaration: MacroWrappedDeclaration<'tree>,
7423 scope: &ScopeInfo,
7424 ancestry: &ParentIndex<'tree>,
7425 ) {
7426 let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
7427 else {
7428 return;
7429 };
7430 let code_unit =
7431 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7432 let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
7433 let is_constructor = scope
7434 .class_unit
7435 .as_ref()
7436 .is_some_and(|class| function.name == class.identifier())
7437 || cpp_callable_is_structural_constructor(
7438 declaration.declarator,
7439 self.source,
7440 ancestry,
7441 );
7442 let already_declared = self.parsed.contains_declaration(&code_unit);
7443 if already_declared {
7444 self.parsed
7445 .record_navigation_range(code_unit.clone(), declaration.range);
7446 if definition.is_none() {
7447 return;
7448 }
7449 }
7452 self.add_declaration_with_range(
7453 ancestry,
7454 code_unit.clone(),
7455 declaration.range,
7456 Some(declaration.declarator),
7457 None,
7458 None,
7459 );
7460 if definition.is_some() {
7461 let recovered_declaration = *self
7462 .source_links
7463 .get(&code_unit)
7464 .and_then(|links| links.last())
7465 .expect("recovered definition source");
7466 let mut properties = self.source_properties.borrow_mut();
7467 let fact = properties
7468 .get_mut(&recovered_declaration)
7469 .expect("recovered definition source fact");
7470 fact.occurrence_role =
7471 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
7472 fact.callable_is_constructor |= is_constructor;
7473 }
7474 let signature = normalize_cpp_whitespace(
7475 self.source
7476 .get(declaration.range.start_byte..declaration.range.end_byte)
7477 .expect("a recovered declaration range covers one source range"),
7478 );
7479 let linkage = if declaration.is_static {
7480 CallableLinkage::Internal
7481 } else {
7482 cpp_callable_linkage(declaration.declarator, self.source, ancestry)
7483 };
7484 let declaration_only = definition.is_none();
7488 self.add_signature_with_metadata(
7489 code_unit.clone(),
7490 cpp_signature_metadata(
7491 signature,
7492 declaration.declarator,
7493 linkage,
7494 self.source,
7495 ancestry,
7496 )
7497 .with_declaration_only(declaration_only),
7498 );
7499 if already_declared {
7500 return;
7501 }
7502 if let Some(parent) = &scope.class_unit {
7503 self.parsed.add_child(parent.clone(), code_unit);
7504 } else if let Some(module) = &scope.module {
7505 self.parsed.add_child(module.clone(), code_unit);
7506 }
7507 }
7508
7509 fn visit_anonymous_local_aggregates_in_function<'tree>(
7514 &mut self,
7515 function: Node<'tree>,
7516 scope: &ScopeInfo,
7517 stack: &mut Vec<CppWork<'tree>>,
7518 ancestry: &ParentIndex<'tree>,
7519 ) {
7520 let Some(body) = cpp_body_node(function) else {
7521 return;
7522 };
7523 let block_scope;
7530 let scope = if scope.class_unit.is_some() {
7531 block_scope = ScopeInfo {
7532 class_unit: None,
7533 declarations_are_fields: false,
7534 ..scope.clone()
7535 };
7536 &block_scope
7537 } else {
7538 scope
7539 };
7540 let mut pending = vec![body];
7541 while let Some(node) = pending.pop() {
7542 if matches!(node.kind(), "function_definition" | "lambda_expression") {
7543 continue;
7544 }
7545 if node.kind() == "declaration"
7546 && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
7547 {
7548 continue;
7549 }
7550 if matches!(
7551 node.kind(),
7552 "class_specifier" | "struct_specifier" | "union_specifier"
7553 ) {
7554 continue;
7555 }
7556 let mut cursor = node.walk();
7557 let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
7558 children.reverse();
7559 pending.extend(children);
7560 }
7561 }
7562
7563 fn visit_error_swallowed_function_declaration<'tree>(
7564 &mut self,
7565 node: Node<'tree>,
7566 scope: &ScopeInfo,
7567 ) -> bool {
7568 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
7569 return false;
7570 };
7571 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
7572 return false;
7573 };
7574 let root = tree.root_node();
7575 let mut cursor = root.walk();
7576 let declarations = root
7577 .named_children(&mut cursor)
7578 .filter(|child| child.kind() != "comment")
7579 .collect::<Vec<_>>();
7580 let [declaration] = declarations.as_slice() else {
7581 return false;
7582 };
7583 if declaration.kind() != "declaration"
7584 || declaration.has_error()
7585 || declaration.start_byte() != start
7586 || declaration.end_byte() != end
7587 {
7588 return false;
7589 }
7590 let recovery = cpp_recovery_window(self.source, start, end);
7591 let reparsed_ancestry = self.recovery_ancestry(root);
7593 self.record_recovered_declarations(recovery, node, |visitor| {
7594 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
7595 });
7596 true
7597 }
7598
7599 fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7622 for candidate in cpp_prototype_macro_candidates(node, self.source) {
7623 let start = candidate.run_start;
7624 let end = candidate.semicolon_end;
7625 if self.byte_range_is_inside_consumed_fragment(start, end) {
7626 continue;
7627 }
7628 let Some(tree) = parse_source_ranges_with_cancellation(
7629 &tree_sitter_cpp::LANGUAGE.into(),
7630 self.source,
7631 &candidate.ranges(),
7632 None,
7633 ) else {
7634 continue;
7635 };
7636 let root = tree.root_node();
7637 let mut cursor = root.walk();
7638 let declarations = root
7639 .named_children(&mut cursor)
7640 .filter(|child| child.kind() != "comment")
7641 .collect::<Vec<_>>();
7642 let [declaration] = declarations.as_slice() else {
7643 continue;
7644 };
7645 if declaration.kind() != "declaration"
7646 || declaration.has_error()
7647 || declaration.start_byte() != start
7648 || declaration.end_byte() != end
7649 || declaration
7650 .child_by_field_name("declarator")
7651 .and_then(extract_function_declarator)
7652 .is_none()
7653 {
7654 continue;
7655 }
7656 let recovery = cpp_recovery_window(self.source, start, end);
7657 let reparsed_ancestry = self.recovery_ancestry(root);
7660 self.record_recovered_declarations(recovery, node, |visitor| {
7661 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
7662 });
7663 self.consumed_fragment_regions.push((start, end));
7664 }
7665 }
7666
7667 fn declare_namespace_levels(
7673 &mut self,
7674 mut package_name: String,
7675 levels: Vec<(String, Range, Option<Range>)>,
7676 node: Node<'_>,
7677 ancestry: &ParentIndex<'_>,
7678 ) -> (String, Option<CodeUnit>) {
7679 let mut module = None;
7680 for (component, range, name_range) in levels {
7681 let full_name = if package_name.is_empty() {
7682 component
7683 } else {
7684 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7685 };
7686 let level = CodeUnit::new_fq(
7687 self.file.clone(),
7688 CodeUnitType::Module,
7689 "",
7690 full_name.clone(),
7691 cpp_namespace_fq(&full_name),
7692 );
7693 if !self.parsed.contains_declaration(&level) {
7694 if let Some(name_range) = name_range {
7695 self.add_declaration_with_name_range(
7696 level.clone(),
7697 range,
7698 name_range,
7699 Some((ancestry, node)),
7700 None,
7701 None,
7702 );
7703 } else {
7704 self.add_declaration_with_range(
7705 ancestry,
7706 level.clone(),
7707 range,
7708 Some(node),
7709 None,
7710 None,
7711 );
7712 }
7713 }
7714 package_name = full_name;
7715 module = Some(level);
7716 }
7717 (package_name, module)
7718 }
7719
7720 fn declare_recovered_namespace_levels(
7727 &mut self,
7728 mut package_name: String,
7729 levels: Vec<(String, Range, Range)>,
7730 ) -> (String, Option<CodeUnit>) {
7731 let mut module = None;
7732 for (component, range, name_range) in levels {
7733 let full_name = if package_name.is_empty() {
7734 component
7735 } else {
7736 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7737 };
7738 let level = CodeUnit::new_fq(
7739 self.file.clone(),
7740 CodeUnitType::Module,
7741 "",
7742 full_name.clone(),
7743 cpp_namespace_fq(&full_name),
7744 );
7745 if !self.parsed.contains_declaration(&level) {
7746 self.add_declaration_with_name_range(
7747 level.clone(),
7748 range,
7749 name_range,
7750 None,
7751 None,
7752 None,
7753 );
7754 }
7755 package_name = full_name;
7756 module = Some(level);
7757 }
7758 (package_name, module)
7759 }
7760
7761 fn recovered_namespace_scope(
7766 &mut self,
7767 node: Node<'_>,
7768 scope: &ScopeInfo,
7769 ) -> Option<ScopeInfo> {
7770 self.orphaned_namespaces.region_at(node.start_byte())?;
7771 let levels = self
7779 .orphaned_namespaces
7780 .enclosing_namespace_levels(node, self.source);
7781 let package_name = levels
7782 .iter()
7783 .map(|(component, ..)| component.as_str())
7784 .collect::<Vec<_>>()
7785 .join(CPP_PACKAGE_SEPARATOR);
7786 if package_name == scope.package_name {
7787 return None;
7788 }
7789 let (package_name, module) = self.declare_recovered_namespace_levels(String::new(), levels);
7790 Some(ScopeInfo {
7791 package_name,
7792 module,
7793 class_unit: None,
7798 template_signature: None,
7799 template_metadata: None,
7800 declarations_are_fields: false,
7801 recovered_specialization_member_scope: false,
7802 visible_using_namespaces: scope.visible_using_namespaces.clone(),
7803 })
7804 }
7805
7806 fn visit_namespace<'tree>(
7807 &mut self,
7808 node: Node<'tree>,
7809 scope: &ScopeInfo,
7810 stack: &mut Vec<CppWork<'tree>>,
7811 ancestry: &ParentIndex<'tree>,
7812 ) {
7813 let name_node = node.child_by_field_name("name");
7814 let Some(name_node) = name_node else {
7815 if let Some(body) = cpp_body_node(node) {
7816 stack.push(CppWork::Container(CppContainer {
7817 node: body,
7818 scope: scope.clone(),
7819 }));
7820 }
7821 return;
7822 };
7823 let explicitly_global = name_node
7830 .child(0)
7831 .is_some_and(|child| !child.is_named() && child.kind() == "::");
7832 let components = cpp_namespace_name_components(name_node, self.source);
7833 if components.is_empty() {
7834 return;
7835 }
7836 let package_name = if explicitly_global {
7842 String::new()
7843 } else {
7844 scope.package_name.clone()
7845 };
7846 let definition = node_range(node);
7847 let mut component_ranges =
7851 cpp_namespace_name_component_ranges(name_node, self.source).into_iter();
7852 let (package_name, module) = self.declare_namespace_levels(
7853 package_name,
7854 components
7855 .into_iter()
7856 .map(|component| {
7857 let name_range = component_ranges
7858 .next()
7859 .filter(|(name, _)| *name == component)
7860 .map(|(_, range)| range);
7861 (component, definition, name_range)
7862 })
7863 .collect(),
7864 node,
7865 ancestry,
7866 );
7867
7868 let namespace_scope = ScopeInfo {
7869 package_name,
7870 module,
7871 class_unit: None,
7879 template_signature: scope.template_signature.clone(),
7880 template_metadata: scope.template_metadata.clone(),
7881 declarations_are_fields: false,
7882 recovered_specialization_member_scope: false,
7883 visible_using_namespaces: scope.visible_using_namespaces.clone(),
7884 };
7885 let container = cpp_body_node(node).unwrap_or(node);
7886 let mut candidates = vec![container];
7894 while let Some(candidate) = candidates.pop() {
7895 if matches!(
7896 candidate.kind(),
7897 "ERROR" | "function_definition" | "labeled_statement"
7898 ) && self.visit_embedded_function_like_export_classes(
7899 candidate,
7900 &namespace_scope,
7901 stack,
7902 ancestry,
7903 ) {
7904 continue;
7905 }
7906 for index in (0..candidate.named_child_count()).rev() {
7907 candidates.push(
7908 candidate
7909 .named_child(index)
7910 .expect("index below the node's own named child count"),
7911 );
7912 }
7913 }
7914 stack.push(CppWork::Container(CppContainer {
7915 node: container,
7916 scope: namespace_scope,
7917 }));
7918 }
7919
7920 fn visit_class_like<'tree>(
7921 &mut self,
7922 node: Node<'tree>,
7923 scope: &ScopeInfo,
7924 stack: &mut Vec<CppWork<'tree>>,
7925 ancestry: &ParentIndex<'tree>,
7926 ) {
7927 let Some(name) = class_like_name(node, self.source, ancestry) else {
7928 return;
7929 };
7930 let name = qualified_class_name_chain(node, self.source, scope)
7931 .map(|chain| chain.join("$"))
7932 .unwrap_or(name);
7933 self.visit_named_class_like(node, name, scope, stack, ancestry);
7934 }
7935
7936 fn visit_named_class_like<'tree>(
7937 &mut self,
7938 node: Node<'tree>,
7939 name: String,
7940 scope: &ScopeInfo,
7941 stack: &mut Vec<CppWork<'tree>>,
7942 ancestry: &ParentIndex<'tree>,
7943 ) {
7944 let body = cpp_body_node(node);
7945 let definition_body_present = body.is_some();
7946 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
7947 .then(|| extract_cpp_supertypes(node, self.source));
7948 self.visit_named_class_like_shape(
7949 node,
7950 name,
7951 body,
7952 definition_body_present,
7953 None,
7954 raw_supertypes,
7955 Some(extract_cpp_base_facts(node, self.source)),
7956 scope,
7957 stack,
7958 ancestry,
7959 );
7960 }
7961
7962 fn mints_tag_at_enclosing_c_scope(
7970 &self,
7971 declaration_node: Node<'_>,
7972 scope: &ScopeInfo,
7973 ancestry: &ParentIndex<'_>,
7974 ) -> bool {
7975 if scope.class_unit.is_none()
7976 || !matches!(
7977 declaration_node.kind(),
7978 "struct_specifier" | "union_specifier" | "enum_specifier"
7979 )
7980 || class_like_name(declaration_node, self.source, ancestry).is_none()
7981 {
7982 return false;
7983 }
7984 let mut ancestor = ancestry.parent(declaration_node);
7988 while let Some(current) = ancestor {
7989 if current.kind() == "class_specifier" {
7990 return false;
7991 }
7992 ancestor = ancestry.parent(current);
7993 }
7994 true
7995 }
7996
7997 #[allow(clippy::too_many_arguments)]
7998 fn visit_named_class_like_shape<'tree>(
7999 &mut self,
8000 declaration_node: Node<'tree>,
8001 name: String,
8002 body: Option<Node<'tree>>,
8003 definition_body_present: bool,
8004 explicit_range: Option<Range>,
8005 raw_supertypes: Option<Vec<String>>,
8006 base_facts: Option<Vec<CppBaseSpecifierFact>>,
8007 scope: &ScopeInfo,
8008 stack: &mut Vec<CppWork<'tree>>,
8009 ancestry: &ParentIndex<'tree>,
8010 ) -> CodeUnit {
8011 let displaced_macro_tail = if explicit_range.is_none() {
8012 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
8013 } else {
8014 None
8015 };
8016 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
8017 let recovered_scope = self.scope_for_recovered_exported_class(
8018 declaration_node,
8019 &name,
8020 definition_body_present,
8021 scope,
8022 ancestry,
8023 );
8024 let c_tag_scope;
8034 let c_tag_scope_applies =
8035 self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
8036 if !self.c_tag_semantics && c_tag_scope_applies {
8037 self.c_tag_scope_witness = true;
8038 }
8039 let scope = if self.c_tag_semantics && c_tag_scope_applies {
8040 c_tag_scope = ScopeInfo {
8041 class_unit: None,
8042 ..recovered_scope.clone()
8043 };
8044 &c_tag_scope
8045 } else {
8046 &recovered_scope
8047 };
8048 let short_name = if let Some(parent) = &scope.class_unit {
8049 cpp_join_nested_short(parent.short_name(), &name)
8050 } else {
8051 name.clone()
8052 };
8053 let qualified_chain = if scope.class_unit.is_none() {
8060 qualified_class_name_chain(declaration_node, self.source, scope)
8061 .filter(|chain| chain.join("$") == name)
8062 } else {
8063 None
8064 };
8065 let fq = if let Some(chain) = qualified_chain {
8066 let mut fq = FqName::new();
8067 cpp_push_package(&mut fq, &scope.package_name);
8068 let mut first = true;
8069 for component in chain {
8070 let kind = if first {
8071 SegmentKind::Type
8072 } else {
8073 SegmentKind::Nested
8074 };
8075 fq.push(segment_interner().intern(&component, kind));
8076 first = false;
8077 }
8078 fq
8079 } else {
8080 cpp_leaf_fq(
8081 &scope.package_name,
8082 scope.class_unit.as_ref(),
8083 &name,
8084 SegmentKind::Nested,
8085 SegmentKind::Type,
8086 )
8087 };
8088 let code_unit = CodeUnit::with_signature_and_fq(
8089 self.file.clone(),
8090 CodeUnitType::Class,
8091 scope.package_name.clone(),
8092 short_name,
8093 scope.template_signature.clone(),
8094 false,
8095 fq,
8096 );
8097 let has_body = definition_body_present;
8098 if !has_body && self.parsed.contains_declaration(&code_unit) {
8099 if let Some(range) = explicit_range {
8100 self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
8101 } else {
8102 self.link_source_declaration(ancestry, &code_unit, declaration_node);
8103 }
8104 self.parsed.record_navigation_range(
8105 code_unit.clone(),
8106 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
8107 );
8108 return code_unit;
8109 }
8110 if has_body {
8111 let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
8118 if let Some(range) = explicit_range {
8119 self.replace_declaration_with_range_deferred(
8120 ancestry,
8121 code_unit.clone(),
8122 range,
8123 Some(declaration_node),
8124 None,
8125 None,
8126 );
8127 } else {
8128 self.replace_declaration_deferred(
8129 ancestry,
8130 code_unit.clone(),
8131 declaration_node,
8132 None,
8133 None,
8134 );
8135 }
8136 for range in earlier_ranges {
8137 self.parsed.add_declaration_range(&code_unit, range);
8138 }
8139 } else {
8140 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8141 }
8142 if let Some(declaration) = self
8143 .source_links
8144 .get(&code_unit)
8145 .and_then(|links| links.last())
8146 .copied()
8147 {
8148 let mut properties = self.source_properties.borrow_mut();
8149 let fact = properties
8150 .get_mut(&declaration)
8151 .expect("linked class has source properties");
8152 fact.occurrence_role = if has_body {
8153 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
8154 } else {
8155 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
8156 };
8157 fact.class_strength = if has_body {
8158 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
8159 } else {
8160 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
8161 };
8162 if let Some(base_facts) = base_facts.as_ref() {
8163 fact.bases = base_facts.clone();
8164 }
8165 }
8166 if let Some(raw_supertypes) = raw_supertypes {
8167 self.parsed
8168 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
8169 }
8170 self.parsed.add_signature(
8171 code_unit.clone(),
8172 render_cpp_type_signature(
8173 declaration_node,
8174 self.source,
8175 scope.template_signature.as_deref(),
8176 ),
8177 );
8178 if let Some(metadata) = &scope.template_metadata {
8179 let primary_short_name = if let Some(parent) = &scope.class_unit {
8180 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
8181 } else {
8182 metadata.primary_name.clone()
8183 };
8184 let primary_fq_name = CodeUnit::new(
8185 self.file.clone(),
8186 CodeUnitType::Class,
8187 scope.package_name.clone(),
8188 primary_short_name,
8189 )
8190 .fq_name();
8191 let mut metadata = metadata.clone();
8192 metadata.primary_fq_name = primary_fq_name;
8193 self.parsed
8194 .set_cpp_template_metadata(code_unit.clone(), metadata);
8195 }
8196 if let Some(parent) = &scope.class_unit {
8197 self.parsed.add_child(parent.clone(), code_unit.clone());
8198 } else if let Some(module) = &scope.module {
8199 self.parsed.add_child(module.clone(), code_unit.clone());
8200 }
8201
8202 if let Some(body) = body {
8203 let mut nested_scope = scope.clone();
8204 nested_scope.class_unit = Some(code_unit.clone());
8205 nested_scope.template_signature = scope.template_signature.clone();
8206 nested_scope.template_metadata = None;
8211 nested_scope.recovered_specialization_member_scope =
8214 scope.template_metadata.as_ref().is_some_and(|metadata| {
8215 declaration_node.kind() == "function_definition" && metadata.is_specialization()
8216 });
8217 nested_scope.declarations_are_fields =
8218 is_recovered_exported_class_container(declaration_node, self.source)
8219 || nested_scope.recovered_specialization_member_scope;
8220 if let Some(displaced) = displaced_macro_tail {
8221 push_cpp_sibling_range(
8229 body,
8230 displaced.split_index,
8231 usize::MAX,
8232 scope.clone(),
8233 stack,
8234 );
8235 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
8236 } else {
8237 stack.push(CppWork::Container(CppContainer {
8238 node: body,
8239 scope: nested_scope,
8240 }));
8241 }
8242 }
8243 if declaration_node.kind() == "enum_specifier" {
8244 self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
8245 if !self.has_enum_enumerator_units(&code_unit) {
8246 self.visit_enum_enumerators_from_text(
8247 ancestry,
8248 declaration_node,
8249 scope,
8250 &code_unit,
8251 );
8252 }
8253 }
8254 code_unit
8255 }
8256
8257 fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
8265 if self.field_owners.is_none() {
8266 self.field_owners = Some(CppFieldOwnerIndex::of(
8267 self.parsed.declarations().iter(),
8268 self.file,
8269 ));
8270 }
8271 debug_assert_eq!(
8272 parent.source(),
8273 self.file,
8274 "the walk's declarations are declarations of the file it is walking"
8275 );
8276 let carried = self
8277 .field_owners
8278 .as_ref()
8279 .expect("the index was just ensured")
8280 .owns_fields(parent.package_name(), parent.short_name());
8281
8282 #[cfg(debug_assertions)]
8283 assert_eq!(
8284 carried,
8285 cpp_declarations_hold_owned_fields(
8286 self.parsed.declarations(),
8287 self.file,
8288 parent.package_name(),
8289 parent.short_name()
8290 ),
8291 "the carried-forward field index must answer what a fresh declaration scan \
8292 answers for {}",
8293 parent.fq_name()
8294 );
8295
8296 carried
8297 }
8298
8299 fn visit_enum_enumerators(
8300 &mut self,
8301 ancestry: &ParentIndex<'_>,
8302 node: Node<'_>,
8303 scope: &ScopeInfo,
8304 parent: &CodeUnit,
8305 ) {
8306 walk_named_tree_preorder(node, false, |child| {
8307 if child.kind() != "enumerator" {
8308 return WalkControl::Continue;
8309 }
8310 let Some(name_node) = child.child_by_field_name("name") else {
8311 return WalkControl::Continue;
8312 };
8313 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
8314 if name.is_empty() {
8315 return WalkControl::Continue;
8316 }
8317 let signature = normalize_cpp_whitespace(node_text(child, self.source));
8318 self.visit_enum_enumerator(ancestry, child, scope, parent, &name, &signature);
8319 WalkControl::Continue
8320 });
8321 }
8322
8323 fn visit_enum_enumerator(
8324 &mut self,
8325 ancestry: &ParentIndex<'_>,
8326 declaration_node: Node<'_>,
8327 scope: &ScopeInfo,
8328 parent: &CodeUnit,
8329 name: &str,
8330 signature: &str,
8331 ) {
8332 let code_unit = CodeUnit::new_fq(
8333 self.file.clone(),
8334 CodeUnitType::Field,
8335 scope.package_name.clone(),
8336 cpp_join_member_short(parent.short_name(), name),
8337 parent
8338 .fq()
8339 .clone()
8340 .with_pushed(segment_interner().intern(name, SegmentKind::Member)),
8341 );
8342 if self.parsed.contains_declaration(&code_unit) {
8343 return;
8344 }
8345 self.add_declaration(
8346 ancestry,
8347 code_unit.clone(),
8348 declaration_node,
8349 Some(parent.clone()),
8350 None,
8351 );
8352 self.parsed.add_signature(code_unit, signature.to_string());
8353 }
8354
8355 fn visit_enum_enumerators_from_text(
8356 &mut self,
8357 ancestry: &ParentIndex<'_>,
8358 node: Node<'_>,
8359 scope: &ScopeInfo,
8360 parent: &CodeUnit,
8361 ) {
8362 let text = node_text(node, self.source);
8363 let Some((_, body)) = text.split_once('{') else {
8364 return;
8365 };
8366 let Some((body, _)) = body.rsplit_once('}') else {
8367 return;
8368 };
8369 for entry in body.split(',') {
8370 let trimmed = entry.trim();
8371 let name = trimmed
8372 .split('=')
8373 .next()
8374 .unwrap_or("")
8375 .split_whitespace()
8376 .next()
8377 .unwrap_or("");
8378 if name.is_empty() {
8379 continue;
8380 }
8381 self.visit_enum_enumerator(ancestry, node, scope, parent, name, trimmed);
8382 }
8383 }
8384
8385 fn visit_function_definition<'tree>(
8386 &mut self,
8387 node: Node<'tree>,
8388 scope: &ScopeInfo,
8389 stack: &mut Vec<CppWork<'tree>>,
8390 ancestry: &ParentIndex<'tree>,
8391 ) {
8392 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
8399 return;
8400 }
8401 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8407 return;
8408 }
8409 if let Some((type_modifier, name_modifier)) = omitted_semicolon_macro_field_pair(node) {
8416 self.visit_omitted_semicolon_macro_field(
8417 node,
8418 type_modifier,
8419 name_modifier,
8420 scope,
8421 ancestry,
8422 );
8423 }
8424 if scope.class_unit.is_some()
8430 && let Some((head_start, type_node)) =
8431 macro_return_template_parameter_field(node, self.source, ancestry)
8432 {
8433 self.visit_macro_return_template_parameter_field(
8434 node, head_start, type_node, scope, ancestry,
8435 );
8436 }
8437 if node.has_error() {
8438 self.visit_macro_swallowed_function_declarations(node, scope);
8439 }
8440 if let Some((class_node, name, raw_supertypes, base_facts)) =
8441 recover_exported_class_function_definition(node, self.source)
8442 {
8443 if let Some(body) = cpp_body_node(node)
8444 && let Some(close) = self
8445 .orphaned_namespaces
8446 .matching_close_brace(body.start_byte())
8447 && close.end_byte < body.end_byte()
8448 {
8449 let class_range = Range {
8450 start_byte: node.start_byte(),
8451 end_byte: close.end_byte,
8452 start_line: node.start_position().row + 1,
8453 end_line: close.end_line,
8454 };
8455 let mut head = vec![node];
8460 let mut keyword = None;
8461 let mut name_node = None;
8462 while let Some(part) = head.pop() {
8463 if part.start_byte() >= body.start_byte() || part.is_missing() {
8464 continue;
8465 }
8466 if matches!(part.kind(), "class" | "struct" | "union") {
8467 keyword = Some(part);
8468 }
8469 if matches!(part.kind(), "identifier" | "type_identifier")
8470 && node_text(part, self.source) == name
8471 {
8472 name_node = Some(part);
8473 }
8474 let mut cursor = part.walk();
8475 head.extend(part.children(&mut cursor));
8476 }
8477 if let (Some(keyword), Some(name_node)) = (keyword, name_node)
8478 && let Some(type_name) = keyword
8479 .parent()
8480 .and_then(|parent| parent.child_by_field_name("name"))
8481 && let Some(tree) = parse_source_ranges_with_cancellation(
8482 &tree_sitter_cpp::LANGUAGE.into(),
8483 self.source,
8484 &[
8485 (keyword.start_byte(), type_name.start_byte()),
8486 (name_node.start_byte(), name_node.end_byte()),
8487 (body.start_byte(), close.end_byte),
8488 ],
8489 None,
8490 )
8491 && let Some(reparsed_class) = tree.root_node().named_child(0)
8492 && let Some(class_body) = cpp_body_node(reparsed_class)
8493 && class_body.start_byte() == body.start_byte()
8494 && class_body.end_byte() == close.end_byte
8495 && let Some(tail) =
8496 cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
8497 {
8498 let class_body_range = class_body.byte_range();
8499 self.with_recovery_source_context(node, |visitor| {
8500 let source_context = visitor.active_source_context.clone();
8501 let class_unit = visitor.visit_named_class_like_shape(
8502 class_node,
8503 name,
8504 None,
8505 true,
8506 Some(class_range),
8507 raw_supertypes,
8508 base_facts,
8509 scope,
8510 stack,
8511 ancestry,
8512 );
8513 visitor.parsed.record_materialization(
8514 MaterializationRecord::RecoveredDeclaration {
8515 recovery: class_range,
8516 unit: class_unit.clone(),
8517 },
8518 );
8519 let member_scope = ScopeInfo {
8520 package_name: class_unit.package_name().to_string(),
8521 class_unit: Some(class_unit),
8522 declarations_are_fields: true,
8523 template_metadata: None,
8524 recovered_specialization_member_scope: false,
8525 ..scope.clone()
8526 };
8527 let tail_range = tail.root_node().byte_range();
8528 let class_body_recovery = cpp_recovery_window(
8529 visitor.source,
8530 class_body_range.start,
8531 class_body_range.end,
8532 );
8533 let tail_recovery =
8534 cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
8535 visitor.partitioned_regions.push(CppPartitionedRegion {
8536 tree: tail,
8537 range: tail_range,
8538 scope: scope.clone(),
8539 recovery: tail_recovery,
8540 source_context: source_context.clone(),
8541 });
8542 visitor.partitioned_regions.push(CppPartitionedRegion {
8543 tree,
8544 range: class_body_range,
8545 scope: member_scope,
8546 recovery: class_body_recovery,
8547 source_context: source_context.clone(),
8548 });
8549 });
8550 return;
8551 }
8552 }
8553 let body = cpp_body_node(class_node);
8554 let displaced_namespace = cpp_body_node(node)
8555 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
8556 let fragmented = cpp_body_node(node).and_then(|body| {
8557 fragmented_export_function_body_region(
8558 node,
8559 body,
8560 self.source,
8561 displaced_namespace.as_ref(),
8562 )
8563 });
8564 if let Some(fragmented) = fragmented {
8570 if let Some(boundary) =
8574 fragmented_export_sibling_class_boundary(node, self.source, ancestry)
8575 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
8576 {
8577 let mut boundary_scope = scope.clone();
8578 for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
8579 if sibling.start_byte() >= boundary.start_byte() {
8580 break;
8581 }
8582 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
8583 boundary_scope.visible_using_namespaces.push(namespace);
8584 }
8585 }
8586 self.recovered_class_sibling_scopes
8587 .insert(boundary.id(), boundary_scope);
8588 }
8589 let mut recovered_constructor = None;
8590 let mut recovered_prefix_tree = None;
8591 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
8592 {
8593 Some(FragmentedExportMembers::Complete(tree)) => {
8594 if let Some(body) = body
8595 && let Some(range) =
8596 cpp_reparsed_synthetic_initializer_constructor_range(
8597 tree.root_node(),
8598 &name,
8599 self.source,
8600 body.end_byte(),
8601 )
8602 {
8603 recovered_constructor = Some(range);
8604 recovered_prefix_tree = Some(tree);
8605 None
8606 } else {
8607 Some(FragmentedExportMembers::Complete(tree))
8608 }
8609 }
8610 outcome => outcome,
8611 };
8612 let mut class_stack = Vec::new();
8613 let class_unit = self.visit_named_class_like_shape(
8614 class_node,
8615 name,
8616 None,
8617 true,
8618 Some(fragmented.class_range),
8619 raw_supertypes,
8620 base_facts.clone(),
8621 scope,
8622 &mut class_stack,
8623 ancestry,
8624 );
8625 self.parsed
8626 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8627 recovery: fragmented.class_range,
8628 unit: class_unit.clone(),
8629 });
8630 let complete = outcome.is_some_and(|outcome| {
8631 self.visit_fragmented_export_class_members(
8632 node,
8633 outcome,
8634 class_unit.clone(),
8635 scope,
8636 )
8637 });
8638 if complete {
8639 self.consumed_fragment_regions
8640 .push((node.start_byte(), fragmented.class_range.end_byte));
8641 } else {
8642 let member_scope = ScopeInfo {
8651 package_name: class_unit.package_name().to_string(),
8652 module: scope.module.clone(),
8653 class_unit: Some(class_unit.clone()),
8654 template_signature: scope.template_signature.clone(),
8655 template_metadata: None,
8656 declarations_are_fields: true,
8657 recovered_specialization_member_scope: false,
8658 visible_using_namespaces: scope.visible_using_namespaces.clone(),
8659 };
8660 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
8661 if candidate.start_byte() >= fragmented.reparse_end {
8662 break;
8663 }
8664 if cpp_fragment_sibling_is_class_member(
8665 candidate,
8666 fragmented.reparse_end,
8667 self.source,
8668 ) {
8669 self.recovered_class_sibling_scopes
8670 .insert(candidate.id(), member_scope.clone());
8671 }
8672 }
8673 if let Some(range) = recovered_constructor
8674 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
8675 {
8676 let prefix_root = prefix_tree.root_node();
8677 let prefix_ancestry = self.recovery_ancestry(prefix_root);
8678 self.visit_recovered_fragment_prefix_members(
8679 node,
8680 prefix_root,
8681 range.start,
8682 &class_unit,
8683 scope,
8684 &prefix_ancestry,
8685 );
8686 self.visit_recovered_fragment_constructor(
8687 range,
8688 body,
8689 class_node,
8690 &class_unit,
8691 scope,
8692 ancestry,
8693 );
8694 }
8695 }
8696 if let Some(boundary) = displaced_namespace {
8697 for item in boundary.namespace_items {
8698 self.recovered_class_sibling_scopes
8699 .insert(item.id(), scope.clone());
8700 }
8701 }
8702 stack.extend(class_stack);
8703 return;
8704 }
8705 let mut stack = Vec::new();
8706 let class_unit = self.visit_named_class_like_shape(
8707 class_node,
8708 name,
8709 body,
8710 body.is_some(),
8711 None,
8712 raw_supertypes,
8713 base_facts,
8714 scope,
8715 &mut stack,
8716 ancestry,
8717 );
8718 self.parsed
8719 .record_materialization(MaterializationRecord::RecoveredDeclaration {
8720 recovery: cpp_declaration_range(node),
8721 unit: class_unit,
8722 });
8723 if let Some(body) = body
8730 && let Some(class_close) = self
8731 .orphaned_namespaces
8732 .matching_close_brace(body.start_byte())
8733 && class_close.start_byte < body.end_byte()
8734 {
8735 let split = {
8736 let mut cursor = body.walk();
8737 body.named_children(&mut cursor)
8738 .position(|child| child.start_byte() > class_close.start_byte)
8739 };
8740 if let Some(split) = split {
8741 let seeded = stack.pop();
8746 match seeded {
8747 Some(CppWork::Container(container)) => {
8748 push_cpp_sibling_range(
8749 body,
8750 split,
8751 usize::MAX,
8752 scope.clone(),
8753 &mut stack,
8754 );
8755 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
8756 }
8757 _ => unreachable!("exported-class seed is always one Container"),
8760 }
8761 }
8762 }
8763 while let Some(work) = stack.pop() {
8764 match work {
8765 CppWork::Container(container) => {
8766 push_cpp_container_work(container.node, container.scope, &mut stack);
8767 }
8768 CppWork::Siblings(siblings) => {
8769 advance_cpp_siblings(siblings, self.source, &mut stack);
8770 }
8771 CppWork::Node(work) => {
8772 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
8773 }
8774 }
8775 }
8776 return;
8777 }
8778 let recovered_constraint_constructor =
8779 cpp_recovered_template_macro_constructor(node, self.source);
8780 let declarator = recovered_constraint_constructor
8781 .map(|(declarator, _)| declarator)
8782 .or_else(|| {
8783 scope.class_unit.as_ref().and_then(|class| {
8784 recovered_macro_decorated_constructor_declarator(
8785 node,
8786 class.identifier(),
8787 self.source,
8788 )
8789 })
8790 })
8791 .or_else(|| node.child_by_field_name("declarator"));
8792 let Some(declarator) = declarator else {
8793 self.visit_malformed_function_definition_container(node, scope, stack);
8794 return;
8795 };
8796 let Some(function_declarator) = extract_function_declarator(declarator) else {
8797 self.visit_malformed_function_definition_container(node, scope, stack);
8798 return;
8799 };
8800 let function = if let Some((_, callable_name)) =
8801 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
8802 {
8803 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
8804 } else {
8805 extract_function_info(function_declarator, self.source, scope)
8806 };
8807 let Some(mut function) = function else {
8808 self.visit_malformed_function_definition_container(node, scope, stack);
8809 return;
8810 };
8811 if let Some((_, template_parameter)) = recovered_constraint_constructor {
8812 function.signature = format!(
8813 "template <{}>{}",
8814 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
8815 function.signature
8816 );
8817 }
8818 let code_unit = function.code_unit(self.file.clone());
8819 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
8824 let signature = if recovered_constraint_constructor.is_some() {
8825 normalize_cpp_whitespace(node_text(function_declarator, self.source))
8826 } else {
8827 render_cpp_function_display_signature_from_node(
8828 node,
8829 self.source,
8830 scope.template_signature.as_deref(),
8831 true,
8832 ancestry,
8833 )
8834 };
8835 self.add_signature_with_metadata(
8836 code_unit.clone(),
8837 cpp_signature_metadata(
8838 signature,
8839 function_declarator,
8840 cpp_callable_linkage(node, self.source, ancestry),
8841 self.source,
8842 ancestry,
8843 )
8844 .with_declaration_only(false),
8845 );
8846 if let Some(parent) = &scope.class_unit {
8847 self.parsed.add_child(parent.clone(), code_unit);
8848 } else if let Some(module) = &scope.module {
8849 self.parsed.add_child(module.clone(), code_unit);
8850 }
8851 self.visit_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
8852 }
8853
8854 fn scope_for_recovered_exported_class<'tree>(
8859 &mut self,
8860 node: Node<'tree>,
8861 name: &str,
8862 definition_body_present: bool,
8863 scope: &ScopeInfo,
8864 ancestry: &ParentIndex<'tree>,
8865 ) -> ScopeInfo {
8866 if !definition_body_present
8867 || !scope.package_name.is_empty()
8868 || scope.class_unit.is_some()
8869 || !(is_recovered_exported_class_container(node, self.source)
8870 || recover_function_like_export_class_pair(node, self.source).is_some()
8871 || recover_embedded_function_like_export_classes(node, self.source)
8872 .iter()
8873 .any(|recovered| recovered.name == name)
8874 || matches!(node.kind(), "declaration" | "field_declaration")
8875 && recover_exported_class_declaration(node, self.source).is_some()
8876 || matches!(
8877 node.kind(),
8878 "class_specifier" | "struct_specifier" | "union_specifier"
8879 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
8880 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
8881 name_node,
8882 self.source,
8883 )))
8884 }) || ancestry.parent(node).is_some_and(|parent| {
8885 matches!(parent.kind(), "declaration" | "field_declaration")
8886 && recover_exported_class_declaration(parent, self.source).is_some()
8887 || is_recovered_exported_class_container(parent, self.source)
8888 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
8889 {
8890 return scope.clone();
8891 }
8892 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
8893 let Some(package_name) = borrowed_namespace
8894 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
8895 else {
8896 return scope.clone();
8897 };
8898
8899 let module = CodeUnit::new_fq(
8900 self.file.clone(),
8901 CodeUnitType::Module,
8902 "",
8903 package_name.clone(),
8904 cpp_namespace_fq(&package_name),
8905 );
8906 let mut recovered = scope.clone();
8907 recovered.package_name = package_name;
8908 recovered.module = Some(module);
8909 recovered
8910 }
8911
8912 fn unique_earlier_namespace_forward<'tree>(
8921 &mut self,
8922 recovered_node: Node<'tree>,
8923 name: &str,
8924 ancestry: &ParentIndex<'tree>,
8925 ) -> Option<String> {
8926 let mut root = recovered_node;
8927 while let Some(parent) = ancestry.parent(root) {
8928 root = parent;
8929 }
8930 let source = self.source;
8931 let scan = self
8932 .namespace_forward_scans
8933 .entry(CppTreeIdentity::of(root))
8934 .or_default();
8935 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
8936 let borrowed = scan.unique_earlier_forward(name, recovered_node);
8937
8938 #[cfg(debug_assertions)]
8939 assert_eq!(
8940 borrowed,
8941 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
8942 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
8943 for {name} at byte {}",
8944 recovered_node.start_byte()
8945 );
8946
8947 borrowed
8948 }
8949
8950 fn visit_malformed_function_definition_container<'tree>(
8951 &mut self,
8952 node: Node<'tree>,
8953 scope: &ScopeInfo,
8954 stack: &mut Vec<CppWork<'tree>>,
8955 ) {
8956 let Some(body) = cpp_body_node(node) else {
8957 return;
8958 };
8959 if !cpp_contains_namespace_definition(body) {
8960 return;
8961 }
8962 stack.push(CppWork::Container(CppContainer {
8963 node: body,
8964 scope: scope.clone(),
8965 }));
8966 }
8967
8968 fn record_recovered_declarations(
8986 &mut self,
8987 recovery: Range,
8988 origin: Node<'_>,
8989 reparse_walk: impl FnOnce(&mut Self),
8990 ) {
8991 let preprocessor_anchor = (origin.start_byte() <= recovery.start_byte
8996 && recovery.start_byte < origin.end_byte())
8997 .then(|| origin.descendant_for_byte_range(recovery.start_byte, recovery.start_byte))
8998 .flatten()
8999 .unwrap_or(origin);
9000 self.with_recovery_source_context_at(origin, preprocessor_anchor, |visitor| {
9001 let source_context = visitor.active_source_context.clone();
9002 visitor.record_recovered_declarations_with_context(
9003 recovery,
9004 source_context,
9005 reparse_walk,
9006 );
9007 });
9008 }
9009
9010 fn record_recovered_declarations_with_context(
9015 &mut self,
9016 recovery: Range,
9017 source_context: Option<CppActiveSourceContext>,
9018 reparse_walk: impl FnOnce(&mut Self),
9019 ) {
9020 #[cfg(any(debug_assertions, test))]
9023 let before = self.parsed.declarations().clone();
9024
9025 self.recovery_captures.push(CppRecoveryCapture::default());
9026 let previous = self.active_source_context.take();
9027 self.active_source_context = source_context;
9028 reparse_walk(self);
9029 self.active_source_context = previous;
9030 let captured = self
9031 .recovery_captures
9032 .pop()
9033 .expect("the capture this call pushed is the one it pops");
9034
9035 let mut minted: Vec<CodeUnit> = captured
9041 .created
9042 .into_iter()
9043 .filter(|unit| self.parsed.contains_declaration(unit))
9044 .collect();
9045 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9046
9047 #[cfg(any(debug_assertions, test))]
9048 {
9049 let mut rediscovered: Vec<CodeUnit> = self
9050 .parsed
9051 .declarations()
9052 .iter()
9053 .filter(|unit| !before.contains(*unit))
9054 .cloned()
9055 .collect();
9056 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9057 assert_eq!(
9058 minted, rediscovered,
9059 "the captured recovered set must be the declaration delta of the reparse \
9060 walk over {recovery:?}"
9061 );
9062 }
9063
9064 for unit in minted {
9065 self.parsed
9066 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9067 recovery,
9068 unit,
9069 });
9070 }
9071 }
9072
9073 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
9076 let start = self
9077 .parsed
9078 .declaration_ranges(unit)
9079 .first()
9080 .map(|range| range.start_byte)
9081 .unwrap_or(usize::MAX);
9082 (start, unit.fq_name().to_string())
9083 }
9084
9085 fn visit_sentinel_macro_region<'tree>(
9086 &mut self,
9087 node: Node<'tree>,
9088 scope: &ScopeInfo,
9089 stack: &mut Vec<CppWork<'tree>>,
9090 ancestry: &ParentIndex<'tree>,
9091 ) -> bool {
9092 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
9093 return true;
9094 }
9095 if let Some((
9096 reparse_start,
9097 class_start,
9098 body_start,
9099 class_close_start,
9100 class_close_end,
9101 class_close_line,
9102 )) = cpp_sentinel_macro_class_region(node, self.source)
9103 {
9104 let Some(class_tree) =
9105 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
9106 else {
9107 return false;
9108 };
9109 let class_root = class_tree.root_node();
9110 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9111 let class_ancestry = self.recovery_ancestry(class_root);
9113 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
9114 class_root,
9115 template_node,
9116 self.source,
9117 &class_ancestry,
9118 ) else {
9119 return false;
9120 };
9121 let class_node = reparsed_class.declaration_node;
9122 let name = reparsed_class.name;
9123 let mut class_scope = scope.clone();
9124 if let Some(template_node) = template_node {
9125 class_scope.template_signature =
9126 cpp_template_signature(template_node, class_node, self.source);
9127 class_scope.template_metadata =
9128 cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
9129 }
9130 let Some(body_tree) =
9131 cpp_reparse_region_items(self.source, body_start, class_close_start)
9132 else {
9133 return false;
9134 };
9135 let raw_supertypes = reparsed_class.raw_supertypes;
9136 let base_facts = reparsed_class.base_facts;
9137 let class_range = Range {
9138 start_byte: class_start,
9139 end_byte: class_close_end,
9140 start_line: class_node.start_position().row + 1,
9141 end_line: class_close_line,
9142 };
9143 let class_scope = self.scope_for_recovered_exported_class(
9144 class_node,
9145 &name,
9146 true,
9147 &class_scope,
9148 &class_ancestry,
9149 );
9150 let mut class_stack = Vec::new();
9151 self.with_recovery_source_context(node, |visitor| {
9152 let class_unit = visitor.visit_named_class_like_shape(
9153 class_node,
9154 name,
9155 None,
9156 true,
9157 Some(class_range),
9158 raw_supertypes,
9159 base_facts,
9160 &class_scope,
9161 &mut class_stack,
9162 &class_ancestry,
9163 );
9164 visitor.parsed.record_materialization(
9165 MaterializationRecord::RecoveredDeclaration {
9166 recovery: class_range,
9167 unit: class_unit.clone(),
9168 },
9169 );
9170 let member_scope = ScopeInfo {
9171 package_name: class_scope.package_name.clone(),
9172 module: class_scope.module.clone(),
9173 class_unit: Some(class_unit),
9174 template_signature: class_scope.template_signature.clone(),
9175 template_metadata: None,
9176 declarations_are_fields: true,
9177 recovered_specialization_member_scope: false,
9178 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
9179 };
9180 let body_root = body_tree.root_node();
9182 visitor.run_container_work(
9183 body_root,
9184 member_scope,
9185 &visitor.recovery_ancestry(body_root),
9186 );
9187 });
9188 self.consumed_fragment_regions
9191 .push((node.start_byte(), class_close_end));
9192 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
9199 stack.push(CppWork::Container(CppContainer {
9200 node,
9201 scope: scope.clone(),
9202 }));
9203 }
9204 return true;
9205 }
9206 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
9207 return false;
9208 };
9209 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
9210 return false;
9211 };
9212 let root = tree.root_node();
9213 if !cpp_reparsed_items_are_indexable(root, self.source) {
9214 return false;
9215 }
9216 let recovery = cpp_recovery_window(self.source, start, end);
9217 let reparsed_ancestry = self.recovery_ancestry(root);
9219 self.record_recovered_declarations(recovery, node, |visitor| {
9220 visitor.visit_container(
9221 root,
9222 &reparsed_ancestry,
9223 &scope.package_name,
9224 scope.module.clone(),
9225 scope.class_unit.clone(),
9226 scope.template_signature.clone(),
9227 scope.visible_using_namespaces.clone(),
9228 );
9229 });
9230 if end > node.end_byte() {
9231 self.consumed_fragment_regions
9232 .push((node.start_byte(), end));
9233 } else if node.kind() == "ERROR" && node.end_byte() > end {
9234 self.consumed_fragment_regions
9241 .push((node.start_byte(), end));
9242 stack.push(CppWork::Container(CppContainer {
9243 node,
9244 scope: scope.clone(),
9245 }));
9246 }
9247 true
9248 }
9249
9250 fn visit_nested_namespace_sentinel<'tree>(
9256 &mut self,
9257 node: Node<'tree>,
9258 scope: &ScopeInfo,
9259 ancestry: &ParentIndex<'tree>,
9260 ) -> bool {
9261 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
9262 return false;
9263 };
9264
9265 let mut package_name = scope.package_name.clone();
9266 let mut module = scope.module.clone();
9267 for component in recovered.namespace_components {
9268 package_name = if package_name.is_empty() {
9269 component
9270 } else {
9271 format!("{package_name}::{component}")
9272 };
9273 let namespace_module = CodeUnit::new_fq(
9274 self.file.clone(),
9275 CodeUnitType::Module,
9276 "",
9277 package_name.clone(),
9278 cpp_namespace_fq(&package_name),
9279 );
9280 if !self.parsed.contains_declaration(&namespace_module) {
9281 self.add_declaration(
9282 ancestry,
9283 namespace_module.clone(),
9284 recovered.function,
9285 None,
9286 None,
9287 );
9288 }
9289 module = Some(namespace_module);
9290 }
9291
9292 let recovered_scope = ScopeInfo {
9293 package_name,
9294 module,
9295 class_unit: None,
9306 template_signature: scope.template_signature.clone(),
9307 template_metadata: scope.template_metadata.clone(),
9308 declarations_are_fields: false,
9309 recovered_specialization_member_scope: false,
9310 visible_using_namespaces: scope.visible_using_namespaces.clone(),
9311 };
9312 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
9313 recovered.function,
9314 recovered.body,
9315 self.source,
9316 ancestry,
9317 ) {
9318 let mut class_scope = recovered_scope.clone();
9319 if let Some(template_node) = fragmented.template_node {
9320 class_scope.template_signature =
9321 cpp_template_signature(template_node, fragmented.class_node, self.source);
9322 class_scope.template_metadata = cpp_template_metadata(
9323 template_node,
9324 fragmented.class_node,
9325 self.source,
9326 ancestry,
9327 );
9328 }
9329 if let Some(outcome) = self
9330 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
9331 {
9332 let mut class_stack = Vec::new();
9333 let class_unit = self.visit_named_class_like_shape(
9334 fragmented.class_node,
9335 fragmented.name.clone(),
9336 None,
9337 true,
9338 Some(fragmented.fragmented.class_range),
9339 fragmented.raw_supertypes.clone(),
9340 Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
9341 &class_scope,
9342 &mut class_stack,
9343 ancestry,
9344 );
9345 self.parsed
9346 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9347 recovery: fragmented.fragmented.class_range,
9348 unit: class_unit.clone(),
9349 });
9350 if self.visit_fragmented_export_class_members(
9351 node,
9352 outcome,
9353 class_unit,
9354 &class_scope,
9355 ) {
9356 self.consumed_fragment_regions.push((
9357 fragmented.consumed_start,
9358 fragmented.fragmented.class_range.end_byte,
9359 ));
9360 }
9361 }
9362 }
9363 self.run_container_work(recovered.body, recovered_scope, ancestry);
9368 true
9369 }
9370
9371 fn visit_declaration<'tree>(
9372 &mut self,
9373 node: Node<'tree>,
9374 scope: &ScopeInfo,
9375 in_class_body: bool,
9376 stack: &mut Vec<CppWork<'tree>>,
9377 ancestry: &ParentIndex<'tree>,
9378 ) {
9379 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
9380 return;
9381 }
9382 if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
9383 return;
9384 }
9385 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
9386 !cpp_active_template_type_parameter(
9387 node,
9388 node_text(declarator, self.source),
9389 self.source,
9390 ancestry,
9391 )
9392 }) {
9393 return;
9394 }
9395 if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
9396 self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
9402 return;
9403 }
9404 if in_class_body
9405 && let Some(parent) = scope.class_unit.as_ref()
9406 && let Some(call) =
9407 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
9408 {
9409 self.visit_recovered_macro_qualified_constructor_definition(
9410 node, call, scope, ancestry,
9411 );
9412 return;
9413 }
9414 if in_class_body
9415 && let Some(parent) = scope.class_unit.as_ref()
9416 && let Some(constructor) = recovered_macro_initialized_constructor_declarator(
9417 node,
9418 parent.identifier(),
9419 self.source,
9420 )
9421 {
9422 self.visit_recovered_macro_initialized_constructor_definition(
9423 node,
9424 constructor,
9425 scope,
9426 ancestry,
9427 );
9428 return;
9429 }
9430 if in_class_body
9431 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
9432 {
9433 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
9434 return;
9435 }
9436 if in_class_body
9437 && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
9438 {
9439 for member in members {
9440 self.add_macro_wrapped_declaration(member, scope, ancestry);
9441 }
9442 return;
9443 }
9444 if in_class_body && self.visit_string_attribute_macro_type_members(node, scope) {
9445 return;
9446 }
9447 if in_class_body
9448 && let Some(declarators) =
9449 recovered_macro_qualified_field_declarators(node, self.source)
9450 {
9451 for declarator in declarators {
9452 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9453 }
9454 return;
9455 }
9456 let recovered_alias_names = recovered_type_alias_names(node, self.source);
9457 if !recovered_alias_names.is_empty() {
9458 self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
9459 return;
9460 }
9461 if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
9462 return;
9463 }
9464 if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
9465 return;
9466 }
9467
9468 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
9469 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
9470 if let Some(outcome) =
9475 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
9476 {
9477 let consumed_region = (
9478 recovered.declaration_node.end_byte(),
9479 fragmented.class_range.end_byte,
9480 );
9481 let code_unit = self.visit_named_class_like_shape(
9482 recovered.declaration_node,
9483 recovered.name,
9484 None,
9485 true,
9486 Some(fragmented.class_range),
9487 recovered.raw_supertypes,
9488 recovered.base_facts,
9489 scope,
9490 stack,
9491 ancestry,
9492 );
9493 self.parsed.record_materialization(
9494 MaterializationRecord::RecoveredDeclaration {
9495 recovery: fragmented.class_range,
9496 unit: code_unit.clone(),
9497 },
9498 );
9499 let consume_fragment =
9500 self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
9501 if consume_fragment {
9507 self.consumed_fragment_regions.push(consumed_region);
9508 }
9509 return;
9510 }
9511 }
9512 let uses_initializer_body = recovered.uses_initializer_body;
9513 let definition_body_present = recovered.body.is_some();
9514 let class_unit = self.visit_named_class_like_shape(
9515 recovered.declaration_node,
9516 recovered.name,
9517 recovered.body,
9518 definition_body_present,
9519 None,
9520 recovered.raw_supertypes,
9521 recovered.base_facts,
9522 scope,
9523 stack,
9524 ancestry,
9525 );
9526 self.parsed
9527 .record_materialization(MaterializationRecord::RecoveredDeclaration {
9528 recovery: cpp_declaration_range(node),
9529 unit: class_unit,
9530 });
9531 if uses_initializer_body {
9532 return;
9533 }
9534 }
9535
9536 if in_class_body && cpp_unterminated_macro_invocation_field(node) {
9547 return;
9548 }
9549
9550 let mut handled_function = false;
9551 let mut handled_declarator = false;
9552 let mut cursor = node.walk();
9553 for child in node.named_children(&mut cursor) {
9554 if matches!(
9555 child.kind(),
9556 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9557 ) {
9558 if cpp_body_node(child).is_some() {
9567 self.visit_class_like(child, scope, stack, ancestry);
9568 }
9569 continue;
9570 }
9571 }
9572
9573 let mut cursor = node.walk();
9574 for child in node.children_by_field_name("declarator", &mut cursor) {
9575 if crate::structural::is_recovered_designator_init_declarator(child) {
9576 handled_declarator = true;
9577 continue;
9578 }
9579 if in_class_body
9580 && let Some(field) = recovered_function_like_field_declarator(node, self.source)
9581 {
9582 handled_declarator = true;
9583 self.visit_variable_declaration(node, field.name, scope, true, ancestry);
9584 continue;
9585 }
9586 if let Some(kind) = classify_declarator(child) {
9587 handled_declarator = true;
9588 match kind {
9589 DeclaratorKind::Function(function_declarator) => {
9590 handled_function = true;
9591 self.visit_function_declaration(node, function_declarator, scope, ancestry);
9592 }
9593 DeclaratorKind::Variable(variable_declarator) => {
9594 self.visit_variable_declaration(
9595 node,
9596 variable_declarator,
9597 scope,
9598 in_class_body,
9599 ancestry,
9600 );
9601 }
9602 }
9603 }
9604 }
9605
9606 if !handled_declarator {
9607 let mut cursor = node.walk();
9608 for child in node.named_children(&mut cursor) {
9609 if crate::structural::is_recovered_designator_init_declarator(child) {
9610 handled_declarator = true;
9611 continue;
9612 }
9613 if !is_unfielded_declarator_candidate(child) {
9614 continue;
9615 }
9616 let Some(kind) = classify_declarator(child) else {
9617 continue;
9618 };
9619 handled_declarator = true;
9620 match kind {
9621 DeclaratorKind::Function(function_declarator) => {
9622 handled_function = true;
9623 self.visit_function_declaration(node, function_declarator, scope, ancestry);
9624 }
9625 DeclaratorKind::Variable(variable_declarator) => {
9626 self.visit_variable_declaration(
9627 node,
9628 variable_declarator,
9629 scope,
9630 in_class_body,
9631 ancestry,
9632 );
9633 }
9634 }
9635 }
9636 }
9637
9638 if handled_function {
9639 return;
9640 }
9641
9642 if !handled_declarator {
9643 if in_class_body {
9644 self.visit_class_members_from_declaration(node, scope, ancestry);
9645 } else {
9646 self.visit_global_variables_from_declaration(node, scope, ancestry);
9647 }
9648 }
9649 }
9650
9651 fn anonymous_aggregate_declaration_parts<'tree>(
9665 node: Node<'tree>,
9666 ) -> Option<(Node<'tree>, Node<'tree>, Vec<Node<'tree>>)> {
9667 let aggregate = node.child_by_field_name("type")?;
9668 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
9669 || aggregate.child_by_field_name("name").is_some()
9670 {
9671 return None;
9672 }
9673 let body = cpp_body_node(aggregate)?;
9674 let mut cursor = node.walk();
9675 let declarators = node
9676 .children_by_field_name("declarator", &mut cursor)
9677 .filter_map(|declarator| match classify_declarator(declarator) {
9678 Some(DeclaratorKind::Variable(variable)) => Some(variable),
9679 Some(DeclaratorKind::Function(_)) | None => None,
9680 })
9681 .collect();
9682 Some((aggregate, body, declarators))
9683 }
9684
9685 fn visit_anonymous_aggregate_declaration<'tree>(
9686 &mut self,
9687 node: Node<'tree>,
9688 scope: &ScopeInfo,
9689 in_class_body: bool,
9690 stack: &mut Vec<CppWork<'tree>>,
9691 ancestry: &ParentIndex<'tree>,
9692 ) -> bool {
9693 if !in_class_body || scope.class_unit.is_none() {
9694 return false;
9695 }
9696 let Some((aggregate, body, declarators)) =
9697 Self::anonymous_aggregate_declaration_parts(node)
9698 else {
9699 return false;
9700 };
9701 if declarators.is_empty() {
9702 stack.push(CppWork::Container(CppContainer {
9703 node: body,
9704 scope: scope.clone(),
9705 }));
9706 return true;
9707 }
9708
9709 for declarator in declarators {
9710 let Some(name) = extract_variable_name(declarator, self.source) else {
9711 continue;
9712 };
9713 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9714 self.visit_named_class_like_shape(
9719 aggregate,
9720 name,
9721 Some(body),
9722 true,
9723 Some(cpp_declaration_range(node)),
9724 None,
9725 None,
9726 scope,
9727 stack,
9728 ancestry,
9729 );
9730 }
9731 true
9732 }
9733
9734 fn visit_anonymous_local_aggregate_declaration<'tree>(
9743 &mut self,
9744 node: Node<'tree>,
9745 scope: &ScopeInfo,
9746 stack: &mut Vec<CppWork<'tree>>,
9747 ancestry: &ParentIndex<'tree>,
9748 ) -> bool {
9749 if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
9750 return false;
9751 }
9752 let Some((aggregate, body, declarators)) =
9753 Self::anonymous_aggregate_declaration_parts(node)
9754 else {
9755 return false;
9756 };
9757 if declarators.is_empty() {
9758 return false;
9759 }
9760
9761 for declarator in &declarators {
9762 self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
9763 }
9764 let name = format!("<anonymous:{}>", aggregate.start_byte());
9765 self.visit_named_class_like_shape(
9766 aggregate,
9767 name,
9768 Some(body),
9769 true,
9770 None,
9771 None,
9772 None,
9773 scope,
9774 stack,
9775 ancestry,
9776 );
9777 true
9778 }
9779
9780 fn visit_function_declaration<'tree>(
9781 &mut self,
9782 declaration_node: Node<'tree>,
9783 declarator: Node<'tree>,
9784 scope: &ScopeInfo,
9785 ancestry: &ParentIndex<'tree>,
9786 ) {
9787 let Some(function) = extract_function_info(declarator, self.source, scope) else {
9788 return;
9789 };
9790 let code_unit = function.code_unit(self.file.clone());
9796 if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
9797 return;
9798 }
9799 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
9800 let signature = render_cpp_function_display_signature_from_node(
9801 declaration_node,
9802 self.source,
9803 scope.template_signature.as_deref(),
9804 false,
9805 ancestry,
9806 );
9807 self.add_signature_with_metadata(
9808 code_unit.clone(),
9809 cpp_signature_metadata(
9810 signature,
9811 declarator,
9812 cpp_callable_linkage(declaration_node, self.source, ancestry),
9813 self.source,
9814 ancestry,
9815 )
9816 .with_declaration_only(true),
9817 );
9818 if let Some(parent) = &scope.class_unit {
9819 self.parsed.add_child(parent.clone(), code_unit);
9820 } else if let Some(module) = &scope.module {
9821 self.parsed.add_child(module.clone(), code_unit);
9822 }
9823 }
9824
9825 fn link_existing_source_declaration(
9826 &mut self,
9827 ancestry: &ParentIndex<'_>,
9828 code_unit: &CodeUnit,
9829 declaration_node: Node<'_>,
9830 ) -> bool {
9831 if !self.parsed.contains_declaration(code_unit) {
9832 return false;
9833 }
9834 self.link_source_declaration(ancestry, code_unit, declaration_node);
9835 self.parsed
9836 .record_navigation_range(code_unit.clone(), cpp_declaration_range(declaration_node));
9837 true
9838 }
9839
9840 fn visit_recovered_macro_qualified_function_declaration<'tree>(
9841 &mut self,
9842 declaration_node: Node<'tree>,
9843 call: Node<'tree>,
9844 scope: &ScopeInfo,
9845 ancestry: &ParentIndex<'tree>,
9846 ) {
9847 let Some(parent) = &scope.class_unit else {
9848 return;
9849 };
9850 let Some(name_node) = call.child_by_field_name("function") else {
9851 return;
9852 };
9853 let Some(arguments) = call.child_by_field_name("arguments") else {
9854 return;
9855 };
9856 let Some((signature, parameter_labels)) =
9857 recovered_macro_qualified_function_parameters(arguments, self.source)
9858 else {
9859 return;
9860 };
9861 let arity = parameter_labels.len();
9862 let function = FunctionInfo {
9863 package_name: scope.package_name.clone(),
9864 owner: Some(CppMemberOwner::Unit(parent.clone())),
9865 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
9866 signature,
9867 };
9868 if function.name.is_empty() {
9869 return;
9870 }
9871 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
9872 if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
9873 return;
9874 }
9875 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
9876 let signature_label = render_cpp_function_display_signature_from_node(
9877 declaration_node,
9878 self.source,
9879 scope.template_signature.as_deref(),
9880 false,
9881 ancestry,
9882 );
9883 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
9884 let modifiers =
9889 cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
9890 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
9891 .with_declaration_only(true)
9892 .with_callable_arity(CallableArity::exact(arity))
9893 .with_callable_linkage(linkage)
9894 .with_callable_modifiers(
9895 modifiers.is_static,
9896 modifiers.is_constructor,
9897 modifiers.visibility,
9898 );
9899 self.add_signature_with_metadata(code_unit.clone(), metadata);
9900 self.parsed.add_child(parent.clone(), code_unit);
9901 }
9902
9903 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
9904 &mut self,
9905 declaration_node: Node<'tree>,
9906 call: Node<'tree>,
9907 scope: &ScopeInfo,
9908 ancestry: &ParentIndex<'tree>,
9909 ) {
9910 let Some(parent) = &scope.class_unit else {
9911 return;
9912 };
9913 let Some(arguments) = call.child_by_field_name("arguments") else {
9914 return;
9915 };
9916 let Some((mut signature, parameter_labels)) =
9917 recovered_macro_qualified_function_parameters(arguments, self.source)
9918 else {
9919 return;
9920 };
9921 if let Some(template_signature) = &scope.template_signature {
9922 signature = format!("{template_signature}{signature}");
9923 }
9924 let arity = parameter_labels.len();
9925 let function = FunctionInfo {
9926 package_name: scope.package_name.clone(),
9927 owner: Some(CppMemberOwner::Unit(parent.clone())),
9928 name: parent.identifier().to_string(),
9929 signature,
9930 };
9931 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
9932 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
9933 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
9934 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
9935 let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
9939 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
9940 .with_declaration_only(false)
9941 .with_callable_arity(CallableArity::exact(arity))
9942 .with_callable_linkage(linkage)
9943 .with_callable_modifiers(
9944 modifiers.is_static,
9945 modifiers.is_constructor,
9946 modifiers.visibility,
9947 );
9948 self.add_signature_with_metadata(code_unit.clone(), metadata);
9949 self.parsed.add_child(parent.clone(), code_unit);
9950 }
9951
9952 fn visit_recovered_macro_initialized_constructor_definition<'tree>(
9958 &mut self,
9959 declaration_node: Node<'tree>,
9960 function_declarator: Node<'tree>,
9961 scope: &ScopeInfo,
9962 ancestry: &ParentIndex<'tree>,
9963 ) {
9964 let Some(parent) = &scope.class_unit else {
9965 return;
9966 };
9967 let Some(function) = extract_function_info(function_declarator, self.source, scope) else {
9968 return;
9969 };
9970 let code_unit = function.code_unit(self.file.clone());
9971 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
9972 let body_start = declaration_node
9975 .child_by_field_name("default_value")
9976 .map_or(declaration_node.end_byte(), |body| body.start_byte());
9977 let head =
9978 collapse_cpp_whitespace(self.source[declaration_node.start_byte()..body_start].trim());
9979 let signature = match scope.template_signature.as_deref() {
9980 Some(template_signature) => format!("template {template_signature} {head} {{...}}"),
9981 None => format!("{head} {{...}}"),
9982 };
9983 self.add_signature_with_metadata(
9984 code_unit.clone(),
9985 cpp_signature_metadata(
9986 signature,
9987 function_declarator,
9988 cpp_callable_linkage(declaration_node, self.source, ancestry),
9989 self.source,
9990 ancestry,
9991 )
9992 .with_declaration_only(false),
9993 );
9994 self.parsed.add_child(parent.clone(), code_unit);
9995 }
9996
9997 fn visit_variable_declaration<'tree>(
9998 &mut self,
9999 declaration_node: Node<'tree>,
10000 declarator: Node<'tree>,
10001 scope: &ScopeInfo,
10002 in_class_body: bool,
10003 ancestry: &ParentIndex<'tree>,
10004 ) {
10005 let Some(name) = extract_variable_name(declarator, self.source) else {
10006 return;
10007 };
10008 let parent = if in_class_body {
10009 let Some(parent) = &scope.class_unit else {
10010 return;
10011 };
10012 Some(parent)
10013 } else {
10014 None
10015 };
10016 let short_name = match parent {
10017 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10018 None => name.clone(),
10019 };
10020 let fq = cpp_leaf_fq(
10021 &scope.package_name,
10022 parent,
10023 &name,
10024 SegmentKind::Member,
10025 SegmentKind::Member,
10026 );
10027 let code_unit = CodeUnit::new_fq(
10028 self.file.clone(),
10029 CodeUnitType::Field,
10030 scope.package_name.clone(),
10031 short_name,
10032 fq,
10033 );
10034 if self.parsed.contains_declaration(&code_unit) {
10035 return;
10036 }
10037 self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10038 self.add_signature_with_metadata(
10039 code_unit.clone(),
10040 SignatureMetadata::new(
10041 render_cpp_field_signature(declaration_node, declarator, self.source),
10042 Vec::new(),
10043 )
10044 .with_cpp_field_linkage(cpp_field_declaration_linkage(
10045 declaration_node,
10046 self.source,
10047 ancestry,
10048 )),
10049 );
10050 if let Some(parent) = &scope.class_unit {
10051 self.parsed.add_child(parent.clone(), code_unit);
10052 } else if let Some(module) = &scope.module {
10053 self.parsed.add_child(module.clone(), code_unit);
10054 }
10055 }
10056
10057 fn visit_omitted_semicolon_macro_field<'tree>(
10061 &mut self,
10062 node: Node<'tree>,
10063 type_modifier: Node<'tree>,
10064 name_modifier: Node<'tree>,
10065 scope: &ScopeInfo,
10066 ancestry: &ParentIndex<'tree>,
10067 ) {
10068 let Some(name_node) = macro_modifier_identifier(name_modifier) else {
10069 return;
10070 };
10071 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
10072 if name.is_empty() {
10073 return;
10074 }
10075 let range = Range {
10076 start_byte: type_modifier.start_byte(),
10077 end_byte: name_modifier.end_byte(),
10078 start_line: type_modifier.start_position().row + 1,
10079 end_line: name_modifier.end_position().row + 1,
10080 };
10081 self.record_macro_head_field(node, range, name, scope, ancestry);
10082 }
10083
10084 fn visit_macro_return_template_parameter_field<'tree>(
10089 &mut self,
10090 node: Node<'tree>,
10091 head_start: Node<'tree>,
10092 type_node: Node<'tree>,
10093 scope: &ScopeInfo,
10094 ancestry: &ParentIndex<'tree>,
10095 ) {
10096 let name = normalize_cpp_whitespace(node_text(type_node, self.source));
10097 let range = Range {
10098 start_byte: head_start.start_byte(),
10099 end_byte: type_node.end_byte(),
10100 start_line: head_start.start_position().row + 1,
10101 end_line: type_node.end_position().row + 1,
10102 };
10103 self.record_macro_head_field(node, range, name, scope, ancestry);
10104 }
10105
10106 fn record_macro_head_field<'tree>(
10110 &mut self,
10111 node: Node<'tree>,
10112 range: Range,
10113 name: String,
10114 scope: &ScopeInfo,
10115 ancestry: &ParentIndex<'tree>,
10116 ) {
10117 if name.is_empty() {
10118 return;
10119 }
10120 let parent = scope.class_unit.as_ref();
10121 let short_name = match parent {
10122 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10123 None => name.clone(),
10124 };
10125 let code_unit = CodeUnit::new_fq(
10126 self.file.clone(),
10127 CodeUnitType::Field,
10128 scope.package_name.clone(),
10129 short_name,
10130 cpp_leaf_fq(
10131 &scope.package_name,
10132 parent,
10133 &name,
10134 SegmentKind::Member,
10135 SegmentKind::Member,
10136 ),
10137 );
10138 if self.parsed.contains_declaration(&code_unit) {
10139 return;
10140 }
10141 self.add_declaration_with_range(
10142 ancestry,
10143 code_unit.clone(),
10144 range,
10145 Some(node),
10146 parent.cloned(),
10147 None,
10148 );
10149 let signature = normalize_cpp_whitespace(&self.source[range.start_byte..range.end_byte]);
10150 self.add_signature_with_metadata(
10151 code_unit.clone(),
10152 SignatureMetadata::new(signature, Vec::new())
10153 .with_cpp_field_linkage(cpp_field_declaration_linkage(node, self.source, ancestry)),
10154 );
10155 if let Some(parent) = &scope.class_unit {
10156 self.parsed.add_child(parent.clone(), code_unit);
10157 } else if let Some(module) = &scope.module {
10158 self.parsed.add_child(module.clone(), code_unit);
10159 }
10160 }
10161
10162 fn variable_declarator_from_declaration_child<'tree>(
10163 child: Node<'tree>,
10164 ) -> Option<Node<'tree>> {
10165 if child.kind() == "init_declarator" {
10166 child.child_by_field_name("declarator")
10167 } else if matches!(
10168 child.kind(),
10169 "identifier"
10170 | "field_identifier"
10171 | "pointer_declarator"
10172 | "reference_declarator"
10173 | "array_declarator"
10174 | "parenthesized_declarator"
10175 | "macro_modifier_declarator"
10176 ) {
10177 Some(child)
10178 } else {
10179 None
10180 }
10181 }
10182
10183 fn visit_class_members_from_declaration<'tree>(
10184 &mut self,
10185 node: Node<'tree>,
10186 scope: &ScopeInfo,
10187 ancestry: &ParentIndex<'tree>,
10188 ) {
10189 let mut cursor = node.walk();
10190 for child in node.named_children(&mut cursor) {
10191 if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
10192 self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
10193 } else if let Some(declarator) = Self::variable_declarator_from_declaration_child(child)
10194 {
10195 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
10196 }
10197 }
10198 }
10199
10200 fn visit_global_variables_from_declaration<'tree>(
10201 &mut self,
10202 node: Node<'tree>,
10203 scope: &ScopeInfo,
10204 ancestry: &ParentIndex<'tree>,
10205 ) {
10206 let mut cursor = node.walk();
10207 for child in node.named_children(&mut cursor) {
10208 if let Some(declarator) = Self::variable_declarator_from_declaration_child(child) {
10209 self.visit_variable_declaration(node, declarator, scope, false, ancestry);
10210 }
10211 }
10212 }
10213
10214 fn visit_type_declaration<'tree>(
10215 &mut self,
10216 node: Node<'tree>,
10217 scope: &ScopeInfo,
10218 stack: &mut Vec<CppWork<'tree>>,
10219 ancestry: &ParentIndex<'tree>,
10220 ) {
10221 let type_node = node.child_by_field_name("type");
10222 if let Some(type_node) = type_node
10223 && matches!(
10224 type_node.kind(),
10225 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10226 )
10227 {
10228 self.visit_class_like(type_node, scope, stack, ancestry);
10229 }
10230
10231 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
10232 let range = Range {
10233 start_byte: node.start_byte(),
10234 end_byte: recovered.end_node.end_byte(),
10235 start_line: node.start_position().row + 1,
10236 end_line: recovered.end_node.end_position().row + 1,
10237 };
10238 let signature = self
10239 .source
10240 .get(range.start_byte..range.end_byte)
10241 .map(normalize_cpp_whitespace)
10242 .unwrap_or_default();
10243 self.record_type_aliases(
10244 ancestry,
10245 node,
10246 scope,
10247 vec![recovered.name],
10248 signature,
10249 range,
10250 );
10251 return;
10252 }
10253
10254 let alias_names = match node.kind() {
10255 "alias_declaration" => extract_alias_declaration_name(node, self.source)
10256 .into_iter()
10257 .collect::<Vec<_>>(),
10258 "type_definition" => extract_typedef_alias_names(node, self.source),
10259 _ => Vec::new(),
10260 };
10261 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
10262 (type_node, alias_names.as_slice())
10263 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
10264 && type_node.child_by_field_name("name").is_none()
10265 {
10266 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
10267 } else {
10268 None
10269 };
10270 self.add_type_aliases(ancestry, node, scope, alias_names);
10271 if let Some((body, alias_name)) = anonymous_aggregate {
10272 let signature = normalize_cpp_whitespace(node_text(node, self.source));
10278 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
10279 debug_assert!(self.parsed.contains_declaration(&alias_unit));
10280 let mut nested_scope = scope.clone();
10281 nested_scope.class_unit = Some(alias_unit);
10282 nested_scope.template_signature = scope.template_signature.clone();
10283 nested_scope.template_metadata = None;
10284 nested_scope.declarations_are_fields = false;
10285 nested_scope.recovered_specialization_member_scope = false;
10286 stack.push(CppWork::Container(CppContainer {
10287 node: body,
10288 scope: nested_scope,
10289 }));
10290 }
10291 }
10292
10293 fn add_type_aliases(
10294 &mut self,
10295 ancestry: &ParentIndex<'_>,
10296 node: Node<'_>,
10297 scope: &ScopeInfo,
10298 alias_names: Vec<String>,
10299 ) {
10300 let signature = normalize_cpp_whitespace(node_text(node, self.source));
10301 self.record_type_aliases(
10302 ancestry,
10303 node,
10304 scope,
10305 alias_names,
10306 signature,
10307 cpp_declaration_range(node),
10308 );
10309 }
10310
10311 fn record_type_aliases(
10312 &mut self,
10313 ancestry: &ParentIndex<'_>,
10314 node: Node<'_>,
10315 scope: &ScopeInfo,
10316 alias_names: Vec<String>,
10317 signature: String,
10318 range: Range,
10319 ) {
10320 if signature.is_empty() {
10321 return;
10322 }
10323 let type_name = node
10324 .child_by_field_name("type")
10325 .and_then(|type_node| type_node.child_by_field_name("name"))
10326 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
10327 for alias_name in alias_names {
10328 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
10329 continue;
10330 }
10331 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
10332 self.note_declaration(&code_unit);
10335 self.link_source_declaration(ancestry, &code_unit, node);
10336 self.parsed
10337 .add_code_unit_with_range(code_unit.clone(), range, None, None);
10338 let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
10339 let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
10340 cpp_structured_type_identity(type_node, self.source, &lexical_scope)
10341 });
10342 self.add_signature_with_metadata(
10343 code_unit.clone(),
10344 SignatureMetadata::new(signature.clone(), Vec::new())
10345 .with_underlying_type_identity(underlying_type_identity),
10346 );
10347 if let Some(metadata) = &scope.template_metadata {
10348 let mut metadata = metadata.clone();
10349 metadata.primary_fq_name = code_unit.fq_name();
10350 self.parsed
10351 .set_cpp_template_metadata(code_unit.clone(), metadata);
10352 }
10353 if let Some(parent) = &scope.class_unit {
10354 self.parsed.add_child(parent.clone(), code_unit.clone());
10355 } else if let Some(module) = &scope.module {
10356 self.parsed.add_child(module.clone(), code_unit.clone());
10357 }
10358 self.parsed.mark_type_alias(code_unit);
10359 }
10360 }
10361
10362 fn type_alias_unit(
10363 &self,
10364 scope: &ScopeInfo,
10365 alias_name: String,
10366 signature: String,
10367 ) -> CodeUnit {
10368 let short_name = if let Some(parent) = &scope.class_unit {
10369 cpp_join_nested_short(parent.short_name(), &alias_name)
10370 } else {
10371 alias_name.clone()
10372 };
10373 let fq = cpp_leaf_fq(
10374 &scope.package_name,
10375 scope.class_unit.as_ref(),
10376 &alias_name,
10377 SegmentKind::Nested,
10378 SegmentKind::Type,
10379 );
10380 CodeUnit::with_signature_and_fq(
10381 self.file.clone(),
10382 CodeUnitType::Class,
10383 scope.package_name.clone(),
10384 short_name,
10385 Some(signature),
10386 false,
10387 fq,
10388 )
10389 }
10390
10391 fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
10392 if let Some(replacement) =
10396 crate::graph::syntax::function_macro_replacement_span(node, self.source)
10397 {
10398 self.consumed_fragment_regions
10399 .push((replacement.start, replacement.end));
10400 }
10401 let Some(name) = extract_macro_name(node, self.source) else {
10402 return;
10403 };
10404 let signature = node_text(node, self.source).trim_end().to_string();
10405 if signature.is_empty() {
10406 return;
10407 }
10408 let fq = cpp_member_fq("", &name);
10409 let code_unit = CodeUnit::with_signature_and_fq(
10416 self.file.clone(),
10417 CodeUnitType::Macro,
10418 "",
10419 name.clone(),
10420 Some(signature.clone()),
10421 false,
10422 fq,
10423 );
10424 if !self.parsed.contains_declaration(&code_unit) {
10425 self.add_declaration(ancestry, code_unit.clone(), node, None, None);
10426 let name_range = node
10427 .child_by_field_name("name")
10428 .map(cpp_declaration_range)
10429 .unwrap_or_else(|| cpp_declaration_range(node));
10430 self.parsed
10431 .record_materialization(MaterializationRecord::GeneratedDeclaration {
10432 site: cpp_declaration_range(node),
10433 argument: name_range,
10434 kind: GenerationKind::PreprocessorDefinition,
10435 unit: code_unit.clone(),
10436 });
10437 self.parsed.add_signature(code_unit, signature);
10438 }
10439 if node.kind() == "preproc_def" {
10440 update_object_macro_field_environment(
10441 node,
10442 self.source,
10443 &mut self.object_macro_fields,
10444 &mut self.ambiguous_object_macro_fields,
10445 );
10446 } else {
10447 self.object_macro_fields.remove(&name);
10448 self.ambiguous_object_macro_fields.remove(&name);
10449 }
10450 }
10451
10452 fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
10453 let Some(directive) = node.child_by_field_name("directive") else {
10454 return;
10455 };
10456 let name = node_text(directive, self.source).trim();
10457 let range = cpp_declaration_range(node);
10458 let fields = object_macro_field_closure(&self.object_macro_fields, name);
10459 self.materialize_object_macro_fields(fields, range, node, scope);
10460 }
10461
10462 fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
10467 if !matches!(node.kind(), "declaration" | "field_declaration") {
10468 return false;
10469 }
10470 if scope.class_unit.is_none() {
10478 return false;
10479 }
10480 let macro_nodes =
10481 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
10482 for macro_node in ¯o_nodes {
10483 let name = node_text(*macro_node, self.source).trim();
10484 let fields = object_macro_field_closure(&self.object_macro_fields, name);
10485 self.materialize_object_macro_fields(
10486 fields,
10487 cpp_declaration_range(*macro_node),
10488 *macro_node,
10489 scope,
10490 );
10491 }
10492 let Some(last) = macro_nodes.last() else {
10493 return false;
10494 };
10495 self.record_collapsed_aggregate_fields(
10501 last.end_byte()..node.end_byte(),
10502 node.start_position().row
10503 + 1
10504 + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
10505 node,
10506 scope,
10507 );
10508 true
10509 }
10510
10511 fn capture_recovered_field_facts(
10515 &mut self,
10516 unit: &CodeUnit,
10517 range: Range,
10518 context: Node<'_>,
10519 field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
10520 names_function_type: bool,
10521 ) {
10522 let declaration = *self.source_links[unit]
10523 .last()
10524 .expect("recovered field follows its source occurrence");
10525 let mut fact = crate::source_facts::capture_declaration(
10526 declaration,
10527 context,
10528 unit,
10529 self.source,
10530 self.primary_ancestry,
10531 );
10532 fact.field_type = field_type;
10533 fact.names_function_type = names_function_type;
10534 fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
10535 let captured = crate::source_context::capture_declaration_context(
10536 context,
10537 unit,
10538 self.source,
10539 &mut self.preprocessor_context_cache,
10540 );
10541 fact.guard_requirements = captured.guard_requirements;
10542 fact.callable_guards = fact.guard_requirements.clone();
10543 fact.callable_activation = Some(range.end_byte);
10544 fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
10545 fact.flattened_macro_namespace = captured.flattened_macro_namespace;
10546 let intern = |node: Node<'_>| {
10547 let mut collector = self.source_collector.borrow_mut();
10548 if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
10549 collector.intern_node(node)
10550 } else {
10551 collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
10552 }
10553 };
10554 fact.conditional_family =
10555 crate::graph::resolver::preprocessor_conditional_family_node(context).map(intern);
10556 fact.exhaustive_conditional_family = captured.exhaustive_conditional_family.map(intern);
10557 fact.displaced_namespace_closing_brace =
10558 captured.displaced_namespace_closing_brace.map(intern);
10559 self.capture_source_dependencies(&mut fact, context, unit);
10560 self.apply_active_source_context(&mut fact);
10561 assert!(
10562 self.source_properties
10563 .borrow_mut()
10564 .insert(declaration, fact)
10565 .is_none()
10566 );
10567 }
10568
10569 fn materialize_object_macro_fields(
10570 &mut self,
10571 fields: Vec<MacroReplacementField>,
10572 range: Range,
10573 context: Node<'_>,
10574 scope: &ScopeInfo,
10575 ) {
10576 let Some(owner) = scope.class_unit.as_ref() else {
10577 return;
10578 };
10579 for field in fields {
10580 let signature = field.declaration.clone();
10581 let mut fq = owner.fq().clone();
10582 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
10583 let short_name = if owner.short_name().is_empty() {
10584 field.name.clone()
10585 } else {
10586 format!("{}.{}", owner.short_name(), field.name)
10587 };
10588 let code_unit = CodeUnit::with_signature_and_fq(
10589 self.file.clone(),
10590 CodeUnitType::Field,
10591 owner.package_name().to_string(),
10592 short_name,
10593 Some(field.declaration),
10594 true,
10595 fq,
10596 );
10597 if self.parsed.contains_declaration(&code_unit) {
10598 continue;
10599 }
10600 self.add_declaration_with_range(
10601 self.primary_ancestry,
10602 code_unit.clone(),
10603 range,
10604 None,
10605 Some(owner.clone()),
10606 None,
10607 );
10608 self.capture_recovered_field_facts(
10609 &code_unit,
10610 range,
10611 context,
10612 field.field_type,
10613 field.names_function_type,
10614 );
10615 self.parsed.add_signature(code_unit, signature);
10616 }
10617 }
10618
10619 fn collapsed_aggregate_owner(
10620 &self,
10621 name: &str,
10622 scope: &ScopeInfo,
10623 parent: Option<&CodeUnit>,
10624 ) -> CodeUnit {
10625 let short_name = parent.map_or_else(
10626 || name.to_string(),
10627 |parent| cpp_join_nested_short(parent.short_name(), name),
10628 );
10629 let fq = cpp_leaf_fq(
10630 &scope.package_name,
10631 parent,
10632 name,
10633 SegmentKind::Nested,
10634 SegmentKind::Type,
10635 );
10636 CodeUnit::with_signature_and_fq(
10637 self.file.clone(),
10638 CodeUnitType::Class,
10639 scope.package_name.clone(),
10640 short_name,
10641 None,
10642 false,
10643 fq,
10644 )
10645 }
10646
10647 fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
10654 let mut cursor = node.walk();
10655 let children = node.children(&mut cursor).collect::<Vec<_>>();
10656 let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
10657 let mut object_macro_fields = self.object_macro_fields.clone();
10658 let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
10659 let mut open = Vec::<usize>::new();
10660 let mut index = 0;
10661 while index < children.len() {
10662 let keyword = children[index];
10663 if update_object_macro_field_environment(
10664 keyword,
10665 self.source,
10666 &mut object_macro_fields,
10667 &mut ambiguous_object_macro_fields,
10668 ) {
10669 index += 1;
10670 continue;
10671 }
10672 if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
10673 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
10674 if !name.is_empty() {
10675 let parent = open
10676 .last()
10677 .and_then(|class| recovered.get(*class))
10678 .map(|(owner, _, _, _)| owner.clone())
10679 .or_else(|| scope.class_unit.clone());
10680 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
10681 recovered.push((
10682 owner,
10683 head.key.start_byte(),
10684 head.opening.end_byte(),
10685 Vec::new(),
10686 ));
10687 let class_index = recovered.len() - 1;
10688 match head.folded_members {
10689 None => open.push(class_index),
10693 Some(members) => {
10699 let macro_nodes = object_macro_identifier_nodes_with_environment(
10700 children[index],
10701 self.source,
10702 &mut object_macro_fields,
10703 &mut ambiguous_object_macro_fields,
10704 );
10705 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
10706 .iter()
10707 .partition(|node| node.start_byte() < head.key.start_byte());
10708 if let Some(&enclosing) = open.last() {
10709 for macro_node in preceding {
10710 recovered[enclosing]
10711 .3
10712 .push(CppCollapsedMember::MacroFields {
10713 context: macro_node,
10714 range: cpp_declaration_range(macro_node),
10715 fields: object_macro_field_closure(
10716 &object_macro_fields,
10717 &normalize_cpp_whitespace(node_text(
10718 macro_node,
10719 self.source,
10720 )),
10721 ),
10722 });
10723 }
10724 }
10725 let closing = cpp_collapsed_aggregate_closing_brace(members);
10726 recovered[class_index]
10727 .3
10728 .extend(cpp_collapsed_aggregate_members(
10729 members,
10730 &inner,
10731 head.opening.end_byte()..closing,
10732 head.opening.end_position().row + 1,
10733 self.source,
10734 &object_macro_fields,
10735 ));
10736 recovered[class_index].2 = members.end_byte();
10737 for &open_class in &open {
10738 recovered[open_class].2 =
10739 recovered[open_class].2.max(members.end_byte());
10740 }
10741 }
10742 }
10743 index += head.width;
10744 continue;
10745 }
10746 }
10747 if let Some(&class_index) = open.last()
10748 && children[index].kind() == "field_declaration"
10749 {
10750 let field = children[index];
10751 let macro_nodes = object_macro_identifier_nodes_with_environment(
10752 field,
10753 self.source,
10754 &mut object_macro_fields,
10755 &mut ambiguous_object_macro_fields,
10756 );
10757 recovered[class_index]
10758 .3
10759 .extend(cpp_collapsed_aggregate_members(
10760 field,
10761 ¯o_nodes,
10762 field.start_byte()..field.end_byte(),
10763 field.start_position().row + 1,
10764 self.source,
10765 &object_macro_fields,
10766 ));
10767 let end = field.end_byte();
10768 for &open_class in &open {
10769 recovered[open_class].2 = recovered[open_class].2.max(end);
10770 }
10771 let closes = count_close_brace_nodes(field);
10772 for _ in 0..closes {
10773 if let Some(closed) = open.pop() {
10774 recovered[closed].2 = end;
10775 }
10776 }
10777 }
10778 index += 1;
10779 }
10780
10781 let mut owners = Vec::with_capacity(recovered.len());
10782 for (owner, start, end, members) in recovered {
10783 let parent = owners
10784 .iter()
10785 .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
10786 .cloned()
10787 .or_else(|| scope.class_unit.clone());
10788 self.declare_collapsed_aggregate(
10789 owner.clone(),
10790 start..end,
10791 Some(node),
10792 parent,
10793 members,
10794 scope,
10795 );
10796 owners.push(owner);
10797 }
10798 }
10799
10800 fn declare_collapsed_aggregate(
10802 &mut self,
10803 owner: CodeUnit,
10804 span: std::ops::Range<usize>,
10805 evidence: Option<Node<'_>>,
10806 parent: Option<CodeUnit>,
10807 members: Vec<CppCollapsedMember<'_>>,
10808 scope: &ScopeInfo,
10809 ) {
10810 let range = Range {
10811 start_byte: span.start,
10812 end_byte: span.end,
10813 start_line: self.source.get(..span.start).map_or(1, |source| {
10814 source.bytes().filter(|byte| *byte == b'\n').count() + 1
10815 }),
10816 end_line: self.source.get(..span.end).map_or(1, |source| {
10817 source.bytes().filter(|byte| *byte == b'\n').count() + 1
10818 }),
10819 };
10820 self.add_declaration_with_range(
10827 self.primary_ancestry,
10828 owner.clone(),
10829 range,
10830 evidence,
10831 parent,
10832 None,
10833 );
10834 let declaration = *self.source_links[&owner]
10838 .last()
10839 .expect("collapsed aggregate has a source occurrence");
10840 {
10841 let mut properties = self.source_properties.borrow_mut();
10842 let fact = properties
10843 .get_mut(&declaration)
10844 .expect("collapsed aggregate retains its original context");
10845 fact.class_strength =
10846 brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
10847 fact.occurrence_role =
10848 brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
10849 }
10850 let owner_scope = ScopeInfo {
10851 class_unit: Some(owner),
10852 declarations_are_fields: true,
10853 ..scope.clone()
10854 };
10855 for member in members {
10856 match member {
10857 CppCollapsedMember::MacroFields {
10858 range,
10859 fields,
10860 context,
10861 } => {
10862 self.materialize_object_macro_fields(fields, range, context, &owner_scope);
10863 }
10864 CppCollapsedMember::Declarations {
10865 span,
10866 start_line,
10867 context,
10868 } => {
10869 self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
10870 }
10871 }
10872 }
10873 }
10874
10875 fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
10886 if node.parent().is_some_and(|parent| parent.is_error()) {
10887 return false;
10890 }
10891 let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
10892 return false;
10893 };
10894 let members = head
10895 .folded_members
10896 .expect("a folded aggregate head carries its member list");
10897 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
10898 if name.is_empty() {
10899 return false;
10900 }
10901 let macro_nodes =
10902 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
10903 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
10904 .iter()
10905 .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
10906 for macro_node in preceding {
10907 let fields = object_macro_field_closure(
10908 &self.object_macro_fields,
10909 &normalize_cpp_whitespace(node_text(macro_node, self.source)),
10910 );
10911 self.materialize_object_macro_fields(
10912 fields,
10913 cpp_declaration_range(macro_node),
10914 macro_node,
10915 scope,
10916 );
10917 }
10918 let parent = scope.class_unit.clone();
10919 let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
10920 let recovered = cpp_collapsed_aggregate_members(
10921 members,
10922 &inner,
10923 head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
10924 head.opening.end_position().row + 1,
10925 self.source,
10926 &self.object_macro_fields,
10927 );
10928 self.declare_collapsed_aggregate(
10929 owner,
10930 head.key.start_byte()..members.end_byte(),
10931 Some(node),
10932 parent,
10933 recovered,
10934 scope,
10935 );
10936 true
10937 }
10938
10939 fn record_collapsed_aggregate_fields(
10944 &mut self,
10945 span: std::ops::Range<usize>,
10946 start_line: usize,
10947 context: Node<'_>,
10948 scope: &ScopeInfo,
10949 ) {
10950 let Some(owner) = scope.class_unit.as_ref() else {
10951 return;
10952 };
10953 for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
10954 let range = Range {
10955 start_byte: field.range.start,
10956 end_byte: field.range.end,
10957 start_line: start_line
10958 + cpp_line_breaks_between(self.source, span.start, field.range.start),
10959 end_line: start_line
10960 + cpp_line_breaks_between(self.source, span.start, field.range.end),
10961 };
10962 let mut fq = owner.fq().clone();
10963 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
10964 let short_name = if owner.short_name().is_empty() {
10965 field.name.clone()
10966 } else {
10967 format!("{}.{}", owner.short_name(), field.name)
10968 };
10969 let signature = normalize_cpp_whitespace(&field.declaration);
10970 let code_unit = CodeUnit::with_signature_and_fq(
10971 self.file.clone(),
10972 CodeUnitType::Field,
10973 owner.package_name().to_string(),
10974 short_name,
10975 Some(signature.clone()),
10976 false,
10977 fq,
10978 );
10979 if self.parsed.contains_declaration(&code_unit) {
10980 continue;
10981 }
10982 self.add_declaration_with_range(
10983 self.primary_ancestry,
10984 code_unit.clone(),
10985 range,
10986 None,
10987 Some(owner.clone()),
10988 None,
10989 );
10990 self.capture_recovered_field_facts(
10991 &code_unit,
10992 range,
10993 context,
10994 field.field_type,
10995 field.names_function_type,
10996 );
10997 self.parsed.add_signature(code_unit, signature);
10998 }
10999 }
11000
11001 fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
11002 let Some(_directive) = node.child_by_field_name("directive") else {
11003 return;
11004 };
11005 if is_cpp_undef_directive(node, self.source) {
11006 update_object_macro_field_environment(
11007 node,
11008 self.source,
11009 &mut self.object_macro_fields,
11010 &mut self.ambiguous_object_macro_fields,
11011 );
11012 return;
11013 }
11014 let directly_in_field_list = node
11015 .parent()
11016 .is_some_and(|parent| parent.kind() == "field_declaration_list");
11017 if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
11018 self.visit_object_macro_fields(node, scope);
11019 }
11020 }
11021}
11022
11023fn object_macro_field_closure(
11031 environment: &HashMap<String, ObjectMacroReplacement>,
11032 name: &str,
11033) -> Vec<MacroReplacementField> {
11034 let mut fields = Vec::new();
11035 let mut visited = HashSet::default();
11036 let mut stack = vec![name.to_string()];
11037 while let Some(current) = stack.pop() {
11038 if !visited.insert(current.clone()) {
11039 continue;
11040 }
11041 let Some(replacement) = environment.get(¤t) else {
11042 continue;
11043 };
11044 fields.extend(replacement.fields.iter().cloned());
11045 stack.extend(replacement.nested.iter().rev().cloned());
11046 }
11047 fields
11048}
11049
11050fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
11056 crate::graph::syntax::object_macro_replacement_span(node, source)
11057 .and_then(|span| source.get(span))
11058 .map(crate::graph::syntax::object_macro_replacement)
11059 .unwrap_or_default()
11060}
11061
11062fn update_object_macro_field_environment(
11067 node: Node<'_>,
11068 source: &str,
11069 fields: &mut HashMap<String, ObjectMacroReplacement>,
11070 ambiguous: &mut HashSet<String>,
11071) -> bool {
11072 match node.kind() {
11073 "preproc_def" => {
11074 let Some(name) = extract_macro_name(node, source) else {
11075 return false;
11076 };
11077 let replacement = object_macro_replacement_of(node, source);
11078 if replacement.is_empty() || ambiguous.contains(&name) {
11079 fields.remove(&name);
11080 ambiguous.insert(name);
11081 } else if let Some(previous) = fields.get(&name) {
11082 if previous != &replacement {
11083 fields.remove(&name);
11084 ambiguous.insert(name);
11085 }
11086 } else {
11087 fields.insert(name, replacement);
11088 }
11089 true
11090 }
11091 "preproc_call" if is_cpp_undef_directive(node, source) => {
11092 if let Some(argument) = node.child_by_field_name("argument") {
11093 let name = node_text(argument, source).trim();
11094 fields.remove(name);
11095 if inside_preprocessor_conditional(node) {
11096 ambiguous.insert(name.to_string());
11097 } else {
11098 ambiguous.remove(name);
11099 }
11100 }
11101 true
11102 }
11103 _ => false,
11104 }
11105}
11106
11107fn object_macro_identifier_nodes<'tree>(
11108 node: Node<'tree>,
11109 source: &str,
11110 fields: &HashMap<String, ObjectMacroReplacement>,
11111) -> Vec<Node<'tree>> {
11112 walk_object_macro_identifier_nodes(node, |current, result| {
11113 if matches!(
11114 current.kind(),
11115 "identifier" | "field_identifier" | "type_identifier"
11116 ) && fields.contains_key(node_text(current, source).trim())
11117 {
11118 result.push(current);
11119 }
11120 false
11121 })
11122}
11123
11124fn walk_object_macro_identifier_nodes<'tree>(
11125 node: Node<'tree>,
11126 mut visit: impl FnMut(Node<'tree>, &mut Vec<Node<'tree>>) -> bool,
11127) -> Vec<Node<'tree>> {
11128 let mut result = Vec::new();
11129 let mut stack = vec![node];
11130 while let Some(current) = stack.pop() {
11131 if visit(current, &mut result) {
11132 continue;
11133 }
11134 let mut cursor = current.walk();
11135 let children = current.children(&mut cursor).collect::<Vec<_>>();
11136 stack.extend(children.into_iter().rev());
11137 }
11138 result.sort_by_key(|node| node.start_byte());
11139 result
11140}
11141
11142fn object_macro_identifier_nodes_with_environment<'tree>(
11147 node: Node<'tree>,
11148 source: &str,
11149 fields: &mut HashMap<String, ObjectMacroReplacement>,
11150 ambiguous: &mut HashSet<String>,
11151) -> Vec<Node<'tree>> {
11152 walk_object_macro_identifier_nodes(node, |current, result| {
11153 if update_object_macro_field_environment(current, source, fields, ambiguous) {
11154 return true;
11155 }
11156 if matches!(
11157 current.kind(),
11158 "identifier" | "field_identifier" | "type_identifier"
11159 ) && fields.contains_key(node_text(current, source).trim())
11160 {
11161 result.push(current);
11162 }
11163 false
11164 })
11165}
11166
11167enum CppCollapsedMember<'tree> {
11170 MacroFields {
11173 context: Node<'tree>,
11174 range: Range,
11175 fields: Vec<MacroReplacementField>,
11176 },
11177 Declarations {
11180 context: Node<'tree>,
11181 span: std::ops::Range<usize>,
11182 start_line: usize,
11183 },
11184}
11185
11186struct CppCollapsedAggregateHead<'tree> {
11201 key: Node<'tree>,
11202 name: Node<'tree>,
11203 opening: Node<'tree>,
11204 folded_members: Option<Node<'tree>>,
11208 width: usize,
11210}
11211
11212fn cpp_collapsed_aggregate_head<'tree>(
11214 children: &[Node<'tree>],
11215 index: usize,
11216 source: &str,
11217) -> Option<CppCollapsedAggregateHead<'tree>> {
11218 let key = *children.get(index)?;
11219 if matches!(key.kind(), "struct" | "class" | "union") {
11220 let name = *children.get(index + 1)?;
11221 let opening = *children.get(index + 2)?;
11222 if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
11223 return None;
11224 }
11225 return Some(CppCollapsedAggregateHead {
11226 key,
11227 name,
11228 opening,
11229 folded_members: None,
11230 width: 3,
11231 });
11232 }
11233 cpp_folded_aggregate_head(key, source)
11234}
11235
11236fn cpp_folded_aggregate_head<'tree>(
11239 node: Node<'tree>,
11240 source: &str,
11241) -> Option<CppCollapsedAggregateHead<'tree>> {
11242 if !matches!(node.kind(), "declaration" | "field_declaration") {
11243 return None;
11244 }
11245 let mut cursor = node.walk();
11246 let children = node.children(&mut cursor).collect::<Vec<_>>();
11247 let key_index = children.iter().position(|child| {
11248 child.is_error()
11249 && matches!(
11250 node_text(*child, source).trim(),
11251 "struct" | "class" | "union"
11252 )
11253 })?;
11254 let declarator = *children.get(key_index + 1)?;
11255 let (name, members) = if declarator.kind() == "init_declarator" {
11258 (
11259 declarator.child_by_field_name("declarator")?,
11260 declarator.child_by_field_name("value")?,
11261 )
11262 } else {
11263 (declarator, *children.get(key_index + 2)?)
11264 };
11265 if !matches!(
11266 name.kind(),
11267 "field_identifier" | "type_identifier" | "identifier"
11268 ) {
11269 return None;
11270 }
11271 if members.kind() != "initializer_list" {
11272 return None;
11273 }
11274 let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
11275 Some(CppCollapsedAggregateHead {
11276 key: children[key_index],
11277 name,
11278 opening,
11279 folded_members: Some(members),
11280 width: 1,
11281 })
11282}
11283
11284fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
11288 let mut cursor = members.walk();
11289 members
11290 .children(&mut cursor)
11291 .filter(|child| child.kind() == "}" && !child.is_missing())
11292 .last()
11293 .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
11294}
11295
11296fn cpp_collapsed_aggregate_members<'tree>(
11300 context: Node<'tree>,
11301 macro_nodes: &[Node<'tree>],
11302 region: std::ops::Range<usize>,
11303 region_start_line: usize,
11304 source: &str,
11305 environment: &HashMap<String, ObjectMacroReplacement>,
11306) -> Vec<CppCollapsedMember<'tree>> {
11307 let mut members = macro_nodes
11308 .iter()
11309 .map(|macro_node| CppCollapsedMember::MacroFields {
11310 context: *macro_node,
11311 range: cpp_declaration_range(*macro_node),
11312 fields: object_macro_field_closure(
11313 environment,
11314 &normalize_cpp_whitespace(node_text(*macro_node, source)),
11315 ),
11316 })
11317 .collect::<Vec<_>>();
11318 let declarations_start = macro_nodes
11322 .last()
11323 .map_or(region.start, |macro_node| macro_node.end_byte());
11324 members.push(CppCollapsedMember::Declarations {
11325 context,
11326 span: declarations_start..region.end,
11327 start_line: region_start_line
11328 + cpp_line_breaks_between(source, region.start, declarations_start),
11329 });
11330 members
11331}
11332
11333fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
11336 source.get(from..to).map_or(0, |slice| {
11337 slice.bytes().filter(|byte| *byte == b'\n').count()
11338 })
11339}
11340
11341fn count_close_brace_nodes(node: Node<'_>) -> usize {
11347 let mut opened = 0usize;
11348 let mut closed = 0usize;
11349 let mut stack = vec![node];
11350 while let Some(current) = stack.pop() {
11351 if !current.is_missing() {
11352 match current.kind() {
11353 "{" => opened += 1,
11354 "}" => closed += 1,
11355 _ => {}
11356 }
11357 }
11358 let mut cursor = current.walk();
11359 stack.extend(current.children(&mut cursor));
11360 }
11361 closed.saturating_sub(opened)
11362}
11363
11364pub fn cpp_field_declaration_linkage<'tree>(
11369 declaration: Node<'tree>,
11370 source: &str,
11371 ancestry: &ParentIndex<'tree>,
11372) -> CppFieldLinkage {
11373 let mut current = ancestry.parent(declaration);
11374 let mut enclosed_by_class = false;
11375 while let Some(node) = current {
11376 if node.kind() == "namespace_definition"
11377 && node
11378 .child_by_field_name("name")
11379 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11380 {
11381 return CppFieldLinkage::Internal;
11382 }
11383 if matches!(
11384 node.kind(),
11385 "class_specifier" | "struct_specifier" | "union_specifier"
11386 ) && node
11387 .child_by_field_name("name")
11388 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11389 {
11390 return CppFieldLinkage::Internal;
11391 }
11392 if matches!(
11393 node.kind(),
11394 "class_specifier" | "struct_specifier" | "union_specifier"
11395 ) {
11396 enclosed_by_class = true;
11397 }
11398 if matches!(node.kind(), "function_definition" | "lambda_expression") {
11399 return CppFieldLinkage::Internal;
11400 }
11401 current = ancestry.parent(node);
11402 }
11403 if enclosed_by_class {
11404 return CppFieldLinkage::External;
11405 }
11406 let mut cursor = declaration.walk();
11407 let mut has_static = false;
11408 let mut has_extern = false;
11409 let mut has_inline = false;
11410 let mut has_const = false;
11411 let mut has_constexpr = false;
11412 for child in declaration.named_children(&mut cursor) {
11413 let text = normalize_cpp_whitespace(node_text(child, source));
11414 match (child.kind(), text.as_str()) {
11415 ("storage_class_specifier", "static") => has_static = true,
11416 ("storage_class_specifier", "extern") => has_extern = true,
11417 ("storage_class_specifier", "inline") => has_inline = true,
11418 ("storage_class_specifier", "constexpr") => has_constexpr = true,
11419 ("type_qualifier", "const") => has_const = true,
11420 ("type_qualifier", "constexpr") => has_constexpr = true,
11421 _ => {}
11422 }
11423 }
11424 if has_static {
11425 CppFieldLinkage::Internal
11426 } else if has_extern || has_inline {
11427 CppFieldLinkage::External
11428 } else if has_const || has_constexpr {
11429 CppFieldLinkage::InternalUnlessExternalPeer
11430 } else {
11431 CppFieldLinkage::External
11432 }
11433}
11434
11435fn cpp_declaration_range(node: Node<'_>) -> Range {
11436 Range {
11437 start_byte: node.start_byte(),
11438 end_byte: node.end_byte(),
11439 start_line: node.start_position().row + 1,
11440 end_line: node.end_position().row + 1,
11441 }
11442}
11443
11444fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
11448 let line_at = |byte: usize| {
11449 source.as_bytes()[..byte]
11450 .iter()
11451 .filter(|&&b| b == b'\n')
11452 .count()
11453 + 1
11454 };
11455 Range {
11456 start_byte,
11457 end_byte,
11458 start_line: line_at(start_byte),
11459 end_line: line_at(end_byte),
11460 }
11461}
11462
11463fn cpp_include_info(raw_snippet: String) -> ImportInfo {
11464 ImportInfo {
11465 raw_snippet,
11466 is_wildcard: false,
11467 is_global: false,
11468 identifier: None,
11469 alias: None,
11470 path: None,
11471 binder_span: None,
11472 }
11473}
11474
11475fn recover_quoted_source_imports(
11476 source: &str,
11477 imports: &mut Vec<SourceImportFact>,
11478 includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
11479 collector: &RefCell<PrimarySourceFactCollector<'_>>,
11480) {
11481 let mut in_block_comment = false;
11482 let mut start = 0;
11483 for line in source.split_inclusive('\n') {
11484 let end = start + line.len();
11485 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
11486 let trimmed = stripped.trim();
11487 if looks_like_quoted_include_line(trimmed) {
11488 let raw = normalize_cpp_whitespace(trimmed);
11489 if !imports.iter().any(|import| import.statement == raw) {
11490 let occurrence = collector.borrow_mut().intern_subspan_bytes(
11491 start,
11492 end,
11493 SourceOccurrenceProvenance::ExplicitSubspan,
11494 );
11495 let region =
11499 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
11500 let target = region.as_ref().and_then(|tree| {
11501 let mut pending = vec![tree.root_node()];
11502 while let Some(node) = pending.pop() {
11503 if node.kind() == "preproc_include"
11504 && let Some(path) = node.child_by_field_name("path")
11505 && path.kind() == "string_literal"
11506 {
11507 let (value, _) = crate::imports::include_path_from_node(node, source)?;
11508 let target = collector.borrow_mut().intern_embedded(
11509 brokk_bifrost_core::analyzer::tree_walk::node_range(path),
11510 );
11511 includes.push(
11512 brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
11513 declaration: occurrence,
11514 target,
11515 path: value.to_owned(),
11516 quoted: true,
11517 },
11518 );
11519 return Some(target);
11520 }
11521 let mut cursor = node.walk();
11522 let children: Vec<_> = node.named_children(&mut cursor).collect();
11523 pending.extend(children.into_iter().rev());
11524 }
11525 None
11526 });
11527 imports.push(SourceImportFact::from_import(
11528 cpp_include_info(raw),
11529 occurrence,
11530 target,
11531 None,
11532 Vec::new(),
11533 ));
11534 }
11535 }
11536 start = end;
11537 }
11538}
11539
11540fn looks_like_quoted_include_line(line: &str) -> bool {
11541 let Some(rest) = line.trim_start().strip_prefix('#') else {
11542 return false;
11543 };
11544 let Some(rest) = rest.trim_start().strip_prefix("include") else {
11545 return false;
11546 };
11547 rest.trim_start().starts_with('"')
11548}
11549
11550fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
11551 let mut raw = Vec::new();
11552 if let Some(bases) = cpp_class_like_base_clause(node) {
11553 collect_cpp_base_nodes(bases, source, &mut raw);
11554 }
11555 raw
11556}
11557
11558fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
11559 let mut facts = Vec::new();
11560 let mut cursor = node.walk();
11561 for child in node.named_children(&mut cursor) {
11562 if child.kind() != "base_class_clause" {
11563 continue;
11564 }
11565 let mut is_virtual = false;
11566 let mut child_cursor = child.walk();
11567 for base in child.children(&mut child_cursor) {
11568 match base.kind() {
11569 "virtual" => is_virtual = true,
11570 "," => is_virtual = false,
11571 "type_identifier"
11572 | "qualified_identifier"
11573 | "scoped_type_identifier"
11574 | "template_type"
11575 | "template_function" => {
11576 if let Some(fact) = crate::source_facts::base_specifier_from_node(
11577 base,
11578 source,
11579 is_virtual,
11580 crate::source_facts::base_access_specifier(child, base, node.kind()),
11581 ) {
11582 facts.push(fact);
11583 }
11584 is_virtual = false;
11585 }
11586 _ => {}
11587 }
11588 }
11589 }
11590 facts
11591}
11592
11593fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
11600 let mut cursor = class_like.walk();
11601 class_like
11602 .named_children(&mut cursor)
11603 .find(|child| child.kind() == "base_class_clause")
11604}
11605
11606fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
11607 walk_named_tree_preorder(node, false, |child| match child.kind() {
11608 "type_identifier" | "qualified_identifier" | "template_type" => {
11609 let text = normalize_cpp_whitespace(node_text(child, source));
11610 if !text.is_empty() {
11611 raw.push(text);
11612 }
11613 WalkControl::SkipChildren
11614 }
11615 _ => WalkControl::Continue,
11616 });
11617}
11618
11619fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
11620 let mut out = String::new();
11621 let chars: Vec<char> = line.chars().collect();
11622 let mut index = 0;
11623 let mut in_string = false;
11624 let mut in_char = false;
11625 let mut escape = false;
11626
11627 while index < chars.len() {
11628 let ch = chars[index];
11629 let next = chars.get(index + 1).copied();
11630
11631 if *in_block_comment {
11632 if ch == '*' && next == Some('/') {
11633 *in_block_comment = false;
11634 index += 2;
11635 } else {
11636 index += 1;
11637 }
11638 continue;
11639 }
11640
11641 if in_string {
11642 out.push(ch);
11643 if escape {
11644 escape = false;
11645 } else if ch == '\\' {
11646 escape = true;
11647 } else if ch == '"' {
11648 in_string = false;
11649 }
11650 index += 1;
11651 continue;
11652 }
11653
11654 if in_char {
11655 out.push(ch);
11656 if escape {
11657 escape = false;
11658 } else if ch == '\\' {
11659 escape = true;
11660 } else if ch == '\'' {
11661 in_char = false;
11662 }
11663 index += 1;
11664 continue;
11665 }
11666
11667 if ch == '/' && next == Some('/') {
11668 break;
11669 }
11670 if ch == '/' && next == Some('*') {
11671 *in_block_comment = true;
11672 index += 2;
11673 continue;
11674 }
11675 if ch == '"' {
11676 in_string = true;
11677 out.push(ch);
11678 index += 1;
11679 continue;
11680 }
11681 if ch == '\'' {
11682 in_char = true;
11683 out.push(ch);
11684 index += 1;
11685 continue;
11686 }
11687
11688 out.push(ch);
11689 index += 1;
11690 }
11691
11692 out
11693}
11694
11695#[derive(Clone)]
11696struct FunctionInfo {
11697 package_name: String,
11698 owner: Option<CppMemberOwner>,
11699 name: String,
11700 signature: String,
11701}
11702
11703#[derive(Clone)]
11710enum CppMemberOwner {
11711 Chain(Vec<String>),
11715 Unit(CodeUnit),
11718}
11719
11720impl CppMemberOwner {
11721 fn short_chain(&self) -> String {
11723 match self {
11724 Self::Chain(chain) => chain.join("$"),
11725 Self::Unit(parent) => parent.short_name().to_string(),
11726 }
11727 }
11728}
11729
11730enum DeclaratorKind<'a> {
11731 Function(Node<'a>),
11732 Variable(Node<'a>),
11733}
11734
11735impl FunctionInfo {
11736 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
11737 self.code_unit_with_synthetic(file, false)
11738 }
11739
11740 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
11741 let short_name = match &self.owner {
11742 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
11743 None => self.name.clone(),
11744 };
11745 let fq = match &self.owner {
11746 Some(CppMemberOwner::Chain(chain)) => {
11747 debug_assert!(
11748 !chain.is_empty(),
11749 "an empty owner chain is no owner; producers return None instead"
11750 );
11751 let mut fq = FqName::new();
11752 cpp_push_package(&mut fq, &self.package_name);
11753 let mut first = true;
11754 for component in chain {
11755 let kind = if first {
11756 SegmentKind::Type
11757 } else {
11758 SegmentKind::Nested
11759 };
11760 fq.push(segment_interner().intern(component, kind));
11761 first = false;
11762 }
11763 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
11764 fq
11765 }
11766 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
11767 .fq()
11768 .clone()
11769 .with_pushed(segment_interner().intern(&self.name, SegmentKind::Member)),
11770 Some(CppMemberOwner::Unit(_)) | None => {
11773 let mut fq = FqName::new();
11774 cpp_push_package(&mut fq, &self.package_name);
11775 fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
11776 fq
11777 }
11778 };
11779 CodeUnit::with_signature_and_fq(
11780 file,
11781 CodeUnitType::Function,
11782 self.package_name.clone(),
11783 short_name,
11784 Some(self.signature.clone()),
11785 synthetic,
11786 fq,
11787 )
11788 }
11789}
11790
11791fn extract_function_info(
11792 declarator: Node<'_>,
11793 source: &str,
11794 scope: &ScopeInfo,
11795) -> Option<FunctionInfo> {
11796 let parameters_node = declarator.child_by_field_name("parameters")?;
11797 let declarator_name_node = declarator
11798 .child_by_field_name("declarator")
11799 .or_else(|| parameters_node.prev_named_sibling())?;
11800 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
11801}
11802
11803fn extract_function_info_from_name(
11804 declarator: Node<'_>,
11805 declarator_name_node: Node<'_>,
11806 source: &str,
11807 scope: &ScopeInfo,
11808) -> Option<FunctionInfo> {
11809 let parameters_node = declarator.child_by_field_name("parameters")?;
11810 let parameters_text = cpp_parameter_signature(parameters_node, source);
11811 let recovered_specialization_member = scope
11812 .recovered_specialization_member_scope
11813 .then(|| {
11814 let terminal = declarator_name_node
11815 .child_by_field_name("name")
11816 .unwrap_or(declarator_name_node);
11817 let name = canonical_cpp_qualified_component(terminal, source)?.name;
11818 let owner = scope.class_unit.as_ref()?;
11819 Some((
11820 Some(CppMemberOwner::Unit(owner.clone())),
11821 name,
11822 scope.package_name.clone(),
11823 ))
11824 })
11825 .flatten();
11826 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
11827 parts
11828 } else if let Some(parts) =
11829 split_structured_templated_cpp_name(declarator_name_node, source, scope)
11830 {
11831 parts
11832 } else {
11833 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
11834 declarator_name_node,
11835 source,
11836 )?);
11837 if raw_name.is_empty() {
11838 return None;
11839 }
11840 split_cpp_name(&raw_name, scope)
11841 };
11842 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
11843 let mut signature = if suffix.is_empty() {
11844 parameters_text
11845 } else {
11846 format!("{parameters_text} {suffix}")
11847 };
11848 if let Some(template_signature) = &scope.template_signature {
11849 signature = format!("{template_signature}{signature}");
11850 }
11851
11852 Some(FunctionInfo {
11853 package_name,
11854 owner,
11855 name,
11856 signature,
11857 })
11858}
11859
11860fn cpp_macro_displaced_callable_parts<'tree>(
11867 function_declarator: Node<'tree>,
11868 source: &str,
11869 ancestry: &ParentIndex<'tree>,
11870) -> Option<(Node<'tree>, Node<'tree>)> {
11871 let definition = ancestry.parent(function_declarator)?;
11872 if definition.kind() != "function_definition"
11873 || definition.child_by_field_name("declarator") != Some(function_declarator)
11874 || definition
11875 .child_by_field_name("body")
11876 .is_none_or(|body| body.kind() != "compound_statement")
11877 {
11878 return None;
11879 }
11880 let macro_type = definition.child_by_field_name("type")?;
11881 if macro_type.kind() != "type_identifier"
11882 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11883 {
11884 return None;
11885 }
11886
11887 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
11888 if apparent_return_type.kind() == "qualified_identifier"
11889 && let (Some(return_type), Some(callable_name)) = (
11890 apparent_return_type.child_by_field_name("scope"),
11891 apparent_return_type.child_by_field_name("name"),
11892 )
11893 && return_type.kind() == "template_type"
11894 && matches!(callable_name.kind(), "identifier" | "field_identifier")
11895 && (0..apparent_return_type.child_count())
11896 .filter_map(|index| apparent_return_type.child(index))
11897 .any(|child| child.kind() == "::" && child.is_missing())
11898 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
11899 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
11900 {
11901 return Some((return_type, callable_name));
11902 }
11903 if !matches!(
11904 apparent_return_type.kind(),
11905 "identifier" | "field_identifier" | "type_identifier"
11906 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
11907 {
11908 return None;
11909 }
11910 let parameters = function_declarator.child_by_field_name("parameters")?;
11911 let mut cursor = function_declarator.walk();
11912 let between = function_declarator
11913 .named_children(&mut cursor)
11914 .filter(|child| child.kind() != "comment")
11915 .filter(|child| {
11916 child.start_byte() >= apparent_return_type.end_byte()
11917 && child.end_byte() <= parameters.start_byte()
11918 && !same_node(*child, apparent_return_type)
11919 && !same_node(*child, parameters)
11920 })
11921 .collect::<Vec<_>>();
11922 let [name_error] = between.as_slice() else {
11923 return None;
11924 };
11925 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
11926 return None;
11927 }
11928 let callable_name = name_error.named_child(0)?;
11929 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
11930 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
11931 {
11932 return None;
11933 }
11934 Some((apparent_return_type, callable_name))
11935}
11936
11937fn cpp_declarator_identity_suffix(
11953 declarator: Node<'_>,
11954 parameters_node: Node<'_>,
11955 source: &str,
11956) -> String {
11957 let mut cursor = declarator.walk();
11958 let parts = declarator
11959 .named_children(&mut cursor)
11960 .filter(|child| child.start_byte() >= parameters_node.end_byte())
11961 .filter(|child| {
11962 matches!(
11963 child.kind(),
11964 "type_qualifier"
11965 | "ref_qualifier"
11966 | "noexcept"
11967 | "throw_specifier"
11968 | "trailing_return_type"
11969 | "requires_clause"
11970 )
11971 })
11972 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
11973 .filter(|text| !text.is_empty())
11974 .collect::<Vec<_>>();
11975 normalize_cpp_qualifier_suffix(&parts.join(" "))
11976}
11977
11978pub(crate) fn cpp_callable_identity_suffix(
11985 function_declarator: Node<'_>,
11986 source: &str,
11987) -> Option<String> {
11988 let parameters_node = function_declarator.child_by_field_name("parameters")?;
11989 Some(cpp_declarator_identity_suffix(
11990 function_declarator,
11991 parameters_node,
11992 source,
11993 ))
11994}
11995
11996pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
11997 match classify_declarator(node)? {
11998 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
11999 DeclaratorKind::Variable(_) => None,
12000 }
12001}
12002
12003fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
12004 match node.kind() {
12005 "function_declarator" => {
12006 let inner = node
12007 .child_by_field_name("declarator")
12008 .or_else(|| node.child_by_field_name("name"))
12009 .or_else(|| last_named_child(node));
12010 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
12011 Some(DeclaratorKind::Variable(node))
12012 } else {
12013 Some(DeclaratorKind::Function(node))
12014 }
12015 }
12016 "macro_modifier" => None,
12019 "init_declarator"
12020 | "pointer_declarator"
12021 | "reference_declarator"
12022 | "parenthesized_declarator"
12023 | "array_declarator"
12024 | "attributed_declarator"
12025 | "template_function" => node
12026 .child_by_field_name("declarator")
12027 .or_else(|| node.child_by_field_name("name"))
12028 .or_else(|| last_named_child(node))
12029 .and_then(classify_declarator),
12030 "identifier" | "field_identifier" | "qualified_identifier" => {
12031 Some(DeclaratorKind::Variable(node))
12032 }
12033 _ => node
12034 .child_by_field_name("declarator")
12035 .or_else(|| node.child_by_field_name("name"))
12036 .or_else(|| last_named_child(node))
12037 .and_then(classify_declarator),
12038 }
12039}
12040
12041fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
12042 matches!(
12043 node.kind(),
12044 "function_declarator"
12045 | "init_declarator"
12046 | "pointer_declarator"
12047 | "reference_declarator"
12048 | "parenthesized_declarator"
12049 | "array_declarator"
12050 | "attributed_declarator"
12051 | "template_function"
12052 | "macro_modifier_declarator"
12053 | "identifier"
12054 | "field_identifier"
12055 | "qualified_identifier"
12056 )
12057}
12058
12059fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
12060 let class_like = first_class_like_child(node);
12061 let mut cursor = node.walk();
12062 node.named_children(&mut cursor).any(|child| {
12063 matches!(
12064 child.kind(),
12065 "init_declarator"
12066 | "pointer_declarator"
12067 | "reference_declarator"
12068 | "array_declarator"
12069 | "function_declarator"
12070 | "parenthesized_declarator"
12071 | "attributed_declarator"
12072 ) || matches!(
12073 child.kind(),
12074 "identifier" | "field_identifier" | "qualified_identifier"
12075 ) && class_like.is_none_or(|class_node| {
12076 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
12077 })
12078 })
12079}
12080
12081struct CppNamespaceForward {
12094 name: String,
12095 start_byte: usize,
12096 namespace_end_byte: usize,
12099 package_name: String,
12100}
12101
12102fn cpp_namespace_forward_entry<'tree>(
12108 node: Node<'tree>,
12109 source: &str,
12110 ancestry: &ParentIndex<'tree>,
12111) -> Option<CppNamespaceForward> {
12112 if !matches!(
12113 node.kind(),
12114 "class_specifier" | "struct_specifier" | "union_specifier"
12115 ) || cpp_body_node(node).is_some()
12116 {
12117 return None;
12118 }
12119 let parent = node.parent()?;
12120 if !(parent.kind() == "declaration_list"
12121 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
12122 {
12123 return None;
12124 }
12125 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
12126 if !namespace.has_error() {
12131 return None;
12132 }
12133 Some(CppNamespaceForward {
12134 name: class_like_name(node, source, ancestry)?,
12135 start_byte: node.start_byte(),
12136 namespace_end_byte: namespace.end_byte(),
12137 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
12138 })
12139}
12140
12141fn cpp_namespace_forward_matches_recovery(
12145 forward: &CppNamespaceForward,
12146 recovered_node: Node<'_>,
12147) -> bool {
12148 forward.start_byte < recovered_node.start_byte()
12149 && forward.namespace_end_byte < recovered_node.start_byte()
12150 && malformed_namespace_is_nearest_recovery_region(
12151 forward.namespace_end_byte,
12152 recovered_node,
12153 )
12154}
12155
12156#[derive(Debug, Default)]
12172pub struct CppRecoveryCapture {
12173 created: Vec<CodeUnit>,
12175 created_units: HashSet<CodeUnit>,
12177 removed_pre_existing: HashSet<CodeUnit>,
12179}
12180
12181#[derive(Debug, Default)]
12193pub struct CppFieldOwnerIndex {
12194 owners: HashMap<String, HashSet<String>>,
12196 ownerless_packages: HashSet<String>,
12198}
12199
12200impl CppFieldOwnerIndex {
12201 fn of<'unit>(
12204 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
12205 file: &ProjectFile,
12206 ) -> Self {
12207 let mut index = Self::default();
12208 for declaration in declarations {
12209 index.record(declaration, file);
12210 }
12211 index
12212 }
12213
12214 fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
12215 if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
12216 return;
12217 }
12218 let short_name = code_unit.short_name();
12219 let package_name = code_unit.package_name();
12220 if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
12221 self.ownerless_packages.insert(package_name.to_string());
12222 }
12223 if !short_name.contains('.') {
12224 return;
12225 }
12226 if !self.owners.contains_key(package_name) {
12227 self.owners
12228 .insert(package_name.to_string(), HashSet::default());
12229 }
12230 let owners = self
12231 .owners
12232 .get_mut(package_name)
12233 .expect("the package entry was just ensured");
12234 for (offset, _) in short_name.match_indices('.') {
12235 let owner = &short_name[..offset];
12236 if !owners.contains(owner) {
12237 owners.insert(owner.to_string());
12238 }
12239 }
12240 }
12241
12242 fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
12245 if owner_short_name.is_empty() {
12246 self.ownerless_packages.contains(package_name)
12247 } else {
12248 self.owners
12249 .get(package_name)
12250 .is_some_and(|owners| owners.contains(owner_short_name))
12251 }
12252 }
12253}
12254
12255#[cfg(any(debug_assertions, test))]
12259fn cpp_declarations_hold_owned_fields<'unit>(
12260 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
12261 file: &ProjectFile,
12262 package_name: &str,
12263 owner_short_name: &str,
12264) -> bool {
12265 let prefix = format!("{owner_short_name}.");
12266 declarations.into_iter().any(|unit| {
12267 unit.kind() == CodeUnitType::Field
12268 && unit.source() == file
12269 && unit.package_name() == package_name
12270 && if owner_short_name.is_empty() {
12271 !unit.short_name().contains(['.', '$'])
12275 } else {
12276 unit.short_name().starts_with(&prefix)
12277 }
12278 })
12279}
12280
12281#[derive(PartialEq, Eq, Hash)]
12289pub struct CppTreeIdentity {
12290 root_id: usize,
12291 start_byte: usize,
12292 end_byte: usize,
12293 kind_id: u16,
12294 child_count: usize,
12295}
12296
12297impl CppTreeIdentity {
12298 fn of(root: Node<'_>) -> Self {
12299 Self {
12300 root_id: root.id(),
12301 start_byte: root.start_byte(),
12302 end_byte: root.end_byte(),
12303 kind_id: root.kind_id(),
12304 child_count: root.child_count(),
12305 }
12306 }
12307}
12308
12309#[derive(Default)]
12323pub struct CppNamespaceForwardScan {
12324 scanned_through: usize,
12326 forwards: HashMap<String, Vec<CppNamespaceForward>>,
12327}
12328
12329impl CppNamespaceForwardScan {
12330 fn advance_to<'tree>(
12337 &mut self,
12338 root: Node<'tree>,
12339 cutoff: usize,
12340 source: &str,
12341 ancestry: &ParentIndex<'tree>,
12342 ) {
12343 if cutoff <= self.scanned_through {
12344 return;
12345 }
12346 let folded_through = self.scanned_through;
12347 let mut cursor = root.walk();
12348 let mut stack = vec![root];
12349 while let Some(current) = stack.pop() {
12350 if (folded_through..cutoff).contains(¤t.start_byte())
12351 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12352 {
12353 self.forwards
12354 .entry(forward.name.clone())
12355 .or_default()
12356 .push(forward);
12357 }
12358 for child in current.named_children(&mut cursor) {
12363 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
12364 stack.push(child);
12365 }
12366 }
12367 }
12368 self.scanned_through = cutoff;
12369 }
12370
12371 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
12375 let mut matching = self
12376 .forwards
12377 .get(name)
12378 .into_iter()
12379 .flatten()
12380 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
12381 let first = matching.next()?;
12382 matching
12383 .next()
12384 .is_none()
12385 .then(|| first.package_name.clone())
12386 }
12387}
12388
12389#[cfg(any(debug_assertions, test))]
12394fn unique_earlier_cpp_namespace_forward<'tree>(
12395 recovered_node: Node<'tree>,
12396 name: &str,
12397 source: &str,
12398 ancestry: &ParentIndex<'tree>,
12399) -> Option<String> {
12400 let mut root = recovered_node;
12401 while let Some(parent) = ancestry.parent(root) {
12402 root = parent;
12403 }
12404
12405 let mut candidates = Vec::new();
12406 let mut stack = vec![root];
12407 while let Some(current) = stack.pop() {
12408 if current.start_byte() < recovered_node.start_byte()
12409 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12410 && forward.name == name
12411 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
12412 {
12413 candidates.push(forward.package_name);
12414 }
12415
12416 let mut cursor = current.walk();
12417 for child in current.named_children(&mut cursor) {
12418 if child.start_byte() < recovered_node.start_byte() {
12419 stack.push(child);
12420 }
12421 }
12422 }
12423
12424 if candidates.len() == 1 {
12425 candidates.pop()
12426 } else {
12427 None
12428 }
12429}
12430
12431fn malformed_namespace_is_nearest_recovery_region(
12432 namespace_end_byte: usize,
12433 recovered_node: Node<'_>,
12434) -> bool {
12435 let mut root = recovered_node;
12436 while let Some(parent) = root.parent() {
12437 root = parent;
12438 }
12439 let mut cursor = root.walk();
12440 root.named_children(&mut cursor)
12441 .filter(|sibling| {
12442 namespace_end_byte <= sibling.start_byte()
12443 && sibling.end_byte() <= recovered_node.start_byte()
12444 })
12445 .all(is_malformed_namespace_recovery_trivia)
12446}
12447
12448fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
12449 matches!(node.kind(), "ERROR" | "comment")
12450 || node.kind().starts_with("preproc_")
12451 || node.kind() == "expression_statement" && node.named_child_count() == 0
12452}
12453
12454fn cpp_namespace_name_for_forward<'tree>(
12458 node: Node<'tree>,
12459 source: &str,
12460 ancestry: &ParentIndex<'tree>,
12461) -> Option<String> {
12462 cpp_namespace_definition_for_forward(node, ancestry)?;
12463 cpp_lexical_namespace_name(node, source)
12464}
12465
12466fn cpp_namespace_definition_for_forward<'tree>(
12467 node: Node<'tree>,
12468 ancestry: &ParentIndex<'tree>,
12469) -> Option<Node<'tree>> {
12470 let declaration = ancestry.parent(node)?;
12471 let mut ancestor = ancestry.parent(declaration);
12472 while let Some(current) = ancestor {
12473 if matches!(
12474 current.kind(),
12475 "compound_statement"
12476 | "field_declaration_list"
12477 | "class_specifier"
12478 | "struct_specifier"
12479 | "union_specifier"
12480 | "function_definition"
12481 | "lambda_expression"
12482 ) {
12483 return None;
12484 }
12485 if current.kind() == "namespace_definition" {
12486 return Some(current);
12487 }
12488 ancestor = ancestry.parent(current);
12489 }
12490 None
12491}
12492
12493fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
12494 match node.kind() {
12495 "template_function" => node
12496 .child_by_field_name("name")
12497 .is_some_and(is_function_pointer_like_inner_declarator),
12498 _ => is_pointer_wrapper_declarator(node),
12499 }
12500}
12501
12502fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
12503 match node.kind() {
12504 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
12505 "parenthesized_declarator" => node
12506 .child_by_field_name("declarator")
12507 .or_else(|| last_named_child(node))
12508 .is_some_and(is_pointer_wrapper_declarator),
12509 _ => false,
12510 }
12511}
12512
12513fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
12514 let cleaned = raw_name.trim_start_matches("template ").trim();
12515 let cleaned = cleaned.trim_start_matches("::");
12522 let parts: Vec<_> = cleaned
12531 .split("::")
12532 .filter(|component| !component.is_empty())
12533 .collect();
12534 if parts.is_empty() {
12535 return (None, cleaned.to_string(), scope.package_name.clone());
12536 }
12537 if parts.len() > 1 {
12538 let name = parts.last().unwrap_or(&cleaned).to_string();
12539 let owner_parts = &parts[..parts.len() - 1];
12540 if let Some(class_unit) = &scope.class_unit {
12541 return (
12544 Some(CppMemberOwner::Unit(class_unit.clone())),
12545 name,
12546 scope.package_name.clone(),
12547 );
12548 }
12549 if !scope.package_name.is_empty() {
12550 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
12565 let owner = (!nested.is_empty()).then(|| {
12566 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
12567 });
12568 return (owner, name, scope.package_name.clone());
12569 }
12570 let (owner, package_name) = if owner_parts.len() > 1 {
12572 (
12584 Some(CppMemberOwner::Chain(vec![
12585 owner_parts.last().unwrap_or(&"").to_string(),
12586 ])),
12587 owner_parts[..owner_parts.len() - 1].join("::"),
12588 )
12589 } else {
12590 (
12602 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
12603 cpp_using_directive_namespace_for_bare_owner(scope),
12604 )
12605 };
12606 return (owner, name, package_name);
12607 }
12608
12609 let package_name = scope.package_name.clone();
12610 let owner = scope
12611 .class_unit
12612 .as_ref()
12613 .map(|parent| CppMemberOwner::Unit(parent.clone()));
12614 (owner, cleaned.to_string(), package_name)
12615}
12616
12617fn strip_redundant_namespace_prefix<'a>(
12631 owner_parts: &'a [&'a str],
12632 package_name: &str,
12633) -> &'a [&'a str] {
12634 if package_name.is_empty() {
12635 return owner_parts;
12636 }
12637 let package_segments: Vec<&str> = package_name.split("::").collect();
12638 let max_prefix = owner_parts.len().min(package_segments.len());
12639 for prefix_len in (1..=max_prefix).rev() {
12640 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
12641 if &owner_parts[..prefix_len] == package_suffix {
12642 return &owner_parts[prefix_len..];
12643 }
12644 }
12645 owner_parts
12646}
12647
12648fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
12659 scope
12660 .visible_using_namespaces
12661 .iter()
12662 .min_by_key(|namespace| namespace.split("::").count())
12663 .cloned()
12664 .unwrap_or_default()
12665}
12666
12667struct CppQualifiedNameComponent {
12668 name: String,
12669 is_template_id: bool,
12670}
12671
12672fn qualified_class_name_chain(
12685 class_node: Node<'_>,
12686 source: &str,
12687 scope: &ScopeInfo,
12688) -> Option<Vec<String>> {
12689 if scope.package_name.is_empty() || scope.class_unit.is_some() {
12690 return None;
12691 }
12692 let name = class_node.child_by_field_name("name")?;
12693 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
12694 if explicitly_global
12695 || components.len() < 2
12696 || components.iter().any(|component| component.is_template_id)
12697 {
12698 return None;
12699 }
12700 let names = components
12701 .iter()
12702 .map(|component| component.name.as_str())
12703 .collect::<Vec<_>>();
12704 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
12705 if class_chain.is_empty() {
12706 return None;
12707 }
12708 Some(class_chain.iter().map(|name| name.to_string()).collect())
12709}
12710
12711fn structured_cpp_qualified_components(
12712 qualified_name: Node<'_>,
12713 source: &str,
12714) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
12715 if qualified_name.kind() != "qualified_identifier" {
12716 return None;
12717 }
12718
12719 let mut components = Vec::new();
12720 let mut current = qualified_name;
12721 let mut explicitly_global = false;
12722 loop {
12723 if current.kind() == "qualified_identifier" {
12724 if let Some(component) = current.child_by_field_name("scope") {
12725 components.push(canonical_cpp_qualified_component(component, source)?);
12726 } else if components.is_empty() {
12727 explicitly_global = true;
12728 } else {
12729 return None;
12730 }
12731 current = current.child_by_field_name("name")?;
12732 } else {
12733 components.push(canonical_cpp_qualified_component(current, source)?);
12734 break;
12735 }
12736 }
12737 Some((components, explicitly_global))
12738}
12739
12740fn split_structured_templated_cpp_name(
12741 declarator_name: Node<'_>,
12742 source: &str,
12743 scope: &ScopeInfo,
12744) -> Option<(Option<CppMemberOwner>, String, String)> {
12745 let (mut components, explicitly_global) =
12746 structured_cpp_qualified_components(declarator_name, source)?;
12747
12748 let terminal = components.pop()?;
12749 let owner_start = components
12750 .iter()
12751 .position(|component| component.is_template_id)?;
12752 let explicit_package = components[..owner_start]
12753 .iter()
12754 .map(|component| component.name.as_str())
12755 .collect::<Vec<_>>()
12756 .join("::");
12757 let explicit_package_is_empty = explicit_package.is_empty();
12758 let package_name = match (
12759 explicitly_global,
12760 scope.package_name.is_empty(),
12761 explicit_package_is_empty,
12762 ) {
12763 (true, _, _) => explicit_package,
12764 (false, _, true) => scope.package_name.clone(),
12765 (false, true, false) => explicit_package,
12766 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
12767 };
12768 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
12774 {
12775 cpp_using_directive_namespace_for_bare_owner(scope)
12776 } else {
12777 package_name
12778 };
12779 let owner_chain = components[owner_start..]
12780 .iter()
12781 .map(|component| component.name.clone())
12782 .collect::<Vec<_>>();
12783 if owner_chain.is_empty() || terminal.name.is_empty() {
12784 return None;
12785 }
12786
12787 Some((
12788 Some(CppMemberOwner::Chain(owner_chain)),
12789 terminal.name,
12790 package_name,
12791 ))
12792}
12793
12794fn canonical_cpp_qualified_component(
12795 mut component: Node<'_>,
12796 source: &str,
12797) -> Option<CppQualifiedNameComponent> {
12798 let mut is_template_id = false;
12799 loop {
12800 match component.kind() {
12801 "template_type" => {
12802 is_template_id = true;
12803 component = component.child_by_field_name("name")?;
12804 }
12805 "dependent_name" => component = component.named_child(0)?,
12806 "identifier"
12807 | "field_identifier"
12808 | "namespace_identifier"
12809 | "type_identifier"
12810 | "operator_name"
12811 | "destructor_name" => {
12812 let name = normalize_cpp_whitespace(node_text(component, source));
12813 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
12814 name,
12815 is_template_id,
12816 });
12817 }
12818 _ => component = component.child_by_field_name("name")?,
12819 }
12820 }
12821}
12822
12823fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
12824 if let Some(name) = macro_decorated_unqualified_name(node) {
12825 return extract_declarator_name(name, source);
12826 }
12827 match node.kind() {
12828 "identifier"
12829 | "field_identifier"
12830 | "type_identifier"
12831 | "operator_name"
12832 | "destructor_name"
12833 | "qualified_identifier" => node_text(node, source).to_string(),
12834 "function_declarator"
12835 | "pointer_declarator"
12836 | "reference_declarator"
12837 | "parenthesized_declarator"
12838 | "array_declarator"
12839 | "template_function" => node
12840 .child_by_field_name("declarator")
12841 .or_else(|| node.child_by_field_name("name"))
12842 .or_else(|| last_named_child(node))
12843 .map(|child| extract_declarator_name(child, source))
12844 .unwrap_or_else(|| node_text(node, source).to_string()),
12845 _ => node
12846 .child_by_field_name("name")
12847 .map(|child| extract_declarator_name(child, source))
12848 .unwrap_or_else(|| node_text(node, source).to_string()),
12849 }
12850}
12851
12852fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
12857 if let Some(name) = macro_decorated_unqualified_name(node) {
12858 return extract_callable_declarator_name(name, source);
12859 }
12860 match node.kind() {
12861 "identifier"
12862 | "field_identifier"
12863 | "type_identifier"
12864 | "operator_name"
12865 | "destructor_name"
12866 | "qualified_identifier" => Some(node_text(node, source).to_string()),
12867 "function_declarator"
12868 | "pointer_declarator"
12869 | "reference_declarator"
12870 | "parenthesized_declarator"
12871 | "array_declarator"
12872 | "template_function" => node
12873 .child_by_field_name("declarator")
12874 .or_else(|| node.child_by_field_name("name"))
12875 .and_then(|child| extract_callable_declarator_name(child, source)),
12876 _ => None,
12877 }
12878}
12879
12880fn extract_variable_name(mut node: Node<'_>, source: &str) -> Option<String> {
12881 loop {
12882 if let Some(name) = macro_decorated_unqualified_name(node) {
12883 node = name;
12884 continue;
12885 }
12886 match node.kind() {
12887 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
12888 let name = node_text(node, source).trim().to_string();
12889 return (!name.is_empty()).then_some(name);
12890 }
12891 _ => {
12892 node = node
12893 .child_by_field_name("declarator")
12894 .or_else(|| node.child_by_field_name("name"))
12895 .or_else(|| last_named_child(node))?;
12896 }
12897 }
12898 }
12899}
12900
12901#[derive(Clone, Copy)]
12907pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
12908 pub(crate) name: Node<'tree>,
12909 pub(crate) declarator: Node<'tree>,
12910}
12911
12912impl RecoveredFunctionLikeFieldDeclarator<'_> {
12913 pub(crate) fn pointer_depth(self) -> i32 {
12914 let mut depth = 0;
12915 let mut current = self.declarator;
12916 while current.kind() != "function_declarator" {
12917 if current.kind() == "pointer_declarator" {
12918 depth += 1;
12919 }
12920 current = current
12921 .child_by_field_name("declarator")
12922 .expect("recovered field wrapper has an inner declarator");
12923 }
12924 depth
12925 }
12926}
12927
12928fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
12939 if node.kind() != "field_declaration" {
12940 return false;
12941 }
12942 let Some(type_node) = node.child_by_field_name("type") else {
12943 return false;
12944 };
12945 if !matches!(type_node.kind(), "identifier" | "type_identifier") {
12946 return false;
12947 }
12948 let Some(declarator) = node.child_by_field_name("declarator") else {
12949 return false;
12950 };
12951 if declarator.kind() != "parenthesized_declarator" {
12952 return false;
12953 }
12954 let mut invented_terminator = false;
12955 for child in children_iter(node) {
12956 if child.kind() != ";" {
12957 continue;
12958 }
12959 if !child.is_missing() {
12960 return false;
12961 }
12962 invented_terminator = true;
12963 }
12964 invented_terminator
12965}
12966
12967pub(crate) struct CppSwallowedAccessLabel<'tree> {
12970 pub(crate) keyword: Node<'tree>,
12972 pub(crate) access: &'static str,
12974}
12975
12976pub(crate) fn cpp_macro_swallowed_access_label<'tree>(
12990 node: Node<'tree>,
12991 source: &str,
12992) -> Option<CppSwallowedAccessLabel<'tree>> {
12993 if !matches!(node.kind(), "field_declaration" | "function_definition") {
12994 return None;
12995 }
12996 node.child_by_field_name("type")
12997 .filter(|type_node| type_node.kind() == "macro_type_specifier")?;
12998 let keyword = node
12999 .child_by_field_name("declarator")
13000 .filter(|declarator| declarator.kind() == "field_identifier")?;
13001 let access = match node_text(keyword, source) {
13002 "public" => "public",
13003 "protected" => "protected",
13004 "private" => "private",
13005 _ => return None,
13006 };
13007 let mut clause = keyword.next_sibling()?;
13008 if clause.kind() == "ERROR" {
13009 clause = clause.child(0)?;
13010 }
13011 if clause.kind() != "bitfield_clause" || clause.child(0)?.kind() != ":" {
13012 return None;
13013 }
13014 Some(CppSwallowedAccessLabel { keyword, access })
13015}
13016
13017pub(crate) fn recovered_function_like_field_declarator<'tree>(
13018 node: Node<'tree>,
13019 source: &str,
13020) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
13021 if node.kind() != "field_declaration" {
13022 return None;
13023 }
13024 let outer_declarator = node.child_by_field_name("declarator")?;
13025 let mut declarator = outer_declarator;
13026 while matches!(
13027 declarator.kind(),
13028 "pointer_declarator"
13029 | "reference_declarator"
13030 | "array_declarator"
13031 | "parenthesized_declarator"
13032 ) {
13033 declarator = declarator.child_by_field_name("declarator")?;
13034 }
13035 if declarator.kind() != "function_declarator" {
13036 return None;
13037 }
13038 let macro_name = declarator.child_by_field_name("declarator")?;
13039 if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
13040 {
13041 return None;
13042 }
13043 let parameters = declarator.child_by_field_name("parameters")?;
13044 let mut cursor = parameters.walk();
13045 let mut arguments = parameters.named_children(&mut cursor);
13046 let parameter = arguments.next()?;
13047 if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
13048 return None;
13049 }
13050 let name = parameter.child_by_field_name("type").filter(|argument| {
13051 matches!(
13052 argument.kind(),
13053 "identifier" | "field_identifier" | "type_identifier"
13054 )
13055 })?;
13056 Some(RecoveredFunctionLikeFieldDeclarator {
13057 name,
13058 declarator: outer_declarator,
13059 })
13060}
13061
13062use brokk_bifrost_core::analyzer::tree_walk::last_named_child;
13063
13064fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
13065 let name_node = node.child_by_field_name("name")?;
13066 let name = normalize_cpp_whitespace(node_text(name_node, source));
13067 (!name.is_empty()).then_some(name)
13068}
13069
13070fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13071 if node.kind() != "declaration" {
13072 return Vec::new();
13073 }
13074 let keyword_is_alias = node
13079 .child_by_field_name("type")
13080 .filter(|node| {
13081 node.kind() == "type_identifier"
13082 && matches!(node_text(*node, source), "using" | "typedef")
13083 })
13084 .map(|keyword| node_text(keyword, source).to_owned())
13085 .or_else(|| {
13086 node.named_children(&mut node.walk())
13087 .find_map(macro_modifier_identifier)
13088 .map(|identifier| node_text(identifier, source).to_owned())
13089 .filter(|text| matches!(text.as_str(), "using" | "typedef"))
13090 });
13091 let Some(keyword_is_alias) = keyword_is_alias else {
13092 return Vec::new();
13093 };
13094 let Some(declarator) = node.child_by_field_name("declarator") else {
13095 return Vec::new();
13096 };
13097 if keyword_is_alias == "using"
13098 && (declarator.kind() != "init_declarator"
13099 || declarator.child_by_field_name("value").is_none())
13100 {
13101 return Vec::new();
13102 }
13103 if keyword_is_alias == "typedef"
13104 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
13105 {
13106 return vec![alias_name];
13107 }
13108 extract_typedef_declarator_name(declarator, source)
13109 .into_iter()
13110 .collect()
13111}
13112
13113pub(crate) fn recovered_alias_type_node<'tree>(
13116 node: Node<'tree>,
13117 alias_name: &str,
13118 source: &str,
13119) -> Option<Node<'tree>> {
13120 if node.kind() != "declaration" {
13121 return None;
13122 }
13123 let declarator = node.child_by_field_name("declarator")?;
13124 let admitted = recovered_type_alias_names(node, source)
13125 .iter()
13126 .any(|name| name == alias_name)
13127 || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
13128 if !admitted {
13129 return None;
13130 }
13131 if declarator.kind() == "init_declarator" {
13132 return declarator.child_by_field_name("value");
13133 }
13134 if node
13138 .named_children(&mut node.walk())
13139 .find_map(macro_modifier_identifier)
13140 .is_some_and(|keyword| matches!(node_text(keyword, source), "using" | "typedef"))
13141 {
13142 return node.child_by_field_name("type");
13143 }
13144 (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
13145 .then_some(declarator)
13146}
13147
13148fn recovered_typedef_error_alias_name(
13149 declaration: Node<'_>,
13150 declarator: Node<'_>,
13151 source: &str,
13152) -> Option<String> {
13153 if declarator.kind() != "qualified_identifier" {
13163 return None;
13164 }
13165 let mut cursor = declaration.walk();
13166 let mut errors = declaration
13167 .named_children(&mut cursor)
13168 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
13169 let error = errors.next()?;
13170 if errors.next().is_some() || error.named_child_count() != 1 {
13171 return None;
13172 }
13173 let name = error.named_child(0)?;
13174 if !matches!(
13175 name.kind(),
13176 "identifier" | "field_identifier" | "type_identifier"
13177 ) {
13178 return None;
13179 }
13180 let name = normalize_cpp_whitespace(node_text(name, source));
13181 (!name.is_empty()).then_some(name)
13182}
13183
13184fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13185 if fragmented_parenthesized_typedef_type(node).is_some() {
13189 return Vec::new();
13190 }
13191 let has_function_like_macro_type = node
13192 .child_by_field_name("type")
13193 .filter(|type_node| type_node.kind() == "type_identifier")
13194 .is_some_and(|type_node| {
13195 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13196 });
13197 let mut names = Vec::new();
13198 let mut cursor = node.walk();
13199 for declarator in node.children_by_field_name("declarator", &mut cursor) {
13200 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
13201 continue;
13202 }
13203 if let Some(name) = extract_typedef_declarator_name(declarator, source)
13204 && !names.contains(&name)
13205 {
13206 names.push(name);
13207 }
13208 }
13209 names
13210}
13211
13212struct RecoveredMacroTypedefAlias<'tree> {
13213 name: String,
13214 end_node: Node<'tree>,
13215}
13216
13217fn recovered_macro_typedef_alias<'tree>(
13221 node: Node<'tree>,
13222 source: &str,
13223) -> Option<RecoveredMacroTypedefAlias<'tree>> {
13224 let type_node = fragmented_parenthesized_typedef_type(node)?;
13225 if type_node.kind() != "type_identifier"
13226 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13227 {
13228 return None;
13229 }
13230
13231 let end_node = node.next_named_sibling()?;
13232 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
13233 return None;
13234 }
13235 let name_node = end_node.named_child(0)?;
13236 if name_node.kind() != "identifier" {
13237 return None;
13238 }
13239 let has_terminator = (0..end_node.child_count()).any(|index| {
13240 end_node
13241 .child(index)
13242 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
13243 });
13244 if !has_terminator {
13245 return None;
13246 }
13247 let name = normalize_cpp_whitespace(node_text(name_node, source));
13248 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
13249}
13250
13251fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
13252 if node.kind() != "type_definition" {
13253 return None;
13254 }
13255 let mut declarator_cursor = node.walk();
13256 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
13257 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
13258 return None;
13259 }
13260 let has_missing_terminator = (0..node.child_count()).any(|index| {
13261 node.child(index)
13262 .is_some_and(|child| child.kind() == ";" && child.is_missing())
13263 });
13264 if !has_missing_terminator {
13265 return None;
13266 }
13267 node.child_by_field_name("type")
13268}
13269
13270pub(crate) fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13271 match node.kind() {
13272 "identifier" | "field_identifier" | "type_identifier" => {
13273 direct_identifier_name(node, source)
13274 }
13275 "qualified_identifier" => node
13276 .child_by_field_name("name")
13277 .and_then(|name| extract_typedef_declarator_name(name, source)),
13278 _ => node
13279 .child_by_field_name("declarator")
13280 .or_else(|| node.child_by_field_name("name"))
13281 .or_else(|| last_named_child(node))
13282 .and_then(|child| extract_typedef_declarator_name(child, source)),
13283 }
13284}
13285
13286fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
13287 if let Some(name_node) = node.child_by_field_name("name") {
13288 let name = normalize_cpp_whitespace(node_text(name_node, source));
13289 return (!name.is_empty()).then_some(name);
13290 }
13291 let mut cursor = node.walk();
13292 node.named_children(&mut cursor)
13293 .find(|child| {
13294 matches!(
13295 child.kind(),
13296 "identifier" | "field_identifier" | "type_identifier"
13297 )
13298 })
13299 .and_then(|name_node| direct_identifier_name(name_node, source))
13300}
13301
13302fn render_cpp_type_signature(
13303 node: Node<'_>,
13304 source: &str,
13305 template_signature: Option<&str>,
13306) -> String {
13307 let text = normalize_cpp_whitespace(node_text(node, source));
13308 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
13309 let rendered = if head.ends_with(';') {
13310 head.to_string()
13311 } else {
13312 format!("{head} {{")
13313 };
13314 if let Some(template_signature) = template_signature {
13315 format!("template {template_signature} {rendered}")
13316 } else {
13317 rendered
13318 }
13319}
13320
13321fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
13322 if let Some(recovered) = recovered_pyobject_head_field(node, source)
13323 && recovered.declarator == declarator
13324 {
13325 let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
13326 let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
13327 return format!("{type_text} {declarator};");
13328 }
13329 if let Some(recovered) = recovered_function_like_field_declarator(node, source)
13330 && recovered.name == declarator
13331 {
13332 let type_text = node
13333 .child_by_field_name("type")
13334 .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
13335 .unwrap_or_default();
13336 let name = normalize_cpp_whitespace(node_text(recovered.name, source));
13337 let mut prefix = String::new();
13338 let mut suffix = String::new();
13339 let mut current = recovered.declarator;
13340 while current.kind() != "function_declarator" {
13341 match current.kind() {
13342 "pointer_declarator" => prefix.push('*'),
13343 "reference_declarator" => prefix.push('&'),
13344 "array_declarator" => {
13345 let size = current
13346 .child_by_field_name("size")
13347 .map(|size| normalize_cpp_whitespace(node_text(size, source)))
13348 .unwrap_or_default();
13349 suffix.push('[');
13350 suffix.push_str(&size);
13351 suffix.push(']');
13352 }
13353 "parenthesized_declarator" => {}
13354 _ => unreachable!("validated recovered field declarator wrapper"),
13355 }
13356 current = current
13357 .child_by_field_name("declarator")
13358 .expect("recovered field wrapper has an inner declarator");
13359 }
13360 let separator = if prefix.is_empty() { "" } else { " " };
13361 return format!("{type_text} {prefix}{separator}{name}{suffix};");
13362 }
13363 if let Some(signature) =
13364 render_recovered_macro_qualified_field_signature(node, declarator, source)
13365 {
13366 return signature;
13367 }
13368 let prefix = cpp_declaration_prefix(node, source);
13369 let name = extract_variable_name(declarator, source).unwrap_or_default();
13370 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
13371 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
13372 || (prefix.ends_with('&') && raw_suffix == "&")
13373 {
13374 String::new()
13375 } else {
13376 raw_suffix
13377 };
13378
13379 let mut rendered = if suffix.is_empty() {
13380 format!("{prefix} {name}")
13381 } else if suffix.starts_with('*') || suffix.starts_with('&') {
13382 format!("{prefix}{suffix} {name}")
13383 } else if suffix.starts_with('[') || suffix.starts_with('(') {
13384 format!("{prefix} {name}{suffix}")
13385 } else {
13386 format!("{prefix} {suffix}{name}")
13387 };
13388 rendered = collapse_cpp_whitespace(&rendered);
13389
13390 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13393 format!("{rendered} = {initializer};")
13394 } else if cpp_direct_semicolon(node).is_some() {
13395 format!("{rendered};")
13396 } else {
13397 rendered
13398 }
13399}
13400
13401fn render_recovered_macro_qualified_field_signature(
13402 node: Node<'_>,
13403 declarator: Node<'_>,
13404 source: &str,
13405) -> Option<String> {
13406 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
13407 if !recovered
13408 .iter()
13409 .any(|candidate| same_node(*candidate, declarator))
13410 {
13411 return None;
13412 }
13413 let pseudo_declarator = node.child_by_field_name("declarator")?;
13414 let mut cursor = node.walk();
13415 let clause = node
13416 .named_children(&mut cursor)
13417 .find(|child| child.kind() == "bitfield_clause")?;
13418 let mut cursor = clause.walk();
13419 let error = clause
13420 .named_children(&mut cursor)
13421 .find(|child| child.kind() == "ERROR")?;
13422 let qualified_type =
13423 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
13424 let prefix = cpp_declaration_prefix(node, source);
13425 let name = extract_variable_name(declarator, source)?;
13426 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
13427 let mut rendered = if suffix.is_empty() {
13428 format!("{prefix} {qualified_type} {name}")
13429 } else {
13430 format!("{prefix} {qualified_type} {suffix} {name}")
13431 };
13432 rendered = collapse_cpp_whitespace(&rendered);
13433
13434 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
13435 Some(format!(
13436 "{rendered} = {};",
13437 normalize_cpp_whitespace(node_text(initializer, source))
13438 ))
13439 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13440 Some(format!("{rendered} = {initializer};"))
13441 } else {
13442 Some(format!("{rendered};"))
13443 }
13444}
13445
13446fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
13447 match node.kind() {
13448 "pointer_expression" => {
13449 let operator = node
13450 .child_by_field_name("operator")
13451 .or_else(|| node.child(0))
13452 .map(|operator| node_text(operator, source))
13453 .unwrap_or("*");
13454 let argument = node
13455 .child_by_field_name("argument")
13456 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13457 .unwrap_or_default();
13458 format!("{operator}{argument}")
13459 }
13460 "unary_expression" => {
13461 let operator = node
13462 .child_by_field_name("operator")
13463 .or_else(|| node.child(0))
13464 .map(|operator| node_text(operator, source))
13465 .unwrap_or_default();
13466 let argument = node
13467 .child_by_field_name("argument")
13468 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13469 .unwrap_or_default();
13470 format!("{operator}{argument}")
13471 }
13472 "identifier" | "field_identifier" => String::new(),
13473 _ => cpp_declarator_suffix_without_name(node, source),
13474 }
13475}
13476
13477fn recovered_macro_qualified_field_initializer<'tree>(
13478 clause: Node<'tree>,
13479 declarator: Node<'tree>,
13480) -> Option<Node<'tree>> {
13481 let mut stack = vec![clause];
13482 while let Some(current) = stack.pop() {
13483 if current.kind() == "assignment_expression"
13484 && current
13485 .child_by_field_name("left")
13486 .is_some_and(|left| same_node(left, declarator))
13487 {
13488 return current.child_by_field_name("right");
13489 }
13490 let mut cursor = current.walk();
13491 stack.extend(current.named_children(&mut cursor));
13492 }
13493 None
13494}
13495
13496fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
13497 let text = node_text(node, source);
13498 let mut cursor = node.walk();
13499 let first_declarator = node.named_children(&mut cursor).find(|child| {
13500 matches!(
13501 child.kind(),
13502 "init_declarator"
13503 | "identifier"
13504 | "field_identifier"
13505 | "pointer_declarator"
13506 | "reference_declarator"
13507 | "array_declarator"
13508 | "function_declarator"
13509 )
13510 });
13511 let prefix = if let Some(first_declarator) = first_declarator {
13512 let end = first_declarator
13513 .start_byte()
13514 .saturating_sub(node.start_byte());
13515 let mut prefix = text.get(..end).unwrap_or(text).to_string();
13516 let declarator_suffix = match first_declarator.kind() {
13517 "init_declarator" => first_declarator
13518 .child_by_field_name("declarator")
13519 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
13520 .unwrap_or_default(),
13521 _ => cpp_declarator_suffix_without_name(first_declarator, source),
13522 };
13523 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
13524 prefix.push_str(&declarator_suffix);
13525 }
13526 return collapse_cpp_whitespace(&prefix)
13527 .trim_end_matches(',')
13528 .trim_end_matches(';')
13529 .trim()
13530 .to_string();
13531 } else {
13532 text
13533 };
13534 collapse_cpp_whitespace(prefix)
13535 .trim_end_matches(',')
13536 .trim_end_matches(';')
13537 .trim()
13538 .to_string()
13539}
13540
13541fn cpp_preserved_initializer(
13542 declaration_node: Node<'_>,
13543 declarator: Node<'_>,
13544 source: &str,
13545) -> Option<String> {
13546 let name = extract_variable_name(declarator, source)?;
13547 let mut cursor = declaration_node.walk();
13548 for child in declaration_node.named_children(&mut cursor) {
13549 if child.kind() != "init_declarator" {
13550 continue;
13551 }
13552 let Some(inner) = child.child_by_field_name("declarator") else {
13553 continue;
13554 };
13555 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
13556 continue;
13557 }
13558 let value = child.child_by_field_name("value")?;
13559 let kind = value.kind();
13560 if matches!(
13561 kind,
13562 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
13563 ) {
13564 return Some(normalize_cpp_whitespace(node_text(value, source)));
13565 }
13566 break;
13567 }
13568 if let Some(equals) = declarator.next_sibling().filter(|node| node.kind() == "=")
13571 && let Some(value) = equals.next_sibling().filter(|node| {
13572 declaration_node
13573 .child_by_field_name("default_value")
13574 .is_some_and(|default| same_node(default, *node))
13575 })
13576 {
13577 let kind = value.kind();
13578 if matches!(
13579 kind,
13580 "number_literal"
13581 | "float_literal"
13582 | "char_literal"
13583 | "true"
13584 | "false"
13585 | "null"
13586 | "nullptr"
13587 | "identifier"
13588 ) {
13589 return Some(normalize_cpp_whitespace(node_text(value, source)));
13590 }
13591 }
13592 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
13593 let pattern = format!(
13594 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
13595 regex::escape(&name)
13596 );
13597 Regex::new(&pattern)
13598 .ok()
13599 .and_then(|regex| regex.captures(&declaration_text))
13600 .and_then(|captures| captures.get(1))
13601 .map(|value| value.as_str().to_string())
13602}
13603
13604fn render_cpp_function_display_signature_from_node<'tree>(
13605 node: Node<'tree>,
13606 source: &str,
13607 template_signature: Option<&str>,
13608 has_body: bool,
13609 ancestry: &ParentIndex<'tree>,
13610) -> String {
13611 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
13612 let parent_text = node_text(root, source);
13613 let body_local_start = root
13614 .child_by_field_name("body")
13615 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
13616 .unwrap_or(parent_text.len());
13617 let display = parent_text
13618 .get(..body_local_start)
13619 .unwrap_or(parent_text)
13620 .trim()
13621 .trim();
13622 let display = if let Some(template_signature) = template_signature {
13623 if display.starts_with("template ") {
13624 display.to_string()
13625 } else {
13626 format!("template {template_signature} {display}")
13627 }
13628 } else {
13629 display.to_string()
13630 };
13631 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
13632 if has_body {
13633 format!("{display} {{...}}")
13634 } else {
13635 format!("{display};")
13636 }
13637}
13638
13639fn cpp_template_signature(
13640 template_node: Node<'_>,
13641 declaration_child: Node<'_>,
13642 source: &str,
13643) -> Option<String> {
13644 let text = source
13645 .get(template_node.start_byte()..declaration_child.start_byte())
13646 .unwrap_or("");
13647 let text = normalize_cpp_whitespace(text);
13648 let start = text.find('<')?;
13649 let end = text.rfind('>')?;
13650 if end < start {
13651 return None;
13652 }
13653 Some(text[start..=end].to_string())
13654}
13655
13656struct RecoveredFragmentedPartialSpecialization<'tree> {
13657 declaration_node: Node<'tree>,
13658 name: String,
13659 range: Range,
13660 prefix_members: Vec<Node<'tree>>,
13661 member_siblings: Vec<Node<'tree>>,
13662 following_declarations: Vec<Node<'tree>>,
13663}
13664
13665struct RecoveredFragmentedPreprocessorClass<'tree> {
13666 declaration_node: Node<'tree>,
13667 class_node: Node<'tree>,
13668 body: Node<'tree>,
13669 name: String,
13670 range: Range,
13671 tail_members: Vec<Node<'tree>>,
13672 member_siblings: Vec<Node<'tree>>,
13673}
13674
13675fn recover_fragmented_preprocessor_class<'tree>(
13684 template_node: Node<'tree>,
13685 source: &str,
13686 ancestry: &ParentIndex<'tree>,
13687) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
13688 let alternative = ancestry.parent(template_node)?;
13689 if alternative.kind() != "preproc_else" {
13690 return None;
13691 }
13692 let conditional = alternative.parent()?;
13693 if conditional.kind() != "preproc_if" {
13694 return None;
13695 }
13696 let declaration_node = template_node
13697 .named_children(&mut template_node.walk())
13698 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
13699 let class_node = declaration_node
13700 .named_children(&mut declaration_node.walk())
13701 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
13702 let body = cpp_body_node(class_node)?;
13703 if class_node.end_byte() >= declaration_node.end_byte() {
13704 return None;
13705 }
13706 let name = class_like_name(class_node, source, ancestry)?;
13707 let is_partial_specialization = class_node
13708 .child_by_field_name("name")
13709 .is_some_and(|class_name| class_name.kind() == "template_type");
13710 if is_partial_specialization {
13711 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
13712 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
13713 return None;
13714 }
13715 } else {
13716 if !class_has_displaced_preprocessor_terminator(class_node) {
13717 return None;
13718 }
13719 let matching_other_branch = conditional
13720 .named_children(&mut conditional.walk())
13721 .take_while(|child| !same_node(*child, alternative))
13722 .filter(|child| child.kind() == "template_declaration")
13723 .filter_map(first_class_like_child)
13724 .any(|candidate| {
13725 cpp_body_node(candidate).is_none()
13726 && class_like_name(candidate, source, ancestry).as_deref()
13727 == Some(name.as_str())
13728 });
13729 if !matching_other_branch {
13730 return None;
13731 }
13732 }
13733
13734 let mut tail_members = Vec::new();
13735 let mut saw_class = false;
13736 let mut declaration_cursor = declaration_node.walk();
13737 for child in declaration_node.named_children(&mut declaration_cursor) {
13738 if same_node(child, class_node) {
13739 saw_class = true;
13740 } else if saw_class {
13741 tail_members.push(child);
13742 }
13743 }
13744
13745 let mut member_siblings = Vec::new();
13746 let mut saw_template = false;
13747 let mut terminator = None;
13748 for index in 0..alternative.child_count() {
13749 let Some(child) = alternative.child(index) else {
13750 continue;
13751 };
13752 if same_node(child, template_node) {
13753 saw_template = true;
13754 continue;
13755 }
13756 if !saw_template {
13757 continue;
13758 }
13759 if displaced_fragmented_class_terminator(alternative, index) {
13760 terminator = alternative.child(index + 1);
13761 break;
13762 }
13763 if child.is_named() {
13764 member_siblings.push(child);
13765 }
13766 }
13767 let terminator = terminator?;
13768 Some(RecoveredFragmentedPreprocessorClass {
13769 declaration_node,
13770 class_node,
13771 body,
13772 name,
13773 range: Range {
13774 start_byte: class_node.start_byte(),
13775 end_byte: terminator.end_byte(),
13776 start_line: class_node.start_position().row + 1,
13777 end_line: terminator.end_position().row + 1,
13778 },
13779 tail_members,
13780 member_siblings,
13781 })
13782}
13783
13784fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
13785 (0..class_node.child_count()).any(|index| {
13786 class_node.child(index).is_some_and(|child| {
13787 child.kind() == "ERROR"
13788 && (0..child.child_count()).any(|error_index| {
13789 child
13790 .child(error_index)
13791 .is_some_and(|token| token.kind() == "#endif")
13792 })
13793 })
13794 })
13795}
13796
13797pub fn cpp_displaced_preprocessor_terminator<'tree>(
13806 conditional: Node<'tree>,
13807) -> Option<Node<'tree>> {
13808 if !conditional.has_error() {
13809 return None;
13810 }
13811 let has_concrete_direct_terminator = conditional
13812 .child_count()
13813 .checked_sub(1)
13814 .and_then(|index| conditional.child(index))
13815 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
13816 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
13817 return None;
13821 }
13822 let mut displaced = None;
13828 let mut conditional_depth = 0usize;
13829 let mut stack = children_iter(conditional)
13830 .map(|child| (child, false))
13831 .collect::<Vec<_>>();
13832 stack.reverse();
13833 while let Some((node, inside_error)) = stack.pop() {
13834 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
13835 continue;
13836 }
13837 if node != conditional
13838 && matches!(
13839 node.kind(),
13840 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
13841 )
13842 {
13843 continue;
13844 }
13845 let inside_error = inside_error || node.kind() == "ERROR";
13846 match node.kind() {
13847 "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
13848 conditional_depth += 1;
13849 }
13850 "#endif" if !node.is_missing() => {
13851 if conditional_depth == 0
13852 && inside_error
13853 && displaced
13854 .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
13855 {
13856 displaced = Some(node);
13857 }
13858 conditional_depth = conditional_depth.saturating_sub(1);
13859 }
13860 _ => {}
13861 }
13862 let first_pushed = stack.len();
13863 stack.extend(children_iter(node).map(|child| (child, inside_error)));
13864 stack[first_pushed..].reverse();
13865 }
13866 displaced
13867}
13868
13869pub fn cpp_displaced_preprocessor_branch<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
13881 if !conditional.has_error() {
13882 return None;
13883 }
13884 let alternative = conditional.child_by_field_name("alternative");
13885 let mut conditional_depth = 0usize;
13886 let mut stack = children_iter(conditional)
13887 .filter(|child| Some(*child) != alternative)
13888 .map(|child| (child, false))
13889 .collect::<Vec<_>>();
13890 stack.reverse();
13891 while let Some((node, inside_error)) = stack.pop() {
13892 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
13893 continue;
13894 }
13895 if matches!(
13896 node.kind(),
13897 "preproc_if"
13898 | "preproc_ifdef"
13899 | "preproc_ifndef"
13900 | "preproc_elif"
13901 | "preproc_elifdef"
13902 | "preproc_else"
13903 ) {
13904 continue;
13905 }
13906 let inside_error = inside_error || node.kind() == "ERROR";
13907 match node.kind() {
13908 "#if" | "#ifdef" | "#ifndef" => conditional_depth += 1,
13909 "#endif" if !node.is_missing() => {
13910 conditional_depth = conditional_depth.saturating_sub(1);
13911 }
13912 "#else" | "#elif" | "#elifdef" | "#elifndef"
13913 if inside_error && conditional_depth == 0 && !node.is_missing() =>
13914 {
13915 return Some(node);
13916 }
13917 _ => {}
13918 }
13919 let first_pushed = stack.len();
13920 stack.extend(children_iter(node).map(|child| (child, inside_error)));
13921 stack[first_pushed..].reverse();
13922 }
13923 None
13924}
13925
13926#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13939pub struct CppDisplacedPreprocessorBoundary {
13940 pub end_byte: usize,
13941 pub end_line: usize,
13942}
13943
13944pub fn cpp_displaced_preprocessor_boundary(
13945 conditional: Node<'_>,
13946) -> Option<CppDisplacedPreprocessorBoundary> {
13947 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
13948 return Some(CppDisplacedPreprocessorBoundary {
13949 end_byte: terminator.end_byte(),
13950 end_line: terminator.end_position().row + 1,
13951 });
13952 }
13953 if let Some(declaration) = displaced_split_declaration(conditional) {
13954 return Some(CppDisplacedPreprocessorBoundary {
13955 end_byte: declaration.end_byte(),
13956 end_line: declaration.end_position().row + 1,
13957 });
13958 }
13959 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
13960 return Some(CppDisplacedPreprocessorBoundary {
13961 end_byte: terminator.end_byte(),
13962 end_line: terminator.end_position().row + 1,
13963 });
13964 }
13965 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
13966 return Some(CppDisplacedPreprocessorBoundary {
13967 end_byte: terminator.end_byte(),
13968 end_line: terminator.end_position().row + 1,
13969 });
13970 }
13971 None
13972}
13973
13974fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
13980 if !conditional.has_error()
13981 || conditional.child_by_field_name("alternative").is_some()
13982 || conditional
13983 .child(conditional.child_count().saturating_sub(1))
13984 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
13985 {
13986 return None;
13987 }
13988 let mut recovered = None;
13989 for nested in named_children_iter(conditional) {
13990 if !matches!(
13991 nested.kind(),
13992 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
13993 ) || nested.child_by_field_name("alternative").is_some()
13994 {
13995 continue;
13996 }
13997 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
13998 continue;
13999 };
14000 if direct.kind() != "#endif" || direct.is_missing() {
14001 continue;
14002 }
14003 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
14004 continue;
14005 };
14006 if displaced.end_byte() >= direct.start_byte() {
14007 continue;
14008 }
14009 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
14010 recovered = Some(direct);
14011 }
14012 }
14013 recovered
14014}
14015
14016fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14017 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
14018 return None;
14019 }
14020 let mut cursor = conditional.walk();
14021 let declarations = conditional
14022 .named_children(&mut cursor)
14023 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
14024 .collect::<Vec<_>>();
14025 let declaration = *declarations.first()?;
14026 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
14027 return None;
14028 }
14029 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
14030 let mut terminator = None;
14031 let mut stack = (0..declaration.child_count())
14032 .filter_map(|index| declaration.child(index))
14033 .filter(|child| child.start_byte() < declarator_start)
14034 .map(|child| (child, false))
14035 .collect::<Vec<_>>();
14036 while let Some((node, inside_error)) = stack.pop() {
14037 let inside_error = inside_error || node.kind() == "ERROR";
14038 if inside_error && node.kind() == "#endif" && !node.is_missing() {
14039 terminator = Some(node);
14040 continue;
14041 }
14042 for index in 0..node.child_count() {
14043 if let Some(child) = node.child(index)
14044 && child.start_byte() < declarator_start
14045 {
14046 stack.push((child, inside_error));
14047 }
14048 }
14049 }
14050 terminator
14051}
14052
14053fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14054 if !conditional.has_error()
14055 || conditional.child_by_field_name("alternative").is_some()
14056 || conditional
14057 .prev_named_sibling()
14058 .filter(|sibling| {
14059 sibling.kind() == "ERROR"
14060 && sibling.child_count() == 1
14061 && sibling
14062 .child(0)
14063 .is_some_and(|child| child.kind() == "typedef")
14064 })
14065 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
14066 .is_none()
14067 {
14068 return None;
14069 }
14070 let mut cursor = conditional.walk();
14071 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
14072 let declaration_index = children
14073 .iter()
14074 .position(|child| child.kind() == "declaration" && child.has_error())?;
14075 let declaration = children[declaration_index];
14076 if !children
14077 .iter()
14078 .skip(declaration_index + 1)
14079 .any(|child| child.end_byte() > declaration.end_byte())
14080 {
14081 return None;
14082 }
14083 let declarator = declaration.child_by_field_name("declarator")?;
14084 let mut error_end = None;
14085 let mut names = Vec::new();
14086 let mut stack = vec![declarator];
14087 while let Some(node) = stack.pop() {
14088 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
14089 error_end =
14090 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
14091 continue;
14092 }
14093 if matches!(node.kind(), "identifier" | "type_identifier") {
14094 names.push(node.start_byte());
14095 }
14096 push_named_children_reversed(node, &mut stack);
14097 }
14098 let error_end = error_end?;
14099 names
14100 .into_iter()
14101 .any(|start| start >= error_end)
14102 .then_some(declaration)
14103}
14104
14105fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
14106 let Some(error) = parent.child(error_index) else {
14107 return false;
14108 };
14109 if error.kind() != "ERROR"
14110 || error.child_count() != 1
14111 || error.child(0).is_none_or(|child| child.kind() != "}")
14112 {
14113 return false;
14114 }
14115 let Some(semicolon) = parent.child(error_index + 1) else {
14116 return false;
14117 };
14118 semicolon.kind() == "expression_statement"
14119 && semicolon.child_count() == 1
14120 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
14121}
14122
14123fn displaced_macro_class_tail(
14129 declaration_node: Node<'_>,
14130 body: Node<'_>,
14131 source: &str,
14132) -> Option<DisplacedMacroClassTail> {
14133 if !matches!(
14134 declaration_node.kind(),
14135 "class_specifier" | "struct_specifier" | "union_specifier"
14136 ) || body.kind() != "field_declaration_list"
14137 {
14138 return None;
14139 }
14140
14141 let child_count = body.named_child_count();
14142 for index in 0..child_count {
14143 let child = body.named_child(index)?;
14144 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
14145 continue;
14146 };
14147 let split_index = index + 1;
14148 if split_index >= child_count {
14149 return None;
14150 }
14151 let mut cursor = body.walk();
14152 if !body
14153 .named_children(&mut cursor)
14154 .skip(split_index)
14155 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
14156 {
14157 return None;
14158 }
14159 return Some(DisplacedMacroClassTail {
14160 split_index,
14161 class_range: Range {
14162 start_byte: declaration_node.start_byte(),
14163 end_byte: terminator.end_byte(),
14164 start_line: declaration_node.start_position().row + 1,
14165 end_line: terminator.end_position().row + 1,
14166 },
14167 });
14168 }
14169 None
14170}
14171
14172fn displaced_macro_field_terminator<'tree>(
14173 field: Node<'tree>,
14174 source: &str,
14175) -> Option<Node<'tree>> {
14176 if field.kind() != "field_declaration" {
14177 return None;
14178 }
14179 let macro_type = field.child_by_field_name("type")?;
14183 let macro_name = match macro_type.kind() {
14184 "type_identifier"
14185 if field.child_by_field_name("declarator")?.kind() == "parenthesized_declarator" =>
14186 {
14187 macro_type
14188 }
14189 "macro_type_specifier" => macro_type.child_by_field_name("name")?,
14190 _ => return None,
14191 };
14192 if !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source))) {
14193 return None;
14194 }
14195 for index in 0..field.child_count() {
14196 let error = field.child(index)?;
14197 if error.kind() != "ERROR"
14198 || error.child_count() != 1
14199 || error.child(0).is_none_or(|child| child.kind() != "}")
14200 {
14201 continue;
14202 }
14203 let semicolon = field.child(index + 1)?;
14204 if semicolon.kind() == ";" {
14205 return Some(semicolon);
14206 }
14207 }
14208 None
14209}
14210
14211fn recover_fragmented_partial_specialization<'tree>(
14212 template_node: Node<'tree>,
14213 declaration_child: Node<'tree>,
14214 source: &str,
14215 ancestry: &ParentIndex<'tree>,
14216) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
14217 if declaration_child.kind() != "function_definition" {
14218 return None;
14219 }
14220 let class_node = declaration_child.child_by_field_name("type")?;
14221 if !matches!(
14222 class_node.kind(),
14223 "class_specifier" | "struct_specifier" | "union_specifier"
14224 ) || !class_node
14225 .child_by_field_name("name")
14226 .and_then(|name| direct_identifier_name(name, source))
14227 .is_some_and(|name| cpp_export_macro_token(&name))
14228 {
14229 return None;
14230 }
14231 let declarator = declaration_child.child_by_field_name("declarator")?;
14232 if declarator.kind() != "template_function" {
14233 return None;
14234 }
14235 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
14236 if metadata.specialization_arguments.is_empty() {
14237 return None;
14238 }
14239 let body = declaration_child.child_by_field_name("body")?;
14240 if body.kind() != "compound_statement" {
14241 return None;
14242 }
14243 let complete_prefix = body.named_child(0).filter(|first| {
14244 first.kind() == "labeled_statement"
14245 && first.has_error()
14246 && first
14247 .named_child(first.named_child_count().saturating_sub(1))
14248 .is_some_and(recovered_declaration_has_class_terminator)
14249 });
14250 let complete_body = complete_prefix.is_some();
14251 let mut prefix_members = Vec::new();
14252 if let Some(prefix) = complete_prefix {
14253 prefix_members.push(prefix);
14254 } else {
14255 let mut body_cursor = body.walk();
14256 for child in body.named_children(&mut body_cursor) {
14257 if !is_structurally_valid_fragmented_class_prefix_member(child) {
14258 break;
14259 }
14260 prefix_members.push(child);
14261 }
14262 }
14263 let containing_declarations = template_node.parent()?;
14264 if !matches!(
14265 containing_declarations.kind(),
14266 "declaration_list" | "compound_statement"
14267 ) {
14268 return None;
14269 }
14270 let mut member_siblings = Vec::new();
14271 let mut following_declarations = Vec::new();
14272 let terminator;
14273 if complete_body {
14274 terminator = complete_prefix?;
14275 let mut cursor = body.walk();
14276 let mut after_prefix = false;
14277 for child in body.named_children(&mut cursor) {
14278 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
14279 after_prefix = true;
14280 } else if after_prefix {
14281 following_declarations.push(child);
14282 }
14283 }
14284 } else {
14285 let mut found_template = false;
14286 let mut cursor = containing_declarations.walk();
14287 let mut class_terminator = None;
14288 for child in containing_declarations.children(&mut cursor) {
14289 if same_node(child, template_node) {
14290 found_template = true;
14291 continue;
14292 }
14293 if found_template && child.kind() == "}" {
14294 class_terminator = Some(child);
14295 break;
14296 }
14297 if found_template && child.kind() == "namespace_definition" {
14304 return None;
14305 }
14306 if found_template && child.is_named() {
14307 member_siblings.push(child);
14308 }
14309 }
14310 terminator = class_terminator?;
14311 }
14312 let name = format!(
14313 "{}<{}>",
14314 metadata.primary_name,
14315 metadata
14316 .specialization_arguments
14317 .iter()
14318 .map(|argument| argument.text.as_str())
14319 .collect::<Vec<_>>()
14320 .join(", ")
14321 );
14322 Some(RecoveredFragmentedPartialSpecialization {
14323 declaration_node: declaration_child,
14324 name,
14325 range: Range {
14326 start_byte: declaration_child.start_byte(),
14327 end_byte: terminator.end_byte(),
14328 start_line: declaration_child.start_position().row + 1,
14329 end_line: terminator.end_position().row + 1,
14330 },
14331 prefix_members,
14332 member_siblings,
14333 following_declarations,
14334 })
14335}
14336
14337pub fn is_recovered_fragmented_partial_specialization_container(
14344 node: Node<'_>,
14345 source: &str,
14346) -> bool {
14347 let Some(template) = node
14348 .parent()
14349 .filter(|parent| parent.kind() == "template_declaration")
14350 else {
14351 return false;
14352 };
14353 let mut root = template;
14354 while let Some(parent) = root.parent() {
14355 root = parent;
14356 }
14357 recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
14358 .is_some()
14359}
14360
14361fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
14362 if declaration.kind() != "declaration" {
14363 return false;
14364 }
14365 (0..declaration.child_count().saturating_sub(1)).any(|index| {
14370 let Some(error) = declaration.child(index) else {
14371 return false;
14372 };
14373 error.kind() == "ERROR"
14374 && error.child_count() == 1
14375 && error.child(0).is_some_and(|child| child.kind() == "}")
14376 && declaration
14377 .child(index + 1)
14378 .is_some_and(|child| child.kind() == ";")
14379 })
14380}
14381
14382fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
14383 if node.has_error() {
14384 return false;
14385 }
14386 match node.kind() {
14387 "declaration"
14388 | "field_declaration"
14389 | "alias_declaration"
14390 | "type_definition"
14391 | "static_assert_declaration" => true,
14392 "labeled_statement" => node
14393 .named_child(node.named_child_count().saturating_sub(1))
14394 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
14395 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
14396 matches!(
14397 child.kind(),
14398 "declaration"
14399 | "field_declaration"
14400 | "alias_declaration"
14401 | "type_definition"
14402 | "function_definition"
14403 )
14404 }),
14405 _ => false,
14406 }
14407}
14408
14409fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
14410 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
14411 .then(|| node.child_by_field_name("declarator"))
14412 .flatten()
14413 .and_then(|declarator| extract_variable_name(declarator, source))
14414}
14415
14416use crate::graph::syntax::has_function_scope_ancestor;
14417
14418fn cpp_template_metadata<'tree>(
14419 template_node: Node<'tree>,
14420 declaration_child: Node<'tree>,
14421 source: &str,
14422 ancestry: &ParentIndex<'tree>,
14423) -> Option<CppTemplateMetadata> {
14424 let parameters_node = template_node.child_by_field_name("parameters")?;
14425 let name_node = cpp_templated_class_name_node(declaration_child)?;
14426 let primary_node = match name_node.kind() {
14427 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
14428 _ => name_node,
14429 };
14430 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
14431 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
14432 return None;
14433 }
14434
14435 let mut parameter_nodes = Vec::new();
14436 let mut parameter_names = Vec::new();
14437 let mut cursor = parameters_node.walk();
14438 for parameter in parameters_node.named_children(&mut cursor) {
14439 if !matches!(
14440 parameter.kind(),
14441 "type_parameter_declaration"
14442 | "optional_type_parameter_declaration"
14443 | "variadic_type_parameter_declaration"
14444 | "template_template_parameter_declaration"
14445 | "parameter_declaration"
14446 | "optional_parameter_declaration"
14447 | "variadic_parameter_declaration"
14448 ) {
14449 continue;
14450 }
14451 let index = parameter_nodes.len();
14452 let name = cpp_template_parameter_name(parameter, source)
14457 .unwrap_or_else(|| format!("<anonymous:{index}>"));
14458 parameter_names.push(name);
14459 parameter_nodes.push(parameter);
14460 }
14461 let parameters = parameter_nodes
14462 .into_iter()
14463 .zip(parameter_names.iter().cloned())
14464 .map(|(parameter, name)| CppTemplateParameterMetadata {
14465 name,
14466 kind: cpp_template_parameter_kind(parameter),
14467 variadic: matches!(
14468 parameter.kind(),
14469 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
14470 ),
14471 default: cpp_template_parameter_default_expression(
14472 parameter,
14473 source,
14474 ¶meter_names,
14475 ancestry,
14476 ),
14477 })
14478 .collect();
14479 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
14480 Vec::new()
14481 } else {
14482 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
14483 .unwrap_or_default()
14484 };
14485 let alias_target = (declaration_child.kind() == "alias_declaration")
14486 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
14487 .flatten();
14488 Some(CppTemplateMetadata {
14489 primary_name,
14490 primary_fq_name: String::new(),
14491 parameters,
14492 specialization_arguments,
14493 alias_target,
14494 })
14495}
14496
14497fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
14498 match node.kind() {
14499 "class_specifier" | "struct_specifier" | "union_specifier" => {
14500 node.child_by_field_name("name")
14501 }
14502 "function_definition" => {
14503 let declarator = node.child_by_field_name("declarator")?;
14504 if matches!(declarator.kind(), "identifier" | "template_function") {
14505 Some(declarator)
14506 } else {
14507 None
14508 }
14509 }
14510 "alias_declaration" => node.child_by_field_name("name"),
14511 _ => None,
14512 }
14513}
14514
14515fn cpp_template_alias_target<'tree>(
14516 alias: Node<'tree>,
14517 source: &str,
14518 parameter_names: &[String],
14519 ancestry: &ParentIndex<'tree>,
14520) -> Option<CppTemplateAliasTargetMetadata> {
14521 let mut type_node = alias.child_by_field_name("type")?;
14522 while type_node.kind() == "type_descriptor" {
14523 type_node = type_node.child_by_field_name("type")?;
14524 }
14525 let global = type_node.child_by_field_name("scope").is_none()
14526 && type_node.child(0).is_some_and(|child| child.kind() == "::");
14527 let mut components = Vec::new();
14528 cpp_template_target_components(type_node, source, &mut components)?;
14529 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
14530 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
14531 components,
14532 global,
14533 arguments,
14534 })
14535}
14536
14537fn cpp_template_target_components(
14538 node: Node<'_>,
14539 source: &str,
14540 out: &mut Vec<String>,
14541) -> Option<()> {
14542 match node.kind() {
14543 "identifier" | "namespace_identifier" | "type_identifier" => {
14544 out.push(node_text(node, source).to_string());
14545 Some(())
14546 }
14547 "template_type" => {
14548 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
14549 }
14550 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14551 if let Some(scope) = node.child_by_field_name("scope") {
14552 cpp_template_target_components(scope, source, out)?;
14553 }
14554 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
14555 }
14556 _ => None,
14557 }
14558}
14559
14560fn cpp_template_argument_expressions<'tree>(
14561 mut node: Node<'tree>,
14562 source: &str,
14563 parameter_names: &[String],
14564 ancestry: &ParentIndex<'tree>,
14565) -> Option<Vec<CppTemplateExpression>> {
14566 loop {
14567 match node.kind() {
14568 "template_type" | "template_function" => {
14569 let arguments = node.child_by_field_name("arguments")?;
14570 let mut cursor = arguments.walk();
14571 return Some(
14572 arguments
14573 .named_children(&mut cursor)
14574 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
14575 .map(|argument| {
14576 cpp_template_expression(argument, source, parameter_names, ancestry)
14577 })
14578 .collect(),
14579 );
14580 }
14581 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
14582 node = node
14583 .child_by_field_name("name")
14584 .or_else(|| node.child_by_field_name("type"))?;
14585 }
14586 _ => return None,
14587 }
14588 }
14589}
14590
14591fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
14592 let candidate = node
14593 .child_by_field_name("name")
14594 .or_else(|| node.child_by_field_name("declarator"))
14595 .or_else(|| cpp_recovered_type_parameter_name(node))
14596 .or_else(|| {
14597 let mut cursor = node.walk();
14598 node.named_children(&mut cursor).find(|child| {
14599 matches!(
14600 child.kind(),
14601 "identifier" | "type_identifier" | "field_identifier"
14602 )
14603 })
14604 })?;
14605 if let Some(name) = direct_identifier_name(candidate, source) {
14606 return Some(name);
14607 }
14608 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
14609 (!name.is_empty()).then_some(name)
14610}
14611
14612fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
14613 match node.kind() {
14614 "type_parameter_declaration"
14615 | "optional_type_parameter_declaration"
14616 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
14617 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
14618 _ if cpp_recovered_type_parameter_name(node).is_some() => CppTemplateParameterKind::Type,
14619 _ => CppTemplateParameterKind::Value,
14620 }
14621}
14622
14623pub fn cpp_recovered_type_parameter_name(parameter: Node<'_>) -> Option<Node<'_>> {
14632 if parameter.kind() != "optional_parameter_declaration"
14633 || parameter.child_by_field_name("declarator").is_some()
14634 {
14635 return None;
14636 }
14637 let dependent = parameter
14638 .child_by_field_name("type")
14639 .filter(|type_node| type_node.kind() == "dependent_type")?;
14640 let mut cursor = dependent.walk();
14641 let mut named = dependent
14642 .named_children(&mut cursor)
14643 .filter(|child| !child.is_extra());
14644 match (named.next(), named.next()) {
14645 (Some(name), None) if name.kind() == "type_identifier" => Some(name),
14646 _ => None,
14647 }
14648}
14649
14650#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
14651pub struct CppTemplateParameterNameMatches {
14652 pub any_parameter: bool,
14653 pub type_parameter: bool,
14654}
14655
14656pub fn cpp_template_parameter_name_matches(
14657 parameters: Node<'_>,
14658 name: &str,
14659 mut parameter_name: impl FnMut(Node<'_>) -> Option<String>,
14660) -> CppTemplateParameterNameMatches {
14661 let mut matches = CppTemplateParameterNameMatches::default();
14662 let mut cursor = parameters.walk();
14663 for parameter in parameters.named_children(&mut cursor) {
14664 if parameter_name(parameter).as_deref() == Some(name) {
14665 matches.any_parameter = true;
14666 matches.type_parameter |=
14667 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type;
14668 }
14669 }
14670 matches
14671}
14672
14673pub fn cpp_type_field_contains_node(parent: Node<'_>, node: Node<'_>) -> bool {
14674 parent.child_by_field_name("type").is_some_and(|type_node| {
14675 type_node.start_byte() <= node.start_byte() && node.end_byte() <= type_node.end_byte()
14676 })
14677}
14678
14679fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
14680 node.child_by_field_name("default_type")
14681 .or_else(|| node.child_by_field_name("default_value"))
14682}
14683
14684fn cpp_template_parameter_default_expression<'tree>(
14685 parameter: Node<'tree>,
14686 source: &str,
14687 parameter_names: &[String],
14688 ancestry: &ParentIndex<'tree>,
14689) -> Option<CppTemplateExpression> {
14690 let default = cpp_template_parameter_default(parameter)?;
14691 let base = cpp_template_expression(default, source, parameter_names, ancestry);
14692 let Some(pointer_error) = parameter.next_named_sibling() else {
14693 return Some(base);
14694 };
14695 let Some(pointer_declarator) =
14696 recovered_abstract_pointer_declarator_term(pointer_error, source)
14697 else {
14698 return Some(base);
14699 };
14700 Some(CppTemplateExpression {
14701 text: format!(
14702 "{}{}",
14703 base.text,
14704 normalize_cpp_whitespace(node_text(pointer_error, source))
14705 ),
14706 term: CppTemplateTerm::Node {
14707 kind: "type_descriptor".to_string(),
14708 children: vec![base.term, pointer_declarator],
14709 },
14710 })
14711}
14712
14713fn recovered_abstract_pointer_declarator_term(
14714 node: Node<'_>,
14715 source: &str,
14716) -> Option<CppTemplateTerm> {
14717 if node.kind() != "ERROR" || node.child_count() == 0 {
14718 return None;
14719 }
14720 let mut children = Vec::new();
14721 for index in 0..node.child_count() {
14722 let child = node.child(index)?;
14723 if child.kind() != "*" {
14724 return None;
14725 }
14726 children.push(CppTemplateTerm::Atom {
14727 kind: "*".to_string(),
14728 text: normalize_cpp_whitespace(node_text(child, source)),
14729 });
14730 }
14731 Some(CppTemplateTerm::Node {
14732 kind: "abstract_pointer_declarator".to_string(),
14733 children,
14734 })
14735}
14736
14737fn cpp_template_expression<'tree>(
14738 node: Node<'tree>,
14739 source: &str,
14740 parameter_names: &[String],
14741 ancestry: &ParentIndex<'tree>,
14742) -> CppTemplateExpression {
14743 let text = normalize_cpp_whitespace(node_text(node, source));
14744 CppTemplateExpression {
14745 text,
14746 term: cpp_template_term(node, source, parameter_names, ancestry),
14747 }
14748}
14749
14750pub fn cpp_template_term<'tree>(
14751 node: Node<'tree>,
14752 source: &str,
14753 parameter_names: &[String],
14754 ancestry: &ParentIndex<'tree>,
14755) -> CppTemplateTerm {
14756 enum Work<'tree> {
14757 Visit(Node<'tree>),
14758 Build { kind: String, child_count: usize },
14759 }
14760
14761 let mut work = vec![Work::Visit(node)];
14762 let mut terms = Vec::new();
14763 while let Some(next) = work.pop() {
14764 match next {
14765 Work::Visit(current) => {
14766 let text = normalize_cpp_whitespace(node_text(current, source));
14767 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
14768 terms.push(CppTemplateTerm::Parameter(text));
14769 continue;
14770 }
14771 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
14772 let mut cursor = current.walk();
14773 let named = current
14774 .named_children(&mut cursor)
14775 .filter(|child| !child.is_extra() && child.kind() != "comment")
14776 .collect::<Vec<_>>();
14777 if let [child] = named.as_slice() {
14778 work.push(Work::Visit(*child));
14779 continue;
14780 }
14781 }
14782 if current.child_count() == 0 {
14783 terms.push(CppTemplateTerm::Atom {
14784 kind: if matches!(
14785 current.kind(),
14786 "identifier"
14787 | "type_identifier"
14788 | "field_identifier"
14789 | "namespace_identifier"
14790 ) {
14791 "identifier".to_string()
14792 } else {
14793 current.kind().to_string()
14794 },
14795 text,
14796 });
14797 continue;
14798 }
14799 let children = (0..current.child_count())
14800 .filter_map(|index| current.child(index))
14801 .filter(|child| !child.is_extra() && child.kind() != "comment")
14802 .collect::<Vec<_>>();
14803 work.push(Work::Build {
14804 kind: current.kind().to_string(),
14805 child_count: children.len(),
14806 });
14807 work.extend(children.into_iter().rev().map(Work::Visit));
14808 }
14809 Work::Build { kind, child_count } => {
14810 let children = terms.split_off(terms.len() - child_count);
14811 terms.push(CppTemplateTerm::Node { kind, children });
14812 }
14813 }
14814 }
14815 terms.pop().expect("template term traversal emits one root")
14816}
14817
14818fn cpp_template_term_leaf_is_parameter<'tree>(
14819 node: Node<'tree>,
14820 text: &str,
14821 parameter_names: &[String],
14822 ancestry: &ParentIndex<'tree>,
14823) -> bool {
14824 if !parameter_names.iter().any(|parameter| parameter == text) {
14825 return false;
14826 }
14827 !ancestry.parent(node).is_some_and(|parent| {
14828 matches!(
14829 parent.kind(),
14830 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
14831 ) && parent.child_by_field_name("scope").is_some()
14832 && parent.child_by_field_name("name") == Some(node)
14833 })
14834}
14835
14836fn enclosing_cpp_declaration_node<'tree>(
14837 mut node: Node<'tree>,
14838 ancestry: &ParentIndex<'tree>,
14839) -> Option<Node<'tree>> {
14840 loop {
14841 match node.kind() {
14842 "declaration"
14843 | "function_declaration"
14844 | "field_declaration"
14845 | "function_definition" => return Some(node),
14846 _ => node = ancestry.parent(node)?,
14847 }
14848 }
14849}
14850
14851fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
14852 let mut params = Vec::new();
14853 let mut cursor = parameters_node.walk();
14854 for child in parameters_node.children(&mut cursor) {
14855 match child.kind() {
14856 "explicit_object_parameter_declaration" => {
14857 if let Some(parameter) = child
14858 .named_children(&mut child.walk())
14859 .find(|node| node.kind() == "parameter_declaration")
14860 {
14861 params.push(format!("this {}", cpp_parameter_type(parameter, source)));
14862 }
14863 }
14864 "parameter_declaration" | "optional_parameter_declaration" => {
14865 params.push(cpp_parameter_type(child, source));
14866 }
14867 "variadic_parameter_declaration" => {
14868 params.push(cpp_parameter_type(child, source));
14869 }
14870 "variadic_parameter" | "..." => params.push("...".to_string()),
14871 _ => {}
14872 }
14873 }
14874
14875 if params.is_empty() {
14876 "()".to_string()
14877 } else {
14878 format!("({})", params.join(", "))
14879 }
14880}
14881
14882enum CppCallableScope<'tree> {
14885 ClassBody {
14889 body: Node<'tree>,
14890 default_visibility: DeclaredVisibility,
14891 },
14892 Qualified,
14898 Unqualified,
14901}
14902
14903fn cpp_callable_scope<'tree>(
14919 declaration: Node<'tree>,
14920 function_declarator: Option<Node<'tree>>,
14921 ancestry: &ParentIndex<'tree>,
14922) -> CppCallableScope<'tree> {
14923 let mut current = Some(declaration);
14924 while let Some(node) = current {
14925 match node.kind() {
14926 "field_declaration_list" => {
14927 let default_visibility =
14928 ancestry
14929 .parent(node)
14930 .map_or(DeclaredVisibility::Unknown, |class_like| {
14931 match class_like.kind() {
14934 "struct_specifier" | "union_specifier" => {
14935 DeclaredVisibility::Public
14936 }
14937 _ => DeclaredVisibility::Private,
14938 }
14939 });
14940 return CppCallableScope::ClassBody {
14941 body: node,
14942 default_visibility,
14943 };
14944 }
14945 "namespace_definition" | "compound_statement" | "translation_unit" => break,
14946 _ => {}
14947 }
14948 current = ancestry.parent(node);
14949 }
14950 if cpp_callable_declarator_is_qualified(function_declarator) {
14951 CppCallableScope::Qualified
14952 } else {
14953 CppCallableScope::Unqualified
14954 }
14955}
14956
14957fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
14964 function_declarator
14965 .and_then(cpp_function_declarator_name_node)
14966 .is_some_and(|name| name.kind() == "qualified_identifier")
14967}
14968
14969fn cpp_member_declared_visibility<'tree>(
14984 declaration: Node<'tree>,
14985 body: Node<'tree>,
14986 default_visibility: DeclaredVisibility,
14987 source: &str,
14988 ancestry: &ParentIndex<'tree>,
14989) -> DeclaredVisibility {
14990 let mut list_level = declaration;
14991 while ancestry.parent(list_level) != Some(body) {
14992 let Some(parent) = ancestry.parent(list_level) else {
14993 return default_visibility;
14994 };
14995 list_level = parent;
14996 }
14997 let mut visibility = default_visibility;
14998 let mut cursor = body.walk();
14999 for child in body.children(&mut cursor) {
15000 if child == list_level {
15001 break;
15002 }
15003 let access = if child.kind() == "access_specifier" {
15004 child.child(0).map(|keyword| keyword.kind())
15005 } else {
15006 cpp_macro_swallowed_access_label(child, source).map(|label| label.access)
15007 };
15008 visibility = match access {
15009 Some("public") => DeclaredVisibility::Public,
15010 Some("protected") => DeclaredVisibility::Protected,
15011 Some("private") => DeclaredVisibility::Private,
15012 _ => visibility,
15013 };
15014 }
15015 visibility
15016}
15017
15018fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
15025 let mut cursor = declaration.walk();
15026 declaration.named_children(&mut cursor).any(|child| {
15027 child.kind() == "storage_class_specifier"
15028 && normalize_cpp_whitespace(node_text(child, source)) == "static"
15029 })
15030}
15031
15032struct CppCallableModifiers {
15035 is_static: bool,
15036 is_constructor: bool,
15037 visibility: DeclaredVisibility,
15038}
15039
15040fn cpp_callable_modifiers<'tree>(
15064 declaration: Node<'tree>,
15065 function_declarator: Option<Node<'tree>>,
15066 is_constructor: bool,
15067 source: &str,
15068 ancestry: &ParentIndex<'tree>,
15069) -> CppCallableModifiers {
15070 match cpp_callable_scope(declaration, function_declarator, ancestry) {
15071 CppCallableScope::ClassBody {
15072 body,
15073 default_visibility,
15074 } => CppCallableModifiers {
15075 is_static: cpp_declares_static_storage_class(declaration, source),
15076 is_constructor,
15077 visibility: cpp_member_declared_visibility(
15078 declaration,
15079 body,
15080 default_visibility,
15081 source,
15082 ancestry,
15083 ),
15084 },
15085 CppCallableScope::Qualified => CppCallableModifiers {
15086 is_static: false,
15087 is_constructor,
15088 visibility: DeclaredVisibility::Unknown,
15089 },
15090 CppCallableScope::Unqualified => CppCallableModifiers {
15091 is_static: false,
15092 is_constructor,
15093 visibility: DeclaredVisibility::Unknown,
15094 },
15095 }
15096}
15097
15098fn cpp_signature_metadata<'tree>(
15107 signature: String,
15108 function_declarator: Node<'tree>,
15109 linkage: CallableLinkage,
15110 source: &str,
15111 ancestry: &ParentIndex<'tree>,
15112) -> SignatureMetadata {
15113 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
15114 let is_constructor =
15115 cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
15116 let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
15117 Some(declaration) => cpp_callable_modifiers(
15118 declaration,
15119 Some(function_declarator),
15120 is_constructor,
15121 source,
15122 ancestry,
15123 ),
15124 None => CppCallableModifiers {
15127 is_static: false,
15128 is_constructor,
15129 visibility: DeclaredVisibility::Unknown,
15130 },
15131 };
15132 let enrich = |mut metadata: SignatureMetadata| {
15133 if let Some(dispatch) = dispatch {
15136 metadata = metadata.with_dispatch_extensibility(dispatch);
15137 }
15138 metadata
15139 .with_callable_linkage(linkage)
15140 .with_callable_modifiers(
15141 modifiers.is_static,
15142 modifiers.is_constructor,
15143 modifiers.visibility,
15144 )
15145 };
15146 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
15147 let return_type_identity =
15148 cpp_callable_return_type_identity(function_declarator, source, ancestry);
15149 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
15150 return enrich(
15151 SignatureMetadata::new(signature, Vec::new())
15152 .with_return_type_text(return_type_text)
15153 .with_return_type_identity(return_type_identity),
15154 );
15155 };
15156 let callable_arity = cpp_callable_arity(parameters_node, source);
15157 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
15158 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
15159 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
15160 let Some(relative_start) = signature
15161 .get(search_from..)
15162 .and_then(|suffix| suffix.find(¶meter_text))
15163 else {
15164 return enrich(
15165 SignatureMetadata::new(signature, Vec::new())
15166 .with_callable_arity(callable_arity)
15167 .with_callable_parameter_types(callable_parameter_types)
15168 .with_return_type_text(return_type_text)
15169 .with_return_type_identity(return_type_identity),
15170 );
15171 };
15172 let parameters_start = search_from + relative_start;
15173 let parameters_end = parameters_start + parameter_text.len();
15174 let mut search_start = parameters_start;
15175 let parameters = cpp_parameter_label_nodes(parameters_node)
15176 .into_iter()
15177 .filter_map(|label_node| {
15178 let label = normalize_cpp_whitespace(node_text(label_node, source));
15179 if label.is_empty() || search_start > parameters_end {
15180 return None;
15181 }
15182 let haystack = signature.get(search_start..parameters_end)?;
15183 let relative_start = haystack.find(&label)?;
15184 let start_byte = search_start + relative_start;
15185 let end_byte = start_byte + label.len();
15186 search_start = end_byte;
15187 Some(ParameterMetadata::new(label, start_byte, end_byte))
15188 })
15189 .collect();
15190 enrich(
15191 SignatureMetadata::new(signature, parameters)
15192 .with_callable_arity(callable_arity)
15193 .with_callable_parameter_types(callable_parameter_types)
15194 .with_return_type_text(return_type_text)
15195 .with_return_type_identity(return_type_identity),
15196 )
15197}
15198
15199pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
15200 function_declarator: Node<'tree>,
15201 source: &str,
15202 ancestry: &ParentIndex<'tree>,
15203) -> bool {
15204 let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
15205 .or_else(|| function_declarator.child_by_field_name("name"))
15206 .or_else(|| last_named_child(function_declarator))
15207 else {
15208 return false;
15209 };
15210 if name_node.kind() == "qualified_identifier" {
15214 let owner = name_node
15215 .child_by_field_name("scope")
15216 .and_then(|scope| canonical_cpp_qualified_component(scope, source));
15217 let member = name_node
15218 .child_by_field_name("name")
15219 .and_then(|name| canonical_cpp_qualified_component(name, source));
15220 return match (owner, member) {
15221 (Some(owner), Some(member)) => owner.name == member.name,
15222 _ => false,
15223 };
15224 }
15225 let Some(callable_name) = direct_identifier_name(name_node, source) else {
15226 return false;
15227 };
15228
15229 let mut current = ancestry.parent(function_declarator);
15230 while let Some(ancestor) = current {
15231 let owner_name = match ancestor.kind() {
15232 "class_specifier" | "struct_specifier" | "union_specifier" => {
15233 class_like_name(ancestor, source, ancestry)
15234 }
15235 "ERROR" => malformed_class_error_owner_name(ancestor, source),
15236 _ => None,
15237 };
15238 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
15239 return true;
15240 }
15241 current = ancestry.parent(ancestor);
15242 }
15243 false
15244}
15245
15246fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
15256 if node.kind() != "ERROR" {
15257 return None;
15258 }
15259 let keyword = node.child(0)?;
15260 if !matches!(keyword.kind(), "class" | "struct" | "union") {
15261 return None;
15262 }
15263 let name_node = node.child(1)?;
15264 let name = direct_identifier_name(name_node, source)?;
15265 let has_body = (2..node.child_count())
15266 .filter_map(|index| node.child(index))
15267 .any(|child| child.kind() == "{");
15268 has_body.then_some(name)
15269}
15270
15271pub fn cpp_declaration_has_native_must_use_attribute(declaration: Node<'_>, source: &str) -> bool {
15278 let declaration = declaration
15283 .parent()
15284 .filter(|parent| {
15285 parent.kind() == "declaration"
15286 && parent
15287 .child_by_field_name("type")
15288 .is_some_and(|type_node| type_node.id() == declaration.id())
15289 })
15290 .unwrap_or(declaration);
15291 let mut pending = vec![declaration];
15292 while let Some(node) = pending.pop() {
15293 if cpp_attribute_node_is_native_must_use(node, source)
15294 && cpp_attribute_is_attached_to_declaration(node, declaration)
15295 {
15296 return true;
15297 }
15298 if node != declaration && matches!(node.kind(), "compound_statement" | "lambda_expression")
15299 {
15300 continue;
15301 }
15302 push_named_children_reversed(node, &mut pending);
15303 }
15304 false
15305}
15306
15307fn cpp_attribute_node_is_native_must_use(node: Node<'_>, source: &str) -> bool {
15308 if node.kind() == "attribute" {
15309 return node.child_by_field_name("name").is_some_and(|name| {
15310 let name = node_text(name, source);
15311 let prefix = node
15312 .child_by_field_name("prefix")
15313 .map(|prefix| node_text(prefix, source));
15314 matches!(
15315 (prefix, name),
15316 (None, "nodiscard")
15317 | (None, "warn_unused_result")
15318 | (Some("gnu"), "warn_unused_result")
15319 )
15320 });
15321 }
15322 if node.kind() != "attribute_specifier" || !node_text(node, source).starts_with("__attribute__")
15323 {
15324 return false;
15325 }
15326 let Some(arguments) = node.named_child(0) else {
15327 return false;
15328 };
15329 let Some(name) = arguments.named_child(0) else {
15330 return false;
15331 };
15332 arguments.kind() == "argument_list"
15333 && name.kind() == "identifier"
15334 && node_text(name, source) == "warn_unused_result"
15335}
15336
15337fn cpp_attribute_is_attached_to_declaration(attribute: Node<'_>, declaration: Node<'_>) -> bool {
15338 let mut current = attribute;
15339 while let Some(parent) = current.parent() {
15340 if parent.id() == declaration.id() {
15341 return true;
15342 }
15343 if matches!(
15344 parent.kind(),
15345 "declaration"
15346 | "field_declaration"
15347 | "function_definition"
15348 | "struct_specifier"
15349 | "class_specifier"
15350 | "union_specifier"
15351 | "enum_specifier"
15352 ) {
15353 return false;
15354 }
15355 current = parent;
15356 }
15357 false
15358}
15359
15360fn cpp_declaration_return_type_is_macro_decorated(
15368 declaration: Node<'_>,
15369 type_node: Node<'_>,
15370 source: &str,
15371) -> bool {
15372 type_node.kind() == "macro_type_specifier"
15373 || cpp_declaration_type_follows_macro_modifier(declaration, type_node)
15374 || (cpp_export_macro_token(node_text(type_node, source))
15375 && (0..declaration.named_child_count()).any(|index| {
15376 declaration
15377 .named_child(index)
15378 .is_some_and(|child| child.kind() == "ERROR")
15379 }))
15380}
15381
15382pub(crate) fn cpp_declaration_type_follows_macro_modifier(
15386 declaration: Node<'_>,
15387 type_node: Node<'_>,
15388) -> bool {
15389 let mut head_cursor = declaration.walk();
15390 declaration
15391 .named_children(&mut head_cursor)
15392 .take_while(|child| *child != type_node)
15393 .any(|child| child.kind() == "macro_modifier")
15394}
15395
15396fn cpp_callable_return_type_parts<'tree>(
15397 function_declarator: Node<'tree>,
15398 source: &str,
15399 ancestry: &ParentIndex<'tree>,
15400) -> Option<(Node<'tree>, Vec<CppStructuredTypeWrapper>)> {
15401 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
15402 return None;
15403 }
15404 if let Some((return_type, _)) =
15405 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
15406 {
15407 return Some((return_type, Vec::new()));
15408 }
15409 let mut cursor = function_declarator.walk();
15410 if let Some(trailing) = function_declarator
15411 .named_children(&mut cursor)
15412 .find(|child| child.kind() == "trailing_return_type")
15413 && let Some(type_descriptor) = trailing.named_child(0)
15414 {
15415 return Some((type_descriptor, Vec::new()));
15416 }
15417
15418 let mut current = function_declarator;
15419 let mut wrappers = Vec::new();
15420 while let Some(parent) = ancestry.parent(current) {
15421 if matches!(
15422 parent.kind(),
15423 "function_definition" | "declaration" | "field_declaration"
15424 ) {
15425 let type_node = parent.child_by_field_name("type")?;
15426 if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
15427 return None;
15428 }
15429 return Some((type_node, wrappers));
15430 }
15431 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
15432 || (matches!(
15433 parent.kind(),
15434 "pointer_declarator"
15435 | "reference_declarator"
15436 | "array_declarator"
15437 | "parenthesized_declarator"
15438 ) && parent.named_child_count() == 1
15439 && parent.named_child(0) == Some(current));
15440 if !wraps_current_declarator {
15441 return None;
15442 }
15443 match parent.kind() {
15444 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
15445 "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
15446 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
15447 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
15448 _ => return None,
15449 }
15450 current = parent;
15451 }
15452 None
15453}
15454
15455pub fn cpp_callable_declaration_return_type_node<'tree>(
15462 callable: Node<'tree>,
15463 source: &str,
15464 ancestry: &ParentIndex<'tree>,
15465) -> Option<Node<'tree>> {
15466 let declarator = callable
15467 .child_by_field_name("declarator")
15468 .and_then(extract_function_declarator)?;
15469 cpp_callable_return_type_parts(declarator, source, ancestry).map(|(node, _)| node)
15470}
15471
15472pub fn cpp_callable_declaration_return_type_identity<'tree>(
15478 callable: Node<'tree>,
15479 source: &str,
15480 ancestry: &ParentIndex<'tree>,
15481) -> Option<StructuredTypeIdentity> {
15482 let declarator = callable
15483 .child_by_field_name("declarator")
15484 .and_then(extract_function_declarator)?;
15485 cpp_callable_return_type_identity(declarator, source, ancestry)
15486}
15487
15488pub(crate) fn cpp_callable_return_type_identity<'tree>(
15489 function_declarator: Node<'tree>,
15490 source: &str,
15491 ancestry: &ParentIndex<'tree>,
15492) -> Option<StructuredTypeIdentity> {
15493 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
15494 let (return_type, wrappers) =
15495 cpp_callable_return_type_parts(function_declarator, source, ancestry)?;
15496 cpp_apply_structured_type_wrappers(
15497 cpp_structured_type_identity(return_type, source, &lexical_scope)?,
15498 wrappers,
15499 )
15500}
15501
15502fn cpp_structured_type_identity(
15503 node: Node<'_>,
15504 source: &str,
15505 lexical_scope: &[String],
15506) -> Option<StructuredTypeIdentity> {
15507 enum Work<'tree> {
15508 Visit(Node<'tree>),
15509 Wrap(CppStructuredTypeWrapper),
15510 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
15511 BuildGeneric { argument_count: usize },
15512 }
15513
15514 let mut work = vec![Work::Visit(node)];
15515 let mut values = Vec::new();
15516 let mut builder = StructuredTypeIdentityBuilder::default();
15517 while let Some(next) = work.pop() {
15518 match next {
15519 Work::Visit(current) => match current.kind() {
15520 "type_descriptor" => {
15521 let type_node = current
15522 .child_by_field_name("type")
15523 .or_else(|| current.named_child(0))?;
15524 let mut wrappers = Vec::new();
15525 let mut cursor = current.walk();
15526 for child in current.named_children(&mut cursor) {
15527 if child.id() != type_node.id() {
15528 wrappers.extend(cpp_structured_declarator_wrappers(child)?);
15529 }
15530 }
15531 work.push(Work::ApplyWrappers(wrappers));
15532 work.push(Work::Visit(type_node));
15533 }
15534 "pointer_declarator" | "abstract_pointer_declarator" => {
15535 let child = current
15536 .child_by_field_name("declarator")
15537 .or_else(|| current.named_child(0))?;
15538 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
15539 work.push(Work::Visit(child));
15540 }
15541 "reference_declarator" => {
15542 let child = current
15543 .child_by_field_name("declarator")
15544 .or_else(|| current.named_child(0))?;
15545 work.push(Work::Wrap(cpp_reference_wrapper(current)?));
15546 work.push(Work::Visit(child));
15547 }
15548 "array_declarator" | "abstract_array_declarator" => {
15549 let child = current
15550 .child_by_field_name("declarator")
15551 .or_else(|| current.named_child(0))?;
15552 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
15553 work.push(Work::Visit(child));
15554 }
15555 "template_type" => {
15556 let name_node = current.child_by_field_name("name")?;
15557 let arguments = current
15558 .child_by_field_name("arguments")
15559 .map(|arguments_node| {
15560 let mut cursor = arguments_node.walk();
15561 arguments_node
15562 .named_children(&mut cursor)
15563 .filter(|child| !child.is_extra() && child.kind() != "comment")
15564 .collect::<Vec<_>>()
15565 })
15566 .unwrap_or_default();
15567 work.push(Work::BuildGeneric {
15568 argument_count: arguments.len(),
15569 });
15570 work.extend(arguments.into_iter().rev().map(Work::Visit));
15571 work.push(Work::Visit(name_node));
15572 }
15573 "qualified_identifier"
15574 | "scoped_identifier"
15575 | "scoped_type_identifier"
15576 | "type_identifier"
15577 | "field_identifier"
15578 | "identifier"
15579 | "namespace_identifier"
15580 | "primitive_type" => {
15581 values.push(builder.named(cpp_structured_named_type(
15582 current,
15583 source,
15584 lexical_scope,
15585 )?)?);
15586 }
15587 _ => {
15588 let child = current.child_by_field_name("type").or_else(|| {
15589 (current.named_child_count() == 1)
15590 .then(|| current.named_child(0))
15591 .flatten()
15592 })?;
15593 work.push(Work::Visit(child));
15594 }
15595 },
15596 Work::Wrap(wrapper) => {
15597 let root = values.pop()?;
15598 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
15599 }
15600 Work::ApplyWrappers(wrappers) => {
15601 let mut root = values.pop()?;
15602 for wrapper in wrappers.into_iter().rev() {
15603 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
15604 }
15605 values.push(root);
15606 }
15607 Work::BuildGeneric { argument_count } => {
15608 let value_count = argument_count.checked_add(1)?;
15609 let start = values.len().checked_sub(value_count)?;
15610 let mut built = values.split_off(start);
15611 let base = built.remove(0);
15612 values.push(builder.generic(base, built)?);
15613 }
15614 }
15615 }
15616 (values.len() == 1)
15617 .then(|| values.pop())
15618 .flatten()
15619 .and_then(|root| builder.finish(root))
15620}
15621
15622fn cpp_structured_named_type(
15623 node: Node<'_>,
15624 source: &str,
15625 lexical_scope: &[String],
15626) -> Option<StructuredTypeName> {
15627 let path = cpp_structured_type_path(node, source)?;
15628 let absolute = node.child_by_field_name("scope").is_none()
15629 && node.child(0).is_some_and(|child| child.kind() == "::");
15630 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
15631}
15632
15633#[derive(Clone, Copy)]
15634enum CppStructuredTypeWrapper {
15635 Pointer,
15636 LvalueReference,
15637 RvalueReference,
15638 Array,
15639}
15640
15641fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
15642 let mut wrappers = Vec::new();
15643 let mut current = node;
15644 loop {
15645 match current.kind() {
15646 "pointer_declarator" | "abstract_pointer_declarator" => {
15647 wrappers.push(CppStructuredTypeWrapper::Pointer)
15648 }
15649 "reference_declarator" | "abstract_reference_declarator" => {
15650 wrappers.push(cpp_reference_wrapper(current)?);
15651 }
15652 "array_declarator" | "abstract_array_declarator" => {
15653 wrappers.push(CppStructuredTypeWrapper::Array)
15654 }
15655 "init_declarator" | "parenthesized_declarator" => {}
15662 _ => break,
15663 }
15664 let Some(child) = current
15665 .child_by_field_name("declarator")
15666 .or_else(|| current.named_child(0))
15667 else {
15668 break;
15669 };
15670 current = child;
15671 }
15672 Some(wrappers)
15673}
15674
15675fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
15676 node.children(&mut node.walk())
15677 .find_map(|child| match child.kind() {
15678 "&" => Some(CppStructuredTypeWrapper::LvalueReference),
15679 "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
15680 _ => None,
15681 })
15682}
15683
15684fn cpp_wrap_structured_type(
15685 identity: StructuredTypeIdentity,
15686 wrapper: CppStructuredTypeWrapper,
15687) -> Option<StructuredTypeIdentity> {
15688 match wrapper {
15689 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
15690 CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
15691 CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
15692 CppStructuredTypeWrapper::Array => identity.wrap_array(),
15693 }
15694}
15695
15696fn cpp_apply_structured_type_wrappers(
15697 mut identity: StructuredTypeIdentity,
15698 wrappers: Vec<CppStructuredTypeWrapper>,
15699) -> Option<StructuredTypeIdentity> {
15700 for wrapper in wrappers.into_iter().rev() {
15701 identity = cpp_wrap_structured_type(identity, wrapper)?;
15702 }
15703 Some(identity)
15704}
15705
15706fn cpp_wrap_structured_type_node(
15707 builder: &mut StructuredTypeIdentityBuilder,
15708 inner: StructuredTypeNodeId,
15709 wrapper: CppStructuredTypeWrapper,
15710) -> Option<StructuredTypeNodeId> {
15711 match wrapper {
15712 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
15713 CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
15714 CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
15715 CppStructuredTypeWrapper::Array => builder.array(inner),
15716 }
15717}
15718
15719pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
15726 let mut path = Vec::new();
15727 let mut stack = vec![node];
15728 while let Some(current) = stack.pop() {
15729 match current.kind() {
15730 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
15731 let component = node_text(current, source).to_string();
15732 if component.is_empty() {
15733 return None;
15734 }
15735 path.push(component);
15736 }
15737 "template_type" | "dependent_type" => {
15738 stack.push(current.child_by_field_name("name")?);
15739 }
15740 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
15741 stack.push(current.child_by_field_name("name")?);
15742 if let Some(scope) = current.child_by_field_name("scope") {
15743 stack.push(scope);
15744 }
15745 }
15746 _ => return None,
15747 }
15748 }
15749 (!path.is_empty()).then_some(path)
15750}
15751
15752fn cpp_callable_lexical_scope<'tree>(
15753 node: Node<'tree>,
15754 source: &str,
15755 ancestry: &ParentIndex<'tree>,
15756) -> Vec<String> {
15757 let mut groups = Vec::new();
15758 let mut current = ancestry.parent(node);
15759 while let Some(parent) = current {
15760 if matches!(
15761 parent.kind(),
15762 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
15763 ) && let Some(name_node) = parent.child_by_field_name("name")
15764 && let Some(components) = cpp_structured_type_path(name_node, source)
15765 && !components.is_empty()
15766 {
15767 groups.push(components);
15768 }
15769 current = ancestry.parent(parent);
15770 }
15771 groups.reverse();
15772 groups.into_iter().flatten().collect()
15773}
15774
15775fn cpp_callable_dispatch_extensibility<'tree>(
15799 function_declarator: Node<'tree>,
15800 ancestry: &ParentIndex<'tree>,
15801) -> Option<DispatchExtensibility> {
15802 let mut declaration = None;
15803 let mut class_body = None;
15807 let mut current = Some(function_declarator);
15808 while let Some(node) = current {
15809 match node.kind() {
15810 "template_declaration"
15811 | "preproc_if"
15812 | "preproc_ifdef"
15813 | "preproc_else"
15814 | "preproc_elif"
15815 | "preproc_call"
15816 | "ERROR" => return Some(DispatchExtensibility::Open),
15817 "declaration" | "field_declaration" | "function_definition" => {
15818 declaration.get_or_insert(node);
15819 }
15820 "field_declaration_list" => {
15821 class_body.get_or_insert(node);
15822 }
15823 "translation_unit" => break,
15824 _ => {}
15825 }
15826 current = ancestry.parent(node);
15827 }
15828 let Some(declaration) = declaration else {
15829 return Some(DispatchExtensibility::Open);
15830 };
15831
15832 let mut saw_virtual_boundary = false;
15833 let mut stack = vec![declaration];
15834 while let Some(node) = stack.pop() {
15835 match node.kind() {
15836 "compound_statement" | "field_declaration_list" => continue,
15837 "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
15838 "virtual"
15839 | "override"
15840 | "virtual_specifier"
15841 | "pure_virtual_clause"
15842 | "template_parameter_list"
15843 | "template_method"
15844 | "template_function"
15845 | "ERROR" => saw_virtual_boundary = true,
15846 _ => {}
15847 }
15848 let mut cursor = node.walk();
15849 stack.extend(node.children(&mut cursor));
15850 }
15851
15852 if saw_virtual_boundary {
15853 return Some(DispatchExtensibility::Open);
15854 }
15855 if let Some(body) = class_body {
15856 let has_base_clause = ancestry
15860 .parent(body)
15861 .and_then(cpp_class_like_base_clause)
15862 .is_some();
15863 return Some(if has_base_clause {
15864 DispatchExtensibility::Open
15865 } else {
15866 DispatchExtensibility::Closed
15867 });
15868 }
15869 if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
15870 return None;
15871 }
15872 Some(DispatchExtensibility::Closed)
15875}
15876
15877fn cpp_callable_linkage<'tree>(
15878 declaration: Node<'tree>,
15879 source: &str,
15880 ancestry: &ParentIndex<'tree>,
15881) -> CallableLinkage {
15882 let mut enclosed_by_class = false;
15883 let mut current = ancestry.parent(declaration);
15884 while let Some(node) = current {
15885 if node.kind() == "namespace_definition"
15886 && node
15887 .child_by_field_name("name")
15888 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
15889 {
15890 return CallableLinkage::Internal;
15891 }
15892 if matches!(
15893 node.kind(),
15894 "class_specifier" | "struct_specifier" | "union_specifier"
15895 ) {
15896 if node
15897 .child_by_field_name("name")
15898 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
15899 {
15900 return CallableLinkage::Internal;
15901 }
15902 enclosed_by_class = true;
15903 }
15904 if node.kind() == "lambda_expression"
15909 || node.kind() == "function_definition"
15910 && !is_recovered_exported_class_container(node, source)
15911 {
15912 return CallableLinkage::Internal;
15913 }
15914 current = ancestry.parent(node);
15915 }
15916
15917 if enclosed_by_class {
15918 return CallableLinkage::External;
15919 }
15920
15921 if cpp_declares_static_storage_class(declaration, source) {
15922 CallableLinkage::Internal
15923 } else {
15924 CallableLinkage::External
15925 }
15926}
15927
15928fn cpp_callable_return_type_text<'tree>(
15929 function_declarator: Node<'tree>,
15930 source: &str,
15931 ancestry: &ParentIndex<'tree>,
15932) -> Option<String> {
15933 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
15934 return None;
15935 }
15936 if let Some((return_type, _)) =
15937 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
15938 {
15939 let text = normalize_cpp_whitespace(node_text(return_type, source));
15940 return (!text.is_empty()).then_some(text);
15941 }
15942 let mut cursor = function_declarator.walk();
15943 if let Some(trailing) = function_declarator
15944 .named_children(&mut cursor)
15945 .find(|child| child.kind() == "trailing_return_type")
15946 && let Some(type_descriptor) = trailing.named_child(0)
15947 {
15948 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
15949 if !text.is_empty() {
15950 return Some(text);
15951 }
15952 }
15953
15954 let mut current = function_declarator;
15955 let mut indirection = String::new();
15956 while let Some(parent) = ancestry.parent(current) {
15957 if matches!(
15958 parent.kind(),
15959 "function_definition" | "declaration" | "field_declaration"
15960 ) {
15961 let type_node = parent.child_by_field_name("type")?;
15962 if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
15963 return None;
15964 }
15965 let base = normalize_cpp_whitespace(node_text(type_node, source));
15966 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
15967 }
15968 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
15969 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
15970 && parent.named_child_count() == 1
15971 && parent.named_child(0) == Some(current));
15972 if wraps_current_declarator {
15973 match parent.kind() {
15974 "pointer_declarator" => indirection.push('*'),
15975 "reference_declarator" => {
15976 let reference = parent
15977 .children(&mut parent.walk())
15978 .find(|child| !child.is_named())
15979 .map(|child| node_text(child, source))
15980 .unwrap_or("&");
15981 indirection.push_str(reference);
15982 }
15983 "init_declarator" | "parenthesized_declarator" => {}
15984 _ => return None,
15985 }
15986 current = parent;
15987 continue;
15988 }
15989 return None;
15990 }
15991 None
15992}
15993
15994pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
15995 let mut required = 0;
15996 let mut total = 0;
15997 let mut repeated = false;
15998 for slot in cpp_callable_parameter_slots(parameters_node, source) {
15999 match slot {
16000 CppParameterSlot::Declared {
16001 required: is_required,
16002 ..
16003 } => {
16004 total += 1;
16005 if is_required {
16006 required += 1;
16007 }
16008 }
16009 CppParameterSlot::OptionalVoid => total += 1,
16010 CppParameterSlot::Ellipsis => repeated = true,
16011 }
16012 }
16013 CallableArity::new(required, total, repeated)
16014}
16015
16016#[derive(Clone, Copy)]
16023enum CppParameterSlot<'tree> {
16024 Declared {
16025 parameter: Node<'tree>,
16026 required: bool,
16027 },
16028 OptionalVoid,
16029 Ellipsis,
16030}
16031
16032fn cpp_callable_parameter_slots<'tree>(
16033 parameters_node: Node<'tree>,
16034 source: &str,
16035) -> Vec<CppParameterSlot<'tree>> {
16036 let mut slots = Vec::new();
16037 let mut cursor = parameters_node.walk();
16038 for parameter in parameters_node.children(&mut cursor) {
16039 match parameter.kind() {
16040 "parameter_declaration" | "optional_parameter_declaration" => {
16041 let is_unnamed_void = parameter.child_by_field_name("declarator").is_none()
16042 && parameter
16043 .child_by_field_name("type")
16044 .is_some_and(|type_node| node_text(type_node, source).trim() == "void");
16045 if is_unnamed_void && parameter.kind() == "parameter_declaration" {
16046 continue;
16047 }
16048 if is_unnamed_void {
16049 slots.push(CppParameterSlot::OptionalVoid);
16050 continue;
16051 }
16052 slots.push(CppParameterSlot::Declared {
16053 parameter,
16054 required: parameter.kind() == "parameter_declaration",
16055 });
16056 }
16057 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16058 slots.push(CppParameterSlot::Ellipsis);
16059 }
16060 _ => {}
16061 }
16062 }
16063 slots
16064}
16065
16066pub(crate) fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
16067 cpp_callable_parameter_slots(parameters_node, source)
16068 .into_iter()
16069 .filter_map(|slot| match slot {
16070 CppParameterSlot::Declared { parameter, .. } => {
16071 Some(cpp_parameter_type(parameter, source))
16072 }
16073 CppParameterSlot::Ellipsis => Some("...".to_string()),
16074 CppParameterSlot::OptionalVoid => None,
16075 })
16076 .collect()
16077}
16078
16079pub(crate) fn cpp_callable_parameter_names(
16083 parameters_node: Node<'_>,
16084 source: &str,
16085) -> Vec<Option<String>> {
16086 cpp_callable_parameter_slots(parameters_node, source)
16087 .into_iter()
16088 .filter_map(|slot| match slot {
16089 CppParameterSlot::Declared { parameter, .. } => Some(
16090 parameter
16091 .child_by_field_name("declarator")
16092 .and_then(|declarator| declarator_name_from_node(declarator, source)),
16093 ),
16094 CppParameterSlot::Ellipsis => Some(None),
16095 CppParameterSlot::OptionalVoid => None,
16096 })
16097 .collect()
16098}
16099
16100#[derive(Debug, Clone, PartialEq, Eq)]
16106pub enum CppParameterType {
16107 Structured(StructuredTypeIdentity),
16110 Ellipsis,
16112 Unstructured,
16115}
16116
16117pub fn cpp_callable_parameter_type_identities<'tree>(
16123 function_declarator: Node<'tree>,
16124 source: &str,
16125 ancestry: &ParentIndex<'tree>,
16126) -> Vec<CppParameterType> {
16127 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16128 return Vec::new();
16129 };
16130 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16131 cpp_callable_parameter_slots(parameters_node, source)
16132 .into_iter()
16133 .filter_map(|slot| match slot {
16134 CppParameterSlot::Ellipsis => Some(CppParameterType::Ellipsis),
16135 CppParameterSlot::Declared { parameter, .. } => Some(
16136 cpp_parameter_type_identity(parameter, source, &lexical_scope)
16137 .map_or(CppParameterType::Unstructured, CppParameterType::Structured),
16138 ),
16139 CppParameterSlot::OptionalVoid => None,
16140 })
16141 .collect()
16142}
16143
16144pub fn cpp_declaration_type_identity<'tree>(
16151 declaration: Node<'tree>,
16152 declarator: Node<'tree>,
16153 source: &str,
16154 ancestry: &ParentIndex<'tree>,
16155) -> Option<StructuredTypeIdentity> {
16156 let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
16157 cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
16158}
16159
16160fn cpp_parameter_type_identity(
16161 parameter: Node<'_>,
16162 source: &str,
16163 lexical_scope: &[String],
16164) -> Option<StructuredTypeIdentity> {
16165 cpp_declaration_type_identity_in_scope(
16166 parameter,
16167 cpp_parameter_declarator(parameter),
16168 source,
16169 lexical_scope,
16170 )
16171}
16172
16173fn cpp_declaration_type_identity_in_scope(
16174 declaration: Node<'_>,
16175 declarator: Option<Node<'_>>,
16176 source: &str,
16177 lexical_scope: &[String],
16178) -> Option<StructuredTypeIdentity> {
16179 let type_node = declaration.child_by_field_name("type")?;
16180 let identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
16181 if let Some(declarator) = declarator {
16182 return cpp_apply_structured_type_wrappers(
16183 identity,
16184 cpp_structured_declarator_wrappers(declarator)?,
16185 );
16186 }
16187 Some(identity)
16188}
16189
16190pub fn cpp_comparable_parameter_shapes<'tree>(
16197 function_declarator: Node<'tree>,
16198 source: &str,
16199 ancestry: &ParentIndex<'tree>,
16200) -> Vec<CppComparableSlot> {
16201 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16202 return Vec::new();
16203 };
16204 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16205 cpp_callable_parameter_slots(parameters_node, source)
16206 .into_iter()
16207 .filter_map(|slot| match slot {
16208 CppParameterSlot::Ellipsis => Some(CppComparableSlot::Ellipsis),
16209 CppParameterSlot::Declared { parameter, .. } => Some(
16210 cpp_comparable_parameter(parameter, source, &lexical_scope)
16211 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape),
16212 ),
16213 CppParameterSlot::OptionalVoid => None,
16214 })
16215 .collect()
16216}
16217
16218fn cpp_comparable_parameter(
16219 parameter: Node<'_>,
16220 source: &str,
16221 lexical_scope: &[String],
16222) -> Option<CppComparableParameter> {
16223 let type_node = parameter.child_by_field_name("type")?;
16224 let levels = match cpp_parameter_declarator(parameter) {
16225 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16226 None => Vec::new(),
16227 };
16228 let mut shape = cpp_comparable_type_shape(
16229 type_node,
16230 cpp_cv_qualifiers(parameter, source),
16231 levels,
16232 source,
16233 lexical_scope,
16234 )?;
16235 shape.adjust_parameter_top_level();
16236 Some(shape)
16237}
16238
16239fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
16250 let mut qualifiers = CppCvQualifiers::default();
16251 let mut cursor = node.walk();
16252 for child in node.named_children(&mut cursor) {
16253 if child.kind() != "type_qualifier" {
16254 continue;
16255 }
16256 match node_text(child, source) {
16257 "const" => qualifiers.konst = true,
16258 "volatile" => qualifiers.volatil = true,
16259 _ => {}
16260 }
16261 }
16262 qualifiers
16263}
16264
16265#[derive(Clone, Copy, Default)]
16266struct CppCvQualifiers {
16267 konst: bool,
16268 volatil: bool,
16269}
16270
16271impl CppCvQualifiers {
16272 fn union(self, other: Self) -> Self {
16273 Self {
16274 konst: self.konst || other.konst,
16275 volatil: self.volatil || other.volatil,
16276 }
16277 }
16278}
16279
16280#[derive(Clone, Copy)]
16282enum CppComparableLevel {
16283 Pointer { konst: bool, volatil: bool },
16284 Reference,
16285 Array,
16286}
16287
16288fn cpp_comparable_declarator_levels(
16301 declarator: Node<'_>,
16302 source: &str,
16303) -> Option<Vec<CppComparableLevel>> {
16304 let mut levels = Vec::new();
16305 let mut current = declarator;
16306 loop {
16307 match current.kind() {
16308 "pointer_declarator" | "abstract_pointer_declarator" => {
16309 let qualifiers = cpp_cv_qualifiers(current, source);
16310 levels.push(CppComparableLevel::Pointer {
16311 konst: qualifiers.konst,
16312 volatil: qualifiers.volatil,
16313 });
16314 }
16315 "reference_declarator" | "abstract_reference_declarator" => {
16316 levels.push(CppComparableLevel::Reference);
16317 }
16318 "array_declarator" | "abstract_array_declarator" => {
16319 levels.push(CppComparableLevel::Array);
16320 }
16321 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
16322 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
16323 _ => return None,
16324 }
16325 let Some(next) = cpp_nested_declarator(current) else {
16326 return Some(levels);
16327 };
16328 current = next;
16329 }
16330}
16331
16332fn cpp_comparable_type_shape(
16339 type_node: Node<'_>,
16340 qualifiers: CppCvQualifiers,
16341 levels: Vec<CppComparableLevel>,
16342 source: &str,
16343 lexical_scope: &[String],
16344) -> Option<CppComparableParameter> {
16345 enum Work<'tree> {
16346 Visit {
16347 node: Node<'tree>,
16348 qualifiers: CppCvQualifiers,
16349 },
16350 ApplyLevels(Vec<CppComparableLevel>),
16351 BuildGeneric {
16352 argument_count: usize,
16353 },
16354 }
16355
16356 let mut nodes: Vec<CppComparableNode> = Vec::new();
16357 let mut values: Vec<usize> = Vec::new();
16358 let mut work = vec![
16359 Work::ApplyLevels(levels),
16360 Work::Visit {
16361 node: type_node,
16362 qualifiers,
16363 },
16364 ];
16365 while let Some(next) = work.pop() {
16366 match next {
16367 Work::Visit { node, qualifiers } => match node.kind() {
16368 "type_descriptor" => {
16369 let inner_type = node
16370 .child_by_field_name("type")
16371 .or_else(|| node.named_child(0))?;
16372 let mut cursor = node.walk();
16373 let declarator = node.child_by_field_name("declarator").or_else(|| {
16374 node.named_children(&mut cursor).find(|child| {
16375 child.id() != inner_type.id() && child.kind() != "type_qualifier"
16376 })
16377 });
16378 let levels = match declarator {
16379 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16380 None => Vec::new(),
16381 };
16382 work.push(Work::ApplyLevels(levels));
16383 work.push(Work::Visit {
16384 node: inner_type,
16385 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
16386 });
16387 }
16388 "sized_type_specifier" => {
16389 let name = StructuredTypeName::new(
16394 vec![normalize_cpp_whitespace(node_text(node, source))],
16395 lexical_scope.to_vec(),
16396 false,
16397 )?;
16398 values.push(cpp_push_comparable_node(
16399 &mut nodes,
16400 CppComparableNode::Named {
16401 name,
16402 primitive: true,
16403 konst: qualifiers.konst,
16404 volatil: qualifiers.volatil,
16405 },
16406 ));
16407 }
16408 "qualified_identifier"
16409 | "scoped_identifier"
16410 | "scoped_type_identifier"
16411 | "type_identifier"
16412 | "field_identifier"
16413 | "identifier"
16414 | "namespace_identifier"
16415 | "primitive_type"
16416 | "template_type" => {
16417 let name = cpp_structured_named_type(node, source, lexical_scope)?;
16418 values.push(cpp_push_comparable_node(
16419 &mut nodes,
16420 CppComparableNode::Named {
16421 name,
16422 primitive: node.kind() == "primitive_type",
16423 konst: qualifiers.konst,
16424 volatil: qualifiers.volatil,
16425 },
16426 ));
16427 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
16428 let mut cursor = arguments_node.walk();
16429 let arguments = arguments_node
16430 .named_children(&mut cursor)
16431 .filter(|child| !child.is_extra() && child.kind() != "comment")
16432 .collect::<Vec<_>>();
16433 work.push(Work::BuildGeneric {
16434 argument_count: arguments.len(),
16435 });
16436 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
16437 node: argument,
16438 qualifiers: CppCvQualifiers::default(),
16439 }));
16440 }
16441 }
16442 _ => {
16443 let inner = node.child_by_field_name("type").or_else(|| {
16444 (node.named_child_count() == 1)
16445 .then(|| node.named_child(0))
16446 .flatten()
16447 })?;
16448 work.push(Work::Visit {
16449 node: inner,
16450 qualifiers,
16451 });
16452 }
16453 },
16454 Work::ApplyLevels(levels) => {
16455 let mut root = values.pop()?;
16456 for level in levels {
16457 let node = match level {
16458 CppComparableLevel::Pointer { konst, volatil } => {
16459 CppComparableNode::Pointer {
16460 inner: root,
16461 konst,
16462 volatil,
16463 }
16464 }
16465 CppComparableLevel::Reference => {
16466 CppComparableNode::Reference { inner: root }
16467 }
16468 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
16469 };
16470 root = cpp_push_comparable_node(&mut nodes, node);
16471 }
16472 values.push(root);
16473 }
16474 Work::BuildGeneric { argument_count } => {
16475 let value_count = argument_count.checked_add(1)?;
16476 let start = values.len().checked_sub(value_count)?;
16477 let mut built = values.split_off(start);
16478 let base = built.remove(0);
16479 values.push(cpp_push_comparable_node(
16480 &mut nodes,
16481 CppComparableNode::Generic {
16482 base,
16483 arguments: built,
16484 },
16485 ));
16486 }
16487 }
16488 }
16489 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
16490 debug_assert_eq!(
16491 root,
16492 nodes.len().saturating_sub(1),
16493 "comparable nodes are appended in post-order, so the root is the last one"
16494 );
16495 Some(CppComparableParameter::new(nodes, root))
16496}
16497
16498fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
16499 nodes.push(node);
16500 nodes.len() - 1
16501}
16502
16503fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
16509 let mut current = node;
16510 loop {
16511 match current.kind() {
16512 "template_type" => return current.child_by_field_name("arguments"),
16513 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16514 current = current.child_by_field_name("name")?;
16515 }
16516 _ => return None,
16517 }
16518 }
16519}
16520
16521pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
16527 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
16528 loop {
16529 if matches!(
16530 current.kind(),
16531 "declaration" | "field_declaration" | "function_definition"
16532 ) && let Some(declarator) = current
16533 .child_by_field_name("declarator")
16534 .and_then(extract_function_declarator)
16535 {
16536 return Some(declarator);
16537 }
16538 current = current.parent()?;
16539 }
16540}
16541
16542fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
16543 let mut labels = Vec::new();
16544 let mut cursor = parameters_node.walk();
16545 for child in parameters_node.children(&mut cursor) {
16546 match child.kind() {
16547 "parameter_declaration" | "optional_parameter_declaration" => {
16548 if let Some(name_node) = child
16549 .child_by_field_name("declarator")
16550 .and_then(cpp_declarator_label_node)
16551 {
16552 labels.push(name_node);
16553 } else {
16554 labels.push(child);
16555 }
16556 }
16557 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16558 labels.push(child);
16559 }
16560 _ => {}
16561 }
16562 }
16563 labels
16564}
16565
16566fn cpp_signature_search_start<'tree>(
16567 signature: &str,
16568 function_declarator: Node<'tree>,
16569 source: &str,
16570 ancestry: &ParentIndex<'tree>,
16571) -> usize {
16572 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
16573 return 0;
16574 };
16575 let raw = node_text(enclosing, source);
16576 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
16577 let offset = function_declarator
16578 .start_byte()
16579 .saturating_sub(enclosing.start_byte())
16580 .saturating_sub(leading_trim_bytes);
16581 offset.min(signature.len())
16582}
16583
16584fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
16585 match node.kind() {
16586 "identifier" | "field_identifier" => Some(node),
16587 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
16588 .child_by_field_name("declarator")
16589 .or_else(|| last_named_child(node))
16590 .and_then(cpp_declarator_label_node),
16591 "array_declarator" => node
16592 .child_by_field_name("declarator")
16593 .and_then(cpp_declarator_label_node),
16594 "function_declarator" => node
16595 .child_by_field_name("declarator")
16596 .or_else(|| node.child_by_field_name("name"))
16597 .or_else(|| last_named_child(node))
16598 .and_then(cpp_declarator_label_node),
16599 _ => None,
16600 }
16601}
16602
16603fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
16604 let base_type = parameter
16605 .child_by_field_name("type")
16606 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
16607 .unwrap_or_default();
16608 let declarator = cpp_parameter_declarator(parameter);
16609 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
16616 let mut cursor = parameter.walk();
16617 let qualifiers = parameter
16618 .named_children(&mut cursor)
16619 .filter(|child| child.kind() == "type_qualifier")
16620 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
16621 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
16622 .collect::<Vec<_>>()
16623 .join(" ");
16624 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
16625 (true, _) => base_type,
16626 (_, true) => qualifiers,
16627 (false, false) => format!("{qualifiers} {base_type}"),
16628 };
16629 let declarator_suffix = declarator
16630 .map(|node| cpp_declarator_suffix_without_name(node, source))
16631 .unwrap_or_default();
16632
16633 let combined = if type_text.is_empty() {
16634 declarator_suffix
16635 } else if declarator_suffix.is_empty() {
16636 type_text
16637 } else {
16638 format!("{type_text} {declarator_suffix}")
16639 };
16640 normalize_cpp_type_text(&combined)
16641}
16642
16643fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
16644 parameter.child_by_field_name("declarator").or_else(|| {
16645 cpp_first_abstract_declarator_child(parameter)
16651 })
16652}
16653
16654pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
16657 let mut current = Some(declarator);
16658 while let Some(node) = current {
16659 if matches!(
16660 node.kind(),
16661 "pointer_declarator"
16662 | "abstract_pointer_declarator"
16663 | "reference_declarator"
16664 | "abstract_reference_declarator"
16665 | "array_declarator"
16666 | "abstract_array_declarator"
16667 | "function_declarator"
16668 | "abstract_function_declarator"
16669 ) {
16670 return true;
16671 }
16672 current = cpp_nested_declarator(node);
16673 }
16674 false
16675}
16676
16677fn is_cpp_abstract_declarator(kind: &str) -> bool {
16678 matches!(
16679 kind,
16680 "abstract_pointer_declarator"
16681 | "abstract_reference_declarator"
16682 | "abstract_array_declarator"
16683 | "abstract_function_declarator"
16684 | "abstract_parenthesized_declarator"
16685 )
16686}
16687
16688fn cpp_first_abstract_declarator_child(node: Node<'_>) -> Option<Node<'_>> {
16689 let mut cursor = node.walk();
16690 node.named_children(&mut cursor)
16691 .find(|child| is_cpp_abstract_declarator(child.kind()))
16692}
16693
16694fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
16695 node.child_by_field_name("declarator").or_else(|| {
16696 if is_cpp_abstract_declarator(node.kind()) {
16697 cpp_first_abstract_declarator_child(node)
16698 } else {
16699 last_named_child(node)
16703 }
16704 })
16705}
16706
16707fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
16708 match node.kind() {
16709 "identifier" | "field_identifier" => String::new(),
16710 "pointer_declarator" | "abstract_pointer_declarator" => {
16711 let inner = cpp_nested_declarator(node)
16712 .map(|child| cpp_declarator_suffix_without_name(child, source))
16713 .unwrap_or_default();
16714 format!("*{inner}")
16715 }
16716 "reference_declarator" | "abstract_reference_declarator" => {
16717 let inner = cpp_nested_declarator(node)
16718 .map(|child| cpp_declarator_suffix_without_name(child, source))
16719 .unwrap_or_default();
16720 let reference = node
16721 .children(&mut node.walk())
16722 .find(|child| matches!(child.kind(), "&" | "&&"))
16723 .map(|child| node_text(child, source))
16724 .unwrap_or("&");
16725 format!("{reference}{inner}")
16726 }
16727 "array_declarator" | "abstract_array_declarator" => {
16728 let inner = cpp_nested_declarator(node)
16729 .map(|child| cpp_declarator_suffix_without_name(child, source))
16730 .unwrap_or_default();
16731 let size = node
16732 .child_by_field_name("size")
16733 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
16734 .unwrap_or_default();
16735 format!("{inner}[{size}]")
16736 }
16737 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
16738 let inner = cpp_nested_declarator(node);
16739 inner
16740 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
16741 .unwrap_or_default()
16742 }
16743 "function_declarator" | "abstract_function_declarator" => {
16744 let inner = cpp_nested_declarator(node)
16745 .map(|child| cpp_declarator_suffix_without_name(child, source))
16746 .unwrap_or_default();
16747 let params = node
16748 .child_by_field_name("parameters")
16749 .map(|child| cpp_parameter_signature(child, source))
16750 .unwrap_or_else(|| "()".to_string());
16751 format!("{inner}{params}")
16752 }
16753 _ => {
16754 let text = normalize_cpp_whitespace(node_text(node, source));
16755 let name = extract_declarator_name(node, source);
16756 if name.is_empty() {
16757 text
16758 } else {
16759 text.replace(&name, "").trim().to_string()
16760 }
16761 }
16762 }
16763}
16764
16765fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
16766 collapse_cpp_whitespace(
16767 suffix
16768 .trim()
16769 .trim_start_matches("->")
16770 .trim_start_matches('{')
16771 .trim_end_matches(';'),
16772 )
16773}
16774
16775pub fn normalize_cpp_whitespace(value: &str) -> String {
16776 collapse_cpp_whitespace(value)
16777}
16778
16779fn normalize_cpp_type_text(value: &str) -> String {
16780 collapse_cpp_whitespace(value)
16781 .replace(", ", ",")
16782 .replace(" <", "<")
16783 .replace("< ", "<")
16784 .replace(" >", ">")
16785}
16786
16787fn collapse_cpp_whitespace(value: &str) -> String {
16788 let mut result = String::new();
16789 let mut prev_space = false;
16790 for ch in value.chars() {
16791 if ch.is_whitespace() {
16792 if !prev_space {
16793 result.push(' ');
16794 }
16795 prev_space = true;
16796 } else {
16797 result.push(ch);
16798 prev_space = false;
16799 }
16800 }
16801 result.trim().to_string()
16802}
16803
16804fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
16805 child_by_field_or_named_child_of_kinds(
16806 node,
16807 "body",
16808 &[
16809 "declaration_list",
16810 "field_declaration_list",
16811 "enumerator_list",
16812 ],
16813 )
16814}
16815
16816fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
16827 if !matches!(
16828 node.kind(),
16829 "class_specifier" | "struct_specifier" | "union_specifier"
16830 ) {
16831 return None;
16832 }
16833 let body = cpp_body_node(node)?;
16834 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
16835 return None;
16836 }
16837 let open = body.child(0)?;
16838 let close = body.child(body.child_count().checked_sub(1)?)?;
16839 if open.kind() != "{"
16840 || open.is_missing()
16841 || close.kind() != "}"
16842 || close.is_missing()
16843 || close.end_byte() != body.end_byte()
16844 || body.end_byte() > node.end_byte()
16845 || node
16846 .parent()
16847 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
16848 {
16849 return None;
16850 }
16851 Some(close)
16852}
16853
16854fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
16855 if node.kind() == "namespace_definition" {
16856 return true;
16857 }
16858 let mut cursor = node.walk();
16859 node.named_children(&mut cursor)
16860 .any(cpp_contains_namespace_definition)
16861}
16862
16863struct CppNestedNamespaceSentinel<'tree> {
16864 function: Node<'tree>,
16865 body: Node<'tree>,
16866 namespace_components: Vec<String>,
16867}
16868
16869#[derive(Debug, Clone)]
16879pub struct CppSentinelRecoveredOwner {
16880 pub range: Range,
16881 pub owner_name_start_byte: usize,
16885 pub namespace_component_count: usize,
16889 pub scope_components: Vec<String>,
16890}
16891
16892#[derive(Debug, Clone)]
16893pub struct CppSentinelRecoveredClass {
16894 pub namespace_range: Range,
16895 pub namespace_scope_components: Vec<String>,
16896 pub class_range: Range,
16897 pub scope_components: Vec<String>,
16899 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
16903}
16904
16905pub fn cpp_sentinel_recovered_scope_for_node(
16911 node: Node<'_>,
16912 source: &str,
16913 recovered_classes: &[CppSentinelRecoveredClass],
16914) -> Option<Vec<String>> {
16915 let contains =
16916 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
16917 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
16918 for recovered in recovered_classes {
16919 for owner in recovered
16920 .owner_ranges
16921 .iter()
16922 .filter(|owner| contains(owner.range))
16923 {
16924 let replace = best_owner.is_none_or(|existing| {
16925 owner.range.end_byte.saturating_sub(owner.range.start_byte)
16926 < existing
16927 .range
16928 .end_byte
16929 .saturating_sub(existing.range.start_byte)
16930 });
16931 if replace {
16932 best_owner = Some(owner);
16933 }
16934 }
16935 }
16936 if let Some(owner) = best_owner {
16937 let mut scope = owner.scope_components.clone();
16938 if node.start_byte() < owner.owner_name_start_byte {
16939 scope.truncate(owner.namespace_component_count);
16940 }
16941 return Some(scope);
16942 }
16943
16944 let class = recovered_classes
16945 .iter()
16946 .filter(|recovered| contains(recovered.class_range))
16947 .min_by_key(|recovered| {
16948 recovered
16949 .class_range
16950 .end_byte
16951 .saturating_sub(recovered.class_range.start_byte)
16952 });
16953 let class_scope = class.is_some();
16954 let mut scope = if let Some(class) = class {
16955 class.scope_components.clone()
16956 } else {
16957 let namespace = recovered_classes
16958 .iter()
16959 .filter(|recovered| contains(recovered.namespace_range))
16960 .min_by_key(|recovered| {
16961 recovered
16962 .namespace_range
16963 .end_byte
16964 .saturating_sub(recovered.namespace_range.start_byte)
16965 })?;
16966 let mut scope = namespace.namespace_scope_components.clone();
16967 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
16968 let common_prefix = scope
16969 .iter()
16970 .zip(&parser_namespace)
16971 .take_while(|(recovered, parser)| recovered == parser)
16972 .count();
16973 scope.extend(parser_namespace.into_iter().skip(common_prefix));
16974 scope
16975 };
16976 if class_scope {
16977 let mut ancestor_components = Vec::new();
16978 let mut ancestor = node.parent();
16979 while let Some(current) = ancestor {
16980 if matches!(
16981 current.kind(),
16982 "class_specifier" | "struct_specifier" | "union_specifier"
16983 ) && let Some(name) = current.child_by_field_name("name")
16984 && let Some(name_components) = cpp_name_components(name, source)
16985 {
16986 ancestor_components.push(
16987 name_components
16988 .into_iter()
16989 .map(|component| component.name)
16990 .collect::<Vec<_>>(),
16991 );
16992 }
16993 ancestor = current.parent();
16994 }
16995 ancestor_components.reverse();
16996 let base_len = scope.len();
16997 for component in ancestor_components.into_iter().flatten() {
16998 if scope.len() >= base_len && scope.last() == Some(&component) {
16999 continue;
17000 }
17001 scope.push(component);
17002 }
17003 }
17004 Some(scope)
17005}
17006
17007struct CppSentinelFragmentedClassTail<'tree> {
17008 class_node: Node<'tree>,
17009 template_node: Option<Node<'tree>>,
17010 name: String,
17011 raw_supertypes: Option<Vec<String>>,
17012 fragmented: FragmentedExportBody,
17013 consumed_start: usize,
17014}
17015
17016struct CppSentinelFragmentedClassErrorPrefix<'tree> {
17017 name: String,
17018 open: Node<'tree>,
17019 raw_supertypes: Option<Vec<String>>,
17020}
17021
17022struct CppSentinelDirectBodyClassRegion {
17023 namespace_components: Vec<String>,
17024 class_start: usize,
17025 class_start_line: usize,
17026 class_close_end: usize,
17027 class_close_line: usize,
17028 name: String,
17029}
17030
17031fn cpp_sentinel_body_class_candidate<'tree>(
17032 child: Node<'tree>,
17033) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17034 if matches!(
17035 child.kind(),
17036 "class_specifier" | "struct_specifier" | "union_specifier"
17037 ) {
17038 return Some((child, None));
17039 }
17040 if child.kind() != "template_declaration" {
17041 if child.kind() == "declaration" {
17042 return Some((first_class_like_child(child)?, None));
17043 }
17044 return None;
17045 }
17046 let mut cursor = child.walk();
17047 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
17048 if matches!(
17049 candidate.kind(),
17050 "class_specifier" | "struct_specifier" | "union_specifier"
17051 ) {
17052 Some(candidate)
17053 } else if candidate.kind() == "declaration" {
17054 first_class_like_child(candidate)
17055 } else {
17056 None
17057 }
17058 })?;
17059 Some((class_node, Some(child)))
17060}
17061
17062fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
17068 node: Node<'tree>,
17069 source: &str,
17070) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
17071 let name = malformed_class_error_owner_name(node, source)?;
17072 let mut cursor = node.walk();
17073 let children = node.children(&mut cursor).collect::<Vec<_>>();
17074 let keyword = children.first()?;
17075 let open_index = children.iter().position(|child| child.kind() == "{")?;
17076 if children[open_index + 1..]
17077 .iter()
17078 .any(|child| child.kind() == "}")
17079 {
17080 return None;
17081 }
17082 let raw_supertypes =
17083 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
17084 Some(CppSentinelFragmentedClassErrorPrefix {
17085 name,
17086 open: children[open_index],
17087 raw_supertypes,
17088 })
17089}
17090
17091fn cpp_sentinel_direct_body_class_candidate<'tree>(
17092 child: Node<'tree>,
17093) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17094 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
17095 return Some(candidate);
17096 }
17097 if child.kind() != "template_declaration" {
17098 return None;
17099 }
17100 let mut cursor = child.walk();
17101 let wrapper = child
17102 .named_children(&mut cursor)
17103 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
17104 Some((first_class_like_child(wrapper)?, Some(child)))
17105}
17106
17107fn cpp_sentinel_direct_namespace_components(
17108 function: Node<'_>,
17109 body: Node<'_>,
17110 source: &str,
17111) -> Option<Vec<String>> {
17112 let mut cursor = function.walk();
17113 let children = function
17114 .named_children(&mut cursor)
17115 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
17116 .collect::<Vec<_>>();
17117 let sentinel_index = children.iter().rposition(|child| {
17118 direct_identifier_name(*child, source)
17119 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
17120 })?;
17121 let mut identifiers = Vec::new();
17122 let mut stack = children[sentinel_index + 1..]
17123 .iter()
17124 .rev()
17125 .copied()
17126 .collect::<Vec<_>>();
17127 while let Some(current) = stack.pop() {
17128 if let Some(name) = direct_identifier_name(current, source) {
17129 identifiers.push(name);
17130 continue;
17131 }
17132 let mut cursor = current.walk();
17133 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
17134 stack.extend(children.into_iter().rev());
17135 }
17136 let [keyword, namespace] = identifiers.as_slice() else {
17137 return None;
17138 };
17139 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
17140 .then(|| vec![namespace.clone()])
17141}
17142
17143fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
17144 let mut sibling = class_semicolon.next_named_sibling();
17145 let namespace_close = loop {
17146 let Some(current) = sibling else {
17147 return false;
17148 };
17149 sibling = current.next_named_sibling();
17150 if current.kind() != "comment" {
17151 break current;
17152 }
17153 };
17154 if !cpp_is_stray_close_brace(namespace_close, source) {
17155 return false;
17156 }
17157 loop {
17158 let Some(current) = sibling else {
17159 return false;
17160 };
17161 sibling = current.next_named_sibling();
17162 if current.kind() == "comment" {
17163 continue;
17164 }
17165 return direct_identifier_name(current, source)
17166 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
17167 }
17168}
17169
17170fn cpp_sentinel_macro_body_class_region<'tree>(
17171 node: Node<'tree>,
17172 source: &str,
17173 ancestry: &ParentIndex<'tree>,
17174) -> Option<CppSentinelDirectBodyClassRegion> {
17175 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
17176 return None;
17177 };
17178 if node.kind() != "function_definition" || !node.has_error() {
17179 return None;
17180 }
17181 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
17182 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
17183 let mut cursor = body.walk();
17184 let candidates = body
17185 .named_children(&mut cursor)
17186 .filter_map(cpp_sentinel_direct_body_class_candidate)
17187 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
17188 .collect::<Vec<_>>();
17189 let [(class_node, template_node)] = candidates.as_slice() else {
17190 return None;
17191 };
17192 let original_body = cpp_body_node(*class_node)?;
17193 let name = class_like_name(*class_node, source, ancestry)?;
17194 if name.is_empty() || cpp_export_macro_token(&name) {
17195 return None;
17196 }
17197
17198 let mut sibling = node.next_named_sibling();
17199 let (class_close_start, class_close_end, class_close_line) = loop {
17200 let current = sibling?;
17201 let next = current.next_named_sibling();
17202 if cpp_is_stray_close_brace(current, source)
17203 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
17204 {
17205 let semicolon = next.expect("checked above");
17206 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
17207 return None;
17208 }
17209 break (
17210 current.start_byte(),
17211 semicolon.end_byte(),
17212 semicolon.end_position().row + 1,
17213 );
17214 }
17215 sibling = next;
17216 };
17217 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
17218 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
17219 let root = tree.root_node();
17220 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
17221 let reparsed_ancestry = ParentIndex::new(root);
17224 let reparsed =
17225 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
17226 if reparsed.name != name
17227 || reparsed.declaration_node.start_byte() != class_node.start_byte()
17228 || reparsed.body.start_byte() != original_body.start_byte()
17229 || class_close_start <= reparsed.body.end_byte()
17230 || class_close_end <= class_node.end_byte()
17231 {
17232 return None;
17233 }
17234 Some(CppSentinelDirectBodyClassRegion {
17235 namespace_components,
17236 class_start: reparse_start,
17237 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
17238 node.start_position().row + 1
17239 }),
17240 class_close_end,
17241 class_close_line,
17242 name,
17243 })
17244}
17245
17246fn cpp_nested_namespace_sentinel<'tree>(
17258 node: Node<'tree>,
17259 source: &str,
17260 ancestry: &ParentIndex<'tree>,
17261) -> Option<CppNestedNamespaceSentinel<'tree>> {
17262 let (function, mut namespace_components) = if node.kind() == "ERROR" {
17268 let mut cursor = node.walk();
17269 let functions = node
17270 .named_children(&mut cursor)
17271 .filter(|child| child.kind() == "function_definition")
17272 .collect::<Vec<_>>();
17273 let [function] = functions.as_slice() else {
17274 return None;
17275 };
17276 let mut cursor = node.walk();
17277 let children = node.children(&mut cursor).collect::<Vec<_>>();
17278 let function_index = children
17279 .iter()
17280 .position(|child| same_node(*child, *function))?;
17281 let [outer_keyword, outer_name, outer_open] =
17282 children.get(function_index.checked_sub(3)?..function_index)?
17283 else {
17284 return None;
17285 };
17286 if outer_keyword.kind() != "namespace"
17287 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
17288 || outer_open.kind() != "{"
17289 {
17290 return None;
17291 }
17292 (
17293 *function,
17294 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
17295 )
17296 } else if node.kind() == "function_definition" {
17297 let declaration_list = node.parent()?;
17298 let namespace = declaration_list.parent()?;
17299 if declaration_list.kind() != "declaration_list"
17300 || namespace.kind() != "namespace_definition"
17301 || namespace.child_by_field_name("body") != Some(declaration_list)
17302 {
17303 return None;
17304 }
17305 (node, Vec::new())
17306 } else {
17307 return None;
17308 };
17309
17310 let mut cursor = function.walk();
17311 let named = function
17312 .named_children(&mut cursor)
17313 .filter(|child| child.kind() != "comment")
17314 .collect::<Vec<_>>();
17315 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
17316 return None;
17317 };
17318 let sentinel = if first_type.kind() == "type_identifier" {
17322 normalize_cpp_whitespace(node_text(*first_type, source))
17323 } else {
17324 let identifier = macro_modifier_identifier(*first_type)?;
17325 normalize_cpp_whitespace(node_text(identifier, source))
17326 };
17327 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17328 return None;
17329 }
17330 let inner_keyword = if inner_error.kind() == "ERROR" && inner_error.named_child_count() == 1 {
17334 inner_error.named_child(0)?
17335 } else if inner_error.kind() == "type_identifier" {
17336 *inner_error
17337 } else {
17338 return None;
17339 };
17340 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
17341 return None;
17342 }
17343 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
17344 return None;
17345 }
17346 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
17347 if inner_name.is_empty() || body.kind() != "compound_statement" {
17348 return None;
17349 }
17350 namespace_components.push(inner_name);
17351
17352 let mut cursor = body.walk();
17353 let has_complete_class = body.named_children(&mut cursor).any(|child| {
17354 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17355 cpp_body_node(class_node).is_some()
17356 && class_like_name(class_node, source, ancestry)
17357 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
17358 })
17359 });
17360 if !has_complete_class
17361 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
17362 {
17363 return None;
17364 }
17365
17366 Some(CppNestedNamespaceSentinel {
17367 function,
17368 body: *body,
17369 namespace_components,
17370 })
17371}
17372
17373fn cpp_root_namespace_sentinel<'tree>(
17382 node: Node<'tree>,
17383 source: &str,
17384 ancestry: &ParentIndex<'tree>,
17385) -> Option<CppNestedNamespaceSentinel<'tree>> {
17386 if node.kind() != "function_definition" || node.parent()?.kind() != "translation_unit" {
17387 return None;
17388 }
17389 let body = node.child_by_field_name("body")?;
17390 let declarator = node.child_by_field_name("declarator")?;
17391 let mut cursor = node.walk();
17392 let named = node
17393 .named_children(&mut cursor)
17394 .filter(|child| child.kind() != "comment")
17395 .collect::<Vec<_>>();
17396 if body.kind() != "compound_statement" || !same_node(*named.last()?, body) {
17397 return None;
17398 }
17399 let (sentinel, strip_namespace_keyword) = match named.as_slice() {
17405 [first, second, _] => {
17406 let first_type = node.child_by_field_name("type")?;
17407 if !same_node(*first, first_type)
17408 || first_type.kind() != "type_identifier"
17409 || !same_node(*second, declarator)
17410 || !node.has_error()
17411 {
17412 return None;
17413 }
17414 (
17415 normalize_cpp_whitespace(node_text(first_type, source)),
17416 true,
17417 )
17418 }
17419 [first, second, third, _] => {
17420 let type_node = node.child_by_field_name("type")?;
17421 let identifier = macro_modifier_identifier(*first)?;
17422 if !same_node(*second, type_node)
17423 || type_node.kind() != "type_identifier"
17424 || normalize_cpp_whitespace(node_text(type_node, source)) != "namespace"
17425 || !same_node(*third, declarator)
17426 {
17427 return None;
17428 }
17429 (
17430 normalize_cpp_whitespace(node_text(identifier, source)),
17431 false,
17432 )
17433 }
17434 _ => return None,
17435 };
17436 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17437 return None;
17438 }
17439 if !matches!(
17440 declarator.kind(),
17441 "qualified_identifier" | "identifier" | "namespace_identifier"
17442 ) {
17443 return None;
17444 }
17445 let mut declarator_components = Vec::new();
17446 let mut valid_components = true;
17447 walk_named_tree_preorder(declarator, true, |component| {
17448 if !matches!(
17449 component.kind(),
17450 "identifier" | "namespace_identifier" | "type_identifier"
17451 ) {
17452 return WalkControl::Continue;
17453 }
17454 let Some(component) = canonical_cpp_qualified_component(component, source) else {
17455 valid_components = false;
17456 return WalkControl::Break;
17457 };
17458 declarator_components.push(component.name);
17459 WalkControl::SkipChildren
17460 });
17461 if strip_namespace_keyword {
17462 if !valid_components
17463 || declarator_components.first().map(String::as_str) != Some("namespace")
17464 {
17465 return None;
17466 }
17467 declarator_components.remove(0);
17468 }
17469 let namespace_components = declarator_components;
17470 if !valid_components
17471 || namespace_components.is_empty()
17472 || namespace_components
17473 .iter()
17474 .any(|component| component.is_empty() || cpp_export_macro_token(component))
17475 {
17476 return None;
17477 }
17478
17479 let mut cursor = body.walk();
17480 let has_complete_class = body.named_children(&mut cursor).any(|child| {
17481 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17482 cpp_body_node(class_node).is_some()
17483 && class_like_name(class_node, source, ancestry)
17484 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
17485 })
17486 });
17487 if !has_complete_class
17488 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
17489 {
17490 return None;
17491 }
17492
17493 Some(CppNestedNamespaceSentinel {
17494 function: node,
17495 body,
17496 namespace_components,
17497 })
17498}
17499
17500fn cpp_sentinel_fragmented_class_tail<'tree>(
17509 function: Node<'tree>,
17510 body: Node<'tree>,
17511 source: &str,
17512 ancestry: &ParentIndex<'tree>,
17513) -> Option<CppSentinelFragmentedClassTail<'tree>> {
17514 let mut cursor = body.walk();
17515 let candidates = body
17516 .named_children(&mut cursor)
17517 .filter_map(|child| {
17518 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
17519 let class_body = cpp_body_node(class_node)?;
17520 if !class_node.has_error() {
17521 return None;
17522 }
17523 let name = class_like_name(class_node, source, ancestry)?;
17524 let raw_supertypes =
17525 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
17526 .then(|| extract_cpp_supertypes(class_node, source));
17527 return Some((
17528 class_node,
17529 template_node,
17530 name,
17531 class_body,
17532 class_body.start_byte().checked_add(1)?,
17533 raw_supertypes,
17534 ));
17535 }
17536 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
17537 Some((
17538 child,
17539 None,
17540 prefix.name,
17541 prefix.open,
17542 prefix.open.end_byte(),
17543 prefix.raw_supertypes,
17544 ))
17545 })
17546 .collect::<Vec<_>>();
17547 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
17548 candidates.as_slice()
17549 else {
17550 return None;
17551 };
17552 if name.is_empty() || cpp_export_macro_token(name) {
17553 return None;
17554 }
17555
17556 let (close, semicolon) =
17557 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
17558
17559 let reparse_end = close.start_byte();
17560 if *reparse_start >= reparse_end {
17561 return None;
17562 }
17563 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
17564 if !cpp_reparsed_members_are_indexable_for_class(tree.root_node(), source, Some(name)) {
17565 return None;
17566 }
17567 let class_range = Range {
17568 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17569 end_byte: semicolon.end_byte(),
17570 start_line: template_node.map_or(class_node.start_position().row, |node| {
17571 node.start_position().row
17572 }) + 1,
17573 end_line: semicolon.end_position().row + 1,
17574 };
17575 Some(CppSentinelFragmentedClassTail {
17576 class_node: *class_node,
17577 template_node: *template_node,
17578 name: name.clone(),
17579 raw_supertypes: raw_supertypes.clone(),
17580 fragmented: FragmentedExportBody {
17581 reparse_start: *reparse_start,
17582 reparse_end,
17583 class_range,
17584 },
17585 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17586 })
17587}
17588
17589pub fn cpp_sentinel_recovered_classes(
17598 root: Node<'_>,
17599 source: &str,
17600) -> Vec<CppSentinelRecoveredClass> {
17601 if !root.has_error()
17607 && !subtree_contains(root, |node| {
17608 matches!(
17609 node.kind(),
17610 "macro_modifier" | "macro_modifier_declarator" | "macro_type_specifier"
17611 )
17612 })
17613 {
17614 return Vec::new();
17615 }
17616 let ancestry = ParentIndex::new(root);
17620 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
17621 let mut stack = vec![root];
17622 while let Some(current) = stack.pop() {
17623 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
17624 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
17625 {
17626 let namespace_components = cpp_sentinel_recovered_namespace_components(
17627 recovered.function,
17628 &recovered.namespace_components,
17629 source,
17630 );
17631 let fragmented = cpp_sentinel_fragmented_class_tail(
17632 recovered.function,
17633 recovered.body,
17634 source,
17635 &ancestry,
17636 );
17637 let mut class_candidates = Vec::new();
17638 let mut cursor = recovered.body.walk();
17639 for (class_node, template_node) in recovered
17640 .body
17641 .named_children(&mut cursor)
17642 .filter_map(cpp_sentinel_body_class_candidate)
17643 {
17644 let Some(name) = class_like_name(class_node, source, &ancestry) else {
17645 continue;
17646 };
17647 if name.is_empty() || cpp_export_macro_token(&name) {
17648 continue;
17649 }
17650 let is_fragmented = fragmented
17651 .as_ref()
17652 .is_some_and(|tail| same_node(tail.class_node, class_node));
17653 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
17654 continue;
17655 }
17656 let class_range = if is_fragmented {
17657 fragmented
17658 .as_ref()
17659 .map(|tail| tail.fragmented.class_range)
17660 .expect("fragmented class range is present when class matches")
17661 } else {
17662 cpp_declaration_range(template_node.unwrap_or(class_node))
17663 };
17664 class_candidates.push((class_range, name));
17665 }
17666 if let Some(fragmented) = fragmented
17667 .as_ref()
17668 .filter(|tail| tail.class_node.kind() == "ERROR")
17669 {
17670 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
17671 }
17672
17673 let mut owner_ranges =
17674 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
17675 cpp_sentinel_extend_unique_owner_ranges(
17676 &mut owner_ranges,
17677 cpp_sentinel_recovered_sibling_owner_ranges(
17678 recovered.function,
17679 &namespace_components,
17680 source,
17681 ),
17682 );
17683 for (class_range, name) in class_candidates {
17684 push_cpp_sentinel_recovered_class(
17685 &mut recovered_classes,
17686 cpp_declaration_range(recovered.body),
17687 &namespace_components,
17688 class_range,
17689 name,
17690 &owner_ranges,
17691 );
17692 }
17693
17694 if let Some(declaration_list) = recovered
17695 .function
17696 .parent()
17697 .filter(|parent| parent.kind() == "declaration_list")
17698 {
17699 let outer_namespace =
17700 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
17701 push_cpp_sentinel_sibling_classes(
17702 &mut recovered_classes,
17703 declaration_list,
17704 recovered.function,
17705 &outer_namespace,
17706 source,
17707 &ancestry,
17708 );
17709 }
17710 } else if let Some(region) =
17711 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
17712 {
17713 let namespace_components = cpp_sentinel_recovered_namespace_components(
17714 current,
17715 ®ion.namespace_components,
17716 source,
17717 );
17718 let owner_container = current
17719 .parent()
17720 .filter(|parent| parent.kind() == "declaration_list")
17721 .unwrap_or(current);
17722 let owner_ranges =
17723 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
17724 push_cpp_sentinel_recovered_class(
17725 &mut recovered_classes,
17726 cpp_declaration_range(owner_container),
17727 &namespace_components,
17728 Range {
17729 start_byte: region.class_start,
17730 end_byte: region.class_close_end,
17731 start_line: region.class_start_line,
17732 end_line: region.class_close_line,
17733 },
17734 region.name,
17735 &owner_ranges,
17736 );
17737 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
17738 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
17743 region;
17744 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
17745 continue;
17746 };
17747 let root = tree.root_node();
17748 let template_node = cpp_sentinel_reparsed_leading_template(root);
17749 let reparsed_ancestry = ParentIndex::new(root);
17751 let Some(reparsed_class) =
17752 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
17753 else {
17754 continue;
17755 };
17756 let class_node = reparsed_class.declaration_node;
17757 let name = reparsed_class.name;
17758 let namespace_components =
17759 cpp_sentinel_recovered_namespace_components(current, &[], source);
17760 let owner_container = current
17761 .parent()
17762 .filter(|parent| parent.kind() == "declaration_list")
17763 .unwrap_or(current);
17764 let mut owner_ranges =
17765 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
17766 cpp_sentinel_extend_unique_owner_ranges(
17767 &mut owner_ranges,
17768 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
17769 );
17770 push_cpp_sentinel_recovered_class(
17771 &mut recovered_classes,
17772 cpp_declaration_range(owner_container),
17773 &namespace_components,
17774 Range {
17775 start_byte: class_start,
17776 end_byte: close_end,
17777 start_line: class_node.start_position().row + 1,
17778 end_line: class_node.end_position().row + 1,
17779 },
17780 name,
17781 &owner_ranges,
17782 );
17783 if owner_container.kind() == "declaration_list" {
17784 push_cpp_sentinel_sibling_classes(
17785 &mut recovered_classes,
17786 owner_container,
17787 current,
17788 &namespace_components,
17789 source,
17790 &ancestry,
17791 );
17792 }
17793 }
17794
17795 let mut cursor = current.walk();
17796 stack.extend(current.named_children(&mut cursor));
17797 }
17798 let shadowed = recovered_classes
17804 .iter()
17805 .map(|candidate| {
17806 recovered_classes.iter().any(|container| {
17807 container.class_range.start_byte <= candidate.class_range.start_byte
17808 && container.class_range.end_byte >= candidate.class_range.end_byte
17809 && container.class_range != candidate.class_range
17810 && container.namespace_scope_components.len()
17811 > candidate.namespace_scope_components.len()
17812 && container
17813 .namespace_scope_components
17814 .starts_with(&candidate.namespace_scope_components)
17815 })
17816 })
17817 .collect::<Vec<_>>();
17818 let mut index = 0usize;
17819 recovered_classes.retain(|_| {
17820 let keep = !shadowed[index];
17821 index += 1;
17822 keep
17823 });
17824 recovered_classes
17825}
17826
17827fn push_cpp_sentinel_sibling_classes<'tree>(
17833 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
17834 declaration_list: Node<'tree>,
17835 sentinel_node: Node<'tree>,
17836 namespace_components: &[String],
17837 source: &str,
17838 ancestry: &ParentIndex<'tree>,
17839) {
17840 let owner_ranges =
17841 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
17842 let namespace_range = cpp_declaration_range(declaration_list);
17843 let mut cursor = declaration_list.walk();
17844 for (class_node, template_node) in declaration_list
17845 .named_children(&mut cursor)
17846 .filter(|child| !same_node(*child, sentinel_node))
17847 .filter_map(cpp_sentinel_body_class_candidate)
17848 {
17849 let Some(name) = class_like_name(class_node, source, ancestry) else {
17850 continue;
17851 };
17852 if name.is_empty()
17853 || cpp_export_macro_token(&name)
17854 || cpp_complete_class_body_close(class_node).is_none()
17855 {
17856 continue;
17857 }
17858 push_cpp_sentinel_recovered_class(
17859 recovered_classes,
17860 namespace_range,
17861 namespace_components,
17862 cpp_declaration_range(template_node.unwrap_or(class_node)),
17863 name,
17864 &owner_ranges,
17865 );
17866 }
17867}
17868
17869fn push_cpp_sentinel_recovered_class(
17870 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
17871 namespace_range: Range,
17872 namespace_components: &[String],
17873 class_range: Range,
17874 name: String,
17875 owner_ranges: &[CppSentinelRecoveredOwner],
17876) {
17877 let mut scope_components = namespace_components.to_vec();
17878 scope_components.push(name);
17879 let owner_ranges = owner_ranges
17880 .iter()
17881 .filter(|owner| owner.scope_components.starts_with(&scope_components))
17882 .cloned()
17883 .collect::<Vec<_>>();
17884 if recovered_classes.iter().any(|existing| {
17885 existing.class_range == class_range && existing.scope_components == scope_components
17886 }) {
17887 return;
17888 }
17889 recovered_classes.push(CppSentinelRecoveredClass {
17890 namespace_range,
17891 namespace_scope_components: namespace_components.to_vec(),
17892 class_range,
17893 scope_components,
17894 owner_ranges,
17895 });
17896}
17897
17898fn cpp_sentinel_recovered_namespace_components(
17899 function: Node<'_>,
17900 recovered_components: &[String],
17901 source: &str,
17902) -> Vec<String> {
17903 let mut ancestor_components = Vec::new();
17904 let mut ancestor = function.parent();
17905 while let Some(current) = ancestor {
17906 if current.kind() == "namespace_definition"
17907 && let Some(name_node) = current.child_by_field_name("name")
17908 && let Some(components) = cpp_name_components(name_node, source)
17909 {
17910 ancestor_components.push(
17911 components
17912 .into_iter()
17913 .map(|component| component.name)
17914 .collect::<Vec<_>>(),
17915 );
17916 }
17917 ancestor = current.parent();
17918 }
17919 ancestor_components.reverse();
17920 let mut ancestors = ancestor_components
17921 .into_iter()
17922 .flatten()
17923 .collect::<Vec<_>>();
17924
17925 let overlap = (0..=ancestors.len().min(recovered_components.len()))
17926 .rev()
17927 .find(|length| {
17928 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
17929 })
17930 .unwrap_or(0);
17931 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
17932 ancestors
17933}
17934
17935fn cpp_sentinel_recovered_owner_ranges(
17936 body: Node<'_>,
17937 namespace_components: &[String],
17938 source: &str,
17939) -> Vec<CppSentinelRecoveredOwner> {
17940 let mut owners = Vec::new();
17941 walk_named_tree_preorder(body, true, |node| {
17942 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
17943 });
17944 owners
17945}
17946
17947fn cpp_sentinel_collect_owner_range(
17948 node: Node<'_>,
17949 namespace_components: &[String],
17950 source: &str,
17951 owners: &mut Vec<CppSentinelRecoveredOwner>,
17952) -> WalkControl {
17953 if node.kind() != "function_definition" {
17954 return WalkControl::Continue;
17955 }
17956 let Some(function_declarator) = extract_function_declarator(node) else {
17957 return WalkControl::Continue;
17958 };
17959 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
17960 return WalkControl::Continue;
17961 };
17962 let Some(mut components) = cpp_name_components(name_node, source) else {
17963 return WalkControl::Continue;
17964 };
17965 if components.len() <= 1 {
17966 return WalkControl::Continue;
17967 }
17968 components.pop();
17969 let mut owner_components = components
17970 .into_iter()
17971 .map(|component| component.name)
17972 .collect::<Vec<_>>();
17973 let overlap = (0..=namespace_components.len().min(owner_components.len()))
17974 .rev()
17975 .find(|length| {
17976 owner_components[..*length]
17977 == namespace_components[namespace_components.len().saturating_sub(*length)..]
17978 })
17979 .unwrap_or(0);
17980 let mut scope_components = namespace_components.to_vec();
17981 scope_components.extend(owner_components.drain(overlap..));
17982 if scope_components.len() <= namespace_components.len() {
17983 return WalkControl::Continue;
17984 }
17985 let range = cpp_declaration_range(node);
17986 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
17987 existing.range == range && existing.scope_components == scope_components
17988 }) {
17989 owners.push(CppSentinelRecoveredOwner {
17990 range,
17991 owner_name_start_byte: name_node.start_byte(),
17992 namespace_component_count: namespace_components.len(),
17993 scope_components,
17994 });
17995 }
17996 WalkControl::Continue
17997}
17998
17999fn cpp_sentinel_extend_unique_owner_ranges(
18000 owners: &mut Vec<CppSentinelRecoveredOwner>,
18001 additional: Vec<CppSentinelRecoveredOwner>,
18002) {
18003 for owner in additional {
18004 if !owners.iter().any(|existing| {
18005 existing.range == owner.range && existing.scope_components == owner.scope_components
18006 }) {
18007 owners.push(owner);
18008 }
18009 }
18010}
18011
18012fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
18013 if node.kind() != "ERROR" || node.named_child_count() != 1 {
18014 return false;
18015 }
18016 let Some(end_name) = node.named_child(0) else {
18017 return false;
18018 };
18019 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
18020 return false;
18021 }
18022 let mut cursor = node.walk();
18023 node.children(&mut cursor)
18024 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
18025}
18026
18027fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18031 parent: Node<'_>,
18032 sentinel_node: Node<'_>,
18033 namespace_components: &[String],
18034 source: &str,
18035) -> Vec<CppSentinelRecoveredOwner> {
18036 let mut owners = Vec::new();
18037 let mut after_sentinel = false;
18038 let mut cursor = parent.walk();
18039 for child in parent.named_children(&mut cursor) {
18040 if !after_sentinel {
18041 if same_node(child, sentinel_node) {
18042 after_sentinel = true;
18043 }
18044 continue;
18045 }
18046 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18047 }
18048 owners
18049}
18050
18051fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18055 parent: Node<'_>,
18056 sentinel_node: Node<'_>,
18057 namespace_components: &[String],
18058 source: &str,
18059) -> Option<Vec<CppSentinelRecoveredOwner>> {
18060 let mut owners = Vec::new();
18061 let mut after_namespace = false;
18062 let mut cursor = parent.walk();
18063 for child in parent.named_children(&mut cursor) {
18064 if !after_namespace {
18065 if same_node(child, sentinel_node) {
18066 after_namespace = true;
18067 }
18068 continue;
18069 }
18070 if cpp_sentinel_namespace_end(child, source) {
18071 return Some(owners);
18072 }
18073 cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18074 }
18075 None
18076}
18077
18078fn cpp_sentinel_append_owner_ranges(
18079 sibling: Node<'_>,
18080 namespace_components: &[String],
18081 source: &str,
18082 owners: &mut Vec<CppSentinelRecoveredOwner>,
18083) {
18084 walk_named_tree_preorder(sibling, true, |node| {
18085 if node.kind() == "namespace_definition" {
18086 return WalkControl::SkipChildren;
18087 }
18088 cpp_sentinel_collect_owner_range(node, namespace_components, source, owners)
18089 });
18090}
18091
18092fn cpp_sentinel_recovered_sibling_owner_ranges(
18093 sentinel_node: Node<'_>,
18094 namespace_components: &[String],
18095 source: &str,
18096) -> Vec<CppSentinelRecoveredOwner> {
18097 let Some(declaration_list) = sentinel_node
18098 .parent()
18099 .filter(|parent| parent.kind() == "declaration_list")
18100 else {
18101 return Vec::new();
18102 };
18103 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18104 declaration_list,
18105 sentinel_node,
18106 namespace_components,
18107 source,
18108 );
18109
18110 let Some(namespace) = declaration_list
18111 .parent()
18112 .filter(|parent| parent.kind() == "namespace_definition")
18113 else {
18114 return owners;
18115 };
18116 let Some(outer_parent) = namespace.parent() else {
18117 return owners;
18118 };
18119 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18120 outer_parent,
18121 namespace,
18122 namespace_components,
18123 source,
18124 ) {
18125 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
18126 }
18127 owners
18128}
18129
18130fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
18131 let mut current = function_declarator.child_by_field_name("declarator")?;
18132 loop {
18133 if let Some(name) = macro_decorated_unqualified_name(current) {
18134 current = name;
18135 continue;
18136 }
18137 if matches!(
18138 current.kind(),
18139 "qualified_identifier"
18140 | "scoped_identifier"
18141 | "scoped_type_identifier"
18142 | "identifier"
18143 | "field_identifier"
18144 | "operator_name"
18145 | "destructor_name"
18146 | "literal_operator_name"
18147 ) {
18148 return Some(current);
18149 }
18150 current = current
18151 .child_by_field_name("declarator")
18152 .or_else(|| current.child_by_field_name("name"))
18153 .or_else(|| last_named_child(current))?;
18154 }
18155}
18156
18157fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
18170 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
18171 return None;
18172 }
18173 let mut cursor = node.walk();
18174 if node
18175 .children(&mut cursor)
18176 .any(|child| child.kind() == "::" && !child.is_missing())
18177 {
18178 return None;
18179 }
18180 node.child_by_field_name("name")
18181}
18182
18183fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
18184 if let Some(name) = macro_decorated_unqualified_name(node) {
18185 return cpp_name_components(name, source);
18186 }
18187 match node.kind() {
18188 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
18189 let mut components = match node.child_by_field_name("scope") {
18190 Some(scope) => cpp_name_components(scope, source)?,
18191 None => Vec::new(),
18192 };
18193 let name = node.child_by_field_name("name")?;
18194 components.push(canonical_cpp_qualified_component(name, source)?);
18195 Some(components)
18196 }
18197 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
18198 }
18199}
18200
18201fn cpp_sentinel_fragment_boundary<'tree>(
18202 function: Node<'tree>,
18203 class_node: Node<'tree>,
18204 class_body: Node<'tree>,
18205 source: &str,
18206) -> Option<(Node<'tree>, Node<'tree>)> {
18207 let declaration_list = function.parent()?;
18208 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
18209 return None;
18210 }
18211 let namespace = declaration_list.parent()?;
18212 if namespace.kind() != "namespace_definition"
18213 || namespace.child_by_field_name("body") != Some(declaration_list)
18214 {
18215 return None;
18216 }
18217 let mut cursor = declaration_list.walk();
18218 let closes = declaration_list
18219 .children(&mut cursor)
18220 .filter(|child| {
18221 !child.is_named()
18222 && child.kind() == "}"
18223 && child.start_byte() >= function.end_byte()
18224 && child.start_byte() > class_node.end_byte()
18225 && child.start_byte() > class_body.start_byte()
18226 })
18227 .collect::<Vec<_>>();
18228 let [close] = closes.as_slice() else {
18229 return None;
18230 };
18231 let semicolon = namespace.next_named_sibling()?;
18232 if !cpp_is_stray_semicolon(semicolon, source)
18233 || close.end_byte() != namespace.end_byte()
18234 || semicolon.start_byte() < namespace.end_byte()
18235 {
18236 return None;
18237 }
18238 Some((*close, semicolon))
18239}
18240
18241fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
18270 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") {
18271 return None;
18272 }
18273 let mut declarator_cursor = node.walk();
18279 let preserved_callable = node
18280 .children_by_field_name("declarator", &mut declarator_cursor)
18281 .find_map(extract_function_declarator);
18282 let mut cursor = node.walk();
18290 let first = node
18291 .named_children(&mut cursor)
18292 .find(|child| child.kind() != "comment")?;
18293 let (first_end, sentinel) = if first.kind() == "type_identifier" {
18302 if !node.has_error()
18303 && node
18304 .child_by_field_name("declarator")
18305 .is_none_or(|declarator| declarator.kind() != "macro_modifier_declarator")
18306 {
18307 return None;
18308 }
18309 (
18310 first.end_byte(),
18311 normalize_cpp_whitespace(node_text(first, source)),
18312 )
18313 } else {
18314 let identifier = macro_modifier_identifier(first)?;
18315 (
18316 first.end_byte(),
18317 normalize_cpp_whitespace(node_text(identifier, source)),
18318 )
18319 };
18320 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
18321 return None;
18322 }
18323 let mut start = first_end;
18330 let mut after_first = false;
18331 let mut cursor = node.walk();
18332 for child in node.named_children(&mut cursor) {
18333 if !after_first {
18334 if same_node(child, first) {
18335 after_first = true;
18336 }
18337 continue;
18338 }
18339 let macro_token = if matches!(child.kind(), "identifier" | "type_identifier") {
18340 Some(child)
18341 } else {
18342 macro_modifier_identifier(child)
18343 };
18344 if macro_token.is_some_and(|token| {
18345 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(token, source)))
18346 }) {
18347 start = child.end_byte();
18348 } else {
18349 break;
18350 }
18351 }
18352 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
18361 let mut class_start = None;
18362 let mut template_start = None;
18363 let mut stack = vec![node];
18364 while let Some(current) = stack.pop() {
18365 if current.start_byte() >= prefix_end {
18366 continue;
18367 }
18368 if matches!(
18369 current.kind(),
18370 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
18371 ) {
18372 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
18373 "class" | "struct" | "union" | "enum" => {
18374 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
18375 seen.min(current.start_byte())
18376 }));
18377 }
18378 "template" => {
18379 template_start =
18380 Some(template_start.map_or(current.start_byte(), |seen: usize| {
18381 seen.min(current.start_byte())
18382 }));
18383 }
18384 _ => {}
18385 }
18386 }
18387 let mut cursor = current.walk();
18388 stack.extend(current.children(&mut cursor));
18389 }
18390 if preserved_callable.is_some_and(|callable| {
18391 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
18392 }) {
18393 return None;
18394 }
18395 if let Some(class_start) = class_start {
18396 start = template_start
18397 .filter(|template_start| *template_start < class_start)
18398 .unwrap_or(class_start);
18399 }
18400 Some((start, class_start))
18401}
18402
18403fn cpp_sentinel_macro_class_region<'tree>(
18409 node: Node<'tree>,
18410 source: &str,
18411) -> Option<(usize, usize, usize, usize, usize, usize)> {
18412 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
18413 return None;
18414 };
18415 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
18416 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
18417 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
18418 if class_start >= body_open_start {
18419 return None;
18420 }
18421 let sibling_close = {
18422 let mut sibling = node.next_named_sibling();
18423 let mut found = None;
18424 while let Some(current) = sibling {
18425 let next = current.next_named_sibling();
18426 if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
18427 found = Some((
18428 current.start_byte(),
18429 semicolon.end_byte(),
18430 semicolon.end_position().row + 1,
18431 ));
18432 break;
18433 }
18434 sibling = next;
18435 }
18436 found
18437 };
18438 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
18451 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18452 return false;
18453 };
18454 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18455 let reparsed_ancestry = ParentIndex::new(tree.root_node());
18457 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
18458 tree.root_node(),
18459 template_node,
18460 source,
18461 &reparsed_ancestry,
18462 ) else {
18463 return false;
18464 };
18465 let body = reparsed_class.body;
18466 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
18467 });
18468 let (class_close_start, class_close_end, class_close_line) =
18469 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
18470 (class_close_start, class_close_end, class_close_line)
18471 } else {
18472 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
18479 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18480 let reparsed_ancestry = ParentIndex::new(tree.root_node());
18482 let reparsed_class = cpp_sentinel_reparsed_class(
18483 tree.root_node(),
18484 template_node,
18485 source,
18486 &reparsed_ancestry,
18487 )?;
18488 let body = reparsed_class.body;
18489 let class_close_end = body.end_byte();
18490 let class_close_start = class_close_end.checked_sub(1)?;
18491 let class_close_line = body.end_position().row + 1;
18492 (class_close_start, class_close_end, class_close_line)
18493 };
18494 if class_close_start <= class_start {
18495 return None;
18496 }
18497
18498 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
18502 let class_root = tree.root_node();
18503 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
18504 let reparsed_ancestry = ParentIndex::new(class_root);
18506 let reparsed_class =
18507 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
18508 let body = reparsed_class.body;
18509 if body.start_byte() != body_open_start {
18513 return None;
18514 }
18515 let body_start = body.start_byte().checked_add(1)?;
18516 (body_start < class_close_start).then_some((
18517 reparse_start,
18518 class_start,
18519 body_start,
18520 class_close_start,
18521 class_close_end,
18522 class_close_line,
18523 ))
18524}
18525
18526fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
18531 let mut stack = vec![node];
18532 while let Some(current) = stack.pop() {
18533 if current.start_byte() == class_start
18534 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
18535 {
18536 let mut ancestor = current.parent();
18540 while let Some(parent) = ancestor {
18541 if matches!(
18542 parent.kind(),
18543 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
18544 ) {
18545 if let Some(body) = cpp_body_node(parent) {
18546 return body
18547 .child(0)
18548 .filter(|open| open.kind() == "{")
18549 .map(|open| open.start_byte());
18550 }
18551 break;
18552 }
18553 if matches!(parent.kind(), "ERROR" | "declaration") {
18554 break;
18555 }
18556 ancestor = parent.parent();
18557 }
18558 let mut sibling = current.next_sibling();
18559 while let Some(candidate) = sibling {
18560 if candidate.kind() == "{" {
18561 return Some(candidate.start_byte());
18562 }
18563 sibling = candidate.next_sibling();
18564 }
18565 }
18566 let mut cursor = current.walk();
18567 stack.extend(current.children(&mut cursor));
18568 }
18569 None
18570}
18571
18572fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
18583 node.next_named_sibling()
18584 .filter(|sibling| sibling.kind() == "compound_statement")
18585}
18586
18587fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
18588 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
18589 let mut end = if class_start.is_some() {
18590 cpp_macro_prefixed_class_end(source, start)?
18591 } else {
18592 node.end_byte()
18593 };
18594 if class_start.is_none()
18595 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
18596 {
18597 end = end.max(namespace_end);
18598 }
18599 let mut sibling = node.next_named_sibling();
18600 while let Some(current) = sibling {
18601 if !cpp_is_stray_semicolon(current, source) {
18602 break;
18603 }
18604 end = current.end_byte();
18605 sibling = current.next_named_sibling();
18606 }
18607 (start < end).then_some((start, end))
18608}
18609
18610fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
18621 let mut sibling = node.next_sibling();
18622 let keyword = loop {
18623 let candidate = sibling?;
18624 sibling = candidate.next_sibling();
18625 if candidate.kind() != "comment" {
18626 break candidate;
18627 }
18628 };
18629 if keyword.kind() != "namespace" {
18630 return None;
18631 }
18632 let name = loop {
18633 let candidate = sibling?;
18634 sibling = candidate.next_sibling();
18635 if candidate.kind() != "comment" {
18636 break candidate;
18637 }
18638 };
18639 if cpp_namespace_name_components(name, source).is_empty() {
18640 return None;
18641 }
18642 let open = loop {
18643 let candidate = sibling?;
18644 sibling = candidate.next_sibling();
18645 if candidate.kind() != "comment" {
18646 break candidate;
18647 }
18648 };
18649 if open.kind() != "{" {
18650 return None;
18651 }
18652
18653 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
18654 let root = tree.root_node();
18655 let mut cursor = root.walk();
18656 let namespace = root
18657 .named_children(&mut cursor)
18658 .find(|candidate| candidate.kind() != "comment")?;
18659 (namespace.kind() == "namespace_definition"
18660 && namespace.start_byte() == keyword.start_byte()
18661 && namespace.child_by_field_name("body").is_some())
18662 .then_some(namespace.end_byte())
18663}
18664
18665fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
18671 let tree = cpp_reparse_region_items(source, start, source.len())?;
18672 let root = tree.root_node();
18673 let mut cursor = root.walk();
18674 for item in root.named_children(&mut cursor) {
18675 if item.end_byte() <= start || item.kind() == "comment" {
18676 continue;
18677 }
18678 let mut stack = vec![item];
18679 while let Some(current) = stack.pop() {
18680 if matches!(
18681 current.kind(),
18682 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
18683 ) && cpp_body_node(current).is_some()
18684 {
18685 return Some(current.end_byte());
18686 }
18687 let mut cursor = current.walk();
18688 stack.extend(current.named_children(&mut cursor));
18689 }
18690 return None;
18694 }
18695 None
18696}
18697
18698fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
18701 node.kind() == "expression_statement"
18702 && node.named_child_count() == 0
18703 && node_text(node, source).trim() == ";"
18704}
18705
18706fn cpp_stray_close_followed_by_semicolon<'tree>(
18707 close: Node<'tree>,
18708 next: Option<Node<'tree>>,
18709 source: &str,
18710) -> Option<Node<'tree>> {
18711 cpp_is_stray_close_brace(close, source)
18712 .then(|| next.filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source)))
18713 .flatten()
18714}
18715
18716#[derive(Clone, Copy)]
18726pub(crate) struct RecoveredPyObjectHeadField<'tree> {
18727 pub(crate) type_node: Node<'tree>,
18728 pub(crate) name: Node<'tree>,
18729 pub(crate) declarator: Node<'tree>,
18730}
18731
18732impl RecoveredPyObjectHeadField<'_> {
18733 pub(crate) fn pointer_depth(self) -> i32 {
18734 let mut depth = 0;
18735 let mut current = self.declarator;
18736 while current != self.name {
18737 debug_assert_eq!(current.kind(), "pointer_declarator");
18738 depth += 1;
18739 current = current
18740 .child_by_field_name("declarator")
18741 .expect("recovered PyObject field pointer has an inner declarator");
18742 }
18743 depth
18744 }
18745}
18746
18747pub(crate) fn recovered_pyobject_head_field<'tree>(
18748 node: Node<'tree>,
18749 source: &str,
18750) -> Option<RecoveredPyObjectHeadField<'tree>> {
18751 if node.kind() != "field_declaration" {
18752 return None;
18753 }
18754 let type_node = node.child_by_field_name("type")?;
18755 if type_node.kind() != "type_identifier"
18756 || node_text(type_node, source).trim() != "PyObject_HEAD"
18757 {
18758 return None;
18759 }
18760 let pseudo_declarator = node.child_by_field_name("declarator")?;
18761 let mut cursor = node.walk();
18762 let errors = node
18763 .named_children(&mut cursor)
18764 .filter(|child| child.kind() == "ERROR")
18765 .collect::<Vec<_>>();
18766 let [error] = errors.as_slice() else {
18767 return None;
18768 };
18769 if error.named_child_count() != 1 {
18770 return None;
18771 }
18772 let error_child = error.named_child(0)?;
18773 if pseudo_declarator.kind() == "field_identifier"
18774 && error.start_byte() >= pseudo_declarator.end_byte()
18775 && error_child.kind() == "identifier"
18776 {
18777 return Some(RecoveredPyObjectHeadField {
18778 type_node: pseudo_declarator,
18779 name: error_child,
18780 declarator: error_child,
18781 });
18782 }
18783 if pseudo_declarator.kind() != "pointer_declarator"
18784 || error.end_byte() > pseudo_declarator.start_byte()
18785 || error_child.kind() != "identifier"
18786 {
18787 return None;
18788 }
18789 let mut name = pseudo_declarator;
18790 while name.kind() == "pointer_declarator" {
18791 name = name.child_by_field_name("declarator")?;
18792 }
18793 (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
18794 type_node: error_child,
18795 name,
18796 declarator: pseudo_declarator,
18797 })
18798}
18799
18800fn recovered_macro_qualified_field_clause<'tree>(
18809 node: Node<'tree>,
18810 source: &str,
18811) -> Option<Node<'tree>> {
18812 if node.kind() != "field_declaration" {
18813 return None;
18814 }
18815 let macro_type = node.child_by_field_name("type")?;
18816 if macro_type.kind() != "type_identifier"
18817 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
18818 {
18819 return None;
18820 }
18821 let mut cursor = node.walk();
18822 node.named_children(&mut cursor)
18823 .find(|child| child.kind() == "bitfield_clause")
18824}
18825
18826fn recovered_macro_qualified_field_declarators<'tree>(
18827 node: Node<'tree>,
18828 source: &str,
18829) -> Option<Vec<Node<'tree>>> {
18830 let pseudo_declarator = node.child_by_field_name("declarator")?;
18831 if pseudo_declarator.kind() != "field_identifier" {
18832 return None;
18833 }
18834 let clause = recovered_macro_qualified_field_clause(node, source)?;
18835 if !(0..clause.named_child_count()).any(|index| {
18836 clause
18837 .named_child(index)
18838 .is_some_and(|child| child.kind() == "ERROR")
18839 }) {
18840 return None;
18841 }
18842 let mut recovered = Vec::new();
18843 let mut stack = vec![clause];
18844 while let Some(current) = stack.pop() {
18845 if current.kind() == "assignment_expression"
18846 && let Some(left) = current.child_by_field_name("left")
18847 && extract_variable_name(left, source).is_some()
18848 {
18849 recovered.push(left);
18850 break;
18851 }
18852 let mut cursor = current.walk();
18853 stack.extend(current.named_children(&mut cursor));
18854 }
18855 if recovered.is_empty() {
18856 return None;
18857 }
18858 let mut cursor = node.walk();
18859 recovered.extend(
18860 node.children_by_field_name("declarator", &mut cursor)
18861 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
18862 );
18863 Some(recovered)
18864}
18865
18866fn recovered_macro_qualified_constructor_call<'tree>(
18872 node: Node<'tree>,
18873 class_name: &str,
18874 source: &str,
18875) -> Option<Node<'tree>> {
18876 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
18877 let error = bitfield
18878 .named_child(0)
18879 .filter(|child| child.kind() == "ERROR")?;
18880 let mut stack = vec![error];
18881 while let Some(current) = stack.pop() {
18882 if current.kind() == "call_expression"
18883 && current
18884 .child_by_field_name("function")
18885 .is_some_and(|function| node_text(function, source) == class_name)
18886 && current
18887 .child_by_field_name("arguments")
18888 .is_some_and(|arguments| arguments.kind() == "argument_list")
18889 {
18890 return Some(current);
18891 }
18892 let mut cursor = current.walk();
18893 stack.extend(current.named_children(&mut cursor));
18894 }
18895 None
18896}
18897
18898fn recovered_macro_initialized_constructor_declarator<'tree>(
18911 node: Node<'tree>,
18912 class_name: &str,
18913 source: &str,
18914) -> Option<Node<'tree>> {
18915 if node.kind() != "field_declaration" {
18916 return None;
18917 }
18918 let constructor = extract_function_declarator(node.child_by_field_name("declarator")?)?;
18919 if cpp_function_declarator_name_node(constructor)
18920 .is_none_or(|name| node_text(name, source) != class_name)
18921 {
18922 return None;
18923 }
18924 let mut cursor = node.walk();
18925 let initializer = node
18926 .named_children(&mut cursor)
18927 .find(|child| child.kind() == "bitfield_clause")?
18928 .named_child(0)?;
18929 (initializer.kind() == "call_expression").then_some(constructor)
18930}
18931
18932fn recovered_macro_qualified_function_call<'tree>(
18940 node: Node<'tree>,
18941 source: &str,
18942) -> Option<Node<'tree>> {
18943 let bitfield = recovered_macro_qualified_field_clause(node, source)?;
18944 let declarator = node.child_by_field_name("declarator")?;
18945 if declarator.kind() != "field_identifier" {
18946 return None;
18947 }
18948 let mut cursor = node.walk();
18949 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18950 if !named.iter().any(|child| {
18951 child.kind() == "storage_class_specifier"
18952 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
18953 }) {
18954 return None;
18955 }
18956 let mut bitfield_cursor = bitfield.walk();
18957 let payload = bitfield
18958 .named_children(&mut bitfield_cursor)
18959 .collect::<Vec<_>>();
18960 let [displaced_error, call] = payload.as_slice() else {
18961 return None;
18962 };
18963 if displaced_error.kind() != "ERROR"
18964 || displaced_error.named_child_count() != 1
18965 || displaced_error
18966 .named_child(0)
18967 .is_none_or(|child| child.kind() != "identifier")
18968 || call.kind() != "call_expression"
18969 || call
18970 .child_by_field_name("function")
18971 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
18972 || call
18973 .child_by_field_name("arguments")
18974 .is_none_or(|arguments| arguments.kind() != "argument_list")
18975 {
18976 return None;
18977 }
18978 Some(*call)
18979}
18980
18981fn recovered_macro_decorated_constructor_declarator<'tree>(
18990 node: Node<'tree>,
18991 class_name: &str,
18992 source: &str,
18993) -> Option<Node<'tree>> {
18994 if node.kind() != "function_definition" {
18995 return None;
18996 }
18997 let mut cursor = node.walk();
18998 let mut heads = Vec::new();
18999 for error in node
19000 .named_children(&mut cursor)
19001 .filter(|child| child.kind() == "ERROR")
19002 {
19003 let mut stack = vec![error];
19004 while let Some(current) = stack.pop() {
19005 let is_constructor_head = match current.kind() {
19006 "macro_modifier_declarator" | "function_declarator" => {
19007 extract_function_declarator(current).is_some_and(|declarator| {
19008 cpp_function_declarator_name_node(declarator)
19009 .is_some_and(|name| node_text(name, source) == class_name)
19010 })
19011 }
19012 _ => false,
19013 };
19014 if is_constructor_head && let Some(declarator) = extract_function_declarator(current) {
19015 heads.push(declarator);
19016 continue;
19017 }
19018 let mut cursor = current.walk();
19019 stack.extend(current.named_children(&mut cursor));
19020 }
19021 }
19022 let [head] = heads.as_slice() else {
19023 return None;
19024 };
19025 Some(*head)
19026}
19027
19028fn recovered_macro_qualified_function_parameters(
19029 arguments: Node<'_>,
19030 source: &str,
19031) -> Option<(String, Vec<String>)> {
19032 if arguments.kind() != "argument_list" {
19033 return None;
19034 }
19035 let mut cursor = arguments.walk();
19036 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
19037 if named.is_empty() {
19038 return Some(("()".to_string(), Vec::new()));
19039 }
19040 let mut types = Vec::new();
19041 let mut labels = Vec::new();
19042 let mut index = 0;
19043 while index < named.len() {
19044 let parameter_type = named[index];
19045 let parameter_name = named.get(index + 1).copied()?;
19046 if !matches!(
19047 parameter_type.kind(),
19048 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
19049 ) || parameter_name.kind() != "ERROR"
19050 || parameter_name.named_child_count() != 1
19051 || parameter_name
19052 .named_child(0)
19053 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
19054 {
19055 return None;
19056 }
19057 let parameter_name = parameter_name.named_child(0)?;
19058 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
19059 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
19060 index += 2;
19061 }
19062 Some((format!("({})", types.join(", ")), labels))
19063}
19064
19065pub fn recovered_macro_return_type_node<'tree>(
19077 node: Node<'tree>,
19078 source: &str,
19079) -> Option<Node<'tree>> {
19080 if node.kind() != "field_declaration" {
19081 return None;
19082 }
19083 let macro_type = node.child_by_field_name("type")?;
19084 if macro_type.kind() != "type_identifier"
19085 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19086 {
19087 return None;
19088 }
19089 let declarator = node.child_by_field_name("declarator")?;
19090 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
19091 return None;
19092 }
19093 let mut has_missing_semicolon = false;
19094 let mut has_real_semicolon = false;
19095 for child in children_iter(node) {
19096 if child.kind() != ";" {
19097 continue;
19098 }
19099 if child.is_missing() {
19100 has_missing_semicolon = true;
19101 } else {
19102 has_real_semicolon = true;
19103 }
19104 }
19105 if !has_missing_semicolon || has_real_semicolon {
19106 return None;
19107 }
19108 let mut next = node.next_named_sibling();
19109 while next.is_some_and(|sibling| sibling.kind() == "comment") {
19110 next = next.and_then(|sibling| sibling.next_named_sibling());
19111 }
19112 let next = next?;
19113 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
19114 return None;
19115 }
19116 let function_declarator = next.child_by_field_name("declarator")?;
19117 extract_function_declarator(function_declarator).map(|_| declarator)
19118}
19119
19120fn omitted_semicolon_macro_field_pair(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
19130 if node.kind() != "function_definition" {
19131 return None;
19132 }
19133 let type_node = node.child_by_field_name("type")?;
19134 let declarator = node.child_by_field_name("declarator")?;
19135 extract_function_declarator(declarator)?;
19136 let mut cursor = node.walk();
19137 let head = node
19138 .named_children(&mut cursor)
19139 .take_while(|child| *child != type_node)
19140 .filter(|child| child.kind() != "comment")
19141 .collect::<Vec<_>>();
19142 let [type_modifier, name_modifier] = head.as_slice() else {
19143 return None;
19144 };
19145 macro_modifier_identifier(*type_modifier)?;
19146 macro_modifier_identifier(*name_modifier)?;
19147 Some((*type_modifier, *name_modifier))
19148}
19149
19150fn macro_return_template_parameter_field<'tree>(
19160 node: Node<'tree>,
19161 source: &str,
19162 ancestry: &ParentIndex<'tree>,
19163) -> Option<(Node<'tree>, Node<'tree>)> {
19164 if node.kind() != "function_definition" {
19165 return None;
19166 }
19167 let type_node = node.child_by_field_name("type")?;
19168 if type_node.kind() != "type_identifier" {
19169 return None;
19170 }
19171 let declarator = node.child_by_field_name("declarator")?;
19172 extract_function_declarator(declarator)?;
19173 let mut cursor = node.walk();
19174 let head = node
19175 .named_children(&mut cursor)
19176 .take_while(|child| *child != type_node)
19177 .filter(|child| child.kind() != "comment")
19178 .collect::<Vec<_>>();
19179 let [first, ..] = head.as_slice() else {
19180 return None;
19181 };
19182 if !head
19183 .iter()
19184 .all(|child| matches!(child.kind(), "macro_modifier" | "storage_class_specifier"))
19185 || !head.iter().any(|child| child.kind() == "macro_modifier")
19186 {
19187 return None;
19188 }
19189 let name = normalize_cpp_whitespace(node_text(type_node, source));
19190 if !cpp_active_template_type_parameter(node, &name, source, ancestry) {
19191 return None;
19192 }
19193 Some((*first, type_node))
19194}
19195
19196pub(crate) fn cpp_active_template_type_parameter<'tree>(
19203 node: Node<'tree>,
19204 name: &str,
19205 source: &str,
19206 ancestry: &ParentIndex<'tree>,
19207) -> bool {
19208 let mut ancestor = ancestry.parent(node);
19209 while let Some(current) = ancestor {
19210 if current.kind() == "template_declaration"
19211 && let Some(parameters) = current.child_by_field_name("parameters")
19212 && cpp_template_parameter_name_matches(parameters, name, |parameter| {
19213 cpp_template_parameter_name(parameter, source)
19214 })
19215 .type_parameter
19216 {
19217 return true;
19218 }
19219 ancestor = ancestry.parent(current);
19220 }
19221 false
19222}
19223
19224fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
19230 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
19231}
19232
19233fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
19234 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
19235 return None;
19236 }
19237 let semicolon = node.next_sibling()?;
19238 if semicolon.kind() != ";" || semicolon.is_missing() {
19239 return None;
19240 }
19241 let row = node.start_position().row;
19242 let mut start = node.start_byte();
19243 let mut sibling = node.prev_sibling();
19244 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
19245 if previous.kind() == ";" {
19246 break;
19247 }
19248 start = previous.start_byte();
19249 sibling = previous.prev_sibling();
19250 }
19251 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
19252}
19253
19254struct PrototypeMacroCandidate {
19258 run_start: usize,
19260 identifier_start: usize,
19263 inner_open_start: usize,
19267 inner_close_end: usize,
19269 outer_close_end: usize,
19271 semicolon_end: usize,
19274}
19275
19276impl PrototypeMacroCandidate {
19277 fn ranges(&self) -> [(usize, usize); 3] {
19283 [
19284 (self.run_start, self.identifier_start),
19285 (self.inner_open_start, self.inner_close_end),
19286 (self.outer_close_end, self.semicolon_end),
19287 ]
19288 }
19289}
19290
19291fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
19293 node.child(node.child_count().checked_sub(1)?)
19294 .filter(|child| child.kind() == ";" && !child.is_missing())
19295}
19296
19297fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
19302 matches!(
19303 node.kind(),
19304 "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
19305 ) && matches!(
19306 normalize_cpp_whitespace(node_text(node, source)).as_str(),
19307 "_" | "__P" | "OF" | "PROTO"
19308 )
19309}
19310
19311fn cpp_prototype_macro_qualified_parts<'tree>(
19315 node: Node<'tree>,
19316 source: &str,
19317) -> Option<(Node<'tree>, Node<'tree>)> {
19318 if node.kind() != "qualified_identifier" {
19319 return None;
19320 }
19321 let declared_name = node
19322 .child_by_field_name("scope")
19323 .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
19324 let macro_name = macro_decorated_unqualified_name(node)?;
19325 cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
19326}
19327
19328fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
19334 if arguments.kind() != "argument_list"
19335 || arguments.named_child_count() != 1
19336 || arguments.child_count() != 3
19337 || arguments
19338 .child(0)
19339 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19340 || arguments
19341 .child(2)
19342 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19343 {
19344 return None;
19345 }
19346 let inner = arguments.named_child(0)?;
19347 let close_index = match inner.kind() {
19348 "parenthesized_expression" => inner.child_count().checked_sub(1)?,
19349 "cast_expression" => inner.child_count().checked_sub(2)?,
19352 _ => return None,
19353 };
19354 (inner
19355 .child(0)
19356 .is_some_and(|open| open.kind() == "(" && !open.is_missing())
19357 && inner
19358 .child(close_index)
19359 .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
19360 .then_some(inner)
19361}
19362
19363fn cpp_prototype_macro_candidate_from_init_declaration(
19364 declaration: Node<'_>,
19365 source: &str,
19366) -> Option<PrototypeMacroCandidate> {
19367 let init = declaration
19368 .child_by_field_name("declarator")
19369 .filter(|declarator| declarator.kind() == "init_declarator")?;
19370 let malformed_declarator = init.child_by_field_name("declarator")?;
19371 let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
19372 cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
19373 } else {
19374 if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
19375 return None;
19376 }
19377 let declared_name_error = init
19378 .prev_named_sibling()
19379 .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
19380 let declared_name = declared_name_error
19381 .named_child(0)
19382 .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
19383 (declared_name, malformed_declarator)
19384 };
19385 let arguments = init
19386 .child_by_field_name("value")
19387 .filter(|value| value.kind() == "argument_list")?;
19388 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19389 let semicolon = cpp_direct_semicolon(declaration)?;
19390 let return_type = declaration.child_by_field_name("type")?;
19391 if return_type.end_byte() > declared_name.start_byte()
19392 || declared_name.end_byte() > macro_name.start_byte()
19393 || macro_name.end_byte() > arguments.start_byte()
19394 || arguments.end_byte() > semicolon.start_byte()
19395 {
19396 return None;
19397 }
19398 Some(cpp_prototype_macro_candidate_from_parts(
19399 declaration.start_byte(),
19400 macro_name,
19401 inner,
19402 arguments,
19403 semicolon,
19404 ))
19405}
19406
19407fn cpp_prototype_macro_candidate_from_qualified_declaration(
19408 declaration: Node<'_>,
19409 source: &str,
19410) -> Option<PrototypeMacroCandidate> {
19411 let qualified = declaration
19412 .child_by_field_name("declarator")
19413 .filter(|declarator| declarator.kind() == "qualified_identifier")?;
19414 let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19415 let open_error = qualified
19416 .next_named_sibling()
19417 .filter(|next| next.kind() == "ERROR")?;
19418 let close_error = last_named_child(declaration)
19419 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
19420 if open_error.child_count() < 3
19421 || open_error
19422 .child(0)
19423 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19424 || open_error
19425 .child(1)
19426 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19427 || close_error.child_count() != 2
19428 || close_error
19429 .child(0)
19430 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19431 || close_error
19432 .child(1)
19433 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19434 {
19435 return None;
19436 }
19437 let inner_open = open_error.child(1)?;
19438 let inner_close = close_error.child(0)?;
19439 let outer_close = close_error.child(1)?;
19440 let semicolon = cpp_direct_semicolon(declaration)?;
19441 let return_type = declaration.child_by_field_name("type")?;
19442 if return_type.end_byte() > qualified.start_byte()
19443 || macro_name.end_byte() > open_error.start_byte()
19444 || inner_open.start_byte() > inner_close.end_byte()
19445 || inner_close.end_byte() > outer_close.start_byte()
19446 || outer_close.end_byte() > semicolon.start_byte()
19447 {
19448 return None;
19449 }
19450 Some(PrototypeMacroCandidate {
19451 run_start: declaration.start_byte(),
19452 identifier_start: macro_name.start_byte(),
19453 inner_open_start: inner_open.start_byte(),
19454 inner_close_end: inner_close.end_byte(),
19455 outer_close_end: outer_close.end_byte(),
19456 semicolon_end: semicolon.end_byte(),
19457 })
19458}
19459
19460fn cpp_prototype_macro_candidate_from_pointer_expression(
19461 statement: Node<'_>,
19462 source: &str,
19463) -> Option<PrototypeMacroCandidate> {
19464 if statement.kind() != "expression_statement"
19465 || statement.named_child_count() != 1
19466 || !statement.has_error()
19467 {
19468 return None;
19469 }
19470 let expansion = statement
19471 .named_child(0)
19472 .filter(|child| child.kind() == "parameter_pack_expansion")?;
19473 let binary = expansion
19474 .child_by_field_name("pattern")
19475 .filter(|pattern| pattern.kind() == "binary_expression")?;
19476 if binary.child_count() != 3
19477 || binary
19478 .child(1)
19479 .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
19480 || expansion
19481 .child(expansion.child_count().checked_sub(1)?)
19482 .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
19483 {
19484 return None;
19485 }
19486 let return_type = binary.child_by_field_name("left")?;
19487 let call = binary
19488 .child_by_field_name("right")
19489 .filter(|right| right.kind() == "call_expression")?;
19490 let qualified = call.child_by_field_name("function")?;
19491 let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19492 let arguments = call
19493 .child_by_field_name("arguments")
19494 .filter(|arguments| arguments.kind() == "argument_list")?;
19495 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19496 let semicolon = cpp_direct_semicolon(statement)?;
19497 if return_type.end_byte() > declared_name.start_byte()
19498 || macro_name.end_byte() > arguments.start_byte()
19499 || arguments.end_byte() > semicolon.start_byte()
19500 {
19501 return None;
19502 }
19503 Some(cpp_prototype_macro_candidate_from_parts(
19504 statement.start_byte(),
19505 macro_name,
19506 inner,
19507 arguments,
19508 semicolon,
19509 ))
19510}
19511
19512fn cpp_prototype_macro_inner_parenthesized_declarator(outer: Node<'_>) -> Option<Node<'_>> {
19518 if outer.kind() != "parenthesized_declarator" || outer.named_child_count() != 1 {
19519 return None;
19520 }
19521 let open = outer.child(0)?;
19522 let close = outer.child(outer.child_count().checked_sub(1)?)?;
19523 if open.kind() != "(" || open.is_missing() || close.kind() != ")" || close.is_missing() {
19524 return None;
19525 }
19526 let inner = outer.named_child(0)?;
19527 if inner.kind() != "parenthesized_declarator" {
19528 return None;
19529 }
19530 let inner_open = inner.child(0)?;
19531 let inner_close = inner.child(inner.child_count().checked_sub(1)?)?;
19532 (inner_open.kind() == "("
19533 && !inner_open.is_missing()
19534 && inner_close.kind() == ")"
19535 && !inner_close.is_missing())
19536 .then_some(inner)
19537}
19538
19539fn cpp_declaration_has_macro_modifier(declaration: Node<'_>) -> bool {
19543 let mut cursor = declaration.walk();
19544 declaration
19545 .named_children(&mut cursor)
19546 .any(|child| child.kind() == "macro_modifier")
19547}
19548
19549fn cpp_prototype_macro_candidate_from_macro_modifier_init(
19555 declaration: Node<'_>,
19556 source: &str,
19557) -> Option<PrototypeMacroCandidate> {
19558 if !cpp_declaration_has_macro_modifier(declaration) {
19559 return None;
19560 }
19561 let declared_name = declaration
19562 .child_by_field_name("type")
19563 .filter(|type_node| type_node.kind() == "type_identifier")?;
19564 let init = declaration
19565 .child_by_field_name("declarator")
19566 .filter(|declarator| declarator.kind() == "init_declarator")?;
19567 let macro_name = init
19568 .child_by_field_name("declarator")
19569 .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
19570 let arguments = init
19571 .child_by_field_name("value")
19572 .filter(|value| value.kind() == "argument_list")?;
19573 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19574 let semicolon = cpp_direct_semicolon(declaration)?;
19575 if declared_name.end_byte() > macro_name.start_byte()
19576 || macro_name.end_byte() > arguments.start_byte()
19577 || arguments.end_byte() > semicolon.start_byte()
19578 {
19579 return None;
19580 }
19581 Some(cpp_prototype_macro_candidate_from_parts(
19582 declaration.start_byte(),
19583 macro_name,
19584 inner,
19585 arguments,
19586 semicolon,
19587 ))
19588}
19589
19590fn cpp_prototype_macro_candidate_from_macro_modifier_declarator(
19595 declaration: Node<'_>,
19596 source: &str,
19597) -> Option<PrototypeMacroCandidate> {
19598 let mut declarator = declaration.child_by_field_name("declarator")?;
19599 while declarator.kind() == "pointer_declarator" {
19600 declarator = declarator.child_by_field_name("declarator")?;
19601 }
19602 let mut saw_name_modifier = false;
19604 let (macro_name, arguments) = loop {
19605 if declarator.kind() != "macro_modifier_declarator" {
19606 return None;
19607 }
19608 let mut cursor = declarator.walk();
19609 let modifier = declarator
19610 .named_children(&mut cursor)
19611 .find(|child| child.kind() == "macro_modifier")
19612 .and_then(macro_modifier_identifier)?;
19613 let inner = declarator.child_by_field_name("declarator")?;
19614 if cpp_is_prototype_macro_identifier(modifier, source) {
19615 break (modifier, inner);
19616 }
19617 saw_name_modifier = true;
19618 declarator = inner;
19619 };
19620 if !saw_name_modifier {
19621 return None;
19622 }
19623 let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
19624 let semicolon = cpp_direct_semicolon(declaration)?;
19625 Some(cpp_prototype_macro_candidate_from_parts(
19626 declaration.start_byte(),
19627 macro_name,
19628 inner,
19629 arguments,
19630 semicolon,
19631 ))
19632}
19633
19634fn cpp_prototype_macro_candidate_from_macro_modifier_type(
19639 declaration: Node<'_>,
19640 source: &str,
19641) -> Option<PrototypeMacroCandidate> {
19642 if !cpp_declaration_has_macro_modifier(declaration) {
19643 return None;
19644 }
19645 let macro_name = declaration
19646 .child_by_field_name("type")
19647 .filter(|type_node| cpp_is_prototype_macro_identifier(*type_node, source))?;
19648 let arguments = declaration.child_by_field_name("declarator")?;
19649 let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
19650 let semicolon = cpp_direct_semicolon(declaration)?;
19651 Some(cpp_prototype_macro_candidate_from_parts(
19652 declaration.start_byte(),
19653 macro_name,
19654 inner,
19655 arguments,
19656 semicolon,
19657 ))
19658}
19659
19660fn cpp_prototype_macro_candidate_from_macro_modifier_flat(
19666 declaration: Node<'_>,
19667 source: &str,
19668) -> Option<PrototypeMacroCandidate> {
19669 if !cpp_declaration_has_macro_modifier(declaration) {
19670 return None;
19671 }
19672 let declared_name = declaration
19673 .child_by_field_name("type")
19674 .filter(|type_node| type_node.kind() == "type_identifier")?;
19675 let mut declarator_cursor = declaration.walk();
19676 let macro_name = declaration
19677 .children_by_field_name("declarator", &mut declarator_cursor)
19678 .next()
19679 .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
19680 let open_error = macro_name
19681 .next_named_sibling()
19682 .filter(|next| next.kind() == "ERROR")?;
19683 let close_error = last_named_child(declaration)
19684 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
19685 if open_error.child_count() < 3
19686 || open_error
19687 .child(0)
19688 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19689 || open_error
19690 .child(1)
19691 .is_none_or(|open| open.kind() != "(" || open.is_missing())
19692 || close_error.child_count() != 2
19693 || close_error
19694 .child(0)
19695 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19696 || close_error
19697 .child(1)
19698 .is_none_or(|close| close.kind() != ")" || close.is_missing())
19699 {
19700 return None;
19701 }
19702 let inner_open = open_error.child(1)?;
19703 let inner_close = close_error.child(0)?;
19704 let outer_close = close_error.child(1)?;
19705 let semicolon = cpp_direct_semicolon(declaration)?;
19706 if declared_name.end_byte() > macro_name.start_byte()
19707 || macro_name.end_byte() > open_error.start_byte()
19708 || inner_open.start_byte() > inner_close.end_byte()
19709 || inner_close.end_byte() > outer_close.start_byte()
19710 || outer_close.end_byte() > semicolon.start_byte()
19711 {
19712 return None;
19713 }
19714 Some(PrototypeMacroCandidate {
19715 run_start: declaration.start_byte(),
19716 identifier_start: macro_name.start_byte(),
19717 inner_open_start: inner_open.start_byte(),
19718 inner_close_end: inner_close.end_byte(),
19719 outer_close_end: outer_close.end_byte(),
19720 semicolon_end: semicolon.end_byte(),
19721 })
19722}
19723
19724fn cpp_prototype_macro_candidate_from_parts(
19725 run_start: usize,
19726 macro_name: Node<'_>,
19727 inner_arguments: Node<'_>,
19728 outer_arguments: Node<'_>,
19729 semicolon: Node<'_>,
19730) -> PrototypeMacroCandidate {
19731 PrototypeMacroCandidate {
19732 run_start,
19733 identifier_start: macro_name.start_byte(),
19734 inner_open_start: inner_arguments.start_byte(),
19735 inner_close_end: inner_arguments.end_byte(),
19736 outer_close_end: outer_arguments.end_byte(),
19737 semicolon_end: semicolon.end_byte(),
19738 }
19739}
19740
19741fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
19746 let candidate = match node.kind() {
19747 "declaration" => cpp_prototype_macro_candidate_from_init_declaration(node, source)
19748 .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
19749 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_init(node, source))
19750 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_declarator(node, source))
19751 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_type(node, source))
19752 .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_flat(node, source)),
19753 "expression_statement" => {
19754 cpp_prototype_macro_candidate_from_pointer_expression(node, source)
19755 }
19756 _ => None,
19757 };
19758 candidate.into_iter().collect()
19759}
19760
19761fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
19762 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
19763 return false;
19764 }
19765 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
19766 return false;
19767 }
19768 let Some(declarator) = (if node.kind() == "function_definition" {
19769 node.child_by_field_name("declarator")
19770 .and_then(extract_function_declarator)
19771 } else {
19772 node.named_child(0)
19773 .filter(|child| child.kind() == "function_declarator")
19774 }) else {
19775 return false;
19776 };
19777 let Some(name) = cpp_function_declarator_name_node(declarator) else {
19778 return false;
19779 };
19780 declarator.start_byte() == node.start_byte()
19781 && name.kind() == "identifier"
19782 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
19783}
19784
19785fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
19798 let region = cpp_reparse_region_items(source, start, end);
19799
19800 #[cfg(debug_assertions)]
19801 assert_eq!(
19802 region.as_ref().map(cpp_tree_shape),
19803 cpp_reparse_padded_class_body(source, start, end)
19804 .as_ref()
19805 .map(cpp_tree_shape),
19806 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
19807 prefix produces"
19808 );
19809
19810 region
19811}
19812
19813#[cfg(any(debug_assertions, test))]
19817fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
19818 if start >= end {
19819 return None;
19823 }
19824 let bytes = source.as_bytes();
19825 let prefix = bytes.get(..start)?;
19826 let interior = bytes.get(start..end)?;
19827 let mut padded = Vec::with_capacity(end);
19828 padded.extend(
19829 prefix
19830 .iter()
19831 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
19832 );
19833 padded.extend_from_slice(interior);
19834 let padded = String::from_utf8(padded).ok()?;
19835 let mut parser = tree_sitter::Parser::new();
19836 parser
19837 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19838 .ok()?;
19839 parser.parse(&padded, None)
19840}
19841
19842#[cfg(any(debug_assertions, test))]
19846fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
19847 let mut shape = Vec::new();
19848 let mut cursor = tree.root_node().walk();
19849 let mut stack = vec![tree.root_node()];
19850 while let Some(node) = stack.pop() {
19851 shape.push((
19852 node.kind(),
19853 node.start_byte(),
19854 node.end_byte(),
19855 node.start_position().row,
19856 node.start_position().column,
19857 node.is_named(),
19858 node.is_missing(),
19859 ));
19860 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
19861 stack.extend(children.into_iter().rev());
19862 }
19863 shape
19864}
19865
19866fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
19887 let mut cursor = root.walk();
19888 let mut saw_item = false;
19889 for child in root.named_children(&mut cursor) {
19890 match child.kind() {
19891 "comment" => {}
19892 "function_definition" => {
19893 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
19894 return false;
19895 }
19896 saw_item = true;
19897 }
19898 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
19899 _ => return false,
19900 }
19901 }
19902 saw_item
19903}
19904
19905fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
19915 if node.kind() != "ERROR" {
19916 return false;
19917 }
19918 let mut stack = Vec::new();
19919 let mut saw_function_declarator = false;
19920 let mut cursor = node.walk();
19921 for child in node.named_children(&mut cursor) {
19922 stack.push(child);
19923 }
19924 while let Some(current) = stack.pop() {
19925 match current.kind() {
19926 "ERROR" => {
19930 let mut cursor = current.walk();
19931 stack.extend(current.named_children(&mut cursor));
19932 }
19933 "function_declarator" => saw_function_declarator = true,
19934 _ => return false,
19935 }
19936 }
19937 saw_function_declarator
19938}
19939
19940fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
19941 if node.kind() != "ERROR" {
19942 return false;
19943 }
19944 let mut cursor = node.walk();
19945 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
19946 let [explicit, constructor_error, tail] = named.as_slice() else {
19947 return false;
19948 };
19949 if explicit.kind() != "explicit_function_specifier" || constructor_error.kind() != "ERROR" {
19950 return false;
19951 }
19952 let mut declarators = Vec::new();
19958 let mut stack = vec![*constructor_error];
19959 while let Some(current) = stack.pop() {
19960 match current.kind() {
19961 "ERROR" => {
19962 let mut cursor = current.walk();
19963 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
19964 stack.extend(children.into_iter().rev());
19965 }
19966 "function_declarator" => declarators.push(current),
19967 _ => return false,
19968 }
19969 }
19970 let (constructor, destructor) = match (declarators.as_slice(), tail.kind()) {
19971 ([constructor], "function_declarator") => (*constructor, *tail),
19972 ([constructor, destructor], "macro_modifier_declarator") => (*constructor, *destructor),
19973 _ => return false,
19974 };
19975 let Some(constructor_name) =
19976 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
19977 else {
19978 return false;
19979 };
19980 let Some(destructor_name) =
19981 extract_function_declarator(destructor).and_then(cpp_function_declarator_name_node)
19982 else {
19983 return false;
19984 };
19985 let Some(destroyed_type) = destructor_name.named_child(0) else {
19986 return false;
19987 };
19988 constructor_name.kind() == "identifier"
19989 && destructor_name.kind() == "destructor_name"
19990 && destroyed_type.kind() == "identifier"
19991 && node_text(constructor_name, source) == node_text(destroyed_type, source)
19992}
19993
19994fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
19995 if node.kind() != "compound_statement" {
19996 return false;
19997 }
19998 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
19999 return false;
20000 };
20001 if prefix.kind() == "labeled_statement"
20002 && prefix.named_child(0).is_some_and(|label| {
20003 matches!(
20004 node_text(label, source).trim(),
20005 "public" | "private" | "protected"
20006 )
20007 })
20008 {
20009 return prefix.named_children(&mut prefix.walk()).any(|child| {
20010 child.kind() == "declaration"
20011 && child.has_error()
20012 && child
20013 .named_children(&mut child.walk())
20014 .any(cpp_reparsed_member_error_is_indexable)
20015 });
20016 }
20017 prefix.kind() == "declaration"
20022 && prefix.has_error()
20023 && prefix
20024 .named_children(&mut prefix.walk())
20025 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
20026}
20027
20028fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
20029 if !cpp_reparsed_member_error_is_indexable(node) {
20030 return false;
20031 }
20032 let Some(preproc) = node.next_named_sibling() else {
20033 return false;
20034 };
20035 preproc.kind() == "preproc_if"
20036 && preproc.has_error()
20037 && preproc
20038 .named_children(&mut preproc.walk())
20039 .any(|child| child.kind() == "expression_statement" && child.has_error())
20040 && preproc
20041 .next_named_sibling()
20042 .is_some_and(|body| body.kind() == "compound_statement")
20043}
20044
20045fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
20050 if node.kind() != "function_definition" {
20051 return None;
20052 }
20053 let body = node.child_by_field_name("body")?;
20054 if body.kind() != "compound_statement" {
20055 return None;
20056 }
20057 let open = body.child(0)?;
20058 let close = body.child(body.child_count().checked_sub(1)?)?;
20059 if open.kind() != "{"
20060 || open.is_missing()
20061 || close.kind() != "}"
20062 || close.is_missing()
20063 || close.end_byte() != body.end_byte()
20064 || body.end_byte() != node.end_byte()
20065 {
20066 return None;
20067 }
20068 Some(body)
20069}
20070
20071fn cpp_reparsed_member_function_errors_are_in_body(
20072 node: Node<'_>,
20073 body: Node<'_>,
20074 source: &str,
20075) -> bool {
20076 let mut cursor = node.walk();
20077 node.children(&mut cursor).all(|child| {
20078 same_node(child, body)
20079 || cpp_reparsed_member_attribute_error(child, source)
20080 || cpp_reparsed_member_signature_identifier_errors(child)
20081 || (!child.has_error() && !child.is_error() && !child.is_missing())
20082 })
20083}
20084
20085fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
20093 if !node.has_error() && !node.is_error() && !node.is_missing() {
20094 return false;
20095 }
20096 let mut stack = vec![node];
20097 let mut saw_error = false;
20098 while let Some(current) = stack.pop() {
20099 if current.is_missing() {
20100 return false;
20101 }
20102 if current.kind() == "ERROR" {
20103 saw_error = true;
20104 let mut cursor = current.walk();
20105 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20106 if children
20107 .iter()
20108 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
20109 {
20110 return false;
20111 }
20112 stack.extend(children);
20113 continue;
20114 }
20115 let mut cursor = current.walk();
20116 stack.extend(current.children(&mut cursor));
20117 }
20118 saw_error
20119}
20120
20121fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
20122 node.kind() == "ERROR"
20123 && node.named_child_count() == 1
20124 && node
20125 .named_child(0)
20126 .is_some_and(|attribute| cpp_reparsed_export_macro_identifier(attribute, source))
20127}
20128
20129fn cpp_reparsed_export_macro_identifier(node: Node<'_>, source: &str) -> bool {
20130 node.kind() == "identifier"
20131 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20132}
20133
20134fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
20140 let Some(body) = cpp_reparsed_member_function_body(node) else {
20141 return false;
20142 };
20143 let mut cursor = node.walk();
20144 let named = node
20145 .named_children(&mut cursor)
20146 .filter(|child| child.kind() != "comment")
20147 .collect::<Vec<_>>();
20148 let [type_node, error, attribute, body_node] = named.as_slice() else {
20149 return false;
20150 };
20151 if !same_node(*body_node, body)
20152 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20153 || !cpp_reparsed_export_macro_identifier(*attribute, source)
20154 || error.kind() != "ERROR"
20155 || error.named_child_count() != 1
20156 {
20157 return false;
20158 }
20159 error
20160 .named_child(0)
20161 .is_some_and(cpp_reparsed_attribute_callable_declarator)
20162}
20163
20164fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
20165 cpp_structured_type_path(node, source).is_some()
20166 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20167}
20168
20169fn cpp_reparsed_member_return_and_declarator_are_indexable(
20170 return_error: Node<'_>,
20171 declarator: Node<'_>,
20172 body_node: Node<'_>,
20173 body: Node<'_>,
20174 source: &str,
20175) -> bool {
20176 return_error.kind() == "ERROR"
20177 && return_error.named_child_count() == 1
20178 && same_node(body_node, body)
20179 && return_error.named_child(0).is_some_and(|return_type| {
20180 cpp_reparsed_member_return_type_is_indexable(return_type, source)
20181 })
20182 && extract_function_declarator(declarator)
20183 .and_then(cpp_function_declarator_name_node)
20184 .is_some()
20185}
20186
20187fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20188 let Some(body) = cpp_reparsed_member_function_body(node) else {
20189 return false;
20190 };
20191 let mut cursor = node.walk();
20192 let named = node
20193 .named_children(&mut cursor)
20194 .filter(|child| child.kind() != "comment")
20195 .collect::<Vec<_>>();
20196 let [friend, return_error, declarator, body_node] = named.as_slice() else {
20197 return false;
20198 };
20199 friend.kind() == "type_identifier"
20200 && node_text(*friend, source) == "friend"
20201 && cpp_reparsed_member_return_and_declarator_are_indexable(
20202 *return_error,
20203 *declarator,
20204 *body_node,
20205 body,
20206 source,
20207 )
20208}
20209
20210fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20211 let Some(body) = cpp_reparsed_member_function_body(node) else {
20212 return false;
20213 };
20214 let mut cursor = node.walk();
20215 let named = node
20216 .named_children(&mut cursor)
20217 .filter(|child| child.kind() != "comment")
20218 .collect::<Vec<_>>();
20219 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
20220 return false;
20221 };
20222 prefix
20223 .iter()
20224 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
20225 && attribute.kind() == "type_identifier"
20226 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20227 && cpp_reparsed_member_return_and_declarator_are_indexable(
20228 *return_error,
20229 *declarator,
20230 *body_node,
20231 body,
20232 source,
20233 )
20234}
20235
20236fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20242 let Some(body) = cpp_reparsed_member_function_body(node) else {
20243 return false;
20244 };
20245 let mut cursor = node.walk();
20246 let named = node
20247 .named_children(&mut cursor)
20248 .filter(|child| child.kind() != "comment")
20249 .collect::<Vec<_>>();
20250 let (template_type, return_part, declarator, body_node) = match named.as_slice() {
20255 [template_type, return_error, declarator, body_node] => {
20256 let Some(return_type) = return_error.named_child(0) else {
20257 return false;
20258 };
20259 if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
20260 return false;
20261 }
20262 (*template_type, return_type, *declarator, *body_node)
20263 }
20264 [template_type, declarator, body_node] => {
20265 let Some(return_type) = (declarator.kind() == "macro_modifier_declarator")
20266 .then(|| {
20267 let mut cursor = declarator.walk();
20268 declarator
20269 .named_children(&mut cursor)
20270 .find(|child| child.kind() == "macro_modifier")
20271 })
20272 .flatten()
20273 .and_then(macro_modifier_identifier)
20274 else {
20275 return false;
20276 };
20277 let Some(function) = declarator.child_by_field_name("declarator") else {
20278 return false;
20279 };
20280 (*template_type, return_type, function, *body_node)
20281 }
20282 _ => return false,
20283 };
20284 let Some(template_name) = template_type.child_by_field_name("name") else {
20285 return false;
20286 };
20287 let Some(arguments) = template_type.child_by_field_name("arguments") else {
20288 return false;
20289 };
20290 let mut cursor = template_type.walk();
20291 let template_errors = template_type
20292 .named_children(&mut cursor)
20293 .filter(|child| child.kind() == "ERROR")
20294 .collect::<Vec<_>>();
20295 let [comment_error] = template_errors.as_slice() else {
20296 return false;
20297 };
20298 let mut cursor = comment_error.walk();
20299 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
20300 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
20301 return false;
20302 };
20303 same_node(body_node, body)
20304 && template_type.kind() == "template_type"
20305 && template_name.kind() == "type_identifier"
20306 && matches!(
20307 node_text(template_name, source).trim(),
20308 "public" | "private" | "protected"
20309 )
20310 && arguments.kind() == "template_argument_list"
20311 && arguments.named_child_count() > 0
20312 && !arguments.has_error()
20313 && !colon.is_named()
20314 && colon.kind() == ":"
20315 && comments.iter().all(|child| child.kind() == "comment")
20316 && !template_keyword.is_named()
20317 && template_keyword.kind() == "template"
20318 && cpp_reparsed_member_return_type_is_indexable(return_part, source)
20319 && extract_function_declarator(declarator)
20320 .and_then(cpp_function_declarator_name_node)
20321 .is_some()
20322}
20323
20324fn cpp_reparsed_preprocessor_constructor<'tree>(
20330 node: Node<'tree>,
20331 class_name: &str,
20332 source: &str,
20333) -> Option<Node<'tree>> {
20334 if node.kind() != "labeled_statement" {
20335 return None;
20336 }
20337 let mut cursor = node.walk();
20338 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20339 let [label, directive_error, declaration] = named.as_slice() else {
20340 return None;
20341 };
20342 if label.kind() != "statement_identifier"
20343 || !matches!(
20344 node_text(*label, source),
20345 "public" | "private" | "protected"
20346 )
20347 || directive_error.kind() != "ERROR"
20348 || directive_error.child_count() != 1
20349 || directive_error
20350 .child(0)
20351 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
20352 || declaration.kind() != "declaration"
20353 || declaration.named_child_count() != 2
20354 {
20355 return None;
20356 }
20357 let apparent_type = declaration.child_by_field_name("type")?;
20358 if apparent_type.kind() != "type_identifier"
20359 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
20360 {
20361 return None;
20362 }
20363 let declarator = declaration.child_by_field_name("declarator")?;
20364 let function = extract_function_declarator(declarator)?;
20365 let name = cpp_function_declarator_name_node(function)?;
20366 (node_text(name, source) == class_name).then_some(*declaration)
20367}
20368
20369fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
20370 if extract_function_declarator(node)
20371 .and_then(cpp_function_declarator_name_node)
20372 .is_some()
20373 {
20374 return true;
20375 }
20376 node.kind() == "init_declarator"
20377 && node
20378 .child_by_field_name("declarator")
20379 .is_some_and(|declarator| declarator.kind() == "identifier")
20380 && node
20381 .child_by_field_name("value")
20382 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
20383}
20384
20385fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
20390 if node.kind() != "ERROR" || node.named_child_count() != 3 {
20391 return false;
20392 }
20393 let mut cursor = node.walk();
20394 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20395 let [type_node, function_declarator, attribute] = named.as_slice() else {
20396 return false;
20397 };
20398 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20399 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
20400 || attribute.kind() != "identifier"
20401 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20402 {
20403 return false;
20404 }
20405 let Some(preproc) =
20406 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20407 else {
20408 return false;
20409 };
20410 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
20411 .filter(|sibling| sibling.kind() == "compound_statement")
20412 else {
20413 return false;
20414 };
20415 let Some(open) = body.child(0) else {
20416 return false;
20417 };
20418 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
20419 return false;
20420 };
20421 let Some(condition) = preproc.child_by_field_name("condition") else {
20422 return false;
20423 };
20424 let mut cursor = preproc.walk();
20425 let payload = preproc
20426 .named_children(&mut cursor)
20427 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
20428 .collect::<Vec<_>>();
20429 let [requires_statement] = payload.as_slice() else {
20430 return false;
20431 };
20432 let requires_clause = requires_statement.named_child(0);
20433
20434 open.kind() == "{"
20435 && !open.is_missing()
20436 && close.kind() == "}"
20437 && !close.is_missing()
20438 && close.end_byte() == body.end_byte()
20439 && requires_statement.kind() == "expression_statement"
20440 && requires_statement.named_child_count() == 1
20441 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
20442}
20443
20444fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20445 let mut sibling = node.next_named_sibling();
20446 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20447 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
20448 }
20449 sibling
20450}
20451
20452fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20453 let mut sibling = node.prev_named_sibling();
20454 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20455 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
20456 }
20457 sibling
20458}
20459
20460fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
20461 let Some(preproc) =
20462 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20463 else {
20464 return false;
20465 };
20466 let Some(error) =
20467 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
20468 else {
20469 return false;
20470 };
20471 cpp_reparsed_attribute_requires_error(error, source)
20472}
20473
20474fn cpp_reparsed_template_macro_prefix_parameter_name<'tree>(
20475 parameter: Node<'tree>,
20476 macro_name: Node<'tree>,
20477 message: Node<'tree>,
20478 source: &str,
20479) -> Option<Node<'tree>> {
20480 let parameter_name = parameter.named_child(0)?;
20481 (parameter.kind() == "type_parameter_declaration"
20482 && parameter_name.kind() == "type_identifier"
20483 && matches!(macro_name.kind(), "identifier" | "type_identifier")
20484 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source)))
20485 && message.kind() == "string_literal")
20486 .then_some(parameter_name)
20487}
20488
20489fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
20490 node: Node<'tree>,
20491 source: &str,
20492) -> Option<Node<'tree>> {
20493 if node.kind() != "ERROR" {
20494 return None;
20495 }
20496 let mut cursor = node.walk();
20497 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20498 let [parameter, macro_name, message] = named.as_slice() else {
20499 return None;
20500 };
20501 cpp_reparsed_template_macro_prefix_parameter_name(*parameter, *macro_name, *message, source)
20502}
20503
20504fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
20509 node: Node<'tree>,
20510 source: &str,
20511) -> Option<Node<'tree>> {
20512 if node.kind() != "ERROR" {
20513 return None;
20514 }
20515 let mut cursor = node.walk();
20516 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20517 let [parameter, macro_name, message, constraint] = named.as_slice() else {
20518 return None;
20519 };
20520 let parameter_name = cpp_reparsed_template_macro_prefix_parameter_name(
20521 *parameter,
20522 *macro_name,
20523 *message,
20524 source,
20525 )?;
20526 let constraint_scope = constraint.child_by_field_name("scope")?;
20527 let constraint_template = constraint.child_by_field_name("name")?;
20528 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
20529 let mut argument_cursor = constraint_arguments.walk();
20530 let constraint_types = constraint_arguments
20531 .named_children(&mut argument_cursor)
20532 .collect::<Vec<_>>();
20533 if constraint.kind() != "qualified_identifier"
20534 || constraint_scope.kind() != "namespace_identifier"
20535 || !matches!(
20536 constraint_template.kind(),
20537 "template_function" | "template_type"
20538 )
20539 || !matches!(constraint_types.as_slice(), [left, right]
20540 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
20541 || constraint_arguments.has_error()
20542 {
20543 return None;
20544 }
20545 let parameter_text = node_text(parameter_name, source);
20546 let mut stack = constraint_types;
20547 while let Some(current) = stack.pop() {
20548 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
20549 return Some(parameter_name);
20550 }
20551 let mut cursor = current.walk();
20552 stack.extend(current.named_children(&mut cursor));
20553 }
20554 None
20555}
20556
20557fn cpp_reparsed_template_macro_companion_is_indexable(
20558 node: Node<'_>,
20559 parameter_name: Node<'_>,
20560 source: &str,
20561) -> bool {
20562 let Some(body) = cpp_reparsed_member_function_body(node) else {
20563 return false;
20564 };
20565 let mut cursor = node.walk();
20566 let named = node
20567 .named_children(&mut cursor)
20568 .filter(|child| child.kind() != "comment")
20569 .collect::<Vec<_>>();
20570 let (constraint, close_error, storage, return_type, declarator, body_node) = match named
20575 .as_slice()
20576 {
20577 [
20578 constraint,
20579 close_error,
20580 storage,
20581 return_error,
20582 declarator,
20583 body_node,
20584 ] => {
20585 let Some(return_type) = return_error.named_child(0) else {
20586 return false;
20587 };
20588 if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
20589 return false;
20590 }
20591 (
20592 *constraint,
20593 *close_error,
20594 *storage,
20595 return_type,
20596 *declarator,
20597 *body_node,
20598 )
20599 }
20600 [constraint, close_error, storage, declarator, body_node] => {
20601 let Some((return_type, function)) = macro_modifier_declarator_parts(*declarator) else {
20602 return false;
20603 };
20604 (
20605 *constraint,
20606 *close_error,
20607 *storage,
20608 return_type,
20609 function,
20610 *body_node,
20611 )
20612 }
20613 _ => return false,
20614 };
20615 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
20616 return false;
20617 };
20618 let Some(constraint_template) = constraint.child_by_field_name("name") else {
20619 return false;
20620 };
20621 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
20622 return false;
20623 };
20624 let mut cursor = constraint_arguments.walk();
20625 let constraint_types = constraint_arguments
20626 .named_children(&mut cursor)
20627 .collect::<Vec<_>>();
20628 same_node(body_node, body)
20629 && constraint.kind() == "qualified_identifier"
20630 && constraint_scope.kind() == "namespace_identifier"
20631 && constraint_template.kind() == "template_type"
20632 && matches!(constraint_types.as_slice(), [left, right]
20633 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
20634 && !constraint_arguments.has_error()
20635 && close_error.kind() == "ERROR"
20636 && close_error.named_child_count() == 0
20637 && storage.kind() == "storage_class_specifier"
20638 && return_type.kind() == "identifier"
20639 && node_text(return_type, source) == node_text(parameter_name, source)
20640 && extract_function_declarator(declarator)
20641 .and_then(cpp_function_declarator_name_node)
20642 .is_some()
20643}
20644
20645fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
20646 node: Node<'tree>,
20647 parameter_name: Node<'_>,
20648 source: &str,
20649) -> Option<Node<'tree>> {
20650 let body = cpp_reparsed_member_function_body(node)?;
20651 let constraint = node.child_by_field_name("type")?;
20652 let constraint_template = constraint.child_by_field_name("name")?;
20653 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
20654 let mut argument_cursor = constraint_arguments.walk();
20655 let constraint_types = constraint_arguments
20656 .named_children(&mut argument_cursor)
20657 .collect::<Vec<_>>();
20658 if constraint.kind() != "qualified_identifier"
20659 || constraint_template.kind() != "template_type"
20660 || !matches!(constraint_types.as_slice(), [left, right]
20661 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
20662 || constraint_arguments.has_error()
20663 || node
20664 .child_by_field_name("body")
20665 .is_none_or(|candidate| !same_node(candidate, body))
20666 {
20667 return None;
20668 }
20669
20670 let mut cursor = node.walk();
20671 let recovery_errors = node
20672 .named_children(&mut cursor)
20673 .filter(|child| child.kind() == "ERROR")
20674 .collect::<Vec<_>>();
20675 if !recovery_errors
20676 .iter()
20677 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
20678 || !recovery_errors.iter().all(|error| {
20679 error.named_child_count() == 0
20680 || cpp_reparsed_constraint_macro_error(*error, source)
20681 || (error.named_child_count() == 1
20682 && error
20683 .named_child(0)
20684 .is_some_and(|child| child.kind() == "function_declarator"))
20685 })
20686 {
20687 return None;
20688 }
20689
20690 let parameter_text = node_text(parameter_name, source);
20691 let mut declarators = node
20692 .child_by_field_name("declarator")
20693 .and_then(extract_function_declarator)
20694 .into_iter()
20695 .collect::<Vec<_>>();
20696 for error in recovery_errors {
20697 let mut stack = vec![error];
20698 while let Some(current) = stack.pop() {
20699 if current.kind() == "function_declarator" {
20700 declarators.push(current);
20701 }
20702 let mut cursor = current.walk();
20703 stack.extend(current.named_children(&mut cursor));
20704 }
20705 }
20706 declarators.into_iter().find(|declarator| {
20707 cpp_function_declarator_name_node(*declarator)
20708 .is_some_and(|name| name.kind() == "identifier")
20709 && declarator
20710 .child_by_field_name("parameters")
20711 .is_some_and(|parameters| {
20712 parameters
20713 .named_children(&mut parameters.walk())
20714 .filter_map(|parameter| parameter.child_by_field_name("type"))
20715 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
20716 })
20717 })
20718}
20719
20720fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
20721 node: Node<'_>,
20722 parameter_name: Node<'_>,
20723 source: &str,
20724) -> bool {
20725 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
20726}
20727
20728fn cpp_reparsed_template_macro_function_companion_is_indexable(
20729 node: Node<'_>,
20730 parameter_name: Node<'_>,
20731 source: &str,
20732) -> bool {
20733 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
20734 return false;
20735 }
20736 let Some(return_type) = node.child_by_field_name("type") else {
20737 return false;
20738 };
20739 let Some(function_declarator) = node
20740 .child_by_field_name("declarator")
20741 .and_then(extract_function_declarator)
20742 else {
20743 return false;
20744 };
20745 if cpp_function_declarator_name_node(function_declarator).is_none()
20746 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
20747 {
20748 return false;
20749 }
20750 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
20751 return false;
20752 };
20753 let parameter_text = node_text(parameter_name, source);
20754 parameters
20755 .named_children(&mut parameters.walk())
20756 .any(|parameter| {
20757 parameter
20758 .child_by_field_name("type")
20759 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
20760 })
20761}
20762
20763fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
20764 if node.kind() != "ERROR" {
20765 return false;
20766 }
20767 let mut stack = vec![node];
20768 while let Some(current) = stack.pop() {
20769 let macro_shape = match current.kind() {
20770 "call_expression" => current
20771 .child_by_field_name("function")
20772 .zip(current.child_by_field_name("arguments")),
20773 "init_declarator" => current
20774 .child_by_field_name("declarator")
20775 .zip(current.child_by_field_name("value")),
20776 _ => None,
20777 };
20778 if let Some((name, arguments)) = macro_shape
20779 && name.kind() == "identifier"
20780 && arguments.kind() == "argument_list"
20781 && arguments.named_child_count() >= 2
20782 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
20783 {
20784 return true;
20785 }
20786 let mut cursor = current.walk();
20787 stack.extend(current.named_children(&mut cursor));
20788 }
20789 false
20790}
20791
20792fn cpp_recovered_template_macro_constructor<'tree>(
20793 node: Node<'tree>,
20794 source: &str,
20795) -> Option<(Node<'tree>, Node<'tree>)> {
20796 let mut prefix = node.prev_named_sibling()?;
20797 while prefix.kind() == "comment" {
20798 prefix = prefix.prev_named_sibling()?;
20799 }
20800 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
20801 let parameter = parameter_name
20802 .parent()
20803 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
20804 let declarator =
20805 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
20806 Some((declarator, parameter))
20807}
20808
20809fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
20810 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
20811 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
20812 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
20813 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
20814 function,
20815 parameter_name,
20816 source,
20817 )
20818 });
20819 }
20820 let Some(parameter_name) =
20821 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
20822 else {
20823 return false;
20824 };
20825 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
20826 cpp_reparsed_template_macro_function_companion_is_indexable(
20827 function,
20828 parameter_name,
20829 source,
20830 )
20831 })
20832}
20833
20834fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20835 let function_name = node
20836 .child_by_field_name("declarator")
20837 .and_then(extract_function_declarator)
20838 .and_then(cpp_function_declarator_name_node);
20839 if let Some(body) = cpp_reparsed_member_function_body(node)
20840 && function_name.is_some()
20841 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
20842 {
20843 return true;
20844 }
20845 cpp_reparsed_attribute_member_function(node, source)
20846 || cpp_reparsed_friend_function_is_indexable(node, source)
20847 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
20848 || cpp_reparsed_access_template_function_is_indexable(node, source)
20849 || cpp_recovered_template_macro_constructor(node, source).is_some()
20850}
20851
20852fn cpp_reparsed_macro_attribute_member_sequence(
20859 children: &[Node<'_>],
20860 index: usize,
20861 source: &str,
20862) -> bool {
20863 let Some(prefix) = children.get(index).copied() else {
20864 return false;
20865 };
20866 let declaration = if prefix.kind() == "labeled_statement" {
20867 prefix
20868 .named_child(prefix.named_child_count().saturating_sub(1))
20869 .filter(|child| child.kind() == "declaration")
20870 } else {
20871 (prefix.kind() == "declaration").then_some(prefix)
20872 };
20873 let Some(declaration) = declaration else {
20874 return false;
20875 };
20876 if !declaration.has_error()
20877 || declaration
20878 .child_by_field_name("declarator")
20879 .and_then(extract_function_declarator)
20880 .and_then(cpp_function_declarator_name_node)
20881 .is_none()
20882 {
20883 return false;
20884 }
20885 let Some(attribute_statement) = children.get(index + 1).copied() else {
20886 return false;
20887 };
20888 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
20889 .then(|| attribute_statement.named_child(0))
20890 .flatten()
20891 .filter(|child| child.kind() == "call_expression")
20892 else {
20893 return false;
20894 };
20895 let Some(attribute_name) = attribute_call
20896 .child_by_field_name("function")
20897 .filter(|function| function.kind() == "identifier")
20898 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
20899 else {
20900 return false;
20901 };
20902 if !cpp_export_macro_token(&attribute_name) {
20903 return false;
20904 }
20905 let Some(body) = children.get(index + 2).copied() else {
20906 return false;
20907 };
20908 body.kind() == "compound_statement"
20909 && body.child(0).is_some_and(|open| open.kind() == "{")
20910 && body
20911 .child(body.child_count().saturating_sub(1))
20912 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
20913 && declaration.end_byte() <= attribute_statement.start_byte()
20914 && attribute_statement.end_byte() <= body.start_byte()
20915}
20916
20917fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
20925 if node.kind() != "ERROR" {
20926 return false;
20927 }
20928 let run = stranded_declaration_run(node, source);
20929 run.complete && !run.declarations.is_empty()
20930}
20931
20932fn cpp_reparsed_bare_constructor_call<'tree>(
20944 node: Node<'tree>,
20945 class_name: &str,
20946 source: &str,
20947) -> Option<Node<'tree>> {
20948 if node.kind() != "expression_statement" {
20949 return None;
20950 }
20951 let call = node
20952 .named_child(0)
20953 .filter(|child| child.kind() == "call_expression")?;
20954 let function = call.child_by_field_name("function")?;
20955 (function.kind() == "identifier" && node_text(function, source).trim() == class_name)
20956 .then_some(call)
20957}
20958
20959fn cpp_reparsed_defaulted_constructor_is_indexable(
20960 node: Node<'_>,
20961 members: &[Node<'_>],
20962 class_name: Option<&str>,
20963 source: &str,
20964) -> bool {
20965 let Some(class_name) = class_name else {
20966 return false;
20967 };
20968 if node.kind() != "expression_statement" || node.named_child_count() != 1 {
20969 return false;
20970 }
20971 let Some(assignment) = node.named_child(0) else {
20972 return false;
20973 };
20974 if assignment.kind() != "assignment_expression" || assignment.child_count() != 3 {
20975 return false;
20976 }
20977 if assignment
20978 .child(1)
20979 .is_none_or(|operator| operator.kind() != "=")
20980 {
20981 return false;
20982 }
20983 let Some(call) = assignment.child_by_field_name("left") else {
20984 return false;
20985 };
20986 let Some(default_value) = assignment.child_by_field_name("right") else {
20987 return false;
20988 };
20989 let Some(function) = call.child_by_field_name("function") else {
20990 return false;
20991 };
20992 let Some(arguments) = call.child_by_field_name("arguments") else {
20993 return false;
20994 };
20995 if call.kind() != "call_expression"
20996 || function.kind() != "identifier"
20997 || arguments.kind() != "argument_list"
20998 || arguments.named_child_count() != 0
20999 || default_value.kind() != "identifier"
21000 || node_text(default_value, source) != "default"
21001 || node_text(function, source) != class_name
21002 {
21003 return false;
21004 }
21005 members.iter().any(|member| {
21006 if same_node(*member, node) {
21007 return false;
21008 }
21009 cpp_reparsed_member_constructor_name(*member, source).is_some_and(|name| name == class_name)
21010 })
21011}
21012
21013fn cpp_reparsed_member_constructor_name(node: Node<'_>, source: &str) -> Option<String> {
21018 let function_declarator = match node.kind() {
21019 "function_definition"
21020 if !node.has_error() && node.child_by_field_name("type").is_none() =>
21021 {
21022 let has_body = node
21023 .child_by_field_name("body")
21024 .is_some_and(|body| body.kind() == "compound_statement");
21025 let has_default_method_clause = node
21026 .named_children(&mut node.walk())
21027 .any(|child| child.kind() == "default_method_clause");
21028 if !has_body && !has_default_method_clause {
21029 return None;
21030 }
21031 node.child_by_field_name("declarator")
21032 .and_then(extract_function_declarator)
21033 }
21034 "ERROR" if cpp_reparsed_member_error_is_indexable(node) => {
21035 let mut stack = node.named_children(&mut node.walk()).collect::<Vec<_>>();
21036 let mut candidate = None;
21037 while let Some(current) = stack.pop() {
21038 if current.kind() == "function_declarator" {
21039 if candidate.is_some() {
21040 return None;
21041 }
21042 candidate = Some(current);
21043 continue;
21044 }
21045 if current.kind() == "ERROR" {
21046 let mut cursor = current.walk();
21047 stack.extend(current.named_children(&mut cursor));
21048 }
21049 }
21050 candidate
21051 }
21052 _ => None,
21053 }?;
21054 cpp_function_declarator_name_node(function_declarator)
21055 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
21056}
21057
21058fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
21059 cpp_reparsed_members_are_indexable_for_class(root, source, None)
21060}
21061
21062fn cpp_reparsed_members_are_indexable_for_class(
21063 root: Node<'_>,
21064 source: &str,
21065 class_name: Option<&str>,
21066) -> bool {
21067 let mut cursor = root.walk();
21068 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
21069 let mut saw_member = false;
21070 let mut index = 0;
21071 while index < children.len() {
21072 let child = children[index];
21073 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
21074 saw_member = true;
21075 index += 3;
21076 continue;
21077 }
21078 if let Some(recovered) = fragmented_class_body(child, source) {
21079 let Some(tree) = cpp_reparse_fragmented_class_body(
21080 source,
21081 recovered.body.reparse_start,
21082 recovered.body.reparse_end,
21083 ) else {
21084 return false;
21085 };
21086 if !cpp_reparsed_members_are_indexable_for_class(
21087 tree.root_node(),
21088 source,
21089 Some(recovered.name.as_str()),
21090 ) {
21091 return false;
21092 }
21093 saw_member = true;
21094 index += 1;
21095 while index < children.len()
21096 && children[index].end_byte() <= recovered.body.class_range.end_byte
21097 {
21098 index += 1;
21099 }
21100 continue;
21101 }
21102 match child.kind() {
21103 "comment" => {}
21104 "labeled_statement" => saw_member = true,
21105 "function_definition" => {
21106 if child.has_error()
21107 && !cpp_reparsed_member_function_is_indexable(child, source)
21108 && cpp_sentinel_macro_region(child, source).is_none()
21109 {
21110 return false;
21111 }
21112 saw_member = true;
21113 }
21114 "expression_statement" if is_string_attribute_macro_statement(child) => {}
21118 "expression_statement"
21119 if cpp_reparsed_defaulted_constructor_is_indexable(
21120 child, &children, class_name, source,
21121 ) =>
21122 {
21123 saw_member = true;
21124 }
21125 "expression_statement"
21126 if class_name.is_some_and(|class_name| {
21127 cpp_reparsed_bare_constructor_call(child, class_name, source).is_some()
21128 }) =>
21129 {
21130 saw_member = true;
21131 }
21132 "ERROR"
21133 if (cpp_reparsed_member_error_is_indexable(child)
21134 || cpp_reparsed_adjacent_copy_control_error(child, source)
21135 || cpp_reparsed_stranded_member_error(child, source))
21136 && (child
21137 .next_named_sibling()
21138 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
21139 || cpp_reparsed_member_error_with_preprocessed_body(child)
21140 || (cpp_reparsed_adjacent_copy_control_error(child, source)
21144 && child
21145 .next_named_sibling()
21146 .is_some_and(|sibling| sibling.kind() == "compound_statement"))) =>
21147 {
21148 saw_member = true;
21149 }
21150 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
21151 saw_member = true;
21152 }
21153 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
21154 saw_member = true;
21155 }
21156 "expression_statement"
21157 if cpp_is_stray_semicolon(child, source)
21158 && child.prev_named_sibling().is_some_and(|error| {
21159 cpp_reparsed_member_error_is_indexable(error)
21160 || cpp_reparsed_adjacent_copy_control_error(error, source)
21161 || cpp_reparsed_stranded_member_error(error, source)
21162 }) =>
21163 {
21164 saw_member = true;
21165 }
21166 "compound_statement"
21167 if cpp_reparsed_constructor_body_is_indexable(child, source)
21168 || cpp_reparsed_attribute_requires_body(child, source)
21169 || child.prev_named_sibling().is_some_and(|error| {
21170 cpp_reparsed_adjacent_copy_control_error(error, source)
21171 }) =>
21172 {
21173 saw_member = true;
21174 }
21175 "macro_type_specifier"
21183 if child
21184 .child_by_field_name("name")
21185 .is_some_and(|name| name.kind() == "identifier")
21186 && child
21187 .child_by_field_name("type")
21188 .is_some_and(|type_node| type_node.kind() == "type_descriptor")
21189 && child
21190 .next_sibling()
21191 .is_some_and(|sibling| sibling.kind() == ";" && !sibling.is_missing()) =>
21192 {
21193 saw_member = true;
21194 }
21195 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
21196 _ => return false,
21197 }
21198 index += 1;
21199 }
21200 saw_member
21201}
21202
21203fn cpp_reparsed_synthetic_initializer_constructor_range(
21211 root: Node<'_>,
21212 class_name: &str,
21213 source: &str,
21214 constructor_end: usize,
21215) -> Option<std::ops::Range<usize>> {
21216 let mut stack = {
21217 let mut cursor = root.walk();
21218 root.named_children(&mut cursor).collect::<Vec<_>>()
21219 };
21220 while let Some(current) = stack.pop() {
21221 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
21222 current,
21223 class_name,
21224 source,
21225 constructor_end,
21226 ) {
21227 return Some(range);
21228 }
21229 if current.kind() == "ERROR" {
21230 let mut cursor = current.walk();
21231 stack.extend(current.named_children(&mut cursor));
21232 }
21233 }
21234 None
21235}
21236
21237fn cpp_reparsed_merged_inline_constructor<'tree>(
21244 root: Node<'tree>,
21245 class_name: &str,
21246 source: &str,
21247) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
21248 let mut stack = vec![root];
21249 while let Some(current) = stack.pop() {
21250 if current.kind() != "labeled_statement" {
21251 let mut cursor = current.walk();
21252 stack.extend(current.named_children(&mut cursor));
21253 continue;
21254 }
21255 let declaration = current
21256 .named_children(&mut current.walk())
21257 .find(|child| child.kind() == "declaration")?;
21258 if declaration
21259 .child_by_field_name("type")
21260 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
21261 {
21262 continue;
21263 }
21264 let following = declaration
21265 .child_by_field_name("declarator")
21266 .and_then(extract_function_declarator)
21267 .and_then(cpp_function_declarator_name_node);
21268 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
21269 continue;
21270 }
21271 let mut declaration_cursor = declaration.walk();
21272 let Some(error) = declaration
21273 .named_children(&mut declaration_cursor)
21274 .find(|child| child.kind() == "ERROR")
21275 else {
21276 continue;
21277 };
21278 let mut error_cursor = error.walk();
21279 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
21280 let Some(constructor) = error_children.iter().copied().find(|child| {
21281 child.kind() == "function_declarator"
21282 && cpp_function_declarator_name_node(*child)
21283 .is_some_and(|name| node_text(name, source).trim() == class_name)
21284 }) else {
21285 continue;
21286 };
21287 let Some(body) = error_children.iter().copied().find_map(|child| {
21288 (child.kind() == "init_declarator")
21289 .then(|| child.child_by_field_name("value"))
21290 .flatten()
21291 .filter(|value| value.kind() == "initializer_list")
21292 }) else {
21293 continue;
21294 };
21295 if constructor.end_byte() > body.start_byte() {
21296 continue;
21297 }
21298 return Some((constructor.start_byte()..body.end_byte(), body));
21299 }
21300 None
21301}
21302
21303fn cpp_reparsed_synthetic_initializer_constructor(
21304 node: Node<'_>,
21305 class_name: &str,
21306 source: &str,
21307 constructor_end: usize,
21308) -> Option<std::ops::Range<usize>> {
21309 if node.kind() != "labeled_statement" {
21310 return None;
21311 }
21312 let mut cursor = node.walk();
21313 let named = node
21314 .named_children(&mut cursor)
21315 .filter(|child| child.kind() != "comment")
21316 .collect::<Vec<_>>();
21317 let label = named.first()?;
21318 if label.kind() != "statement_identifier"
21319 || !matches!(
21320 node_text(*label, source).trim(),
21321 "public" | "private" | "protected"
21322 )
21323 {
21324 return None;
21325 }
21326 let call_error_index = named.iter().position(|child| {
21327 if child.kind() != "ERROR" {
21328 return false;
21329 }
21330 let type_child = (0..child.child_count()).find_map(|index| {
21335 let candidate = child.child(index)?;
21336 (child.field_name_for_child(index as u32) == Some("type")
21337 && candidate.kind() == "type_identifier"
21338 && node_text(candidate, source).trim() == class_name)
21339 .then_some(candidate)
21340 });
21341 if type_child.is_some() {
21342 return true;
21343 }
21344 let mut stack = vec![*child];
21345 while let Some(current) = stack.pop() {
21346 if current.kind() == "call_expression"
21347 && current
21348 .child_by_field_name("function")
21349 .is_some_and(|function| {
21350 function.kind() == "identifier"
21351 && node_text(function, source).trim() == class_name
21352 })
21353 {
21354 return true;
21355 }
21356 let mut cursor = current.walk();
21357 stack.extend(current.named_children(&mut cursor));
21358 }
21359 false
21360 })?;
21361 let constructor_start = {
21362 let head = named[call_error_index];
21363 let type_child = (0..head.child_count()).find_map(|index| {
21364 let candidate = head.child(index)?;
21365 (head.field_name_for_child(index as u32) == Some("type")
21366 && candidate.kind() == "type_identifier"
21367 && node_text(candidate, source).trim() == class_name)
21368 .then_some(candidate)
21369 });
21370 if let Some(type_node) = type_child {
21371 type_node.start_byte()
21372 } else {
21373 let mut stack = vec![head];
21374 let mut found = None;
21375 while let Some(current) = stack.pop() {
21376 if current.kind() == "call_expression"
21377 && current
21378 .child_by_field_name("function")
21379 .is_some_and(|function| {
21380 function.kind() == "identifier"
21381 && node_text(function, source).trim() == class_name
21382 })
21383 {
21384 found = Some(current);
21385 break;
21386 }
21387 let mut cursor = current.walk();
21388 stack.extend(current.named_children(&mut cursor));
21389 }
21390 found?.start_byte()
21391 }
21392 };
21393 named.iter().skip(call_error_index + 1).find(|child| {
21394 child.kind() == "declaration" && child.has_error() && {
21395 let mut cursor = child.walk();
21396 child.named_children(&mut cursor).any(|declarator| {
21397 declarator.kind() == "init_declarator"
21398 && declarator
21399 .child_by_field_name("declarator")
21400 .is_some_and(|declarator| declarator.kind() == "function_declarator")
21401 && declarator
21402 .child_by_field_name("value")
21403 .is_some_and(|value| value.kind() == "initializer_list")
21404 })
21405 }
21406 })?;
21407 Some(constructor_start..constructor_end)
21408}
21409
21410fn cpp_reparsed_exact_constructor_declarator<'tree>(
21411 root: Node<'tree>,
21412 start: usize,
21413 class_name: &str,
21414 source: &str,
21415) -> Option<Node<'tree>> {
21416 let mut candidate = None;
21417 let mut stack = vec![root];
21418 while let Some(current) = stack.pop() {
21419 if current.kind() == "function_declarator"
21420 && current.start_byte() == start
21421 && cpp_function_declarator_name_node(current)
21422 .is_some_and(|name| node_text(name, source).trim() == class_name)
21423 {
21424 if candidate.is_some() {
21425 return None;
21426 }
21427 candidate = Some(current);
21428 continue;
21429 }
21430 let mut cursor = current.walk();
21431 stack.extend(current.named_children(&mut cursor));
21432 }
21433 candidate
21434}
21435
21436fn cpp_is_indexable_item_kind(kind: &str) -> bool {
21437 matches!(
21438 kind,
21439 "namespace_definition"
21440 | "class_specifier"
21441 | "struct_specifier"
21442 | "union_specifier"
21443 | "enum_specifier"
21444 | "function_definition"
21445 | "template_declaration"
21446 | "declaration"
21447 | "field_declaration"
21448 | "alias_declaration"
21449 | "static_assert_declaration"
21450 | "type_definition"
21451 | "using_declaration"
21452 | "linkage_specification"
21453 | "preproc_def"
21454 | "preproc_function_def"
21455 | "preproc_include"
21456 | "preproc_if"
21457 | "preproc_ifdef"
21458 | "preproc_call"
21459 )
21460}
21461
21462#[cfg(test)]
21463mod tests {
21464 use super::*;
21465 use crate::adapter::parse_cpp_file;
21466 use brokk_bifrost_core::analyzer::parsed_file::{
21467 finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
21468 };
21469 use brokk_bifrost_test_macros::hardwon;
21470 use std::fmt::Write;
21471
21472 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
21473 let tree = crate::diagnostics::parse_cpp_tree(source);
21474 let file = ProjectFile::new(std::env::temp_dir(), name);
21475 parse_cpp_file(&file, source, &tree)
21476 }
21477
21478 #[test]
21479 fn native_must_use_attribute_nodes_are_structural() {
21480 let source = "int must_use_value() __attribute__((warn_unused_result));\nint must_use_value() { return 1; }\n[[nodiscard]] int standard_value();\n";
21481 let tree = crate::diagnostics::parse_cpp_tree(source);
21482 let root = tree.root_node();
21483 let mut declarations = Vec::new();
21484 walk_named_tree_preorder(root, true, |node| {
21485 if matches!(
21486 node.kind(),
21487 "declaration" | "function_definition" | "field_declaration"
21488 ) {
21489 declarations.push(node);
21490 }
21491 WalkControl::Continue
21492 });
21493 assert_eq!(
21494 declarations
21495 .into_iter()
21496 .map(|declaration| {
21497 cpp_declaration_has_native_must_use_attribute(declaration, source)
21498 })
21499 .collect::<Vec<_>>(),
21500 vec![true, false, true]
21501 );
21502 let enum_source = "[[nodiscard]] enum Marker { MarkerValue } marker;";
21503 let enum_tree = crate::diagnostics::parse_cpp_tree(enum_source);
21504 let mut enum_specifier = None;
21505 walk_named_tree_preorder(enum_tree.root_node(), true, |node| {
21506 if node.kind() == "enum_specifier" {
21507 enum_specifier = Some(node);
21508 WalkControl::Break
21509 } else {
21510 WalkControl::Continue
21511 }
21512 });
21513 assert!(cpp_declaration_has_native_must_use_attribute(
21514 enum_specifier.expect("enum declaration"),
21515 enum_source
21516 ));
21517 }
21518
21519 fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
21520 let mut rows = parsed
21521 .signature_metadata
21522 .iter()
21523 .flat_map(|(unit, entries)| {
21524 entries.iter().map(move |metadata| {
21525 assert!(
21526 metadata.callable_modifiers_recorded(),
21527 "{} must record that the walk read its declaration shape",
21528 unit.fq_name()
21529 );
21530 format!(
21531 "{} decl_only={} static={} constructor={} visibility={:?}",
21532 unit.fq_name(),
21533 metadata.is_declaration_only(),
21534 metadata.callable_is_static(),
21535 metadata.callable_is_constructor(),
21536 metadata
21537 .callable_declared_visibility()
21538 .expect("a recorded modifier set states a visibility"),
21539 )
21540 })
21541 })
21542 .collect::<Vec<_>>();
21543 rows.sort();
21544 rows
21545 }
21546
21547 #[test]
21551 #[hardwon("#3520: exact C++ lookup could not round-trip nested terminal identities")]
21552 fn rendered_name_fq_round_trips_nested_class_spellings() {
21553 assert!(cpp_rendered_name_fq("").is_empty());
21554 for rendered in [
21555 "Only",
21556 "Top$Only",
21557 "ns.Outer$Inner",
21558 "Top$Only.b",
21559 "ns.Outer$Inner.a",
21560 "Buck$Stop",
21561 ] {
21562 let fq = cpp_rendered_name_fq(rendered);
21563 assert_eq!(
21564 fq.display_native(Language::Cpp, segment_interner()),
21565 rendered,
21566 "native round trip for {rendered}"
21567 );
21568 }
21569 let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
21570 .segments()
21571 .iter()
21572 .map(|segment| segment_interner().resolve(*segment).1)
21573 .collect();
21574 assert_eq!(
21575 kinds,
21576 [
21577 SegmentKind::Unknown,
21578 SegmentKind::Unknown,
21579 SegmentKind::Nested
21580 ]
21581 );
21582 }
21583
21584 #[test]
21588 #[hardwon(
21589 "509d66c85788: C++ declaration recovery regressed for colon source aliases name nested and namespace identities"
21590 )]
21591 fn colon_source_aliases_name_nested_and_namespace_identities() {
21592 let native = |rendered: &str| {
21593 cpp_colon_source_aliases(rendered)
21594 .into_iter()
21595 .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
21596 .collect::<Vec<_>>()
21597 };
21598 let top = native("Top::Only");
21599 assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
21600 assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
21601 assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
21602 let nested = native("ns::Outer::Inner");
21603 assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
21604 assert!(
21605 nested.contains(&"ns::Outer.Inner".to_string()),
21606 "{nested:?}"
21607 );
21608 let member = native("Top::Only::b");
21609 assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
21610 assert!(cpp_colon_source_aliases("Top$Only").is_empty());
21611 assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
21612 assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
21613 assert!(cpp_colon_source_aliases("Only").is_empty());
21614 assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
21615 fq.segments()
21616 .iter()
21617 .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
21618 }));
21619 }
21620
21621 #[test]
21622 #[hardwon(
21623 "aa581047b253: C++ declaration recovery regressed for displaced terminator ignores nested initializer endif"
21624 )]
21625 fn displaced_terminator_ignores_nested_initializer_endif() {
21626 let source = "#ifdef ENABLE_ITEMS\n\
21627struct Item { int reg; };\n\
21628static const struct Item items[] = {{1}};\n\
21629static const struct Item extras[] = {\n\
21630 {0},\n\
21631#ifndef REDUCED\n\
21632 {1},\n\
21633#endif\n\
21634};\n\
21635int read_item(int i) { return items[i].reg; }\n\
21636#endif\n";
21637 let mut parser = tree_sitter::Parser::new();
21638 parser
21639 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21640 .expect("C++ grammar");
21641 let tree = parser.parse(source, None).expect("fixture tree");
21642 let conditional = tree
21643 .root_node()
21644 .named_child(0)
21645 .filter(|node| node.kind() == "preproc_ifdef")
21646 .expect("outer conditional");
21647
21648 assert!(conditional.has_error());
21649 assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
21650 assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
21651 }
21652
21653 #[test]
21654 #[hardwon(
21655 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21656 )]
21657 fn pyobject_head_field_recovery_publishes_only_the_real_member() {
21658 let source = "struct Image { PyObject_HEAD Imaging image; };";
21659 let parsed = parse_cpp_declarations(source, "image.h");
21660 let names = parsed
21661 .declarations()
21662 .iter()
21663 .map(|unit| unit.fq_name())
21664 .collect::<Vec<_>>();
21665
21666 assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
21667 assert!(
21668 names.iter().all(|name| name != "Image.Imaging"),
21669 "the pseudo-declarator must not become a field: {names:#?}"
21670 );
21671
21672 let pointer = parse_cpp_declarations(
21673 "struct Image { PyObject_HEAD Imaging *image; };",
21674 "image-pointer.h",
21675 );
21676 let pointer_names = pointer
21677 .declarations()
21678 .iter()
21679 .map(|unit| unit.fq_name())
21680 .collect::<Vec<_>>();
21681 assert!(
21682 pointer_names.iter().any(|name| name == "Image.image"),
21683 "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
21684 );
21685 assert!(
21686 pointer_names.iter().all(|name| name != "Image.Imaging"),
21687 "the pointer recovery error must not become a field: {pointer_names:#?}"
21688 );
21689
21690 let unrelated_macro = parse_cpp_declarations(
21691 "struct Image { OTHER_HEAD Imaging other; };",
21692 "image-near-miss.h",
21693 );
21694 let unrelated_names = unrelated_macro
21695 .declarations()
21696 .iter()
21697 .map(|unit| unit.fq_name())
21698 .collect::<Vec<_>>();
21699 assert!(
21700 unrelated_names.iter().any(|name| name == "Image.other"),
21701 "an object-like macro in specifier position is a `macro_modifier`, so the \
21702 member is the structured best-effort reading, the same one the \
21703 macro-declaration grammar gives `ffc_internal` and `PyObject_HEAD`: {unrelated_names:#?}"
21704 );
21705 assert!(
21706 unrelated_names
21707 .iter()
21708 .all(|name| name != "Image.OTHER_HEAD"),
21709 "the macro marker must not become a declaration of its own: {unrelated_names:#?}"
21710 );
21711 }
21712
21713 #[test]
21714 #[hardwon(
21715 "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21716 )]
21717 fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
21718 let source = r#"namespace testing {
21719namespace internal {
21720 namespace detail {
21721 class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
21722 public:
21723 int value() const { return count_ + 1; }
21724 private:
21725 int count_;
21726 };
21727 class GTEST_API_ [[nodiscard]] OtherReporter {
21728 public:
21729 int value() const { return count_ + 2; }
21730 private:
21731 int count_;
21732 };
21733 }
21734
21735 template <typename T>
21736 void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
21737
21738 class TailReporter {};
21739}
21740}
21741"#;
21742 let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
21743 let declarations = parsed.declarations();
21744 let mut parser = tree_sitter::Parser::new();
21745 parser
21746 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21747 .unwrap();
21748 let tree = parser.parse(source, None).unwrap();
21749 let tail_start = source.find("TailReporter").expect("tail class");
21750 let tail_node = tree
21751 .root_node()
21752 .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
21753 .expect("tail class AST node");
21754 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
21755 assert!(
21756 tree.root_node().has_error(),
21757 "the malformed class must exercise recovery"
21758 );
21759 assert!(index.region_at(tail_start).is_some());
21760 assert_eq!(
21761 index.enclosing_namespace_components(tail_node, source),
21762 ["testing", "internal"]
21763 );
21764 let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
21765 let mut recovered_parsed = ParsedFile::new(String::new());
21766 let class_unit = CodeUnit::new_fq(
21767 file.clone(),
21768 CodeUnitType::Class,
21769 "testing",
21770 "ScopedFakeTestPartResultReporter",
21771 cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
21772 );
21773 let scope = ScopeInfo {
21774 package_name: "testing".to_string(),
21775 module: None,
21776 class_unit: Some(class_unit),
21777 template_signature: Some("<typename T>".to_string()),
21778 template_metadata: Some(CppTemplateMetadata {
21779 primary_name: "ScopedFakeTestPartResultReporter".to_string(),
21780 primary_fq_name: String::new(),
21781 parameters: Vec::new(),
21782 specialization_arguments: Vec::new(),
21783 alias_target: None,
21784 }),
21785 declarations_are_fields: true,
21786 recovered_specialization_member_scope: true,
21787 visible_using_namespaces: Vec::new(),
21788 };
21789 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
21790 let properties = RefCell::new(HashMap::default());
21791 let dependencies =
21792 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
21793 let ancestry = ParentIndex::new(tree.root_node());
21794 let mut visitor = CppVisitor {
21795 file: &file,
21796 source,
21797 parsed: &mut recovered_parsed,
21798 source_collector: &collector,
21799 source_properties: &properties,
21800 source_dependencies: &dependencies,
21801 primary_ancestry: &ancestry,
21802 primary_root_id: tree.root_node().id(),
21803 source_links: HashMap::default(),
21804 source_declarations: HashSet::default(),
21805 preprocessor_context_cache: Default::default(),
21806 active_source_context: None,
21807 c_tag_semantics: false,
21808 c_tag_scope_witness: false,
21809 recovered_class_sibling_scopes: HashMap::default(),
21810 consumed_fragment_regions: Vec::new(),
21811 orphaned_namespaces: &index,
21812 partitioned_regions: Vec::new(),
21813 namespace_forward_scans: HashMap::default(),
21814 field_owners: None,
21815 recovery_captures: Vec::new(),
21816 object_macro_fields: HashMap::default(),
21817 ambiguous_object_macro_fields: HashSet::default(),
21818 };
21819 let recovered = visitor
21820 .recovered_namespace_scope(tail_node, &scope)
21821 .expect("the tail must use the stolen namespace scope");
21822 assert_eq!(recovered.package_name, "testing::internal");
21823 assert!(
21824 recovered.class_unit.is_none(),
21825 "recovered namespace declarations cannot retain the malformed class owner"
21826 );
21827 assert!(recovered.template_signature.is_none());
21828 assert!(recovered.template_metadata.is_none());
21829 assert!(!recovered.declarations_are_fields);
21830 assert!(!recovered.recovered_specialization_member_scope);
21831 assert!(
21832 declarations
21833 .iter()
21834 .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
21835 "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
21836 );
21837 assert!(
21838 declarations
21839 .iter()
21840 .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
21841 "the recovered free function remains in its namespace: {declarations:#?}"
21842 );
21843 assert!(
21844 declarations
21845 .iter()
21846 .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
21847 "the independent nested class keeps its ordinary class owner: {declarations:#?}"
21848 );
21849 assert!(
21850 declarations.iter().all(|unit| {
21851 !unit
21852 .short_name()
21853 .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
21854 }),
21855 "recovered namespace declarations must not retain a class owner: {declarations:#?}"
21856 );
21857 assert!(
21858 declarations
21859 .iter()
21860 .all(|unit| !unit.identifier().is_empty()),
21861 "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
21862 );
21863 }
21864
21865 #[test]
21872 fn recovered_namespace_levels_name_their_own_name_tokens() {
21873 let source = r#"namespace outer::app {
21878
21879 class Target {
21880 int value_;
21881 };
21882
21883 template <typename T>
21884 class Holder {
21885 public:
21886 explicit constexpr Holder( T lhs ): m_lhs( lhs ) {}
21887
21888#define HOLDER_DEFINE_OP( id, op ) \
21889 template <typename U> \
21890 constexpr friend auto operator op( Holder&& lhs, U&& rhs ) \
21891 -> std::enable_if_t<is_##id##_comparable<T, U>::value, Target> { \
21892 return Target{}; \
21893 }
21894
21895 HOLDER_DEFINE_OP( equal, == )
21896#undef HOLDER_DEFINE_OP
21897 T m_lhs;
21898 };
21899
21900 class Tail {};
21901"#;
21902 let parsed = parse_cpp_declarations(source, "recovered-namespace-name.h");
21903 let mut parser = tree_sitter::Parser::new();
21904 parser
21905 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21906 .unwrap();
21907 let tree = parser.parse(source, None).unwrap();
21908 assert!(
21909 tree.root_node().has_error(),
21910 "the fixture must reproduce the parse damage"
21911 );
21912 let target_start = source.find("class Target").expect("target class");
21913 let target_node = tree
21914 .root_node()
21915 .named_descendant_for_byte_range(target_start, target_start + "class".len())
21916 .expect("target class AST node");
21917 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
21918 assert_eq!(
21923 index.enclosing_namespace_components(target_node, source),
21924 ["outer", "app"]
21925 );
21926 let file = ProjectFile::new(std::env::temp_dir(), "recovered-namespace-name.h");
21927 let mut recovered_parsed = ParsedFile::new(String::new());
21928 let collector = RefCell::new(PrimarySourceFactCollector::new(source));
21929 let properties = RefCell::new(HashMap::default());
21930 let dependencies =
21931 RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
21932 let ancestry = ParentIndex::new(tree.root_node());
21933 let mut visitor = CppVisitor {
21934 file: &file,
21935 source,
21936 parsed: &mut recovered_parsed,
21937 source_collector: &collector,
21938 source_properties: &properties,
21939 source_dependencies: &dependencies,
21940 primary_ancestry: &ancestry,
21941 primary_root_id: tree.root_node().id(),
21942 source_links: HashMap::default(),
21943 source_declarations: HashSet::default(),
21944 preprocessor_context_cache: Default::default(),
21945 active_source_context: None,
21946 c_tag_semantics: false,
21947 c_tag_scope_witness: false,
21948 recovered_class_sibling_scopes: HashMap::default(),
21949 consumed_fragment_regions: Vec::new(),
21950 orphaned_namespaces: &index,
21951 partitioned_regions: Vec::new(),
21952 namespace_forward_scans: HashMap::default(),
21953 field_owners: None,
21954 recovery_captures: Vec::new(),
21955 object_macro_fields: HashMap::default(),
21956 ambiguous_object_macro_fields: HashSet::default(),
21957 };
21958 visitor.visit_container(
21961 tree.root_node(),
21962 &ancestry,
21963 "",
21964 None,
21965 None,
21966 None,
21967 Vec::new(),
21968 );
21969 let collector = collector.borrow();
21970 for (module_name, expected_name) in [
21971 (
21972 "outer",
21973 Range {
21974 start_byte: 10,
21975 end_byte: 15,
21976 start_line: 1,
21977 end_line: 1,
21978 },
21979 ),
21980 (
21981 "outer::app",
21982 Range {
21983 start_byte: 17,
21984 end_byte: 20,
21985 start_line: 1,
21986 end_line: 1,
21987 },
21988 ),
21989 ] {
21990 let declaration = visitor
21991 .source_links
21992 .iter()
21993 .find(|(unit, _)| unit.fq_name() == module_name)
21994 .expect("the recovered module links a source declaration")
21995 .1
21996 .last()
21997 .expect("the recovered module declaration");
21998 let declaration = collector.declaration(*declaration);
21999 let name = declaration
22000 .name
22001 .expect("the recovered module records its name occurrence");
22002 assert_eq!(
22003 collector.occurrence(name).range,
22004 expected_name,
22005 "the recovered module names its own token: {module_name}"
22006 );
22007 }
22008 assert!(
22009 parsed.declarations().iter().any(|unit| {
22010 unit.fq_name() == "outer::app.Target" || unit.fq_name() == "outer::app.Tail"
22011 }),
22012 "the recovered namespace members keep their namespace: {:#?}",
22013 parsed.declarations()
22014 );
22015 }
22016
22017 #[test]
22018 #[hardwon(
22019 "3d77aac19607: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22020 )]
22021 fn object_like_field_macros_materialize_owner_specific_declarations() {
22022 let source = r#"#define PUBLIC_FIELDS int public_value;
22023#define PRIVATE_FIELDS int private_value;
22024#define NOT_A_FIELD_LIST not a declaration
22025
22026struct First {
22027 PUBLIC_FIELDS
22028 PRIVATE_FIELDS
22029};
22030struct Second {
22031 PUBLIC_FIELDS
22032 NOT_A_FIELD_LIST
22033};
22034#undef PUBLIC_FIELDS
22035struct Third {
22036 PUBLIC_FIELDS
22037};
22038"#;
22039 let parsed = parse_cpp_declarations(source, "macro-fields.c");
22040 let fields = parsed
22041 .declarations()
22042 .iter()
22043 .filter(|unit| unit.is_field())
22044 .map(|unit| unit.fq_name())
22045 .collect::<Vec<_>>();
22046
22047 assert!(
22048 fields.contains(&"First.public_value".to_string()),
22049 "{fields:?}"
22050 );
22051 assert!(
22052 fields.contains(&"First.private_value".to_string()),
22053 "{fields:?}"
22054 );
22055 assert!(
22056 fields.contains(&"Second.public_value".to_string()),
22057 "{fields:?}"
22058 );
22059 assert!(
22060 !fields.iter().any(|field| field.contains("not_a_field")),
22061 "malformed macro must fail closed: {fields:?}"
22062 );
22063 assert!(
22064 !fields.iter().any(|field| field.starts_with("Third.")),
22065 "undefined macro must fail closed: {fields:?}"
22066 );
22067 }
22068
22069 #[test]
22070 fn conditional_macro_redefinition_is_ambiguous() {
22071 let source = "#define FIELDS int first;\n#if USE_FIRST\n#else\n#undef FIELDS\n#define FIELDS int second;\n#endif\nstruct Item { FIELDS };\n";
22072 let tree = crate::diagnostics::parse_cpp_tree(source);
22073 let fields = collect_cpp_object_macro_fields(tree.root_node(), source);
22074 assert!(
22075 !fields.contains_key("FIELDS"),
22076 "conditional macro state must not select one branch's declaration: {fields:?}"
22077 );
22078 }
22079
22080 #[test]
22081 #[hardwon(
22082 "daa20f600d11: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22083 )]
22084 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
22085 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
22086 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
22087 let mut macros = parsed
22088 .declarations()
22089 .iter()
22090 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
22091 .collect::<Vec<_>>();
22092 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
22093
22094 assert_eq!(macros.len(), 2, "{macros:#?}");
22095 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
22096 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
22097 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
22098 assert_eq!(
22099 parsed.declaration_ranges(macros[1])[0].start_byte,
22100 source.rfind("#define VALUE 2").expect("second definition")
22101 );
22102 }
22103
22104 #[test]
22105 #[hardwon(
22106 "5de822e5d66f: C++ declaration recovery regressed for identifies export macro class base displaced into declarator"
22107 )]
22108 fn identifies_export_macro_class_base_displaced_into_declarator() {
22109 let source = r#"#define PROJECT_API_
22110namespace project {
22111namespace internal {
22112template <typename T>
22113class Base {};
22114}
22115template <typename T>
22116class Wrapper;
22117template <>
22118class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
22119}
22120"#;
22121 let mut parser = tree_sitter::Parser::new();
22122 parser
22123 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22124 .unwrap();
22125 let tree = parser.parse(source, None).unwrap();
22126 let start = source.find("internal::Base<int>").expect("base");
22127 let mut base = tree
22128 .root_node()
22129 .descendant_for_byte_range(start, start + 8)
22130 .expect("base syntax");
22131 while base.kind() != "qualified_identifier" {
22132 base = base.parent().expect("qualified base ancestor");
22133 }
22134 assert!(
22135 is_recovered_exported_class_base_type_node(base, source),
22136 "{}",
22137 tree.root_node().to_sexp()
22138 );
22139 }
22140
22141 fn all_function_identities(parsed: &ParsedFile) -> Vec<(String, Option<String>)> {
22142 parsed
22143 .declarations()
22144 .iter()
22145 .filter(|unit| unit.is_function())
22146 .map(|unit| (unit.fq_name(), unit.signature().map(str::to_string)))
22147 .collect()
22148 }
22149
22150 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
22151 let mut identities = all_function_identities(parsed)
22152 .into_iter()
22153 .map(|(name, signature)| (name, signature.unwrap_or_default()))
22154 .collect::<Vec<_>>();
22155 identities.sort();
22156 identities
22157 }
22158
22159 #[test]
22166 #[hardwon(
22167 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers parser owned declaration shapes"
22168 )]
22169 fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
22170 let cases = [
22171 (
22172 "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
22173 "pg_typemap_fit_to_result",
22174 "(VALUE, VALUE)",
22175 ),
22176 (
22177 "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
22178 "pg_typemap_result_value",
22179 "(t_typemap *, VALUE, int, int)",
22180 ),
22181 (
22182 "void pg_typemap_mark _(( void * ));",
22183 "pg_typemap_mark",
22184 "(void *)",
22185 ),
22186 (
22187 "void init_pg_type_map _(( void ));",
22188 "init_pg_type_map",
22189 "(void)",
22190 ),
22191 ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
22192 (
22193 "extern VALUE pg_extern _(( VALUE, VALUE ));",
22194 "pg_extern",
22195 "(VALUE, VALUE)",
22196 ),
22197 (
22198 "size_t pg_typemap_memsize _(( const void * ));",
22199 "pg_typemap_memsize",
22200 "(const void *)",
22201 ),
22202 (
22203 "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
22204 "pg_wrap_socket_io",
22205 "(int, VALUE, VALUE *, int *)",
22206 ),
22207 ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
22208 ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
22209 ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
22210 ];
22211
22212 for (index, (source, name, signature)) in cases.into_iter().enumerate() {
22213 let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
22214 assert_eq!(
22215 function_identities(&parsed),
22216 vec![(name.to_string(), signature.to_string())],
22217 "{source}: {:#?}",
22218 parsed.declarations()
22219 );
22220 assert!(
22221 parsed.declarations().iter().all(|unit| !unit.is_field()),
22222 "{source} must not retain the malformed field: {:#?}",
22223 parsed.declarations()
22224 );
22225 }
22226 }
22227
22228 #[test]
22237 #[hardwon(
22238 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers pointer return expression statements"
22239 )]
22240 fn c_prototype_macro_recovers_pointer_return_expression_statements() {
22241 let cases = [
22242 (
22243 "PGconn *pg_get_pgconn _(( VALUE ));",
22244 "pg_get_pgconn",
22245 "(VALUE)",
22246 ),
22247 (
22248 "PGresult* pgresult_get _(( VALUE ));",
22249 "pgresult_get",
22250 "(VALUE)",
22251 ),
22252 ];
22253 for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
22254 let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
22255 let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
22256 assert_eq!(
22257 function_identities(&parsed),
22258 vec![(name.to_string(), signature.to_string())],
22259 "{source}: {:#?}",
22260 parsed.declarations()
22261 );
22262 assert_eq!(
22263 parsed
22264 .declarations()
22265 .iter()
22266 .filter(|unit| unit.is_field())
22267 .map(|unit| unit.fq_name())
22268 .collect::<Vec<_>>(),
22269 vec!["rb_mPG".to_string()],
22270 "{source}: {:#?}",
22271 parsed.declarations()
22272 );
22273 }
22274 }
22275
22276 #[test]
22284 #[hardwon(
22285 "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers the issue witness inside the real ruby pg header block"
22286 )]
22287 fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
22288 let source = r#"VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
22289VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
22290int pg_typemap_fit_to_copy_get _(( VALUE ));
22291VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
22292t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
22293VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
22294void pg_typemap_mark _(( void * ));
22295size_t pg_typemap_memsize _(( const void * ));
22296void pg_typemap_compact _(( void * ));
22297
22298PGconn *pg_get_pgconn _(( VALUE ));
22299t_pg_connection *pg_get_connection _(( VALUE ));
22300VALUE pgconn_block _(( int, VALUE *, VALUE ));
22301#ifdef __GNUC__
22302__attribute__((format(printf, 3, 4)))
22303#endif
22304NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
22305VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
22306void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
22307
22308
22309VALUE pg_new_result _(( PGresult *, VALUE ));
22310VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
22311PGresult* pgresult_get _(( VALUE ));
22312VALUE pg_result_check _(( VALUE ));
22313VALUE pg_result_clear _(( VALUE ));
22314VALUE pg_tuple_new _(( VALUE, int ));
22315
22316/*
22317 * Fetch the data pointer for the result object
22318 */
22319static inline t_pg_result *
22320pgresult_get_this( VALUE self )
22321{
22322 return RTYPEDDATA_DATA(self);
22323}
22324
22325
22326rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
22327const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
22328rb_encoding *pg_conn_enc_get _(( PGconn * ));
22329
22330"#;
22331 let parsed = parse_cpp_declarations(source, "pg.h");
22332 assert!(
22333 function_identities(&parsed)
22334 .iter()
22335 .any(|(name, signature)| name == "pg_typemap_result_value"
22336 && signature == "(t_typemap *, VALUE, int, int)"),
22337 "the real issue witness must be a Function with its C signature: {:#?}",
22338 parsed.declarations()
22339 );
22340 assert!(
22341 parsed
22342 .declarations()
22343 .iter()
22344 .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
22345 "the issue witness must not leave its return type as a Field name: {:#?}",
22346 parsed.declarations()
22347 );
22348 }
22349
22350 #[test]
22357 #[hardwon(
22358 "6b1e2f76b805: C++ declaration recovery regressed for a macro decorated constructor is named for the constructor"
22359 )]
22360 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
22361 let source = r#"class SIMD_4x26 final {
22362 public:
22363 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
22364 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
22365 int m_v;
22366};
22367"#;
22368 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
22369 assert_eq!(
22370 function_identities(&parsed),
22371 vec![
22372 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
22373 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
22374 ],
22375 "{:#?}",
22376 parsed.declarations()
22377 );
22378 }
22379
22380 #[test]
22381 #[hardwon(
22382 "e3ff69209ab2: C++ variable recovery must keep the terminal name after a missing scope separator"
22383 )]
22384 fn a_missing_scope_separator_keeps_variable_name_and_real_qualified_names() {
22385 let source = r#"struct cpu_spec {};
22386static struct cpu_spec __initdata cpu_specs[];
22387static struct cpu_spec ns::cpu_specs[];
22388"#;
22389 let tree = crate::diagnostics::parse_cpp_tree(source);
22390 let root = tree.root_node();
22391 let declarations = root
22392 .named_children(&mut root.walk())
22393 .filter(|node| node.kind() == "declaration")
22394 .collect::<Vec<_>>();
22395 let names = declarations
22396 .iter()
22397 .filter_map(|declaration| declaration.child_by_field_name("declarator"))
22398 .filter_map(|declarator| extract_variable_name(declarator, source))
22399 .collect::<Vec<_>>();
22400 assert_eq!(names, ["cpu_specs", "ns::cpu_specs"]);
22401 }
22402
22403 #[test]
22408 #[hardwon(
22409 "a0b3bd93484b: C++ declaration recovery regressed for a macro wrapped declaration and the declarations it swallowed are indexed"
22410 )]
22411 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
22412 let source = r#"#include <cstdint>
22413struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
22414 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
22415 struct llama_model * model,
22416 struct llama_context_params params),
22417 "use llama_init_from_model instead");
22418 LLAMA_API int32_t llama_tokenize(
22419 const struct llama_vocab * vocab,
22420 const char * text,
22421 bool parse_special);
22422 LLAMA_API int32_t llama_other(int a);
22423"#;
22424 let parsed = parse_cpp_declarations(source, "llama.h");
22425 assert_eq!(
22426 function_identities(&parsed),
22427 vec![
22428 (
22429 "llama_new_context_with_model".to_string(),
22430 "(struct llama_model *, struct llama_context_params)".to_string()
22431 ),
22432 ("llama_other".to_string(), "(int)".to_string()),
22433 (
22434 "llama_tokenize".to_string(),
22435 "(const struct llama_vocab *, const char *, bool)".to_string()
22436 ),
22437 ],
22438 "{:#?}",
22439 parsed.declarations()
22440 );
22441
22442 for (name, expected) in [
22445 (
22446 "llama_new_context_with_model",
22447 "LLAMA_API struct llama_context * llama_new_context_with_model(",
22448 ),
22449 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
22450 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
22451 ] {
22452 let unit = parsed
22453 .declarations()
22454 .iter()
22455 .find(|unit| unit.is_function() && unit.fq_name() == name)
22456 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
22457 let [range] = parsed.declaration_ranges(unit) else {
22458 panic!("{name} must have exactly one range");
22459 };
22460 let text = &source[range.start_byte..range.end_byte];
22461 assert!(
22462 text.starts_with(expected),
22463 "{name} range is {text:?}, expected it to start with {expected:?}"
22464 );
22465 assert!(
22466 text.ends_with(')') || text.ends_with(';'),
22467 "{name}: {text:?}"
22468 );
22469 }
22470 }
22471
22472 #[test]
22487 #[hardwon(
22488 "9e1f8732e419: C++ declaration recovery regressed for a flattened macro invocation run is read from its token order"
22489 )]
22490 fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
22491 let source = r#" LIBRARY_DEPRECATED(
22492 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
22493 "use library_init_from_file_with_params instead"
22494 );
22495 LIBRARY_DEPRECATED(
22496 LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
22497 "use library_init_from_buffer_with_params instead"
22498 );
22499 LIBRARY_DEPRECATED(
22500 LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
22501 "use library_init_with_params instead"
22502 );
22503"#;
22504 let mut parser = tree_sitter::Parser::new();
22505 parser
22506 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22507 .expect("C++ grammar");
22508 let tree = parser.parse(source, None).expect("C++ tree");
22509 let root = tree.root_node();
22510
22511 let mut cursor = root.walk();
22515 let items = root.children(&mut cursor).collect::<Vec<_>>();
22516 let [
22517 first,
22518 first_statement,
22519 second,
22520 second_statement,
22521 third,
22522 third_statement,
22523 ] = items.as_slice()
22524 else {
22525 panic!("{}", root.to_sexp());
22526 };
22527 let envelopes = [
22528 (first, first_statement),
22529 (second, second_statement),
22530 (third, third_statement),
22531 ];
22532 for (envelope, statement) in envelopes {
22533 assert_eq!(envelope.kind(), "ERROR", "{}", root.to_sexp());
22534 assert_eq!(
22535 statement.kind(),
22536 "expression_statement",
22537 "{}",
22538 root.to_sexp()
22539 );
22540 assert!(
22541 node_text(*envelope, source).ends_with(')'),
22542 "the invocation's own `)` closes its parameter list; the `;` is the statement's: {}",
22543 root.to_sexp()
22544 );
22545 }
22546
22547 let ancestry = ParentIndex::new(root);
22551 let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
22552 .expect("the first invocation");
22553 assert_eq!(
22554 &source[..first_run.invocation_end],
22555 &source[..source.find("instead\"\n );").expect("first hint")
22556 + "instead\"\n );".len()]
22557 );
22558 for (envelope, _) in envelopes {
22559 let run = collapsed_macro_declaration_run(*envelope, source, &ancestry)
22560 .expect("each invocation");
22561 assert_eq!(
22562 run.region_end, run.invocation_end,
22563 "a self-contained invocation owns only its own bytes"
22564 );
22565 }
22566
22567 let parsed = parse_cpp_declarations(source, "library.h");
22568 assert_eq!(
22569 function_identities(&parsed),
22570 vec![
22571 (
22572 "library_init".to_string(),
22573 "(struct library_model_loader *)".to_string()
22574 ),
22575 (
22576 "library_init_from_buffer".to_string(),
22577 "(void *, size_t)".to_string()
22578 ),
22579 (
22580 "library_init_from_file".to_string(),
22581 "(const char *)".to_string()
22582 ),
22583 ],
22584 "{:#?}",
22585 parsed.declarations()
22586 );
22587 }
22588
22589 #[test]
22590 #[hardwon(
22591 "9e1f8732e419: C++ declaration recovery must not lift a conditional function-body error to file scope"
22592 )]
22593 fn a_conditional_error_inside_an_ordinary_function_is_not_global_scope() {
22594 let source = r#"void host() {
22595#ifdef ENABLED
22596LIBRARY_DEPRECATED(
22597 int hidden(const char* value),
22598 "hint"
22599);
22600#endif
22601}
22602"#;
22603 let mut parser = tree_sitter::Parser::new();
22604 parser
22605 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22606 .expect("C++ grammar");
22607 let tree = parser.parse(source, None).expect("C++ tree");
22608 let root = tree.root_node();
22609 let function = root.named_child(0).expect("ordinary function");
22610 let body = function
22611 .child_by_field_name("body")
22612 .expect("ordinary function body");
22613 let conditional = body.named_child(0).expect("conditional body");
22614 let mut conditional_cursor = conditional.walk();
22615 let envelope = conditional
22616 .named_children(&mut conditional_cursor)
22617 .find(|child| child.kind() == "ERROR")
22618 .expect("macro error");
22619 assert_eq!(conditional.kind(), "preproc_ifdef");
22620 assert_eq!(envelope.kind(), "ERROR");
22621
22622 let ancestry = ParentIndex::new(root);
22623 assert!(declaration_scope_container(envelope, &ancestry).is_none());
22624 assert!(collapsed_macro_declaration_run(envelope, source, &ancestry).is_none());
22625
22626 let parsed = parse_cpp_declarations(source, "function-body-macro.h");
22627 assert!(
22628 parsed
22629 .declarations()
22630 .iter()
22631 .all(|unit| unit.fq_name() != "hidden"),
22632 "a function-body macro error must not become a global declaration: {:#?}",
22633 parsed.declarations()
22634 );
22635 }
22636
22637 #[test]
22643 #[hardwon(
22644 "9e1f8732e419: C++ declaration recovery regressed for an invocation that fills its scope is left to the ordinary reader"
22645 )]
22646 fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
22647 let source = r#"LIBRARY_DEPRECATED(
22648 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
22649 "use library_init_from_file_with_params instead"
22650 );"#;
22651 let mut parser = tree_sitter::Parser::new();
22652 parser
22653 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22654 .expect("C++ grammar");
22655 let tree = parser.parse(source, None).expect("C++ tree");
22656 let root = tree.root_node();
22657 let head = root.named_child(0).expect("the invocation");
22658 let ancestry = ParentIndex::new(root);
22659 assert!(
22660 collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
22661 "{}",
22662 root.to_sexp()
22663 );
22664 assert!(
22665 !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
22666 "the ordinary reader must be the one that has it: {}",
22667 root.to_sexp()
22668 );
22669 let unindexed = ParentIndex::unindexed();
22672 assert!(
22673 collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
22674 "indexed and unindexed climbs must agree"
22675 );
22676 assert_eq!(
22677 macro_wrapped_declarations(head, source, &ancestry).len(),
22678 macro_wrapped_declarations(head, source, &unindexed).len(),
22679 "indexed and unindexed climbs must agree"
22680 );
22681 }
22682
22683 #[test]
22688 #[hardwon(
22689 "a0b3bd93484b: C++ declaration recovery regressed for a macro call without a wrapped declaration recovers nothing"
22690 )]
22691 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
22692 for source in [
22693 "int before;\nFOO(1, 2);\nint after;\n",
22694 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
22695 "int before;\nMACRO(int a, int b);\nint after;\n",
22696 "DECLARE_HANDLE(HWND);\nint after;\n",
22697 ] {
22698 let parsed = parse_cpp_declarations(source, "macro-call.h");
22699 assert_eq!(
22700 function_identities(&parsed),
22701 Vec::new(),
22702 "{source:?} must declare no function: {:#?}",
22703 parsed.declarations()
22704 );
22705 }
22706 }
22707
22708 #[test]
22713 #[hardwon(
22714 "c1aac56476f0: C++ declaration recovery regressed for a string attribute macro member keeps itself and the member after it"
22715 )]
22716 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
22717 let source = r#"#include <string_view>
22718namespace Botan {
22719class DL_Group final {
22720 public:
22721 DL_Group() = default;
22722 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
22723 DL_Group(std::string_view pem, int format);
22724 size_t get_p() const;
22725};
22726}
22727"#;
22728 let parsed = parse_cpp_declarations(source, "dl_group.h");
22729 assert_eq!(
22730 function_identities(&parsed),
22731 vec![
22732 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
22733 (
22734 "Botan.DL_Group.DL_Group".to_string(),
22735 "(std::string_view)".to_string()
22736 ),
22737 (
22738 "Botan.DL_Group.DL_Group".to_string(),
22739 "(std::string_view, int)".to_string()
22740 ),
22741 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
22742 ],
22743 "{:#?}",
22744 parsed.declarations()
22745 );
22746 }
22747
22748 #[test]
22753 #[hardwon(
22754 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps its string attribute members"
22755 )]
22756 fn an_export_macro_class_keeps_its_string_attribute_members() {
22757 let source = r#"#include <string_view>
22758namespace Botan {
22759class BOTAN_PUBLIC_API(2, 0) DL_Group final {
22760 public:
22761 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
22762 DL_Group(std::string_view pem, int format);
22763 size_t get_p() const;
22764};
22765}
22766"#;
22767 let parsed = parse_cpp_declarations(source, "dl_group.h");
22768 assert!(
22769 parsed
22770 .declarations()
22771 .iter()
22772 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
22773 "{:#?}",
22774 parsed.declarations()
22775 );
22776 assert_eq!(
22777 function_identities(&parsed),
22778 vec![
22779 (
22780 "Botan.DL_Group.DL_Group".to_string(),
22781 "(std::string_view)".to_string()
22782 ),
22783 (
22784 "Botan.DL_Group.DL_Group".to_string(),
22785 "(std::string_view, int)".to_string()
22786 ),
22787 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
22788 ],
22789 "{:#?}",
22790 parsed.declarations()
22791 );
22792 }
22793
22794 #[test]
22800 #[hardwon(
22801 "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps stranded and access labeled constructors"
22802 )]
22803 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
22804 let source = r#"namespace Botan {
22805class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
22806 public:
22807 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
22808 XMSS_Parameters(int oid, int len);
22809 size_t len() const;
22810
22811 private:
22812 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
22813 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
22814
22815 int m_oid;
22816 int m_wots_oid;
22817 size_t m_element_size;
22818 size_t m_tree_height;
22819};
22820}
22821"#;
22822 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
22823 let constructors = function_identities(&parsed)
22824 .into_iter()
22825 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
22826 .map(|(_, signature)| signature)
22827 .collect::<Vec<_>>();
22828 assert_eq!(
22829 constructors,
22830 vec![
22831 "()".to_string(),
22832 "(int, int)".to_string(),
22833 "(int, int, size_t, size_t)".to_string(),
22834 ],
22835 "{:#?}",
22836 parsed.declarations()
22837 );
22838 }
22839
22840 #[test]
22844 #[hardwon(
22845 "6b1e2f76b805: C++ declaration recovery regressed for a genuine qualified out of line definition keeps its scope"
22846 )]
22847 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
22848 let source = r#"namespace shell {
22849struct Outer {
22850 struct Inner {
22851 Inner(int v);
22852 void run(int v);
22853 };
22854};
22855Outer::Inner::Inner(int v) {}
22856void Outer::Inner::run(int v) {}
22857}
22858"#;
22859 let parsed = parse_cpp_declarations(source, "outer.cpp");
22860 let names = function_identities(&parsed)
22861 .into_iter()
22862 .map(|(fq_name, _)| fq_name)
22863 .collect::<Vec<_>>();
22864 assert!(
22865 names
22866 .iter()
22867 .all(|name| name.starts_with("shell.Outer$Inner.")),
22868 "{names:#?}"
22869 );
22870 }
22871
22872 #[test]
22873 #[hardwon(
22874 "3aa210fd2699: C++ declaration recovery regressed for macro decorated template class keeps member scope without forward declaration"
22875 )]
22876 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
22877 let source = r#"namespace control {
22878template <typename T>
22879class AnySpan;
22880template <typename T>
22881class ABSL_ATTRIBUTE_VIEW AnySpan {
22882 public:
22883 int begin() const;
22884};
22885}
22886
22887namespace absl {
22888ABSL_NAMESPACE_BEGIN
22889template <typename T>
22890class ABSL_ATTRIBUTE_VIEW Span {
22891 public:
22892 int begin() const;
22893 int back() const;
22894};
22895
22896int begin();
22897int back();
22898}
22899"#;
22900 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
22901 let declarations = parsed.declarations();
22902 assert!(
22903 declarations
22904 .iter()
22905 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
22906 );
22907 for method in ["begin", "back"] {
22908 assert!(declarations.iter().any(|unit| {
22909 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
22910 }));
22911 assert!(
22912 declarations.iter().any(|unit| {
22913 unit.is_function() && unit.fq_name() == format!("absl.{method}")
22914 })
22915 );
22916 }
22917 assert!(
22918 declarations
22919 .iter()
22920 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
22921 );
22922 assert!(
22923 declarations
22924 .iter()
22925 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
22926 );
22927 assert!(
22928 declarations
22929 .iter()
22930 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
22931 );
22932 }
22933
22934 #[test]
22935 #[hardwon(
22936 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22937 )]
22938 fn explicit_global_member_definition_has_canonical_package_boundary() {
22939 let source = r#"
22940namespace arangodb::aql {
22941class ExecutionPlan {
22942 public:
22943 template<class... Args> Node* createNode(Args&&... args);
22944};
22945}
22946
22947template<class... Args>
22948Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
22949"#;
22950 let parsed = parse_cpp_declarations(source, "global-member.cpp");
22951
22952 assert!(parsed.declarations().iter().any(|unit| {
22953 unit.is_function()
22954 && unit.package_name() == "arangodb::aql"
22955 && unit.short_name() == "ExecutionPlan.createNode"
22956 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
22957 }));
22958 }
22959
22960 #[test]
22961 #[hardwon(
22962 "b020725e0ba9: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22963 )]
22964 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
22965 let source = r#"
22966#ifndef TINYXML2_INCLUDED
22967#define TINYXML2_INCLUDED
22968namespace tinyxml2 {
22969class TINYXML2_LIB XMLUtil {
22970 public:
22971 static const char* SkipWhiteSpace(const char* p) {
22972 while (*p) {
22973 if (*p == ' ') {
22974 ++p;
22975 }
22976 }
22977 return p;
22978 }
22979 static bool StringEqual(const char* p, const char* q) {
22980 return p == q;
22981 }
22982 class TINYXML2_LIB Helper {
22983 public:
22984 void Touch();
22985 };
22986 static void ToStr(int value, char* buffer);
22987 private:
22988 static const char* writeBoolTrue;
22989};
22990
22991class TINYXML2_LIB XMLNode {
22992 public:
22993 virtual XMLNode* ShallowClone() const = 0;
22994 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
22995};
22996}
22997#endif
22998"#;
22999 let mut parser = tree_sitter::Parser::new();
23000 parser
23001 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23002 .unwrap();
23003 let tree = parser.parse(source, None).unwrap();
23004 let mut boundary_found = false;
23005 walk_named_tree_preorder(tree.root_node(), true, |node| {
23006 if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
23007 && name == "XMLUtil"
23008 {
23009 boundary_found = fragmented_export_sibling_class_boundary(
23010 node,
23011 source,
23012 &ParentIndex::unindexed(),
23013 )
23014 .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
23015 .is_some_and(|(_, name, _, _)| name == "XMLNode");
23016 }
23017 WalkControl::Continue
23018 });
23019 assert!(
23020 boundary_found,
23021 "fixture must exercise the recovered sibling boundary"
23022 );
23023
23024 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
23025 assert!(
23026 parsed
23027 .declarations()
23028 .iter()
23029 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
23030 "{:#?}",
23031 parsed.declarations()
23032 );
23033 assert!(
23034 parsed
23035 .declarations()
23036 .iter()
23037 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
23038 "{:#?}",
23039 parsed.declarations()
23040 );
23041 assert!(parsed.declarations().iter().any(|unit| {
23042 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
23043 }));
23044 assert!(
23045 parsed
23046 .declarations()
23047 .iter()
23048 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
23049 );
23050 assert!(
23051 parsed
23052 .declarations()
23053 .iter()
23054 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
23055 );
23056 }
23057
23058 #[test]
23059 #[hardwon(
23060 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23061 )]
23062 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
23063 let parsed = parse_cpp_declarations(
23067 r#"
23068namespace cwg311 {
23069namespace X { namespace Y {} }
23070namespace ::cwg311::X {}
23071}
23072"#,
23073 "explicit-global-namespace.cpp",
23074 );
23075
23076 assert!(parsed.declarations().iter().any(|unit| {
23077 unit.kind() == CodeUnitType::Module
23078 && unit.short_name() == "cwg311::X"
23079 && unit.fq_name() == "cwg311::X"
23080 }));
23081 assert!(
23082 parsed
23083 .declarations()
23084 .iter()
23085 .all(|unit| !unit.short_name().contains("::::")),
23086 "recovered namespace names must not retain empty scope components: {:#?}",
23087 parsed.declarations()
23088 );
23089 }
23090
23091 #[test]
23092 #[hardwon(
23093 "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23094 )]
23095 fn repeated_scope_separator_does_not_create_empty_function_owner() {
23096 let scope = ScopeInfo {
23097 package_name: "X".to_string(),
23098 module: None,
23099 class_unit: None,
23100 template_signature: None,
23101 template_metadata: None,
23102 declarations_are_fields: false,
23103 recovered_specialization_member_scope: false,
23104 visible_using_namespaces: Vec::new(),
23105 };
23106
23107 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
23108
23109 assert!(owner.is_none());
23110 assert_eq!(name, "doit");
23111 assert_eq!(package, "X");
23112 }
23113
23114 #[test]
23115 #[hardwon(
23116 "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23117 )]
23118 fn trailing_decltype_expression_is_not_a_function_declarator() {
23119 let source = r#"
23120namespace boost { namespace detail {
23121#if ! defined(BOOST_NO_SFINAE_EXPR) && \
23122 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
23123 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
23124#define BOOST_THREAD_PROVIDES_INVOKE
23125#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
23126template <class Fp, class A0, class ...Args>
23127inline auto
23128invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
23129 BOOST_THREAD_RV_REF(Args) ...args)
23130 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
23131{
23132 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
23133}
23134#endif
23135#endif
23136}}
23137"#;
23138 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
23139
23140 assert!(
23141 parsed
23142 .declarations()
23143 .iter()
23144 .all(|unit| unit.short_name() != ".*f")
23145 );
23146 }
23147
23148 fn find_class_named<'tree>(
23149 root: Node<'tree>,
23150 source: &str,
23151 expected_name: &str,
23152 ) -> Option<Node<'tree>> {
23153 let mut stack = vec![root];
23154 while let Some(node) = stack.pop() {
23155 if node.kind() == "class_specifier"
23156 && node
23157 .child_by_field_name("name")
23158 .is_some_and(|name| node_text(name, source) == expected_name)
23159 {
23160 return Some(node);
23161 }
23162 let mut cursor = node.walk();
23163 stack.extend(node.named_children(&mut cursor));
23164 }
23165 None
23166 }
23167
23168 #[test]
23169 #[hardwon(
23170 "9ac72fd81fc1: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23171 )]
23172 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
23173 let source = r#"EXPORT void definition(struct Value value) {}
23174EXPORT void prototype(struct Value value);
23175"#;
23176 let mut parser = tree_sitter::Parser::new();
23177 parser
23178 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23179 .unwrap();
23180 let tree = parser.parse(source, None).unwrap();
23181 let root = tree.root_node();
23182 let mut cursor = root.walk();
23183 let callables = root
23184 .named_children(&mut cursor)
23185 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
23186 .collect::<Vec<_>>();
23187
23188 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
23189 for callable in callables {
23190 assert!(
23195 cpp_sentinel_macro_parts(callable, source).is_none(),
23196 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
23197 );
23198 }
23199 }
23200
23201 #[test]
23202 #[hardwon(
23203 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23204 )]
23205 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
23206 let source = r#"namespace absl {
23207ABSL_NAMESPACE_BEGIN
23208// Generate a floating-point variate conforming to a Beta distribution:
23209template <typename RealType = double>
23210class beta_distribution {
23211 public:
23212 using result_type = RealType;
23213
23214
23215 beta_distribution() : beta_distribution(1) {}
23216
23217 explicit beta_distribution(result_type alpha, result_type beta = 1)
23218 : param_(alpha, beta) {}
23219
23220 explicit beta_distribution(const param_type& p) : param_(p) {}
23221
23222 void reset() {}
23223
23224 // Generating functions
23225 template <typename URBG>
23226 result_type operator()(URBG& g) { // NOLINT(runtime/references)
23227 return (*this)(g, param_);
23228 }
23229
23230};
23231ABSL_NAMESPACE_END
23232} // namespace absl
23233"#;
23234 let mut parser = tree_sitter::Parser::new();
23235 parser
23236 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23237 .unwrap();
23238 let tree = parser.parse(source, None).unwrap();
23239 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
23240 let body = namespace
23241 .child_by_field_name("body")
23242 .expect("fixture namespace body");
23243 let sentinel = body.named_child(0).expect("sentinel envelope");
23244
23245 assert_eq!(sentinel.kind(), "ERROR");
23250 assert!(
23251 cpp_sentinel_macro_parts(sentinel, source).is_some(),
23252 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
23253 );
23254
23255 let parsed = parse_cpp_declarations(source, "beta-distribution.cpp");
23256 for expected in [
23257 "absl.beta_distribution",
23258 "absl.beta_distribution.beta_distribution",
23259 "absl.beta_distribution.reset",
23260 ] {
23261 assert!(
23262 parsed
23263 .declarations()
23264 .iter()
23265 .any(|unit| unit.fq_name() == expected),
23266 "missing recovered declaration {expected}: {:#?}",
23267 parsed.declarations()
23268 );
23269 }
23270 }
23271
23272 #[test]
23273 #[hardwon(
23274 "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23275 )]
23276 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
23277 let source = r#"namespace absl {
23278ABSL_NAMESPACE_BEGIN
23279// absl::discrete_distribution
23280//
23281// A discrete distribution produces random integers i, where 0 <= i < n
23282template <typename IntType = int>
23283class discrete_distribution {
23284 public:
23285 using result_type = IntType;
23286 class param_type {
23287 public:
23288 param_type() { init(); }
23289 template <typename InputIterator>
23290 explicit param_type(InputIterator begin, InputIterator end)
23291 : p_(begin, end) {
23292 init();
23293 }
23294 };
23295 discrete_distribution() : param_() {}
23296 explicit discrete_distribution(const param_type& p) : param_(p) {}
23297};
23298ABSL_NAMESPACE_END
23299} // namespace absl
23300"#;
23301 let mut parser = tree_sitter::Parser::new();
23302 parser
23303 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23304 .unwrap();
23305 let tree = parser.parse(source, None).unwrap();
23306 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
23307 let body = namespace
23308 .child_by_field_name("body")
23309 .expect("fixture namespace body");
23310 let sentinel = body.named_child(0).expect("sentinel envelope");
23311 let callable = sentinel
23312 .child_by_field_name("declarator")
23313 .and_then(extract_function_declarator)
23314 .and_then(cpp_function_declarator_name_node)
23315 .expect("preserved callable name");
23316
23317 assert_eq!(sentinel.kind(), "function_definition");
23318 assert_eq!(callable.kind(), "identifier");
23319 assert!(
23320 cpp_sentinel_macro_parts(sentinel, source).is_some(),
23321 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
23322 );
23323 }
23324
23325 #[test]
23326 #[hardwon(
23327 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23328 )]
23329 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
23330 let source = r#"
23331#define CPPCHECKLIB
23332class Library {
23333 struct Container {
23334 CPPCHECKLIB static std::string toString(Yield yield);
23335 CPPCHECKLIB static std::string toString(Action action);
23336 };
23337};
23338"#;
23339 let mut parser = tree_sitter::Parser::new();
23340 parser
23341 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23342 .unwrap();
23343 let tree = parser.parse(source, None).unwrap();
23344 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
23345 let parsed = parse_cpp_file(&file, source, &tree);
23346 assert!(
23347 parsed
23348 .declarations()
23349 .iter()
23350 .all(|unit| unit.fq_name() != "Library$Container.std"),
23351 "the qualified return-type namespace must not become a field: {:#?}",
23352 parsed.declarations()
23353 );
23354 for expected in ["(Yield)", "(Action)"] {
23355 assert!(
23356 parsed.declarations().iter().any(|unit| {
23357 unit.is_function()
23358 && unit.fq_name() == "Library$Container.toString"
23359 && unit.signature() == Some(expected)
23360 }),
23361 "recovered toString overload {expected} is missing: {:#?}",
23362 parsed.declarations()
23363 );
23364 }
23365 }
23366
23367 #[test]
23368 #[hardwon(
23369 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23370 )]
23371 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
23372 let source = r#"
23373#define SIMPLECPP_LIB
23374namespace simplecpp {
23375using TokenString = std::string;
23376struct Location { int line{}; };
23377class SIMPLECPP_LIB Token {
23378 TokenString prefix;
23379 void prefix_method() {}
23380 public:
23381 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
23382 whitespaceahead(wsahead), location(loc), string(s)
23383 // The comment must not hide the constructor body from recovery.
23384 {
23385 flags();
23386 }
23387 TokenString string;
23388 bool whitespaceahead;
23389 Location location;
23390 Token *previous{};
23391 private:
23392 void flags() {
23393 whitespaceahead = true;
23394 }
23395};
23396}
23397"#;
23398 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
23399
23400 let location_fields = parsed
23401 .declarations()
23402 .iter()
23403 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
23404 .collect::<Vec<_>>();
23405 assert_eq!(
23406 location_fields.len(),
23407 1,
23408 "location should have one class-owned declaration: {:#?}",
23409 parsed.declarations()
23410 );
23411 assert!(
23412 location_fields[0].is_field(),
23413 "location has wrong kind: {:#?}",
23414 parsed.declarations()
23415 );
23416 assert!(
23417 parsed.declarations().iter().all(|unit| {
23418 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
23419 })
23420 );
23421 assert!(
23422 parsed.declarations().iter().all(|unit| {
23423 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
23424 })
23425 );
23426 assert!(
23427 parsed
23428 .declarations()
23429 .iter()
23430 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
23431 );
23432 assert!(
23433 parsed
23434 .declarations()
23435 .iter()
23436 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
23437 "the recovered class must retain its constructor: {:#?}",
23438 parsed.declarations()
23439 );
23440 assert!(
23441 parsed
23442 .declarations()
23443 .iter()
23444 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
23445 );
23446 assert!(parsed.declarations().iter().any(|unit| {
23447 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
23448 }));
23449 let constructor = parsed
23450 .declarations()
23451 .iter()
23452 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
23453 .expect("recovered constructor");
23454 let constructor_start = source.find("Token(const").expect("constructor start");
23455 let constructor_end = source
23456 .get(
23457 ..source
23458 .find(" TokenString string;")
23459 .expect("constructor end"),
23460 )
23461 .expect("constructor slice")
23462 .trim_end()
23463 .len();
23464 assert!(
23465 parsed
23466 .navigation_ranges
23467 .get(constructor)
23468 .is_some_and(|ranges| {
23469 ranges.iter().any(|range| {
23470 range.start_byte == constructor_start && range.end_byte == constructor_end
23471 })
23472 }),
23473 "constructor navigation must span the full body: {:#?}",
23474 parsed.navigation_ranges
23475 );
23476 assert_eq!(
23477 parsed
23478 .signature_metadata
23479 .get(constructor)
23480 .and_then(|metadata| metadata.first())
23481 .and_then(SignatureMetadata::callable_linkage),
23482 Some(CallableLinkage::External)
23483 );
23484 let token_class = parsed
23485 .declarations()
23486 .iter()
23487 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
23488 .expect("recovered Token class");
23489 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
23490 assert!(
23491 parsed
23492 .navigation_ranges
23493 .get(token_class)
23494 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
23495 "class navigation must include the terminating semicolon: {:#?}",
23496 parsed.navigation_ranges
23497 );
23498 }
23499
23500 #[test]
23501 #[hardwon(
23502 "b28b8950890a: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23503 )]
23504 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
23505 let source = r#"
23506#define SIMPLECPP_LIB
23507namespace simplecpp {
23508using TokenString = std::string;
23509class Macro;
23510struct Location {
23511 unsigned int fileIndex{};
23512 unsigned int line{};
23513 unsigned int col{};
23514};
23515struct Output {
23516 int type;
23517};
23518class SIMPLECPP_LIB Token {
23519 public:
23520 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
23521 whitespaceahead(wsahead), location(loc), string(s) {
23522 flags();
23523 }
23524 Token(const Token &tok) :
23525 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
23526 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
23527 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
23528 Token &operator=(const Token &tok) = delete;
23529 const TokenString& str() const { return string; }
23530 void setstr(const std::string &s) { string = s; flags(); }
23531 bool isOneOf(const char ops[]) const;
23532 TokenString macro;
23533 char op;
23534 bool comment;
23535 bool name;
23536 bool number;
23537 bool whitespaceahead;
23538 Location location;
23539 Token *previous{};
23540 Token *next{};
23541 private:
23542 void flags() {
23543 name = !string.empty();
23544 comment = false;
23545 number = false;
23546 op = 0;
23547 }
23548 TokenString string;
23549};
23550}
23551struct Following {
23552 int type;
23553};
23554class SIMPLECPP_LIB Later {
23555 public:
23556 Later(int value) : value(value) {}
23557 int value;
23558};
23559"#;
23560 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
23561 assert!(
23562 parsed
23563 .declarations()
23564 .iter()
23565 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
23566 );
23567 assert!(
23568 !parsed
23569 .declarations()
23570 .iter()
23571 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
23572 );
23573 assert!(
23574 parsed
23575 .declarations()
23576 .iter()
23577 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
23578 );
23579 assert!(
23580 !parsed
23581 .declarations()
23582 .iter()
23583 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
23584 );
23585 assert!(
23586 parsed
23587 .declarations()
23588 .iter()
23589 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
23590 );
23591 assert!(
23592 parsed
23593 .declarations()
23594 .iter()
23595 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
23596 );
23597 assert!(
23598 parsed
23599 .declarations()
23600 .iter()
23601 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
23602 );
23603 assert!(
23604 parsed
23605 .declarations()
23606 .iter()
23607 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
23608 );
23609 assert!(
23610 parsed
23611 .declarations()
23612 .iter()
23613 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
23614 );
23615 assert!(
23616 parsed
23617 .declarations()
23618 .iter()
23619 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
23620 );
23621 assert!(parsed.declarations().iter().all(|unit| {
23622 !matches!(
23623 unit.fq_name().as_str(),
23624 "simplecpp.Token.Following" | "simplecpp.Token.Later"
23625 )
23626 }));
23627 assert!(
23628 !parsed
23629 .declarations()
23630 .iter()
23631 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
23632 "the following struct must remain outside the recovered Token class"
23633 );
23634 }
23635
23636 #[test]
23637 #[hardwon(
23638 "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23639 )]
23640 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
23641 let source = r#"
23642#define SIMPLECPP_LIB
23643namespace {
23644namespace simplecpp {
23645using TokenString = std::string;
23646struct Location { int line{}; };
23647class SIMPLECPP_LIB HiddenToken {
23648 public:
23649 HiddenToken(const TokenString &s, const Location &loc) :
23650 location(loc), string(s) {
23651 flags();
23652 }
23653 TokenString string;
23654 Location location;
23655 HiddenToken *previous{};
23656 private:
23657 void flags() {}
23658};
23659}
23660}
23661"#;
23662 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
23663 let constructor = parsed
23664 .declarations()
23665 .iter()
23666 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
23667 .expect("recovered anonymous-namespace constructor");
23668 assert_eq!(
23669 parsed
23670 .signature_metadata
23671 .get(constructor)
23672 .and_then(|metadata| metadata.first())
23673 .and_then(SignatureMetadata::callable_linkage),
23674 Some(CallableLinkage::Internal)
23675 );
23676 }
23677
23678 #[test]
23679 #[hardwon(
23680 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23681 )]
23682 fn macro_qualified_static_field_keeps_real_declarator() {
23683 let source = r#"#define JSON_INLINE_VARIABLE
23684struct Reader {
23685static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
23686static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
23687static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
23688};"#;
23689 let mut parser = tree_sitter::Parser::new();
23690 parser
23691 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23692 .unwrap();
23693 let tree = parser.parse(source, None).unwrap();
23694 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
23695 let parsed = parse_cpp_file(&file, source, &tree);
23696 for expected in [
23697 "Reader.npos",
23698 "Reader.other",
23699 "Reader.pointer",
23700 "Reader.reference",
23701 ] {
23702 assert!(
23703 parsed
23704 .declarations()
23705 .iter()
23706 .any(|unit| unit.is_field() && unit.fq_name() == expected),
23707 "real macro-decorated field {expected} is missing: {:#?}",
23708 parsed.declarations()
23709 );
23710 }
23711 assert!(
23712 parsed
23713 .declarations()
23714 .iter()
23715 .all(|unit| unit.fq_name() != "Reader.std"),
23716 "qualified type prefix became a pseudo-field: {:#?}",
23717 parsed.declarations()
23718 );
23719 let root = tree.root_node();
23720 let mut stack = vec![root];
23721 let mut signatures = Vec::new();
23722 while let Some(current) = stack.pop() {
23727 if current.kind() == "field_declaration" {
23728 let mut cursor = current.walk();
23729 for declarator in current.children_by_field_name("declarator", &mut cursor) {
23730 signatures.push(render_cpp_field_signature(current, declarator, source));
23731 }
23732 }
23733 let mut cursor = current.walk();
23734 stack.extend(current.named_children(&mut cursor));
23735 }
23736 signatures.sort();
23737 assert_eq!(
23738 signatures,
23739 [
23740 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
23741 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
23742 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
23743 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
23744 ]
23745 );
23746 }
23747
23748 fn member_function_linkage(source: &str) -> CallableLinkage {
23749 let mut parser = tree_sitter::Parser::new();
23750 parser
23751 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23752 .unwrap();
23753 let tree = parser.parse(source, None).unwrap();
23754 let ancestry = ParentIndex::new(tree.root_node());
23755 let mut stack = vec![tree.root_node()];
23756 while let Some(node) = stack.pop() {
23757 if node.kind() == "function_definition" {
23758 let mut current = node.parent();
23759 while let Some(parent) = current {
23760 if matches!(
23761 parent.kind(),
23762 "class_specifier" | "struct_specifier" | "union_specifier"
23763 ) {
23764 return cpp_callable_linkage(node, source, &ancestry);
23765 }
23766 current = parent.parent();
23767 }
23768 }
23769 let mut cursor = node.walk();
23770 stack.extend(node.named_children(&mut cursor));
23771 }
23772 panic!("fixture has no member function definition");
23773 }
23774
23775 #[test]
23776 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
23777 assert_eq!(
23778 member_function_linkage("struct Named { int method() { return 1; } };"),
23779 CallableLinkage::External
23780 );
23781 assert_eq!(
23782 member_function_linkage(
23783 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
23784 ),
23785 CallableLinkage::Internal
23786 );
23787 assert_eq!(
23788 member_function_linkage("struct { int method() { return 1; } } instance;"),
23789 CallableLinkage::Internal
23790 );
23791 assert_eq!(
23792 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
23793 CallableLinkage::Internal
23794 );
23795 }
23796
23797 #[test]
23798 #[hardwon(
23799 "fe77a0120e5e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23800 )]
23801 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
23802 let source = r#"
23803#ifndef PROTON_VALUE_HPP
23804#define PROTON_VALUE_HPP
23805namespace proton {
23806namespace internal {
23807class value_base {
23808 protected:
23809 internal::data& data();
23810 internal::data data_;
23811 friend class codec::encoder;
23812 friend class codec::decoder;
23813};
23814}
23815class value : public internal::value_base, private internal::comparable<value> {
23816 private:
23817 template<class T, class U=void> struct assignable :
23818 public std::enable_if<codec::is_encodable<T>::value, U> {};
23819 template<class U> struct assignable<value, U> {};
23820 public:
23821 PN_CPP_EXTERN value();
23822 PN_CPP_EXTERN value(const value&);
23823 PN_CPP_EXTERN value& operator=(const value&);
23824 PN_CPP_EXTERN value(value&&);
23825 PN_CPP_EXTERN value& operator=(value&&);
23826 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
23827 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
23828 codec::encoder e(*this);
23829 e << x;
23830 return *this;
23831 }
23832 PN_CPP_EXTERN type_id type() const;
23833 PN_CPP_EXTERN bool empty() const;
23834 PN_CPP_EXTERN void clear();
23835 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
23836 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
23837 friend PN_CPP_EXTERN void swap(value&, value&);
23838 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
23839 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
23840 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
23841 value(pn_data_t* d);
23842 void reset(pn_data_t* d = 0);
23843};
23844}
23845#endif
23846"#;
23847 let mut parser = tree_sitter::Parser::new();
23848 parser
23849 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23850 .unwrap();
23851 let tree = parser.parse(source, None).unwrap();
23852 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
23853 let parsed = parse_cpp_file(&file, source, &tree);
23854 let macro_constructors = parsed
23859 .signature_metadata
23860 .iter()
23861 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value.value")
23862 .flat_map(|(_, metadata)| metadata)
23863 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
23864 .collect::<Vec<_>>();
23865
23866 assert_eq!(
23867 macro_constructors.len(),
23868 3,
23869 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
23870 parsed.declarations()
23871 );
23872 assert!(
23873 macro_constructors.iter().all(|metadata| {
23874 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
23875 }),
23876 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
23877 );
23878 }
23879
23880 #[test]
23881 #[hardwon(
23882 "c53d3b9c1b19: C++ declaration recovery regressed for recovered export class typedef uses displaced alias name"
23883 )]
23884 fn recovered_export_class_typedef_uses_displaced_alias_name() {
23885 let source = r#"
23886namespace spi {
23887class Filter {
23888public:
23889 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
23890};
23891}
23892namespace filter {
23893class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
23894{
23895public:
23896 typedef spi::Filter BASE_CLASS;
23897 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
23898 BEGIN_LOG4CXX_CAST_MAP()
23899 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
23900 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
23901 END_LOG4CXX_CAST_MAP()
23902 FilterDecision decide() const;
23903};
23904}
23905"#;
23906 let mut parser = tree_sitter::Parser::new();
23907 parser
23908 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23909 .unwrap();
23910 let tree = parser.parse(source, None).unwrap();
23911 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
23912 let parsed = parse_cpp_file(&file, source, &tree);
23913 assert!(
23914 parsed.declarations().iter().any(|unit| {
23915 unit.is_class()
23916 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
23917 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
23918 }),
23919 "the displaced typedef alias must retain its declared name: {:#?}",
23920 parsed.declarations()
23921 );
23922 assert!(
23923 parsed
23924 .declarations()
23925 .iter()
23926 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
23927 "the qualified underlying type must not become a false nested alias: {:#?}",
23928 parsed.declarations()
23929 );
23930 }
23931
23932 #[test]
23933 #[hardwon(
23934 "f7a2bb562e28: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23935 )]
23936 fn exported_single_base_recovery_uses_displaced_class_name() {
23937 let source = r#"
23938class CORE_EXPORT QgsPoint : public AbstractGeometry
23939{
23940 Q_GADGET
23941
23942 Q_PROPERTY( double x READ x WRITE setX )
23943 Q_PROPERTY( double y READ y WRITE setY )
23944 Q_PROPERTY( double z READ z WRITE setZ )
23945 Q_PROPERTY( double m READ m WRITE setM )
23946
23947 public:
23948#ifndef SIP_RUN
23949 QgsPoint(
23950 double x = std::numeric_limits<double>::quiet_NaN(),
23951 double y = std::numeric_limits<double>::quiet_NaN(),
23952 double z = std::numeric_limits<double>::quiet_NaN(),
23953 double m = std::numeric_limits<double>::quiet_NaN(),
23954 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
23955 );
23956#else
23957 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 )];
23958 % MethodCode
23959 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
23960 {
23961 int state;
23962 sipIsErr = 0;
23963 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
23964 if ( !sipIsErr )
23965 {
23966 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
23967 }
23968 sipReleaseType( p, sipType_QgsPointXY, state );
23969 }
23970 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
23971 {
23972 int state;
23973 sipIsErr = 0;
23974
23975 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
23976 if ( !sipIsErr )
23977 {
23978 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
23979 }
23980 sipReleaseType( p, sipType_QPointF, state );
23981 }
23982 else if (
23983 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
23984 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
23985 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
23986 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
23987 {
23988 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
23989 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
23990 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
23991 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
23992 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
23993 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
23994 }
23995 else // Invalid ctor arguments
23996 {
23997 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
23998 sipIsErr = 1;
23999 }
24000 % End
24001#endif
24002
24003 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
24004 explicit QgsPoint( QPointF p ) SIP_SKIP;
24005 explicit QgsPoint(
24006 Qgis::WkbType wkbType,
24007 double x = std::numeric_limits<double>::quiet_NaN(),
24008 double y = std::numeric_limits<double>::quiet_NaN(),
24009 double z = std::numeric_limits<double>::quiet_NaN(),
24010 double m = std::numeric_limits<double>::quiet_NaN()
24011 ) SIP_SKIP;
24012 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
24013 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
24014 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
24015#ifndef SIP_RUN
24016 private:
24017 bool fuzzyHelper(
24018 double epsilon,
24019 const AbstractGeometry &other,
24020 bool is3DFlag,
24021 bool isMeasureFlag
24022 ) const
24023 {
24024 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
24025 }
24026#endif
24027};
24028class Ordinary : public Base { public: Ordinary(); };
24029class API_EXPORT Plain { public: Plain(); };
24030class API_EXPORT : public Base {};
24031class
24032PN_CPP_CLASS_EXTERN Sender : public Link {
24033 Sender();
24034 struct impl;
24035 struct impl& get_impl() const;
24036};
24037class thread_ctx_t {};
24038class ctx_t ZMQ_FINAL : public thread_ctx_t {
24039 bool start();
24040};
24041"#;
24042 let mut parser = tree_sitter::Parser::new();
24043 parser
24044 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24045 .unwrap();
24046 let tree = parser.parse(source, None).unwrap();
24047 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
24048 let parsed = parse_cpp_file(&file, source, &tree);
24049 let declarations = parsed.declarations();
24050
24051 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
24052 assert!(
24053 declarations
24054 .iter()
24055 .any(|unit| unit.is_class() && unit.fq_name() == expected),
24056 "missing recovered class {expected}: {declarations:#?}"
24057 );
24058 }
24059 let qgs_point = declarations
24060 .iter()
24061 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
24062 .expect("recovered QgsPoint class");
24063 assert_eq!(
24064 parsed.raw_supertypes.get(qgs_point),
24065 Some(&vec!["AbstractGeometry".to_string()]),
24066 "single-base export recovery must retain its displaced base"
24067 );
24068 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
24069 assert!(
24070 parsed
24071 .navigation_ranges
24072 .get(qgs_point)
24073 .is_some_and(|ranges| {
24074 !ranges.is_empty()
24075 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
24076 }),
24077 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
24078 parsed.navigation_ranges.get(qgs_point)
24079 );
24080 let sender = declarations
24081 .iter()
24082 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
24083 .expect("recovered Sender class");
24084 assert_eq!(
24085 parsed.raw_supertypes.get(sender),
24086 Some(&vec!["Link".to_string()]),
24087 "post-declarator export recovery must retain its displaced base"
24088 );
24089 let recovered_member = declarations
24090 .iter()
24091 .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
24092 .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
24093 assert_eq!(
24094 parsed
24095 .signature_metadata
24096 .get(recovered_member)
24097 .and_then(|metadata| metadata.first())
24098 .and_then(SignatureMetadata::callable_linkage),
24099 Some(CallableLinkage::External),
24100 "a named recovered class's members have external linkage"
24101 );
24102 let ctx = declarations
24103 .iter()
24104 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
24105 .expect("recovered ctx_t class");
24106 assert_eq!(
24107 parsed.raw_supertypes.get(ctx),
24108 Some(&vec!["thread_ctx_t".to_string()]),
24109 "postfix export-macro recovery must retain its displaced base"
24110 );
24111 assert!(
24112 declarations.iter().any(|unit| {
24113 unit.is_function()
24114 && unit.fq_name() == "QgsPoint.QgsPoint"
24115 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
24116 }),
24117 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
24118 );
24119 assert!(
24120 declarations.iter().all(|unit| {
24121 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
24122 }),
24123 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
24124 );
24125 }
24126
24127 #[test]
24128 #[hardwon(
24129 "364341b21a48: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24130 )]
24131 fn function_like_export_macro_classes_keep_names_and_base_edges() {
24132 let source = r#"
24136namespace api {
24137class PROJECT_PUBLIC_API(2, 0) Prelude {
24138 public:
24139 Prelude();
24140};
24141class PROJECT_PUBLIC_API(2, 0) Base {
24142 public:
24143 Base(int value);
24144};
24145class PROJECT_PUBLIC_API(2, 0) Mixin {
24146 public:
24147 Mixin();
24148};
24149class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
24150 public:
24151 Adopted(int value);
24152};
24153class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
24154 public:
24155 Derived(int value);
24156};
24157class PROJECT_PUBLIC_API(2, 0) Solo final {
24158 public:
24159 Solo();
24160};
24161class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
24162 public:
24163 Blended(int value);
24164};
24165class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
24166 public:
24167 Woven(int value);
24168};
24169} // namespace api
24170"#;
24171 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
24172 let declarations = parsed.declarations();
24173 let class_named = |name: &str| {
24174 declarations
24175 .iter()
24176 .find(|unit| unit.is_class() && unit.fq_name() == name)
24177 .unwrap_or_else(|| {
24178 panic!("missing function-like export macro class {name}: {declarations:#?}")
24179 })
24180 };
24181 let base = class_named("api.Base");
24182 class_named("api.Prelude");
24183 class_named("api.Mixin");
24184
24185 assert_eq!(
24186 parsed.raw_supertypes.get(class_named("api.Adopted")),
24187 Some(&vec!["Base".to_string()])
24188 );
24189 assert_eq!(
24190 parsed.raw_supertypes.get(class_named("api.Derived")),
24191 Some(&vec!["Base".to_string()])
24192 );
24193 assert_eq!(
24194 parsed.raw_supertypes.get(class_named("api.Solo")),
24195 None,
24196 "a final class without a base list must not invent a supertype"
24197 );
24198 assert_eq!(
24199 parsed.raw_supertypes.get(class_named("api.Blended")),
24200 Some(&vec!["Base".to_string(), "Mixin".to_string()])
24201 );
24202 assert_eq!(
24203 parsed.raw_supertypes.get(class_named("api.Woven")),
24204 Some(&vec!["Base".to_string(), "Mixin".to_string()])
24205 );
24206 assert!(
24207 declarations
24208 .iter()
24209 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
24210 "the export macro must not become a declaration: {declarations:#?}"
24211 );
24212 assert!(
24213 declarations.iter().all(|unit| !matches!(
24214 unit.identifier(),
24215 "final" | "public" | "protected" | "private"
24216 )),
24217 "the head specifiers must not become declarations: {declarations:#?}"
24218 );
24219 assert!(
24220 parsed
24221 .navigation_ranges
24222 .get(base)
24223 .is_some_and(|ranges| !ranges.is_empty()),
24224 "the recovered base must retain a navigable declaration range"
24225 );
24226 }
24227
24228 #[test]
24229 #[hardwon(
24230 "861f46399c96: C++ declaration recovery regressed for function like export macro classes are named by position not spelling"
24231 )]
24232 fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
24233 let source = r#"
24239namespace api {
24240class PROJECT_PUBLIC_API(2, 0) Base {
24241 public:
24242 Base();
24243};
24244class PROJECT_PUBLIC_API(2, 0) Mixin {
24245 public:
24246 Mixin();
24247};
24248class PROJECT_PUBLIC_API(2, 0) Name {
24249 public:
24250 Name();
24251};
24252class PROJECT_PUBLIC_API(2, 0) X509_CA final {
24253 public:
24254 X509_CA();
24255};
24256class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
24257 public:
24258 HSS_LMS_KEY();
24259};
24260class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
24261 public:
24262 GOST_3410();
24263};
24264class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
24265 public:
24266 PKCS11_RSA();
24267};
24268class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
24269 public:
24270 Plain();
24271};
24272class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
24273 public:
24274 Decorated();
24275};
24276class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
24277 public:
24278 Layered();
24279};
24280} // namespace api
24281"#;
24282 let parsed = parse_cpp_declarations(source, "positional-export.hpp");
24283 let declarations = parsed.declarations();
24284 let class_named = |name: &str| {
24285 declarations
24286 .iter()
24287 .find(|unit| unit.is_class() && unit.fq_name() == name)
24288 .unwrap_or_else(|| {
24289 panic!("missing function-like export macro class {name}: {declarations:#?}")
24290 })
24291 };
24292 for (name, bases) in [
24293 ("api.Name", None),
24294 ("api.X509_CA", None),
24295 ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
24296 ("api.GOST_3410", Some(vec!["Base"])),
24297 ("api.PKCS11_RSA", None),
24298 ("api.Plain", None),
24299 ("api.Decorated", Some(vec!["Base"])),
24300 ("api.Layered", Some(vec!["Base", "Mixin"])),
24301 ] {
24302 let expected =
24303 bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
24304 assert_eq!(
24305 parsed.raw_supertypes.get(class_named(name)),
24306 expected.as_ref(),
24307 "{name}"
24308 );
24309 }
24310 assert!(
24311 declarations.iter().all(|unit| !matches!(
24312 unit.identifier(),
24313 "PROJECT_PUBLIC_API"
24314 | "OTHER_MACRO"
24315 | "FIRST_MACRO"
24316 | "SECOND_MACRO"
24317 | "final"
24318 | "public"
24319 )),
24320 "macros and head specifiers must not become declarations: {declarations:#?}"
24321 );
24322 }
24323
24324 #[test]
24325 #[hardwon(
24326 "7552b3145622: C++ declaration recovery regressed for export class head with a virtual base recovers its fragmented body"
24327 )]
24328 fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
24329 let source = r#"
24335namespace api {
24336
24337/**
24338* Doc comment
24339*/
24340class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
24341 public:
24342 /**
24343 * Construct from a point.
24344 */
24345 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
24346
24347#if defined(PROJECT_HAS_LEGACY_POINT)
24348 /**
24349 * Construct from a legacy point.
24350 */
24351 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
24352#endif
24353
24354 std::string algo_name() const override;
24355
24356 AlgorithmIdentifier algorithm_identifier() const override;
24357};
24358
24359}
24360"#;
24361 let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
24362 let declarations = parsed.declarations();
24363 let class = declarations
24364 .iter()
24365 .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
24366 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
24367 assert_eq!(
24368 parsed.raw_supertypes.get(class),
24369 Some(&vec!["BaseKey".to_string()]),
24370 "the virtual base is the class's base: {declarations:#?}"
24371 );
24372 for member in [
24373 "api.VirtualBased.algo_name",
24374 "api.VirtualBased.algorithm_identifier",
24375 ] {
24376 assert!(
24377 declarations
24378 .iter()
24379 .any(|unit| unit.is_function() && unit.fq_name() == member),
24380 "{member} must be owned by the recovered class: {declarations:#?}"
24381 );
24382 }
24383 assert!(
24384 declarations
24385 .iter()
24386 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
24387 "an unrecovered head must not mint a macro-named class: {declarations:#?}"
24388 );
24389 }
24390
24391 #[test]
24392 #[hardwon(
24393 "7552b3145622: C++ declaration recovery regressed for export class head after object macro lines recovers its name and bases"
24394 )]
24395 fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
24396 let source = r#"
24403namespace api {
24404
24405DIAGNOSTIC_PUSH
24406DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
24407
24408class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
24409 public virtual api::Inner::Key {
24410 public:
24411 std::string algo_name() const override;
24412};
24413
24414DIAGNOSTIC_POP
24415
24416}
24417"#;
24418 let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
24419 let declarations = parsed.declarations();
24420 let class = declarations
24421 .iter()
24422 .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
24423 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
24424 assert_eq!(
24425 parsed.raw_supertypes.get(class),
24426 Some(&vec![
24427 "api::Outer::Key".to_string(),
24428 "api::Inner::Key".to_string()
24429 ]),
24430 "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
24431 );
24432 assert!(
24433 declarations
24434 .iter()
24435 .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
24436 "the member is owned by the recovered class: {declarations:#?}"
24437 );
24438 assert!(
24439 declarations
24440 .iter()
24441 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
24442 "the macro invocation must not mint a declaration: {declarations:#?}"
24443 );
24444 }
24445
24446 fn function_like_export_class_fixture(head: &str, name: &str) -> String {
24447 format!(
24448 r#"
24449namespace api {{
24450class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
24451 public:
24452 /** Return a descriptive string. */
24453 const char* what() const noexcept override {{ return m_msg.c_str(); }}
24454
24455 /** Return the type of error. */
24456 virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
24457
24458 /** Return an associated error code. */
24459 virtual int error_code() const noexcept {{ return 0; }}
24460
24461 /** Avoid throwing the base directly. */
24462 explicit Exception(std::string_view msg);
24463
24464 /** Avoid throwing the base directly. */
24465 Exception(const char* prefix, std::string_view msg);
24466
24467 /** Avoid throwing the base directly. */
24468 Exception(std::string_view msg, const std::exception& e);
24469
24470 private:
24471 std::string m_msg;
24472}};
24473
24474class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
24475 public:
24476 explicit {name}(std::string_view msg);
24477
24478 explicit {name}(std::string_view msg, std::string_view where);
24479
24480 {name}(std::string_view msg, const std::exception& e);
24481
24482 ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
24483}};
24484}} // namespace api
24485"#
24486 )
24487 }
24488
24489 #[test]
24490 #[hardwon(
24491 "861f46399c96: C++ declaration recovery regressed for embedded function like export class is named by position not spelling"
24492 )]
24493 fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
24494 for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
24497 let source = function_like_export_class_fixture(head, name);
24498 let mut parser = tree_sitter::Parser::new();
24499 parser
24500 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24501 .expect("set C++ grammar");
24502 let tree = parser.parse(&source, None).expect("parse fixture");
24503 let mut embedded = Vec::new();
24504 let mut stack = vec![tree.root_node()];
24505 while let Some(node) = stack.pop() {
24506 embedded.extend(
24507 recover_embedded_function_like_export_classes(node, &source)
24508 .into_iter()
24509 .map(|recovered| (recovered.name, recovered.raw_supertypes)),
24510 );
24511 let mut cursor = node.walk();
24512 stack.extend(node.named_children(&mut cursor));
24513 }
24514 assert!(
24515 embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
24516 "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
24517 tree.root_node().to_sexp()
24518 );
24519 assert!(
24520 embedded
24521 .iter()
24522 .all(|(recovered, _)| recovered != "OTHER_MACRO"),
24523 "{head}: the object-like macro is not a class: {embedded:#?}"
24524 );
24525
24526 let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
24527 let declarations = parsed.declarations();
24528 let class = declarations
24529 .iter()
24530 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
24531 .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
24532 assert_eq!(
24533 parsed.raw_supertypes.get(class),
24534 Some(&vec!["Exception".to_string()]),
24535 "{head}"
24536 );
24537 assert!(
24538 declarations
24539 .iter()
24540 .all(|unit| unit.identifier() != "OTHER_MACRO"),
24541 "{head}: the object-like macro must not become a declaration: {declarations:#?}"
24542 );
24543 }
24544 }
24545
24546 #[test]
24547 #[hardwon(
24548 "861f46399c96: C++ declaration recovery regressed for function like export class head with virtual qualified bases does not invent a name"
24549 )]
24550 fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
24551 let source = r#"
24561namespace api {
24562class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
24563 public virtual Botan::EC_PublicKey {
24564 public:
24565 std::string algo_name() const override { return "ECDSA"; }
24566};
24567} // namespace api
24568"#;
24569 let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
24570 let declarations = parsed.declarations();
24571 assert!(
24572 declarations
24573 .iter()
24574 .all(|unit| !unit.identifier().is_empty()),
24575 "no declaration may carry an empty name: {declarations:#?}"
24576 );
24577 assert!(
24578 declarations
24579 .iter()
24580 .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
24581 "macros and head specifiers must not become declarations: {declarations:#?}"
24582 );
24583 let class = declarations
24584 .iter()
24585 .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
24586 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
24587 assert_eq!(
24588 parsed.raw_supertypes.get(class),
24589 Some(&vec![
24590 "Botan::TPM2::PublicKey".to_string(),
24591 "Botan::EC_PublicKey".to_string()
24592 ]),
24593 "both qualified virtual bases are bases: {declarations:#?}"
24594 );
24595 let source_facts = parsed.source_facts.as_ref().unwrap();
24596 let declaration = parsed
24597 .source_declaration_units
24598 .iter()
24599 .find(|(_, unit)| unit == class)
24600 .map(|(declaration, _)| *declaration)
24601 .expect("recovered class source bridge");
24602 let bases = source_facts
24603 .cpp
24604 .as_ref()
24605 .unwrap()
24606 .declarations
24607 .iter()
24608 .find(|fact| fact.declaration == declaration)
24609 .expect("recovered class source fact")
24610 .bases
24611 .clone();
24612 assert_eq!(
24613 bases
24614 .iter()
24615 .map(|base| base.components.clone())
24616 .collect::<Vec<_>>(),
24617 vec![
24618 vec!["Botan", "TPM2", "PublicKey"],
24619 vec!["Botan", "EC_PublicKey"],
24620 ],
24621 "generalized recovery must publish structured base components"
24622 );
24623 assert!(
24624 bases.iter().all(|base| base.is_virtual),
24625 "generalized recovery must preserve virtual base qualifiers"
24626 );
24627 assert!(
24628 declarations
24629 .iter()
24630 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
24631 "the head must not mint a macro-named class: {declarations:#?}"
24632 );
24633 let mixed_source = source.replace(
24634 "public virtual Botan::EC_PublicKey",
24635 "public Botan::EC_PublicKey",
24636 );
24637 let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
24638 let qualifiers = mixed
24639 .source_facts
24640 .as_ref()
24641 .unwrap()
24642 .cpp
24643 .as_ref()
24644 .unwrap()
24645 .declarations
24646 .iter()
24647 .flat_map(|declaration| &declaration.bases)
24648 .map(|base| base.is_virtual)
24649 .collect::<Vec<_>>();
24650 assert_eq!(
24651 qualifiers,
24652 [true, false],
24653 "base qualifiers must not cross the comma"
24654 );
24655 }
24656
24657 #[test]
24658 #[hardwon(
24659 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24660 )]
24661 fn function_like_export_class_survives_a_preceding_malformed_body() {
24662 let source = function_like_export_class_fixture("Invalid_Argument", "Invalid_Argument");
24663 let mut parser = tree_sitter::Parser::new();
24664 parser
24665 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24666 .expect("set C++ grammar");
24667 let tree = parser.parse(&source, None).expect("parse fixture");
24668 let mut stack = vec![tree.root_node()];
24669 let mut saw_embedded_shape = false;
24670 while let Some(node) = stack.pop() {
24671 saw_embedded_shape |= recover_embedded_function_like_export_classes(node, &source)
24672 .iter()
24673 .any(|recovered| recovered.name == "Invalid_Argument");
24674 let mut cursor = node.walk();
24675 stack.extend(node.named_children(&mut cursor));
24676 }
24677 assert!(
24678 saw_embedded_shape,
24679 "fixture must retain the embedded error geometry: {}",
24680 tree.root_node().to_sexp()
24681 );
24682
24683 let parsed = parse_cpp_file(
24684 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
24685 &source,
24686 &tree,
24687 );
24688 let declarations = parsed.declarations();
24689 let exception = declarations
24690 .iter()
24691 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
24692 .expect("qualified-base export class");
24693 let invalid = declarations
24694 .iter()
24695 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
24696 .expect("class embedded in the preceding malformed body");
24697
24698 assert_eq!(
24699 parsed.raw_supertypes.get(exception),
24700 Some(&vec!["std::exception".to_string()])
24701 );
24702 assert_eq!(
24703 parsed.raw_supertypes.get(invalid),
24704 Some(&vec!["Exception".to_string()])
24705 );
24706 assert!(
24707 parsed.materialization_records.iter().any(|record| matches!(
24708 record,
24709 MaterializationRecord::RecoveredDeclaration { unit, .. }
24710 if unit == invalid
24711 )),
24712 "the embedded class must retain recovery provenance: {:#?}",
24713 parsed.materialization_records
24714 );
24715 }
24716
24717 #[test]
24718 #[hardwon(
24719 "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24720 )]
24721 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
24722 let source = r#"
24723public:
24724 explicit Lookup_Error(std::string_view err) : Exception(err) {}
24725
24726 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
24727"#;
24728 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
24729 .expect("reparse merged constructor body");
24730 let (range, body) =
24731 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
24732 .unwrap_or_else(|| {
24733 panic!(
24734 "the merged constructor must retain its structured declarator/body: {}",
24735 tree.root_node().to_sexp()
24736 )
24737 });
24738 assert_eq!(
24739 source.get(range).expect("constructor range"),
24740 "Lookup_Error(std::string_view err) : Exception(err) {}"
24741 );
24742 assert_eq!(node_text(body, source), "{}");
24743 }
24744
24745 #[test]
24746 #[hardwon(
24747 "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24748 )]
24749 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
24750 let positive_source =
24751 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
24752 let mut parser = tree_sitter::Parser::new();
24753 parser
24754 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24755 .unwrap();
24756 let positive_tree = parser.parse(positive_source, None).unwrap();
24757 assert!(cpp_reparsed_members_are_indexable(
24758 positive_tree.root_node(),
24759 positive_source
24760 ));
24761
24762 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
24763 let negative_tree = parser.parse(negative_source, None).unwrap();
24764 assert!(!cpp_reparsed_members_are_indexable(
24765 negative_tree.root_node(),
24766 negative_source
24767 ));
24768 }
24769
24770 #[test]
24771 #[hardwon(
24772 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24773 )]
24774 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
24775 let copy_control_source = r#"
24776public:
24777 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
24778 explicit Token(const Token* tok);
24779 ~Token();
24780 Token* astOperand1() { return nullptr; }
24781"#;
24782 let constraint_source = r#"
24783private:
24784 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
24785 static T *tokAtImpl(T *tok, int index) {
24786 return tok;
24787 }
24788
24789 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
24790 static T *linkAtImpl(T *tok, int index) {
24791 return tok;
24792 }
24793
24794public:
24795 int late() const { return 1; }
24796"#;
24797 let mut parser = tree_sitter::Parser::new();
24798 parser
24799 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24800 .unwrap();
24801 let copy_control_tree = parser
24802 .parse(copy_control_source, None)
24803 .expect("parse copy-control fixture");
24804 assert!(
24805 copy_control_tree.root_node().has_error(),
24806 "fixture must exercise adjacent copy-control recovery"
24807 );
24808 assert!(
24809 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
24810 "a complete late getter must remain recoverable after adjacent copy-control declarations"
24811 );
24812 let mut cursor = copy_control_tree.root_node().walk();
24813 assert!(
24814 copy_control_tree
24815 .root_node()
24816 .named_children(&mut cursor)
24817 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
24818 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
24819 copy_control_tree.root_node().to_sexp()
24820 );
24821 let constraint_tree = parser
24822 .parse(constraint_source, None)
24823 .expect("parse constraint-macro fixture");
24824 assert!(constraint_tree.root_node().has_error());
24825 assert!(
24826 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
24827 "complete constraint-macro members must not hide a later ordinary member"
24828 );
24829 let mut cursor = constraint_tree.root_node().walk();
24830 assert!(
24831 constraint_tree
24832 .root_node()
24833 .named_children(&mut cursor)
24834 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
24835 child,
24836 constraint_source
24837 )),
24838 "fixture must retain the split constraint-macro prefix/function geometry"
24839 );
24840 }
24841
24842 #[test]
24843 #[hardwon(
24844 "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24845 )]
24846 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
24847 let source = r#"
24848struct Analyzer {
24849 struct Action {
24850 Action() = default;
24851 Action(const Action&) = default;
24852 Action& operator=(const Action& rhs) & = default;
24853
24854 template<class T,
24855 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
24856 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
24857 // NOLINTNEXTLINE(google-explicit-constructor)
24858 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
24859 {}
24860
24861 enum : std::uint16_t { None = 0, Read = (1 << 0) };
24862 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
24863
24864 private:
24865 unsigned int mFlag{};
24866 };
24867
24868 enum class Direction : unsigned char { Forward, Reverse };
24869 virtual Action analyze(Direction d) const = 0;
24870};
24871"#;
24872 let mut parser = tree_sitter::Parser::new();
24873 parser
24874 .set_language(&tree_sitter_cpp::LANGUAGE.into())
24875 .unwrap();
24876 let tree = parser.parse(source, None).unwrap();
24877 assert!(tree.root_node().has_error());
24878 let root = tree.root_node();
24879 let outer = root
24880 .named_children(&mut root.walk())
24881 .find(|child| child.kind() == "ERROR")
24882 .expect("fragmented Analyzer prefix");
24883 let outer_recovered =
24884 fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
24885 assert_eq!(outer_recovered.name, "Analyzer");
24886 let outer_tree = cpp_reparse_fragmented_class_body(
24887 source,
24888 outer_recovered.body.reparse_start,
24889 outer_recovered.body.reparse_end,
24890 )
24891 .expect("reparse Analyzer body");
24892 let outer_root = outer_tree.root_node();
24893 let action_prefix = outer_root
24894 .named_children(&mut outer_root.walk())
24895 .find(|child| child.kind() == "ERROR")
24896 .expect("fragmented Action prefix");
24897 let action_recovered = fragmented_class_body(action_prefix, source)
24898 .expect("structured Action fragment boundary");
24899 assert_eq!(action_recovered.name, "Action");
24900 let action_tree = cpp_reparse_fragmented_class_body(
24901 source,
24902 action_recovered.body.reparse_start,
24903 action_recovered.body.reparse_end,
24904 )
24905 .expect("reparse Action body");
24906 let action_root = action_tree.root_node();
24907 let macro_prefix = action_root
24908 .named_children(&mut action_root.walk())
24909 .find(|child| child.kind() == "ERROR")
24910 .expect("constraint macro prefix");
24911 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
24912 .expect("structured template macro prefix");
24913 let macro_companion =
24914 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
24915 assert!(
24916 cpp_reparsed_template_macro_constructor_companion_is_indexable(
24917 macro_companion,
24918 macro_parameter,
24919 source,
24920 ),
24921 "split constrained constructor must be admitted: {}",
24922 macro_companion.to_sexp()
24923 );
24924 assert!(
24925 cpp_reparsed_members_are_indexable_for_class(action_root, source, Some("Action")),
24926 "complete Action body must pass the recovery gate: {}",
24927 action_tree.root_node().to_sexp()
24928 );
24929 assert!(
24930 cpp_reparsed_members_are_indexable_for_class(outer_root, source, Some("Analyzer")),
24931 "complete Analyzer body must pass the recovery gate: {}",
24932 outer_tree.root_node().to_sexp()
24933 );
24934 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
24935 let parsed = parse_cpp_file(&file, source, &tree);
24936 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
24937 assert!(
24938 parsed
24939 .declarations()
24940 .iter()
24941 .any(|unit| unit.fq_name() == expected),
24942 "missing recovered declaration {expected}: {:#?}",
24943 parsed.declarations()
24944 );
24945 }
24946 assert!(
24947 parsed
24948 .declarations()
24949 .iter()
24950 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
24951 "nested members must not remain flattened: {:#?}",
24952 parsed.declarations()
24953 );
24954 }
24955
24956 #[test]
24957 #[hardwon("e3ff69209ab2: ABI 15 defaulted-constructor recovery must retain class ownership")]
24958 fn defaulted_constructor_gate_rejects_unrelated_expression_assignments() {
24959 let source = "value() = default;\nint value();\n";
24960 let tree = crate::diagnostics::parse_cpp_tree(source);
24961 let root = tree.root_node();
24962 assert!(
24963 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("Owner")),
24964 "a default assignment and a same-name free declaration do not prove an Owner constructor: {}",
24965 root.to_sexp()
24966 );
24967 assert!(
24968 !cpp_reparsed_members_are_indexable_for_class(root, source, Some("value")),
24969 "a return-typed same-name declaration is not a constructor witness: {}",
24970 root.to_sexp()
24971 );
24972
24973 let damaged_source = r#"
24974value(int x) {
24975 broken(
24976}
24977value() = default;
24978int value();
24979"#;
24980 let damaged_tree = crate::diagnostics::parse_cpp_tree(damaged_source);
24981 let damaged_root = damaged_tree.root_node();
24982 let mut damaged_stack = vec![damaged_root];
24989 let mut damaged_assignment = None;
24990 while let Some(node) = damaged_stack.pop() {
24991 if node.kind() == "expression_statement" {
24992 damaged_assignment = Some(node);
24993 break;
24994 }
24995 let mut cursor = node.walk();
24996 damaged_stack.extend(node.named_children(&mut cursor));
24997 }
24998 let damaged_assignment =
24999 damaged_assignment.expect("damaged witness probe retains the assignment expression");
25000 let mut damaged_cursor = damaged_root.walk();
25001 let damaged_members = damaged_root
25002 .named_children(&mut damaged_cursor)
25003 .collect::<Vec<_>>();
25004 assert!(
25005 !cpp_reparsed_defaulted_constructor_is_indexable(
25006 damaged_assignment,
25007 &damaged_members,
25008 Some("value"),
25009 damaged_source,
25010 ),
25011 "an errorful same-name function must not witness a recovered defaulted constructor: {}",
25012 damaged_root.to_sexp()
25013 );
25014 assert!(
25015 !cpp_reparsed_members_are_indexable_for_class(
25016 damaged_root,
25017 damaged_source,
25018 Some("value")
25019 ),
25020 "the damaged witness must not pass the member gate either: {}",
25021 damaged_root.to_sexp()
25022 );
25023 }
25024
25025 #[test]
25026 #[hardwon(
25027 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25028 )]
25029 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
25030 let source = r#"
25031raw_hash_set& operator=(raw_hash_set&& that) {
25032 return move_assign(
25033 std::move(that),
25034 typename AllocTraits::propagate_on_container_move_assignment());
25035}
25036
25037iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
25038 return {};
25039}
25040
25041void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
25042
25043iterator insert(const_iterator hint, value_type&& value)
25044 ABSL_ATTRIBUTE_LIFETIME_BOUND {
25045 return {};
25046}
25047
25048friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
25049 return left.size() == right.size();
25050}
25051
25052static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
25053 return static_cast<slot_type*>(buffer);
25054}
25055
25056protected:
25057// Included-range recovery can attach this comment to the template prefix.
25058template <class K>
25059void AssertOnFind([[maybe_unused]] const K& key) {
25060 Check(key);
25061}
25062"#;
25063 let mut parser = tree_sitter::Parser::new();
25064 parser
25065 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25066 .unwrap();
25067 let tree = parser.parse(source, None).unwrap();
25068 assert!(
25069 tree.root_node().has_error(),
25070 "the fixture must exercise tree-sitter's errorful member shapes"
25071 );
25072 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
25073
25074 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
25075 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
25076 assert!(!cpp_reparsed_members_are_indexable(
25077 incomplete_tree.root_node(),
25078 incomplete_source
25079 ));
25080
25081 let outside_error_source = "int foo() stray_attribute {}\n";
25082 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
25083 assert!(outside_error_tree.root_node().has_error());
25084 assert!(!cpp_reparsed_members_are_indexable(
25085 outside_error_tree.root_node(),
25086 outside_error_source
25087 ));
25088
25089 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
25090 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
25091 assert!(!cpp_reparsed_members_are_indexable(
25092 variable_initializer_tree.root_node(),
25093 variable_initializer_source
25094 ));
25095 }
25096
25097 #[test]
25098 #[hardwon(
25099 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25100 )]
25101 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
25102 let positive_source = r#"
25103std::pair<iterator, bool> insert(init_type&& value)
25104 ABSL_ATTRIBUTE_LIFETIME_BOUND
25105#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
25106 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
25107#endif
25108{
25109 return emplace(std::move(value));
25110}
25111"#;
25112 let mut parser = tree_sitter::Parser::new();
25113 parser
25114 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25115 .unwrap();
25116 let positive_tree = parser.parse(positive_source, None).unwrap();
25117 assert!(
25118 positive_tree.root_node().has_error(),
25119 "the fixture must exercise the split attribute/requires shape"
25120 );
25121 assert!(cpp_reparsed_members_are_indexable(
25122 positive_tree.root_node(),
25123 positive_source
25124 ));
25125
25126 let template_return_source = r#"
25127pair<int> insert(init_type&& value)
25128 ABSL_ATTRIBUTE_LIFETIME_BOUND
25129#if LANGUAGE_LEVEL >= 202002L
25130 requires(!Predicate<init_type>::value)
25131#endif
25132// Attributes and the function body may be separated by comments.
25133{
25134 return {};
25135}
25136"#;
25137 let template_return_tree = parser.parse(template_return_source, None).unwrap();
25138 assert!(
25139 cpp_reparsed_members_are_indexable(
25140 template_return_tree.root_node(),
25141 template_return_source
25142 ),
25143 "template-return attribute/requires tree: {}",
25144 template_return_tree.root_node().to_sexp()
25145 );
25146
25147 let no_body_source = r#"
25148std::pair<iterator, bool> insert(init_type&& value)
25149 ABSL_ATTRIBUTE_LIFETIME_BOUND
25150#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
25151 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
25152#endif
25153+ 0;
25154"#;
25155 let no_body_tree = parser.parse(no_body_source, None).unwrap();
25156 assert!(!cpp_reparsed_members_are_indexable(
25157 no_body_tree.root_node(),
25158 no_body_source
25159 ));
25160
25161 let extra_payload_source = r#"
25162pair<int> insert(init_type&& value)
25163 ABSL_ATTRIBUTE_LIFETIME_BOUND
25164#if LANGUAGE_LEVEL >= 202002L
25165 int unrelated;
25166 requires(Predicate<init_type>::value)
25167#endif
25168{
25169 return {};
25170}
25171"#;
25172 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
25173 assert!(!cpp_reparsed_members_are_indexable(
25174 extra_payload_tree.root_node(),
25175 extra_payload_source
25176 ));
25177
25178 let variable_initializer_source = r#"
25179int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
25180#if LANGUAGE_LEVEL >= 202002L
25181 requires(true)
25182#endif
25183{
25184 bad;
25185}
25186"#;
25187 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
25188 assert!(!cpp_reparsed_members_are_indexable(
25189 variable_initializer_tree.root_node(),
25190 variable_initializer_source
25191 ));
25192 }
25193
25194 #[test]
25195 #[hardwon(
25196 "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25197 )]
25198 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
25199 let source = r#"namespace absl {
25200ABSL_NAMESPACE_BEGIN namespace container_internal {
25201
25202class raw_hash_set : public Base {
25203 public:
25204 using value_type = int;
25205
25206 template <class U,
25207 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
25208 void insert(U value) { (void)value; }
25209
25210 struct InsertSlot {
25211 raw_hash_set& s;
25212 };
25213};
25214
25215}
25216ABSL_NAMESPACE_END
25217}"#;
25218 let mut parser = tree_sitter::Parser::new();
25219 parser
25220 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25221 .unwrap();
25222 let tree = parser.parse(source, None).unwrap();
25223 let root = tree.root_node();
25224 let outer_namespace = root
25225 .named_children(&mut root.walk())
25226 .find(|child| child.kind() == "namespace_definition")
25227 .expect("outer absl namespace");
25228 let declaration_list = outer_namespace
25229 .child_by_field_name("body")
25230 .expect("outer namespace body");
25231 let sentinel_function = declaration_list
25232 .named_children(&mut declaration_list.walk())
25233 .find(|child| child.kind() == "function_definition")
25234 .expect("malformed namespace sentinel function");
25235 let ancestry = ParentIndex::new(root);
25236 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
25237 .expect("structured nested namespace sentinel");
25238 let fragmented =
25239 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
25240 .expect("fragmented raw_hash_set class");
25241 assert_eq!(fragmented.class_node.kind(), "ERROR");
25242 assert_eq!(fragmented.name, "raw_hash_set");
25243 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
25244
25245 let outer_scope =
25246 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
25247 let mut outer_siblings = Vec::new();
25248 push_cpp_sentinel_sibling_classes(
25249 &mut outer_siblings,
25250 declaration_list,
25251 sentinel.function,
25252 &outer_scope,
25253 source,
25254 &ancestry,
25255 );
25256 let [outer_shadow] = outer_siblings.as_slice() else {
25257 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
25258 };
25259 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
25260 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
25261
25262 let field = " raw_hash_set& s;";
25263 let start = source.find(field).expect("InsertSlot field") + 4;
25264 let node = root
25265 .descendant_for_byte_range(start, start + "raw_hash_set".len())
25266 .expect("raw_hash_set type node");
25267 let recovered = cpp_sentinel_recovered_classes(root, source);
25268 let [deep_class] = recovered.as_slice() else {
25269 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
25270 };
25271 assert_eq!(
25272 deep_class.namespace_scope_components,
25273 vec!["absl", "container_internal"]
25274 );
25275 assert_eq!(
25276 deep_class.scope_components,
25277 vec!["absl", "container_internal", "raw_hash_set"]
25278 );
25279 assert!(
25280 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
25281 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
25282 );
25283
25284 assert_eq!(
25285 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
25286 Some(vec![
25287 "absl".to_string(),
25288 "container_internal".to_string(),
25289 "raw_hash_set".to_string(),
25290 "InsertSlot".to_string(),
25291 ])
25292 );
25293
25294 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
25295 let parsed = parse_cpp_file(&file, source, &tree);
25296 let raw_hash_set = parsed
25297 .declarations()
25298 .iter()
25299 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
25300 .expect("recovered raw_hash_set class");
25301 assert_eq!(
25302 raw_hash_set.fq_name(),
25303 "absl::container_internal.raw_hash_set",
25304 "the recovered declaration must publish under the deeper sentinel namespace"
25305 );
25306 assert_eq!(
25307 parsed.raw_supertypes.get(raw_hash_set),
25308 Some(&vec!["Base".to_string()]),
25309 "the structured base clause on the fragmented ERROR prefix must survive publication"
25310 );
25311 assert!(
25312 parsed.materialization_records.iter().any(|record| matches!(
25313 record,
25314 MaterializationRecord::RecoveredDeclaration { recovery, unit }
25315 if unit == raw_hash_set && *recovery == deep_class.class_range
25316 )),
25317 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
25318 parsed.materialization_records
25319 );
25320 }
25321
25322 #[test]
25349 #[hardwon(
25350 "90181359ed41: C++ declaration recovery regressed for the parent index answers what tree sitter answers"
25351 )]
25352 fn the_parent_index_answers_what_tree_sitter_answers() {
25353 const SHAPES: [&str; 5] = [
25354 "namespace outer { namespace inner { struct Tag { int field; }; } }",
25355 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
25356 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
25357 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
25358 "class API Broken : public First, public Second {\n void member();\n",
25359 ];
25360 for source in SHAPES {
25361 let mut parser = tree_sitter::Parser::new();
25362 parser
25363 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25364 .unwrap();
25365 let tree = parser.parse(source, None).unwrap();
25366 let root = tree.root_node();
25367 let ancestry = ParentIndex::new(root);
25368 let mut nodes = 0usize;
25369 let mut stack = vec![root];
25370 while let Some(node) = stack.pop() {
25371 nodes += 1;
25372 assert_eq!(
25373 node.parent().map(|parent| parent.id()),
25374 ancestry.parent(node).map(|parent| parent.id()),
25375 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
25376 );
25377 let mut cursor = node.walk();
25378 stack.extend(node.children(&mut cursor));
25379 }
25380 assert!(nodes > 1, "{source:?} produced no tree to compare");
25381 }
25382 }
25383
25384 #[test]
25391 #[hardwon(
25392 "e6a524ac8d71: C++ declaration recovery regressed for deeply nested callable ancestor questions use the parent index"
25393 )]
25394 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
25395 const DEPTH: usize = 64;
25396 let mut source = String::new();
25397 for level in 0..DEPTH {
25398 writeln!(source, "namespace n{level} {{").unwrap();
25399 }
25400 source.push_str("int deepest(int value);\n");
25401 for _ in 0..DEPTH {
25402 source.push_str("}\n");
25403 }
25404
25405 let mut parser = tree_sitter::Parser::new();
25406 parser
25407 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25408 .unwrap();
25409 let tree = parser.parse(&source, None).unwrap();
25410 let root = tree.root_node();
25411 let ancestry = ParentIndex::new(root);
25412 let mut function_declarator = None;
25413 walk_named_tree_preorder(root, true, |node| {
25414 if node.kind() == "function_declarator" {
25415 function_declarator = Some(node);
25416 WalkControl::Break
25417 } else {
25418 WalkControl::Continue
25419 }
25420 });
25421 let function_declarator = function_declarator.expect("deepest function declarator");
25422 let ancestor_count =
25423 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
25424
25425 ancestry.reset_parent_query_count_for_test();
25426 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
25427 assert_eq!(DEPTH, lexical_scope.len());
25428 assert_eq!(
25429 ancestor_count + 1,
25430 ancestry.parent_query_count_for_test(),
25431 "lexical-scope ancestry bypassed the parent index"
25432 );
25433
25434 ancestry.reset_parent_query_count_for_test();
25435 assert_eq!(
25436 Some(DispatchExtensibility::Closed),
25437 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
25438 );
25439 assert_eq!(
25440 ancestor_count,
25441 ancestry.parent_query_count_for_test(),
25442 "dispatch ancestry bypassed the parent index"
25443 );
25444
25445 ancestry.reset_parent_query_count_for_test();
25446 assert_eq!(
25447 CallableLinkage::External,
25448 cpp_callable_linkage(function_declarator, &source, &ancestry)
25449 );
25450 assert_eq!(
25451 ancestor_count + 1,
25452 ancestry.parent_query_count_for_test(),
25453 "linkage ancestry bypassed the parent index"
25454 );
25455
25456 ancestry.reset_parent_query_count_for_test();
25457 assert!(!cpp_callable_is_structural_constructor(
25458 function_declarator,
25459 &source,
25460 &ancestry
25461 ));
25462 assert_eq!(
25463 ancestor_count + 1,
25464 ancestry.parent_query_count_for_test(),
25465 "constructor ancestry bypassed the parent index"
25466 );
25467 }
25468
25469 #[test]
25479 #[hardwon(
25480 "84b42ca510ce: C++ declaration recovery regressed for callable dispatch extensibility comes from the class body"
25481 )]
25482 fn callable_dispatch_extensibility_comes_from_the_class_body() {
25483 const SOURCE: &str = "\
25484class Plain { public: long send(long order); };
25485class Derived : public Base { public: long send(long order); };
25486class Sealed : public Base { public: long send(long order) final; };
25487class Hiding : public Base { public: long send(long order) { return order; } };
25488class Virtual { public: virtual long send(long order); };
25489namespace shop { long free_call(long order); }
25490long Plain::send(long order) { return order; }
25491";
25492 let mut parser = tree_sitter::Parser::new();
25493 parser
25494 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25495 .unwrap();
25496 let tree = parser.parse(SOURCE, None).unwrap();
25497 let root = tree.root_node();
25498 let ancestry = ParentIndex::new(root);
25499 let mut answers = Vec::new();
25500 walk_named_tree_preorder(root, true, |node| {
25501 if node.kind() != "function_declarator" {
25502 return WalkControl::Continue;
25503 }
25504 let name = cpp_function_declarator_name_node(node).expect("declarator name");
25505 answers.push((
25506 node_text(name, SOURCE).to_string(),
25507 cpp_callable_dispatch_extensibility(node, &ancestry),
25508 ));
25509 WalkControl::Continue
25510 });
25511 assert_eq!(
25512 answers,
25513 vec![
25514 ("send".to_string(), Some(DispatchExtensibility::Closed)),
25515 ("send".to_string(), Some(DispatchExtensibility::Open)),
25516 ("send".to_string(), Some(DispatchExtensibility::Closed)),
25517 ("send".to_string(), Some(DispatchExtensibility::Open)),
25518 ("send".to_string(), Some(DispatchExtensibility::Open)),
25519 ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
25520 ("Plain::send".to_string(), None),
25521 ],
25522 "the class body states the fact; a qualified definition states none"
25523 );
25524 }
25525
25526 #[test]
25527 #[hardwon(
25528 "8b8f8ecaac59: C++ declaration recovery regressed for cpp alias and macro dedup comparison count is linear"
25529 )]
25530 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
25531 const DISTINCT_PER_KIND: usize = 64;
25532 let mut source = String::new();
25533 for index in 0..DISTINCT_PER_KIND {
25534 writeln!(source, "typedef int Alias{index};").unwrap();
25535 }
25536 writeln!(source, "typedef long Alias0;").unwrap();
25537 for index in 0..DISTINCT_PER_KIND {
25538 writeln!(source, "#define MACRO_{index} {index}").unwrap();
25539 }
25540 writeln!(source, "#define MACRO_0 duplicate").unwrap();
25541 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
25542
25543 let mut parser = tree_sitter::Parser::new();
25544 parser
25545 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25546 .unwrap();
25547 let tree = parser.parse(&source, None).unwrap();
25548 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
25549
25550 start_declaration_identity_comparison_probe();
25551 let parsed = parse_cpp_file(&file, &source, &tree);
25552 let comparisons = finish_declaration_identity_comparison_probe();
25553
25554 assert_eq!(
25555 DISTINCT_PER_KIND + 1,
25556 parsed
25557 .declarations()
25558 .iter()
25559 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
25560 .count(),
25561 "every physical typedef alias declaration must be retained so \
25562 conditional branch guards stay available to the resolver"
25563 );
25564 assert_eq!(
25565 DISTINCT_PER_KIND + 1,
25566 parsed
25567 .declarations()
25568 .iter()
25569 .filter(|unit| {
25570 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
25571 })
25572 .count(),
25573 "distinct macro redefinitions must remain available to temporal lookup"
25574 );
25575 assert_eq!(
25576 2,
25577 parsed
25578 .declarations()
25579 .iter()
25580 .filter(|unit| {
25581 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
25582 })
25583 .count(),
25584 "function overloads must remain distinct"
25585 );
25586
25587 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
25588 assert!(
25589 comparisons <= dedup_inputs * 4,
25590 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
25591 );
25592 }
25593
25594 #[test]
25595 #[hardwon("3ba12c745afa: sentinel recovery requires the class's own body close")]
25596 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
25597 let negative_source = r#"namespace absl {
25598ABSL_NAMESPACE_BEGIN namespace container_internal {
25599template <typename T>
25600class broken {
25601 public:
25602 using value_type = T;
25603 T operator->() const { return &operator*(); }
25604}
25605}
25606"#;
25607 let mut parser = tree_sitter::Parser::new();
25608 parser
25609 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25610 .unwrap();
25611 let negative_tree = parser.parse(negative_source, None).unwrap();
25612 let broken = find_class_named(negative_tree.root_node(), negative_source, "broken")
25613 .expect("the negative fixture must expose the malformed class node");
25614 assert!(
25615 broken.has_error(),
25616 "the negative fixture must retain a parser error"
25617 );
25618 let mut envelope = None;
25623 {
25624 let mut stack = vec![negative_tree.root_node()];
25625 while let Some(node) = stack.pop() {
25626 if cpp_nested_namespace_sentinel(
25627 node,
25628 negative_source,
25629 &ParentIndex::new(negative_tree.root_node()),
25630 )
25631 .is_some()
25632 || cpp_root_namespace_sentinel(
25633 node,
25634 negative_source,
25635 &ParentIndex::new(negative_tree.root_node()),
25636 )
25637 .is_some()
25638 || cpp_sentinel_macro_class_region(node, negative_source).is_some()
25639 {
25640 envelope = Some(node);
25641 break;
25642 }
25643 stack.extend(node.named_children(&mut node.walk()));
25644 }
25645 }
25646 assert!(
25647 envelope.is_none(),
25648 "an incomplete class must not prove a sentinel envelope: {}",
25649 negative_tree.root_node().to_sexp()
25650 );
25651 let recovered = cpp_sentinel_recovered_classes(negative_tree.root_node(), negative_source);
25652 assert!(
25653 recovered
25654 .iter()
25655 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
25656 "an incomplete class must not borrow the namespace close: {recovered:#?}"
25657 );
25658
25659 let positive_source = r#"namespace absl {
25660ABSL_NAMESPACE_BEGIN namespace container_internal {
25661template <typename T>
25662class broken {
25663 public:
25664 using value_type = T;
25665 T operator->() const { return &operator*(); }
25666 using alias = value_type;
25667};
25668}
25669}
25670"#;
25671 let positive_tree = parser.parse(positive_source, None).unwrap();
25672 let broken = find_class_named(positive_tree.root_node(), positive_source, "broken")
25673 .expect("the positive fixture must expose the broken class node");
25674 assert!(
25675 broken.has_error(),
25676 "the positive fixture must retain an internal parser error"
25677 );
25678 assert!(
25679 cpp_complete_class_body_close(broken).is_some(),
25680 "the positive fixture must expose a real class body close"
25681 );
25682 let recovered = cpp_sentinel_recovered_classes(positive_tree.root_node(), positive_source);
25683 assert!(
25684 recovered.iter().any(|class| {
25685 class.scope_components == ["absl", "container_internal", "broken"]
25686 }),
25687 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
25688 );
25689 }
25690
25691 #[test]
25692 #[hardwon(
25693 "0e6df36dfa5d: C++ declaration recovery regressed for sentinel recovery keeps members after nested body close"
25694 )]
25695 fn sentinel_recovery_keeps_members_after_nested_body_close() {
25696 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
25697NLOHMANN_BASIC_JSON_TPL_DECLARATION
25698class basic_json {
25699 private:
25700 union storage {
25701 int value;
25702 } data;
25703 public:
25704 using late_alias = int;
25705 late_alias value() const;
25706};
25707NLOHMANN_JSON_NAMESPACE_END
25708"#;
25709 let mut parser = tree_sitter::Parser::new();
25710 parser
25711 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25712 .unwrap();
25713 let tree = parser.parse(source, None).unwrap();
25714 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
25715 let basic_json = recovered
25716 .iter()
25717 .find(|class| {
25718 class
25719 .scope_components
25720 .last()
25721 .is_some_and(|name| name == "basic_json")
25722 })
25723 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
25724 let late_alias = source
25725 .find("late_alias value")
25726 .expect("late alias reference");
25727 assert!(
25728 basic_json.class_range.start_byte < late_alias
25729 && late_alias < basic_json.class_range.end_byte,
25730 "the recovered class range must include members after a nested close: {basic_json:#?}"
25731 );
25732 }
25733
25734 #[test]
25735 #[hardwon(
25736 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery collects guarded sibling owner without crossing namespace sibling"
25737 )]
25738 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
25739 let source = r#"namespace absl {
25740ABSL_NAMESPACE_BEGIN namespace container_internal {
25741template <typename T>
25742struct broken {
25743 using value_type = T;
25744};
25745}
25746
25747#ifdef OWNER_DEF
25748template <typename T>
25749typename broken<T>::value_type broken<T>::method() {
25750 value_type value{};
25751 return value;
25752}
25753#endif
25754
25755namespace sibling {
25756template <typename T>
25757typename broken<T>::value_type broken<T>::other() {
25758 value_type value{};
25759 return value;
25760}
25761}
25762
25763ABSL_NAMESPACE_END
25764}
25765"#;
25766 let mut parser = tree_sitter::Parser::new();
25767 parser
25768 .set_language(&tree_sitter_cpp::LANGUAGE.into())
25769 .unwrap();
25770 let tree = parser.parse(source, None).unwrap();
25771 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
25772 let broken = recovered
25773 .iter()
25774 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
25775 .expect("the sentinel class must be recovered");
25776 let method_start = source
25777 .find("typename broken<T>::value_type broken<T>::method()")
25778 .expect("guarded sibling owner");
25779 let method_end = source[method_start..]
25780 .find("\n}")
25781 .map(|offset| method_start + offset + 2)
25782 .expect("guarded sibling owner close");
25783 assert!(
25784 broken
25785 .owner_ranges
25786 .iter()
25787 .any(|owner| owner.range.start_byte <= method_start
25788 && method_end <= owner.range.end_byte),
25789 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
25790 );
25791 let sibling_start = source
25792 .find("typename broken<T>::value_type broken<T>::other()")
25793 .expect("nested namespace sibling owner");
25794 assert!(
25795 broken
25796 .owner_ranges
25797 .iter()
25798 .all(|owner| owner.range.start_byte > sibling_start
25799 || owner.range.end_byte <= sibling_start),
25800 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
25801 );
25802 }
25803
25804 #[test]
25805 #[hardwon(
25806 "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery discards outer siblings without namespace end marker"
25807 )]
25808 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
25809 let source = r#"#ifdef OUTER
25810namespace absl {
25811ABSL_NAMESPACE_BEGIN namespace container_internal {
25812template <typename T>
25813struct broken {
25814 using value_type = T;
25815};
25816}
25817}
25818
25819#ifdef OWNER_DEF
25820template <typename T>
25821typename broken<T>::value_type broken<T>::method() {
25822 value_type value{};
25823 return value;
25824}
25825#endif
25826#endif
25827"#;
25828 let parsed = parse_cpp_declarations(source, "discard-outer.h");
25833 assert!(
25834 parsed
25835 .declarations()
25836 .iter()
25837 .any(|unit| unit.is_class() && unit.fq_name() == "absl::container_internal.broken"),
25838 "the sentinel class must be recovered: {:#?}",
25839 parsed.declarations()
25840 );
25841 let method = parsed
25842 .declarations()
25843 .iter()
25844 .find(|unit| unit.is_function() && unit.short_name().ends_with("method"))
25845 .expect("the guarded out-of-line definition");
25846 assert_eq!(
25847 method.fq_name(),
25848 "broken.method",
25849 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {:#?}",
25850 parsed.declarations()
25851 );
25852 }
25853
25854 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
25856 let mut signatures = all_function_identities(parsed)
25857 .into_iter()
25858 .filter(|(name, _)| name == fq_name)
25859 .filter_map(|(_, signature)| signature)
25860 .collect::<Vec<_>>();
25861 signatures.sort();
25862 signatures.dedup();
25863 signatures
25864 }
25865
25866 #[test]
25867 #[hardwon(
25868 "64ce2f1b00cf: C++ declaration recovery regressed for callable parameter types come from the ast parameter list"
25869 )]
25870 fn callable_parameter_types_come_from_the_ast_parameter_list() {
25871 let source = r#"
25872template <typename T, ENABLE_BYTES(T)>
25873Vec256<T> DupOdd(Vec256<T> value) { return value; }
25874
25875struct Visitor {
25876 void fail(this auto const& self) {}
25877 void consume(this Visitor const& self, int count) {}
25878};
25879"#;
25880 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
25881 let dup_odd = parsed
25882 .declarations()
25883 .iter()
25884 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
25885 .expect("DupOdd declaration");
25886 assert_eq!(
25887 dup_odd.signature(),
25888 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
25889 );
25890 assert_eq!(
25891 parsed
25892 .signature_metadata
25893 .get(dup_odd)
25894 .and_then(|metadata| metadata.first())
25895 .and_then(SignatureMetadata::callable_parameter_types),
25896 Some(["Vec256<T>".to_string()].as_slice())
25897 );
25898
25899 let fail = parsed
25900 .declarations()
25901 .iter()
25902 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
25903 .expect("explicit-object member");
25904 assert_eq!(fail.signature(), Some("(this const auto &)"));
25905 let metadata = parsed
25906 .signature_metadata
25907 .get(fail)
25908 .and_then(|metadata| metadata.first())
25909 .expect("explicit-object signature metadata");
25910 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
25911 assert!(
25912 metadata
25913 .callable_arity()
25914 .is_some_and(|arity| arity.accepts(0))
25915 );
25916 let consume = parsed
25917 .declarations()
25918 .iter()
25919 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.consume")
25920 .expect("explicit-object member with an invocation argument");
25921 assert_eq!(consume.signature(), Some("(this const Visitor &, int)"));
25922 let metadata = parsed
25923 .signature_metadata
25924 .get(consume)
25925 .and_then(|metadata| metadata.first())
25926 .expect("typed explicit-object signature metadata");
25927 assert_eq!(
25928 metadata.callable_parameter_types(),
25929 Some(["int".to_string()].as_slice())
25930 );
25931 assert!(
25932 metadata
25933 .callable_arity()
25934 .is_some_and(|arity| arity.accepts(1) && !arity.accepts(0) && !arity.accepts(2))
25935 );
25936 }
25937
25938 #[test]
25939 #[hardwon(
25940 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers survive parameter list whitespace"
25941 )]
25942 fn trailing_qualifiers_survive_parameter_list_whitespace() {
25943 let source = r#"
25948struct Widget {
25949 bool multiline(int settings, int supprs) const;
25950 bool doublespace(int settings, int supprs) const;
25951 bool noexcept_multiline(int settings, int supprs) noexcept;
25952 bool ref_multiline(int settings, int supprs) &&;
25953};
25954bool
25955Widget::multiline (int settings,
25956 int supprs) const
25957{ return settings + supprs > 0; }
25958bool Widget::doublespace(int settings, int supprs) const { return true; }
25959bool Widget::noexcept_multiline(int settings,
25960 int supprs) noexcept { return true; }
25961bool Widget::ref_multiline(int settings,
25962 int supprs) && { return true; }
25963"#;
25964 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
25965 assert_eq!(
25966 vec!["(int, int) const".to_string()],
25967 identity_signatures(&parsed, "Widget.multiline")
25968 );
25969 assert_eq!(
25970 vec!["(int, int) const".to_string()],
25971 identity_signatures(&parsed, "Widget.doublespace")
25972 );
25973 assert_eq!(
25974 vec!["(int, int) noexcept".to_string()],
25975 identity_signatures(&parsed, "Widget.noexcept_multiline")
25976 );
25977 assert_eq!(
25978 vec!["(int, int) &&".to_string()],
25979 identity_signatures(&parsed, "Widget.ref_multiline")
25980 );
25981 }
25982
25983 #[test]
25984 #[hardwon(
25985 "3a22f72d49b5: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25986 )]
25987 fn macro_fragmented_plain_class_keeps_following_member_signature() {
25988 let source = r#"
25989struct CString {};
25990class CMessage {
25991public:
25992 CString GetParams(unsigned int index, unsigned int length = -1) const
25993 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
25994 return GetParamsColon(index, length);
25995 }
25996 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
25997};
25998CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
25999 return {};
26000}
26001"#;
26002 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
26003 assert_eq!(
26004 vec!["(unsigned int, unsigned int) const".to_string()],
26005 identity_signatures(&parsed, "CMessage.GetParamsColon")
26006 );
26007 }
26008
26009 #[test]
26010 #[hardwon(
26011 "ed735e56af02: C++ declaration recovery regressed for namespaced macro fragment keeps prefix members and following classes"
26012 )]
26013 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
26014 let source = r#"
26015#pragma once
26016#define DEMO_DEPRECATED(message)
26017namespace demo {
26018struct Base {
26019 static int aligned(int value) { return value; }
26020 int legacy(int value) const
26021 DEMO_DEPRECATED("use replacement()") { return value; }
26022 int replacement() const;
26023 void run(int value);
26024};
26025struct OtherBase {
26026 void run(int value);
26027 static int aligned(int value) { return value; }
26028};
26029struct Derived : Base {};
26030struct Override : Base {
26031 void run(int value);
26032 static int aligned(int value) { return value; }
26033};
26034struct RecoveredOverride : Base {
26035 int legacy(int value) const
26036 DEMO_DEPRECATED("use replacement()") { return value; }
26037 void run(int value);
26038};
26039struct Hidden : Base {
26040 void run(int first, int second);
26041 static int aligned(int first, int second) { return first + second; }
26042};
26043struct Ambiguous : Base, OtherBase {};
26044}
26045struct Global {};
26046"#;
26047 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
26048 let declarations = parsed.declarations();
26049 let fq_names = declarations
26050 .iter()
26051 .map(|unit| unit.fq_name())
26052 .collect::<std::collections::BTreeSet<_>>();
26053
26054 for expected in [
26055 "demo.Base",
26056 "demo.Base.aligned",
26057 "demo.Base.legacy",
26058 "demo.Base.replacement",
26059 "demo.Base.run",
26060 "demo.Derived",
26061 "demo.OtherBase",
26062 "demo.Override",
26063 "demo.RecoveredOverride",
26064 "demo.Hidden",
26065 "demo.Ambiguous",
26066 "Global",
26067 ] {
26068 assert!(
26069 fq_names.contains(expected),
26070 "missing {expected} from namespaced macro fragment: {declarations:#?}"
26071 );
26072 }
26073 assert!(
26074 !fq_names.contains("Derived"),
26075 "following class escaped its namespace: {declarations:#?}"
26076 );
26077 assert!(
26078 !fq_names.contains("demo.Global"),
26079 "global class crossed the recovered namespace boundary: {declarations:#?}"
26080 );
26081 }
26082
26083 #[test]
26084 #[hardwon(
26085 "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers still separate genuine overloads"
26086 )]
26087 fn trailing_qualifiers_still_separate_genuine_overloads() {
26088 let source = r#"
26091struct Widget {
26092 int* slot(int index);
26093 const int* slot(int index) const;
26094 int log(int severity) &;
26095 int log(int severity) &&;
26096};
26097"#;
26098 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
26099 assert_eq!(
26100 vec!["(int)".to_string(), "(int) const".to_string()],
26101 identity_signatures(&parsed, "Widget.slot")
26102 );
26103 assert_eq!(
26104 vec!["(int) &".to_string(), "(int) &&".to_string()],
26105 identity_signatures(&parsed, "Widget.log")
26106 );
26107 }
26108
26109 #[test]
26110 #[hardwon(
26111 "026332d8e7db: C++ declaration recovery regressed for virtual specifier is not part of the identity signature"
26112 )]
26113 fn virtual_specifier_is_not_part_of_the_identity_signature() {
26114 let source = r#"
26117struct Base {
26118 virtual void run(int value) const;
26119};
26120struct Widget : Base {
26121 void run(int value) const override;
26122};
26123void Widget::run(int value) const {}
26124"#;
26125 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
26126 assert_eq!(
26127 vec!["(int) const".to_string()],
26128 identity_signatures(&parsed, "Widget.run")
26129 );
26130 }
26131
26132 #[test]
26133 #[hardwon("026332d8e7db: top-level cv is ignored without collapsing pointee overloads")]
26134 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
26135 let source = r#"
26139struct Widget {
26140 bool value_params(const int settings, const int supprs);
26141 void pointee_const(const int* p);
26142 void pointer_const(int* const p);
26143 void both_const(const int* const p);
26144 void reference_const(const int& p);
26145 void array_const(const int values[4]);
26146 void take(const int* p);
26147 void take(int* p);
26148};
26149bool Widget::value_params(int settings, int supprs) { return true; }
26150void Widget::pointer_const(int* p) {}
26151void Widget::both_const(const int* p) {}
26152"#;
26153 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
26154 assert_eq!(
26155 vec!["(int, int)".to_string()],
26156 identity_signatures(&parsed, "Widget.value_params")
26157 );
26158 assert_eq!(
26159 vec!["(int *)".to_string()],
26160 identity_signatures(&parsed, "Widget.pointer_const")
26161 );
26162 assert_eq!(
26163 vec!["(const int *)".to_string()],
26164 identity_signatures(&parsed, "Widget.both_const")
26165 );
26166 assert_eq!(
26168 vec!["(const int *)".to_string()],
26169 identity_signatures(&parsed, "Widget.pointee_const")
26170 );
26171 assert_eq!(
26172 vec!["(const int &)".to_string()],
26173 identity_signatures(&parsed, "Widget.reference_const")
26174 );
26175 assert_eq!(
26176 vec!["(const int [4])".to_string()],
26177 identity_signatures(&parsed, "Widget.array_const")
26178 );
26179 assert_eq!(
26180 vec!["(const int *)".to_string(), "(int *)".to_string()],
26181 identity_signatures(&parsed, "Widget.take")
26182 );
26183 }
26184
26185 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
26186 let mut parser = tree_sitter::Parser::new();
26187 parser
26188 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26189 .unwrap();
26190 let tree = parser.parse(source, None).unwrap();
26191 let start = source.find(callable_name).expect("callable declaration");
26192 let declarator =
26193 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
26194 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
26195 }
26196
26197 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
26198 let mut shapes = comparable_shapes(source, callable_name);
26199 assert_eq!(1, shapes.len(), "{shapes:?}");
26200 match shapes.remove(0) {
26201 CppComparableSlot::Shape(shape) => shape,
26202 other => panic!("expected a comparable shape, got {other:?}"),
26203 }
26204 }
26205
26206 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
26207 let mut current = shape.root();
26208 loop {
26209 match shape.node(current) {
26210 CppComparableNode::Named { .. } => return shape.node(current),
26211 CppComparableNode::Pointer { inner, .. }
26212 | CppComparableNode::Reference { inner }
26213 | CppComparableNode::Array { inner } => current = *inner,
26214 CppComparableNode::Generic { base, .. } => current = *base,
26215 }
26216 }
26217 }
26218
26219 #[test]
26220 #[hardwon(
26221 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps pointee const"
26222 )]
26223 fn comparable_shape_keeps_pointee_const() {
26224 assert_ne!(
26225 sole_comparable_shape("void f(const char* p);", "f("),
26226 sole_comparable_shape("void f(char* p);", "f(")
26227 );
26228 }
26229
26230 #[test]
26231 #[hardwon(
26232 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps inner pointer const"
26233 )]
26234 fn comparable_shape_keeps_inner_pointer_const() {
26235 assert_ne!(
26236 sole_comparable_shape("void f(int** p);", "f("),
26237 sole_comparable_shape("void f(int* const* p);", "f(")
26238 );
26239 }
26240
26241 #[test]
26242 #[hardwon(
26243 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level pointer const"
26244 )]
26245 fn comparable_shape_drops_top_level_pointer_const() {
26246 assert_eq!(
26247 sole_comparable_shape("void f(int* const p);", "f("),
26248 sole_comparable_shape("void f(int* p);", "f(")
26249 );
26250 }
26251
26252 #[test]
26253 #[hardwon(
26254 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level base const"
26255 )]
26256 fn comparable_shape_drops_top_level_base_const() {
26257 assert_eq!(
26258 sole_comparable_shape("void f(const int p);", "f("),
26259 sole_comparable_shape("void f(int p);", "f(")
26260 );
26261 }
26262
26263 #[test]
26264 #[hardwon(
26265 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape decays top level array to pointer"
26266 )]
26267 fn comparable_shape_decays_top_level_array_to_pointer() {
26268 assert_eq!(
26269 sole_comparable_shape("void f(int a[3]);", "f("),
26270 sole_comparable_shape("void f(int* a);", "f(")
26271 );
26272 assert_eq!(
26273 sole_comparable_shape("void f(int* a[3]);", "f("),
26274 sole_comparable_shape("void f(int** a);", "f(")
26275 );
26276 }
26277
26278 #[test]
26279 #[hardwon(
26280 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps array behind pointer"
26281 )]
26282 fn comparable_shape_keeps_array_behind_pointer() {
26283 assert_ne!(
26284 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
26285 sole_comparable_shape("struct S { void f(int** a); };", "f(")
26286 );
26287 }
26288
26289 #[test]
26290 #[hardwon(
26291 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape records written name and lexical scope"
26292 )]
26293 fn comparable_shape_records_written_name_and_lexical_scope() {
26294 let declared =
26295 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
26296 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
26297 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
26298 panic!("named leaf");
26299 };
26300 assert_eq!(["Msg".to_string()].as_slice(), name.path());
26301 assert_eq!(
26302 ["ns".to_string(), "S".to_string()].as_slice(),
26303 name.lexical_scope()
26304 );
26305 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
26306 panic!("named leaf");
26307 };
26308 assert_eq!(
26309 ["ns".to_string(), "Msg".to_string()].as_slice(),
26310 name.path()
26311 );
26312 assert!(name.lexical_scope().is_empty());
26313 assert_ne!(declared, defined);
26314 }
26315
26316 #[test]
26317 #[hardwon(
26318 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape marks sized primitive leaf"
26319 )]
26320 fn comparable_shape_marks_sized_primitive_leaf() {
26321 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
26322 let CppComparableNode::Named {
26323 name, primitive, ..
26324 } = comparable_named_leaf(&shape)
26325 else {
26326 panic!("named leaf");
26327 };
26328 assert!(primitive);
26329 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
26330 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
26331 }
26332
26333 #[test]
26334 #[hardwon(
26335 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports function pointer parameter as unstructured"
26336 )]
26337 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
26338 assert_eq!(
26339 vec![CppComparableSlot::Unstructured],
26340 comparable_shapes("void f(void (*cb)(int));", "f(")
26341 );
26342 }
26343
26344 #[test]
26345 #[hardwon(
26346 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports ellipsis slot"
26347 )]
26348 fn comparable_shape_reports_ellipsis_slot() {
26349 let shapes = comparable_shapes("void f(int a, ...);", "f(");
26350 assert_eq!(2, shapes.len(), "{shapes:?}");
26351 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
26352 }
26353
26354 #[test]
26355 #[hardwon(
26356 "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps template argument const"
26357 )]
26358 fn comparable_shape_keeps_template_argument_const() {
26359 assert_ne!(
26360 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
26361 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
26362 );
26363 }
26364
26365 #[test]
26368 #[hardwon(
26369 "9cce90a89128: C++ declaration recovery regressed for c file mints aggregate member tag at file scope"
26370 )]
26371 fn c_file_mints_aggregate_member_tag_at_file_scope() {
26372 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
26373 let parsed = parse_cpp_declarations(source, "x.c");
26374 let declarations = parsed.declarations();
26375
26376 assert!(
26377 declarations
26378 .iter()
26379 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
26380 "expected a file-scope inner tag, got {declarations:?}"
26381 );
26382 assert!(
26383 declarations
26384 .iter()
26385 .all(|unit| unit.fq_name() != "outer$inner"),
26386 "expected no nested identity, got {declarations:?}"
26387 );
26388 assert!(
26389 declarations
26390 .iter()
26391 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
26392 );
26393 assert!(
26395 declarations
26396 .iter()
26397 .any(|unit| unit.fq_name() == "inner.value")
26398 );
26399 assert!(
26400 declarations
26401 .iter()
26402 .any(|unit| unit.fq_name() == "outer.item")
26403 );
26404
26405 let outer = declarations
26406 .iter()
26407 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
26408 .expect("outer");
26409 assert!(
26410 parsed
26411 .children
26412 .get(outer)
26413 .into_iter()
26414 .flatten()
26415 .all(|child| child.fq_name() != "inner"),
26416 "the tag must not hang off the aggregate it is written inside: {:?}",
26417 parsed.children
26418 );
26419 }
26420
26421 #[test]
26425 #[hardwon(
26426 "9cce90a89128: C++ declaration recovery regressed for header and cpp files keep nested tag identity"
26427 )]
26428 fn header_and_cpp_files_keep_nested_tag_identity() {
26429 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
26430 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
26431 let parsed = parse_cpp_declarations(source, name);
26432 let declarations = parsed.declarations();
26433 assert!(
26434 declarations
26435 .iter()
26436 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
26437 "{name} must keep the nested identity, got {declarations:?}"
26438 );
26439 assert!(
26440 declarations.iter().all(|unit| unit.fq_name() != "inner"),
26441 "{name} must not mint a file-scope tag, got {declarations:?}"
26442 );
26443 assert!(
26444 declarations
26445 .iter()
26446 .any(|unit| unit.fq_name() == "outer$inner.value")
26447 );
26448 }
26449 }
26450
26451 #[test]
26452 fn c_projection_keeps_defaulted_constructor_nested_in_template_class() {
26453 let source = r#"
26454template <class K, class V, class Traits = CacheTraits<K>>
26455class ARCCache {
26456 public:
26457 struct ArcSizeInfo {
26458 void BSize() {}
26459 };
26460 private:
26461 struct BMapVal;
26462 struct BListVal;
26463 typedef std::unordered_map<K, BMapVal> BMap;
26464 typedef typename BMap::iterator bmap_iter;
26465 typedef typename std::list<BListVal> BList;
26466 typedef typename BList::iterator blist_iter;
26467 struct BListVal {
26468 bmap_iter map_iter;
26469 BListVal() = default;
26470 };
26471 struct DefinedVal {
26472 DefinedVal() {}
26473 };
26474};
26475"#;
26476 let tree = crate::diagnostics::parse_cpp_tree(source);
26477 let file = ProjectFile::new(std::env::temp_dir(), "arc-cache.hpp");
26478 let cpp = crate::adapter::parse_cpp_file_in_dialect(
26479 &file,
26480 source,
26481 &tree,
26482 LanguageDialect::Standard(Language::Cpp),
26483 );
26484 let c =
26485 crate::adapter::parse_cpp_file_in_dialect(&file, source, &tree, LanguageDialect::CppC);
26486 for (dialect, parsed) in [("C++", &cpp), ("C", &c)] {
26487 for fq_name in [
26488 "ARCCache$ArcSizeInfo.BSize",
26489 "ARCCache$BListVal.BListVal",
26490 "ARCCache$DefinedVal.DefinedVal",
26491 ] {
26492 assert!(
26493 parsed
26494 .declarations()
26495 .iter()
26496 .any(|unit| unit.fq_name() == fq_name),
26497 "{dialect} reading must contain {fq_name}: {:#?}",
26498 parsed.declarations()
26499 );
26500 }
26501 for flattened in [
26502 "ArcSizeInfo.BSize",
26503 "BListVal.BListVal",
26504 "DefinedVal.DefinedVal",
26505 ] {
26506 assert!(
26507 parsed
26508 .declarations()
26509 .iter()
26510 .all(|unit| unit.fq_name() != flattened),
26511 "{dialect} reading must not flatten {flattened}: {:#?}",
26512 parsed.declarations()
26513 );
26514 }
26515 }
26516 }
26517
26518 #[test]
26520 #[hardwon(
26521 "9cce90a89128: C++ declaration recovery regressed for uppercase c extension keeps cpp tag scope"
26522 )]
26523 fn uppercase_c_extension_keeps_cpp_tag_scope() {
26524 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
26525 let parsed = parse_cpp_declarations(source, "x.C");
26526 assert!(
26527 parsed
26528 .declarations()
26529 .iter()
26530 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
26531 );
26532 }
26533
26534 #[test]
26537 #[hardwon(
26538 "9cce90a89128: C++ declaration recovery regressed for c file mints every nesting level at file scope"
26539 )]
26540 fn c_file_mints_every_nesting_level_at_file_scope() {
26541 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
26542 let parsed = parse_cpp_declarations(source, "z.c");
26543 let declarations = parsed.declarations();
26544
26545 for tag in ["a", "b", "c"] {
26546 assert!(
26547 declarations
26548 .iter()
26549 .any(|unit| unit.is_class() && unit.fq_name() == tag),
26550 "expected a file-scope {tag}, got {declarations:?}"
26551 );
26552 }
26553 assert!(
26554 declarations
26555 .iter()
26556 .all(|unit| !unit.fq_name().contains('$')),
26557 "no level may keep a nested identity, got {declarations:?}"
26558 );
26559 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
26561 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
26562 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
26563 }
26564
26565 #[test]
26568 #[hardwon(
26569 "9cce90a89128: C++ declaration recovery regressed for c file mints member list enum at file scope with its enumerators"
26570 )]
26571 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
26572 let source = "struct outer { enum color { RED, GREEN } c; };\n";
26573 let parsed = parse_cpp_declarations(source, "e.c");
26574 let declarations = parsed.declarations();
26575
26576 let color = declarations
26577 .iter()
26578 .find(|unit| unit.is_class() && unit.fq_name() == "color")
26579 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
26580 assert!(
26581 declarations
26582 .iter()
26583 .all(|unit| unit.fq_name() != "outer$color")
26584 );
26585 for enumerator in ["color.RED", "color.GREEN"] {
26586 assert!(
26587 declarations.iter().any(|unit| unit.fq_name() == enumerator),
26588 "expected {enumerator}, got {declarations:?}"
26589 );
26590 }
26591 let children = parsed
26592 .children
26593 .get(color)
26594 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
26595 assert!(
26596 ["color.RED", "color.GREEN"]
26597 .iter()
26598 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
26599 "enumerators must hang off their enum: {children:?}"
26600 );
26601 }
26602
26603 #[test]
26604 #[hardwon(
26605 "9cce90a89128: C++ declaration recovery regressed for c file mints member list union at file scope"
26606 )]
26607 fn c_file_mints_member_list_union_at_file_scope() {
26608 let source = "struct outer { union inner { int a; float b; } item; };\n";
26609 let parsed = parse_cpp_declarations(source, "u.c");
26610 let declarations = parsed.declarations();
26611 assert!(
26612 declarations
26613 .iter()
26614 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
26615 "expected a file-scope inner union, got {declarations:?}"
26616 );
26617 assert!(
26618 declarations
26619 .iter()
26620 .all(|unit| unit.fq_name() != "outer$inner")
26621 );
26622 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
26623 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
26624 }
26625
26626 #[test]
26629 #[hardwon(
26630 "9cce90a89128: C++ declaration recovery regressed for c file member list tag lands in the enclosing namespace"
26631 )]
26632 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
26633 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
26634 let parsed = parse_cpp_declarations(source, "n.c");
26635 let declarations = parsed.declarations();
26636 let inner = declarations
26637 .iter()
26638 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
26639 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
26640 assert_eq!(inner.package_name(), "ns");
26641 assert!(
26642 declarations
26643 .iter()
26644 .all(|unit| unit.fq_name() != "ns.outer$inner")
26645 );
26646 }
26647
26648 #[test]
26653 #[hardwon(
26654 "9cce90a89128: C++ declaration recovery regressed for function local tags are unchanged in both dialects"
26655 )]
26656 fn function_local_tags_are_unchanged_in_both_dialects() {
26657 let source =
26658 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
26659 for name in ["y.c", "y.cpp"] {
26660 let parsed = parse_cpp_declarations(source, name);
26661 let declarations = parsed.declarations();
26662 assert!(
26663 declarations
26664 .iter()
26665 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
26666 "{name}: {declarations:?}"
26667 );
26668 for tag in ["localtag", "deeper", "localtag$deeper"] {
26669 assert!(
26670 declarations.iter().all(|unit| unit.fq_name() != tag),
26671 "{name} must not mint {tag}, got {declarations:?}"
26672 );
26673 }
26674 }
26675 }
26676
26677 #[test]
26680 #[hardwon(
26681 "9cce90a89128: C++ declaration recovery regressed for anonymous typedef struct is identical in both dialects"
26682 )]
26683 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
26684 let source = "typedef struct { int v; } T;\n";
26685 for name in ["t.c", "t.cpp"] {
26686 let parsed = parse_cpp_declarations(source, name);
26687 let declarations = parsed.declarations();
26688 assert!(
26689 declarations
26690 .iter()
26691 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
26692 "{name}: {declarations:?}"
26693 );
26694 }
26695 }
26696
26697 #[test]
26704 #[hardwon(
26705 "605f5c5219c4: C++ declaration recovery regressed for anonymous aggregate members are identical in both dialects"
26706 )]
26707 fn anonymous_aggregate_members_are_identical_in_both_dialects() {
26708 let sources = [
26709 "struct outer { union { int a; float b; }; };\n",
26710 "struct outer { struct { int *ops; } sock; };\n",
26711 "void run(void) { struct { int v; } item; }\n",
26712 "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
26713 ];
26714 for source in sources {
26715 let names = |name: &str| {
26716 parse_cpp_declarations(source, name)
26717 .declarations()
26718 .iter()
26719 .map(|unit| unit.fq_name())
26720 .collect::<std::collections::BTreeSet<_>>()
26721 };
26722 let c_names = names("a.c");
26723 let cpp_names = names("a.cpp");
26724 assert_eq!(
26725 c_names, cpp_names,
26726 "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
26727 );
26728 assert!(
26729 c_names
26730 .iter()
26731 .any(|name| name.contains('.') && name != "outer"),
26732 "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
26733 );
26734 }
26735 }
26736
26737 #[test]
26738 #[hardwon(
26739 "31913fee42a0: C++ declaration recovery regressed for c anonymous aggregate members keep promoted and named receiver shapes"
26740 )]
26741 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
26742 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
26743 let parsed = parse_cpp_declarations(source, "socket.c");
26744 let declarations = parsed.declarations();
26745 assert_eq!(
26746 declarations
26747 .iter()
26748 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
26749 .count(),
26750 1,
26751 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
26752 );
26753 for expected in [
26754 "PAL_HANDLE",
26755 "PAL_HANDLE.sock",
26756 "PAL_HANDLE$sock",
26757 "PAL_HANDLE$sock.ops",
26758 ] {
26759 assert!(
26760 declarations.iter().any(|unit| unit.fq_name() == expected),
26761 "expected {expected}, got {declarations:?}"
26762 );
26763 }
26764 }
26765
26766 fn namespace_forward_scan_agreement(source: &str) -> usize {
26780 let mut parser = tree_sitter::Parser::new();
26781 parser
26782 .set_language(&tree_sitter_cpp::LANGUAGE.into())
26783 .unwrap();
26784 let tree = parser.parse(source, None).unwrap();
26785 let root = tree.root_node();
26786 let ancestry = ParentIndex::new(root);
26787
26788 let mut nodes = Vec::new();
26789 let mut names = std::collections::BTreeSet::new();
26790 let mut cursor = root.walk();
26791 let mut stack = vec![root];
26792 while let Some(node) = stack.pop() {
26793 if matches!(
26794 node.kind(),
26795 "class_specifier" | "struct_specifier" | "union_specifier"
26796 ) && let Some(name) = class_like_name(node, source, &ancestry)
26797 {
26798 names.insert(name);
26799 }
26800 nodes.push(node);
26801 stack.extend(node.named_children(&mut cursor));
26802 }
26803 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
26804 assert!(!names.is_empty(), "fixture declares no class-like name");
26805
26806 let mut answered = 0usize;
26807 for reversed in [false, true] {
26808 let mut scan = CppNamespaceForwardScan::default();
26809 let ordered: Vec<_> = if reversed {
26810 nodes.iter().rev().copied().collect()
26811 } else {
26812 nodes.clone()
26813 };
26814 answered = 0;
26815 for node in ordered {
26816 for name in &names {
26817 scan.advance_to(root, node.start_byte(), source, &ancestry);
26818 let carried = scan.unique_earlier_forward(name, node);
26819 assert_eq!(
26820 carried,
26821 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
26822 "carried-forward scan and prefix scan disagree about {name} at \
26823 {} node starting at byte {} (reversed order: {reversed})",
26824 node.kind(),
26825 node.start_byte()
26826 );
26827 answered += usize::from(carried.is_some());
26828 }
26829 }
26830 }
26831 answered
26832 }
26833
26834 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
26841namespace ns {
26842class Widget;
26843class Gadget;
26844int x = ;
26845}
26846class API Widget {
26847public:
26848 void first();
26849};
26850class API Gadget {
26851public:
26852 void second();
26853};
26854"#;
26855
26856 #[test]
26857 #[hardwon(
26858 "d3b385cc16ff: C++ declaration recovery regressed for carried forward namespace scan answers what the prefix scan answers"
26859 )]
26860 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
26861 assert!(
26862 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
26863 "the fixture must actually reach the namespace-borrow path"
26864 );
26865
26866 for source in [
26871 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
26872 r#"#define API
26873namespace ns {
26874class Widget;
26875class Widget;
26876int x = ;
26877}
26878class API Widget {
26879public:
26880 void method();
26881};
26882"#,
26883 r#"#define API
26884namespace ns {
26885void host() {
26886 class Widget;
26887}
26888int x = ;
26889}
26890class API Widget {
26891public:
26892 void method();
26893};
26894"#,
26895 ] {
26896 assert_eq!(
26897 namespace_forward_scan_agreement(source),
26898 0,
26899 "no borrow is justified here: {source}"
26900 );
26901 }
26902 }
26903
26904 fn fragmented_class_reparse_agreement(body: &str) {
26914 for prefix in [
26915 String::new(),
26916 "// leading comment\n".to_string(),
26917 "class Widget : public Base { ".to_string(),
26922 "namespace filler {\n".to_string()
26923 + &"struct Filler { int member; };\n".repeat(200)
26924 + "}\n",
26925 "namespace filler {\n".to_string()
26926 + &"struct Filler { int member; };\n".repeat(200)
26927 + "}\nclass Widget : public Base { ",
26928 ] {
26929 let source = format!("{prefix}{body}");
26930 let start = prefix.len();
26931 let end = source.len();
26932 let region = cpp_reparse_fragmented_class_body(&source, start, end)
26933 .expect("the region reparse must produce a tree");
26934 let padded = cpp_reparse_padded_class_body(&source, start, end)
26935 .expect("the padded reparse must produce a tree");
26936 assert_eq!(
26937 cpp_tree_shape(®ion),
26938 cpp_tree_shape(&padded),
26939 "region and padded reparse disagree at offset {start} of {end} bytes"
26940 );
26941 assert_eq!(
26942 region.root_node().start_byte(),
26943 start,
26944 "the reparsed region keeps its original offsets"
26945 );
26946 }
26947 }
26948
26949 #[test]
26950 #[hardwon(
26951 "a4a47ac2395c: C++ declaration recovery regressed for the region reparse of a fragmented class body is the padded reparse"
26952 )]
26953 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
26954 fragmented_class_reparse_agreement(
26958 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
26959 );
26960 fragmented_class_reparse_agreement(
26961 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
26962 );
26963 fragmented_class_reparse_agreement(
26966 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
26967 );
26968 fragmented_class_reparse_agreement(
26969 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
26970 );
26971 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
26974 }
26975
26976 fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
26977 let mut siblings = Vec::new();
26978 let mut anchor = node;
26979 while let Some(parent) = anchor.parent() {
26980 let at_translation_unit = parent.kind() == "translation_unit";
26981 let mut sibling = anchor.next_named_sibling();
26982 while let Some(current) = sibling {
26983 if at_translation_unit
26984 && (current.kind() == "namespace_definition"
26985 || (current.kind() == "function_definition"
26986 && first_class_like_child(current).is_some()))
26987 {
26988 return siblings;
26989 }
26990 siblings.push(current);
26991 if cpp_is_stray_close_brace(current, source) {
26992 if let Some(semicolon) = current
26993 .next_named_sibling()
26994 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
26995 {
26996 siblings.push(semicolon);
26997 }
26998 return siblings;
26999 }
27000 if current.start_byte() >= node.end_byte()
27001 && matches!(current.kind(), "ERROR" | "labeled_statement")
27002 && cpp_nested_stray_close_brace(current, source).is_some()
27003 {
27004 return siblings;
27005 }
27006 sibling = current.next_named_sibling();
27007 }
27008 anchor = parent;
27009 }
27010 siblings
27011 }
27012
27013 #[test]
27014 #[hardwon(
27015 "1c7c8490fb76: C++ declaration recovery regressed for following named siblings cursor matches native traversal"
27016 )]
27017 fn following_named_siblings_cursor_matches_native_traversal() {
27018 let source = r#"
27022namespace api {
27023class PROJECT_API(2, 0) Widget : public virtual Base {
27024 public:
27025 Widget() {}
27026 void first();
27027};
27028enum After { Value };
27029}
27030"#;
27031 let mut parser = tree_sitter::Parser::new();
27032 parser
27033 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27034 .unwrap();
27035 let tree = parser.parse(source, None).unwrap();
27036 let root = tree.root_node();
27037 assert!(root.has_error(), "fixture must remain damaged");
27038
27039 let ancestry = ParentIndex::new(root);
27040 let mut stack = vec![root];
27041 let mut checked = 0;
27042 let mut damaged = 0;
27043 let mut anonymous = 0;
27044 let mut nonempty_sequences = 0;
27045 let mut named_then_anonymous = 0;
27046 while let Some(node) = stack.pop() {
27047 let expected = native_following_named_siblings(node, source)
27048 .iter()
27049 .map(Node::id)
27050 .collect::<Vec<_>>();
27051 let actual = cpp_following_named_siblings(node, source, &ancestry)
27052 .iter()
27053 .map(Node::id)
27054 .collect::<Vec<_>>();
27055 assert_eq!(
27056 actual,
27057 expected,
27058 "cursor traversal diverged at byte {}",
27059 node.start_byte()
27060 );
27061
27062 let mut cursor = node.walk();
27063 let children = node.children(&mut cursor).collect::<Vec<_>>();
27064 stack.extend(children.iter().copied());
27065 checked += 1;
27066 damaged += usize::from(node.is_error() || node.is_missing());
27067 anonymous += usize::from(!node.is_named());
27068 nonempty_sequences += usize::from(!actual.is_empty());
27069 named_then_anonymous += usize::from(
27070 children
27071 .windows(2)
27072 .any(|pair| pair[0].is_named() && !pair[1].is_named()),
27073 );
27074 }
27075 assert!(checked > 0, "fixture must provide nodes to compare");
27076 assert!(damaged > 0, "fixture must cover damaged nodes");
27077 assert!(anonymous > 0, "fixture must cover anonymous nodes");
27078 assert!(
27079 nonempty_sequences > 0,
27080 "fixture must exercise following named siblings"
27081 );
27082 assert!(
27083 named_then_anonymous > 0,
27084 "fixture must exercise anonymous nodes following a named anchor"
27085 );
27086 }
27087
27088 fn many_enums_and_mixed_declarations() -> String {
27092 let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
27093 for index in 0..40 {
27094 let _ = write!(
27095 source,
27096 "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
27097 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
27098 class Color{index}Like {{ public: int member{index}; }};\n"
27099 );
27100 }
27101 source.push_str("namespace outer {\n");
27102 for index in 0..20 {
27103 let _ = write!(
27104 source,
27105 "enum Shade{index} {{ DARK{index} }};\n\
27106 struct Shade{index}Holder {{ int field{index}; }};\n"
27107 );
27108 }
27109 source.push_str("}\n");
27110 source
27111 }
27112
27113 fn field_owner_index_agreement(source: &str, name: &str) -> usize {
27129 let parsed = parse_cpp_declarations(source, name);
27130 let file = ProjectFile::new(std::env::temp_dir(), name);
27131 let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
27132
27133 let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
27134 declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
27135
27136 let foreign: Vec<CodeUnit> = declarations
27137 .iter()
27138 .filter(|unit| unit.kind() == CodeUnitType::Field)
27139 .map(|unit| {
27140 CodeUnit::new_fq(
27141 elsewhere.clone(),
27142 unit.kind(),
27143 unit.package_name().to_string(),
27144 unit.short_name().to_string(),
27145 unit.fq().clone(),
27146 )
27147 })
27148 .collect();
27149
27150 let mut packages: Vec<String> = declarations
27156 .iter()
27157 .map(|unit| unit.package_name().to_string())
27158 .collect();
27159 packages.push(String::new());
27160 packages.sort();
27161 packages.dedup();
27162 let deeper: Vec<CodeUnit> = packages
27163 .iter()
27164 .map(|package_name| {
27165 CodeUnit::new_fq(
27166 file.clone(),
27167 CodeUnitType::Field,
27168 package_name.clone(),
27169 "SynthOwner.middle.leaf".to_string(),
27170 cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
27171 )
27172 })
27173 .collect();
27174
27175 let mut questions: Vec<(String, String)> = Vec::new();
27179 for unit in declarations.iter().chain(deeper.iter()) {
27180 let package_name = unit.package_name().to_string();
27181 let short_name = unit.short_name();
27182 questions.push((package_name.clone(), short_name.to_string()));
27183 questions.push((package_name.clone(), String::new()));
27184 for (offset, _) in short_name.match_indices('.') {
27185 questions.push((package_name.clone(), short_name[..offset].to_string()));
27186 }
27187 }
27188 questions.sort();
27189 questions.dedup();
27190
27191 let mut index = CppFieldOwnerIndex::default();
27192 let mut recorded: Vec<&CodeUnit> = Vec::new();
27193 let mut answered = 0usize;
27194 for unit in foreign
27195 .iter()
27196 .chain(declarations.iter())
27197 .chain(deeper.iter())
27198 {
27199 index.record(unit, &file);
27200 recorded.push(unit);
27201 for (package_name, owner_short_name) in &questions {
27202 let carried = index.owns_fields(package_name, owner_short_name);
27203 assert_eq!(
27204 carried,
27205 cpp_declarations_hold_owned_fields(
27206 recorded.iter().copied(),
27207 &file,
27208 package_name,
27209 owner_short_name
27210 ),
27211 "the carried field index and the declaration scan disagree about \
27212 {package_name:?}/{owner_short_name:?} after recording {}",
27213 unit.fq_name()
27214 );
27215 answered += usize::from(carried);
27216 }
27217 }
27218
27219 let rebuilt = CppFieldOwnerIndex::of(
27222 foreign
27223 .iter()
27224 .chain(declarations.iter())
27225 .chain(deeper.iter()),
27226 &file,
27227 );
27228 for (package_name, owner_short_name) in &questions {
27229 assert_eq!(
27230 rebuilt.owns_fields(package_name, owner_short_name),
27231 index.owns_fields(package_name, owner_short_name),
27232 "a rebuilt index must answer what the incremental one answers for \
27233 {package_name:?}/{owner_short_name:?}"
27234 );
27235 }
27236 answered
27237 }
27238
27239 #[test]
27248 #[hardwon(
27249 "69c2e1f7e41c: C++ declaration recovery regressed for a replacement that removes children drops the field index"
27250 )]
27251 fn a_replacement_that_removes_children_drops_the_field_index() {
27252 let source =
27253 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
27254 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
27255 let mut names: Vec<_> = parsed
27256 .declarations()
27257 .iter()
27258 .map(|unit| unit.fq_name())
27259 .collect();
27260 names.sort();
27261 assert_eq!(
27262 names,
27263 vec![
27264 "Color".to_string(),
27265 "First".to_string(),
27266 "First.A".to_string()
27267 ],
27268 "the replaced Color owns no field any more"
27269 );
27270 }
27271
27272 #[test]
27281 #[hardwon(
27282 "a1556d125712: C++ declaration recovery regressed for a recovery that restores an existing declaration mints nothing"
27283 )]
27284 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
27285 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
27286 BEGIN_NS\n\
27287 namespace demo { struct Widget { void doWork(); }; }\n\
27288 END_NS\n";
27289 let parsed = parse_cpp_declarations(source, "restored.cpp");
27290 let recovered: Vec<String> = parsed
27291 .materialization_records
27292 .iter()
27293 .filter_map(|record| match record {
27294 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
27295 _ => None,
27296 })
27297 .collect();
27298 assert!(
27299 recovered.is_empty(),
27300 "the region declares nothing the file did not already declare: {recovered:?}"
27301 );
27302 let mut names: Vec<String> = parsed
27303 .declarations()
27304 .iter()
27305 .map(|unit| unit.fq_name())
27306 .collect();
27307 names.sort();
27308 assert_eq!(
27309 names,
27310 vec![
27311 "demo".to_string(),
27312 "demo.Widget".to_string(),
27313 "demo.Widget.doWork".to_string(),
27314 ]
27315 );
27316 }
27317
27318 #[test]
27323 #[hardwon(
27324 "a1556d125712: C++ declaration recovery regressed for repeated sentinel recoveries record only what each one minted"
27325 )]
27326 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
27327 let mut source = String::new();
27328 for index in 0..4 {
27329 let _ = write!(
27330 source,
27331 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
27332 );
27333 }
27334 source.push_str("void outside() {}\n");
27335 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
27336
27337 let recovered: Vec<(String, (usize, usize))> = parsed
27338 .materialization_records
27339 .iter()
27340 .filter_map(|record| match record {
27341 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
27342 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
27343 }
27344 _ => None,
27345 })
27346 .collect();
27347
27348 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
27349 for index in 0..4 {
27350 let region = format!("namespace demo{index}");
27353 let region_start = source.find(®ion).expect("each region is in the source");
27354 let start = source[..region_start]
27355 .rfind("BEGIN_NS")
27356 .expect("each region opens with a sentinel")
27357 + "BEGIN_NS".len();
27358 let end = start
27359 + source[start..]
27360 .find("END_NS")
27361 .expect("each region closes with a sentinel")
27362 - 1;
27363 let window = (start, end);
27364 for name in [
27365 format!("demo{index}"),
27366 format!("demo{index}.Widget{index}"),
27367 format!("demo{index}.Widget{index}.doWork{index}"),
27368 ] {
27369 expected.push((name, window));
27370 }
27371 }
27372 assert_eq!(
27373 recovered, expected,
27374 "each recovery records its own minted declarations, in order"
27375 );
27376 assert!(
27377 parsed
27378 .declarations()
27379 .iter()
27380 .any(|unit| unit.fq_name() == "outside"),
27381 "the declaration outside every region stays parsed and unrecovered"
27382 );
27383 }
27384
27385 #[test]
27386 #[hardwon(
27387 "69c2e1f7e41c: C++ declaration recovery regressed for carried forward field index answers what the declaration scan answers"
27388 )]
27389 fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
27390 assert!(
27391 field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
27392 "the fixture must actually own fields"
27393 );
27394
27395 for (source, name) in [
27401 ("struct S { enum E { V }; };\n", "nested.hpp"),
27402 (
27403 "enum Color { RED };\nstruct Color { int RED; };\n",
27404 "class-like.c",
27405 ),
27406 ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
27407 (
27408 "enum E { V };\nnamespace ns { enum E { V }; }\n",
27409 "repeated.hpp",
27410 ),
27411 ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
27412 ] {
27413 field_owner_index_agreement(source, name);
27414 }
27415 }
27416
27417 fn repeated_class_blocks(blocks: usize) -> String {
27422 let mut source = String::from("namespace demo {\n");
27423 for index in 0..blocks {
27424 let _ = write!(
27425 source,
27426 "\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"
27427 );
27428 }
27429 source.push_str("\n}\n");
27430 source
27431 }
27432
27433 #[test]
27441 #[hardwon(
27442 "4530afa0b64f: C++ declaration recovery regressed for recovered class body lookup cost does not grow with the rest of the file"
27443 )]
27444 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
27445 let mut answers = Vec::new();
27446 let mut visits = Vec::new();
27447 let mut node_counts = Vec::new();
27448 for blocks in [200usize, 400] {
27449 let source = repeated_class_blocks(blocks);
27450 let mut parser = tree_sitter::Parser::new();
27451 parser
27452 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27453 .unwrap();
27454 let tree = parser.parse(&source, None).unwrap();
27455 let start_byte = source.find("class Widget0 ").expect("first class");
27456 let end_byte = start_byte
27457 + source[start_byte..]
27458 .find("};")
27459 .expect("first class terminator")
27460 + "};".len();
27461 let range = Range {
27462 start_byte,
27463 end_byte,
27464 start_line: 0,
27465 end_line: 0,
27466 };
27467 reset_recovered_class_body_node_visits_for_test();
27468 let recovered_export_classes =
27469 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
27470 answers.push(recovered_class_body_at(
27471 &recovered_export_classes,
27472 tree.root_node(),
27473 &source,
27474 "Widget0",
27475 &range,
27476 ));
27477 visits.push(recovered_class_body_node_visits_for_test());
27478 let mut nodes = 0usize;
27479 let mut stack = vec![tree.root_node()];
27480 while let Some(node) = stack.pop() {
27481 nodes += 1;
27482 let mut cursor = node.walk();
27483 stack.extend(node.named_children(&mut cursor));
27484 }
27485 node_counts.push(nodes);
27486 }
27487
27488 assert_eq!(
27489 answers,
27490 vec![None, None],
27491 "no recovered shape claims a plain class"
27492 );
27493 assert_eq!(
27494 visits[0], visits[1],
27495 "the walk must follow the range's own path, so doubling the unrelated \
27496 classes must not change the node count: {visits:?} over trees of \
27497 {node_counts:?} nodes"
27498 );
27499 assert!(
27500 visits[1] * 20 < node_counts[1],
27501 "the walk must stay far below one pass over the tree: {visits:?} over \
27502 trees of {node_counts:?} nodes"
27503 );
27504 }
27505
27506 #[test]
27507 #[hardwon("#2982: C MBEDTLS_PRIVATE fields lost direct and chained receiver types")]
27508 fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
27509 let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
27510 let mut parser = tree_sitter::Parser::new();
27511 parser
27512 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27513 .expect("C++ grammar");
27514 let tree = parser.parse(source, None).expect("fixture tree");
27515 let mut stack = vec![tree.root_node()];
27516 let mut recovered = None;
27517 while let Some(node) = stack.pop() {
27518 if node.kind() == "field_declaration"
27519 && let Some(field) = recovered_function_like_field_declarator(node, source)
27520 {
27521 recovered = Some((node, field.name));
27522 break;
27523 }
27524 let mut cursor = node.walk();
27525 stack.extend(node.named_children(&mut cursor));
27526 }
27527 let (declaration, name) = recovered.unwrap_or_else(|| {
27528 panic!(
27529 "pointer-wrapped macro field was not recovered: {}",
27530 tree.root_node().to_sexp()
27531 )
27532 });
27533 assert_eq!(node_text(name, source), "handshake");
27534 let recovered =
27535 recovered_function_like_field_declarator(declaration, source).expect("recovered field");
27536 assert_eq!(recovered.pointer_depth(), 1);
27537 assert_eq!(
27538 render_cpp_field_signature(declaration, name, source),
27539 "struct handshake * handshake;"
27540 );
27541 }
27542
27543 #[test]
27544 #[hardwon(
27545 "4f0531e0e865: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
27546 )]
27547 fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
27548 let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
27549 let mut parser = tree_sitter::Parser::new();
27550 parser
27551 .set_language(&tree_sitter_cpp::LANGUAGE.into())
27552 .expect("C++ grammar");
27553 let tree = parser.parse(source, None).expect("fixture tree");
27554 let mut stack = vec![tree.root_node()];
27559 let mut declaration = None;
27560 while let Some(node) = stack.pop() {
27561 if node.kind() == "field_declaration" {
27562 declaration = Some(node);
27563 break;
27564 }
27565 let mut cursor = node.walk();
27566 stack.extend(node.named_children(&mut cursor));
27567 }
27568 let declaration = declaration.unwrap_or_else(|| {
27569 panic!(
27570 "the pointer field must arrive as an ordinary field declaration: {}",
27571 tree.root_node().to_sexp()
27572 )
27573 });
27574 let declarator = declaration
27575 .child_by_field_name("declarator")
27576 .expect("pointer field declarator");
27577 assert_eq!(node_text(declarator, source), "*image");
27578 assert_eq!(
27579 render_cpp_field_signature(declaration, declarator, source),
27580 "PyObject_HEAD ImagingObject * image;",
27581 "the macro marker is retained as modifier provenance, the same \
27582 treatment an attribute or storage-class specifier gets"
27583 );
27584 assert_eq!(
27585 crate::source_facts::capture_field_type(declaration, "image", source),
27586 (
27587 Some(
27588 brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
27589 type_text: "ImagingObject".to_owned(),
27590 indirection: 1,
27591 binds_indirectly: true,
27592 template_arguments: None,
27593 }
27594 ),
27595 false,
27596 ),
27597 "canonical field facts must use the declared type, not the macro token"
27598 );
27599 }
27600
27601 #[test]
27602 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
27603 let source = "class outer { class inner { int v; }; };\n";
27604 let c_parsed = parse_cpp_declarations(source, "k.c");
27605 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
27606 let c_declarations = c_parsed.declarations();
27607 let cpp_declarations = cpp_parsed.declarations();
27608 assert!(
27609 c_declarations
27610 .iter()
27611 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
27612 "{c_declarations:?}"
27613 );
27614 assert_eq!(
27615 c_declarations
27616 .iter()
27617 .map(|unit| unit.fq_name())
27618 .collect::<std::collections::BTreeSet<_>>(),
27619 cpp_declarations
27620 .iter()
27621 .map(|unit| unit.fq_name())
27622 .collect::<std::collections::BTreeSet<_>>()
27623 );
27624 }
27625 #[test]
27626 fn callable_metadata_records_c_receiver_contracts_structurally() {
27627 const SOURCE: &str = concat!(
27628 "static long accumulate(const long *lines, int count) { return count; }\n",
27629 "\n",
27630 "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
27631 );
27632 let parsed = parse_cpp_declarations(SOURCE, "shop.c");
27633
27634 assert_eq!(
27635 recorded_callable_modifiers(&parsed),
27636 vec![
27637 "accumulate decl_only=false static=false constructor=false \
27638 visibility=Unknown"
27639 .to_string(),
27640 "total decl_only=false static=false constructor=false visibility=Unknown"
27641 .to_string(),
27642 ],
27643 );
27644 }
27645 #[test]
27646 fn callable_metadata_records_cpp_receiver_contracts_structurally() {
27647 const SOURCE: &str = concat!(
27648 "namespace shop {\n",
27649 "\n",
27650 "class Client {\n",
27651 "public:\n",
27652 " Client();\n",
27653 " void send(int order);\n",
27654 " static int count();\n",
27655 "protected:\n",
27656 " virtual int audit(int n) const { return n; }\n",
27657 "private:\n",
27658 " int tally(int n) { return n; }\n",
27659 "};\n",
27660 "\n",
27661 "struct Ledger {\n",
27662 " void record(int n) { (void)n; }\n",
27663 "};\n",
27664 "\n",
27665 "Client::Client() {}\n",
27666 "void Client::send(int order) { (void)order; }\n",
27667 "int Client::count() { return 0; }\n",
27668 "\n",
27669 "int total(int order) { return order; }\n",
27670 "\n",
27671 "}\n",
27672 );
27673 let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
27674
27675 assert_eq!(
27676 recorded_callable_modifiers(&parsed),
27677 vec![
27678 "shop.Client.Client decl_only=false static=false constructor=true \
27679 visibility=Unknown"
27680 .to_string(),
27681 "shop.Client.Client decl_only=true static=false constructor=true \
27682 visibility=Public"
27683 .to_string(),
27684 "shop.Client.audit decl_only=false static=false constructor=false \
27685 visibility=Protected"
27686 .to_string(),
27687 "shop.Client.count decl_only=false static=false constructor=false \
27688 visibility=Unknown"
27689 .to_string(),
27690 "shop.Client.count decl_only=true static=true constructor=false \
27691 visibility=Public"
27692 .to_string(),
27693 "shop.Client.send decl_only=false static=false constructor=false \
27694 visibility=Unknown"
27695 .to_string(),
27696 "shop.Client.send decl_only=true static=false constructor=false \
27697 visibility=Public"
27698 .to_string(),
27699 "shop.Client.tally decl_only=false static=false constructor=false \
27700 visibility=Private"
27701 .to_string(),
27702 "shop.Ledger.record decl_only=false static=false constructor=false \
27703 visibility=Public"
27704 .to_string(),
27705 "shop.total decl_only=false static=false constructor=false visibility=Unknown"
27706 .to_string(),
27707 ],
27708 );
27709 }
27710}