1use brokk_bifrost_core::analyzer::common::{node_source_text, parse_source_region};
8use brokk_bifrost_core::analyzer::fq_name::{
9 FqName, SegmentId, SegmentKind, joined_segments, normalize_joined, segment_interner,
10};
11use brokk_bifrost_core::analyzer::model::{
12 CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
13 CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
14 CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
15 ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
16 StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
17};
18use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
19use brokk_bifrost_core::analyzer::structural::materialization::{
20 GenerationKind, MaterializationRecord,
21};
22use brokk_bifrost_core::analyzer::tree_walk::{ParentIndex, WalkControl, walk_named_tree_preorder};
23use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
24use brokk_bifrost_core::hash::{HashMap, HashSet};
25use regex::Regex;
26use tree_sitter::{Node, Parser, Tree};
27
28fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
30 segment_interner().intern(text, kind)
31}
32
33fn cpp_push_package(fq: &mut FqName, package_name: &str) {
41 for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
42 fq.push(cpp_segment(component, SegmentKind::Package));
43 }
44}
45
46const CPP_PACKAGE_SEPARATOR: &str = "::";
48
49fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
56 let mut first = true;
57 for component in chain.split('$').filter(|c| !c.is_empty()) {
61 let kind = if first {
62 SegmentKind::Type
63 } else {
64 SegmentKind::Nested
65 };
66 fq.push(cpp_segment(component, kind));
67 first = false;
68 }
69}
70
71fn cpp_namespace_fq(full_name: &str) -> FqName {
75 let mut fq = FqName::new();
76 cpp_push_package(&mut fq, full_name);
77 fq
78}
79
80fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
96 let mut components = Vec::new();
97 let mut stack = vec![node];
98 while let Some(current) = stack.pop() {
99 match current.kind() {
100 "namespace_identifier" | "identifier" => {
101 components.push(normalize_cpp_whitespace(node_text(current, source)));
102 }
103 "nested_namespace_specifier" => {
104 for index in (0..current.named_child_count()).rev() {
105 stack.push(
106 current
107 .named_child(index)
108 .expect("index below the node's own named child count"),
109 );
110 }
111 }
112 _ => return cpp_raw_namespace_name_components(node, source),
113 }
114 }
115 if components.iter().any(String::is_empty) {
116 return cpp_raw_namespace_name_components(node, source);
117 }
118 components
119}
120
121fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
135 let start = node
136 .child(0)
137 .filter(|child| !child.is_named() && child.kind() == "::")
138 .map_or(node.start_byte(), |marker| marker.end_byte());
139 let text = normalize_cpp_whitespace(
140 source
141 .get(start..node.end_byte())
142 .expect("namespace name node covers one source range"),
143 );
144 let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
145 if text.is_empty() {
146 return Vec::new();
147 }
148 vec![text]
149}
150
151fn cpp_lexical_namespace_name<'tree>(
157 node: Node<'tree>,
158 source: &str,
159 ancestry: &ParentIndex<'tree>,
160) -> Option<String> {
161 let mut components = Vec::new();
162 let mut ancestor = ancestry.parent(node);
163 while let Some(current) = ancestor {
164 if current.kind() == "namespace_definition" {
165 let name_node = current.child_by_field_name("name")?;
166 let name = normalize_cpp_whitespace(node_text(name_node, source));
167 if name.is_empty() {
168 return None;
169 }
170 components.push(name);
171 }
172 ancestor = ancestry.parent(current);
173 }
174 if components.is_empty() {
175 return None;
176 }
177 components.reverse();
178 Some(
182 normalize_joined(
183 &components.join(CPP_PACKAGE_SEPARATOR),
184 CPP_PACKAGE_SEPARATOR,
185 )
186 .into_owned(),
187 )
188}
189
190fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
196 if parent_short.is_empty() {
197 name.to_string()
198 } else {
199 format!("{parent_short}${name}")
200 }
201}
202
203fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
206 if parent_short.is_empty() {
207 name.to_string()
208 } else {
209 format!("{parent_short}.{name}")
210 }
211}
212
213fn cpp_leaf_fq(
220 package_name: &str,
221 parent: Option<&CodeUnit>,
222 name: &str,
223 kind_if_nested: SegmentKind,
224 kind_if_top: SegmentKind,
225) -> FqName {
226 if let Some(parent) = parent {
227 parent
228 .fq()
229 .clone()
230 .with_pushed(cpp_segment(name, kind_if_nested))
231 } else {
232 let mut fq = FqName::new();
233 cpp_push_package(&mut fq, package_name);
234 fq.push(cpp_segment(name, kind_if_top));
235 fq
236 }
237}
238
239pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
246 let mut fq = FqName::new();
247 cpp_push_package(&mut fq, package_name);
248 match short_name.rsplit_once('.') {
249 Some((owner_chain, member)) => {
250 cpp_push_type_chain(&mut fq, owner_chain);
251 fq.push(cpp_segment(member, SegmentKind::Member));
252 }
253 None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
254 }
255 fq
256}
257
258#[derive(Clone)]
259pub struct ScopeInfo {
260 package_name: String,
261 module: Option<CodeUnit>,
262 class_unit: Option<CodeUnit>,
263 template_signature: Option<String>,
264 template_metadata: Option<CppTemplateMetadata>,
265 declarations_are_fields: bool,
266 recovered_specialization_member_scope: bool,
267 visible_using_namespaces: Vec<String>,
279}
280
281struct CppContainer<'tree> {
282 node: Node<'tree>,
283 scope: ScopeInfo,
284}
285
286struct CppNodeWork<'tree> {
287 node: Node<'tree>,
288 scope: ScopeInfo,
289}
290
291struct CppSiblingsWork<'tree> {
298 children: std::vec::IntoIter<Node<'tree>>,
299 scope: ScopeInfo,
300}
301
302enum CppWork<'tree> {
303 Container(CppContainer<'tree>),
304 Node(CppNodeWork<'tree>),
305 Siblings(CppSiblingsWork<'tree>),
306}
307
308fn class_like_name<'tree>(
309 node: Node<'tree>,
310 source: &str,
311 ancestry: &ParentIndex<'tree>,
312) -> Option<String> {
313 let best = class_like_name_from_children(node, source);
314 if let Some(parent) = ancestry.parent(node)
315 && matches!(
316 parent.kind(),
317 "declaration" | "field_declaration" | "function_definition"
318 )
319 && cpp_body_node(node).is_none()
328 && node
329 .child_by_field_name("name")
330 .map(|name_node| {
331 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
332 })
333 .unwrap_or(false)
334 && let Some(recovered) = exported_class_name_from_node(parent, source)
335 && best.as_deref() != Some(recovered.as_str())
336 {
337 return Some(recovered);
338 }
339 best.or_else(|| {
340 node.child_by_field_name("name")
341 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
342 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
343 })
344}
345
346fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
347 let mut grammar_name = None;
348 if let Some(name_node) = node.child_by_field_name("name") {
349 let name = normalize_cpp_whitespace(node_text(name_node, source));
350 if name.is_empty() {
351 return None;
352 }
353 if !cpp_export_macro_token(&name) {
354 return Some(name);
355 }
356 grammar_name = Some(name);
357 }
358
359 let mut best = None;
360 let mut cursor = node.walk();
361 let mut stack = Vec::new();
362 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
363 if matches!(
364 child.kind(),
365 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
366 ) {
367 break;
368 }
369 stack.push(child);
370 }
371
372 while let Some(current) = stack.pop() {
373 if matches!(current.kind(), "type_identifier" | "identifier") {
374 let name = normalize_cpp_whitespace(node_text(current, source));
375 if !name.is_empty() && !cpp_export_macro_token(&name) {
376 best = Some(name);
377 }
378 continue;
379 }
380
381 for index in (0..current.named_child_count()).rev() {
382 if let Some(child) = current.named_child(index) {
383 stack.push(child);
384 }
385 }
386 }
387 best.or(grammar_name)
388}
389
390pub fn cpp_export_macro_token(token: &str) -> bool {
391 token
392 .chars()
393 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
394}
395
396struct RecoveredExportedClass<'tree> {
397 declaration_node: Node<'tree>,
398 name: String,
399 body: Option<Node<'tree>>,
400 raw_supertypes: Option<Vec<String>>,
401 uses_initializer_body: bool,
402 fragmented_body: Option<FragmentedExportBody>,
407}
408
409struct FragmentedExportBody {
415 reparse_start: usize,
416 reparse_end: usize,
417 class_range: Range,
418}
419
420struct DisplacedFragmentNamespaceBoundary<'tree> {
421 class_close: Node<'tree>,
422 class_semicolon: Node<'tree>,
423 namespace_items: Vec<Node<'tree>>,
424}
425
426enum FragmentedExportMembers {
431 Complete(Tree),
432 ConditionalConstructor(Tree),
433}
434
435#[derive(Clone, Copy)]
436struct DisplacedMacroClassTail {
437 split_index: usize,
438 class_range: Range,
439}
440
441fn recover_exported_class_declaration<'tree>(
442 node: Node<'tree>,
443 source: &str,
444) -> Option<RecoveredExportedClass<'tree>> {
445 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
446 return Some(recovered);
447 }
448
449 let class_node = first_class_like_child(node)?;
450 if let Some(name_node) = class_node.child_by_field_name("name") {
451 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
452 if cpp_export_macro_token(&class_name) {
453 let mut cursor = node.walk();
457 if node
458 .children_by_field_name("declarator", &mut cursor)
459 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
460 {
461 return None;
462 }
463 } else if has_direct_cpp_declarator(node) {
464 return None;
465 }
466 }
467 let name = exported_class_name_from_node(class_node, source)?;
468 Some(RecoveredExportedClass {
469 declaration_node: class_node,
470 name,
471 body: cpp_body_node(class_node),
472 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
473 .then(|| extract_cpp_supertypes(class_node, source)),
474 uses_initializer_body: false,
475 fragmented_body: None,
476 })
477}
478
479fn recover_malformed_exported_base_class<'tree>(
480 node: Node<'tree>,
481 source: &str,
482) -> Option<RecoveredExportedClass<'tree>> {
483 if node.kind() != "declaration" {
484 return None;
485 }
486 let class_node = node.child_by_field_name("type")?;
487 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
488 return None;
489 }
490 let macro_name = class_node
491 .child_by_field_name("name")
492 .and_then(|name| direct_identifier_name(name, source))?;
493 if !cpp_export_macro_token(¯o_name) {
494 return None;
495 }
496
497 let mut named_cursor = node.walk();
498 let mut named = node.named_children(&mut named_cursor);
499 if named
500 .next()
501 .is_none_or(|child| !same_node(child, class_node))
502 {
503 return None;
504 }
505 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
506 if displaced.kind() != "ERROR" {
507 return None;
508 }
509 let name = displaced_exported_class_name(displaced, source)?;
510
511 let remaining = named.collect::<Vec<_>>();
512 let init = *remaining.last()?;
513 if init.kind() != "init_declarator" {
514 return None;
515 }
516 let final_base = init
517 .child_by_field_name("declarator")
518 .and_then(|base| recovered_malformed_base_name(base, source))?;
519 let body = init.child_by_field_name("value")?;
520 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
524 return None;
525 }
526
527 if remaining[..remaining.len() - 1]
528 .iter()
529 .any(|child| match child.kind() {
530 "qualified_identifier"
531 | "scoped_type_identifier"
532 | "type_identifier"
533 | "identifier" => false,
534 "ERROR" => !is_malformed_inheritance_access(*child, source),
535 _ => true,
536 })
537 {
538 return None;
539 }
540
541 let mut raw_supertypes = Vec::new();
542 for base in &remaining[..remaining.len() - 1] {
543 if base.kind() == "ERROR" {
544 continue;
545 }
546 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
547 }
548 raw_supertypes.push(final_base);
549
550 Some(RecoveredExportedClass {
551 declaration_node: node,
552 name,
553 body: Some(body),
554 raw_supertypes: Some(raw_supertypes),
555 uses_initializer_body: true,
556 fragmented_body: fragmented_export_body_region(node, body, source),
557 })
558}
559
560fn fragmented_export_body_region(
576 node: Node<'_>,
577 body: Node<'_>,
578 source: &str,
579) -> Option<FragmentedExportBody> {
580 let reparse_start = body.start_byte() + 1;
581 let close = direct_close_brace(body)?;
582 if close.end_byte() > close.start_byte() {
583 return Some(FragmentedExportBody {
584 reparse_start,
585 reparse_end: close.start_byte(),
586 class_range: cpp_declaration_range(node),
587 });
588 }
589 let mut sibling = node.next_named_sibling();
592 let displaced_close = loop {
593 let Some(current) = sibling else {
594 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
595 };
596 if cpp_is_stray_close_brace(current, source) {
597 break current;
598 }
599 sibling = current.next_named_sibling();
600 };
601 Some(FragmentedExportBody {
602 reparse_start,
603 reparse_end: displaced_close.start_byte(),
604 class_range: Range {
605 start_byte: node.start_byte(),
606 end_byte: displaced_close.end_byte(),
607 start_line: node.start_position().row + 1,
608 end_line: displaced_close.end_position().row + 1,
609 },
610 })
611}
612
613fn fragmented_export_function_body_region(
621 node: Node<'_>,
622 body: Node<'_>,
623 source: &str,
624 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
625) -> Option<FragmentedExportBody> {
626 let reparse_start = body.start_byte().checked_add(1)?;
627 if let Some(boundary) = displaced_namespace {
628 return Some(FragmentedExportBody {
629 reparse_start,
630 reparse_end: boundary.class_close.start_byte(),
631 class_range: Range {
632 start_byte: node.start_byte(),
633 end_byte: boundary.class_semicolon.end_byte(),
634 start_line: node.start_position().row + 1,
635 end_line: boundary.class_semicolon.end_position().row + 1,
636 },
637 });
638 }
639 let siblings = cpp_following_named_siblings(node, source);
640 let boundary = fragmented_export_sibling_class_boundary(node, source);
641 let boundary_index = boundary.and_then(|boundary| {
642 siblings
643 .iter()
644 .position(|candidate| same_node(*candidate, boundary))
645 });
646 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
647 let mut sibling_index = 0;
648 while let Some(current) = siblings.get(sibling_index).copied() {
661 if current.kind() == "comment" {
662 sibling_index += 1;
663 continue;
664 }
665 if is_trailing_attribute_macro_sibling(current) {
666 sibling_index += 1;
667 continue;
668 }
669 if cpp_is_stray_semicolon(current, source) {
670 return None;
671 }
672 break;
673 }
674 while let Some(current) = siblings.get(sibling_index).copied() {
675 let next = siblings.get(sibling_index + 1).copied();
676 if cpp_is_stray_close_brace(current, source)
677 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
678 {
679 let semicolon = next.expect("checked above");
680 return Some(FragmentedExportBody {
681 reparse_start,
682 reparse_end: current.start_byte(),
683 class_range: Range {
684 start_byte: node.start_byte(),
685 end_byte: semicolon.end_byte(),
686 start_line: node.start_position().row + 1,
687 end_line: semicolon.end_position().row + 1,
688 },
689 });
690 }
691 if current.start_byte() >= body.end_byte()
698 && let Some(close) = cpp_nested_stray_close_brace(current, source)
699 {
700 return Some(FragmentedExportBody {
701 reparse_start,
702 reparse_end: close.start_byte(),
703 class_range: Range {
704 start_byte: node.start_byte(),
705 end_byte: current.end_byte(),
706 start_line: node.start_position().row + 1,
707 end_line: current.end_position().row + 1,
708 },
709 });
710 }
711 sibling_index += 1;
712 }
713 boundary.map(|boundary| FragmentedExportBody {
714 reparse_start,
715 reparse_end: boundary.start_byte(),
716 class_range: Range {
717 start_byte: node.start_byte(),
718 end_byte: boundary.start_byte(),
719 start_line: node.start_position().row + 1,
720 end_line: boundary.start_position().row + 1,
721 },
722 })
723}
724
725fn fragmented_export_sibling_class_boundary<'tree>(
730 node: Node<'tree>,
731 source: &str,
732) -> Option<Node<'tree>> {
733 let node_parent = node.parent()?;
734 cpp_following_named_siblings(node, source)
735 .into_iter()
736 .find(|candidate| {
737 recover_exported_class_function_definition(*candidate, source).is_some()
738 && candidate
739 .parent()
740 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
741 })
742}
743
744fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
749 if node.kind() != "expression_statement" {
750 return false;
751 }
752 let mut cursor = node.walk();
753 let mut children = node.named_children(&mut cursor);
754 children
755 .next()
756 .is_some_and(|child| child.kind() == "identifier")
757 && children.next().is_none()
758}
759
760fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
766 let mut stack = vec![node];
767 while let Some(current) = stack.pop() {
768 if cpp_is_stray_close_brace(current, source) {
769 return Some(current);
770 }
771 let mut cursor = current.walk();
772 stack.extend(current.named_children(&mut cursor));
773 }
774 None
775}
776
777fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
782 let mut siblings = Vec::new();
783 let mut anchor = node;
784 while let Some(parent) = anchor.parent() {
785 let at_translation_unit = parent.kind() == "translation_unit";
786 let mut sibling = anchor.next_named_sibling();
787 while let Some(current) = sibling {
788 if at_translation_unit
789 && (current.kind() == "namespace_definition"
790 || (current.kind() == "function_definition"
791 && first_class_like_child(current).is_some()))
792 {
793 return siblings;
794 }
795 siblings.push(current);
796 if cpp_is_stray_close_brace(current, source) {
797 if let Some(semicolon) = current
798 .next_named_sibling()
799 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
800 {
801 siblings.push(semicolon);
802 }
803 return siblings;
804 }
805 if current.start_byte() >= node.end_byte()
806 && matches!(current.kind(), "ERROR" | "labeled_statement")
807 && cpp_nested_stray_close_brace(current, source).is_some()
808 {
809 return siblings;
810 }
811 sibling = current.next_named_sibling();
812 }
813 anchor = parent;
814 }
815 siblings
816}
817
818fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
819 if node.start_byte() >= class_end {
820 return false;
821 }
822 node.end_byte() <= class_end
823 || cpp_nested_stray_close_brace(node, source)
824 .is_some_and(|close| close.start_byte() == class_end)
825}
826
827fn fragmented_plain_class_body<'tree>(
834 node: Node<'tree>,
835 source: &str,
836) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
837 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
838 return Some(recovered);
839 }
840 if node.kind() != "ERROR" {
841 return None;
842 }
843 let mut cursor = node.walk();
844 let children = node.children(&mut cursor).collect::<Vec<_>>();
845 let keyword = children.first()?;
846 if !matches!(keyword.kind(), "class" | "struct" | "union") {
847 return None;
848 }
849 let name_node = children
850 .iter()
851 .copied()
852 .skip(1)
853 .find(|child| child.is_named())?;
854 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
855 return None;
856 }
857 let name = normalize_cpp_whitespace(node_text(name_node, source));
858 if name.is_empty() || cpp_export_macro_token(&name) {
859 return None;
860 }
861 let open_index = children.iter().position(|child| child.kind() == "{")?;
862 let open = children[open_index];
863 let nested_class_opens = children[open_index + 1..]
864 .iter()
865 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
866 .count();
867 let mut closes_remaining = 1 + nested_class_opens;
868 let mut sibling = node.next_named_sibling();
869 while let Some(candidate) = sibling {
870 let next = candidate.next_named_sibling();
871 if cpp_is_stray_close_brace(candidate, source) {
872 closes_remaining -= 1;
873 if closes_remaining == 0 {
874 let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
875 if open.end_byte() >= candidate.start_byte() {
876 return None;
877 }
878 return Some((
879 node,
880 name,
881 FragmentedExportBody {
882 reparse_start: open.end_byte(),
883 reparse_end: candidate.start_byte(),
884 class_range: Range {
885 start_byte: node.start_byte(),
886 end_byte: semicolon.end_byte(),
887 start_line: node.start_position().row + 1,
888 end_line: semicolon.end_position().row + 1,
889 },
890 },
891 ));
892 }
893 }
894 sibling = next;
895 }
896 None
897}
898
899pub(crate) fn recovered_fragmented_plain_class_has_body(
900 node: Node<'_>,
901 source: &str,
902 expected_name: &str,
903 expected_range: &Range,
904) -> bool {
905 fragmented_plain_class_body(node, source).is_some_and(|(_, name, fragmented)| {
906 name == expected_name
907 && fragmented.class_range.start_byte == expected_range.start_byte
908 && fragmented.class_range.end_byte == expected_range.end_byte
909 })
910}
911
912fn fragmented_plain_class_declaration_body<'tree>(
919 node: Node<'tree>,
920 source: &str,
921) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
922 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
923 return None;
924 }
925 let class_node = node.child_by_field_name("type")?;
926 if !matches!(
927 class_node.kind(),
928 "class_specifier" | "struct_specifier" | "union_specifier"
929 ) {
930 return None;
931 }
932 let name_node = class_node.child_by_field_name("name")?;
933 let name = normalize_cpp_whitespace(node_text(name_node, source));
934 if name.is_empty() || cpp_export_macro_token(&name) {
935 return None;
936 }
937 let body = cpp_body_node(class_node)?;
938 if body.kind() != "field_declaration_list" {
939 return None;
940 }
941 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
942 if declarator.start_byte() < class_node.end_byte() {
943 return None;
944 }
945 let mut cursor = node.walk();
946 node.named_children(&mut cursor).any(|child| {
947 if child.kind() != "ERROR"
948 || child.start_byte() < class_node.end_byte()
949 || child.end_byte() > declarator.start_byte()
950 {
951 return false;
952 }
953 let mut cursor = child.walk();
954 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
955 let Some((return_type, attributes)) = components.split_last() else {
956 return false;
957 };
958 matches!(
959 return_type.kind(),
960 "identifier"
961 | "type_identifier"
962 | "primitive_type"
963 | "decltype"
964 | "placeholder_type_specifier"
965 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
966 && attributes.iter().all(|attribute| {
967 matches!(attribute.kind(), "identifier" | "type_identifier")
968 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
969 *attribute, source,
970 )))
971 })
972 })
973 } else {
974 let mut cursor = node.walk();
975 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
976 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
977 if same_node(*candidate_class, class_node)
978 && continuation.kind() == "identifier"
979 && node_text(*continuation, source) == "else"
980 && continuation_body.kind() == "compound_statement"
981 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
982 && continuation_body
983 .child(continuation_body.child_count().saturating_sub(1))
984 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
985 };
986 if !displaced_member {
987 return None;
988 }
989 let open = body
990 .children(&mut body.walk())
991 .find(|child| child.kind() == "{")?;
992 let siblings = cpp_following_named_siblings(node, source);
993 let ordinary_boundary =
994 siblings
995 .iter()
996 .copied()
997 .enumerate()
998 .find_map(|(close_index, close)| {
999 cpp_is_stray_close_brace(close, source)
1000 .then(|| {
1001 siblings
1002 .get(close_index + 1)
1003 .copied()
1004 .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1005 .map(|semicolon| (close, semicolon))
1006 })
1007 .flatten()
1008 });
1009 let (close, semicolon) =
1010 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1011 (boundary.class_close, boundary.class_semicolon)
1012 } else {
1013 ordinary_boundary?
1014 };
1015 if open.end_byte() >= close.start_byte() {
1016 return None;
1017 }
1018 Some((
1019 class_node,
1020 name,
1021 FragmentedExportBody {
1022 reparse_start: open.end_byte(),
1023 reparse_end: close.start_byte(),
1024 class_range: Range {
1025 start_byte: class_node.start_byte(),
1026 end_byte: semicolon.end_byte(),
1027 start_line: class_node.start_position().row + 1,
1028 end_line: semicolon.end_position().row + 1,
1029 },
1030 },
1031 ))
1032}
1033
1034fn displaced_export_function_namespace_shape<'tree>(
1035 declaration: Node<'tree>,
1036 source: &str,
1037) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1038 let mut nested = Vec::new();
1039 for index in (0..declaration.named_child_count()).rev() {
1040 nested.push(declaration.named_child(index)?);
1041 }
1042 while let Some(current) = nested.pop() {
1043 if recover_exported_class_function_definition(current, source).is_some() {
1049 return None;
1050 }
1051 for index in (0..current.named_child_count()).rev() {
1052 nested.push(current.named_child(index)?);
1053 }
1054 }
1055 let mut same_envelope_sibling = declaration.next_named_sibling();
1056 while let Some(current) = same_envelope_sibling {
1057 if recover_exported_class_function_definition(current, source).is_some() {
1058 return None;
1059 }
1060 same_envelope_sibling = current.next_named_sibling();
1061 }
1062 let declaration_list = declaration.parent()?;
1063 if declaration_list.kind() != "declaration_list" {
1064 return None;
1065 }
1066 let namespace = declaration_list.parent()?;
1067 if namespace.kind() != "namespace_definition"
1068 || namespace.child_by_field_name("body") != Some(declaration_list)
1069 {
1070 return None;
1071 }
1072 let class_close = direct_close_brace(declaration_list)?;
1073 let trailing_semicolon = namespace.next_named_sibling()?;
1074 if trailing_semicolon.kind() != "expression_statement"
1075 || trailing_semicolon.named_child_count() != 0
1076 {
1077 return None;
1078 }
1079 let siblings = cpp_following_named_siblings(namespace, source);
1085 let trailing_index = siblings
1086 .iter()
1087 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1088 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1089 recover_exported_class_function_definition(*candidate, source).is_some()
1090 }) {
1091 return None;
1096 }
1097 let mut namespace_items = Vec::new();
1098 let mut nested_fragment_end = 0;
1099 for current in siblings.into_iter().skip(trailing_index + 1) {
1100 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1101 {
1102 return Some(DisplacedFragmentNamespaceBoundary {
1103 class_close,
1104 class_semicolon: trailing_semicolon,
1105 namespace_items,
1106 });
1107 }
1108 if current.start_byte() >= nested_fragment_end
1109 && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1110 {
1111 nested_fragment_end = fragmented.class_range.end_byte;
1112 } else if current.start_byte() >= nested_fragment_end
1113 && recover_exported_class_function_definition(current, source).is_some()
1114 && let Some(body) = cpp_body_node(current)
1115 && let Some(fragmented) =
1116 fragmented_export_function_body_region(current, body, source, None)
1117 {
1118 nested_fragment_end = fragmented.class_range.end_byte;
1119 }
1120 namespace_items.push(current);
1121 }
1122 None
1123}
1124
1125fn displaced_fragment_namespace_boundary<'tree>(
1126 declaration: Node<'tree>,
1127 body: Node<'tree>,
1128 source: &str,
1129) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1130 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1131 let reparse_start = body.start_byte() + 1;
1132 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1133 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1134}
1135
1136fn displaced_fragment_namespace_geometry<'tree>(
1142 declaration: Node<'tree>,
1143 source: &str,
1144) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1145 let envelope = declaration
1149 .parent()
1150 .filter(|parent| {
1151 parent.kind() == "template_declaration"
1152 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1153 })
1154 .unwrap_or(declaration);
1155 let declaration_list = envelope.parent()?;
1156 if declaration_list.kind() != "declaration_list" {
1157 return None;
1158 }
1159 let namespace = declaration_list.parent()?;
1160 if namespace.kind() != "namespace_definition"
1161 || namespace.child_by_field_name("body") != Some(declaration_list)
1162 {
1163 return None;
1164 }
1165 let class_close = direct_close_brace(declaration_list)?;
1166 let trailing_semicolon = namespace.next_named_sibling()?;
1167 if trailing_semicolon.kind() != "expression_statement"
1168 || trailing_semicolon.named_child_count() != 0
1169 {
1170 return None;
1171 }
1172 let mut namespace_items = Vec::new();
1173 let mut sibling = trailing_semicolon.next_named_sibling();
1174 let mut nested_fragment_end = 0;
1175 loop {
1176 let current = sibling?;
1177 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1178 {
1179 break;
1180 }
1181 if current.start_byte() >= nested_fragment_end
1182 && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1183 {
1184 nested_fragment_end = fragmented.class_range.end_byte;
1185 }
1186 namespace_items.push(current);
1187 sibling = current.next_named_sibling();
1188 }
1189 Some(DisplacedFragmentNamespaceBoundary {
1190 class_close,
1191 class_semicolon: trailing_semicolon,
1192 namespace_items,
1193 })
1194}
1195
1196fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1198 (0..node.child_count())
1199 .filter_map(|index| node.child(index))
1200 .find(|child| !child.is_named() && child.kind() == "}")
1201}
1202
1203fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1206 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1207}
1208
1209fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1220 let bytes = source.as_bytes();
1221 if bytes.get(open_byte) != Some(&b'{') {
1222 return None;
1223 }
1224 let mut depth = 0usize;
1225 let mut i = open_byte;
1226 while i < bytes.len() {
1227 match bytes[i] {
1228 b'{' => depth += 1,
1229 b'}' => {
1230 depth = depth.checked_sub(1)?;
1231 if depth == 0 {
1232 return Some(i);
1233 }
1234 }
1235 b'/' if bytes.get(i + 1) == Some(&b'/') => {
1236 while i < bytes.len() && bytes[i] != b'\n' {
1237 i += 1;
1238 }
1239 continue;
1240 }
1241 b'/' if bytes.get(i + 1) == Some(&b'*') => {
1242 i += 2;
1243 while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1244 i += 1;
1245 }
1246 i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1247 continue;
1248 }
1249 quote @ (b'"' | b'\'') => {
1250 if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1253 return None;
1254 }
1255 i += 1;
1256 while i < bytes.len() && bytes[i] != quote {
1257 i += if bytes[i] == b'\\' { 2 } else { 1 };
1258 }
1259 if i >= bytes.len() {
1260 return None;
1261 }
1262 }
1263 _ => {}
1264 }
1265 i += 1;
1266 }
1267 None
1268}
1269
1270fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1271 let mut name = None;
1272 let mut colon_count = 0;
1273 let mut access_count = 0;
1274 for index in 0..node.child_count() {
1275 let child = node.child(index)?;
1276 match child.kind() {
1277 "identifier" | "type_identifier" if child.is_named() => {
1278 if name.is_some() {
1279 return None;
1280 }
1281 let candidate = normalize_cpp_whitespace(node_text(child, source));
1282 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1283 return None;
1284 }
1285 name = Some(candidate);
1286 }
1287 "template_function" | "template_type" if child.is_named() => {
1288 if name.is_some() {
1289 return None;
1290 }
1291 let candidate = child
1292 .child_by_field_name("name")
1293 .and_then(|name| direct_identifier_name(name, source))?;
1294 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1295 return None;
1296 }
1297 name = Some(candidate);
1298 }
1299 ":" if !child.is_named() => colon_count += 1,
1300 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1301 _ => return None,
1302 }
1303 }
1304 (colon_count == 1 && access_count == 1)
1305 .then_some(name)
1306 .flatten()
1307}
1308
1309fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1310 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1311 return false;
1312 }
1313 node.named_child(0)
1314 .and_then(|child| direct_identifier_name(child, source))
1315 .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1316}
1317
1318fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1319 (0..node.child_count()).any(|index| {
1320 node.child(index)
1321 .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1322 })
1323}
1324
1325fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1326 match node.kind() {
1327 "type_identifier" | "identifier" | "namespace_identifier" => {
1328 recovered_base_atom(node, source)
1329 }
1330 "template_type" | "template_function" => node
1331 .child_by_field_name("name")
1332 .and_then(|name| recovered_malformed_base_name(name, source)),
1333 "ERROR" => None,
1334 "qualified_identifier" | "scoped_type_identifier" => {
1335 let suffix = node
1336 .child_by_field_name("name")
1337 .and_then(|name| recovered_malformed_base_name(name, source))?;
1338 let scope = node
1339 .child_by_field_name("scope")
1340 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1341 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1342 malformed_qualified_prefix(node, source)?
1343 } else {
1344 if malformed_qualified_prefix(node, source).is_some() {
1345 return None;
1346 }
1347 scope
1348 };
1349 Some(format!("{prefix}::{suffix}"))
1350 }
1351 _ => None,
1352 }
1353}
1354
1355fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1356 if !matches!(
1357 node.kind(),
1358 "identifier" | "type_identifier" | "namespace_identifier"
1359 ) {
1360 return None;
1361 }
1362 let name = normalize_cpp_whitespace(node_text(node, source));
1363 (!name.is_empty()).then_some(name)
1364}
1365
1366fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1367 let mut prefix = None;
1368 let mut cursor = node.walk();
1369 for error in node
1370 .named_children(&mut cursor)
1371 .filter(|child| child.kind() == "ERROR")
1372 {
1373 if error.named_child_count() != 1 || prefix.is_some() {
1374 return None;
1375 }
1376 prefix = error
1377 .named_child(0)
1378 .and_then(|child| recovered_base_atom(child, source));
1379 prefix.as_ref()?;
1380 }
1381 prefix
1382}
1383
1384fn recover_exported_class_function_definition<'tree>(
1385 node: Node<'tree>,
1386 source: &str,
1387) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
1388 if node.kind() != "function_definition" {
1389 return None;
1390 }
1391 let type_node = node.child_by_field_name("type")?;
1392 let declarator = node.child_by_field_name("declarator")?;
1393
1394 if matches!(
1395 type_node.kind(),
1396 "class_specifier" | "struct_specifier" | "union_specifier"
1397 ) {
1398 let type_name = type_node
1399 .child_by_field_name("name")
1400 .and_then(|name| direct_identifier_name(name, source));
1401 let exported_macro_type = type_name
1402 .as_ref()
1403 .is_some_and(|name| cpp_export_macro_token(name));
1404 if exported_macro_type {
1405 let mut cursor = node.walk();
1406 let errors_before_declarator = node
1407 .named_children(&mut cursor)
1408 .filter(|child| {
1409 child.kind() == "ERROR"
1410 && child.start_byte() >= type_node.end_byte()
1411 && child.end_byte() <= declarator.start_byte()
1412 })
1413 .collect::<Vec<_>>();
1414 if let Some(name) = errors_before_declarator
1415 .iter()
1416 .find_map(|error| displaced_exported_class_name(*error, source))
1417 {
1418 let raw_supertypes = errors_before_declarator
1419 .iter()
1420 .any(|error| malformed_inheritance_syntax(*error))
1421 .then(|| recovered_malformed_base_name(declarator, source))
1422 .flatten()
1423 .map(|base| vec![base]);
1424 return Some((node, name, raw_supertypes));
1425 }
1426 if errors_before_declarator
1427 .iter()
1428 .any(|error| malformed_inheritance_syntax(*error))
1429 {
1430 return None;
1431 }
1432 }
1433 if !exported_macro_type
1434 && let Some(name) = type_name
1435 && !cpp_export_macro_token(&name)
1436 && let Some(base) =
1437 recovered_postfix_export_macro_base(node, type_node, declarator, source)
1438 {
1439 return Some((node, name, Some(vec![base])));
1440 }
1441 if let Some(name) = direct_identifier_name(declarator, source)
1442 && exported_macro_type
1443 && !cpp_export_macro_token(&name)
1444 {
1445 let raw_supertypes = exported_macro_type
1446 .then(|| recovered_single_base_after_declarator(node, declarator, source))
1447 .flatten()
1448 .map(|base| vec![base]);
1449 return Some((node, name, raw_supertypes));
1450 }
1451 if declarator.kind() == "parenthesized_declarator"
1452 && type_node
1453 .child_by_field_name("name")
1454 .and_then(|name| direct_identifier_name(name, source))
1455 .is_some_and(|name| cpp_export_macro_token(&name))
1456 {
1457 let body_start = node
1458 .child_by_field_name("body")
1459 .map(|body| body.start_byte())
1460 .unwrap_or(node.end_byte());
1461 let mut cursor = node.walk();
1462 if let Some(name) = node
1463 .named_children(&mut cursor)
1464 .filter(|child| {
1465 child.kind() == "ERROR"
1466 && child.start_byte() >= declarator.end_byte()
1467 && child.end_byte() <= body_start
1468 })
1469 .find_map(|error| declarator_name_from_node(error, source))
1470 {
1471 return Some((node, name, None));
1472 }
1473 }
1474 }
1475
1476 let declarator_text = direct_identifier_name(declarator, source)?;
1477 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
1478 return None;
1479 }
1480 class_identifier_before_body(node, source).map(|name| (node, name, None))
1481}
1482
1483pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
1491 if !matches!(
1492 node.kind(),
1493 "qualified_identifier" | "scoped_type_identifier" | "template_type"
1494 ) {
1495 return false;
1496 }
1497 if let Some(function) = node.parent().filter(|parent| {
1498 parent.kind() == "function_definition"
1499 && parent
1500 .child_by_field_name("declarator")
1501 .is_some_and(|declarator| same_node(declarator, node))
1502 }) {
1503 return recover_exported_class_function_definition(function, source)
1504 .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
1505 }
1506 let Some(initializer) = node.parent().filter(|parent| {
1507 parent.kind() == "init_declarator"
1508 && parent
1509 .child_by_field_name("declarator")
1510 .is_some_and(|declarator| same_node(declarator, node))
1511 }) else {
1512 return false;
1513 };
1514 initializer
1515 .parent()
1516 .filter(|parent| parent.kind() == "declaration")
1517 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
1518 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
1519}
1520
1521struct CppSentinelReparsedClass<'tree> {
1527 declaration_node: Node<'tree>,
1528 name: String,
1529 body: Node<'tree>,
1530 raw_supertypes: Option<Vec<String>>,
1531}
1532
1533fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
1534 let mut cursor = root.walk();
1535 root.named_children(&mut cursor)
1536 .find(|child| child.kind() != "comment")
1537 .filter(|child| child.kind() == "template_declaration")
1538}
1539
1540fn cpp_sentinel_reparsed_class<'tree>(
1541 root: Node<'tree>,
1542 template_node: Option<Node<'tree>>,
1543 source: &str,
1544 ancestry: &ParentIndex<'tree>,
1545) -> Option<CppSentinelReparsedClass<'tree>> {
1546 let container = template_node.unwrap_or(root);
1547 let mut cursor = container.walk();
1548 for child in container.named_children(&mut cursor) {
1549 if matches!(
1550 child.kind(),
1551 "class_specifier" | "struct_specifier" | "union_specifier"
1552 ) {
1553 let name = class_like_name(child, source, ancestry)?;
1554 let body = cpp_body_node(child)?;
1555 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
1556 .then(|| extract_cpp_supertypes(child, source));
1557 return Some(CppSentinelReparsedClass {
1558 declaration_node: child,
1559 name,
1560 body,
1561 raw_supertypes,
1562 });
1563 }
1564 if child.kind() == "declaration"
1565 && let Some(class_node) = first_class_like_child(child)
1566 {
1567 let name = class_like_name(class_node, source, ancestry)?;
1568 let body = cpp_body_node(class_node)?;
1569 let raw_supertypes =
1570 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1571 .then(|| extract_cpp_supertypes(class_node, source));
1572 return Some(CppSentinelReparsedClass {
1573 declaration_node: class_node,
1574 name,
1575 body,
1576 raw_supertypes,
1577 });
1578 }
1579 if child.kind() == "function_definition"
1584 && let Some(class_node) = first_class_like_child(child)
1585 && let Some(body) = cpp_body_node(class_node)
1586 && let Some(name) = class_like_name(class_node, source, ancestry)
1587 {
1588 let raw_supertypes =
1589 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1590 .then(|| extract_cpp_supertypes(class_node, source));
1591 return Some(CppSentinelReparsedClass {
1592 declaration_node: class_node,
1593 name,
1594 body,
1595 raw_supertypes,
1596 });
1597 }
1598 if child.kind() == "function_definition"
1599 && let Some((_, name, raw_supertypes)) =
1600 recover_exported_class_function_definition(child, source)
1601 {
1602 let body = cpp_body_node(child)?;
1603 return Some(CppSentinelReparsedClass {
1604 declaration_node: child,
1605 name,
1606 body,
1607 raw_supertypes,
1608 });
1609 }
1610 }
1611 None
1612}
1613
1614fn recovered_postfix_export_macro_base(
1615 node: Node<'_>,
1616 type_node: Node<'_>,
1617 declarator: Node<'_>,
1618 source: &str,
1619) -> Option<String> {
1620 let mut cursor = node.walk();
1621 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
1622 child.kind() == "ERROR"
1623 && child.start_byte() >= type_node.end_byte()
1624 && child.end_byte() <= declarator.start_byte()
1625 && postfix_export_macro_inheritance(*child, source)
1626 });
1627 malformed_clauses.next()?;
1628 if malformed_clauses.next().is_some() {
1629 return None;
1630 }
1631 recovered_malformed_base_name(declarator, source)
1632}
1633
1634fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
1635 let mut macro_count = 0;
1636 let mut colon_count = 0;
1637 let mut access_count = 0;
1638 for index in 0..node.child_count() {
1639 let Some(child) = node.child(index) else {
1640 return false;
1641 };
1642 match child.kind() {
1643 "identifier" | "type_identifier" if child.is_named() => {
1644 let candidate = normalize_cpp_whitespace(node_text(child, source));
1645 if !cpp_export_macro_token(&candidate) {
1646 return false;
1647 }
1648 macro_count += 1;
1649 }
1650 ":" if !child.is_named() => colon_count += 1,
1651 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1652 _ => return false,
1653 }
1654 }
1655 macro_count == 1 && colon_count == 1 && access_count == 1
1656}
1657
1658fn recovered_single_base_after_declarator(
1659 node: Node<'_>,
1660 declarator: Node<'_>,
1661 source: &str,
1662) -> Option<String> {
1663 let body_start = node
1664 .child_by_field_name("body")
1665 .map(|body| body.start_byte())
1666 .unwrap_or(node.end_byte());
1667 let mut cursor = node.walk();
1668 let mut bases = node
1669 .named_children(&mut cursor)
1670 .filter(|child| {
1671 child.kind() == "ERROR"
1672 && child.start_byte() >= declarator.end_byte()
1673 && child.end_byte() <= body_start
1674 })
1675 .filter_map(|error| displaced_exported_class_name(error, source));
1676 let base = bases.next()?;
1677 bases.next().is_none().then_some(base)
1678}
1679
1680fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
1681 (0..node.child_count()).any(|index| {
1682 node.child(index)
1683 .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
1684 })
1685}
1686
1687pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
1688 recover_exported_class_function_definition(node, source).is_some()
1689}
1690
1691fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
1692 matches!(
1693 kind,
1694 "ERROR"
1695 | "preproc_if"
1696 | "preproc_ifdef"
1697 | "preproc_ifndef"
1698 | "preproc_else"
1699 | "preproc_elif"
1700 ) || (kind == "labeled_statement" && in_class_scope)
1701}
1702
1703pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
1704 if node.kind() != "declaration" {
1705 return false;
1706 }
1707 let mut ancestor = node.parent();
1708 while let Some(container) = ancestor {
1709 match container.kind() {
1710 "compound_statement" => {
1711 return container.parent().is_some_and(|class_container| {
1712 is_recovered_exported_class_container(class_container, source)
1713 });
1714 }
1715 "template_declaration" | "linkage_specification" | "declaration_list" => {}
1718 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
1719 _ => return false,
1720 }
1721 ancestor = container.parent();
1722 }
1723 false
1724}
1725
1726pub fn recovered_exported_class_has_body(
1727 node: Node<'_>,
1728 source: &str,
1729 expected_name: &str,
1730) -> Option<bool> {
1731 match node.kind() {
1732 "function_definition" => {
1733 let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
1734 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
1735 }
1736 "declaration" | "field_declaration" => {
1737 let recovered = recover_exported_class_declaration(node, source)?;
1738 (recovered.name == expected_name).then(|| recovered.body.is_some())
1739 }
1740 _ => None,
1741 }
1742}
1743
1744fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
1745 let body_start = node
1746 .child_by_field_name("body")
1747 .map(|body| body.start_byte())
1748 .unwrap_or(node.end_byte());
1749 let mut stack = Vec::new();
1750 for index in (0..node.named_child_count()).rev() {
1751 let Some(child) = node.named_child(index) else {
1752 continue;
1753 };
1754 if child.start_byte() >= body_start {
1755 continue;
1756 }
1757 stack.push(child);
1758 }
1759
1760 let mut best = None;
1761 while let Some(current) = stack.pop() {
1762 if matches!(current.kind(), "identifier" | "type_identifier") {
1763 let name = normalize_cpp_whitespace(node_text(current, source));
1764 if !name.is_empty()
1765 && !cpp_export_macro_token(&name)
1766 && !matches!(name.as_str(), "class" | "struct" | "union")
1767 {
1768 best = Some(name);
1769 }
1770 continue;
1771 }
1772
1773 for index in (0..current.named_child_count()).rev() {
1774 if let Some(child) = current.named_child(index)
1775 && child.start_byte() < body_start
1776 {
1777 stack.push(child);
1778 }
1779 }
1780 }
1781 best
1782}
1783
1784fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1785 if node.kind() == "declaration"
1786 && node
1787 .child_by_field_name("type")
1788 .or_else(|| first_class_like_child(node))
1789 .is_some_and(|type_node| {
1790 matches!(
1791 type_node.kind(),
1792 "class_specifier" | "struct_specifier" | "union_specifier"
1793 )
1794 })
1795 && let Some(name) = node
1796 .child_by_field_name("declarator")
1797 .and_then(|declarator| declarator_name_from_node(declarator, source))
1798 && !cpp_export_macro_token(&name)
1799 {
1800 return Some(name);
1801 }
1802
1803 if node.kind() == "function_definition"
1804 && node.child_by_field_name("type").is_some_and(|type_node| {
1805 matches!(
1806 type_node.kind(),
1807 "class_specifier" | "struct_specifier" | "union_specifier"
1808 )
1809 })
1810 && let Some(name) = node
1811 .child_by_field_name("declarator")
1812 .and_then(|declarator| direct_identifier_name(declarator, source))
1813 && !cpp_export_macro_token(&name)
1814 {
1815 return Some(name);
1816 }
1817
1818 let class_node = if matches!(
1819 node.kind(),
1820 "class_specifier" | "struct_specifier" | "union_specifier"
1821 ) {
1822 node
1823 } else {
1824 first_class_like_child(node)?
1825 };
1826 class_like_name_from_children(class_node, source)
1827}
1828
1829fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
1830 if !matches!(
1831 node.kind(),
1832 "identifier" | "field_identifier" | "type_identifier"
1833 ) {
1834 return None;
1835 }
1836 let name = normalize_cpp_whitespace(node_text(node, source));
1837 (!name.is_empty()).then_some(name)
1838}
1839
1840fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1841 match node.kind() {
1842 "identifier" | "field_identifier" | "type_identifier" => {
1843 let name = normalize_cpp_whitespace(node_text(node, source));
1844 (!name.is_empty()).then_some(name)
1845 }
1846 _ => {
1847 let mut cursor = node.walk();
1848 node.named_children(&mut cursor)
1849 .find_map(|child| declarator_name_from_node(child, source))
1850 }
1851 }
1852}
1853
1854fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
1855 let mut cursor = node.walk();
1856 node.named_children(&mut cursor).find(|child| {
1857 matches!(
1858 child.kind(),
1859 "class_specifier" | "struct_specifier" | "union_specifier"
1860 )
1861 })
1862}
1863
1864fn push_cpp_container_work<'tree>(
1869 node: Node<'tree>,
1870 scope: ScopeInfo,
1871 stack: &mut Vec<CppWork<'tree>>,
1872) {
1873 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
1874}
1875
1876fn push_cpp_sibling_range<'tree>(
1880 parent: Node<'tree>,
1881 start_index: usize,
1882 end_index: usize,
1883 scope: ScopeInfo,
1884 stack: &mut Vec<CppWork<'tree>>,
1885) {
1886 let mut cursor = parent.walk();
1887 let children = parent
1888 .named_children(&mut cursor)
1889 .skip(start_index)
1890 .take(end_index.saturating_sub(start_index))
1891 .collect::<Vec<_>>()
1892 .into_iter();
1893 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
1894}
1895
1896fn advance_cpp_siblings<'tree>(
1904 mut siblings: CppSiblingsWork<'tree>,
1905 source: &str,
1906 stack: &mut Vec<CppWork<'tree>>,
1907) {
1908 let Some(child) = siblings.children.next() else {
1909 return;
1910 };
1911 let current_scope = siblings.scope.clone();
1912 if let Some(namespace) = cpp_using_namespace_target(child, source) {
1913 siblings.scope.visible_using_namespaces.push(namespace);
1914 }
1915 if !siblings.children.as_slice().is_empty() {
1916 stack.push(CppWork::Siblings(siblings));
1917 }
1918 stack.push(CppWork::Node(CppNodeWork {
1919 node: child,
1920 scope: current_scope,
1921 }));
1922}
1923
1924fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
1931 if node.kind() != "using_declaration" {
1932 return None;
1933 }
1934 let mut cursor = node.walk();
1935 let is_namespace_directive = node
1936 .children(&mut cursor)
1937 .any(|child| child.kind() == "namespace");
1938 if !is_namespace_directive {
1939 return None;
1940 }
1941 let target = node.named_child(0)?;
1942 let start = target
1950 .child(0)
1951 .filter(|child| !child.is_named() && child.kind() == "::")
1952 .map_or(target.start_byte(), |marker| marker.end_byte());
1953 let text = normalize_cpp_whitespace(
1954 source
1955 .get(start..target.end_byte())
1956 .expect("using-directive target covers one source range"),
1957 );
1958 (!text.is_empty()).then_some(text)
1959}
1960
1961pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
1974 let mut parser = Parser::new();
1975 if parser
1976 .set_language(&tree_sitter_cpp::LANGUAGE.into())
1977 .is_err()
1978 {
1979 return Vec::new();
1980 }
1981 let Some(tree) = parser.parse(source, None) else {
1982 return Vec::new();
1983 };
1984 let mut namespaces = Vec::new();
1985 let mut seen = std::collections::HashSet::new();
1986 let mut stack = vec![tree.root_node()];
1987 while let Some(node) = stack.pop() {
1988 if let Some(namespace) = cpp_using_namespace_target(node, source)
1989 && seen.insert(namespace.clone())
1990 {
1991 namespaces.push(namespace);
1992 }
1993 let mut cursor = node.walk();
1994 stack.extend(node.named_children(&mut cursor));
1995 }
1996 namespaces
1997}
1998
1999pub struct CppVisitor<'a> {
2000 pub file: &'a ProjectFile,
2001 pub source: &'a str,
2002 pub parsed: &'a mut ParsedFile,
2003 pub c_tag_semantics: bool,
2014 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
2015 pub consumed_fragment_regions: Vec<(usize, usize)>,
2024}
2025
2026impl<'a> CppVisitor<'a> {
2027 #[allow(clippy::too_many_arguments)]
2028 pub fn visit_container(
2029 &mut self,
2030 node: Node<'_>,
2031 package_name: &str,
2032 module: Option<CodeUnit>,
2033 class_unit: Option<CodeUnit>,
2034 template_signature: Option<String>,
2035 visible_using_namespaces: Vec<String>,
2036 ) {
2037 let scope = ScopeInfo {
2038 package_name: package_name.to_string(),
2039 module,
2040 class_unit,
2041 template_signature,
2042 template_metadata: None,
2043 declarations_are_fields: false,
2044 recovered_specialization_member_scope: false,
2045 visible_using_namespaces,
2046 };
2047 self.run_container_work(node, scope);
2048 }
2049
2050 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
2054 self.consumed_fragment_regions
2055 .iter()
2056 .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
2057 }
2058
2059 fn run_container_work<'tree>(&mut self, node: Node<'tree>, scope: ScopeInfo) {
2063 let mut root = node;
2071 while let Some(parent) = root.parent() {
2072 root = parent;
2073 }
2074 let ancestry = ParentIndex::new(root);
2075 let mut stack = vec![CppWork::Container(CppContainer { node, scope })];
2076 while let Some(work) = stack.pop() {
2077 match work {
2078 CppWork::Container(container) => {
2079 push_cpp_container_work(container.node, container.scope, &mut stack);
2080 }
2081 CppWork::Siblings(siblings) => {
2082 advance_cpp_siblings(siblings, self.source, &mut stack);
2083 }
2084 CppWork::Node(work) => {
2085 if self.node_is_inside_consumed_fragment(work.node) {
2086 continue;
2087 }
2088 self.visit_node(work.node, &work.scope, &mut stack, &ancestry);
2089 }
2090 }
2091 }
2092 }
2093
2094 fn reparse_fragmented_export_class_members(
2099 &self,
2100 fragmented: &FragmentedExportBody,
2101 class_name: &str,
2102 ) -> Option<FragmentedExportMembers> {
2103 if fragmented.reparse_start >= fragmented.reparse_end {
2104 return None;
2105 }
2106 let tree = cpp_reparse_fragmented_class_body(
2107 self.source,
2108 fragmented.reparse_start,
2109 fragmented.reparse_end,
2110 )?;
2111 if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
2112 return Some(FragmentedExportMembers::Complete(tree));
2113 }
2114 let has_conditional_constructor = {
2115 let root = tree.root_node();
2116 let mut cursor = root.walk();
2117 root.named_children(&mut cursor).any(|child| {
2118 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
2119 })
2120 };
2121 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
2122 }
2123
2124 fn visit_fragmented_export_class_members(
2127 &mut self,
2128 outcome: FragmentedExportMembers,
2129 class_unit: CodeUnit,
2130 scope: &ScopeInfo,
2131 ) -> bool {
2132 let (tree, complete) = match outcome {
2133 FragmentedExportMembers::Complete(tree) => (tree, true),
2134 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
2135 };
2136 let root = tree.root_node();
2137 let class_name = class_unit.identifier().to_string();
2138 let member_scope = ScopeInfo {
2139 package_name: class_unit.package_name().to_string(),
2144 module: scope.module.clone(),
2145 class_unit: Some(class_unit),
2146 template_signature: scope.template_signature.clone(),
2147 template_metadata: None,
2148 declarations_are_fields: true,
2149 recovered_specialization_member_scope: false,
2150 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2151 };
2152 if !complete {
2153 let mut cursor = root.walk();
2159 let constructors = root
2160 .named_children(&mut cursor)
2161 .filter_map(|child| {
2162 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
2163 })
2164 .collect::<Vec<_>>();
2165 let reparsed_ancestry = ParentIndex::new(root);
2168 for constructor in constructors {
2169 let mut stack = Vec::new();
2170 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
2171 while let Some(work) = stack.pop() {
2172 match work {
2173 CppWork::Container(container) => {
2174 push_cpp_container_work(container.node, container.scope, &mut stack);
2175 }
2176 CppWork::Siblings(siblings) => {
2177 advance_cpp_siblings(siblings, self.source, &mut stack);
2178 }
2179 CppWork::Node(work) => {
2180 self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
2181 }
2182 }
2183 }
2184 }
2185 return false;
2186 }
2187 self.run_container_work(root, member_scope);
2188 true
2189 }
2190
2191 fn visit_recovered_fragment_constructor<'tree>(
2192 &mut self,
2193 range: std::ops::Range<usize>,
2194 constructor_body: Node<'tree>,
2195 class_declaration: Node<'tree>,
2196 class_unit: &CodeUnit,
2197 scope: &ScopeInfo,
2198 ancestry: &ParentIndex<'tree>,
2199 ) {
2200 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
2201 return;
2202 };
2203 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
2204 tree.root_node(),
2205 range.start,
2206 class_unit.identifier(),
2207 self.source,
2208 ) else {
2209 return;
2210 };
2211 let member_scope = ScopeInfo {
2212 package_name: class_unit.package_name().to_string(),
2213 module: scope.module.clone(),
2214 class_unit: Some(class_unit.clone()),
2215 template_signature: scope.template_signature.clone(),
2216 template_metadata: None,
2217 declarations_are_fields: true,
2218 recovered_specialization_member_scope: false,
2219 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2220 };
2221 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
2222 else {
2223 return;
2224 };
2225 debug_assert_eq!(function.name, class_unit.identifier());
2226 let code_unit = function.code_unit(self.file.clone());
2227 self.parsed.add_code_unit_with_range(
2228 code_unit.clone(),
2229 Range {
2230 start_byte: function_declarator.start_byte(),
2231 end_byte: constructor_body.end_byte(),
2232 start_line: function_declarator.start_position().row + 1,
2233 end_line: constructor_body.end_position().row + 1,
2234 },
2235 None,
2236 None,
2237 );
2238 self.parsed.add_signature_with_metadata(
2239 code_unit.clone(),
2240 cpp_signature_metadata(
2241 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
2242 function_declarator,
2243 self.source,
2244 ancestry,
2245 )
2246 .with_declaration_only(false)
2247 .with_callable_linkage(cpp_callable_linkage(
2248 class_declaration,
2249 self.source,
2250 ancestry,
2251 )),
2252 );
2253 self.parsed.add_child(class_unit.clone(), code_unit);
2254 }
2255
2256 fn visit_recovered_fragment_prefix_members<'tree>(
2257 &mut self,
2258 root: Node<'tree>,
2259 constructor_start: usize,
2260 class_unit: &CodeUnit,
2261 scope: &ScopeInfo,
2262 ancestry: &ParentIndex<'tree>,
2263 ) {
2264 let member_scope = ScopeInfo {
2265 package_name: class_unit.package_name().to_string(),
2266 module: scope.module.clone(),
2267 class_unit: Some(class_unit.clone()),
2268 template_signature: scope.template_signature.clone(),
2269 template_metadata: None,
2270 declarations_are_fields: true,
2271 recovered_specialization_member_scope: false,
2272 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2273 };
2274 let mut stack = vec![root];
2275 while let Some(current) = stack.pop() {
2276 if current.kind() == "comment" || current.start_byte() >= constructor_start {
2277 continue;
2278 }
2279 if current.end_byte() <= constructor_start
2280 && current.kind() != "translation_unit"
2281 && current.kind() != "labeled_statement"
2282 && current.kind() != "ERROR"
2283 {
2284 let mut work_stack = Vec::new();
2285 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
2286 while let Some(work) = work_stack.pop() {
2287 match work {
2288 CppWork::Container(container) => {
2289 push_cpp_container_work(
2290 container.node,
2291 container.scope,
2292 &mut work_stack,
2293 );
2294 }
2295 CppWork::Siblings(siblings) => {
2296 advance_cpp_siblings(siblings, self.source, &mut work_stack);
2297 }
2298 CppWork::Node(work) => {
2299 self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
2300 }
2301 }
2302 }
2303 continue;
2304 }
2305 if matches!(
2306 current.kind(),
2307 "translation_unit" | "labeled_statement" | "ERROR"
2308 ) {
2309 let mut cursor = current.walk();
2310 stack.extend(current.named_children(&mut cursor));
2311 }
2312 }
2313 }
2314
2315 fn visit_node<'tree>(
2316 &mut self,
2317 node: Node<'tree>,
2318 scope: &ScopeInfo,
2319 stack: &mut Vec<CppWork<'tree>>,
2320 ancestry: &ParentIndex<'tree>,
2321 ) {
2322 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
2323 self.visit_node(node, &recovered_scope, stack, ancestry);
2324 return;
2325 }
2326 if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
2327 {
2328 let displaced_namespace_items =
2329 displaced_fragment_namespace_geometry(node, self.source)
2330 .map(|boundary| boundary.namespace_items)
2331 .unwrap_or_default();
2332 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
2333 let mut class_stack = Vec::new();
2334 let parser_visible_body =
2337 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
2338 .then(|| cpp_body_node(class_node))
2339 .flatten();
2340 let class_unit = self.visit_named_class_like_shape(
2341 class_node,
2342 name,
2343 parser_visible_body,
2344 true,
2345 Some(fragmented.class_range),
2346 Some(extract_cpp_supertypes(class_node, self.source)),
2347 scope,
2348 &mut class_stack,
2349 ancestry,
2350 );
2351 let member_scope = ScopeInfo {
2352 package_name: class_unit.package_name().to_string(),
2353 module: scope.module.clone(),
2354 class_unit: Some(class_unit.clone()),
2355 template_signature: scope.template_signature.clone(),
2356 template_metadata: None,
2357 declarations_are_fields: true,
2358 recovered_specialization_member_scope: false,
2359 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2360 };
2361 let complete = outcome.is_some_and(|outcome| {
2362 self.visit_fragmented_export_class_members(outcome, class_unit, scope)
2363 });
2364 if complete {
2365 self.consumed_fragment_regions
2366 .push((node.start_byte(), fragmented.class_range.end_byte));
2367 } else {
2368 for candidate in cpp_following_named_siblings(node, self.source) {
2378 if candidate.start_byte() >= fragmented.reparse_end {
2379 break;
2380 }
2381 if cpp_fragment_sibling_is_class_member(
2382 candidate,
2383 fragmented.reparse_end,
2384 self.source,
2385 ) {
2386 self.recovered_class_sibling_scopes
2387 .insert(candidate.id(), member_scope.clone());
2388 }
2389 }
2390 }
2391 for item in displaced_namespace_items {
2392 self.recovered_class_sibling_scopes
2393 .insert(item.id(), scope.clone());
2394 }
2395 stack.extend(class_stack);
2396 return;
2397 }
2398 match node.kind() {
2399 "template_declaration" => {
2400 if let Some(recovered) =
2401 recover_fragmented_preprocessor_class(node, self.source, ancestry)
2402 {
2403 let mut template_scope = scope.clone();
2404 template_scope.template_signature =
2405 cpp_template_signature(node, recovered.declaration_node, self.source);
2406 template_scope.template_metadata =
2407 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
2408 let raw_supertypes =
2409 Some(extract_cpp_supertypes(recovered.class_node, self.source));
2410 let mut class_stack = Vec::new();
2411 let class_unit = self.visit_named_class_like_shape(
2412 recovered.class_node,
2413 recovered.name,
2414 Some(recovered.body),
2415 true,
2416 Some(recovered.range),
2417 raw_supertypes,
2418 &template_scope,
2419 &mut class_stack,
2420 ancestry,
2421 );
2422 self.parsed.record_materialization(
2423 MaterializationRecord::RecoveredDeclaration {
2424 recovery: recovered.range,
2425 unit: class_unit.clone(),
2426 },
2427 );
2428 let member_scope = ScopeInfo {
2429 package_name: template_scope.package_name.clone(),
2430 module: template_scope.module.clone(),
2431 class_unit: Some(class_unit.clone()),
2432 template_signature: template_scope.template_signature.clone(),
2433 template_metadata: None,
2434 declarations_are_fields: true,
2435 recovered_specialization_member_scope: recovered
2436 .class_node
2437 .child_by_field_name("name")
2438 .is_some_and(|name| name.kind() == "template_type"),
2439 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
2440 };
2441 for tail_member in recovered.tail_members.into_iter().rev() {
2442 stack.push(CppWork::Node(CppNodeWork {
2443 node: tail_member,
2444 scope: member_scope.clone(),
2445 }));
2446 }
2447 stack.extend(class_stack);
2448 for sibling in recovered.member_siblings {
2449 self.recovered_class_sibling_scopes
2450 .insert(sibling.id(), member_scope.clone());
2451 }
2452 return;
2453 }
2454 for index in (0..node.named_child_count()).rev() {
2455 let Some(child) = node.named_child(index) else {
2456 continue;
2457 };
2458 if matches!(
2459 child.kind(),
2460 "class_specifier"
2461 | "struct_specifier"
2462 | "union_specifier"
2463 | "enum_specifier"
2464 | "function_definition"
2465 | "declaration"
2466 | "field_declaration"
2467 | "alias_declaration"
2468 | "namespace_definition"
2469 ) {
2470 let mut template_scope = scope.clone();
2471 template_scope.template_signature =
2472 cpp_template_signature(node, child, self.source);
2473 template_scope.template_metadata =
2474 cpp_template_metadata(node, child, self.source, ancestry);
2475 if let Some(recovered) = recover_fragmented_partial_specialization(
2476 node,
2477 child,
2478 self.source,
2479 ancestry,
2480 ) {
2481 let code_unit = self.visit_named_class_like_shape(
2482 recovered.declaration_node,
2483 recovered.name,
2484 None,
2485 true,
2486 Some(recovered.range),
2487 None,
2488 &template_scope,
2489 stack,
2490 ancestry,
2491 );
2492 self.parsed.record_materialization(
2493 MaterializationRecord::RecoveredDeclaration {
2494 recovery: recovered.range,
2495 unit: code_unit.clone(),
2496 },
2497 );
2498 let mut member_scope = template_scope.clone();
2499 member_scope.class_unit = Some(code_unit);
2500 member_scope.declarations_are_fields = true;
2501 member_scope.recovered_specialization_member_scope = true;
2502 for prefix_member in recovered.prefix_members.into_iter().rev() {
2503 stack.push(CppWork::Node(CppNodeWork {
2504 node: prefix_member,
2505 scope: member_scope.clone(),
2506 }));
2507 }
2508 for sibling in recovered.member_siblings {
2509 self.recovered_class_sibling_scopes
2510 .insert(sibling.id(), member_scope.clone());
2511 }
2512 for following in recovered.following_declarations.into_iter().rev() {
2513 stack.push(CppWork::Node(CppNodeWork {
2514 node: following,
2515 scope: scope.clone(),
2516 }));
2517 }
2518 return;
2519 }
2520 stack.push(CppWork::Node(CppNodeWork {
2521 node: child,
2522 scope: template_scope,
2523 }));
2524 }
2525 }
2526 }
2527 "namespace_definition" => self.visit_namespace(node, scope, stack),
2528 "linkage_specification" => {
2529 if let Some(body) = cpp_body_node(node) {
2530 stack.push(CppWork::Container(CppContainer {
2531 node: body,
2532 scope: scope.clone(),
2533 }));
2534 } else {
2535 stack.push(CppWork::Container(CppContainer {
2536 node,
2537 scope: scope.clone(),
2538 }));
2539 }
2540 }
2541 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
2542 self.visit_class_like(node, scope, stack, ancestry)
2543 }
2544 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
2545 "ERROR" => {
2552 if !self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
2553 self.visit_macro_swallowed_function_declarations(node, scope);
2554 stack.push(CppWork::Container(CppContainer {
2555 node,
2556 scope: scope.clone(),
2557 }));
2558 }
2559 }
2560 "declaration" => {
2561 if scope.class_unit.is_some()
2562 && scope.declarations_are_fields
2563 && scope.recovered_specialization_member_scope
2564 && let Some(alias_name) =
2565 recovered_using_declaration_alias_name(node, self.source)
2566 {
2567 self.add_type_aliases(node, scope, vec![alias_name]);
2568 } else {
2569 self.visit_declaration(
2570 node,
2571 scope,
2572 scope.declarations_are_fields,
2573 stack,
2574 ancestry,
2575 )
2576 }
2577 }
2578 "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
2579 "type_definition" | "alias_declaration" => {
2580 self.visit_type_declaration(node, scope, stack, ancestry)
2581 }
2582 "preproc_def" | "preproc_function_def" => self.visit_macro(node),
2583 "preproc_include" => self.visit_include(node),
2584 kind if preserves_declaration_scope_through_wrapper(
2585 kind,
2586 scope.class_unit.is_some(),
2587 ) =>
2588 {
2589 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
2595 let mut range = cpp_declaration_range(node);
2596 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
2597 range.end_byte = boundary.end_byte;
2598 range.end_line = boundary.end_line;
2599 }
2600 self.parsed.record_materialization(
2601 MaterializationRecord::ConfigurationConditional { range },
2602 );
2603 }
2604 stack.push(CppWork::Container(CppContainer {
2605 node,
2606 scope: scope.clone(),
2607 }))
2608 }
2609 _ => {}
2610 }
2611 }
2612
2613 fn visit_macro_swallowed_function_declarations<'tree>(
2614 &mut self,
2615 envelope: Node<'tree>,
2616 scope: &ScopeInfo,
2617 ) {
2618 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
2619 || envelope.kind() == "ERROR"
2620 && envelope
2621 .parent()
2622 .is_some_and(|parent| parent.kind() == "ERROR")
2623 {
2624 return;
2625 }
2626 let mut stack = (0..envelope.named_child_count())
2627 .filter_map(|index| envelope.named_child(index))
2628 .collect::<Vec<_>>();
2629 while let Some(node) = stack.pop() {
2630 if node.kind() == "function_declarator" {
2631 self.visit_error_swallowed_function_declaration(node, scope);
2632 }
2633 for index in 0..node.named_child_count() {
2634 if let Some(child) = node.named_child(index) {
2635 stack.push(child);
2636 }
2637 }
2638 }
2639 }
2640
2641 fn visit_error_swallowed_function_declaration<'tree>(
2642 &mut self,
2643 node: Node<'tree>,
2644 scope: &ScopeInfo,
2645 ) -> bool {
2646 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
2647 return false;
2648 };
2649 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
2650 return false;
2651 };
2652 let root = tree.root_node();
2653 let mut cursor = root.walk();
2654 let declarations = root
2655 .named_children(&mut cursor)
2656 .filter(|child| child.kind() != "comment")
2657 .collect::<Vec<_>>();
2658 let [declaration] = declarations.as_slice() else {
2659 return false;
2660 };
2661 if declaration.kind() != "declaration"
2662 || declaration.has_error()
2663 || declaration.start_byte() != start
2664 || declaration.end_byte() != end
2665 {
2666 return false;
2667 }
2668 let recovery = cpp_recovery_window(self.source, start, end);
2669 self.record_recovered_declarations(recovery, |visitor| {
2670 visitor.run_container_work(root, scope.clone());
2671 });
2672 true
2673 }
2674
2675 fn visit_namespace<'tree>(
2676 &mut self,
2677 node: Node<'tree>,
2678 scope: &ScopeInfo,
2679 stack: &mut Vec<CppWork<'tree>>,
2680 ) {
2681 let name_node = node.child_by_field_name("name");
2682 let Some(name_node) = name_node else {
2683 if let Some(body) = cpp_body_node(node) {
2684 stack.push(CppWork::Container(CppContainer {
2685 node: body,
2686 scope: scope.clone(),
2687 }));
2688 }
2689 return;
2690 };
2691 let explicitly_global = name_node
2698 .child(0)
2699 .is_some_and(|child| !child.is_named() && child.kind() == "::");
2700 let components = cpp_namespace_name_components(name_node, self.source);
2701 if components.is_empty() {
2702 return;
2703 }
2704 let mut package_name = if explicitly_global {
2710 String::new()
2711 } else {
2712 scope.package_name.clone()
2713 };
2714 let mut module = None;
2715 for component in components {
2716 let full_name = if package_name.is_empty() {
2717 component
2718 } else {
2719 format!("{package_name}::{component}")
2720 };
2721 let level = CodeUnit::new_fq(
2722 self.file.clone(),
2723 CodeUnitType::Module,
2724 "",
2725 full_name.clone(),
2726 cpp_namespace_fq(&full_name),
2727 );
2728 if !self.parsed.contains_declaration(&level) {
2729 self.parsed
2730 .add_code_unit(level.clone(), node, self.source, None, None);
2731 }
2732 package_name = full_name;
2733 module = Some(level);
2734 }
2735
2736 let namespace_scope = ScopeInfo {
2737 package_name,
2738 module,
2739 class_unit: None,
2747 template_signature: scope.template_signature.clone(),
2748 template_metadata: scope.template_metadata.clone(),
2749 declarations_are_fields: false,
2750 recovered_specialization_member_scope: false,
2751 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2752 };
2753 let container = cpp_body_node(node).unwrap_or(node);
2754 stack.push(CppWork::Container(CppContainer {
2755 node: container,
2756 scope: namespace_scope,
2757 }));
2758 }
2759
2760 fn visit_class_like<'tree>(
2761 &mut self,
2762 node: Node<'tree>,
2763 scope: &ScopeInfo,
2764 stack: &mut Vec<CppWork<'tree>>,
2765 ancestry: &ParentIndex<'tree>,
2766 ) {
2767 let Some(name) = class_like_name(node, self.source, ancestry) else {
2768 return;
2769 };
2770 let name = qualified_class_name_chain(node, self.source, scope)
2771 .map(|chain| chain.join("$"))
2772 .unwrap_or(name);
2773 self.visit_named_class_like(node, name, scope, stack, ancestry);
2774 }
2775
2776 fn visit_named_class_like<'tree>(
2777 &mut self,
2778 node: Node<'tree>,
2779 name: String,
2780 scope: &ScopeInfo,
2781 stack: &mut Vec<CppWork<'tree>>,
2782 ancestry: &ParentIndex<'tree>,
2783 ) {
2784 let body = cpp_body_node(node);
2785 let definition_body_present = body.is_some();
2786 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
2787 .then(|| extract_cpp_supertypes(node, self.source));
2788 self.visit_named_class_like_shape(
2789 node,
2790 name,
2791 body,
2792 definition_body_present,
2793 None,
2794 raw_supertypes,
2795 scope,
2796 stack,
2797 ancestry,
2798 );
2799 }
2800
2801 fn mints_tag_at_enclosing_c_scope(
2809 &self,
2810 declaration_node: Node<'_>,
2811 scope: &ScopeInfo,
2812 ancestry: &ParentIndex<'_>,
2813 ) -> bool {
2814 self.c_tag_semantics
2815 && scope.class_unit.is_some()
2816 && class_like_name(declaration_node, self.source, ancestry).is_some()
2817 && matches!(
2818 declaration_node.kind(),
2819 "struct_specifier" | "union_specifier" | "enum_specifier"
2820 )
2821 }
2822
2823 #[allow(clippy::too_many_arguments)]
2824 fn visit_named_class_like_shape<'tree>(
2825 &mut self,
2826 declaration_node: Node<'tree>,
2827 name: String,
2828 body: Option<Node<'tree>>,
2829 definition_body_present: bool,
2830 explicit_range: Option<Range>,
2831 raw_supertypes: Option<Vec<String>>,
2832 scope: &ScopeInfo,
2833 stack: &mut Vec<CppWork<'tree>>,
2834 ancestry: &ParentIndex<'tree>,
2835 ) -> CodeUnit {
2836 let displaced_macro_tail = if explicit_range.is_none() {
2837 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
2838 } else {
2839 None
2840 };
2841 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
2842 let recovered_scope = self.scope_for_recovered_exported_class(
2843 declaration_node,
2844 &name,
2845 definition_body_present,
2846 scope,
2847 ancestry,
2848 );
2849 let c_tag_scope;
2859 let scope =
2860 if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
2861 c_tag_scope = ScopeInfo {
2862 class_unit: None,
2863 ..recovered_scope.clone()
2864 };
2865 &c_tag_scope
2866 } else {
2867 &recovered_scope
2868 };
2869 let short_name = if let Some(parent) = &scope.class_unit {
2870 cpp_join_nested_short(parent.short_name(), &name)
2871 } else {
2872 name.clone()
2873 };
2874 let qualified_chain = if scope.class_unit.is_none() {
2881 qualified_class_name_chain(declaration_node, self.source, scope)
2882 .filter(|chain| chain.join("$") == name)
2883 } else {
2884 None
2885 };
2886 let fq = if let Some(chain) = qualified_chain {
2887 let mut fq = FqName::new();
2888 cpp_push_package(&mut fq, &scope.package_name);
2889 let mut first = true;
2890 for component in chain {
2891 let kind = if first {
2892 SegmentKind::Type
2893 } else {
2894 SegmentKind::Nested
2895 };
2896 fq.push(cpp_segment(&component, kind));
2897 first = false;
2898 }
2899 fq
2900 } else {
2901 cpp_leaf_fq(
2902 &scope.package_name,
2903 scope.class_unit.as_ref(),
2904 &name,
2905 SegmentKind::Nested,
2906 SegmentKind::Type,
2907 )
2908 };
2909 let code_unit = CodeUnit::with_signature_and_fq(
2910 self.file.clone(),
2911 CodeUnitType::Class,
2912 scope.package_name.clone(),
2913 short_name,
2914 scope.template_signature.clone(),
2915 false,
2916 fq,
2917 );
2918 let has_body = definition_body_present;
2919 if !has_body && self.parsed.contains_declaration(&code_unit) {
2920 self.parsed.record_navigation_range(
2921 code_unit.clone(),
2922 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
2923 );
2924 return code_unit;
2925 }
2926 if has_body {
2927 if let Some(range) = explicit_range {
2928 self.parsed.replace_code_unit_with_range_deferred(
2929 code_unit.clone(),
2930 range,
2931 None,
2932 None,
2933 );
2934 } else {
2935 self.parsed.replace_code_unit_deferred(
2936 code_unit.clone(),
2937 declaration_node,
2938 self.source,
2939 None,
2940 None,
2941 );
2942 }
2943 } else {
2944 self.parsed
2945 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
2946 }
2947 if let Some(raw_supertypes) = raw_supertypes {
2948 self.parsed
2949 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
2950 }
2951 self.parsed.add_signature(
2952 code_unit.clone(),
2953 render_cpp_type_signature(
2954 declaration_node,
2955 self.source,
2956 scope.template_signature.as_deref(),
2957 ),
2958 );
2959 if let Some(metadata) = &scope.template_metadata {
2960 let primary_short_name = if let Some(parent) = &scope.class_unit {
2961 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
2962 } else {
2963 metadata.primary_name.clone()
2964 };
2965 let primary_fq_name = CodeUnit::new(
2966 self.file.clone(),
2967 CodeUnitType::Class,
2968 scope.package_name.clone(),
2969 primary_short_name,
2970 )
2971 .fq_name();
2972 let mut metadata = metadata.clone();
2973 metadata.primary_fq_name = primary_fq_name;
2974 self.parsed
2975 .set_cpp_template_metadata(code_unit.clone(), metadata);
2976 }
2977 if let Some(parent) = &scope.class_unit {
2978 self.parsed.add_child(parent.clone(), code_unit.clone());
2979 } else if let Some(module) = &scope.module {
2980 self.parsed.add_child(module.clone(), code_unit.clone());
2981 }
2982
2983 if let Some(body) = body {
2984 let mut nested_scope = scope.clone();
2985 nested_scope.class_unit = Some(code_unit.clone());
2986 nested_scope.template_signature = scope.template_signature.clone();
2987 nested_scope.template_metadata = None;
2992 nested_scope.recovered_specialization_member_scope =
2995 scope.template_metadata.as_ref().is_some_and(|metadata| {
2996 declaration_node.kind() == "function_definition" && metadata.is_specialization()
2997 });
2998 nested_scope.declarations_are_fields =
2999 is_recovered_exported_class_container(declaration_node, self.source)
3000 || nested_scope.recovered_specialization_member_scope;
3001 if let Some(displaced) = displaced_macro_tail {
3002 push_cpp_sibling_range(
3010 body,
3011 displaced.split_index,
3012 usize::MAX,
3013 scope.clone(),
3014 stack,
3015 );
3016 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
3017 } else {
3018 stack.push(CppWork::Container(CppContainer {
3019 node: body,
3020 scope: nested_scope,
3021 }));
3022 }
3023 }
3024 if declaration_node.kind() == "enum_specifier" {
3025 self.visit_enum_enumerators(declaration_node, scope, &code_unit);
3026 if !self.has_enum_enumerator_units(&code_unit) {
3027 self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
3028 }
3029 }
3030 code_unit
3031 }
3032
3033 fn has_enum_enumerator_units(&self, parent: &CodeUnit) -> bool {
3034 let prefix = format!("{}.", parent.short_name());
3035 let parent_short = parent.short_name();
3036 self.parsed.declarations().iter().any(|unit| {
3037 unit.kind() == CodeUnitType::Field
3038 && unit.source() == parent.source()
3039 && unit.package_name() == parent.package_name()
3040 && if parent_short.is_empty() {
3041 !unit.short_name().contains(['.', '$'])
3045 } else {
3046 unit.short_name().starts_with(&prefix)
3047 }
3048 })
3049 }
3050
3051 fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
3052 walk_named_tree_preorder(node, false, |child| {
3053 if child.kind() != "enumerator" {
3054 return WalkControl::Continue;
3055 }
3056 let Some(name_node) = child.child_by_field_name("name") else {
3057 return WalkControl::Continue;
3058 };
3059 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
3060 if name.is_empty() {
3061 return WalkControl::Continue;
3062 }
3063 let code_unit = CodeUnit::new_fq(
3064 self.file.clone(),
3065 CodeUnitType::Field,
3066 scope.package_name.clone(),
3067 cpp_join_member_short(parent.short_name(), &name),
3068 parent
3069 .fq()
3070 .clone()
3071 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
3072 );
3073 if self.parsed.contains_declaration(&code_unit) {
3074 return WalkControl::Continue;
3075 }
3076 self.parsed.add_code_unit(
3077 code_unit.clone(),
3078 child,
3079 self.source,
3080 Some(parent.clone()),
3081 None,
3082 );
3083 self.parsed.add_signature(
3084 code_unit,
3085 normalize_cpp_whitespace(node_text(child, self.source)),
3086 );
3087 WalkControl::Continue
3088 });
3089 }
3090
3091 fn visit_enum_enumerators_from_text(
3092 &mut self,
3093 node: Node<'_>,
3094 scope: &ScopeInfo,
3095 parent: &CodeUnit,
3096 ) {
3097 let text = node_text(node, self.source);
3098 let Some((_, body)) = text.split_once('{') else {
3099 return;
3100 };
3101 let Some((body, _)) = body.rsplit_once('}') else {
3102 return;
3103 };
3104 for entry in body.split(',') {
3105 let trimmed = entry.trim();
3106 let name = trimmed
3107 .split('=')
3108 .next()
3109 .unwrap_or("")
3110 .split_whitespace()
3111 .next()
3112 .unwrap_or("");
3113 if name.is_empty() {
3114 continue;
3115 }
3116 let code_unit = CodeUnit::new_fq(
3117 self.file.clone(),
3118 CodeUnitType::Field,
3119 scope.package_name.clone(),
3120 cpp_join_member_short(parent.short_name(), name),
3121 parent
3122 .fq()
3123 .clone()
3124 .with_pushed(cpp_segment(name, SegmentKind::Member)),
3125 );
3126 if self.parsed.contains_declaration(&code_unit) {
3127 continue;
3128 }
3129 self.parsed.add_code_unit(
3130 code_unit.clone(),
3131 node,
3132 self.source,
3133 Some(parent.clone()),
3134 None,
3135 );
3136 self.parsed.add_signature(code_unit, trimmed.to_string());
3137 }
3138 }
3139
3140 fn visit_function_definition<'tree>(
3141 &mut self,
3142 node: Node<'tree>,
3143 scope: &ScopeInfo,
3144 stack: &mut Vec<CppWork<'tree>>,
3145 ancestry: &ParentIndex<'tree>,
3146 ) {
3147 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
3153 return;
3154 }
3155 if node.has_error() {
3156 self.visit_macro_swallowed_function_declarations(node, scope);
3157 }
3158 if let Some((class_node, name, raw_supertypes)) =
3159 recover_exported_class_function_definition(node, self.source)
3160 {
3161 let body = cpp_body_node(class_node);
3162 let displaced_namespace = cpp_body_node(node)
3163 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
3164 let fragmented = cpp_body_node(node).and_then(|body| {
3165 fragmented_export_function_body_region(
3166 node,
3167 body,
3168 self.source,
3169 displaced_namespace.as_ref(),
3170 )
3171 });
3172 if let Some(fragmented) = fragmented {
3178 if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
3182 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
3183 {
3184 let mut boundary_scope = scope.clone();
3185 for sibling in cpp_following_named_siblings(node, self.source) {
3186 if sibling.start_byte() >= boundary.start_byte() {
3187 break;
3188 }
3189 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
3190 boundary_scope.visible_using_namespaces.push(namespace);
3191 }
3192 }
3193 self.recovered_class_sibling_scopes
3194 .insert(boundary.id(), boundary_scope);
3195 }
3196 let mut recovered_constructor = None;
3197 let mut recovered_prefix_tree = None;
3198 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
3199 {
3200 Some(FragmentedExportMembers::Complete(tree)) => {
3201 if let Some(body) = body
3202 && let Some(range) =
3203 cpp_reparsed_synthetic_initializer_constructor_range(
3204 tree.root_node(),
3205 &name,
3206 self.source,
3207 body.end_byte(),
3208 )
3209 {
3210 recovered_constructor = Some(range);
3211 recovered_prefix_tree = Some(tree);
3212 None
3213 } else {
3214 Some(FragmentedExportMembers::Complete(tree))
3215 }
3216 }
3217 outcome => outcome,
3218 };
3219 let mut class_stack = Vec::new();
3220 let class_unit = self.visit_named_class_like_shape(
3221 class_node,
3222 name,
3223 None,
3224 true,
3225 Some(fragmented.class_range),
3226 raw_supertypes,
3227 scope,
3228 &mut class_stack,
3229 ancestry,
3230 );
3231 self.parsed
3232 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3233 recovery: fragmented.class_range,
3234 unit: class_unit.clone(),
3235 });
3236 let complete = outcome.is_some_and(|outcome| {
3237 self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
3238 });
3239 if complete {
3240 self.consumed_fragment_regions
3241 .push((node.start_byte(), fragmented.class_range.end_byte));
3242 } else {
3243 let member_scope = ScopeInfo {
3252 package_name: class_unit.package_name().to_string(),
3253 module: scope.module.clone(),
3254 class_unit: Some(class_unit.clone()),
3255 template_signature: scope.template_signature.clone(),
3256 template_metadata: None,
3257 declarations_are_fields: true,
3258 recovered_specialization_member_scope: false,
3259 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3260 };
3261 for candidate in cpp_following_named_siblings(node, self.source) {
3262 if candidate.start_byte() >= fragmented.reparse_end {
3263 break;
3264 }
3265 if cpp_fragment_sibling_is_class_member(
3266 candidate,
3267 fragmented.reparse_end,
3268 self.source,
3269 ) {
3270 self.recovered_class_sibling_scopes
3271 .insert(candidate.id(), member_scope.clone());
3272 }
3273 }
3274 if let Some(range) = recovered_constructor
3275 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
3276 {
3277 self.visit_recovered_fragment_prefix_members(
3278 prefix_tree.root_node(),
3279 range.start,
3280 &class_unit,
3281 scope,
3282 ancestry,
3283 );
3284 self.visit_recovered_fragment_constructor(
3285 range,
3286 body,
3287 class_node,
3288 &class_unit,
3289 scope,
3290 ancestry,
3291 );
3292 }
3293 }
3294 if let Some(boundary) = displaced_namespace {
3295 for item in boundary.namespace_items {
3296 self.recovered_class_sibling_scopes
3297 .insert(item.id(), scope.clone());
3298 }
3299 }
3300 stack.extend(class_stack);
3301 return;
3302 }
3303 let mut stack = Vec::new();
3304 let class_unit = self.visit_named_class_like_shape(
3305 class_node,
3306 name,
3307 body,
3308 body.is_some(),
3309 None,
3310 raw_supertypes,
3311 scope,
3312 &mut stack,
3313 ancestry,
3314 );
3315 self.parsed
3316 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3317 recovery: cpp_declaration_range(node),
3318 unit: class_unit,
3319 });
3320 if let Some(body) = body
3327 && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
3328 && class_close < body.end_byte()
3329 {
3330 let split = {
3331 let mut cursor = body.walk();
3332 body.named_children(&mut cursor)
3333 .position(|child| child.start_byte() > class_close)
3334 };
3335 if let Some(split) = split {
3336 let seeded = stack.pop();
3341 match seeded {
3342 Some(CppWork::Container(container)) => {
3343 push_cpp_sibling_range(
3344 body,
3345 split,
3346 usize::MAX,
3347 scope.clone(),
3348 &mut stack,
3349 );
3350 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
3351 }
3352 _ => unreachable!("exported-class seed is always one Container"),
3355 }
3356 }
3357 }
3358 while let Some(work) = stack.pop() {
3359 match work {
3360 CppWork::Container(container) => {
3361 push_cpp_container_work(container.node, container.scope, &mut stack);
3362 }
3363 CppWork::Siblings(siblings) => {
3364 advance_cpp_siblings(siblings, self.source, &mut stack);
3365 }
3366 CppWork::Node(work) => {
3367 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
3368 }
3369 }
3370 }
3371 return;
3372 }
3373 let recovered_constraint_constructor =
3374 cpp_recovered_template_macro_constructor(node, self.source);
3375 let declarator = recovered_constraint_constructor
3376 .map(|(declarator, _)| declarator)
3377 .or_else(|| node.child_by_field_name("declarator"));
3378 let Some(declarator) = declarator else {
3379 self.visit_malformed_function_definition_container(node, scope, stack);
3380 return;
3381 };
3382 let Some(function_declarator) = extract_function_declarator(declarator) else {
3383 self.visit_malformed_function_definition_container(node, scope, stack);
3384 return;
3385 };
3386 let function = if let Some((_, callable_name)) =
3387 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
3388 {
3389 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
3390 } else {
3391 extract_function_info(function_declarator, self.source, scope)
3392 };
3393 let Some(mut function) = function else {
3394 self.visit_malformed_function_definition_container(node, scope, stack);
3395 return;
3396 };
3397 if let Some((_, template_parameter)) = recovered_constraint_constructor {
3398 function.signature = format!(
3399 "template <{}>{}",
3400 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
3401 function.signature
3402 );
3403 }
3404 let code_unit = function.code_unit(self.file.clone());
3405 self.parsed
3410 .add_code_unit(code_unit.clone(), node, self.source, None, None);
3411 let signature = if recovered_constraint_constructor.is_some() {
3412 normalize_cpp_whitespace(node_text(function_declarator, self.source))
3413 } else {
3414 render_cpp_function_display_signature_from_node(
3415 node,
3416 self.source,
3417 scope.template_signature.as_deref(),
3418 true,
3419 ancestry,
3420 )
3421 };
3422 self.parsed.add_signature_with_metadata(
3423 code_unit.clone(),
3424 cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
3425 .with_declaration_only(false)
3426 .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
3427 );
3428 if let Some(parent) = &scope.class_unit {
3429 self.parsed.add_child(parent.clone(), code_unit);
3430 } else if let Some(module) = &scope.module {
3431 self.parsed.add_child(module.clone(), code_unit);
3432 }
3433 }
3434
3435 fn scope_for_recovered_exported_class<'tree>(
3440 &self,
3441 node: Node<'tree>,
3442 name: &str,
3443 definition_body_present: bool,
3444 scope: &ScopeInfo,
3445 ancestry: &ParentIndex<'tree>,
3446 ) -> ScopeInfo {
3447 if !definition_body_present
3448 || !scope.package_name.is_empty()
3449 || scope.class_unit.is_some()
3450 || !(is_recovered_exported_class_container(node, self.source)
3451 || matches!(node.kind(), "declaration" | "field_declaration")
3452 && recover_exported_class_declaration(node, self.source).is_some()
3453 || matches!(
3454 node.kind(),
3455 "class_specifier" | "struct_specifier" | "union_specifier"
3456 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
3457 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
3458 name_node,
3459 self.source,
3460 )))
3461 }) || ancestry.parent(node).is_some_and(|parent| {
3462 matches!(parent.kind(), "declaration" | "field_declaration")
3463 && recover_exported_class_declaration(parent, self.source).is_some()
3464 || is_recovered_exported_class_container(parent, self.source)
3465 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
3466 {
3467 return scope.clone();
3468 }
3469 let Some(package_name) =
3470 unique_earlier_cpp_namespace_forward(node, name, self.source, ancestry)
3471 else {
3472 return scope.clone();
3473 };
3474
3475 let module = CodeUnit::new_fq(
3476 self.file.clone(),
3477 CodeUnitType::Module,
3478 "",
3479 package_name.clone(),
3480 cpp_namespace_fq(&package_name),
3481 );
3482 let mut recovered = scope.clone();
3483 recovered.package_name = package_name;
3484 recovered.module = Some(module);
3485 recovered
3486 }
3487
3488 fn visit_malformed_function_definition_container<'tree>(
3489 &mut self,
3490 node: Node<'tree>,
3491 scope: &ScopeInfo,
3492 stack: &mut Vec<CppWork<'tree>>,
3493 ) {
3494 let Some(body) = cpp_body_node(node) else {
3495 return;
3496 };
3497 if !cpp_contains_namespace_definition(body) {
3498 return;
3499 }
3500 stack.push(CppWork::Container(CppContainer {
3501 node: body,
3502 scope: scope.clone(),
3503 }));
3504 }
3505
3506 fn record_recovered_declarations(
3524 &mut self,
3525 recovery: Range,
3526 reparse_walk: impl FnOnce(&mut Self),
3527 ) {
3528 let before = self.parsed.declarations().clone();
3529 reparse_walk(self);
3530 let mut minted: Vec<CodeUnit> = self
3531 .parsed
3532 .declarations()
3533 .iter()
3534 .filter(|unit| !before.contains(*unit))
3535 .cloned()
3536 .collect();
3537 minted.sort_by_cached_key(|unit| {
3538 let start = self
3539 .parsed
3540 .declaration_ranges(unit)
3541 .first()
3542 .map(|range| range.start_byte)
3543 .unwrap_or(usize::MAX);
3544 (start, unit.fq_name().to_string())
3545 });
3546 for unit in minted {
3547 self.parsed
3548 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3549 recovery,
3550 unit,
3551 });
3552 }
3553 }
3554
3555 fn visit_sentinel_macro_region<'tree>(
3556 &mut self,
3557 node: Node<'tree>,
3558 scope: &ScopeInfo,
3559 stack: &mut Vec<CppWork<'tree>>,
3560 ancestry: &ParentIndex<'tree>,
3561 ) -> bool {
3562 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
3563 return true;
3564 }
3565 if let Some((
3566 reparse_start,
3567 class_start,
3568 body_start,
3569 class_close_start,
3570 class_close_end,
3571 class_close_line,
3572 )) = cpp_sentinel_macro_class_region(node, self.source)
3573 {
3574 let Some(class_tree) =
3575 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
3576 else {
3577 return false;
3578 };
3579 let class_root = class_tree.root_node();
3580 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
3581 let class_ancestry = ParentIndex::new(class_root);
3583 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
3584 class_root,
3585 template_node,
3586 self.source,
3587 &class_ancestry,
3588 ) else {
3589 return false;
3590 };
3591 let class_node = reparsed_class.declaration_node;
3592 let name = reparsed_class.name;
3593 let mut class_scope = scope.clone();
3594 if let Some(template_node) = template_node {
3595 class_scope.template_signature =
3596 cpp_template_signature(template_node, class_node, self.source);
3597 class_scope.template_metadata =
3598 cpp_template_metadata(template_node, class_node, self.source, ancestry);
3599 }
3600 let Some(body_tree) =
3601 cpp_reparse_region_items(self.source, body_start, class_close_start)
3602 else {
3603 return false;
3604 };
3605 let raw_supertypes = reparsed_class.raw_supertypes;
3606 let class_range = Range {
3607 start_byte: class_start,
3608 end_byte: class_close_end,
3609 start_line: class_node.start_position().row + 1,
3610 end_line: class_close_line,
3611 };
3612 let class_scope = self.scope_for_recovered_exported_class(
3613 class_node,
3614 &name,
3615 true,
3616 &class_scope,
3617 ancestry,
3618 );
3619 let mut class_stack = Vec::new();
3620 let class_unit = self.visit_named_class_like_shape(
3621 class_node,
3622 name,
3623 None,
3624 true,
3625 Some(class_range),
3626 raw_supertypes,
3627 &class_scope,
3628 &mut class_stack,
3629 ancestry,
3630 );
3631 self.parsed
3632 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3633 recovery: class_range,
3634 unit: class_unit.clone(),
3635 });
3636 let member_scope = ScopeInfo {
3637 package_name: class_scope.package_name.clone(),
3638 module: class_scope.module.clone(),
3639 class_unit: Some(class_unit),
3640 template_signature: class_scope.template_signature.clone(),
3641 template_metadata: None,
3642 declarations_are_fields: true,
3643 recovered_specialization_member_scope: false,
3644 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
3645 };
3646 self.run_container_work(body_tree.root_node(), member_scope);
3647 self.consumed_fragment_regions
3650 .push((node.start_byte(), class_close_end));
3651 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
3658 stack.push(CppWork::Container(CppContainer {
3659 node,
3660 scope: scope.clone(),
3661 }));
3662 }
3663 return true;
3664 }
3665 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
3666 return false;
3667 };
3668 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
3669 return false;
3670 };
3671 let root = tree.root_node();
3672 if !cpp_reparsed_items_are_indexable(root, self.source) {
3673 return false;
3674 }
3675 let recovery = cpp_recovery_window(self.source, start, end);
3676 self.record_recovered_declarations(recovery, |visitor| {
3677 visitor.visit_container(
3678 root,
3679 &scope.package_name,
3680 scope.module.clone(),
3681 scope.class_unit.clone(),
3682 scope.template_signature.clone(),
3683 scope.visible_using_namespaces.clone(),
3684 );
3685 });
3686 if end > node.end_byte() {
3687 self.consumed_fragment_regions
3688 .push((node.start_byte(), end));
3689 } else if node.kind() == "ERROR" && node.end_byte() > end {
3690 self.consumed_fragment_regions
3697 .push((node.start_byte(), end));
3698 stack.push(CppWork::Container(CppContainer {
3699 node,
3700 scope: scope.clone(),
3701 }));
3702 }
3703 true
3704 }
3705
3706 fn visit_nested_namespace_sentinel<'tree>(
3712 &mut self,
3713 node: Node<'tree>,
3714 scope: &ScopeInfo,
3715 ancestry: &ParentIndex<'tree>,
3716 ) -> bool {
3717 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
3718 return false;
3719 };
3720
3721 let mut package_name = scope.package_name.clone();
3722 let mut module = scope.module.clone();
3723 for component in recovered.namespace_components {
3724 package_name = if package_name.is_empty() {
3725 component
3726 } else {
3727 format!("{package_name}::{component}")
3728 };
3729 let namespace_module = CodeUnit::new_fq(
3730 self.file.clone(),
3731 CodeUnitType::Module,
3732 "",
3733 package_name.clone(),
3734 cpp_namespace_fq(&package_name),
3735 );
3736 if !self.parsed.contains_declaration(&namespace_module) {
3737 self.parsed.add_code_unit(
3738 namespace_module.clone(),
3739 recovered.function,
3740 self.source,
3741 None,
3742 None,
3743 );
3744 }
3745 module = Some(namespace_module);
3746 }
3747
3748 let recovered_scope = ScopeInfo {
3749 package_name,
3750 module,
3751 class_unit: scope.class_unit.clone(),
3752 template_signature: scope.template_signature.clone(),
3753 template_metadata: scope.template_metadata.clone(),
3754 declarations_are_fields: false,
3755 recovered_specialization_member_scope: false,
3756 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3757 };
3758 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
3759 recovered.function,
3760 recovered.body,
3761 self.source,
3762 ancestry,
3763 ) {
3764 let mut class_scope = recovered_scope.clone();
3765 if let Some(template_node) = fragmented.template_node {
3766 class_scope.template_signature =
3767 cpp_template_signature(template_node, fragmented.class_node, self.source);
3768 class_scope.template_metadata = cpp_template_metadata(
3769 template_node,
3770 fragmented.class_node,
3771 self.source,
3772 ancestry,
3773 );
3774 }
3775 if let Some(outcome) = self
3776 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
3777 {
3778 let mut class_stack = Vec::new();
3779 let class_unit = self.visit_named_class_like_shape(
3780 fragmented.class_node,
3781 fragmented.name.clone(),
3782 None,
3783 true,
3784 Some(fragmented.fragmented.class_range),
3785 fragmented.raw_supertypes.clone(),
3786 &class_scope,
3787 &mut class_stack,
3788 ancestry,
3789 );
3790 self.parsed
3791 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3792 recovery: fragmented.fragmented.class_range,
3793 unit: class_unit.clone(),
3794 });
3795 if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
3796 self.consumed_fragment_regions.push((
3797 fragmented.consumed_start,
3798 fragmented.fragmented.class_range.end_byte,
3799 ));
3800 }
3801 }
3802 }
3803 self.run_container_work(recovered.body, recovered_scope);
3807 true
3808 }
3809
3810 fn visit_declaration<'tree>(
3811 &mut self,
3812 node: Node<'tree>,
3813 scope: &ScopeInfo,
3814 in_class_body: bool,
3815 stack: &mut Vec<CppWork<'tree>>,
3816 ancestry: &ParentIndex<'tree>,
3817 ) {
3818 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
3819 return;
3820 }
3821 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
3822 !cpp_active_template_type_parameter(
3823 node,
3824 node_text(declarator, self.source),
3825 self.source,
3826 ancestry,
3827 )
3828 }) {
3829 return;
3830 }
3831 if in_class_body
3832 && let Some(parent) = scope.class_unit.as_ref()
3833 && let Some(call) =
3834 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
3835 {
3836 self.visit_recovered_macro_qualified_constructor_definition(
3837 node, call, scope, ancestry,
3838 );
3839 return;
3840 }
3841 if in_class_body
3842 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
3843 {
3844 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
3845 return;
3846 }
3847 if in_class_body
3848 && let Some(declarators) =
3849 recovered_macro_qualified_field_declarators(node, self.source)
3850 {
3851 for declarator in declarators {
3852 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
3853 }
3854 return;
3855 }
3856 let recovered_alias_names = recovered_type_alias_names(node, self.source);
3857 if !recovered_alias_names.is_empty() {
3858 self.add_type_aliases(node, scope, recovered_alias_names);
3859 return;
3860 }
3861 if self.visit_c_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry)
3862 {
3863 return;
3864 }
3865
3866 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
3867 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
3868 if let Some(outcome) =
3873 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
3874 {
3875 let consumed_region = (
3876 recovered.declaration_node.end_byte(),
3877 fragmented.class_range.end_byte,
3878 );
3879 let code_unit = self.visit_named_class_like_shape(
3880 recovered.declaration_node,
3881 recovered.name,
3882 None,
3883 true,
3884 Some(fragmented.class_range),
3885 recovered.raw_supertypes,
3886 scope,
3887 stack,
3888 ancestry,
3889 );
3890 self.parsed.record_materialization(
3891 MaterializationRecord::RecoveredDeclaration {
3892 recovery: fragmented.class_range,
3893 unit: code_unit.clone(),
3894 },
3895 );
3896 let consume_fragment =
3897 self.visit_fragmented_export_class_members(outcome, code_unit, scope);
3898 if consume_fragment {
3904 self.consumed_fragment_regions.push(consumed_region);
3905 }
3906 return;
3907 }
3908 }
3909 let uses_initializer_body = recovered.uses_initializer_body;
3910 let definition_body_present = recovered.body.is_some();
3911 let class_unit = self.visit_named_class_like_shape(
3912 recovered.declaration_node,
3913 recovered.name,
3914 recovered.body,
3915 definition_body_present,
3916 None,
3917 recovered.raw_supertypes,
3918 scope,
3919 stack,
3920 ancestry,
3921 );
3922 self.parsed
3923 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3924 recovery: cpp_declaration_range(node),
3925 unit: class_unit,
3926 });
3927 if uses_initializer_body {
3928 return;
3929 }
3930 }
3931
3932 let mut handled_function = false;
3933 let mut handled_declarator = false;
3934 let mut cursor = node.walk();
3935 for child in node.named_children(&mut cursor) {
3936 if matches!(
3937 child.kind(),
3938 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3939 ) {
3940 if cpp_body_node(child).is_some() {
3949 self.visit_class_like(child, scope, stack, ancestry);
3950 }
3951 continue;
3952 }
3953 }
3954
3955 let mut cursor = node.walk();
3956 for child in node.children_by_field_name("declarator", &mut cursor) {
3957 if crate::structural::is_recovered_designator_init_declarator(child) {
3958 handled_declarator = true;
3959 continue;
3960 }
3961 if let Some(kind) = classify_declarator(child) {
3962 handled_declarator = true;
3963 match kind {
3964 DeclaratorKind::Function(function_declarator) => {
3965 handled_function = true;
3966 self.visit_function_declaration(node, function_declarator, scope, ancestry);
3967 }
3968 DeclaratorKind::Variable(variable_declarator) => {
3969 self.visit_variable_declaration(
3970 node,
3971 variable_declarator,
3972 scope,
3973 in_class_body,
3974 ancestry,
3975 );
3976 }
3977 }
3978 }
3979 }
3980
3981 if !handled_declarator {
3982 let mut cursor = node.walk();
3983 for child in node.named_children(&mut cursor) {
3984 if crate::structural::is_recovered_designator_init_declarator(child) {
3985 handled_declarator = true;
3986 continue;
3987 }
3988 if !is_unfielded_declarator_candidate(child) {
3989 continue;
3990 }
3991 let Some(kind) = classify_declarator(child) else {
3992 continue;
3993 };
3994 handled_declarator = true;
3995 match kind {
3996 DeclaratorKind::Function(function_declarator) => {
3997 handled_function = true;
3998 self.visit_function_declaration(node, function_declarator, scope, ancestry);
3999 }
4000 DeclaratorKind::Variable(variable_declarator) => {
4001 self.visit_variable_declaration(
4002 node,
4003 variable_declarator,
4004 scope,
4005 in_class_body,
4006 ancestry,
4007 );
4008 }
4009 }
4010 }
4011 }
4012
4013 if handled_function {
4014 return;
4015 }
4016
4017 if !handled_declarator {
4018 if in_class_body {
4019 self.visit_class_members_from_declaration(node, scope, ancestry);
4020 } else {
4021 self.visit_global_variables_from_declaration(node, scope, ancestry);
4022 }
4023 }
4024 }
4025
4026 fn visit_c_anonymous_aggregate_declaration<'tree>(
4035 &mut self,
4036 node: Node<'tree>,
4037 scope: &ScopeInfo,
4038 in_class_body: bool,
4039 stack: &mut Vec<CppWork<'tree>>,
4040 ancestry: &ParentIndex<'tree>,
4041 ) -> bool {
4042 if !self.c_tag_semantics || !in_class_body || scope.class_unit.is_none() {
4043 return false;
4044 }
4045 let Some(aggregate) = node.child_by_field_name("type") else {
4046 return false;
4047 };
4048 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
4049 || aggregate.child_by_field_name("name").is_some()
4050 {
4051 return false;
4052 }
4053 let Some(body) = cpp_body_node(aggregate) else {
4054 return false;
4055 };
4056
4057 let mut cursor = node.walk();
4058 let declarators = node
4059 .children_by_field_name("declarator", &mut cursor)
4060 .filter_map(|declarator| match classify_declarator(declarator) {
4061 Some(DeclaratorKind::Variable(variable)) => Some(variable),
4062 Some(DeclaratorKind::Function(_)) | None => None,
4063 })
4064 .collect::<Vec<_>>();
4065 if declarators.is_empty() {
4066 stack.push(CppWork::Container(CppContainer {
4067 node: body,
4068 scope: scope.clone(),
4069 }));
4070 return true;
4071 }
4072
4073 for declarator in declarators {
4074 let Some(name) = extract_variable_name(declarator, self.source) else {
4075 continue;
4076 };
4077 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
4078 self.visit_named_class_like_shape(
4079 aggregate,
4080 name,
4081 Some(body),
4082 true,
4083 None,
4084 None,
4085 scope,
4086 stack,
4087 ancestry,
4088 );
4089 }
4090 true
4091 }
4092
4093 fn visit_function_declaration<'tree>(
4094 &mut self,
4095 declaration_node: Node<'tree>,
4096 declarator: Node<'tree>,
4097 scope: &ScopeInfo,
4098 ancestry: &ParentIndex<'tree>,
4099 ) {
4100 let Some(function) = extract_function_info(declarator, self.source, scope) else {
4101 return;
4102 };
4103 let code_unit =
4104 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
4105 if self.parsed.contains_declaration(&code_unit) {
4106 self.parsed
4107 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
4108 return;
4109 }
4110 self.parsed
4111 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4112 let signature = render_cpp_function_display_signature_from_node(
4113 declaration_node,
4114 self.source,
4115 scope.template_signature.as_deref(),
4116 false,
4117 ancestry,
4118 );
4119 self.parsed.add_signature_with_metadata(
4120 code_unit.clone(),
4121 cpp_signature_metadata(signature, declarator, self.source, ancestry)
4122 .with_declaration_only(true)
4123 .with_callable_linkage(cpp_callable_linkage(
4124 declaration_node,
4125 self.source,
4126 ancestry,
4127 )),
4128 );
4129 if let Some(parent) = &scope.class_unit {
4130 self.parsed.add_child(parent.clone(), code_unit);
4131 } else if let Some(module) = &scope.module {
4132 self.parsed.add_child(module.clone(), code_unit);
4133 }
4134 }
4135
4136 fn visit_recovered_macro_qualified_function_declaration<'tree>(
4137 &mut self,
4138 declaration_node: Node<'tree>,
4139 call: Node<'tree>,
4140 scope: &ScopeInfo,
4141 ancestry: &ParentIndex<'tree>,
4142 ) {
4143 let Some(parent) = &scope.class_unit else {
4144 return;
4145 };
4146 let Some(name_node) = call.child_by_field_name("function") else {
4147 return;
4148 };
4149 let Some(arguments) = call.child_by_field_name("arguments") else {
4150 return;
4151 };
4152 let Some((signature, parameter_labels)) =
4153 recovered_macro_qualified_function_parameters(arguments, self.source)
4154 else {
4155 return;
4156 };
4157 let arity = parameter_labels.len();
4158 let function = FunctionInfo {
4159 package_name: scope.package_name.clone(),
4160 owner: Some(CppMemberOwner::Unit(parent.clone())),
4161 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
4162 signature,
4163 };
4164 if function.name.is_empty() {
4165 return;
4166 }
4167 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
4168 if self.parsed.contains_declaration(&code_unit) {
4169 self.parsed
4170 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
4171 return;
4172 }
4173 self.parsed
4174 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4175 let signature_label = render_cpp_function_display_signature_from_node(
4176 declaration_node,
4177 self.source,
4178 scope.template_signature.as_deref(),
4179 false,
4180 ancestry,
4181 );
4182 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
4183 .with_declaration_only(true)
4184 .with_callable_arity(CallableArity::exact(arity))
4185 .with_callable_linkage(cpp_callable_linkage(
4186 declaration_node,
4187 self.source,
4188 ancestry,
4189 ));
4190 self.parsed
4191 .add_signature_with_metadata(code_unit.clone(), metadata);
4192 self.parsed.add_child(parent.clone(), code_unit);
4193 }
4194
4195 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
4196 &mut self,
4197 declaration_node: Node<'tree>,
4198 call: Node<'tree>,
4199 scope: &ScopeInfo,
4200 ancestry: &ParentIndex<'tree>,
4201 ) {
4202 let Some(parent) = &scope.class_unit else {
4203 return;
4204 };
4205 let Some(arguments) = call.child_by_field_name("arguments") else {
4206 return;
4207 };
4208 let Some((mut signature, parameter_labels)) =
4209 recovered_macro_qualified_function_parameters(arguments, self.source)
4210 else {
4211 return;
4212 };
4213 if let Some(template_signature) = &scope.template_signature {
4214 signature = format!("{template_signature}{signature}");
4215 }
4216 let arity = parameter_labels.len();
4217 let function = FunctionInfo {
4218 package_name: scope.package_name.clone(),
4219 owner: Some(CppMemberOwner::Unit(parent.clone())),
4220 name: parent.identifier().to_string(),
4221 signature,
4222 };
4223 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
4224 self.parsed
4225 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4226 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
4227 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
4228 .with_declaration_only(false)
4229 .with_callable_arity(CallableArity::exact(arity))
4230 .with_callable_linkage(cpp_callable_linkage(
4231 declaration_node,
4232 self.source,
4233 ancestry,
4234 ));
4235 self.parsed
4236 .add_signature_with_metadata(code_unit.clone(), metadata);
4237 self.parsed.add_child(parent.clone(), code_unit);
4238 }
4239
4240 fn visit_variable_declaration<'tree>(
4241 &mut self,
4242 declaration_node: Node<'tree>,
4243 declarator: Node<'tree>,
4244 scope: &ScopeInfo,
4245 in_class_body: bool,
4246 ancestry: &ParentIndex<'tree>,
4247 ) {
4248 let Some(name) = extract_variable_name(declarator, self.source) else {
4249 return;
4250 };
4251 let parent = if in_class_body {
4252 let Some(parent) = &scope.class_unit else {
4253 return;
4254 };
4255 Some(parent)
4256 } else {
4257 None
4258 };
4259 let short_name = match parent {
4260 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
4261 None => name.clone(),
4262 };
4263 let fq = cpp_leaf_fq(
4264 &scope.package_name,
4265 parent,
4266 &name,
4267 SegmentKind::Member,
4268 SegmentKind::Member,
4269 );
4270 let code_unit = CodeUnit::new_fq(
4271 self.file.clone(),
4272 CodeUnitType::Field,
4273 scope.package_name.clone(),
4274 short_name,
4275 fq,
4276 );
4277 if self.parsed.contains_declaration(&code_unit) {
4278 return;
4279 }
4280 self.parsed
4281 .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4282 self.parsed.add_signature_with_metadata(
4283 code_unit.clone(),
4284 SignatureMetadata::new(
4285 render_cpp_field_signature(declaration_node, declarator, self.source),
4286 Vec::new(),
4287 )
4288 .with_cpp_field_linkage(cpp_field_declaration_linkage(
4289 declaration_node,
4290 self.source,
4291 ancestry,
4292 )),
4293 );
4294 if let Some(parent) = &scope.class_unit {
4295 self.parsed.add_child(parent.clone(), code_unit);
4296 } else if let Some(module) = &scope.module {
4297 self.parsed.add_child(module.clone(), code_unit);
4298 }
4299 }
4300
4301 fn visit_class_members_from_declaration<'tree>(
4302 &mut self,
4303 node: Node<'tree>,
4304 scope: &ScopeInfo,
4305 ancestry: &ParentIndex<'tree>,
4306 ) {
4307 let mut cursor = node.walk();
4308 for child in node.named_children(&mut cursor) {
4309 if child.kind() == "init_declarator"
4310 && let Some(inner) = child.child_by_field_name("declarator")
4311 {
4312 self.visit_variable_declaration(node, inner, scope, true, ancestry);
4313 } else if matches!(
4314 child.kind(),
4315 "identifier"
4316 | "field_identifier"
4317 | "pointer_declarator"
4318 | "reference_declarator"
4319 | "array_declarator"
4320 | "parenthesized_declarator"
4321 ) {
4322 self.visit_variable_declaration(node, child, scope, true, ancestry);
4323 }
4324 }
4325 }
4326
4327 fn visit_global_variables_from_declaration<'tree>(
4328 &mut self,
4329 node: Node<'tree>,
4330 scope: &ScopeInfo,
4331 ancestry: &ParentIndex<'tree>,
4332 ) {
4333 let mut cursor = node.walk();
4334 for child in node.named_children(&mut cursor) {
4335 if child.kind() == "init_declarator"
4336 && let Some(inner) = child.child_by_field_name("declarator")
4337 {
4338 self.visit_variable_declaration(node, inner, scope, false, ancestry);
4339 } else if matches!(
4340 child.kind(),
4341 "identifier"
4342 | "field_identifier"
4343 | "pointer_declarator"
4344 | "reference_declarator"
4345 | "array_declarator"
4346 | "parenthesized_declarator"
4347 ) {
4348 self.visit_variable_declaration(node, child, scope, false, ancestry);
4349 }
4350 }
4351 }
4352
4353 fn visit_include(&mut self, node: Node<'_>) {
4354 let raw = normalize_cpp_whitespace(node_text(node, self.source));
4355 self.parsed.imports.push(ImportInfo {
4356 raw_snippet: raw,
4357 is_wildcard: false,
4358 is_global: false,
4359 identifier: None,
4360 alias: None,
4361 path: None,
4362 binder_span: None,
4363 });
4364 }
4365
4366 fn visit_type_declaration<'tree>(
4367 &mut self,
4368 node: Node<'tree>,
4369 scope: &ScopeInfo,
4370 stack: &mut Vec<CppWork<'tree>>,
4371 ancestry: &ParentIndex<'tree>,
4372 ) {
4373 let type_node = node.child_by_field_name("type");
4374 if let Some(type_node) = type_node
4375 && matches!(
4376 type_node.kind(),
4377 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
4378 )
4379 {
4380 self.visit_class_like(type_node, scope, stack, ancestry);
4381 }
4382
4383 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
4384 let range = Range {
4385 start_byte: node.start_byte(),
4386 end_byte: recovered.end_node.end_byte(),
4387 start_line: node.start_position().row + 1,
4388 end_line: recovered.end_node.end_position().row + 1,
4389 };
4390 let signature = self
4391 .source
4392 .get(range.start_byte..range.end_byte)
4393 .map(normalize_cpp_whitespace)
4394 .unwrap_or_default();
4395 self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
4396 return;
4397 }
4398
4399 let alias_names = match node.kind() {
4400 "alias_declaration" => extract_alias_declaration_name(node, self.source)
4401 .into_iter()
4402 .collect::<Vec<_>>(),
4403 "type_definition" => extract_typedef_alias_names(node, self.source),
4404 _ => Vec::new(),
4405 };
4406 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
4407 (type_node, alias_names.as_slice())
4408 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
4409 && type_node.child_by_field_name("name").is_none()
4410 {
4411 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
4412 } else {
4413 None
4414 };
4415 self.add_type_aliases(node, scope, alias_names);
4416 if let Some((body, alias_name)) = anonymous_aggregate {
4417 let signature = normalize_cpp_whitespace(node_text(node, self.source));
4423 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
4424 debug_assert!(self.parsed.contains_declaration(&alias_unit));
4425 let mut nested_scope = scope.clone();
4426 nested_scope.class_unit = Some(alias_unit);
4427 nested_scope.template_signature = scope.template_signature.clone();
4428 nested_scope.template_metadata = None;
4429 nested_scope.declarations_are_fields = false;
4430 nested_scope.recovered_specialization_member_scope = false;
4431 stack.push(CppWork::Container(CppContainer {
4432 node: body,
4433 scope: nested_scope,
4434 }));
4435 }
4436 }
4437
4438 fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
4439 let signature = normalize_cpp_whitespace(node_text(node, self.source));
4440 self.record_type_aliases(
4441 node,
4442 scope,
4443 alias_names,
4444 signature,
4445 cpp_declaration_range(node),
4446 );
4447 }
4448
4449 fn record_type_aliases(
4450 &mut self,
4451 node: Node<'_>,
4452 scope: &ScopeInfo,
4453 alias_names: Vec<String>,
4454 signature: String,
4455 range: Range,
4456 ) {
4457 if signature.is_empty() {
4458 return;
4459 }
4460 let type_name = node
4461 .child_by_field_name("type")
4462 .and_then(|type_node| type_node.child_by_field_name("name"))
4463 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
4464 for alias_name in alias_names {
4465 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
4466 continue;
4467 }
4468 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
4469 self.parsed
4472 .add_code_unit_with_range(code_unit.clone(), range, None, None);
4473 self.parsed
4474 .add_signature(code_unit.clone(), signature.clone());
4475 if let Some(metadata) = &scope.template_metadata {
4476 let mut metadata = metadata.clone();
4477 metadata.primary_fq_name = code_unit.fq_name();
4478 self.parsed
4479 .set_cpp_template_metadata(code_unit.clone(), metadata);
4480 }
4481 if let Some(parent) = &scope.class_unit {
4482 self.parsed.add_child(parent.clone(), code_unit.clone());
4483 } else if let Some(module) = &scope.module {
4484 self.parsed.add_child(module.clone(), code_unit.clone());
4485 }
4486 self.parsed.mark_type_alias(code_unit);
4487 }
4488 }
4489
4490 fn type_alias_unit(
4491 &self,
4492 scope: &ScopeInfo,
4493 alias_name: String,
4494 signature: String,
4495 ) -> CodeUnit {
4496 let short_name = if let Some(parent) = &scope.class_unit {
4497 cpp_join_nested_short(parent.short_name(), &alias_name)
4498 } else {
4499 alias_name.clone()
4500 };
4501 let fq = cpp_leaf_fq(
4502 &scope.package_name,
4503 scope.class_unit.as_ref(),
4504 &alias_name,
4505 SegmentKind::Nested,
4506 SegmentKind::Type,
4507 );
4508 CodeUnit::with_signature_and_fq(
4509 self.file.clone(),
4510 CodeUnitType::Class,
4511 scope.package_name.clone(),
4512 short_name,
4513 Some(signature),
4514 false,
4515 fq,
4516 )
4517 }
4518
4519 fn visit_macro(&mut self, node: Node<'_>) {
4520 let Some(name) = extract_macro_name(node, self.source) else {
4521 return;
4522 };
4523 let signature = node_text(node, self.source).trim_end().to_string();
4524 if signature.is_empty() {
4525 return;
4526 }
4527 let fq = cpp_member_fq("", &name);
4528 let code_unit = CodeUnit::new_fq(self.file.clone(), CodeUnitType::Macro, "", name, fq);
4529 if self.parsed.contains_declaration_identity(&code_unit) {
4530 return;
4531 }
4532 self.parsed
4533 .add_code_unit(code_unit.clone(), node, self.source, None, None);
4534 let name_range = node
4535 .child_by_field_name("name")
4536 .map(cpp_declaration_range)
4537 .unwrap_or_else(|| cpp_declaration_range(node));
4538 self.parsed
4539 .record_materialization(MaterializationRecord::GeneratedDeclaration {
4540 site: cpp_declaration_range(node),
4541 argument: name_range,
4542 kind: GenerationKind::PreprocessorDefinition,
4543 unit: code_unit.clone(),
4544 });
4545 self.parsed.add_signature(code_unit, signature);
4546 }
4547}
4548
4549pub fn cpp_field_declaration_linkage<'tree>(
4554 declaration: Node<'tree>,
4555 source: &str,
4556 ancestry: &ParentIndex<'tree>,
4557) -> CppFieldLinkage {
4558 let mut current = ancestry.parent(declaration);
4559 let mut enclosed_by_class = false;
4560 while let Some(node) = current {
4561 if node.kind() == "namespace_definition"
4562 && node
4563 .child_by_field_name("name")
4564 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4565 {
4566 return CppFieldLinkage::Internal;
4567 }
4568 if matches!(
4569 node.kind(),
4570 "class_specifier" | "struct_specifier" | "union_specifier"
4571 ) && node
4572 .child_by_field_name("name")
4573 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4574 {
4575 return CppFieldLinkage::Internal;
4576 }
4577 if matches!(
4578 node.kind(),
4579 "class_specifier" | "struct_specifier" | "union_specifier"
4580 ) {
4581 enclosed_by_class = true;
4582 }
4583 if matches!(node.kind(), "function_definition" | "lambda_expression") {
4584 return CppFieldLinkage::Internal;
4585 }
4586 current = ancestry.parent(node);
4587 }
4588 if enclosed_by_class {
4589 return CppFieldLinkage::External;
4590 }
4591 let mut cursor = declaration.walk();
4592 let mut has_static = false;
4593 let mut has_extern = false;
4594 let mut has_inline = false;
4595 let mut has_const = false;
4596 let mut has_constexpr = false;
4597 for child in declaration.named_children(&mut cursor) {
4598 let text = normalize_cpp_whitespace(node_text(child, source));
4599 match (child.kind(), text.as_str()) {
4600 ("storage_class_specifier", "static") => has_static = true,
4601 ("storage_class_specifier", "extern") => has_extern = true,
4602 ("storage_class_specifier", "inline") => has_inline = true,
4603 ("storage_class_specifier", "constexpr") => has_constexpr = true,
4604 ("type_qualifier", "const") => has_const = true,
4605 ("type_qualifier", "constexpr") => has_constexpr = true,
4606 _ => {}
4607 }
4608 }
4609 if has_static {
4610 CppFieldLinkage::Internal
4611 } else if has_extern || has_inline {
4612 CppFieldLinkage::External
4613 } else if has_const || has_constexpr {
4614 CppFieldLinkage::InternalUnlessExternalPeer
4615 } else {
4616 CppFieldLinkage::External
4617 }
4618}
4619
4620fn cpp_declaration_range(node: Node<'_>) -> Range {
4621 Range {
4622 start_byte: node.start_byte(),
4623 end_byte: node.end_byte(),
4624 start_line: node.start_position().row + 1,
4625 end_line: node.end_position().row + 1,
4626 }
4627}
4628
4629fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
4633 let line_at = |byte: usize| {
4634 source.as_bytes()[..byte]
4635 .iter()
4636 .filter(|&&b| b == b'\n')
4637 .count()
4638 + 1
4639 };
4640 Range {
4641 start_byte,
4642 end_byte,
4643 start_line: line_at(start_byte),
4644 end_line: line_at(end_byte),
4645 }
4646}
4647
4648pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
4649 let mut in_block_comment = false;
4650 for line in source.lines() {
4651 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
4652 let trimmed = stripped.trim();
4653 if !looks_like_quoted_include_line(trimmed) {
4654 continue;
4655 }
4656
4657 let raw = normalize_cpp_whitespace(trimmed);
4658 if parsed
4662 .imports
4663 .iter()
4664 .any(|import| import.raw_snippet == raw)
4665 {
4666 continue;
4667 }
4668
4669 parsed.imports.push(ImportInfo {
4670 raw_snippet: raw,
4671 is_wildcard: false,
4672 is_global: false,
4673 identifier: None,
4674 alias: None,
4675 path: None,
4676 binder_span: None,
4677 });
4678 }
4679}
4680
4681fn looks_like_quoted_include_line(line: &str) -> bool {
4682 let Some(rest) = line.trim_start().strip_prefix('#') else {
4683 return false;
4684 };
4685 let Some(rest) = rest.trim_start().strip_prefix("include") else {
4686 return false;
4687 };
4688 rest.trim_start().starts_with('"')
4689}
4690
4691fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
4692 let mut raw = Vec::new();
4693 let mut cursor = node.walk();
4694 for child in node.named_children(&mut cursor) {
4695 if child.kind() == "base_class_clause" {
4696 collect_cpp_base_nodes(child, source, &mut raw);
4697 }
4698 }
4699 raw
4700}
4701
4702fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
4703 walk_named_tree_preorder(node, false, |child| match child.kind() {
4704 "type_identifier" | "qualified_identifier" | "template_type" => {
4705 let text = normalize_cpp_whitespace(node_text(child, source));
4706 if !text.is_empty() {
4707 raw.push(text);
4708 }
4709 WalkControl::SkipChildren
4710 }
4711 _ => WalkControl::Continue,
4712 });
4713}
4714
4715fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
4716 let mut out = String::new();
4717 let chars: Vec<char> = line.chars().collect();
4718 let mut index = 0;
4719 let mut in_string = false;
4720 let mut in_char = false;
4721 let mut escape = false;
4722
4723 while index < chars.len() {
4724 let ch = chars[index];
4725 let next = chars.get(index + 1).copied();
4726
4727 if *in_block_comment {
4728 if ch == '*' && next == Some('/') {
4729 *in_block_comment = false;
4730 index += 2;
4731 } else {
4732 index += 1;
4733 }
4734 continue;
4735 }
4736
4737 if in_string {
4738 out.push(ch);
4739 if escape {
4740 escape = false;
4741 } else if ch == '\\' {
4742 escape = true;
4743 } else if ch == '"' {
4744 in_string = false;
4745 }
4746 index += 1;
4747 continue;
4748 }
4749
4750 if in_char {
4751 out.push(ch);
4752 if escape {
4753 escape = false;
4754 } else if ch == '\\' {
4755 escape = true;
4756 } else if ch == '\'' {
4757 in_char = false;
4758 }
4759 index += 1;
4760 continue;
4761 }
4762
4763 if ch == '/' && next == Some('/') {
4764 break;
4765 }
4766 if ch == '/' && next == Some('*') {
4767 *in_block_comment = true;
4768 index += 2;
4769 continue;
4770 }
4771 if ch == '"' {
4772 in_string = true;
4773 out.push(ch);
4774 index += 1;
4775 continue;
4776 }
4777 if ch == '\'' {
4778 in_char = true;
4779 out.push(ch);
4780 index += 1;
4781 continue;
4782 }
4783
4784 out.push(ch);
4785 index += 1;
4786 }
4787
4788 out
4789}
4790
4791#[derive(Clone)]
4792struct FunctionInfo {
4793 package_name: String,
4794 owner: Option<CppMemberOwner>,
4795 name: String,
4796 signature: String,
4797}
4798
4799#[derive(Clone)]
4806enum CppMemberOwner {
4807 Chain(Vec<String>),
4811 Unit(CodeUnit),
4814}
4815
4816impl CppMemberOwner {
4817 fn short_chain(&self) -> String {
4819 match self {
4820 Self::Chain(chain) => chain.join("$"),
4821 Self::Unit(parent) => parent.short_name().to_string(),
4822 }
4823 }
4824}
4825
4826enum DeclaratorKind<'a> {
4827 Function(Node<'a>),
4828 Variable(Node<'a>),
4829}
4830
4831impl FunctionInfo {
4832 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
4833 self.code_unit_with_synthetic(file, false)
4834 }
4835
4836 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
4837 let short_name = match &self.owner {
4838 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
4839 None => self.name.clone(),
4840 };
4841 let fq = match &self.owner {
4842 Some(CppMemberOwner::Chain(chain)) => {
4843 debug_assert!(
4844 !chain.is_empty(),
4845 "an empty owner chain is no owner; producers return None instead"
4846 );
4847 let mut fq = FqName::new();
4848 cpp_push_package(&mut fq, &self.package_name);
4849 let mut first = true;
4850 for component in chain {
4851 let kind = if first {
4852 SegmentKind::Type
4853 } else {
4854 SegmentKind::Nested
4855 };
4856 fq.push(cpp_segment(component, kind));
4857 first = false;
4858 }
4859 fq.push(cpp_segment(&self.name, SegmentKind::Member));
4860 fq
4861 }
4862 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
4863 .fq()
4864 .clone()
4865 .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
4866 Some(CppMemberOwner::Unit(_)) | None => {
4869 let mut fq = FqName::new();
4870 cpp_push_package(&mut fq, &self.package_name);
4871 fq.push(cpp_segment(&self.name, SegmentKind::Member));
4872 fq
4873 }
4874 };
4875 CodeUnit::with_signature_and_fq(
4876 file,
4877 CodeUnitType::Function,
4878 self.package_name.clone(),
4879 short_name,
4880 Some(self.signature.clone()),
4881 synthetic,
4882 fq,
4883 )
4884 }
4885}
4886
4887fn extract_function_info(
4888 declarator: Node<'_>,
4889 source: &str,
4890 scope: &ScopeInfo,
4891) -> Option<FunctionInfo> {
4892 let parameters_node = declarator.child_by_field_name("parameters")?;
4893 let declarator_name_node = declarator
4894 .child_by_field_name("declarator")
4895 .or_else(|| parameters_node.prev_named_sibling())?;
4896 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
4897}
4898
4899fn extract_function_info_from_name(
4900 declarator: Node<'_>,
4901 declarator_name_node: Node<'_>,
4902 source: &str,
4903 scope: &ScopeInfo,
4904) -> Option<FunctionInfo> {
4905 let parameters_node = declarator.child_by_field_name("parameters")?;
4906 let parameters_text = cpp_parameter_signature(parameters_node, source);
4907 let recovered_specialization_member = scope
4908 .recovered_specialization_member_scope
4909 .then(|| {
4910 let terminal = declarator_name_node
4911 .child_by_field_name("name")
4912 .unwrap_or(declarator_name_node);
4913 let name = canonical_cpp_qualified_component(terminal, source)?.name;
4914 let owner = scope.class_unit.as_ref()?;
4915 Some((
4916 Some(CppMemberOwner::Unit(owner.clone())),
4917 name,
4918 scope.package_name.clone(),
4919 ))
4920 })
4921 .flatten();
4922 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
4923 parts
4924 } else if let Some(parts) =
4925 split_structured_templated_cpp_name(declarator_name_node, source, scope)
4926 {
4927 parts
4928 } else {
4929 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
4930 declarator_name_node,
4931 source,
4932 )?);
4933 if raw_name.is_empty() {
4934 return None;
4935 }
4936 split_cpp_name(&raw_name, scope)
4937 };
4938 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
4939 let mut signature = if suffix.is_empty() {
4940 parameters_text
4941 } else {
4942 format!("{parameters_text} {suffix}")
4943 };
4944 if let Some(template_signature) = &scope.template_signature {
4945 signature = format!("{template_signature}{signature}");
4946 }
4947
4948 Some(FunctionInfo {
4949 package_name,
4950 owner,
4951 name,
4952 signature,
4953 })
4954}
4955
4956fn cpp_macro_displaced_callable_parts<'tree>(
4963 function_declarator: Node<'tree>,
4964 source: &str,
4965 ancestry: &ParentIndex<'tree>,
4966) -> Option<(Node<'tree>, Node<'tree>)> {
4967 let definition = ancestry.parent(function_declarator)?;
4968 if definition.kind() != "function_definition"
4969 || definition.child_by_field_name("declarator") != Some(function_declarator)
4970 || definition
4971 .child_by_field_name("body")
4972 .is_none_or(|body| body.kind() != "compound_statement")
4973 {
4974 return None;
4975 }
4976 let macro_type = definition.child_by_field_name("type")?;
4977 if macro_type.kind() != "type_identifier"
4978 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
4979 {
4980 return None;
4981 }
4982
4983 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
4984 if apparent_return_type.kind() == "qualified_identifier"
4985 && let (Some(return_type), Some(callable_name)) = (
4986 apparent_return_type.child_by_field_name("scope"),
4987 apparent_return_type.child_by_field_name("name"),
4988 )
4989 && return_type.kind() == "template_type"
4990 && matches!(callable_name.kind(), "identifier" | "field_identifier")
4991 && (0..apparent_return_type.child_count())
4992 .filter_map(|index| apparent_return_type.child(index))
4993 .any(|child| child.kind() == "::" && child.is_missing())
4994 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
4995 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
4996 {
4997 return Some((return_type, callable_name));
4998 }
4999 if !matches!(
5000 apparent_return_type.kind(),
5001 "identifier" | "field_identifier" | "type_identifier"
5002 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
5003 {
5004 return None;
5005 }
5006 let parameters = function_declarator.child_by_field_name("parameters")?;
5007 let mut cursor = function_declarator.walk();
5008 let between = function_declarator
5009 .named_children(&mut cursor)
5010 .filter(|child| child.kind() != "comment")
5011 .filter(|child| {
5012 child.start_byte() >= apparent_return_type.end_byte()
5013 && child.end_byte() <= parameters.start_byte()
5014 && !same_node(*child, apparent_return_type)
5015 && !same_node(*child, parameters)
5016 })
5017 .collect::<Vec<_>>();
5018 let [name_error] = between.as_slice() else {
5019 return None;
5020 };
5021 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
5022 return None;
5023 }
5024 let callable_name = name_error.named_child(0)?;
5025 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
5026 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
5027 {
5028 return None;
5029 }
5030 Some((apparent_return_type, callable_name))
5031}
5032
5033fn cpp_declarator_identity_suffix(
5049 declarator: Node<'_>,
5050 parameters_node: Node<'_>,
5051 source: &str,
5052) -> String {
5053 let mut cursor = declarator.walk();
5054 let parts = declarator
5055 .named_children(&mut cursor)
5056 .filter(|child| child.start_byte() >= parameters_node.end_byte())
5057 .filter(|child| {
5058 matches!(
5059 child.kind(),
5060 "type_qualifier"
5061 | "ref_qualifier"
5062 | "noexcept"
5063 | "throw_specifier"
5064 | "trailing_return_type"
5065 | "requires_clause"
5066 )
5067 })
5068 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
5069 .filter(|text| !text.is_empty())
5070 .collect::<Vec<_>>();
5071 normalize_cpp_qualifier_suffix(&parts.join(" "))
5072}
5073
5074pub(crate) fn cpp_callable_identity_suffix(
5081 function_declarator: Node<'_>,
5082 source: &str,
5083) -> Option<String> {
5084 let parameters_node = function_declarator.child_by_field_name("parameters")?;
5085 Some(cpp_declarator_identity_suffix(
5086 function_declarator,
5087 parameters_node,
5088 source,
5089 ))
5090}
5091
5092fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
5093 match classify_declarator(node)? {
5094 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
5095 DeclaratorKind::Variable(_) => None,
5096 }
5097}
5098
5099fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
5100 match node.kind() {
5101 "function_declarator" => {
5102 let inner = node
5103 .child_by_field_name("declarator")
5104 .or_else(|| node.child_by_field_name("name"))
5105 .or_else(|| last_named_child(node));
5106 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
5107 Some(DeclaratorKind::Variable(node))
5108 } else {
5109 Some(DeclaratorKind::Function(node))
5110 }
5111 }
5112 "init_declarator"
5113 | "pointer_declarator"
5114 | "reference_declarator"
5115 | "parenthesized_declarator"
5116 | "array_declarator"
5117 | "attributed_declarator"
5118 | "template_function" => node
5119 .child_by_field_name("declarator")
5120 .or_else(|| node.child_by_field_name("name"))
5121 .or_else(|| last_named_child(node))
5122 .and_then(classify_declarator),
5123 "identifier" | "field_identifier" | "qualified_identifier" => {
5124 Some(DeclaratorKind::Variable(node))
5125 }
5126 _ => node
5127 .child_by_field_name("declarator")
5128 .or_else(|| node.child_by_field_name("name"))
5129 .or_else(|| last_named_child(node))
5130 .and_then(classify_declarator),
5131 }
5132}
5133
5134fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
5135 matches!(
5136 node.kind(),
5137 "function_declarator"
5138 | "init_declarator"
5139 | "pointer_declarator"
5140 | "reference_declarator"
5141 | "parenthesized_declarator"
5142 | "array_declarator"
5143 | "attributed_declarator"
5144 | "template_function"
5145 | "identifier"
5146 | "field_identifier"
5147 | "qualified_identifier"
5148 )
5149}
5150
5151fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
5152 let class_like = first_class_like_child(node);
5153 let mut cursor = node.walk();
5154 node.named_children(&mut cursor).any(|child| {
5155 matches!(
5156 child.kind(),
5157 "init_declarator"
5158 | "pointer_declarator"
5159 | "reference_declarator"
5160 | "array_declarator"
5161 | "function_declarator"
5162 | "parenthesized_declarator"
5163 | "attributed_declarator"
5164 ) || matches!(
5165 child.kind(),
5166 "identifier" | "field_identifier" | "qualified_identifier"
5167 ) && class_like.is_none_or(|class_node| {
5168 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
5169 })
5170 })
5171}
5172
5173fn unique_earlier_cpp_namespace_forward<'tree>(
5185 recovered_node: Node<'tree>,
5186 name: &str,
5187 source: &str,
5188 ancestry: &ParentIndex<'tree>,
5189) -> Option<String> {
5190 let mut root = recovered_node;
5191 while let Some(parent) = ancestry.parent(root) {
5192 root = parent;
5193 }
5194
5195 let mut candidates = Vec::new();
5196 let mut stack = vec![root];
5197 while let Some(current) = stack.pop() {
5198 if current.start_byte() < recovered_node.start_byte()
5199 && matches!(
5200 current.kind(),
5201 "class_specifier" | "struct_specifier" | "union_specifier"
5202 )
5203 && cpp_body_node(current).is_none()
5204 && current.parent().is_some_and(|parent| {
5205 parent.kind() == "declaration_list"
5206 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent)
5207 })
5208 && class_like_name(current, source, ancestry).as_deref() == Some(name)
5209 && cpp_namespace_definition_for_forward(current, ancestry).is_some_and(|namespace| {
5210 namespace.has_error()
5216 && namespace.end_byte() < recovered_node.start_byte()
5217 && malformed_namespace_is_nearest_recovery_region(namespace, recovered_node)
5218 })
5219 && let Some(package_name) = cpp_namespace_name_for_forward(current, source, ancestry)
5220 {
5221 candidates.push(package_name);
5222 }
5223
5224 let mut cursor = current.walk();
5225 for child in current.named_children(&mut cursor) {
5226 if child.start_byte() < recovered_node.start_byte() {
5227 stack.push(child);
5228 }
5229 }
5230 }
5231
5232 if candidates.len() == 1 {
5233 candidates.pop()
5234 } else {
5235 None
5236 }
5237}
5238
5239fn malformed_namespace_is_nearest_recovery_region(
5240 namespace: Node<'_>,
5241 recovered_node: Node<'_>,
5242) -> bool {
5243 let mut root = recovered_node;
5244 while let Some(parent) = root.parent() {
5245 root = parent;
5246 }
5247 let mut cursor = root.walk();
5248 root.named_children(&mut cursor)
5249 .filter(|sibling| {
5250 namespace.end_byte() <= sibling.start_byte()
5251 && sibling.end_byte() <= recovered_node.start_byte()
5252 })
5253 .all(is_malformed_namespace_recovery_trivia)
5254}
5255
5256fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
5257 matches!(node.kind(), "ERROR" | "comment")
5258 || node.kind().starts_with("preproc_")
5259 || node.kind() == "expression_statement" && node.named_child_count() == 0
5260}
5261
5262fn cpp_namespace_name_for_forward<'tree>(
5266 node: Node<'tree>,
5267 source: &str,
5268 ancestry: &ParentIndex<'tree>,
5269) -> Option<String> {
5270 cpp_namespace_definition_for_forward(node, ancestry)?;
5271 cpp_lexical_namespace_name(node, source, ancestry)
5272}
5273
5274fn cpp_namespace_definition_for_forward<'tree>(
5275 node: Node<'tree>,
5276 ancestry: &ParentIndex<'tree>,
5277) -> Option<Node<'tree>> {
5278 let declaration = ancestry.parent(node)?;
5279 let mut ancestor = ancestry.parent(declaration);
5280 while let Some(current) = ancestor {
5281 if matches!(
5282 current.kind(),
5283 "compound_statement"
5284 | "field_declaration_list"
5285 | "class_specifier"
5286 | "struct_specifier"
5287 | "union_specifier"
5288 | "function_definition"
5289 | "lambda_expression"
5290 ) {
5291 return None;
5292 }
5293 if current.kind() == "namespace_definition" {
5294 return Some(current);
5295 }
5296 ancestor = ancestry.parent(current);
5297 }
5298 None
5299}
5300
5301fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
5302 match node.kind() {
5303 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
5304 "parenthesized_declarator" => node
5305 .child_by_field_name("declarator")
5306 .or_else(|| last_named_child(node))
5307 .is_some_and(is_pointer_wrapper_declarator),
5308 "template_function" => node
5309 .child_by_field_name("name")
5310 .is_some_and(is_function_pointer_like_inner_declarator),
5311 _ => false,
5312 }
5313}
5314
5315fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
5316 match node.kind() {
5317 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
5318 "parenthesized_declarator" => node
5319 .child_by_field_name("declarator")
5320 .or_else(|| last_named_child(node))
5321 .is_some_and(is_pointer_wrapper_declarator),
5322 _ => false,
5323 }
5324}
5325
5326fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
5327 let cleaned = raw_name.trim_start_matches("template ").trim();
5328 let cleaned = cleaned.trim_start_matches("::");
5335 let parts: Vec<_> = cleaned
5344 .split("::")
5345 .filter(|component| !component.is_empty())
5346 .collect();
5347 if parts.is_empty() {
5348 return (None, cleaned.to_string(), scope.package_name.clone());
5349 }
5350 if parts.len() > 1 {
5351 let name = parts.last().unwrap_or(&cleaned).to_string();
5352 let owner_parts = &parts[..parts.len() - 1];
5353 if let Some(class_unit) = &scope.class_unit {
5354 return (
5357 Some(CppMemberOwner::Unit(class_unit.clone())),
5358 name,
5359 scope.package_name.clone(),
5360 );
5361 }
5362 if !scope.package_name.is_empty() {
5363 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
5378 let owner = (!nested.is_empty()).then(|| {
5379 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
5380 });
5381 return (owner, name, scope.package_name.clone());
5382 }
5383 let (owner, package_name) = if owner_parts.len() > 1 {
5385 (
5397 Some(CppMemberOwner::Chain(vec![
5398 owner_parts.last().unwrap_or(&"").to_string(),
5399 ])),
5400 owner_parts[..owner_parts.len() - 1].join("::"),
5401 )
5402 } else {
5403 (
5415 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
5416 cpp_using_directive_namespace_for_bare_owner(scope),
5417 )
5418 };
5419 return (owner, name, package_name);
5420 }
5421
5422 let package_name = scope.package_name.clone();
5423 let owner = scope
5424 .class_unit
5425 .as_ref()
5426 .map(|parent| CppMemberOwner::Unit(parent.clone()));
5427 (owner, cleaned.to_string(), package_name)
5428}
5429
5430fn strip_redundant_namespace_prefix<'a>(
5444 owner_parts: &'a [&'a str],
5445 package_name: &str,
5446) -> &'a [&'a str] {
5447 if package_name.is_empty() {
5448 return owner_parts;
5449 }
5450 let package_segments: Vec<&str> = package_name.split("::").collect();
5451 let max_prefix = owner_parts.len().min(package_segments.len());
5452 for prefix_len in (1..=max_prefix).rev() {
5453 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
5454 if &owner_parts[..prefix_len] == package_suffix {
5455 return &owner_parts[prefix_len..];
5456 }
5457 }
5458 owner_parts
5459}
5460
5461fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
5472 scope
5473 .visible_using_namespaces
5474 .iter()
5475 .min_by_key(|namespace| namespace.split("::").count())
5476 .cloned()
5477 .unwrap_or_default()
5478}
5479
5480struct CppQualifiedNameComponent {
5481 name: String,
5482 is_template_id: bool,
5483}
5484
5485fn qualified_class_name_chain(
5498 class_node: Node<'_>,
5499 source: &str,
5500 scope: &ScopeInfo,
5501) -> Option<Vec<String>> {
5502 if scope.package_name.is_empty() || scope.class_unit.is_some() {
5503 return None;
5504 }
5505 let name = class_node.child_by_field_name("name")?;
5506 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
5507 if explicitly_global
5508 || components.len() < 2
5509 || components.iter().any(|component| component.is_template_id)
5510 {
5511 return None;
5512 }
5513 let names = components
5514 .iter()
5515 .map(|component| component.name.as_str())
5516 .collect::<Vec<_>>();
5517 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
5518 if class_chain.is_empty() {
5519 return None;
5520 }
5521 Some(class_chain.iter().map(|name| name.to_string()).collect())
5522}
5523
5524fn structured_cpp_qualified_components(
5525 qualified_name: Node<'_>,
5526 source: &str,
5527) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
5528 if qualified_name.kind() != "qualified_identifier" {
5529 return None;
5530 }
5531
5532 let mut components = Vec::new();
5533 let mut current = qualified_name;
5534 let mut explicitly_global = false;
5535 loop {
5536 if current.kind() == "qualified_identifier" {
5537 if let Some(component) = current.child_by_field_name("scope") {
5538 components.push(canonical_cpp_qualified_component(component, source)?);
5539 } else if components.is_empty() {
5540 explicitly_global = true;
5541 } else {
5542 return None;
5543 }
5544 current = current.child_by_field_name("name")?;
5545 } else {
5546 components.push(canonical_cpp_qualified_component(current, source)?);
5547 break;
5548 }
5549 }
5550 Some((components, explicitly_global))
5551}
5552
5553fn split_structured_templated_cpp_name(
5554 declarator_name: Node<'_>,
5555 source: &str,
5556 scope: &ScopeInfo,
5557) -> Option<(Option<CppMemberOwner>, String, String)> {
5558 let (mut components, explicitly_global) =
5559 structured_cpp_qualified_components(declarator_name, source)?;
5560
5561 let terminal = components.pop()?;
5562 let owner_start = components
5563 .iter()
5564 .position(|component| component.is_template_id)?;
5565 let explicit_package = components[..owner_start]
5566 .iter()
5567 .map(|component| component.name.as_str())
5568 .collect::<Vec<_>>()
5569 .join("::");
5570 let explicit_package_is_empty = explicit_package.is_empty();
5571 let package_name = match (
5572 explicitly_global,
5573 scope.package_name.is_empty(),
5574 explicit_package_is_empty,
5575 ) {
5576 (true, _, _) => explicit_package,
5577 (false, _, true) => scope.package_name.clone(),
5578 (false, true, false) => explicit_package,
5579 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
5580 };
5581 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
5587 {
5588 cpp_using_directive_namespace_for_bare_owner(scope)
5589 } else {
5590 package_name
5591 };
5592 let owner_chain = components[owner_start..]
5593 .iter()
5594 .map(|component| component.name.clone())
5595 .collect::<Vec<_>>();
5596 if owner_chain.is_empty() || terminal.name.is_empty() {
5597 return None;
5598 }
5599
5600 Some((
5601 Some(CppMemberOwner::Chain(owner_chain)),
5602 terminal.name,
5603 package_name,
5604 ))
5605}
5606
5607fn canonical_cpp_qualified_component(
5608 mut component: Node<'_>,
5609 source: &str,
5610) -> Option<CppQualifiedNameComponent> {
5611 let mut is_template_id = false;
5612 loop {
5613 match component.kind() {
5614 "template_type" => {
5615 is_template_id = true;
5616 component = component.child_by_field_name("name")?;
5617 }
5618 "dependent_name" => component = component.named_child(0)?,
5619 "identifier"
5620 | "field_identifier"
5621 | "namespace_identifier"
5622 | "type_identifier"
5623 | "operator_name"
5624 | "destructor_name" => {
5625 let name = normalize_cpp_whitespace(node_text(component, source));
5626 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
5627 name,
5628 is_template_id,
5629 });
5630 }
5631 _ => component = component.child_by_field_name("name")?,
5632 }
5633 }
5634}
5635
5636fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
5637 match node.kind() {
5638 "identifier"
5639 | "field_identifier"
5640 | "type_identifier"
5641 | "operator_name"
5642 | "destructor_name"
5643 | "qualified_identifier" => node_text(node, source).to_string(),
5644 "function_declarator"
5645 | "pointer_declarator"
5646 | "reference_declarator"
5647 | "parenthesized_declarator"
5648 | "array_declarator"
5649 | "template_function" => node
5650 .child_by_field_name("declarator")
5651 .or_else(|| node.child_by_field_name("name"))
5652 .or_else(|| last_named_child(node))
5653 .map(|child| extract_declarator_name(child, source))
5654 .unwrap_or_else(|| node_text(node, source).to_string()),
5655 _ => node
5656 .child_by_field_name("name")
5657 .map(|child| extract_declarator_name(child, source))
5658 .unwrap_or_else(|| node_text(node, source).to_string()),
5659 }
5660}
5661
5662fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
5667 match node.kind() {
5668 "identifier"
5669 | "field_identifier"
5670 | "type_identifier"
5671 | "operator_name"
5672 | "destructor_name"
5673 | "qualified_identifier" => Some(node_text(node, source).to_string()),
5674 "function_declarator"
5675 | "pointer_declarator"
5676 | "reference_declarator"
5677 | "parenthesized_declarator"
5678 | "array_declarator"
5679 | "template_function" => node
5680 .child_by_field_name("declarator")
5681 .or_else(|| node.child_by_field_name("name"))
5682 .and_then(|child| extract_callable_declarator_name(child, source)),
5683 _ => None,
5684 }
5685}
5686
5687fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
5688 match node.kind() {
5689 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
5690 let name = node_text(node, source).trim().to_string();
5691 (!name.is_empty()).then_some(name)
5692 }
5693 _ => node
5694 .child_by_field_name("declarator")
5695 .or_else(|| node.child_by_field_name("name"))
5696 .or_else(|| last_named_child(node))
5697 .and_then(|child| extract_variable_name(child, source)),
5698 }
5699}
5700
5701fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
5702 let count = node.named_child_count();
5703 if count == 0 {
5704 None
5705 } else {
5706 node.named_child(count - 1)
5707 }
5708}
5709
5710fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
5711 let name_node = node.child_by_field_name("name")?;
5712 let name = normalize_cpp_whitespace(node_text(name_node, source));
5713 (!name.is_empty()).then_some(name)
5714}
5715
5716fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5717 if node.kind() != "declaration" {
5718 return Vec::new();
5719 }
5720 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
5721 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
5722 }) else {
5723 return Vec::new();
5724 };
5725 let Some(declarator) = node.child_by_field_name("declarator") else {
5726 return Vec::new();
5727 };
5728 if node_text(keyword, source) == "using"
5729 && (declarator.kind() != "init_declarator"
5730 || declarator.child_by_field_name("value").is_none())
5731 {
5732 return Vec::new();
5733 }
5734 if node_text(keyword, source) == "typedef"
5735 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
5736 {
5737 return vec![alias_name];
5738 }
5739 extract_typedef_declarator_name(declarator, source)
5740 .into_iter()
5741 .collect()
5742}
5743
5744fn recovered_typedef_error_alias_name(
5745 declaration: Node<'_>,
5746 declarator: Node<'_>,
5747 source: &str,
5748) -> Option<String> {
5749 if declarator.kind() != "qualified_identifier" {
5759 return None;
5760 }
5761 let mut cursor = declaration.walk();
5762 let mut errors = declaration
5763 .named_children(&mut cursor)
5764 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
5765 let error = errors.next()?;
5766 if errors.next().is_some() || error.named_child_count() != 1 {
5767 return None;
5768 }
5769 let name = error.named_child(0)?;
5770 if !matches!(
5771 name.kind(),
5772 "identifier" | "field_identifier" | "type_identifier"
5773 ) {
5774 return None;
5775 }
5776 let name = normalize_cpp_whitespace(node_text(name, source));
5777 (!name.is_empty()).then_some(name)
5778}
5779
5780fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5781 if fragmented_parenthesized_typedef_type(node).is_some() {
5785 return Vec::new();
5786 }
5787 let has_function_like_macro_type = node
5788 .child_by_field_name("type")
5789 .filter(|type_node| type_node.kind() == "type_identifier")
5790 .is_some_and(|type_node| {
5791 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5792 });
5793 let mut names = Vec::new();
5794 let mut cursor = node.walk();
5795 for declarator in node.children_by_field_name("declarator", &mut cursor) {
5796 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
5797 continue;
5798 }
5799 if let Some(name) = extract_typedef_declarator_name(declarator, source)
5800 && !names.contains(&name)
5801 {
5802 names.push(name);
5803 }
5804 }
5805 names
5806}
5807
5808struct RecoveredMacroTypedefAlias<'tree> {
5809 name: String,
5810 end_node: Node<'tree>,
5811}
5812
5813fn recovered_macro_typedef_alias<'tree>(
5817 node: Node<'tree>,
5818 source: &str,
5819) -> Option<RecoveredMacroTypedefAlias<'tree>> {
5820 let type_node = fragmented_parenthesized_typedef_type(node)?;
5821 if type_node.kind() != "type_identifier"
5822 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5823 {
5824 return None;
5825 }
5826
5827 let end_node = node.next_named_sibling()?;
5828 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
5829 return None;
5830 }
5831 let name_node = end_node.named_child(0)?;
5832 if name_node.kind() != "identifier" {
5833 return None;
5834 }
5835 let has_terminator = (0..end_node.child_count()).any(|index| {
5836 end_node
5837 .child(index)
5838 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
5839 });
5840 if !has_terminator {
5841 return None;
5842 }
5843 let name = normalize_cpp_whitespace(node_text(name_node, source));
5844 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
5845}
5846
5847fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
5848 if node.kind() != "type_definition" {
5849 return None;
5850 }
5851 let mut declarator_cursor = node.walk();
5852 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
5853 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
5854 return None;
5855 }
5856 let has_missing_terminator = (0..node.child_count()).any(|index| {
5857 node.child(index)
5858 .is_some_and(|child| child.kind() == ";" && child.is_missing())
5859 });
5860 if !has_missing_terminator {
5861 return None;
5862 }
5863 node.child_by_field_name("type")
5864}
5865
5866fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
5867 match node.kind() {
5868 "identifier" | "field_identifier" | "type_identifier" => {
5869 let name = normalize_cpp_whitespace(node_text(node, source));
5870 (!name.is_empty()).then_some(name)
5871 }
5872 "qualified_identifier" => node
5873 .child_by_field_name("name")
5874 .and_then(|name| extract_typedef_declarator_name(name, source)),
5875 _ => node
5876 .child_by_field_name("declarator")
5877 .or_else(|| node.child_by_field_name("name"))
5878 .or_else(|| last_named_child(node))
5879 .and_then(|child| extract_typedef_declarator_name(child, source)),
5880 }
5881}
5882
5883fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
5884 let name = node
5885 .child_by_field_name("name")
5886 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5887 .or_else(|| {
5888 let mut cursor = node.walk();
5889 node.named_children(&mut cursor)
5890 .find(|child| {
5891 matches!(
5892 child.kind(),
5893 "identifier" | "field_identifier" | "type_identifier"
5894 )
5895 })
5896 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5897 })?;
5898 (!name.is_empty()).then_some(name)
5899}
5900
5901fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
5902 left.id() == right.id()
5903}
5904
5905fn render_cpp_type_signature(
5906 node: Node<'_>,
5907 source: &str,
5908 template_signature: Option<&str>,
5909) -> String {
5910 let text = normalize_cpp_whitespace(node_text(node, source));
5911 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
5912 let rendered = if head.ends_with(';') {
5913 head.to_string()
5914 } else {
5915 format!("{head} {{")
5916 };
5917 if let Some(template_signature) = template_signature {
5918 format!("template {template_signature} {rendered}")
5919 } else {
5920 rendered
5921 }
5922}
5923
5924fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
5925 if let Some(signature) =
5926 render_recovered_macro_qualified_field_signature(node, declarator, source)
5927 {
5928 return signature;
5929 }
5930 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
5931 let prefix = cpp_declaration_prefix(node, source);
5932 let name = extract_variable_name(declarator, source).unwrap_or_default();
5933 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
5934 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
5935 || (prefix.ends_with('&') && raw_suffix == "&")
5936 {
5937 String::new()
5938 } else {
5939 raw_suffix
5940 };
5941
5942 let mut rendered = if suffix.is_empty() {
5943 format!("{prefix} {name}")
5944 } else if suffix.starts_with('*') || suffix.starts_with('&') {
5945 format!("{prefix}{suffix} {name}")
5946 } else if suffix.starts_with('[') || suffix.starts_with('(') {
5947 format!("{prefix} {name}{suffix}")
5948 } else {
5949 format!("{prefix} {suffix}{name}")
5950 };
5951 rendered = collapse_cpp_whitespace(&rendered);
5952
5953 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
5954 format!("{rendered} = {initializer};")
5955 } else if declaration_text.ends_with(';') {
5956 format!("{rendered};")
5957 } else {
5958 rendered
5959 }
5960}
5961
5962fn render_recovered_macro_qualified_field_signature(
5963 node: Node<'_>,
5964 declarator: Node<'_>,
5965 source: &str,
5966) -> Option<String> {
5967 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
5968 if !recovered
5969 .iter()
5970 .any(|candidate| same_node(*candidate, declarator))
5971 {
5972 return None;
5973 }
5974 let pseudo_declarator = node.child_by_field_name("declarator")?;
5975 let mut cursor = node.walk();
5976 let clause = node
5977 .named_children(&mut cursor)
5978 .find(|child| child.kind() == "bitfield_clause")?;
5979 let mut cursor = clause.walk();
5980 let error = clause
5981 .named_children(&mut cursor)
5982 .find(|child| child.kind() == "ERROR")?;
5983 let qualified_type =
5984 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
5985 let prefix = cpp_declaration_prefix(node, source);
5986 let name = extract_variable_name(declarator, source)?;
5987 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
5988 let mut rendered = if suffix.is_empty() {
5989 format!("{prefix} {qualified_type} {name}")
5990 } else {
5991 format!("{prefix} {qualified_type} {suffix} {name}")
5992 };
5993 rendered = collapse_cpp_whitespace(&rendered);
5994
5995 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
5996 Some(format!(
5997 "{rendered} = {};",
5998 normalize_cpp_whitespace(node_text(initializer, source))
5999 ))
6000 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
6001 Some(format!("{rendered} = {initializer};"))
6002 } else {
6003 Some(format!("{rendered};"))
6004 }
6005}
6006
6007fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
6008 match node.kind() {
6009 "pointer_expression" => {
6010 let operator = node
6011 .child_by_field_name("operator")
6012 .or_else(|| node.child(0))
6013 .map(|operator| node_text(operator, source))
6014 .unwrap_or("*");
6015 let argument = node
6016 .child_by_field_name("argument")
6017 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
6018 .unwrap_or_default();
6019 format!("{operator}{argument}")
6020 }
6021 "unary_expression" => {
6022 let operator = node
6023 .child_by_field_name("operator")
6024 .or_else(|| node.child(0))
6025 .map(|operator| node_text(operator, source))
6026 .unwrap_or_default();
6027 let argument = node
6028 .child_by_field_name("argument")
6029 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
6030 .unwrap_or_default();
6031 format!("{operator}{argument}")
6032 }
6033 "identifier" | "field_identifier" => String::new(),
6034 _ => cpp_declarator_suffix_without_name(node, source),
6035 }
6036}
6037
6038fn recovered_macro_qualified_field_initializer<'tree>(
6039 clause: Node<'tree>,
6040 declarator: Node<'tree>,
6041) -> Option<Node<'tree>> {
6042 let mut stack = vec![clause];
6043 while let Some(current) = stack.pop() {
6044 if current.kind() == "assignment_expression"
6045 && current
6046 .child_by_field_name("left")
6047 .is_some_and(|left| same_node(left, declarator))
6048 {
6049 return current.child_by_field_name("right");
6050 }
6051 let mut cursor = current.walk();
6052 stack.extend(current.named_children(&mut cursor));
6053 }
6054 None
6055}
6056
6057fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
6058 let text = node_text(node, source);
6059 let mut cursor = node.walk();
6060 let first_declarator = node.named_children(&mut cursor).find(|child| {
6061 matches!(
6062 child.kind(),
6063 "init_declarator"
6064 | "identifier"
6065 | "field_identifier"
6066 | "pointer_declarator"
6067 | "reference_declarator"
6068 | "array_declarator"
6069 | "function_declarator"
6070 )
6071 });
6072 let prefix = if let Some(first_declarator) = first_declarator {
6073 let end = first_declarator
6074 .start_byte()
6075 .saturating_sub(node.start_byte());
6076 let mut prefix = text.get(..end).unwrap_or(text).to_string();
6077 let declarator_suffix = match first_declarator.kind() {
6078 "init_declarator" => first_declarator
6079 .child_by_field_name("declarator")
6080 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
6081 .unwrap_or_default(),
6082 _ => cpp_declarator_suffix_without_name(first_declarator, source),
6083 };
6084 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
6085 prefix.push_str(&declarator_suffix);
6086 }
6087 return collapse_cpp_whitespace(&prefix)
6088 .trim_end_matches(',')
6089 .trim_end_matches(';')
6090 .trim()
6091 .to_string();
6092 } else {
6093 text
6094 };
6095 collapse_cpp_whitespace(prefix)
6096 .trim_end_matches(',')
6097 .trim_end_matches(';')
6098 .trim()
6099 .to_string()
6100}
6101
6102fn cpp_preserved_initializer(
6103 declaration_node: Node<'_>,
6104 declarator: Node<'_>,
6105 source: &str,
6106) -> Option<String> {
6107 let name = extract_variable_name(declarator, source)?;
6108 let mut cursor = declaration_node.walk();
6109 for child in declaration_node.named_children(&mut cursor) {
6110 if child.kind() != "init_declarator" {
6111 continue;
6112 }
6113 let Some(inner) = child.child_by_field_name("declarator") else {
6114 continue;
6115 };
6116 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
6117 continue;
6118 }
6119 let value = child.child_by_field_name("value")?;
6120 let kind = value.kind();
6121 if matches!(
6122 kind,
6123 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
6124 ) {
6125 return Some(normalize_cpp_whitespace(node_text(value, source)));
6126 }
6127 break;
6128 }
6129 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
6130 let pattern = format!(
6131 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
6132 regex::escape(&name)
6133 );
6134 Regex::new(&pattern)
6135 .ok()
6136 .and_then(|regex| regex.captures(&declaration_text))
6137 .and_then(|captures| captures.get(1))
6138 .map(|value| value.as_str().to_string())
6139}
6140
6141fn render_cpp_function_display_signature_from_node<'tree>(
6142 node: Node<'tree>,
6143 source: &str,
6144 template_signature: Option<&str>,
6145 has_body: bool,
6146 ancestry: &ParentIndex<'tree>,
6147) -> String {
6148 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
6149 let parent_text = node_text(root, source);
6150 let body_local_start = root
6151 .child_by_field_name("body")
6152 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
6153 .unwrap_or(parent_text.len());
6154 let display = parent_text
6155 .get(..body_local_start)
6156 .unwrap_or(parent_text)
6157 .trim()
6158 .trim();
6159 let display = if let Some(template_signature) = template_signature {
6160 if display.starts_with("template ") {
6161 display.to_string()
6162 } else {
6163 format!("template {template_signature} {display}")
6164 }
6165 } else {
6166 display.to_string()
6167 };
6168 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
6169 if has_body {
6170 format!("{display} {{...}}")
6171 } else {
6172 format!("{display};")
6173 }
6174}
6175
6176fn cpp_template_signature(
6177 template_node: Node<'_>,
6178 declaration_child: Node<'_>,
6179 source: &str,
6180) -> Option<String> {
6181 let text = source
6182 .get(template_node.start_byte()..declaration_child.start_byte())
6183 .unwrap_or("");
6184 let text = normalize_cpp_whitespace(text);
6185 let start = text.find('<')?;
6186 let end = text.rfind('>')?;
6187 if end < start {
6188 return None;
6189 }
6190 Some(text[start..=end].to_string())
6191}
6192
6193struct RecoveredFragmentedPartialSpecialization<'tree> {
6194 declaration_node: Node<'tree>,
6195 name: String,
6196 range: Range,
6197 prefix_members: Vec<Node<'tree>>,
6198 member_siblings: Vec<Node<'tree>>,
6199 following_declarations: Vec<Node<'tree>>,
6200}
6201
6202struct RecoveredFragmentedPreprocessorClass<'tree> {
6203 declaration_node: Node<'tree>,
6204 class_node: Node<'tree>,
6205 body: Node<'tree>,
6206 name: String,
6207 range: Range,
6208 tail_members: Vec<Node<'tree>>,
6209 member_siblings: Vec<Node<'tree>>,
6210}
6211
6212fn recover_fragmented_preprocessor_class<'tree>(
6221 template_node: Node<'tree>,
6222 source: &str,
6223 ancestry: &ParentIndex<'tree>,
6224) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
6225 let alternative = ancestry.parent(template_node)?;
6226 if alternative.kind() != "preproc_else" {
6227 return None;
6228 }
6229 let conditional = alternative.parent()?;
6230 if conditional.kind() != "preproc_if" {
6231 return None;
6232 }
6233 let declaration_node = template_node
6234 .named_children(&mut template_node.walk())
6235 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
6236 let class_node = declaration_node
6237 .named_children(&mut declaration_node.walk())
6238 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
6239 let body = cpp_body_node(class_node)?;
6240 if class_node.end_byte() >= declaration_node.end_byte() {
6241 return None;
6242 }
6243 let name = class_like_name(class_node, source, ancestry)?;
6244 let is_partial_specialization = class_node
6245 .child_by_field_name("name")
6246 .is_some_and(|class_name| class_name.kind() == "template_type");
6247 if is_partial_specialization {
6248 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
6249 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
6250 return None;
6251 }
6252 } else {
6253 if !class_has_displaced_preprocessor_terminator(class_node) {
6254 return None;
6255 }
6256 let matching_other_branch = conditional
6257 .named_children(&mut conditional.walk())
6258 .take_while(|child| !same_node(*child, alternative))
6259 .filter(|child| child.kind() == "template_declaration")
6260 .filter_map(first_class_like_child)
6261 .any(|candidate| {
6262 cpp_body_node(candidate).is_none()
6263 && class_like_name(candidate, source, ancestry).as_deref()
6264 == Some(name.as_str())
6265 });
6266 if !matching_other_branch {
6267 return None;
6268 }
6269 }
6270
6271 let mut tail_members = Vec::new();
6272 let mut saw_class = false;
6273 let mut declaration_cursor = declaration_node.walk();
6274 for child in declaration_node.named_children(&mut declaration_cursor) {
6275 if same_node(child, class_node) {
6276 saw_class = true;
6277 } else if saw_class {
6278 tail_members.push(child);
6279 }
6280 }
6281
6282 let mut member_siblings = Vec::new();
6283 let mut saw_template = false;
6284 let mut terminator = None;
6285 for index in 0..alternative.child_count() {
6286 let Some(child) = alternative.child(index) else {
6287 continue;
6288 };
6289 if same_node(child, template_node) {
6290 saw_template = true;
6291 continue;
6292 }
6293 if !saw_template {
6294 continue;
6295 }
6296 if displaced_fragmented_class_terminator(alternative, index) {
6297 terminator = alternative.child(index + 1);
6298 break;
6299 }
6300 if child.is_named() {
6301 member_siblings.push(child);
6302 }
6303 }
6304 let terminator = terminator?;
6305 Some(RecoveredFragmentedPreprocessorClass {
6306 declaration_node,
6307 class_node,
6308 body,
6309 name,
6310 range: Range {
6311 start_byte: class_node.start_byte(),
6312 end_byte: terminator.end_byte(),
6313 start_line: class_node.start_position().row + 1,
6314 end_line: terminator.end_position().row + 1,
6315 },
6316 tail_members,
6317 member_siblings,
6318 })
6319}
6320
6321fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
6322 (0..class_node.child_count()).any(|index| {
6323 class_node.child(index).is_some_and(|child| {
6324 child.kind() == "ERROR"
6325 && (0..child.child_count()).any(|error_index| {
6326 child
6327 .child(error_index)
6328 .is_some_and(|token| token.kind() == "#endif")
6329 })
6330 })
6331 })
6332}
6333
6334pub fn cpp_displaced_preprocessor_terminator<'tree>(
6343 conditional: Node<'tree>,
6344) -> Option<Node<'tree>> {
6345 if !conditional.has_error() {
6346 return None;
6347 }
6348 let has_concrete_direct_terminator = conditional
6349 .child_count()
6350 .checked_sub(1)
6351 .and_then(|index| conditional.child(index))
6352 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
6353 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
6354 return None;
6358 }
6359 let mut displaced = None;
6360 let mut stack = (0..conditional.child_count())
6361 .filter_map(|index| conditional.child(index))
6362 .map(|child| (child, false))
6363 .collect::<Vec<_>>();
6364 while let Some((node, inside_error)) = stack.pop() {
6365 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
6366 continue;
6367 }
6368 if node.kind() == "#endif" && !node.is_missing() && inside_error {
6369 if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
6370 displaced = Some(node);
6371 }
6372 continue;
6373 }
6374 if node != conditional
6375 && matches!(
6376 node.kind(),
6377 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
6378 )
6379 {
6380 continue;
6381 }
6382 let inside_error = inside_error || node.kind() == "ERROR";
6383 for index in 0..node.child_count() {
6384 if let Some(child) = node.child(index) {
6385 stack.push((child, inside_error));
6386 }
6387 }
6388 }
6389 displaced
6390}
6391
6392#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6405pub struct CppDisplacedPreprocessorBoundary {
6406 pub end_byte: usize,
6407 pub end_line: usize,
6408}
6409
6410pub fn cpp_displaced_preprocessor_boundary(
6411 conditional: Node<'_>,
6412) -> Option<CppDisplacedPreprocessorBoundary> {
6413 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
6414 return Some(CppDisplacedPreprocessorBoundary {
6415 end_byte: terminator.end_byte(),
6416 end_line: terminator.end_position().row + 1,
6417 });
6418 }
6419 if let Some(declaration) = displaced_split_declaration(conditional) {
6420 return Some(CppDisplacedPreprocessorBoundary {
6421 end_byte: declaration.end_byte(),
6422 end_line: declaration.end_position().row + 1,
6423 });
6424 }
6425 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
6426 return Some(CppDisplacedPreprocessorBoundary {
6427 end_byte: terminator.end_byte(),
6428 end_line: terminator.end_position().row + 1,
6429 });
6430 }
6431 None
6432}
6433
6434fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
6435 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
6436 return None;
6437 }
6438 let mut cursor = conditional.walk();
6439 let declarations = conditional
6440 .named_children(&mut cursor)
6441 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
6442 .collect::<Vec<_>>();
6443 let declaration = *declarations.first()?;
6444 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
6445 return None;
6446 }
6447 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
6448 let mut terminator = None;
6449 let mut stack = (0..declaration.child_count())
6450 .filter_map(|index| declaration.child(index))
6451 .filter(|child| child.start_byte() < declarator_start)
6452 .map(|child| (child, false))
6453 .collect::<Vec<_>>();
6454 while let Some((node, inside_error)) = stack.pop() {
6455 let inside_error = inside_error || node.kind() == "ERROR";
6456 if inside_error && node.kind() == "#endif" && !node.is_missing() {
6457 terminator = Some(node);
6458 continue;
6459 }
6460 for index in 0..node.child_count() {
6461 if let Some(child) = node.child(index)
6462 && child.start_byte() < declarator_start
6463 {
6464 stack.push((child, inside_error));
6465 }
6466 }
6467 }
6468 terminator
6469}
6470
6471fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
6472 if !conditional.has_error()
6473 || conditional.child_by_field_name("alternative").is_some()
6474 || conditional
6475 .prev_named_sibling()
6476 .filter(|sibling| {
6477 sibling.kind() == "ERROR"
6478 && sibling.child_count() == 1
6479 && sibling
6480 .child(0)
6481 .is_some_and(|child| child.kind() == "typedef")
6482 })
6483 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
6484 .is_none()
6485 {
6486 return None;
6487 }
6488 let mut cursor = conditional.walk();
6489 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
6490 let declaration_index = children
6491 .iter()
6492 .position(|child| child.kind() == "declaration" && child.has_error())?;
6493 let declaration = children[declaration_index];
6494 if !children
6495 .iter()
6496 .skip(declaration_index + 1)
6497 .any(|child| child.end_byte() > declaration.end_byte())
6498 {
6499 return None;
6500 }
6501 let declarator = declaration.child_by_field_name("declarator")?;
6502 let mut error_end = None;
6503 let mut names = Vec::new();
6504 let mut stack = vec![declarator];
6505 while let Some(node) = stack.pop() {
6506 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
6507 error_end =
6508 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
6509 continue;
6510 }
6511 if matches!(node.kind(), "identifier" | "type_identifier") {
6512 names.push(node.start_byte());
6513 }
6514 for index in (0..node.named_child_count()).rev() {
6515 if let Some(child) = node.named_child(index) {
6516 stack.push(child);
6517 }
6518 }
6519 }
6520 let error_end = error_end?;
6521 names
6522 .into_iter()
6523 .any(|start| start >= error_end)
6524 .then_some(declaration)
6525}
6526
6527fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
6528 let Some(error) = parent.child(error_index) else {
6529 return false;
6530 };
6531 if error.kind() != "ERROR"
6532 || error.child_count() != 1
6533 || error.child(0).is_none_or(|child| child.kind() != "}")
6534 {
6535 return false;
6536 }
6537 let Some(semicolon) = parent.child(error_index + 1) else {
6538 return false;
6539 };
6540 semicolon.kind() == "expression_statement"
6541 && semicolon.child_count() == 1
6542 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
6543}
6544
6545fn displaced_macro_class_tail(
6551 declaration_node: Node<'_>,
6552 body: Node<'_>,
6553 source: &str,
6554) -> Option<DisplacedMacroClassTail> {
6555 if !matches!(
6556 declaration_node.kind(),
6557 "class_specifier" | "struct_specifier" | "union_specifier"
6558 ) || body.kind() != "field_declaration_list"
6559 {
6560 return None;
6561 }
6562
6563 let child_count = body.named_child_count();
6564 for index in 0..child_count {
6565 let child = body.named_child(index)?;
6566 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
6567 continue;
6568 };
6569 let split_index = index + 1;
6570 if split_index >= child_count {
6571 return None;
6572 }
6573 let mut cursor = body.walk();
6574 if !body
6575 .named_children(&mut cursor)
6576 .skip(split_index)
6577 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
6578 {
6579 return None;
6580 }
6581 return Some(DisplacedMacroClassTail {
6582 split_index,
6583 class_range: Range {
6584 start_byte: declaration_node.start_byte(),
6585 end_byte: terminator.end_byte(),
6586 start_line: declaration_node.start_position().row + 1,
6587 end_line: terminator.end_position().row + 1,
6588 },
6589 });
6590 }
6591 None
6592}
6593
6594fn displaced_macro_field_terminator<'tree>(
6595 field: Node<'tree>,
6596 source: &str,
6597) -> Option<Node<'tree>> {
6598 if field.kind() != "field_declaration" {
6599 return None;
6600 }
6601 let macro_type = field.child_by_field_name("type")?;
6602 if macro_type.kind() != "type_identifier"
6603 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
6604 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
6605 {
6606 return None;
6607 }
6608 for index in 0..field.child_count() {
6609 let error = field.child(index)?;
6610 if error.kind() != "ERROR"
6611 || error.child_count() != 1
6612 || error.child(0).is_none_or(|child| child.kind() != "}")
6613 {
6614 continue;
6615 }
6616 let semicolon = field.child(index + 1)?;
6617 if semicolon.kind() == ";" {
6618 return Some(semicolon);
6619 }
6620 }
6621 None
6622}
6623
6624fn recover_fragmented_partial_specialization<'tree>(
6625 template_node: Node<'tree>,
6626 declaration_child: Node<'tree>,
6627 source: &str,
6628 ancestry: &ParentIndex<'tree>,
6629) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
6630 if declaration_child.kind() != "function_definition" {
6631 return None;
6632 }
6633 let class_node = declaration_child.child_by_field_name("type")?;
6634 if !matches!(
6635 class_node.kind(),
6636 "class_specifier" | "struct_specifier" | "union_specifier"
6637 ) || !class_node
6638 .child_by_field_name("name")
6639 .and_then(|name| direct_identifier_name(name, source))
6640 .is_some_and(|name| cpp_export_macro_token(&name))
6641 {
6642 return None;
6643 }
6644 let declarator = declaration_child.child_by_field_name("declarator")?;
6645 if declarator.kind() != "template_function" {
6646 return None;
6647 }
6648 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
6649 if metadata.specialization_arguments.is_empty() {
6650 return None;
6651 }
6652 let body = declaration_child.child_by_field_name("body")?;
6653 if body.kind() != "compound_statement" {
6654 return None;
6655 }
6656 let complete_prefix = body.named_child(0).filter(|first| {
6657 first.kind() == "labeled_statement"
6658 && first.has_error()
6659 && first
6660 .named_child(first.named_child_count().saturating_sub(1))
6661 .is_some_and(recovered_declaration_has_class_terminator)
6662 });
6663 let complete_body = complete_prefix.is_some();
6664 let mut prefix_members = Vec::new();
6665 if let Some(prefix) = complete_prefix {
6666 prefix_members.push(prefix);
6667 } else {
6668 let mut body_cursor = body.walk();
6669 for child in body.named_children(&mut body_cursor) {
6670 if !is_structurally_valid_fragmented_class_prefix_member(child) {
6671 break;
6672 }
6673 prefix_members.push(child);
6674 }
6675 }
6676 let containing_declarations = template_node.parent()?;
6677 if !matches!(
6678 containing_declarations.kind(),
6679 "declaration_list" | "compound_statement"
6680 ) {
6681 return None;
6682 }
6683 let mut member_siblings = Vec::new();
6684 let mut following_declarations = Vec::new();
6685 let terminator;
6686 if complete_body {
6687 terminator = complete_prefix?;
6688 let mut cursor = body.walk();
6689 let mut after_prefix = false;
6690 for child in body.named_children(&mut cursor) {
6691 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
6692 after_prefix = true;
6693 } else if after_prefix {
6694 following_declarations.push(child);
6695 }
6696 }
6697 } else {
6698 let mut found_template = false;
6699 let mut cursor = containing_declarations.walk();
6700 let mut class_terminator = None;
6701 for child in containing_declarations.children(&mut cursor) {
6702 if same_node(child, template_node) {
6703 found_template = true;
6704 continue;
6705 }
6706 if found_template && child.kind() == "}" {
6707 class_terminator = Some(child);
6708 break;
6709 }
6710 if found_template && child.kind() == "namespace_definition" {
6717 return None;
6718 }
6719 if found_template && child.is_named() {
6720 member_siblings.push(child);
6721 }
6722 }
6723 terminator = class_terminator?;
6724 }
6725 let name = format!(
6726 "{}<{}>",
6727 metadata.primary_name,
6728 metadata
6729 .specialization_arguments
6730 .iter()
6731 .map(|argument| argument.text.as_str())
6732 .collect::<Vec<_>>()
6733 .join(", ")
6734 );
6735 Some(RecoveredFragmentedPartialSpecialization {
6736 declaration_node: declaration_child,
6737 name,
6738 range: Range {
6739 start_byte: declaration_child.start_byte(),
6740 end_byte: terminator.end_byte(),
6741 start_line: declaration_child.start_position().row + 1,
6742 end_line: terminator.end_position().row + 1,
6743 },
6744 prefix_members,
6745 member_siblings,
6746 following_declarations,
6747 })
6748}
6749
6750fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
6751 if declaration.kind() != "declaration" {
6752 return false;
6753 }
6754 (0..declaration.child_count().saturating_sub(1)).any(|index| {
6759 let Some(error) = declaration.child(index) else {
6760 return false;
6761 };
6762 error.kind() == "ERROR"
6763 && error.child_count() == 1
6764 && error.child(0).is_some_and(|child| child.kind() == "}")
6765 && declaration
6766 .child(index + 1)
6767 .is_some_and(|child| child.kind() == ";")
6768 })
6769}
6770
6771fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
6772 if node.has_error() {
6773 return false;
6774 }
6775 match node.kind() {
6776 "declaration"
6777 | "field_declaration"
6778 | "alias_declaration"
6779 | "type_definition"
6780 | "static_assert_declaration" => true,
6781 "labeled_statement" => node
6782 .named_child(node.named_child_count().saturating_sub(1))
6783 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
6784 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
6785 matches!(
6786 child.kind(),
6787 "declaration"
6788 | "field_declaration"
6789 | "alias_declaration"
6790 | "type_definition"
6791 | "function_definition"
6792 )
6793 }),
6794 _ => false,
6795 }
6796}
6797
6798fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
6799 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
6800 .then(|| node.child_by_field_name("declarator"))
6801 .flatten()
6802 .and_then(|declarator| extract_variable_name(declarator, source))
6803}
6804
6805fn cpp_template_metadata<'tree>(
6806 template_node: Node<'tree>,
6807 declaration_child: Node<'tree>,
6808 source: &str,
6809 ancestry: &ParentIndex<'tree>,
6810) -> Option<CppTemplateMetadata> {
6811 let parameters_node = template_node.child_by_field_name("parameters")?;
6812 let name_node = cpp_templated_class_name_node(declaration_child)?;
6813 let primary_node = match name_node.kind() {
6814 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
6815 _ => name_node,
6816 };
6817 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
6818 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
6819 return None;
6820 }
6821
6822 let mut parameter_nodes = Vec::new();
6823 let mut parameter_names = Vec::new();
6824 let mut cursor = parameters_node.walk();
6825 for parameter in parameters_node.named_children(&mut cursor) {
6826 if !matches!(
6827 parameter.kind(),
6828 "type_parameter_declaration"
6829 | "optional_type_parameter_declaration"
6830 | "variadic_type_parameter_declaration"
6831 | "template_template_parameter_declaration"
6832 | "parameter_declaration"
6833 | "optional_parameter_declaration"
6834 | "variadic_parameter_declaration"
6835 ) {
6836 continue;
6837 }
6838 let index = parameter_nodes.len();
6839 let name = cpp_template_parameter_name(parameter, source)
6844 .unwrap_or_else(|| format!("<anonymous:{index}>"));
6845 parameter_names.push(name);
6846 parameter_nodes.push(parameter);
6847 }
6848 let parameters = parameter_nodes
6849 .into_iter()
6850 .zip(parameter_names.iter().cloned())
6851 .map(|(parameter, name)| CppTemplateParameterMetadata {
6852 name,
6853 kind: cpp_template_parameter_kind(parameter),
6854 variadic: matches!(
6855 parameter.kind(),
6856 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
6857 ),
6858 default: cpp_template_parameter_default_expression(
6859 parameter,
6860 source,
6861 ¶meter_names,
6862 ancestry,
6863 ),
6864 })
6865 .collect();
6866 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
6867 Vec::new()
6868 } else {
6869 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
6870 .unwrap_or_default()
6871 };
6872 let alias_target = (declaration_child.kind() == "alias_declaration")
6873 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
6874 .flatten();
6875 Some(CppTemplateMetadata {
6876 primary_name,
6877 primary_fq_name: String::new(),
6878 parameters,
6879 specialization_arguments,
6880 alias_target,
6881 })
6882}
6883
6884fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
6885 match node.kind() {
6886 "class_specifier" | "struct_specifier" | "union_specifier" => {
6887 node.child_by_field_name("name")
6888 }
6889 "function_definition" => {
6890 let declarator = node.child_by_field_name("declarator")?;
6891 if matches!(declarator.kind(), "identifier" | "template_function") {
6892 Some(declarator)
6893 } else {
6894 None
6895 }
6896 }
6897 "alias_declaration" => node.child_by_field_name("name"),
6898 _ => None,
6899 }
6900}
6901
6902fn cpp_template_alias_target<'tree>(
6903 alias: Node<'tree>,
6904 source: &str,
6905 parameter_names: &[String],
6906 ancestry: &ParentIndex<'tree>,
6907) -> Option<CppTemplateAliasTargetMetadata> {
6908 let mut type_node = alias.child_by_field_name("type")?;
6909 while type_node.kind() == "type_descriptor" {
6910 type_node = type_node.child_by_field_name("type")?;
6911 }
6912 let global = type_node.child_by_field_name("scope").is_none()
6913 && type_node.child(0).is_some_and(|child| child.kind() == "::");
6914 let mut components = Vec::new();
6915 cpp_template_target_components(type_node, source, &mut components)?;
6916 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
6917 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
6918 components,
6919 global,
6920 arguments,
6921 })
6922}
6923
6924fn cpp_template_target_components(
6925 node: Node<'_>,
6926 source: &str,
6927 out: &mut Vec<String>,
6928) -> Option<()> {
6929 match node.kind() {
6930 "identifier" | "namespace_identifier" | "type_identifier" => {
6931 out.push(node_text(node, source).to_string());
6932 Some(())
6933 }
6934 "template_type" => {
6935 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6936 }
6937 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
6938 if let Some(scope) = node.child_by_field_name("scope") {
6939 cpp_template_target_components(scope, source, out)?;
6940 }
6941 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6942 }
6943 _ => None,
6944 }
6945}
6946
6947fn cpp_template_argument_expressions<'tree>(
6948 mut node: Node<'tree>,
6949 source: &str,
6950 parameter_names: &[String],
6951 ancestry: &ParentIndex<'tree>,
6952) -> Option<Vec<CppTemplateExpression>> {
6953 loop {
6954 match node.kind() {
6955 "template_type" | "template_function" => {
6956 let arguments = node.child_by_field_name("arguments")?;
6957 let mut cursor = arguments.walk();
6958 return Some(
6959 arguments
6960 .named_children(&mut cursor)
6961 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
6962 .map(|argument| {
6963 cpp_template_expression(argument, source, parameter_names, ancestry)
6964 })
6965 .collect(),
6966 );
6967 }
6968 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
6969 node = node
6970 .child_by_field_name("name")
6971 .or_else(|| node.child_by_field_name("type"))?;
6972 }
6973 _ => return None,
6974 }
6975 }
6976}
6977
6978fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
6979 let candidate = node
6980 .child_by_field_name("name")
6981 .or_else(|| node.child_by_field_name("declarator"))
6982 .or_else(|| {
6983 let mut cursor = node.walk();
6984 node.named_children(&mut cursor).find(|child| {
6985 matches!(
6986 child.kind(),
6987 "identifier" | "type_identifier" | "field_identifier"
6988 )
6989 })
6990 })?;
6991 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
6992 (!name.is_empty()).then_some(name)
6993}
6994
6995fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
6996 match node.kind() {
6997 "type_parameter_declaration"
6998 | "optional_type_parameter_declaration"
6999 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
7000 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
7001 _ => CppTemplateParameterKind::Value,
7002 }
7003}
7004
7005fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
7006 node.child_by_field_name("default_type")
7007 .or_else(|| node.child_by_field_name("default_value"))
7008}
7009
7010fn cpp_template_parameter_default_expression<'tree>(
7011 parameter: Node<'tree>,
7012 source: &str,
7013 parameter_names: &[String],
7014 ancestry: &ParentIndex<'tree>,
7015) -> Option<CppTemplateExpression> {
7016 let default = cpp_template_parameter_default(parameter)?;
7017 let base = cpp_template_expression(default, source, parameter_names, ancestry);
7018 let Some(pointer_error) = parameter.next_named_sibling() else {
7019 return Some(base);
7020 };
7021 let Some(pointer_declarator) =
7022 recovered_abstract_pointer_declarator_term(pointer_error, source)
7023 else {
7024 return Some(base);
7025 };
7026 Some(CppTemplateExpression {
7027 text: format!(
7028 "{}{}",
7029 base.text,
7030 normalize_cpp_whitespace(node_text(pointer_error, source))
7031 ),
7032 term: CppTemplateTerm::Node {
7033 kind: "type_descriptor".to_string(),
7034 children: vec![base.term, pointer_declarator],
7035 },
7036 })
7037}
7038
7039fn recovered_abstract_pointer_declarator_term(
7040 node: Node<'_>,
7041 source: &str,
7042) -> Option<CppTemplateTerm> {
7043 if node.kind() != "ERROR" || node.child_count() == 0 {
7044 return None;
7045 }
7046 let mut children = Vec::new();
7047 for index in 0..node.child_count() {
7048 let child = node.child(index)?;
7049 if child.kind() != "*" {
7050 return None;
7051 }
7052 children.push(CppTemplateTerm::Atom {
7053 kind: "*".to_string(),
7054 text: normalize_cpp_whitespace(node_text(child, source)),
7055 });
7056 }
7057 Some(CppTemplateTerm::Node {
7058 kind: "abstract_pointer_declarator".to_string(),
7059 children,
7060 })
7061}
7062
7063fn cpp_template_expression<'tree>(
7064 node: Node<'tree>,
7065 source: &str,
7066 parameter_names: &[String],
7067 ancestry: &ParentIndex<'tree>,
7068) -> CppTemplateExpression {
7069 let text = normalize_cpp_whitespace(node_text(node, source));
7070 CppTemplateExpression {
7071 text,
7072 term: cpp_template_term(node, source, parameter_names, ancestry),
7073 }
7074}
7075
7076pub fn cpp_template_term<'tree>(
7077 node: Node<'tree>,
7078 source: &str,
7079 parameter_names: &[String],
7080 ancestry: &ParentIndex<'tree>,
7081) -> CppTemplateTerm {
7082 enum Work<'tree> {
7083 Visit(Node<'tree>),
7084 Build { kind: String, child_count: usize },
7085 }
7086
7087 let mut work = vec![Work::Visit(node)];
7088 let mut terms = Vec::new();
7089 while let Some(next) = work.pop() {
7090 match next {
7091 Work::Visit(current) => {
7092 let text = normalize_cpp_whitespace(node_text(current, source));
7093 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
7094 terms.push(CppTemplateTerm::Parameter(text));
7095 continue;
7096 }
7097 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
7098 let mut cursor = current.walk();
7099 let named = current
7100 .named_children(&mut cursor)
7101 .filter(|child| !child.is_extra() && child.kind() != "comment")
7102 .collect::<Vec<_>>();
7103 if let [child] = named.as_slice() {
7104 work.push(Work::Visit(*child));
7105 continue;
7106 }
7107 }
7108 if current.child_count() == 0 {
7109 terms.push(CppTemplateTerm::Atom {
7110 kind: if matches!(
7111 current.kind(),
7112 "identifier"
7113 | "type_identifier"
7114 | "field_identifier"
7115 | "namespace_identifier"
7116 ) {
7117 "identifier".to_string()
7118 } else {
7119 current.kind().to_string()
7120 },
7121 text,
7122 });
7123 continue;
7124 }
7125 let children = (0..current.child_count())
7126 .filter_map(|index| current.child(index))
7127 .filter(|child| !child.is_extra() && child.kind() != "comment")
7128 .collect::<Vec<_>>();
7129 work.push(Work::Build {
7130 kind: current.kind().to_string(),
7131 child_count: children.len(),
7132 });
7133 work.extend(children.into_iter().rev().map(Work::Visit));
7134 }
7135 Work::Build { kind, child_count } => {
7136 let children = terms.split_off(terms.len() - child_count);
7137 terms.push(CppTemplateTerm::Node { kind, children });
7138 }
7139 }
7140 }
7141 terms.pop().expect("template term traversal emits one root")
7142}
7143
7144fn cpp_template_term_leaf_is_parameter<'tree>(
7145 node: Node<'tree>,
7146 text: &str,
7147 parameter_names: &[String],
7148 ancestry: &ParentIndex<'tree>,
7149) -> bool {
7150 if !parameter_names.iter().any(|parameter| parameter == text) {
7151 return false;
7152 }
7153 !ancestry.parent(node).is_some_and(|parent| {
7154 matches!(
7155 parent.kind(),
7156 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
7157 ) && parent.child_by_field_name("scope").is_some()
7158 && parent.child_by_field_name("name") == Some(node)
7159 })
7160}
7161
7162fn enclosing_cpp_declaration_node<'tree>(
7163 mut node: Node<'tree>,
7164 ancestry: &ParentIndex<'tree>,
7165) -> Option<Node<'tree>> {
7166 loop {
7167 match node.kind() {
7168 "declaration"
7169 | "function_declaration"
7170 | "field_declaration"
7171 | "function_definition" => return Some(node),
7172 _ => node = ancestry.parent(node)?,
7173 }
7174 }
7175}
7176
7177fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
7178 let mut params = Vec::new();
7179 let mut cursor = parameters_node.walk();
7180 for child in parameters_node.children(&mut cursor) {
7181 match child.kind() {
7182 "parameter_declaration" | "optional_parameter_declaration" => {
7183 params.push(cpp_parameter_type(child, source));
7184 }
7185 "variadic_parameter_declaration" => {
7186 params.push(cpp_parameter_type(child, source));
7187 }
7188 "variadic_parameter" | "..." => params.push("...".to_string()),
7189 _ => {}
7190 }
7191 }
7192
7193 if params.is_empty() {
7194 "()".to_string()
7195 } else {
7196 format!("({})", params.join(", "))
7197 }
7198}
7199
7200fn cpp_signature_metadata<'tree>(
7201 signature: String,
7202 function_declarator: Node<'tree>,
7203 source: &str,
7204 ancestry: &ParentIndex<'tree>,
7205) -> SignatureMetadata {
7206 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
7207 let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
7208 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
7209 let return_type_identity =
7210 cpp_callable_return_type_identity(function_declarator, source, ancestry);
7211 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
7212 return enrich(
7213 SignatureMetadata::new(signature, Vec::new())
7214 .with_return_type_text(return_type_text)
7215 .with_return_type_identity(return_type_identity),
7216 );
7217 };
7218 let callable_arity = cpp_callable_arity(parameters_node, source);
7219 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
7220 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
7221 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
7222 let Some(relative_start) = signature
7223 .get(search_from..)
7224 .and_then(|suffix| suffix.find(¶meter_text))
7225 else {
7226 return enrich(
7227 SignatureMetadata::new(signature, Vec::new())
7228 .with_callable_arity(callable_arity)
7229 .with_callable_parameter_types(callable_parameter_types)
7230 .with_return_type_text(return_type_text)
7231 .with_return_type_identity(return_type_identity),
7232 );
7233 };
7234 let parameters_start = search_from + relative_start;
7235 let parameters_end = parameters_start + parameter_text.len();
7236 let mut search_start = parameters_start;
7237 let parameters = cpp_parameter_label_nodes(parameters_node)
7238 .into_iter()
7239 .filter_map(|label_node| {
7240 let label = normalize_cpp_whitespace(node_text(label_node, source));
7241 if label.is_empty() || search_start > parameters_end {
7242 return None;
7243 }
7244 let haystack = signature.get(search_start..parameters_end)?;
7245 let relative_start = haystack.find(&label)?;
7246 let start_byte = search_start + relative_start;
7247 let end_byte = start_byte + label.len();
7248 search_start = end_byte;
7249 Some(ParameterMetadata::new(label, start_byte, end_byte))
7250 })
7251 .collect();
7252 enrich(
7253 SignatureMetadata::new(signature, parameters)
7254 .with_callable_arity(callable_arity)
7255 .with_callable_parameter_types(callable_parameter_types)
7256 .with_return_type_text(return_type_text)
7257 .with_return_type_identity(return_type_identity),
7258 )
7259}
7260
7261fn cpp_callable_is_structural_constructor<'tree>(
7262 function_declarator: Node<'tree>,
7263 source: &str,
7264 ancestry: &ParentIndex<'tree>,
7265) -> bool {
7266 let Some(name_node) = function_declarator
7267 .child_by_field_name("declarator")
7268 .or_else(|| function_declarator.child_by_field_name("name"))
7269 .or_else(|| last_named_child(function_declarator))
7270 else {
7271 return false;
7272 };
7273 let Some(callable_name) = direct_identifier_name(name_node, source) else {
7274 return false;
7275 };
7276
7277 let mut current = ancestry.parent(function_declarator);
7278 while let Some(ancestor) = current {
7279 let owner_name = match ancestor.kind() {
7280 "class_specifier" | "struct_specifier" | "union_specifier" => {
7281 class_like_name(ancestor, source, ancestry)
7282 }
7283 "ERROR" => malformed_class_error_owner_name(ancestor, source),
7284 _ => None,
7285 };
7286 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
7287 return true;
7288 }
7289 current = ancestry.parent(ancestor);
7290 }
7291 false
7292}
7293
7294fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
7304 if node.kind() != "ERROR" {
7305 return None;
7306 }
7307 let keyword = node.child(0)?;
7308 if !matches!(keyword.kind(), "class" | "struct" | "union") {
7309 return None;
7310 }
7311 let name_node = node.child(1)?;
7312 let name = direct_identifier_name(name_node, source)?;
7313 let has_body = (2..node.child_count())
7314 .filter_map(|index| node.child(index))
7315 .any(|child| child.kind() == "{");
7316 has_body.then_some(name)
7317}
7318
7319fn cpp_callable_return_type_identity<'tree>(
7320 function_declarator: Node<'tree>,
7321 source: &str,
7322 ancestry: &ParentIndex<'tree>,
7323) -> Option<StructuredTypeIdentity> {
7324 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
7325 return None;
7326 }
7327 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
7328 if let Some((return_type, _)) =
7329 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
7330 {
7331 return cpp_structured_type_identity(return_type, source, &lexical_scope);
7332 }
7333 let mut cursor = function_declarator.walk();
7334 if let Some(trailing) = function_declarator
7335 .named_children(&mut cursor)
7336 .find(|child| child.kind() == "trailing_return_type")
7337 && let Some(type_descriptor) = trailing.named_child(0)
7338 {
7339 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
7340 }
7341
7342 let mut current = function_declarator;
7343 let mut wrappers = Vec::new();
7344 while let Some(parent) = ancestry.parent(current) {
7345 if matches!(
7346 parent.kind(),
7347 "function_definition" | "declaration" | "field_declaration"
7348 ) {
7349 let type_node = parent.child_by_field_name("type")?;
7350 if cpp_export_macro_token(node_text(type_node, source))
7351 && (0..parent.named_child_count()).any(|index| {
7352 parent
7353 .named_child(index)
7354 .is_some_and(|child| child.kind() == "ERROR")
7355 })
7356 {
7357 return None;
7358 }
7359 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
7360 for wrapper in wrappers.into_iter().rev() {
7361 identity = cpp_wrap_structured_type(identity, wrapper)?;
7362 }
7363 return Some(identity);
7364 }
7365 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
7366 || (matches!(
7367 parent.kind(),
7368 "pointer_declarator"
7369 | "reference_declarator"
7370 | "array_declarator"
7371 | "parenthesized_declarator"
7372 ) && parent.named_child_count() == 1
7373 && parent.named_child(0) == Some(current));
7374 if !wraps_current_declarator {
7375 return None;
7376 }
7377 match parent.kind() {
7378 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
7379 "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
7380 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
7381 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
7382 _ => return None,
7383 }
7384 current = parent;
7385 }
7386 None
7387}
7388
7389fn cpp_structured_type_identity(
7390 node: Node<'_>,
7391 source: &str,
7392 lexical_scope: &[String],
7393) -> Option<StructuredTypeIdentity> {
7394 enum Work<'tree> {
7395 Visit(Node<'tree>),
7396 Wrap(CppStructuredTypeWrapper),
7397 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
7398 BuildGeneric { argument_count: usize },
7399 }
7400
7401 let mut work = vec![Work::Visit(node)];
7402 let mut values = Vec::new();
7403 let mut builder = StructuredTypeIdentityBuilder::default();
7404 while let Some(next) = work.pop() {
7405 match next {
7406 Work::Visit(current) => match current.kind() {
7407 "type_descriptor" => {
7408 let type_node = current
7409 .child_by_field_name("type")
7410 .or_else(|| current.named_child(0))?;
7411 let mut wrappers = Vec::new();
7412 let mut cursor = current.walk();
7413 for child in current.named_children(&mut cursor) {
7414 if child.id() != type_node.id() {
7415 wrappers.extend(cpp_structured_declarator_wrappers(child));
7416 }
7417 }
7418 work.push(Work::ApplyWrappers(wrappers));
7419 work.push(Work::Visit(type_node));
7420 }
7421 "pointer_declarator" | "abstract_pointer_declarator" => {
7422 let child = current
7423 .child_by_field_name("declarator")
7424 .or_else(|| current.named_child(0))?;
7425 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
7426 work.push(Work::Visit(child));
7427 }
7428 "reference_declarator" => {
7429 let child = current
7430 .child_by_field_name("declarator")
7431 .or_else(|| current.named_child(0))?;
7432 work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
7433 work.push(Work::Visit(child));
7434 }
7435 "array_declarator" | "abstract_array_declarator" => {
7436 let child = current
7437 .child_by_field_name("declarator")
7438 .or_else(|| current.named_child(0))?;
7439 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
7440 work.push(Work::Visit(child));
7441 }
7442 "template_type" => {
7443 let name_node = current.child_by_field_name("name")?;
7444 let arguments = current
7445 .child_by_field_name("arguments")
7446 .map(|arguments_node| {
7447 let mut cursor = arguments_node.walk();
7448 arguments_node
7449 .named_children(&mut cursor)
7450 .filter(|child| !child.is_extra() && child.kind() != "comment")
7451 .collect::<Vec<_>>()
7452 })
7453 .unwrap_or_default();
7454 work.push(Work::BuildGeneric {
7455 argument_count: arguments.len(),
7456 });
7457 work.extend(arguments.into_iter().rev().map(Work::Visit));
7458 work.push(Work::Visit(name_node));
7459 }
7460 "qualified_identifier"
7461 | "scoped_identifier"
7462 | "scoped_type_identifier"
7463 | "type_identifier"
7464 | "field_identifier"
7465 | "identifier"
7466 | "namespace_identifier"
7467 | "primitive_type" => {
7468 values.push(builder.named(cpp_structured_named_type(
7469 current,
7470 source,
7471 lexical_scope,
7472 )?)?);
7473 }
7474 _ => {
7475 let child = current.child_by_field_name("type").or_else(|| {
7476 (current.named_child_count() == 1)
7477 .then(|| current.named_child(0))
7478 .flatten()
7479 })?;
7480 work.push(Work::Visit(child));
7481 }
7482 },
7483 Work::Wrap(wrapper) => {
7484 let root = values.pop()?;
7485 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
7486 }
7487 Work::ApplyWrappers(wrappers) => {
7488 let mut root = values.pop()?;
7489 for wrapper in wrappers.into_iter().rev() {
7490 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
7491 }
7492 values.push(root);
7493 }
7494 Work::BuildGeneric { argument_count } => {
7495 let value_count = argument_count.checked_add(1)?;
7496 let start = values.len().checked_sub(value_count)?;
7497 let mut built = values.split_off(start);
7498 let base = built.remove(0);
7499 values.push(builder.generic(base, built)?);
7500 }
7501 }
7502 }
7503 (values.len() == 1)
7504 .then(|| values.pop())
7505 .flatten()
7506 .and_then(|root| builder.finish(root))
7507}
7508
7509fn cpp_structured_named_type(
7510 node: Node<'_>,
7511 source: &str,
7512 lexical_scope: &[String],
7513) -> Option<StructuredTypeName> {
7514 let path = cpp_structured_type_path(node, source)?;
7515 let absolute = node.child_by_field_name("scope").is_none()
7516 && node.child(0).is_some_and(|child| child.kind() == "::");
7517 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
7518}
7519
7520#[derive(Clone, Copy)]
7521enum CppStructuredTypeWrapper {
7522 Pointer,
7523 Reference,
7524 Array,
7525}
7526
7527fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
7528 let mut wrappers = Vec::new();
7529 let mut current = node;
7530 loop {
7531 match current.kind() {
7532 "pointer_declarator" | "abstract_pointer_declarator" => {
7533 wrappers.push(CppStructuredTypeWrapper::Pointer)
7534 }
7535 "reference_declarator" | "abstract_reference_declarator" => {
7536 wrappers.push(CppStructuredTypeWrapper::Reference)
7537 }
7538 "array_declarator" | "abstract_array_declarator" => {
7539 wrappers.push(CppStructuredTypeWrapper::Array)
7540 }
7541 _ => break,
7542 }
7543 let Some(child) = current
7544 .child_by_field_name("declarator")
7545 .or_else(|| current.named_child(0))
7546 else {
7547 break;
7548 };
7549 current = child;
7550 }
7551 wrappers
7552}
7553
7554fn cpp_wrap_structured_type(
7555 identity: StructuredTypeIdentity,
7556 wrapper: CppStructuredTypeWrapper,
7557) -> Option<StructuredTypeIdentity> {
7558 match wrapper {
7559 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
7560 CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
7561 CppStructuredTypeWrapper::Array => identity.wrap_array(),
7562 }
7563}
7564
7565fn cpp_wrap_structured_type_node(
7566 builder: &mut StructuredTypeIdentityBuilder,
7567 inner: StructuredTypeNodeId,
7568 wrapper: CppStructuredTypeWrapper,
7569) -> Option<StructuredTypeNodeId> {
7570 match wrapper {
7571 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
7572 CppStructuredTypeWrapper::Reference => builder.reference(inner),
7573 CppStructuredTypeWrapper::Array => builder.array(inner),
7574 }
7575}
7576
7577fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
7578 let mut path = Vec::new();
7579 let mut stack = vec![node];
7580 while let Some(current) = stack.pop() {
7581 match current.kind() {
7582 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
7583 let component = node_text(current, source).to_string();
7584 if component.is_empty() {
7585 return None;
7586 }
7587 path.push(component);
7588 }
7589 "template_type" | "dependent_type" => {
7590 stack.push(current.child_by_field_name("name")?);
7591 }
7592 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
7593 stack.push(current.child_by_field_name("name")?);
7594 if let Some(scope) = current.child_by_field_name("scope") {
7595 stack.push(scope);
7596 }
7597 }
7598 _ => return None,
7599 }
7600 }
7601 (!path.is_empty()).then_some(path)
7602}
7603
7604fn cpp_callable_lexical_scope<'tree>(
7605 node: Node<'tree>,
7606 source: &str,
7607 ancestry: &ParentIndex<'tree>,
7608) -> Vec<String> {
7609 let mut groups = Vec::new();
7610 let mut current = ancestry.parent(node);
7611 while let Some(parent) = current {
7612 if matches!(
7613 parent.kind(),
7614 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
7615 ) && let Some(name_node) = parent.child_by_field_name("name")
7616 && let Some(components) = cpp_structured_type_path(name_node, source)
7617 && !components.is_empty()
7618 {
7619 groups.push(components);
7620 }
7621 current = ancestry.parent(parent);
7622 }
7623 groups.reverse();
7624 groups.into_iter().flatten().collect()
7625}
7626
7627fn cpp_callable_dispatch_extensibility<'tree>(
7628 function_declarator: Node<'tree>,
7629 ancestry: &ParentIndex<'tree>,
7630) -> DispatchExtensibility {
7631 let mut declaration = None;
7632 let mut current = Some(function_declarator);
7633 while let Some(node) = current {
7634 match node.kind() {
7635 "template_declaration"
7636 | "preproc_if"
7637 | "preproc_ifdef"
7638 | "preproc_else"
7639 | "preproc_elif"
7640 | "preproc_call"
7641 | "ERROR" => return DispatchExtensibility::Open,
7642 "declaration" | "field_declaration" | "function_definition" => {
7643 declaration.get_or_insert(node);
7644 }
7645 "translation_unit" => break,
7646 _ => {}
7647 }
7648 current = ancestry.parent(node);
7649 }
7650 let Some(declaration) = declaration else {
7651 return DispatchExtensibility::Open;
7652 };
7653
7654 let mut saw_virtual_boundary = false;
7655 let mut stack = vec![declaration];
7656 while let Some(node) = stack.pop() {
7657 match node.kind() {
7658 "compound_statement" | "field_declaration_list" => continue,
7659 "final" | "final_specifier" => return DispatchExtensibility::Closed,
7660 "virtual"
7661 | "override"
7662 | "virtual_specifier"
7663 | "pure_virtual_clause"
7664 | "template_parameter_list"
7665 | "template_method"
7666 | "template_function"
7667 | "ERROR" => saw_virtual_boundary = true,
7668 _ => {}
7669 }
7670 let mut cursor = node.walk();
7671 stack.extend(node.children(&mut cursor));
7672 }
7673
7674 if saw_virtual_boundary {
7675 DispatchExtensibility::Open
7676 } else {
7677 DispatchExtensibility::Closed
7678 }
7679}
7680
7681fn cpp_callable_linkage<'tree>(
7682 declaration: Node<'tree>,
7683 source: &str,
7684 ancestry: &ParentIndex<'tree>,
7685) -> CallableLinkage {
7686 let mut enclosed_by_class = false;
7687 let mut current = ancestry.parent(declaration);
7688 while let Some(node) = current {
7689 if node.kind() == "namespace_definition"
7690 && node
7691 .child_by_field_name("name")
7692 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7693 {
7694 return CallableLinkage::Internal;
7695 }
7696 if matches!(
7697 node.kind(),
7698 "class_specifier" | "struct_specifier" | "union_specifier"
7699 ) {
7700 if node
7701 .child_by_field_name("name")
7702 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7703 {
7704 return CallableLinkage::Internal;
7705 }
7706 enclosed_by_class = true;
7707 }
7708 if matches!(node.kind(), "function_definition" | "lambda_expression") {
7709 return CallableLinkage::Internal;
7710 }
7711 current = ancestry.parent(node);
7712 }
7713
7714 if enclosed_by_class {
7715 return CallableLinkage::External;
7716 }
7717
7718 let mut cursor = declaration.walk();
7719 if declaration.named_children(&mut cursor).any(|child| {
7720 child.kind() == "storage_class_specifier"
7721 && normalize_cpp_whitespace(node_text(child, source)) == "static"
7722 }) {
7723 CallableLinkage::Internal
7724 } else {
7725 CallableLinkage::External
7726 }
7727}
7728
7729fn cpp_callable_return_type_text<'tree>(
7730 function_declarator: Node<'tree>,
7731 source: &str,
7732 ancestry: &ParentIndex<'tree>,
7733) -> Option<String> {
7734 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
7735 return None;
7736 }
7737 if let Some((return_type, _)) =
7738 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
7739 {
7740 let text = normalize_cpp_whitespace(node_text(return_type, source));
7741 return (!text.is_empty()).then_some(text);
7742 }
7743 let mut cursor = function_declarator.walk();
7744 if let Some(trailing) = function_declarator
7745 .named_children(&mut cursor)
7746 .find(|child| child.kind() == "trailing_return_type")
7747 && let Some(type_descriptor) = trailing.named_child(0)
7748 {
7749 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
7750 if !text.is_empty() {
7751 return Some(text);
7752 }
7753 }
7754
7755 let mut current = function_declarator;
7756 let mut indirection = String::new();
7757 while let Some(parent) = ancestry.parent(current) {
7758 if matches!(
7759 parent.kind(),
7760 "function_definition" | "declaration" | "field_declaration"
7761 ) {
7762 let type_node = parent.child_by_field_name("type")?;
7763 if cpp_export_macro_token(node_text(type_node, source))
7764 && (0..parent.named_child_count()).any(|index| {
7765 parent
7766 .named_child(index)
7767 .is_some_and(|child| child.kind() == "ERROR")
7768 })
7769 {
7770 return None;
7775 }
7776 let base = normalize_cpp_whitespace(node_text(type_node, source));
7777 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
7778 }
7779 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
7780 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
7781 && parent.named_child_count() == 1
7782 && parent.named_child(0) == Some(current));
7783 if wraps_current_declarator {
7784 match parent.kind() {
7785 "pointer_declarator" => indirection.push('*'),
7786 "reference_declarator" => {
7787 let reference = parent
7788 .children(&mut parent.walk())
7789 .find(|child| !child.is_named())
7790 .map(|child| node_text(child, source))
7791 .unwrap_or("&");
7792 indirection.push_str(reference);
7793 }
7794 "init_declarator" | "parenthesized_declarator" => {}
7795 _ => return None,
7796 }
7797 current = parent;
7798 continue;
7799 }
7800 return None;
7801 }
7802 None
7803}
7804
7805fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
7806 let mut required = 0;
7807 let mut total = 0;
7808 let mut repeated = false;
7809 let mut cursor = parameters_node.walk();
7810 for child in parameters_node.children(&mut cursor) {
7811 match child.kind() {
7812 "parameter_declaration" => {
7813 if cpp_parameter_is_explicit_object(child, source) {
7814 continue;
7815 }
7816 if child.child_by_field_name("declarator").is_none()
7817 && child
7818 .child_by_field_name("type")
7819 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
7820 {
7821 continue;
7822 }
7823 required += 1;
7824 total += 1;
7825 }
7826 "optional_parameter_declaration" => total += 1,
7827 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7828 repeated = true;
7829 }
7830 _ => {}
7831 }
7832 }
7833 CallableArity::new(required, total, repeated)
7834}
7835
7836fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
7837 parameter
7838 .child_by_field_name("type")
7839 .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
7840 .and_then(|type_node| type_node.child_by_field_name("constraint"))
7841 .is_some_and(|constraint| {
7842 constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
7843 })
7844}
7845
7846#[derive(Clone, Copy)]
7854enum CppParameterSlot<'tree> {
7855 Declared(Node<'tree>),
7856 Ellipsis,
7857}
7858
7859fn cpp_callable_parameter_slots<'tree>(
7860 parameters_node: Node<'tree>,
7861 source: &str,
7862) -> Vec<CppParameterSlot<'tree>> {
7863 let mut slots = Vec::new();
7864 let mut cursor = parameters_node.walk();
7865 for parameter in parameters_node.children(&mut cursor) {
7866 match parameter.kind() {
7867 "parameter_declaration" | "optional_parameter_declaration" => {
7868 if cpp_parameter_is_explicit_object(parameter, source)
7869 || (parameter.child_by_field_name("declarator").is_none()
7870 && parameter
7871 .child_by_field_name("type")
7872 .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
7873 {
7874 continue;
7875 }
7876 slots.push(CppParameterSlot::Declared(parameter));
7877 }
7878 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7879 slots.push(CppParameterSlot::Ellipsis);
7880 }
7881 _ => {}
7882 }
7883 }
7884 slots
7885}
7886
7887fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
7888 cpp_callable_parameter_slots(parameters_node, source)
7889 .into_iter()
7890 .map(|slot| match slot {
7891 CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
7892 CppParameterSlot::Ellipsis => "...".to_string(),
7893 })
7894 .collect()
7895}
7896
7897#[derive(Debug, Clone, PartialEq, Eq)]
7903pub enum CppParameterType {
7904 Structured(StructuredTypeIdentity),
7907 Ellipsis,
7909 Unstructured,
7912}
7913
7914pub fn cpp_callable_parameter_type_identities<'tree>(
7920 function_declarator: Node<'tree>,
7921 source: &str,
7922 ancestry: &ParentIndex<'tree>,
7923) -> Vec<CppParameterType> {
7924 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
7925 return Vec::new();
7926 };
7927 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
7928 cpp_callable_parameter_slots(parameters_node, source)
7929 .into_iter()
7930 .map(|slot| match slot {
7931 CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
7932 CppParameterSlot::Declared(parameter) => {
7933 cpp_parameter_type_identity(parameter, source, &lexical_scope)
7934 .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
7935 }
7936 })
7937 .collect()
7938}
7939
7940fn cpp_parameter_type_identity(
7941 parameter: Node<'_>,
7942 source: &str,
7943 lexical_scope: &[String],
7944) -> Option<StructuredTypeIdentity> {
7945 let type_node = parameter.child_by_field_name("type")?;
7946 let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
7947 if let Some(declarator) = cpp_parameter_declarator(parameter) {
7948 for wrapper in cpp_structured_declarator_wrappers(declarator)
7949 .into_iter()
7950 .rev()
7951 {
7952 identity = cpp_wrap_structured_type(identity, wrapper)?;
7953 }
7954 }
7955 Some(identity)
7956}
7957
7958#[derive(Debug, Clone, PartialEq, Eq)]
7971pub enum CppComparableSlot {
7972 Shape(CppComparableParameter),
7974 Ellipsis,
7976 Unstructured,
7979}
7980
7981#[derive(Debug, Clone, PartialEq, Eq)]
7994pub struct CppComparableParameter {
7995 nodes: Vec<CppComparableNode>,
7996 root: usize,
7997}
7998
7999#[derive(Debug, Clone, PartialEq, Eq)]
8007pub enum CppComparableNode {
8008 Named {
8009 name: StructuredTypeName,
8010 primitive: bool,
8011 konst: bool,
8012 volatil: bool,
8013 },
8014 Pointer {
8015 inner: usize,
8016 konst: bool,
8017 volatil: bool,
8018 },
8019 Reference {
8020 inner: usize,
8021 },
8022 Array {
8023 inner: usize,
8024 },
8025 Generic {
8026 base: usize,
8027 arguments: Vec<usize>,
8028 },
8029}
8030
8031impl CppComparableParameter {
8032 pub fn root(&self) -> usize {
8033 self.root
8034 }
8035
8036 pub fn node(&self, index: usize) -> &CppComparableNode {
8037 &self.nodes[index]
8038 }
8039
8040 fn adjust_parameter_top_level(&mut self) {
8051 let root = self.root;
8052 match &mut self.nodes[root] {
8053 CppComparableNode::Named { konst, volatil, .. }
8054 | CppComparableNode::Pointer { konst, volatil, .. } => {
8055 *konst = false;
8056 *volatil = false;
8057 }
8058 CppComparableNode::Array { inner } => {
8059 let inner = *inner;
8060 self.nodes[root] = CppComparableNode::Pointer {
8061 inner,
8062 konst: false,
8063 volatil: false,
8064 };
8065 }
8066 CppComparableNode::Generic { base, .. } => {
8067 let base = *base;
8068 let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
8069 unreachable!("a comparable generic's base is always a named leaf");
8070 };
8071 *konst = false;
8072 *volatil = false;
8073 }
8074 CppComparableNode::Reference { .. } => {}
8075 }
8076 }
8077}
8078
8079pub fn cpp_comparable_parameter_shapes<'tree>(
8086 function_declarator: Node<'tree>,
8087 source: &str,
8088 ancestry: &ParentIndex<'tree>,
8089) -> Vec<CppComparableSlot> {
8090 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
8091 return Vec::new();
8092 };
8093 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
8094 cpp_callable_parameter_slots(parameters_node, source)
8095 .into_iter()
8096 .map(|slot| match slot {
8097 CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
8098 CppParameterSlot::Declared(parameter) => {
8099 cpp_comparable_parameter(parameter, source, &lexical_scope)
8100 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
8101 }
8102 })
8103 .collect()
8104}
8105
8106fn cpp_comparable_parameter(
8107 parameter: Node<'_>,
8108 source: &str,
8109 lexical_scope: &[String],
8110) -> Option<CppComparableParameter> {
8111 let type_node = parameter.child_by_field_name("type")?;
8112 let levels = match cpp_parameter_declarator(parameter) {
8113 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
8114 None => Vec::new(),
8115 };
8116 let mut shape = cpp_comparable_type_shape(
8117 type_node,
8118 cpp_cv_qualifiers(parameter, source),
8119 levels,
8120 source,
8121 lexical_scope,
8122 )?;
8123 shape.adjust_parameter_top_level();
8124 Some(shape)
8125}
8126
8127fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
8138 let mut qualifiers = CppCvQualifiers::default();
8139 let mut cursor = node.walk();
8140 for child in node.named_children(&mut cursor) {
8141 if child.kind() != "type_qualifier" {
8142 continue;
8143 }
8144 match node_text(child, source) {
8145 "const" => qualifiers.konst = true,
8146 "volatile" => qualifiers.volatil = true,
8147 _ => {}
8148 }
8149 }
8150 qualifiers
8151}
8152
8153#[derive(Clone, Copy, Default)]
8154struct CppCvQualifiers {
8155 konst: bool,
8156 volatil: bool,
8157}
8158
8159impl CppCvQualifiers {
8160 fn union(self, other: Self) -> Self {
8161 Self {
8162 konst: self.konst || other.konst,
8163 volatil: self.volatil || other.volatil,
8164 }
8165 }
8166}
8167
8168#[derive(Clone, Copy)]
8170enum CppComparableLevel {
8171 Pointer { konst: bool, volatil: bool },
8172 Reference,
8173 Array,
8174}
8175
8176fn cpp_comparable_declarator_levels(
8189 declarator: Node<'_>,
8190 source: &str,
8191) -> Option<Vec<CppComparableLevel>> {
8192 let mut levels = Vec::new();
8193 let mut current = declarator;
8194 loop {
8195 match current.kind() {
8196 "pointer_declarator" | "abstract_pointer_declarator" => {
8197 let qualifiers = cpp_cv_qualifiers(current, source);
8198 levels.push(CppComparableLevel::Pointer {
8199 konst: qualifiers.konst,
8200 volatil: qualifiers.volatil,
8201 });
8202 }
8203 "reference_declarator" | "abstract_reference_declarator" => {
8204 levels.push(CppComparableLevel::Reference);
8205 }
8206 "array_declarator" | "abstract_array_declarator" => {
8207 levels.push(CppComparableLevel::Array);
8208 }
8209 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
8210 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
8211 _ => return None,
8212 }
8213 let Some(next) = cpp_nested_declarator(current) else {
8214 return Some(levels);
8215 };
8216 current = next;
8217 }
8218}
8219
8220fn cpp_comparable_type_shape(
8227 type_node: Node<'_>,
8228 qualifiers: CppCvQualifiers,
8229 levels: Vec<CppComparableLevel>,
8230 source: &str,
8231 lexical_scope: &[String],
8232) -> Option<CppComparableParameter> {
8233 enum Work<'tree> {
8234 Visit {
8235 node: Node<'tree>,
8236 qualifiers: CppCvQualifiers,
8237 },
8238 ApplyLevels(Vec<CppComparableLevel>),
8239 BuildGeneric {
8240 argument_count: usize,
8241 },
8242 }
8243
8244 let mut nodes: Vec<CppComparableNode> = Vec::new();
8245 let mut values: Vec<usize> = Vec::new();
8246 let mut work = vec![
8247 Work::ApplyLevels(levels),
8248 Work::Visit {
8249 node: type_node,
8250 qualifiers,
8251 },
8252 ];
8253 while let Some(next) = work.pop() {
8254 match next {
8255 Work::Visit { node, qualifiers } => match node.kind() {
8256 "type_descriptor" => {
8257 let inner_type = node
8258 .child_by_field_name("type")
8259 .or_else(|| node.named_child(0))?;
8260 let mut cursor = node.walk();
8261 let declarator = node.child_by_field_name("declarator").or_else(|| {
8262 node.named_children(&mut cursor).find(|child| {
8263 child.id() != inner_type.id() && child.kind() != "type_qualifier"
8264 })
8265 });
8266 let levels = match declarator {
8267 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
8268 None => Vec::new(),
8269 };
8270 work.push(Work::ApplyLevels(levels));
8271 work.push(Work::Visit {
8272 node: inner_type,
8273 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
8274 });
8275 }
8276 "sized_type_specifier" => {
8277 let name = StructuredTypeName::new(
8282 vec![normalize_cpp_whitespace(node_text(node, source))],
8283 lexical_scope.to_vec(),
8284 false,
8285 )?;
8286 values.push(cpp_push_comparable_node(
8287 &mut nodes,
8288 CppComparableNode::Named {
8289 name,
8290 primitive: true,
8291 konst: qualifiers.konst,
8292 volatil: qualifiers.volatil,
8293 },
8294 ));
8295 }
8296 "qualified_identifier"
8297 | "scoped_identifier"
8298 | "scoped_type_identifier"
8299 | "type_identifier"
8300 | "field_identifier"
8301 | "identifier"
8302 | "namespace_identifier"
8303 | "primitive_type"
8304 | "template_type" => {
8305 let name = cpp_structured_named_type(node, source, lexical_scope)?;
8306 values.push(cpp_push_comparable_node(
8307 &mut nodes,
8308 CppComparableNode::Named {
8309 name,
8310 primitive: node.kind() == "primitive_type",
8311 konst: qualifiers.konst,
8312 volatil: qualifiers.volatil,
8313 },
8314 ));
8315 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
8316 let mut cursor = arguments_node.walk();
8317 let arguments = arguments_node
8318 .named_children(&mut cursor)
8319 .filter(|child| !child.is_extra() && child.kind() != "comment")
8320 .collect::<Vec<_>>();
8321 work.push(Work::BuildGeneric {
8322 argument_count: arguments.len(),
8323 });
8324 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
8325 node: argument,
8326 qualifiers: CppCvQualifiers::default(),
8327 }));
8328 }
8329 }
8330 _ => {
8331 let inner = node.child_by_field_name("type").or_else(|| {
8332 (node.named_child_count() == 1)
8333 .then(|| node.named_child(0))
8334 .flatten()
8335 })?;
8336 work.push(Work::Visit {
8337 node: inner,
8338 qualifiers,
8339 });
8340 }
8341 },
8342 Work::ApplyLevels(levels) => {
8343 let mut root = values.pop()?;
8344 for level in levels {
8345 let node = match level {
8346 CppComparableLevel::Pointer { konst, volatil } => {
8347 CppComparableNode::Pointer {
8348 inner: root,
8349 konst,
8350 volatil,
8351 }
8352 }
8353 CppComparableLevel::Reference => {
8354 CppComparableNode::Reference { inner: root }
8355 }
8356 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
8357 };
8358 root = cpp_push_comparable_node(&mut nodes, node);
8359 }
8360 values.push(root);
8361 }
8362 Work::BuildGeneric { argument_count } => {
8363 let value_count = argument_count.checked_add(1)?;
8364 let start = values.len().checked_sub(value_count)?;
8365 let mut built = values.split_off(start);
8366 let base = built.remove(0);
8367 values.push(cpp_push_comparable_node(
8368 &mut nodes,
8369 CppComparableNode::Generic {
8370 base,
8371 arguments: built,
8372 },
8373 ));
8374 }
8375 }
8376 }
8377 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
8378 debug_assert_eq!(
8379 root,
8380 nodes.len().saturating_sub(1),
8381 "comparable nodes are appended in post-order, so the root is the last one"
8382 );
8383 Some(CppComparableParameter { nodes, root })
8384}
8385
8386fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
8387 nodes.push(node);
8388 nodes.len() - 1
8389}
8390
8391fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
8397 let mut current = node;
8398 loop {
8399 match current.kind() {
8400 "template_type" => return current.child_by_field_name("arguments"),
8401 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
8402 current = current.child_by_field_name("name")?;
8403 }
8404 _ => return None,
8405 }
8406 }
8407}
8408
8409pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
8415 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
8416 loop {
8417 if matches!(
8418 current.kind(),
8419 "declaration" | "field_declaration" | "function_definition"
8420 ) && let Some(declarator) = current
8421 .child_by_field_name("declarator")
8422 .and_then(extract_function_declarator)
8423 {
8424 return Some(declarator);
8425 }
8426 current = current.parent()?;
8427 }
8428}
8429
8430fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
8431 let mut labels = Vec::new();
8432 let mut cursor = parameters_node.walk();
8433 for child in parameters_node.children(&mut cursor) {
8434 match child.kind() {
8435 "parameter_declaration" | "optional_parameter_declaration" => {
8436 if let Some(name_node) = child
8437 .child_by_field_name("declarator")
8438 .and_then(cpp_declarator_label_node)
8439 {
8440 labels.push(name_node);
8441 } else {
8442 labels.push(child);
8443 }
8444 }
8445 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
8446 labels.push(child);
8447 }
8448 _ => {}
8449 }
8450 }
8451 labels
8452}
8453
8454fn cpp_signature_search_start<'tree>(
8455 signature: &str,
8456 function_declarator: Node<'tree>,
8457 source: &str,
8458 ancestry: &ParentIndex<'tree>,
8459) -> usize {
8460 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
8461 return 0;
8462 };
8463 let raw = node_text(enclosing, source);
8464 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
8465 let offset = function_declarator
8466 .start_byte()
8467 .saturating_sub(enclosing.start_byte())
8468 .saturating_sub(leading_trim_bytes);
8469 offset.min(signature.len())
8470}
8471
8472fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
8473 match node.kind() {
8474 "identifier" | "field_identifier" => Some(node),
8475 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
8476 .child_by_field_name("declarator")
8477 .or_else(|| last_named_child(node))
8478 .and_then(cpp_declarator_label_node),
8479 "array_declarator" => node
8480 .child_by_field_name("declarator")
8481 .and_then(cpp_declarator_label_node),
8482 "function_declarator" => node
8483 .child_by_field_name("declarator")
8484 .or_else(|| node.child_by_field_name("name"))
8485 .or_else(|| last_named_child(node))
8486 .and_then(cpp_declarator_label_node),
8487 _ => None,
8488 }
8489}
8490
8491fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
8492 let base_type = parameter
8493 .child_by_field_name("type")
8494 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
8495 .unwrap_or_default();
8496 let declarator = cpp_parameter_declarator(parameter);
8497 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
8504 let mut cursor = parameter.walk();
8505 let qualifiers = parameter
8506 .named_children(&mut cursor)
8507 .filter(|child| child.kind() == "type_qualifier")
8508 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8509 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
8510 .collect::<Vec<_>>()
8511 .join(" ");
8512 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
8513 (true, _) => base_type,
8514 (_, true) => qualifiers,
8515 (false, false) => format!("{qualifiers} {base_type}"),
8516 };
8517 let declarator_suffix = declarator
8518 .map(|node| cpp_declarator_suffix_without_name(node, source))
8519 .unwrap_or_default();
8520
8521 let combined = if type_text.is_empty() {
8522 declarator_suffix
8523 } else if declarator_suffix.is_empty() {
8524 type_text
8525 } else {
8526 format!("{type_text} {declarator_suffix}")
8527 };
8528 normalize_cpp_type_text(&combined)
8529}
8530
8531fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
8532 parameter.child_by_field_name("declarator").or_else(|| {
8533 let mut cursor = parameter.walk();
8539 parameter
8540 .named_children(&mut cursor)
8541 .find(|child| is_cpp_abstract_declarator(child.kind()))
8542 })
8543}
8544
8545pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
8548 let mut current = Some(declarator);
8549 while let Some(node) = current {
8550 if matches!(
8551 node.kind(),
8552 "pointer_declarator"
8553 | "abstract_pointer_declarator"
8554 | "reference_declarator"
8555 | "abstract_reference_declarator"
8556 | "array_declarator"
8557 | "abstract_array_declarator"
8558 | "function_declarator"
8559 | "abstract_function_declarator"
8560 ) {
8561 return true;
8562 }
8563 current = cpp_nested_declarator(node);
8564 }
8565 false
8566}
8567
8568fn is_cpp_abstract_declarator(kind: &str) -> bool {
8569 matches!(
8570 kind,
8571 "abstract_pointer_declarator"
8572 | "abstract_reference_declarator"
8573 | "abstract_array_declarator"
8574 | "abstract_function_declarator"
8575 | "abstract_parenthesized_declarator"
8576 )
8577}
8578
8579fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
8580 node.child_by_field_name("declarator").or_else(|| {
8581 if is_cpp_abstract_declarator(node.kind()) {
8582 let mut cursor = node.walk();
8583 node.named_children(&mut cursor)
8584 .find(|child| is_cpp_abstract_declarator(child.kind()))
8585 } else {
8586 last_named_child(node)
8590 }
8591 })
8592}
8593
8594fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
8595 match node.kind() {
8596 "identifier" | "field_identifier" => String::new(),
8597 "pointer_declarator" | "abstract_pointer_declarator" => {
8598 let inner = cpp_nested_declarator(node)
8599 .map(|child| cpp_declarator_suffix_without_name(child, source))
8600 .unwrap_or_default();
8601 format!("*{inner}")
8602 }
8603 "reference_declarator" | "abstract_reference_declarator" => {
8604 let inner = cpp_nested_declarator(node)
8605 .map(|child| cpp_declarator_suffix_without_name(child, source))
8606 .unwrap_or_default();
8607 let reference = node
8608 .children(&mut node.walk())
8609 .find(|child| matches!(child.kind(), "&" | "&&"))
8610 .map(|child| node_text(child, source))
8611 .unwrap_or("&");
8612 format!("{reference}{inner}")
8613 }
8614 "array_declarator" | "abstract_array_declarator" => {
8615 let inner = cpp_nested_declarator(node)
8616 .map(|child| cpp_declarator_suffix_without_name(child, source))
8617 .unwrap_or_default();
8618 let size = node
8619 .child_by_field_name("size")
8620 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8621 .unwrap_or_default();
8622 format!("{inner}[{size}]")
8623 }
8624 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
8625 let inner = cpp_nested_declarator(node);
8626 inner
8627 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
8628 .unwrap_or_default()
8629 }
8630 "function_declarator" | "abstract_function_declarator" => {
8631 let inner = cpp_nested_declarator(node)
8632 .map(|child| cpp_declarator_suffix_without_name(child, source))
8633 .unwrap_or_default();
8634 let params = node
8635 .child_by_field_name("parameters")
8636 .map(|child| cpp_parameter_signature(child, source))
8637 .unwrap_or_else(|| "()".to_string());
8638 format!("{inner}{params}")
8639 }
8640 _ => {
8641 let text = normalize_cpp_whitespace(node_text(node, source));
8642 let name = extract_declarator_name(node, source);
8643 if name.is_empty() {
8644 text
8645 } else {
8646 text.replace(&name, "").trim().to_string()
8647 }
8648 }
8649 }
8650}
8651
8652fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
8653 collapse_cpp_whitespace(
8654 suffix
8655 .trim()
8656 .trim_start_matches("->")
8657 .trim_start_matches('{')
8658 .trim_end_matches(';'),
8659 )
8660}
8661
8662pub fn normalize_cpp_whitespace(value: &str) -> String {
8663 collapse_cpp_whitespace(value)
8664}
8665
8666fn normalize_cpp_type_text(value: &str) -> String {
8667 collapse_cpp_whitespace(value)
8668 .replace(", ", ",")
8669 .replace(" <", "<")
8670 .replace("< ", "<")
8671 .replace(" >", ">")
8672}
8673
8674fn collapse_cpp_whitespace(value: &str) -> String {
8675 let mut result = String::new();
8676 let mut prev_space = false;
8677 for ch in value.chars() {
8678 if ch.is_whitespace() {
8679 if !prev_space {
8680 result.push(' ');
8681 }
8682 prev_space = true;
8683 } else {
8684 result.push(ch);
8685 prev_space = false;
8686 }
8687 }
8688 result.trim().to_string()
8689}
8690
8691pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
8692 node_source_text(node, source)
8693}
8694
8695pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
8696 walk_named_tree_preorder(node, true, |node| {
8697 match node.kind() {
8698 "type_identifier" | "identifier" | "qualified_identifier" => {
8699 let text = node_text(node, source).trim();
8700 if !text.is_empty() {
8701 identifiers.insert(text.to_string());
8702 }
8703 }
8704 _ => {}
8705 }
8706 WalkControl::Continue
8707 });
8708}
8709
8710fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
8711 node.child_by_field_name("body").or_else(|| {
8712 let mut cursor = node.walk();
8713 node.named_children(&mut cursor).find(|child| {
8714 matches!(
8715 child.kind(),
8716 "declaration_list" | "field_declaration_list" | "enumerator_list"
8717 )
8718 })
8719 })
8720}
8721
8722fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
8733 if !matches!(
8734 node.kind(),
8735 "class_specifier" | "struct_specifier" | "union_specifier"
8736 ) {
8737 return None;
8738 }
8739 let body = cpp_body_node(node)?;
8740 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
8741 return None;
8742 }
8743 let open = body.child(0)?;
8744 let close = body.child(body.child_count().checked_sub(1)?)?;
8745 if open.kind() != "{"
8746 || open.is_missing()
8747 || close.kind() != "}"
8748 || close.is_missing()
8749 || close.end_byte() != body.end_byte()
8750 || body.end_byte() > node.end_byte()
8751 || node
8752 .parent()
8753 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
8754 {
8755 return None;
8756 }
8757 Some(close)
8758}
8759
8760fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
8761 if node.kind() == "namespace_definition" {
8762 return true;
8763 }
8764 let mut cursor = node.walk();
8765 node.named_children(&mut cursor)
8766 .any(cpp_contains_namespace_definition)
8767}
8768
8769struct CppNestedNamespaceSentinel<'tree> {
8770 function: Node<'tree>,
8771 body: Node<'tree>,
8772 namespace_components: Vec<String>,
8773}
8774
8775#[derive(Debug, Clone)]
8785pub struct CppSentinelRecoveredOwner {
8786 pub range: Range,
8787 pub owner_name_start_byte: usize,
8791 pub namespace_component_count: usize,
8795 pub scope_components: Vec<String>,
8796}
8797
8798#[derive(Debug, Clone)]
8799pub struct CppSentinelRecoveredClass {
8800 pub namespace_range: Range,
8801 pub namespace_scope_components: Vec<String>,
8802 pub class_range: Range,
8803 pub scope_components: Vec<String>,
8805 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
8809}
8810
8811pub fn cpp_sentinel_recovered_scope_for_node(
8817 node: Node<'_>,
8818 source: &str,
8819 recovered_classes: &[CppSentinelRecoveredClass],
8820) -> Option<Vec<String>> {
8821 let contains =
8822 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
8823 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
8824 for recovered in recovered_classes {
8825 for owner in recovered
8826 .owner_ranges
8827 .iter()
8828 .filter(|owner| contains(owner.range))
8829 {
8830 let replace = best_owner.is_none_or(|existing| {
8831 owner.range.end_byte.saturating_sub(owner.range.start_byte)
8832 < existing
8833 .range
8834 .end_byte
8835 .saturating_sub(existing.range.start_byte)
8836 });
8837 if replace {
8838 best_owner = Some(owner);
8839 }
8840 }
8841 }
8842 if let Some(owner) = best_owner {
8843 let mut scope = owner.scope_components.clone();
8844 if node.start_byte() < owner.owner_name_start_byte {
8845 scope.truncate(owner.namespace_component_count);
8846 }
8847 return Some(scope);
8848 }
8849
8850 let class = recovered_classes
8851 .iter()
8852 .filter(|recovered| contains(recovered.class_range))
8853 .min_by_key(|recovered| {
8854 recovered
8855 .class_range
8856 .end_byte
8857 .saturating_sub(recovered.class_range.start_byte)
8858 });
8859 let class_scope = class.is_some();
8860 let mut scope = if let Some(class) = class {
8861 class.scope_components.clone()
8862 } else {
8863 let namespace = recovered_classes
8864 .iter()
8865 .filter(|recovered| contains(recovered.namespace_range))
8866 .min_by_key(|recovered| {
8867 recovered
8868 .namespace_range
8869 .end_byte
8870 .saturating_sub(recovered.namespace_range.start_byte)
8871 })?;
8872 let mut scope = namespace.namespace_scope_components.clone();
8873 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
8874 let common_prefix = scope
8875 .iter()
8876 .zip(&parser_namespace)
8877 .take_while(|(recovered, parser)| recovered == parser)
8878 .count();
8879 scope.extend(parser_namespace.into_iter().skip(common_prefix));
8880 scope
8881 };
8882 if class_scope {
8883 let mut ancestor_components = Vec::new();
8884 let mut ancestor = node.parent();
8885 while let Some(current) = ancestor {
8886 if matches!(
8887 current.kind(),
8888 "class_specifier" | "struct_specifier" | "union_specifier"
8889 ) && let Some(name) = current.child_by_field_name("name")
8890 && let Some(name_components) = cpp_name_components(name, source)
8891 {
8892 ancestor_components.push(
8893 name_components
8894 .into_iter()
8895 .map(|component| component.name)
8896 .collect::<Vec<_>>(),
8897 );
8898 }
8899 ancestor = current.parent();
8900 }
8901 ancestor_components.reverse();
8902 let base_len = scope.len();
8903 for component in ancestor_components.into_iter().flatten() {
8904 if scope.len() >= base_len && scope.last() == Some(&component) {
8905 continue;
8906 }
8907 scope.push(component);
8908 }
8909 }
8910 Some(scope)
8911}
8912
8913struct CppSentinelFragmentedClassTail<'tree> {
8914 class_node: Node<'tree>,
8915 template_node: Option<Node<'tree>>,
8916 name: String,
8917 raw_supertypes: Option<Vec<String>>,
8918 fragmented: FragmentedExportBody,
8919 consumed_start: usize,
8920}
8921
8922struct CppSentinelFragmentedClassErrorPrefix<'tree> {
8923 name: String,
8924 open: Node<'tree>,
8925 raw_supertypes: Option<Vec<String>>,
8926}
8927
8928struct CppSentinelDirectBodyClassRegion {
8929 namespace_components: Vec<String>,
8930 class_start: usize,
8931 class_start_line: usize,
8932 class_close_end: usize,
8933 class_close_line: usize,
8934 name: String,
8935}
8936
8937fn cpp_sentinel_body_class_candidate<'tree>(
8938 child: Node<'tree>,
8939) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
8940 if matches!(
8941 child.kind(),
8942 "class_specifier" | "struct_specifier" | "union_specifier"
8943 ) {
8944 return Some((child, None));
8945 }
8946 if child.kind() != "template_declaration" {
8947 if child.kind() == "declaration" {
8948 return Some((first_class_like_child(child)?, None));
8949 }
8950 return None;
8951 }
8952 let mut cursor = child.walk();
8953 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
8954 if matches!(
8955 candidate.kind(),
8956 "class_specifier" | "struct_specifier" | "union_specifier"
8957 ) {
8958 Some(candidate)
8959 } else if candidate.kind() == "declaration" {
8960 first_class_like_child(candidate)
8961 } else {
8962 None
8963 }
8964 })?;
8965 Some((class_node, Some(child)))
8966}
8967
8968fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
8974 node: Node<'tree>,
8975 source: &str,
8976) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
8977 let name = malformed_class_error_owner_name(node, source)?;
8978 let mut cursor = node.walk();
8979 let children = node.children(&mut cursor).collect::<Vec<_>>();
8980 let keyword = children.first()?;
8981 let open_index = children.iter().position(|child| child.kind() == "{")?;
8982 if children[open_index + 1..]
8983 .iter()
8984 .any(|child| child.kind() == "}")
8985 {
8986 return None;
8987 }
8988 let raw_supertypes =
8989 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
8990 Some(CppSentinelFragmentedClassErrorPrefix {
8991 name,
8992 open: children[open_index],
8993 raw_supertypes,
8994 })
8995}
8996
8997fn cpp_sentinel_direct_body_class_candidate<'tree>(
8998 child: Node<'tree>,
8999) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
9000 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
9001 return Some(candidate);
9002 }
9003 if child.kind() != "template_declaration" {
9004 return None;
9005 }
9006 let mut cursor = child.walk();
9007 let wrapper = child
9008 .named_children(&mut cursor)
9009 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
9010 Some((first_class_like_child(wrapper)?, Some(child)))
9011}
9012
9013fn cpp_sentinel_direct_namespace_components(
9014 function: Node<'_>,
9015 body: Node<'_>,
9016 source: &str,
9017) -> Option<Vec<String>> {
9018 let mut cursor = function.walk();
9019 let children = function
9020 .named_children(&mut cursor)
9021 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
9022 .collect::<Vec<_>>();
9023 let sentinel_index = children.iter().rposition(|child| {
9024 direct_identifier_name(*child, source)
9025 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
9026 })?;
9027 let mut identifiers = Vec::new();
9028 let mut stack = children[sentinel_index + 1..]
9029 .iter()
9030 .rev()
9031 .copied()
9032 .collect::<Vec<_>>();
9033 while let Some(current) = stack.pop() {
9034 if let Some(name) = direct_identifier_name(current, source) {
9035 identifiers.push(name);
9036 continue;
9037 }
9038 let mut cursor = current.walk();
9039 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
9040 stack.extend(children.into_iter().rev());
9041 }
9042 let [keyword, namespace] = identifiers.as_slice() else {
9043 return None;
9044 };
9045 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
9046 .then(|| vec![namespace.clone()])
9047}
9048
9049fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
9050 let mut sibling = class_semicolon.next_named_sibling();
9051 let namespace_close = loop {
9052 let Some(current) = sibling else {
9053 return false;
9054 };
9055 sibling = current.next_named_sibling();
9056 if current.kind() != "comment" {
9057 break current;
9058 }
9059 };
9060 if !cpp_is_stray_close_brace(namespace_close, source) {
9061 return false;
9062 }
9063 loop {
9064 let Some(current) = sibling else {
9065 return false;
9066 };
9067 sibling = current.next_named_sibling();
9068 if current.kind() == "comment" {
9069 continue;
9070 }
9071 return direct_identifier_name(current, source)
9072 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
9073 }
9074}
9075
9076fn cpp_sentinel_macro_body_class_region<'tree>(
9077 node: Node<'tree>,
9078 source: &str,
9079 ancestry: &ParentIndex<'tree>,
9080) -> Option<CppSentinelDirectBodyClassRegion> {
9081 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
9082 return None;
9083 };
9084 if node.kind() != "function_definition" || !node.has_error() {
9085 return None;
9086 }
9087 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
9088 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
9089 let mut cursor = body.walk();
9090 let candidates = body
9091 .named_children(&mut cursor)
9092 .filter_map(cpp_sentinel_direct_body_class_candidate)
9093 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
9094 .collect::<Vec<_>>();
9095 let [(class_node, template_node)] = candidates.as_slice() else {
9096 return None;
9097 };
9098 let original_body = cpp_body_node(*class_node)?;
9099 let name = class_like_name(*class_node, source, ancestry)?;
9100 if name.is_empty() || cpp_export_macro_token(&name) {
9101 return None;
9102 }
9103
9104 let mut sibling = node.next_named_sibling();
9105 let (class_close_start, class_close_end, class_close_line) = loop {
9106 let current = sibling?;
9107 let next = current.next_named_sibling();
9108 if cpp_is_stray_close_brace(current, source)
9109 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
9110 {
9111 let semicolon = next.expect("checked above");
9112 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
9113 return None;
9114 }
9115 break (
9116 current.start_byte(),
9117 semicolon.end_byte(),
9118 semicolon.end_position().row + 1,
9119 );
9120 }
9121 sibling = next;
9122 };
9123 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
9124 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
9125 let root = tree.root_node();
9126 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
9127 let reparsed_ancestry = ParentIndex::new(root);
9130 let reparsed =
9131 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
9132 if reparsed.name != name
9133 || reparsed.declaration_node.start_byte() != class_node.start_byte()
9134 || reparsed.body.start_byte() != original_body.start_byte()
9135 || class_close_start <= reparsed.body.end_byte()
9136 || class_close_end <= class_node.end_byte()
9137 {
9138 return None;
9139 }
9140 Some(CppSentinelDirectBodyClassRegion {
9141 namespace_components,
9142 class_start: reparse_start,
9143 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
9144 node.start_position().row + 1
9145 }),
9146 class_close_end,
9147 class_close_line,
9148 name,
9149 })
9150}
9151
9152fn cpp_nested_namespace_sentinel<'tree>(
9164 node: Node<'tree>,
9165 source: &str,
9166 ancestry: &ParentIndex<'tree>,
9167) -> Option<CppNestedNamespaceSentinel<'tree>> {
9168 if !node.has_error() {
9169 return None;
9170 }
9171
9172 let (function, mut namespace_components) = if node.kind() == "ERROR" {
9173 let mut cursor = node.walk();
9174 let functions = node
9175 .named_children(&mut cursor)
9176 .filter(|child| child.kind() == "function_definition")
9177 .collect::<Vec<_>>();
9178 let [function] = functions.as_slice() else {
9179 return None;
9180 };
9181 if !function.has_error() {
9182 return None;
9183 }
9184 let mut cursor = node.walk();
9185 let children = node.children(&mut cursor).collect::<Vec<_>>();
9186 let function_index = children
9187 .iter()
9188 .position(|child| same_node(*child, *function))?;
9189 let [outer_keyword, outer_name, outer_open] =
9190 children.get(function_index.checked_sub(3)?..function_index)?
9191 else {
9192 return None;
9193 };
9194 if outer_keyword.kind() != "namespace"
9195 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
9196 || outer_open.kind() != "{"
9197 {
9198 return None;
9199 }
9200 (
9201 *function,
9202 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
9203 )
9204 } else if node.kind() == "function_definition" {
9205 let declaration_list = node.parent()?;
9206 let namespace = declaration_list.parent()?;
9207 if declaration_list.kind() != "declaration_list"
9208 || namespace.kind() != "namespace_definition"
9209 || namespace.child_by_field_name("body") != Some(declaration_list)
9210 {
9211 return None;
9212 }
9213 (node, Vec::new())
9214 } else {
9215 return None;
9216 };
9217
9218 let mut cursor = function.walk();
9219 let named = function
9220 .named_children(&mut cursor)
9221 .filter(|child| child.kind() != "comment")
9222 .collect::<Vec<_>>();
9223 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
9224 return None;
9225 };
9226 if first_type.kind() != "type_identifier" {
9227 return None;
9228 }
9229 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
9230 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
9231 return None;
9232 }
9233 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
9234 return None;
9235 }
9236 let inner_keyword = inner_error.named_child(0)?;
9237 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
9238 return None;
9239 }
9240 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
9241 return None;
9242 }
9243 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
9244 if inner_name.is_empty() || body.kind() != "compound_statement" {
9245 return None;
9246 }
9247 namespace_components.push(inner_name);
9248
9249 let mut cursor = body.walk();
9250 let has_complete_class = body.named_children(&mut cursor).any(|child| {
9251 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
9252 cpp_body_node(class_node).is_some()
9253 && class_like_name(class_node, source, ancestry)
9254 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
9255 })
9256 });
9257 if !has_complete_class
9258 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
9259 {
9260 return None;
9261 }
9262
9263 Some(CppNestedNamespaceSentinel {
9264 function,
9265 body: *body,
9266 namespace_components,
9267 })
9268}
9269
9270fn cpp_root_namespace_sentinel<'tree>(
9279 node: Node<'tree>,
9280 source: &str,
9281 ancestry: &ParentIndex<'tree>,
9282) -> Option<CppNestedNamespaceSentinel<'tree>> {
9283 if node.kind() != "function_definition"
9284 || !node.has_error()
9285 || node.parent()?.kind() != "translation_unit"
9286 {
9287 return None;
9288 }
9289 let first_type = node.child_by_field_name("type")?;
9290 let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
9291 if first_type.kind() != "type_identifier"
9292 || sentinel.is_empty()
9293 || !cpp_export_macro_token(&sentinel)
9294 {
9295 return None;
9296 }
9297 let declarator = node.child_by_field_name("declarator")?;
9298 let body = node.child_by_field_name("body")?;
9299 if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
9300 return None;
9301 }
9302 let mut cursor = node.walk();
9303 let named = node
9304 .named_children(&mut cursor)
9305 .filter(|child| child.kind() != "comment")
9306 .collect::<Vec<_>>();
9307 let [named_type, named_declarator, named_body] = named.as_slice() else {
9308 return None;
9309 };
9310 if !same_node(*named_type, first_type)
9311 || !same_node(*named_declarator, declarator)
9312 || !same_node(*named_body, body)
9313 {
9314 return None;
9315 }
9316 let mut declarator_components = Vec::new();
9317 let mut valid_components = true;
9318 walk_named_tree_preorder(declarator, true, |component| {
9319 if !matches!(
9320 component.kind(),
9321 "identifier" | "namespace_identifier" | "type_identifier"
9322 ) {
9323 return WalkControl::Continue;
9324 }
9325 let Some(component) = canonical_cpp_qualified_component(component, source) else {
9326 valid_components = false;
9327 return WalkControl::Break;
9328 };
9329 declarator_components.push(component.name);
9330 WalkControl::SkipChildren
9331 });
9332 if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
9333 return None;
9334 }
9335 declarator_components.remove(0);
9336 let namespace_components = declarator_components;
9337 if namespace_components.is_empty()
9338 || namespace_components
9339 .iter()
9340 .any(|component| component.is_empty() || cpp_export_macro_token(component))
9341 {
9342 return None;
9343 }
9344
9345 let mut cursor = body.walk();
9346 let has_complete_class = body.named_children(&mut cursor).any(|child| {
9347 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
9348 cpp_body_node(class_node).is_some()
9349 && class_like_name(class_node, source, ancestry)
9350 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
9351 })
9352 });
9353 if !has_complete_class
9354 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
9355 {
9356 return None;
9357 }
9358
9359 Some(CppNestedNamespaceSentinel {
9360 function: node,
9361 body,
9362 namespace_components,
9363 })
9364}
9365
9366fn cpp_sentinel_fragmented_class_tail<'tree>(
9375 function: Node<'tree>,
9376 body: Node<'tree>,
9377 source: &str,
9378 ancestry: &ParentIndex<'tree>,
9379) -> Option<CppSentinelFragmentedClassTail<'tree>> {
9380 let mut cursor = body.walk();
9381 let candidates = body
9382 .named_children(&mut cursor)
9383 .filter_map(|child| {
9384 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
9385 let class_body = cpp_body_node(class_node)?;
9386 if !class_node.has_error() {
9387 return None;
9388 }
9389 let name = class_like_name(class_node, source, ancestry)?;
9390 let raw_supertypes =
9391 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
9392 .then(|| extract_cpp_supertypes(class_node, source));
9393 return Some((
9394 class_node,
9395 template_node,
9396 name,
9397 class_body,
9398 class_body.start_byte().checked_add(1)?,
9399 raw_supertypes,
9400 ));
9401 }
9402 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
9403 Some((
9404 child,
9405 None,
9406 prefix.name,
9407 prefix.open,
9408 prefix.open.end_byte(),
9409 prefix.raw_supertypes,
9410 ))
9411 })
9412 .collect::<Vec<_>>();
9413 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
9414 candidates.as_slice()
9415 else {
9416 return None;
9417 };
9418 if name.is_empty() || cpp_export_macro_token(name) {
9419 return None;
9420 }
9421
9422 let (close, semicolon) =
9423 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
9424
9425 let reparse_end = close.start_byte();
9426 if *reparse_start >= reparse_end {
9427 return None;
9428 }
9429 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
9430 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
9431 return None;
9432 }
9433 let class_range = Range {
9434 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
9435 end_byte: semicolon.end_byte(),
9436 start_line: template_node.map_or(class_node.start_position().row, |node| {
9437 node.start_position().row
9438 }) + 1,
9439 end_line: semicolon.end_position().row + 1,
9440 };
9441 Some(CppSentinelFragmentedClassTail {
9442 class_node: *class_node,
9443 template_node: *template_node,
9444 name: name.clone(),
9445 raw_supertypes: raw_supertypes.clone(),
9446 fragmented: FragmentedExportBody {
9447 reparse_start: *reparse_start,
9448 reparse_end,
9449 class_range,
9450 },
9451 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
9452 })
9453}
9454
9455pub fn cpp_sentinel_recovered_classes(
9464 root: Node<'_>,
9465 source: &str,
9466) -> Vec<CppSentinelRecoveredClass> {
9467 if !root.has_error() {
9468 return Vec::new();
9469 }
9470 let ancestry = ParentIndex::new(root);
9474 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
9475 let mut stack = vec![root];
9476 while let Some(current) = stack.pop() {
9477 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
9478 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
9479 {
9480 let namespace_components = cpp_sentinel_recovered_namespace_components(
9481 recovered.function,
9482 &recovered.namespace_components,
9483 source,
9484 );
9485 let fragmented = cpp_sentinel_fragmented_class_tail(
9486 recovered.function,
9487 recovered.body,
9488 source,
9489 &ancestry,
9490 );
9491 let mut class_candidates = Vec::new();
9492 let mut cursor = recovered.body.walk();
9493 for (class_node, template_node) in recovered
9494 .body
9495 .named_children(&mut cursor)
9496 .filter_map(cpp_sentinel_body_class_candidate)
9497 {
9498 let Some(name) = class_like_name(class_node, source, &ancestry) else {
9499 continue;
9500 };
9501 if name.is_empty() || cpp_export_macro_token(&name) {
9502 continue;
9503 }
9504 let is_fragmented = fragmented
9505 .as_ref()
9506 .is_some_and(|tail| same_node(tail.class_node, class_node));
9507 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
9508 continue;
9509 }
9510 let class_range = if is_fragmented {
9511 fragmented
9512 .as_ref()
9513 .map(|tail| tail.fragmented.class_range)
9514 .expect("fragmented class range is present when class matches")
9515 } else {
9516 cpp_declaration_range(template_node.unwrap_or(class_node))
9517 };
9518 class_candidates.push((class_range, name));
9519 }
9520 if let Some(fragmented) = fragmented
9521 .as_ref()
9522 .filter(|tail| tail.class_node.kind() == "ERROR")
9523 {
9524 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
9525 }
9526
9527 let mut owner_ranges =
9528 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
9529 cpp_sentinel_extend_unique_owner_ranges(
9530 &mut owner_ranges,
9531 cpp_sentinel_recovered_sibling_owner_ranges(
9532 recovered.function,
9533 &namespace_components,
9534 source,
9535 ),
9536 );
9537 for (class_range, name) in class_candidates {
9538 push_cpp_sentinel_recovered_class(
9539 &mut recovered_classes,
9540 cpp_declaration_range(recovered.body),
9541 &namespace_components,
9542 class_range,
9543 name,
9544 &owner_ranges,
9545 );
9546 }
9547
9548 if let Some(declaration_list) = recovered
9549 .function
9550 .parent()
9551 .filter(|parent| parent.kind() == "declaration_list")
9552 {
9553 let outer_namespace =
9554 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
9555 push_cpp_sentinel_sibling_classes(
9556 &mut recovered_classes,
9557 declaration_list,
9558 recovered.function,
9559 &outer_namespace,
9560 source,
9561 &ancestry,
9562 );
9563 }
9564 } else if let Some(region) =
9565 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
9566 {
9567 let namespace_components = cpp_sentinel_recovered_namespace_components(
9568 current,
9569 ®ion.namespace_components,
9570 source,
9571 );
9572 let owner_container = current
9573 .parent()
9574 .filter(|parent| parent.kind() == "declaration_list")
9575 .unwrap_or(current);
9576 let owner_ranges =
9577 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
9578 push_cpp_sentinel_recovered_class(
9579 &mut recovered_classes,
9580 cpp_declaration_range(owner_container),
9581 &namespace_components,
9582 Range {
9583 start_byte: region.class_start,
9584 end_byte: region.class_close_end,
9585 start_line: region.class_start_line,
9586 end_line: region.class_close_line,
9587 },
9588 region.name,
9589 &owner_ranges,
9590 );
9591 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
9592 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
9597 region;
9598 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
9599 continue;
9600 };
9601 let root = tree.root_node();
9602 let template_node = cpp_sentinel_reparsed_leading_template(root);
9603 let reparsed_ancestry = ParentIndex::new(root);
9605 let Some(reparsed_class) =
9606 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
9607 else {
9608 continue;
9609 };
9610 let class_node = reparsed_class.declaration_node;
9611 let name = reparsed_class.name;
9612 let namespace_components =
9613 cpp_sentinel_recovered_namespace_components(current, &[], source);
9614 let owner_container = current
9615 .parent()
9616 .filter(|parent| parent.kind() == "declaration_list")
9617 .unwrap_or(current);
9618 let mut owner_ranges =
9619 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
9620 cpp_sentinel_extend_unique_owner_ranges(
9621 &mut owner_ranges,
9622 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
9623 );
9624 push_cpp_sentinel_recovered_class(
9625 &mut recovered_classes,
9626 cpp_declaration_range(owner_container),
9627 &namespace_components,
9628 Range {
9629 start_byte: class_start,
9630 end_byte: close_end,
9631 start_line: class_node.start_position().row + 1,
9632 end_line: class_node.end_position().row + 1,
9633 },
9634 name,
9635 &owner_ranges,
9636 );
9637 if owner_container.kind() == "declaration_list" {
9638 push_cpp_sentinel_sibling_classes(
9639 &mut recovered_classes,
9640 owner_container,
9641 current,
9642 &namespace_components,
9643 source,
9644 &ancestry,
9645 );
9646 }
9647 }
9648
9649 let mut cursor = current.walk();
9650 stack.extend(current.named_children(&mut cursor));
9651 }
9652 let shadowed = recovered_classes
9658 .iter()
9659 .map(|candidate| {
9660 recovered_classes.iter().any(|container| {
9661 container.class_range.start_byte <= candidate.class_range.start_byte
9662 && container.class_range.end_byte >= candidate.class_range.end_byte
9663 && container.class_range != candidate.class_range
9664 && container.namespace_scope_components.len()
9665 > candidate.namespace_scope_components.len()
9666 && container
9667 .namespace_scope_components
9668 .starts_with(&candidate.namespace_scope_components)
9669 })
9670 })
9671 .collect::<Vec<_>>();
9672 let mut index = 0usize;
9673 recovered_classes.retain(|_| {
9674 let keep = !shadowed[index];
9675 index += 1;
9676 keep
9677 });
9678 recovered_classes
9679}
9680
9681fn push_cpp_sentinel_sibling_classes<'tree>(
9687 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
9688 declaration_list: Node<'tree>,
9689 sentinel_node: Node<'tree>,
9690 namespace_components: &[String],
9691 source: &str,
9692 ancestry: &ParentIndex<'tree>,
9693) {
9694 let owner_ranges =
9695 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
9696 let namespace_range = cpp_declaration_range(declaration_list);
9697 let mut cursor = declaration_list.walk();
9698 for (class_node, template_node) in declaration_list
9699 .named_children(&mut cursor)
9700 .filter(|child| !same_node(*child, sentinel_node))
9701 .filter_map(cpp_sentinel_body_class_candidate)
9702 {
9703 let Some(name) = class_like_name(class_node, source, ancestry) else {
9704 continue;
9705 };
9706 if name.is_empty()
9707 || cpp_export_macro_token(&name)
9708 || cpp_complete_class_body_close(class_node).is_none()
9709 {
9710 continue;
9711 }
9712 push_cpp_sentinel_recovered_class(
9713 recovered_classes,
9714 namespace_range,
9715 namespace_components,
9716 cpp_declaration_range(template_node.unwrap_or(class_node)),
9717 name,
9718 &owner_ranges,
9719 );
9720 }
9721}
9722
9723fn push_cpp_sentinel_recovered_class(
9724 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
9725 namespace_range: Range,
9726 namespace_components: &[String],
9727 class_range: Range,
9728 name: String,
9729 owner_ranges: &[CppSentinelRecoveredOwner],
9730) {
9731 let mut scope_components = namespace_components.to_vec();
9732 scope_components.push(name);
9733 let owner_ranges = owner_ranges
9734 .iter()
9735 .filter(|owner| owner.scope_components.starts_with(&scope_components))
9736 .cloned()
9737 .collect::<Vec<_>>();
9738 if recovered_classes.iter().any(|existing| {
9739 existing.class_range == class_range && existing.scope_components == scope_components
9740 }) {
9741 return;
9742 }
9743 recovered_classes.push(CppSentinelRecoveredClass {
9744 namespace_range,
9745 namespace_scope_components: namespace_components.to_vec(),
9746 class_range,
9747 scope_components,
9748 owner_ranges,
9749 });
9750}
9751
9752fn cpp_sentinel_recovered_namespace_components(
9753 function: Node<'_>,
9754 recovered_components: &[String],
9755 source: &str,
9756) -> Vec<String> {
9757 let mut ancestor_components = Vec::new();
9758 let mut ancestor = function.parent();
9759 while let Some(current) = ancestor {
9760 if current.kind() == "namespace_definition"
9761 && let Some(name_node) = current.child_by_field_name("name")
9762 && let Some(components) = cpp_name_components(name_node, source)
9763 {
9764 ancestor_components.push(
9765 components
9766 .into_iter()
9767 .map(|component| component.name)
9768 .collect::<Vec<_>>(),
9769 );
9770 }
9771 ancestor = current.parent();
9772 }
9773 ancestor_components.reverse();
9774 let mut ancestors = ancestor_components
9775 .into_iter()
9776 .flatten()
9777 .collect::<Vec<_>>();
9778
9779 let overlap = (0..=ancestors.len().min(recovered_components.len()))
9780 .rev()
9781 .find(|length| {
9782 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
9783 })
9784 .unwrap_or(0);
9785 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
9786 ancestors
9787}
9788
9789fn cpp_sentinel_recovered_owner_ranges(
9790 body: Node<'_>,
9791 namespace_components: &[String],
9792 source: &str,
9793) -> Vec<CppSentinelRecoveredOwner> {
9794 let mut owners = Vec::new();
9795 walk_named_tree_preorder(body, true, |node| {
9796 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
9797 });
9798 owners
9799}
9800
9801fn cpp_sentinel_collect_owner_range(
9802 node: Node<'_>,
9803 namespace_components: &[String],
9804 source: &str,
9805 owners: &mut Vec<CppSentinelRecoveredOwner>,
9806) -> WalkControl {
9807 if node.kind() != "function_definition" {
9808 return WalkControl::Continue;
9809 }
9810 let Some(function_declarator) = extract_function_declarator(node) else {
9811 return WalkControl::Continue;
9812 };
9813 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
9814 return WalkControl::Continue;
9815 };
9816 let Some(mut components) = cpp_name_components(name_node, source) else {
9817 return WalkControl::Continue;
9818 };
9819 if components.len() <= 1 {
9820 return WalkControl::Continue;
9821 }
9822 components.pop();
9823 let mut owner_components = components
9824 .into_iter()
9825 .map(|component| component.name)
9826 .collect::<Vec<_>>();
9827 let overlap = (0..=namespace_components.len().min(owner_components.len()))
9828 .rev()
9829 .find(|length| {
9830 owner_components[..*length]
9831 == namespace_components[namespace_components.len().saturating_sub(*length)..]
9832 })
9833 .unwrap_or(0);
9834 let mut scope_components = namespace_components.to_vec();
9835 scope_components.extend(owner_components.drain(overlap..));
9836 if scope_components.len() <= namespace_components.len() {
9837 return WalkControl::Continue;
9838 }
9839 let range = cpp_declaration_range(node);
9840 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
9841 existing.range == range && existing.scope_components == scope_components
9842 }) {
9843 owners.push(CppSentinelRecoveredOwner {
9844 range,
9845 owner_name_start_byte: name_node.start_byte(),
9846 namespace_component_count: namespace_components.len(),
9847 scope_components,
9848 });
9849 }
9850 WalkControl::Continue
9851}
9852
9853fn cpp_sentinel_extend_unique_owner_ranges(
9854 owners: &mut Vec<CppSentinelRecoveredOwner>,
9855 additional: Vec<CppSentinelRecoveredOwner>,
9856) {
9857 for owner in additional {
9858 if !owners.iter().any(|existing| {
9859 existing.range == owner.range && existing.scope_components == owner.scope_components
9860 }) {
9861 owners.push(owner);
9862 }
9863 }
9864}
9865
9866fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
9867 if node.kind() != "ERROR" || node.named_child_count() != 1 {
9868 return false;
9869 }
9870 let Some(end_name) = node.named_child(0) else {
9871 return false;
9872 };
9873 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
9874 return false;
9875 }
9876 let mut cursor = node.walk();
9877 node.children(&mut cursor)
9878 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
9879}
9880
9881fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
9885 parent: Node<'_>,
9886 sentinel_node: Node<'_>,
9887 namespace_components: &[String],
9888 source: &str,
9889) -> Vec<CppSentinelRecoveredOwner> {
9890 let mut owners = Vec::new();
9891 let mut after_sentinel = false;
9892 let mut cursor = parent.walk();
9893 for child in parent.named_children(&mut cursor) {
9894 if !after_sentinel {
9895 if same_node(child, sentinel_node) {
9896 after_sentinel = true;
9897 }
9898 continue;
9899 }
9900 walk_named_tree_preorder(child, true, |node| {
9901 if node.kind() == "namespace_definition" {
9902 return WalkControl::SkipChildren;
9903 }
9904 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
9905 });
9906 }
9907 owners
9908}
9909
9910fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
9914 parent: Node<'_>,
9915 sentinel_node: Node<'_>,
9916 namespace_components: &[String],
9917 source: &str,
9918) -> Option<Vec<CppSentinelRecoveredOwner>> {
9919 let mut owners = Vec::new();
9920 let mut after_namespace = false;
9921 let mut cursor = parent.walk();
9922 for child in parent.named_children(&mut cursor) {
9923 if !after_namespace {
9924 if same_node(child, sentinel_node) {
9925 after_namespace = true;
9926 }
9927 continue;
9928 }
9929 if cpp_sentinel_namespace_end(child, source) {
9930 return Some(owners);
9931 }
9932 walk_named_tree_preorder(child, true, |node| {
9933 if node.kind() == "namespace_definition" {
9934 return WalkControl::SkipChildren;
9935 }
9936 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
9937 });
9938 }
9939 None
9940}
9941
9942fn cpp_sentinel_recovered_sibling_owner_ranges(
9943 sentinel_node: Node<'_>,
9944 namespace_components: &[String],
9945 source: &str,
9946) -> Vec<CppSentinelRecoveredOwner> {
9947 let Some(declaration_list) = sentinel_node
9948 .parent()
9949 .filter(|parent| parent.kind() == "declaration_list")
9950 else {
9951 return Vec::new();
9952 };
9953 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
9954 declaration_list,
9955 sentinel_node,
9956 namespace_components,
9957 source,
9958 );
9959
9960 let Some(namespace) = declaration_list
9961 .parent()
9962 .filter(|parent| parent.kind() == "namespace_definition")
9963 else {
9964 return owners;
9965 };
9966 let Some(outer_parent) = namespace.parent() else {
9967 return owners;
9968 };
9969 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
9970 outer_parent,
9971 namespace,
9972 namespace_components,
9973 source,
9974 ) {
9975 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
9976 }
9977 owners
9978}
9979
9980fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
9981 let mut current = function_declarator.child_by_field_name("declarator")?;
9982 loop {
9983 if matches!(
9984 current.kind(),
9985 "qualified_identifier"
9986 | "scoped_identifier"
9987 | "scoped_type_identifier"
9988 | "identifier"
9989 | "field_identifier"
9990 | "operator_name"
9991 | "destructor_name"
9992 | "literal_operator_name"
9993 ) {
9994 return Some(current);
9995 }
9996 current = current
9997 .child_by_field_name("declarator")
9998 .or_else(|| current.child_by_field_name("name"))
9999 .or_else(|| last_named_child(current))?;
10000 }
10001}
10002
10003fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
10004 match node.kind() {
10005 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
10006 let mut components = match node.child_by_field_name("scope") {
10007 Some(scope) => cpp_name_components(scope, source)?,
10008 None => Vec::new(),
10009 };
10010 let name = node.child_by_field_name("name")?;
10011 components.push(canonical_cpp_qualified_component(name, source)?);
10012 Some(components)
10013 }
10014 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
10015 }
10016}
10017
10018fn cpp_sentinel_fragment_boundary<'tree>(
10019 function: Node<'tree>,
10020 class_node: Node<'tree>,
10021 class_body: Node<'tree>,
10022 source: &str,
10023) -> Option<(Node<'tree>, Node<'tree>)> {
10024 let declaration_list = function.parent()?;
10025 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
10026 return None;
10027 }
10028 let namespace = declaration_list.parent()?;
10029 if namespace.kind() != "namespace_definition"
10030 || namespace.child_by_field_name("body") != Some(declaration_list)
10031 {
10032 return None;
10033 }
10034 let mut cursor = declaration_list.walk();
10035 let closes = declaration_list
10036 .children(&mut cursor)
10037 .filter(|child| {
10038 !child.is_named()
10039 && child.kind() == "}"
10040 && child.start_byte() >= function.end_byte()
10041 && child.start_byte() > class_node.end_byte()
10042 && child.start_byte() > class_body.start_byte()
10043 })
10044 .collect::<Vec<_>>();
10045 let [close] = closes.as_slice() else {
10046 return None;
10047 };
10048 let semicolon = namespace.next_named_sibling()?;
10049 if !cpp_is_stray_semicolon(semicolon, source)
10050 || close.end_byte() != namespace.end_byte()
10051 || semicolon.start_byte() < namespace.end_byte()
10052 {
10053 return None;
10054 }
10055 Some((*close, semicolon))
10056}
10057
10058fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
10084 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
10085 {
10086 return None;
10087 }
10088 let mut declarator_cursor = node.walk();
10094 let preserved_callable = node
10095 .children_by_field_name("declarator", &mut declarator_cursor)
10096 .find_map(extract_function_declarator);
10097 let mut cursor = node.walk();
10105 let first = node
10106 .named_children(&mut cursor)
10107 .find(|child| child.kind() != "comment")?;
10108 if first.kind() != "type_identifier" {
10109 return None;
10110 }
10111 let sentinel = normalize_cpp_whitespace(node_text(first, source));
10112 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
10113 return None;
10114 }
10115 let mut start = first.end_byte();
10122 let mut after_first = false;
10123 let mut cursor = node.walk();
10124 for child in node.named_children(&mut cursor) {
10125 if !after_first {
10126 if same_node(child, first) {
10127 after_first = true;
10128 }
10129 continue;
10130 }
10131 if matches!(child.kind(), "identifier" | "type_identifier")
10132 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
10133 {
10134 start = child.end_byte();
10135 } else {
10136 break;
10137 }
10138 }
10139 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
10148 let mut class_start = None;
10149 let mut template_start = None;
10150 let mut stack = vec![node];
10151 while let Some(current) = stack.pop() {
10152 if current.start_byte() >= prefix_end {
10153 continue;
10154 }
10155 if matches!(
10156 current.kind(),
10157 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
10158 ) {
10159 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
10160 "class" | "struct" | "union" | "enum" => {
10161 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
10162 seen.min(current.start_byte())
10163 }));
10164 }
10165 "template" => {
10166 template_start =
10167 Some(template_start.map_or(current.start_byte(), |seen: usize| {
10168 seen.min(current.start_byte())
10169 }));
10170 }
10171 _ => {}
10172 }
10173 }
10174 let mut cursor = current.walk();
10175 stack.extend(current.children(&mut cursor));
10176 }
10177 if preserved_callable.is_some_and(|callable| {
10178 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
10179 }) {
10180 return None;
10181 }
10182 if let Some(class_start) = class_start {
10183 start = template_start
10184 .filter(|template_start| *template_start < class_start)
10185 .unwrap_or(class_start);
10186 }
10187 Some((start, class_start))
10188}
10189
10190fn cpp_sentinel_macro_class_region<'tree>(
10196 node: Node<'tree>,
10197 source: &str,
10198) -> Option<(usize, usize, usize, usize, usize, usize)> {
10199 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
10200 return None;
10201 };
10202 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
10203 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
10204 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
10205 if class_start >= body_open_start {
10206 return None;
10207 }
10208 let sibling_close = {
10209 let mut sibling = node.next_named_sibling();
10210 let mut found = None;
10211 while let Some(current) = sibling {
10212 let next = current.next_named_sibling();
10213 if cpp_is_stray_close_brace(current, source)
10214 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
10215 {
10216 let semicolon = next.expect("checked above");
10217 found = Some((
10218 current.start_byte(),
10219 semicolon.end_byte(),
10220 semicolon.end_position().row + 1,
10221 ));
10222 break;
10223 }
10224 sibling = next;
10225 }
10226 found
10227 };
10228 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
10241 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
10242 return false;
10243 };
10244 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
10245 let reparsed_ancestry = ParentIndex::new(tree.root_node());
10247 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
10248 tree.root_node(),
10249 template_node,
10250 source,
10251 &reparsed_ancestry,
10252 ) else {
10253 return false;
10254 };
10255 let body = reparsed_class.body;
10256 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
10257 });
10258 let (class_close_start, class_close_end, class_close_line) =
10259 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
10260 (class_close_start, class_close_end, class_close_line)
10261 } else {
10262 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
10269 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
10270 let reparsed_ancestry = ParentIndex::new(tree.root_node());
10272 let reparsed_class = cpp_sentinel_reparsed_class(
10273 tree.root_node(),
10274 template_node,
10275 source,
10276 &reparsed_ancestry,
10277 )?;
10278 let body = reparsed_class.body;
10279 let class_close_end = body.end_byte();
10280 let class_close_start = class_close_end.checked_sub(1)?;
10281 let class_close_line = body.end_position().row + 1;
10282 (class_close_start, class_close_end, class_close_line)
10283 };
10284 if class_close_start <= class_start {
10285 return None;
10286 }
10287
10288 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
10292 let class_root = tree.root_node();
10293 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
10294 let reparsed_ancestry = ParentIndex::new(class_root);
10296 let reparsed_class =
10297 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
10298 let body = reparsed_class.body;
10299 if body.start_byte() != body_open_start {
10303 return None;
10304 }
10305 let body_start = body.start_byte().checked_add(1)?;
10306 (body_start < class_close_start).then_some((
10307 reparse_start,
10308 class_start,
10309 body_start,
10310 class_close_start,
10311 class_close_end,
10312 class_close_line,
10313 ))
10314}
10315
10316fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
10321 let mut stack = vec![node];
10322 while let Some(current) = stack.pop() {
10323 if current.start_byte() == class_start
10324 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
10325 {
10326 let mut sibling = current.next_sibling();
10327 while let Some(candidate) = sibling {
10328 if candidate.kind() == "{" {
10329 return Some(candidate.start_byte());
10330 }
10331 sibling = candidate.next_sibling();
10332 }
10333 }
10334 let mut cursor = current.walk();
10335 stack.extend(current.children(&mut cursor));
10336 }
10337 None
10338}
10339
10340fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
10351 node.next_named_sibling()
10352 .filter(|sibling| sibling.kind() == "compound_statement")
10353}
10354
10355fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
10356 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
10357 let mut end = if class_start.is_some() {
10358 cpp_macro_prefixed_class_end(source, start)?
10359 } else {
10360 node.end_byte()
10361 };
10362 if class_start.is_none()
10363 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
10364 {
10365 end = end.max(namespace_end);
10366 }
10367 let mut sibling = node.next_named_sibling();
10368 while let Some(current) = sibling {
10369 if !cpp_is_stray_semicolon(current, source) {
10370 break;
10371 }
10372 end = current.end_byte();
10373 sibling = current.next_named_sibling();
10374 }
10375 (start < end).then_some((start, end))
10376}
10377
10378fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
10389 let mut sibling = node.next_sibling();
10390 let keyword = loop {
10391 let candidate = sibling?;
10392 sibling = candidate.next_sibling();
10393 if candidate.kind() != "comment" {
10394 break candidate;
10395 }
10396 };
10397 if keyword.kind() != "namespace" {
10398 return None;
10399 }
10400 let name = loop {
10401 let candidate = sibling?;
10402 sibling = candidate.next_sibling();
10403 if candidate.kind() != "comment" {
10404 break candidate;
10405 }
10406 };
10407 if cpp_namespace_name_components(name, source).is_empty() {
10408 return None;
10409 }
10410 let open = loop {
10411 let candidate = sibling?;
10412 sibling = candidate.next_sibling();
10413 if candidate.kind() != "comment" {
10414 break candidate;
10415 }
10416 };
10417 if open.kind() != "{" {
10418 return None;
10419 }
10420
10421 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
10422 let root = tree.root_node();
10423 let mut cursor = root.walk();
10424 let namespace = root
10425 .named_children(&mut cursor)
10426 .find(|candidate| candidate.kind() != "comment")?;
10427 (namespace.kind() == "namespace_definition"
10428 && namespace.start_byte() == keyword.start_byte()
10429 && namespace.child_by_field_name("body").is_some())
10430 .then_some(namespace.end_byte())
10431}
10432
10433fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
10439 let tree = cpp_reparse_region_items(source, start, source.len())?;
10440 let root = tree.root_node();
10441 let mut cursor = root.walk();
10442 for item in root.named_children(&mut cursor) {
10443 if item.end_byte() <= start || item.kind() == "comment" {
10444 continue;
10445 }
10446 let mut stack = vec![item];
10447 while let Some(current) = stack.pop() {
10448 if matches!(
10449 current.kind(),
10450 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10451 ) && cpp_body_node(current).is_some()
10452 {
10453 return Some(current.end_byte());
10454 }
10455 let mut cursor = current.walk();
10456 stack.extend(current.named_children(&mut cursor));
10457 }
10458 return None;
10462 }
10463 None
10464}
10465
10466fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
10469 node.kind() == "expression_statement"
10470 && node.named_child_count() == 0
10471 && node_text(node, source).trim() == ";"
10472}
10473
10474fn recovered_macro_qualified_field_declarators<'tree>(
10483 node: Node<'tree>,
10484 source: &str,
10485) -> Option<Vec<Node<'tree>>> {
10486 if node.kind() != "field_declaration" {
10487 return None;
10488 }
10489 let macro_type = node.child_by_field_name("type")?;
10490 if macro_type.kind() != "type_identifier"
10491 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10492 {
10493 return None;
10494 }
10495 let pseudo_declarator = node.child_by_field_name("declarator")?;
10496 if pseudo_declarator.kind() != "field_identifier" {
10497 return None;
10498 }
10499 let mut cursor = node.walk();
10500 let clause = node
10501 .named_children(&mut cursor)
10502 .find(|child| child.kind() == "bitfield_clause")?;
10503 if !(0..clause.named_child_count()).any(|index| {
10504 clause
10505 .named_child(index)
10506 .is_some_and(|child| child.kind() == "ERROR")
10507 }) {
10508 return None;
10509 }
10510 let mut recovered = Vec::new();
10511 let mut stack = vec![clause];
10512 while let Some(current) = stack.pop() {
10513 if current.kind() == "assignment_expression"
10514 && let Some(left) = current.child_by_field_name("left")
10515 && extract_variable_name(left, source).is_some()
10516 {
10517 recovered.push(left);
10518 break;
10519 }
10520 let mut cursor = current.walk();
10521 stack.extend(current.named_children(&mut cursor));
10522 }
10523 if recovered.is_empty() {
10524 return None;
10525 }
10526 let mut cursor = node.walk();
10527 recovered.extend(
10528 node.children_by_field_name("declarator", &mut cursor)
10529 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
10530 );
10531 Some(recovered)
10532}
10533
10534fn recovered_macro_qualified_constructor_call<'tree>(
10540 node: Node<'tree>,
10541 class_name: &str,
10542 source: &str,
10543) -> Option<Node<'tree>> {
10544 if node.kind() != "field_declaration" {
10545 return None;
10546 }
10547 let macro_type = node.child_by_field_name("type")?;
10548 if macro_type.kind() != "type_identifier"
10549 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10550 {
10551 return None;
10552 }
10553 let mut cursor = node.walk();
10554 let bitfield = node
10555 .named_children(&mut cursor)
10556 .find(|child| child.kind() == "bitfield_clause")?;
10557 let error = bitfield
10558 .named_child(0)
10559 .filter(|child| child.kind() == "ERROR")?;
10560 let mut stack = vec![error];
10561 while let Some(current) = stack.pop() {
10562 if current.kind() == "call_expression"
10563 && current
10564 .child_by_field_name("function")
10565 .is_some_and(|function| node_text(function, source) == class_name)
10566 && current
10567 .child_by_field_name("arguments")
10568 .is_some_and(|arguments| arguments.kind() == "argument_list")
10569 {
10570 return Some(current);
10571 }
10572 let mut cursor = current.walk();
10573 stack.extend(current.named_children(&mut cursor));
10574 }
10575 None
10576}
10577
10578fn recovered_macro_qualified_function_call<'tree>(
10586 node: Node<'tree>,
10587 source: &str,
10588) -> Option<Node<'tree>> {
10589 if node.kind() != "field_declaration" {
10590 return None;
10591 }
10592 let macro_type = node.child_by_field_name("type")?;
10593 if macro_type.kind() != "type_identifier"
10594 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10595 {
10596 return None;
10597 }
10598 let declarator = node.child_by_field_name("declarator")?;
10599 if declarator.kind() != "field_identifier" {
10600 return None;
10601 }
10602 let mut cursor = node.walk();
10603 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10604 if !named.iter().any(|child| {
10605 child.kind() == "storage_class_specifier"
10606 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
10607 }) {
10608 return None;
10609 }
10610 let bitfield = named
10611 .iter()
10612 .find(|child| child.kind() == "bitfield_clause")?;
10613 let mut bitfield_cursor = bitfield.walk();
10614 let payload = bitfield
10615 .named_children(&mut bitfield_cursor)
10616 .collect::<Vec<_>>();
10617 let [displaced_error, call] = payload.as_slice() else {
10618 return None;
10619 };
10620 if displaced_error.kind() != "ERROR"
10621 || displaced_error.named_child_count() != 1
10622 || displaced_error
10623 .named_child(0)
10624 .is_none_or(|child| child.kind() != "identifier")
10625 || call.kind() != "call_expression"
10626 || call
10627 .child_by_field_name("function")
10628 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
10629 || call
10630 .child_by_field_name("arguments")
10631 .is_none_or(|arguments| arguments.kind() != "argument_list")
10632 {
10633 return None;
10634 }
10635 Some(*call)
10636}
10637
10638fn recovered_macro_qualified_function_parameters(
10639 arguments: Node<'_>,
10640 source: &str,
10641) -> Option<(String, Vec<String>)> {
10642 if arguments.kind() != "argument_list" {
10643 return None;
10644 }
10645 let mut cursor = arguments.walk();
10646 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
10647 if named.is_empty() {
10648 return Some(("()".to_string(), Vec::new()));
10649 }
10650 let mut types = Vec::new();
10651 let mut labels = Vec::new();
10652 let mut index = 0;
10653 while index < named.len() {
10654 let parameter_type = named[index];
10655 let parameter_name = named.get(index + 1).copied()?;
10656 if !matches!(
10657 parameter_type.kind(),
10658 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
10659 ) || parameter_name.kind() != "ERROR"
10660 || parameter_name.named_child_count() != 1
10661 || parameter_name
10662 .named_child(0)
10663 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
10664 {
10665 return None;
10666 }
10667 let parameter_name = parameter_name.named_child(0)?;
10668 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
10669 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
10670 index += 2;
10671 }
10672 Some((format!("({})", types.join(", ")), labels))
10673}
10674
10675pub fn recovered_macro_return_type_node<'tree>(
10687 node: Node<'tree>,
10688 source: &str,
10689) -> Option<Node<'tree>> {
10690 if node.kind() != "field_declaration" {
10691 return None;
10692 }
10693 let macro_type = node.child_by_field_name("type")?;
10694 if macro_type.kind() != "type_identifier"
10695 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10696 {
10697 return None;
10698 }
10699 let declarator = node.child_by_field_name("declarator")?;
10700 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
10701 return None;
10702 }
10703 let mut has_missing_semicolon = false;
10704 let mut has_real_semicolon = false;
10705 for index in 0..node.child_count() {
10706 let Some(child) = node.child(index) else {
10707 continue;
10708 };
10709 if child.kind() != ";" {
10710 continue;
10711 }
10712 if child.is_missing() {
10713 has_missing_semicolon = true;
10714 } else {
10715 has_real_semicolon = true;
10716 }
10717 }
10718 if !has_missing_semicolon || has_real_semicolon {
10719 return None;
10720 }
10721 let mut next = node.next_named_sibling();
10722 while next.is_some_and(|sibling| sibling.kind() == "comment") {
10723 next = next.and_then(|sibling| sibling.next_named_sibling());
10724 }
10725 let next = next?;
10726 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
10727 return None;
10728 }
10729 let function_declarator = next.child_by_field_name("declarator")?;
10730 extract_function_declarator(function_declarator).map(|_| declarator)
10731}
10732
10733pub(crate) fn cpp_active_template_type_parameter<'tree>(
10740 node: Node<'tree>,
10741 name: &str,
10742 source: &str,
10743 ancestry: &ParentIndex<'tree>,
10744) -> bool {
10745 let mut ancestor = ancestry.parent(node);
10746 while let Some(current) = ancestor {
10747 if current.kind() == "template_declaration"
10748 && let Some(parameters) = current.child_by_field_name("parameters")
10749 {
10750 let mut cursor = parameters.walk();
10751 if parameters.named_children(&mut cursor).any(|parameter| {
10752 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
10753 && cpp_template_parameter_name(parameter, source)
10754 .is_some_and(|parameter_name| parameter_name == name)
10755 }) {
10756 return true;
10757 }
10758 }
10759 ancestor = ancestry.parent(current);
10760 }
10761 false
10762}
10763
10764fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
10770 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
10771}
10772
10773fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
10774 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
10775 return None;
10776 }
10777 let semicolon = node.next_sibling()?;
10778 if semicolon.kind() != ";" || semicolon.is_missing() {
10779 return None;
10780 }
10781 let row = node.start_position().row;
10782 let mut start = node.start_byte();
10783 let mut sibling = node.prev_sibling();
10784 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
10785 if previous.kind() == ";" {
10786 break;
10787 }
10788 start = previous.start_byte();
10789 sibling = previous.prev_sibling();
10790 }
10791 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
10792}
10793
10794fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
10795 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
10796 return false;
10797 }
10798 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
10799 return false;
10800 }
10801 let Some(declarator) = (if node.kind() == "function_definition" {
10802 node.child_by_field_name("declarator")
10803 .and_then(extract_function_declarator)
10804 } else {
10805 node.named_child(0)
10806 .filter(|child| child.kind() == "function_declarator")
10807 }) else {
10808 return false;
10809 };
10810 let Some(name) = cpp_function_declarator_name_node(declarator) else {
10811 return false;
10812 };
10813 declarator.start_byte() == node.start_byte()
10814 && name.kind() == "identifier"
10815 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
10816}
10817
10818fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
10823 let bytes = source.as_bytes();
10824 let prefix = bytes.get(..start)?;
10825 let interior = bytes.get(start..end)?;
10826 let mut padded = Vec::with_capacity(end);
10827 padded.extend(
10828 prefix
10829 .iter()
10830 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
10831 );
10832 padded.extend_from_slice(interior);
10833 let padded = String::from_utf8(padded).ok()?;
10834 let mut parser = Parser::new();
10835 parser
10836 .set_language(&tree_sitter_cpp::LANGUAGE.into())
10837 .ok()?;
10838 parser.parse(&padded, None)
10839}
10840
10841fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
10862 let mut cursor = root.walk();
10863 let mut saw_item = false;
10864 for child in root.named_children(&mut cursor) {
10865 match child.kind() {
10866 "comment" => {}
10867 "function_definition" => {
10868 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
10869 return false;
10870 }
10871 saw_item = true;
10872 }
10873 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
10874 _ => return false,
10875 }
10876 }
10877 saw_item
10878}
10879
10880fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
10890 if node.kind() != "ERROR" {
10891 return false;
10892 }
10893 let mut stack = Vec::new();
10894 let mut saw_function_declarator = false;
10895 let mut cursor = node.walk();
10896 for child in node.named_children(&mut cursor) {
10897 stack.push(child);
10898 }
10899 while let Some(current) = stack.pop() {
10900 match current.kind() {
10901 "ERROR" => {
10905 let mut cursor = current.walk();
10906 stack.extend(current.named_children(&mut cursor));
10907 }
10908 "function_declarator" => saw_function_declarator = true,
10909 _ => return false,
10910 }
10911 }
10912 saw_function_declarator
10913}
10914
10915fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
10916 if node.kind() != "ERROR" {
10917 return false;
10918 }
10919 let mut cursor = node.walk();
10920 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10921 let [explicit, constructor_error, destructor] = named.as_slice() else {
10922 return false;
10923 };
10924 let Some(constructor) = constructor_error.named_child(0) else {
10925 return false;
10926 };
10927 let Some(constructor_name) =
10928 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
10929 else {
10930 return false;
10931 };
10932 let Some(destructor_name) =
10933 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
10934 else {
10935 return false;
10936 };
10937 let Some(destroyed_type) = destructor_name.named_child(0) else {
10938 return false;
10939 };
10940 explicit.kind() == "explicit_function_specifier"
10941 && constructor_error.kind() == "ERROR"
10942 && constructor_error.named_child_count() == 1
10943 && constructor.kind() == "function_declarator"
10944 && constructor_name.kind() == "identifier"
10945 && destructor.kind() == "function_declarator"
10946 && destructor_name.kind() == "destructor_name"
10947 && destroyed_type.kind() == "identifier"
10948 && node_text(constructor_name, source) == node_text(destroyed_type, source)
10949}
10950
10951fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
10952 if node.kind() != "compound_statement" {
10953 return false;
10954 }
10955 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
10956 return false;
10957 };
10958 if prefix.kind() == "labeled_statement"
10959 && prefix.named_child(0).is_some_and(|label| {
10960 matches!(
10961 node_text(label, source).trim(),
10962 "public" | "private" | "protected"
10963 )
10964 })
10965 {
10966 return prefix.named_children(&mut prefix.walk()).any(|child| {
10967 child.kind() == "declaration"
10968 && child.has_error()
10969 && child
10970 .named_children(&mut child.walk())
10971 .any(cpp_reparsed_member_error_is_indexable)
10972 });
10973 }
10974 prefix.kind() == "declaration"
10979 && prefix.has_error()
10980 && prefix
10981 .named_children(&mut prefix.walk())
10982 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
10983}
10984
10985fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
10986 if !cpp_reparsed_member_error_is_indexable(node) {
10987 return false;
10988 }
10989 let Some(preproc) = node.next_named_sibling() else {
10990 return false;
10991 };
10992 preproc.kind() == "preproc_if"
10993 && preproc.has_error()
10994 && preproc
10995 .named_children(&mut preproc.walk())
10996 .any(|child| child.kind() == "expression_statement" && child.has_error())
10997 && preproc
10998 .next_named_sibling()
10999 .is_some_and(|body| body.kind() == "compound_statement")
11000}
11001
11002fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
11007 if node.kind() != "function_definition" {
11008 return None;
11009 }
11010 let body = node.child_by_field_name("body")?;
11011 if body.kind() != "compound_statement" {
11012 return None;
11013 }
11014 let open = body.child(0)?;
11015 let close = body.child(body.child_count().checked_sub(1)?)?;
11016 if open.kind() != "{"
11017 || open.is_missing()
11018 || close.kind() != "}"
11019 || close.is_missing()
11020 || close.end_byte() != body.end_byte()
11021 || body.end_byte() != node.end_byte()
11022 {
11023 return None;
11024 }
11025 Some(body)
11026}
11027
11028fn cpp_reparsed_member_function_errors_are_in_body(
11029 node: Node<'_>,
11030 body: Node<'_>,
11031 source: &str,
11032) -> bool {
11033 let mut cursor = node.walk();
11034 node.children(&mut cursor).all(|child| {
11035 same_node(child, body)
11036 || cpp_reparsed_member_attribute_error(child, source)
11037 || cpp_reparsed_member_signature_identifier_errors(child)
11038 || (!child.has_error() && !child.is_error() && !child.is_missing())
11039 })
11040}
11041
11042fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
11050 if !node.has_error() && !node.is_error() && !node.is_missing() {
11051 return false;
11052 }
11053 let mut stack = vec![node];
11054 let mut saw_error = false;
11055 while let Some(current) = stack.pop() {
11056 if current.is_missing() {
11057 return false;
11058 }
11059 if current.kind() == "ERROR" {
11060 saw_error = true;
11061 let mut cursor = current.walk();
11062 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
11063 if children
11064 .iter()
11065 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
11066 {
11067 return false;
11068 }
11069 stack.extend(children);
11070 continue;
11071 }
11072 let mut cursor = current.walk();
11073 stack.extend(current.children(&mut cursor));
11074 }
11075 saw_error
11076}
11077
11078fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
11079 node.kind() == "ERROR"
11080 && node.named_child_count() == 1
11081 && node.named_child(0).is_some_and(|attribute| {
11082 attribute.kind() == "identifier"
11083 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
11084 })
11085}
11086
11087fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
11093 let Some(body) = cpp_reparsed_member_function_body(node) else {
11094 return false;
11095 };
11096 let mut cursor = node.walk();
11097 let named = node
11098 .named_children(&mut cursor)
11099 .filter(|child| child.kind() != "comment")
11100 .collect::<Vec<_>>();
11101 let [type_node, error, attribute, body_node] = named.as_slice() else {
11102 return false;
11103 };
11104 if !same_node(*body_node, body)
11105 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
11106 || attribute.kind() != "identifier"
11107 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
11108 || error.kind() != "ERROR"
11109 || error.named_child_count() != 1
11110 {
11111 return false;
11112 }
11113 error
11114 .named_child(0)
11115 .is_some_and(cpp_reparsed_attribute_callable_declarator)
11116}
11117
11118fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
11119 cpp_structured_type_path(node, source).is_some()
11120 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
11121}
11122
11123fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11124 let Some(body) = cpp_reparsed_member_function_body(node) else {
11125 return false;
11126 };
11127 let mut cursor = node.walk();
11128 let named = node
11129 .named_children(&mut cursor)
11130 .filter(|child| child.kind() != "comment")
11131 .collect::<Vec<_>>();
11132 let [friend, return_error, declarator, body_node] = named.as_slice() else {
11133 return false;
11134 };
11135 let Some(return_type) = return_error.named_child(0) else {
11136 return false;
11137 };
11138 same_node(*body_node, body)
11139 && friend.kind() == "type_identifier"
11140 && node_text(*friend, source) == "friend"
11141 && return_error.kind() == "ERROR"
11142 && return_error.named_child_count() == 1
11143 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
11144 && extract_function_declarator(*declarator)
11145 .and_then(cpp_function_declarator_name_node)
11146 .is_some()
11147}
11148
11149fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11150 let Some(body) = cpp_reparsed_member_function_body(node) else {
11151 return false;
11152 };
11153 let mut cursor = node.walk();
11154 let named = node
11155 .named_children(&mut cursor)
11156 .filter(|child| child.kind() != "comment")
11157 .collect::<Vec<_>>();
11158 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
11159 return false;
11160 };
11161 let Some(return_type) = return_error.named_child(0) else {
11162 return false;
11163 };
11164 same_node(*body_node, body)
11165 && prefix
11166 .iter()
11167 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
11168 && attribute.kind() == "type_identifier"
11169 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
11170 && return_error.kind() == "ERROR"
11171 && return_error.named_child_count() == 1
11172 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
11173 && extract_function_declarator(*declarator)
11174 .and_then(cpp_function_declarator_name_node)
11175 .is_some()
11176}
11177
11178fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11184 let Some(body) = cpp_reparsed_member_function_body(node) else {
11185 return false;
11186 };
11187 let mut cursor = node.walk();
11188 let named = node
11189 .named_children(&mut cursor)
11190 .filter(|child| child.kind() != "comment")
11191 .collect::<Vec<_>>();
11192 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
11193 return false;
11194 };
11195 let Some(template_name) = template_type.child_by_field_name("name") else {
11196 return false;
11197 };
11198 let Some(arguments) = template_type.child_by_field_name("arguments") else {
11199 return false;
11200 };
11201 let Some(return_type) = return_error.named_child(0) else {
11202 return false;
11203 };
11204 let mut cursor = template_type.walk();
11205 let template_errors = template_type
11206 .named_children(&mut cursor)
11207 .filter(|child| child.kind() == "ERROR")
11208 .collect::<Vec<_>>();
11209 let [comment_error] = template_errors.as_slice() else {
11210 return false;
11211 };
11212 let mut cursor = comment_error.walk();
11213 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
11214 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
11215 return false;
11216 };
11217 same_node(*body_node, body)
11218 && template_type.kind() == "template_type"
11219 && template_name.kind() == "type_identifier"
11220 && matches!(
11221 node_text(template_name, source).trim(),
11222 "public" | "private" | "protected"
11223 )
11224 && arguments.kind() == "template_argument_list"
11225 && arguments.named_child_count() > 0
11226 && !arguments.has_error()
11227 && !colon.is_named()
11228 && colon.kind() == ":"
11229 && comments.iter().all(|child| child.kind() == "comment")
11230 && !template_keyword.is_named()
11231 && template_keyword.kind() == "template"
11232 && return_error.kind() == "ERROR"
11233 && return_error.named_child_count() == 1
11234 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
11235 && extract_function_declarator(*declarator)
11236 .and_then(cpp_function_declarator_name_node)
11237 .is_some()
11238}
11239
11240fn cpp_reparsed_preprocessor_constructor<'tree>(
11246 node: Node<'tree>,
11247 class_name: &str,
11248 source: &str,
11249) -> Option<Node<'tree>> {
11250 if node.kind() != "labeled_statement" {
11251 return None;
11252 }
11253 let mut cursor = node.walk();
11254 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11255 let [label, directive_error, declaration] = named.as_slice() else {
11256 return None;
11257 };
11258 if label.kind() != "statement_identifier"
11259 || !matches!(
11260 node_text(*label, source),
11261 "public" | "private" | "protected"
11262 )
11263 || directive_error.kind() != "ERROR"
11264 || directive_error.child_count() != 1
11265 || directive_error
11266 .child(0)
11267 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
11268 || declaration.kind() != "declaration"
11269 || declaration.named_child_count() != 2
11270 {
11271 return None;
11272 }
11273 let apparent_type = declaration.child_by_field_name("type")?;
11274 if apparent_type.kind() != "type_identifier"
11275 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
11276 {
11277 return None;
11278 }
11279 let declarator = declaration.child_by_field_name("declarator")?;
11280 let function = extract_function_declarator(declarator)?;
11281 let name = cpp_function_declarator_name_node(function)?;
11282 (node_text(name, source) == class_name).then_some(*declaration)
11283}
11284
11285fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
11286 if extract_function_declarator(node)
11287 .and_then(cpp_function_declarator_name_node)
11288 .is_some()
11289 {
11290 return true;
11291 }
11292 node.kind() == "init_declarator"
11293 && node
11294 .child_by_field_name("declarator")
11295 .is_some_and(|declarator| declarator.kind() == "identifier")
11296 && node
11297 .child_by_field_name("value")
11298 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
11299}
11300
11301fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
11306 if node.kind() != "ERROR" || node.named_child_count() != 3 {
11307 return false;
11308 }
11309 let mut cursor = node.walk();
11310 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11311 let [type_node, function_declarator, attribute] = named.as_slice() else {
11312 return false;
11313 };
11314 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
11315 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
11316 || attribute.kind() != "identifier"
11317 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
11318 {
11319 return false;
11320 }
11321 let Some(preproc) =
11322 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
11323 else {
11324 return false;
11325 };
11326 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
11327 .filter(|sibling| sibling.kind() == "compound_statement")
11328 else {
11329 return false;
11330 };
11331 let Some(open) = body.child(0) else {
11332 return false;
11333 };
11334 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
11335 return false;
11336 };
11337 let Some(condition) = preproc.child_by_field_name("condition") else {
11338 return false;
11339 };
11340 let mut cursor = preproc.walk();
11341 let payload = preproc
11342 .named_children(&mut cursor)
11343 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
11344 .collect::<Vec<_>>();
11345 let [requires_statement] = payload.as_slice() else {
11346 return false;
11347 };
11348 let requires_clause = requires_statement.named_child(0);
11349
11350 open.kind() == "{"
11351 && !open.is_missing()
11352 && close.kind() == "}"
11353 && !close.is_missing()
11354 && close.end_byte() == body.end_byte()
11355 && requires_statement.kind() == "expression_statement"
11356 && requires_statement.named_child_count() == 1
11357 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
11358}
11359
11360fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
11361 let mut sibling = node.next_named_sibling();
11362 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
11363 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
11364 }
11365 sibling
11366}
11367
11368fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
11369 let mut sibling = node.prev_named_sibling();
11370 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
11371 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
11372 }
11373 sibling
11374}
11375
11376fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
11377 let Some(preproc) =
11378 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
11379 else {
11380 return false;
11381 };
11382 let Some(error) =
11383 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
11384 else {
11385 return false;
11386 };
11387 cpp_reparsed_attribute_requires_error(error, source)
11388}
11389
11390fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
11391 node: Node<'tree>,
11392 source: &str,
11393) -> Option<Node<'tree>> {
11394 if node.kind() != "ERROR" {
11395 return None;
11396 }
11397 let mut cursor = node.walk();
11398 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11399 let [parameter, macro_name, message] = named.as_slice() else {
11400 return None;
11401 };
11402 let parameter_name = parameter.named_child(0)?;
11403 (parameter.kind() == "type_parameter_declaration"
11404 && parameter_name.kind() == "type_identifier"
11405 && macro_name.kind() == "type_identifier"
11406 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
11407 && message.kind() == "string_literal")
11408 .then_some(parameter_name)
11409}
11410
11411fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
11416 node: Node<'tree>,
11417 source: &str,
11418) -> Option<Node<'tree>> {
11419 if node.kind() != "ERROR" {
11420 return None;
11421 }
11422 let mut cursor = node.walk();
11423 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11424 let [parameter, macro_name, message, constraint] = named.as_slice() else {
11425 return None;
11426 };
11427 let parameter_name = parameter.named_child(0)?;
11428 let constraint_scope = constraint.child_by_field_name("scope")?;
11429 let constraint_template = constraint.child_by_field_name("name")?;
11430 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
11431 let mut argument_cursor = constraint_arguments.walk();
11432 let constraint_types = constraint_arguments
11433 .named_children(&mut argument_cursor)
11434 .collect::<Vec<_>>();
11435 if parameter.kind() != "type_parameter_declaration"
11436 || parameter_name.kind() != "type_identifier"
11437 || macro_name.kind() != "type_identifier"
11438 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
11439 || message.kind() != "string_literal"
11440 || constraint.kind() != "qualified_identifier"
11441 || constraint_scope.kind() != "namespace_identifier"
11442 || !matches!(
11443 constraint_template.kind(),
11444 "template_function" | "template_type"
11445 )
11446 || !matches!(constraint_types.as_slice(), [left, right]
11447 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
11448 || constraint_arguments.has_error()
11449 {
11450 return None;
11451 }
11452 let parameter_text = node_text(parameter_name, source);
11453 let mut stack = constraint_types;
11454 while let Some(current) = stack.pop() {
11455 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
11456 return Some(parameter_name);
11457 }
11458 let mut cursor = current.walk();
11459 stack.extend(current.named_children(&mut cursor));
11460 }
11461 None
11462}
11463
11464fn cpp_reparsed_template_macro_companion_is_indexable(
11465 node: Node<'_>,
11466 parameter_name: Node<'_>,
11467 source: &str,
11468) -> bool {
11469 let Some(body) = cpp_reparsed_member_function_body(node) else {
11470 return false;
11471 };
11472 let mut cursor = node.walk();
11473 let named = node
11474 .named_children(&mut cursor)
11475 .filter(|child| child.kind() != "comment")
11476 .collect::<Vec<_>>();
11477 let [
11478 constraint,
11479 close_error,
11480 storage,
11481 return_error,
11482 declarator,
11483 body_node,
11484 ] = named.as_slice()
11485 else {
11486 return false;
11487 };
11488 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
11489 return false;
11490 };
11491 let Some(constraint_template) = constraint.child_by_field_name("name") else {
11492 return false;
11493 };
11494 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
11495 return false;
11496 };
11497 let Some(return_type) = return_error.named_child(0) else {
11498 return false;
11499 };
11500 let mut cursor = constraint_arguments.walk();
11501 let constraint_types = constraint_arguments
11502 .named_children(&mut cursor)
11503 .collect::<Vec<_>>();
11504 same_node(*body_node, body)
11505 && constraint.kind() == "qualified_identifier"
11506 && constraint_scope.kind() == "namespace_identifier"
11507 && constraint_template.kind() == "template_type"
11508 && matches!(constraint_types.as_slice(), [left, right]
11509 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
11510 && !constraint_arguments.has_error()
11511 && close_error.kind() == "ERROR"
11512 && close_error.named_child_count() == 0
11513 && storage.kind() == "storage_class_specifier"
11514 && return_error.kind() == "ERROR"
11515 && return_error.named_child_count() == 1
11516 && return_type.kind() == "identifier"
11517 && node_text(return_type, source) == node_text(parameter_name, source)
11518 && extract_function_declarator(*declarator)
11519 .and_then(cpp_function_declarator_name_node)
11520 .is_some()
11521}
11522
11523fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
11524 node: Node<'tree>,
11525 parameter_name: Node<'_>,
11526 source: &str,
11527) -> Option<Node<'tree>> {
11528 let body = cpp_reparsed_member_function_body(node)?;
11529 let constraint = node.child_by_field_name("type")?;
11530 let constraint_template = constraint.child_by_field_name("name")?;
11531 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
11532 let mut argument_cursor = constraint_arguments.walk();
11533 let constraint_types = constraint_arguments
11534 .named_children(&mut argument_cursor)
11535 .collect::<Vec<_>>();
11536 if constraint.kind() != "qualified_identifier"
11537 || constraint_template.kind() != "template_type"
11538 || !matches!(constraint_types.as_slice(), [left, right]
11539 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
11540 || constraint_arguments.has_error()
11541 || node
11542 .child_by_field_name("body")
11543 .is_none_or(|candidate| !same_node(candidate, body))
11544 {
11545 return None;
11546 }
11547
11548 let mut cursor = node.walk();
11549 let recovery_errors = node
11550 .named_children(&mut cursor)
11551 .filter(|child| child.kind() == "ERROR")
11552 .collect::<Vec<_>>();
11553 if !recovery_errors
11554 .iter()
11555 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
11556 || !recovery_errors.iter().all(|error| {
11557 error.named_child_count() == 0
11558 || cpp_reparsed_constraint_macro_error(*error, source)
11559 || (error.named_child_count() == 1
11560 && error
11561 .named_child(0)
11562 .is_some_and(|child| child.kind() == "function_declarator"))
11563 })
11564 {
11565 return None;
11566 }
11567
11568 let parameter_text = node_text(parameter_name, source);
11569 let mut declarators = node
11570 .child_by_field_name("declarator")
11571 .and_then(extract_function_declarator)
11572 .into_iter()
11573 .collect::<Vec<_>>();
11574 for error in recovery_errors {
11575 let mut stack = vec![error];
11576 while let Some(current) = stack.pop() {
11577 if current.kind() == "function_declarator" {
11578 declarators.push(current);
11579 }
11580 let mut cursor = current.walk();
11581 stack.extend(current.named_children(&mut cursor));
11582 }
11583 }
11584 declarators.into_iter().find(|declarator| {
11585 cpp_function_declarator_name_node(*declarator)
11586 .is_some_and(|name| name.kind() == "identifier")
11587 && declarator
11588 .child_by_field_name("parameters")
11589 .is_some_and(|parameters| {
11590 parameters
11591 .named_children(&mut parameters.walk())
11592 .filter_map(|parameter| parameter.child_by_field_name("type"))
11593 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
11594 })
11595 })
11596}
11597
11598fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
11599 node: Node<'_>,
11600 parameter_name: Node<'_>,
11601 source: &str,
11602) -> bool {
11603 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
11604}
11605
11606fn cpp_reparsed_template_macro_function_companion_is_indexable(
11607 node: Node<'_>,
11608 parameter_name: Node<'_>,
11609 source: &str,
11610) -> bool {
11611 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
11612 return false;
11613 }
11614 let Some(return_type) = node.child_by_field_name("type") else {
11615 return false;
11616 };
11617 let Some(function_declarator) = node
11618 .child_by_field_name("declarator")
11619 .and_then(extract_function_declarator)
11620 else {
11621 return false;
11622 };
11623 if cpp_function_declarator_name_node(function_declarator).is_none()
11624 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
11625 {
11626 return false;
11627 }
11628 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
11629 return false;
11630 };
11631 let parameter_text = node_text(parameter_name, source);
11632 parameters
11633 .named_children(&mut parameters.walk())
11634 .any(|parameter| {
11635 parameter
11636 .child_by_field_name("type")
11637 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
11638 })
11639}
11640
11641fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
11642 if node.kind() != "ERROR" {
11643 return false;
11644 }
11645 let mut stack = vec![node];
11646 while let Some(current) = stack.pop() {
11647 let macro_shape = match current.kind() {
11648 "call_expression" => current
11649 .child_by_field_name("function")
11650 .zip(current.child_by_field_name("arguments")),
11651 "init_declarator" => current
11652 .child_by_field_name("declarator")
11653 .zip(current.child_by_field_name("value")),
11654 _ => None,
11655 };
11656 if let Some((name, arguments)) = macro_shape
11657 && name.kind() == "identifier"
11658 && arguments.kind() == "argument_list"
11659 && arguments.named_child_count() >= 2
11660 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
11661 {
11662 return true;
11663 }
11664 let mut cursor = current.walk();
11665 stack.extend(current.named_children(&mut cursor));
11666 }
11667 false
11668}
11669
11670fn cpp_recovered_template_macro_constructor<'tree>(
11671 node: Node<'tree>,
11672 source: &str,
11673) -> Option<(Node<'tree>, Node<'tree>)> {
11674 let mut prefix = node.prev_named_sibling()?;
11675 while prefix.kind() == "comment" {
11676 prefix = prefix.prev_named_sibling()?;
11677 }
11678 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
11679 let parameter = parameter_name
11680 .parent()
11681 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
11682 let declarator =
11683 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
11684 Some((declarator, parameter))
11685}
11686
11687fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
11688 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
11689 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
11690 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
11691 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
11692 function,
11693 parameter_name,
11694 source,
11695 )
11696 });
11697 }
11698 let Some(parameter_name) =
11699 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
11700 else {
11701 return false;
11702 };
11703 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
11704 cpp_reparsed_template_macro_function_companion_is_indexable(
11705 function,
11706 parameter_name,
11707 source,
11708 )
11709 })
11710}
11711
11712fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11713 let function_name = node
11714 .child_by_field_name("declarator")
11715 .and_then(extract_function_declarator)
11716 .and_then(cpp_function_declarator_name_node);
11717 if let Some(body) = cpp_reparsed_member_function_body(node)
11718 && function_name.is_some()
11719 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
11720 {
11721 return true;
11722 }
11723 cpp_reparsed_attribute_member_function(node, source)
11724 || cpp_reparsed_friend_function_is_indexable(node, source)
11725 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
11726 || cpp_reparsed_access_template_function_is_indexable(node, source)
11727 || cpp_recovered_template_macro_constructor(node, source).is_some()
11728}
11729
11730fn cpp_reparsed_macro_attribute_member_sequence(
11737 children: &[Node<'_>],
11738 index: usize,
11739 source: &str,
11740) -> bool {
11741 let Some(prefix) = children.get(index).copied() else {
11742 return false;
11743 };
11744 let declaration = if prefix.kind() == "labeled_statement" {
11745 prefix
11746 .named_child(prefix.named_child_count().saturating_sub(1))
11747 .filter(|child| child.kind() == "declaration")
11748 } else {
11749 (prefix.kind() == "declaration").then_some(prefix)
11750 };
11751 let Some(declaration) = declaration else {
11752 return false;
11753 };
11754 if !declaration.has_error()
11755 || declaration
11756 .child_by_field_name("declarator")
11757 .and_then(extract_function_declarator)
11758 .and_then(cpp_function_declarator_name_node)
11759 .is_none()
11760 {
11761 return false;
11762 }
11763 let Some(attribute_statement) = children.get(index + 1).copied() else {
11764 return false;
11765 };
11766 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
11767 .then(|| attribute_statement.named_child(0))
11768 .flatten()
11769 .filter(|child| child.kind() == "call_expression")
11770 else {
11771 return false;
11772 };
11773 let Some(attribute_name) = attribute_call
11774 .child_by_field_name("function")
11775 .filter(|function| function.kind() == "identifier")
11776 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
11777 else {
11778 return false;
11779 };
11780 if !cpp_export_macro_token(&attribute_name) {
11781 return false;
11782 }
11783 let Some(body) = children.get(index + 2).copied() else {
11784 return false;
11785 };
11786 body.kind() == "compound_statement"
11787 && body.child(0).is_some_and(|open| open.kind() == "{")
11788 && body
11789 .child(body.child_count().saturating_sub(1))
11790 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
11791 && declaration.end_byte() <= attribute_statement.start_byte()
11792 && attribute_statement.end_byte() <= body.start_byte()
11793}
11794
11795fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
11796 let mut cursor = root.walk();
11797 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
11798 let mut saw_member = false;
11799 let mut index = 0;
11800 while index < children.len() {
11801 let child = children[index];
11802 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
11803 saw_member = true;
11804 index += 3;
11805 continue;
11806 }
11807 if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
11808 let Some(tree) = cpp_reparse_fragmented_class_body(
11809 source,
11810 fragmented.reparse_start,
11811 fragmented.reparse_end,
11812 ) else {
11813 return false;
11814 };
11815 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
11816 return false;
11817 }
11818 saw_member = true;
11819 index += 1;
11820 while index < children.len()
11821 && children[index].end_byte() <= fragmented.class_range.end_byte
11822 {
11823 index += 1;
11824 }
11825 continue;
11826 }
11827 match child.kind() {
11828 "comment" => {}
11829 "labeled_statement" => saw_member = true,
11830 "function_definition" => {
11831 if child.has_error()
11832 && !cpp_reparsed_member_function_is_indexable(child, source)
11833 && cpp_sentinel_macro_region(child, source).is_none()
11834 {
11835 return false;
11836 }
11837 saw_member = true;
11838 }
11839 "ERROR"
11840 if (cpp_reparsed_member_error_is_indexable(child)
11841 || cpp_reparsed_adjacent_copy_control_error(child, source))
11842 && (child
11843 .next_named_sibling()
11844 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
11845 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
11846 {
11847 saw_member = true;
11848 }
11849 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
11850 saw_member = true;
11851 }
11852 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
11853 saw_member = true;
11854 }
11855 "expression_statement"
11856 if cpp_is_stray_semicolon(child, source)
11857 && child.prev_named_sibling().is_some_and(|error| {
11858 cpp_reparsed_member_error_is_indexable(error)
11859 || cpp_reparsed_adjacent_copy_control_error(error, source)
11860 }) =>
11861 {
11862 saw_member = true;
11863 }
11864 "compound_statement"
11865 if cpp_reparsed_constructor_body_is_indexable(child, source)
11866 || cpp_reparsed_attribute_requires_body(child, source) =>
11867 {
11868 saw_member = true;
11869 }
11870 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
11871 _ => return false,
11872 }
11873 index += 1;
11874 }
11875 saw_member
11876}
11877
11878fn cpp_reparsed_synthetic_initializer_constructor_range(
11886 root: Node<'_>,
11887 class_name: &str,
11888 source: &str,
11889 constructor_end: usize,
11890) -> Option<std::ops::Range<usize>> {
11891 let mut stack = {
11892 let mut cursor = root.walk();
11893 root.named_children(&mut cursor).collect::<Vec<_>>()
11894 };
11895 while let Some(current) = stack.pop() {
11896 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
11897 current,
11898 class_name,
11899 source,
11900 constructor_end,
11901 ) {
11902 return Some(range);
11903 }
11904 if current.kind() == "ERROR" {
11905 let mut cursor = current.walk();
11906 stack.extend(current.named_children(&mut cursor));
11907 }
11908 }
11909 None
11910}
11911
11912fn cpp_reparsed_synthetic_initializer_constructor(
11913 node: Node<'_>,
11914 class_name: &str,
11915 source: &str,
11916 constructor_end: usize,
11917) -> Option<std::ops::Range<usize>> {
11918 if node.kind() != "labeled_statement" {
11919 return None;
11920 }
11921 let mut cursor = node.walk();
11922 let named = node
11923 .named_children(&mut cursor)
11924 .filter(|child| child.kind() != "comment")
11925 .collect::<Vec<_>>();
11926 let label = named.first()?;
11927 if label.kind() != "statement_identifier"
11928 || !matches!(
11929 node_text(*label, source).trim(),
11930 "public" | "private" | "protected"
11931 )
11932 {
11933 return None;
11934 }
11935 let call_error_index = named.iter().position(|child| {
11936 if child.kind() != "ERROR" {
11937 return false;
11938 }
11939 let mut stack = vec![*child];
11940 while let Some(current) = stack.pop() {
11941 if current.kind() == "call_expression"
11942 && current
11943 .child_by_field_name("function")
11944 .is_some_and(|function| {
11945 function.kind() == "identifier"
11946 && node_text(function, source).trim() == class_name
11947 })
11948 {
11949 return true;
11950 }
11951 let mut cursor = current.walk();
11952 stack.extend(current.named_children(&mut cursor));
11953 }
11954 false
11955 })?;
11956 let constructor_call = {
11957 let mut stack = vec![named[call_error_index]];
11958 let mut found = None;
11959 while let Some(current) = stack.pop() {
11960 if current.kind() == "call_expression"
11961 && current
11962 .child_by_field_name("function")
11963 .is_some_and(|function| {
11964 function.kind() == "identifier"
11965 && node_text(function, source).trim() == class_name
11966 })
11967 {
11968 found = Some(current);
11969 break;
11970 }
11971 let mut cursor = current.walk();
11972 stack.extend(current.named_children(&mut cursor));
11973 }
11974 found
11975 };
11976 let constructor_call = constructor_call?;
11977 named.iter().skip(call_error_index + 1).find(|child| {
11978 child.kind() == "declaration" && child.has_error() && {
11979 let mut cursor = child.walk();
11980 child.named_children(&mut cursor).any(|declarator| {
11981 declarator.kind() == "init_declarator"
11982 && declarator
11983 .child_by_field_name("declarator")
11984 .is_some_and(|declarator| declarator.kind() == "function_declarator")
11985 && declarator
11986 .child_by_field_name("value")
11987 .is_some_and(|value| value.kind() == "initializer_list")
11988 })
11989 }
11990 })?;
11991 Some(constructor_call.start_byte()..constructor_end)
11992}
11993
11994fn cpp_reparsed_exact_constructor_declarator<'tree>(
11995 root: Node<'tree>,
11996 start: usize,
11997 class_name: &str,
11998 source: &str,
11999) -> Option<Node<'tree>> {
12000 let mut candidate = None;
12001 let mut stack = vec![root];
12002 while let Some(current) = stack.pop() {
12003 if current.kind() == "function_declarator"
12004 && current.start_byte() == start
12005 && cpp_function_declarator_name_node(current)
12006 .is_some_and(|name| node_text(name, source).trim() == class_name)
12007 {
12008 if candidate.is_some() {
12009 return None;
12010 }
12011 candidate = Some(current);
12012 continue;
12013 }
12014 let mut cursor = current.walk();
12015 stack.extend(current.named_children(&mut cursor));
12016 }
12017 candidate
12018}
12019
12020fn cpp_is_indexable_item_kind(kind: &str) -> bool {
12021 matches!(
12022 kind,
12023 "namespace_definition"
12024 | "class_specifier"
12025 | "struct_specifier"
12026 | "union_specifier"
12027 | "enum_specifier"
12028 | "function_definition"
12029 | "template_declaration"
12030 | "declaration"
12031 | "field_declaration"
12032 | "alias_declaration"
12033 | "static_assert_declaration"
12034 | "type_definition"
12035 | "using_declaration"
12036 | "linkage_specification"
12037 | "preproc_def"
12038 | "preproc_function_def"
12039 | "preproc_include"
12040 | "preproc_if"
12041 | "preproc_ifdef"
12042 | "preproc_call"
12043 )
12044}
12045
12046#[cfg(test)]
12047mod tests {
12048 use super::*;
12049 use crate::adapter::parse_cpp_file;
12050 use brokk_bifrost_core::analyzer::parsed_file::{
12051 finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
12052 start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
12053 };
12054 use std::fmt::Write;
12055
12056 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
12057 let mut parser = tree_sitter::Parser::new();
12058 parser
12059 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12060 .unwrap();
12061 let tree = parser.parse(source, None).unwrap();
12062 let file = ProjectFile::new(std::env::temp_dir(), name);
12063 parse_cpp_file(&file, source, &tree)
12064 }
12065
12066 #[test]
12067 fn identifies_export_macro_class_base_displaced_into_declarator() {
12068 let source = r#"#define PROJECT_API_
12069namespace project {
12070namespace internal {
12071template <typename T>
12072class Base {};
12073}
12074template <typename T>
12075class Wrapper;
12076template <>
12077class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
12078}
12079"#;
12080 let mut parser = tree_sitter::Parser::new();
12081 parser
12082 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12083 .unwrap();
12084 let tree = parser.parse(source, None).unwrap();
12085 let start = source.find("internal::Base<int>").expect("base");
12086 let mut base = tree
12087 .root_node()
12088 .descendant_for_byte_range(start, start + 8)
12089 .expect("base syntax");
12090 while base.kind() != "qualified_identifier" {
12091 base = base.parent().expect("qualified base ancestor");
12092 }
12093 assert!(
12094 is_recovered_exported_class_base_type_node(base, source),
12095 "{}",
12096 tree.root_node().to_sexp()
12097 );
12098 }
12099
12100 #[test]
12101 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
12102 let source = r#"namespace control {
12103template <typename T>
12104class AnySpan;
12105template <typename T>
12106class ABSL_ATTRIBUTE_VIEW AnySpan {
12107 public:
12108 int begin() const;
12109};
12110}
12111
12112namespace absl {
12113ABSL_NAMESPACE_BEGIN
12114template <typename T>
12115class ABSL_ATTRIBUTE_VIEW Span {
12116 public:
12117 int begin() const;
12118 int back() const;
12119};
12120
12121int begin();
12122int back();
12123}
12124"#;
12125 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
12126 let declarations = parsed.declarations();
12127 assert!(
12128 declarations
12129 .iter()
12130 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
12131 );
12132 for method in ["begin", "back"] {
12133 assert!(declarations.iter().any(|unit| {
12134 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
12135 }));
12136 assert!(
12137 declarations.iter().any(|unit| {
12138 unit.is_function() && unit.fq_name() == format!("absl.{method}")
12139 })
12140 );
12141 }
12142 assert!(
12143 declarations
12144 .iter()
12145 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
12146 );
12147 assert!(
12148 declarations
12149 .iter()
12150 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
12151 );
12152 assert!(
12153 declarations
12154 .iter()
12155 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
12156 );
12157 }
12158
12159 #[test]
12160 fn explicit_global_member_definition_has_canonical_package_boundary() {
12161 let source = r#"
12162namespace arangodb::aql {
12163class ExecutionPlan {
12164 public:
12165 template<class... Args> Node* createNode(Args&&... args);
12166};
12167}
12168
12169template<class... Args>
12170Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
12171"#;
12172 let parsed = parse_cpp_declarations(source, "global-member.cpp");
12173
12174 assert!(parsed.declarations().iter().any(|unit| {
12175 unit.is_function()
12176 && unit.package_name() == "arangodb::aql"
12177 && unit.short_name() == "ExecutionPlan.createNode"
12178 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
12179 }));
12180 }
12181
12182 #[test]
12183 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
12184 let source = r#"
12185#ifndef TINYXML2_INCLUDED
12186#define TINYXML2_INCLUDED
12187namespace tinyxml2 {
12188class TINYXML2_LIB XMLUtil {
12189 public:
12190 static const char* SkipWhiteSpace(const char* p) {
12191 while (*p) {
12192 if (*p == ' ') {
12193 ++p;
12194 }
12195 }
12196 return p;
12197 }
12198 static bool StringEqual(const char* p, const char* q) {
12199 return p == q;
12200 }
12201 class TINYXML2_LIB Helper {
12202 public:
12203 void Touch();
12204 };
12205 static void ToStr(int value, char* buffer);
12206 private:
12207 static const char* writeBoolTrue;
12208};
12209
12210class TINYXML2_LIB XMLNode {
12211 public:
12212 virtual XMLNode* ShallowClone() const = 0;
12213 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
12214};
12215}
12216#endif
12217"#;
12218 let mut parser = tree_sitter::Parser::new();
12219 parser
12220 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12221 .unwrap();
12222 let tree = parser.parse(source, None).unwrap();
12223 let mut boundary_found = false;
12224 walk_named_tree_preorder(tree.root_node(), true, |node| {
12225 if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
12226 && name == "XMLUtil"
12227 {
12228 boundary_found = fragmented_export_sibling_class_boundary(node, source)
12229 .and_then(|boundary| {
12230 recover_exported_class_function_definition(boundary, source)
12231 })
12232 .is_some_and(|(_, name, _)| name == "XMLNode");
12233 }
12234 WalkControl::Continue
12235 });
12236 assert!(
12237 boundary_found,
12238 "fixture must exercise the recovered sibling boundary"
12239 );
12240
12241 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
12242 assert!(
12243 parsed
12244 .declarations()
12245 .iter()
12246 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
12247 "{:#?}",
12248 parsed.declarations()
12249 );
12250 assert!(
12251 parsed
12252 .declarations()
12253 .iter()
12254 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
12255 "{:#?}",
12256 parsed.declarations()
12257 );
12258 assert!(parsed.declarations().iter().any(|unit| {
12259 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
12260 }));
12261 assert!(
12262 parsed
12263 .declarations()
12264 .iter()
12265 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
12266 );
12267 assert!(
12268 parsed
12269 .declarations()
12270 .iter()
12271 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
12272 );
12273 }
12274
12275 #[test]
12276 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
12277 let parsed = parse_cpp_declarations(
12281 r#"
12282namespace cwg311 {
12283namespace X { namespace Y {} }
12284namespace ::cwg311::X {}
12285}
12286"#,
12287 "explicit-global-namespace.cpp",
12288 );
12289
12290 assert!(parsed.declarations().iter().any(|unit| {
12291 unit.kind() == CodeUnitType::Module
12292 && unit.short_name() == "cwg311::X"
12293 && unit.fq_name() == "cwg311::X"
12294 }));
12295 assert!(
12296 parsed
12297 .declarations()
12298 .iter()
12299 .all(|unit| !unit.short_name().contains("::::")),
12300 "recovered namespace names must not retain empty scope components: {:#?}",
12301 parsed.declarations()
12302 );
12303 }
12304
12305 #[test]
12306 fn repeated_scope_separator_does_not_create_empty_function_owner() {
12307 let scope = ScopeInfo {
12308 package_name: "X".to_string(),
12309 module: None,
12310 class_unit: None,
12311 template_signature: None,
12312 template_metadata: None,
12313 declarations_are_fields: false,
12314 recovered_specialization_member_scope: false,
12315 visible_using_namespaces: Vec::new(),
12316 };
12317
12318 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
12319
12320 assert!(owner.is_none());
12321 assert_eq!(name, "doit");
12322 assert_eq!(package, "X");
12323 }
12324
12325 #[test]
12326 fn trailing_decltype_expression_is_not_a_function_declarator() {
12327 let source = r#"
12328namespace boost { namespace detail {
12329#if ! defined(BOOST_NO_SFINAE_EXPR) && \
12330 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
12331 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
12332#define BOOST_THREAD_PROVIDES_INVOKE
12333#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
12334template <class Fp, class A0, class ...Args>
12335inline auto
12336invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
12337 BOOST_THREAD_RV_REF(Args) ...args)
12338 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
12339{
12340 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
12341}
12342#endif
12343#endif
12344}}
12345"#;
12346 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
12347
12348 assert!(
12349 parsed
12350 .declarations()
12351 .iter()
12352 .all(|unit| unit.short_name() != ".*f")
12353 );
12354 }
12355
12356 fn find_class_named<'tree>(
12357 root: Node<'tree>,
12358 source: &str,
12359 expected_name: &str,
12360 ) -> Option<Node<'tree>> {
12361 let mut stack = vec![root];
12362 while let Some(node) = stack.pop() {
12363 if node.kind() == "class_specifier"
12364 && node
12365 .child_by_field_name("name")
12366 .is_some_and(|name| node_text(name, source) == expected_name)
12367 {
12368 return Some(node);
12369 }
12370 let mut cursor = node.walk();
12371 stack.extend(node.named_children(&mut cursor));
12372 }
12373 None
12374 }
12375
12376 #[test]
12377 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
12378 let source = r#"EXPORT void definition(struct Value value) {}
12379EXPORT void prototype(struct Value value);
12380"#;
12381 let mut parser = tree_sitter::Parser::new();
12382 parser
12383 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12384 .unwrap();
12385 let tree = parser.parse(source, None).unwrap();
12386 let root = tree.root_node();
12387 let mut cursor = root.walk();
12388 let callables = root
12389 .named_children(&mut cursor)
12390 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
12391 .collect::<Vec<_>>();
12392
12393 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
12394 for callable in callables {
12395 assert!(callable.has_error(), "fixture must exercise error recovery");
12396 assert!(
12397 cpp_sentinel_macro_parts(callable, source).is_none(),
12398 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
12399 );
12400 }
12401 }
12402
12403 #[test]
12404 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
12405 let source = r#"namespace absl {
12406ABSL_NAMESPACE_BEGIN
12407// Generate a floating-point variate conforming to a Beta distribution:
12408template <typename RealType = double>
12409class beta_distribution {
12410 public:
12411 using result_type = RealType;
12412
12413
12414 beta_distribution() : beta_distribution(1) {}
12415
12416 explicit beta_distribution(result_type alpha, result_type beta = 1)
12417 : param_(alpha, beta) {}
12418
12419 explicit beta_distribution(const param_type& p) : param_(p) {}
12420
12421 void reset() {}
12422
12423 // Generating functions
12424 template <typename URBG>
12425 result_type operator()(URBG& g) { // NOLINT(runtime/references)
12426 return (*this)(g, param_);
12427 }
12428
12429};
12430ABSL_NAMESPACE_END
12431} // namespace absl
12432"#;
12433 let mut parser = tree_sitter::Parser::new();
12434 parser
12435 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12436 .unwrap();
12437 let tree = parser.parse(source, None).unwrap();
12438 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
12439 let body = namespace
12440 .child_by_field_name("body")
12441 .expect("fixture namespace body");
12442 let sentinel = body.named_child(0).expect("sentinel envelope");
12443 let callable = sentinel
12444 .child_by_field_name("declarator")
12445 .and_then(extract_function_declarator)
12446 .and_then(cpp_function_declarator_name_node)
12447 .expect("preserved callable name");
12448
12449 assert_eq!(sentinel.kind(), "function_definition");
12450 assert_eq!(callable.kind(), "operator_name");
12451 assert!(
12452 cpp_sentinel_macro_parts(sentinel, source).is_some(),
12453 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
12454 );
12455 }
12456
12457 #[test]
12458 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
12459 let source = r#"namespace absl {
12460ABSL_NAMESPACE_BEGIN
12461// absl::discrete_distribution
12462//
12463// A discrete distribution produces random integers i, where 0 <= i < n
12464template <typename IntType = int>
12465class discrete_distribution {
12466 public:
12467 using result_type = IntType;
12468 class param_type {
12469 public:
12470 param_type() { init(); }
12471 template <typename InputIterator>
12472 explicit param_type(InputIterator begin, InputIterator end)
12473 : p_(begin, end) {
12474 init();
12475 }
12476 };
12477 discrete_distribution() : param_() {}
12478 explicit discrete_distribution(const param_type& p) : param_(p) {}
12479};
12480ABSL_NAMESPACE_END
12481} // namespace absl
12482"#;
12483 let mut parser = tree_sitter::Parser::new();
12484 parser
12485 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12486 .unwrap();
12487 let tree = parser.parse(source, None).unwrap();
12488 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
12489 let body = namespace
12490 .child_by_field_name("body")
12491 .expect("fixture namespace body");
12492 let sentinel = body.named_child(0).expect("sentinel envelope");
12493 let callable = sentinel
12494 .child_by_field_name("declarator")
12495 .and_then(extract_function_declarator)
12496 .and_then(cpp_function_declarator_name_node)
12497 .expect("preserved callable name");
12498
12499 assert_eq!(sentinel.kind(), "function_definition");
12500 assert_eq!(callable.kind(), "identifier");
12501 assert!(
12502 cpp_sentinel_macro_parts(sentinel, source).is_some(),
12503 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
12504 );
12505 }
12506
12507 #[test]
12508 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
12509 let source = r#"
12510#define CPPCHECKLIB
12511class Library {
12512 struct Container {
12513 CPPCHECKLIB static std::string toString(Yield yield);
12514 CPPCHECKLIB static std::string toString(Action action);
12515 };
12516};
12517"#;
12518 let mut parser = tree_sitter::Parser::new();
12519 parser
12520 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12521 .unwrap();
12522 let tree = parser.parse(source, None).unwrap();
12523 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
12524 let parsed = parse_cpp_file(&file, source, &tree);
12525 assert!(
12526 parsed
12527 .declarations()
12528 .iter()
12529 .all(|unit| unit.fq_name() != "Library$Container.std"),
12530 "the qualified return-type namespace must not become a field: {:#?}",
12531 parsed.declarations()
12532 );
12533 for expected in ["(Yield)", "(Action)"] {
12534 assert!(
12535 parsed.declarations().iter().any(|unit| {
12536 unit.is_function()
12537 && unit.fq_name() == "Library$Container.toString"
12538 && unit.signature() == Some(expected)
12539 }),
12540 "recovered toString overload {expected} is missing: {:#?}",
12541 parsed.declarations()
12542 );
12543 }
12544 }
12545
12546 #[test]
12547 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
12548 let source = r#"
12549#define SIMPLECPP_LIB
12550namespace simplecpp {
12551using TokenString = std::string;
12552struct Location { int line{}; };
12553class SIMPLECPP_LIB Token {
12554 TokenString prefix;
12555 void prefix_method() {}
12556 public:
12557 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
12558 whitespaceahead(wsahead), location(loc), string(s)
12559 // The comment must not hide the constructor body from recovery.
12560 {
12561 flags();
12562 }
12563 TokenString string;
12564 bool whitespaceahead;
12565 Location location;
12566 Token *previous{};
12567 private:
12568 void flags() {
12569 whitespaceahead = true;
12570 }
12571};
12572}
12573"#;
12574 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
12575
12576 let location_fields = parsed
12577 .declarations()
12578 .iter()
12579 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
12580 .collect::<Vec<_>>();
12581 assert_eq!(
12582 location_fields.len(),
12583 1,
12584 "location should have one class-owned declaration: {:#?}",
12585 parsed.declarations()
12586 );
12587 assert!(
12588 location_fields[0].is_field(),
12589 "location has wrong kind: {:#?}",
12590 parsed.declarations()
12591 );
12592 assert!(
12593 parsed.declarations().iter().all(|unit| {
12594 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
12595 })
12596 );
12597 assert!(
12598 parsed.declarations().iter().all(|unit| {
12599 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
12600 })
12601 );
12602 assert!(
12603 parsed
12604 .declarations()
12605 .iter()
12606 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
12607 );
12608 assert!(
12609 parsed
12610 .declarations()
12611 .iter()
12612 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
12613 "the recovered class must retain its constructor: {:#?}",
12614 parsed.declarations()
12615 );
12616 assert!(
12617 parsed
12618 .declarations()
12619 .iter()
12620 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
12621 );
12622 assert!(parsed.declarations().iter().any(|unit| {
12623 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
12624 }));
12625 let constructor = parsed
12626 .declarations()
12627 .iter()
12628 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
12629 .expect("recovered constructor");
12630 let constructor_start = source.find("Token(const").expect("constructor start");
12631 let constructor_end = source
12632 .get(
12633 ..source
12634 .find(" TokenString string;")
12635 .expect("constructor end"),
12636 )
12637 .expect("constructor slice")
12638 .trim_end()
12639 .len();
12640 assert!(
12641 parsed
12642 .navigation_ranges
12643 .get(constructor)
12644 .is_some_and(|ranges| {
12645 ranges.iter().any(|range| {
12646 range.start_byte == constructor_start && range.end_byte == constructor_end
12647 })
12648 }),
12649 "constructor navigation must span the full body: {:#?}",
12650 parsed.navigation_ranges
12651 );
12652 assert_eq!(
12653 parsed
12654 .signature_metadata
12655 .get(constructor)
12656 .and_then(|metadata| metadata.first())
12657 .and_then(SignatureMetadata::callable_linkage),
12658 Some(CallableLinkage::External)
12659 );
12660 let token_class = parsed
12661 .declarations()
12662 .iter()
12663 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
12664 .expect("recovered Token class");
12665 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
12666 assert!(
12667 parsed
12668 .navigation_ranges
12669 .get(token_class)
12670 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
12671 "class navigation must include the terminating semicolon: {:#?}",
12672 parsed.navigation_ranges
12673 );
12674 }
12675
12676 #[test]
12677 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
12678 let source = r#"
12679#define SIMPLECPP_LIB
12680namespace simplecpp {
12681using TokenString = std::string;
12682class Macro;
12683struct Location {
12684 unsigned int fileIndex{};
12685 unsigned int line{};
12686 unsigned int col{};
12687};
12688struct Output {
12689 int type;
12690};
12691class SIMPLECPP_LIB Token {
12692 public:
12693 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
12694 whitespaceahead(wsahead), location(loc), string(s) {
12695 flags();
12696 }
12697 Token(const Token &tok) :
12698 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
12699 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
12700 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
12701 Token &operator=(const Token &tok) = delete;
12702 const TokenString& str() const { return string; }
12703 void setstr(const std::string &s) { string = s; flags(); }
12704 bool isOneOf(const char ops[]) const;
12705 TokenString macro;
12706 char op;
12707 bool comment;
12708 bool name;
12709 bool number;
12710 bool whitespaceahead;
12711 Location location;
12712 Token *previous{};
12713 Token *next{};
12714 private:
12715 void flags() {
12716 name = !string.empty();
12717 comment = false;
12718 number = false;
12719 op = 0;
12720 }
12721 TokenString string;
12722};
12723}
12724struct Following {
12725 int type;
12726};
12727class SIMPLECPP_LIB Later {
12728 public:
12729 Later(int value) : value(value) {}
12730 int value;
12731};
12732"#;
12733 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
12734 assert!(
12735 parsed
12736 .declarations()
12737 .iter()
12738 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
12739 );
12740 assert!(
12741 !parsed
12742 .declarations()
12743 .iter()
12744 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
12745 );
12746 assert!(
12747 parsed
12748 .declarations()
12749 .iter()
12750 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
12751 );
12752 assert!(
12753 !parsed
12754 .declarations()
12755 .iter()
12756 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
12757 );
12758 assert!(
12759 parsed
12760 .declarations()
12761 .iter()
12762 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
12763 );
12764 assert!(
12765 parsed
12766 .declarations()
12767 .iter()
12768 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
12769 );
12770 assert!(
12771 parsed
12772 .declarations()
12773 .iter()
12774 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
12775 );
12776 assert!(
12777 parsed
12778 .declarations()
12779 .iter()
12780 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
12781 );
12782 assert!(
12783 parsed
12784 .declarations()
12785 .iter()
12786 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
12787 );
12788 assert!(
12789 parsed
12790 .declarations()
12791 .iter()
12792 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
12793 );
12794 assert!(parsed.declarations().iter().all(|unit| {
12795 !matches!(
12796 unit.fq_name().as_str(),
12797 "simplecpp.Token.Following" | "simplecpp.Token.Later"
12798 )
12799 }));
12800 assert!(
12801 !parsed
12802 .declarations()
12803 .iter()
12804 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
12805 "the following struct must remain outside the recovered Token class"
12806 );
12807 }
12808
12809 #[test]
12810 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
12811 let source = r#"
12812#define SIMPLECPP_LIB
12813namespace {
12814namespace simplecpp {
12815using TokenString = std::string;
12816struct Location { int line{}; };
12817class SIMPLECPP_LIB HiddenToken {
12818 public:
12819 HiddenToken(const TokenString &s, const Location &loc) :
12820 location(loc), string(s) {
12821 flags();
12822 }
12823 TokenString string;
12824 Location location;
12825 HiddenToken *previous{};
12826 private:
12827 void flags() {}
12828};
12829}
12830}
12831"#;
12832 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
12833 let constructor = parsed
12834 .declarations()
12835 .iter()
12836 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
12837 .expect("recovered anonymous-namespace constructor");
12838 assert_eq!(
12839 parsed
12840 .signature_metadata
12841 .get(constructor)
12842 .and_then(|metadata| metadata.first())
12843 .and_then(SignatureMetadata::callable_linkage),
12844 Some(CallableLinkage::Internal)
12845 );
12846 }
12847
12848 #[test]
12849 fn macro_qualified_static_field_keeps_real_declarator() {
12850 let source = r#"#define JSON_INLINE_VARIABLE
12851struct Reader {
12852static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
12853static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
12854static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
12855};"#;
12856 let mut parser = tree_sitter::Parser::new();
12857 parser
12858 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12859 .unwrap();
12860 let tree = parser.parse(source, None).unwrap();
12861 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
12862 let parsed = parse_cpp_file(&file, source, &tree);
12863 for expected in [
12864 "Reader.npos",
12865 "Reader.other",
12866 "Reader.pointer",
12867 "Reader.reference",
12868 ] {
12869 assert!(
12870 parsed
12871 .declarations()
12872 .iter()
12873 .any(|unit| unit.is_field() && unit.fq_name() == expected),
12874 "real macro-decorated field {expected} is missing: {:#?}",
12875 parsed.declarations()
12876 );
12877 }
12878 assert!(
12879 parsed
12880 .declarations()
12881 .iter()
12882 .all(|unit| unit.fq_name() != "Reader.std"),
12883 "qualified type prefix became a pseudo-field: {:#?}",
12884 parsed.declarations()
12885 );
12886 let root = tree.root_node();
12887 let mut stack = vec![root];
12888 let mut signatures = Vec::new();
12889 while let Some(current) = stack.pop() {
12890 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
12891 {
12892 signatures.extend(
12893 declarators
12894 .into_iter()
12895 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
12896 );
12897 }
12898 let mut cursor = current.walk();
12899 stack.extend(current.named_children(&mut cursor));
12900 }
12901 signatures.sort();
12902 assert_eq!(
12903 signatures,
12904 [
12905 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
12906 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
12907 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
12908 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
12909 ]
12910 );
12911 }
12912
12913 fn member_function_linkage(source: &str) -> CallableLinkage {
12914 let mut parser = tree_sitter::Parser::new();
12915 parser
12916 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12917 .unwrap();
12918 let tree = parser.parse(source, None).unwrap();
12919 let ancestry = ParentIndex::new(tree.root_node());
12920 let mut stack = vec![tree.root_node()];
12921 while let Some(node) = stack.pop() {
12922 if node.kind() == "function_definition" {
12923 let mut current = node.parent();
12924 while let Some(parent) = current {
12925 if matches!(
12926 parent.kind(),
12927 "class_specifier" | "struct_specifier" | "union_specifier"
12928 ) {
12929 return cpp_callable_linkage(node, source, &ancestry);
12930 }
12931 current = parent.parent();
12932 }
12933 }
12934 let mut cursor = node.walk();
12935 stack.extend(node.named_children(&mut cursor));
12936 }
12937 panic!("fixture has no member function definition");
12938 }
12939
12940 #[test]
12941 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
12942 assert_eq!(
12943 member_function_linkage("struct Named { int method() { return 1; } };"),
12944 CallableLinkage::External
12945 );
12946 assert_eq!(
12947 member_function_linkage(
12948 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
12949 ),
12950 CallableLinkage::Internal
12951 );
12952 assert_eq!(
12953 member_function_linkage("struct { int method() { return 1; } } instance;"),
12954 CallableLinkage::Internal
12955 );
12956 assert_eq!(
12957 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
12958 CallableLinkage::Internal
12959 );
12960 }
12961
12962 #[test]
12963 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
12964 let source = r#"
12965#ifndef PROTON_VALUE_HPP
12966#define PROTON_VALUE_HPP
12967namespace proton {
12968namespace internal {
12969class value_base {
12970 protected:
12971 internal::data& data();
12972 internal::data data_;
12973 friend class codec::encoder;
12974 friend class codec::decoder;
12975};
12976}
12977class value : public internal::value_base, private internal::comparable<value> {
12978 private:
12979 template<class T, class U=void> struct assignable :
12980 public std::enable_if<codec::is_encodable<T>::value, U> {};
12981 template<class U> struct assignable<value, U> {};
12982 public:
12983 PN_CPP_EXTERN value();
12984 PN_CPP_EXTERN value(const value&);
12985 PN_CPP_EXTERN value& operator=(const value&);
12986 PN_CPP_EXTERN value(value&&);
12987 PN_CPP_EXTERN value& operator=(value&&);
12988 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
12989 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
12990 codec::encoder e(*this);
12991 e << x;
12992 return *this;
12993 }
12994 PN_CPP_EXTERN type_id type() const;
12995 PN_CPP_EXTERN bool empty() const;
12996 PN_CPP_EXTERN void clear();
12997 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
12998 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
12999 friend PN_CPP_EXTERN void swap(value&, value&);
13000 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
13001 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
13002 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
13003 value(pn_data_t* d);
13004 void reset(pn_data_t* d = 0);
13005};
13006}
13007#endif
13008"#;
13009 let mut parser = tree_sitter::Parser::new();
13010 parser
13011 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13012 .unwrap();
13013 let tree = parser.parse(source, None).unwrap();
13014 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
13015 let parsed = parse_cpp_file(&file, source, &tree);
13016 let macro_constructors = parsed
13017 .signature_metadata
13018 .iter()
13019 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
13020 .flat_map(|(_, metadata)| metadata)
13021 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
13022 .collect::<Vec<_>>();
13023
13024 assert_eq!(
13025 macro_constructors.len(),
13026 3,
13027 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
13028 parsed.declarations()
13029 );
13030 assert!(
13031 macro_constructors.iter().all(|metadata| {
13032 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
13033 }),
13034 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
13035 );
13036 }
13037
13038 #[test]
13039 fn recovered_export_class_typedef_uses_displaced_alias_name() {
13040 let source = r#"
13041namespace spi {
13042class Filter {
13043public:
13044 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
13045};
13046}
13047namespace filter {
13048class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
13049{
13050public:
13051 typedef spi::Filter BASE_CLASS;
13052 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
13053 BEGIN_LOG4CXX_CAST_MAP()
13054 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
13055 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
13056 END_LOG4CXX_CAST_MAP()
13057 FilterDecision decide() const;
13058};
13059}
13060"#;
13061 let mut parser = tree_sitter::Parser::new();
13062 parser
13063 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13064 .unwrap();
13065 let tree = parser.parse(source, None).unwrap();
13066 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
13067 let parsed = parse_cpp_file(&file, source, &tree);
13068 assert!(
13069 parsed.declarations().iter().any(|unit| {
13070 unit.is_class()
13071 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
13072 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
13073 }),
13074 "the displaced typedef alias must retain its declared name: {:#?}",
13075 parsed.declarations()
13076 );
13077 assert!(
13078 parsed
13079 .declarations()
13080 .iter()
13081 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
13082 "the qualified underlying type must not become a false nested alias: {:#?}",
13083 parsed.declarations()
13084 );
13085 }
13086
13087 #[test]
13088 fn exported_single_base_recovery_uses_displaced_class_name() {
13089 let source = r#"
13090class CORE_EXPORT QgsPoint : public AbstractGeometry
13091{
13092 Q_GADGET
13093
13094 Q_PROPERTY( double x READ x WRITE setX )
13095 Q_PROPERTY( double y READ y WRITE setY )
13096 Q_PROPERTY( double z READ z WRITE setZ )
13097 Q_PROPERTY( double m READ m WRITE setM )
13098
13099 public:
13100#ifndef SIP_RUN
13101 QgsPoint(
13102 double x = std::numeric_limits<double>::quiet_NaN(),
13103 double y = std::numeric_limits<double>::quiet_NaN(),
13104 double z = std::numeric_limits<double>::quiet_NaN(),
13105 double m = std::numeric_limits<double>::quiet_NaN(),
13106 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
13107 );
13108#else
13109 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 )];
13110 % MethodCode
13111 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
13112 {
13113 int state;
13114 sipIsErr = 0;
13115 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
13116 if ( !sipIsErr )
13117 {
13118 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
13119 }
13120 sipReleaseType( p, sipType_QgsPointXY, state );
13121 }
13122 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
13123 {
13124 int state;
13125 sipIsErr = 0;
13126
13127 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
13128 if ( !sipIsErr )
13129 {
13130 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
13131 }
13132 sipReleaseType( p, sipType_QPointF, state );
13133 }
13134 else if (
13135 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
13136 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
13137 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
13138 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
13139 {
13140 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
13141 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
13142 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
13143 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
13144 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
13145 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
13146 }
13147 else // Invalid ctor arguments
13148 {
13149 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
13150 sipIsErr = 1;
13151 }
13152 % End
13153#endif
13154
13155 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
13156 explicit QgsPoint( QPointF p ) SIP_SKIP;
13157 explicit QgsPoint(
13158 Qgis::WkbType wkbType,
13159 double x = std::numeric_limits<double>::quiet_NaN(),
13160 double y = std::numeric_limits<double>::quiet_NaN(),
13161 double z = std::numeric_limits<double>::quiet_NaN(),
13162 double m = std::numeric_limits<double>::quiet_NaN()
13163 ) SIP_SKIP;
13164 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
13165 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
13166 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
13167#ifndef SIP_RUN
13168 private:
13169 bool fuzzyHelper(
13170 double epsilon,
13171 const AbstractGeometry &other,
13172 bool is3DFlag,
13173 bool isMeasureFlag
13174 ) const
13175 {
13176 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
13177 }
13178#endif
13179};
13180class Ordinary : public Base { public: Ordinary(); };
13181class API_EXPORT Plain { public: Plain(); };
13182class API_EXPORT : public Base {};
13183class
13184PN_CPP_CLASS_EXTERN Sender : public Link {
13185 Sender();
13186};
13187class thread_ctx_t {};
13188class ctx_t ZMQ_FINAL : public thread_ctx_t {
13189 bool start();
13190};
13191"#;
13192 let mut parser = tree_sitter::Parser::new();
13193 parser
13194 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13195 .unwrap();
13196 let tree = parser.parse(source, None).unwrap();
13197 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
13198 let parsed = parse_cpp_file(&file, source, &tree);
13199 let declarations = parsed.declarations();
13200
13201 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
13202 assert!(
13203 declarations
13204 .iter()
13205 .any(|unit| unit.is_class() && unit.fq_name() == expected),
13206 "missing recovered class {expected}: {declarations:#?}"
13207 );
13208 }
13209 let qgs_point = declarations
13210 .iter()
13211 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
13212 .expect("recovered QgsPoint class");
13213 assert_eq!(
13214 parsed.raw_supertypes.get(qgs_point),
13215 Some(&vec!["AbstractGeometry".to_string()]),
13216 "single-base export recovery must retain its displaced base"
13217 );
13218 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
13219 assert!(
13220 parsed
13221 .navigation_ranges
13222 .get(qgs_point)
13223 .is_some_and(|ranges| {
13224 !ranges.is_empty()
13225 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
13226 }),
13227 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
13228 parsed.navigation_ranges.get(qgs_point)
13229 );
13230 let sender = declarations
13231 .iter()
13232 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
13233 .expect("recovered Sender class");
13234 assert_eq!(
13235 parsed.raw_supertypes.get(sender),
13236 Some(&vec!["Link".to_string()]),
13237 "post-declarator export recovery must retain its displaced base"
13238 );
13239 let ctx = declarations
13240 .iter()
13241 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
13242 .expect("recovered ctx_t class");
13243 assert_eq!(
13244 parsed.raw_supertypes.get(ctx),
13245 Some(&vec!["thread_ctx_t".to_string()]),
13246 "postfix export-macro recovery must retain its displaced base"
13247 );
13248 assert!(
13249 declarations.iter().any(|unit| {
13250 unit.is_function()
13251 && unit.fq_name() == "QgsPoint.QgsPoint"
13252 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
13253 }),
13254 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
13255 );
13256 assert!(
13257 declarations.iter().all(|unit| {
13258 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
13259 }),
13260 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
13261 );
13262 }
13263
13264 #[test]
13265 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
13266 let positive_source =
13267 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
13268 let mut parser = tree_sitter::Parser::new();
13269 parser
13270 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13271 .unwrap();
13272 let positive_tree = parser.parse(positive_source, None).unwrap();
13273 assert!(cpp_reparsed_members_are_indexable(
13274 positive_tree.root_node(),
13275 positive_source
13276 ));
13277
13278 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
13279 let negative_tree = parser.parse(negative_source, None).unwrap();
13280 assert!(!cpp_reparsed_members_are_indexable(
13281 negative_tree.root_node(),
13282 negative_source
13283 ));
13284 }
13285
13286 #[test]
13287 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
13288 let copy_control_source = r#"
13289public:
13290 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
13291 explicit Token(const Token* tok);
13292 ~Token();
13293 Token* astOperand1() { return nullptr; }
13294"#;
13295 let constraint_source = r#"
13296private:
13297 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
13298 static T *tokAtImpl(T *tok, int index) {
13299 return tok;
13300 }
13301
13302 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
13303 static T *linkAtImpl(T *tok, int index) {
13304 return tok;
13305 }
13306
13307public:
13308 int late() const { return 1; }
13309"#;
13310 let mut parser = tree_sitter::Parser::new();
13311 parser
13312 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13313 .unwrap();
13314 let copy_control_tree = parser
13315 .parse(copy_control_source, None)
13316 .expect("parse copy-control fixture");
13317 assert!(
13318 copy_control_tree.root_node().has_error(),
13319 "fixture must exercise adjacent copy-control recovery"
13320 );
13321 assert!(
13322 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
13323 "a complete late getter must remain recoverable after adjacent copy-control declarations"
13324 );
13325 let mut cursor = copy_control_tree.root_node().walk();
13326 assert!(
13327 copy_control_tree
13328 .root_node()
13329 .named_children(&mut cursor)
13330 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
13331 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
13332 copy_control_tree.root_node().to_sexp()
13333 );
13334 let constraint_tree = parser
13335 .parse(constraint_source, None)
13336 .expect("parse constraint-macro fixture");
13337 assert!(constraint_tree.root_node().has_error());
13338 assert!(
13339 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
13340 "complete constraint-macro members must not hide a later ordinary member"
13341 );
13342 let mut cursor = constraint_tree.root_node().walk();
13343 assert!(
13344 constraint_tree
13345 .root_node()
13346 .named_children(&mut cursor)
13347 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
13348 child,
13349 constraint_source
13350 )),
13351 "fixture must retain the split constraint-macro prefix/function geometry"
13352 );
13353 }
13354
13355 #[test]
13356 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
13357 let source = r#"
13358struct Analyzer {
13359 struct Action {
13360 Action() = default;
13361 Action(const Action&) = default;
13362 Action& operator=(const Action& rhs) & = default;
13363
13364 template<class T,
13365 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
13366 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
13367 // NOLINTNEXTLINE(google-explicit-constructor)
13368 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
13369 {}
13370
13371 enum : std::uint16_t { None = 0, Read = (1 << 0) };
13372 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
13373
13374 private:
13375 unsigned int mFlag{};
13376 };
13377
13378 enum class Direction : unsigned char { Forward, Reverse };
13379 virtual Action analyze(Direction d) const = 0;
13380};
13381"#;
13382 let mut parser = tree_sitter::Parser::new();
13383 parser
13384 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13385 .unwrap();
13386 let tree = parser.parse(source, None).unwrap();
13387 assert!(tree.root_node().has_error());
13388 let root = tree.root_node();
13389 let outer = root
13390 .named_children(&mut root.walk())
13391 .find(|child| child.kind() == "ERROR")
13392 .expect("fragmented Analyzer prefix");
13393 let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
13394 .expect("structured Analyzer fragment boundary");
13395 assert_eq!(outer_name, "Analyzer");
13396 let outer_tree = cpp_reparse_fragmented_class_body(
13397 source,
13398 outer_fragment.reparse_start,
13399 outer_fragment.reparse_end,
13400 )
13401 .expect("reparse Analyzer body");
13402 let outer_root = outer_tree.root_node();
13403 let action_prefix = outer_root
13404 .named_children(&mut outer_root.walk())
13405 .find(|child| child.kind() == "ERROR")
13406 .expect("fragmented Action prefix");
13407 let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
13408 .expect("structured Action fragment boundary");
13409 assert_eq!(action_name, "Action");
13410 let action_tree = cpp_reparse_fragmented_class_body(
13411 source,
13412 action_fragment.reparse_start,
13413 action_fragment.reparse_end,
13414 )
13415 .expect("reparse Action body");
13416 let action_root = action_tree.root_node();
13417 let macro_prefix = action_root
13418 .named_children(&mut action_root.walk())
13419 .find(|child| child.kind() == "ERROR")
13420 .expect("constraint macro prefix");
13421 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
13422 .expect("structured template macro prefix");
13423 let macro_companion =
13424 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
13425 assert!(
13426 cpp_reparsed_template_macro_constructor_companion_is_indexable(
13427 macro_companion,
13428 macro_parameter,
13429 source,
13430 ),
13431 "split constrained constructor must be admitted: {}",
13432 macro_companion.to_sexp()
13433 );
13434 assert!(
13435 cpp_reparsed_members_are_indexable(action_root, source),
13436 "complete Action body must pass the recovery gate: {}",
13437 action_tree.root_node().to_sexp()
13438 );
13439 assert!(
13440 cpp_reparsed_members_are_indexable(outer_root, source),
13441 "complete Analyzer body must pass the recovery gate: {}",
13442 outer_tree.root_node().to_sexp()
13443 );
13444 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
13445 let parsed = parse_cpp_file(&file, source, &tree);
13446 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
13447 assert!(
13448 parsed
13449 .declarations()
13450 .iter()
13451 .any(|unit| unit.fq_name() == expected),
13452 "missing recovered declaration {expected}: {:#?}",
13453 parsed.declarations()
13454 );
13455 }
13456 assert!(
13457 parsed
13458 .declarations()
13459 .iter()
13460 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
13461 "nested members must not remain flattened: {:#?}",
13462 parsed.declarations()
13463 );
13464 }
13465
13466 #[test]
13467 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
13468 let source = r#"
13469raw_hash_set& operator=(raw_hash_set&& that) {
13470 return move_assign(
13471 std::move(that),
13472 typename AllocTraits::propagate_on_container_move_assignment());
13473}
13474
13475iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
13476 return {};
13477}
13478
13479void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
13480
13481iterator insert(const_iterator hint, value_type&& value)
13482 ABSL_ATTRIBUTE_LIFETIME_BOUND {
13483 return {};
13484}
13485
13486friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
13487 return left.size() == right.size();
13488}
13489
13490static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
13491 return static_cast<slot_type*>(buffer);
13492}
13493
13494protected:
13495// Included-range recovery can attach this comment to the template prefix.
13496template <class K>
13497void AssertOnFind([[maybe_unused]] const K& key) {
13498 Check(key);
13499}
13500"#;
13501 let mut parser = tree_sitter::Parser::new();
13502 parser
13503 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13504 .unwrap();
13505 let tree = parser.parse(source, None).unwrap();
13506 assert!(
13507 tree.root_node().has_error(),
13508 "the fixture must exercise tree-sitter's errorful member shapes"
13509 );
13510 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
13511
13512 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
13513 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
13514 assert!(!cpp_reparsed_members_are_indexable(
13515 incomplete_tree.root_node(),
13516 incomplete_source
13517 ));
13518
13519 let outside_error_source = "int foo() stray_attribute {}\n";
13520 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
13521 assert!(outside_error_tree.root_node().has_error());
13522 assert!(!cpp_reparsed_members_are_indexable(
13523 outside_error_tree.root_node(),
13524 outside_error_source
13525 ));
13526
13527 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
13528 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
13529 assert!(!cpp_reparsed_members_are_indexable(
13530 variable_initializer_tree.root_node(),
13531 variable_initializer_source
13532 ));
13533 }
13534
13535 #[test]
13536 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
13537 let positive_source = r#"
13538std::pair<iterator, bool> insert(init_type&& value)
13539 ABSL_ATTRIBUTE_LIFETIME_BOUND
13540#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
13541 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
13542#endif
13543{
13544 return emplace(std::move(value));
13545}
13546"#;
13547 let mut parser = tree_sitter::Parser::new();
13548 parser
13549 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13550 .unwrap();
13551 let positive_tree = parser.parse(positive_source, None).unwrap();
13552 assert!(
13553 positive_tree.root_node().has_error(),
13554 "the fixture must exercise the split attribute/requires shape"
13555 );
13556 assert!(cpp_reparsed_members_are_indexable(
13557 positive_tree.root_node(),
13558 positive_source
13559 ));
13560
13561 let template_return_source = r#"
13562pair<int> insert(init_type&& value)
13563 ABSL_ATTRIBUTE_LIFETIME_BOUND
13564#if LANGUAGE_LEVEL >= 202002L
13565 requires(!Predicate<init_type>::value)
13566#endif
13567// Attributes and the function body may be separated by comments.
13568{
13569 return {};
13570}
13571"#;
13572 let template_return_tree = parser.parse(template_return_source, None).unwrap();
13573 assert!(
13574 cpp_reparsed_members_are_indexable(
13575 template_return_tree.root_node(),
13576 template_return_source
13577 ),
13578 "template-return attribute/requires tree: {}",
13579 template_return_tree.root_node().to_sexp()
13580 );
13581
13582 let no_body_source = r#"
13583std::pair<iterator, bool> insert(init_type&& value)
13584 ABSL_ATTRIBUTE_LIFETIME_BOUND
13585#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
13586 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
13587#endif
13588+ 0;
13589"#;
13590 let no_body_tree = parser.parse(no_body_source, None).unwrap();
13591 assert!(!cpp_reparsed_members_are_indexable(
13592 no_body_tree.root_node(),
13593 no_body_source
13594 ));
13595
13596 let extra_payload_source = r#"
13597pair<int> insert(init_type&& value)
13598 ABSL_ATTRIBUTE_LIFETIME_BOUND
13599#if LANGUAGE_LEVEL >= 202002L
13600 int unrelated;
13601 requires(Predicate<init_type>::value)
13602#endif
13603{
13604 return {};
13605}
13606"#;
13607 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
13608 assert!(!cpp_reparsed_members_are_indexable(
13609 extra_payload_tree.root_node(),
13610 extra_payload_source
13611 ));
13612
13613 let variable_initializer_source = r#"
13614int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
13615#if LANGUAGE_LEVEL >= 202002L
13616 requires(true)
13617#endif
13618{
13619 bad;
13620}
13621"#;
13622 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
13623 assert!(!cpp_reparsed_members_are_indexable(
13624 variable_initializer_tree.root_node(),
13625 variable_initializer_source
13626 ));
13627 }
13628
13629 #[test]
13630 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
13631 let source = r#"namespace absl {
13632ABSL_NAMESPACE_BEGIN namespace container_internal {
13633
13634class raw_hash_set : public Base {
13635 public:
13636 using value_type = int;
13637
13638 template <class U,
13639 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
13640 void insert(U value) { (void)value; }
13641
13642 struct InsertSlot {
13643 raw_hash_set& s;
13644 };
13645};
13646
13647}
13648ABSL_NAMESPACE_END
13649}"#;
13650 let mut parser = tree_sitter::Parser::new();
13651 parser
13652 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13653 .unwrap();
13654 let tree = parser.parse(source, None).unwrap();
13655 let root = tree.root_node();
13656 let outer_namespace = root
13657 .named_children(&mut root.walk())
13658 .find(|child| child.kind() == "namespace_definition")
13659 .expect("outer absl namespace");
13660 let declaration_list = outer_namespace
13661 .child_by_field_name("body")
13662 .expect("outer namespace body");
13663 let sentinel_function = declaration_list
13664 .named_children(&mut declaration_list.walk())
13665 .find(|child| child.kind() == "function_definition")
13666 .expect("malformed namespace sentinel function");
13667 let ancestry = ParentIndex::new(root);
13668 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
13669 .expect("structured nested namespace sentinel");
13670 let fragmented =
13671 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
13672 .expect("fragmented raw_hash_set class");
13673 assert_eq!(fragmented.class_node.kind(), "ERROR");
13674 assert_eq!(fragmented.name, "raw_hash_set");
13675 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
13676
13677 let outer_scope =
13678 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
13679 let mut outer_siblings = Vec::new();
13680 push_cpp_sentinel_sibling_classes(
13681 &mut outer_siblings,
13682 declaration_list,
13683 sentinel.function,
13684 &outer_scope,
13685 source,
13686 &ancestry,
13687 );
13688 let [outer_shadow] = outer_siblings.as_slice() else {
13689 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
13690 };
13691 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
13692 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
13693
13694 let field = " raw_hash_set& s;";
13695 let start = source.find(field).expect("InsertSlot field") + 4;
13696 let node = root
13697 .descendant_for_byte_range(start, start + "raw_hash_set".len())
13698 .expect("raw_hash_set type node");
13699 let recovered = cpp_sentinel_recovered_classes(root, source);
13700 let [deep_class] = recovered.as_slice() else {
13701 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
13702 };
13703 assert_eq!(
13704 deep_class.namespace_scope_components,
13705 vec!["absl", "container_internal"]
13706 );
13707 assert_eq!(
13708 deep_class.scope_components,
13709 vec!["absl", "container_internal", "raw_hash_set"]
13710 );
13711 assert!(
13712 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
13713 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
13714 );
13715
13716 assert_eq!(
13717 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
13718 Some(vec![
13719 "absl".to_string(),
13720 "container_internal".to_string(),
13721 "raw_hash_set".to_string(),
13722 "InsertSlot".to_string(),
13723 ])
13724 );
13725
13726 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
13727 let parsed = parse_cpp_file(&file, source, &tree);
13728 let raw_hash_set = parsed
13729 .declarations()
13730 .iter()
13731 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
13732 .expect("recovered raw_hash_set class");
13733 assert_eq!(
13734 raw_hash_set.fq_name(),
13735 "absl::container_internal.raw_hash_set",
13736 "the recovered declaration must publish under the deeper sentinel namespace"
13737 );
13738 assert_eq!(
13739 parsed.raw_supertypes.get(raw_hash_set),
13740 Some(&vec!["Base".to_string()]),
13741 "the structured base clause on the fragmented ERROR prefix must survive publication"
13742 );
13743 assert!(
13744 parsed.materialization_records.iter().any(|record| matches!(
13745 record,
13746 MaterializationRecord::RecoveredDeclaration { recovery, unit }
13747 if unit == raw_hash_set && *recovery == deep_class.class_range
13748 )),
13749 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
13750 parsed.materialization_records
13751 );
13752 }
13753
13754 #[test]
13781 fn the_parent_index_answers_what_tree_sitter_answers() {
13782 const SHAPES: [&str; 5] = [
13783 "namespace outer { namespace inner { struct Tag { int field; }; } }",
13784 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
13785 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
13786 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
13787 "class API Broken : public First, public Second {\n void member();\n",
13788 ];
13789 for source in SHAPES {
13790 let mut parser = tree_sitter::Parser::new();
13791 parser
13792 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13793 .unwrap();
13794 let tree = parser.parse(source, None).unwrap();
13795 let root = tree.root_node();
13796 let ancestry = ParentIndex::new(root);
13797 let mut nodes = 0usize;
13798 let mut stack = vec![root];
13799 while let Some(node) = stack.pop() {
13800 nodes += 1;
13801 assert_eq!(
13802 node.parent().map(|parent| parent.id()),
13803 ancestry.parent(node).map(|parent| parent.id()),
13804 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
13805 );
13806 let mut cursor = node.walk();
13807 stack.extend(node.children(&mut cursor));
13808 }
13809 assert!(nodes > 1, "{source:?} produced no tree to compare");
13810 }
13811 }
13812
13813 #[test]
13820 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
13821 const DEPTH: usize = 64;
13822 let mut source = String::new();
13823 for level in 0..DEPTH {
13824 writeln!(source, "namespace n{level} {{").unwrap();
13825 }
13826 source.push_str("int deepest(int value);\n");
13827 for _ in 0..DEPTH {
13828 source.push_str("}\n");
13829 }
13830
13831 let mut parser = tree_sitter::Parser::new();
13832 parser
13833 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13834 .unwrap();
13835 let tree = parser.parse(&source, None).unwrap();
13836 let root = tree.root_node();
13837 let ancestry = ParentIndex::new(root);
13838 let mut function_declarator = None;
13839 walk_named_tree_preorder(root, true, |node| {
13840 if node.kind() == "function_declarator" {
13841 function_declarator = Some(node);
13842 WalkControl::Break
13843 } else {
13844 WalkControl::Continue
13845 }
13846 });
13847 let function_declarator = function_declarator.expect("deepest function declarator");
13848 let ancestor_count =
13849 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
13850
13851 ancestry.reset_parent_query_count_for_test();
13852 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
13853 assert_eq!(DEPTH, lexical_scope.len());
13854 assert_eq!(
13855 ancestor_count + 1,
13856 ancestry.parent_query_count_for_test(),
13857 "lexical-scope ancestry bypassed the parent index"
13858 );
13859
13860 ancestry.reset_parent_query_count_for_test();
13861 assert_eq!(
13862 DispatchExtensibility::Closed,
13863 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
13864 );
13865 assert_eq!(
13866 ancestor_count,
13867 ancestry.parent_query_count_for_test(),
13868 "dispatch ancestry bypassed the parent index"
13869 );
13870
13871 ancestry.reset_parent_query_count_for_test();
13872 assert_eq!(
13873 CallableLinkage::External,
13874 cpp_callable_linkage(function_declarator, &source, &ancestry)
13875 );
13876 assert_eq!(
13877 ancestor_count + 1,
13878 ancestry.parent_query_count_for_test(),
13879 "linkage ancestry bypassed the parent index"
13880 );
13881
13882 ancestry.reset_parent_query_count_for_test();
13883 assert!(!cpp_callable_is_structural_constructor(
13884 function_declarator,
13885 &source,
13886 &ancestry
13887 ));
13888 assert_eq!(
13889 ancestor_count + 1,
13890 ancestry.parent_query_count_for_test(),
13891 "constructor ancestry bypassed the parent index"
13892 );
13893 }
13894
13895 #[test]
13900 fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
13901 for aggregates in [64usize, 512] {
13902 let mut source =
13903 String::from("typedef unsigned long long u64;\nnamespace generated {\n");
13904 for index in 0..aggregates {
13905 writeln!(source, "struct tag{index};").unwrap();
13906 }
13907 for index in (0..aggregates).rev() {
13908 writeln!(
13909 source,
13910 "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
13911 )
13912 .unwrap();
13913 }
13914 source.push_str("}\n");
13915
13916 start_code_unit_removal_scan_probe();
13917 let parsed = parse_cpp_declarations(&source, "vmlinux.h");
13918 let scanned = finish_code_unit_removal_scan_probe();
13919
13920 let expected_names: Vec<String> = (0..aggregates)
13921 .rev()
13922 .map(|index| format!("tag{index}"))
13923 .collect();
13924 let top_level_names: Vec<String> = parsed
13925 .top_level_declarations
13926 .iter()
13927 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
13928 .map(|unit| unit.short_name().to_string())
13929 .collect();
13930 let namespace = parsed
13931 .declarations()
13932 .iter()
13933 .find(|unit| {
13934 unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
13935 })
13936 .expect("generated namespace should be declared");
13937 let child_names: Vec<String> = parsed.children[namespace]
13938 .iter()
13939 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
13940 .map(|unit| unit.short_name().to_string())
13941 .collect();
13942 assert_eq!(
13943 aggregates,
13944 parsed
13945 .declarations()
13946 .iter()
13947 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
13948 .count(),
13949 "every aggregate must still be declared at {aggregates} aggregates"
13950 );
13951 assert_eq!(expected_names, top_level_names);
13952 assert_eq!(expected_names, child_names);
13953 assert_eq!(
13954 0, scanned,
13955 "replacing {aggregates} forward declarations must compact their shared lists once"
13956 );
13957 }
13958 }
13959
13960 #[test]
13961 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
13962 const DISTINCT_PER_KIND: usize = 64;
13963 let mut source = String::new();
13964 for index in 0..DISTINCT_PER_KIND {
13965 writeln!(source, "typedef int Alias{index};").unwrap();
13966 }
13967 writeln!(source, "typedef long Alias0;").unwrap();
13968 for index in 0..DISTINCT_PER_KIND {
13969 writeln!(source, "#define MACRO_{index} {index}").unwrap();
13970 }
13971 writeln!(source, "#define MACRO_0 duplicate").unwrap();
13972 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
13973
13974 let mut parser = tree_sitter::Parser::new();
13975 parser
13976 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13977 .unwrap();
13978 let tree = parser.parse(&source, None).unwrap();
13979 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
13980
13981 start_declaration_identity_comparison_probe();
13982 let parsed = parse_cpp_file(&file, &source, &tree);
13983 let comparisons = finish_declaration_identity_comparison_probe();
13984
13985 assert_eq!(
13986 DISTINCT_PER_KIND + 1,
13987 parsed
13988 .declarations()
13989 .iter()
13990 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
13991 .count(),
13992 "every physical typedef alias declaration must be retained so \
13993 conditional branch guards stay available to the resolver"
13994 );
13995 assert_eq!(
13996 DISTINCT_PER_KIND,
13997 parsed
13998 .declarations()
13999 .iter()
14000 .filter(|unit| {
14001 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
14002 })
14003 .count(),
14004 "macros should retain semantic-identity deduplication"
14005 );
14006 assert_eq!(
14007 2,
14008 parsed
14009 .declarations()
14010 .iter()
14011 .filter(|unit| {
14012 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
14013 })
14014 .count(),
14015 "function overloads must remain distinct"
14016 );
14017
14018 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
14019 assert!(
14020 comparisons <= dedup_inputs * 4,
14021 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
14022 );
14023 }
14024
14025 #[test]
14026 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
14027 let source = r#"namespace absl {
14028ABSL_NAMESPACE_BEGIN namespace container_internal {
14029template <typename T>
14030class broken {
14031 public:
14032 using value_type = T;
14033 T operator->() const { return &operator*(); }
14034 using alias = value_type;
14035};
14036}
14037}
14038"#;
14039 let mut parser = tree_sitter::Parser::new();
14040 parser
14041 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14042 .unwrap();
14043 let tree = parser.parse(source, None).unwrap();
14044 let broken = find_class_named(tree.root_node(), source, "broken")
14045 .expect("the positive fixture must expose the broken class node");
14046 assert!(
14047 broken.has_error(),
14048 "the positive fixture must retain an internal parser error"
14049 );
14050 assert!(
14051 cpp_complete_class_body_close(broken).is_some(),
14052 "the positive fixture must expose a real class body close"
14053 );
14054 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
14055 assert!(
14056 recovered.iter().any(|class| {
14057 class.scope_components == ["absl", "container_internal", "broken"]
14058 }),
14059 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
14060 );
14061 }
14062
14063 #[test]
14064 fn sentinel_recovery_keeps_members_after_nested_body_close() {
14065 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
14066NLOHMANN_BASIC_JSON_TPL_DECLARATION
14067class basic_json {
14068 private:
14069 union storage {
14070 int value;
14071 } data;
14072 public:
14073 using late_alias = int;
14074 late_alias value() const;
14075};
14076NLOHMANN_JSON_NAMESPACE_END
14077"#;
14078 let mut parser = tree_sitter::Parser::new();
14079 parser
14080 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14081 .unwrap();
14082 let tree = parser.parse(source, None).unwrap();
14083 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
14084 let basic_json = recovered
14085 .iter()
14086 .find(|class| {
14087 class
14088 .scope_components
14089 .last()
14090 .is_some_and(|name| name == "basic_json")
14091 })
14092 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
14093 let late_alias = source
14094 .find("late_alias value")
14095 .expect("late alias reference");
14096 assert!(
14097 basic_json.class_range.start_byte < late_alias
14098 && late_alias < basic_json.class_range.end_byte,
14099 "the recovered class range must include members after a nested close: {basic_json:#?}"
14100 );
14101 }
14102
14103 #[test]
14104 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
14105 let source = r#"namespace absl {
14106ABSL_NAMESPACE_BEGIN namespace container_internal {
14107template <typename T>
14108class broken {
14109 public:
14110 using value_type = T;
14111 T operator->() const { return &operator*(); }
14112}
14113}
14114"#;
14115 let mut parser = tree_sitter::Parser::new();
14116 parser
14117 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14118 .unwrap();
14119 let tree = parser.parse(source, None).unwrap();
14120 let broken = find_class_named(tree.root_node(), source, "broken")
14121 .expect("the negative fixture must expose the malformed class node");
14122 assert!(
14123 broken.has_error(),
14124 "the negative fixture must retain a parser error"
14125 );
14126 assert!(
14127 cpp_complete_class_body_close(broken).is_none(),
14128 "the malformed class must not expose a real body close"
14129 );
14130 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
14131 assert!(
14132 recovered
14133 .iter()
14134 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
14135 "an incomplete class must not borrow the namespace close: {recovered:#?}"
14136 );
14137 }
14138
14139 #[test]
14140 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
14141 let source = r#"namespace absl {
14142ABSL_NAMESPACE_BEGIN namespace container_internal {
14143template <typename T>
14144struct broken {
14145 using value_type = T;
14146};
14147}
14148
14149#ifdef OWNER_DEF
14150template <typename T>
14151typename broken<T>::value_type broken<T>::method() {
14152 value_type value{};
14153 return value;
14154}
14155#endif
14156
14157namespace sibling {
14158template <typename T>
14159typename broken<T>::value_type broken<T>::other() {
14160 value_type value{};
14161 return value;
14162}
14163}
14164
14165ABSL_NAMESPACE_END
14166}
14167"#;
14168 let mut parser = tree_sitter::Parser::new();
14169 parser
14170 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14171 .unwrap();
14172 let tree = parser.parse(source, None).unwrap();
14173 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
14174 let broken = recovered
14175 .iter()
14176 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
14177 .expect("the sentinel class must be recovered");
14178 let method_start = source
14179 .find("typename broken<T>::value_type broken<T>::method()")
14180 .expect("guarded sibling owner");
14181 let method_end = source[method_start..]
14182 .find("\n}")
14183 .map(|offset| method_start + offset + 2)
14184 .expect("guarded sibling owner close");
14185 assert!(
14186 broken
14187 .owner_ranges
14188 .iter()
14189 .any(|owner| owner.range.start_byte <= method_start
14190 && method_end <= owner.range.end_byte),
14191 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
14192 );
14193 let sibling_start = source
14194 .find("typename broken<T>::value_type broken<T>::other()")
14195 .expect("nested namespace sibling owner");
14196 assert!(
14197 broken
14198 .owner_ranges
14199 .iter()
14200 .all(|owner| owner.range.start_byte > sibling_start
14201 || owner.range.end_byte <= sibling_start),
14202 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
14203 );
14204 }
14205
14206 #[test]
14207 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
14208 let source = r#"#ifdef OUTER
14209namespace absl {
14210ABSL_NAMESPACE_BEGIN namespace container_internal {
14211template <typename T>
14212struct broken {
14213 using value_type = T;
14214};
14215}
14216}
14217
14218#ifdef OWNER_DEF
14219template <typename T>
14220typename broken<T>::value_type broken<T>::method() {
14221 value_type value{};
14222 return value;
14223}
14224#endif
14225#endif
14226"#;
14227 let mut parser = tree_sitter::Parser::new();
14228 parser
14229 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14230 .unwrap();
14231 let tree = parser.parse(source, None).unwrap();
14232 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
14233 let broken = recovered
14234 .iter()
14235 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
14236 .expect("the sentinel class must be recovered");
14237 let method_start = source
14238 .find("typename broken<T>::value_type broken<T>::method()")
14239 .expect("outer sibling owner");
14240 assert!(
14241 broken
14242 .owner_ranges
14243 .iter()
14244 .all(|owner| owner.range.start_byte > method_start
14245 || owner.range.end_byte <= method_start),
14246 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
14247 );
14248 }
14249
14250 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
14252 let mut signatures = parsed
14253 .declarations()
14254 .iter()
14255 .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
14256 .filter_map(|unit| unit.signature().map(str::to_string))
14257 .collect::<Vec<_>>();
14258 signatures.sort();
14259 signatures.dedup();
14260 signatures
14261 }
14262
14263 #[test]
14264 fn callable_parameter_types_come_from_the_ast_parameter_list() {
14265 let source = r#"
14266template <typename T, ENABLE_BYTES(T)>
14267Vec256<T> DupOdd(Vec256<T> value) { return value; }
14268
14269struct Visitor {
14270 void fail(this auto const& self) {}
14271};
14272"#;
14273 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
14274 let dup_odd = parsed
14275 .declarations()
14276 .iter()
14277 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
14278 .expect("DupOdd declaration");
14279 assert_eq!(
14280 dup_odd.signature(),
14281 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
14282 );
14283 assert_eq!(
14284 parsed
14285 .signature_metadata
14286 .get(dup_odd)
14287 .and_then(|metadata| metadata.first())
14288 .and_then(SignatureMetadata::callable_parameter_types),
14289 Some(["Vec256<T>".to_string()].as_slice())
14290 );
14291
14292 let fail = parsed
14293 .declarations()
14294 .iter()
14295 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
14296 .expect("explicit-object member");
14297 assert_eq!(fail.signature(), Some("(const this auto &)"));
14298 let metadata = parsed
14299 .signature_metadata
14300 .get(fail)
14301 .and_then(|metadata| metadata.first())
14302 .expect("explicit-object signature metadata");
14303 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
14304 assert!(
14305 metadata
14306 .callable_arity()
14307 .is_some_and(|arity| arity.accepts(0))
14308 );
14309 }
14310
14311 #[test]
14312 fn trailing_qualifiers_survive_parameter_list_whitespace() {
14313 let source = r#"
14318struct Widget {
14319 bool multiline(int settings, int supprs) const;
14320 bool doublespace(int settings, int supprs) const;
14321 bool noexcept_multiline(int settings, int supprs) noexcept;
14322 bool ref_multiline(int settings, int supprs) &&;
14323};
14324bool
14325Widget::multiline (int settings,
14326 int supprs) const
14327{ return settings + supprs > 0; }
14328bool Widget::doublespace(int settings, int supprs) const { return true; }
14329bool Widget::noexcept_multiline(int settings,
14330 int supprs) noexcept { return true; }
14331bool Widget::ref_multiline(int settings,
14332 int supprs) && { return true; }
14333"#;
14334 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
14335 assert_eq!(
14336 vec!["(int, int) const".to_string()],
14337 identity_signatures(&parsed, "Widget.multiline")
14338 );
14339 assert_eq!(
14340 vec!["(int, int) const".to_string()],
14341 identity_signatures(&parsed, "Widget.doublespace")
14342 );
14343 assert_eq!(
14344 vec!["(int, int) noexcept".to_string()],
14345 identity_signatures(&parsed, "Widget.noexcept_multiline")
14346 );
14347 assert_eq!(
14348 vec!["(int, int) &&".to_string()],
14349 identity_signatures(&parsed, "Widget.ref_multiline")
14350 );
14351 }
14352
14353 #[test]
14354 fn macro_fragmented_plain_class_keeps_following_member_signature() {
14355 let source = r#"
14356struct CString {};
14357class CMessage {
14358public:
14359 CString GetParams(unsigned int index, unsigned int length = -1) const
14360 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
14361 return GetParamsColon(index, length);
14362 }
14363 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
14364};
14365CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
14366 return {};
14367}
14368"#;
14369 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
14370 assert_eq!(
14371 vec!["(unsigned int, unsigned int) const".to_string()],
14372 identity_signatures(&parsed, "CMessage.GetParamsColon")
14373 );
14374 }
14375
14376 #[test]
14377 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
14378 let source = r#"
14379#pragma once
14380#define DEMO_DEPRECATED(message)
14381namespace demo {
14382struct Base {
14383 static int aligned(int value) { return value; }
14384 int legacy(int value) const
14385 DEMO_DEPRECATED("use replacement()") { return value; }
14386 int replacement() const;
14387 void run(int value);
14388};
14389struct OtherBase {
14390 void run(int value);
14391 static int aligned(int value) { return value; }
14392};
14393struct Derived : Base {};
14394struct Override : Base {
14395 void run(int value);
14396 static int aligned(int value) { return value; }
14397};
14398struct RecoveredOverride : Base {
14399 int legacy(int value) const
14400 DEMO_DEPRECATED("use replacement()") { return value; }
14401 void run(int value);
14402};
14403struct Hidden : Base {
14404 void run(int first, int second);
14405 static int aligned(int first, int second) { return first + second; }
14406};
14407struct Ambiguous : Base, OtherBase {};
14408}
14409struct Global {};
14410"#;
14411 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
14412 let declarations = parsed.declarations();
14413 let fq_names = declarations
14414 .iter()
14415 .map(|unit| unit.fq_name())
14416 .collect::<std::collections::BTreeSet<_>>();
14417
14418 for expected in [
14419 "demo.Base",
14420 "demo.Base.aligned",
14421 "demo.Base.legacy",
14422 "demo.Base.replacement",
14423 "demo.Base.run",
14424 "demo.Derived",
14425 "demo.OtherBase",
14426 "demo.Override",
14427 "demo.RecoveredOverride",
14428 "demo.Hidden",
14429 "demo.Ambiguous",
14430 "Global",
14431 ] {
14432 assert!(
14433 fq_names.contains(expected),
14434 "missing {expected} from namespaced macro fragment: {declarations:#?}"
14435 );
14436 }
14437 assert!(
14438 !fq_names.contains("Derived"),
14439 "following class escaped its namespace: {declarations:#?}"
14440 );
14441 assert!(
14442 !fq_names.contains("demo.Global"),
14443 "global class crossed the recovered namespace boundary: {declarations:#?}"
14444 );
14445 }
14446
14447 #[test]
14448 fn trailing_qualifiers_still_separate_genuine_overloads() {
14449 let source = r#"
14452struct Widget {
14453 int* slot(int index);
14454 const int* slot(int index) const;
14455 int log(int severity) &;
14456 int log(int severity) &&;
14457};
14458"#;
14459 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
14460 assert_eq!(
14461 vec!["(int)".to_string(), "(int) const".to_string()],
14462 identity_signatures(&parsed, "Widget.slot")
14463 );
14464 assert_eq!(
14465 vec!["(int) &".to_string(), "(int) &&".to_string()],
14466 identity_signatures(&parsed, "Widget.log")
14467 );
14468 }
14469
14470 #[test]
14471 fn virtual_specifier_is_not_part_of_the_identity_signature() {
14472 let source = r#"
14475struct Base {
14476 virtual void run(int value) const;
14477};
14478struct Widget : Base {
14479 void run(int value) const override;
14480};
14481void Widget::run(int value) const {}
14482"#;
14483 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
14484 assert_eq!(
14485 vec!["(int) const".to_string()],
14486 identity_signatures(&parsed, "Widget.run")
14487 );
14488 }
14489
14490 #[test]
14491 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
14492 let source = r#"
14496struct Widget {
14497 bool value_params(const int settings, const int supprs);
14498 void pointee_const(const int* p);
14499 void pointer_const(int* const p);
14500 void both_const(const int* const p);
14501 void reference_const(const int& p);
14502 void array_const(const int values[4]);
14503};
14504bool Widget::value_params(int settings, int supprs) { return true; }
14505void Widget::pointer_const(int* p) {}
14506void Widget::both_const(const int* p) {}
14507"#;
14508 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
14509 assert_eq!(
14510 vec!["(int, int)".to_string()],
14511 identity_signatures(&parsed, "Widget.value_params")
14512 );
14513 assert_eq!(
14514 vec!["(int *)".to_string()],
14515 identity_signatures(&parsed, "Widget.pointer_const")
14516 );
14517 assert_eq!(
14518 vec!["(const int *)".to_string()],
14519 identity_signatures(&parsed, "Widget.both_const")
14520 );
14521 assert_eq!(
14523 vec!["(const int *)".to_string()],
14524 identity_signatures(&parsed, "Widget.pointee_const")
14525 );
14526 assert_eq!(
14527 vec!["(const int &)".to_string()],
14528 identity_signatures(&parsed, "Widget.reference_const")
14529 );
14530 assert_eq!(
14531 vec!["(const int [4])".to_string()],
14532 identity_signatures(&parsed, "Widget.array_const")
14533 );
14534 }
14535
14536 #[test]
14537 fn top_level_parameter_const_still_separates_pointee_overloads() {
14538 let source = r#"
14539struct Widget {
14540 void take(const int* p);
14541 void take(int* p);
14542};
14543"#;
14544 let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
14545 assert_eq!(
14546 vec!["(const int *)".to_string(), "(int *)".to_string()],
14547 identity_signatures(&parsed, "Widget.take")
14548 );
14549 }
14550
14551 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
14552 let mut parser = tree_sitter::Parser::new();
14553 parser
14554 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14555 .unwrap();
14556 let tree = parser.parse(source, None).unwrap();
14557 let start = source.find(callable_name).expect("callable declaration");
14558 let declarator =
14559 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
14560 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
14561 }
14562
14563 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
14564 let mut shapes = comparable_shapes(source, callable_name);
14565 assert_eq!(1, shapes.len(), "{shapes:?}");
14566 match shapes.remove(0) {
14567 CppComparableSlot::Shape(shape) => shape,
14568 other => panic!("expected a comparable shape, got {other:?}"),
14569 }
14570 }
14571
14572 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
14573 let mut current = shape.root();
14574 loop {
14575 match shape.node(current) {
14576 CppComparableNode::Named { .. } => return shape.node(current),
14577 CppComparableNode::Pointer { inner, .. }
14578 | CppComparableNode::Reference { inner }
14579 | CppComparableNode::Array { inner } => current = *inner,
14580 CppComparableNode::Generic { base, .. } => current = *base,
14581 }
14582 }
14583 }
14584
14585 #[test]
14586 fn comparable_shape_keeps_pointee_const() {
14587 assert_ne!(
14588 sole_comparable_shape("void f(const char* p);", "f("),
14589 sole_comparable_shape("void f(char* p);", "f(")
14590 );
14591 }
14592
14593 #[test]
14594 fn comparable_shape_keeps_inner_pointer_const() {
14595 assert_ne!(
14596 sole_comparable_shape("void f(int** p);", "f("),
14597 sole_comparable_shape("void f(int* const* p);", "f(")
14598 );
14599 }
14600
14601 #[test]
14602 fn comparable_shape_drops_top_level_pointer_const() {
14603 assert_eq!(
14604 sole_comparable_shape("void f(int* const p);", "f("),
14605 sole_comparable_shape("void f(int* p);", "f(")
14606 );
14607 }
14608
14609 #[test]
14610 fn comparable_shape_drops_top_level_base_const() {
14611 assert_eq!(
14612 sole_comparable_shape("void f(const int p);", "f("),
14613 sole_comparable_shape("void f(int p);", "f(")
14614 );
14615 }
14616
14617 #[test]
14618 fn comparable_shape_decays_top_level_array_to_pointer() {
14619 assert_eq!(
14620 sole_comparable_shape("void f(int a[3]);", "f("),
14621 sole_comparable_shape("void f(int* a);", "f(")
14622 );
14623 assert_eq!(
14624 sole_comparable_shape("void f(int* a[3]);", "f("),
14625 sole_comparable_shape("void f(int** a);", "f(")
14626 );
14627 }
14628
14629 #[test]
14630 fn comparable_shape_keeps_array_behind_pointer() {
14631 assert_ne!(
14632 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
14633 sole_comparable_shape("struct S { void f(int** a); };", "f(")
14634 );
14635 }
14636
14637 #[test]
14638 fn comparable_shape_records_written_name_and_lexical_scope() {
14639 let declared =
14640 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
14641 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
14642 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
14643 panic!("named leaf");
14644 };
14645 assert_eq!(["Msg".to_string()].as_slice(), name.path());
14646 assert_eq!(
14647 ["ns".to_string(), "S".to_string()].as_slice(),
14648 name.lexical_scope()
14649 );
14650 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
14651 panic!("named leaf");
14652 };
14653 assert_eq!(
14654 ["ns".to_string(), "Msg".to_string()].as_slice(),
14655 name.path()
14656 );
14657 assert!(name.lexical_scope().is_empty());
14658 assert_ne!(declared, defined);
14659 }
14660
14661 #[test]
14662 fn comparable_shape_marks_sized_primitive_leaf() {
14663 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
14664 let CppComparableNode::Named {
14665 name, primitive, ..
14666 } = comparable_named_leaf(&shape)
14667 else {
14668 panic!("named leaf");
14669 };
14670 assert!(primitive);
14671 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
14672 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
14673 }
14674
14675 #[test]
14676 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
14677 assert_eq!(
14678 vec![CppComparableSlot::Unstructured],
14679 comparable_shapes("void f(void (*cb)(int));", "f(")
14680 );
14681 }
14682
14683 #[test]
14684 fn comparable_shape_reports_ellipsis_slot() {
14685 let shapes = comparable_shapes("void f(int a, ...);", "f(");
14686 assert_eq!(2, shapes.len(), "{shapes:?}");
14687 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
14688 }
14689
14690 #[test]
14691 fn comparable_shape_keeps_template_argument_const() {
14692 assert_ne!(
14693 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
14694 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
14695 );
14696 }
14697
14698 #[test]
14701 fn c_file_mints_aggregate_member_tag_at_file_scope() {
14702 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
14703 let parsed = parse_cpp_declarations(source, "x.c");
14704 let declarations = parsed.declarations();
14705
14706 assert!(
14707 declarations
14708 .iter()
14709 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
14710 "expected a file-scope inner tag, got {declarations:?}"
14711 );
14712 assert!(
14713 declarations
14714 .iter()
14715 .all(|unit| unit.fq_name() != "outer$inner"),
14716 "expected no nested identity, got {declarations:?}"
14717 );
14718 assert!(
14719 declarations
14720 .iter()
14721 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
14722 );
14723 assert!(
14725 declarations
14726 .iter()
14727 .any(|unit| unit.fq_name() == "inner.value")
14728 );
14729 assert!(
14730 declarations
14731 .iter()
14732 .any(|unit| unit.fq_name() == "outer.item")
14733 );
14734
14735 let outer = declarations
14736 .iter()
14737 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
14738 .expect("outer");
14739 assert!(
14740 parsed
14741 .children
14742 .get(outer)
14743 .into_iter()
14744 .flatten()
14745 .all(|child| child.fq_name() != "inner"),
14746 "the tag must not hang off the aggregate it is written inside: {:?}",
14747 parsed.children
14748 );
14749 }
14750
14751 #[test]
14755 fn header_and_cpp_files_keep_nested_tag_identity() {
14756 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
14757 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
14758 let parsed = parse_cpp_declarations(source, name);
14759 let declarations = parsed.declarations();
14760 assert!(
14761 declarations
14762 .iter()
14763 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
14764 "{name} must keep the nested identity, got {declarations:?}"
14765 );
14766 assert!(
14767 declarations.iter().all(|unit| unit.fq_name() != "inner"),
14768 "{name} must not mint a file-scope tag, got {declarations:?}"
14769 );
14770 assert!(
14771 declarations
14772 .iter()
14773 .any(|unit| unit.fq_name() == "outer$inner.value")
14774 );
14775 }
14776 }
14777
14778 #[test]
14780 fn uppercase_c_extension_keeps_cpp_tag_scope() {
14781 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
14782 let parsed = parse_cpp_declarations(source, "x.C");
14783 assert!(
14784 parsed
14785 .declarations()
14786 .iter()
14787 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
14788 );
14789 }
14790
14791 #[test]
14794 fn c_file_mints_every_nesting_level_at_file_scope() {
14795 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
14796 let parsed = parse_cpp_declarations(source, "z.c");
14797 let declarations = parsed.declarations();
14798
14799 for tag in ["a", "b", "c"] {
14800 assert!(
14801 declarations
14802 .iter()
14803 .any(|unit| unit.is_class() && unit.fq_name() == tag),
14804 "expected a file-scope {tag}, got {declarations:?}"
14805 );
14806 }
14807 assert!(
14808 declarations
14809 .iter()
14810 .all(|unit| !unit.fq_name().contains('$')),
14811 "no level may keep a nested identity, got {declarations:?}"
14812 );
14813 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
14815 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
14816 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
14817 }
14818
14819 #[test]
14822 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
14823 let source = "struct outer { enum color { RED, GREEN } c; };\n";
14824 let parsed = parse_cpp_declarations(source, "e.c");
14825 let declarations = parsed.declarations();
14826
14827 let color = declarations
14828 .iter()
14829 .find(|unit| unit.is_class() && unit.fq_name() == "color")
14830 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
14831 assert!(
14832 declarations
14833 .iter()
14834 .all(|unit| unit.fq_name() != "outer$color")
14835 );
14836 for enumerator in ["color.RED", "color.GREEN"] {
14837 assert!(
14838 declarations.iter().any(|unit| unit.fq_name() == enumerator),
14839 "expected {enumerator}, got {declarations:?}"
14840 );
14841 }
14842 let children = parsed
14843 .children
14844 .get(color)
14845 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
14846 assert!(
14847 ["color.RED", "color.GREEN"]
14848 .iter()
14849 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
14850 "enumerators must hang off their enum: {children:?}"
14851 );
14852 }
14853
14854 #[test]
14855 fn c_file_mints_member_list_union_at_file_scope() {
14856 let source = "struct outer { union inner { int a; float b; } item; };\n";
14857 let parsed = parse_cpp_declarations(source, "u.c");
14858 let declarations = parsed.declarations();
14859 assert!(
14860 declarations
14861 .iter()
14862 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
14863 "expected a file-scope inner union, got {declarations:?}"
14864 );
14865 assert!(
14866 declarations
14867 .iter()
14868 .all(|unit| unit.fq_name() != "outer$inner")
14869 );
14870 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
14871 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
14872 }
14873
14874 #[test]
14877 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
14878 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
14879 let parsed = parse_cpp_declarations(source, "n.c");
14880 let declarations = parsed.declarations();
14881 let inner = declarations
14882 .iter()
14883 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
14884 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
14885 assert_eq!(inner.package_name(), "ns");
14886 assert!(
14887 declarations
14888 .iter()
14889 .all(|unit| unit.fq_name() != "ns.outer$inner")
14890 );
14891 }
14892
14893 #[test]
14898 fn function_local_tags_are_unchanged_in_both_dialects() {
14899 let source =
14900 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
14901 for name in ["y.c", "y.cpp"] {
14902 let parsed = parse_cpp_declarations(source, name);
14903 let declarations = parsed.declarations();
14904 assert!(
14905 declarations
14906 .iter()
14907 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
14908 "{name}: {declarations:?}"
14909 );
14910 for tag in ["localtag", "deeper", "localtag$deeper"] {
14911 assert!(
14912 declarations.iter().all(|unit| unit.fq_name() != tag),
14913 "{name} must not mint {tag}, got {declarations:?}"
14914 );
14915 }
14916 }
14917 }
14918
14919 #[test]
14922 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
14923 let source = "typedef struct { int v; } T;\n";
14924 for name in ["t.c", "t.cpp"] {
14925 let parsed = parse_cpp_declarations(source, name);
14926 let declarations = parsed.declarations();
14927 assert!(
14928 declarations
14929 .iter()
14930 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
14931 "{name}: {declarations:?}"
14932 );
14933 }
14934 }
14935
14936 #[test]
14937 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
14938 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
14939 let parsed = parse_cpp_declarations(source, "socket.c");
14940 let declarations = parsed.declarations();
14941 assert_eq!(
14942 declarations
14943 .iter()
14944 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
14945 .count(),
14946 1,
14947 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
14948 );
14949 for expected in [
14950 "PAL_HANDLE",
14951 "PAL_HANDLE.sock",
14952 "PAL_HANDLE$sock",
14953 "PAL_HANDLE$sock.ops",
14954 ] {
14955 assert!(
14956 declarations.iter().any(|unit| unit.fq_name() == expected),
14957 "expected {expected}, got {declarations:?}"
14958 );
14959 }
14960 }
14961
14962 #[test]
14965 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
14966 let source = "class outer { class inner { int v; }; };\n";
14967 let c_parsed = parse_cpp_declarations(source, "k.c");
14968 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
14969 let c_declarations = c_parsed.declarations();
14970 let cpp_declarations = cpp_parsed.declarations();
14971 assert!(
14972 c_declarations
14973 .iter()
14974 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
14975 "{c_declarations:?}"
14976 );
14977 assert_eq!(
14978 c_declarations
14979 .iter()
14980 .map(|unit| unit.fq_name())
14981 .collect::<std::collections::BTreeSet<_>>(),
14982 cpp_declarations
14983 .iter()
14984 .map(|unit| unit.fq_name())
14985 .collect::<std::collections::BTreeSet<_>>()
14986 );
14987 }
14988}