1use crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::{MacroReplacementField, ObjectMacroReplacement};
9use brokk_bifrost_core::analyzer::common::{
10 node_source_text, parse_source_ranges_with_cancellation, parse_source_region,
11};
12use brokk_bifrost_core::analyzer::fq_name::{
13 FqName, SegmentId, SegmentKind, joined_segments, normalize_joined, segment_interner,
14};
15use brokk_bifrost_core::analyzer::model::{
16 CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
17 CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
18 CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
19 ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
20 StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
21};
22use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
23use brokk_bifrost_core::analyzer::structural::adapter_helpers::node_range;
24use brokk_bifrost_core::analyzer::structural::materialization::{
25 GenerationKind, MaterializationRecord,
26};
27use brokk_bifrost_core::analyzer::structural::resolution::DeclaredVisibility;
28use brokk_bifrost_core::analyzer::symbol_path::parse_symbol_path;
29use brokk_bifrost_core::analyzer::tree_walk::{
30 NodeKindIds, ParentIndex, WalkControl, children_iter, named_children_iter,
31 push_children_reversed, push_named_children_reversed, walk_named_tree_preorder,
32};
33use brokk_bifrost_core::analyzer::{CodeUnit, Language, ProjectFile};
34use brokk_bifrost_core::hash::{HashMap, HashSet};
35use regex::Regex;
36use tree_sitter::{Node, Parser, Tree};
37
38fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
40 segment_interner().intern(text, kind)
41}
42
43fn cpp_push_package(fq: &mut FqName, package_name: &str) {
51 for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
52 fq.push(cpp_segment(component, SegmentKind::Package));
53 }
54}
55
56const CPP_PACKAGE_SEPARATOR: &str = "::";
58
59fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
66 let mut first = true;
67 for component in chain.split('$').filter(|c| !c.is_empty()) {
71 let kind = if first {
72 SegmentKind::Type
73 } else {
74 SegmentKind::Nested
75 };
76 fq.push(cpp_segment(component, kind));
77 first = false;
78 }
79}
80
81fn cpp_namespace_fq(full_name: &str) -> FqName {
85 let mut fq = FqName::new();
86 cpp_push_package(&mut fq, full_name);
87 fq
88}
89
90fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
106 let mut components = Vec::new();
107 let mut stack = vec![node];
108 while let Some(current) = stack.pop() {
109 match current.kind() {
110 "namespace_identifier" | "identifier" => {
111 components.push(normalize_cpp_whitespace(node_text(current, source)));
112 }
113 "nested_namespace_specifier" => {
114 for index in (0..current.named_child_count()).rev() {
115 stack.push(
116 current
117 .named_child(index)
118 .expect("index below the node's own named child count"),
119 );
120 }
121 }
122 _ => return cpp_raw_namespace_name_components(node, source),
123 }
124 }
125 if components.iter().any(String::is_empty) {
126 return cpp_raw_namespace_name_components(node, source);
127 }
128 components
129}
130
131fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
145 let start = node
146 .child(0)
147 .filter(|child| !child.is_named() && child.kind() == "::")
148 .map_or(node.start_byte(), |marker| marker.end_byte());
149 let text = normalize_cpp_whitespace(
150 source
151 .get(start..node.end_byte())
152 .expect("namespace name node covers one source range"),
153 );
154 let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
155 if text.is_empty() {
156 return Vec::new();
157 }
158 vec![text]
159}
160
161fn cpp_lexical_namespace_name<'tree>(
167 node: Node<'tree>,
168 source: &str,
169 ancestry: &ParentIndex<'tree>,
170) -> Option<String> {
171 let mut components = Vec::new();
172 let mut ancestor = ancestry.parent(node);
173 while let Some(current) = ancestor {
174 if current.kind() == "namespace_definition" {
175 let name_node = current.child_by_field_name("name")?;
176 let name = normalize_cpp_whitespace(node_text(name_node, source));
177 if name.is_empty() {
178 return None;
179 }
180 components.push(name);
181 }
182 ancestor = ancestry.parent(current);
183 }
184 if components.is_empty() {
185 return None;
186 }
187 components.reverse();
188 Some(
192 normalize_joined(
193 &components.join(CPP_PACKAGE_SEPARATOR),
194 CPP_PACKAGE_SEPARATOR,
195 )
196 .into_owned(),
197 )
198}
199
200fn cpp_join_nested_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_join_member_short(parent_short: &str, name: &str) -> String {
216 if parent_short.is_empty() {
217 name.to_string()
218 } else {
219 format!("{parent_short}.{name}")
220 }
221}
222
223fn cpp_leaf_fq(
230 package_name: &str,
231 parent: Option<&CodeUnit>,
232 name: &str,
233 kind_if_nested: SegmentKind,
234 kind_if_top: SegmentKind,
235) -> FqName {
236 if let Some(parent) = parent {
237 parent
238 .fq()
239 .clone()
240 .with_pushed(cpp_segment(name, kind_if_nested))
241 } else {
242 let mut fq = FqName::new();
243 cpp_push_package(&mut fq, package_name);
244 fq.push(cpp_segment(name, kind_if_top));
245 fq
246 }
247}
248
249pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
256 let mut fq = FqName::new();
257 cpp_push_package(&mut fq, package_name);
258 match short_name.rsplit_once('.') {
259 Some((owner_chain, member)) => {
260 cpp_push_type_chain(&mut fq, owner_chain);
261 fq.push(cpp_segment(member, SegmentKind::Member));
262 }
263 None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
264 }
265 fq
266}
267
268pub fn cpp_rendered_name_fq(rendered: &str) -> FqName {
283 let mut fq = FqName::new();
284 for component in parse_symbol_path(Language::Cpp, rendered) {
285 let mut first = true;
289 for part in component.split('$').filter(|part| !part.is_empty()) {
290 let kind = if first {
291 SegmentKind::Unknown
294 } else {
295 SegmentKind::Nested
296 };
297 fq.push(cpp_segment(part, kind));
298 first = false;
299 }
300 }
301 fq
302}
303
304pub fn cpp_colon_source_aliases(rendered: &str) -> Vec<FqName> {
318 if !rendered.contains("::") || rendered.contains('.') || rendered.contains('$') {
319 return Vec::new();
320 }
321 let texts = parse_symbol_path(Language::Cpp, rendered);
322 if texts.len() < 2 {
323 return Vec::new();
324 }
325 cpp_qualified_kind_interpretations(&texts)
326}
327
328fn cpp_qualified_kind_interpretations(texts: &[String]) -> Vec<FqName> {
332 let len = texts.len();
333 let mut names = Vec::new();
334 names.push(cpp_fq_with_kinds(texts, vec![SegmentKind::Package; len]));
335 for packages in 0..len {
336 let rest = len - packages;
337 let mut type_chain = Vec::with_capacity(len);
338 type_chain.extend(std::iter::repeat_n(SegmentKind::Package, packages));
339 type_chain.push(SegmentKind::Type);
340 type_chain.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 1));
341 names.push(cpp_fq_with_kinds(texts, type_chain));
342 if rest >= 2 {
343 let mut member = Vec::with_capacity(len);
344 member.extend(std::iter::repeat_n(SegmentKind::Package, packages));
345 member.push(SegmentKind::Type);
346 member.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 2));
347 member.push(SegmentKind::Member);
348 names.push(cpp_fq_with_kinds(texts, member));
349 } else if packages >= 1 {
350 let mut function = vec![SegmentKind::Package; packages];
351 function.push(SegmentKind::Member);
352 names.push(cpp_fq_with_kinds(texts, function));
353 }
354 }
355 names
356}
357
358fn cpp_fq_with_kinds(texts: &[String], kinds: Vec<SegmentKind>) -> FqName {
359 assert_eq!(
360 texts.len(),
361 kinds.len(),
362 "a qualified interpretation names every segment"
363 );
364 let mut fq = FqName::new();
365 for (text, kind) in texts.iter().zip(kinds) {
366 fq.push(cpp_segment(text, kind));
367 }
368 fq
369}
370
371#[derive(Clone)]
372pub struct ScopeInfo {
373 package_name: String,
374 module: Option<CodeUnit>,
375 class_unit: Option<CodeUnit>,
376 template_signature: Option<String>,
377 template_metadata: Option<CppTemplateMetadata>,
378 declarations_are_fields: bool,
379 recovered_specialization_member_scope: bool,
380 visible_using_namespaces: Vec<String>,
392}
393
394struct CppContainer<'tree> {
395 node: Node<'tree>,
396 scope: ScopeInfo,
397}
398
399struct CppNodeWork<'tree> {
400 node: Node<'tree>,
401 scope: ScopeInfo,
402}
403
404struct CppSiblingsWork<'tree> {
411 children: std::vec::IntoIter<Node<'tree>>,
412 scope: ScopeInfo,
413}
414
415enum CppWork<'tree> {
416 Container(CppContainer<'tree>),
417 Node(CppNodeWork<'tree>),
418 Siblings(CppSiblingsWork<'tree>),
419}
420
421fn class_like_name<'tree>(
422 node: Node<'tree>,
423 source: &str,
424 ancestry: &ParentIndex<'tree>,
425) -> Option<String> {
426 let best = class_like_name_from_children(node, source);
427 if let Some(parent) = ancestry.parent(node)
428 && matches!(
429 parent.kind(),
430 "declaration" | "field_declaration" | "function_definition"
431 )
432 && cpp_body_node(node).is_none()
441 && node
442 .child_by_field_name("name")
443 .map(|name_node| {
444 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
445 })
446 .unwrap_or(false)
447 && let Some(recovered) = exported_class_name_from_node(parent, source)
448 && best.as_deref() != Some(recovered.as_str())
449 {
450 return Some(recovered);
451 }
452 best.or_else(|| {
453 node.child_by_field_name("name")
454 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
455 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
456 })
457}
458
459fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
460 let mut grammar_name = None;
461 if let Some(name_node) = node.child_by_field_name("name") {
462 let name = normalize_cpp_whitespace(node_text(name_node, source));
463 if name.is_empty() {
464 return None;
465 }
466 if !cpp_export_macro_token(&name) {
467 return Some(name);
468 }
469 grammar_name = Some(name);
470 }
471
472 let mut best = None;
473 let mut cursor = node.walk();
474 let mut stack = Vec::new();
475 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
476 if matches!(
477 child.kind(),
478 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
479 ) {
480 break;
481 }
482 stack.push(child);
483 }
484
485 while let Some(current) = stack.pop() {
486 if matches!(current.kind(), "type_identifier" | "identifier") {
487 let name = normalize_cpp_whitespace(node_text(current, source));
488 if !name.is_empty() && !cpp_export_macro_token(&name) {
489 best = Some(name);
490 }
491 continue;
492 }
493
494 push_named_children_reversed(current, &mut stack);
495 }
496 best.or(grammar_name)
497}
498
499pub fn cpp_export_macro_token(token: &str) -> bool {
500 token
501 .chars()
502 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
503}
504
505struct RecoveredExportedClass<'tree> {
506 declaration_node: Node<'tree>,
507 name: String,
508 body: Option<Node<'tree>>,
509 raw_supertypes: Option<Vec<String>>,
510 uses_initializer_body: bool,
511 fragmented_body: Option<FragmentedExportBody>,
516}
517
518struct RecoveredFunctionLikeExportClassPair {
519 name: String,
520 range: Range,
521 raw_supertypes: Option<Vec<String>>,
522 fragmented_body: FragmentedExportBody,
523}
524
525struct RecoveredEmbeddedFunctionLikeExportClass {
526 name: String,
527 range: Range,
528 raw_supertypes: Vec<String>,
529 fragmented_body: FragmentedExportBody,
530}
531
532struct FragmentedExportBody {
538 reparse_start: usize,
539 reparse_end: usize,
540 class_range: Range,
541}
542
543fn recovered_fragmented_export_body(
544 body: Node<'_>,
545 class_range: Range,
546) -> Option<FragmentedExportBody> {
547 let open = body.child(0).filter(|child| child.kind() == "{")?;
548 let close = body
549 .child(body.child_count().saturating_sub(1))
550 .filter(|child| child.kind() == "}" && !child.is_missing());
551 Some(FragmentedExportBody {
552 reparse_start: open.end_byte(),
553 reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
558 class_range,
559 })
560}
561
562struct DisplacedFragmentNamespaceBoundary<'tree> {
563 class_close: Node<'tree>,
564 class_semicolon: Node<'tree>,
565 namespace_items: Vec<Node<'tree>>,
566}
567
568enum FragmentedExportMembers {
573 Complete(Tree),
574 ConditionalConstructor(Tree),
575}
576
577#[derive(Clone, Copy)]
578struct DisplacedMacroClassTail {
579 split_index: usize,
580 class_range: Range,
581}
582
583fn recover_exported_class_declaration<'tree>(
584 node: Node<'tree>,
585 source: &str,
586) -> Option<RecoveredExportedClass<'tree>> {
587 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
588 return Some(recovered);
589 }
590
591 let class_node = first_class_like_child(node)?;
592 if let Some(name_node) = class_node.child_by_field_name("name") {
593 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
594 if cpp_export_macro_token(&class_name) {
595 let mut cursor = node.walk();
599 if node
600 .children_by_field_name("declarator", &mut cursor)
601 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
602 {
603 return None;
604 }
605 } else if has_direct_cpp_declarator(node) {
606 return None;
607 }
608 }
609 let name = exported_class_name_from_node(class_node, source)?;
610 Some(RecoveredExportedClass {
611 declaration_node: class_node,
612 name,
613 body: cpp_body_node(class_node),
614 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
615 .then(|| extract_cpp_supertypes(class_node, source)),
616 uses_initializer_body: false,
617 fragmented_body: None,
618 })
619}
620
621fn recover_malformed_exported_base_class<'tree>(
622 node: Node<'tree>,
623 source: &str,
624) -> Option<RecoveredExportedClass<'tree>> {
625 if node.kind() != "declaration" {
626 return None;
627 }
628 let class_node = node.child_by_field_name("type")?;
629 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
630 return None;
631 }
632 let macro_name = class_node
633 .child_by_field_name("name")
634 .and_then(|name| direct_identifier_name(name, source))?;
635 if !cpp_export_macro_token(¯o_name) {
636 return None;
637 }
638
639 let mut named_cursor = node.walk();
640 let mut named = node.named_children(&mut named_cursor);
641 if named
642 .next()
643 .is_none_or(|child| !same_node(child, class_node))
644 {
645 return None;
646 }
647 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
648 if displaced.kind() != "ERROR" {
649 return None;
650 }
651 let name = displaced_exported_class_name(displaced, source)?;
652
653 let remaining = named.collect::<Vec<_>>();
654 let init = *remaining.last()?;
655 if init.kind() != "init_declarator" {
656 return None;
657 }
658 let final_base = init
659 .child_by_field_name("declarator")
660 .and_then(|base| recovered_malformed_base_name(base, source))?;
661 let body = init.child_by_field_name("value")?;
662 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
666 return None;
667 }
668
669 if remaining[..remaining.len() - 1]
670 .iter()
671 .any(|child| match child.kind() {
672 "qualified_identifier"
673 | "scoped_type_identifier"
674 | "type_identifier"
675 | "identifier" => false,
676 "ERROR" => !is_malformed_inheritance_access(*child, source),
677 _ => true,
678 })
679 {
680 return None;
681 }
682
683 let mut raw_supertypes = Vec::new();
684 for base in &remaining[..remaining.len() - 1] {
685 if base.kind() == "ERROR" {
686 continue;
687 }
688 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
689 }
690 raw_supertypes.push(final_base);
691
692 Some(RecoveredExportedClass {
693 declaration_node: node,
694 name,
695 body: Some(body),
696 raw_supertypes: Some(raw_supertypes),
697 uses_initializer_body: true,
698 fragmented_body: fragmented_export_body_region(node, body, source),
699 })
700}
701
702fn fragmented_export_body_region(
718 node: Node<'_>,
719 body: Node<'_>,
720 source: &str,
721) -> Option<FragmentedExportBody> {
722 let reparse_start = body.start_byte() + 1;
723 let close = direct_close_brace(body)?;
724 if close.end_byte() > close.start_byte() {
725 return Some(FragmentedExportBody {
726 reparse_start,
727 reparse_end: close.start_byte(),
728 class_range: cpp_declaration_range(node),
729 });
730 }
731 let mut sibling = node.next_named_sibling();
734 let displaced_close = loop {
735 let Some(current) = sibling else {
736 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
737 };
738 if cpp_is_stray_close_brace(current, source) {
739 break current;
740 }
741 sibling = current.next_named_sibling();
742 };
743 Some(FragmentedExportBody {
744 reparse_start,
745 reparse_end: displaced_close.start_byte(),
746 class_range: Range {
747 start_byte: node.start_byte(),
748 end_byte: displaced_close.end_byte(),
749 start_line: node.start_position().row + 1,
750 end_line: displaced_close.end_position().row + 1,
751 },
752 })
753}
754
755fn fragmented_export_function_body_region(
763 node: Node<'_>,
764 body: Node<'_>,
765 source: &str,
766 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
767) -> Option<FragmentedExportBody> {
768 let reparse_start = body.start_byte().checked_add(1)?;
769 if let Some(boundary) = displaced_namespace {
770 return Some(FragmentedExportBody {
771 reparse_start,
772 reparse_end: boundary.class_close.start_byte(),
773 class_range: Range {
774 start_byte: node.start_byte(),
775 end_byte: boundary.class_semicolon.end_byte(),
776 start_line: node.start_position().row + 1,
777 end_line: boundary.class_semicolon.end_position().row + 1,
778 },
779 });
780 }
781 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
782 let boundary =
783 fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
784 let boundary_index = boundary.and_then(|boundary| {
785 siblings
786 .iter()
787 .position(|candidate| same_node(*candidate, boundary))
788 });
789 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
790 let mut sibling_index = 0;
791 while let Some(current) = siblings.get(sibling_index).copied() {
804 if current.kind() == "comment" {
805 sibling_index += 1;
806 continue;
807 }
808 if is_trailing_attribute_macro_sibling(current) {
809 sibling_index += 1;
810 continue;
811 }
812 if cpp_is_stray_semicolon(current, source) {
813 return None;
814 }
815 break;
816 }
817 while let Some(current) = siblings.get(sibling_index).copied() {
818 let next = siblings.get(sibling_index + 1).copied();
819 if cpp_is_stray_close_brace(current, source)
820 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
821 {
822 let semicolon = next.expect("checked above");
823 return Some(FragmentedExportBody {
824 reparse_start,
825 reparse_end: current.start_byte(),
826 class_range: Range {
827 start_byte: node.start_byte(),
828 end_byte: semicolon.end_byte(),
829 start_line: node.start_position().row + 1,
830 end_line: semicolon.end_position().row + 1,
831 },
832 });
833 }
834 if current.start_byte() >= body.end_byte()
841 && let Some(close) = cpp_nested_stray_close_brace(current, source)
842 {
843 return Some(FragmentedExportBody {
844 reparse_start,
845 reparse_end: close.start_byte(),
846 class_range: Range {
847 start_byte: node.start_byte(),
848 end_byte: current.end_byte(),
849 start_line: node.start_position().row + 1,
850 end_line: current.end_position().row + 1,
851 },
852 });
853 }
854 sibling_index += 1;
855 }
856 boundary.map(|boundary| FragmentedExportBody {
857 reparse_start,
858 reparse_end: boundary.start_byte(),
859 class_range: Range {
860 start_byte: node.start_byte(),
861 end_byte: boundary.start_byte(),
862 start_line: node.start_position().row + 1,
863 end_line: boundary.start_position().row + 1,
864 },
865 })
866}
867
868fn fragmented_export_sibling_class_boundary<'tree>(
873 node: Node<'tree>,
874 source: &str,
875 ancestry: &ParentIndex<'tree>,
876) -> Option<Node<'tree>> {
877 let node_parent = ancestry.parent(node)?;
878 cpp_following_named_siblings(node, source, ancestry)
879 .into_iter()
880 .find(|candidate| {
881 recover_exported_class_function_definition(*candidate, source).is_some()
882 && ancestry
883 .parent(*candidate)
884 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
885 })
886}
887
888fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
893 if node.kind() != "expression_statement" {
894 return false;
895 }
896 let mut cursor = node.walk();
897 let mut children = node.named_children(&mut cursor);
898 children
899 .next()
900 .is_some_and(|child| child.kind() == "identifier")
901 && children.next().is_none()
902}
903
904fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
910 let mut stack = vec![node];
911 while let Some(current) = stack.pop() {
912 if cpp_is_stray_close_brace(current, source) {
913 return Some(current);
914 }
915 let mut cursor = current.walk();
916 stack.extend(current.named_children(&mut cursor));
917 }
918 None
919}
920
921fn cpp_following_named_siblings<'tree>(
935 node: Node<'tree>,
936 source: &str,
937 ancestry: &ParentIndex<'tree>,
938) -> Vec<Node<'tree>> {
939 let mut siblings = Vec::new();
940 let mut anchor = node;
941 while let Some(parent) = ancestry.parent(anchor) {
942 let at_translation_unit = parent.kind() == "translation_unit";
943 let mut cursor = parent.walk();
944 assert!(
945 cursor.goto_first_child(),
946 "ParentIndex returned a parent without children"
947 );
948 while !same_node(cursor.node(), anchor) {
949 assert!(
950 cursor.goto_next_sibling(),
951 "ParentIndex parent does not contain its child anchor"
952 );
953 }
954 while cursor.goto_next_sibling() {
955 let current = cursor.node();
956 if !current.is_named() {
957 continue;
958 }
959 if at_translation_unit
960 && (current.kind() == "namespace_definition"
961 || (current.kind() == "function_definition"
962 && first_class_like_child(current).is_some()))
963 {
964 return siblings;
965 }
966 siblings.push(current);
967 if cpp_is_stray_close_brace(current, source) {
968 if let Some(semicolon) = current
969 .next_named_sibling()
970 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
971 {
972 siblings.push(semicolon);
973 }
974 return siblings;
975 }
976 if current.start_byte() >= node.end_byte()
977 && matches!(current.kind(), "ERROR" | "labeled_statement")
978 && cpp_nested_stray_close_brace(current, source).is_some()
979 {
980 return siblings;
981 }
982 }
983 anchor = parent;
984 }
985 siblings
986}
987
988fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
989 if node.start_byte() >= class_end {
990 return false;
991 }
992 node.end_byte() <= class_end
993 || cpp_nested_stray_close_brace(node, source)
994 .is_some_and(|close| close.start_byte() == class_end)
995}
996
997struct FragmentedClassRecovery<'tree> {
1001 declaration_node: Node<'tree>,
1002 name: String,
1003 raw_supertypes: Vec<String>,
1004 body: FragmentedExportBody,
1005}
1006
1007fn fragmented_class_body<'tree>(
1019 node: Node<'tree>,
1020 source: &str,
1021) -> Option<FragmentedClassRecovery<'tree>> {
1022 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
1023 return Some(recovered);
1024 }
1025 let supported_container = node.kind() == "ERROR"
1026 || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
1027 if !supported_container {
1028 return None;
1029 }
1030 let mut cursor = node.walk();
1031 let children = node.children(&mut cursor).collect::<Vec<_>>();
1032 if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
1033 return Some(recovered);
1034 }
1035 let keyword = children.first()?;
1036 if !matches!(keyword.kind(), "class" | "struct" | "union") {
1037 return None;
1038 }
1039 let name_node = children
1040 .iter()
1041 .copied()
1042 .skip(1)
1043 .find(|child| child.is_named())?;
1044 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
1045 return None;
1046 }
1047 let name = normalize_cpp_whitespace(node_text(name_node, source));
1048 if name.is_empty() || cpp_export_macro_token(&name) {
1049 return None;
1050 }
1051 let open_index = children.iter().position(|child| child.kind() == "{")?;
1052 Some(FragmentedClassRecovery {
1053 declaration_node: node,
1054 name,
1055 raw_supertypes: extract_cpp_supertypes(node, source),
1056 body: fragmented_displaced_class_body(node, &children, open_index, source)?,
1057 })
1058}
1059
1060fn fragmented_export_macro_class_body<'tree>(
1071 node: Node<'tree>,
1072 children: &[Node<'tree>],
1073 source: &str,
1074) -> Option<FragmentedClassRecovery<'tree>> {
1075 let class_node = *children.first()?;
1076 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1077 return None;
1078 }
1079 class_node
1083 .child_by_field_name("name")
1084 .and_then(|name| direct_identifier_name(name, source))?;
1085 let invocation = *children.get(1)?;
1086 if invocation.is_named() || invocation.kind() != "(" {
1087 return None;
1088 }
1089 let open_index = children
1090 .iter()
1091 .position(|child| !child.is_named() && child.kind() == "{")?;
1092 let open = children[open_index];
1093 let name_node = recovered_export_head_name(node, open, source)?;
1094 let name = normalize_cpp_whitespace(node_text(name_node, source));
1095 if name.is_empty() || cpp_export_macro_token(&name) {
1096 return None;
1097 }
1098 Some(FragmentedClassRecovery {
1099 declaration_node: node,
1100 name,
1101 raw_supertypes: recovered_export_head_bases(
1102 node,
1103 name_node.end_byte(),
1104 open.start_byte(),
1105 source,
1106 ),
1107 body: fragmented_displaced_class_body(node, children, open_index, source)
1108 .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1109 })
1110}
1111
1112fn fragmented_displaced_class_body(
1118 node: Node<'_>,
1119 children: &[Node<'_>],
1120 open_index: usize,
1121 source: &str,
1122) -> Option<FragmentedExportBody> {
1123 let open = children[open_index];
1124 let nested_class_opens = children[open_index + 1..]
1125 .iter()
1126 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1127 .count();
1128 let mut closes_remaining = 1 + nested_class_opens;
1129 let mut sibling = node.next_named_sibling();
1130 while let Some(candidate) = sibling {
1131 let next = candidate.next_named_sibling();
1132 if cpp_is_stray_close_brace(candidate, source) {
1133 closes_remaining -= 1;
1134 if closes_remaining == 0 {
1135 let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
1136 if open.end_byte() >= candidate.start_byte() {
1137 return None;
1138 }
1139 return Some(FragmentedExportBody {
1140 reparse_start: open.end_byte(),
1141 reparse_end: candidate.start_byte(),
1142 class_range: Range {
1143 start_byte: node.start_byte(),
1144 end_byte: semicolon.end_byte(),
1145 start_line: node.start_position().row + 1,
1146 end_line: semicolon.end_position().row + 1,
1147 },
1148 });
1149 }
1150 }
1151 sibling = next;
1152 }
1153 None
1154}
1155
1156fn fragmented_container_close_class_body(
1164 node: Node<'_>,
1165 open: Node<'_>,
1166 source: &str,
1167) -> Option<FragmentedExportBody> {
1168 let parent = node.parent()?;
1169 if !matches!(
1170 parent.kind(),
1171 "declaration_list" | "field_declaration_list" | "compound_statement"
1172 ) {
1173 return None;
1174 }
1175 let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1176 if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1181 || open.end_byte() >= close.start_byte()
1182 {
1183 return None;
1184 }
1185 Some(FragmentedExportBody {
1186 reparse_start: open.end_byte(),
1187 reparse_end: close.start_byte(),
1188 class_range: Range {
1189 start_byte: node.start_byte(),
1190 end_byte: close.end_byte(),
1191 start_line: node.start_position().row + 1,
1192 end_line: close.end_position().row + 1,
1193 },
1194 })
1195}
1196
1197pub(crate) fn recovered_fragmented_class_has_body(
1198 node: Node<'_>,
1199 source: &str,
1200 expected_name: &str,
1201 expected_range: &Range,
1202) -> bool {
1203 fragmented_class_body(node, source).is_some_and(|recovered| {
1204 recovered.name == expected_name
1205 && recovered.body.class_range.start_byte == expected_range.start_byte
1206 && recovered.body.class_range.end_byte == expected_range.end_byte
1207 })
1208}
1209
1210fn fragmented_plain_class_declaration_body<'tree>(
1217 node: Node<'tree>,
1218 source: &str,
1219) -> Option<FragmentedClassRecovery<'tree>> {
1220 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1221 return None;
1222 }
1223 let class_node = node.child_by_field_name("type")?;
1224 if !matches!(
1225 class_node.kind(),
1226 "class_specifier" | "struct_specifier" | "union_specifier"
1227 ) {
1228 return None;
1229 }
1230 let name_node = class_node.child_by_field_name("name")?;
1231 let name = normalize_cpp_whitespace(node_text(name_node, source));
1232 if name.is_empty() || cpp_export_macro_token(&name) {
1233 return None;
1234 }
1235 let body = cpp_body_node(class_node)?;
1236 if body.kind() != "field_declaration_list" {
1237 return None;
1238 }
1239 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1240 if declarator.start_byte() < class_node.end_byte() {
1241 return None;
1242 }
1243 let mut cursor = node.walk();
1244 node.named_children(&mut cursor).any(|child| {
1245 if child.kind() != "ERROR"
1246 || child.start_byte() < class_node.end_byte()
1247 || child.end_byte() > declarator.start_byte()
1248 {
1249 return false;
1250 }
1251 let mut cursor = child.walk();
1252 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1253 let Some((return_type, attributes)) = components.split_last() else {
1254 return false;
1255 };
1256 matches!(
1257 return_type.kind(),
1258 "identifier"
1259 | "type_identifier"
1260 | "primitive_type"
1261 | "decltype"
1262 | "placeholder_type_specifier"
1263 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1264 && attributes.iter().all(|attribute| {
1265 matches!(attribute.kind(), "identifier" | "type_identifier")
1266 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1267 *attribute, source,
1268 )))
1269 })
1270 })
1271 } else {
1272 let mut cursor = node.walk();
1273 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1274 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1275 if same_node(*candidate_class, class_node)
1276 && continuation.kind() == "identifier"
1277 && node_text(*continuation, source) == "else"
1278 && continuation_body.kind() == "compound_statement"
1279 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1280 && continuation_body
1281 .child(continuation_body.child_count().saturating_sub(1))
1282 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1283 };
1284 if !displaced_member {
1285 return None;
1286 }
1287 let open = body
1288 .children(&mut body.walk())
1289 .find(|child| child.kind() == "{")?;
1290 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1291 let ordinary_boundary =
1292 siblings
1293 .iter()
1294 .copied()
1295 .enumerate()
1296 .find_map(|(close_index, close)| {
1297 cpp_is_stray_close_brace(close, source)
1298 .then(|| {
1299 siblings
1300 .get(close_index + 1)
1301 .copied()
1302 .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1303 .map(|semicolon| (close, semicolon))
1304 })
1305 .flatten()
1306 });
1307 let (close, semicolon) =
1308 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1309 (boundary.class_close, boundary.class_semicolon)
1310 } else {
1311 ordinary_boundary?
1312 };
1313 if open.end_byte() >= close.start_byte() {
1314 return None;
1315 }
1316 Some(FragmentedClassRecovery {
1317 declaration_node: class_node,
1318 name,
1319 raw_supertypes: extract_cpp_supertypes(class_node, source),
1320 body: FragmentedExportBody {
1321 reparse_start: open.end_byte(),
1322 reparse_end: close.start_byte(),
1323 class_range: Range {
1324 start_byte: class_node.start_byte(),
1325 end_byte: semicolon.end_byte(),
1326 start_line: class_node.start_position().row + 1,
1327 end_line: semicolon.end_position().row + 1,
1328 },
1329 },
1330 })
1331}
1332
1333fn displaced_export_function_namespace_shape<'tree>(
1334 declaration: Node<'tree>,
1335 source: &str,
1336) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1337 let mut nested = Vec::new();
1338 for index in (0..declaration.named_child_count()).rev() {
1339 nested.push(declaration.named_child(index)?);
1340 }
1341 while let Some(current) = nested.pop() {
1342 if recover_exported_class_function_definition(current, source).is_some() {
1348 return None;
1349 }
1350 for index in (0..current.named_child_count()).rev() {
1351 nested.push(current.named_child(index)?);
1352 }
1353 }
1354 let mut same_envelope_sibling = declaration.next_named_sibling();
1355 while let Some(current) = same_envelope_sibling {
1356 if recover_exported_class_function_definition(current, source).is_some() {
1357 return None;
1358 }
1359 same_envelope_sibling = current.next_named_sibling();
1360 }
1361 let declaration_list = declaration.parent()?;
1362 if declaration_list.kind() != "declaration_list" {
1363 return None;
1364 }
1365 let namespace = declaration_list.parent()?;
1366 if namespace.kind() != "namespace_definition"
1367 || namespace.child_by_field_name("body") != Some(declaration_list)
1368 {
1369 return None;
1370 }
1371 let class_close = direct_close_brace(declaration_list)?;
1372 let trailing_semicolon = namespace.next_named_sibling()?;
1373 if trailing_semicolon.kind() != "expression_statement"
1374 || trailing_semicolon.named_child_count() != 0
1375 {
1376 return None;
1377 }
1378 let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1384 let trailing_index = siblings
1385 .iter()
1386 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1387 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1388 recover_exported_class_function_definition(*candidate, source).is_some()
1389 }) {
1390 return None;
1395 }
1396 let mut namespace_items = Vec::new();
1397 let mut nested_fragment_end = 0;
1398 for current in siblings.into_iter().skip(trailing_index + 1) {
1399 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1400 {
1401 return Some(DisplacedFragmentNamespaceBoundary {
1402 class_close,
1403 class_semicolon: trailing_semicolon,
1404 namespace_items,
1405 });
1406 }
1407 if current.start_byte() >= nested_fragment_end
1408 && let Some(recovered) = fragmented_class_body(current, source)
1409 {
1410 nested_fragment_end = recovered.body.class_range.end_byte;
1411 } else if current.start_byte() >= nested_fragment_end
1412 && recover_exported_class_function_definition(current, source).is_some()
1413 && let Some(body) = cpp_body_node(current)
1414 && let Some(fragmented) =
1415 fragmented_export_function_body_region(current, body, source, None)
1416 {
1417 nested_fragment_end = fragmented.class_range.end_byte;
1418 }
1419 namespace_items.push(current);
1420 }
1421 None
1422}
1423
1424fn displaced_fragment_namespace_boundary<'tree>(
1425 declaration: Node<'tree>,
1426 body: Node<'tree>,
1427 source: &str,
1428) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1429 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1430 let reparse_start = body.start_byte() + 1;
1431 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1432 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1433}
1434
1435fn displaced_fragment_namespace_geometry<'tree>(
1441 declaration: Node<'tree>,
1442 source: &str,
1443) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1444 let envelope = declaration
1448 .parent()
1449 .filter(|parent| {
1450 parent.kind() == "template_declaration"
1451 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1452 })
1453 .unwrap_or(declaration);
1454 let declaration_list = envelope.parent()?;
1455 if declaration_list.kind() != "declaration_list" {
1456 return None;
1457 }
1458 let namespace = declaration_list.parent()?;
1459 if namespace.kind() != "namespace_definition"
1460 || namespace.child_by_field_name("body") != Some(declaration_list)
1461 {
1462 return None;
1463 }
1464 let class_close = direct_close_brace(declaration_list)?;
1465 let trailing_semicolon = namespace.next_named_sibling()?;
1466 if trailing_semicolon.kind() != "expression_statement"
1467 || trailing_semicolon.named_child_count() != 0
1468 {
1469 return None;
1470 }
1471 let mut namespace_items = Vec::new();
1472 let mut sibling = trailing_semicolon.next_named_sibling();
1473 let mut nested_fragment_end = 0;
1474 loop {
1475 let current = sibling?;
1476 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1477 {
1478 break;
1479 }
1480 if current.start_byte() >= nested_fragment_end
1481 && let Some(recovered) = fragmented_class_body(current, source)
1482 {
1483 nested_fragment_end = recovered.body.class_range.end_byte;
1484 }
1485 namespace_items.push(current);
1486 sibling = current.next_named_sibling();
1487 }
1488 Some(DisplacedFragmentNamespaceBoundary {
1489 class_close,
1490 class_semicolon: trailing_semicolon,
1491 namespace_items,
1492 })
1493}
1494
1495fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1497 (0..node.child_count())
1498 .filter_map(|index| node.child(index))
1499 .find(|child| !child.is_named() && child.kind() == "}")
1500}
1501
1502fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1505 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1506}
1507
1508fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1519 let bytes = source.as_bytes();
1520 if bytes.get(open_byte) != Some(&b'{') {
1521 return None;
1522 }
1523 let mut depth = 0usize;
1524 let mut i = open_byte;
1525 while i < bytes.len() {
1526 match bytes[i] {
1527 b'{' => depth += 1,
1528 b'}' => {
1529 depth = depth.checked_sub(1)?;
1530 if depth == 0 {
1531 return Some(i);
1532 }
1533 }
1534 b'/' if bytes.get(i + 1) == Some(&b'/') => {
1535 while i < bytes.len() && bytes[i] != b'\n' {
1536 i += 1;
1537 }
1538 continue;
1539 }
1540 b'/' if bytes.get(i + 1) == Some(&b'*') => {
1541 i += 2;
1542 while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1543 i += 1;
1544 }
1545 i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1546 continue;
1547 }
1548 quote @ (b'"' | b'\'') => {
1549 if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1552 return None;
1553 }
1554 i += 1;
1555 while i < bytes.len() && bytes[i] != quote {
1556 i += if bytes[i] == b'\\' { 2 } else { 1 };
1557 }
1558 if i >= bytes.len() {
1559 return None;
1560 }
1561 }
1562 _ => {}
1563 }
1564 i += 1;
1565 }
1566 None
1567}
1568
1569fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1570 let mut name = None;
1571 let mut colon_count = 0;
1572 let mut access_count = 0;
1573 for index in 0..node.child_count() {
1574 let child = node.child(index)?;
1575 match child.kind() {
1576 "identifier" | "type_identifier" if child.is_named() => {
1577 if name.is_some() {
1578 return None;
1579 }
1580 let candidate = normalize_cpp_whitespace(node_text(child, source));
1581 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1582 return None;
1583 }
1584 name = Some(candidate);
1585 }
1586 "template_function" | "template_type" if child.is_named() => {
1587 if name.is_some() {
1588 return None;
1589 }
1590 let candidate = child
1591 .child_by_field_name("name")
1592 .and_then(|name| direct_identifier_name(name, source))?;
1593 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1594 return None;
1595 }
1596 name = Some(candidate);
1597 }
1598 ":" if !child.is_named() => colon_count += 1,
1599 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1600 _ => return None,
1601 }
1602 }
1603 (colon_count == 1 && access_count == 1)
1604 .then_some(name)
1605 .flatten()
1606}
1607
1608fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1609 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1610 return false;
1611 }
1612 node.named_child(0)
1613 .and_then(|child| direct_identifier_name(child, source))
1614 .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1615}
1616
1617fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1618 (0..node.child_count()).any(|index| {
1619 node.child(index)
1620 .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1621 })
1622}
1623
1624fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1625 match node.kind() {
1626 "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1627 recovered_base_atom(node, source)
1628 }
1629 "template_type" | "template_function" => node
1630 .child_by_field_name("name")
1631 .and_then(|name| recovered_malformed_base_name(name, source)),
1632 "ERROR" => None,
1633 "qualified_identifier" | "scoped_type_identifier" => {
1634 let suffix = node
1635 .child_by_field_name("name")
1636 .and_then(|name| recovered_malformed_base_name(name, source))?;
1637 let scope = node
1638 .child_by_field_name("scope")
1639 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1640 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1641 malformed_qualified_prefix(node, source)?
1642 } else {
1643 if malformed_qualified_prefix(node, source).is_some() {
1644 return None;
1645 }
1646 scope
1647 };
1648 Some(format!("{prefix}::{suffix}"))
1649 }
1650 _ => None,
1651 }
1652}
1653
1654fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1655 if !matches!(
1656 node.kind(),
1657 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1658 ) {
1659 return None;
1660 }
1661 let name = normalize_cpp_whitespace(node_text(node, source));
1662 (!name.is_empty()).then_some(name)
1663}
1664
1665fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1666 let mut prefix = None;
1667 let mut cursor = node.walk();
1668 for error in node
1669 .named_children(&mut cursor)
1670 .filter(|child| child.kind() == "ERROR")
1671 {
1672 if prefix.is_some() {
1673 return None;
1674 }
1675 let mut error_cursor = error.walk();
1680 let atoms = error
1681 .named_children(&mut error_cursor)
1682 .map(|child| recovered_base_atom(child, source))
1683 .collect::<Option<Vec<_>>>()?;
1684 let [atom] = atoms
1685 .iter()
1686 .filter(|atom| atom.as_str() != "virtual")
1687 .collect::<Vec<_>>()[..]
1688 else {
1689 return None;
1690 };
1691 prefix = Some(atom.clone());
1692 }
1693 prefix
1694}
1695
1696struct StrandedRun<'tree> {
1700 declarations: Vec<MacroWrappedDeclaration<'tree>>,
1701 complete: bool,
1707}
1708
1709struct MacroWrappedDeclaration<'tree> {
1710 declarator: Node<'tree>,
1711 range: Range,
1712 is_static: bool,
1717}
1718
1719fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1733 declaration_scope_container(node, ancestry).is_some()
1734}
1735
1736fn declaration_scope_container<'tree>(
1741 node: Node<'tree>,
1742 ancestry: &ParentIndex<'tree>,
1743) -> Option<Node<'tree>> {
1744 let mut parent = ancestry.parent(node)?;
1745 loop {
1746 match parent.kind() {
1747 "translation_unit" => return Some(parent),
1748 "declaration_list" => {
1749 return ancestry
1750 .parent(parent)
1751 .is_some_and(|grandparent| {
1752 matches!(
1753 grandparent.kind(),
1754 "namespace_definition" | "linkage_specification"
1755 )
1756 })
1757 .then_some(parent);
1758 }
1759 "ERROR" => match ancestry.parent(parent) {
1760 Some(grandparent) => parent = grandparent,
1761 None => return Some(parent),
1764 },
1765 _ => return None,
1766 }
1767 }
1768}
1769
1770fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1772 node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
1773}
1774
1775fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1780 matches!(
1781 node.kind(),
1782 "identifier"
1783 | "type_identifier"
1784 | "primitive_type"
1785 | "sized_type_specifier"
1786 | "struct_specifier"
1787 | "union_specifier"
1788 | "enum_specifier"
1789 | "type_qualifier"
1790 | "storage_class_specifier"
1791 | "explicit_function_specifier"
1792 | "virtual_function_specifier"
1793 | "qualified_identifier"
1794 | "template_type"
1795 | "dependent_type"
1796 | "placeholder_type_specifier"
1797 )
1798}
1799
1800fn is_macro_argument_error(node: Node<'_>) -> bool {
1806 if node.kind() != "ERROR" {
1807 return false;
1808 }
1809 let mut cursor = node.walk();
1810 node.named_children(&mut cursor).all(|child| {
1811 matches!(
1812 child.kind(),
1813 "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1814 )
1815 })
1816}
1817
1818fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1827 declarator
1828 .next_sibling()
1829 .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1830 .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1831}
1832
1833fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1855 let mut parts = Vec::new();
1856 let mut cursor = node.walk();
1857 for child in node.named_children(&mut cursor) {
1858 if child.kind() == "ERROR" {
1859 let mut error_cursor = child.walk();
1860 parts.extend(child.named_children(&mut error_cursor));
1861 } else {
1862 parts.push(child);
1863 }
1864 }
1865
1866 let mut declarations = Vec::new();
1867 let mut start = None;
1868 let mut is_static = false;
1869 let mut complete = true;
1870 for part in parts {
1871 if part.kind() == "comment" {
1872 continue;
1873 }
1874 if let Some(declarator) = extract_function_declarator(part) {
1875 let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1876 declarations.push(MacroWrappedDeclaration {
1877 declarator,
1878 range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
1879 is_static,
1880 });
1881 is_static = false;
1882 continue;
1883 }
1884 if !is_recovered_declaration_type_part(part) {
1885 complete = false;
1886 break;
1887 }
1888 is_static |= part.kind() == "storage_class_specifier"
1889 && normalize_cpp_whitespace(node_text(part, source)) == "static";
1890 start.get_or_insert(part.start_byte());
1891 }
1892 StrandedRun {
1893 declarations,
1894 complete: complete && start.is_none(),
1895 }
1896}
1897
1898fn macro_wrapped_declarations<'tree>(
1924 envelope: Node<'tree>,
1925 source: &str,
1926 ancestry: &ParentIndex<'tree>,
1927) -> Vec<MacroWrappedDeclaration<'tree>> {
1928 let mut declarations = Vec::new();
1929 if !is_declaration_scope_error(envelope, ancestry) {
1930 return declarations;
1931 }
1932 let mut cursor = envelope.walk();
1933 let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
1934 let [macro_name, arguments @ ..] = children.as_slice() else {
1935 return declarations;
1936 };
1937 if macro_name.kind() != "identifier" {
1938 return declarations;
1939 }
1940 let mut wrapped_declaration_seen = false;
1941 for argument in arguments {
1942 match argument.kind() {
1943 "comment" => {}
1944 "parameter_declaration" => {
1945 let recovered = stranded_declaration_run(*argument, source).declarations;
1946 if recovered.is_empty() {
1947 break;
1948 }
1949 wrapped_declaration_seen = true;
1950 declarations.extend(recovered);
1951 }
1952 "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
1957 _ => break,
1958 }
1959 }
1960 declarations
1961}
1962
1963struct CollapsedMacroDeclarationRun {
1966 invocation_end: usize,
1969 region_end: usize,
1974}
1975
1976struct MacroInvocationTokens<'tree> {
1988 stack: Vec<Node<'tree>>,
1989 frontier: Option<Node<'tree>>,
1999}
2000
2001impl<'tree> MacroInvocationTokens<'tree> {
2002 fn new(node: Node<'tree>) -> Self {
2003 Self {
2004 stack: vec![node],
2005 frontier: Some(node),
2006 }
2007 }
2008}
2009
2010impl<'tree> Iterator for MacroInvocationTokens<'tree> {
2011 type Item = Node<'tree>;
2012
2013 fn next(&mut self) -> Option<Node<'tree>> {
2014 loop {
2015 let Some(node) = self.stack.pop() else {
2016 let sibling = self.frontier?.next_sibling()?;
2017 self.frontier = Some(sibling);
2018 self.stack.push(sibling);
2019 continue;
2020 };
2021 if node.child_count() == 0 {
2022 if node.kind() == "comment" || node.is_missing() {
2023 continue;
2024 }
2025 return Some(node);
2026 }
2027 let mut cursor = node.walk();
2028 let children = node.children(&mut cursor).collect::<Vec<_>>();
2029 self.stack.extend(children.into_iter().rev());
2030 }
2031 }
2032}
2033
2034fn collapsed_macro_declaration_run<'tree>(
2081 node: Node<'tree>,
2082 source: &str,
2083 ancestry: &ParentIndex<'tree>,
2084) -> Option<CollapsedMacroDeclarationRun> {
2085 let container_end = declaration_scope_container(node, ancestry)?.end_byte();
2086 let mut tokens = MacroInvocationTokens::new(node);
2087 let name = tokens.next()?;
2088 if !matches!(name.kind(), "identifier" | "type_identifier")
2089 || !cpp_export_macro_token(node_text(name, source))
2090 {
2091 return None;
2092 }
2093 if tokens.next()?.kind() != "(" {
2094 return None;
2095 }
2096 let mut depth = 1usize;
2097 let invocation_end = loop {
2098 let token = tokens.next()?;
2099 match token.kind() {
2100 "(" => depth += 1,
2101 ";" => return None,
2102 ")" => {
2103 depth -= 1;
2104 if depth == 0 {
2105 let semicolon = tokens.next()?;
2106 if semicolon.kind() != ";" {
2107 return None;
2108 }
2109 break semicolon.end_byte();
2110 }
2111 }
2112 _ => {}
2113 }
2114 };
2115 if invocation_end >= container_end {
2121 return None;
2122 }
2123 let region_end = if node.end_byte() > invocation_end {
2132 container_end
2133 } else {
2134 invocation_end
2135 };
2136 Some(CollapsedMacroDeclarationRun {
2137 invocation_end,
2138 region_end,
2139 })
2140}
2141
2142fn string_attribute_macro_member_declarators<'tree>(
2163 field: Node<'tree>,
2164 source: &str,
2165) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2166 if field.kind() != "field_declaration"
2167 || field
2168 .child_by_field_name("type")
2169 .is_none_or(|type_node| type_node.kind() != "type_identifier")
2170 {
2171 return None;
2172 }
2173 let declarator = field.child_by_field_name("declarator")?;
2174 if declarator.kind() != "parenthesized_declarator" {
2175 return None;
2176 }
2177 let opening = declarator.named_child(0)?;
2178 if opening.kind() != "ERROR"
2179 || opening
2180 .named_child(0)
2181 .is_none_or(|word| word.kind() != "identifier")
2182 {
2183 return None;
2184 }
2185 let declarations = stranded_declaration_run(declarator, source).declarations;
2186 (!declarations.is_empty()).then_some(declarations)
2187}
2188
2189fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2196 let Some(call) = (node.kind() == "expression_statement")
2197 .then(|| node.named_child(0))
2198 .flatten()
2199 .filter(|child| child.kind() == "call_expression")
2200 else {
2201 return false;
2202 };
2203 call.child_by_field_name("function")
2204 .is_some_and(|function| function.kind() == "identifier")
2205 && call
2206 .child_by_field_name("arguments")
2207 .is_some_and(|arguments| {
2208 let mut cursor = arguments.walk();
2209 arguments.named_child_count() > 0
2210 && arguments
2211 .named_children(&mut cursor)
2212 .all(|argument| argument.kind() == "string_literal")
2213 })
2214}
2215
2216fn cpp_access_label_constructor_call_start(
2229 node: Node<'_>,
2230 class_name: &str,
2231 source: &str,
2232) -> Option<usize> {
2233 if node.kind() != "labeled_statement" {
2234 return None;
2235 }
2236 let label = node.named_child(0)?;
2237 if label.kind() != "statement_identifier"
2238 || !matches!(
2239 node_text(label, source).trim(),
2240 "public" | "private" | "protected"
2241 )
2242 {
2243 return None;
2244 }
2245 let mut starts = Vec::new();
2246 let mut stack = vec![node];
2247 while let Some(current) = stack.pop() {
2248 if current.kind() == "call_expression"
2249 && current
2250 .child_by_field_name("function")
2251 .is_some_and(|function| {
2252 function.kind() == "identifier"
2253 && node_text(function, source).trim() == class_name
2254 })
2255 {
2256 starts.push(current.start_byte());
2257 }
2258 let mut cursor = current.walk();
2259 stack.extend(current.named_children(&mut cursor));
2260 }
2261 let [start] = starts.as_slice() else {
2262 return None;
2263 };
2264 Some(*start)
2265}
2266
2267fn cpp_declarator_function_definition<'tree>(
2271 declarator: Node<'tree>,
2272 ancestry: &ParentIndex<'tree>,
2273) -> Option<Node<'tree>> {
2274 let mut current = declarator;
2275 while let Some(parent) = ancestry.parent(current) {
2276 match parent.kind() {
2277 "function_definition" if parent.child_by_field_name("body").is_some() => {
2278 return Some(parent);
2279 }
2280 "pointer_declarator"
2281 | "reference_declarator"
2282 | "parenthesized_declarator"
2283 | "array_declarator" => current = parent,
2284 _ => return None,
2285 }
2286 }
2287 None
2288}
2289
2290fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2294 let mut current = node;
2295 while let Some(parent) = ancestry.parent(current) {
2296 if parent.kind() == "namespace_definition"
2297 && parent.child_by_field_name("body") == Some(current)
2298 {
2299 return true;
2300 }
2301 current = parent;
2302 }
2303 false
2304}
2305
2306pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2320 let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2321 let root = tree.root_node();
2322 let mut cursor = root.walk();
2323 let items = root
2324 .named_children(&mut cursor)
2325 .filter(|child| child.kind() != "comment")
2326 .collect::<Vec<_>>();
2327 let [item] = items.as_slice() else {
2328 return None;
2329 };
2330 if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2331 return None;
2332 }
2333 match item.kind() {
2334 "function_definition" => Some(item.child_by_field_name("body").is_some()),
2335 "declaration" | "field_declaration" => Some(false),
2336 _ => None,
2337 }
2338}
2339
2340pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2352 let ancestry = ParentIndex::unindexed();
2353 !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2354 || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2355}
2356
2357fn recover_exported_class_function_definition<'tree>(
2358 node: Node<'tree>,
2359 source: &str,
2360) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
2361 if node.kind() != "function_definition" {
2362 return None;
2363 }
2364 if let Some(prefix) = node.prev_named_sibling()
2365 && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2366 && recovered.range.end_byte == node.end_byte()
2367 {
2368 return Some((node, recovered.name, recovered.raw_supertypes));
2369 }
2370 let type_node = node.child_by_field_name("type")?;
2371 let declarator = node.child_by_field_name("declarator")?;
2372
2373 if matches!(
2374 type_node.kind(),
2375 "class_specifier" | "struct_specifier" | "union_specifier"
2376 ) {
2377 let type_name = type_node
2378 .child_by_field_name("name")
2379 .and_then(|name| direct_identifier_name(name, source));
2380 let exported_macro_type = type_name
2381 .as_ref()
2382 .is_some_and(|name| cpp_export_macro_token(name));
2383 if exported_macro_type {
2384 let mut cursor = node.walk();
2385 let errors_before_declarator = node
2386 .named_children(&mut cursor)
2387 .filter(|child| {
2388 child.kind() == "ERROR"
2389 && child.start_byte() >= type_node.end_byte()
2390 && child.end_byte() <= declarator.start_byte()
2391 })
2392 .collect::<Vec<_>>();
2393 if let Some(name) = errors_before_declarator
2394 .iter()
2395 .find_map(|error| displaced_exported_class_name(*error, source))
2396 {
2397 let raw_supertypes = errors_before_declarator
2398 .iter()
2399 .any(|error| malformed_inheritance_syntax(*error))
2400 .then(|| recovered_malformed_base_name(declarator, source))
2401 .flatten()
2402 .map(|base| vec![base]);
2403 return Some((node, name, raw_supertypes));
2404 }
2405 if errors_before_declarator
2406 .iter()
2407 .any(|error| malformed_inheritance_syntax(*error))
2408 {
2409 return None;
2410 }
2411 }
2412 if !exported_macro_type
2413 && let Some(name) = type_name
2414 && !cpp_export_macro_token(&name)
2415 && let Some(base) =
2416 recovered_postfix_export_macro_base(node, type_node, declarator, source)
2417 {
2418 return Some((node, name, Some(vec![base])));
2419 }
2420 if let Some(name) = direct_identifier_name(declarator, source)
2421 && exported_macro_type
2422 && !cpp_export_macro_token(&name)
2423 {
2424 let raw_supertypes = exported_macro_type
2425 .then(|| recovered_single_base_after_declarator(node, declarator, source))
2426 .flatten()
2427 .map(|base| vec![base]);
2428 return Some((node, name, raw_supertypes));
2429 }
2430 if declarator.kind() == "parenthesized_declarator"
2431 && type_node
2432 .child_by_field_name("name")
2433 .and_then(|name| direct_identifier_name(name, source))
2434 .is_some_and(|name| cpp_export_macro_token(&name))
2435 {
2436 if let Some((name, base)) =
2437 recovered_function_like_export_class_owner(declarator, source)
2438 {
2439 return Some((node, name, Some(vec![base])));
2440 }
2441 let body_start = node
2442 .child_by_field_name("body")
2443 .map(|body| body.start_byte())
2444 .unwrap_or(node.end_byte());
2445 let mut cursor = node.walk();
2446 if let Some(name) = node
2447 .named_children(&mut cursor)
2448 .filter(|child| {
2449 child.kind() == "ERROR"
2450 && child.start_byte() >= declarator.end_byte()
2451 && child.end_byte() <= body_start
2452 })
2453 .find_map(|error| declarator_name_from_node(error, source))
2454 {
2455 return Some((node, name, None));
2456 }
2457 }
2458 }
2459
2460 let declarator_text = direct_identifier_name(declarator, source)?;
2461 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2462 return None;
2463 }
2464 class_identifier_before_body(node, source).map(|name| (node, name, None))
2465}
2466
2467fn recovered_function_like_export_class_owner(
2468 declarator: Node<'_>,
2469 source: &str,
2470) -> Option<(String, String)> {
2471 if declarator.kind() != "parenthesized_declarator" {
2472 return None;
2473 }
2474 let mut cursor = declarator.walk();
2475 let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2476 let [prefix, base] = children.as_slice() else {
2477 return None;
2478 };
2479 if prefix.kind() != "ERROR"
2480 || !matches!(
2481 base.kind(),
2482 "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2483 )
2484 {
2485 return None;
2486 }
2487 let mut identifiers = Vec::new();
2488 let mut prefix_cursor = prefix.walk();
2489 for child in prefix.named_children(&mut prefix_cursor) {
2490 match child.kind() {
2491 "number_literal" | "string_literal" | "char_literal" => {}
2492 "identifier" | "type_identifier" => {
2493 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2494 }
2495 _ => return None,
2496 }
2497 }
2498 let name = match identifiers.as_slice() {
2499 [name] => name.clone(),
2500 [name, final_token] if final_token == "final" => name.clone(),
2501 _ => return None,
2502 };
2503 if name.is_empty() || cpp_export_macro_token(&name) {
2504 return None;
2505 }
2506 let base = recovered_malformed_base_name(*base, source)?;
2507 Some((name, base))
2508}
2509
2510fn recovered_export_head_bases(
2524 node: Node<'_>,
2525 after: usize,
2526 before: usize,
2527 source: &str,
2528) -> Vec<String> {
2529 let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2530 let mut bases = Vec::new();
2531 let mut cursor = node.walk();
2532 for child in node.named_children(&mut cursor) {
2533 if child.kind() == "ERROR" {
2534 let mut error_cursor = child.walk();
2535 bases.extend(
2536 child
2537 .named_children(&mut error_cursor)
2538 .filter(within)
2539 .filter_map(|part| recovered_malformed_base_name(part, source)),
2540 );
2541 } else if within(&child)
2542 && let Some(base) = recovered_malformed_base_name(child, source)
2543 {
2544 bases.push(base);
2545 }
2546 }
2547 bases.retain(|base| {
2548 !matches!(
2549 base.as_str(),
2550 "final" | "public" | "protected" | "private" | "virtual"
2551 )
2552 });
2553 bases
2554}
2555
2556fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2561 if token.is_named() {
2562 token.kind() == "identifier" && node_text(token, source) == "final"
2563 } else {
2564 token.kind() == "final"
2565 }
2566}
2567
2568fn recovered_export_head_name<'tree>(
2581 node: Node<'tree>,
2582 tail: Node<'tree>,
2583 source: &str,
2584) -> Option<Node<'tree>> {
2585 export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
2586}
2587
2588fn recovered_export_pair_head_name<'tree>(
2595 prefix: Node<'tree>,
2596 sibling: Node<'tree>,
2597 tail: Node<'tree>,
2598 source: &str,
2599) -> Option<Node<'tree>> {
2600 let mut tokens = export_head_tokens(prefix, None);
2601 tokens.extend(export_head_tokens(sibling, Some(tail)));
2602 export_head_name_from_tokens(&tokens, source)
2603}
2604
2605fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
2609 let mut tokens = Vec::new();
2610 let mut cursor = node.walk();
2611 for child in node.children(&mut cursor) {
2612 if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
2613 break;
2614 }
2615 if child.kind() == "ERROR" {
2616 let mut fragment_cursor = child.walk();
2617 tokens.extend(child.children(&mut fragment_cursor));
2618 } else {
2619 tokens.push(child);
2620 }
2621 }
2622 tokens
2623}
2624
2625fn export_head_name_from_tokens<'tree>(
2627 tokens: &[Node<'tree>],
2628 source: &str,
2629) -> Option<Node<'tree>> {
2630 let mut name = None;
2631 for token in tokens.iter().copied() {
2632 if recovered_export_head_final(token, source) || (!token.is_named() && token.kind() == ":")
2633 {
2634 break;
2635 }
2636 if token.is_named()
2637 && !token.is_missing()
2638 && matches!(
2639 token.kind(),
2640 "identifier" | "type_identifier" | "field_identifier"
2641 )
2642 {
2643 name = Some(token);
2644 }
2645 }
2646 name
2647}
2648
2649fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2652 let mut cursor = declaration.walk();
2653 declaration
2654 .named_children(&mut cursor)
2655 .find(|child| child.kind() == "init_declarator")
2656}
2657
2658fn recovered_export_declaration_tail<'tree>(
2666 declaration: Node<'tree>,
2667 head_end: usize,
2668 source: &str,
2669) -> Option<(Vec<String>, Node<'tree>)> {
2670 let init = recovered_export_init_declarator(declaration)?;
2671 let body = init.child_by_field_name("value")?;
2672 if body.kind() != "initializer_list" {
2673 return None;
2674 }
2675 let mut bases = recovered_export_head_bases(declaration, head_end, init.start_byte(), source);
2676 bases.extend(recovered_export_head_bases(
2677 init,
2678 init.start_byte(),
2679 body.start_byte(),
2680 source,
2681 ));
2682 Some((bases, body))
2683}
2684
2685fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
2699 match node.kind() {
2700 "ERROR" => {
2701 let Some(class_node) = first_class_like_child(node) else {
2702 return false;
2703 };
2704 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2705 return false;
2706 }
2707 if class_node
2708 .child_by_field_name("name")
2709 .and_then(|name| direct_identifier_name(name, source))
2710 .is_none()
2711 {
2712 return false;
2713 }
2714 class_node
2715 .next_sibling()
2716 .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
2717 }
2718 "declaration" => {
2719 let mut cursor = node.walk();
2720 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
2721 let Some(keyword) = children
2727 .iter()
2728 .copied()
2729 .flat_map(|child| {
2730 let mut cursor = child.walk();
2731 if child.kind() == "ERROR" {
2732 child.named_children(&mut cursor).collect::<Vec<_>>()
2733 } else {
2734 vec![child]
2735 }
2736 })
2737 .find(|child| {
2738 child.kind() == "identifier"
2739 && matches!(node_text(*child, source), "class" | "struct" | "union")
2740 })
2741 else {
2742 return false;
2743 };
2744 children.last().is_some_and(|init| {
2745 init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
2746 let mut cursor = init.walk();
2747 let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
2748 matches!(parts.as_slice(), [macro_name, arguments]
2749 if matches!(macro_name.kind(), "identifier" | "type_identifier")
2750 && arguments.kind() == "argument_list")
2751 }
2752 })
2753 }
2754 _ => false,
2755 }
2756}
2757
2758fn recover_function_like_export_class_pair(
2759 node: Node<'_>,
2760 source: &str,
2761) -> Option<RecoveredFunctionLikeExportClassPair> {
2762 if !is_function_like_export_class_head(node, source) {
2763 return None;
2764 }
2765 let sibling = node.next_named_sibling()?;
2766 let (name, raw_supertypes, body) = match sibling.kind() {
2767 "compound_statement" => (
2773 recovered_export_pair_head_name(node, sibling, sibling, source)
2774 .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2775 None,
2776 sibling,
2777 ),
2778 "expression_statement" => {
2779 let compound = sibling.named_child(0)?;
2780 if compound.kind() != "compound_literal_expression" {
2781 return None;
2782 }
2783 let body = compound.child_by_field_name("value")?;
2784 if body.kind() != "initializer_list" {
2785 return None;
2786 }
2787 (
2788 compound
2789 .child_by_field_name("type")
2790 .and_then(|name| direct_identifier_name(name, source))?,
2791 None,
2792 body,
2793 )
2794 }
2795 "labeled_statement" => {
2796 let label = sibling.child_by_field_name("label")?;
2797 if label.kind() != "statement_identifier" {
2798 return None;
2799 }
2800 let name = normalize_cpp_whitespace(node_text(label, source));
2801 let declaration = sibling
2802 .named_children(&mut sibling.walk())
2803 .find(|child| child.kind() == "declaration")?;
2804 let access = declaration.child_by_field_name("type")?;
2805 if !matches!(
2806 node_text(access, source),
2807 "public" | "protected" | "private"
2808 ) {
2809 return None;
2810 }
2811 let (bases, body) =
2812 recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
2813 (name, (!bases.is_empty()).then_some(bases), body)
2814 }
2815 "function_definition" => {
2821 let body = sibling.child_by_field_name("body")?;
2822 if body.kind() != "compound_statement" {
2823 return None;
2824 }
2825 let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
2826 let bases = recovered_export_head_bases(
2827 sibling,
2828 name_node.end_byte(),
2829 body.start_byte(),
2830 source,
2831 );
2832 (
2833 normalize_cpp_whitespace(node_text(name_node, source)),
2834 (!bases.is_empty()).then_some(bases),
2835 body,
2836 )
2837 }
2838 "declaration" => {
2842 let init = recovered_export_init_declarator(sibling)?;
2843 let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
2844 let (bases, body) =
2845 recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
2846 (
2847 normalize_cpp_whitespace(node_text(name_node, source)),
2848 (!bases.is_empty()).then_some(bases),
2849 body,
2850 )
2851 }
2852 _ => return None,
2853 };
2854 debug_assert!(
2855 !name.is_empty(),
2856 "a recovered class head names its class by an identifier token"
2857 );
2858 let range = Range {
2859 start_byte: node.start_byte(),
2860 end_byte: sibling.end_byte(),
2861 start_line: node.start_position().row + 1,
2862 end_line: sibling.end_position().row + 1,
2863 };
2864 Some(RecoveredFunctionLikeExportClassPair {
2865 name,
2866 raw_supertypes,
2867 range,
2868 fragmented_body: recovered_fragmented_export_body(body, range)?,
2869 })
2870}
2871
2872fn recover_embedded_function_like_export_classes(
2879 node: Node<'_>,
2880 source: &str,
2881) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
2882 if !node.is_error() {
2883 return Vec::new();
2884 }
2885
2886 let mut nodes = Vec::new();
2887 let mut class_tokens = Vec::new();
2888 let mut stack = vec![node];
2889 while let Some(current) = stack.pop() {
2890 nodes.push(current);
2891 if !current.is_named() && current.kind() == "class" {
2895 class_tokens.push(current);
2896 }
2897 push_children_reversed(current, &mut stack);
2898 }
2899 if class_tokens.is_empty() {
2900 return Vec::new();
2901 }
2902 class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2903 nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2904
2905 let language = node.language();
2908 let class_kind = NodeKindIds::new(&language, "class");
2909 let identifier_kinds = [
2910 NodeKindIds::new(&language, "identifier"),
2911 NodeKindIds::new(&language, "type_identifier"),
2912 NodeKindIds::new(&language, "field_identifier"),
2913 ];
2914 let argument_list_kind = NodeKindIds::new(&language, "argument_list");
2915 let colon_kind = NodeKindIds::new(&language, ":");
2916 let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
2917
2918 let mut recovered = Vec::new();
2919 for class_token in class_tokens {
2920 debug_assert!(class_kind.matches(class_token));
2921 let row = class_token.start_position().row;
2922 let is_identifier = |candidate: &Node<'_>| {
2928 !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
2929 };
2930 let Some(macro_name) = nodes.iter().copied().find(|candidate| {
2931 candidate.start_byte() >= class_token.end_byte()
2932 && candidate.start_position().row == row
2933 && is_identifier(candidate)
2934 }) else {
2935 continue;
2936 };
2937 let Some(arguments) = nodes.iter().copied().find(|candidate| {
2938 argument_list_kind.matches(*candidate)
2939 && candidate.start_byte() >= macro_name.end_byte()
2940 && candidate.start_position().row == row
2941 }) else {
2942 continue;
2943 };
2944 if nodes.iter().any(|candidate| {
2945 is_identifier(candidate)
2946 && candidate.start_byte() >= macro_name.end_byte()
2947 && candidate.end_byte() <= arguments.start_byte()
2948 }) {
2949 continue;
2950 }
2951 let Some(head_end) = nodes.iter().copied().find(|candidate| {
2952 candidate.start_byte() >= arguments.end_byte()
2953 && (recovered_export_head_final(*candidate, source)
2954 || (!candidate.is_named() && colon_kind.matches(*candidate)))
2955 }) else {
2956 continue;
2957 };
2958 let Some(name_node) = nodes.iter().copied().rfind(|candidate| {
2959 is_identifier(candidate)
2960 && candidate.start_byte() >= arguments.end_byte()
2961 && candidate.end_byte() <= head_end.start_byte()
2962 && candidate.start_position().row == row
2963 }) else {
2964 continue;
2965 };
2966 let name = normalize_cpp_whitespace(node_text(name_node, source));
2967 let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
2968 field_initializer_kind.matches(*candidate)
2969 && candidate.start_byte() >= name_node.end_byte()
2970 && candidate
2971 .child_by_field_name("field")
2972 .or_else(|| candidate.named_child(0))
2973 .is_some()
2974 && candidate
2975 .child_by_field_name("value")
2976 .or_else(|| {
2977 let mut cursor = candidate.walk();
2978 candidate
2979 .named_children(&mut cursor)
2980 .find(|child| child.kind() == "initializer_list")
2981 })
2982 .is_some_and(|value| value.kind() == "initializer_list")
2983 }) else {
2984 continue;
2985 };
2986 let has_access = nodes.iter().copied().any(|candidate| {
2987 candidate.start_byte() >= name_node.end_byte()
2988 && candidate.end_byte() <= base_initializer.start_byte()
2989 && matches!(
2990 normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
2991 "public" | "protected" | "private"
2992 )
2993 });
2994 if !has_access {
2995 continue;
2996 }
2997 let Some(base_node) = base_initializer
2998 .child_by_field_name("field")
2999 .or_else(|| base_initializer.named_child(0))
3000 else {
3001 continue;
3002 };
3003 let Some(base) = recovered_malformed_base_name(base_node, source) else {
3004 continue;
3005 };
3006 let body = base_initializer
3007 .child_by_field_name("value")
3008 .or_else(|| {
3009 let mut cursor = base_initializer.walk();
3010 base_initializer
3011 .named_children(&mut cursor)
3012 .find(|child| child.kind() == "initializer_list")
3013 })
3014 .expect("initializer-list value checked above");
3015 let range = Range {
3016 start_byte: class_token.start_byte(),
3017 end_byte: body.end_byte(),
3018 start_line: class_token.start_position().row + 1,
3019 end_line: body.end_position().row + 1,
3020 };
3021 if recovered
3022 .iter()
3023 .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
3024 existing.name == name && existing.range == range
3025 })
3026 {
3027 continue;
3028 }
3029 recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
3030 name,
3031 range,
3032 raw_supertypes: vec![base],
3033 fragmented_body: match recovered_fragmented_export_body(body, range) {
3034 Some(fragmented) => fragmented,
3035 None => continue,
3036 },
3037 });
3038 }
3039 recovered
3040}
3041
3042fn lifted_function_like_export_class_namespace<'tree>(
3043 node: Node<'tree>,
3044 source: &str,
3045 ancestry: &ParentIndex<'tree>,
3046) -> Option<String> {
3047 let mut anchor = node;
3053 let parent = loop {
3054 let parent = ancestry.parent(anchor)?;
3055 if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
3056 break parent;
3057 }
3058 anchor = parent;
3059 };
3060 let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
3061 sibling.start_byte() > anchor.end_byte()
3062 && sibling.kind() == "ERROR"
3063 && sibling.named_child_count() == 0
3064 && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
3065 });
3066 if !has_later_close {
3067 return None;
3068 }
3069 let candidates = parent
3070 .named_children(&mut parent.walk())
3071 .filter(|sibling| {
3072 sibling.kind() == "namespace_definition"
3073 && sibling.has_error()
3074 && sibling.end_byte() < anchor.start_byte()
3075 })
3076 .filter_map(|namespace| {
3077 namespace
3078 .child_by_field_name("name")
3079 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
3080 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
3081 })
3082 .collect::<Vec<_>>();
3083 let [namespace] = candidates.as_slice() else {
3084 return None;
3085 };
3086 Some(namespace.clone())
3087}
3088
3089pub(crate) fn recovered_function_like_export_class_pair_has_body(
3090 node: Node<'_>,
3091 source: &str,
3092 identifier: &str,
3093 range: &Range,
3094) -> bool {
3095 recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
3096 recovered.name == identifier
3097 && recovered.range.start_byte == range.start_byte
3098 && recovered.range.end_byte == range.end_byte
3099 })
3100}
3101
3102#[derive(Default)]
3118pub struct CppRecoveredExportClassIndex {
3119 by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3120}
3121
3122impl CppRecoveredExportClassIndex {
3123 pub fn build(root: Node<'_>, source: &str) -> Self {
3124 let mut by_error_node: HashMap<
3125 (usize, usize),
3126 Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3127 > = HashMap::default();
3128 let mut cursor = root.walk();
3131 let mut stack = vec![root];
3132 while let Some(node) = stack.pop() {
3133 if node.is_error() {
3134 let recovered = recover_embedded_function_like_export_classes(node, source);
3135 if !recovered.is_empty() {
3136 by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3137 }
3138 }
3139 stack.extend(node.named_children(&mut cursor));
3140 }
3141 Self { by_error_node }
3142 }
3143
3144 pub fn approximate_size(&self) -> usize {
3146 self.by_error_node
3147 .values()
3148 .fold(0usize, |total, recovered| {
3149 recovered.iter().fold(
3150 total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3151 |acc, class| {
3152 acc.saturating_add(std::mem::size_of::<
3153 RecoveredEmbeddedFunctionLikeExportClass,
3154 >())
3155 .saturating_add(class.name.len())
3156 .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3157 },
3158 )
3159 })
3160 }
3161
3162 fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3163 self.by_error_node
3164 .get(&(node.start_byte(), node.end_byte()))
3165 .is_some_and(|recovered| {
3166 recovered.iter().any(|class| {
3167 class.name == identifier
3168 && class.range.start_byte == range.start_byte
3169 && class.range.end_byte == range.end_byte
3170 })
3171 })
3172 }
3173}
3174
3175#[cfg(any(test, feature = "test-support"))]
3182thread_local! {
3183 static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3184 const { std::cell::Cell::new(0) };
3185}
3186
3187#[cfg(any(test, feature = "test-support"))]
3191#[doc(hidden)]
3192pub fn recovered_class_body_node_visits_for_test() -> usize {
3193 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3194}
3195
3196#[cfg(any(test, feature = "test-support"))]
3198#[doc(hidden)]
3199pub fn reset_recovered_class_body_node_visits_for_test() {
3200 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3201}
3202
3203#[cfg(any(test, feature = "test-support"))]
3204fn record_recovered_class_body_visit() {
3205 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3206}
3207
3208#[cfg(not(any(test, feature = "test-support")))]
3209fn record_recovered_class_body_visit() {}
3210
3211pub(crate) fn recovered_class_body_at(
3234 recovered_export_classes: &CppRecoveredExportClassIndex,
3235 root: Node<'_>,
3236 source: &str,
3237 identifier: &str,
3238 range: &Range,
3239) -> Option<bool> {
3240 let covers_range_start = |node: &Node<'_>| {
3241 node.start_byte() <= range.start_byte
3242 && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3243 };
3244 let mut stack = vec![root];
3245 let mut saw_forward = false;
3246 while let Some(node) = stack.pop() {
3247 record_recovered_class_body_visit();
3248 if (node.start_byte() == range.start_byte
3252 && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3253 || recovered_export_classes.claims(node, identifier, range)
3254 || (node.start_byte() == range.start_byte
3255 && recovered_fragmented_class_has_body(node, source, identifier, range))
3256 {
3257 return Some(true);
3258 }
3259 if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3260 return Some(true);
3261 }
3262 if node.start_byte() <= range.start_byte
3269 && range.start_byte < node.end_byte()
3270 && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3271 {
3272 if has_body {
3273 return Some(true);
3274 }
3275 saw_forward = true;
3276 continue;
3277 }
3278 let mut cursor = node.walk();
3279 stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3280 }
3281 saw_forward.then_some(false)
3282}
3283
3284fn recovered_collapsed_aggregate_has_body(
3297 node: Node<'_>,
3298 source: &str,
3299 identifier: &str,
3300 range: &Range,
3301) -> bool {
3302 let claims = |head: &CppCollapsedAggregateHead<'_>| {
3303 head.key.start_byte() == range.start_byte
3304 && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3305 };
3306 if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3307 return true;
3308 }
3309 if !node.is_error() {
3310 return false;
3311 }
3312 let mut cursor = node.walk();
3313 let children = node.children(&mut cursor).collect::<Vec<_>>();
3314 children
3315 .iter()
3316 .enumerate()
3317 .filter(|(_, child)| {
3318 child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3319 })
3320 .any(|(index, _)| {
3321 cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3322 })
3323}
3324
3325pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3333 if !matches!(
3334 node.kind(),
3335 "qualified_identifier" | "scoped_type_identifier" | "template_type"
3336 ) {
3337 return false;
3338 }
3339 if let Some(function) = node.parent().filter(|parent| {
3340 parent.kind() == "function_definition"
3341 && parent
3342 .child_by_field_name("declarator")
3343 .is_some_and(|declarator| same_node(declarator, node))
3344 }) {
3345 return recover_exported_class_function_definition(function, source)
3346 .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
3347 }
3348 let Some(initializer) = node.parent().filter(|parent| {
3349 parent.kind() == "init_declarator"
3350 && parent
3351 .child_by_field_name("declarator")
3352 .is_some_and(|declarator| same_node(declarator, node))
3353 }) else {
3354 return false;
3355 };
3356 initializer
3357 .parent()
3358 .filter(|parent| parent.kind() == "declaration")
3359 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3360 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3361}
3362
3363struct CppSentinelReparsedClass<'tree> {
3369 declaration_node: Node<'tree>,
3370 name: String,
3371 body: Node<'tree>,
3372 raw_supertypes: Option<Vec<String>>,
3373}
3374
3375fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3376 let mut cursor = root.walk();
3377 root.named_children(&mut cursor)
3378 .find(|child| child.kind() != "comment")
3379 .filter(|child| child.kind() == "template_declaration")
3380}
3381
3382fn cpp_sentinel_reparsed_class<'tree>(
3383 root: Node<'tree>,
3384 template_node: Option<Node<'tree>>,
3385 source: &str,
3386 ancestry: &ParentIndex<'tree>,
3387) -> Option<CppSentinelReparsedClass<'tree>> {
3388 let container = template_node.unwrap_or(root);
3389 let mut cursor = container.walk();
3390 for child in container.named_children(&mut cursor) {
3391 if matches!(
3392 child.kind(),
3393 "class_specifier" | "struct_specifier" | "union_specifier"
3394 ) {
3395 let name = class_like_name(child, source, ancestry)?;
3396 let body = cpp_body_node(child)?;
3397 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
3398 .then(|| extract_cpp_supertypes(child, source));
3399 return Some(CppSentinelReparsedClass {
3400 declaration_node: child,
3401 name,
3402 body,
3403 raw_supertypes,
3404 });
3405 }
3406 if child.kind() == "declaration"
3407 && let Some(class_node) = first_class_like_child(child)
3408 {
3409 let name = class_like_name(class_node, source, ancestry)?;
3410 let body = cpp_body_node(class_node)?;
3411 let raw_supertypes =
3412 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3413 .then(|| extract_cpp_supertypes(class_node, source));
3414 return Some(CppSentinelReparsedClass {
3415 declaration_node: class_node,
3416 name,
3417 body,
3418 raw_supertypes,
3419 });
3420 }
3421 if child.kind() == "function_definition"
3426 && let Some(class_node) = first_class_like_child(child)
3427 && let Some(body) = cpp_body_node(class_node)
3428 && let Some(name) = class_like_name(class_node, source, ancestry)
3429 {
3430 let raw_supertypes =
3431 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3432 .then(|| extract_cpp_supertypes(class_node, source));
3433 return Some(CppSentinelReparsedClass {
3434 declaration_node: class_node,
3435 name,
3436 body,
3437 raw_supertypes,
3438 });
3439 }
3440 if child.kind() == "function_definition"
3441 && let Some((_, name, raw_supertypes)) =
3442 recover_exported_class_function_definition(child, source)
3443 {
3444 let body = cpp_body_node(child)?;
3445 return Some(CppSentinelReparsedClass {
3446 declaration_node: child,
3447 name,
3448 body,
3449 raw_supertypes,
3450 });
3451 }
3452 }
3453 None
3454}
3455
3456fn recovered_postfix_export_macro_base(
3457 node: Node<'_>,
3458 type_node: Node<'_>,
3459 declarator: Node<'_>,
3460 source: &str,
3461) -> Option<String> {
3462 let mut cursor = node.walk();
3463 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
3464 child.kind() == "ERROR"
3465 && child.start_byte() >= type_node.end_byte()
3466 && child.end_byte() <= declarator.start_byte()
3467 && postfix_export_macro_inheritance(*child, source)
3468 });
3469 malformed_clauses.next()?;
3470 if malformed_clauses.next().is_some() {
3471 return None;
3472 }
3473 recovered_malformed_base_name(declarator, source)
3474}
3475
3476fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
3477 let mut macro_count = 0;
3478 let mut colon_count = 0;
3479 let mut access_count = 0;
3480 for index in 0..node.child_count() {
3481 let Some(child) = node.child(index) else {
3482 return false;
3483 };
3484 match child.kind() {
3485 "identifier" | "type_identifier" if child.is_named() => {
3486 let candidate = normalize_cpp_whitespace(node_text(child, source));
3487 if !cpp_export_macro_token(&candidate) {
3488 return false;
3489 }
3490 macro_count += 1;
3491 }
3492 ":" if !child.is_named() => colon_count += 1,
3493 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
3494 _ => return false,
3495 }
3496 }
3497 macro_count == 1 && colon_count == 1 && access_count == 1
3498}
3499
3500fn recovered_single_base_after_declarator(
3501 node: Node<'_>,
3502 declarator: Node<'_>,
3503 source: &str,
3504) -> Option<String> {
3505 let body_start = node
3506 .child_by_field_name("body")
3507 .map(|body| body.start_byte())
3508 .unwrap_or(node.end_byte());
3509 let mut cursor = node.walk();
3510 let mut bases = node
3511 .named_children(&mut cursor)
3512 .filter(|child| {
3513 child.kind() == "ERROR"
3514 && child.start_byte() >= declarator.end_byte()
3515 && child.end_byte() <= body_start
3516 })
3517 .filter_map(|error| displaced_exported_class_name(error, source));
3518 let base = bases.next()?;
3519 bases.next().is_none().then_some(base)
3520}
3521
3522fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
3523 (0..node.child_count()).any(|index| {
3524 node.child(index)
3525 .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
3526 })
3527}
3528
3529pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3530 recover_exported_class_function_definition(node, source).is_some()
3531}
3532
3533fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3534 matches!(
3535 kind,
3536 "ERROR"
3537 | "preproc_if"
3538 | "preproc_ifdef"
3539 | "preproc_ifndef"
3540 | "preproc_else"
3541 | "preproc_elif"
3542 ) || (kind == "labeled_statement" && in_class_scope)
3543}
3544
3545pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3546 if node.kind() != "declaration" {
3547 return false;
3548 }
3549 let mut ancestor = node.parent();
3550 while let Some(container) = ancestor {
3551 match container.kind() {
3552 "compound_statement" => {
3553 return container.parent().is_some_and(|class_container| {
3554 is_recovered_exported_class_container(class_container, source)
3555 });
3556 }
3557 "template_declaration" | "linkage_specification" | "declaration_list" => {}
3560 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3561 _ => return false,
3562 }
3563 ancestor = container.parent();
3564 }
3565 false
3566}
3567
3568pub fn recovered_exported_class_has_body(
3569 node: Node<'_>,
3570 source: &str,
3571 expected_name: &str,
3572) -> Option<bool> {
3573 match node.kind() {
3574 "function_definition" => {
3575 let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
3576 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3577 }
3578 "declaration" | "field_declaration" => {
3579 let recovered = recover_exported_class_declaration(node, source)?;
3580 (recovered.name == expected_name).then(|| recovered.body.is_some())
3581 }
3582 _ => None,
3583 }
3584}
3585
3586fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3587 let body_start = node
3588 .child_by_field_name("body")
3589 .map(|body| body.start_byte())
3590 .unwrap_or(node.end_byte());
3591 let mut stack = Vec::new();
3592 for index in (0..node.named_child_count()).rev() {
3593 let Some(child) = node.named_child(index) else {
3594 continue;
3595 };
3596 if child.start_byte() >= body_start {
3597 continue;
3598 }
3599 stack.push(child);
3600 }
3601
3602 let mut best = None;
3603 while let Some(current) = stack.pop() {
3604 if matches!(current.kind(), "identifier" | "type_identifier") {
3605 let name = normalize_cpp_whitespace(node_text(current, source));
3606 if !name.is_empty()
3607 && !cpp_export_macro_token(&name)
3608 && !matches!(name.as_str(), "class" | "struct" | "union")
3609 {
3610 best = Some(name);
3611 }
3612 continue;
3613 }
3614
3615 for index in (0..current.named_child_count()).rev() {
3616 if let Some(child) = current.named_child(index)
3617 && child.start_byte() < body_start
3618 {
3619 stack.push(child);
3620 }
3621 }
3622 }
3623 best
3624}
3625
3626fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3627 if node.kind() == "declaration"
3628 && node
3629 .child_by_field_name("type")
3630 .or_else(|| first_class_like_child(node))
3631 .is_some_and(|type_node| {
3632 matches!(
3633 type_node.kind(),
3634 "class_specifier" | "struct_specifier" | "union_specifier"
3635 )
3636 })
3637 && let Some(name) = node
3638 .child_by_field_name("declarator")
3639 .and_then(|declarator| declarator_name_from_node(declarator, source))
3640 && !cpp_export_macro_token(&name)
3641 {
3642 return Some(name);
3643 }
3644
3645 if node.kind() == "function_definition"
3646 && node.child_by_field_name("type").is_some_and(|type_node| {
3647 matches!(
3648 type_node.kind(),
3649 "class_specifier" | "struct_specifier" | "union_specifier"
3650 )
3651 })
3652 && let Some(name) = node
3653 .child_by_field_name("declarator")
3654 .and_then(|declarator| direct_identifier_name(declarator, source))
3655 && !cpp_export_macro_token(&name)
3656 {
3657 return Some(name);
3658 }
3659
3660 let class_node = if matches!(
3661 node.kind(),
3662 "class_specifier" | "struct_specifier" | "union_specifier"
3663 ) {
3664 node
3665 } else {
3666 first_class_like_child(node)?
3667 };
3668 class_like_name_from_children(class_node, source)
3669}
3670
3671fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3672 if !matches!(
3673 node.kind(),
3674 "identifier" | "field_identifier" | "type_identifier"
3675 ) {
3676 return None;
3677 }
3678 let name = normalize_cpp_whitespace(node_text(node, source));
3679 (!name.is_empty()).then_some(name)
3680}
3681
3682fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3683 match node.kind() {
3684 "identifier" | "field_identifier" | "type_identifier" => {
3685 let name = normalize_cpp_whitespace(node_text(node, source));
3686 (!name.is_empty()).then_some(name)
3687 }
3688 _ => {
3689 let mut cursor = node.walk();
3690 node.named_children(&mut cursor)
3691 .find_map(|child| declarator_name_from_node(child, source))
3692 }
3693 }
3694}
3695
3696fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3697 let mut cursor = node.walk();
3698 node.named_children(&mut cursor).find(|child| {
3699 matches!(
3700 child.kind(),
3701 "class_specifier" | "struct_specifier" | "union_specifier"
3702 )
3703 })
3704}
3705
3706fn push_cpp_container_work<'tree>(
3711 node: Node<'tree>,
3712 scope: ScopeInfo,
3713 stack: &mut Vec<CppWork<'tree>>,
3714) {
3715 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3716}
3717
3718fn push_cpp_sibling_range<'tree>(
3722 parent: Node<'tree>,
3723 start_index: usize,
3724 end_index: usize,
3725 scope: ScopeInfo,
3726 stack: &mut Vec<CppWork<'tree>>,
3727) {
3728 let mut cursor = parent.walk();
3729 let children = parent
3730 .named_children(&mut cursor)
3731 .skip(start_index)
3732 .take(end_index.saturating_sub(start_index))
3733 .collect::<Vec<_>>()
3734 .into_iter();
3735 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3736}
3737
3738fn advance_cpp_siblings<'tree>(
3746 mut siblings: CppSiblingsWork<'tree>,
3747 source: &str,
3748 stack: &mut Vec<CppWork<'tree>>,
3749) {
3750 let Some(child) = siblings.children.next() else {
3751 return;
3752 };
3753 let current_scope = siblings.scope.clone();
3754 if let Some(namespace) = cpp_using_namespace_target(child, source) {
3755 siblings.scope.visible_using_namespaces.push(namespace);
3756 }
3757 if !siblings.children.as_slice().is_empty() {
3758 stack.push(CppWork::Siblings(siblings));
3759 }
3760 stack.push(CppWork::Node(CppNodeWork {
3761 node: child,
3762 scope: current_scope,
3763 }));
3764}
3765
3766fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3773 if node.kind() != "using_declaration" {
3774 return None;
3775 }
3776 let mut cursor = node.walk();
3777 let is_namespace_directive = node
3778 .children(&mut cursor)
3779 .any(|child| child.kind() == "namespace");
3780 if !is_namespace_directive {
3781 return None;
3782 }
3783 let target = node.named_child(0)?;
3784 let start = target
3792 .child(0)
3793 .filter(|child| !child.is_named() && child.kind() == "::")
3794 .map_or(target.start_byte(), |marker| marker.end_byte());
3795 let text = normalize_cpp_whitespace(
3796 source
3797 .get(start..target.end_byte())
3798 .expect("using-directive target covers one source range"),
3799 );
3800 (!text.is_empty()).then_some(text)
3801}
3802
3803pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
3816 let mut parser = Parser::new();
3817 if parser
3818 .set_language(&tree_sitter_cpp::LANGUAGE.into())
3819 .is_err()
3820 {
3821 return Vec::new();
3822 }
3823 let Some(tree) = parser.parse(source, None) else {
3824 return Vec::new();
3825 };
3826 let mut namespaces = Vec::new();
3827 let mut seen = std::collections::HashSet::new();
3828 let mut stack = vec![tree.root_node()];
3829 while let Some(node) = stack.pop() {
3830 if let Some(namespace) = cpp_using_namespace_target(node, source)
3831 && seen.insert(namespace.clone())
3832 {
3833 namespaces.push(namespace);
3834 }
3835 let mut cursor = node.walk();
3836 stack.extend(node.named_children(&mut cursor));
3837 }
3838 namespaces
3839}
3840
3841pub struct CppVisitor<'a> {
3842 pub file: &'a ProjectFile,
3843 pub source: &'a str,
3844 pub parsed: &'a mut ParsedFile,
3845 pub c_tag_semantics: bool,
3856 pub(crate) c_tag_scope_witness: bool,
3859 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
3860 pub consumed_fragment_regions: Vec<(usize, usize)>,
3869 pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
3874 pub partitioned_regions: Vec<(Tree, std::ops::Range<usize>, ScopeInfo)>,
3877 pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
3882 pub field_owners: Option<CppFieldOwnerIndex>,
3886 pub recovery_captures: Vec<CppRecoveryCapture>,
3890 pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
3894 pub ambiguous_object_macro_fields: HashSet<String>,
3897}
3898
3899#[derive(Clone, Debug, PartialEq, Eq)]
3906pub enum ObjectMacroFieldEvent {
3907 Define {
3908 name: String,
3909 replacement: ObjectMacroReplacement,
3910 conditional: bool,
3911 },
3912 Undef {
3913 name: String,
3914 conditional: bool,
3915 },
3916}
3917
3918pub fn collect_cpp_object_macro_fields<'tree>(
3922 root: Node<'tree>,
3923 source: &str,
3924) -> HashMap<String, ObjectMacroReplacement> {
3925 let mut fields = HashMap::default();
3926 let mut ambiguous = HashSet::default();
3927 for event in collect_cpp_object_macro_field_events(root, source) {
3928 match event {
3929 ObjectMacroFieldEvent::Define {
3930 name,
3931 replacement: value,
3932 conditional,
3933 } => {
3934 if value.is_empty() {
3935 fields.remove(&name);
3936 if conditional {
3937 ambiguous.insert(name);
3938 } else {
3939 ambiguous.remove(&name);
3940 }
3941 } else if ambiguous.contains(&name) {
3942 } else if let Some(previous) = fields.get(&name) {
3944 if previous != &value {
3945 fields.remove(&name);
3946 ambiguous.insert(name);
3947 }
3948 } else {
3949 fields.insert(name, value);
3950 }
3951 }
3952 ObjectMacroFieldEvent::Undef { name, conditional } => {
3953 fields.remove(&name);
3954 if conditional {
3955 ambiguous.insert(name);
3956 } else {
3957 ambiguous.remove(&name);
3958 }
3959 }
3960 }
3961 }
3962 fields
3963}
3964
3965pub fn collect_cpp_object_macro_field_events<'tree>(
3970 root: Node<'tree>,
3971 source: &str,
3972) -> Vec<ObjectMacroFieldEvent> {
3973 let mut events = Vec::new();
3974 let mut stack = vec![root];
3975 while let Some(node) = stack.pop() {
3976 if node.kind() == "preproc_def"
3977 && let Some(name) = extract_macro_name(node, source)
3978 {
3979 let replacement = object_macro_replacement_of(node, source);
3980 events.push(ObjectMacroFieldEvent::Define {
3981 name,
3982 replacement,
3983 conditional: inside_preprocessor_conditional(node),
3984 });
3985 } else if is_cpp_undef_directive(node, source)
3986 && let Some(argument) = node.child_by_field_name("argument")
3987 {
3988 events.push(ObjectMacroFieldEvent::Undef {
3989 name: node_text(argument, source).trim().to_string(),
3990 conditional: inside_preprocessor_conditional(node),
3991 });
3992 }
3993 let mut cursor = node.walk();
3994 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3995 stack.extend(children.into_iter().rev());
3996 }
3997 events
3998}
3999
4000fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
4001 let mut current = node.parent();
4002 while let Some(parent) = current {
4003 if matches!(
4004 parent.kind(),
4005 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
4006 ) {
4007 return true;
4008 }
4009 current = parent.parent();
4010 }
4011 false
4012}
4013
4014fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
4015 node.kind() == "preproc_call"
4016 && node
4017 .child_by_field_name("directive")
4018 .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
4019}
4020
4021impl<'a> CppVisitor<'a> {
4022 fn add_declaration(
4030 &mut self,
4031 code_unit: CodeUnit,
4032 node: Node<'_>,
4033 parent: Option<CodeUnit>,
4034 top_level: Option<CodeUnit>,
4035 ) {
4036 self.note_declaration(&code_unit);
4037 let source = self.source;
4038 self.parsed
4039 .add_code_unit(code_unit, node, source, parent, top_level);
4040 }
4041
4042 fn add_declaration_with_range(
4044 &mut self,
4045 code_unit: CodeUnit,
4046 range: Range,
4047 parent: Option<CodeUnit>,
4048 top_level: Option<CodeUnit>,
4049 ) {
4050 self.note_declaration(&code_unit);
4051 self.parsed
4052 .add_code_unit_with_range(code_unit, range, parent, top_level);
4053 }
4054
4055 fn replace_declaration_deferred(
4057 &mut self,
4058 code_unit: CodeUnit,
4059 node: Node<'_>,
4060 parent: Option<CodeUnit>,
4061 top_level: Option<CodeUnit>,
4062 ) {
4063 self.note_replaced_declaration(&code_unit);
4064 let source = self.source;
4065 self.parsed
4066 .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
4067 }
4068
4069 fn replace_declaration_with_range_deferred(
4071 &mut self,
4072 code_unit: CodeUnit,
4073 range: Range,
4074 parent: Option<CodeUnit>,
4075 top_level: Option<CodeUnit>,
4076 ) {
4077 self.note_replaced_declaration(&code_unit);
4078 self.parsed
4079 .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
4080 }
4081
4082 fn note_declaration(&mut self, code_unit: &CodeUnit) {
4089 if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
4090 for capture in &mut self.recovery_captures {
4091 if capture.removed_pre_existing.contains(code_unit) {
4092 continue;
4093 }
4094 if capture.created_units.insert(code_unit.clone()) {
4095 capture.created.push(code_unit.clone());
4096 }
4097 }
4098 }
4099 if let Some(field_owners) = self.field_owners.as_mut() {
4100 field_owners.record(code_unit, self.file);
4101 }
4102 }
4103
4104 fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
4113 let removes_children = self.parsed.contains_declaration(code_unit)
4114 && self
4115 .parsed
4116 .children
4117 .get(code_unit)
4118 .is_some_and(|children| !children.is_empty());
4119 if removes_children {
4120 if !self.recovery_captures.is_empty() {
4121 let removed = self.declarations_a_replacement_removes(code_unit);
4122 for capture in &mut self.recovery_captures {
4123 for unit in &removed {
4124 if !capture.created_units.contains(unit) {
4129 capture.removed_pre_existing.insert(unit.clone());
4130 }
4131 }
4132 }
4133 }
4134 self.field_owners = None;
4135 }
4136 self.note_declaration(code_unit);
4137 }
4138
4139 fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
4142 let mut removed = Vec::new();
4143 let mut seen = HashSet::default();
4144 let mut pending: Vec<CodeUnit> = self
4145 .parsed
4146 .children
4147 .get(code_unit)
4148 .cloned()
4149 .unwrap_or_default();
4150 while let Some(unit) = pending.pop() {
4151 if !seen.insert(unit.clone()) {
4152 continue;
4153 }
4154 if let Some(children) = self.parsed.children.get(&unit) {
4155 pending.extend(children.iter().cloned());
4156 }
4157 removed.push(unit);
4158 }
4159 removed
4160 }
4161
4162 fn visit_function_like_export_class_pair<'tree>(
4163 &mut self,
4164 node: Node<'tree>,
4165 scope: &ScopeInfo,
4166 stack: &mut Vec<CppWork<'tree>>,
4167 ancestry: &ParentIndex<'tree>,
4168 ) -> bool {
4169 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
4170 return false;
4171 };
4172 let member_outcome = self
4173 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
4174 let mut displaced = node.next_named_sibling();
4180 while let Some(candidate) = displaced {
4181 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
4182 break;
4183 }
4184 displaced = candidate.next_named_sibling();
4185 }
4186 let class_unit = self.visit_named_class_like_shape(
4187 node,
4188 recovered.name,
4189 None,
4194 true,
4195 Some(recovered.range),
4196 recovered.raw_supertypes,
4197 scope,
4198 stack,
4199 ancestry,
4200 );
4201 self.parsed
4202 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4203 recovery: recovered.range,
4204 unit: class_unit.clone(),
4205 });
4206 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4207 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4208 tree.root_node(),
4209 class_unit.identifier(),
4210 self.source,
4211 )
4212 {
4213 self.visit_recovered_fragment_constructor(
4214 range,
4215 body,
4216 node,
4217 &class_unit,
4218 scope,
4219 ancestry,
4220 );
4221 }
4222 if let Some(outcome) = member_outcome {
4223 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4224 }
4225 self.consumed_fragment_regions
4226 .push((node.start_byte(), recovered.range.end_byte));
4227 true
4228 }
4229
4230 fn visit_embedded_function_like_export_classes<'tree>(
4231 &mut self,
4232 node: Node<'tree>,
4233 scope: &ScopeInfo,
4234 stack: &mut Vec<CppWork<'tree>>,
4235 ancestry: &ParentIndex<'tree>,
4236 ) -> bool {
4237 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
4238 let found = !recovered_classes.is_empty();
4239 for recovered in recovered_classes {
4240 let member_outcome = self.reparse_fragmented_export_class_members(
4241 &recovered.fragmented_body,
4242 &recovered.name,
4243 );
4244 let class_unit = self.visit_named_class_like_shape(
4245 node,
4246 recovered.name,
4247 None,
4248 true,
4249 Some(recovered.range),
4250 Some(recovered.raw_supertypes),
4251 scope,
4252 stack,
4253 ancestry,
4254 );
4255 self.parsed
4256 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4257 recovery: recovered.range,
4258 unit: class_unit.clone(),
4259 });
4260 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4261 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4262 tree.root_node(),
4263 class_unit.identifier(),
4264 self.source,
4265 )
4266 {
4267 self.visit_recovered_fragment_constructor(
4268 range,
4269 body,
4270 node,
4271 &class_unit,
4272 scope,
4273 ancestry,
4274 );
4275 }
4276 if let Some(outcome) = member_outcome {
4277 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4278 }
4279 }
4280 found
4281 }
4282
4283 #[allow(clippy::too_many_arguments)]
4291 pub fn visit_container<'tree>(
4292 &mut self,
4293 node: Node<'tree>,
4294 ancestry: &ParentIndex<'tree>,
4295 package_name: &str,
4296 module: Option<CodeUnit>,
4297 class_unit: Option<CodeUnit>,
4298 template_signature: Option<String>,
4299 visible_using_namespaces: Vec<String>,
4300 ) {
4301 let scope = ScopeInfo {
4302 package_name: package_name.to_string(),
4303 module,
4304 class_unit,
4305 template_signature,
4306 template_metadata: None,
4307 declarations_are_fields: false,
4308 recovered_specialization_member_scope: false,
4309 visible_using_namespaces,
4310 };
4311 if node.is_error() {
4318 self.visit_object_macro_error_classes(node, &scope);
4319 }
4320 self.run_container_work(node, scope, ancestry);
4321 while let Some((tree, range, scope)) = self.partitioned_regions.pop() {
4322 let root = tree.root_node();
4323 let container = root
4324 .descendant_for_byte_range(range.start, range.end)
4325 .expect("the queued container belongs to this tree");
4326 assert_eq!(container.byte_range(), range);
4327 self.run_container_work(container, scope, &ParentIndex::new(root));
4328 }
4329 }
4330
4331 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
4335 self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
4336 }
4337
4338 fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
4343 self.consumed_fragment_regions
4344 .iter()
4345 .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
4346 }
4347
4348 fn run_container_work<'tree>(
4360 &mut self,
4361 node: Node<'tree>,
4362 scope: ScopeInfo,
4363 ancestry: &ParentIndex<'tree>,
4364 ) {
4365 self.drain_cpp_work(
4366 vec![CppWork::Container(CppContainer { node, scope })],
4367 ancestry,
4368 );
4369 }
4370
4371 fn drain_cpp_work<'tree>(
4378 &mut self,
4379 mut stack: Vec<CppWork<'tree>>,
4380 ancestry: &ParentIndex<'tree>,
4381 ) {
4382 while let Some(work) = stack.pop() {
4383 match work {
4384 CppWork::Container(container) => {
4385 push_cpp_container_work(container.node, container.scope, &mut stack);
4386 }
4387 CppWork::Siblings(siblings) => {
4388 advance_cpp_siblings(siblings, self.source, &mut stack);
4389 }
4390 CppWork::Node(work) => {
4391 if self.node_is_inside_consumed_fragment(work.node) {
4392 continue;
4393 }
4394 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
4395 }
4396 }
4397 }
4398 }
4399
4400 fn reparse_fragmented_export_class_members(
4405 &self,
4406 fragmented: &FragmentedExportBody,
4407 class_name: &str,
4408 ) -> Option<FragmentedExportMembers> {
4409 if fragmented.reparse_start >= fragmented.reparse_end {
4410 return None;
4411 }
4412 let tree = cpp_reparse_fragmented_class_body(
4413 self.source,
4414 fragmented.reparse_start,
4415 fragmented.reparse_end,
4416 )?;
4417 if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
4418 return Some(FragmentedExportMembers::Complete(tree));
4419 }
4420 let has_conditional_constructor = {
4421 let root = tree.root_node();
4422 let mut cursor = root.walk();
4423 root.named_children(&mut cursor).any(|child| {
4424 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
4425 })
4426 };
4427 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
4428 }
4429
4430 fn visit_fragmented_export_class_members(
4433 &mut self,
4434 outcome: FragmentedExportMembers,
4435 class_unit: CodeUnit,
4436 scope: &ScopeInfo,
4437 ) -> bool {
4438 let (tree, complete) = match outcome {
4439 FragmentedExportMembers::Complete(tree) => (tree, true),
4440 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
4441 };
4442 let root = tree.root_node();
4443 let class_name = class_unit.identifier().to_string();
4444 let member_scope = ScopeInfo {
4445 package_name: class_unit.package_name().to_string(),
4450 module: scope.module.clone(),
4451 class_unit: Some(class_unit),
4452 template_signature: scope.template_signature.clone(),
4453 template_metadata: None,
4454 declarations_are_fields: true,
4455 recovered_specialization_member_scope: false,
4456 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4457 };
4458 if !complete {
4459 let mut cursor = root.walk();
4465 let constructors = root
4466 .named_children(&mut cursor)
4467 .filter_map(|child| {
4468 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
4469 })
4470 .collect::<Vec<_>>();
4471 let reparsed_ancestry = ParentIndex::new(root);
4474 for constructor in constructors {
4475 let mut stack = Vec::new();
4476 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
4477 while let Some(work) = stack.pop() {
4478 match work {
4479 CppWork::Container(container) => {
4480 push_cpp_container_work(container.node, container.scope, &mut stack);
4481 }
4482 CppWork::Siblings(siblings) => {
4483 advance_cpp_siblings(siblings, self.source, &mut stack);
4484 }
4485 CppWork::Node(work) => {
4486 self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
4487 }
4488 }
4489 }
4490 }
4491 return false;
4492 }
4493 self.run_container_work(root, member_scope, &ParentIndex::new(root));
4495 true
4496 }
4497
4498 fn visit_recovered_fragment_constructor<'tree>(
4499 &mut self,
4500 range: std::ops::Range<usize>,
4501 constructor_body: Node<'tree>,
4502 class_declaration: Node<'tree>,
4503 class_unit: &CodeUnit,
4504 scope: &ScopeInfo,
4505 ancestry: &ParentIndex<'tree>,
4506 ) {
4507 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
4508 return;
4509 };
4510 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
4511 tree.root_node(),
4512 range.start,
4513 class_unit.identifier(),
4514 self.source,
4515 ) else {
4516 return;
4517 };
4518 let member_scope = ScopeInfo {
4519 package_name: class_unit.package_name().to_string(),
4520 module: scope.module.clone(),
4521 class_unit: Some(class_unit.clone()),
4522 template_signature: scope.template_signature.clone(),
4523 template_metadata: None,
4524 declarations_are_fields: true,
4525 recovered_specialization_member_scope: false,
4526 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4527 };
4528 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
4529 else {
4530 return;
4531 };
4532 debug_assert_eq!(function.name, class_unit.identifier());
4533 let code_unit = function.code_unit(self.file.clone());
4534 self.add_declaration_with_range(
4535 code_unit.clone(),
4536 Range {
4537 start_byte: function_declarator.start_byte(),
4538 end_byte: constructor_body.end_byte(),
4539 start_line: function_declarator.start_position().row + 1,
4540 end_line: constructor_body.end_position().row + 1,
4541 },
4542 None,
4543 None,
4544 );
4545 self.parsed.add_signature_with_metadata(
4546 code_unit.clone(),
4547 cpp_signature_metadata(
4548 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
4549 function_declarator,
4550 cpp_callable_linkage(class_declaration, self.source, ancestry),
4551 self.source,
4552 ancestry,
4553 )
4554 .with_declaration_only(false),
4555 );
4556 self.parsed.add_child(class_unit.clone(), code_unit);
4557 }
4558
4559 fn visit_recovered_fragment_prefix_members<'tree>(
4560 &mut self,
4561 root: Node<'tree>,
4562 constructor_start: usize,
4563 class_unit: &CodeUnit,
4564 scope: &ScopeInfo,
4565 ancestry: &ParentIndex<'tree>,
4566 ) {
4567 let member_scope = ScopeInfo {
4568 package_name: class_unit.package_name().to_string(),
4569 module: scope.module.clone(),
4570 class_unit: Some(class_unit.clone()),
4571 template_signature: scope.template_signature.clone(),
4572 template_metadata: None,
4573 declarations_are_fields: true,
4574 recovered_specialization_member_scope: false,
4575 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4576 };
4577 let mut stack = vec![root];
4578 while let Some(current) = stack.pop() {
4579 if current.kind() == "comment" || current.start_byte() >= constructor_start {
4580 continue;
4581 }
4582 if current.end_byte() <= constructor_start
4583 && current.kind() != "translation_unit"
4584 && current.kind() != "labeled_statement"
4585 && current.kind() != "ERROR"
4586 {
4587 let mut work_stack = Vec::new();
4588 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
4589 while let Some(work) = work_stack.pop() {
4590 match work {
4591 CppWork::Container(container) => {
4592 push_cpp_container_work(
4593 container.node,
4594 container.scope,
4595 &mut work_stack,
4596 );
4597 }
4598 CppWork::Siblings(siblings) => {
4599 advance_cpp_siblings(siblings, self.source, &mut work_stack);
4600 }
4601 CppWork::Node(work) => {
4602 self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
4603 }
4604 }
4605 }
4606 continue;
4607 }
4608 if matches!(
4609 current.kind(),
4610 "translation_unit" | "labeled_statement" | "ERROR"
4611 ) {
4612 let mut cursor = current.walk();
4613 stack.extend(current.named_children(&mut cursor));
4614 }
4615 }
4616 }
4617
4618 fn visit_node<'tree>(
4619 &mut self,
4620 node: Node<'tree>,
4621 scope: &ScopeInfo,
4622 stack: &mut Vec<CppWork<'tree>>,
4623 ancestry: &ParentIndex<'tree>,
4624 ) {
4625 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
4626 self.visit_node(node, &recovered_scope, stack, ancestry);
4627 return;
4628 }
4629 if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
4630 self.visit_node(node, &recovered_scope, stack, ancestry);
4631 return;
4632 }
4633 if node.kind() == "function_definition" && node.has_error() {
4639 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4640 }
4641 if let Some(FragmentedClassRecovery {
4642 declaration_node: class_node,
4643 name,
4644 raw_supertypes,
4645 body: fragmented,
4646 }) = fragmented_class_body(node, self.source)
4647 {
4648 let displaced_namespace_items =
4649 displaced_fragment_namespace_geometry(node, self.source)
4650 .map(|boundary| boundary.namespace_items)
4651 .unwrap_or_default();
4652 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
4653 let mut class_stack = Vec::new();
4654 let parser_visible_body =
4657 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
4658 .then(|| cpp_body_node(class_node))
4659 .flatten();
4660 let class_unit = self.visit_named_class_like_shape(
4661 class_node,
4662 name,
4663 parser_visible_body,
4664 true,
4665 Some(fragmented.class_range),
4666 Some(raw_supertypes),
4667 scope,
4668 &mut class_stack,
4669 ancestry,
4670 );
4671 let member_scope = ScopeInfo {
4672 package_name: class_unit.package_name().to_string(),
4673 module: scope.module.clone(),
4674 class_unit: Some(class_unit.clone()),
4675 template_signature: scope.template_signature.clone(),
4676 template_metadata: None,
4677 declarations_are_fields: true,
4678 recovered_specialization_member_scope: false,
4679 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4680 };
4681 let complete = outcome.is_some_and(|outcome| {
4682 self.visit_fragmented_export_class_members(outcome, class_unit, scope)
4683 });
4684 if complete {
4685 self.consumed_fragment_regions
4686 .push((node.start_byte(), fragmented.class_range.end_byte));
4687 } else {
4688 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
4698 if candidate.start_byte() >= fragmented.reparse_end {
4699 break;
4700 }
4701 if cpp_fragment_sibling_is_class_member(
4702 candidate,
4703 fragmented.reparse_end,
4704 self.source,
4705 ) {
4706 self.recovered_class_sibling_scopes
4707 .insert(candidate.id(), member_scope.clone());
4708 }
4709 }
4710 }
4711 for item in displaced_namespace_items {
4712 self.recovered_class_sibling_scopes
4713 .insert(item.id(), scope.clone());
4714 }
4715 stack.extend(class_stack);
4716 return;
4717 }
4718 if self.visit_folded_aggregate(node, scope) {
4719 return;
4720 }
4721 match node.kind() {
4722 "template_declaration" => {
4723 if let Some(recovered) =
4724 recover_fragmented_preprocessor_class(node, self.source, ancestry)
4725 {
4726 let mut template_scope = scope.clone();
4727 template_scope.template_signature =
4728 cpp_template_signature(node, recovered.declaration_node, self.source);
4729 template_scope.template_metadata =
4730 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
4731 let raw_supertypes =
4732 Some(extract_cpp_supertypes(recovered.class_node, self.source));
4733 let mut class_stack = Vec::new();
4734 let class_unit = self.visit_named_class_like_shape(
4735 recovered.class_node,
4736 recovered.name,
4737 Some(recovered.body),
4738 true,
4739 Some(recovered.range),
4740 raw_supertypes,
4741 &template_scope,
4742 &mut class_stack,
4743 ancestry,
4744 );
4745 self.parsed.record_materialization(
4746 MaterializationRecord::RecoveredDeclaration {
4747 recovery: recovered.range,
4748 unit: class_unit.clone(),
4749 },
4750 );
4751 let member_scope = ScopeInfo {
4752 package_name: template_scope.package_name.clone(),
4753 module: template_scope.module.clone(),
4754 class_unit: Some(class_unit.clone()),
4755 template_signature: template_scope.template_signature.clone(),
4756 template_metadata: None,
4757 declarations_are_fields: true,
4758 recovered_specialization_member_scope: recovered
4759 .class_node
4760 .child_by_field_name("name")
4761 .is_some_and(|name| name.kind() == "template_type"),
4762 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
4763 };
4764 for tail_member in recovered.tail_members.into_iter().rev() {
4765 stack.push(CppWork::Node(CppNodeWork {
4766 node: tail_member,
4767 scope: member_scope.clone(),
4768 }));
4769 }
4770 stack.extend(class_stack);
4771 for sibling in recovered.member_siblings {
4772 self.recovered_class_sibling_scopes
4773 .insert(sibling.id(), member_scope.clone());
4774 }
4775 return;
4776 }
4777 for index in (0..node.named_child_count()).rev() {
4778 let Some(child) = node.named_child(index) else {
4779 continue;
4780 };
4781 if matches!(
4782 child.kind(),
4783 "class_specifier"
4784 | "struct_specifier"
4785 | "union_specifier"
4786 | "enum_specifier"
4787 | "function_definition"
4788 | "declaration"
4789 | "field_declaration"
4790 | "alias_declaration"
4791 | "namespace_definition"
4792 ) {
4793 let mut template_scope = scope.clone();
4794 template_scope.template_signature =
4795 cpp_template_signature(node, child, self.source);
4796 template_scope.template_metadata =
4797 cpp_template_metadata(node, child, self.source, ancestry);
4798 if let Some(recovered) = recover_fragmented_partial_specialization(
4799 node,
4800 child,
4801 self.source,
4802 ancestry,
4803 ) {
4804 let code_unit = self.visit_named_class_like_shape(
4805 recovered.declaration_node,
4806 recovered.name,
4807 None,
4808 true,
4809 Some(recovered.range),
4810 None,
4811 &template_scope,
4812 stack,
4813 ancestry,
4814 );
4815 self.parsed.record_materialization(
4816 MaterializationRecord::RecoveredDeclaration {
4817 recovery: recovered.range,
4818 unit: code_unit.clone(),
4819 },
4820 );
4821 let mut member_scope = template_scope.clone();
4822 member_scope.class_unit = Some(code_unit);
4823 member_scope.declarations_are_fields = true;
4824 member_scope.recovered_specialization_member_scope = true;
4825 for prefix_member in recovered.prefix_members.into_iter().rev() {
4826 stack.push(CppWork::Node(CppNodeWork {
4827 node: prefix_member,
4828 scope: member_scope.clone(),
4829 }));
4830 }
4831 for sibling in recovered.member_siblings {
4832 self.recovered_class_sibling_scopes
4833 .insert(sibling.id(), member_scope.clone());
4834 }
4835 for following in recovered.following_declarations.into_iter().rev() {
4836 stack.push(CppWork::Node(CppNodeWork {
4837 node: following,
4838 scope: scope.clone(),
4839 }));
4840 }
4841 return;
4842 }
4843 stack.push(CppWork::Node(CppNodeWork {
4844 node: child,
4845 scope: template_scope,
4846 }));
4847 }
4848 }
4849 }
4850 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
4851 "linkage_specification" => {
4852 if let Some(body) = cpp_body_node(node) {
4853 stack.push(CppWork::Container(CppContainer {
4854 node: body,
4855 scope: scope.clone(),
4856 }));
4857 } else {
4858 stack.push(CppWork::Container(CppContainer {
4859 node,
4860 scope: scope.clone(),
4861 }));
4862 }
4863 }
4864 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
4865 self.visit_class_like(node, scope, stack, ancestry)
4866 }
4867 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
4868 "ERROR" => {
4875 self.visit_object_macro_error_classes(node, scope);
4876 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4877 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4878 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
4879 return;
4880 }
4881 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4882 return;
4883 }
4884 self.visit_macro_swallowed_function_declarations(node, scope);
4885 self.visit_macro_wrapped_declarations(node, scope, ancestry);
4886 self.visit_stranded_class_members(node, scope, ancestry);
4887 stack.push(CppWork::Container(CppContainer {
4888 node,
4889 scope: scope.clone(),
4890 }));
4891 }
4892 }
4893 "declaration" => {
4894 if node.has_error() {
4895 if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4902 return;
4903 }
4904 self.visit_prototype_macro_declarations(node, scope);
4905 if self.node_is_inside_consumed_fragment(node) {
4906 return;
4913 }
4914 }
4915 if scope.class_unit.is_some()
4916 && scope.declarations_are_fields
4917 && scope.recovered_specialization_member_scope
4918 && let Some(alias_name) =
4919 recovered_using_declaration_alias_name(node, self.source)
4920 {
4921 self.add_type_aliases(node, scope, vec![alias_name], ancestry);
4922 } else {
4923 self.visit_declaration(
4924 node,
4925 scope,
4926 scope.declarations_are_fields,
4927 stack,
4928 ancestry,
4929 )
4930 }
4931 }
4932 "expression_statement" => {
4946 if node.has_error() {
4947 self.visit_prototype_macro_declarations(node, scope);
4948 }
4949 }
4950 "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
4951 "preproc_call" => self.visit_preproc_call(node, scope),
4952 "type_definition" | "alias_declaration" => {
4953 self.visit_type_declaration(node, scope, stack, ancestry)
4954 }
4955 "preproc_def" | "preproc_function_def" => self.visit_macro(node),
4956 "preproc_include" => {}
4962 kind if preserves_declaration_scope_through_wrapper(
4963 kind,
4964 scope.class_unit.is_some(),
4965 ) =>
4966 {
4967 if kind == "labeled_statement" {
4973 self.visit_access_label_constructor(node, scope);
4974 }
4975 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
4976 let mut range = cpp_declaration_range(node);
4977 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
4978 range.end_byte = boundary.end_byte;
4979 range.end_line = boundary.end_line;
4980 }
4981 self.parsed.record_materialization(
4982 MaterializationRecord::ConfigurationConditional { range },
4983 );
4984 if node.has_error() {
4985 let mut candidates = vec![node];
4994 while let Some(candidate) = candidates.pop() {
4995 if candidate.kind() == "ERROR"
5006 && !cpp_is_inside_namespace_body(candidate, ancestry)
5007 && self.visit_function_like_export_class_pair(
5008 candidate, scope, stack, ancestry,
5009 )
5010 {
5011 continue;
5012 }
5013 for index in (0..candidate.named_child_count()).rev() {
5014 candidates.push(
5015 candidate
5016 .named_child(index)
5017 .expect("index below the node's own named child count"),
5018 );
5019 }
5020 }
5021 }
5022 }
5023 stack.push(CppWork::Container(CppContainer {
5024 node,
5025 scope: scope.clone(),
5026 }))
5027 }
5028 _ => {
5034 self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
5035 }
5036 }
5037 }
5038
5039 fn visit_macro_swallowed_function_declarations<'tree>(
5040 &mut self,
5041 envelope: Node<'tree>,
5042 scope: &ScopeInfo,
5043 ) {
5044 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
5045 || envelope.kind() == "ERROR"
5046 && envelope
5047 .parent()
5048 .is_some_and(|parent| parent.kind() == "ERROR")
5049 {
5050 return;
5051 }
5052 let mut stack = (0..envelope.named_child_count())
5053 .filter_map(|index| envelope.named_child(index))
5054 .collect::<Vec<_>>();
5055 while let Some(node) = stack.pop() {
5056 if node.kind() == "function_declarator" {
5057 self.visit_error_swallowed_function_declaration(node, scope);
5058 }
5059 for child in named_children_iter(node) {
5060 stack.push(child);
5061 }
5062 }
5063 }
5064
5065 fn visit_macro_wrapped_declarations<'tree>(
5069 &mut self,
5070 envelope: Node<'tree>,
5071 scope: &ScopeInfo,
5072 ancestry: &ParentIndex<'tree>,
5073 ) {
5074 let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
5075 if recovered.is_empty() {
5076 return;
5077 }
5078 let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
5079 self.record_recovered_declarations(recovery, |visitor| {
5080 for declaration in recovered {
5081 visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
5082 }
5083 });
5084 }
5085
5086 fn visit_collapsed_macro_declaration_run<'tree>(
5113 &mut self,
5114 envelope: Node<'tree>,
5115 scope: &ScopeInfo,
5116 ancestry: &ParentIndex<'tree>,
5117 ) -> bool {
5118 let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
5119 return false;
5120 };
5121 let start = envelope.start_byte();
5122 let end = run.region_end;
5123 let recovery = cpp_recovery_window(self.source, start, end);
5124 self.record_recovered_declarations(recovery, |visitor| {
5125 let mut position = start;
5126 while position < end {
5127 let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
5128 return;
5129 };
5130 let root = tree.root_node();
5131 let ancestry = ParentIndex::new(root);
5134 let mut cursor = root.walk();
5135 let collapsed =
5136 root.named_children(&mut cursor)
5137 .enumerate()
5138 .find_map(|(index, item)| {
5139 collapsed_macro_declaration_run(item, visitor.source, &ancestry)
5140 .map(|run| (index, item, run))
5141 });
5142 let mut stack = Vec::new();
5147 push_cpp_sibling_range(
5148 root,
5149 0,
5150 collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
5151 scope.clone(),
5152 &mut stack,
5153 );
5154 visitor.drain_cpp_work(stack, &ancestry);
5155 let Some((_, item, run)) = collapsed else {
5156 return;
5157 };
5158 if let Some(head) =
5162 cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
5163 {
5164 let head_root = head.root_node();
5165 visitor.run_container_work(
5166 head_root,
5167 scope.clone(),
5168 &ParentIndex::new(head_root),
5169 );
5170 }
5171 assert!(
5172 run.invocation_end > position,
5173 "a collapsed run at {position} must end after the byte the scan resumed \
5174 from, but ended at {}",
5175 run.invocation_end
5176 );
5177 position = run.invocation_end;
5178 }
5179 });
5180 self.consumed_fragment_regions.push((start, end));
5181 true
5182 }
5183
5184 fn visit_stranded_class_members<'tree>(
5194 &mut self,
5195 node: Node<'tree>,
5196 scope: &ScopeInfo,
5197 ancestry: &ParentIndex<'tree>,
5198 ) {
5199 if scope.class_unit.is_none() || !scope.declarations_are_fields {
5200 return;
5201 }
5202 for member in stranded_declaration_run(node, self.source).declarations {
5203 self.add_macro_wrapped_declaration(member, scope, ancestry);
5204 }
5205 }
5206
5207 fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5215 let Some(class_unit) = scope.class_unit.clone() else {
5216 return;
5217 };
5218 if !scope.declarations_are_fields {
5219 return;
5220 }
5221 let class_name = class_unit.identifier().to_string();
5222 let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
5223 else {
5224 return;
5225 };
5226 let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
5227 return;
5228 };
5229 let root = tree.root_node();
5230 let Some(declarator) =
5231 cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
5232 else {
5233 return;
5234 };
5235 let reparsed_ancestry = ParentIndex::new(root);
5236 let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
5237 let range = cpp_declaration_range(definition.unwrap_or(declarator));
5238 let recovery = cpp_recovery_window(self.source, start, node.end_byte());
5239 self.record_recovered_declarations(recovery, |visitor| {
5240 visitor.add_macro_wrapped_declaration(
5241 MacroWrappedDeclaration {
5242 declarator,
5243 range,
5244 is_static: false,
5245 },
5246 scope,
5247 &reparsed_ancestry,
5248 );
5249 });
5250 }
5251
5252 fn add_macro_wrapped_declaration<'tree>(
5253 &mut self,
5254 declaration: MacroWrappedDeclaration<'tree>,
5255 scope: &ScopeInfo,
5256 ancestry: &ParentIndex<'tree>,
5257 ) {
5258 let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
5259 else {
5260 return;
5261 };
5262 let code_unit =
5263 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
5264 if self.parsed.contains_declaration(&code_unit) {
5265 self.parsed
5266 .record_navigation_range(code_unit, declaration.range);
5267 return;
5268 }
5269 self.add_declaration_with_range(code_unit.clone(), declaration.range, None, None);
5270 let signature = normalize_cpp_whitespace(
5271 self.source
5272 .get(declaration.range.start_byte..declaration.range.end_byte)
5273 .expect("a recovered declaration range covers one source range"),
5274 );
5275 let linkage = if declaration.is_static {
5276 CallableLinkage::Internal
5277 } else {
5278 cpp_callable_linkage(declaration.declarator, self.source, ancestry)
5279 };
5280 let declaration_only =
5284 cpp_declarator_function_definition(declaration.declarator, ancestry).is_none();
5285 self.parsed.add_signature_with_metadata(
5286 code_unit.clone(),
5287 cpp_signature_metadata(
5288 signature,
5289 declaration.declarator,
5290 linkage,
5291 self.source,
5292 ancestry,
5293 )
5294 .with_declaration_only(declaration_only),
5295 );
5296 if let Some(parent) = &scope.class_unit {
5297 self.parsed.add_child(parent.clone(), code_unit);
5298 } else if let Some(module) = &scope.module {
5299 self.parsed.add_child(module.clone(), code_unit);
5300 }
5301 }
5302
5303 fn visit_anonymous_local_aggregates_in_function<'tree>(
5308 &mut self,
5309 function: Node<'tree>,
5310 scope: &ScopeInfo,
5311 stack: &mut Vec<CppWork<'tree>>,
5312 ancestry: &ParentIndex<'tree>,
5313 ) {
5314 let Some(body) = cpp_body_node(function) else {
5315 return;
5316 };
5317 let block_scope;
5324 let scope = if scope.class_unit.is_some() {
5325 block_scope = ScopeInfo {
5326 class_unit: None,
5327 declarations_are_fields: false,
5328 ..scope.clone()
5329 };
5330 &block_scope
5331 } else {
5332 scope
5333 };
5334 let mut pending = vec![body];
5335 while let Some(node) = pending.pop() {
5336 if matches!(node.kind(), "function_definition" | "lambda_expression") {
5337 continue;
5338 }
5339 if node.kind() == "declaration"
5340 && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
5341 {
5342 continue;
5343 }
5344 if matches!(
5345 node.kind(),
5346 "class_specifier" | "struct_specifier" | "union_specifier"
5347 ) {
5348 continue;
5349 }
5350 let mut cursor = node.walk();
5351 let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
5352 children.reverse();
5353 pending.extend(children);
5354 }
5355 }
5356
5357 fn visit_error_swallowed_function_declaration<'tree>(
5358 &mut self,
5359 node: Node<'tree>,
5360 scope: &ScopeInfo,
5361 ) -> bool {
5362 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
5363 return false;
5364 };
5365 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
5366 return false;
5367 };
5368 let root = tree.root_node();
5369 let mut cursor = root.walk();
5370 let declarations = root
5371 .named_children(&mut cursor)
5372 .filter(|child| child.kind() != "comment")
5373 .collect::<Vec<_>>();
5374 let [declaration] = declarations.as_slice() else {
5375 return false;
5376 };
5377 if declaration.kind() != "declaration"
5378 || declaration.has_error()
5379 || declaration.start_byte() != start
5380 || declaration.end_byte() != end
5381 {
5382 return false;
5383 }
5384 let recovery = cpp_recovery_window(self.source, start, end);
5385 let reparsed_ancestry = ParentIndex::new(root);
5387 self.record_recovered_declarations(recovery, |visitor| {
5388 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5389 });
5390 true
5391 }
5392
5393 fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5416 for candidate in cpp_prototype_macro_candidates(node, self.source) {
5417 let start = candidate.run_start;
5418 let end = candidate.semicolon_end;
5419 if self.byte_range_is_inside_consumed_fragment(start, end) {
5420 continue;
5421 }
5422 let Some(tree) = parse_source_ranges_with_cancellation(
5423 &tree_sitter_cpp::LANGUAGE.into(),
5424 self.source,
5425 &candidate.ranges(),
5426 None,
5427 ) else {
5428 continue;
5429 };
5430 let root = tree.root_node();
5431 let mut cursor = root.walk();
5432 let declarations = root
5433 .named_children(&mut cursor)
5434 .filter(|child| child.kind() != "comment")
5435 .collect::<Vec<_>>();
5436 let [declaration] = declarations.as_slice() else {
5437 continue;
5438 };
5439 if declaration.kind() != "declaration"
5440 || declaration.has_error()
5441 || declaration.start_byte() != start
5442 || declaration.end_byte() != end
5443 || declaration
5444 .child_by_field_name("declarator")
5445 .and_then(extract_function_declarator)
5446 .is_none()
5447 {
5448 continue;
5449 }
5450 let recovery = cpp_recovery_window(self.source, start, end);
5451 let reparsed_ancestry = ParentIndex::new(root);
5454 self.record_recovered_declarations(recovery, |visitor| {
5455 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5456 });
5457 self.consumed_fragment_regions.push((start, end));
5458 }
5459 }
5460
5461 fn declare_namespace_levels(
5467 &mut self,
5468 mut package_name: String,
5469 levels: Vec<(String, Range)>,
5470 ) -> (String, Option<CodeUnit>) {
5471 let mut module = None;
5472 for (component, range) in levels {
5473 let full_name = if package_name.is_empty() {
5474 component
5475 } else {
5476 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
5477 };
5478 let level = CodeUnit::new_fq(
5479 self.file.clone(),
5480 CodeUnitType::Module,
5481 "",
5482 full_name.clone(),
5483 cpp_namespace_fq(&full_name),
5484 );
5485 if !self.parsed.contains_declaration(&level) {
5486 self.add_declaration_with_range(level.clone(), range, None, None);
5487 }
5488 package_name = full_name;
5489 module = Some(level);
5490 }
5491 (package_name, module)
5492 }
5493
5494 fn recovered_namespace_scope(
5499 &mut self,
5500 node: Node<'_>,
5501 scope: &ScopeInfo,
5502 ) -> Option<ScopeInfo> {
5503 self.orphaned_namespaces.region_at(node.start_byte())?;
5504 let levels = self
5510 .orphaned_namespaces
5511 .enclosing_namespace_levels(node, self.source);
5512 let package_name = levels
5513 .iter()
5514 .map(|(component, _)| component.as_str())
5515 .collect::<Vec<_>>()
5516 .join(CPP_PACKAGE_SEPARATOR);
5517 if package_name == scope.package_name {
5518 return None;
5519 }
5520 let (package_name, module) = self.declare_namespace_levels(String::new(), levels);
5521 Some(ScopeInfo {
5522 package_name,
5523 module,
5524 class_unit: None,
5529 template_signature: None,
5530 template_metadata: None,
5531 declarations_are_fields: false,
5532 recovered_specialization_member_scope: false,
5533 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5534 })
5535 }
5536
5537 fn visit_namespace<'tree>(
5538 &mut self,
5539 node: Node<'tree>,
5540 scope: &ScopeInfo,
5541 stack: &mut Vec<CppWork<'tree>>,
5542 ancestry: &ParentIndex<'tree>,
5543 ) {
5544 let name_node = node.child_by_field_name("name");
5545 let Some(name_node) = name_node else {
5546 if let Some(body) = cpp_body_node(node) {
5547 stack.push(CppWork::Container(CppContainer {
5548 node: body,
5549 scope: scope.clone(),
5550 }));
5551 }
5552 return;
5553 };
5554 let explicitly_global = name_node
5561 .child(0)
5562 .is_some_and(|child| !child.is_named() && child.kind() == "::");
5563 let components = cpp_namespace_name_components(name_node, self.source);
5564 if components.is_empty() {
5565 return;
5566 }
5567 let package_name = if explicitly_global {
5573 String::new()
5574 } else {
5575 scope.package_name.clone()
5576 };
5577 let definition = node_range(node);
5578 let (package_name, module) = self.declare_namespace_levels(
5579 package_name,
5580 components
5581 .into_iter()
5582 .map(|component| (component, definition))
5583 .collect(),
5584 );
5585
5586 let namespace_scope = ScopeInfo {
5587 package_name,
5588 module,
5589 class_unit: None,
5597 template_signature: scope.template_signature.clone(),
5598 template_metadata: scope.template_metadata.clone(),
5599 declarations_are_fields: false,
5600 recovered_specialization_member_scope: false,
5601 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5602 };
5603 let container = cpp_body_node(node).unwrap_or(node);
5604 let mut candidates = vec![container];
5612 while let Some(candidate) = candidates.pop() {
5613 if matches!(
5614 candidate.kind(),
5615 "ERROR" | "function_definition" | "labeled_statement"
5616 ) && self.visit_embedded_function_like_export_classes(
5617 candidate,
5618 &namespace_scope,
5619 stack,
5620 ancestry,
5621 ) {
5622 continue;
5623 }
5624 for index in (0..candidate.named_child_count()).rev() {
5625 candidates.push(
5626 candidate
5627 .named_child(index)
5628 .expect("index below the node's own named child count"),
5629 );
5630 }
5631 }
5632 stack.push(CppWork::Container(CppContainer {
5633 node: container,
5634 scope: namespace_scope,
5635 }));
5636 }
5637
5638 fn visit_class_like<'tree>(
5639 &mut self,
5640 node: Node<'tree>,
5641 scope: &ScopeInfo,
5642 stack: &mut Vec<CppWork<'tree>>,
5643 ancestry: &ParentIndex<'tree>,
5644 ) {
5645 let Some(name) = class_like_name(node, self.source, ancestry) else {
5646 return;
5647 };
5648 let name = qualified_class_name_chain(node, self.source, scope)
5649 .map(|chain| chain.join("$"))
5650 .unwrap_or(name);
5651 self.visit_named_class_like(node, name, scope, stack, ancestry);
5652 }
5653
5654 fn visit_named_class_like<'tree>(
5655 &mut self,
5656 node: Node<'tree>,
5657 name: String,
5658 scope: &ScopeInfo,
5659 stack: &mut Vec<CppWork<'tree>>,
5660 ancestry: &ParentIndex<'tree>,
5661 ) {
5662 let body = cpp_body_node(node);
5663 let definition_body_present = body.is_some();
5664 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
5665 .then(|| extract_cpp_supertypes(node, self.source));
5666 self.visit_named_class_like_shape(
5667 node,
5668 name,
5669 body,
5670 definition_body_present,
5671 None,
5672 raw_supertypes,
5673 scope,
5674 stack,
5675 ancestry,
5676 );
5677 }
5678
5679 fn mints_tag_at_enclosing_c_scope(
5687 &self,
5688 declaration_node: Node<'_>,
5689 scope: &ScopeInfo,
5690 ancestry: &ParentIndex<'_>,
5691 ) -> bool {
5692 scope.class_unit.is_some()
5693 && matches!(
5694 declaration_node.kind(),
5695 "struct_specifier" | "union_specifier" | "enum_specifier"
5696 )
5697 && class_like_name(declaration_node, self.source, ancestry).is_some()
5698 }
5699
5700 #[allow(clippy::too_many_arguments)]
5701 fn visit_named_class_like_shape<'tree>(
5702 &mut self,
5703 declaration_node: Node<'tree>,
5704 name: String,
5705 body: Option<Node<'tree>>,
5706 definition_body_present: bool,
5707 explicit_range: Option<Range>,
5708 raw_supertypes: Option<Vec<String>>,
5709 scope: &ScopeInfo,
5710 stack: &mut Vec<CppWork<'tree>>,
5711 ancestry: &ParentIndex<'tree>,
5712 ) -> CodeUnit {
5713 let displaced_macro_tail = if explicit_range.is_none() {
5714 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
5715 } else {
5716 None
5717 };
5718 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
5719 let recovered_scope = self.scope_for_recovered_exported_class(
5720 declaration_node,
5721 &name,
5722 definition_body_present,
5723 scope,
5724 ancestry,
5725 );
5726 let c_tag_scope;
5736 let c_tag_scope_applies =
5737 self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
5738 if !self.c_tag_semantics && c_tag_scope_applies {
5739 self.c_tag_scope_witness = true;
5740 }
5741 let scope = if self.c_tag_semantics && c_tag_scope_applies {
5742 c_tag_scope = ScopeInfo {
5743 class_unit: None,
5744 ..recovered_scope.clone()
5745 };
5746 &c_tag_scope
5747 } else {
5748 &recovered_scope
5749 };
5750 let short_name = if let Some(parent) = &scope.class_unit {
5751 cpp_join_nested_short(parent.short_name(), &name)
5752 } else {
5753 name.clone()
5754 };
5755 let qualified_chain = if scope.class_unit.is_none() {
5762 qualified_class_name_chain(declaration_node, self.source, scope)
5763 .filter(|chain| chain.join("$") == name)
5764 } else {
5765 None
5766 };
5767 let fq = if let Some(chain) = qualified_chain {
5768 let mut fq = FqName::new();
5769 cpp_push_package(&mut fq, &scope.package_name);
5770 let mut first = true;
5771 for component in chain {
5772 let kind = if first {
5773 SegmentKind::Type
5774 } else {
5775 SegmentKind::Nested
5776 };
5777 fq.push(cpp_segment(&component, kind));
5778 first = false;
5779 }
5780 fq
5781 } else {
5782 cpp_leaf_fq(
5783 &scope.package_name,
5784 scope.class_unit.as_ref(),
5785 &name,
5786 SegmentKind::Nested,
5787 SegmentKind::Type,
5788 )
5789 };
5790 let code_unit = CodeUnit::with_signature_and_fq(
5791 self.file.clone(),
5792 CodeUnitType::Class,
5793 scope.package_name.clone(),
5794 short_name,
5795 scope.template_signature.clone(),
5796 false,
5797 fq,
5798 );
5799 let has_body = definition_body_present;
5800 if !has_body && self.parsed.contains_declaration(&code_unit) {
5801 self.parsed.record_navigation_range(
5802 code_unit.clone(),
5803 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
5804 );
5805 return code_unit;
5806 }
5807 if has_body {
5808 let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
5815 if let Some(range) = explicit_range {
5816 self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
5817 } else {
5818 self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
5819 }
5820 for range in earlier_ranges {
5821 self.parsed.add_declaration_range(&code_unit, range);
5822 }
5823 } else {
5824 self.add_declaration(code_unit.clone(), declaration_node, None, None);
5825 }
5826 if let Some(raw_supertypes) = raw_supertypes {
5827 self.parsed
5828 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
5829 }
5830 self.parsed.add_signature(
5831 code_unit.clone(),
5832 render_cpp_type_signature(
5833 declaration_node,
5834 self.source,
5835 scope.template_signature.as_deref(),
5836 ),
5837 );
5838 if let Some(metadata) = &scope.template_metadata {
5839 let primary_short_name = if let Some(parent) = &scope.class_unit {
5840 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
5841 } else {
5842 metadata.primary_name.clone()
5843 };
5844 let primary_fq_name = CodeUnit::new(
5845 self.file.clone(),
5846 CodeUnitType::Class,
5847 scope.package_name.clone(),
5848 primary_short_name,
5849 )
5850 .fq_name();
5851 let mut metadata = metadata.clone();
5852 metadata.primary_fq_name = primary_fq_name;
5853 self.parsed
5854 .set_cpp_template_metadata(code_unit.clone(), metadata);
5855 }
5856 if let Some(parent) = &scope.class_unit {
5857 self.parsed.add_child(parent.clone(), code_unit.clone());
5858 } else if let Some(module) = &scope.module {
5859 self.parsed.add_child(module.clone(), code_unit.clone());
5860 }
5861
5862 if let Some(body) = body {
5863 let mut nested_scope = scope.clone();
5864 nested_scope.class_unit = Some(code_unit.clone());
5865 nested_scope.template_signature = scope.template_signature.clone();
5866 nested_scope.template_metadata = None;
5871 nested_scope.recovered_specialization_member_scope =
5874 scope.template_metadata.as_ref().is_some_and(|metadata| {
5875 declaration_node.kind() == "function_definition" && metadata.is_specialization()
5876 });
5877 nested_scope.declarations_are_fields =
5878 is_recovered_exported_class_container(declaration_node, self.source)
5879 || nested_scope.recovered_specialization_member_scope;
5880 if let Some(displaced) = displaced_macro_tail {
5881 push_cpp_sibling_range(
5889 body,
5890 displaced.split_index,
5891 usize::MAX,
5892 scope.clone(),
5893 stack,
5894 );
5895 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
5896 } else {
5897 stack.push(CppWork::Container(CppContainer {
5898 node: body,
5899 scope: nested_scope,
5900 }));
5901 }
5902 }
5903 if declaration_node.kind() == "enum_specifier" {
5904 self.visit_enum_enumerators(declaration_node, scope, &code_unit);
5905 if !self.has_enum_enumerator_units(&code_unit) {
5906 self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
5907 }
5908 }
5909 code_unit
5910 }
5911
5912 fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
5920 if self.field_owners.is_none() {
5921 self.field_owners = Some(CppFieldOwnerIndex::of(
5922 self.parsed.declarations().iter(),
5923 self.file,
5924 ));
5925 }
5926 debug_assert_eq!(
5927 parent.source(),
5928 self.file,
5929 "the walk's declarations are declarations of the file it is walking"
5930 );
5931 let carried = self
5932 .field_owners
5933 .as_ref()
5934 .expect("the index was just ensured")
5935 .owns_fields(parent.package_name(), parent.short_name());
5936
5937 #[cfg(debug_assertions)]
5938 assert_eq!(
5939 carried,
5940 cpp_declarations_hold_owned_fields(
5941 self.parsed.declarations(),
5942 self.file,
5943 parent.package_name(),
5944 parent.short_name()
5945 ),
5946 "the carried-forward field index must answer what a fresh declaration scan \
5947 answers for {}",
5948 parent.fq_name()
5949 );
5950
5951 carried
5952 }
5953
5954 fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
5955 walk_named_tree_preorder(node, false, |child| {
5956 if child.kind() != "enumerator" {
5957 return WalkControl::Continue;
5958 }
5959 let Some(name_node) = child.child_by_field_name("name") else {
5960 return WalkControl::Continue;
5961 };
5962 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
5963 if name.is_empty() {
5964 return WalkControl::Continue;
5965 }
5966 let code_unit = CodeUnit::new_fq(
5967 self.file.clone(),
5968 CodeUnitType::Field,
5969 scope.package_name.clone(),
5970 cpp_join_member_short(parent.short_name(), &name),
5971 parent
5972 .fq()
5973 .clone()
5974 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
5975 );
5976 if self.parsed.contains_declaration(&code_unit) {
5977 return WalkControl::Continue;
5978 }
5979 self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
5980 self.parsed.add_signature(
5981 code_unit,
5982 normalize_cpp_whitespace(node_text(child, self.source)),
5983 );
5984 WalkControl::Continue
5985 });
5986 }
5987
5988 fn visit_enum_enumerators_from_text(
5989 &mut self,
5990 node: Node<'_>,
5991 scope: &ScopeInfo,
5992 parent: &CodeUnit,
5993 ) {
5994 let text = node_text(node, self.source);
5995 let Some((_, body)) = text.split_once('{') else {
5996 return;
5997 };
5998 let Some((body, _)) = body.rsplit_once('}') else {
5999 return;
6000 };
6001 for entry in body.split(',') {
6002 let trimmed = entry.trim();
6003 let name = trimmed
6004 .split('=')
6005 .next()
6006 .unwrap_or("")
6007 .split_whitespace()
6008 .next()
6009 .unwrap_or("");
6010 if name.is_empty() {
6011 continue;
6012 }
6013 let code_unit = CodeUnit::new_fq(
6014 self.file.clone(),
6015 CodeUnitType::Field,
6016 scope.package_name.clone(),
6017 cpp_join_member_short(parent.short_name(), name),
6018 parent
6019 .fq()
6020 .clone()
6021 .with_pushed(cpp_segment(name, SegmentKind::Member)),
6022 );
6023 if self.parsed.contains_declaration(&code_unit) {
6024 continue;
6025 }
6026 self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
6027 self.parsed.add_signature(code_unit, trimmed.to_string());
6028 }
6029 }
6030
6031 fn visit_function_definition<'tree>(
6032 &mut self,
6033 node: Node<'tree>,
6034 scope: &ScopeInfo,
6035 stack: &mut Vec<CppWork<'tree>>,
6036 ancestry: &ParentIndex<'tree>,
6037 ) {
6038 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6045 return;
6046 }
6047 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6053 return;
6054 }
6055 if node.has_error() {
6056 self.visit_macro_swallowed_function_declarations(node, scope);
6057 }
6058 if let Some((class_node, name, raw_supertypes)) =
6059 recover_exported_class_function_definition(node, self.source)
6060 {
6061 if let Some(body) = cpp_body_node(node)
6062 && let Some(close) = self
6063 .orphaned_namespaces
6064 .matching_close_brace(body.start_byte())
6065 && close.end_byte < body.end_byte()
6066 {
6067 let class_range = Range {
6068 start_byte: node.start_byte(),
6069 end_byte: close.end_byte,
6070 start_line: node.start_position().row + 1,
6071 end_line: close.end_line,
6072 };
6073 let mut head = vec![node];
6078 let mut keyword = None;
6079 let mut name_node = None;
6080 while let Some(part) = head.pop() {
6081 if part.start_byte() >= body.start_byte() || part.is_missing() {
6082 continue;
6083 }
6084 if matches!(part.kind(), "class" | "struct" | "union") {
6085 keyword = Some(part);
6086 }
6087 if matches!(part.kind(), "identifier" | "type_identifier")
6088 && node_text(part, self.source) == name
6089 {
6090 name_node = Some(part);
6091 }
6092 let mut cursor = part.walk();
6093 head.extend(part.children(&mut cursor));
6094 }
6095 if let (Some(keyword), Some(name_node)) = (keyword, name_node)
6096 && let Some(type_name) = keyword
6097 .parent()
6098 .and_then(|parent| parent.child_by_field_name("name"))
6099 && let Some(tree) = parse_source_ranges_with_cancellation(
6100 &tree_sitter_cpp::LANGUAGE.into(),
6101 self.source,
6102 &[
6103 (keyword.start_byte(), type_name.start_byte()),
6104 (name_node.start_byte(), name_node.end_byte()),
6105 (body.start_byte(), close.end_byte),
6106 ],
6107 None,
6108 )
6109 && let Some(reparsed_class) = tree.root_node().named_child(0)
6110 && let Some(class_body) = cpp_body_node(reparsed_class)
6111 && class_body.start_byte() == body.start_byte()
6112 && class_body.end_byte() == close.end_byte
6113 && let Some(tail) =
6114 cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
6115 {
6116 let class_unit = self.visit_named_class_like_shape(
6117 class_node,
6118 name,
6119 None,
6120 true,
6121 Some(class_range),
6122 raw_supertypes,
6123 scope,
6124 stack,
6125 ancestry,
6126 );
6127 self.parsed.record_materialization(
6128 MaterializationRecord::RecoveredDeclaration {
6129 recovery: class_range,
6130 unit: class_unit.clone(),
6131 },
6132 );
6133 let member_scope = ScopeInfo {
6134 package_name: class_unit.package_name().to_string(),
6135 class_unit: Some(class_unit),
6136 declarations_are_fields: true,
6137 template_metadata: None,
6138 recovered_specialization_member_scope: false,
6139 ..scope.clone()
6140 };
6141 let class_body_range = class_body.byte_range();
6142 let tail_range = tail.root_node().byte_range();
6143 self.partitioned_regions
6144 .push((tail, tail_range, scope.clone()));
6145 self.partitioned_regions
6146 .push((tree, class_body_range, member_scope));
6147 return;
6148 }
6149 }
6150 let body = cpp_body_node(class_node);
6151 let displaced_namespace = cpp_body_node(node)
6152 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
6153 let fragmented = cpp_body_node(node).and_then(|body| {
6154 fragmented_export_function_body_region(
6155 node,
6156 body,
6157 self.source,
6158 displaced_namespace.as_ref(),
6159 )
6160 });
6161 if let Some(fragmented) = fragmented {
6167 if let Some(boundary) =
6171 fragmented_export_sibling_class_boundary(node, self.source, ancestry)
6172 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
6173 {
6174 let mut boundary_scope = scope.clone();
6175 for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
6176 if sibling.start_byte() >= boundary.start_byte() {
6177 break;
6178 }
6179 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
6180 boundary_scope.visible_using_namespaces.push(namespace);
6181 }
6182 }
6183 self.recovered_class_sibling_scopes
6184 .insert(boundary.id(), boundary_scope);
6185 }
6186 let mut recovered_constructor = None;
6187 let mut recovered_prefix_tree = None;
6188 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
6189 {
6190 Some(FragmentedExportMembers::Complete(tree)) => {
6191 if let Some(body) = body
6192 && let Some(range) =
6193 cpp_reparsed_synthetic_initializer_constructor_range(
6194 tree.root_node(),
6195 &name,
6196 self.source,
6197 body.end_byte(),
6198 )
6199 {
6200 recovered_constructor = Some(range);
6201 recovered_prefix_tree = Some(tree);
6202 None
6203 } else {
6204 Some(FragmentedExportMembers::Complete(tree))
6205 }
6206 }
6207 outcome => outcome,
6208 };
6209 let mut class_stack = Vec::new();
6210 let class_unit = self.visit_named_class_like_shape(
6211 class_node,
6212 name,
6213 None,
6214 true,
6215 Some(fragmented.class_range),
6216 raw_supertypes,
6217 scope,
6218 &mut class_stack,
6219 ancestry,
6220 );
6221 self.parsed
6222 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6223 recovery: fragmented.class_range,
6224 unit: class_unit.clone(),
6225 });
6226 let complete = outcome.is_some_and(|outcome| {
6227 self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
6228 });
6229 if complete {
6230 self.consumed_fragment_regions
6231 .push((node.start_byte(), fragmented.class_range.end_byte));
6232 } else {
6233 let member_scope = ScopeInfo {
6242 package_name: class_unit.package_name().to_string(),
6243 module: scope.module.clone(),
6244 class_unit: Some(class_unit.clone()),
6245 template_signature: scope.template_signature.clone(),
6246 template_metadata: None,
6247 declarations_are_fields: true,
6248 recovered_specialization_member_scope: false,
6249 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6250 };
6251 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6252 if candidate.start_byte() >= fragmented.reparse_end {
6253 break;
6254 }
6255 if cpp_fragment_sibling_is_class_member(
6256 candidate,
6257 fragmented.reparse_end,
6258 self.source,
6259 ) {
6260 self.recovered_class_sibling_scopes
6261 .insert(candidate.id(), member_scope.clone());
6262 }
6263 }
6264 if let Some(range) = recovered_constructor
6265 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
6266 {
6267 self.visit_recovered_fragment_prefix_members(
6268 prefix_tree.root_node(),
6269 range.start,
6270 &class_unit,
6271 scope,
6272 ancestry,
6273 );
6274 self.visit_recovered_fragment_constructor(
6275 range,
6276 body,
6277 class_node,
6278 &class_unit,
6279 scope,
6280 ancestry,
6281 );
6282 }
6283 }
6284 if let Some(boundary) = displaced_namespace {
6285 for item in boundary.namespace_items {
6286 self.recovered_class_sibling_scopes
6287 .insert(item.id(), scope.clone());
6288 }
6289 }
6290 stack.extend(class_stack);
6291 return;
6292 }
6293 let mut stack = Vec::new();
6294 let class_unit = self.visit_named_class_like_shape(
6295 class_node,
6296 name,
6297 body,
6298 body.is_some(),
6299 None,
6300 raw_supertypes,
6301 scope,
6302 &mut stack,
6303 ancestry,
6304 );
6305 self.parsed
6306 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6307 recovery: cpp_declaration_range(node),
6308 unit: class_unit,
6309 });
6310 if let Some(body) = body
6317 && let Some(class_close) = self
6318 .orphaned_namespaces
6319 .matching_close_brace(body.start_byte())
6320 && class_close.start_byte < body.end_byte()
6321 {
6322 let split = {
6323 let mut cursor = body.walk();
6324 body.named_children(&mut cursor)
6325 .position(|child| child.start_byte() > class_close.start_byte)
6326 };
6327 if let Some(split) = split {
6328 let seeded = stack.pop();
6333 match seeded {
6334 Some(CppWork::Container(container)) => {
6335 push_cpp_sibling_range(
6336 body,
6337 split,
6338 usize::MAX,
6339 scope.clone(),
6340 &mut stack,
6341 );
6342 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
6343 }
6344 _ => unreachable!("exported-class seed is always one Container"),
6347 }
6348 }
6349 }
6350 while let Some(work) = stack.pop() {
6351 match work {
6352 CppWork::Container(container) => {
6353 push_cpp_container_work(container.node, container.scope, &mut stack);
6354 }
6355 CppWork::Siblings(siblings) => {
6356 advance_cpp_siblings(siblings, self.source, &mut stack);
6357 }
6358 CppWork::Node(work) => {
6359 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
6360 }
6361 }
6362 }
6363 return;
6364 }
6365 let recovered_constraint_constructor =
6366 cpp_recovered_template_macro_constructor(node, self.source);
6367 let declarator = recovered_constraint_constructor
6368 .map(|(declarator, _)| declarator)
6369 .or_else(|| node.child_by_field_name("declarator"));
6370 let Some(declarator) = declarator else {
6371 self.visit_malformed_function_definition_container(node, scope, stack);
6372 return;
6373 };
6374 let Some(function_declarator) = extract_function_declarator(declarator) else {
6375 self.visit_malformed_function_definition_container(node, scope, stack);
6376 return;
6377 };
6378 let function = if let Some((_, callable_name)) =
6379 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
6380 {
6381 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
6382 } else {
6383 extract_function_info(function_declarator, self.source, scope)
6384 };
6385 let Some(mut function) = function else {
6386 self.visit_malformed_function_definition_container(node, scope, stack);
6387 return;
6388 };
6389 if let Some((_, template_parameter)) = recovered_constraint_constructor {
6390 function.signature = format!(
6391 "template <{}>{}",
6392 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
6393 function.signature
6394 );
6395 }
6396 let code_unit = function.code_unit(self.file.clone());
6397 self.add_declaration(code_unit.clone(), node, None, None);
6402 let signature = if recovered_constraint_constructor.is_some() {
6403 normalize_cpp_whitespace(node_text(function_declarator, self.source))
6404 } else {
6405 render_cpp_function_display_signature_from_node(
6406 node,
6407 self.source,
6408 scope.template_signature.as_deref(),
6409 true,
6410 ancestry,
6411 )
6412 };
6413 self.parsed.add_signature_with_metadata(
6414 code_unit.clone(),
6415 cpp_signature_metadata(
6416 signature,
6417 function_declarator,
6418 cpp_callable_linkage(node, self.source, ancestry),
6419 self.source,
6420 ancestry,
6421 )
6422 .with_declaration_only(false),
6423 );
6424 if let Some(parent) = &scope.class_unit {
6425 self.parsed.add_child(parent.clone(), code_unit);
6426 } else if let Some(module) = &scope.module {
6427 self.parsed.add_child(module.clone(), code_unit);
6428 }
6429 self.visit_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
6430 }
6431
6432 fn scope_for_recovered_exported_class<'tree>(
6437 &mut self,
6438 node: Node<'tree>,
6439 name: &str,
6440 definition_body_present: bool,
6441 scope: &ScopeInfo,
6442 ancestry: &ParentIndex<'tree>,
6443 ) -> ScopeInfo {
6444 if !definition_body_present
6445 || !scope.package_name.is_empty()
6446 || scope.class_unit.is_some()
6447 || !(is_recovered_exported_class_container(node, self.source)
6448 || recover_function_like_export_class_pair(node, self.source).is_some()
6449 || recover_embedded_function_like_export_classes(node, self.source)
6450 .iter()
6451 .any(|recovered| recovered.name == name)
6452 || matches!(node.kind(), "declaration" | "field_declaration")
6453 && recover_exported_class_declaration(node, self.source).is_some()
6454 || matches!(
6455 node.kind(),
6456 "class_specifier" | "struct_specifier" | "union_specifier"
6457 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
6458 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
6459 name_node,
6460 self.source,
6461 )))
6462 }) || ancestry.parent(node).is_some_and(|parent| {
6463 matches!(parent.kind(), "declaration" | "field_declaration")
6464 && recover_exported_class_declaration(parent, self.source).is_some()
6465 || is_recovered_exported_class_container(parent, self.source)
6466 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
6467 {
6468 return scope.clone();
6469 }
6470 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
6471 let Some(package_name) = borrowed_namespace
6472 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
6473 else {
6474 return scope.clone();
6475 };
6476
6477 let module = CodeUnit::new_fq(
6478 self.file.clone(),
6479 CodeUnitType::Module,
6480 "",
6481 package_name.clone(),
6482 cpp_namespace_fq(&package_name),
6483 );
6484 let mut recovered = scope.clone();
6485 recovered.package_name = package_name;
6486 recovered.module = Some(module);
6487 recovered
6488 }
6489
6490 fn unique_earlier_namespace_forward<'tree>(
6499 &mut self,
6500 recovered_node: Node<'tree>,
6501 name: &str,
6502 ancestry: &ParentIndex<'tree>,
6503 ) -> Option<String> {
6504 let mut root = recovered_node;
6505 while let Some(parent) = ancestry.parent(root) {
6506 root = parent;
6507 }
6508 let source = self.source;
6509 let scan = self
6510 .namespace_forward_scans
6511 .entry(CppTreeIdentity::of(root))
6512 .or_default();
6513 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
6514 let borrowed = scan.unique_earlier_forward(name, recovered_node);
6515
6516 #[cfg(debug_assertions)]
6517 assert_eq!(
6518 borrowed,
6519 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
6520 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
6521 for {name} at byte {}",
6522 recovered_node.start_byte()
6523 );
6524
6525 borrowed
6526 }
6527
6528 fn visit_malformed_function_definition_container<'tree>(
6529 &mut self,
6530 node: Node<'tree>,
6531 scope: &ScopeInfo,
6532 stack: &mut Vec<CppWork<'tree>>,
6533 ) {
6534 let Some(body) = cpp_body_node(node) else {
6535 return;
6536 };
6537 if !cpp_contains_namespace_definition(body) {
6538 return;
6539 }
6540 stack.push(CppWork::Container(CppContainer {
6541 node: body,
6542 scope: scope.clone(),
6543 }));
6544 }
6545
6546 fn record_recovered_declarations(
6564 &mut self,
6565 recovery: Range,
6566 reparse_walk: impl FnOnce(&mut Self),
6567 ) {
6568 #[cfg(any(debug_assertions, test))]
6571 let before = self.parsed.declarations().clone();
6572
6573 self.recovery_captures.push(CppRecoveryCapture::default());
6574 reparse_walk(self);
6575 let captured = self
6576 .recovery_captures
6577 .pop()
6578 .expect("the capture this call pushed is the one it pops");
6579
6580 let mut minted: Vec<CodeUnit> = captured
6586 .created
6587 .into_iter()
6588 .filter(|unit| self.parsed.contains_declaration(unit))
6589 .collect();
6590 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6591
6592 #[cfg(any(debug_assertions, test))]
6593 {
6594 let mut rediscovered: Vec<CodeUnit> = self
6595 .parsed
6596 .declarations()
6597 .iter()
6598 .filter(|unit| !before.contains(*unit))
6599 .cloned()
6600 .collect();
6601 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6602 assert_eq!(
6603 minted, rediscovered,
6604 "the captured recovered set must be the declaration delta of the reparse \
6605 walk over {recovery:?}"
6606 );
6607 }
6608
6609 for unit in minted {
6610 self.parsed
6611 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6612 recovery,
6613 unit,
6614 });
6615 }
6616 }
6617
6618 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
6621 let start = self
6622 .parsed
6623 .declaration_ranges(unit)
6624 .first()
6625 .map(|range| range.start_byte)
6626 .unwrap_or(usize::MAX);
6627 (start, unit.fq_name().to_string())
6628 }
6629
6630 fn visit_sentinel_macro_region<'tree>(
6631 &mut self,
6632 node: Node<'tree>,
6633 scope: &ScopeInfo,
6634 stack: &mut Vec<CppWork<'tree>>,
6635 ancestry: &ParentIndex<'tree>,
6636 ) -> bool {
6637 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
6638 return true;
6639 }
6640 if let Some((
6641 reparse_start,
6642 class_start,
6643 body_start,
6644 class_close_start,
6645 class_close_end,
6646 class_close_line,
6647 )) = cpp_sentinel_macro_class_region(node, self.source)
6648 {
6649 let Some(class_tree) =
6650 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
6651 else {
6652 return false;
6653 };
6654 let class_root = class_tree.root_node();
6655 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
6656 let class_ancestry = ParentIndex::new(class_root);
6658 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
6659 class_root,
6660 template_node,
6661 self.source,
6662 &class_ancestry,
6663 ) else {
6664 return false;
6665 };
6666 let class_node = reparsed_class.declaration_node;
6667 let name = reparsed_class.name;
6668 let mut class_scope = scope.clone();
6669 if let Some(template_node) = template_node {
6670 class_scope.template_signature =
6671 cpp_template_signature(template_node, class_node, self.source);
6672 class_scope.template_metadata =
6673 cpp_template_metadata(template_node, class_node, self.source, ancestry);
6674 }
6675 let Some(body_tree) =
6676 cpp_reparse_region_items(self.source, body_start, class_close_start)
6677 else {
6678 return false;
6679 };
6680 let raw_supertypes = reparsed_class.raw_supertypes;
6681 let class_range = Range {
6682 start_byte: class_start,
6683 end_byte: class_close_end,
6684 start_line: class_node.start_position().row + 1,
6685 end_line: class_close_line,
6686 };
6687 let class_scope = self.scope_for_recovered_exported_class(
6688 class_node,
6689 &name,
6690 true,
6691 &class_scope,
6692 ancestry,
6693 );
6694 let mut class_stack = Vec::new();
6695 let class_unit = self.visit_named_class_like_shape(
6696 class_node,
6697 name,
6698 None,
6699 true,
6700 Some(class_range),
6701 raw_supertypes,
6702 &class_scope,
6703 &mut class_stack,
6704 ancestry,
6705 );
6706 self.parsed
6707 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6708 recovery: class_range,
6709 unit: class_unit.clone(),
6710 });
6711 let member_scope = ScopeInfo {
6712 package_name: class_scope.package_name.clone(),
6713 module: class_scope.module.clone(),
6714 class_unit: Some(class_unit),
6715 template_signature: class_scope.template_signature.clone(),
6716 template_metadata: None,
6717 declarations_are_fields: true,
6718 recovered_specialization_member_scope: false,
6719 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
6720 };
6721 let body_root = body_tree.root_node();
6723 self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
6724 self.consumed_fragment_regions
6727 .push((node.start_byte(), class_close_end));
6728 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
6735 stack.push(CppWork::Container(CppContainer {
6736 node,
6737 scope: scope.clone(),
6738 }));
6739 }
6740 return true;
6741 }
6742 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
6743 return false;
6744 };
6745 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
6746 return false;
6747 };
6748 let root = tree.root_node();
6749 if !cpp_reparsed_items_are_indexable(root, self.source) {
6750 return false;
6751 }
6752 let recovery = cpp_recovery_window(self.source, start, end);
6753 let reparsed_ancestry = ParentIndex::new(root);
6755 self.record_recovered_declarations(recovery, |visitor| {
6756 visitor.visit_container(
6757 root,
6758 &reparsed_ancestry,
6759 &scope.package_name,
6760 scope.module.clone(),
6761 scope.class_unit.clone(),
6762 scope.template_signature.clone(),
6763 scope.visible_using_namespaces.clone(),
6764 );
6765 });
6766 if end > node.end_byte() {
6767 self.consumed_fragment_regions
6768 .push((node.start_byte(), end));
6769 } else if node.kind() == "ERROR" && node.end_byte() > end {
6770 self.consumed_fragment_regions
6777 .push((node.start_byte(), end));
6778 stack.push(CppWork::Container(CppContainer {
6779 node,
6780 scope: scope.clone(),
6781 }));
6782 }
6783 true
6784 }
6785
6786 fn visit_nested_namespace_sentinel<'tree>(
6792 &mut self,
6793 node: Node<'tree>,
6794 scope: &ScopeInfo,
6795 ancestry: &ParentIndex<'tree>,
6796 ) -> bool {
6797 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
6798 return false;
6799 };
6800
6801 let mut package_name = scope.package_name.clone();
6802 let mut module = scope.module.clone();
6803 for component in recovered.namespace_components {
6804 package_name = if package_name.is_empty() {
6805 component
6806 } else {
6807 format!("{package_name}::{component}")
6808 };
6809 let namespace_module = CodeUnit::new_fq(
6810 self.file.clone(),
6811 CodeUnitType::Module,
6812 "",
6813 package_name.clone(),
6814 cpp_namespace_fq(&package_name),
6815 );
6816 if !self.parsed.contains_declaration(&namespace_module) {
6817 self.add_declaration(namespace_module.clone(), recovered.function, None, None);
6818 }
6819 module = Some(namespace_module);
6820 }
6821
6822 let recovered_scope = ScopeInfo {
6823 package_name,
6824 module,
6825 class_unit: None,
6836 template_signature: scope.template_signature.clone(),
6837 template_metadata: scope.template_metadata.clone(),
6838 declarations_are_fields: false,
6839 recovered_specialization_member_scope: false,
6840 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6841 };
6842 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
6843 recovered.function,
6844 recovered.body,
6845 self.source,
6846 ancestry,
6847 ) {
6848 let mut class_scope = recovered_scope.clone();
6849 if let Some(template_node) = fragmented.template_node {
6850 class_scope.template_signature =
6851 cpp_template_signature(template_node, fragmented.class_node, self.source);
6852 class_scope.template_metadata = cpp_template_metadata(
6853 template_node,
6854 fragmented.class_node,
6855 self.source,
6856 ancestry,
6857 );
6858 }
6859 if let Some(outcome) = self
6860 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
6861 {
6862 let mut class_stack = Vec::new();
6863 let class_unit = self.visit_named_class_like_shape(
6864 fragmented.class_node,
6865 fragmented.name.clone(),
6866 None,
6867 true,
6868 Some(fragmented.fragmented.class_range),
6869 fragmented.raw_supertypes.clone(),
6870 &class_scope,
6871 &mut class_stack,
6872 ancestry,
6873 );
6874 self.parsed
6875 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6876 recovery: fragmented.fragmented.class_range,
6877 unit: class_unit.clone(),
6878 });
6879 if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
6880 self.consumed_fragment_regions.push((
6881 fragmented.consumed_start,
6882 fragmented.fragmented.class_range.end_byte,
6883 ));
6884 }
6885 }
6886 }
6887 self.run_container_work(recovered.body, recovered_scope, ancestry);
6892 true
6893 }
6894
6895 fn visit_declaration<'tree>(
6896 &mut self,
6897 node: Node<'tree>,
6898 scope: &ScopeInfo,
6899 in_class_body: bool,
6900 stack: &mut Vec<CppWork<'tree>>,
6901 ancestry: &ParentIndex<'tree>,
6902 ) {
6903 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6904 return;
6905 }
6906 if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
6907 return;
6908 }
6909 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
6910 !cpp_active_template_type_parameter(
6911 node,
6912 node_text(declarator, self.source),
6913 self.source,
6914 ancestry,
6915 )
6916 }) {
6917 return;
6918 }
6919 if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
6920 self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
6926 return;
6927 }
6928 if in_class_body
6929 && let Some(parent) = scope.class_unit.as_ref()
6930 && let Some(call) =
6931 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
6932 {
6933 self.visit_recovered_macro_qualified_constructor_definition(
6934 node, call, scope, ancestry,
6935 );
6936 return;
6937 }
6938 if in_class_body
6939 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
6940 {
6941 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
6942 return;
6943 }
6944 if in_class_body
6945 && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
6946 {
6947 for member in members {
6948 self.add_macro_wrapped_declaration(member, scope, ancestry);
6949 }
6950 return;
6951 }
6952 if in_class_body
6953 && let Some(declarators) =
6954 recovered_macro_qualified_field_declarators(node, self.source)
6955 {
6956 for declarator in declarators {
6957 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6958 }
6959 return;
6960 }
6961 let recovered_alias_names = recovered_type_alias_names(node, self.source);
6962 if !recovered_alias_names.is_empty() {
6963 self.add_type_aliases(node, scope, recovered_alias_names, ancestry);
6964 return;
6965 }
6966 if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
6967 return;
6968 }
6969 if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
6970 return;
6971 }
6972
6973 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
6974 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
6975 if let Some(outcome) =
6980 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
6981 {
6982 let consumed_region = (
6983 recovered.declaration_node.end_byte(),
6984 fragmented.class_range.end_byte,
6985 );
6986 let code_unit = self.visit_named_class_like_shape(
6987 recovered.declaration_node,
6988 recovered.name,
6989 None,
6990 true,
6991 Some(fragmented.class_range),
6992 recovered.raw_supertypes,
6993 scope,
6994 stack,
6995 ancestry,
6996 );
6997 self.parsed.record_materialization(
6998 MaterializationRecord::RecoveredDeclaration {
6999 recovery: fragmented.class_range,
7000 unit: code_unit.clone(),
7001 },
7002 );
7003 let consume_fragment =
7004 self.visit_fragmented_export_class_members(outcome, code_unit, scope);
7005 if consume_fragment {
7011 self.consumed_fragment_regions.push(consumed_region);
7012 }
7013 return;
7014 }
7015 }
7016 let uses_initializer_body = recovered.uses_initializer_body;
7017 let definition_body_present = recovered.body.is_some();
7018 let class_unit = self.visit_named_class_like_shape(
7019 recovered.declaration_node,
7020 recovered.name,
7021 recovered.body,
7022 definition_body_present,
7023 None,
7024 recovered.raw_supertypes,
7025 scope,
7026 stack,
7027 ancestry,
7028 );
7029 self.parsed
7030 .record_materialization(MaterializationRecord::RecoveredDeclaration {
7031 recovery: cpp_declaration_range(node),
7032 unit: class_unit,
7033 });
7034 if uses_initializer_body {
7035 return;
7036 }
7037 }
7038
7039 if in_class_body && cpp_unterminated_macro_invocation_field(node) {
7050 return;
7051 }
7052
7053 let mut handled_function = false;
7054 let mut handled_declarator = false;
7055 let mut cursor = node.walk();
7056 for child in node.named_children(&mut cursor) {
7057 if matches!(
7058 child.kind(),
7059 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
7060 ) {
7061 if cpp_body_node(child).is_some() {
7070 self.visit_class_like(child, scope, stack, ancestry);
7071 }
7072 continue;
7073 }
7074 }
7075
7076 let mut cursor = node.walk();
7077 for child in node.children_by_field_name("declarator", &mut cursor) {
7078 if crate::structural::is_recovered_designator_init_declarator(child) {
7079 handled_declarator = true;
7080 continue;
7081 }
7082 if in_class_body
7083 && let Some(field) = recovered_function_like_field_declarator(node, self.source)
7084 {
7085 handled_declarator = true;
7086 self.visit_variable_declaration(node, field.name, scope, true, ancestry);
7087 continue;
7088 }
7089 if let Some(kind) = classify_declarator(child) {
7090 handled_declarator = true;
7091 match kind {
7092 DeclaratorKind::Function(function_declarator) => {
7093 handled_function = true;
7094 self.visit_function_declaration(node, function_declarator, scope, ancestry);
7095 }
7096 DeclaratorKind::Variable(variable_declarator) => {
7097 self.visit_variable_declaration(
7098 node,
7099 variable_declarator,
7100 scope,
7101 in_class_body,
7102 ancestry,
7103 );
7104 }
7105 }
7106 }
7107 }
7108
7109 if !handled_declarator {
7110 let mut cursor = node.walk();
7111 for child in node.named_children(&mut cursor) {
7112 if crate::structural::is_recovered_designator_init_declarator(child) {
7113 handled_declarator = true;
7114 continue;
7115 }
7116 if !is_unfielded_declarator_candidate(child) {
7117 continue;
7118 }
7119 let Some(kind) = classify_declarator(child) else {
7120 continue;
7121 };
7122 handled_declarator = true;
7123 match kind {
7124 DeclaratorKind::Function(function_declarator) => {
7125 handled_function = true;
7126 self.visit_function_declaration(node, function_declarator, scope, ancestry);
7127 }
7128 DeclaratorKind::Variable(variable_declarator) => {
7129 self.visit_variable_declaration(
7130 node,
7131 variable_declarator,
7132 scope,
7133 in_class_body,
7134 ancestry,
7135 );
7136 }
7137 }
7138 }
7139 }
7140
7141 if handled_function {
7142 return;
7143 }
7144
7145 if !handled_declarator {
7146 if in_class_body {
7147 self.visit_class_members_from_declaration(node, scope, ancestry);
7148 } else {
7149 self.visit_global_variables_from_declaration(node, scope, ancestry);
7150 }
7151 }
7152 }
7153
7154 fn visit_anonymous_aggregate_declaration<'tree>(
7168 &mut self,
7169 node: Node<'tree>,
7170 scope: &ScopeInfo,
7171 in_class_body: bool,
7172 stack: &mut Vec<CppWork<'tree>>,
7173 ancestry: &ParentIndex<'tree>,
7174 ) -> bool {
7175 if !in_class_body || scope.class_unit.is_none() {
7176 return false;
7177 }
7178 let Some(aggregate) = node.child_by_field_name("type") else {
7179 return false;
7180 };
7181 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7182 || aggregate.child_by_field_name("name").is_some()
7183 {
7184 return false;
7185 }
7186 let Some(body) = cpp_body_node(aggregate) else {
7187 return false;
7188 };
7189
7190 let mut cursor = node.walk();
7191 let declarators = node
7192 .children_by_field_name("declarator", &mut cursor)
7193 .filter_map(|declarator| match classify_declarator(declarator) {
7194 Some(DeclaratorKind::Variable(variable)) => Some(variable),
7195 Some(DeclaratorKind::Function(_)) | None => None,
7196 })
7197 .collect::<Vec<_>>();
7198 if declarators.is_empty() {
7199 stack.push(CppWork::Container(CppContainer {
7200 node: body,
7201 scope: scope.clone(),
7202 }));
7203 return true;
7204 }
7205
7206 for declarator in declarators {
7207 let Some(name) = extract_variable_name(declarator, self.source) else {
7208 continue;
7209 };
7210 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
7211 self.visit_named_class_like_shape(
7216 aggregate,
7217 name,
7218 Some(body),
7219 true,
7220 Some(cpp_declaration_range(node)),
7221 None,
7222 scope,
7223 stack,
7224 ancestry,
7225 );
7226 }
7227 true
7228 }
7229
7230 fn visit_anonymous_local_aggregate_declaration<'tree>(
7239 &mut self,
7240 node: Node<'tree>,
7241 scope: &ScopeInfo,
7242 stack: &mut Vec<CppWork<'tree>>,
7243 ancestry: &ParentIndex<'tree>,
7244 ) -> bool {
7245 if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
7246 return false;
7247 }
7248 let Some(aggregate) = node.child_by_field_name("type") else {
7249 return false;
7250 };
7251 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7252 || aggregate.child_by_field_name("name").is_some()
7253 {
7254 return false;
7255 }
7256 let Some(body) = cpp_body_node(aggregate) else {
7257 return false;
7258 };
7259 let mut cursor = node.walk();
7260 let declarators = node
7261 .children_by_field_name("declarator", &mut cursor)
7262 .filter_map(|declarator| match classify_declarator(declarator) {
7263 Some(DeclaratorKind::Variable(variable)) => Some(variable),
7264 Some(DeclaratorKind::Function(_)) | None => None,
7265 })
7266 .collect::<Vec<_>>();
7267 if declarators.is_empty() {
7268 return false;
7269 }
7270
7271 for declarator in &declarators {
7272 self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
7273 }
7274 let name = format!("<anonymous:{}>", aggregate.start_byte());
7275 self.visit_named_class_like_shape(
7276 aggregate,
7277 name,
7278 Some(body),
7279 true,
7280 None,
7281 None,
7282 scope,
7283 stack,
7284 ancestry,
7285 );
7286 true
7287 }
7288
7289 fn visit_function_declaration<'tree>(
7290 &mut self,
7291 declaration_node: Node<'tree>,
7292 declarator: Node<'tree>,
7293 scope: &ScopeInfo,
7294 ancestry: &ParentIndex<'tree>,
7295 ) {
7296 let Some(function) = extract_function_info(declarator, self.source, scope) else {
7297 return;
7298 };
7299 let code_unit = function.code_unit(self.file.clone());
7305 if self.parsed.contains_declaration(&code_unit) {
7306 self.parsed
7307 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7308 return;
7309 }
7310 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7311 let signature = render_cpp_function_display_signature_from_node(
7312 declaration_node,
7313 self.source,
7314 scope.template_signature.as_deref(),
7315 false,
7316 ancestry,
7317 );
7318 self.parsed.add_signature_with_metadata(
7319 code_unit.clone(),
7320 cpp_signature_metadata(
7321 signature,
7322 declarator,
7323 cpp_callable_linkage(declaration_node, self.source, ancestry),
7324 self.source,
7325 ancestry,
7326 )
7327 .with_declaration_only(true),
7328 );
7329 if let Some(parent) = &scope.class_unit {
7330 self.parsed.add_child(parent.clone(), code_unit);
7331 } else if let Some(module) = &scope.module {
7332 self.parsed.add_child(module.clone(), code_unit);
7333 }
7334 }
7335
7336 fn visit_recovered_macro_qualified_function_declaration<'tree>(
7337 &mut self,
7338 declaration_node: Node<'tree>,
7339 call: Node<'tree>,
7340 scope: &ScopeInfo,
7341 ancestry: &ParentIndex<'tree>,
7342 ) {
7343 let Some(parent) = &scope.class_unit else {
7344 return;
7345 };
7346 let Some(name_node) = call.child_by_field_name("function") else {
7347 return;
7348 };
7349 let Some(arguments) = call.child_by_field_name("arguments") else {
7350 return;
7351 };
7352 let Some((signature, parameter_labels)) =
7353 recovered_macro_qualified_function_parameters(arguments, self.source)
7354 else {
7355 return;
7356 };
7357 let arity = parameter_labels.len();
7358 let function = FunctionInfo {
7359 package_name: scope.package_name.clone(),
7360 owner: Some(CppMemberOwner::Unit(parent.clone())),
7361 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
7362 signature,
7363 };
7364 if function.name.is_empty() {
7365 return;
7366 }
7367 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7368 if self.parsed.contains_declaration(&code_unit) {
7369 self.parsed
7370 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7371 return;
7372 }
7373 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7374 let signature_label = render_cpp_function_display_signature_from_node(
7375 declaration_node,
7376 self.source,
7377 scope.template_signature.as_deref(),
7378 false,
7379 ancestry,
7380 );
7381 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
7382 let modifiers =
7387 cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
7388 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7389 .with_declaration_only(true)
7390 .with_callable_arity(CallableArity::exact(arity))
7391 .with_callable_linkage(linkage)
7392 .with_callable_modifiers(
7393 modifiers.is_static,
7394 modifiers.is_constructor,
7395 modifiers.visibility,
7396 );
7397 self.parsed
7398 .add_signature_with_metadata(code_unit.clone(), metadata);
7399 self.parsed.add_child(parent.clone(), code_unit);
7400 }
7401
7402 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
7403 &mut self,
7404 declaration_node: Node<'tree>,
7405 call: Node<'tree>,
7406 scope: &ScopeInfo,
7407 ancestry: &ParentIndex<'tree>,
7408 ) {
7409 let Some(parent) = &scope.class_unit else {
7410 return;
7411 };
7412 let Some(arguments) = call.child_by_field_name("arguments") else {
7413 return;
7414 };
7415 let Some((mut signature, parameter_labels)) =
7416 recovered_macro_qualified_function_parameters(arguments, self.source)
7417 else {
7418 return;
7419 };
7420 if let Some(template_signature) = &scope.template_signature {
7421 signature = format!("{template_signature}{signature}");
7422 }
7423 let arity = parameter_labels.len();
7424 let function = FunctionInfo {
7425 package_name: scope.package_name.clone(),
7426 owner: Some(CppMemberOwner::Unit(parent.clone())),
7427 name: parent.identifier().to_string(),
7428 signature,
7429 };
7430 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7431 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7432 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
7433 let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
7434 let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
7438 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7439 .with_declaration_only(false)
7440 .with_callable_arity(CallableArity::exact(arity))
7441 .with_callable_linkage(linkage)
7442 .with_callable_modifiers(
7443 modifiers.is_static,
7444 modifiers.is_constructor,
7445 modifiers.visibility,
7446 );
7447 self.parsed
7448 .add_signature_with_metadata(code_unit.clone(), metadata);
7449 self.parsed.add_child(parent.clone(), code_unit);
7450 }
7451
7452 fn visit_variable_declaration<'tree>(
7453 &mut self,
7454 declaration_node: Node<'tree>,
7455 declarator: Node<'tree>,
7456 scope: &ScopeInfo,
7457 in_class_body: bool,
7458 ancestry: &ParentIndex<'tree>,
7459 ) {
7460 let Some(name) = extract_variable_name(declarator, self.source) else {
7461 return;
7462 };
7463 let parent = if in_class_body {
7464 let Some(parent) = &scope.class_unit else {
7465 return;
7466 };
7467 Some(parent)
7468 } else {
7469 None
7470 };
7471 let short_name = match parent {
7472 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
7473 None => name.clone(),
7474 };
7475 let fq = cpp_leaf_fq(
7476 &scope.package_name,
7477 parent,
7478 &name,
7479 SegmentKind::Member,
7480 SegmentKind::Member,
7481 );
7482 let code_unit = CodeUnit::new_fq(
7483 self.file.clone(),
7484 CodeUnitType::Field,
7485 scope.package_name.clone(),
7486 short_name,
7487 fq,
7488 );
7489 if self.parsed.contains_declaration(&code_unit) {
7490 return;
7491 }
7492 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7493 self.parsed.add_signature_with_metadata(
7494 code_unit.clone(),
7495 SignatureMetadata::new(
7496 render_cpp_field_signature(declaration_node, declarator, self.source),
7497 Vec::new(),
7498 )
7499 .with_cpp_field_linkage(cpp_field_declaration_linkage(
7500 declaration_node,
7501 self.source,
7502 ancestry,
7503 )),
7504 );
7505 if let Some(parent) = &scope.class_unit {
7506 self.parsed.add_child(parent.clone(), code_unit);
7507 } else if let Some(module) = &scope.module {
7508 self.parsed.add_child(module.clone(), code_unit);
7509 }
7510 }
7511
7512 fn visit_class_members_from_declaration<'tree>(
7513 &mut self,
7514 node: Node<'tree>,
7515 scope: &ScopeInfo,
7516 ancestry: &ParentIndex<'tree>,
7517 ) {
7518 let mut cursor = node.walk();
7519 for child in node.named_children(&mut cursor) {
7520 if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
7521 self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
7522 } else if child.kind() == "init_declarator"
7523 && let Some(inner) = child.child_by_field_name("declarator")
7524 {
7525 self.visit_variable_declaration(node, inner, scope, true, ancestry);
7526 } else if matches!(
7527 child.kind(),
7528 "identifier"
7529 | "field_identifier"
7530 | "pointer_declarator"
7531 | "reference_declarator"
7532 | "array_declarator"
7533 | "parenthesized_declarator"
7534 ) {
7535 self.visit_variable_declaration(node, child, scope, true, ancestry);
7536 }
7537 }
7538 }
7539
7540 fn visit_global_variables_from_declaration<'tree>(
7541 &mut self,
7542 node: Node<'tree>,
7543 scope: &ScopeInfo,
7544 ancestry: &ParentIndex<'tree>,
7545 ) {
7546 let mut cursor = node.walk();
7547 for child in node.named_children(&mut cursor) {
7548 if child.kind() == "init_declarator"
7549 && let Some(inner) = child.child_by_field_name("declarator")
7550 {
7551 self.visit_variable_declaration(node, inner, scope, false, ancestry);
7552 } else if matches!(
7553 child.kind(),
7554 "identifier"
7555 | "field_identifier"
7556 | "pointer_declarator"
7557 | "reference_declarator"
7558 | "array_declarator"
7559 | "parenthesized_declarator"
7560 ) {
7561 self.visit_variable_declaration(node, child, scope, false, ancestry);
7562 }
7563 }
7564 }
7565
7566 fn visit_type_declaration<'tree>(
7567 &mut self,
7568 node: Node<'tree>,
7569 scope: &ScopeInfo,
7570 stack: &mut Vec<CppWork<'tree>>,
7571 ancestry: &ParentIndex<'tree>,
7572 ) {
7573 let type_node = node.child_by_field_name("type");
7574 if let Some(type_node) = type_node
7575 && matches!(
7576 type_node.kind(),
7577 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
7578 )
7579 {
7580 self.visit_class_like(type_node, scope, stack, ancestry);
7581 }
7582
7583 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
7584 let range = Range {
7585 start_byte: node.start_byte(),
7586 end_byte: recovered.end_node.end_byte(),
7587 start_line: node.start_position().row + 1,
7588 end_line: recovered.end_node.end_position().row + 1,
7589 };
7590 let signature = self
7591 .source
7592 .get(range.start_byte..range.end_byte)
7593 .map(normalize_cpp_whitespace)
7594 .unwrap_or_default();
7595 self.record_type_aliases(
7596 node,
7597 scope,
7598 vec![recovered.name],
7599 signature,
7600 range,
7601 ancestry,
7602 );
7603 return;
7604 }
7605
7606 let alias_names = match node.kind() {
7607 "alias_declaration" => extract_alias_declaration_name(node, self.source)
7608 .into_iter()
7609 .collect::<Vec<_>>(),
7610 "type_definition" => extract_typedef_alias_names(node, self.source),
7611 _ => Vec::new(),
7612 };
7613 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
7614 (type_node, alias_names.as_slice())
7615 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
7616 && type_node.child_by_field_name("name").is_none()
7617 {
7618 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
7619 } else {
7620 None
7621 };
7622 self.add_type_aliases(node, scope, alias_names, ancestry);
7623 if let Some((body, alias_name)) = anonymous_aggregate {
7624 let signature = normalize_cpp_whitespace(node_text(node, self.source));
7630 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
7631 debug_assert!(self.parsed.contains_declaration(&alias_unit));
7632 let mut nested_scope = scope.clone();
7633 nested_scope.class_unit = Some(alias_unit);
7634 nested_scope.template_signature = scope.template_signature.clone();
7635 nested_scope.template_metadata = None;
7636 nested_scope.declarations_are_fields = false;
7637 nested_scope.recovered_specialization_member_scope = false;
7638 stack.push(CppWork::Container(CppContainer {
7639 node: body,
7640 scope: nested_scope,
7641 }));
7642 }
7643 }
7644
7645 fn add_type_aliases(
7646 &mut self,
7647 node: Node<'_>,
7648 scope: &ScopeInfo,
7649 alias_names: Vec<String>,
7650 ancestry: &ParentIndex<'_>,
7651 ) {
7652 let signature = normalize_cpp_whitespace(node_text(node, self.source));
7653 self.record_type_aliases(
7654 node,
7655 scope,
7656 alias_names,
7657 signature,
7658 cpp_declaration_range(node),
7659 ancestry,
7660 );
7661 }
7662
7663 fn record_type_aliases(
7664 &mut self,
7665 node: Node<'_>,
7666 scope: &ScopeInfo,
7667 alias_names: Vec<String>,
7668 signature: String,
7669 range: Range,
7670 ancestry: &ParentIndex<'_>,
7671 ) {
7672 if signature.is_empty() {
7673 return;
7674 }
7675 let type_name = node
7676 .child_by_field_name("type")
7677 .and_then(|type_node| type_node.child_by_field_name("name"))
7678 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
7679 for alias_name in alias_names {
7680 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
7681 continue;
7682 }
7683 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
7684 self.add_declaration_with_range(code_unit.clone(), range, None, None);
7687 let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
7688 let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
7689 cpp_structured_type_identity(type_node, self.source, &lexical_scope)
7690 });
7691 self.parsed.add_signature_with_metadata(
7692 code_unit.clone(),
7693 SignatureMetadata::new(signature.clone(), Vec::new())
7694 .with_underlying_type_identity(underlying_type_identity),
7695 );
7696 if let Some(metadata) = &scope.template_metadata {
7697 let mut metadata = metadata.clone();
7698 metadata.primary_fq_name = code_unit.fq_name();
7699 self.parsed
7700 .set_cpp_template_metadata(code_unit.clone(), metadata);
7701 }
7702 if let Some(parent) = &scope.class_unit {
7703 self.parsed.add_child(parent.clone(), code_unit.clone());
7704 } else if let Some(module) = &scope.module {
7705 self.parsed.add_child(module.clone(), code_unit.clone());
7706 }
7707 self.parsed.mark_type_alias(code_unit);
7708 }
7709 }
7710
7711 fn type_alias_unit(
7712 &self,
7713 scope: &ScopeInfo,
7714 alias_name: String,
7715 signature: String,
7716 ) -> CodeUnit {
7717 let short_name = if let Some(parent) = &scope.class_unit {
7718 cpp_join_nested_short(parent.short_name(), &alias_name)
7719 } else {
7720 alias_name.clone()
7721 };
7722 let fq = cpp_leaf_fq(
7723 &scope.package_name,
7724 scope.class_unit.as_ref(),
7725 &alias_name,
7726 SegmentKind::Nested,
7727 SegmentKind::Type,
7728 );
7729 CodeUnit::with_signature_and_fq(
7730 self.file.clone(),
7731 CodeUnitType::Class,
7732 scope.package_name.clone(),
7733 short_name,
7734 Some(signature),
7735 false,
7736 fq,
7737 )
7738 }
7739
7740 fn visit_macro(&mut self, node: Node<'_>) {
7741 if let Some(replacement) =
7745 crate::graph::syntax::function_macro_replacement_span(node, self.source)
7746 {
7747 self.consumed_fragment_regions
7748 .push((replacement.start, replacement.end));
7749 }
7750 let Some(name) = extract_macro_name(node, self.source) else {
7751 return;
7752 };
7753 let signature = node_text(node, self.source).trim_end().to_string();
7754 if signature.is_empty() {
7755 return;
7756 }
7757 let fq = cpp_member_fq("", &name);
7758 let code_unit = CodeUnit::with_signature_and_fq(
7765 self.file.clone(),
7766 CodeUnitType::Macro,
7767 "",
7768 name.clone(),
7769 Some(signature.clone()),
7770 false,
7771 fq,
7772 );
7773 if !self.parsed.contains_declaration(&code_unit) {
7774 self.add_declaration(code_unit.clone(), node, None, None);
7775 let name_range = node
7776 .child_by_field_name("name")
7777 .map(cpp_declaration_range)
7778 .unwrap_or_else(|| cpp_declaration_range(node));
7779 self.parsed
7780 .record_materialization(MaterializationRecord::GeneratedDeclaration {
7781 site: cpp_declaration_range(node),
7782 argument: name_range,
7783 kind: GenerationKind::PreprocessorDefinition,
7784 unit: code_unit.clone(),
7785 });
7786 self.parsed.add_signature(code_unit, signature);
7787 }
7788 if node.kind() == "preproc_def" {
7789 update_object_macro_field_environment(
7790 node,
7791 self.source,
7792 &mut self.object_macro_fields,
7793 &mut self.ambiguous_object_macro_fields,
7794 );
7795 } else {
7796 self.object_macro_fields.remove(&name);
7797 self.ambiguous_object_macro_fields.remove(&name);
7798 }
7799 }
7800
7801 fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7802 let Some(directive) = node.child_by_field_name("directive") else {
7803 return;
7804 };
7805 let name = node_text(directive, self.source).trim();
7806 let range = cpp_declaration_range(node);
7807 let fields = object_macro_field_closure(&self.object_macro_fields, name);
7808 self.materialize_object_macro_fields(fields, range, scope);
7809 }
7810
7811 fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7816 if !matches!(node.kind(), "declaration" | "field_declaration") {
7817 return false;
7818 }
7819 if scope.class_unit.is_none() {
7827 return false;
7828 }
7829 let macro_nodes =
7830 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7831 for macro_node in ¯o_nodes {
7832 let name = node_text(*macro_node, self.source).trim();
7833 let fields = object_macro_field_closure(&self.object_macro_fields, name);
7834 self.materialize_object_macro_fields(fields, cpp_declaration_range(*macro_node), scope);
7835 }
7836 let Some(last) = macro_nodes.last() else {
7837 return false;
7838 };
7839 self.record_collapsed_aggregate_fields(
7845 last.end_byte()..node.end_byte(),
7846 node.start_position().row
7847 + 1
7848 + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
7849 scope,
7850 );
7851 true
7852 }
7853
7854 fn materialize_object_macro_fields(
7855 &mut self,
7856 fields: Vec<MacroReplacementField>,
7857 range: Range,
7858 scope: &ScopeInfo,
7859 ) {
7860 let Some(owner) = scope.class_unit.as_ref() else {
7861 return;
7862 };
7863 for field in fields {
7864 let signature = field.declaration.clone();
7865 let mut fq = owner.fq().clone();
7866 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7867 let short_name = if owner.short_name().is_empty() {
7868 field.name.clone()
7869 } else {
7870 format!("{}.{}", owner.short_name(), field.name)
7871 };
7872 let code_unit = CodeUnit::with_signature_and_fq(
7873 self.file.clone(),
7874 CodeUnitType::Field,
7875 owner.package_name().to_string(),
7876 short_name,
7877 Some(field.declaration),
7878 true,
7879 fq,
7880 );
7881 if self.parsed.contains_declaration(&code_unit) {
7882 continue;
7883 }
7884 self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7885 self.parsed.add_signature(code_unit, signature);
7886 }
7887 }
7888
7889 fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7896 let mut cursor = node.walk();
7897 let children = node.children(&mut cursor).collect::<Vec<_>>();
7898 let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember>)>::new();
7899 let mut object_macro_fields = self.object_macro_fields.clone();
7900 let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
7901 let mut open = Vec::<usize>::new();
7902 let mut index = 0;
7903 while index < children.len() {
7904 let keyword = children[index];
7905 if update_object_macro_field_environment(
7906 keyword,
7907 self.source,
7908 &mut object_macro_fields,
7909 &mut ambiguous_object_macro_fields,
7910 ) {
7911 index += 1;
7912 continue;
7913 }
7914 if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
7915 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7916 if !name.is_empty() {
7917 let parent = open
7918 .last()
7919 .and_then(|class| recovered.get(*class))
7920 .map(|(owner, _, _, _)| owner.clone())
7921 .or_else(|| scope.class_unit.clone());
7922 let short_name = parent.as_ref().map_or_else(
7923 || name.clone(),
7924 |parent| cpp_join_nested_short(parent.short_name(), &name),
7925 );
7926 let fq = cpp_leaf_fq(
7927 &scope.package_name,
7928 parent.as_ref(),
7929 &name,
7930 SegmentKind::Nested,
7931 SegmentKind::Type,
7932 );
7933 let owner = CodeUnit::with_signature_and_fq(
7934 self.file.clone(),
7935 CodeUnitType::Class,
7936 scope.package_name.clone(),
7937 short_name,
7938 None,
7939 false,
7940 fq,
7941 );
7942 recovered.push((
7943 owner,
7944 head.key.start_byte(),
7945 head.opening.end_byte(),
7946 Vec::new(),
7947 ));
7948 let class_index = recovered.len() - 1;
7949 match head.folded_members {
7950 None => open.push(class_index),
7954 Some(members) => {
7960 let macro_nodes = object_macro_identifier_nodes_with_environment(
7961 children[index],
7962 self.source,
7963 &mut object_macro_fields,
7964 &mut ambiguous_object_macro_fields,
7965 );
7966 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7967 .iter()
7968 .partition(|node| node.start_byte() < head.key.start_byte());
7969 if let Some(&enclosing) = open.last() {
7970 for macro_node in preceding {
7971 recovered[enclosing]
7972 .3
7973 .push(CppCollapsedMember::MacroFields {
7974 range: cpp_declaration_range(macro_node),
7975 fields: object_macro_field_closure(
7976 &object_macro_fields,
7977 &normalize_cpp_whitespace(node_text(
7978 macro_node,
7979 self.source,
7980 )),
7981 ),
7982 });
7983 }
7984 }
7985 let closing = cpp_collapsed_aggregate_closing_brace(members);
7986 recovered[class_index]
7987 .3
7988 .extend(cpp_collapsed_aggregate_members(
7989 &inner,
7990 head.opening.end_byte()..closing,
7991 head.opening.end_position().row + 1,
7992 self.source,
7993 &object_macro_fields,
7994 ));
7995 recovered[class_index].2 = members.end_byte();
7996 for &open_class in &open {
7997 recovered[open_class].2 =
7998 recovered[open_class].2.max(members.end_byte());
7999 }
8000 }
8001 }
8002 index += head.width;
8003 continue;
8004 }
8005 }
8006 if let Some(&class_index) = open.last()
8007 && children[index].kind() == "field_declaration"
8008 {
8009 let field = children[index];
8010 let macro_nodes = object_macro_identifier_nodes_with_environment(
8011 field,
8012 self.source,
8013 &mut object_macro_fields,
8014 &mut ambiguous_object_macro_fields,
8015 );
8016 recovered[class_index]
8017 .3
8018 .extend(cpp_collapsed_aggregate_members(
8019 ¯o_nodes,
8020 field.start_byte()..field.end_byte(),
8021 field.start_position().row + 1,
8022 self.source,
8023 &object_macro_fields,
8024 ));
8025 let end = field.end_byte();
8026 for &open_class in &open {
8027 recovered[open_class].2 = recovered[open_class].2.max(end);
8028 }
8029 let closes = count_close_brace_nodes(field);
8030 for _ in 0..closes {
8031 if let Some(closed) = open.pop() {
8032 recovered[closed].2 = end;
8033 }
8034 }
8035 }
8036 index += 1;
8037 }
8038
8039 let mut owners = Vec::with_capacity(recovered.len());
8040 for (owner, start, end, members) in recovered {
8041 let parent = owners
8042 .iter()
8043 .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
8044 .cloned()
8045 .or_else(|| scope.class_unit.clone());
8046 self.declare_collapsed_aggregate(owner.clone(), start..end, parent, members, scope);
8047 owners.push(owner);
8048 }
8049 }
8050
8051 fn declare_collapsed_aggregate(
8053 &mut self,
8054 owner: CodeUnit,
8055 span: std::ops::Range<usize>,
8056 parent: Option<CodeUnit>,
8057 members: Vec<CppCollapsedMember>,
8058 scope: &ScopeInfo,
8059 ) {
8060 let range = Range {
8061 start_byte: span.start,
8062 end_byte: span.end,
8063 start_line: self.source.get(..span.start).map_or(1, |source| {
8064 source.bytes().filter(|byte| *byte == b'\n').count() + 1
8065 }),
8066 end_line: self.source.get(..span.end).map_or(1, |source| {
8067 source.bytes().filter(|byte| *byte == b'\n').count() + 1
8068 }),
8069 };
8070 self.add_declaration_with_range(owner.clone(), range, parent, None);
8077 let owner_scope = ScopeInfo {
8078 class_unit: Some(owner),
8079 declarations_are_fields: true,
8080 ..scope.clone()
8081 };
8082 for member in members {
8083 match member {
8084 CppCollapsedMember::MacroFields { range, fields } => {
8085 self.materialize_object_macro_fields(fields, range, &owner_scope);
8086 }
8087 CppCollapsedMember::Declarations { span, start_line } => {
8088 self.record_collapsed_aggregate_fields(span, start_line, &owner_scope);
8089 }
8090 }
8091 }
8092 }
8093
8094 fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
8105 if node.parent().is_some_and(|parent| parent.is_error()) {
8106 return false;
8109 }
8110 let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
8111 return false;
8112 };
8113 let members = head
8114 .folded_members
8115 .expect("a folded aggregate head carries its member list");
8116 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
8117 if name.is_empty() {
8118 return false;
8119 }
8120 let macro_nodes =
8121 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
8122 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
8123 .iter()
8124 .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
8125 for macro_node in preceding {
8126 let fields = object_macro_field_closure(
8127 &self.object_macro_fields,
8128 &normalize_cpp_whitespace(node_text(macro_node, self.source)),
8129 );
8130 self.materialize_object_macro_fields(fields, cpp_declaration_range(macro_node), scope);
8131 }
8132 let parent = scope.class_unit.clone();
8133 let short_name = parent.as_ref().map_or_else(
8134 || name.clone(),
8135 |parent| cpp_join_nested_short(parent.short_name(), &name),
8136 );
8137 let fq = cpp_leaf_fq(
8138 &scope.package_name,
8139 parent.as_ref(),
8140 &name,
8141 SegmentKind::Nested,
8142 SegmentKind::Type,
8143 );
8144 let owner = CodeUnit::with_signature_and_fq(
8145 self.file.clone(),
8146 CodeUnitType::Class,
8147 scope.package_name.clone(),
8148 short_name,
8149 None,
8150 false,
8151 fq,
8152 );
8153 let recovered = cpp_collapsed_aggregate_members(
8154 &inner,
8155 head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
8156 head.opening.end_position().row + 1,
8157 self.source,
8158 &self.object_macro_fields,
8159 );
8160 self.declare_collapsed_aggregate(
8161 owner,
8162 head.key.start_byte()..members.end_byte(),
8163 parent,
8164 recovered,
8165 scope,
8166 );
8167 true
8168 }
8169
8170 fn record_collapsed_aggregate_fields(
8175 &mut self,
8176 span: std::ops::Range<usize>,
8177 start_line: usize,
8178 scope: &ScopeInfo,
8179 ) {
8180 let Some(owner) = scope.class_unit.as_ref() else {
8181 return;
8182 };
8183 for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
8184 let range = Range {
8185 start_byte: field.range.start,
8186 end_byte: field.range.end,
8187 start_line: start_line
8188 + cpp_line_breaks_between(self.source, span.start, field.range.start),
8189 end_line: start_line
8190 + cpp_line_breaks_between(self.source, span.start, field.range.end),
8191 };
8192 let mut fq = owner.fq().clone();
8193 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
8194 let short_name = if owner.short_name().is_empty() {
8195 field.name.clone()
8196 } else {
8197 format!("{}.{}", owner.short_name(), field.name)
8198 };
8199 let signature = normalize_cpp_whitespace(&field.declaration);
8200 let code_unit = CodeUnit::with_signature_and_fq(
8201 self.file.clone(),
8202 CodeUnitType::Field,
8203 owner.package_name().to_string(),
8204 short_name,
8205 Some(signature.clone()),
8206 false,
8207 fq,
8208 );
8209 if self.parsed.contains_declaration(&code_unit) {
8210 continue;
8211 }
8212 self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
8213 self.parsed.add_signature(code_unit, signature);
8214 }
8215 }
8216
8217 fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
8218 let Some(_directive) = node.child_by_field_name("directive") else {
8219 return;
8220 };
8221 if is_cpp_undef_directive(node, self.source) {
8222 update_object_macro_field_environment(
8223 node,
8224 self.source,
8225 &mut self.object_macro_fields,
8226 &mut self.ambiguous_object_macro_fields,
8227 );
8228 return;
8229 }
8230 let directly_in_field_list = node
8231 .parent()
8232 .is_some_and(|parent| parent.kind() == "field_declaration_list");
8233 if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
8234 self.visit_object_macro_fields(node, scope);
8235 }
8236 }
8237}
8238
8239fn object_macro_field_closure(
8247 environment: &HashMap<String, ObjectMacroReplacement>,
8248 name: &str,
8249) -> Vec<MacroReplacementField> {
8250 let mut fields = Vec::new();
8251 let mut visited = HashSet::default();
8252 let mut stack = vec![name.to_string()];
8253 while let Some(current) = stack.pop() {
8254 if !visited.insert(current.clone()) {
8255 continue;
8256 }
8257 let Some(replacement) = environment.get(¤t) else {
8258 continue;
8259 };
8260 fields.extend(replacement.fields.iter().cloned());
8261 stack.extend(replacement.nested.iter().rev().cloned());
8262 }
8263 fields
8264}
8265
8266fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
8272 crate::graph::syntax::object_macro_replacement_span(node, source)
8273 .and_then(|span| source.get(span))
8274 .map(crate::graph::syntax::object_macro_replacement)
8275 .unwrap_or_default()
8276}
8277
8278fn update_object_macro_field_environment(
8283 node: Node<'_>,
8284 source: &str,
8285 fields: &mut HashMap<String, ObjectMacroReplacement>,
8286 ambiguous: &mut HashSet<String>,
8287) -> bool {
8288 match node.kind() {
8289 "preproc_def" => {
8290 let Some(name) = extract_macro_name(node, source) else {
8291 return false;
8292 };
8293 let replacement = object_macro_replacement_of(node, source);
8294 if replacement.is_empty() || ambiguous.contains(&name) {
8295 fields.remove(&name);
8296 ambiguous.insert(name);
8297 } else if let Some(previous) = fields.get(&name) {
8298 if previous != &replacement {
8299 fields.remove(&name);
8300 ambiguous.insert(name);
8301 }
8302 } else {
8303 fields.insert(name, replacement);
8304 }
8305 true
8306 }
8307 "preproc_call" if is_cpp_undef_directive(node, source) => {
8308 if let Some(argument) = node.child_by_field_name("argument") {
8309 let name = node_text(argument, source).trim();
8310 fields.remove(name);
8311 if inside_preprocessor_conditional(node) {
8312 ambiguous.insert(name.to_string());
8313 } else {
8314 ambiguous.remove(name);
8315 }
8316 }
8317 true
8318 }
8319 _ => false,
8320 }
8321}
8322
8323fn object_macro_identifier_nodes<'tree>(
8324 node: Node<'tree>,
8325 source: &str,
8326 fields: &HashMap<String, ObjectMacroReplacement>,
8327) -> Vec<Node<'tree>> {
8328 let mut result = Vec::new();
8329 let mut stack = vec![node];
8330 while let Some(current) = stack.pop() {
8331 if matches!(
8332 current.kind(),
8333 "identifier" | "field_identifier" | "type_identifier"
8334 ) && fields.contains_key(node_text(current, source).trim())
8335 {
8336 result.push(current);
8337 }
8338 let mut cursor = current.walk();
8339 let children = current.children(&mut cursor).collect::<Vec<_>>();
8340 stack.extend(children.into_iter().rev());
8341 }
8342 result.sort_by_key(|node| node.start_byte());
8343 result
8344}
8345
8346fn object_macro_identifier_nodes_with_environment<'tree>(
8351 node: Node<'tree>,
8352 source: &str,
8353 fields: &mut HashMap<String, ObjectMacroReplacement>,
8354 ambiguous: &mut HashSet<String>,
8355) -> Vec<Node<'tree>> {
8356 let mut result = Vec::new();
8357 let mut stack = vec![node];
8358 while let Some(current) = stack.pop() {
8359 if update_object_macro_field_environment(current, source, fields, ambiguous) {
8360 continue;
8361 }
8362 if matches!(
8363 current.kind(),
8364 "identifier" | "field_identifier" | "type_identifier"
8365 ) && fields.contains_key(node_text(current, source).trim())
8366 {
8367 result.push(current);
8368 }
8369 let mut cursor = current.walk();
8370 let children = current.children(&mut cursor).collect::<Vec<_>>();
8371 stack.extend(children.into_iter().rev());
8372 }
8373 result.sort_by_key(|node| node.start_byte());
8374 result
8375}
8376
8377enum CppCollapsedMember {
8380 MacroFields {
8383 range: Range,
8384 fields: Vec<MacroReplacementField>,
8385 },
8386 Declarations {
8389 span: std::ops::Range<usize>,
8390 start_line: usize,
8391 },
8392}
8393
8394struct CppCollapsedAggregateHead<'tree> {
8409 key: Node<'tree>,
8410 name: Node<'tree>,
8411 opening: Node<'tree>,
8412 folded_members: Option<Node<'tree>>,
8416 width: usize,
8418}
8419
8420fn cpp_collapsed_aggregate_head<'tree>(
8422 children: &[Node<'tree>],
8423 index: usize,
8424 source: &str,
8425) -> Option<CppCollapsedAggregateHead<'tree>> {
8426 let key = *children.get(index)?;
8427 if matches!(key.kind(), "struct" | "class" | "union") {
8428 let name = *children.get(index + 1)?;
8429 let opening = *children.get(index + 2)?;
8430 if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
8431 return None;
8432 }
8433 return Some(CppCollapsedAggregateHead {
8434 key,
8435 name,
8436 opening,
8437 folded_members: None,
8438 width: 3,
8439 });
8440 }
8441 cpp_folded_aggregate_head(key, source)
8442}
8443
8444fn cpp_folded_aggregate_head<'tree>(
8447 node: Node<'tree>,
8448 source: &str,
8449) -> Option<CppCollapsedAggregateHead<'tree>> {
8450 if !matches!(node.kind(), "declaration" | "field_declaration") {
8451 return None;
8452 }
8453 let mut cursor = node.walk();
8454 let children = node.children(&mut cursor).collect::<Vec<_>>();
8455 let key_index = children.iter().position(|child| {
8456 child.is_error()
8457 && matches!(
8458 node_text(*child, source).trim(),
8459 "struct" | "class" | "union"
8460 )
8461 })?;
8462 let declarator = *children.get(key_index + 1)?;
8463 let (name, members) = if declarator.kind() == "init_declarator" {
8466 (
8467 declarator.child_by_field_name("declarator")?,
8468 declarator.child_by_field_name("value")?,
8469 )
8470 } else {
8471 (declarator, *children.get(key_index + 2)?)
8472 };
8473 if !matches!(
8474 name.kind(),
8475 "field_identifier" | "type_identifier" | "identifier"
8476 ) {
8477 return None;
8478 }
8479 if members.kind() != "initializer_list" {
8480 return None;
8481 }
8482 let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
8483 Some(CppCollapsedAggregateHead {
8484 key: children[key_index],
8485 name,
8486 opening,
8487 folded_members: Some(members),
8488 width: 1,
8489 })
8490}
8491
8492fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
8496 let mut cursor = members.walk();
8497 members
8498 .children(&mut cursor)
8499 .filter(|child| child.kind() == "}" && !child.is_missing())
8500 .last()
8501 .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
8502}
8503
8504fn cpp_collapsed_aggregate_members(
8508 macro_nodes: &[Node<'_>],
8509 region: std::ops::Range<usize>,
8510 region_start_line: usize,
8511 source: &str,
8512 environment: &HashMap<String, ObjectMacroReplacement>,
8513) -> Vec<CppCollapsedMember> {
8514 let mut members = macro_nodes
8515 .iter()
8516 .map(|macro_node| CppCollapsedMember::MacroFields {
8517 range: cpp_declaration_range(*macro_node),
8518 fields: object_macro_field_closure(
8519 environment,
8520 &normalize_cpp_whitespace(node_text(*macro_node, source)),
8521 ),
8522 })
8523 .collect::<Vec<_>>();
8524 let declarations_start = macro_nodes
8528 .last()
8529 .map_or(region.start, |macro_node| macro_node.end_byte());
8530 members.push(CppCollapsedMember::Declarations {
8531 span: declarations_start..region.end,
8532 start_line: region_start_line
8533 + cpp_line_breaks_between(source, region.start, declarations_start),
8534 });
8535 members
8536}
8537
8538fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
8541 source.get(from..to).map_or(0, |slice| {
8542 slice.bytes().filter(|byte| *byte == b'\n').count()
8543 })
8544}
8545
8546fn count_close_brace_nodes(node: Node<'_>) -> usize {
8552 let mut opened = 0usize;
8553 let mut closed = 0usize;
8554 let mut stack = vec![node];
8555 while let Some(current) = stack.pop() {
8556 if !current.is_missing() {
8557 match current.kind() {
8558 "{" => opened += 1,
8559 "}" => closed += 1,
8560 _ => {}
8561 }
8562 }
8563 let mut cursor = current.walk();
8564 stack.extend(current.children(&mut cursor));
8565 }
8566 closed.saturating_sub(opened)
8567}
8568
8569pub fn cpp_field_declaration_linkage<'tree>(
8574 declaration: Node<'tree>,
8575 source: &str,
8576 ancestry: &ParentIndex<'tree>,
8577) -> CppFieldLinkage {
8578 let mut current = ancestry.parent(declaration);
8579 let mut enclosed_by_class = false;
8580 while let Some(node) = current {
8581 if node.kind() == "namespace_definition"
8582 && node
8583 .child_by_field_name("name")
8584 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8585 {
8586 return CppFieldLinkage::Internal;
8587 }
8588 if matches!(
8589 node.kind(),
8590 "class_specifier" | "struct_specifier" | "union_specifier"
8591 ) && node
8592 .child_by_field_name("name")
8593 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8594 {
8595 return CppFieldLinkage::Internal;
8596 }
8597 if matches!(
8598 node.kind(),
8599 "class_specifier" | "struct_specifier" | "union_specifier"
8600 ) {
8601 enclosed_by_class = true;
8602 }
8603 if matches!(node.kind(), "function_definition" | "lambda_expression") {
8604 return CppFieldLinkage::Internal;
8605 }
8606 current = ancestry.parent(node);
8607 }
8608 if enclosed_by_class {
8609 return CppFieldLinkage::External;
8610 }
8611 let mut cursor = declaration.walk();
8612 let mut has_static = false;
8613 let mut has_extern = false;
8614 let mut has_inline = false;
8615 let mut has_const = false;
8616 let mut has_constexpr = false;
8617 for child in declaration.named_children(&mut cursor) {
8618 let text = normalize_cpp_whitespace(node_text(child, source));
8619 match (child.kind(), text.as_str()) {
8620 ("storage_class_specifier", "static") => has_static = true,
8621 ("storage_class_specifier", "extern") => has_extern = true,
8622 ("storage_class_specifier", "inline") => has_inline = true,
8623 ("storage_class_specifier", "constexpr") => has_constexpr = true,
8624 ("type_qualifier", "const") => has_const = true,
8625 ("type_qualifier", "constexpr") => has_constexpr = true,
8626 _ => {}
8627 }
8628 }
8629 if has_static {
8630 CppFieldLinkage::Internal
8631 } else if has_extern || has_inline {
8632 CppFieldLinkage::External
8633 } else if has_const || has_constexpr {
8634 CppFieldLinkage::InternalUnlessExternalPeer
8635 } else {
8636 CppFieldLinkage::External
8637 }
8638}
8639
8640fn cpp_declaration_range(node: Node<'_>) -> Range {
8641 Range {
8642 start_byte: node.start_byte(),
8643 end_byte: node.end_byte(),
8644 start_line: node.start_position().row + 1,
8645 end_line: node.end_position().row + 1,
8646 }
8647}
8648
8649fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
8653 let line_at = |byte: usize| {
8654 source.as_bytes()[..byte]
8655 .iter()
8656 .filter(|&&b| b == b'\n')
8657 .count()
8658 + 1
8659 };
8660 Range {
8661 start_byte,
8662 end_byte,
8663 start_line: line_at(start_byte),
8664 end_line: line_at(end_byte),
8665 }
8666}
8667
8668pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
8675 walk_named_tree_preorder(root, true, |node| {
8676 if node.kind() == "preproc_include" {
8677 let raw = normalize_cpp_whitespace(node_text(node, source));
8678 if !raw.is_empty() {
8679 parsed.imports.push(ImportInfo {
8680 raw_snippet: raw,
8681 is_wildcard: false,
8682 is_global: false,
8683 identifier: None,
8684 alias: None,
8685 path: None,
8686 binder_span: None,
8687 });
8688 }
8689 return WalkControl::SkipChildren;
8690 }
8691 WalkControl::Continue
8692 });
8693}
8694
8695pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
8696 let mut in_block_comment = false;
8697 for line in source.lines() {
8698 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
8699 let trimmed = stripped.trim();
8700 if !looks_like_quoted_include_line(trimmed) {
8701 continue;
8702 }
8703
8704 let raw = normalize_cpp_whitespace(trimmed);
8705 if parsed
8709 .imports
8710 .iter()
8711 .any(|import| import.raw_snippet == raw)
8712 {
8713 continue;
8714 }
8715
8716 parsed.imports.push(ImportInfo {
8717 raw_snippet: raw,
8718 is_wildcard: false,
8719 is_global: false,
8720 identifier: None,
8721 alias: None,
8722 path: None,
8723 binder_span: None,
8724 });
8725 }
8726}
8727
8728fn looks_like_quoted_include_line(line: &str) -> bool {
8729 let Some(rest) = line.trim_start().strip_prefix('#') else {
8730 return false;
8731 };
8732 let Some(rest) = rest.trim_start().strip_prefix("include") else {
8733 return false;
8734 };
8735 rest.trim_start().starts_with('"')
8736}
8737
8738fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
8739 let mut raw = Vec::new();
8740 if let Some(bases) = cpp_class_like_base_clause(node) {
8741 collect_cpp_base_nodes(bases, source, &mut raw);
8742 }
8743 raw
8744}
8745
8746fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
8753 let mut cursor = class_like.walk();
8754 class_like
8755 .named_children(&mut cursor)
8756 .find(|child| child.kind() == "base_class_clause")
8757}
8758
8759fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
8760 walk_named_tree_preorder(node, false, |child| match child.kind() {
8761 "type_identifier" | "qualified_identifier" | "template_type" => {
8762 let text = normalize_cpp_whitespace(node_text(child, source));
8763 if !text.is_empty() {
8764 raw.push(text);
8765 }
8766 WalkControl::SkipChildren
8767 }
8768 _ => WalkControl::Continue,
8769 });
8770}
8771
8772fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
8773 let mut out = String::new();
8774 let chars: Vec<char> = line.chars().collect();
8775 let mut index = 0;
8776 let mut in_string = false;
8777 let mut in_char = false;
8778 let mut escape = false;
8779
8780 while index < chars.len() {
8781 let ch = chars[index];
8782 let next = chars.get(index + 1).copied();
8783
8784 if *in_block_comment {
8785 if ch == '*' && next == Some('/') {
8786 *in_block_comment = false;
8787 index += 2;
8788 } else {
8789 index += 1;
8790 }
8791 continue;
8792 }
8793
8794 if in_string {
8795 out.push(ch);
8796 if escape {
8797 escape = false;
8798 } else if ch == '\\' {
8799 escape = true;
8800 } else if ch == '"' {
8801 in_string = false;
8802 }
8803 index += 1;
8804 continue;
8805 }
8806
8807 if in_char {
8808 out.push(ch);
8809 if escape {
8810 escape = false;
8811 } else if ch == '\\' {
8812 escape = true;
8813 } else if ch == '\'' {
8814 in_char = false;
8815 }
8816 index += 1;
8817 continue;
8818 }
8819
8820 if ch == '/' && next == Some('/') {
8821 break;
8822 }
8823 if ch == '/' && next == Some('*') {
8824 *in_block_comment = true;
8825 index += 2;
8826 continue;
8827 }
8828 if ch == '"' {
8829 in_string = true;
8830 out.push(ch);
8831 index += 1;
8832 continue;
8833 }
8834 if ch == '\'' {
8835 in_char = true;
8836 out.push(ch);
8837 index += 1;
8838 continue;
8839 }
8840
8841 out.push(ch);
8842 index += 1;
8843 }
8844
8845 out
8846}
8847
8848#[derive(Clone)]
8849struct FunctionInfo {
8850 package_name: String,
8851 owner: Option<CppMemberOwner>,
8852 name: String,
8853 signature: String,
8854}
8855
8856#[derive(Clone)]
8863enum CppMemberOwner {
8864 Chain(Vec<String>),
8868 Unit(CodeUnit),
8871}
8872
8873impl CppMemberOwner {
8874 fn short_chain(&self) -> String {
8876 match self {
8877 Self::Chain(chain) => chain.join("$"),
8878 Self::Unit(parent) => parent.short_name().to_string(),
8879 }
8880 }
8881}
8882
8883enum DeclaratorKind<'a> {
8884 Function(Node<'a>),
8885 Variable(Node<'a>),
8886}
8887
8888impl FunctionInfo {
8889 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
8890 self.code_unit_with_synthetic(file, false)
8891 }
8892
8893 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
8894 let short_name = match &self.owner {
8895 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
8896 None => self.name.clone(),
8897 };
8898 let fq = match &self.owner {
8899 Some(CppMemberOwner::Chain(chain)) => {
8900 debug_assert!(
8901 !chain.is_empty(),
8902 "an empty owner chain is no owner; producers return None instead"
8903 );
8904 let mut fq = FqName::new();
8905 cpp_push_package(&mut fq, &self.package_name);
8906 let mut first = true;
8907 for component in chain {
8908 let kind = if first {
8909 SegmentKind::Type
8910 } else {
8911 SegmentKind::Nested
8912 };
8913 fq.push(cpp_segment(component, kind));
8914 first = false;
8915 }
8916 fq.push(cpp_segment(&self.name, SegmentKind::Member));
8917 fq
8918 }
8919 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
8920 .fq()
8921 .clone()
8922 .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
8923 Some(CppMemberOwner::Unit(_)) | None => {
8926 let mut fq = FqName::new();
8927 cpp_push_package(&mut fq, &self.package_name);
8928 fq.push(cpp_segment(&self.name, SegmentKind::Member));
8929 fq
8930 }
8931 };
8932 CodeUnit::with_signature_and_fq(
8933 file,
8934 CodeUnitType::Function,
8935 self.package_name.clone(),
8936 short_name,
8937 Some(self.signature.clone()),
8938 synthetic,
8939 fq,
8940 )
8941 }
8942}
8943
8944fn extract_function_info(
8945 declarator: Node<'_>,
8946 source: &str,
8947 scope: &ScopeInfo,
8948) -> Option<FunctionInfo> {
8949 let parameters_node = declarator.child_by_field_name("parameters")?;
8950 let declarator_name_node = declarator
8951 .child_by_field_name("declarator")
8952 .or_else(|| parameters_node.prev_named_sibling())?;
8953 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
8954}
8955
8956fn extract_function_info_from_name(
8957 declarator: Node<'_>,
8958 declarator_name_node: Node<'_>,
8959 source: &str,
8960 scope: &ScopeInfo,
8961) -> Option<FunctionInfo> {
8962 let parameters_node = declarator.child_by_field_name("parameters")?;
8963 let parameters_text = cpp_parameter_signature(parameters_node, source);
8964 let recovered_specialization_member = scope
8965 .recovered_specialization_member_scope
8966 .then(|| {
8967 let terminal = declarator_name_node
8968 .child_by_field_name("name")
8969 .unwrap_or(declarator_name_node);
8970 let name = canonical_cpp_qualified_component(terminal, source)?.name;
8971 let owner = scope.class_unit.as_ref()?;
8972 Some((
8973 Some(CppMemberOwner::Unit(owner.clone())),
8974 name,
8975 scope.package_name.clone(),
8976 ))
8977 })
8978 .flatten();
8979 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
8980 parts
8981 } else if let Some(parts) =
8982 split_structured_templated_cpp_name(declarator_name_node, source, scope)
8983 {
8984 parts
8985 } else {
8986 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
8987 declarator_name_node,
8988 source,
8989 )?);
8990 if raw_name.is_empty() {
8991 return None;
8992 }
8993 split_cpp_name(&raw_name, scope)
8994 };
8995 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
8996 let mut signature = if suffix.is_empty() {
8997 parameters_text
8998 } else {
8999 format!("{parameters_text} {suffix}")
9000 };
9001 if let Some(template_signature) = &scope.template_signature {
9002 signature = format!("{template_signature}{signature}");
9003 }
9004
9005 Some(FunctionInfo {
9006 package_name,
9007 owner,
9008 name,
9009 signature,
9010 })
9011}
9012
9013fn cpp_macro_displaced_callable_parts<'tree>(
9020 function_declarator: Node<'tree>,
9021 source: &str,
9022 ancestry: &ParentIndex<'tree>,
9023) -> Option<(Node<'tree>, Node<'tree>)> {
9024 let definition = ancestry.parent(function_declarator)?;
9025 if definition.kind() != "function_definition"
9026 || definition.child_by_field_name("declarator") != Some(function_declarator)
9027 || definition
9028 .child_by_field_name("body")
9029 .is_none_or(|body| body.kind() != "compound_statement")
9030 {
9031 return None;
9032 }
9033 let macro_type = definition.child_by_field_name("type")?;
9034 if macro_type.kind() != "type_identifier"
9035 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9036 {
9037 return None;
9038 }
9039
9040 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
9041 if apparent_return_type.kind() == "qualified_identifier"
9042 && let (Some(return_type), Some(callable_name)) = (
9043 apparent_return_type.child_by_field_name("scope"),
9044 apparent_return_type.child_by_field_name("name"),
9045 )
9046 && return_type.kind() == "template_type"
9047 && matches!(callable_name.kind(), "identifier" | "field_identifier")
9048 && (0..apparent_return_type.child_count())
9049 .filter_map(|index| apparent_return_type.child(index))
9050 .any(|child| child.kind() == "::" && child.is_missing())
9051 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
9052 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
9053 {
9054 return Some((return_type, callable_name));
9055 }
9056 if !matches!(
9057 apparent_return_type.kind(),
9058 "identifier" | "field_identifier" | "type_identifier"
9059 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
9060 {
9061 return None;
9062 }
9063 let parameters = function_declarator.child_by_field_name("parameters")?;
9064 let mut cursor = function_declarator.walk();
9065 let between = function_declarator
9066 .named_children(&mut cursor)
9067 .filter(|child| child.kind() != "comment")
9068 .filter(|child| {
9069 child.start_byte() >= apparent_return_type.end_byte()
9070 && child.end_byte() <= parameters.start_byte()
9071 && !same_node(*child, apparent_return_type)
9072 && !same_node(*child, parameters)
9073 })
9074 .collect::<Vec<_>>();
9075 let [name_error] = between.as_slice() else {
9076 return None;
9077 };
9078 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
9079 return None;
9080 }
9081 let callable_name = name_error.named_child(0)?;
9082 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
9083 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
9084 {
9085 return None;
9086 }
9087 Some((apparent_return_type, callable_name))
9088}
9089
9090fn cpp_declarator_identity_suffix(
9106 declarator: Node<'_>,
9107 parameters_node: Node<'_>,
9108 source: &str,
9109) -> String {
9110 let mut cursor = declarator.walk();
9111 let parts = declarator
9112 .named_children(&mut cursor)
9113 .filter(|child| child.start_byte() >= parameters_node.end_byte())
9114 .filter(|child| {
9115 matches!(
9116 child.kind(),
9117 "type_qualifier"
9118 | "ref_qualifier"
9119 | "noexcept"
9120 | "throw_specifier"
9121 | "trailing_return_type"
9122 | "requires_clause"
9123 )
9124 })
9125 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
9126 .filter(|text| !text.is_empty())
9127 .collect::<Vec<_>>();
9128 normalize_cpp_qualifier_suffix(&parts.join(" "))
9129}
9130
9131pub(crate) fn cpp_callable_identity_suffix(
9138 function_declarator: Node<'_>,
9139 source: &str,
9140) -> Option<String> {
9141 let parameters_node = function_declarator.child_by_field_name("parameters")?;
9142 Some(cpp_declarator_identity_suffix(
9143 function_declarator,
9144 parameters_node,
9145 source,
9146 ))
9147}
9148
9149pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
9150 match classify_declarator(node)? {
9151 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
9152 DeclaratorKind::Variable(_) => None,
9153 }
9154}
9155
9156fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
9157 match node.kind() {
9158 "function_declarator" => {
9159 let inner = node
9160 .child_by_field_name("declarator")
9161 .or_else(|| node.child_by_field_name("name"))
9162 .or_else(|| last_named_child(node));
9163 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
9164 Some(DeclaratorKind::Variable(node))
9165 } else {
9166 Some(DeclaratorKind::Function(node))
9167 }
9168 }
9169 "init_declarator"
9170 | "pointer_declarator"
9171 | "reference_declarator"
9172 | "parenthesized_declarator"
9173 | "array_declarator"
9174 | "attributed_declarator"
9175 | "template_function" => node
9176 .child_by_field_name("declarator")
9177 .or_else(|| node.child_by_field_name("name"))
9178 .or_else(|| last_named_child(node))
9179 .and_then(classify_declarator),
9180 "identifier" | "field_identifier" | "qualified_identifier" => {
9181 Some(DeclaratorKind::Variable(node))
9182 }
9183 _ => node
9184 .child_by_field_name("declarator")
9185 .or_else(|| node.child_by_field_name("name"))
9186 .or_else(|| last_named_child(node))
9187 .and_then(classify_declarator),
9188 }
9189}
9190
9191fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
9192 matches!(
9193 node.kind(),
9194 "function_declarator"
9195 | "init_declarator"
9196 | "pointer_declarator"
9197 | "reference_declarator"
9198 | "parenthesized_declarator"
9199 | "array_declarator"
9200 | "attributed_declarator"
9201 | "template_function"
9202 | "identifier"
9203 | "field_identifier"
9204 | "qualified_identifier"
9205 )
9206}
9207
9208fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
9209 let class_like = first_class_like_child(node);
9210 let mut cursor = node.walk();
9211 node.named_children(&mut cursor).any(|child| {
9212 matches!(
9213 child.kind(),
9214 "init_declarator"
9215 | "pointer_declarator"
9216 | "reference_declarator"
9217 | "array_declarator"
9218 | "function_declarator"
9219 | "parenthesized_declarator"
9220 | "attributed_declarator"
9221 ) || matches!(
9222 child.kind(),
9223 "identifier" | "field_identifier" | "qualified_identifier"
9224 ) && class_like.is_none_or(|class_node| {
9225 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
9226 })
9227 })
9228}
9229
9230struct CppNamespaceForward {
9243 name: String,
9244 start_byte: usize,
9245 namespace_end_byte: usize,
9248 package_name: String,
9249}
9250
9251fn cpp_namespace_forward_entry<'tree>(
9257 node: Node<'tree>,
9258 source: &str,
9259 ancestry: &ParentIndex<'tree>,
9260) -> Option<CppNamespaceForward> {
9261 if !matches!(
9262 node.kind(),
9263 "class_specifier" | "struct_specifier" | "union_specifier"
9264 ) || cpp_body_node(node).is_some()
9265 {
9266 return None;
9267 }
9268 let parent = node.parent()?;
9269 if !(parent.kind() == "declaration_list"
9270 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
9271 {
9272 return None;
9273 }
9274 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
9275 if !namespace.has_error() {
9280 return None;
9281 }
9282 Some(CppNamespaceForward {
9283 name: class_like_name(node, source, ancestry)?,
9284 start_byte: node.start_byte(),
9285 namespace_end_byte: namespace.end_byte(),
9286 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
9287 })
9288}
9289
9290fn cpp_namespace_forward_matches_recovery(
9294 forward: &CppNamespaceForward,
9295 recovered_node: Node<'_>,
9296) -> bool {
9297 forward.start_byte < recovered_node.start_byte()
9298 && forward.namespace_end_byte < recovered_node.start_byte()
9299 && malformed_namespace_is_nearest_recovery_region(
9300 forward.namespace_end_byte,
9301 recovered_node,
9302 )
9303}
9304
9305#[derive(Debug, Default)]
9321pub struct CppRecoveryCapture {
9322 created: Vec<CodeUnit>,
9324 created_units: HashSet<CodeUnit>,
9326 removed_pre_existing: HashSet<CodeUnit>,
9328}
9329
9330#[derive(Debug, Default)]
9342pub struct CppFieldOwnerIndex {
9343 owners: HashMap<String, HashSet<String>>,
9345 ownerless_packages: HashSet<String>,
9347}
9348
9349impl CppFieldOwnerIndex {
9350 fn of<'unit>(
9353 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9354 file: &ProjectFile,
9355 ) -> Self {
9356 let mut index = Self::default();
9357 for declaration in declarations {
9358 index.record(declaration, file);
9359 }
9360 index
9361 }
9362
9363 fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
9364 if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
9365 return;
9366 }
9367 let short_name = code_unit.short_name();
9368 let package_name = code_unit.package_name();
9369 if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
9370 self.ownerless_packages.insert(package_name.to_string());
9371 }
9372 if !short_name.contains('.') {
9373 return;
9374 }
9375 if !self.owners.contains_key(package_name) {
9376 self.owners
9377 .insert(package_name.to_string(), HashSet::default());
9378 }
9379 let owners = self
9380 .owners
9381 .get_mut(package_name)
9382 .expect("the package entry was just ensured");
9383 for (offset, _) in short_name.match_indices('.') {
9384 let owner = &short_name[..offset];
9385 if !owners.contains(owner) {
9386 owners.insert(owner.to_string());
9387 }
9388 }
9389 }
9390
9391 fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
9394 if owner_short_name.is_empty() {
9395 self.ownerless_packages.contains(package_name)
9396 } else {
9397 self.owners
9398 .get(package_name)
9399 .is_some_and(|owners| owners.contains(owner_short_name))
9400 }
9401 }
9402}
9403
9404#[cfg(any(debug_assertions, test))]
9408fn cpp_declarations_hold_owned_fields<'unit>(
9409 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9410 file: &ProjectFile,
9411 package_name: &str,
9412 owner_short_name: &str,
9413) -> bool {
9414 let prefix = format!("{owner_short_name}.");
9415 declarations.into_iter().any(|unit| {
9416 unit.kind() == CodeUnitType::Field
9417 && unit.source() == file
9418 && unit.package_name() == package_name
9419 && if owner_short_name.is_empty() {
9420 !unit.short_name().contains(['.', '$'])
9424 } else {
9425 unit.short_name().starts_with(&prefix)
9426 }
9427 })
9428}
9429
9430#[derive(PartialEq, Eq, Hash)]
9438pub struct CppTreeIdentity {
9439 root_id: usize,
9440 start_byte: usize,
9441 end_byte: usize,
9442 kind_id: u16,
9443 child_count: usize,
9444}
9445
9446impl CppTreeIdentity {
9447 fn of(root: Node<'_>) -> Self {
9448 Self {
9449 root_id: root.id(),
9450 start_byte: root.start_byte(),
9451 end_byte: root.end_byte(),
9452 kind_id: root.kind_id(),
9453 child_count: root.child_count(),
9454 }
9455 }
9456}
9457
9458#[derive(Default)]
9472pub struct CppNamespaceForwardScan {
9473 scanned_through: usize,
9475 forwards: HashMap<String, Vec<CppNamespaceForward>>,
9476}
9477
9478impl CppNamespaceForwardScan {
9479 fn advance_to<'tree>(
9486 &mut self,
9487 root: Node<'tree>,
9488 cutoff: usize,
9489 source: &str,
9490 ancestry: &ParentIndex<'tree>,
9491 ) {
9492 if cutoff <= self.scanned_through {
9493 return;
9494 }
9495 let folded_through = self.scanned_through;
9496 let mut cursor = root.walk();
9497 let mut stack = vec![root];
9498 while let Some(current) = stack.pop() {
9499 if (folded_through..cutoff).contains(¤t.start_byte())
9500 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9501 {
9502 self.forwards
9503 .entry(forward.name.clone())
9504 .or_default()
9505 .push(forward);
9506 }
9507 for child in current.named_children(&mut cursor) {
9512 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
9513 stack.push(child);
9514 }
9515 }
9516 }
9517 self.scanned_through = cutoff;
9518 }
9519
9520 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
9524 let mut matching = self
9525 .forwards
9526 .get(name)
9527 .into_iter()
9528 .flatten()
9529 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
9530 let first = matching.next()?;
9531 matching
9532 .next()
9533 .is_none()
9534 .then(|| first.package_name.clone())
9535 }
9536}
9537
9538#[cfg(any(debug_assertions, test))]
9543fn unique_earlier_cpp_namespace_forward<'tree>(
9544 recovered_node: Node<'tree>,
9545 name: &str,
9546 source: &str,
9547 ancestry: &ParentIndex<'tree>,
9548) -> Option<String> {
9549 let mut root = recovered_node;
9550 while let Some(parent) = ancestry.parent(root) {
9551 root = parent;
9552 }
9553
9554 let mut candidates = Vec::new();
9555 let mut stack = vec![root];
9556 while let Some(current) = stack.pop() {
9557 if current.start_byte() < recovered_node.start_byte()
9558 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9559 && forward.name == name
9560 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
9561 {
9562 candidates.push(forward.package_name);
9563 }
9564
9565 let mut cursor = current.walk();
9566 for child in current.named_children(&mut cursor) {
9567 if child.start_byte() < recovered_node.start_byte() {
9568 stack.push(child);
9569 }
9570 }
9571 }
9572
9573 if candidates.len() == 1 {
9574 candidates.pop()
9575 } else {
9576 None
9577 }
9578}
9579
9580fn malformed_namespace_is_nearest_recovery_region(
9581 namespace_end_byte: usize,
9582 recovered_node: Node<'_>,
9583) -> bool {
9584 let mut root = recovered_node;
9585 while let Some(parent) = root.parent() {
9586 root = parent;
9587 }
9588 let mut cursor = root.walk();
9589 root.named_children(&mut cursor)
9590 .filter(|sibling| {
9591 namespace_end_byte <= sibling.start_byte()
9592 && sibling.end_byte() <= recovered_node.start_byte()
9593 })
9594 .all(is_malformed_namespace_recovery_trivia)
9595}
9596
9597fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
9598 matches!(node.kind(), "ERROR" | "comment")
9599 || node.kind().starts_with("preproc_")
9600 || node.kind() == "expression_statement" && node.named_child_count() == 0
9601}
9602
9603fn cpp_namespace_name_for_forward<'tree>(
9607 node: Node<'tree>,
9608 source: &str,
9609 ancestry: &ParentIndex<'tree>,
9610) -> Option<String> {
9611 cpp_namespace_definition_for_forward(node, ancestry)?;
9612 cpp_lexical_namespace_name(node, source, ancestry)
9613}
9614
9615fn cpp_namespace_definition_for_forward<'tree>(
9616 node: Node<'tree>,
9617 ancestry: &ParentIndex<'tree>,
9618) -> Option<Node<'tree>> {
9619 let declaration = ancestry.parent(node)?;
9620 let mut ancestor = ancestry.parent(declaration);
9621 while let Some(current) = ancestor {
9622 if matches!(
9623 current.kind(),
9624 "compound_statement"
9625 | "field_declaration_list"
9626 | "class_specifier"
9627 | "struct_specifier"
9628 | "union_specifier"
9629 | "function_definition"
9630 | "lambda_expression"
9631 ) {
9632 return None;
9633 }
9634 if current.kind() == "namespace_definition" {
9635 return Some(current);
9636 }
9637 ancestor = ancestry.parent(current);
9638 }
9639 None
9640}
9641
9642fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
9643 match node.kind() {
9644 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9645 "parenthesized_declarator" => node
9646 .child_by_field_name("declarator")
9647 .or_else(|| last_named_child(node))
9648 .is_some_and(is_pointer_wrapper_declarator),
9649 "template_function" => node
9650 .child_by_field_name("name")
9651 .is_some_and(is_function_pointer_like_inner_declarator),
9652 _ => false,
9653 }
9654}
9655
9656fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
9657 match node.kind() {
9658 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9659 "parenthesized_declarator" => node
9660 .child_by_field_name("declarator")
9661 .or_else(|| last_named_child(node))
9662 .is_some_and(is_pointer_wrapper_declarator),
9663 _ => false,
9664 }
9665}
9666
9667fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
9668 let cleaned = raw_name.trim_start_matches("template ").trim();
9669 let cleaned = cleaned.trim_start_matches("::");
9676 let parts: Vec<_> = cleaned
9685 .split("::")
9686 .filter(|component| !component.is_empty())
9687 .collect();
9688 if parts.is_empty() {
9689 return (None, cleaned.to_string(), scope.package_name.clone());
9690 }
9691 if parts.len() > 1 {
9692 let name = parts.last().unwrap_or(&cleaned).to_string();
9693 let owner_parts = &parts[..parts.len() - 1];
9694 if let Some(class_unit) = &scope.class_unit {
9695 return (
9698 Some(CppMemberOwner::Unit(class_unit.clone())),
9699 name,
9700 scope.package_name.clone(),
9701 );
9702 }
9703 if !scope.package_name.is_empty() {
9704 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
9719 let owner = (!nested.is_empty()).then(|| {
9720 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
9721 });
9722 return (owner, name, scope.package_name.clone());
9723 }
9724 let (owner, package_name) = if owner_parts.len() > 1 {
9726 (
9738 Some(CppMemberOwner::Chain(vec![
9739 owner_parts.last().unwrap_or(&"").to_string(),
9740 ])),
9741 owner_parts[..owner_parts.len() - 1].join("::"),
9742 )
9743 } else {
9744 (
9756 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
9757 cpp_using_directive_namespace_for_bare_owner(scope),
9758 )
9759 };
9760 return (owner, name, package_name);
9761 }
9762
9763 let package_name = scope.package_name.clone();
9764 let owner = scope
9765 .class_unit
9766 .as_ref()
9767 .map(|parent| CppMemberOwner::Unit(parent.clone()));
9768 (owner, cleaned.to_string(), package_name)
9769}
9770
9771fn strip_redundant_namespace_prefix<'a>(
9785 owner_parts: &'a [&'a str],
9786 package_name: &str,
9787) -> &'a [&'a str] {
9788 if package_name.is_empty() {
9789 return owner_parts;
9790 }
9791 let package_segments: Vec<&str> = package_name.split("::").collect();
9792 let max_prefix = owner_parts.len().min(package_segments.len());
9793 for prefix_len in (1..=max_prefix).rev() {
9794 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
9795 if &owner_parts[..prefix_len] == package_suffix {
9796 return &owner_parts[prefix_len..];
9797 }
9798 }
9799 owner_parts
9800}
9801
9802fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
9813 scope
9814 .visible_using_namespaces
9815 .iter()
9816 .min_by_key(|namespace| namespace.split("::").count())
9817 .cloned()
9818 .unwrap_or_default()
9819}
9820
9821struct CppQualifiedNameComponent {
9822 name: String,
9823 is_template_id: bool,
9824}
9825
9826fn qualified_class_name_chain(
9839 class_node: Node<'_>,
9840 source: &str,
9841 scope: &ScopeInfo,
9842) -> Option<Vec<String>> {
9843 if scope.package_name.is_empty() || scope.class_unit.is_some() {
9844 return None;
9845 }
9846 let name = class_node.child_by_field_name("name")?;
9847 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
9848 if explicitly_global
9849 || components.len() < 2
9850 || components.iter().any(|component| component.is_template_id)
9851 {
9852 return None;
9853 }
9854 let names = components
9855 .iter()
9856 .map(|component| component.name.as_str())
9857 .collect::<Vec<_>>();
9858 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
9859 if class_chain.is_empty() {
9860 return None;
9861 }
9862 Some(class_chain.iter().map(|name| name.to_string()).collect())
9863}
9864
9865fn structured_cpp_qualified_components(
9866 qualified_name: Node<'_>,
9867 source: &str,
9868) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
9869 if qualified_name.kind() != "qualified_identifier" {
9870 return None;
9871 }
9872
9873 let mut components = Vec::new();
9874 let mut current = qualified_name;
9875 let mut explicitly_global = false;
9876 loop {
9877 if current.kind() == "qualified_identifier" {
9878 if let Some(component) = current.child_by_field_name("scope") {
9879 components.push(canonical_cpp_qualified_component(component, source)?);
9880 } else if components.is_empty() {
9881 explicitly_global = true;
9882 } else {
9883 return None;
9884 }
9885 current = current.child_by_field_name("name")?;
9886 } else {
9887 components.push(canonical_cpp_qualified_component(current, source)?);
9888 break;
9889 }
9890 }
9891 Some((components, explicitly_global))
9892}
9893
9894fn split_structured_templated_cpp_name(
9895 declarator_name: Node<'_>,
9896 source: &str,
9897 scope: &ScopeInfo,
9898) -> Option<(Option<CppMemberOwner>, String, String)> {
9899 let (mut components, explicitly_global) =
9900 structured_cpp_qualified_components(declarator_name, source)?;
9901
9902 let terminal = components.pop()?;
9903 let owner_start = components
9904 .iter()
9905 .position(|component| component.is_template_id)?;
9906 let explicit_package = components[..owner_start]
9907 .iter()
9908 .map(|component| component.name.as_str())
9909 .collect::<Vec<_>>()
9910 .join("::");
9911 let explicit_package_is_empty = explicit_package.is_empty();
9912 let package_name = match (
9913 explicitly_global,
9914 scope.package_name.is_empty(),
9915 explicit_package_is_empty,
9916 ) {
9917 (true, _, _) => explicit_package,
9918 (false, _, true) => scope.package_name.clone(),
9919 (false, true, false) => explicit_package,
9920 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
9921 };
9922 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
9928 {
9929 cpp_using_directive_namespace_for_bare_owner(scope)
9930 } else {
9931 package_name
9932 };
9933 let owner_chain = components[owner_start..]
9934 .iter()
9935 .map(|component| component.name.clone())
9936 .collect::<Vec<_>>();
9937 if owner_chain.is_empty() || terminal.name.is_empty() {
9938 return None;
9939 }
9940
9941 Some((
9942 Some(CppMemberOwner::Chain(owner_chain)),
9943 terminal.name,
9944 package_name,
9945 ))
9946}
9947
9948fn canonical_cpp_qualified_component(
9949 mut component: Node<'_>,
9950 source: &str,
9951) -> Option<CppQualifiedNameComponent> {
9952 let mut is_template_id = false;
9953 loop {
9954 match component.kind() {
9955 "template_type" => {
9956 is_template_id = true;
9957 component = component.child_by_field_name("name")?;
9958 }
9959 "dependent_name" => component = component.named_child(0)?,
9960 "identifier"
9961 | "field_identifier"
9962 | "namespace_identifier"
9963 | "type_identifier"
9964 | "operator_name"
9965 | "destructor_name" => {
9966 let name = normalize_cpp_whitespace(node_text(component, source));
9967 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
9968 name,
9969 is_template_id,
9970 });
9971 }
9972 _ => component = component.child_by_field_name("name")?,
9973 }
9974 }
9975}
9976
9977fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
9978 if let Some(name) = macro_decorated_unqualified_name(node) {
9979 return extract_declarator_name(name, source);
9980 }
9981 match node.kind() {
9982 "identifier"
9983 | "field_identifier"
9984 | "type_identifier"
9985 | "operator_name"
9986 | "destructor_name"
9987 | "qualified_identifier" => node_text(node, source).to_string(),
9988 "function_declarator"
9989 | "pointer_declarator"
9990 | "reference_declarator"
9991 | "parenthesized_declarator"
9992 | "array_declarator"
9993 | "template_function" => node
9994 .child_by_field_name("declarator")
9995 .or_else(|| node.child_by_field_name("name"))
9996 .or_else(|| last_named_child(node))
9997 .map(|child| extract_declarator_name(child, source))
9998 .unwrap_or_else(|| node_text(node, source).to_string()),
9999 _ => node
10000 .child_by_field_name("name")
10001 .map(|child| extract_declarator_name(child, source))
10002 .unwrap_or_else(|| node_text(node, source).to_string()),
10003 }
10004}
10005
10006fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
10011 if let Some(name) = macro_decorated_unqualified_name(node) {
10012 return extract_callable_declarator_name(name, source);
10013 }
10014 match node.kind() {
10015 "identifier"
10016 | "field_identifier"
10017 | "type_identifier"
10018 | "operator_name"
10019 | "destructor_name"
10020 | "qualified_identifier" => Some(node_text(node, source).to_string()),
10021 "function_declarator"
10022 | "pointer_declarator"
10023 | "reference_declarator"
10024 | "parenthesized_declarator"
10025 | "array_declarator"
10026 | "template_function" => node
10027 .child_by_field_name("declarator")
10028 .or_else(|| node.child_by_field_name("name"))
10029 .and_then(|child| extract_callable_declarator_name(child, source)),
10030 _ => None,
10031 }
10032}
10033
10034fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
10035 match node.kind() {
10036 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
10037 let name = node_text(node, source).trim().to_string();
10038 (!name.is_empty()).then_some(name)
10039 }
10040 _ => node
10041 .child_by_field_name("declarator")
10042 .or_else(|| node.child_by_field_name("name"))
10043 .or_else(|| last_named_child(node))
10044 .and_then(|child| extract_variable_name(child, source)),
10045 }
10046}
10047
10048#[derive(Clone, Copy)]
10054pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
10055 pub(crate) name: Node<'tree>,
10056 pub(crate) declarator: Node<'tree>,
10057}
10058
10059impl RecoveredFunctionLikeFieldDeclarator<'_> {
10060 pub(crate) fn pointer_depth(self) -> i32 {
10061 let mut depth = 0;
10062 let mut current = self.declarator;
10063 while current.kind() != "function_declarator" {
10064 if current.kind() == "pointer_declarator" {
10065 depth += 1;
10066 }
10067 current = current
10068 .child_by_field_name("declarator")
10069 .expect("recovered field wrapper has an inner declarator");
10070 }
10071 depth
10072 }
10073}
10074
10075fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
10086 if node.kind() != "field_declaration" {
10087 return false;
10088 }
10089 let Some(type_node) = node.child_by_field_name("type") else {
10090 return false;
10091 };
10092 if !matches!(type_node.kind(), "identifier" | "type_identifier") {
10093 return false;
10094 }
10095 let Some(declarator) = node.child_by_field_name("declarator") else {
10096 return false;
10097 };
10098 if declarator.kind() != "parenthesized_declarator" {
10099 return false;
10100 }
10101 let mut invented_terminator = false;
10102 for child in children_iter(node) {
10103 if child.kind() != ";" {
10104 continue;
10105 }
10106 if !child.is_missing() {
10107 return false;
10108 }
10109 invented_terminator = true;
10110 }
10111 invented_terminator
10112}
10113
10114pub(crate) fn recovered_function_like_field_declarator<'tree>(
10115 node: Node<'tree>,
10116 source: &str,
10117) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
10118 if node.kind() != "field_declaration" {
10119 return None;
10120 }
10121 let outer_declarator = node.child_by_field_name("declarator")?;
10122 let mut declarator = outer_declarator;
10123 while matches!(
10124 declarator.kind(),
10125 "pointer_declarator"
10126 | "reference_declarator"
10127 | "array_declarator"
10128 | "parenthesized_declarator"
10129 ) {
10130 declarator = declarator.child_by_field_name("declarator")?;
10131 }
10132 if declarator.kind() != "function_declarator" {
10133 return None;
10134 }
10135 let macro_name = declarator.child_by_field_name("declarator")?;
10136 if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
10137 {
10138 return None;
10139 }
10140 let parameters = declarator.child_by_field_name("parameters")?;
10141 let mut cursor = parameters.walk();
10142 let mut arguments = parameters.named_children(&mut cursor);
10143 let parameter = arguments.next()?;
10144 if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
10145 return None;
10146 }
10147 let name = parameter.child_by_field_name("type").filter(|argument| {
10148 matches!(
10149 argument.kind(),
10150 "identifier" | "field_identifier" | "type_identifier"
10151 )
10152 })?;
10153 Some(RecoveredFunctionLikeFieldDeclarator {
10154 name,
10155 declarator: outer_declarator,
10156 })
10157}
10158
10159fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
10160 let count = node.named_child_count();
10161 if count == 0 {
10162 None
10163 } else {
10164 node.named_child(count - 1)
10165 }
10166}
10167
10168fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
10169 let name_node = node.child_by_field_name("name")?;
10170 let name = normalize_cpp_whitespace(node_text(name_node, source));
10171 (!name.is_empty()).then_some(name)
10172}
10173
10174fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
10175 if node.kind() != "declaration" {
10176 return Vec::new();
10177 }
10178 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
10179 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
10180 }) else {
10181 return Vec::new();
10182 };
10183 let Some(declarator) = node.child_by_field_name("declarator") else {
10184 return Vec::new();
10185 };
10186 if node_text(keyword, source) == "using"
10187 && (declarator.kind() != "init_declarator"
10188 || declarator.child_by_field_name("value").is_none())
10189 {
10190 return Vec::new();
10191 }
10192 if node_text(keyword, source) == "typedef"
10193 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
10194 {
10195 return vec![alias_name];
10196 }
10197 extract_typedef_declarator_name(declarator, source)
10198 .into_iter()
10199 .collect()
10200}
10201
10202fn recovered_typedef_error_alias_name(
10203 declaration: Node<'_>,
10204 declarator: Node<'_>,
10205 source: &str,
10206) -> Option<String> {
10207 if declarator.kind() != "qualified_identifier" {
10217 return None;
10218 }
10219 let mut cursor = declaration.walk();
10220 let mut errors = declaration
10221 .named_children(&mut cursor)
10222 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
10223 let error = errors.next()?;
10224 if errors.next().is_some() || error.named_child_count() != 1 {
10225 return None;
10226 }
10227 let name = error.named_child(0)?;
10228 if !matches!(
10229 name.kind(),
10230 "identifier" | "field_identifier" | "type_identifier"
10231 ) {
10232 return None;
10233 }
10234 let name = normalize_cpp_whitespace(node_text(name, source));
10235 (!name.is_empty()).then_some(name)
10236}
10237
10238fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
10239 if fragmented_parenthesized_typedef_type(node).is_some() {
10243 return Vec::new();
10244 }
10245 let has_function_like_macro_type = node
10246 .child_by_field_name("type")
10247 .filter(|type_node| type_node.kind() == "type_identifier")
10248 .is_some_and(|type_node| {
10249 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
10250 });
10251 let mut names = Vec::new();
10252 let mut cursor = node.walk();
10253 for declarator in node.children_by_field_name("declarator", &mut cursor) {
10254 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
10255 continue;
10256 }
10257 if let Some(name) = extract_typedef_declarator_name(declarator, source)
10258 && !names.contains(&name)
10259 {
10260 names.push(name);
10261 }
10262 }
10263 names
10264}
10265
10266struct RecoveredMacroTypedefAlias<'tree> {
10267 name: String,
10268 end_node: Node<'tree>,
10269}
10270
10271fn recovered_macro_typedef_alias<'tree>(
10275 node: Node<'tree>,
10276 source: &str,
10277) -> Option<RecoveredMacroTypedefAlias<'tree>> {
10278 let type_node = fragmented_parenthesized_typedef_type(node)?;
10279 if type_node.kind() != "type_identifier"
10280 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
10281 {
10282 return None;
10283 }
10284
10285 let end_node = node.next_named_sibling()?;
10286 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
10287 return None;
10288 }
10289 let name_node = end_node.named_child(0)?;
10290 if name_node.kind() != "identifier" {
10291 return None;
10292 }
10293 let has_terminator = (0..end_node.child_count()).any(|index| {
10294 end_node
10295 .child(index)
10296 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
10297 });
10298 if !has_terminator {
10299 return None;
10300 }
10301 let name = normalize_cpp_whitespace(node_text(name_node, source));
10302 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
10303}
10304
10305fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
10306 if node.kind() != "type_definition" {
10307 return None;
10308 }
10309 let mut declarator_cursor = node.walk();
10310 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
10311 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
10312 return None;
10313 }
10314 let has_missing_terminator = (0..node.child_count()).any(|index| {
10315 node.child(index)
10316 .is_some_and(|child| child.kind() == ";" && child.is_missing())
10317 });
10318 if !has_missing_terminator {
10319 return None;
10320 }
10321 node.child_by_field_name("type")
10322}
10323
10324fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
10325 match node.kind() {
10326 "identifier" | "field_identifier" | "type_identifier" => {
10327 let name = normalize_cpp_whitespace(node_text(node, source));
10328 (!name.is_empty()).then_some(name)
10329 }
10330 "qualified_identifier" => node
10331 .child_by_field_name("name")
10332 .and_then(|name| extract_typedef_declarator_name(name, source)),
10333 _ => node
10334 .child_by_field_name("declarator")
10335 .or_else(|| node.child_by_field_name("name"))
10336 .or_else(|| last_named_child(node))
10337 .and_then(|child| extract_typedef_declarator_name(child, source)),
10338 }
10339}
10340
10341fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
10342 let name = node
10343 .child_by_field_name("name")
10344 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10345 .or_else(|| {
10346 let mut cursor = node.walk();
10347 node.named_children(&mut cursor)
10348 .find(|child| {
10349 matches!(
10350 child.kind(),
10351 "identifier" | "field_identifier" | "type_identifier"
10352 )
10353 })
10354 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10355 })?;
10356 (!name.is_empty()).then_some(name)
10357}
10358
10359fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
10360 left.id() == right.id()
10361}
10362
10363fn render_cpp_type_signature(
10364 node: Node<'_>,
10365 source: &str,
10366 template_signature: Option<&str>,
10367) -> String {
10368 let text = normalize_cpp_whitespace(node_text(node, source));
10369 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
10370 let rendered = if head.ends_with(';') {
10371 head.to_string()
10372 } else {
10373 format!("{head} {{")
10374 };
10375 if let Some(template_signature) = template_signature {
10376 format!("template {template_signature} {rendered}")
10377 } else {
10378 rendered
10379 }
10380}
10381
10382fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
10383 if let Some(recovered) = recovered_pyobject_head_field(node, source)
10384 && recovered.declarator == declarator
10385 {
10386 let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
10387 let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
10388 return format!("{type_text} {declarator};");
10389 }
10390 if let Some(recovered) = recovered_function_like_field_declarator(node, source)
10391 && recovered.name == declarator
10392 {
10393 let type_text = node
10394 .child_by_field_name("type")
10395 .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
10396 .unwrap_or_default();
10397 let name = normalize_cpp_whitespace(node_text(recovered.name, source));
10398 let mut prefix = String::new();
10399 let mut suffix = String::new();
10400 let mut current = recovered.declarator;
10401 while current.kind() != "function_declarator" {
10402 match current.kind() {
10403 "pointer_declarator" => prefix.push('*'),
10404 "reference_declarator" => prefix.push('&'),
10405 "array_declarator" => {
10406 let size = current
10407 .child_by_field_name("size")
10408 .map(|size| normalize_cpp_whitespace(node_text(size, source)))
10409 .unwrap_or_default();
10410 suffix.push('[');
10411 suffix.push_str(&size);
10412 suffix.push(']');
10413 }
10414 "parenthesized_declarator" => {}
10415 _ => unreachable!("validated recovered field declarator wrapper"),
10416 }
10417 current = current
10418 .child_by_field_name("declarator")
10419 .expect("recovered field wrapper has an inner declarator");
10420 }
10421 let separator = if prefix.is_empty() { "" } else { " " };
10422 return format!("{type_text} {prefix}{separator}{name}{suffix};");
10423 }
10424 if let Some(signature) =
10425 render_recovered_macro_qualified_field_signature(node, declarator, source)
10426 {
10427 return signature;
10428 }
10429 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
10430 let prefix = cpp_declaration_prefix(node, source);
10431 let name = extract_variable_name(declarator, source).unwrap_or_default();
10432 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
10433 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
10434 || (prefix.ends_with('&') && raw_suffix == "&")
10435 {
10436 String::new()
10437 } else {
10438 raw_suffix
10439 };
10440
10441 let mut rendered = if suffix.is_empty() {
10442 format!("{prefix} {name}")
10443 } else if suffix.starts_with('*') || suffix.starts_with('&') {
10444 format!("{prefix}{suffix} {name}")
10445 } else if suffix.starts_with('[') || suffix.starts_with('(') {
10446 format!("{prefix} {name}{suffix}")
10447 } else {
10448 format!("{prefix} {suffix}{name}")
10449 };
10450 rendered = collapse_cpp_whitespace(&rendered);
10451
10452 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10453 format!("{rendered} = {initializer};")
10454 } else if declaration_text.ends_with(';') {
10455 format!("{rendered};")
10456 } else {
10457 rendered
10458 }
10459}
10460
10461fn render_recovered_macro_qualified_field_signature(
10462 node: Node<'_>,
10463 declarator: Node<'_>,
10464 source: &str,
10465) -> Option<String> {
10466 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
10467 if !recovered
10468 .iter()
10469 .any(|candidate| same_node(*candidate, declarator))
10470 {
10471 return None;
10472 }
10473 let pseudo_declarator = node.child_by_field_name("declarator")?;
10474 let mut cursor = node.walk();
10475 let clause = node
10476 .named_children(&mut cursor)
10477 .find(|child| child.kind() == "bitfield_clause")?;
10478 let mut cursor = clause.walk();
10479 let error = clause
10480 .named_children(&mut cursor)
10481 .find(|child| child.kind() == "ERROR")?;
10482 let qualified_type =
10483 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
10484 let prefix = cpp_declaration_prefix(node, source);
10485 let name = extract_variable_name(declarator, source)?;
10486 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
10487 let mut rendered = if suffix.is_empty() {
10488 format!("{prefix} {qualified_type} {name}")
10489 } else {
10490 format!("{prefix} {qualified_type} {suffix} {name}")
10491 };
10492 rendered = collapse_cpp_whitespace(&rendered);
10493
10494 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
10495 Some(format!(
10496 "{rendered} = {};",
10497 normalize_cpp_whitespace(node_text(initializer, source))
10498 ))
10499 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10500 Some(format!("{rendered} = {initializer};"))
10501 } else {
10502 Some(format!("{rendered};"))
10503 }
10504}
10505
10506fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
10507 match node.kind() {
10508 "pointer_expression" => {
10509 let operator = node
10510 .child_by_field_name("operator")
10511 .or_else(|| node.child(0))
10512 .map(|operator| node_text(operator, source))
10513 .unwrap_or("*");
10514 let argument = node
10515 .child_by_field_name("argument")
10516 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10517 .unwrap_or_default();
10518 format!("{operator}{argument}")
10519 }
10520 "unary_expression" => {
10521 let operator = node
10522 .child_by_field_name("operator")
10523 .or_else(|| node.child(0))
10524 .map(|operator| node_text(operator, source))
10525 .unwrap_or_default();
10526 let argument = node
10527 .child_by_field_name("argument")
10528 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10529 .unwrap_or_default();
10530 format!("{operator}{argument}")
10531 }
10532 "identifier" | "field_identifier" => String::new(),
10533 _ => cpp_declarator_suffix_without_name(node, source),
10534 }
10535}
10536
10537fn recovered_macro_qualified_field_initializer<'tree>(
10538 clause: Node<'tree>,
10539 declarator: Node<'tree>,
10540) -> Option<Node<'tree>> {
10541 let mut stack = vec![clause];
10542 while let Some(current) = stack.pop() {
10543 if current.kind() == "assignment_expression"
10544 && current
10545 .child_by_field_name("left")
10546 .is_some_and(|left| same_node(left, declarator))
10547 {
10548 return current.child_by_field_name("right");
10549 }
10550 let mut cursor = current.walk();
10551 stack.extend(current.named_children(&mut cursor));
10552 }
10553 None
10554}
10555
10556fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
10557 let text = node_text(node, source);
10558 let mut cursor = node.walk();
10559 let first_declarator = node.named_children(&mut cursor).find(|child| {
10560 matches!(
10561 child.kind(),
10562 "init_declarator"
10563 | "identifier"
10564 | "field_identifier"
10565 | "pointer_declarator"
10566 | "reference_declarator"
10567 | "array_declarator"
10568 | "function_declarator"
10569 )
10570 });
10571 let prefix = if let Some(first_declarator) = first_declarator {
10572 let end = first_declarator
10573 .start_byte()
10574 .saturating_sub(node.start_byte());
10575 let mut prefix = text.get(..end).unwrap_or(text).to_string();
10576 let declarator_suffix = match first_declarator.kind() {
10577 "init_declarator" => first_declarator
10578 .child_by_field_name("declarator")
10579 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
10580 .unwrap_or_default(),
10581 _ => cpp_declarator_suffix_without_name(first_declarator, source),
10582 };
10583 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
10584 prefix.push_str(&declarator_suffix);
10585 }
10586 return collapse_cpp_whitespace(&prefix)
10587 .trim_end_matches(',')
10588 .trim_end_matches(';')
10589 .trim()
10590 .to_string();
10591 } else {
10592 text
10593 };
10594 collapse_cpp_whitespace(prefix)
10595 .trim_end_matches(',')
10596 .trim_end_matches(';')
10597 .trim()
10598 .to_string()
10599}
10600
10601fn cpp_preserved_initializer(
10602 declaration_node: Node<'_>,
10603 declarator: Node<'_>,
10604 source: &str,
10605) -> Option<String> {
10606 let name = extract_variable_name(declarator, source)?;
10607 let mut cursor = declaration_node.walk();
10608 for child in declaration_node.named_children(&mut cursor) {
10609 if child.kind() != "init_declarator" {
10610 continue;
10611 }
10612 let Some(inner) = child.child_by_field_name("declarator") else {
10613 continue;
10614 };
10615 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
10616 continue;
10617 }
10618 let value = child.child_by_field_name("value")?;
10619 let kind = value.kind();
10620 if matches!(
10621 kind,
10622 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
10623 ) {
10624 return Some(normalize_cpp_whitespace(node_text(value, source)));
10625 }
10626 break;
10627 }
10628 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
10629 let pattern = format!(
10630 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
10631 regex::escape(&name)
10632 );
10633 Regex::new(&pattern)
10634 .ok()
10635 .and_then(|regex| regex.captures(&declaration_text))
10636 .and_then(|captures| captures.get(1))
10637 .map(|value| value.as_str().to_string())
10638}
10639
10640fn render_cpp_function_display_signature_from_node<'tree>(
10641 node: Node<'tree>,
10642 source: &str,
10643 template_signature: Option<&str>,
10644 has_body: bool,
10645 ancestry: &ParentIndex<'tree>,
10646) -> String {
10647 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
10648 let parent_text = node_text(root, source);
10649 let body_local_start = root
10650 .child_by_field_name("body")
10651 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
10652 .unwrap_or(parent_text.len());
10653 let display = parent_text
10654 .get(..body_local_start)
10655 .unwrap_or(parent_text)
10656 .trim()
10657 .trim();
10658 let display = if let Some(template_signature) = template_signature {
10659 if display.starts_with("template ") {
10660 display.to_string()
10661 } else {
10662 format!("template {template_signature} {display}")
10663 }
10664 } else {
10665 display.to_string()
10666 };
10667 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
10668 if has_body {
10669 format!("{display} {{...}}")
10670 } else {
10671 format!("{display};")
10672 }
10673}
10674
10675fn cpp_template_signature(
10676 template_node: Node<'_>,
10677 declaration_child: Node<'_>,
10678 source: &str,
10679) -> Option<String> {
10680 let text = source
10681 .get(template_node.start_byte()..declaration_child.start_byte())
10682 .unwrap_or("");
10683 let text = normalize_cpp_whitespace(text);
10684 let start = text.find('<')?;
10685 let end = text.rfind('>')?;
10686 if end < start {
10687 return None;
10688 }
10689 Some(text[start..=end].to_string())
10690}
10691
10692struct RecoveredFragmentedPartialSpecialization<'tree> {
10693 declaration_node: Node<'tree>,
10694 name: String,
10695 range: Range,
10696 prefix_members: Vec<Node<'tree>>,
10697 member_siblings: Vec<Node<'tree>>,
10698 following_declarations: Vec<Node<'tree>>,
10699}
10700
10701struct RecoveredFragmentedPreprocessorClass<'tree> {
10702 declaration_node: Node<'tree>,
10703 class_node: Node<'tree>,
10704 body: Node<'tree>,
10705 name: String,
10706 range: Range,
10707 tail_members: Vec<Node<'tree>>,
10708 member_siblings: Vec<Node<'tree>>,
10709}
10710
10711fn recover_fragmented_preprocessor_class<'tree>(
10720 template_node: Node<'tree>,
10721 source: &str,
10722 ancestry: &ParentIndex<'tree>,
10723) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
10724 let alternative = ancestry.parent(template_node)?;
10725 if alternative.kind() != "preproc_else" {
10726 return None;
10727 }
10728 let conditional = alternative.parent()?;
10729 if conditional.kind() != "preproc_if" {
10730 return None;
10731 }
10732 let declaration_node = template_node
10733 .named_children(&mut template_node.walk())
10734 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
10735 let class_node = declaration_node
10736 .named_children(&mut declaration_node.walk())
10737 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
10738 let body = cpp_body_node(class_node)?;
10739 if class_node.end_byte() >= declaration_node.end_byte() {
10740 return None;
10741 }
10742 let name = class_like_name(class_node, source, ancestry)?;
10743 let is_partial_specialization = class_node
10744 .child_by_field_name("name")
10745 .is_some_and(|class_name| class_name.kind() == "template_type");
10746 if is_partial_specialization {
10747 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
10748 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
10749 return None;
10750 }
10751 } else {
10752 if !class_has_displaced_preprocessor_terminator(class_node) {
10753 return None;
10754 }
10755 let matching_other_branch = conditional
10756 .named_children(&mut conditional.walk())
10757 .take_while(|child| !same_node(*child, alternative))
10758 .filter(|child| child.kind() == "template_declaration")
10759 .filter_map(first_class_like_child)
10760 .any(|candidate| {
10761 cpp_body_node(candidate).is_none()
10762 && class_like_name(candidate, source, ancestry).as_deref()
10763 == Some(name.as_str())
10764 });
10765 if !matching_other_branch {
10766 return None;
10767 }
10768 }
10769
10770 let mut tail_members = Vec::new();
10771 let mut saw_class = false;
10772 let mut declaration_cursor = declaration_node.walk();
10773 for child in declaration_node.named_children(&mut declaration_cursor) {
10774 if same_node(child, class_node) {
10775 saw_class = true;
10776 } else if saw_class {
10777 tail_members.push(child);
10778 }
10779 }
10780
10781 let mut member_siblings = Vec::new();
10782 let mut saw_template = false;
10783 let mut terminator = None;
10784 for index in 0..alternative.child_count() {
10785 let Some(child) = alternative.child(index) else {
10786 continue;
10787 };
10788 if same_node(child, template_node) {
10789 saw_template = true;
10790 continue;
10791 }
10792 if !saw_template {
10793 continue;
10794 }
10795 if displaced_fragmented_class_terminator(alternative, index) {
10796 terminator = alternative.child(index + 1);
10797 break;
10798 }
10799 if child.is_named() {
10800 member_siblings.push(child);
10801 }
10802 }
10803 let terminator = terminator?;
10804 Some(RecoveredFragmentedPreprocessorClass {
10805 declaration_node,
10806 class_node,
10807 body,
10808 name,
10809 range: Range {
10810 start_byte: class_node.start_byte(),
10811 end_byte: terminator.end_byte(),
10812 start_line: class_node.start_position().row + 1,
10813 end_line: terminator.end_position().row + 1,
10814 },
10815 tail_members,
10816 member_siblings,
10817 })
10818}
10819
10820fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
10821 (0..class_node.child_count()).any(|index| {
10822 class_node.child(index).is_some_and(|child| {
10823 child.kind() == "ERROR"
10824 && (0..child.child_count()).any(|error_index| {
10825 child
10826 .child(error_index)
10827 .is_some_and(|token| token.kind() == "#endif")
10828 })
10829 })
10830 })
10831}
10832
10833pub fn cpp_displaced_preprocessor_terminator<'tree>(
10842 conditional: Node<'tree>,
10843) -> Option<Node<'tree>> {
10844 if !conditional.has_error() {
10845 return None;
10846 }
10847 let has_concrete_direct_terminator = conditional
10848 .child_count()
10849 .checked_sub(1)
10850 .and_then(|index| conditional.child(index))
10851 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
10852 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
10853 return None;
10857 }
10858 let mut displaced = None;
10864 let mut conditional_depth = 0usize;
10865 let mut stack = children_iter(conditional)
10866 .map(|child| (child, false))
10867 .collect::<Vec<_>>();
10868 stack.reverse();
10869 while let Some((node, inside_error)) = stack.pop() {
10870 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
10871 continue;
10872 }
10873 if node != conditional
10874 && matches!(
10875 node.kind(),
10876 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
10877 )
10878 {
10879 continue;
10880 }
10881 let inside_error = inside_error || node.kind() == "ERROR";
10882 match node.kind() {
10883 "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
10884 conditional_depth += 1;
10885 }
10886 "#endif" if !node.is_missing() => {
10887 if conditional_depth == 0
10888 && inside_error
10889 && displaced
10890 .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
10891 {
10892 displaced = Some(node);
10893 }
10894 conditional_depth = conditional_depth.saturating_sub(1);
10895 }
10896 _ => {}
10897 }
10898 let first_pushed = stack.len();
10899 stack.extend(children_iter(node).map(|child| (child, inside_error)));
10900 stack[first_pushed..].reverse();
10901 }
10902 displaced
10903}
10904
10905#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10918pub struct CppDisplacedPreprocessorBoundary {
10919 pub end_byte: usize,
10920 pub end_line: usize,
10921}
10922
10923pub fn cpp_displaced_preprocessor_boundary(
10924 conditional: Node<'_>,
10925) -> Option<CppDisplacedPreprocessorBoundary> {
10926 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
10927 return Some(CppDisplacedPreprocessorBoundary {
10928 end_byte: terminator.end_byte(),
10929 end_line: terminator.end_position().row + 1,
10930 });
10931 }
10932 if let Some(declaration) = displaced_split_declaration(conditional) {
10933 return Some(CppDisplacedPreprocessorBoundary {
10934 end_byte: declaration.end_byte(),
10935 end_line: declaration.end_position().row + 1,
10936 });
10937 }
10938 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
10939 return Some(CppDisplacedPreprocessorBoundary {
10940 end_byte: terminator.end_byte(),
10941 end_line: terminator.end_position().row + 1,
10942 });
10943 }
10944 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
10945 return Some(CppDisplacedPreprocessorBoundary {
10946 end_byte: terminator.end_byte(),
10947 end_line: terminator.end_position().row + 1,
10948 });
10949 }
10950 None
10951}
10952
10953fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10959 if !conditional.has_error()
10960 || conditional.child_by_field_name("alternative").is_some()
10961 || conditional
10962 .child(conditional.child_count().saturating_sub(1))
10963 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
10964 {
10965 return None;
10966 }
10967 let mut recovered = None;
10968 for nested in named_children_iter(conditional) {
10969 if !matches!(
10970 nested.kind(),
10971 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
10972 ) || nested.child_by_field_name("alternative").is_some()
10973 {
10974 continue;
10975 }
10976 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
10977 continue;
10978 };
10979 if direct.kind() != "#endif" || direct.is_missing() {
10980 continue;
10981 }
10982 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
10983 continue;
10984 };
10985 if displaced.end_byte() >= direct.start_byte() {
10986 continue;
10987 }
10988 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
10989 recovered = Some(direct);
10990 }
10991 }
10992 recovered
10993}
10994
10995fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10996 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
10997 return None;
10998 }
10999 let mut cursor = conditional.walk();
11000 let declarations = conditional
11001 .named_children(&mut cursor)
11002 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
11003 .collect::<Vec<_>>();
11004 let declaration = *declarations.first()?;
11005 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
11006 return None;
11007 }
11008 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
11009 let mut terminator = None;
11010 let mut stack = (0..declaration.child_count())
11011 .filter_map(|index| declaration.child(index))
11012 .filter(|child| child.start_byte() < declarator_start)
11013 .map(|child| (child, false))
11014 .collect::<Vec<_>>();
11015 while let Some((node, inside_error)) = stack.pop() {
11016 let inside_error = inside_error || node.kind() == "ERROR";
11017 if inside_error && node.kind() == "#endif" && !node.is_missing() {
11018 terminator = Some(node);
11019 continue;
11020 }
11021 for index in 0..node.child_count() {
11022 if let Some(child) = node.child(index)
11023 && child.start_byte() < declarator_start
11024 {
11025 stack.push((child, inside_error));
11026 }
11027 }
11028 }
11029 terminator
11030}
11031
11032fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
11033 if !conditional.has_error()
11034 || conditional.child_by_field_name("alternative").is_some()
11035 || conditional
11036 .prev_named_sibling()
11037 .filter(|sibling| {
11038 sibling.kind() == "ERROR"
11039 && sibling.child_count() == 1
11040 && sibling
11041 .child(0)
11042 .is_some_and(|child| child.kind() == "typedef")
11043 })
11044 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
11045 .is_none()
11046 {
11047 return None;
11048 }
11049 let mut cursor = conditional.walk();
11050 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
11051 let declaration_index = children
11052 .iter()
11053 .position(|child| child.kind() == "declaration" && child.has_error())?;
11054 let declaration = children[declaration_index];
11055 if !children
11056 .iter()
11057 .skip(declaration_index + 1)
11058 .any(|child| child.end_byte() > declaration.end_byte())
11059 {
11060 return None;
11061 }
11062 let declarator = declaration.child_by_field_name("declarator")?;
11063 let mut error_end = None;
11064 let mut names = Vec::new();
11065 let mut stack = vec![declarator];
11066 while let Some(node) = stack.pop() {
11067 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
11068 error_end =
11069 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
11070 continue;
11071 }
11072 if matches!(node.kind(), "identifier" | "type_identifier") {
11073 names.push(node.start_byte());
11074 }
11075 push_named_children_reversed(node, &mut stack);
11076 }
11077 let error_end = error_end?;
11078 names
11079 .into_iter()
11080 .any(|start| start >= error_end)
11081 .then_some(declaration)
11082}
11083
11084fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
11085 let Some(error) = parent.child(error_index) else {
11086 return false;
11087 };
11088 if error.kind() != "ERROR"
11089 || error.child_count() != 1
11090 || error.child(0).is_none_or(|child| child.kind() != "}")
11091 {
11092 return false;
11093 }
11094 let Some(semicolon) = parent.child(error_index + 1) else {
11095 return false;
11096 };
11097 semicolon.kind() == "expression_statement"
11098 && semicolon.child_count() == 1
11099 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
11100}
11101
11102fn displaced_macro_class_tail(
11108 declaration_node: Node<'_>,
11109 body: Node<'_>,
11110 source: &str,
11111) -> Option<DisplacedMacroClassTail> {
11112 if !matches!(
11113 declaration_node.kind(),
11114 "class_specifier" | "struct_specifier" | "union_specifier"
11115 ) || body.kind() != "field_declaration_list"
11116 {
11117 return None;
11118 }
11119
11120 let child_count = body.named_child_count();
11121 for index in 0..child_count {
11122 let child = body.named_child(index)?;
11123 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
11124 continue;
11125 };
11126 let split_index = index + 1;
11127 if split_index >= child_count {
11128 return None;
11129 }
11130 let mut cursor = body.walk();
11131 if !body
11132 .named_children(&mut cursor)
11133 .skip(split_index)
11134 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
11135 {
11136 return None;
11137 }
11138 return Some(DisplacedMacroClassTail {
11139 split_index,
11140 class_range: Range {
11141 start_byte: declaration_node.start_byte(),
11142 end_byte: terminator.end_byte(),
11143 start_line: declaration_node.start_position().row + 1,
11144 end_line: terminator.end_position().row + 1,
11145 },
11146 });
11147 }
11148 None
11149}
11150
11151fn displaced_macro_field_terminator<'tree>(
11152 field: Node<'tree>,
11153 source: &str,
11154) -> Option<Node<'tree>> {
11155 if field.kind() != "field_declaration" {
11156 return None;
11157 }
11158 let macro_type = field.child_by_field_name("type")?;
11159 if macro_type.kind() != "type_identifier"
11160 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11161 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
11162 {
11163 return None;
11164 }
11165 for index in 0..field.child_count() {
11166 let error = field.child(index)?;
11167 if error.kind() != "ERROR"
11168 || error.child_count() != 1
11169 || error.child(0).is_none_or(|child| child.kind() != "}")
11170 {
11171 continue;
11172 }
11173 let semicolon = field.child(index + 1)?;
11174 if semicolon.kind() == ";" {
11175 return Some(semicolon);
11176 }
11177 }
11178 None
11179}
11180
11181fn recover_fragmented_partial_specialization<'tree>(
11182 template_node: Node<'tree>,
11183 declaration_child: Node<'tree>,
11184 source: &str,
11185 ancestry: &ParentIndex<'tree>,
11186) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
11187 if declaration_child.kind() != "function_definition" {
11188 return None;
11189 }
11190 let class_node = declaration_child.child_by_field_name("type")?;
11191 if !matches!(
11192 class_node.kind(),
11193 "class_specifier" | "struct_specifier" | "union_specifier"
11194 ) || !class_node
11195 .child_by_field_name("name")
11196 .and_then(|name| direct_identifier_name(name, source))
11197 .is_some_and(|name| cpp_export_macro_token(&name))
11198 {
11199 return None;
11200 }
11201 let declarator = declaration_child.child_by_field_name("declarator")?;
11202 if declarator.kind() != "template_function" {
11203 return None;
11204 }
11205 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
11206 if metadata.specialization_arguments.is_empty() {
11207 return None;
11208 }
11209 let body = declaration_child.child_by_field_name("body")?;
11210 if body.kind() != "compound_statement" {
11211 return None;
11212 }
11213 let complete_prefix = body.named_child(0).filter(|first| {
11214 first.kind() == "labeled_statement"
11215 && first.has_error()
11216 && first
11217 .named_child(first.named_child_count().saturating_sub(1))
11218 .is_some_and(recovered_declaration_has_class_terminator)
11219 });
11220 let complete_body = complete_prefix.is_some();
11221 let mut prefix_members = Vec::new();
11222 if let Some(prefix) = complete_prefix {
11223 prefix_members.push(prefix);
11224 } else {
11225 let mut body_cursor = body.walk();
11226 for child in body.named_children(&mut body_cursor) {
11227 if !is_structurally_valid_fragmented_class_prefix_member(child) {
11228 break;
11229 }
11230 prefix_members.push(child);
11231 }
11232 }
11233 let containing_declarations = template_node.parent()?;
11234 if !matches!(
11235 containing_declarations.kind(),
11236 "declaration_list" | "compound_statement"
11237 ) {
11238 return None;
11239 }
11240 let mut member_siblings = Vec::new();
11241 let mut following_declarations = Vec::new();
11242 let terminator;
11243 if complete_body {
11244 terminator = complete_prefix?;
11245 let mut cursor = body.walk();
11246 let mut after_prefix = false;
11247 for child in body.named_children(&mut cursor) {
11248 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
11249 after_prefix = true;
11250 } else if after_prefix {
11251 following_declarations.push(child);
11252 }
11253 }
11254 } else {
11255 let mut found_template = false;
11256 let mut cursor = containing_declarations.walk();
11257 let mut class_terminator = None;
11258 for child in containing_declarations.children(&mut cursor) {
11259 if same_node(child, template_node) {
11260 found_template = true;
11261 continue;
11262 }
11263 if found_template && child.kind() == "}" {
11264 class_terminator = Some(child);
11265 break;
11266 }
11267 if found_template && child.kind() == "namespace_definition" {
11274 return None;
11275 }
11276 if found_template && child.is_named() {
11277 member_siblings.push(child);
11278 }
11279 }
11280 terminator = class_terminator?;
11281 }
11282 let name = format!(
11283 "{}<{}>",
11284 metadata.primary_name,
11285 metadata
11286 .specialization_arguments
11287 .iter()
11288 .map(|argument| argument.text.as_str())
11289 .collect::<Vec<_>>()
11290 .join(", ")
11291 );
11292 Some(RecoveredFragmentedPartialSpecialization {
11293 declaration_node: declaration_child,
11294 name,
11295 range: Range {
11296 start_byte: declaration_child.start_byte(),
11297 end_byte: terminator.end_byte(),
11298 start_line: declaration_child.start_position().row + 1,
11299 end_line: terminator.end_position().row + 1,
11300 },
11301 prefix_members,
11302 member_siblings,
11303 following_declarations,
11304 })
11305}
11306
11307pub fn is_recovered_fragmented_partial_specialization_container(
11314 node: Node<'_>,
11315 source: &str,
11316) -> bool {
11317 let Some(template) = node
11318 .parent()
11319 .filter(|parent| parent.kind() == "template_declaration")
11320 else {
11321 return false;
11322 };
11323 let mut root = template;
11324 while let Some(parent) = root.parent() {
11325 root = parent;
11326 }
11327 recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
11328 .is_some()
11329}
11330
11331fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
11332 if declaration.kind() != "declaration" {
11333 return false;
11334 }
11335 (0..declaration.child_count().saturating_sub(1)).any(|index| {
11340 let Some(error) = declaration.child(index) else {
11341 return false;
11342 };
11343 error.kind() == "ERROR"
11344 && error.child_count() == 1
11345 && error.child(0).is_some_and(|child| child.kind() == "}")
11346 && declaration
11347 .child(index + 1)
11348 .is_some_and(|child| child.kind() == ";")
11349 })
11350}
11351
11352fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
11353 if node.has_error() {
11354 return false;
11355 }
11356 match node.kind() {
11357 "declaration"
11358 | "field_declaration"
11359 | "alias_declaration"
11360 | "type_definition"
11361 | "static_assert_declaration" => true,
11362 "labeled_statement" => node
11363 .named_child(node.named_child_count().saturating_sub(1))
11364 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
11365 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
11366 matches!(
11367 child.kind(),
11368 "declaration"
11369 | "field_declaration"
11370 | "alias_declaration"
11371 | "type_definition"
11372 | "function_definition"
11373 )
11374 }),
11375 _ => false,
11376 }
11377}
11378
11379fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
11380 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
11381 .then(|| node.child_by_field_name("declarator"))
11382 .flatten()
11383 .and_then(|declarator| extract_variable_name(declarator, source))
11384}
11385
11386fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
11387 while let Some(parent) = node.parent() {
11388 if matches!(parent.kind(), "function_definition" | "lambda_expression") {
11389 return true;
11390 }
11391 node = parent;
11392 }
11393 false
11394}
11395
11396fn cpp_template_metadata<'tree>(
11397 template_node: Node<'tree>,
11398 declaration_child: Node<'tree>,
11399 source: &str,
11400 ancestry: &ParentIndex<'tree>,
11401) -> Option<CppTemplateMetadata> {
11402 let parameters_node = template_node.child_by_field_name("parameters")?;
11403 let name_node = cpp_templated_class_name_node(declaration_child)?;
11404 let primary_node = match name_node.kind() {
11405 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
11406 _ => name_node,
11407 };
11408 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
11409 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
11410 return None;
11411 }
11412
11413 let mut parameter_nodes = Vec::new();
11414 let mut parameter_names = Vec::new();
11415 let mut cursor = parameters_node.walk();
11416 for parameter in parameters_node.named_children(&mut cursor) {
11417 if !matches!(
11418 parameter.kind(),
11419 "type_parameter_declaration"
11420 | "optional_type_parameter_declaration"
11421 | "variadic_type_parameter_declaration"
11422 | "template_template_parameter_declaration"
11423 | "parameter_declaration"
11424 | "optional_parameter_declaration"
11425 | "variadic_parameter_declaration"
11426 ) {
11427 continue;
11428 }
11429 let index = parameter_nodes.len();
11430 let name = cpp_template_parameter_name(parameter, source)
11435 .unwrap_or_else(|| format!("<anonymous:{index}>"));
11436 parameter_names.push(name);
11437 parameter_nodes.push(parameter);
11438 }
11439 let parameters = parameter_nodes
11440 .into_iter()
11441 .zip(parameter_names.iter().cloned())
11442 .map(|(parameter, name)| CppTemplateParameterMetadata {
11443 name,
11444 kind: cpp_template_parameter_kind(parameter),
11445 variadic: matches!(
11446 parameter.kind(),
11447 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
11448 ),
11449 default: cpp_template_parameter_default_expression(
11450 parameter,
11451 source,
11452 ¶meter_names,
11453 ancestry,
11454 ),
11455 })
11456 .collect();
11457 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
11458 Vec::new()
11459 } else {
11460 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
11461 .unwrap_or_default()
11462 };
11463 let alias_target = (declaration_child.kind() == "alias_declaration")
11464 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
11465 .flatten();
11466 Some(CppTemplateMetadata {
11467 primary_name,
11468 primary_fq_name: String::new(),
11469 parameters,
11470 specialization_arguments,
11471 alias_target,
11472 })
11473}
11474
11475fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
11476 match node.kind() {
11477 "class_specifier" | "struct_specifier" | "union_specifier" => {
11478 node.child_by_field_name("name")
11479 }
11480 "function_definition" => {
11481 let declarator = node.child_by_field_name("declarator")?;
11482 if matches!(declarator.kind(), "identifier" | "template_function") {
11483 Some(declarator)
11484 } else {
11485 None
11486 }
11487 }
11488 "alias_declaration" => node.child_by_field_name("name"),
11489 _ => None,
11490 }
11491}
11492
11493fn cpp_template_alias_target<'tree>(
11494 alias: Node<'tree>,
11495 source: &str,
11496 parameter_names: &[String],
11497 ancestry: &ParentIndex<'tree>,
11498) -> Option<CppTemplateAliasTargetMetadata> {
11499 let mut type_node = alias.child_by_field_name("type")?;
11500 while type_node.kind() == "type_descriptor" {
11501 type_node = type_node.child_by_field_name("type")?;
11502 }
11503 let global = type_node.child_by_field_name("scope").is_none()
11504 && type_node.child(0).is_some_and(|child| child.kind() == "::");
11505 let mut components = Vec::new();
11506 cpp_template_target_components(type_node, source, &mut components)?;
11507 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
11508 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
11509 components,
11510 global,
11511 arguments,
11512 })
11513}
11514
11515fn cpp_template_target_components(
11516 node: Node<'_>,
11517 source: &str,
11518 out: &mut Vec<String>,
11519) -> Option<()> {
11520 match node.kind() {
11521 "identifier" | "namespace_identifier" | "type_identifier" => {
11522 out.push(node_text(node, source).to_string());
11523 Some(())
11524 }
11525 "template_type" => {
11526 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11527 }
11528 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11529 if let Some(scope) = node.child_by_field_name("scope") {
11530 cpp_template_target_components(scope, source, out)?;
11531 }
11532 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11533 }
11534 _ => None,
11535 }
11536}
11537
11538fn cpp_template_argument_expressions<'tree>(
11539 mut node: Node<'tree>,
11540 source: &str,
11541 parameter_names: &[String],
11542 ancestry: &ParentIndex<'tree>,
11543) -> Option<Vec<CppTemplateExpression>> {
11544 loop {
11545 match node.kind() {
11546 "template_type" | "template_function" => {
11547 let arguments = node.child_by_field_name("arguments")?;
11548 let mut cursor = arguments.walk();
11549 return Some(
11550 arguments
11551 .named_children(&mut cursor)
11552 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
11553 .map(|argument| {
11554 cpp_template_expression(argument, source, parameter_names, ancestry)
11555 })
11556 .collect(),
11557 );
11558 }
11559 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
11560 node = node
11561 .child_by_field_name("name")
11562 .or_else(|| node.child_by_field_name("type"))?;
11563 }
11564 _ => return None,
11565 }
11566 }
11567}
11568
11569fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
11570 let candidate = node
11571 .child_by_field_name("name")
11572 .or_else(|| node.child_by_field_name("declarator"))
11573 .or_else(|| {
11574 let mut cursor = node.walk();
11575 node.named_children(&mut cursor).find(|child| {
11576 matches!(
11577 child.kind(),
11578 "identifier" | "type_identifier" | "field_identifier"
11579 )
11580 })
11581 })?;
11582 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
11583 (!name.is_empty()).then_some(name)
11584}
11585
11586fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
11587 match node.kind() {
11588 "type_parameter_declaration"
11589 | "optional_type_parameter_declaration"
11590 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
11591 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
11592 _ => CppTemplateParameterKind::Value,
11593 }
11594}
11595
11596fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
11597 node.child_by_field_name("default_type")
11598 .or_else(|| node.child_by_field_name("default_value"))
11599}
11600
11601fn cpp_template_parameter_default_expression<'tree>(
11602 parameter: Node<'tree>,
11603 source: &str,
11604 parameter_names: &[String],
11605 ancestry: &ParentIndex<'tree>,
11606) -> Option<CppTemplateExpression> {
11607 let default = cpp_template_parameter_default(parameter)?;
11608 let base = cpp_template_expression(default, source, parameter_names, ancestry);
11609 let Some(pointer_error) = parameter.next_named_sibling() else {
11610 return Some(base);
11611 };
11612 let Some(pointer_declarator) =
11613 recovered_abstract_pointer_declarator_term(pointer_error, source)
11614 else {
11615 return Some(base);
11616 };
11617 Some(CppTemplateExpression {
11618 text: format!(
11619 "{}{}",
11620 base.text,
11621 normalize_cpp_whitespace(node_text(pointer_error, source))
11622 ),
11623 term: CppTemplateTerm::Node {
11624 kind: "type_descriptor".to_string(),
11625 children: vec![base.term, pointer_declarator],
11626 },
11627 })
11628}
11629
11630fn recovered_abstract_pointer_declarator_term(
11631 node: Node<'_>,
11632 source: &str,
11633) -> Option<CppTemplateTerm> {
11634 if node.kind() != "ERROR" || node.child_count() == 0 {
11635 return None;
11636 }
11637 let mut children = Vec::new();
11638 for index in 0..node.child_count() {
11639 let child = node.child(index)?;
11640 if child.kind() != "*" {
11641 return None;
11642 }
11643 children.push(CppTemplateTerm::Atom {
11644 kind: "*".to_string(),
11645 text: normalize_cpp_whitespace(node_text(child, source)),
11646 });
11647 }
11648 Some(CppTemplateTerm::Node {
11649 kind: "abstract_pointer_declarator".to_string(),
11650 children,
11651 })
11652}
11653
11654fn cpp_template_expression<'tree>(
11655 node: Node<'tree>,
11656 source: &str,
11657 parameter_names: &[String],
11658 ancestry: &ParentIndex<'tree>,
11659) -> CppTemplateExpression {
11660 let text = normalize_cpp_whitespace(node_text(node, source));
11661 CppTemplateExpression {
11662 text,
11663 term: cpp_template_term(node, source, parameter_names, ancestry),
11664 }
11665}
11666
11667pub fn cpp_template_term<'tree>(
11668 node: Node<'tree>,
11669 source: &str,
11670 parameter_names: &[String],
11671 ancestry: &ParentIndex<'tree>,
11672) -> CppTemplateTerm {
11673 enum Work<'tree> {
11674 Visit(Node<'tree>),
11675 Build { kind: String, child_count: usize },
11676 }
11677
11678 let mut work = vec![Work::Visit(node)];
11679 let mut terms = Vec::new();
11680 while let Some(next) = work.pop() {
11681 match next {
11682 Work::Visit(current) => {
11683 let text = normalize_cpp_whitespace(node_text(current, source));
11684 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
11685 terms.push(CppTemplateTerm::Parameter(text));
11686 continue;
11687 }
11688 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
11689 let mut cursor = current.walk();
11690 let named = current
11691 .named_children(&mut cursor)
11692 .filter(|child| !child.is_extra() && child.kind() != "comment")
11693 .collect::<Vec<_>>();
11694 if let [child] = named.as_slice() {
11695 work.push(Work::Visit(*child));
11696 continue;
11697 }
11698 }
11699 if current.child_count() == 0 {
11700 terms.push(CppTemplateTerm::Atom {
11701 kind: if matches!(
11702 current.kind(),
11703 "identifier"
11704 | "type_identifier"
11705 | "field_identifier"
11706 | "namespace_identifier"
11707 ) {
11708 "identifier".to_string()
11709 } else {
11710 current.kind().to_string()
11711 },
11712 text,
11713 });
11714 continue;
11715 }
11716 let children = (0..current.child_count())
11717 .filter_map(|index| current.child(index))
11718 .filter(|child| !child.is_extra() && child.kind() != "comment")
11719 .collect::<Vec<_>>();
11720 work.push(Work::Build {
11721 kind: current.kind().to_string(),
11722 child_count: children.len(),
11723 });
11724 work.extend(children.into_iter().rev().map(Work::Visit));
11725 }
11726 Work::Build { kind, child_count } => {
11727 let children = terms.split_off(terms.len() - child_count);
11728 terms.push(CppTemplateTerm::Node { kind, children });
11729 }
11730 }
11731 }
11732 terms.pop().expect("template term traversal emits one root")
11733}
11734
11735fn cpp_template_term_leaf_is_parameter<'tree>(
11736 node: Node<'tree>,
11737 text: &str,
11738 parameter_names: &[String],
11739 ancestry: &ParentIndex<'tree>,
11740) -> bool {
11741 if !parameter_names.iter().any(|parameter| parameter == text) {
11742 return false;
11743 }
11744 !ancestry.parent(node).is_some_and(|parent| {
11745 matches!(
11746 parent.kind(),
11747 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
11748 ) && parent.child_by_field_name("scope").is_some()
11749 && parent.child_by_field_name("name") == Some(node)
11750 })
11751}
11752
11753fn enclosing_cpp_declaration_node<'tree>(
11754 mut node: Node<'tree>,
11755 ancestry: &ParentIndex<'tree>,
11756) -> Option<Node<'tree>> {
11757 loop {
11758 match node.kind() {
11759 "declaration"
11760 | "function_declaration"
11761 | "field_declaration"
11762 | "function_definition" => return Some(node),
11763 _ => node = ancestry.parent(node)?,
11764 }
11765 }
11766}
11767
11768fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
11769 let mut params = Vec::new();
11770 let mut cursor = parameters_node.walk();
11771 for child in parameters_node.children(&mut cursor) {
11772 match child.kind() {
11773 "parameter_declaration" | "optional_parameter_declaration" => {
11774 params.push(cpp_parameter_type(child, source));
11775 }
11776 "variadic_parameter_declaration" => {
11777 params.push(cpp_parameter_type(child, source));
11778 }
11779 "variadic_parameter" | "..." => params.push("...".to_string()),
11780 _ => {}
11781 }
11782 }
11783
11784 if params.is_empty() {
11785 "()".to_string()
11786 } else {
11787 format!("({})", params.join(", "))
11788 }
11789}
11790
11791enum CppCallableScope<'tree> {
11794 ClassBody {
11798 body: Node<'tree>,
11799 default_visibility: DeclaredVisibility,
11800 },
11801 Qualified,
11807 Unqualified,
11810}
11811
11812fn cpp_callable_scope<'tree>(
11828 declaration: Node<'tree>,
11829 function_declarator: Option<Node<'tree>>,
11830 ancestry: &ParentIndex<'tree>,
11831) -> CppCallableScope<'tree> {
11832 let mut current = Some(declaration);
11833 while let Some(node) = current {
11834 match node.kind() {
11835 "field_declaration_list" => {
11836 let default_visibility =
11837 ancestry
11838 .parent(node)
11839 .map_or(DeclaredVisibility::Unknown, |class_like| {
11840 match class_like.kind() {
11843 "struct_specifier" | "union_specifier" => {
11844 DeclaredVisibility::Public
11845 }
11846 _ => DeclaredVisibility::Private,
11847 }
11848 });
11849 return CppCallableScope::ClassBody {
11850 body: node,
11851 default_visibility,
11852 };
11853 }
11854 "namespace_definition" | "compound_statement" | "translation_unit" => break,
11855 _ => {}
11856 }
11857 current = ancestry.parent(node);
11858 }
11859 if cpp_callable_declarator_is_qualified(function_declarator) {
11860 CppCallableScope::Qualified
11861 } else {
11862 CppCallableScope::Unqualified
11863 }
11864}
11865
11866fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
11873 function_declarator
11874 .and_then(cpp_function_declarator_name_node)
11875 .is_some_and(|name| name.kind() == "qualified_identifier")
11876}
11877
11878fn cpp_member_declared_visibility<'tree>(
11890 declaration: Node<'tree>,
11891 body: Node<'tree>,
11892 default_visibility: DeclaredVisibility,
11893 ancestry: &ParentIndex<'tree>,
11894) -> DeclaredVisibility {
11895 let mut list_level = declaration;
11896 while ancestry.parent(list_level) != Some(body) {
11897 let Some(parent) = ancestry.parent(list_level) else {
11898 return default_visibility;
11899 };
11900 list_level = parent;
11901 }
11902 let mut visibility = default_visibility;
11903 let mut cursor = body.walk();
11904 for child in body.children(&mut cursor) {
11905 if child == list_level {
11906 break;
11907 }
11908 if child.kind() == "access_specifier" {
11909 visibility = match child.child(0).map(|keyword| keyword.kind()) {
11910 Some("public") => DeclaredVisibility::Public,
11911 Some("protected") => DeclaredVisibility::Protected,
11912 Some("private") => DeclaredVisibility::Private,
11913 _ => visibility,
11914 };
11915 }
11916 }
11917 visibility
11918}
11919
11920fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
11927 let mut cursor = declaration.walk();
11928 declaration.named_children(&mut cursor).any(|child| {
11929 child.kind() == "storage_class_specifier"
11930 && normalize_cpp_whitespace(node_text(child, source)) == "static"
11931 })
11932}
11933
11934struct CppCallableModifiers {
11937 is_static: bool,
11938 is_constructor: bool,
11939 visibility: DeclaredVisibility,
11940}
11941
11942fn cpp_callable_modifiers<'tree>(
11966 declaration: Node<'tree>,
11967 function_declarator: Option<Node<'tree>>,
11968 is_constructor: bool,
11969 source: &str,
11970 ancestry: &ParentIndex<'tree>,
11971) -> CppCallableModifiers {
11972 match cpp_callable_scope(declaration, function_declarator, ancestry) {
11973 CppCallableScope::ClassBody {
11974 body,
11975 default_visibility,
11976 } => CppCallableModifiers {
11977 is_static: cpp_declares_static_storage_class(declaration, source),
11978 is_constructor,
11979 visibility: cpp_member_declared_visibility(
11980 declaration,
11981 body,
11982 default_visibility,
11983 ancestry,
11984 ),
11985 },
11986 CppCallableScope::Qualified => CppCallableModifiers {
11987 is_static: false,
11988 is_constructor,
11989 visibility: DeclaredVisibility::Unknown,
11990 },
11991 CppCallableScope::Unqualified => CppCallableModifiers {
11992 is_static: false,
11993 is_constructor,
11994 visibility: DeclaredVisibility::Unknown,
11995 },
11996 }
11997}
11998
11999fn cpp_signature_metadata<'tree>(
12008 signature: String,
12009 function_declarator: Node<'tree>,
12010 linkage: CallableLinkage,
12011 source: &str,
12012 ancestry: &ParentIndex<'tree>,
12013) -> SignatureMetadata {
12014 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
12015 let is_constructor =
12016 cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
12017 let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
12018 Some(declaration) => cpp_callable_modifiers(
12019 declaration,
12020 Some(function_declarator),
12021 is_constructor,
12022 source,
12023 ancestry,
12024 ),
12025 None => CppCallableModifiers {
12028 is_static: false,
12029 is_constructor,
12030 visibility: DeclaredVisibility::Unknown,
12031 },
12032 };
12033 let enrich = |mut metadata: SignatureMetadata| {
12034 if let Some(dispatch) = dispatch {
12037 metadata = metadata.with_dispatch_extensibility(dispatch);
12038 }
12039 metadata
12040 .with_callable_linkage(linkage)
12041 .with_callable_modifiers(
12042 modifiers.is_static,
12043 modifiers.is_constructor,
12044 modifiers.visibility,
12045 )
12046 };
12047 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
12048 let return_type_identity =
12049 cpp_callable_return_type_identity(function_declarator, source, ancestry);
12050 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12051 return enrich(
12052 SignatureMetadata::new(signature, Vec::new())
12053 .with_return_type_text(return_type_text)
12054 .with_return_type_identity(return_type_identity),
12055 );
12056 };
12057 let callable_arity = cpp_callable_arity(parameters_node, source);
12058 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
12059 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
12060 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
12061 let Some(relative_start) = signature
12062 .get(search_from..)
12063 .and_then(|suffix| suffix.find(¶meter_text))
12064 else {
12065 return enrich(
12066 SignatureMetadata::new(signature, Vec::new())
12067 .with_callable_arity(callable_arity)
12068 .with_callable_parameter_types(callable_parameter_types)
12069 .with_return_type_text(return_type_text)
12070 .with_return_type_identity(return_type_identity),
12071 );
12072 };
12073 let parameters_start = search_from + relative_start;
12074 let parameters_end = parameters_start + parameter_text.len();
12075 let mut search_start = parameters_start;
12076 let parameters = cpp_parameter_label_nodes(parameters_node)
12077 .into_iter()
12078 .filter_map(|label_node| {
12079 let label = normalize_cpp_whitespace(node_text(label_node, source));
12080 if label.is_empty() || search_start > parameters_end {
12081 return None;
12082 }
12083 let haystack = signature.get(search_start..parameters_end)?;
12084 let relative_start = haystack.find(&label)?;
12085 let start_byte = search_start + relative_start;
12086 let end_byte = start_byte + label.len();
12087 search_start = end_byte;
12088 Some(ParameterMetadata::new(label, start_byte, end_byte))
12089 })
12090 .collect();
12091 enrich(
12092 SignatureMetadata::new(signature, parameters)
12093 .with_callable_arity(callable_arity)
12094 .with_callable_parameter_types(callable_parameter_types)
12095 .with_return_type_text(return_type_text)
12096 .with_return_type_identity(return_type_identity),
12097 )
12098}
12099
12100fn cpp_callable_is_structural_constructor<'tree>(
12101 function_declarator: Node<'tree>,
12102 source: &str,
12103 ancestry: &ParentIndex<'tree>,
12104) -> bool {
12105 let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
12106 .or_else(|| function_declarator.child_by_field_name("name"))
12107 .or_else(|| last_named_child(function_declarator))
12108 else {
12109 return false;
12110 };
12111 if name_node.kind() == "qualified_identifier" {
12115 let owner = name_node
12116 .child_by_field_name("scope")
12117 .and_then(|scope| canonical_cpp_qualified_component(scope, source));
12118 let member = name_node
12119 .child_by_field_name("name")
12120 .and_then(|name| canonical_cpp_qualified_component(name, source));
12121 return match (owner, member) {
12122 (Some(owner), Some(member)) => owner.name == member.name,
12123 _ => false,
12124 };
12125 }
12126 let Some(callable_name) = direct_identifier_name(name_node, source) else {
12127 return false;
12128 };
12129
12130 let mut current = ancestry.parent(function_declarator);
12131 while let Some(ancestor) = current {
12132 let owner_name = match ancestor.kind() {
12133 "class_specifier" | "struct_specifier" | "union_specifier" => {
12134 class_like_name(ancestor, source, ancestry)
12135 }
12136 "ERROR" => malformed_class_error_owner_name(ancestor, source),
12137 _ => None,
12138 };
12139 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
12140 return true;
12141 }
12142 current = ancestry.parent(ancestor);
12143 }
12144 false
12145}
12146
12147fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
12157 if node.kind() != "ERROR" {
12158 return None;
12159 }
12160 let keyword = node.child(0)?;
12161 if !matches!(keyword.kind(), "class" | "struct" | "union") {
12162 return None;
12163 }
12164 let name_node = node.child(1)?;
12165 let name = direct_identifier_name(name_node, source)?;
12166 let has_body = (2..node.child_count())
12167 .filter_map(|index| node.child(index))
12168 .any(|child| child.kind() == "{");
12169 has_body.then_some(name)
12170}
12171
12172pub fn cpp_callable_declaration_return_type_identity<'tree>(
12178 callable: Node<'tree>,
12179 source: &str,
12180 ancestry: &ParentIndex<'tree>,
12181) -> Option<StructuredTypeIdentity> {
12182 let declarator = callable
12183 .child_by_field_name("declarator")
12184 .and_then(extract_function_declarator)?;
12185 cpp_callable_return_type_identity(declarator, source, ancestry)
12186}
12187
12188pub(crate) fn cpp_callable_return_type_identity<'tree>(
12189 function_declarator: Node<'tree>,
12190 source: &str,
12191 ancestry: &ParentIndex<'tree>,
12192) -> Option<StructuredTypeIdentity> {
12193 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
12194 return None;
12195 }
12196 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12197 if let Some((return_type, _)) =
12198 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
12199 {
12200 return cpp_structured_type_identity(return_type, source, &lexical_scope);
12201 }
12202 let mut cursor = function_declarator.walk();
12203 if let Some(trailing) = function_declarator
12204 .named_children(&mut cursor)
12205 .find(|child| child.kind() == "trailing_return_type")
12206 && let Some(type_descriptor) = trailing.named_child(0)
12207 {
12208 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
12209 }
12210
12211 let mut current = function_declarator;
12212 let mut wrappers = Vec::new();
12213 while let Some(parent) = ancestry.parent(current) {
12214 if matches!(
12215 parent.kind(),
12216 "function_definition" | "declaration" | "field_declaration"
12217 ) {
12218 let type_node = parent.child_by_field_name("type")?;
12219 if cpp_export_macro_token(node_text(type_node, source))
12220 && (0..parent.named_child_count()).any(|index| {
12221 parent
12222 .named_child(index)
12223 .is_some_and(|child| child.kind() == "ERROR")
12224 })
12225 {
12226 return None;
12227 }
12228 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
12229 for wrapper in wrappers.into_iter().rev() {
12230 identity = cpp_wrap_structured_type(identity, wrapper)?;
12231 }
12232 return Some(identity);
12233 }
12234 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
12235 || (matches!(
12236 parent.kind(),
12237 "pointer_declarator"
12238 | "reference_declarator"
12239 | "array_declarator"
12240 | "parenthesized_declarator"
12241 ) && parent.named_child_count() == 1
12242 && parent.named_child(0) == Some(current));
12243 if !wraps_current_declarator {
12244 return None;
12245 }
12246 match parent.kind() {
12247 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
12248 "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
12249 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
12250 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
12251 _ => return None,
12252 }
12253 current = parent;
12254 }
12255 None
12256}
12257
12258fn cpp_structured_type_identity(
12259 node: Node<'_>,
12260 source: &str,
12261 lexical_scope: &[String],
12262) -> Option<StructuredTypeIdentity> {
12263 enum Work<'tree> {
12264 Visit(Node<'tree>),
12265 Wrap(CppStructuredTypeWrapper),
12266 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
12267 BuildGeneric { argument_count: usize },
12268 }
12269
12270 let mut work = vec![Work::Visit(node)];
12271 let mut values = Vec::new();
12272 let mut builder = StructuredTypeIdentityBuilder::default();
12273 while let Some(next) = work.pop() {
12274 match next {
12275 Work::Visit(current) => match current.kind() {
12276 "type_descriptor" => {
12277 let type_node = current
12278 .child_by_field_name("type")
12279 .or_else(|| current.named_child(0))?;
12280 let mut wrappers = Vec::new();
12281 let mut cursor = current.walk();
12282 for child in current.named_children(&mut cursor) {
12283 if child.id() != type_node.id() {
12284 wrappers.extend(cpp_structured_declarator_wrappers(child)?);
12285 }
12286 }
12287 work.push(Work::ApplyWrappers(wrappers));
12288 work.push(Work::Visit(type_node));
12289 }
12290 "pointer_declarator" | "abstract_pointer_declarator" => {
12291 let child = current
12292 .child_by_field_name("declarator")
12293 .or_else(|| current.named_child(0))?;
12294 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
12295 work.push(Work::Visit(child));
12296 }
12297 "reference_declarator" => {
12298 let child = current
12299 .child_by_field_name("declarator")
12300 .or_else(|| current.named_child(0))?;
12301 work.push(Work::Wrap(cpp_reference_wrapper(current)?));
12302 work.push(Work::Visit(child));
12303 }
12304 "array_declarator" | "abstract_array_declarator" => {
12305 let child = current
12306 .child_by_field_name("declarator")
12307 .or_else(|| current.named_child(0))?;
12308 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
12309 work.push(Work::Visit(child));
12310 }
12311 "template_type" => {
12312 let name_node = current.child_by_field_name("name")?;
12313 let arguments = current
12314 .child_by_field_name("arguments")
12315 .map(|arguments_node| {
12316 let mut cursor = arguments_node.walk();
12317 arguments_node
12318 .named_children(&mut cursor)
12319 .filter(|child| !child.is_extra() && child.kind() != "comment")
12320 .collect::<Vec<_>>()
12321 })
12322 .unwrap_or_default();
12323 work.push(Work::BuildGeneric {
12324 argument_count: arguments.len(),
12325 });
12326 work.extend(arguments.into_iter().rev().map(Work::Visit));
12327 work.push(Work::Visit(name_node));
12328 }
12329 "qualified_identifier"
12330 | "scoped_identifier"
12331 | "scoped_type_identifier"
12332 | "type_identifier"
12333 | "field_identifier"
12334 | "identifier"
12335 | "namespace_identifier"
12336 | "primitive_type" => {
12337 values.push(builder.named(cpp_structured_named_type(
12338 current,
12339 source,
12340 lexical_scope,
12341 )?)?);
12342 }
12343 _ => {
12344 let child = current.child_by_field_name("type").or_else(|| {
12345 (current.named_child_count() == 1)
12346 .then(|| current.named_child(0))
12347 .flatten()
12348 })?;
12349 work.push(Work::Visit(child));
12350 }
12351 },
12352 Work::Wrap(wrapper) => {
12353 let root = values.pop()?;
12354 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
12355 }
12356 Work::ApplyWrappers(wrappers) => {
12357 let mut root = values.pop()?;
12358 for wrapper in wrappers.into_iter().rev() {
12359 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
12360 }
12361 values.push(root);
12362 }
12363 Work::BuildGeneric { argument_count } => {
12364 let value_count = argument_count.checked_add(1)?;
12365 let start = values.len().checked_sub(value_count)?;
12366 let mut built = values.split_off(start);
12367 let base = built.remove(0);
12368 values.push(builder.generic(base, built)?);
12369 }
12370 }
12371 }
12372 (values.len() == 1)
12373 .then(|| values.pop())
12374 .flatten()
12375 .and_then(|root| builder.finish(root))
12376}
12377
12378fn cpp_structured_named_type(
12379 node: Node<'_>,
12380 source: &str,
12381 lexical_scope: &[String],
12382) -> Option<StructuredTypeName> {
12383 let path = cpp_structured_type_path(node, source)?;
12384 let absolute = node.child_by_field_name("scope").is_none()
12385 && node.child(0).is_some_and(|child| child.kind() == "::");
12386 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
12387}
12388
12389#[derive(Clone, Copy)]
12390enum CppStructuredTypeWrapper {
12391 Pointer,
12392 LvalueReference,
12393 RvalueReference,
12394 Array,
12395}
12396
12397fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
12398 let mut wrappers = Vec::new();
12399 let mut current = node;
12400 loop {
12401 match current.kind() {
12402 "pointer_declarator" | "abstract_pointer_declarator" => {
12403 wrappers.push(CppStructuredTypeWrapper::Pointer)
12404 }
12405 "reference_declarator" | "abstract_reference_declarator" => {
12406 wrappers.push(cpp_reference_wrapper(current)?);
12407 }
12408 "array_declarator" | "abstract_array_declarator" => {
12409 wrappers.push(CppStructuredTypeWrapper::Array)
12410 }
12411 "init_declarator" | "parenthesized_declarator" => {}
12418 _ => break,
12419 }
12420 let Some(child) = current
12421 .child_by_field_name("declarator")
12422 .or_else(|| current.named_child(0))
12423 else {
12424 break;
12425 };
12426 current = child;
12427 }
12428 Some(wrappers)
12429}
12430
12431fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
12432 node.children(&mut node.walk())
12433 .find_map(|child| match child.kind() {
12434 "&" => Some(CppStructuredTypeWrapper::LvalueReference),
12435 "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
12436 _ => None,
12437 })
12438}
12439
12440fn cpp_wrap_structured_type(
12441 identity: StructuredTypeIdentity,
12442 wrapper: CppStructuredTypeWrapper,
12443) -> Option<StructuredTypeIdentity> {
12444 match wrapper {
12445 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
12446 CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
12447 CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
12448 CppStructuredTypeWrapper::Array => identity.wrap_array(),
12449 }
12450}
12451
12452fn cpp_wrap_structured_type_node(
12453 builder: &mut StructuredTypeIdentityBuilder,
12454 inner: StructuredTypeNodeId,
12455 wrapper: CppStructuredTypeWrapper,
12456) -> Option<StructuredTypeNodeId> {
12457 match wrapper {
12458 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
12459 CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
12460 CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
12461 CppStructuredTypeWrapper::Array => builder.array(inner),
12462 }
12463}
12464
12465pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
12472 let mut path = Vec::new();
12473 let mut stack = vec![node];
12474 while let Some(current) = stack.pop() {
12475 match current.kind() {
12476 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
12477 let component = node_text(current, source).to_string();
12478 if component.is_empty() {
12479 return None;
12480 }
12481 path.push(component);
12482 }
12483 "template_type" | "dependent_type" => {
12484 stack.push(current.child_by_field_name("name")?);
12485 }
12486 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12487 stack.push(current.child_by_field_name("name")?);
12488 if let Some(scope) = current.child_by_field_name("scope") {
12489 stack.push(scope);
12490 }
12491 }
12492 _ => return None,
12493 }
12494 }
12495 (!path.is_empty()).then_some(path)
12496}
12497
12498fn cpp_callable_lexical_scope<'tree>(
12499 node: Node<'tree>,
12500 source: &str,
12501 ancestry: &ParentIndex<'tree>,
12502) -> Vec<String> {
12503 let mut groups = Vec::new();
12504 let mut current = ancestry.parent(node);
12505 while let Some(parent) = current {
12506 if matches!(
12507 parent.kind(),
12508 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
12509 ) && let Some(name_node) = parent.child_by_field_name("name")
12510 && let Some(components) = cpp_structured_type_path(name_node, source)
12511 && !components.is_empty()
12512 {
12513 groups.push(components);
12514 }
12515 current = ancestry.parent(parent);
12516 }
12517 groups.reverse();
12518 groups.into_iter().flatten().collect()
12519}
12520
12521fn cpp_callable_dispatch_extensibility<'tree>(
12545 function_declarator: Node<'tree>,
12546 ancestry: &ParentIndex<'tree>,
12547) -> Option<DispatchExtensibility> {
12548 let mut declaration = None;
12549 let mut class_body = None;
12553 let mut current = Some(function_declarator);
12554 while let Some(node) = current {
12555 match node.kind() {
12556 "template_declaration"
12557 | "preproc_if"
12558 | "preproc_ifdef"
12559 | "preproc_else"
12560 | "preproc_elif"
12561 | "preproc_call"
12562 | "ERROR" => return Some(DispatchExtensibility::Open),
12563 "declaration" | "field_declaration" | "function_definition" => {
12564 declaration.get_or_insert(node);
12565 }
12566 "field_declaration_list" => {
12567 class_body.get_or_insert(node);
12568 }
12569 "translation_unit" => break,
12570 _ => {}
12571 }
12572 current = ancestry.parent(node);
12573 }
12574 let Some(declaration) = declaration else {
12575 return Some(DispatchExtensibility::Open);
12576 };
12577
12578 let mut saw_virtual_boundary = false;
12579 let mut stack = vec![declaration];
12580 while let Some(node) = stack.pop() {
12581 match node.kind() {
12582 "compound_statement" | "field_declaration_list" => continue,
12583 "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
12584 "virtual"
12585 | "override"
12586 | "virtual_specifier"
12587 | "pure_virtual_clause"
12588 | "template_parameter_list"
12589 | "template_method"
12590 | "template_function"
12591 | "ERROR" => saw_virtual_boundary = true,
12592 _ => {}
12593 }
12594 let mut cursor = node.walk();
12595 stack.extend(node.children(&mut cursor));
12596 }
12597
12598 if saw_virtual_boundary {
12599 return Some(DispatchExtensibility::Open);
12600 }
12601 if let Some(body) = class_body {
12602 let has_base_clause = ancestry
12606 .parent(body)
12607 .and_then(cpp_class_like_base_clause)
12608 .is_some();
12609 return Some(if has_base_clause {
12610 DispatchExtensibility::Open
12611 } else {
12612 DispatchExtensibility::Closed
12613 });
12614 }
12615 if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
12616 return None;
12617 }
12618 Some(DispatchExtensibility::Closed)
12621}
12622
12623fn cpp_callable_linkage<'tree>(
12624 declaration: Node<'tree>,
12625 source: &str,
12626 ancestry: &ParentIndex<'tree>,
12627) -> CallableLinkage {
12628 let mut enclosed_by_class = false;
12629 let mut current = ancestry.parent(declaration);
12630 while let Some(node) = current {
12631 if node.kind() == "namespace_definition"
12632 && node
12633 .child_by_field_name("name")
12634 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12635 {
12636 return CallableLinkage::Internal;
12637 }
12638 if matches!(
12639 node.kind(),
12640 "class_specifier" | "struct_specifier" | "union_specifier"
12641 ) {
12642 if node
12643 .child_by_field_name("name")
12644 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12645 {
12646 return CallableLinkage::Internal;
12647 }
12648 enclosed_by_class = true;
12649 }
12650 if node.kind() == "lambda_expression"
12655 || node.kind() == "function_definition"
12656 && !is_recovered_exported_class_container(node, source)
12657 {
12658 return CallableLinkage::Internal;
12659 }
12660 current = ancestry.parent(node);
12661 }
12662
12663 if enclosed_by_class {
12664 return CallableLinkage::External;
12665 }
12666
12667 if cpp_declares_static_storage_class(declaration, source) {
12668 CallableLinkage::Internal
12669 } else {
12670 CallableLinkage::External
12671 }
12672}
12673
12674fn cpp_callable_return_type_text<'tree>(
12675 function_declarator: Node<'tree>,
12676 source: &str,
12677 ancestry: &ParentIndex<'tree>,
12678) -> Option<String> {
12679 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
12680 return None;
12681 }
12682 if let Some((return_type, _)) =
12683 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
12684 {
12685 let text = normalize_cpp_whitespace(node_text(return_type, source));
12686 return (!text.is_empty()).then_some(text);
12687 }
12688 let mut cursor = function_declarator.walk();
12689 if let Some(trailing) = function_declarator
12690 .named_children(&mut cursor)
12691 .find(|child| child.kind() == "trailing_return_type")
12692 && let Some(type_descriptor) = trailing.named_child(0)
12693 {
12694 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
12695 if !text.is_empty() {
12696 return Some(text);
12697 }
12698 }
12699
12700 let mut current = function_declarator;
12701 let mut indirection = String::new();
12702 while let Some(parent) = ancestry.parent(current) {
12703 if matches!(
12704 parent.kind(),
12705 "function_definition" | "declaration" | "field_declaration"
12706 ) {
12707 let type_node = parent.child_by_field_name("type")?;
12708 if cpp_export_macro_token(node_text(type_node, source))
12709 && (0..parent.named_child_count()).any(|index| {
12710 parent
12711 .named_child(index)
12712 .is_some_and(|child| child.kind() == "ERROR")
12713 })
12714 {
12715 return None;
12720 }
12721 let base = normalize_cpp_whitespace(node_text(type_node, source));
12722 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
12723 }
12724 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
12725 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
12726 && parent.named_child_count() == 1
12727 && parent.named_child(0) == Some(current));
12728 if wraps_current_declarator {
12729 match parent.kind() {
12730 "pointer_declarator" => indirection.push('*'),
12731 "reference_declarator" => {
12732 let reference = parent
12733 .children(&mut parent.walk())
12734 .find(|child| !child.is_named())
12735 .map(|child| node_text(child, source))
12736 .unwrap_or("&");
12737 indirection.push_str(reference);
12738 }
12739 "init_declarator" | "parenthesized_declarator" => {}
12740 _ => return None,
12741 }
12742 current = parent;
12743 continue;
12744 }
12745 return None;
12746 }
12747 None
12748}
12749
12750fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
12751 let mut required = 0;
12752 let mut total = 0;
12753 let mut repeated = false;
12754 let mut cursor = parameters_node.walk();
12755 for child in parameters_node.children(&mut cursor) {
12756 match child.kind() {
12757 "parameter_declaration" => {
12758 if cpp_parameter_is_explicit_object(child, source) {
12759 continue;
12760 }
12761 if child.child_by_field_name("declarator").is_none()
12762 && child
12763 .child_by_field_name("type")
12764 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
12765 {
12766 continue;
12767 }
12768 required += 1;
12769 total += 1;
12770 }
12771 "optional_parameter_declaration" => total += 1,
12772 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12773 repeated = true;
12774 }
12775 _ => {}
12776 }
12777 }
12778 CallableArity::new(required, total, repeated)
12779}
12780
12781fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
12782 parameter
12783 .child_by_field_name("type")
12784 .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
12785 .and_then(|type_node| type_node.child_by_field_name("constraint"))
12786 .is_some_and(|constraint| {
12787 constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
12788 })
12789}
12790
12791#[derive(Clone, Copy)]
12799enum CppParameterSlot<'tree> {
12800 Declared(Node<'tree>),
12801 Ellipsis,
12802}
12803
12804fn cpp_callable_parameter_slots<'tree>(
12805 parameters_node: Node<'tree>,
12806 source: &str,
12807) -> Vec<CppParameterSlot<'tree>> {
12808 let mut slots = Vec::new();
12809 let mut cursor = parameters_node.walk();
12810 for parameter in parameters_node.children(&mut cursor) {
12811 match parameter.kind() {
12812 "parameter_declaration" | "optional_parameter_declaration" => {
12813 if cpp_parameter_is_explicit_object(parameter, source)
12814 || (parameter.child_by_field_name("declarator").is_none()
12815 && parameter
12816 .child_by_field_name("type")
12817 .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
12818 {
12819 continue;
12820 }
12821 slots.push(CppParameterSlot::Declared(parameter));
12822 }
12823 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12824 slots.push(CppParameterSlot::Ellipsis);
12825 }
12826 _ => {}
12827 }
12828 }
12829 slots
12830}
12831
12832fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
12833 cpp_callable_parameter_slots(parameters_node, source)
12834 .into_iter()
12835 .map(|slot| match slot {
12836 CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
12837 CppParameterSlot::Ellipsis => "...".to_string(),
12838 })
12839 .collect()
12840}
12841
12842#[derive(Debug, Clone, PartialEq, Eq)]
12848pub enum CppParameterType {
12849 Structured(StructuredTypeIdentity),
12852 Ellipsis,
12854 Unstructured,
12857}
12858
12859pub fn cpp_callable_parameter_type_identities<'tree>(
12865 function_declarator: Node<'tree>,
12866 source: &str,
12867 ancestry: &ParentIndex<'tree>,
12868) -> Vec<CppParameterType> {
12869 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12870 return Vec::new();
12871 };
12872 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12873 cpp_callable_parameter_slots(parameters_node, source)
12874 .into_iter()
12875 .map(|slot| match slot {
12876 CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
12877 CppParameterSlot::Declared(parameter) => {
12878 cpp_parameter_type_identity(parameter, source, &lexical_scope)
12879 .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
12880 }
12881 })
12882 .collect()
12883}
12884
12885pub fn cpp_declaration_type_identity<'tree>(
12892 declaration: Node<'tree>,
12893 declarator: Node<'tree>,
12894 source: &str,
12895 ancestry: &ParentIndex<'tree>,
12896) -> Option<StructuredTypeIdentity> {
12897 let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
12898 cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
12899}
12900
12901fn cpp_parameter_type_identity(
12902 parameter: Node<'_>,
12903 source: &str,
12904 lexical_scope: &[String],
12905) -> Option<StructuredTypeIdentity> {
12906 cpp_declaration_type_identity_in_scope(
12907 parameter,
12908 cpp_parameter_declarator(parameter),
12909 source,
12910 lexical_scope,
12911 )
12912}
12913
12914fn cpp_declaration_type_identity_in_scope(
12915 declaration: Node<'_>,
12916 declarator: Option<Node<'_>>,
12917 source: &str,
12918 lexical_scope: &[String],
12919) -> Option<StructuredTypeIdentity> {
12920 let type_node = declaration.child_by_field_name("type")?;
12921 let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
12922 if let Some(declarator) = declarator {
12923 for wrapper in cpp_structured_declarator_wrappers(declarator)?
12924 .into_iter()
12925 .rev()
12926 {
12927 identity = cpp_wrap_structured_type(identity, wrapper)?;
12928 }
12929 }
12930 Some(identity)
12931}
12932
12933#[derive(Debug, Clone, PartialEq, Eq)]
12946pub enum CppComparableSlot {
12947 Shape(CppComparableParameter),
12949 Ellipsis,
12951 Unstructured,
12954}
12955
12956#[derive(Debug, Clone, PartialEq, Eq)]
12969pub struct CppComparableParameter {
12970 nodes: Vec<CppComparableNode>,
12971 root: usize,
12972}
12973
12974#[derive(Debug, Clone, PartialEq, Eq)]
12982pub enum CppComparableNode {
12983 Named {
12984 name: StructuredTypeName,
12985 primitive: bool,
12986 konst: bool,
12987 volatil: bool,
12988 },
12989 Pointer {
12990 inner: usize,
12991 konst: bool,
12992 volatil: bool,
12993 },
12994 Reference {
12995 inner: usize,
12996 },
12997 Array {
12998 inner: usize,
12999 },
13000 Generic {
13001 base: usize,
13002 arguments: Vec<usize>,
13003 },
13004}
13005
13006impl CppComparableParameter {
13007 pub fn root(&self) -> usize {
13008 self.root
13009 }
13010
13011 pub fn node(&self, index: usize) -> &CppComparableNode {
13012 &self.nodes[index]
13013 }
13014
13015 fn adjust_parameter_top_level(&mut self) {
13026 let root = self.root;
13027 match &mut self.nodes[root] {
13028 CppComparableNode::Named { konst, volatil, .. }
13029 | CppComparableNode::Pointer { konst, volatil, .. } => {
13030 *konst = false;
13031 *volatil = false;
13032 }
13033 CppComparableNode::Array { inner } => {
13034 let inner = *inner;
13035 self.nodes[root] = CppComparableNode::Pointer {
13036 inner,
13037 konst: false,
13038 volatil: false,
13039 };
13040 }
13041 CppComparableNode::Generic { base, .. } => {
13042 let base = *base;
13043 let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
13044 unreachable!("a comparable generic's base is always a named leaf");
13045 };
13046 *konst = false;
13047 *volatil = false;
13048 }
13049 CppComparableNode::Reference { .. } => {}
13050 }
13051 }
13052}
13053
13054pub fn cpp_comparable_parameter_shapes<'tree>(
13061 function_declarator: Node<'tree>,
13062 source: &str,
13063 ancestry: &ParentIndex<'tree>,
13064) -> Vec<CppComparableSlot> {
13065 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
13066 return Vec::new();
13067 };
13068 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
13069 cpp_callable_parameter_slots(parameters_node, source)
13070 .into_iter()
13071 .map(|slot| match slot {
13072 CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
13073 CppParameterSlot::Declared(parameter) => {
13074 cpp_comparable_parameter(parameter, source, &lexical_scope)
13075 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
13076 }
13077 })
13078 .collect()
13079}
13080
13081fn cpp_comparable_parameter(
13082 parameter: Node<'_>,
13083 source: &str,
13084 lexical_scope: &[String],
13085) -> Option<CppComparableParameter> {
13086 let type_node = parameter.child_by_field_name("type")?;
13087 let levels = match cpp_parameter_declarator(parameter) {
13088 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
13089 None => Vec::new(),
13090 };
13091 let mut shape = cpp_comparable_type_shape(
13092 type_node,
13093 cpp_cv_qualifiers(parameter, source),
13094 levels,
13095 source,
13096 lexical_scope,
13097 )?;
13098 shape.adjust_parameter_top_level();
13099 Some(shape)
13100}
13101
13102fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
13113 let mut qualifiers = CppCvQualifiers::default();
13114 let mut cursor = node.walk();
13115 for child in node.named_children(&mut cursor) {
13116 if child.kind() != "type_qualifier" {
13117 continue;
13118 }
13119 match node_text(child, source) {
13120 "const" => qualifiers.konst = true,
13121 "volatile" => qualifiers.volatil = true,
13122 _ => {}
13123 }
13124 }
13125 qualifiers
13126}
13127
13128#[derive(Clone, Copy, Default)]
13129struct CppCvQualifiers {
13130 konst: bool,
13131 volatil: bool,
13132}
13133
13134impl CppCvQualifiers {
13135 fn union(self, other: Self) -> Self {
13136 Self {
13137 konst: self.konst || other.konst,
13138 volatil: self.volatil || other.volatil,
13139 }
13140 }
13141}
13142
13143#[derive(Clone, Copy)]
13145enum CppComparableLevel {
13146 Pointer { konst: bool, volatil: bool },
13147 Reference,
13148 Array,
13149}
13150
13151fn cpp_comparable_declarator_levels(
13164 declarator: Node<'_>,
13165 source: &str,
13166) -> Option<Vec<CppComparableLevel>> {
13167 let mut levels = Vec::new();
13168 let mut current = declarator;
13169 loop {
13170 match current.kind() {
13171 "pointer_declarator" | "abstract_pointer_declarator" => {
13172 let qualifiers = cpp_cv_qualifiers(current, source);
13173 levels.push(CppComparableLevel::Pointer {
13174 konst: qualifiers.konst,
13175 volatil: qualifiers.volatil,
13176 });
13177 }
13178 "reference_declarator" | "abstract_reference_declarator" => {
13179 levels.push(CppComparableLevel::Reference);
13180 }
13181 "array_declarator" | "abstract_array_declarator" => {
13182 levels.push(CppComparableLevel::Array);
13183 }
13184 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
13185 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
13186 _ => return None,
13187 }
13188 let Some(next) = cpp_nested_declarator(current) else {
13189 return Some(levels);
13190 };
13191 current = next;
13192 }
13193}
13194
13195fn cpp_comparable_type_shape(
13202 type_node: Node<'_>,
13203 qualifiers: CppCvQualifiers,
13204 levels: Vec<CppComparableLevel>,
13205 source: &str,
13206 lexical_scope: &[String],
13207) -> Option<CppComparableParameter> {
13208 enum Work<'tree> {
13209 Visit {
13210 node: Node<'tree>,
13211 qualifiers: CppCvQualifiers,
13212 },
13213 ApplyLevels(Vec<CppComparableLevel>),
13214 BuildGeneric {
13215 argument_count: usize,
13216 },
13217 }
13218
13219 let mut nodes: Vec<CppComparableNode> = Vec::new();
13220 let mut values: Vec<usize> = Vec::new();
13221 let mut work = vec![
13222 Work::ApplyLevels(levels),
13223 Work::Visit {
13224 node: type_node,
13225 qualifiers,
13226 },
13227 ];
13228 while let Some(next) = work.pop() {
13229 match next {
13230 Work::Visit { node, qualifiers } => match node.kind() {
13231 "type_descriptor" => {
13232 let inner_type = node
13233 .child_by_field_name("type")
13234 .or_else(|| node.named_child(0))?;
13235 let mut cursor = node.walk();
13236 let declarator = node.child_by_field_name("declarator").or_else(|| {
13237 node.named_children(&mut cursor).find(|child| {
13238 child.id() != inner_type.id() && child.kind() != "type_qualifier"
13239 })
13240 });
13241 let levels = match declarator {
13242 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
13243 None => Vec::new(),
13244 };
13245 work.push(Work::ApplyLevels(levels));
13246 work.push(Work::Visit {
13247 node: inner_type,
13248 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
13249 });
13250 }
13251 "sized_type_specifier" => {
13252 let name = StructuredTypeName::new(
13257 vec![normalize_cpp_whitespace(node_text(node, source))],
13258 lexical_scope.to_vec(),
13259 false,
13260 )?;
13261 values.push(cpp_push_comparable_node(
13262 &mut nodes,
13263 CppComparableNode::Named {
13264 name,
13265 primitive: true,
13266 konst: qualifiers.konst,
13267 volatil: qualifiers.volatil,
13268 },
13269 ));
13270 }
13271 "qualified_identifier"
13272 | "scoped_identifier"
13273 | "scoped_type_identifier"
13274 | "type_identifier"
13275 | "field_identifier"
13276 | "identifier"
13277 | "namespace_identifier"
13278 | "primitive_type"
13279 | "template_type" => {
13280 let name = cpp_structured_named_type(node, source, lexical_scope)?;
13281 values.push(cpp_push_comparable_node(
13282 &mut nodes,
13283 CppComparableNode::Named {
13284 name,
13285 primitive: node.kind() == "primitive_type",
13286 konst: qualifiers.konst,
13287 volatil: qualifiers.volatil,
13288 },
13289 ));
13290 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
13291 let mut cursor = arguments_node.walk();
13292 let arguments = arguments_node
13293 .named_children(&mut cursor)
13294 .filter(|child| !child.is_extra() && child.kind() != "comment")
13295 .collect::<Vec<_>>();
13296 work.push(Work::BuildGeneric {
13297 argument_count: arguments.len(),
13298 });
13299 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
13300 node: argument,
13301 qualifiers: CppCvQualifiers::default(),
13302 }));
13303 }
13304 }
13305 _ => {
13306 let inner = node.child_by_field_name("type").or_else(|| {
13307 (node.named_child_count() == 1)
13308 .then(|| node.named_child(0))
13309 .flatten()
13310 })?;
13311 work.push(Work::Visit {
13312 node: inner,
13313 qualifiers,
13314 });
13315 }
13316 },
13317 Work::ApplyLevels(levels) => {
13318 let mut root = values.pop()?;
13319 for level in levels {
13320 let node = match level {
13321 CppComparableLevel::Pointer { konst, volatil } => {
13322 CppComparableNode::Pointer {
13323 inner: root,
13324 konst,
13325 volatil,
13326 }
13327 }
13328 CppComparableLevel::Reference => {
13329 CppComparableNode::Reference { inner: root }
13330 }
13331 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
13332 };
13333 root = cpp_push_comparable_node(&mut nodes, node);
13334 }
13335 values.push(root);
13336 }
13337 Work::BuildGeneric { argument_count } => {
13338 let value_count = argument_count.checked_add(1)?;
13339 let start = values.len().checked_sub(value_count)?;
13340 let mut built = values.split_off(start);
13341 let base = built.remove(0);
13342 values.push(cpp_push_comparable_node(
13343 &mut nodes,
13344 CppComparableNode::Generic {
13345 base,
13346 arguments: built,
13347 },
13348 ));
13349 }
13350 }
13351 }
13352 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
13353 debug_assert_eq!(
13354 root,
13355 nodes.len().saturating_sub(1),
13356 "comparable nodes are appended in post-order, so the root is the last one"
13357 );
13358 Some(CppComparableParameter { nodes, root })
13359}
13360
13361fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
13362 nodes.push(node);
13363 nodes.len() - 1
13364}
13365
13366fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
13372 let mut current = node;
13373 loop {
13374 match current.kind() {
13375 "template_type" => return current.child_by_field_name("arguments"),
13376 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
13377 current = current.child_by_field_name("name")?;
13378 }
13379 _ => return None,
13380 }
13381 }
13382}
13383
13384pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
13390 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
13391 loop {
13392 if matches!(
13393 current.kind(),
13394 "declaration" | "field_declaration" | "function_definition"
13395 ) && let Some(declarator) = current
13396 .child_by_field_name("declarator")
13397 .and_then(extract_function_declarator)
13398 {
13399 return Some(declarator);
13400 }
13401 current = current.parent()?;
13402 }
13403}
13404
13405fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
13406 let mut labels = Vec::new();
13407 let mut cursor = parameters_node.walk();
13408 for child in parameters_node.children(&mut cursor) {
13409 match child.kind() {
13410 "parameter_declaration" | "optional_parameter_declaration" => {
13411 if let Some(name_node) = child
13412 .child_by_field_name("declarator")
13413 .and_then(cpp_declarator_label_node)
13414 {
13415 labels.push(name_node);
13416 } else {
13417 labels.push(child);
13418 }
13419 }
13420 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
13421 labels.push(child);
13422 }
13423 _ => {}
13424 }
13425 }
13426 labels
13427}
13428
13429fn cpp_signature_search_start<'tree>(
13430 signature: &str,
13431 function_declarator: Node<'tree>,
13432 source: &str,
13433 ancestry: &ParentIndex<'tree>,
13434) -> usize {
13435 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
13436 return 0;
13437 };
13438 let raw = node_text(enclosing, source);
13439 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
13440 let offset = function_declarator
13441 .start_byte()
13442 .saturating_sub(enclosing.start_byte())
13443 .saturating_sub(leading_trim_bytes);
13444 offset.min(signature.len())
13445}
13446
13447fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
13448 match node.kind() {
13449 "identifier" | "field_identifier" => Some(node),
13450 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
13451 .child_by_field_name("declarator")
13452 .or_else(|| last_named_child(node))
13453 .and_then(cpp_declarator_label_node),
13454 "array_declarator" => node
13455 .child_by_field_name("declarator")
13456 .and_then(cpp_declarator_label_node),
13457 "function_declarator" => node
13458 .child_by_field_name("declarator")
13459 .or_else(|| node.child_by_field_name("name"))
13460 .or_else(|| last_named_child(node))
13461 .and_then(cpp_declarator_label_node),
13462 _ => None,
13463 }
13464}
13465
13466fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
13467 let base_type = parameter
13468 .child_by_field_name("type")
13469 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
13470 .unwrap_or_default();
13471 let declarator = cpp_parameter_declarator(parameter);
13472 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
13479 let mut cursor = parameter.walk();
13480 let qualifiers = parameter
13481 .named_children(&mut cursor)
13482 .filter(|child| child.kind() == "type_qualifier")
13483 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
13484 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
13485 .collect::<Vec<_>>()
13486 .join(" ");
13487 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
13488 (true, _) => base_type,
13489 (_, true) => qualifiers,
13490 (false, false) => format!("{qualifiers} {base_type}"),
13491 };
13492 let declarator_suffix = declarator
13493 .map(|node| cpp_declarator_suffix_without_name(node, source))
13494 .unwrap_or_default();
13495
13496 let combined = if type_text.is_empty() {
13497 declarator_suffix
13498 } else if declarator_suffix.is_empty() {
13499 type_text
13500 } else {
13501 format!("{type_text} {declarator_suffix}")
13502 };
13503 normalize_cpp_type_text(&combined)
13504}
13505
13506fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
13507 parameter.child_by_field_name("declarator").or_else(|| {
13508 let mut cursor = parameter.walk();
13514 parameter
13515 .named_children(&mut cursor)
13516 .find(|child| is_cpp_abstract_declarator(child.kind()))
13517 })
13518}
13519
13520pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
13523 let mut current = Some(declarator);
13524 while let Some(node) = current {
13525 if matches!(
13526 node.kind(),
13527 "pointer_declarator"
13528 | "abstract_pointer_declarator"
13529 | "reference_declarator"
13530 | "abstract_reference_declarator"
13531 | "array_declarator"
13532 | "abstract_array_declarator"
13533 | "function_declarator"
13534 | "abstract_function_declarator"
13535 ) {
13536 return true;
13537 }
13538 current = cpp_nested_declarator(node);
13539 }
13540 false
13541}
13542
13543fn is_cpp_abstract_declarator(kind: &str) -> bool {
13544 matches!(
13545 kind,
13546 "abstract_pointer_declarator"
13547 | "abstract_reference_declarator"
13548 | "abstract_array_declarator"
13549 | "abstract_function_declarator"
13550 | "abstract_parenthesized_declarator"
13551 )
13552}
13553
13554fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
13555 node.child_by_field_name("declarator").or_else(|| {
13556 if is_cpp_abstract_declarator(node.kind()) {
13557 let mut cursor = node.walk();
13558 node.named_children(&mut cursor)
13559 .find(|child| is_cpp_abstract_declarator(child.kind()))
13560 } else {
13561 last_named_child(node)
13565 }
13566 })
13567}
13568
13569fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
13570 match node.kind() {
13571 "identifier" | "field_identifier" => String::new(),
13572 "pointer_declarator" | "abstract_pointer_declarator" => {
13573 let inner = cpp_nested_declarator(node)
13574 .map(|child| cpp_declarator_suffix_without_name(child, source))
13575 .unwrap_or_default();
13576 format!("*{inner}")
13577 }
13578 "reference_declarator" | "abstract_reference_declarator" => {
13579 let inner = cpp_nested_declarator(node)
13580 .map(|child| cpp_declarator_suffix_without_name(child, source))
13581 .unwrap_or_default();
13582 let reference = node
13583 .children(&mut node.walk())
13584 .find(|child| matches!(child.kind(), "&" | "&&"))
13585 .map(|child| node_text(child, source))
13586 .unwrap_or("&");
13587 format!("{reference}{inner}")
13588 }
13589 "array_declarator" | "abstract_array_declarator" => {
13590 let inner = cpp_nested_declarator(node)
13591 .map(|child| cpp_declarator_suffix_without_name(child, source))
13592 .unwrap_or_default();
13593 let size = node
13594 .child_by_field_name("size")
13595 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
13596 .unwrap_or_default();
13597 format!("{inner}[{size}]")
13598 }
13599 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
13600 let inner = cpp_nested_declarator(node);
13601 inner
13602 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
13603 .unwrap_or_default()
13604 }
13605 "function_declarator" | "abstract_function_declarator" => {
13606 let inner = cpp_nested_declarator(node)
13607 .map(|child| cpp_declarator_suffix_without_name(child, source))
13608 .unwrap_or_default();
13609 let params = node
13610 .child_by_field_name("parameters")
13611 .map(|child| cpp_parameter_signature(child, source))
13612 .unwrap_or_else(|| "()".to_string());
13613 format!("{inner}{params}")
13614 }
13615 _ => {
13616 let text = normalize_cpp_whitespace(node_text(node, source));
13617 let name = extract_declarator_name(node, source);
13618 if name.is_empty() {
13619 text
13620 } else {
13621 text.replace(&name, "").trim().to_string()
13622 }
13623 }
13624 }
13625}
13626
13627fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
13628 collapse_cpp_whitespace(
13629 suffix
13630 .trim()
13631 .trim_start_matches("->")
13632 .trim_start_matches('{')
13633 .trim_end_matches(';'),
13634 )
13635}
13636
13637pub fn normalize_cpp_whitespace(value: &str) -> String {
13638 collapse_cpp_whitespace(value)
13639}
13640
13641fn normalize_cpp_type_text(value: &str) -> String {
13642 collapse_cpp_whitespace(value)
13643 .replace(", ", ",")
13644 .replace(" <", "<")
13645 .replace("< ", "<")
13646 .replace(" >", ">")
13647}
13648
13649fn collapse_cpp_whitespace(value: &str) -> String {
13650 let mut result = String::new();
13651 let mut prev_space = false;
13652 for ch in value.chars() {
13653 if ch.is_whitespace() {
13654 if !prev_space {
13655 result.push(' ');
13656 }
13657 prev_space = true;
13658 } else {
13659 result.push(ch);
13660 prev_space = false;
13661 }
13662 }
13663 result.trim().to_string()
13664}
13665
13666pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
13667 node_source_text(node, source)
13668}
13669
13670pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
13671 walk_named_tree_preorder(node, true, |node| {
13672 match node.kind() {
13673 "type_identifier" | "identifier" | "qualified_identifier" => {
13674 let text = node_text(node, source).trim();
13675 if !text.is_empty() {
13676 identifiers.insert(text.to_string());
13677 }
13678 }
13679 _ => {}
13680 }
13681 WalkControl::Continue
13682 });
13683}
13684
13685fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
13686 node.child_by_field_name("body").or_else(|| {
13687 let mut cursor = node.walk();
13688 node.named_children(&mut cursor).find(|child| {
13689 matches!(
13690 child.kind(),
13691 "declaration_list" | "field_declaration_list" | "enumerator_list"
13692 )
13693 })
13694 })
13695}
13696
13697fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
13708 if !matches!(
13709 node.kind(),
13710 "class_specifier" | "struct_specifier" | "union_specifier"
13711 ) {
13712 return None;
13713 }
13714 let body = cpp_body_node(node)?;
13715 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
13716 return None;
13717 }
13718 let open = body.child(0)?;
13719 let close = body.child(body.child_count().checked_sub(1)?)?;
13720 if open.kind() != "{"
13721 || open.is_missing()
13722 || close.kind() != "}"
13723 || close.is_missing()
13724 || close.end_byte() != body.end_byte()
13725 || body.end_byte() > node.end_byte()
13726 || node
13727 .parent()
13728 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
13729 {
13730 return None;
13731 }
13732 Some(close)
13733}
13734
13735fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
13736 if node.kind() == "namespace_definition" {
13737 return true;
13738 }
13739 let mut cursor = node.walk();
13740 node.named_children(&mut cursor)
13741 .any(cpp_contains_namespace_definition)
13742}
13743
13744struct CppNestedNamespaceSentinel<'tree> {
13745 function: Node<'tree>,
13746 body: Node<'tree>,
13747 namespace_components: Vec<String>,
13748}
13749
13750#[derive(Debug, Clone)]
13760pub struct CppSentinelRecoveredOwner {
13761 pub range: Range,
13762 pub owner_name_start_byte: usize,
13766 pub namespace_component_count: usize,
13770 pub scope_components: Vec<String>,
13771}
13772
13773#[derive(Debug, Clone)]
13774pub struct CppSentinelRecoveredClass {
13775 pub namespace_range: Range,
13776 pub namespace_scope_components: Vec<String>,
13777 pub class_range: Range,
13778 pub scope_components: Vec<String>,
13780 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
13784}
13785
13786pub fn cpp_sentinel_recovered_scope_for_node(
13792 node: Node<'_>,
13793 source: &str,
13794 recovered_classes: &[CppSentinelRecoveredClass],
13795) -> Option<Vec<String>> {
13796 let contains =
13797 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
13798 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
13799 for recovered in recovered_classes {
13800 for owner in recovered
13801 .owner_ranges
13802 .iter()
13803 .filter(|owner| contains(owner.range))
13804 {
13805 let replace = best_owner.is_none_or(|existing| {
13806 owner.range.end_byte.saturating_sub(owner.range.start_byte)
13807 < existing
13808 .range
13809 .end_byte
13810 .saturating_sub(existing.range.start_byte)
13811 });
13812 if replace {
13813 best_owner = Some(owner);
13814 }
13815 }
13816 }
13817 if let Some(owner) = best_owner {
13818 let mut scope = owner.scope_components.clone();
13819 if node.start_byte() < owner.owner_name_start_byte {
13820 scope.truncate(owner.namespace_component_count);
13821 }
13822 return Some(scope);
13823 }
13824
13825 let class = recovered_classes
13826 .iter()
13827 .filter(|recovered| contains(recovered.class_range))
13828 .min_by_key(|recovered| {
13829 recovered
13830 .class_range
13831 .end_byte
13832 .saturating_sub(recovered.class_range.start_byte)
13833 });
13834 let class_scope = class.is_some();
13835 let mut scope = if let Some(class) = class {
13836 class.scope_components.clone()
13837 } else {
13838 let namespace = recovered_classes
13839 .iter()
13840 .filter(|recovered| contains(recovered.namespace_range))
13841 .min_by_key(|recovered| {
13842 recovered
13843 .namespace_range
13844 .end_byte
13845 .saturating_sub(recovered.namespace_range.start_byte)
13846 })?;
13847 let mut scope = namespace.namespace_scope_components.clone();
13848 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
13849 let common_prefix = scope
13850 .iter()
13851 .zip(&parser_namespace)
13852 .take_while(|(recovered, parser)| recovered == parser)
13853 .count();
13854 scope.extend(parser_namespace.into_iter().skip(common_prefix));
13855 scope
13856 };
13857 if class_scope {
13858 let mut ancestor_components = Vec::new();
13859 let mut ancestor = node.parent();
13860 while let Some(current) = ancestor {
13861 if matches!(
13862 current.kind(),
13863 "class_specifier" | "struct_specifier" | "union_specifier"
13864 ) && let Some(name) = current.child_by_field_name("name")
13865 && let Some(name_components) = cpp_name_components(name, source)
13866 {
13867 ancestor_components.push(
13868 name_components
13869 .into_iter()
13870 .map(|component| component.name)
13871 .collect::<Vec<_>>(),
13872 );
13873 }
13874 ancestor = current.parent();
13875 }
13876 ancestor_components.reverse();
13877 let base_len = scope.len();
13878 for component in ancestor_components.into_iter().flatten() {
13879 if scope.len() >= base_len && scope.last() == Some(&component) {
13880 continue;
13881 }
13882 scope.push(component);
13883 }
13884 }
13885 Some(scope)
13886}
13887
13888struct CppSentinelFragmentedClassTail<'tree> {
13889 class_node: Node<'tree>,
13890 template_node: Option<Node<'tree>>,
13891 name: String,
13892 raw_supertypes: Option<Vec<String>>,
13893 fragmented: FragmentedExportBody,
13894 consumed_start: usize,
13895}
13896
13897struct CppSentinelFragmentedClassErrorPrefix<'tree> {
13898 name: String,
13899 open: Node<'tree>,
13900 raw_supertypes: Option<Vec<String>>,
13901}
13902
13903struct CppSentinelDirectBodyClassRegion {
13904 namespace_components: Vec<String>,
13905 class_start: usize,
13906 class_start_line: usize,
13907 class_close_end: usize,
13908 class_close_line: usize,
13909 name: String,
13910}
13911
13912fn cpp_sentinel_body_class_candidate<'tree>(
13913 child: Node<'tree>,
13914) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13915 if matches!(
13916 child.kind(),
13917 "class_specifier" | "struct_specifier" | "union_specifier"
13918 ) {
13919 return Some((child, None));
13920 }
13921 if child.kind() != "template_declaration" {
13922 if child.kind() == "declaration" {
13923 return Some((first_class_like_child(child)?, None));
13924 }
13925 return None;
13926 }
13927 let mut cursor = child.walk();
13928 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
13929 if matches!(
13930 candidate.kind(),
13931 "class_specifier" | "struct_specifier" | "union_specifier"
13932 ) {
13933 Some(candidate)
13934 } else if candidate.kind() == "declaration" {
13935 first_class_like_child(candidate)
13936 } else {
13937 None
13938 }
13939 })?;
13940 Some((class_node, Some(child)))
13941}
13942
13943fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
13949 node: Node<'tree>,
13950 source: &str,
13951) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
13952 let name = malformed_class_error_owner_name(node, source)?;
13953 let mut cursor = node.walk();
13954 let children = node.children(&mut cursor).collect::<Vec<_>>();
13955 let keyword = children.first()?;
13956 let open_index = children.iter().position(|child| child.kind() == "{")?;
13957 if children[open_index + 1..]
13958 .iter()
13959 .any(|child| child.kind() == "}")
13960 {
13961 return None;
13962 }
13963 let raw_supertypes =
13964 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
13965 Some(CppSentinelFragmentedClassErrorPrefix {
13966 name,
13967 open: children[open_index],
13968 raw_supertypes,
13969 })
13970}
13971
13972fn cpp_sentinel_direct_body_class_candidate<'tree>(
13973 child: Node<'tree>,
13974) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13975 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
13976 return Some(candidate);
13977 }
13978 if child.kind() != "template_declaration" {
13979 return None;
13980 }
13981 let mut cursor = child.walk();
13982 let wrapper = child
13983 .named_children(&mut cursor)
13984 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
13985 Some((first_class_like_child(wrapper)?, Some(child)))
13986}
13987
13988fn cpp_sentinel_direct_namespace_components(
13989 function: Node<'_>,
13990 body: Node<'_>,
13991 source: &str,
13992) -> Option<Vec<String>> {
13993 let mut cursor = function.walk();
13994 let children = function
13995 .named_children(&mut cursor)
13996 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
13997 .collect::<Vec<_>>();
13998 let sentinel_index = children.iter().rposition(|child| {
13999 direct_identifier_name(*child, source)
14000 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
14001 })?;
14002 let mut identifiers = Vec::new();
14003 let mut stack = children[sentinel_index + 1..]
14004 .iter()
14005 .rev()
14006 .copied()
14007 .collect::<Vec<_>>();
14008 while let Some(current) = stack.pop() {
14009 if let Some(name) = direct_identifier_name(current, source) {
14010 identifiers.push(name);
14011 continue;
14012 }
14013 let mut cursor = current.walk();
14014 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
14015 stack.extend(children.into_iter().rev());
14016 }
14017 let [keyword, namespace] = identifiers.as_slice() else {
14018 return None;
14019 };
14020 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
14021 .then(|| vec![namespace.clone()])
14022}
14023
14024fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
14025 let mut sibling = class_semicolon.next_named_sibling();
14026 let namespace_close = loop {
14027 let Some(current) = sibling else {
14028 return false;
14029 };
14030 sibling = current.next_named_sibling();
14031 if current.kind() != "comment" {
14032 break current;
14033 }
14034 };
14035 if !cpp_is_stray_close_brace(namespace_close, source) {
14036 return false;
14037 }
14038 loop {
14039 let Some(current) = sibling else {
14040 return false;
14041 };
14042 sibling = current.next_named_sibling();
14043 if current.kind() == "comment" {
14044 continue;
14045 }
14046 return direct_identifier_name(current, source)
14047 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
14048 }
14049}
14050
14051fn cpp_sentinel_macro_body_class_region<'tree>(
14052 node: Node<'tree>,
14053 source: &str,
14054 ancestry: &ParentIndex<'tree>,
14055) -> Option<CppSentinelDirectBodyClassRegion> {
14056 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
14057 return None;
14058 };
14059 if node.kind() != "function_definition" || !node.has_error() {
14060 return None;
14061 }
14062 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
14063 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
14064 let mut cursor = body.walk();
14065 let candidates = body
14066 .named_children(&mut cursor)
14067 .filter_map(cpp_sentinel_direct_body_class_candidate)
14068 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
14069 .collect::<Vec<_>>();
14070 let [(class_node, template_node)] = candidates.as_slice() else {
14071 return None;
14072 };
14073 let original_body = cpp_body_node(*class_node)?;
14074 let name = class_like_name(*class_node, source, ancestry)?;
14075 if name.is_empty() || cpp_export_macro_token(&name) {
14076 return None;
14077 }
14078
14079 let mut sibling = node.next_named_sibling();
14080 let (class_close_start, class_close_end, class_close_line) = loop {
14081 let current = sibling?;
14082 let next = current.next_named_sibling();
14083 if cpp_is_stray_close_brace(current, source)
14084 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
14085 {
14086 let semicolon = next.expect("checked above");
14087 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
14088 return None;
14089 }
14090 break (
14091 current.start_byte(),
14092 semicolon.end_byte(),
14093 semicolon.end_position().row + 1,
14094 );
14095 }
14096 sibling = next;
14097 };
14098 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
14099 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
14100 let root = tree.root_node();
14101 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
14102 let reparsed_ancestry = ParentIndex::new(root);
14105 let reparsed =
14106 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
14107 if reparsed.name != name
14108 || reparsed.declaration_node.start_byte() != class_node.start_byte()
14109 || reparsed.body.start_byte() != original_body.start_byte()
14110 || class_close_start <= reparsed.body.end_byte()
14111 || class_close_end <= class_node.end_byte()
14112 {
14113 return None;
14114 }
14115 Some(CppSentinelDirectBodyClassRegion {
14116 namespace_components,
14117 class_start: reparse_start,
14118 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
14119 node.start_position().row + 1
14120 }),
14121 class_close_end,
14122 class_close_line,
14123 name,
14124 })
14125}
14126
14127fn cpp_nested_namespace_sentinel<'tree>(
14139 node: Node<'tree>,
14140 source: &str,
14141 ancestry: &ParentIndex<'tree>,
14142) -> Option<CppNestedNamespaceSentinel<'tree>> {
14143 if !node.has_error() {
14144 return None;
14145 }
14146
14147 let (function, mut namespace_components) = if node.kind() == "ERROR" {
14148 let mut cursor = node.walk();
14149 let functions = node
14150 .named_children(&mut cursor)
14151 .filter(|child| child.kind() == "function_definition")
14152 .collect::<Vec<_>>();
14153 let [function] = functions.as_slice() else {
14154 return None;
14155 };
14156 if !function.has_error() {
14157 return None;
14158 }
14159 let mut cursor = node.walk();
14160 let children = node.children(&mut cursor).collect::<Vec<_>>();
14161 let function_index = children
14162 .iter()
14163 .position(|child| same_node(*child, *function))?;
14164 let [outer_keyword, outer_name, outer_open] =
14165 children.get(function_index.checked_sub(3)?..function_index)?
14166 else {
14167 return None;
14168 };
14169 if outer_keyword.kind() != "namespace"
14170 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
14171 || outer_open.kind() != "{"
14172 {
14173 return None;
14174 }
14175 (
14176 *function,
14177 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
14178 )
14179 } else if node.kind() == "function_definition" {
14180 let declaration_list = node.parent()?;
14181 let namespace = declaration_list.parent()?;
14182 if declaration_list.kind() != "declaration_list"
14183 || namespace.kind() != "namespace_definition"
14184 || namespace.child_by_field_name("body") != Some(declaration_list)
14185 {
14186 return None;
14187 }
14188 (node, Vec::new())
14189 } else {
14190 return None;
14191 };
14192
14193 let mut cursor = function.walk();
14194 let named = function
14195 .named_children(&mut cursor)
14196 .filter(|child| child.kind() != "comment")
14197 .collect::<Vec<_>>();
14198 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
14199 return None;
14200 };
14201 if first_type.kind() != "type_identifier" {
14202 return None;
14203 }
14204 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
14205 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
14206 return None;
14207 }
14208 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
14209 return None;
14210 }
14211 let inner_keyword = inner_error.named_child(0)?;
14212 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
14213 return None;
14214 }
14215 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
14216 return None;
14217 }
14218 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
14219 if inner_name.is_empty() || body.kind() != "compound_statement" {
14220 return None;
14221 }
14222 namespace_components.push(inner_name);
14223
14224 let mut cursor = body.walk();
14225 let has_complete_class = body.named_children(&mut cursor).any(|child| {
14226 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
14227 cpp_body_node(class_node).is_some()
14228 && class_like_name(class_node, source, ancestry)
14229 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
14230 })
14231 });
14232 if !has_complete_class
14233 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
14234 {
14235 return None;
14236 }
14237
14238 Some(CppNestedNamespaceSentinel {
14239 function,
14240 body: *body,
14241 namespace_components,
14242 })
14243}
14244
14245fn cpp_root_namespace_sentinel<'tree>(
14254 node: Node<'tree>,
14255 source: &str,
14256 ancestry: &ParentIndex<'tree>,
14257) -> Option<CppNestedNamespaceSentinel<'tree>> {
14258 if node.kind() != "function_definition"
14259 || !node.has_error()
14260 || node.parent()?.kind() != "translation_unit"
14261 {
14262 return None;
14263 }
14264 let first_type = node.child_by_field_name("type")?;
14265 let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
14266 if first_type.kind() != "type_identifier"
14267 || sentinel.is_empty()
14268 || !cpp_export_macro_token(&sentinel)
14269 {
14270 return None;
14271 }
14272 let declarator = node.child_by_field_name("declarator")?;
14273 let body = node.child_by_field_name("body")?;
14274 if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
14275 return None;
14276 }
14277 let mut cursor = node.walk();
14278 let named = node
14279 .named_children(&mut cursor)
14280 .filter(|child| child.kind() != "comment")
14281 .collect::<Vec<_>>();
14282 let [named_type, named_declarator, named_body] = named.as_slice() else {
14283 return None;
14284 };
14285 if !same_node(*named_type, first_type)
14286 || !same_node(*named_declarator, declarator)
14287 || !same_node(*named_body, body)
14288 {
14289 return None;
14290 }
14291 let mut declarator_components = Vec::new();
14292 let mut valid_components = true;
14293 walk_named_tree_preorder(declarator, true, |component| {
14294 if !matches!(
14295 component.kind(),
14296 "identifier" | "namespace_identifier" | "type_identifier"
14297 ) {
14298 return WalkControl::Continue;
14299 }
14300 let Some(component) = canonical_cpp_qualified_component(component, source) else {
14301 valid_components = false;
14302 return WalkControl::Break;
14303 };
14304 declarator_components.push(component.name);
14305 WalkControl::SkipChildren
14306 });
14307 if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
14308 return None;
14309 }
14310 declarator_components.remove(0);
14311 let namespace_components = declarator_components;
14312 if namespace_components.is_empty()
14313 || namespace_components
14314 .iter()
14315 .any(|component| component.is_empty() || cpp_export_macro_token(component))
14316 {
14317 return None;
14318 }
14319
14320 let mut cursor = body.walk();
14321 let has_complete_class = body.named_children(&mut cursor).any(|child| {
14322 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
14323 cpp_body_node(class_node).is_some()
14324 && class_like_name(class_node, source, ancestry)
14325 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
14326 })
14327 });
14328 if !has_complete_class
14329 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
14330 {
14331 return None;
14332 }
14333
14334 Some(CppNestedNamespaceSentinel {
14335 function: node,
14336 body,
14337 namespace_components,
14338 })
14339}
14340
14341fn cpp_sentinel_fragmented_class_tail<'tree>(
14350 function: Node<'tree>,
14351 body: Node<'tree>,
14352 source: &str,
14353 ancestry: &ParentIndex<'tree>,
14354) -> Option<CppSentinelFragmentedClassTail<'tree>> {
14355 let mut cursor = body.walk();
14356 let candidates = body
14357 .named_children(&mut cursor)
14358 .filter_map(|child| {
14359 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
14360 let class_body = cpp_body_node(class_node)?;
14361 if !class_node.has_error() {
14362 return None;
14363 }
14364 let name = class_like_name(class_node, source, ancestry)?;
14365 let raw_supertypes =
14366 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
14367 .then(|| extract_cpp_supertypes(class_node, source));
14368 return Some((
14369 class_node,
14370 template_node,
14371 name,
14372 class_body,
14373 class_body.start_byte().checked_add(1)?,
14374 raw_supertypes,
14375 ));
14376 }
14377 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
14378 Some((
14379 child,
14380 None,
14381 prefix.name,
14382 prefix.open,
14383 prefix.open.end_byte(),
14384 prefix.raw_supertypes,
14385 ))
14386 })
14387 .collect::<Vec<_>>();
14388 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
14389 candidates.as_slice()
14390 else {
14391 return None;
14392 };
14393 if name.is_empty() || cpp_export_macro_token(name) {
14394 return None;
14395 }
14396
14397 let (close, semicolon) =
14398 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
14399
14400 let reparse_end = close.start_byte();
14401 if *reparse_start >= reparse_end {
14402 return None;
14403 }
14404 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
14405 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
14406 return None;
14407 }
14408 let class_range = Range {
14409 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
14410 end_byte: semicolon.end_byte(),
14411 start_line: template_node.map_or(class_node.start_position().row, |node| {
14412 node.start_position().row
14413 }) + 1,
14414 end_line: semicolon.end_position().row + 1,
14415 };
14416 Some(CppSentinelFragmentedClassTail {
14417 class_node: *class_node,
14418 template_node: *template_node,
14419 name: name.clone(),
14420 raw_supertypes: raw_supertypes.clone(),
14421 fragmented: FragmentedExportBody {
14422 reparse_start: *reparse_start,
14423 reparse_end,
14424 class_range,
14425 },
14426 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
14427 })
14428}
14429
14430pub fn cpp_sentinel_recovered_classes(
14439 root: Node<'_>,
14440 source: &str,
14441) -> Vec<CppSentinelRecoveredClass> {
14442 if !root.has_error() {
14443 return Vec::new();
14444 }
14445 let ancestry = ParentIndex::new(root);
14449 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
14450 let mut stack = vec![root];
14451 while let Some(current) = stack.pop() {
14452 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
14453 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
14454 {
14455 let namespace_components = cpp_sentinel_recovered_namespace_components(
14456 recovered.function,
14457 &recovered.namespace_components,
14458 source,
14459 );
14460 let fragmented = cpp_sentinel_fragmented_class_tail(
14461 recovered.function,
14462 recovered.body,
14463 source,
14464 &ancestry,
14465 );
14466 let mut class_candidates = Vec::new();
14467 let mut cursor = recovered.body.walk();
14468 for (class_node, template_node) in recovered
14469 .body
14470 .named_children(&mut cursor)
14471 .filter_map(cpp_sentinel_body_class_candidate)
14472 {
14473 let Some(name) = class_like_name(class_node, source, &ancestry) else {
14474 continue;
14475 };
14476 if name.is_empty() || cpp_export_macro_token(&name) {
14477 continue;
14478 }
14479 let is_fragmented = fragmented
14480 .as_ref()
14481 .is_some_and(|tail| same_node(tail.class_node, class_node));
14482 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
14483 continue;
14484 }
14485 let class_range = if is_fragmented {
14486 fragmented
14487 .as_ref()
14488 .map(|tail| tail.fragmented.class_range)
14489 .expect("fragmented class range is present when class matches")
14490 } else {
14491 cpp_declaration_range(template_node.unwrap_or(class_node))
14492 };
14493 class_candidates.push((class_range, name));
14494 }
14495 if let Some(fragmented) = fragmented
14496 .as_ref()
14497 .filter(|tail| tail.class_node.kind() == "ERROR")
14498 {
14499 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
14500 }
14501
14502 let mut owner_ranges =
14503 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
14504 cpp_sentinel_extend_unique_owner_ranges(
14505 &mut owner_ranges,
14506 cpp_sentinel_recovered_sibling_owner_ranges(
14507 recovered.function,
14508 &namespace_components,
14509 source,
14510 ),
14511 );
14512 for (class_range, name) in class_candidates {
14513 push_cpp_sentinel_recovered_class(
14514 &mut recovered_classes,
14515 cpp_declaration_range(recovered.body),
14516 &namespace_components,
14517 class_range,
14518 name,
14519 &owner_ranges,
14520 );
14521 }
14522
14523 if let Some(declaration_list) = recovered
14524 .function
14525 .parent()
14526 .filter(|parent| parent.kind() == "declaration_list")
14527 {
14528 let outer_namespace =
14529 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
14530 push_cpp_sentinel_sibling_classes(
14531 &mut recovered_classes,
14532 declaration_list,
14533 recovered.function,
14534 &outer_namespace,
14535 source,
14536 &ancestry,
14537 );
14538 }
14539 } else if let Some(region) =
14540 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
14541 {
14542 let namespace_components = cpp_sentinel_recovered_namespace_components(
14543 current,
14544 ®ion.namespace_components,
14545 source,
14546 );
14547 let owner_container = current
14548 .parent()
14549 .filter(|parent| parent.kind() == "declaration_list")
14550 .unwrap_or(current);
14551 let owner_ranges =
14552 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
14553 push_cpp_sentinel_recovered_class(
14554 &mut recovered_classes,
14555 cpp_declaration_range(owner_container),
14556 &namespace_components,
14557 Range {
14558 start_byte: region.class_start,
14559 end_byte: region.class_close_end,
14560 start_line: region.class_start_line,
14561 end_line: region.class_close_line,
14562 },
14563 region.name,
14564 &owner_ranges,
14565 );
14566 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
14567 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
14572 region;
14573 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
14574 continue;
14575 };
14576 let root = tree.root_node();
14577 let template_node = cpp_sentinel_reparsed_leading_template(root);
14578 let reparsed_ancestry = ParentIndex::new(root);
14580 let Some(reparsed_class) =
14581 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
14582 else {
14583 continue;
14584 };
14585 let class_node = reparsed_class.declaration_node;
14586 let name = reparsed_class.name;
14587 let namespace_components =
14588 cpp_sentinel_recovered_namespace_components(current, &[], source);
14589 let owner_container = current
14590 .parent()
14591 .filter(|parent| parent.kind() == "declaration_list")
14592 .unwrap_or(current);
14593 let mut owner_ranges =
14594 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
14595 cpp_sentinel_extend_unique_owner_ranges(
14596 &mut owner_ranges,
14597 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
14598 );
14599 push_cpp_sentinel_recovered_class(
14600 &mut recovered_classes,
14601 cpp_declaration_range(owner_container),
14602 &namespace_components,
14603 Range {
14604 start_byte: class_start,
14605 end_byte: close_end,
14606 start_line: class_node.start_position().row + 1,
14607 end_line: class_node.end_position().row + 1,
14608 },
14609 name,
14610 &owner_ranges,
14611 );
14612 if owner_container.kind() == "declaration_list" {
14613 push_cpp_sentinel_sibling_classes(
14614 &mut recovered_classes,
14615 owner_container,
14616 current,
14617 &namespace_components,
14618 source,
14619 &ancestry,
14620 );
14621 }
14622 }
14623
14624 let mut cursor = current.walk();
14625 stack.extend(current.named_children(&mut cursor));
14626 }
14627 let shadowed = recovered_classes
14633 .iter()
14634 .map(|candidate| {
14635 recovered_classes.iter().any(|container| {
14636 container.class_range.start_byte <= candidate.class_range.start_byte
14637 && container.class_range.end_byte >= candidate.class_range.end_byte
14638 && container.class_range != candidate.class_range
14639 && container.namespace_scope_components.len()
14640 > candidate.namespace_scope_components.len()
14641 && container
14642 .namespace_scope_components
14643 .starts_with(&candidate.namespace_scope_components)
14644 })
14645 })
14646 .collect::<Vec<_>>();
14647 let mut index = 0usize;
14648 recovered_classes.retain(|_| {
14649 let keep = !shadowed[index];
14650 index += 1;
14651 keep
14652 });
14653 recovered_classes
14654}
14655
14656fn push_cpp_sentinel_sibling_classes<'tree>(
14662 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14663 declaration_list: Node<'tree>,
14664 sentinel_node: Node<'tree>,
14665 namespace_components: &[String],
14666 source: &str,
14667 ancestry: &ParentIndex<'tree>,
14668) {
14669 let owner_ranges =
14670 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
14671 let namespace_range = cpp_declaration_range(declaration_list);
14672 let mut cursor = declaration_list.walk();
14673 for (class_node, template_node) in declaration_list
14674 .named_children(&mut cursor)
14675 .filter(|child| !same_node(*child, sentinel_node))
14676 .filter_map(cpp_sentinel_body_class_candidate)
14677 {
14678 let Some(name) = class_like_name(class_node, source, ancestry) else {
14679 continue;
14680 };
14681 if name.is_empty()
14682 || cpp_export_macro_token(&name)
14683 || cpp_complete_class_body_close(class_node).is_none()
14684 {
14685 continue;
14686 }
14687 push_cpp_sentinel_recovered_class(
14688 recovered_classes,
14689 namespace_range,
14690 namespace_components,
14691 cpp_declaration_range(template_node.unwrap_or(class_node)),
14692 name,
14693 &owner_ranges,
14694 );
14695 }
14696}
14697
14698fn push_cpp_sentinel_recovered_class(
14699 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14700 namespace_range: Range,
14701 namespace_components: &[String],
14702 class_range: Range,
14703 name: String,
14704 owner_ranges: &[CppSentinelRecoveredOwner],
14705) {
14706 let mut scope_components = namespace_components.to_vec();
14707 scope_components.push(name);
14708 let owner_ranges = owner_ranges
14709 .iter()
14710 .filter(|owner| owner.scope_components.starts_with(&scope_components))
14711 .cloned()
14712 .collect::<Vec<_>>();
14713 if recovered_classes.iter().any(|existing| {
14714 existing.class_range == class_range && existing.scope_components == scope_components
14715 }) {
14716 return;
14717 }
14718 recovered_classes.push(CppSentinelRecoveredClass {
14719 namespace_range,
14720 namespace_scope_components: namespace_components.to_vec(),
14721 class_range,
14722 scope_components,
14723 owner_ranges,
14724 });
14725}
14726
14727fn cpp_sentinel_recovered_namespace_components(
14728 function: Node<'_>,
14729 recovered_components: &[String],
14730 source: &str,
14731) -> Vec<String> {
14732 let mut ancestor_components = Vec::new();
14733 let mut ancestor = function.parent();
14734 while let Some(current) = ancestor {
14735 if current.kind() == "namespace_definition"
14736 && let Some(name_node) = current.child_by_field_name("name")
14737 && let Some(components) = cpp_name_components(name_node, source)
14738 {
14739 ancestor_components.push(
14740 components
14741 .into_iter()
14742 .map(|component| component.name)
14743 .collect::<Vec<_>>(),
14744 );
14745 }
14746 ancestor = current.parent();
14747 }
14748 ancestor_components.reverse();
14749 let mut ancestors = ancestor_components
14750 .into_iter()
14751 .flatten()
14752 .collect::<Vec<_>>();
14753
14754 let overlap = (0..=ancestors.len().min(recovered_components.len()))
14755 .rev()
14756 .find(|length| {
14757 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
14758 })
14759 .unwrap_or(0);
14760 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
14761 ancestors
14762}
14763
14764fn cpp_sentinel_recovered_owner_ranges(
14765 body: Node<'_>,
14766 namespace_components: &[String],
14767 source: &str,
14768) -> Vec<CppSentinelRecoveredOwner> {
14769 let mut owners = Vec::new();
14770 walk_named_tree_preorder(body, true, |node| {
14771 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14772 });
14773 owners
14774}
14775
14776fn cpp_sentinel_collect_owner_range(
14777 node: Node<'_>,
14778 namespace_components: &[String],
14779 source: &str,
14780 owners: &mut Vec<CppSentinelRecoveredOwner>,
14781) -> WalkControl {
14782 if node.kind() != "function_definition" {
14783 return WalkControl::Continue;
14784 }
14785 let Some(function_declarator) = extract_function_declarator(node) else {
14786 return WalkControl::Continue;
14787 };
14788 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
14789 return WalkControl::Continue;
14790 };
14791 let Some(mut components) = cpp_name_components(name_node, source) else {
14792 return WalkControl::Continue;
14793 };
14794 if components.len() <= 1 {
14795 return WalkControl::Continue;
14796 }
14797 components.pop();
14798 let mut owner_components = components
14799 .into_iter()
14800 .map(|component| component.name)
14801 .collect::<Vec<_>>();
14802 let overlap = (0..=namespace_components.len().min(owner_components.len()))
14803 .rev()
14804 .find(|length| {
14805 owner_components[..*length]
14806 == namespace_components[namespace_components.len().saturating_sub(*length)..]
14807 })
14808 .unwrap_or(0);
14809 let mut scope_components = namespace_components.to_vec();
14810 scope_components.extend(owner_components.drain(overlap..));
14811 if scope_components.len() <= namespace_components.len() {
14812 return WalkControl::Continue;
14813 }
14814 let range = cpp_declaration_range(node);
14815 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
14816 existing.range == range && existing.scope_components == scope_components
14817 }) {
14818 owners.push(CppSentinelRecoveredOwner {
14819 range,
14820 owner_name_start_byte: name_node.start_byte(),
14821 namespace_component_count: namespace_components.len(),
14822 scope_components,
14823 });
14824 }
14825 WalkControl::Continue
14826}
14827
14828fn cpp_sentinel_extend_unique_owner_ranges(
14829 owners: &mut Vec<CppSentinelRecoveredOwner>,
14830 additional: Vec<CppSentinelRecoveredOwner>,
14831) {
14832 for owner in additional {
14833 if !owners.iter().any(|existing| {
14834 existing.range == owner.range && existing.scope_components == owner.scope_components
14835 }) {
14836 owners.push(owner);
14837 }
14838 }
14839}
14840
14841fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
14842 if node.kind() != "ERROR" || node.named_child_count() != 1 {
14843 return false;
14844 }
14845 let Some(end_name) = node.named_child(0) else {
14846 return false;
14847 };
14848 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
14849 return false;
14850 }
14851 let mut cursor = node.walk();
14852 node.children(&mut cursor)
14853 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
14854}
14855
14856fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14860 parent: Node<'_>,
14861 sentinel_node: Node<'_>,
14862 namespace_components: &[String],
14863 source: &str,
14864) -> Vec<CppSentinelRecoveredOwner> {
14865 let mut owners = Vec::new();
14866 let mut after_sentinel = false;
14867 let mut cursor = parent.walk();
14868 for child in parent.named_children(&mut cursor) {
14869 if !after_sentinel {
14870 if same_node(child, sentinel_node) {
14871 after_sentinel = true;
14872 }
14873 continue;
14874 }
14875 walk_named_tree_preorder(child, true, |node| {
14876 if node.kind() == "namespace_definition" {
14877 return WalkControl::SkipChildren;
14878 }
14879 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14880 });
14881 }
14882 owners
14883}
14884
14885fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14889 parent: Node<'_>,
14890 sentinel_node: Node<'_>,
14891 namespace_components: &[String],
14892 source: &str,
14893) -> Option<Vec<CppSentinelRecoveredOwner>> {
14894 let mut owners = Vec::new();
14895 let mut after_namespace = false;
14896 let mut cursor = parent.walk();
14897 for child in parent.named_children(&mut cursor) {
14898 if !after_namespace {
14899 if same_node(child, sentinel_node) {
14900 after_namespace = true;
14901 }
14902 continue;
14903 }
14904 if cpp_sentinel_namespace_end(child, source) {
14905 return Some(owners);
14906 }
14907 walk_named_tree_preorder(child, true, |node| {
14908 if node.kind() == "namespace_definition" {
14909 return WalkControl::SkipChildren;
14910 }
14911 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14912 });
14913 }
14914 None
14915}
14916
14917fn cpp_sentinel_recovered_sibling_owner_ranges(
14918 sentinel_node: Node<'_>,
14919 namespace_components: &[String],
14920 source: &str,
14921) -> Vec<CppSentinelRecoveredOwner> {
14922 let Some(declaration_list) = sentinel_node
14923 .parent()
14924 .filter(|parent| parent.kind() == "declaration_list")
14925 else {
14926 return Vec::new();
14927 };
14928 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14929 declaration_list,
14930 sentinel_node,
14931 namespace_components,
14932 source,
14933 );
14934
14935 let Some(namespace) = declaration_list
14936 .parent()
14937 .filter(|parent| parent.kind() == "namespace_definition")
14938 else {
14939 return owners;
14940 };
14941 let Some(outer_parent) = namespace.parent() else {
14942 return owners;
14943 };
14944 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14945 outer_parent,
14946 namespace,
14947 namespace_components,
14948 source,
14949 ) {
14950 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
14951 }
14952 owners
14953}
14954
14955fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
14956 let mut current = function_declarator.child_by_field_name("declarator")?;
14957 loop {
14958 if let Some(name) = macro_decorated_unqualified_name(current) {
14959 current = name;
14960 continue;
14961 }
14962 if matches!(
14963 current.kind(),
14964 "qualified_identifier"
14965 | "scoped_identifier"
14966 | "scoped_type_identifier"
14967 | "identifier"
14968 | "field_identifier"
14969 | "operator_name"
14970 | "destructor_name"
14971 | "literal_operator_name"
14972 ) {
14973 return Some(current);
14974 }
14975 current = current
14976 .child_by_field_name("declarator")
14977 .or_else(|| current.child_by_field_name("name"))
14978 .or_else(|| last_named_child(current))?;
14979 }
14980}
14981
14982fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
14995 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
14996 return None;
14997 }
14998 let mut cursor = node.walk();
14999 if node
15000 .children(&mut cursor)
15001 .any(|child| child.kind() == "::" && !child.is_missing())
15002 {
15003 return None;
15004 }
15005 node.child_by_field_name("name")
15006}
15007
15008fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
15009 if let Some(name) = macro_decorated_unqualified_name(node) {
15010 return cpp_name_components(name, source);
15011 }
15012 match node.kind() {
15013 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
15014 let mut components = match node.child_by_field_name("scope") {
15015 Some(scope) => cpp_name_components(scope, source)?,
15016 None => Vec::new(),
15017 };
15018 let name = node.child_by_field_name("name")?;
15019 components.push(canonical_cpp_qualified_component(name, source)?);
15020 Some(components)
15021 }
15022 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
15023 }
15024}
15025
15026fn cpp_sentinel_fragment_boundary<'tree>(
15027 function: Node<'tree>,
15028 class_node: Node<'tree>,
15029 class_body: Node<'tree>,
15030 source: &str,
15031) -> Option<(Node<'tree>, Node<'tree>)> {
15032 let declaration_list = function.parent()?;
15033 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
15034 return None;
15035 }
15036 let namespace = declaration_list.parent()?;
15037 if namespace.kind() != "namespace_definition"
15038 || namespace.child_by_field_name("body") != Some(declaration_list)
15039 {
15040 return None;
15041 }
15042 let mut cursor = declaration_list.walk();
15043 let closes = declaration_list
15044 .children(&mut cursor)
15045 .filter(|child| {
15046 !child.is_named()
15047 && child.kind() == "}"
15048 && child.start_byte() >= function.end_byte()
15049 && child.start_byte() > class_node.end_byte()
15050 && child.start_byte() > class_body.start_byte()
15051 })
15052 .collect::<Vec<_>>();
15053 let [close] = closes.as_slice() else {
15054 return None;
15055 };
15056 let semicolon = namespace.next_named_sibling()?;
15057 if !cpp_is_stray_semicolon(semicolon, source)
15058 || close.end_byte() != namespace.end_byte()
15059 || semicolon.start_byte() < namespace.end_byte()
15060 {
15061 return None;
15062 }
15063 Some((*close, semicolon))
15064}
15065
15066fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
15092 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
15093 {
15094 return None;
15095 }
15096 let mut declarator_cursor = node.walk();
15102 let preserved_callable = node
15103 .children_by_field_name("declarator", &mut declarator_cursor)
15104 .find_map(extract_function_declarator);
15105 let mut cursor = node.walk();
15113 let first = node
15114 .named_children(&mut cursor)
15115 .find(|child| child.kind() != "comment")?;
15116 if first.kind() != "type_identifier" {
15117 return None;
15118 }
15119 let sentinel = normalize_cpp_whitespace(node_text(first, source));
15120 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
15121 return None;
15122 }
15123 let mut start = first.end_byte();
15130 let mut after_first = false;
15131 let mut cursor = node.walk();
15132 for child in node.named_children(&mut cursor) {
15133 if !after_first {
15134 if same_node(child, first) {
15135 after_first = true;
15136 }
15137 continue;
15138 }
15139 if matches!(child.kind(), "identifier" | "type_identifier")
15140 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
15141 {
15142 start = child.end_byte();
15143 } else {
15144 break;
15145 }
15146 }
15147 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
15156 let mut class_start = None;
15157 let mut template_start = None;
15158 let mut stack = vec![node];
15159 while let Some(current) = stack.pop() {
15160 if current.start_byte() >= prefix_end {
15161 continue;
15162 }
15163 if matches!(
15164 current.kind(),
15165 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
15166 ) {
15167 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
15168 "class" | "struct" | "union" | "enum" => {
15169 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
15170 seen.min(current.start_byte())
15171 }));
15172 }
15173 "template" => {
15174 template_start =
15175 Some(template_start.map_or(current.start_byte(), |seen: usize| {
15176 seen.min(current.start_byte())
15177 }));
15178 }
15179 _ => {}
15180 }
15181 }
15182 let mut cursor = current.walk();
15183 stack.extend(current.children(&mut cursor));
15184 }
15185 if preserved_callable.is_some_and(|callable| {
15186 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
15187 }) {
15188 return None;
15189 }
15190 if let Some(class_start) = class_start {
15191 start = template_start
15192 .filter(|template_start| *template_start < class_start)
15193 .unwrap_or(class_start);
15194 }
15195 Some((start, class_start))
15196}
15197
15198fn cpp_sentinel_macro_class_region<'tree>(
15204 node: Node<'tree>,
15205 source: &str,
15206) -> Option<(usize, usize, usize, usize, usize, usize)> {
15207 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
15208 return None;
15209 };
15210 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
15211 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
15212 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
15213 if class_start >= body_open_start {
15214 return None;
15215 }
15216 let sibling_close = {
15217 let mut sibling = node.next_named_sibling();
15218 let mut found = None;
15219 while let Some(current) = sibling {
15220 let next = current.next_named_sibling();
15221 if cpp_is_stray_close_brace(current, source)
15222 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
15223 {
15224 let semicolon = next.expect("checked above");
15225 found = Some((
15226 current.start_byte(),
15227 semicolon.end_byte(),
15228 semicolon.end_position().row + 1,
15229 ));
15230 break;
15231 }
15232 sibling = next;
15233 }
15234 found
15235 };
15236 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
15249 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
15250 return false;
15251 };
15252 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
15253 let reparsed_ancestry = ParentIndex::new(tree.root_node());
15255 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
15256 tree.root_node(),
15257 template_node,
15258 source,
15259 &reparsed_ancestry,
15260 ) else {
15261 return false;
15262 };
15263 let body = reparsed_class.body;
15264 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
15265 });
15266 let (class_close_start, class_close_end, class_close_line) =
15267 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
15268 (class_close_start, class_close_end, class_close_line)
15269 } else {
15270 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
15277 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
15278 let reparsed_ancestry = ParentIndex::new(tree.root_node());
15280 let reparsed_class = cpp_sentinel_reparsed_class(
15281 tree.root_node(),
15282 template_node,
15283 source,
15284 &reparsed_ancestry,
15285 )?;
15286 let body = reparsed_class.body;
15287 let class_close_end = body.end_byte();
15288 let class_close_start = class_close_end.checked_sub(1)?;
15289 let class_close_line = body.end_position().row + 1;
15290 (class_close_start, class_close_end, class_close_line)
15291 };
15292 if class_close_start <= class_start {
15293 return None;
15294 }
15295
15296 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
15300 let class_root = tree.root_node();
15301 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
15302 let reparsed_ancestry = ParentIndex::new(class_root);
15304 let reparsed_class =
15305 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
15306 let body = reparsed_class.body;
15307 if body.start_byte() != body_open_start {
15311 return None;
15312 }
15313 let body_start = body.start_byte().checked_add(1)?;
15314 (body_start < class_close_start).then_some((
15315 reparse_start,
15316 class_start,
15317 body_start,
15318 class_close_start,
15319 class_close_end,
15320 class_close_line,
15321 ))
15322}
15323
15324fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
15329 let mut stack = vec![node];
15330 while let Some(current) = stack.pop() {
15331 if current.start_byte() == class_start
15332 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
15333 {
15334 let mut sibling = current.next_sibling();
15335 while let Some(candidate) = sibling {
15336 if candidate.kind() == "{" {
15337 return Some(candidate.start_byte());
15338 }
15339 sibling = candidate.next_sibling();
15340 }
15341 }
15342 let mut cursor = current.walk();
15343 stack.extend(current.children(&mut cursor));
15344 }
15345 None
15346}
15347
15348fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
15359 node.next_named_sibling()
15360 .filter(|sibling| sibling.kind() == "compound_statement")
15361}
15362
15363fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
15364 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
15365 let mut end = if class_start.is_some() {
15366 cpp_macro_prefixed_class_end(source, start)?
15367 } else {
15368 node.end_byte()
15369 };
15370 if class_start.is_none()
15371 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
15372 {
15373 end = end.max(namespace_end);
15374 }
15375 let mut sibling = node.next_named_sibling();
15376 while let Some(current) = sibling {
15377 if !cpp_is_stray_semicolon(current, source) {
15378 break;
15379 }
15380 end = current.end_byte();
15381 sibling = current.next_named_sibling();
15382 }
15383 (start < end).then_some((start, end))
15384}
15385
15386fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
15397 let mut sibling = node.next_sibling();
15398 let keyword = loop {
15399 let candidate = sibling?;
15400 sibling = candidate.next_sibling();
15401 if candidate.kind() != "comment" {
15402 break candidate;
15403 }
15404 };
15405 if keyword.kind() != "namespace" {
15406 return None;
15407 }
15408 let name = loop {
15409 let candidate = sibling?;
15410 sibling = candidate.next_sibling();
15411 if candidate.kind() != "comment" {
15412 break candidate;
15413 }
15414 };
15415 if cpp_namespace_name_components(name, source).is_empty() {
15416 return None;
15417 }
15418 let open = loop {
15419 let candidate = sibling?;
15420 sibling = candidate.next_sibling();
15421 if candidate.kind() != "comment" {
15422 break candidate;
15423 }
15424 };
15425 if open.kind() != "{" {
15426 return None;
15427 }
15428
15429 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
15430 let root = tree.root_node();
15431 let mut cursor = root.walk();
15432 let namespace = root
15433 .named_children(&mut cursor)
15434 .find(|candidate| candidate.kind() != "comment")?;
15435 (namespace.kind() == "namespace_definition"
15436 && namespace.start_byte() == keyword.start_byte()
15437 && namespace.child_by_field_name("body").is_some())
15438 .then_some(namespace.end_byte())
15439}
15440
15441fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
15447 let tree = cpp_reparse_region_items(source, start, source.len())?;
15448 let root = tree.root_node();
15449 let mut cursor = root.walk();
15450 for item in root.named_children(&mut cursor) {
15451 if item.end_byte() <= start || item.kind() == "comment" {
15452 continue;
15453 }
15454 let mut stack = vec![item];
15455 while let Some(current) = stack.pop() {
15456 if matches!(
15457 current.kind(),
15458 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
15459 ) && cpp_body_node(current).is_some()
15460 {
15461 return Some(current.end_byte());
15462 }
15463 let mut cursor = current.walk();
15464 stack.extend(current.named_children(&mut cursor));
15465 }
15466 return None;
15470 }
15471 None
15472}
15473
15474fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
15477 node.kind() == "expression_statement"
15478 && node.named_child_count() == 0
15479 && node_text(node, source).trim() == ";"
15480}
15481
15482#[derive(Clone, Copy)]
15492pub(crate) struct RecoveredPyObjectHeadField<'tree> {
15493 pub(crate) type_node: Node<'tree>,
15494 pub(crate) name: Node<'tree>,
15495 pub(crate) declarator: Node<'tree>,
15496}
15497
15498impl RecoveredPyObjectHeadField<'_> {
15499 pub(crate) fn pointer_depth(self) -> i32 {
15500 let mut depth = 0;
15501 let mut current = self.declarator;
15502 while current != self.name {
15503 debug_assert_eq!(current.kind(), "pointer_declarator");
15504 depth += 1;
15505 current = current
15506 .child_by_field_name("declarator")
15507 .expect("recovered PyObject field pointer has an inner declarator");
15508 }
15509 depth
15510 }
15511}
15512
15513pub(crate) fn recovered_pyobject_head_field<'tree>(
15514 node: Node<'tree>,
15515 source: &str,
15516) -> Option<RecoveredPyObjectHeadField<'tree>> {
15517 if node.kind() != "field_declaration" {
15518 return None;
15519 }
15520 let type_node = node.child_by_field_name("type")?;
15521 if type_node.kind() != "type_identifier"
15522 || node_text(type_node, source).trim() != "PyObject_HEAD"
15523 {
15524 return None;
15525 }
15526 let pseudo_declarator = node.child_by_field_name("declarator")?;
15527 let mut cursor = node.walk();
15528 let errors = node
15529 .named_children(&mut cursor)
15530 .filter(|child| child.kind() == "ERROR")
15531 .collect::<Vec<_>>();
15532 let [error] = errors.as_slice() else {
15533 return None;
15534 };
15535 if error.named_child_count() != 1 {
15536 return None;
15537 }
15538 let error_child = error.named_child(0)?;
15539 if pseudo_declarator.kind() == "field_identifier"
15540 && error.start_byte() >= pseudo_declarator.end_byte()
15541 && error_child.kind() == "identifier"
15542 {
15543 return Some(RecoveredPyObjectHeadField {
15544 type_node: pseudo_declarator,
15545 name: error_child,
15546 declarator: error_child,
15547 });
15548 }
15549 if pseudo_declarator.kind() != "pointer_declarator"
15550 || error.end_byte() > pseudo_declarator.start_byte()
15551 || error_child.kind() != "identifier"
15552 {
15553 return None;
15554 }
15555 let mut name = pseudo_declarator;
15556 while name.kind() == "pointer_declarator" {
15557 name = name.child_by_field_name("declarator")?;
15558 }
15559 (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
15560 type_node: error_child,
15561 name,
15562 declarator: pseudo_declarator,
15563 })
15564}
15565
15566fn recovered_macro_qualified_field_declarators<'tree>(
15575 node: Node<'tree>,
15576 source: &str,
15577) -> Option<Vec<Node<'tree>>> {
15578 if node.kind() != "field_declaration" {
15579 return None;
15580 }
15581 let macro_type = node.child_by_field_name("type")?;
15582 if macro_type.kind() != "type_identifier"
15583 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15584 {
15585 return None;
15586 }
15587 let pseudo_declarator = node.child_by_field_name("declarator")?;
15588 if pseudo_declarator.kind() != "field_identifier" {
15589 return None;
15590 }
15591 let mut cursor = node.walk();
15592 let clause = node
15593 .named_children(&mut cursor)
15594 .find(|child| child.kind() == "bitfield_clause")?;
15595 if !(0..clause.named_child_count()).any(|index| {
15596 clause
15597 .named_child(index)
15598 .is_some_and(|child| child.kind() == "ERROR")
15599 }) {
15600 return None;
15601 }
15602 let mut recovered = Vec::new();
15603 let mut stack = vec![clause];
15604 while let Some(current) = stack.pop() {
15605 if current.kind() == "assignment_expression"
15606 && let Some(left) = current.child_by_field_name("left")
15607 && extract_variable_name(left, source).is_some()
15608 {
15609 recovered.push(left);
15610 break;
15611 }
15612 let mut cursor = current.walk();
15613 stack.extend(current.named_children(&mut cursor));
15614 }
15615 if recovered.is_empty() {
15616 return None;
15617 }
15618 let mut cursor = node.walk();
15619 recovered.extend(
15620 node.children_by_field_name("declarator", &mut cursor)
15621 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
15622 );
15623 Some(recovered)
15624}
15625
15626fn recovered_macro_qualified_constructor_call<'tree>(
15632 node: Node<'tree>,
15633 class_name: &str,
15634 source: &str,
15635) -> Option<Node<'tree>> {
15636 if node.kind() != "field_declaration" {
15637 return None;
15638 }
15639 let macro_type = node.child_by_field_name("type")?;
15640 if macro_type.kind() != "type_identifier"
15641 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15642 {
15643 return None;
15644 }
15645 let mut cursor = node.walk();
15646 let bitfield = node
15647 .named_children(&mut cursor)
15648 .find(|child| child.kind() == "bitfield_clause")?;
15649 let error = bitfield
15650 .named_child(0)
15651 .filter(|child| child.kind() == "ERROR")?;
15652 let mut stack = vec![error];
15653 while let Some(current) = stack.pop() {
15654 if current.kind() == "call_expression"
15655 && current
15656 .child_by_field_name("function")
15657 .is_some_and(|function| node_text(function, source) == class_name)
15658 && current
15659 .child_by_field_name("arguments")
15660 .is_some_and(|arguments| arguments.kind() == "argument_list")
15661 {
15662 return Some(current);
15663 }
15664 let mut cursor = current.walk();
15665 stack.extend(current.named_children(&mut cursor));
15666 }
15667 None
15668}
15669
15670fn recovered_macro_qualified_function_call<'tree>(
15678 node: Node<'tree>,
15679 source: &str,
15680) -> Option<Node<'tree>> {
15681 if node.kind() != "field_declaration" {
15682 return None;
15683 }
15684 let macro_type = node.child_by_field_name("type")?;
15685 if macro_type.kind() != "type_identifier"
15686 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15687 {
15688 return None;
15689 }
15690 let declarator = node.child_by_field_name("declarator")?;
15691 if declarator.kind() != "field_identifier" {
15692 return None;
15693 }
15694 let mut cursor = node.walk();
15695 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15696 if !named.iter().any(|child| {
15697 child.kind() == "storage_class_specifier"
15698 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
15699 }) {
15700 return None;
15701 }
15702 let bitfield = named
15703 .iter()
15704 .find(|child| child.kind() == "bitfield_clause")?;
15705 let mut bitfield_cursor = bitfield.walk();
15706 let payload = bitfield
15707 .named_children(&mut bitfield_cursor)
15708 .collect::<Vec<_>>();
15709 let [displaced_error, call] = payload.as_slice() else {
15710 return None;
15711 };
15712 if displaced_error.kind() != "ERROR"
15713 || displaced_error.named_child_count() != 1
15714 || displaced_error
15715 .named_child(0)
15716 .is_none_or(|child| child.kind() != "identifier")
15717 || call.kind() != "call_expression"
15718 || call
15719 .child_by_field_name("function")
15720 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
15721 || call
15722 .child_by_field_name("arguments")
15723 .is_none_or(|arguments| arguments.kind() != "argument_list")
15724 {
15725 return None;
15726 }
15727 Some(*call)
15728}
15729
15730fn recovered_macro_qualified_function_parameters(
15731 arguments: Node<'_>,
15732 source: &str,
15733) -> Option<(String, Vec<String>)> {
15734 if arguments.kind() != "argument_list" {
15735 return None;
15736 }
15737 let mut cursor = arguments.walk();
15738 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
15739 if named.is_empty() {
15740 return Some(("()".to_string(), Vec::new()));
15741 }
15742 let mut types = Vec::new();
15743 let mut labels = Vec::new();
15744 let mut index = 0;
15745 while index < named.len() {
15746 let parameter_type = named[index];
15747 let parameter_name = named.get(index + 1).copied()?;
15748 if !matches!(
15749 parameter_type.kind(),
15750 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
15751 ) || parameter_name.kind() != "ERROR"
15752 || parameter_name.named_child_count() != 1
15753 || parameter_name
15754 .named_child(0)
15755 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
15756 {
15757 return None;
15758 }
15759 let parameter_name = parameter_name.named_child(0)?;
15760 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
15761 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
15762 index += 2;
15763 }
15764 Some((format!("({})", types.join(", ")), labels))
15765}
15766
15767pub fn recovered_macro_return_type_node<'tree>(
15779 node: Node<'tree>,
15780 source: &str,
15781) -> Option<Node<'tree>> {
15782 if node.kind() != "field_declaration" {
15783 return None;
15784 }
15785 let macro_type = node.child_by_field_name("type")?;
15786 if macro_type.kind() != "type_identifier"
15787 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15788 {
15789 return None;
15790 }
15791 let declarator = node.child_by_field_name("declarator")?;
15792 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
15793 return None;
15794 }
15795 let mut has_missing_semicolon = false;
15796 let mut has_real_semicolon = false;
15797 for child in children_iter(node) {
15798 if child.kind() != ";" {
15799 continue;
15800 }
15801 if child.is_missing() {
15802 has_missing_semicolon = true;
15803 } else {
15804 has_real_semicolon = true;
15805 }
15806 }
15807 if !has_missing_semicolon || has_real_semicolon {
15808 return None;
15809 }
15810 let mut next = node.next_named_sibling();
15811 while next.is_some_and(|sibling| sibling.kind() == "comment") {
15812 next = next.and_then(|sibling| sibling.next_named_sibling());
15813 }
15814 let next = next?;
15815 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
15816 return None;
15817 }
15818 let function_declarator = next.child_by_field_name("declarator")?;
15819 extract_function_declarator(function_declarator).map(|_| declarator)
15820}
15821
15822pub(crate) fn cpp_active_template_type_parameter<'tree>(
15829 node: Node<'tree>,
15830 name: &str,
15831 source: &str,
15832 ancestry: &ParentIndex<'tree>,
15833) -> bool {
15834 let mut ancestor = ancestry.parent(node);
15835 while let Some(current) = ancestor {
15836 if current.kind() == "template_declaration"
15837 && let Some(parameters) = current.child_by_field_name("parameters")
15838 {
15839 let mut cursor = parameters.walk();
15840 if parameters.named_children(&mut cursor).any(|parameter| {
15841 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
15842 && cpp_template_parameter_name(parameter, source)
15843 .is_some_and(|parameter_name| parameter_name == name)
15844 }) {
15845 return true;
15846 }
15847 }
15848 ancestor = ancestry.parent(current);
15849 }
15850 false
15851}
15852
15853fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
15859 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
15860}
15861
15862fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
15863 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
15864 return None;
15865 }
15866 let semicolon = node.next_sibling()?;
15867 if semicolon.kind() != ";" || semicolon.is_missing() {
15868 return None;
15869 }
15870 let row = node.start_position().row;
15871 let mut start = node.start_byte();
15872 let mut sibling = node.prev_sibling();
15873 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
15874 if previous.kind() == ";" {
15875 break;
15876 }
15877 start = previous.start_byte();
15878 sibling = previous.prev_sibling();
15879 }
15880 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
15881}
15882
15883struct PrototypeMacroCandidate {
15887 run_start: usize,
15889 identifier_start: usize,
15892 inner_open_start: usize,
15896 inner_close_end: usize,
15898 outer_close_end: usize,
15900 semicolon_end: usize,
15903}
15904
15905impl PrototypeMacroCandidate {
15906 fn ranges(&self) -> [(usize, usize); 3] {
15912 [
15913 (self.run_start, self.identifier_start),
15914 (self.inner_open_start, self.inner_close_end),
15915 (self.outer_close_end, self.semicolon_end),
15916 ]
15917 }
15918}
15919
15920fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
15922 node.child(node.child_count().checked_sub(1)?)
15923 .filter(|child| child.kind() == ";" && !child.is_missing())
15924}
15925
15926fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
15931 matches!(
15932 node.kind(),
15933 "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
15934 ) && matches!(
15935 normalize_cpp_whitespace(node_text(node, source)).as_str(),
15936 "_" | "__P" | "OF" | "PROTO"
15937 )
15938}
15939
15940fn cpp_prototype_macro_qualified_parts<'tree>(
15944 node: Node<'tree>,
15945 source: &str,
15946) -> Option<(Node<'tree>, Node<'tree>)> {
15947 if node.kind() != "qualified_identifier" {
15948 return None;
15949 }
15950 let declared_name = node
15951 .child_by_field_name("scope")
15952 .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
15953 let macro_name = macro_decorated_unqualified_name(node)?;
15954 cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
15955}
15956
15957fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
15963 if arguments.kind() != "argument_list"
15964 || arguments.named_child_count() != 1
15965 || arguments.child_count() != 3
15966 || arguments
15967 .child(0)
15968 .is_none_or(|open| open.kind() != "(" || open.is_missing())
15969 || arguments
15970 .child(2)
15971 .is_none_or(|close| close.kind() != ")" || close.is_missing())
15972 {
15973 return None;
15974 }
15975 let inner = arguments.named_child(0)?;
15976 let close_index = match inner.kind() {
15977 "parenthesized_expression" => inner.child_count().checked_sub(1)?,
15978 "cast_expression" => inner.child_count().checked_sub(2)?,
15981 _ => return None,
15982 };
15983 (inner
15984 .child(0)
15985 .is_some_and(|open| open.kind() == "(" && !open.is_missing())
15986 && inner
15987 .child(close_index)
15988 .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
15989 .then_some(inner)
15990}
15991
15992fn cpp_prototype_macro_candidate_from_init_declaration(
15993 declaration: Node<'_>,
15994 source: &str,
15995) -> Option<PrototypeMacroCandidate> {
15996 let init = declaration
15997 .child_by_field_name("declarator")
15998 .filter(|declarator| declarator.kind() == "init_declarator")?;
15999 let malformed_declarator = init.child_by_field_name("declarator")?;
16000 let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
16001 cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
16002 } else {
16003 if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
16004 return None;
16005 }
16006 let declared_name_error = init
16007 .prev_named_sibling()
16008 .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
16009 let declared_name = declared_name_error
16010 .named_child(0)
16011 .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
16012 (declared_name, malformed_declarator)
16013 };
16014 let arguments = init
16015 .child_by_field_name("value")
16016 .filter(|value| value.kind() == "argument_list")?;
16017 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
16018 let semicolon = cpp_direct_semicolon(declaration)?;
16019 let return_type = declaration.child_by_field_name("type")?;
16020 if return_type.end_byte() > declared_name.start_byte()
16021 || declared_name.end_byte() > macro_name.start_byte()
16022 || macro_name.end_byte() > arguments.start_byte()
16023 || arguments.end_byte() > semicolon.start_byte()
16024 {
16025 return None;
16026 }
16027 Some(PrototypeMacroCandidate {
16028 run_start: declaration.start_byte(),
16029 identifier_start: macro_name.start_byte(),
16030 inner_open_start: inner.start_byte(),
16031 inner_close_end: inner.end_byte(),
16032 outer_close_end: arguments.end_byte(),
16033 semicolon_end: semicolon.end_byte(),
16034 })
16035}
16036
16037fn cpp_prototype_macro_candidate_from_qualified_declaration(
16038 declaration: Node<'_>,
16039 source: &str,
16040) -> Option<PrototypeMacroCandidate> {
16041 let qualified = declaration
16042 .child_by_field_name("declarator")
16043 .filter(|declarator| declarator.kind() == "qualified_identifier")?;
16044 let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
16045 let open_error = qualified
16046 .next_named_sibling()
16047 .filter(|next| next.kind() == "ERROR")?;
16048 let close_error = last_named_child(declaration)
16049 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
16050 if open_error.child_count() < 3
16051 || open_error
16052 .child(0)
16053 .is_none_or(|open| open.kind() != "(" || open.is_missing())
16054 || open_error
16055 .child(1)
16056 .is_none_or(|open| open.kind() != "(" || open.is_missing())
16057 || close_error.child_count() != 2
16058 || close_error
16059 .child(0)
16060 .is_none_or(|close| close.kind() != ")" || close.is_missing())
16061 || close_error
16062 .child(1)
16063 .is_none_or(|close| close.kind() != ")" || close.is_missing())
16064 {
16065 return None;
16066 }
16067 let inner_open = open_error.child(1)?;
16068 let inner_close = close_error.child(0)?;
16069 let outer_close = close_error.child(1)?;
16070 let semicolon = cpp_direct_semicolon(declaration)?;
16071 let return_type = declaration.child_by_field_name("type")?;
16072 if return_type.end_byte() > qualified.start_byte()
16073 || macro_name.end_byte() > open_error.start_byte()
16074 || inner_open.start_byte() > inner_close.end_byte()
16075 || inner_close.end_byte() > outer_close.start_byte()
16076 || outer_close.end_byte() > semicolon.start_byte()
16077 {
16078 return None;
16079 }
16080 Some(PrototypeMacroCandidate {
16081 run_start: declaration.start_byte(),
16082 identifier_start: macro_name.start_byte(),
16083 inner_open_start: inner_open.start_byte(),
16084 inner_close_end: inner_close.end_byte(),
16085 outer_close_end: outer_close.end_byte(),
16086 semicolon_end: semicolon.end_byte(),
16087 })
16088}
16089
16090fn cpp_prototype_macro_candidate_from_pointer_expression(
16091 statement: Node<'_>,
16092 source: &str,
16093) -> Option<PrototypeMacroCandidate> {
16094 if statement.kind() != "expression_statement"
16095 || statement.named_child_count() != 1
16096 || !statement.has_error()
16097 {
16098 return None;
16099 }
16100 let expansion = statement
16101 .named_child(0)
16102 .filter(|child| child.kind() == "parameter_pack_expansion")?;
16103 let binary = expansion
16104 .child_by_field_name("pattern")
16105 .filter(|pattern| pattern.kind() == "binary_expression")?;
16106 if binary.child_count() != 3
16107 || binary
16108 .child(1)
16109 .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
16110 || expansion
16111 .child(expansion.child_count().checked_sub(1)?)
16112 .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
16113 {
16114 return None;
16115 }
16116 let return_type = binary.child_by_field_name("left")?;
16117 let call = binary
16118 .child_by_field_name("right")
16119 .filter(|right| right.kind() == "call_expression")?;
16120 let qualified = call.child_by_field_name("function")?;
16121 let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
16122 let arguments = call
16123 .child_by_field_name("arguments")
16124 .filter(|arguments| arguments.kind() == "argument_list")?;
16125 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
16126 let semicolon = cpp_direct_semicolon(statement)?;
16127 if return_type.end_byte() > declared_name.start_byte()
16128 || macro_name.end_byte() > arguments.start_byte()
16129 || arguments.end_byte() > semicolon.start_byte()
16130 {
16131 return None;
16132 }
16133 Some(PrototypeMacroCandidate {
16134 run_start: statement.start_byte(),
16135 identifier_start: macro_name.start_byte(),
16136 inner_open_start: inner.start_byte(),
16137 inner_close_end: inner.end_byte(),
16138 outer_close_end: arguments.end_byte(),
16139 semicolon_end: semicolon.end_byte(),
16140 })
16141}
16142
16143fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
16148 let candidate = match node.kind() {
16149 "declaration" if node.has_error() => {
16150 cpp_prototype_macro_candidate_from_init_declaration(node, source)
16151 .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
16152 }
16153 "expression_statement" => {
16154 cpp_prototype_macro_candidate_from_pointer_expression(node, source)
16155 }
16156 _ => None,
16157 };
16158 candidate.into_iter().collect()
16159}
16160
16161fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
16162 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
16163 return false;
16164 }
16165 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
16166 return false;
16167 }
16168 let Some(declarator) = (if node.kind() == "function_definition" {
16169 node.child_by_field_name("declarator")
16170 .and_then(extract_function_declarator)
16171 } else {
16172 node.named_child(0)
16173 .filter(|child| child.kind() == "function_declarator")
16174 }) else {
16175 return false;
16176 };
16177 let Some(name) = cpp_function_declarator_name_node(declarator) else {
16178 return false;
16179 };
16180 declarator.start_byte() == node.start_byte()
16181 && name.kind() == "identifier"
16182 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
16183}
16184
16185fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
16198 let region = cpp_reparse_region_items(source, start, end);
16199
16200 #[cfg(debug_assertions)]
16201 assert_eq!(
16202 region.as_ref().map(cpp_tree_shape),
16203 cpp_reparse_padded_class_body(source, start, end)
16204 .as_ref()
16205 .map(cpp_tree_shape),
16206 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
16207 prefix produces"
16208 );
16209
16210 region
16211}
16212
16213#[cfg(any(debug_assertions, test))]
16217fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
16218 if start >= end {
16219 return None;
16223 }
16224 let bytes = source.as_bytes();
16225 let prefix = bytes.get(..start)?;
16226 let interior = bytes.get(start..end)?;
16227 let mut padded = Vec::with_capacity(end);
16228 padded.extend(
16229 prefix
16230 .iter()
16231 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
16232 );
16233 padded.extend_from_slice(interior);
16234 let padded = String::from_utf8(padded).ok()?;
16235 let mut parser = Parser::new();
16236 parser
16237 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16238 .ok()?;
16239 parser.parse(&padded, None)
16240}
16241
16242#[cfg(any(debug_assertions, test))]
16246fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
16247 let mut shape = Vec::new();
16248 let mut cursor = tree.root_node().walk();
16249 let mut stack = vec![tree.root_node()];
16250 while let Some(node) = stack.pop() {
16251 shape.push((
16252 node.kind(),
16253 node.start_byte(),
16254 node.end_byte(),
16255 node.start_position().row,
16256 node.start_position().column,
16257 node.is_named(),
16258 node.is_missing(),
16259 ));
16260 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
16261 stack.extend(children.into_iter().rev());
16262 }
16263 shape
16264}
16265
16266fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
16287 let mut cursor = root.walk();
16288 let mut saw_item = false;
16289 for child in root.named_children(&mut cursor) {
16290 match child.kind() {
16291 "comment" => {}
16292 "function_definition" => {
16293 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
16294 return false;
16295 }
16296 saw_item = true;
16297 }
16298 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
16299 _ => return false,
16300 }
16301 }
16302 saw_item
16303}
16304
16305fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
16315 if node.kind() != "ERROR" {
16316 return false;
16317 }
16318 let mut stack = Vec::new();
16319 let mut saw_function_declarator = false;
16320 let mut cursor = node.walk();
16321 for child in node.named_children(&mut cursor) {
16322 stack.push(child);
16323 }
16324 while let Some(current) = stack.pop() {
16325 match current.kind() {
16326 "ERROR" => {
16330 let mut cursor = current.walk();
16331 stack.extend(current.named_children(&mut cursor));
16332 }
16333 "function_declarator" => saw_function_declarator = true,
16334 _ => return false,
16335 }
16336 }
16337 saw_function_declarator
16338}
16339
16340fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
16341 if node.kind() != "ERROR" {
16342 return false;
16343 }
16344 let mut cursor = node.walk();
16345 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16346 let [explicit, constructor_error, destructor] = named.as_slice() else {
16347 return false;
16348 };
16349 let Some(constructor) = constructor_error.named_child(0) else {
16350 return false;
16351 };
16352 let Some(constructor_name) =
16353 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
16354 else {
16355 return false;
16356 };
16357 let Some(destructor_name) =
16358 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
16359 else {
16360 return false;
16361 };
16362 let Some(destroyed_type) = destructor_name.named_child(0) else {
16363 return false;
16364 };
16365 explicit.kind() == "explicit_function_specifier"
16366 && constructor_error.kind() == "ERROR"
16367 && constructor_error.named_child_count() == 1
16368 && constructor.kind() == "function_declarator"
16369 && constructor_name.kind() == "identifier"
16370 && destructor.kind() == "function_declarator"
16371 && destructor_name.kind() == "destructor_name"
16372 && destroyed_type.kind() == "identifier"
16373 && node_text(constructor_name, source) == node_text(destroyed_type, source)
16374}
16375
16376fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
16377 if node.kind() != "compound_statement" {
16378 return false;
16379 }
16380 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
16381 return false;
16382 };
16383 if prefix.kind() == "labeled_statement"
16384 && prefix.named_child(0).is_some_and(|label| {
16385 matches!(
16386 node_text(label, source).trim(),
16387 "public" | "private" | "protected"
16388 )
16389 })
16390 {
16391 return prefix.named_children(&mut prefix.walk()).any(|child| {
16392 child.kind() == "declaration"
16393 && child.has_error()
16394 && child
16395 .named_children(&mut child.walk())
16396 .any(cpp_reparsed_member_error_is_indexable)
16397 });
16398 }
16399 prefix.kind() == "declaration"
16404 && prefix.has_error()
16405 && prefix
16406 .named_children(&mut prefix.walk())
16407 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
16408}
16409
16410fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
16411 if !cpp_reparsed_member_error_is_indexable(node) {
16412 return false;
16413 }
16414 let Some(preproc) = node.next_named_sibling() else {
16415 return false;
16416 };
16417 preproc.kind() == "preproc_if"
16418 && preproc.has_error()
16419 && preproc
16420 .named_children(&mut preproc.walk())
16421 .any(|child| child.kind() == "expression_statement" && child.has_error())
16422 && preproc
16423 .next_named_sibling()
16424 .is_some_and(|body| body.kind() == "compound_statement")
16425}
16426
16427fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
16432 if node.kind() != "function_definition" {
16433 return None;
16434 }
16435 let body = node.child_by_field_name("body")?;
16436 if body.kind() != "compound_statement" {
16437 return None;
16438 }
16439 let open = body.child(0)?;
16440 let close = body.child(body.child_count().checked_sub(1)?)?;
16441 if open.kind() != "{"
16442 || open.is_missing()
16443 || close.kind() != "}"
16444 || close.is_missing()
16445 || close.end_byte() != body.end_byte()
16446 || body.end_byte() != node.end_byte()
16447 {
16448 return None;
16449 }
16450 Some(body)
16451}
16452
16453fn cpp_reparsed_member_function_errors_are_in_body(
16454 node: Node<'_>,
16455 body: Node<'_>,
16456 source: &str,
16457) -> bool {
16458 let mut cursor = node.walk();
16459 node.children(&mut cursor).all(|child| {
16460 same_node(child, body)
16461 || cpp_reparsed_member_attribute_error(child, source)
16462 || cpp_reparsed_member_signature_identifier_errors(child)
16463 || (!child.has_error() && !child.is_error() && !child.is_missing())
16464 })
16465}
16466
16467fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
16475 if !node.has_error() && !node.is_error() && !node.is_missing() {
16476 return false;
16477 }
16478 let mut stack = vec![node];
16479 let mut saw_error = false;
16480 while let Some(current) = stack.pop() {
16481 if current.is_missing() {
16482 return false;
16483 }
16484 if current.kind() == "ERROR" {
16485 saw_error = true;
16486 let mut cursor = current.walk();
16487 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
16488 if children
16489 .iter()
16490 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
16491 {
16492 return false;
16493 }
16494 stack.extend(children);
16495 continue;
16496 }
16497 let mut cursor = current.walk();
16498 stack.extend(current.children(&mut cursor));
16499 }
16500 saw_error
16501}
16502
16503fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
16504 node.kind() == "ERROR"
16505 && node.named_child_count() == 1
16506 && node.named_child(0).is_some_and(|attribute| {
16507 attribute.kind() == "identifier"
16508 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
16509 })
16510}
16511
16512fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
16518 let Some(body) = cpp_reparsed_member_function_body(node) else {
16519 return false;
16520 };
16521 let mut cursor = node.walk();
16522 let named = node
16523 .named_children(&mut cursor)
16524 .filter(|child| child.kind() != "comment")
16525 .collect::<Vec<_>>();
16526 let [type_node, error, attribute, body_node] = named.as_slice() else {
16527 return false;
16528 };
16529 if !same_node(*body_node, body)
16530 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16531 || attribute.kind() != "identifier"
16532 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16533 || error.kind() != "ERROR"
16534 || error.named_child_count() != 1
16535 {
16536 return false;
16537 }
16538 error
16539 .named_child(0)
16540 .is_some_and(cpp_reparsed_attribute_callable_declarator)
16541}
16542
16543fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
16544 cpp_structured_type_path(node, source).is_some()
16545 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
16546}
16547
16548fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16549 let Some(body) = cpp_reparsed_member_function_body(node) else {
16550 return false;
16551 };
16552 let mut cursor = node.walk();
16553 let named = node
16554 .named_children(&mut cursor)
16555 .filter(|child| child.kind() != "comment")
16556 .collect::<Vec<_>>();
16557 let [friend, return_error, declarator, body_node] = named.as_slice() else {
16558 return false;
16559 };
16560 let Some(return_type) = return_error.named_child(0) else {
16561 return false;
16562 };
16563 same_node(*body_node, body)
16564 && friend.kind() == "type_identifier"
16565 && node_text(*friend, source) == "friend"
16566 && return_error.kind() == "ERROR"
16567 && return_error.named_child_count() == 1
16568 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16569 && extract_function_declarator(*declarator)
16570 .and_then(cpp_function_declarator_name_node)
16571 .is_some()
16572}
16573
16574fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16575 let Some(body) = cpp_reparsed_member_function_body(node) else {
16576 return false;
16577 };
16578 let mut cursor = node.walk();
16579 let named = node
16580 .named_children(&mut cursor)
16581 .filter(|child| child.kind() != "comment")
16582 .collect::<Vec<_>>();
16583 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
16584 return false;
16585 };
16586 let Some(return_type) = return_error.named_child(0) else {
16587 return false;
16588 };
16589 same_node(*body_node, body)
16590 && prefix
16591 .iter()
16592 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
16593 && attribute.kind() == "type_identifier"
16594 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16595 && return_error.kind() == "ERROR"
16596 && return_error.named_child_count() == 1
16597 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16598 && extract_function_declarator(*declarator)
16599 .and_then(cpp_function_declarator_name_node)
16600 .is_some()
16601}
16602
16603fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16609 let Some(body) = cpp_reparsed_member_function_body(node) else {
16610 return false;
16611 };
16612 let mut cursor = node.walk();
16613 let named = node
16614 .named_children(&mut cursor)
16615 .filter(|child| child.kind() != "comment")
16616 .collect::<Vec<_>>();
16617 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
16618 return false;
16619 };
16620 let Some(template_name) = template_type.child_by_field_name("name") else {
16621 return false;
16622 };
16623 let Some(arguments) = template_type.child_by_field_name("arguments") else {
16624 return false;
16625 };
16626 let Some(return_type) = return_error.named_child(0) else {
16627 return false;
16628 };
16629 let mut cursor = template_type.walk();
16630 let template_errors = template_type
16631 .named_children(&mut cursor)
16632 .filter(|child| child.kind() == "ERROR")
16633 .collect::<Vec<_>>();
16634 let [comment_error] = template_errors.as_slice() else {
16635 return false;
16636 };
16637 let mut cursor = comment_error.walk();
16638 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
16639 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
16640 return false;
16641 };
16642 same_node(*body_node, body)
16643 && template_type.kind() == "template_type"
16644 && template_name.kind() == "type_identifier"
16645 && matches!(
16646 node_text(template_name, source).trim(),
16647 "public" | "private" | "protected"
16648 )
16649 && arguments.kind() == "template_argument_list"
16650 && arguments.named_child_count() > 0
16651 && !arguments.has_error()
16652 && !colon.is_named()
16653 && colon.kind() == ":"
16654 && comments.iter().all(|child| child.kind() == "comment")
16655 && !template_keyword.is_named()
16656 && template_keyword.kind() == "template"
16657 && return_error.kind() == "ERROR"
16658 && return_error.named_child_count() == 1
16659 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16660 && extract_function_declarator(*declarator)
16661 .and_then(cpp_function_declarator_name_node)
16662 .is_some()
16663}
16664
16665fn cpp_reparsed_preprocessor_constructor<'tree>(
16671 node: Node<'tree>,
16672 class_name: &str,
16673 source: &str,
16674) -> Option<Node<'tree>> {
16675 if node.kind() != "labeled_statement" {
16676 return None;
16677 }
16678 let mut cursor = node.walk();
16679 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16680 let [label, directive_error, declaration] = named.as_slice() else {
16681 return None;
16682 };
16683 if label.kind() != "statement_identifier"
16684 || !matches!(
16685 node_text(*label, source),
16686 "public" | "private" | "protected"
16687 )
16688 || directive_error.kind() != "ERROR"
16689 || directive_error.child_count() != 1
16690 || directive_error
16691 .child(0)
16692 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
16693 || declaration.kind() != "declaration"
16694 || declaration.named_child_count() != 2
16695 {
16696 return None;
16697 }
16698 let apparent_type = declaration.child_by_field_name("type")?;
16699 if apparent_type.kind() != "type_identifier"
16700 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
16701 {
16702 return None;
16703 }
16704 let declarator = declaration.child_by_field_name("declarator")?;
16705 let function = extract_function_declarator(declarator)?;
16706 let name = cpp_function_declarator_name_node(function)?;
16707 (node_text(name, source) == class_name).then_some(*declaration)
16708}
16709
16710fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
16711 if extract_function_declarator(node)
16712 .and_then(cpp_function_declarator_name_node)
16713 .is_some()
16714 {
16715 return true;
16716 }
16717 node.kind() == "init_declarator"
16718 && node
16719 .child_by_field_name("declarator")
16720 .is_some_and(|declarator| declarator.kind() == "identifier")
16721 && node
16722 .child_by_field_name("value")
16723 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
16724}
16725
16726fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
16731 if node.kind() != "ERROR" || node.named_child_count() != 3 {
16732 return false;
16733 }
16734 let mut cursor = node.walk();
16735 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16736 let [type_node, function_declarator, attribute] = named.as_slice() else {
16737 return false;
16738 };
16739 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16740 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
16741 || attribute.kind() != "identifier"
16742 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16743 {
16744 return false;
16745 }
16746 let Some(preproc) =
16747 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16748 else {
16749 return false;
16750 };
16751 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
16752 .filter(|sibling| sibling.kind() == "compound_statement")
16753 else {
16754 return false;
16755 };
16756 let Some(open) = body.child(0) else {
16757 return false;
16758 };
16759 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
16760 return false;
16761 };
16762 let Some(condition) = preproc.child_by_field_name("condition") else {
16763 return false;
16764 };
16765 let mut cursor = preproc.walk();
16766 let payload = preproc
16767 .named_children(&mut cursor)
16768 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
16769 .collect::<Vec<_>>();
16770 let [requires_statement] = payload.as_slice() else {
16771 return false;
16772 };
16773 let requires_clause = requires_statement.named_child(0);
16774
16775 open.kind() == "{"
16776 && !open.is_missing()
16777 && close.kind() == "}"
16778 && !close.is_missing()
16779 && close.end_byte() == body.end_byte()
16780 && requires_statement.kind() == "expression_statement"
16781 && requires_statement.named_child_count() == 1
16782 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
16783}
16784
16785fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16786 let mut sibling = node.next_named_sibling();
16787 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16788 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
16789 }
16790 sibling
16791}
16792
16793fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16794 let mut sibling = node.prev_named_sibling();
16795 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16796 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
16797 }
16798 sibling
16799}
16800
16801fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
16802 let Some(preproc) =
16803 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16804 else {
16805 return false;
16806 };
16807 let Some(error) =
16808 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
16809 else {
16810 return false;
16811 };
16812 cpp_reparsed_attribute_requires_error(error, source)
16813}
16814
16815fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
16816 node: Node<'tree>,
16817 source: &str,
16818) -> Option<Node<'tree>> {
16819 if node.kind() != "ERROR" {
16820 return None;
16821 }
16822 let mut cursor = node.walk();
16823 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16824 let [parameter, macro_name, message] = named.as_slice() else {
16825 return None;
16826 };
16827 let parameter_name = parameter.named_child(0)?;
16828 (parameter.kind() == "type_parameter_declaration"
16829 && parameter_name.kind() == "type_identifier"
16830 && macro_name.kind() == "type_identifier"
16831 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16832 && message.kind() == "string_literal")
16833 .then_some(parameter_name)
16834}
16835
16836fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
16841 node: Node<'tree>,
16842 source: &str,
16843) -> Option<Node<'tree>> {
16844 if node.kind() != "ERROR" {
16845 return None;
16846 }
16847 let mut cursor = node.walk();
16848 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16849 let [parameter, macro_name, message, constraint] = named.as_slice() else {
16850 return None;
16851 };
16852 let parameter_name = parameter.named_child(0)?;
16853 let constraint_scope = constraint.child_by_field_name("scope")?;
16854 let constraint_template = constraint.child_by_field_name("name")?;
16855 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16856 let mut argument_cursor = constraint_arguments.walk();
16857 let constraint_types = constraint_arguments
16858 .named_children(&mut argument_cursor)
16859 .collect::<Vec<_>>();
16860 if parameter.kind() != "type_parameter_declaration"
16861 || parameter_name.kind() != "type_identifier"
16862 || macro_name.kind() != "type_identifier"
16863 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16864 || message.kind() != "string_literal"
16865 || constraint.kind() != "qualified_identifier"
16866 || constraint_scope.kind() != "namespace_identifier"
16867 || !matches!(
16868 constraint_template.kind(),
16869 "template_function" | "template_type"
16870 )
16871 || !matches!(constraint_types.as_slice(), [left, right]
16872 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16873 || constraint_arguments.has_error()
16874 {
16875 return None;
16876 }
16877 let parameter_text = node_text(parameter_name, source);
16878 let mut stack = constraint_types;
16879 while let Some(current) = stack.pop() {
16880 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
16881 return Some(parameter_name);
16882 }
16883 let mut cursor = current.walk();
16884 stack.extend(current.named_children(&mut cursor));
16885 }
16886 None
16887}
16888
16889fn cpp_reparsed_template_macro_companion_is_indexable(
16890 node: Node<'_>,
16891 parameter_name: Node<'_>,
16892 source: &str,
16893) -> bool {
16894 let Some(body) = cpp_reparsed_member_function_body(node) else {
16895 return false;
16896 };
16897 let mut cursor = node.walk();
16898 let named = node
16899 .named_children(&mut cursor)
16900 .filter(|child| child.kind() != "comment")
16901 .collect::<Vec<_>>();
16902 let [
16903 constraint,
16904 close_error,
16905 storage,
16906 return_error,
16907 declarator,
16908 body_node,
16909 ] = named.as_slice()
16910 else {
16911 return false;
16912 };
16913 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
16914 return false;
16915 };
16916 let Some(constraint_template) = constraint.child_by_field_name("name") else {
16917 return false;
16918 };
16919 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
16920 return false;
16921 };
16922 let Some(return_type) = return_error.named_child(0) else {
16923 return false;
16924 };
16925 let mut cursor = constraint_arguments.walk();
16926 let constraint_types = constraint_arguments
16927 .named_children(&mut cursor)
16928 .collect::<Vec<_>>();
16929 same_node(*body_node, body)
16930 && constraint.kind() == "qualified_identifier"
16931 && constraint_scope.kind() == "namespace_identifier"
16932 && constraint_template.kind() == "template_type"
16933 && matches!(constraint_types.as_slice(), [left, right]
16934 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16935 && !constraint_arguments.has_error()
16936 && close_error.kind() == "ERROR"
16937 && close_error.named_child_count() == 0
16938 && storage.kind() == "storage_class_specifier"
16939 && return_error.kind() == "ERROR"
16940 && return_error.named_child_count() == 1
16941 && return_type.kind() == "identifier"
16942 && node_text(return_type, source) == node_text(parameter_name, source)
16943 && extract_function_declarator(*declarator)
16944 .and_then(cpp_function_declarator_name_node)
16945 .is_some()
16946}
16947
16948fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
16949 node: Node<'tree>,
16950 parameter_name: Node<'_>,
16951 source: &str,
16952) -> Option<Node<'tree>> {
16953 let body = cpp_reparsed_member_function_body(node)?;
16954 let constraint = node.child_by_field_name("type")?;
16955 let constraint_template = constraint.child_by_field_name("name")?;
16956 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16957 let mut argument_cursor = constraint_arguments.walk();
16958 let constraint_types = constraint_arguments
16959 .named_children(&mut argument_cursor)
16960 .collect::<Vec<_>>();
16961 if constraint.kind() != "qualified_identifier"
16962 || constraint_template.kind() != "template_type"
16963 || !matches!(constraint_types.as_slice(), [left, right]
16964 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16965 || constraint_arguments.has_error()
16966 || node
16967 .child_by_field_name("body")
16968 .is_none_or(|candidate| !same_node(candidate, body))
16969 {
16970 return None;
16971 }
16972
16973 let mut cursor = node.walk();
16974 let recovery_errors = node
16975 .named_children(&mut cursor)
16976 .filter(|child| child.kind() == "ERROR")
16977 .collect::<Vec<_>>();
16978 if !recovery_errors
16979 .iter()
16980 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
16981 || !recovery_errors.iter().all(|error| {
16982 error.named_child_count() == 0
16983 || cpp_reparsed_constraint_macro_error(*error, source)
16984 || (error.named_child_count() == 1
16985 && error
16986 .named_child(0)
16987 .is_some_and(|child| child.kind() == "function_declarator"))
16988 })
16989 {
16990 return None;
16991 }
16992
16993 let parameter_text = node_text(parameter_name, source);
16994 let mut declarators = node
16995 .child_by_field_name("declarator")
16996 .and_then(extract_function_declarator)
16997 .into_iter()
16998 .collect::<Vec<_>>();
16999 for error in recovery_errors {
17000 let mut stack = vec![error];
17001 while let Some(current) = stack.pop() {
17002 if current.kind() == "function_declarator" {
17003 declarators.push(current);
17004 }
17005 let mut cursor = current.walk();
17006 stack.extend(current.named_children(&mut cursor));
17007 }
17008 }
17009 declarators.into_iter().find(|declarator| {
17010 cpp_function_declarator_name_node(*declarator)
17011 .is_some_and(|name| name.kind() == "identifier")
17012 && declarator
17013 .child_by_field_name("parameters")
17014 .is_some_and(|parameters| {
17015 parameters
17016 .named_children(&mut parameters.walk())
17017 .filter_map(|parameter| parameter.child_by_field_name("type"))
17018 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
17019 })
17020 })
17021}
17022
17023fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
17024 node: Node<'_>,
17025 parameter_name: Node<'_>,
17026 source: &str,
17027) -> bool {
17028 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
17029}
17030
17031fn cpp_reparsed_template_macro_function_companion_is_indexable(
17032 node: Node<'_>,
17033 parameter_name: Node<'_>,
17034 source: &str,
17035) -> bool {
17036 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
17037 return false;
17038 }
17039 let Some(return_type) = node.child_by_field_name("type") else {
17040 return false;
17041 };
17042 let Some(function_declarator) = node
17043 .child_by_field_name("declarator")
17044 .and_then(extract_function_declarator)
17045 else {
17046 return false;
17047 };
17048 if cpp_function_declarator_name_node(function_declarator).is_none()
17049 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
17050 {
17051 return false;
17052 }
17053 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
17054 return false;
17055 };
17056 let parameter_text = node_text(parameter_name, source);
17057 parameters
17058 .named_children(&mut parameters.walk())
17059 .any(|parameter| {
17060 parameter
17061 .child_by_field_name("type")
17062 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
17063 })
17064}
17065
17066fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
17067 if node.kind() != "ERROR" {
17068 return false;
17069 }
17070 let mut stack = vec![node];
17071 while let Some(current) = stack.pop() {
17072 let macro_shape = match current.kind() {
17073 "call_expression" => current
17074 .child_by_field_name("function")
17075 .zip(current.child_by_field_name("arguments")),
17076 "init_declarator" => current
17077 .child_by_field_name("declarator")
17078 .zip(current.child_by_field_name("value")),
17079 _ => None,
17080 };
17081 if let Some((name, arguments)) = macro_shape
17082 && name.kind() == "identifier"
17083 && arguments.kind() == "argument_list"
17084 && arguments.named_child_count() >= 2
17085 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
17086 {
17087 return true;
17088 }
17089 let mut cursor = current.walk();
17090 stack.extend(current.named_children(&mut cursor));
17091 }
17092 false
17093}
17094
17095fn cpp_recovered_template_macro_constructor<'tree>(
17096 node: Node<'tree>,
17097 source: &str,
17098) -> Option<(Node<'tree>, Node<'tree>)> {
17099 let mut prefix = node.prev_named_sibling()?;
17100 while prefix.kind() == "comment" {
17101 prefix = prefix.prev_named_sibling()?;
17102 }
17103 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
17104 let parameter = parameter_name
17105 .parent()
17106 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
17107 let declarator =
17108 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
17109 Some((declarator, parameter))
17110}
17111
17112fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
17113 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
17114 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
17115 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
17116 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
17117 function,
17118 parameter_name,
17119 source,
17120 )
17121 });
17122 }
17123 let Some(parameter_name) =
17124 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
17125 else {
17126 return false;
17127 };
17128 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
17129 cpp_reparsed_template_macro_function_companion_is_indexable(
17130 function,
17131 parameter_name,
17132 source,
17133 )
17134 })
17135}
17136
17137fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
17138 let function_name = node
17139 .child_by_field_name("declarator")
17140 .and_then(extract_function_declarator)
17141 .and_then(cpp_function_declarator_name_node);
17142 if let Some(body) = cpp_reparsed_member_function_body(node)
17143 && function_name.is_some()
17144 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
17145 {
17146 return true;
17147 }
17148 cpp_reparsed_attribute_member_function(node, source)
17149 || cpp_reparsed_friend_function_is_indexable(node, source)
17150 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
17151 || cpp_reparsed_access_template_function_is_indexable(node, source)
17152 || cpp_recovered_template_macro_constructor(node, source).is_some()
17153}
17154
17155fn cpp_reparsed_macro_attribute_member_sequence(
17162 children: &[Node<'_>],
17163 index: usize,
17164 source: &str,
17165) -> bool {
17166 let Some(prefix) = children.get(index).copied() else {
17167 return false;
17168 };
17169 let declaration = if prefix.kind() == "labeled_statement" {
17170 prefix
17171 .named_child(prefix.named_child_count().saturating_sub(1))
17172 .filter(|child| child.kind() == "declaration")
17173 } else {
17174 (prefix.kind() == "declaration").then_some(prefix)
17175 };
17176 let Some(declaration) = declaration else {
17177 return false;
17178 };
17179 if !declaration.has_error()
17180 || declaration
17181 .child_by_field_name("declarator")
17182 .and_then(extract_function_declarator)
17183 .and_then(cpp_function_declarator_name_node)
17184 .is_none()
17185 {
17186 return false;
17187 }
17188 let Some(attribute_statement) = children.get(index + 1).copied() else {
17189 return false;
17190 };
17191 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
17192 .then(|| attribute_statement.named_child(0))
17193 .flatten()
17194 .filter(|child| child.kind() == "call_expression")
17195 else {
17196 return false;
17197 };
17198 let Some(attribute_name) = attribute_call
17199 .child_by_field_name("function")
17200 .filter(|function| function.kind() == "identifier")
17201 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
17202 else {
17203 return false;
17204 };
17205 if !cpp_export_macro_token(&attribute_name) {
17206 return false;
17207 }
17208 let Some(body) = children.get(index + 2).copied() else {
17209 return false;
17210 };
17211 body.kind() == "compound_statement"
17212 && body.child(0).is_some_and(|open| open.kind() == "{")
17213 && body
17214 .child(body.child_count().saturating_sub(1))
17215 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
17216 && declaration.end_byte() <= attribute_statement.start_byte()
17217 && attribute_statement.end_byte() <= body.start_byte()
17218}
17219
17220fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
17228 if node.kind() != "ERROR" {
17229 return false;
17230 }
17231 let run = stranded_declaration_run(node, source);
17232 run.complete && !run.declarations.is_empty()
17233}
17234
17235fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
17236 let mut cursor = root.walk();
17237 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
17238 let mut saw_member = false;
17239 let mut index = 0;
17240 while index < children.len() {
17241 let child = children[index];
17242 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
17243 saw_member = true;
17244 index += 3;
17245 continue;
17246 }
17247 if let Some(recovered) = fragmented_class_body(child, source) {
17248 let Some(tree) = cpp_reparse_fragmented_class_body(
17249 source,
17250 recovered.body.reparse_start,
17251 recovered.body.reparse_end,
17252 ) else {
17253 return false;
17254 };
17255 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
17256 return false;
17257 }
17258 saw_member = true;
17259 index += 1;
17260 while index < children.len()
17261 && children[index].end_byte() <= recovered.body.class_range.end_byte
17262 {
17263 index += 1;
17264 }
17265 continue;
17266 }
17267 match child.kind() {
17268 "comment" => {}
17269 "labeled_statement" => saw_member = true,
17270 "function_definition" => {
17271 if child.has_error()
17272 && !cpp_reparsed_member_function_is_indexable(child, source)
17273 && cpp_sentinel_macro_region(child, source).is_none()
17274 {
17275 return false;
17276 }
17277 saw_member = true;
17278 }
17279 "expression_statement" if is_string_attribute_macro_statement(child) => {}
17283 "ERROR"
17284 if (cpp_reparsed_member_error_is_indexable(child)
17285 || cpp_reparsed_adjacent_copy_control_error(child, source)
17286 || cpp_reparsed_stranded_member_error(child, source))
17287 && (child
17288 .next_named_sibling()
17289 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
17290 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
17291 {
17292 saw_member = true;
17293 }
17294 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
17295 saw_member = true;
17296 }
17297 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
17298 saw_member = true;
17299 }
17300 "expression_statement"
17301 if cpp_is_stray_semicolon(child, source)
17302 && child.prev_named_sibling().is_some_and(|error| {
17303 cpp_reparsed_member_error_is_indexable(error)
17304 || cpp_reparsed_adjacent_copy_control_error(error, source)
17305 || cpp_reparsed_stranded_member_error(error, source)
17306 }) =>
17307 {
17308 saw_member = true;
17309 }
17310 "compound_statement"
17311 if cpp_reparsed_constructor_body_is_indexable(child, source)
17312 || cpp_reparsed_attribute_requires_body(child, source) =>
17313 {
17314 saw_member = true;
17315 }
17316 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
17317 _ => return false,
17318 }
17319 index += 1;
17320 }
17321 saw_member
17322}
17323
17324fn cpp_reparsed_synthetic_initializer_constructor_range(
17332 root: Node<'_>,
17333 class_name: &str,
17334 source: &str,
17335 constructor_end: usize,
17336) -> Option<std::ops::Range<usize>> {
17337 let mut stack = {
17338 let mut cursor = root.walk();
17339 root.named_children(&mut cursor).collect::<Vec<_>>()
17340 };
17341 while let Some(current) = stack.pop() {
17342 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
17343 current,
17344 class_name,
17345 source,
17346 constructor_end,
17347 ) {
17348 return Some(range);
17349 }
17350 if current.kind() == "ERROR" {
17351 let mut cursor = current.walk();
17352 stack.extend(current.named_children(&mut cursor));
17353 }
17354 }
17355 None
17356}
17357
17358fn cpp_reparsed_merged_inline_constructor<'tree>(
17365 root: Node<'tree>,
17366 class_name: &str,
17367 source: &str,
17368) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
17369 let mut stack = vec![root];
17370 while let Some(current) = stack.pop() {
17371 if current.kind() != "labeled_statement" {
17372 let mut cursor = current.walk();
17373 stack.extend(current.named_children(&mut cursor));
17374 continue;
17375 }
17376 let declaration = current
17377 .named_children(&mut current.walk())
17378 .find(|child| child.kind() == "declaration")?;
17379 if declaration
17380 .child_by_field_name("type")
17381 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
17382 {
17383 continue;
17384 }
17385 let following = declaration
17386 .child_by_field_name("declarator")
17387 .and_then(extract_function_declarator)
17388 .and_then(cpp_function_declarator_name_node);
17389 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
17390 continue;
17391 }
17392 let mut declaration_cursor = declaration.walk();
17393 let Some(error) = declaration
17394 .named_children(&mut declaration_cursor)
17395 .find(|child| child.kind() == "ERROR")
17396 else {
17397 continue;
17398 };
17399 let mut error_cursor = error.walk();
17400 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
17401 let Some(constructor) = error_children.iter().copied().find(|child| {
17402 child.kind() == "function_declarator"
17403 && cpp_function_declarator_name_node(*child)
17404 .is_some_and(|name| node_text(name, source).trim() == class_name)
17405 }) else {
17406 continue;
17407 };
17408 let Some(body) = error_children.iter().copied().find_map(|child| {
17409 (child.kind() == "init_declarator")
17410 .then(|| child.child_by_field_name("value"))
17411 .flatten()
17412 .filter(|value| value.kind() == "initializer_list")
17413 }) else {
17414 continue;
17415 };
17416 if constructor.end_byte() > body.start_byte() {
17417 continue;
17418 }
17419 return Some((constructor.start_byte()..body.end_byte(), body));
17420 }
17421 None
17422}
17423
17424fn cpp_reparsed_synthetic_initializer_constructor(
17425 node: Node<'_>,
17426 class_name: &str,
17427 source: &str,
17428 constructor_end: usize,
17429) -> Option<std::ops::Range<usize>> {
17430 if node.kind() != "labeled_statement" {
17431 return None;
17432 }
17433 let mut cursor = node.walk();
17434 let named = node
17435 .named_children(&mut cursor)
17436 .filter(|child| child.kind() != "comment")
17437 .collect::<Vec<_>>();
17438 let label = named.first()?;
17439 if label.kind() != "statement_identifier"
17440 || !matches!(
17441 node_text(*label, source).trim(),
17442 "public" | "private" | "protected"
17443 )
17444 {
17445 return None;
17446 }
17447 let call_error_index = named.iter().position(|child| {
17448 if child.kind() != "ERROR" {
17449 return false;
17450 }
17451 let mut stack = vec![*child];
17452 while let Some(current) = stack.pop() {
17453 if current.kind() == "call_expression"
17454 && current
17455 .child_by_field_name("function")
17456 .is_some_and(|function| {
17457 function.kind() == "identifier"
17458 && node_text(function, source).trim() == class_name
17459 })
17460 {
17461 return true;
17462 }
17463 let mut cursor = current.walk();
17464 stack.extend(current.named_children(&mut cursor));
17465 }
17466 false
17467 })?;
17468 let constructor_call = {
17469 let mut stack = vec![named[call_error_index]];
17470 let mut found = None;
17471 while let Some(current) = stack.pop() {
17472 if current.kind() == "call_expression"
17473 && current
17474 .child_by_field_name("function")
17475 .is_some_and(|function| {
17476 function.kind() == "identifier"
17477 && node_text(function, source).trim() == class_name
17478 })
17479 {
17480 found = Some(current);
17481 break;
17482 }
17483 let mut cursor = current.walk();
17484 stack.extend(current.named_children(&mut cursor));
17485 }
17486 found
17487 };
17488 let constructor_call = constructor_call?;
17489 named.iter().skip(call_error_index + 1).find(|child| {
17490 child.kind() == "declaration" && child.has_error() && {
17491 let mut cursor = child.walk();
17492 child.named_children(&mut cursor).any(|declarator| {
17493 declarator.kind() == "init_declarator"
17494 && declarator
17495 .child_by_field_name("declarator")
17496 .is_some_and(|declarator| declarator.kind() == "function_declarator")
17497 && declarator
17498 .child_by_field_name("value")
17499 .is_some_and(|value| value.kind() == "initializer_list")
17500 })
17501 }
17502 })?;
17503 Some(constructor_call.start_byte()..constructor_end)
17504}
17505
17506fn cpp_reparsed_exact_constructor_declarator<'tree>(
17507 root: Node<'tree>,
17508 start: usize,
17509 class_name: &str,
17510 source: &str,
17511) -> Option<Node<'tree>> {
17512 let mut candidate = None;
17513 let mut stack = vec![root];
17514 while let Some(current) = stack.pop() {
17515 if current.kind() == "function_declarator"
17516 && current.start_byte() == start
17517 && cpp_function_declarator_name_node(current)
17518 .is_some_and(|name| node_text(name, source).trim() == class_name)
17519 {
17520 if candidate.is_some() {
17521 return None;
17522 }
17523 candidate = Some(current);
17524 continue;
17525 }
17526 let mut cursor = current.walk();
17527 stack.extend(current.named_children(&mut cursor));
17528 }
17529 candidate
17530}
17531
17532fn cpp_is_indexable_item_kind(kind: &str) -> bool {
17533 matches!(
17534 kind,
17535 "namespace_definition"
17536 | "class_specifier"
17537 | "struct_specifier"
17538 | "union_specifier"
17539 | "enum_specifier"
17540 | "function_definition"
17541 | "template_declaration"
17542 | "declaration"
17543 | "field_declaration"
17544 | "alias_declaration"
17545 | "static_assert_declaration"
17546 | "type_definition"
17547 | "using_declaration"
17548 | "linkage_specification"
17549 | "preproc_def"
17550 | "preproc_function_def"
17551 | "preproc_include"
17552 | "preproc_if"
17553 | "preproc_ifdef"
17554 | "preproc_call"
17555 )
17556}
17557
17558#[cfg(test)]
17559mod tests {
17560 use super::*;
17561 use crate::adapter::parse_cpp_file;
17562 use brokk_bifrost_core::analyzer::parsed_file::{
17563 finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
17564 start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
17565 };
17566 use std::fmt::Write;
17567
17568 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
17569 let mut parser = tree_sitter::Parser::new();
17570 parser
17571 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17572 .unwrap();
17573 let tree = parser.parse(source, None).unwrap();
17574 let file = ProjectFile::new(std::env::temp_dir(), name);
17575 parse_cpp_file(&file, source, &tree)
17576 }
17577
17578 #[test]
17582 fn rendered_name_fq_round_trips_nested_class_spellings() {
17583 assert!(cpp_rendered_name_fq("").is_empty());
17584 for rendered in [
17585 "Only",
17586 "Top$Only",
17587 "ns.Outer$Inner",
17588 "Top$Only.b",
17589 "ns.Outer$Inner.a",
17590 "Buck$Stop",
17591 ] {
17592 let fq = cpp_rendered_name_fq(rendered);
17593 assert_eq!(
17594 fq.display_native(Language::Cpp, segment_interner()),
17595 rendered,
17596 "native round trip for {rendered}"
17597 );
17598 }
17599 let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
17600 .segments()
17601 .iter()
17602 .map(|segment| segment_interner().resolve(*segment).1)
17603 .collect();
17604 assert_eq!(
17605 kinds,
17606 [
17607 SegmentKind::Unknown,
17608 SegmentKind::Unknown,
17609 SegmentKind::Nested
17610 ]
17611 );
17612 }
17613
17614 #[test]
17618 fn colon_source_aliases_name_nested_and_namespace_identities() {
17619 let native = |rendered: &str| {
17620 cpp_colon_source_aliases(rendered)
17621 .into_iter()
17622 .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
17623 .collect::<Vec<_>>()
17624 };
17625 let top = native("Top::Only");
17626 assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
17627 assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
17628 assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
17629 let nested = native("ns::Outer::Inner");
17630 assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
17631 assert!(
17632 nested.contains(&"ns::Outer.Inner".to_string()),
17633 "{nested:?}"
17634 );
17635 let member = native("Top::Only::b");
17636 assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
17637 assert!(cpp_colon_source_aliases("Top$Only").is_empty());
17638 assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
17639 assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
17640 assert!(cpp_colon_source_aliases("Only").is_empty());
17641 assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
17642 fq.segments()
17643 .iter()
17644 .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
17645 }));
17646 }
17647
17648 #[test]
17649 fn displaced_terminator_ignores_nested_initializer_endif() {
17650 let source = "#ifdef ENABLE_ITEMS\n\
17651struct Item { int reg; };\n\
17652static const struct Item items[] = {{1}};\n\
17653static const struct Item extras[] = {\n\
17654 {0},\n\
17655#ifndef REDUCED\n\
17656 {1},\n\
17657#endif\n\
17658};\n\
17659int read_item(int i) { return items[i].reg; }\n\
17660#endif\n";
17661 let mut parser = tree_sitter::Parser::new();
17662 parser
17663 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17664 .expect("C++ grammar");
17665 let tree = parser.parse(source, None).expect("fixture tree");
17666 let conditional = tree
17667 .root_node()
17668 .named_child(0)
17669 .filter(|node| node.kind() == "preproc_ifdef")
17670 .expect("outer conditional");
17671
17672 assert!(conditional.has_error());
17673 assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
17674 assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
17675 }
17676
17677 #[test]
17678 fn pyobject_head_field_recovery_publishes_only_the_real_member() {
17679 let source = "struct Image { PyObject_HEAD Imaging image; };";
17680 let parsed = parse_cpp_declarations(source, "image.h");
17681 let names = parsed
17682 .declarations()
17683 .iter()
17684 .map(|unit| unit.fq_name())
17685 .collect::<Vec<_>>();
17686
17687 assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
17688 assert!(
17689 names.iter().all(|name| name != "Image.Imaging"),
17690 "the pseudo-declarator must not become a field: {names:#?}"
17691 );
17692
17693 let pointer = parse_cpp_declarations(
17694 "struct Image { PyObject_HEAD Imaging *image; };",
17695 "image-pointer.h",
17696 );
17697 let pointer_names = pointer
17698 .declarations()
17699 .iter()
17700 .map(|unit| unit.fq_name())
17701 .collect::<Vec<_>>();
17702 assert!(
17703 pointer_names.iter().any(|name| name == "Image.image"),
17704 "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
17705 );
17706 assert!(
17707 pointer_names.iter().all(|name| name != "Image.Imaging"),
17708 "the pointer recovery error must not become a field: {pointer_names:#?}"
17709 );
17710
17711 let unrelated_macro = parse_cpp_declarations(
17712 "struct Image { OTHER_HEAD Imaging other; };",
17713 "image-near-miss.h",
17714 );
17715 let unrelated_names = unrelated_macro
17716 .declarations()
17717 .iter()
17718 .map(|unit| unit.fq_name())
17719 .collect::<Vec<_>>();
17720 assert!(
17721 unrelated_names.iter().all(|name| name != "Image.other"),
17722 "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
17723 );
17724 }
17725
17726 #[test]
17727 fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
17728 let source = r#"namespace testing {
17729namespace internal {
17730 namespace detail {
17731 class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
17732 public:
17733 int value() const { return count_ + 1; }
17734 private:
17735 int count_;
17736 };
17737 class GTEST_API_ [[nodiscard]] OtherReporter {
17738 public:
17739 int value() const { return count_ + 2; }
17740 private:
17741 int count_;
17742 };
17743 }
17744
17745 template <typename T>
17746 void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
17747
17748 class TailReporter {};
17749}
17750}
17751"#;
17752 let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
17753 let declarations = parsed.declarations();
17754 let mut parser = tree_sitter::Parser::new();
17755 parser
17756 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17757 .unwrap();
17758 let tree = parser.parse(source, None).unwrap();
17759 let tail_start = source.find("TailReporter").expect("tail class");
17760 let tail_node = tree
17761 .root_node()
17762 .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
17763 .expect("tail class AST node");
17764 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
17765 assert!(
17766 tree.root_node().has_error(),
17767 "the malformed class must exercise recovery"
17768 );
17769 assert!(index.region_at(tail_start).is_some());
17770 assert_eq!(
17771 index.enclosing_namespace_components(tail_node, source),
17772 ["testing", "internal"]
17773 );
17774 let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
17775 let mut recovered_parsed = ParsedFile::new(String::new());
17776 let class_unit = CodeUnit::new_fq(
17777 file.clone(),
17778 CodeUnitType::Class,
17779 "testing",
17780 "ScopedFakeTestPartResultReporter",
17781 cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
17782 );
17783 let scope = ScopeInfo {
17784 package_name: "testing".to_string(),
17785 module: None,
17786 class_unit: Some(class_unit),
17787 template_signature: Some("<typename T>".to_string()),
17788 template_metadata: Some(CppTemplateMetadata {
17789 primary_name: "ScopedFakeTestPartResultReporter".to_string(),
17790 primary_fq_name: String::new(),
17791 parameters: Vec::new(),
17792 specialization_arguments: Vec::new(),
17793 alias_target: None,
17794 }),
17795 declarations_are_fields: true,
17796 recovered_specialization_member_scope: true,
17797 visible_using_namespaces: Vec::new(),
17798 };
17799 let mut visitor = CppVisitor {
17800 file: &file,
17801 source,
17802 parsed: &mut recovered_parsed,
17803 c_tag_semantics: false,
17804 c_tag_scope_witness: false,
17805 recovered_class_sibling_scopes: HashMap::default(),
17806 consumed_fragment_regions: Vec::new(),
17807 orphaned_namespaces: &index,
17808 partitioned_regions: Vec::new(),
17809 namespace_forward_scans: HashMap::default(),
17810 field_owners: None,
17811 recovery_captures: Vec::new(),
17812 object_macro_fields: HashMap::default(),
17813 ambiguous_object_macro_fields: HashSet::default(),
17814 };
17815 let recovered = visitor
17816 .recovered_namespace_scope(tail_node, &scope)
17817 .expect("the tail must use the stolen namespace scope");
17818 assert_eq!(recovered.package_name, "testing::internal");
17819 assert!(
17820 recovered.class_unit.is_none(),
17821 "recovered namespace declarations cannot retain the malformed class owner"
17822 );
17823 assert!(recovered.template_signature.is_none());
17824 assert!(recovered.template_metadata.is_none());
17825 assert!(!recovered.declarations_are_fields);
17826 assert!(!recovered.recovered_specialization_member_scope);
17827 assert!(
17828 declarations
17829 .iter()
17830 .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
17831 "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
17832 );
17833 assert!(
17834 declarations
17835 .iter()
17836 .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
17837 "the recovered free function remains in its namespace: {declarations:#?}"
17838 );
17839 assert!(
17840 declarations
17841 .iter()
17842 .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
17843 "the independent nested class keeps its ordinary class owner: {declarations:#?}"
17844 );
17845 assert!(
17846 declarations.iter().all(|unit| {
17847 !unit
17848 .short_name()
17849 .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
17850 }),
17851 "recovered namespace declarations must not retain a class owner: {declarations:#?}"
17852 );
17853 assert!(
17854 declarations
17855 .iter()
17856 .all(|unit| !unit.identifier().is_empty()),
17857 "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
17858 );
17859 }
17860
17861 #[test]
17862 fn object_like_field_macros_materialize_owner_specific_declarations() {
17863 let source = r#"#define PUBLIC_FIELDS int public_value;
17864#define PRIVATE_FIELDS int private_value;
17865#define NOT_A_FIELD_LIST not a declaration
17866
17867struct First {
17868 PUBLIC_FIELDS
17869 PRIVATE_FIELDS
17870};
17871struct Second {
17872 PUBLIC_FIELDS
17873 NOT_A_FIELD_LIST
17874};
17875#undef PUBLIC_FIELDS
17876struct Third {
17877 PUBLIC_FIELDS
17878};
17879"#;
17880 let parsed = parse_cpp_declarations(source, "macro-fields.c");
17881 let fields = parsed
17882 .declarations()
17883 .iter()
17884 .filter(|unit| unit.is_field())
17885 .map(|unit| unit.fq_name())
17886 .collect::<Vec<_>>();
17887
17888 assert!(
17889 fields.contains(&"First.public_value".to_string()),
17890 "{fields:?}"
17891 );
17892 assert!(
17893 fields.contains(&"First.private_value".to_string()),
17894 "{fields:?}"
17895 );
17896 assert!(
17897 fields.contains(&"Second.public_value".to_string()),
17898 "{fields:?}"
17899 );
17900 assert!(
17901 !fields.iter().any(|field| field.contains("not_a_field")),
17902 "malformed macro must fail closed: {fields:?}"
17903 );
17904 assert!(
17905 !fields.iter().any(|field| field.starts_with("Third.")),
17906 "undefined macro must fail closed: {fields:?}"
17907 );
17908 }
17909
17910 #[test]
17911 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
17912 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
17913 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
17914 let mut macros = parsed
17915 .declarations()
17916 .iter()
17917 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
17918 .collect::<Vec<_>>();
17919 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
17920
17921 assert_eq!(macros.len(), 2, "{macros:#?}");
17922 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
17923 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
17924 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
17925 assert_eq!(
17926 parsed.declaration_ranges(macros[1])[0].start_byte,
17927 source.rfind("#define VALUE 2").expect("second definition")
17928 );
17929 }
17930
17931 #[test]
17932 fn identifies_export_macro_class_base_displaced_into_declarator() {
17933 let source = r#"#define PROJECT_API_
17934namespace project {
17935namespace internal {
17936template <typename T>
17937class Base {};
17938}
17939template <typename T>
17940class Wrapper;
17941template <>
17942class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
17943}
17944"#;
17945 let mut parser = tree_sitter::Parser::new();
17946 parser
17947 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17948 .unwrap();
17949 let tree = parser.parse(source, None).unwrap();
17950 let start = source.find("internal::Base<int>").expect("base");
17951 let mut base = tree
17952 .root_node()
17953 .descendant_for_byte_range(start, start + 8)
17954 .expect("base syntax");
17955 while base.kind() != "qualified_identifier" {
17956 base = base.parent().expect("qualified base ancestor");
17957 }
17958 assert!(
17959 is_recovered_exported_class_base_type_node(base, source),
17960 "{}",
17961 tree.root_node().to_sexp()
17962 );
17963 }
17964
17965 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
17966 let mut identities = parsed
17967 .declarations()
17968 .iter()
17969 .filter(|unit| unit.is_function())
17970 .map(|unit| {
17971 (
17972 unit.fq_name(),
17973 unit.signature().unwrap_or_default().to_string(),
17974 )
17975 })
17976 .collect::<Vec<_>>();
17977 identities.sort();
17978 identities
17979 }
17980
17981 #[test]
17988 fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
17989 let cases = [
17990 (
17991 "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
17992 "pg_typemap_fit_to_result",
17993 "(VALUE, VALUE)",
17994 ),
17995 (
17996 "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
17997 "pg_typemap_result_value",
17998 "(t_typemap *, VALUE, int, int)",
17999 ),
18000 (
18001 "void pg_typemap_mark _(( void * ));",
18002 "pg_typemap_mark",
18003 "(void *)",
18004 ),
18005 (
18006 "void init_pg_type_map _(( void ));",
18007 "init_pg_type_map",
18008 "(void)",
18009 ),
18010 ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
18011 (
18012 "extern VALUE pg_extern _(( VALUE, VALUE ));",
18013 "pg_extern",
18014 "(VALUE, VALUE)",
18015 ),
18016 (
18017 "size_t pg_typemap_memsize _(( const void * ));",
18018 "pg_typemap_memsize",
18019 "(const void *)",
18020 ),
18021 (
18022 "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
18023 "pg_wrap_socket_io",
18024 "(int, VALUE, VALUE *, int *)",
18025 ),
18026 ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
18027 ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
18028 ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
18029 ];
18030
18031 for (index, (source, name, signature)) in cases.into_iter().enumerate() {
18032 let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
18033 assert_eq!(
18034 function_identities(&parsed),
18035 vec![(name.to_string(), signature.to_string())],
18036 "{source}: {:#?}",
18037 parsed.declarations()
18038 );
18039 assert!(
18040 parsed.declarations().iter().all(|unit| !unit.is_field()),
18041 "{source} must not retain the malformed field: {:#?}",
18042 parsed.declarations()
18043 );
18044 }
18045 }
18046
18047 #[test]
18056 fn c_prototype_macro_recovers_pointer_return_expression_statements() {
18057 let cases = [
18058 (
18059 "PGconn *pg_get_pgconn _(( VALUE ));",
18060 "pg_get_pgconn",
18061 "(VALUE)",
18062 ),
18063 (
18064 "PGresult* pgresult_get _(( VALUE ));",
18065 "pgresult_get",
18066 "(VALUE)",
18067 ),
18068 ];
18069 for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
18070 let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
18071 let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
18072 assert_eq!(
18073 function_identities(&parsed),
18074 vec![(name.to_string(), signature.to_string())],
18075 "{source}: {:#?}",
18076 parsed.declarations()
18077 );
18078 assert_eq!(
18079 parsed
18080 .declarations()
18081 .iter()
18082 .filter(|unit| unit.is_field())
18083 .map(|unit| unit.fq_name())
18084 .collect::<Vec<_>>(),
18085 vec!["rb_mPG".to_string()],
18086 "{source}: {:#?}",
18087 parsed.declarations()
18088 );
18089 }
18090 }
18091
18092 #[test]
18100 fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
18101 let source = r#"VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
18102VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
18103int pg_typemap_fit_to_copy_get _(( VALUE ));
18104VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
18105t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
18106VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
18107void pg_typemap_mark _(( void * ));
18108size_t pg_typemap_memsize _(( const void * ));
18109void pg_typemap_compact _(( void * ));
18110
18111PGconn *pg_get_pgconn _(( VALUE ));
18112t_pg_connection *pg_get_connection _(( VALUE ));
18113VALUE pgconn_block _(( int, VALUE *, VALUE ));
18114#ifdef __GNUC__
18115__attribute__((format(printf, 3, 4)))
18116#endif
18117NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
18118VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
18119void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
18120
18121
18122VALUE pg_new_result _(( PGresult *, VALUE ));
18123VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
18124PGresult* pgresult_get _(( VALUE ));
18125VALUE pg_result_check _(( VALUE ));
18126VALUE pg_result_clear _(( VALUE ));
18127VALUE pg_tuple_new _(( VALUE, int ));
18128
18129/*
18130 * Fetch the data pointer for the result object
18131 */
18132static inline t_pg_result *
18133pgresult_get_this( VALUE self )
18134{
18135 return RTYPEDDATA_DATA(self);
18136}
18137
18138
18139rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
18140const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
18141rb_encoding *pg_conn_enc_get _(( PGconn * ));
18142
18143"#;
18144 let parsed = parse_cpp_declarations(source, "pg.h");
18145 assert!(
18146 function_identities(&parsed)
18147 .iter()
18148 .any(|(name, signature)| name == "pg_typemap_result_value"
18149 && signature == "(t_typemap *, VALUE, int, int)"),
18150 "the real issue witness must be a Function with its C signature: {:#?}",
18151 parsed.declarations()
18152 );
18153 assert!(
18154 parsed
18155 .declarations()
18156 .iter()
18157 .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
18158 "the issue witness must not leave its return type as a Field name: {:#?}",
18159 parsed.declarations()
18160 );
18161 }
18162
18163 #[test]
18170 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
18171 let source = r#"class SIMD_4x26 final {
18172 public:
18173 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
18174 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
18175 int m_v;
18176};
18177"#;
18178 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
18179 assert_eq!(
18180 function_identities(&parsed),
18181 vec![
18182 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
18183 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
18184 ],
18185 "{:#?}",
18186 parsed.declarations()
18187 );
18188 }
18189
18190 #[test]
18195 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
18196 let source = r#"#include <cstdint>
18197struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
18198 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
18199 struct llama_model * model,
18200 struct llama_context_params params),
18201 "use llama_init_from_model instead");
18202 LLAMA_API int32_t llama_tokenize(
18203 const struct llama_vocab * vocab,
18204 const char * text,
18205 bool parse_special);
18206 LLAMA_API int32_t llama_other(int a);
18207"#;
18208 let parsed = parse_cpp_declarations(source, "llama.h");
18209 assert_eq!(
18210 function_identities(&parsed),
18211 vec![
18212 (
18213 "llama_new_context_with_model".to_string(),
18214 "(struct llama_model *, struct llama_context_params)".to_string()
18215 ),
18216 ("llama_other".to_string(), "(int)".to_string()),
18217 (
18218 "llama_tokenize".to_string(),
18219 "(const struct llama_vocab *, const char *, bool)".to_string()
18220 ),
18221 ],
18222 "{:#?}",
18223 parsed.declarations()
18224 );
18225
18226 for (name, expected) in [
18229 (
18230 "llama_new_context_with_model",
18231 "LLAMA_API struct llama_context * llama_new_context_with_model(",
18232 ),
18233 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
18234 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
18235 ] {
18236 let unit = parsed
18237 .declarations()
18238 .iter()
18239 .find(|unit| unit.is_function() && unit.fq_name() == name)
18240 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
18241 let [range] = parsed.declaration_ranges(unit) else {
18242 panic!("{name} must have exactly one range");
18243 };
18244 let text = &source[range.start_byte..range.end_byte];
18245 assert!(
18246 text.starts_with(expected),
18247 "{name} range is {text:?}, expected it to start with {expected:?}"
18248 );
18249 assert!(
18250 text.ends_with(')') || text.ends_with(';'),
18251 "{name}: {text:?}"
18252 );
18253 }
18254 }
18255
18256 #[test]
18270 fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
18271 let source = r#" LIBRARY_DEPRECATED(
18272 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
18273 "use library_init_from_file_with_params instead"
18274 );
18275 LIBRARY_DEPRECATED(
18276 LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
18277 "use library_init_from_buffer_with_params instead"
18278 );
18279 LIBRARY_DEPRECATED(
18280 LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
18281 "use library_init_with_params instead"
18282 );
18283"#;
18284 let mut parser = tree_sitter::Parser::new();
18285 parser
18286 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18287 .expect("C++ grammar");
18288 let tree = parser.parse(source, None).expect("C++ tree");
18289 let root = tree.root_node();
18290
18291 let mut cursor = root.walk();
18295 let items = root.children(&mut cursor).collect::<Vec<_>>();
18296 let [first, hint_statement, rest @ ..] = items.as_slice() else {
18297 panic!("{}", root.to_sexp());
18298 };
18299 assert_eq!(first.kind(), "ERROR");
18300 assert_eq!(hint_statement.kind(), "expression_statement");
18301 assert!(
18302 node_text(*first, source).ends_with(','),
18303 "the first invocation's own `)` and `;` are the statement's, not its own: {}",
18304 root.to_sexp()
18305 );
18306 let swallowing = rest
18307 .iter()
18308 .find(|item| item.kind() == "ERROR")
18309 .unwrap_or_else(|| panic!("{}", root.to_sexp()));
18310 let mut swallowing_cursor = swallowing.walk();
18311 let flattened = swallowing
18312 .children(&mut swallowing_cursor)
18313 .map(|child| child.kind())
18314 .collect::<Vec<_>>();
18315 assert_eq!(
18316 flattened,
18317 vec![
18318 "identifier",
18319 "(",
18320 "parameter_declaration",
18321 ",",
18322 "ERROR",
18323 "type_identifier",
18324 "(",
18325 "parameter_declaration",
18326 ",",
18327 "\"",
18328 "identifier",
18329 "identifier",
18330 "identifier",
18331 "\"",
18332 ")",
18333 ],
18334 "the third invocation must be flattened into the second one's node: {}",
18335 root.to_sexp()
18336 );
18337
18338 let ancestry = ParentIndex::new(root);
18340 let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
18341 .expect("the first invocation");
18342 assert_eq!(
18343 &source[..first_run.invocation_end],
18344 &source[..source.find("instead\"\n );").expect("first hint")
18345 + "instead\"\n );".len()]
18346 );
18347 assert_eq!(
18348 first_run.region_end, first_run.invocation_end,
18349 "the first invocation swallowed nothing, so the recovery owns only its own bytes"
18350 );
18351 let swallowing_run = collapsed_macro_declaration_run(*swallowing, source, &ancestry)
18352 .expect("the second invocation");
18353 assert!(
18354 swallowing_run.invocation_end < swallowing.end_byte(),
18355 "the second invocation swallowed the third"
18356 );
18357 assert_eq!(
18358 swallowing_run.region_end,
18359 root.end_byte(),
18360 "a swallowing invocation owns the region to the close of its declaration scope"
18361 );
18362
18363 let parsed = parse_cpp_declarations(source, "library.h");
18364 assert_eq!(
18365 function_identities(&parsed),
18366 vec![
18367 (
18368 "library_init".to_string(),
18369 "(struct library_model_loader *)".to_string()
18370 ),
18371 (
18372 "library_init_from_buffer".to_string(),
18373 "(void *, size_t)".to_string()
18374 ),
18375 (
18376 "library_init_from_file".to_string(),
18377 "(const char *)".to_string()
18378 ),
18379 ],
18380 "{:#?}",
18381 parsed.declarations()
18382 );
18383 }
18384
18385 #[test]
18391 fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
18392 let source = r#"LIBRARY_DEPRECATED(
18393 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
18394 "use library_init_from_file_with_params instead"
18395 );"#;
18396 let mut parser = tree_sitter::Parser::new();
18397 parser
18398 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18399 .expect("C++ grammar");
18400 let tree = parser.parse(source, None).expect("C++ tree");
18401 let root = tree.root_node();
18402 let head = root.named_child(0).expect("the invocation");
18403 let ancestry = ParentIndex::new(root);
18404 assert!(
18405 collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
18406 "{}",
18407 root.to_sexp()
18408 );
18409 assert!(
18410 !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
18411 "the ordinary reader must be the one that has it: {}",
18412 root.to_sexp()
18413 );
18414 let unindexed = ParentIndex::unindexed();
18417 assert!(
18418 collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
18419 "indexed and unindexed climbs must agree"
18420 );
18421 assert_eq!(
18422 macro_wrapped_declarations(head, source, &ancestry).len(),
18423 macro_wrapped_declarations(head, source, &unindexed).len(),
18424 "indexed and unindexed climbs must agree"
18425 );
18426 }
18427
18428 #[test]
18433 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
18434 for source in [
18435 "int before;\nFOO(1, 2);\nint after;\n",
18436 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
18437 "int before;\nMACRO(int a, int b);\nint after;\n",
18438 "DECLARE_HANDLE(HWND);\nint after;\n",
18439 ] {
18440 let parsed = parse_cpp_declarations(source, "macro-call.h");
18441 assert_eq!(
18442 function_identities(&parsed),
18443 Vec::new(),
18444 "{source:?} must declare no function: {:#?}",
18445 parsed.declarations()
18446 );
18447 }
18448 }
18449
18450 #[test]
18455 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
18456 let source = r#"#include <string_view>
18457namespace Botan {
18458class DL_Group final {
18459 public:
18460 DL_Group() = default;
18461 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
18462 DL_Group(std::string_view pem, int format);
18463 size_t get_p() const;
18464};
18465}
18466"#;
18467 let parsed = parse_cpp_declarations(source, "dl_group.h");
18468 assert_eq!(
18469 function_identities(&parsed),
18470 vec![
18471 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
18472 (
18473 "Botan.DL_Group.DL_Group".to_string(),
18474 "(std::string_view)".to_string()
18475 ),
18476 (
18477 "Botan.DL_Group.DL_Group".to_string(),
18478 "(std::string_view, int)".to_string()
18479 ),
18480 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
18481 ],
18482 "{:#?}",
18483 parsed.declarations()
18484 );
18485 }
18486
18487 #[test]
18492 fn an_export_macro_class_keeps_its_string_attribute_members() {
18493 let source = r#"#include <string_view>
18494namespace Botan {
18495class BOTAN_PUBLIC_API(2, 0) DL_Group final {
18496 public:
18497 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
18498 DL_Group(std::string_view pem, int format);
18499 size_t get_p() const;
18500};
18501}
18502"#;
18503 let parsed = parse_cpp_declarations(source, "dl_group.h");
18504 assert!(
18505 parsed
18506 .declarations()
18507 .iter()
18508 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
18509 "{:#?}",
18510 parsed.declarations()
18511 );
18512 assert_eq!(
18513 function_identities(&parsed),
18514 vec![
18515 (
18516 "Botan.DL_Group.DL_Group".to_string(),
18517 "(std::string_view)".to_string()
18518 ),
18519 (
18520 "Botan.DL_Group.DL_Group".to_string(),
18521 "(std::string_view, int)".to_string()
18522 ),
18523 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
18524 ],
18525 "{:#?}",
18526 parsed.declarations()
18527 );
18528 }
18529
18530 #[test]
18536 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
18537 let source = r#"namespace Botan {
18538class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
18539 public:
18540 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
18541 XMSS_Parameters(int oid, int len);
18542 size_t len() const;
18543
18544 private:
18545 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
18546 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
18547
18548 int m_oid;
18549 int m_wots_oid;
18550 size_t m_element_size;
18551 size_t m_tree_height;
18552};
18553}
18554"#;
18555 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
18556 let constructors = function_identities(&parsed)
18557 .into_iter()
18558 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
18559 .map(|(_, signature)| signature)
18560 .collect::<Vec<_>>();
18561 assert_eq!(
18562 constructors,
18563 vec![
18564 "()".to_string(),
18565 "(int, int)".to_string(),
18566 "(int, int, size_t, size_t)".to_string(),
18567 ],
18568 "{:#?}",
18569 parsed.declarations()
18570 );
18571 }
18572
18573 #[test]
18577 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
18578 let source = r#"namespace shell {
18579struct Outer {
18580 struct Inner {
18581 Inner(int v);
18582 void run(int v);
18583 };
18584};
18585Outer::Inner::Inner(int v) {}
18586void Outer::Inner::run(int v) {}
18587}
18588"#;
18589 let parsed = parse_cpp_declarations(source, "outer.cpp");
18590 let names = function_identities(&parsed)
18591 .into_iter()
18592 .map(|(fq_name, _)| fq_name)
18593 .collect::<Vec<_>>();
18594 assert!(
18595 names
18596 .iter()
18597 .all(|name| name.starts_with("shell.Outer$Inner.")),
18598 "{names:#?}"
18599 );
18600 }
18601
18602 #[test]
18603 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
18604 let source = r#"namespace control {
18605template <typename T>
18606class AnySpan;
18607template <typename T>
18608class ABSL_ATTRIBUTE_VIEW AnySpan {
18609 public:
18610 int begin() const;
18611};
18612}
18613
18614namespace absl {
18615ABSL_NAMESPACE_BEGIN
18616template <typename T>
18617class ABSL_ATTRIBUTE_VIEW Span {
18618 public:
18619 int begin() const;
18620 int back() const;
18621};
18622
18623int begin();
18624int back();
18625}
18626"#;
18627 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
18628 let declarations = parsed.declarations();
18629 assert!(
18630 declarations
18631 .iter()
18632 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
18633 );
18634 for method in ["begin", "back"] {
18635 assert!(declarations.iter().any(|unit| {
18636 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
18637 }));
18638 assert!(
18639 declarations.iter().any(|unit| {
18640 unit.is_function() && unit.fq_name() == format!("absl.{method}")
18641 })
18642 );
18643 }
18644 assert!(
18645 declarations
18646 .iter()
18647 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
18648 );
18649 assert!(
18650 declarations
18651 .iter()
18652 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
18653 );
18654 assert!(
18655 declarations
18656 .iter()
18657 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
18658 );
18659 }
18660
18661 #[test]
18662 fn explicit_global_member_definition_has_canonical_package_boundary() {
18663 let source = r#"
18664namespace arangodb::aql {
18665class ExecutionPlan {
18666 public:
18667 template<class... Args> Node* createNode(Args&&... args);
18668};
18669}
18670
18671template<class... Args>
18672Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
18673"#;
18674 let parsed = parse_cpp_declarations(source, "global-member.cpp");
18675
18676 assert!(parsed.declarations().iter().any(|unit| {
18677 unit.is_function()
18678 && unit.package_name() == "arangodb::aql"
18679 && unit.short_name() == "ExecutionPlan.createNode"
18680 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
18681 }));
18682 }
18683
18684 #[test]
18685 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
18686 let source = r#"
18687#ifndef TINYXML2_INCLUDED
18688#define TINYXML2_INCLUDED
18689namespace tinyxml2 {
18690class TINYXML2_LIB XMLUtil {
18691 public:
18692 static const char* SkipWhiteSpace(const char* p) {
18693 while (*p) {
18694 if (*p == ' ') {
18695 ++p;
18696 }
18697 }
18698 return p;
18699 }
18700 static bool StringEqual(const char* p, const char* q) {
18701 return p == q;
18702 }
18703 class TINYXML2_LIB Helper {
18704 public:
18705 void Touch();
18706 };
18707 static void ToStr(int value, char* buffer);
18708 private:
18709 static const char* writeBoolTrue;
18710};
18711
18712class TINYXML2_LIB XMLNode {
18713 public:
18714 virtual XMLNode* ShallowClone() const = 0;
18715 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
18716};
18717}
18718#endif
18719"#;
18720 let mut parser = tree_sitter::Parser::new();
18721 parser
18722 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18723 .unwrap();
18724 let tree = parser.parse(source, None).unwrap();
18725 let mut boundary_found = false;
18726 walk_named_tree_preorder(tree.root_node(), true, |node| {
18727 if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
18728 && name == "XMLUtil"
18729 {
18730 boundary_found = fragmented_export_sibling_class_boundary(
18731 node,
18732 source,
18733 &ParentIndex::unindexed(),
18734 )
18735 .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
18736 .is_some_and(|(_, name, _)| name == "XMLNode");
18737 }
18738 WalkControl::Continue
18739 });
18740 assert!(
18741 boundary_found,
18742 "fixture must exercise the recovered sibling boundary"
18743 );
18744
18745 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
18746 assert!(
18747 parsed
18748 .declarations()
18749 .iter()
18750 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
18751 "{:#?}",
18752 parsed.declarations()
18753 );
18754 assert!(
18755 parsed
18756 .declarations()
18757 .iter()
18758 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
18759 "{:#?}",
18760 parsed.declarations()
18761 );
18762 assert!(parsed.declarations().iter().any(|unit| {
18763 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
18764 }));
18765 assert!(
18766 parsed
18767 .declarations()
18768 .iter()
18769 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
18770 );
18771 assert!(
18772 parsed
18773 .declarations()
18774 .iter()
18775 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
18776 );
18777 }
18778
18779 #[test]
18780 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
18781 let parsed = parse_cpp_declarations(
18785 r#"
18786namespace cwg311 {
18787namespace X { namespace Y {} }
18788namespace ::cwg311::X {}
18789}
18790"#,
18791 "explicit-global-namespace.cpp",
18792 );
18793
18794 assert!(parsed.declarations().iter().any(|unit| {
18795 unit.kind() == CodeUnitType::Module
18796 && unit.short_name() == "cwg311::X"
18797 && unit.fq_name() == "cwg311::X"
18798 }));
18799 assert!(
18800 parsed
18801 .declarations()
18802 .iter()
18803 .all(|unit| !unit.short_name().contains("::::")),
18804 "recovered namespace names must not retain empty scope components: {:#?}",
18805 parsed.declarations()
18806 );
18807 }
18808
18809 #[test]
18810 fn repeated_scope_separator_does_not_create_empty_function_owner() {
18811 let scope = ScopeInfo {
18812 package_name: "X".to_string(),
18813 module: None,
18814 class_unit: None,
18815 template_signature: None,
18816 template_metadata: None,
18817 declarations_are_fields: false,
18818 recovered_specialization_member_scope: false,
18819 visible_using_namespaces: Vec::new(),
18820 };
18821
18822 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
18823
18824 assert!(owner.is_none());
18825 assert_eq!(name, "doit");
18826 assert_eq!(package, "X");
18827 }
18828
18829 #[test]
18830 fn trailing_decltype_expression_is_not_a_function_declarator() {
18831 let source = r#"
18832namespace boost { namespace detail {
18833#if ! defined(BOOST_NO_SFINAE_EXPR) && \
18834 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
18835 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
18836#define BOOST_THREAD_PROVIDES_INVOKE
18837#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
18838template <class Fp, class A0, class ...Args>
18839inline auto
18840invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
18841 BOOST_THREAD_RV_REF(Args) ...args)
18842 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
18843{
18844 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
18845}
18846#endif
18847#endif
18848}}
18849"#;
18850 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
18851
18852 assert!(
18853 parsed
18854 .declarations()
18855 .iter()
18856 .all(|unit| unit.short_name() != ".*f")
18857 );
18858 }
18859
18860 fn find_class_named<'tree>(
18861 root: Node<'tree>,
18862 source: &str,
18863 expected_name: &str,
18864 ) -> Option<Node<'tree>> {
18865 let mut stack = vec![root];
18866 while let Some(node) = stack.pop() {
18867 if node.kind() == "class_specifier"
18868 && node
18869 .child_by_field_name("name")
18870 .is_some_and(|name| node_text(name, source) == expected_name)
18871 {
18872 return Some(node);
18873 }
18874 let mut cursor = node.walk();
18875 stack.extend(node.named_children(&mut cursor));
18876 }
18877 None
18878 }
18879
18880 #[test]
18881 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
18882 let source = r#"EXPORT void definition(struct Value value) {}
18883EXPORT void prototype(struct Value value);
18884"#;
18885 let mut parser = tree_sitter::Parser::new();
18886 parser
18887 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18888 .unwrap();
18889 let tree = parser.parse(source, None).unwrap();
18890 let root = tree.root_node();
18891 let mut cursor = root.walk();
18892 let callables = root
18893 .named_children(&mut cursor)
18894 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
18895 .collect::<Vec<_>>();
18896
18897 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
18898 for callable in callables {
18899 assert!(callable.has_error(), "fixture must exercise error recovery");
18900 assert!(
18901 cpp_sentinel_macro_parts(callable, source).is_none(),
18902 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
18903 );
18904 }
18905 }
18906
18907 #[test]
18908 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
18909 let source = r#"namespace absl {
18910ABSL_NAMESPACE_BEGIN
18911// Generate a floating-point variate conforming to a Beta distribution:
18912template <typename RealType = double>
18913class beta_distribution {
18914 public:
18915 using result_type = RealType;
18916
18917
18918 beta_distribution() : beta_distribution(1) {}
18919
18920 explicit beta_distribution(result_type alpha, result_type beta = 1)
18921 : param_(alpha, beta) {}
18922
18923 explicit beta_distribution(const param_type& p) : param_(p) {}
18924
18925 void reset() {}
18926
18927 // Generating functions
18928 template <typename URBG>
18929 result_type operator()(URBG& g) { // NOLINT(runtime/references)
18930 return (*this)(g, param_);
18931 }
18932
18933};
18934ABSL_NAMESPACE_END
18935} // namespace absl
18936"#;
18937 let mut parser = tree_sitter::Parser::new();
18938 parser
18939 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18940 .unwrap();
18941 let tree = parser.parse(source, None).unwrap();
18942 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18943 let body = namespace
18944 .child_by_field_name("body")
18945 .expect("fixture namespace body");
18946 let sentinel = body.named_child(0).expect("sentinel envelope");
18947 let callable = sentinel
18948 .child_by_field_name("declarator")
18949 .and_then(extract_function_declarator)
18950 .and_then(cpp_function_declarator_name_node)
18951 .expect("preserved callable name");
18952
18953 assert_eq!(sentinel.kind(), "function_definition");
18954 assert_eq!(callable.kind(), "operator_name");
18955 assert!(
18956 cpp_sentinel_macro_parts(sentinel, source).is_some(),
18957 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
18958 );
18959 }
18960
18961 #[test]
18962 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
18963 let source = r#"namespace absl {
18964ABSL_NAMESPACE_BEGIN
18965// absl::discrete_distribution
18966//
18967// A discrete distribution produces random integers i, where 0 <= i < n
18968template <typename IntType = int>
18969class discrete_distribution {
18970 public:
18971 using result_type = IntType;
18972 class param_type {
18973 public:
18974 param_type() { init(); }
18975 template <typename InputIterator>
18976 explicit param_type(InputIterator begin, InputIterator end)
18977 : p_(begin, end) {
18978 init();
18979 }
18980 };
18981 discrete_distribution() : param_() {}
18982 explicit discrete_distribution(const param_type& p) : param_(p) {}
18983};
18984ABSL_NAMESPACE_END
18985} // namespace absl
18986"#;
18987 let mut parser = tree_sitter::Parser::new();
18988 parser
18989 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18990 .unwrap();
18991 let tree = parser.parse(source, None).unwrap();
18992 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18993 let body = namespace
18994 .child_by_field_name("body")
18995 .expect("fixture namespace body");
18996 let sentinel = body.named_child(0).expect("sentinel envelope");
18997 let callable = sentinel
18998 .child_by_field_name("declarator")
18999 .and_then(extract_function_declarator)
19000 .and_then(cpp_function_declarator_name_node)
19001 .expect("preserved callable name");
19002
19003 assert_eq!(sentinel.kind(), "function_definition");
19004 assert_eq!(callable.kind(), "identifier");
19005 assert!(
19006 cpp_sentinel_macro_parts(sentinel, source).is_some(),
19007 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
19008 );
19009 }
19010
19011 #[test]
19012 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
19013 let source = r#"
19014#define CPPCHECKLIB
19015class Library {
19016 struct Container {
19017 CPPCHECKLIB static std::string toString(Yield yield);
19018 CPPCHECKLIB static std::string toString(Action action);
19019 };
19020};
19021"#;
19022 let mut parser = tree_sitter::Parser::new();
19023 parser
19024 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19025 .unwrap();
19026 let tree = parser.parse(source, None).unwrap();
19027 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
19028 let parsed = parse_cpp_file(&file, source, &tree);
19029 assert!(
19030 parsed
19031 .declarations()
19032 .iter()
19033 .all(|unit| unit.fq_name() != "Library$Container.std"),
19034 "the qualified return-type namespace must not become a field: {:#?}",
19035 parsed.declarations()
19036 );
19037 for expected in ["(Yield)", "(Action)"] {
19038 assert!(
19039 parsed.declarations().iter().any(|unit| {
19040 unit.is_function()
19041 && unit.fq_name() == "Library$Container.toString"
19042 && unit.signature() == Some(expected)
19043 }),
19044 "recovered toString overload {expected} is missing: {:#?}",
19045 parsed.declarations()
19046 );
19047 }
19048 }
19049
19050 #[test]
19051 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
19052 let source = r#"
19053#define SIMPLECPP_LIB
19054namespace simplecpp {
19055using TokenString = std::string;
19056struct Location { int line{}; };
19057class SIMPLECPP_LIB Token {
19058 TokenString prefix;
19059 void prefix_method() {}
19060 public:
19061 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
19062 whitespaceahead(wsahead), location(loc), string(s)
19063 // The comment must not hide the constructor body from recovery.
19064 {
19065 flags();
19066 }
19067 TokenString string;
19068 bool whitespaceahead;
19069 Location location;
19070 Token *previous{};
19071 private:
19072 void flags() {
19073 whitespaceahead = true;
19074 }
19075};
19076}
19077"#;
19078 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
19079
19080 let location_fields = parsed
19081 .declarations()
19082 .iter()
19083 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
19084 .collect::<Vec<_>>();
19085 assert_eq!(
19086 location_fields.len(),
19087 1,
19088 "location should have one class-owned declaration: {:#?}",
19089 parsed.declarations()
19090 );
19091 assert!(
19092 location_fields[0].is_field(),
19093 "location has wrong kind: {:#?}",
19094 parsed.declarations()
19095 );
19096 assert!(
19097 parsed.declarations().iter().all(|unit| {
19098 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
19099 })
19100 );
19101 assert!(
19102 parsed.declarations().iter().all(|unit| {
19103 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
19104 })
19105 );
19106 assert!(
19107 parsed
19108 .declarations()
19109 .iter()
19110 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
19111 );
19112 assert!(
19113 parsed
19114 .declarations()
19115 .iter()
19116 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
19117 "the recovered class must retain its constructor: {:#?}",
19118 parsed.declarations()
19119 );
19120 assert!(
19121 parsed
19122 .declarations()
19123 .iter()
19124 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
19125 );
19126 assert!(parsed.declarations().iter().any(|unit| {
19127 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
19128 }));
19129 let constructor = parsed
19130 .declarations()
19131 .iter()
19132 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
19133 .expect("recovered constructor");
19134 let constructor_start = source.find("Token(const").expect("constructor start");
19135 let constructor_end = source
19136 .get(
19137 ..source
19138 .find(" TokenString string;")
19139 .expect("constructor end"),
19140 )
19141 .expect("constructor slice")
19142 .trim_end()
19143 .len();
19144 assert!(
19145 parsed
19146 .navigation_ranges
19147 .get(constructor)
19148 .is_some_and(|ranges| {
19149 ranges.iter().any(|range| {
19150 range.start_byte == constructor_start && range.end_byte == constructor_end
19151 })
19152 }),
19153 "constructor navigation must span the full body: {:#?}",
19154 parsed.navigation_ranges
19155 );
19156 assert_eq!(
19157 parsed
19158 .signature_metadata
19159 .get(constructor)
19160 .and_then(|metadata| metadata.first())
19161 .and_then(SignatureMetadata::callable_linkage),
19162 Some(CallableLinkage::External)
19163 );
19164 let token_class = parsed
19165 .declarations()
19166 .iter()
19167 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
19168 .expect("recovered Token class");
19169 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
19170 assert!(
19171 parsed
19172 .navigation_ranges
19173 .get(token_class)
19174 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
19175 "class navigation must include the terminating semicolon: {:#?}",
19176 parsed.navigation_ranges
19177 );
19178 }
19179
19180 #[test]
19181 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
19182 let source = r#"
19183#define SIMPLECPP_LIB
19184namespace simplecpp {
19185using TokenString = std::string;
19186class Macro;
19187struct Location {
19188 unsigned int fileIndex{};
19189 unsigned int line{};
19190 unsigned int col{};
19191};
19192struct Output {
19193 int type;
19194};
19195class SIMPLECPP_LIB Token {
19196 public:
19197 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
19198 whitespaceahead(wsahead), location(loc), string(s) {
19199 flags();
19200 }
19201 Token(const Token &tok) :
19202 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
19203 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
19204 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
19205 Token &operator=(const Token &tok) = delete;
19206 const TokenString& str() const { return string; }
19207 void setstr(const std::string &s) { string = s; flags(); }
19208 bool isOneOf(const char ops[]) const;
19209 TokenString macro;
19210 char op;
19211 bool comment;
19212 bool name;
19213 bool number;
19214 bool whitespaceahead;
19215 Location location;
19216 Token *previous{};
19217 Token *next{};
19218 private:
19219 void flags() {
19220 name = !string.empty();
19221 comment = false;
19222 number = false;
19223 op = 0;
19224 }
19225 TokenString string;
19226};
19227}
19228struct Following {
19229 int type;
19230};
19231class SIMPLECPP_LIB Later {
19232 public:
19233 Later(int value) : value(value) {}
19234 int value;
19235};
19236"#;
19237 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
19238 assert!(
19239 parsed
19240 .declarations()
19241 .iter()
19242 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
19243 );
19244 assert!(
19245 !parsed
19246 .declarations()
19247 .iter()
19248 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
19249 );
19250 assert!(
19251 parsed
19252 .declarations()
19253 .iter()
19254 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
19255 );
19256 assert!(
19257 !parsed
19258 .declarations()
19259 .iter()
19260 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
19261 );
19262 assert!(
19263 parsed
19264 .declarations()
19265 .iter()
19266 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
19267 );
19268 assert!(
19269 parsed
19270 .declarations()
19271 .iter()
19272 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
19273 );
19274 assert!(
19275 parsed
19276 .declarations()
19277 .iter()
19278 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
19279 );
19280 assert!(
19281 parsed
19282 .declarations()
19283 .iter()
19284 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
19285 );
19286 assert!(
19287 parsed
19288 .declarations()
19289 .iter()
19290 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
19291 );
19292 assert!(
19293 parsed
19294 .declarations()
19295 .iter()
19296 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
19297 );
19298 assert!(parsed.declarations().iter().all(|unit| {
19299 !matches!(
19300 unit.fq_name().as_str(),
19301 "simplecpp.Token.Following" | "simplecpp.Token.Later"
19302 )
19303 }));
19304 assert!(
19305 !parsed
19306 .declarations()
19307 .iter()
19308 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
19309 "the following struct must remain outside the recovered Token class"
19310 );
19311 }
19312
19313 #[test]
19314 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
19315 let source = r#"
19316#define SIMPLECPP_LIB
19317namespace {
19318namespace simplecpp {
19319using TokenString = std::string;
19320struct Location { int line{}; };
19321class SIMPLECPP_LIB HiddenToken {
19322 public:
19323 HiddenToken(const TokenString &s, const Location &loc) :
19324 location(loc), string(s) {
19325 flags();
19326 }
19327 TokenString string;
19328 Location location;
19329 HiddenToken *previous{};
19330 private:
19331 void flags() {}
19332};
19333}
19334}
19335"#;
19336 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
19337 let constructor = parsed
19338 .declarations()
19339 .iter()
19340 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
19341 .expect("recovered anonymous-namespace constructor");
19342 assert_eq!(
19343 parsed
19344 .signature_metadata
19345 .get(constructor)
19346 .and_then(|metadata| metadata.first())
19347 .and_then(SignatureMetadata::callable_linkage),
19348 Some(CallableLinkage::Internal)
19349 );
19350 }
19351
19352 #[test]
19353 fn macro_qualified_static_field_keeps_real_declarator() {
19354 let source = r#"#define JSON_INLINE_VARIABLE
19355struct Reader {
19356static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
19357static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
19358static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
19359};"#;
19360 let mut parser = tree_sitter::Parser::new();
19361 parser
19362 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19363 .unwrap();
19364 let tree = parser.parse(source, None).unwrap();
19365 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
19366 let parsed = parse_cpp_file(&file, source, &tree);
19367 for expected in [
19368 "Reader.npos",
19369 "Reader.other",
19370 "Reader.pointer",
19371 "Reader.reference",
19372 ] {
19373 assert!(
19374 parsed
19375 .declarations()
19376 .iter()
19377 .any(|unit| unit.is_field() && unit.fq_name() == expected),
19378 "real macro-decorated field {expected} is missing: {:#?}",
19379 parsed.declarations()
19380 );
19381 }
19382 assert!(
19383 parsed
19384 .declarations()
19385 .iter()
19386 .all(|unit| unit.fq_name() != "Reader.std"),
19387 "qualified type prefix became a pseudo-field: {:#?}",
19388 parsed.declarations()
19389 );
19390 let root = tree.root_node();
19391 let mut stack = vec![root];
19392 let mut signatures = Vec::new();
19393 while let Some(current) = stack.pop() {
19394 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
19395 {
19396 signatures.extend(
19397 declarators
19398 .into_iter()
19399 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
19400 );
19401 }
19402 let mut cursor = current.walk();
19403 stack.extend(current.named_children(&mut cursor));
19404 }
19405 signatures.sort();
19406 assert_eq!(
19407 signatures,
19408 [
19409 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
19410 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
19411 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
19412 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
19413 ]
19414 );
19415 }
19416
19417 fn member_function_linkage(source: &str) -> CallableLinkage {
19418 let mut parser = tree_sitter::Parser::new();
19419 parser
19420 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19421 .unwrap();
19422 let tree = parser.parse(source, None).unwrap();
19423 let ancestry = ParentIndex::new(tree.root_node());
19424 let mut stack = vec![tree.root_node()];
19425 while let Some(node) = stack.pop() {
19426 if node.kind() == "function_definition" {
19427 let mut current = node.parent();
19428 while let Some(parent) = current {
19429 if matches!(
19430 parent.kind(),
19431 "class_specifier" | "struct_specifier" | "union_specifier"
19432 ) {
19433 return cpp_callable_linkage(node, source, &ancestry);
19434 }
19435 current = parent.parent();
19436 }
19437 }
19438 let mut cursor = node.walk();
19439 stack.extend(node.named_children(&mut cursor));
19440 }
19441 panic!("fixture has no member function definition");
19442 }
19443
19444 #[test]
19445 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
19446 assert_eq!(
19447 member_function_linkage("struct Named { int method() { return 1; } };"),
19448 CallableLinkage::External
19449 );
19450 assert_eq!(
19451 member_function_linkage(
19452 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
19453 ),
19454 CallableLinkage::Internal
19455 );
19456 assert_eq!(
19457 member_function_linkage("struct { int method() { return 1; } } instance;"),
19458 CallableLinkage::Internal
19459 );
19460 assert_eq!(
19461 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
19462 CallableLinkage::Internal
19463 );
19464 }
19465
19466 #[test]
19467 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
19468 let source = r#"
19469#ifndef PROTON_VALUE_HPP
19470#define PROTON_VALUE_HPP
19471namespace proton {
19472namespace internal {
19473class value_base {
19474 protected:
19475 internal::data& data();
19476 internal::data data_;
19477 friend class codec::encoder;
19478 friend class codec::decoder;
19479};
19480}
19481class value : public internal::value_base, private internal::comparable<value> {
19482 private:
19483 template<class T, class U=void> struct assignable :
19484 public std::enable_if<codec::is_encodable<T>::value, U> {};
19485 template<class U> struct assignable<value, U> {};
19486 public:
19487 PN_CPP_EXTERN value();
19488 PN_CPP_EXTERN value(const value&);
19489 PN_CPP_EXTERN value& operator=(const value&);
19490 PN_CPP_EXTERN value(value&&);
19491 PN_CPP_EXTERN value& operator=(value&&);
19492 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
19493 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
19494 codec::encoder e(*this);
19495 e << x;
19496 return *this;
19497 }
19498 PN_CPP_EXTERN type_id type() const;
19499 PN_CPP_EXTERN bool empty() const;
19500 PN_CPP_EXTERN void clear();
19501 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
19502 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
19503 friend PN_CPP_EXTERN void swap(value&, value&);
19504 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
19505 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
19506 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
19507 value(pn_data_t* d);
19508 void reset(pn_data_t* d = 0);
19509};
19510}
19511#endif
19512"#;
19513 let mut parser = tree_sitter::Parser::new();
19514 parser
19515 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19516 .unwrap();
19517 let tree = parser.parse(source, None).unwrap();
19518 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
19519 let parsed = parse_cpp_file(&file, source, &tree);
19520 let macro_constructors = parsed
19521 .signature_metadata
19522 .iter()
19523 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
19524 .flat_map(|(_, metadata)| metadata)
19525 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
19526 .collect::<Vec<_>>();
19527
19528 assert_eq!(
19529 macro_constructors.len(),
19530 3,
19531 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
19532 parsed.declarations()
19533 );
19534 assert!(
19535 macro_constructors.iter().all(|metadata| {
19536 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
19537 }),
19538 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
19539 );
19540 }
19541
19542 #[test]
19543 fn recovered_export_class_typedef_uses_displaced_alias_name() {
19544 let source = r#"
19545namespace spi {
19546class Filter {
19547public:
19548 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
19549};
19550}
19551namespace filter {
19552class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
19553{
19554public:
19555 typedef spi::Filter BASE_CLASS;
19556 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
19557 BEGIN_LOG4CXX_CAST_MAP()
19558 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
19559 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
19560 END_LOG4CXX_CAST_MAP()
19561 FilterDecision decide() const;
19562};
19563}
19564"#;
19565 let mut parser = tree_sitter::Parser::new();
19566 parser
19567 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19568 .unwrap();
19569 let tree = parser.parse(source, None).unwrap();
19570 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
19571 let parsed = parse_cpp_file(&file, source, &tree);
19572 assert!(
19573 parsed.declarations().iter().any(|unit| {
19574 unit.is_class()
19575 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
19576 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
19577 }),
19578 "the displaced typedef alias must retain its declared name: {:#?}",
19579 parsed.declarations()
19580 );
19581 assert!(
19582 parsed
19583 .declarations()
19584 .iter()
19585 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
19586 "the qualified underlying type must not become a false nested alias: {:#?}",
19587 parsed.declarations()
19588 );
19589 }
19590
19591 #[test]
19592 fn exported_single_base_recovery_uses_displaced_class_name() {
19593 let source = r#"
19594class CORE_EXPORT QgsPoint : public AbstractGeometry
19595{
19596 Q_GADGET
19597
19598 Q_PROPERTY( double x READ x WRITE setX )
19599 Q_PROPERTY( double y READ y WRITE setY )
19600 Q_PROPERTY( double z READ z WRITE setZ )
19601 Q_PROPERTY( double m READ m WRITE setM )
19602
19603 public:
19604#ifndef SIP_RUN
19605 QgsPoint(
19606 double x = std::numeric_limits<double>::quiet_NaN(),
19607 double y = std::numeric_limits<double>::quiet_NaN(),
19608 double z = std::numeric_limits<double>::quiet_NaN(),
19609 double m = std::numeric_limits<double>::quiet_NaN(),
19610 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
19611 );
19612#else
19613 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 )];
19614 % MethodCode
19615 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
19616 {
19617 int state;
19618 sipIsErr = 0;
19619 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
19620 if ( !sipIsErr )
19621 {
19622 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
19623 }
19624 sipReleaseType( p, sipType_QgsPointXY, state );
19625 }
19626 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
19627 {
19628 int state;
19629 sipIsErr = 0;
19630
19631 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
19632 if ( !sipIsErr )
19633 {
19634 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
19635 }
19636 sipReleaseType( p, sipType_QPointF, state );
19637 }
19638 else if (
19639 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
19640 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
19641 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
19642 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
19643 {
19644 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
19645 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
19646 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
19647 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
19648 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
19649 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
19650 }
19651 else // Invalid ctor arguments
19652 {
19653 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
19654 sipIsErr = 1;
19655 }
19656 % End
19657#endif
19658
19659 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
19660 explicit QgsPoint( QPointF p ) SIP_SKIP;
19661 explicit QgsPoint(
19662 Qgis::WkbType wkbType,
19663 double x = std::numeric_limits<double>::quiet_NaN(),
19664 double y = std::numeric_limits<double>::quiet_NaN(),
19665 double z = std::numeric_limits<double>::quiet_NaN(),
19666 double m = std::numeric_limits<double>::quiet_NaN()
19667 ) SIP_SKIP;
19668 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19669 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
19670 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19671#ifndef SIP_RUN
19672 private:
19673 bool fuzzyHelper(
19674 double epsilon,
19675 const AbstractGeometry &other,
19676 bool is3DFlag,
19677 bool isMeasureFlag
19678 ) const
19679 {
19680 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
19681 }
19682#endif
19683};
19684class Ordinary : public Base { public: Ordinary(); };
19685class API_EXPORT Plain { public: Plain(); };
19686class API_EXPORT : public Base {};
19687class
19688PN_CPP_CLASS_EXTERN Sender : public Link {
19689 Sender();
19690 struct impl;
19691 struct impl& get_impl() const;
19692};
19693class thread_ctx_t {};
19694class ctx_t ZMQ_FINAL : public thread_ctx_t {
19695 bool start();
19696};
19697"#;
19698 let mut parser = tree_sitter::Parser::new();
19699 parser
19700 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19701 .unwrap();
19702 let tree = parser.parse(source, None).unwrap();
19703 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
19704 let parsed = parse_cpp_file(&file, source, &tree);
19705 let declarations = parsed.declarations();
19706
19707 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
19708 assert!(
19709 declarations
19710 .iter()
19711 .any(|unit| unit.is_class() && unit.fq_name() == expected),
19712 "missing recovered class {expected}: {declarations:#?}"
19713 );
19714 }
19715 let qgs_point = declarations
19716 .iter()
19717 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
19718 .expect("recovered QgsPoint class");
19719 assert_eq!(
19720 parsed.raw_supertypes.get(qgs_point),
19721 Some(&vec!["AbstractGeometry".to_string()]),
19722 "single-base export recovery must retain its displaced base"
19723 );
19724 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
19725 assert!(
19726 parsed
19727 .navigation_ranges
19728 .get(qgs_point)
19729 .is_some_and(|ranges| {
19730 !ranges.is_empty()
19731 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
19732 }),
19733 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
19734 parsed.navigation_ranges.get(qgs_point)
19735 );
19736 let sender = declarations
19737 .iter()
19738 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
19739 .expect("recovered Sender class");
19740 assert_eq!(
19741 parsed.raw_supertypes.get(sender),
19742 Some(&vec!["Link".to_string()]),
19743 "post-declarator export recovery must retain its displaced base"
19744 );
19745 let recovered_member = declarations
19746 .iter()
19747 .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
19748 .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
19749 assert_eq!(
19750 parsed
19751 .signature_metadata
19752 .get(recovered_member)
19753 .and_then(|metadata| metadata.first())
19754 .and_then(SignatureMetadata::callable_linkage),
19755 Some(CallableLinkage::External),
19756 "a named recovered class's members have external linkage"
19757 );
19758 let ctx = declarations
19759 .iter()
19760 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
19761 .expect("recovered ctx_t class");
19762 assert_eq!(
19763 parsed.raw_supertypes.get(ctx),
19764 Some(&vec!["thread_ctx_t".to_string()]),
19765 "postfix export-macro recovery must retain its displaced base"
19766 );
19767 assert!(
19768 declarations.iter().any(|unit| {
19769 unit.is_function()
19770 && unit.fq_name() == "QgsPoint.QgsPoint"
19771 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
19772 }),
19773 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
19774 );
19775 assert!(
19776 declarations.iter().all(|unit| {
19777 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
19778 }),
19779 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
19780 );
19781 }
19782
19783 #[test]
19784 fn function_like_export_macro_classes_keep_names_and_base_edges() {
19785 let source = r#"
19789namespace api {
19790class PROJECT_PUBLIC_API(2, 0) Prelude {
19791 public:
19792 Prelude();
19793};
19794class PROJECT_PUBLIC_API(2, 0) Base {
19795 public:
19796 Base(int value);
19797};
19798class PROJECT_PUBLIC_API(2, 0) Mixin {
19799 public:
19800 Mixin();
19801};
19802class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
19803 public:
19804 Adopted(int value);
19805};
19806class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
19807 public:
19808 Derived(int value);
19809};
19810class PROJECT_PUBLIC_API(2, 0) Solo final {
19811 public:
19812 Solo();
19813};
19814class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
19815 public:
19816 Blended(int value);
19817};
19818class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
19819 public:
19820 Woven(int value);
19821};
19822} // namespace api
19823"#;
19824 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
19825 let declarations = parsed.declarations();
19826 let class_named = |name: &str| {
19827 declarations
19828 .iter()
19829 .find(|unit| unit.is_class() && unit.fq_name() == name)
19830 .unwrap_or_else(|| {
19831 panic!("missing function-like export macro class {name}: {declarations:#?}")
19832 })
19833 };
19834 let base = class_named("api.Base");
19835 class_named("api.Prelude");
19836 class_named("api.Mixin");
19837
19838 assert_eq!(
19839 parsed.raw_supertypes.get(class_named("api.Adopted")),
19840 Some(&vec!["Base".to_string()])
19841 );
19842 assert_eq!(
19843 parsed.raw_supertypes.get(class_named("api.Derived")),
19844 Some(&vec!["Base".to_string()])
19845 );
19846 assert_eq!(
19847 parsed.raw_supertypes.get(class_named("api.Solo")),
19848 None,
19849 "a final class without a base list must not invent a supertype"
19850 );
19851 assert_eq!(
19852 parsed.raw_supertypes.get(class_named("api.Blended")),
19853 Some(&vec!["Base".to_string(), "Mixin".to_string()])
19854 );
19855 assert_eq!(
19856 parsed.raw_supertypes.get(class_named("api.Woven")),
19857 Some(&vec!["Base".to_string(), "Mixin".to_string()])
19858 );
19859 assert!(
19860 declarations
19861 .iter()
19862 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19863 "the export macro must not become a declaration: {declarations:#?}"
19864 );
19865 assert!(
19866 declarations.iter().all(|unit| !matches!(
19867 unit.identifier(),
19868 "final" | "public" | "protected" | "private"
19869 )),
19870 "the head specifiers must not become declarations: {declarations:#?}"
19871 );
19872 assert!(
19873 parsed
19874 .navigation_ranges
19875 .get(base)
19876 .is_some_and(|ranges| !ranges.is_empty()),
19877 "the recovered base must retain a navigable declaration range"
19878 );
19879 }
19880
19881 #[test]
19882 fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
19883 let source = r#"
19889namespace api {
19890class PROJECT_PUBLIC_API(2, 0) Base {
19891 public:
19892 Base();
19893};
19894class PROJECT_PUBLIC_API(2, 0) Mixin {
19895 public:
19896 Mixin();
19897};
19898class PROJECT_PUBLIC_API(2, 0) Name {
19899 public:
19900 Name();
19901};
19902class PROJECT_PUBLIC_API(2, 0) X509_CA final {
19903 public:
19904 X509_CA();
19905};
19906class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
19907 public:
19908 HSS_LMS_KEY();
19909};
19910class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
19911 public:
19912 GOST_3410();
19913};
19914class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
19915 public:
19916 PKCS11_RSA();
19917};
19918class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
19919 public:
19920 Plain();
19921};
19922class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
19923 public:
19924 Decorated();
19925};
19926class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
19927 public:
19928 Layered();
19929};
19930} // namespace api
19931"#;
19932 let parsed = parse_cpp_declarations(source, "positional-export.hpp");
19933 let declarations = parsed.declarations();
19934 let class_named = |name: &str| {
19935 declarations
19936 .iter()
19937 .find(|unit| unit.is_class() && unit.fq_name() == name)
19938 .unwrap_or_else(|| {
19939 panic!("missing function-like export macro class {name}: {declarations:#?}")
19940 })
19941 };
19942 for (name, bases) in [
19943 ("api.Name", None),
19944 ("api.X509_CA", None),
19945 ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
19946 ("api.GOST_3410", Some(vec!["Base"])),
19947 ("api.PKCS11_RSA", None),
19948 ("api.Plain", None),
19949 ("api.Decorated", Some(vec!["Base"])),
19950 ("api.Layered", Some(vec!["Base", "Mixin"])),
19951 ] {
19952 let expected =
19953 bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
19954 assert_eq!(
19955 parsed.raw_supertypes.get(class_named(name)),
19956 expected.as_ref(),
19957 "{name}"
19958 );
19959 }
19960 assert!(
19961 declarations.iter().all(|unit| !matches!(
19962 unit.identifier(),
19963 "PROJECT_PUBLIC_API"
19964 | "OTHER_MACRO"
19965 | "FIRST_MACRO"
19966 | "SECOND_MACRO"
19967 | "final"
19968 | "public"
19969 )),
19970 "macros and head specifiers must not become declarations: {declarations:#?}"
19971 );
19972 }
19973
19974 #[test]
19975 fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
19976 let source = r#"
19982namespace api {
19983
19984/**
19985* Doc comment
19986*/
19987class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
19988 public:
19989 /**
19990 * Construct from a point.
19991 */
19992 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
19993
19994#if defined(PROJECT_HAS_LEGACY_POINT)
19995 /**
19996 * Construct from a legacy point.
19997 */
19998 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
19999#endif
20000
20001 std::string algo_name() const override;
20002
20003 AlgorithmIdentifier algorithm_identifier() const override;
20004};
20005
20006}
20007"#;
20008 let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
20009 let declarations = parsed.declarations();
20010 let class = declarations
20011 .iter()
20012 .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
20013 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
20014 assert_eq!(
20015 parsed.raw_supertypes.get(class),
20016 Some(&vec!["BaseKey".to_string()]),
20017 "the virtual base is the class's base: {declarations:#?}"
20018 );
20019 for member in [
20020 "api.VirtualBased.algo_name",
20021 "api.VirtualBased.algorithm_identifier",
20022 ] {
20023 assert!(
20024 declarations
20025 .iter()
20026 .any(|unit| unit.is_function() && unit.fq_name() == member),
20027 "{member} must be owned by the recovered class: {declarations:#?}"
20028 );
20029 }
20030 assert!(
20031 declarations
20032 .iter()
20033 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
20034 "an unrecovered head must not mint a macro-named class: {declarations:#?}"
20035 );
20036 }
20037
20038 #[test]
20039 fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
20040 let source = r#"
20047namespace api {
20048
20049DIAGNOSTIC_PUSH
20050DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
20051
20052class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
20053 public virtual api::Inner::Key {
20054 public:
20055 std::string algo_name() const override;
20056};
20057
20058DIAGNOSTIC_POP
20059
20060}
20061"#;
20062 let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
20063 let declarations = parsed.declarations();
20064 let class = declarations
20065 .iter()
20066 .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
20067 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
20068 assert_eq!(
20069 parsed.raw_supertypes.get(class),
20070 Some(&vec![
20071 "api::Outer::Key".to_string(),
20072 "api::Inner::Key".to_string()
20073 ]),
20074 "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
20075 );
20076 assert!(
20077 declarations
20078 .iter()
20079 .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
20080 "the member is owned by the recovered class: {declarations:#?}"
20081 );
20082 assert!(
20083 declarations
20084 .iter()
20085 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
20086 "the macro invocation must not mint a declaration: {declarations:#?}"
20087 );
20088 }
20089
20090 #[test]
20091 fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
20092 let fixture = |head: &str, name: &str| {
20095 format!(
20096 r#"
20097namespace api {{
20098class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
20099 public:
20100 /** Return a descriptive string. */
20101 const char* what() const noexcept override {{ return m_msg.c_str(); }}
20102
20103 /** Return the type of error. */
20104 virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
20105
20106 /** Return an associated error code. */
20107 virtual int error_code() const noexcept {{ return 0; }}
20108
20109 /** Avoid throwing the base directly. */
20110 explicit Exception(std::string_view msg);
20111
20112 /** Avoid throwing the base directly. */
20113 Exception(const char* prefix, std::string_view msg);
20114
20115 /** Avoid throwing the base directly. */
20116 Exception(std::string_view msg, const std::exception& e);
20117
20118 private:
20119 std::string m_msg;
20120}};
20121
20122class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
20123 public:
20124 explicit {name}(std::string_view msg);
20125
20126 explicit {name}(std::string_view msg, std::string_view where);
20127
20128 {name}(std::string_view msg, const std::exception& e);
20129
20130 ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
20131}};
20132}} // namespace api
20133"#
20134 )
20135 };
20136 for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
20137 let source = fixture(head, name);
20138 let mut parser = Parser::new();
20139 parser
20140 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20141 .expect("set C++ grammar");
20142 let tree = parser.parse(&source, None).expect("parse fixture");
20143 let mut embedded = Vec::new();
20144 let mut stack = vec![tree.root_node()];
20145 while let Some(node) = stack.pop() {
20146 embedded.extend(
20147 recover_embedded_function_like_export_classes(node, &source)
20148 .into_iter()
20149 .map(|recovered| (recovered.name, recovered.raw_supertypes)),
20150 );
20151 let mut cursor = node.walk();
20152 stack.extend(node.named_children(&mut cursor));
20153 }
20154 assert!(
20155 embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
20156 "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
20157 tree.root_node().to_sexp()
20158 );
20159 assert!(
20160 embedded
20161 .iter()
20162 .all(|(recovered, _)| recovered != "OTHER_MACRO"),
20163 "{head}: the object-like macro is not a class: {embedded:#?}"
20164 );
20165
20166 let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
20167 let declarations = parsed.declarations();
20168 let class = declarations
20169 .iter()
20170 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
20171 .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
20172 assert_eq!(
20173 parsed.raw_supertypes.get(class),
20174 Some(&vec!["Exception".to_string()]),
20175 "{head}"
20176 );
20177 assert!(
20178 declarations
20179 .iter()
20180 .all(|unit| unit.identifier() != "OTHER_MACRO"),
20181 "{head}: the object-like macro must not become a declaration: {declarations:#?}"
20182 );
20183 }
20184 }
20185
20186 #[test]
20187 fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
20188 let source = r#"
20198namespace api {
20199class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
20200 public virtual Botan::EC_PublicKey {
20201 public:
20202 std::string algo_name() const override { return "ECDSA"; }
20203};
20204} // namespace api
20205"#;
20206 let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
20207 let declarations = parsed.declarations();
20208 assert!(
20209 declarations
20210 .iter()
20211 .all(|unit| !unit.identifier().is_empty()),
20212 "no declaration may carry an empty name: {declarations:#?}"
20213 );
20214 assert!(
20215 declarations
20216 .iter()
20217 .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
20218 "macros and head specifiers must not become declarations: {declarations:#?}"
20219 );
20220 let class = declarations
20221 .iter()
20222 .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
20223 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
20224 assert_eq!(
20225 parsed.raw_supertypes.get(class),
20226 Some(&vec![
20227 "Botan::TPM2::PublicKey".to_string(),
20228 "Botan::EC_PublicKey".to_string()
20229 ]),
20230 "both qualified virtual bases are bases: {declarations:#?}"
20231 );
20232 assert!(
20233 declarations
20234 .iter()
20235 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
20236 "the head must not mint a macro-named class: {declarations:#?}"
20237 );
20238 }
20239
20240 #[test]
20241 fn function_like_export_class_survives_a_preceding_malformed_body() {
20242 let source = r#"
20243namespace api {
20244class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
20245 public:
20246 /** Return a descriptive string. */
20247 const char* what() const noexcept override { return m_msg.c_str(); }
20248
20249 /** Return the type of error. */
20250 virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
20251
20252 /** Return an associated error code. */
20253 virtual int error_code() const noexcept { return 0; }
20254
20255 /** Avoid throwing the base directly. */
20256 explicit Exception(std::string_view msg);
20257
20258 /** Avoid throwing the base directly. */
20259 Exception(const char* prefix, std::string_view msg);
20260
20261 /** Avoid throwing the base directly. */
20262 Exception(std::string_view msg, const std::exception& e);
20263
20264 private:
20265 std::string m_msg;
20266};
20267
20268class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
20269 public:
20270 explicit Invalid_Argument(std::string_view msg);
20271
20272 explicit Invalid_Argument(std::string_view msg, std::string_view where);
20273
20274 Invalid_Argument(std::string_view msg, const std::exception& e);
20275
20276 ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
20277};
20278} // namespace api
20279"#;
20280 let mut parser = Parser::new();
20281 parser
20282 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20283 .expect("set C++ grammar");
20284 let tree = parser.parse(source, None).expect("parse fixture");
20285 let mut stack = vec![tree.root_node()];
20286 let mut saw_embedded_shape = false;
20287 while let Some(node) = stack.pop() {
20288 saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
20289 .iter()
20290 .any(|recovered| recovered.name == "Invalid_Argument");
20291 let mut cursor = node.walk();
20292 stack.extend(node.named_children(&mut cursor));
20293 }
20294 assert!(
20295 saw_embedded_shape,
20296 "fixture must retain the embedded error geometry: {}",
20297 tree.root_node().to_sexp()
20298 );
20299
20300 let parsed = parse_cpp_file(
20301 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
20302 source,
20303 &tree,
20304 );
20305 let declarations = parsed.declarations();
20306 let exception = declarations
20307 .iter()
20308 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
20309 .expect("qualified-base export class");
20310 let invalid = declarations
20311 .iter()
20312 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
20313 .expect("class embedded in the preceding malformed body");
20314
20315 assert_eq!(
20316 parsed.raw_supertypes.get(exception),
20317 Some(&vec!["std::exception".to_string()])
20318 );
20319 assert_eq!(
20320 parsed.raw_supertypes.get(invalid),
20321 Some(&vec!["Exception".to_string()])
20322 );
20323 assert!(
20324 parsed.materialization_records.iter().any(|record| matches!(
20325 record,
20326 MaterializationRecord::RecoveredDeclaration { unit, .. }
20327 if unit == invalid
20328 )),
20329 "the embedded class must retain recovery provenance: {:#?}",
20330 parsed.materialization_records
20331 );
20332 }
20333
20334 #[test]
20335 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
20336 let source = r#"
20337public:
20338 explicit Lookup_Error(std::string_view err) : Exception(err) {}
20339
20340 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
20341"#;
20342 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
20343 .expect("reparse merged constructor body");
20344 let (range, body) =
20345 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
20346 .unwrap_or_else(|| {
20347 panic!(
20348 "the merged constructor must retain its structured declarator/body: {}",
20349 tree.root_node().to_sexp()
20350 )
20351 });
20352 assert_eq!(
20353 source.get(range).expect("constructor range"),
20354 "Lookup_Error(std::string_view err) : Exception(err) {}"
20355 );
20356 assert_eq!(node_text(body, source), "{}");
20357 }
20358
20359 #[test]
20360 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
20361 let positive_source =
20362 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
20363 let mut parser = tree_sitter::Parser::new();
20364 parser
20365 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20366 .unwrap();
20367 let positive_tree = parser.parse(positive_source, None).unwrap();
20368 assert!(cpp_reparsed_members_are_indexable(
20369 positive_tree.root_node(),
20370 positive_source
20371 ));
20372
20373 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
20374 let negative_tree = parser.parse(negative_source, None).unwrap();
20375 assert!(!cpp_reparsed_members_are_indexable(
20376 negative_tree.root_node(),
20377 negative_source
20378 ));
20379 }
20380
20381 #[test]
20382 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
20383 let copy_control_source = r#"
20384public:
20385 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
20386 explicit Token(const Token* tok);
20387 ~Token();
20388 Token* astOperand1() { return nullptr; }
20389"#;
20390 let constraint_source = r#"
20391private:
20392 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
20393 static T *tokAtImpl(T *tok, int index) {
20394 return tok;
20395 }
20396
20397 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
20398 static T *linkAtImpl(T *tok, int index) {
20399 return tok;
20400 }
20401
20402public:
20403 int late() const { return 1; }
20404"#;
20405 let mut parser = tree_sitter::Parser::new();
20406 parser
20407 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20408 .unwrap();
20409 let copy_control_tree = parser
20410 .parse(copy_control_source, None)
20411 .expect("parse copy-control fixture");
20412 assert!(
20413 copy_control_tree.root_node().has_error(),
20414 "fixture must exercise adjacent copy-control recovery"
20415 );
20416 assert!(
20417 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
20418 "a complete late getter must remain recoverable after adjacent copy-control declarations"
20419 );
20420 let mut cursor = copy_control_tree.root_node().walk();
20421 assert!(
20422 copy_control_tree
20423 .root_node()
20424 .named_children(&mut cursor)
20425 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
20426 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
20427 copy_control_tree.root_node().to_sexp()
20428 );
20429 let constraint_tree = parser
20430 .parse(constraint_source, None)
20431 .expect("parse constraint-macro fixture");
20432 assert!(constraint_tree.root_node().has_error());
20433 assert!(
20434 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
20435 "complete constraint-macro members must not hide a later ordinary member"
20436 );
20437 let mut cursor = constraint_tree.root_node().walk();
20438 assert!(
20439 constraint_tree
20440 .root_node()
20441 .named_children(&mut cursor)
20442 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
20443 child,
20444 constraint_source
20445 )),
20446 "fixture must retain the split constraint-macro prefix/function geometry"
20447 );
20448 }
20449
20450 #[test]
20451 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
20452 let source = r#"
20453struct Analyzer {
20454 struct Action {
20455 Action() = default;
20456 Action(const Action&) = default;
20457 Action& operator=(const Action& rhs) & = default;
20458
20459 template<class T,
20460 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
20461 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
20462 // NOLINTNEXTLINE(google-explicit-constructor)
20463 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
20464 {}
20465
20466 enum : std::uint16_t { None = 0, Read = (1 << 0) };
20467 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
20468
20469 private:
20470 unsigned int mFlag{};
20471 };
20472
20473 enum class Direction : unsigned char { Forward, Reverse };
20474 virtual Action analyze(Direction d) const = 0;
20475};
20476"#;
20477 let mut parser = tree_sitter::Parser::new();
20478 parser
20479 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20480 .unwrap();
20481 let tree = parser.parse(source, None).unwrap();
20482 assert!(tree.root_node().has_error());
20483 let root = tree.root_node();
20484 let outer = root
20485 .named_children(&mut root.walk())
20486 .find(|child| child.kind() == "ERROR")
20487 .expect("fragmented Analyzer prefix");
20488 let outer_recovered =
20489 fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
20490 assert_eq!(outer_recovered.name, "Analyzer");
20491 let outer_tree = cpp_reparse_fragmented_class_body(
20492 source,
20493 outer_recovered.body.reparse_start,
20494 outer_recovered.body.reparse_end,
20495 )
20496 .expect("reparse Analyzer body");
20497 let outer_root = outer_tree.root_node();
20498 let action_prefix = outer_root
20499 .named_children(&mut outer_root.walk())
20500 .find(|child| child.kind() == "ERROR")
20501 .expect("fragmented Action prefix");
20502 let action_recovered = fragmented_class_body(action_prefix, source)
20503 .expect("structured Action fragment boundary");
20504 assert_eq!(action_recovered.name, "Action");
20505 let action_tree = cpp_reparse_fragmented_class_body(
20506 source,
20507 action_recovered.body.reparse_start,
20508 action_recovered.body.reparse_end,
20509 )
20510 .expect("reparse Action body");
20511 let action_root = action_tree.root_node();
20512 let macro_prefix = action_root
20513 .named_children(&mut action_root.walk())
20514 .find(|child| child.kind() == "ERROR")
20515 .expect("constraint macro prefix");
20516 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
20517 .expect("structured template macro prefix");
20518 let macro_companion =
20519 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
20520 assert!(
20521 cpp_reparsed_template_macro_constructor_companion_is_indexable(
20522 macro_companion,
20523 macro_parameter,
20524 source,
20525 ),
20526 "split constrained constructor must be admitted: {}",
20527 macro_companion.to_sexp()
20528 );
20529 assert!(
20530 cpp_reparsed_members_are_indexable(action_root, source),
20531 "complete Action body must pass the recovery gate: {}",
20532 action_tree.root_node().to_sexp()
20533 );
20534 assert!(
20535 cpp_reparsed_members_are_indexable(outer_root, source),
20536 "complete Analyzer body must pass the recovery gate: {}",
20537 outer_tree.root_node().to_sexp()
20538 );
20539 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
20540 let parsed = parse_cpp_file(&file, source, &tree);
20541 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
20542 assert!(
20543 parsed
20544 .declarations()
20545 .iter()
20546 .any(|unit| unit.fq_name() == expected),
20547 "missing recovered declaration {expected}: {:#?}",
20548 parsed.declarations()
20549 );
20550 }
20551 assert!(
20552 parsed
20553 .declarations()
20554 .iter()
20555 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
20556 "nested members must not remain flattened: {:#?}",
20557 parsed.declarations()
20558 );
20559 }
20560
20561 #[test]
20562 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
20563 let source = r#"
20564raw_hash_set& operator=(raw_hash_set&& that) {
20565 return move_assign(
20566 std::move(that),
20567 typename AllocTraits::propagate_on_container_move_assignment());
20568}
20569
20570iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
20571 return {};
20572}
20573
20574void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
20575
20576iterator insert(const_iterator hint, value_type&& value)
20577 ABSL_ATTRIBUTE_LIFETIME_BOUND {
20578 return {};
20579}
20580
20581friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
20582 return left.size() == right.size();
20583}
20584
20585static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
20586 return static_cast<slot_type*>(buffer);
20587}
20588
20589protected:
20590// Included-range recovery can attach this comment to the template prefix.
20591template <class K>
20592void AssertOnFind([[maybe_unused]] const K& key) {
20593 Check(key);
20594}
20595"#;
20596 let mut parser = tree_sitter::Parser::new();
20597 parser
20598 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20599 .unwrap();
20600 let tree = parser.parse(source, None).unwrap();
20601 assert!(
20602 tree.root_node().has_error(),
20603 "the fixture must exercise tree-sitter's errorful member shapes"
20604 );
20605 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
20606
20607 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
20608 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
20609 assert!(!cpp_reparsed_members_are_indexable(
20610 incomplete_tree.root_node(),
20611 incomplete_source
20612 ));
20613
20614 let outside_error_source = "int foo() stray_attribute {}\n";
20615 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
20616 assert!(outside_error_tree.root_node().has_error());
20617 assert!(!cpp_reparsed_members_are_indexable(
20618 outside_error_tree.root_node(),
20619 outside_error_source
20620 ));
20621
20622 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
20623 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
20624 assert!(!cpp_reparsed_members_are_indexable(
20625 variable_initializer_tree.root_node(),
20626 variable_initializer_source
20627 ));
20628 }
20629
20630 #[test]
20631 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
20632 let positive_source = r#"
20633std::pair<iterator, bool> insert(init_type&& value)
20634 ABSL_ATTRIBUTE_LIFETIME_BOUND
20635#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
20636 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
20637#endif
20638{
20639 return emplace(std::move(value));
20640}
20641"#;
20642 let mut parser = tree_sitter::Parser::new();
20643 parser
20644 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20645 .unwrap();
20646 let positive_tree = parser.parse(positive_source, None).unwrap();
20647 assert!(
20648 positive_tree.root_node().has_error(),
20649 "the fixture must exercise the split attribute/requires shape"
20650 );
20651 assert!(cpp_reparsed_members_are_indexable(
20652 positive_tree.root_node(),
20653 positive_source
20654 ));
20655
20656 let template_return_source = r#"
20657pair<int> insert(init_type&& value)
20658 ABSL_ATTRIBUTE_LIFETIME_BOUND
20659#if LANGUAGE_LEVEL >= 202002L
20660 requires(!Predicate<init_type>::value)
20661#endif
20662// Attributes and the function body may be separated by comments.
20663{
20664 return {};
20665}
20666"#;
20667 let template_return_tree = parser.parse(template_return_source, None).unwrap();
20668 assert!(
20669 cpp_reparsed_members_are_indexable(
20670 template_return_tree.root_node(),
20671 template_return_source
20672 ),
20673 "template-return attribute/requires tree: {}",
20674 template_return_tree.root_node().to_sexp()
20675 );
20676
20677 let no_body_source = r#"
20678std::pair<iterator, bool> insert(init_type&& value)
20679 ABSL_ATTRIBUTE_LIFETIME_BOUND
20680#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
20681 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
20682#endif
20683+ 0;
20684"#;
20685 let no_body_tree = parser.parse(no_body_source, None).unwrap();
20686 assert!(!cpp_reparsed_members_are_indexable(
20687 no_body_tree.root_node(),
20688 no_body_source
20689 ));
20690
20691 let extra_payload_source = r#"
20692pair<int> insert(init_type&& value)
20693 ABSL_ATTRIBUTE_LIFETIME_BOUND
20694#if LANGUAGE_LEVEL >= 202002L
20695 int unrelated;
20696 requires(Predicate<init_type>::value)
20697#endif
20698{
20699 return {};
20700}
20701"#;
20702 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
20703 assert!(!cpp_reparsed_members_are_indexable(
20704 extra_payload_tree.root_node(),
20705 extra_payload_source
20706 ));
20707
20708 let variable_initializer_source = r#"
20709int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
20710#if LANGUAGE_LEVEL >= 202002L
20711 requires(true)
20712#endif
20713{
20714 bad;
20715}
20716"#;
20717 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
20718 assert!(!cpp_reparsed_members_are_indexable(
20719 variable_initializer_tree.root_node(),
20720 variable_initializer_source
20721 ));
20722 }
20723
20724 #[test]
20725 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
20726 let source = r#"namespace absl {
20727ABSL_NAMESPACE_BEGIN namespace container_internal {
20728
20729class raw_hash_set : public Base {
20730 public:
20731 using value_type = int;
20732
20733 template <class U,
20734 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
20735 void insert(U value) { (void)value; }
20736
20737 struct InsertSlot {
20738 raw_hash_set& s;
20739 };
20740};
20741
20742}
20743ABSL_NAMESPACE_END
20744}"#;
20745 let mut parser = tree_sitter::Parser::new();
20746 parser
20747 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20748 .unwrap();
20749 let tree = parser.parse(source, None).unwrap();
20750 let root = tree.root_node();
20751 let outer_namespace = root
20752 .named_children(&mut root.walk())
20753 .find(|child| child.kind() == "namespace_definition")
20754 .expect("outer absl namespace");
20755 let declaration_list = outer_namespace
20756 .child_by_field_name("body")
20757 .expect("outer namespace body");
20758 let sentinel_function = declaration_list
20759 .named_children(&mut declaration_list.walk())
20760 .find(|child| child.kind() == "function_definition")
20761 .expect("malformed namespace sentinel function");
20762 let ancestry = ParentIndex::new(root);
20763 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
20764 .expect("structured nested namespace sentinel");
20765 let fragmented =
20766 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
20767 .expect("fragmented raw_hash_set class");
20768 assert_eq!(fragmented.class_node.kind(), "ERROR");
20769 assert_eq!(fragmented.name, "raw_hash_set");
20770 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
20771
20772 let outer_scope =
20773 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
20774 let mut outer_siblings = Vec::new();
20775 push_cpp_sentinel_sibling_classes(
20776 &mut outer_siblings,
20777 declaration_list,
20778 sentinel.function,
20779 &outer_scope,
20780 source,
20781 &ancestry,
20782 );
20783 let [outer_shadow] = outer_siblings.as_slice() else {
20784 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
20785 };
20786 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
20787 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
20788
20789 let field = " raw_hash_set& s;";
20790 let start = source.find(field).expect("InsertSlot field") + 4;
20791 let node = root
20792 .descendant_for_byte_range(start, start + "raw_hash_set".len())
20793 .expect("raw_hash_set type node");
20794 let recovered = cpp_sentinel_recovered_classes(root, source);
20795 let [deep_class] = recovered.as_slice() else {
20796 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
20797 };
20798 assert_eq!(
20799 deep_class.namespace_scope_components,
20800 vec!["absl", "container_internal"]
20801 );
20802 assert_eq!(
20803 deep_class.scope_components,
20804 vec!["absl", "container_internal", "raw_hash_set"]
20805 );
20806 assert!(
20807 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
20808 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
20809 );
20810
20811 assert_eq!(
20812 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
20813 Some(vec![
20814 "absl".to_string(),
20815 "container_internal".to_string(),
20816 "raw_hash_set".to_string(),
20817 "InsertSlot".to_string(),
20818 ])
20819 );
20820
20821 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
20822 let parsed = parse_cpp_file(&file, source, &tree);
20823 let raw_hash_set = parsed
20824 .declarations()
20825 .iter()
20826 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
20827 .expect("recovered raw_hash_set class");
20828 assert_eq!(
20829 raw_hash_set.fq_name(),
20830 "absl::container_internal.raw_hash_set",
20831 "the recovered declaration must publish under the deeper sentinel namespace"
20832 );
20833 assert_eq!(
20834 parsed.raw_supertypes.get(raw_hash_set),
20835 Some(&vec!["Base".to_string()]),
20836 "the structured base clause on the fragmented ERROR prefix must survive publication"
20837 );
20838 assert!(
20839 parsed.materialization_records.iter().any(|record| matches!(
20840 record,
20841 MaterializationRecord::RecoveredDeclaration { recovery, unit }
20842 if unit == raw_hash_set && *recovery == deep_class.class_range
20843 )),
20844 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
20845 parsed.materialization_records
20846 );
20847 }
20848
20849 #[test]
20876 fn the_parent_index_answers_what_tree_sitter_answers() {
20877 const SHAPES: [&str; 5] = [
20878 "namespace outer { namespace inner { struct Tag { int field; }; } }",
20879 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
20880 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
20881 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
20882 "class API Broken : public First, public Second {\n void member();\n",
20883 ];
20884 for source in SHAPES {
20885 let mut parser = tree_sitter::Parser::new();
20886 parser
20887 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20888 .unwrap();
20889 let tree = parser.parse(source, None).unwrap();
20890 let root = tree.root_node();
20891 let ancestry = ParentIndex::new(root);
20892 let mut nodes = 0usize;
20893 let mut stack = vec![root];
20894 while let Some(node) = stack.pop() {
20895 nodes += 1;
20896 assert_eq!(
20897 node.parent().map(|parent| parent.id()),
20898 ancestry.parent(node).map(|parent| parent.id()),
20899 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
20900 );
20901 let mut cursor = node.walk();
20902 stack.extend(node.children(&mut cursor));
20903 }
20904 assert!(nodes > 1, "{source:?} produced no tree to compare");
20905 }
20906 }
20907
20908 #[test]
20915 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
20916 const DEPTH: usize = 64;
20917 let mut source = String::new();
20918 for level in 0..DEPTH {
20919 writeln!(source, "namespace n{level} {{").unwrap();
20920 }
20921 source.push_str("int deepest(int value);\n");
20922 for _ in 0..DEPTH {
20923 source.push_str("}\n");
20924 }
20925
20926 let mut parser = tree_sitter::Parser::new();
20927 parser
20928 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20929 .unwrap();
20930 let tree = parser.parse(&source, None).unwrap();
20931 let root = tree.root_node();
20932 let ancestry = ParentIndex::new(root);
20933 let mut function_declarator = None;
20934 walk_named_tree_preorder(root, true, |node| {
20935 if node.kind() == "function_declarator" {
20936 function_declarator = Some(node);
20937 WalkControl::Break
20938 } else {
20939 WalkControl::Continue
20940 }
20941 });
20942 let function_declarator = function_declarator.expect("deepest function declarator");
20943 let ancestor_count =
20944 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
20945
20946 ancestry.reset_parent_query_count_for_test();
20947 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
20948 assert_eq!(DEPTH, lexical_scope.len());
20949 assert_eq!(
20950 ancestor_count + 1,
20951 ancestry.parent_query_count_for_test(),
20952 "lexical-scope ancestry bypassed the parent index"
20953 );
20954
20955 ancestry.reset_parent_query_count_for_test();
20956 assert_eq!(
20957 Some(DispatchExtensibility::Closed),
20958 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
20959 );
20960 assert_eq!(
20961 ancestor_count,
20962 ancestry.parent_query_count_for_test(),
20963 "dispatch ancestry bypassed the parent index"
20964 );
20965
20966 ancestry.reset_parent_query_count_for_test();
20967 assert_eq!(
20968 CallableLinkage::External,
20969 cpp_callable_linkage(function_declarator, &source, &ancestry)
20970 );
20971 assert_eq!(
20972 ancestor_count + 1,
20973 ancestry.parent_query_count_for_test(),
20974 "linkage ancestry bypassed the parent index"
20975 );
20976
20977 ancestry.reset_parent_query_count_for_test();
20978 assert!(!cpp_callable_is_structural_constructor(
20979 function_declarator,
20980 &source,
20981 &ancestry
20982 ));
20983 assert_eq!(
20984 ancestor_count + 1,
20985 ancestry.parent_query_count_for_test(),
20986 "constructor ancestry bypassed the parent index"
20987 );
20988 }
20989
20990 #[test]
21000 fn callable_dispatch_extensibility_comes_from_the_class_body() {
21001 const SOURCE: &str = "\
21002class Plain { public: long send(long order); };
21003class Derived : public Base { public: long send(long order); };
21004class Sealed : public Base { public: long send(long order) final; };
21005class Hiding : public Base { public: long send(long order) { return order; } };
21006class Virtual { public: virtual long send(long order); };
21007namespace shop { long free_call(long order); }
21008long Plain::send(long order) { return order; }
21009";
21010 let mut parser = tree_sitter::Parser::new();
21011 parser
21012 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21013 .unwrap();
21014 let tree = parser.parse(SOURCE, None).unwrap();
21015 let root = tree.root_node();
21016 let ancestry = ParentIndex::new(root);
21017 let mut answers = Vec::new();
21018 walk_named_tree_preorder(root, true, |node| {
21019 if node.kind() != "function_declarator" {
21020 return WalkControl::Continue;
21021 }
21022 let name = cpp_function_declarator_name_node(node).expect("declarator name");
21023 answers.push((
21024 node_text(name, SOURCE).to_string(),
21025 cpp_callable_dispatch_extensibility(node, &ancestry),
21026 ));
21027 WalkControl::Continue
21028 });
21029 assert_eq!(
21030 answers,
21031 vec![
21032 ("send".to_string(), Some(DispatchExtensibility::Closed)),
21033 ("send".to_string(), Some(DispatchExtensibility::Open)),
21034 ("send".to_string(), Some(DispatchExtensibility::Closed)),
21035 ("send".to_string(), Some(DispatchExtensibility::Open)),
21036 ("send".to_string(), Some(DispatchExtensibility::Open)),
21037 ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
21038 ("Plain::send".to_string(), None),
21039 ],
21040 "the class body states the fact; a qualified definition states none"
21041 );
21042 }
21043
21044 #[test]
21049 fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
21050 for aggregates in [64usize, 512] {
21051 let mut source =
21052 String::from("typedef unsigned long long u64;\nnamespace generated {\n");
21053 for index in 0..aggregates {
21054 writeln!(source, "struct tag{index};").unwrap();
21055 }
21056 for index in (0..aggregates).rev() {
21057 writeln!(
21058 source,
21059 "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
21060 )
21061 .unwrap();
21062 }
21063 source.push_str("}\n");
21064
21065 start_code_unit_removal_scan_probe();
21066 let parsed = parse_cpp_declarations(&source, "vmlinux.h");
21067 let scanned = finish_code_unit_removal_scan_probe();
21068
21069 let expected_names: Vec<String> = (0..aggregates)
21070 .rev()
21071 .map(|index| format!("tag{index}"))
21072 .collect();
21073 let top_level_names: Vec<String> = parsed
21074 .top_level_declarations
21075 .iter()
21076 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
21077 .map(|unit| unit.short_name().to_string())
21078 .collect();
21079 let namespace = parsed
21080 .declarations()
21081 .iter()
21082 .find(|unit| {
21083 unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
21084 })
21085 .expect("generated namespace should be declared");
21086 let child_names: Vec<String> = parsed.children[namespace]
21087 .iter()
21088 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
21089 .map(|unit| unit.short_name().to_string())
21090 .collect();
21091 assert_eq!(
21092 aggregates,
21093 parsed
21094 .declarations()
21095 .iter()
21096 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
21097 .count(),
21098 "every aggregate must still be declared at {aggregates} aggregates"
21099 );
21100 assert_eq!(expected_names, top_level_names);
21101 assert_eq!(expected_names, child_names);
21102 assert_eq!(
21103 0, scanned,
21104 "replacing {aggregates} forward declarations must compact their shared lists once"
21105 );
21106 }
21107 }
21108
21109 #[test]
21110 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
21111 const DISTINCT_PER_KIND: usize = 64;
21112 let mut source = String::new();
21113 for index in 0..DISTINCT_PER_KIND {
21114 writeln!(source, "typedef int Alias{index};").unwrap();
21115 }
21116 writeln!(source, "typedef long Alias0;").unwrap();
21117 for index in 0..DISTINCT_PER_KIND {
21118 writeln!(source, "#define MACRO_{index} {index}").unwrap();
21119 }
21120 writeln!(source, "#define MACRO_0 duplicate").unwrap();
21121 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
21122
21123 let mut parser = tree_sitter::Parser::new();
21124 parser
21125 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21126 .unwrap();
21127 let tree = parser.parse(&source, None).unwrap();
21128 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
21129
21130 start_declaration_identity_comparison_probe();
21131 let parsed = parse_cpp_file(&file, &source, &tree);
21132 let comparisons = finish_declaration_identity_comparison_probe();
21133
21134 assert_eq!(
21135 DISTINCT_PER_KIND + 1,
21136 parsed
21137 .declarations()
21138 .iter()
21139 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
21140 .count(),
21141 "every physical typedef alias declaration must be retained so \
21142 conditional branch guards stay available to the resolver"
21143 );
21144 assert_eq!(
21145 DISTINCT_PER_KIND + 1,
21146 parsed
21147 .declarations()
21148 .iter()
21149 .filter(|unit| {
21150 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
21151 })
21152 .count(),
21153 "distinct macro redefinitions must remain available to temporal lookup"
21154 );
21155 assert_eq!(
21156 2,
21157 parsed
21158 .declarations()
21159 .iter()
21160 .filter(|unit| {
21161 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
21162 })
21163 .count(),
21164 "function overloads must remain distinct"
21165 );
21166
21167 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
21168 assert!(
21169 comparisons <= dedup_inputs * 4,
21170 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
21171 );
21172 }
21173
21174 #[test]
21175 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
21176 let source = r#"namespace absl {
21177ABSL_NAMESPACE_BEGIN namespace container_internal {
21178template <typename T>
21179class broken {
21180 public:
21181 using value_type = T;
21182 T operator->() const { return &operator*(); }
21183 using alias = value_type;
21184};
21185}
21186}
21187"#;
21188 let mut parser = tree_sitter::Parser::new();
21189 parser
21190 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21191 .unwrap();
21192 let tree = parser.parse(source, None).unwrap();
21193 let broken = find_class_named(tree.root_node(), source, "broken")
21194 .expect("the positive fixture must expose the broken class node");
21195 assert!(
21196 broken.has_error(),
21197 "the positive fixture must retain an internal parser error"
21198 );
21199 assert!(
21200 cpp_complete_class_body_close(broken).is_some(),
21201 "the positive fixture must expose a real class body close"
21202 );
21203 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
21204 assert!(
21205 recovered.iter().any(|class| {
21206 class.scope_components == ["absl", "container_internal", "broken"]
21207 }),
21208 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
21209 );
21210 }
21211
21212 #[test]
21213 fn sentinel_recovery_keeps_members_after_nested_body_close() {
21214 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
21215NLOHMANN_BASIC_JSON_TPL_DECLARATION
21216class basic_json {
21217 private:
21218 union storage {
21219 int value;
21220 } data;
21221 public:
21222 using late_alias = int;
21223 late_alias value() const;
21224};
21225NLOHMANN_JSON_NAMESPACE_END
21226"#;
21227 let mut parser = tree_sitter::Parser::new();
21228 parser
21229 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21230 .unwrap();
21231 let tree = parser.parse(source, None).unwrap();
21232 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
21233 let basic_json = recovered
21234 .iter()
21235 .find(|class| {
21236 class
21237 .scope_components
21238 .last()
21239 .is_some_and(|name| name == "basic_json")
21240 })
21241 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
21242 let late_alias = source
21243 .find("late_alias value")
21244 .expect("late alias reference");
21245 assert!(
21246 basic_json.class_range.start_byte < late_alias
21247 && late_alias < basic_json.class_range.end_byte,
21248 "the recovered class range must include members after a nested close: {basic_json:#?}"
21249 );
21250 }
21251
21252 #[test]
21253 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
21254 let source = r#"namespace absl {
21255ABSL_NAMESPACE_BEGIN namespace container_internal {
21256template <typename T>
21257class broken {
21258 public:
21259 using value_type = T;
21260 T operator->() const { return &operator*(); }
21261}
21262}
21263"#;
21264 let mut parser = tree_sitter::Parser::new();
21265 parser
21266 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21267 .unwrap();
21268 let tree = parser.parse(source, None).unwrap();
21269 let broken = find_class_named(tree.root_node(), source, "broken")
21270 .expect("the negative fixture must expose the malformed class node");
21271 assert!(
21272 broken.has_error(),
21273 "the negative fixture must retain a parser error"
21274 );
21275 assert!(
21276 cpp_complete_class_body_close(broken).is_none(),
21277 "the malformed class must not expose a real body close"
21278 );
21279 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
21280 assert!(
21281 recovered
21282 .iter()
21283 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
21284 "an incomplete class must not borrow the namespace close: {recovered:#?}"
21285 );
21286 }
21287
21288 #[test]
21289 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
21290 let source = r#"namespace absl {
21291ABSL_NAMESPACE_BEGIN namespace container_internal {
21292template <typename T>
21293struct broken {
21294 using value_type = T;
21295};
21296}
21297
21298#ifdef OWNER_DEF
21299template <typename T>
21300typename broken<T>::value_type broken<T>::method() {
21301 value_type value{};
21302 return value;
21303}
21304#endif
21305
21306namespace sibling {
21307template <typename T>
21308typename broken<T>::value_type broken<T>::other() {
21309 value_type value{};
21310 return value;
21311}
21312}
21313
21314ABSL_NAMESPACE_END
21315}
21316"#;
21317 let mut parser = tree_sitter::Parser::new();
21318 parser
21319 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21320 .unwrap();
21321 let tree = parser.parse(source, None).unwrap();
21322 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
21323 let broken = recovered
21324 .iter()
21325 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
21326 .expect("the sentinel class must be recovered");
21327 let method_start = source
21328 .find("typename broken<T>::value_type broken<T>::method()")
21329 .expect("guarded sibling owner");
21330 let method_end = source[method_start..]
21331 .find("\n}")
21332 .map(|offset| method_start + offset + 2)
21333 .expect("guarded sibling owner close");
21334 assert!(
21335 broken
21336 .owner_ranges
21337 .iter()
21338 .any(|owner| owner.range.start_byte <= method_start
21339 && method_end <= owner.range.end_byte),
21340 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
21341 );
21342 let sibling_start = source
21343 .find("typename broken<T>::value_type broken<T>::other()")
21344 .expect("nested namespace sibling owner");
21345 assert!(
21346 broken
21347 .owner_ranges
21348 .iter()
21349 .all(|owner| owner.range.start_byte > sibling_start
21350 || owner.range.end_byte <= sibling_start),
21351 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
21352 );
21353 }
21354
21355 #[test]
21356 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
21357 let source = r#"#ifdef OUTER
21358namespace absl {
21359ABSL_NAMESPACE_BEGIN namespace container_internal {
21360template <typename T>
21361struct broken {
21362 using value_type = T;
21363};
21364}
21365}
21366
21367#ifdef OWNER_DEF
21368template <typename T>
21369typename broken<T>::value_type broken<T>::method() {
21370 value_type value{};
21371 return value;
21372}
21373#endif
21374#endif
21375"#;
21376 let mut parser = tree_sitter::Parser::new();
21377 parser
21378 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21379 .unwrap();
21380 let tree = parser.parse(source, None).unwrap();
21381 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
21382 let broken = recovered
21383 .iter()
21384 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
21385 .expect("the sentinel class must be recovered");
21386 let method_start = source
21387 .find("typename broken<T>::value_type broken<T>::method()")
21388 .expect("outer sibling owner");
21389 assert!(
21390 broken
21391 .owner_ranges
21392 .iter()
21393 .all(|owner| owner.range.start_byte > method_start
21394 || owner.range.end_byte <= method_start),
21395 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
21396 );
21397 }
21398
21399 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
21401 let mut signatures = parsed
21402 .declarations()
21403 .iter()
21404 .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
21405 .filter_map(|unit| unit.signature().map(str::to_string))
21406 .collect::<Vec<_>>();
21407 signatures.sort();
21408 signatures.dedup();
21409 signatures
21410 }
21411
21412 #[test]
21413 fn callable_parameter_types_come_from_the_ast_parameter_list() {
21414 let source = r#"
21415template <typename T, ENABLE_BYTES(T)>
21416Vec256<T> DupOdd(Vec256<T> value) { return value; }
21417
21418struct Visitor {
21419 void fail(this auto const& self) {}
21420};
21421"#;
21422 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
21423 let dup_odd = parsed
21424 .declarations()
21425 .iter()
21426 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
21427 .expect("DupOdd declaration");
21428 assert_eq!(
21429 dup_odd.signature(),
21430 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
21431 );
21432 assert_eq!(
21433 parsed
21434 .signature_metadata
21435 .get(dup_odd)
21436 .and_then(|metadata| metadata.first())
21437 .and_then(SignatureMetadata::callable_parameter_types),
21438 Some(["Vec256<T>".to_string()].as_slice())
21439 );
21440
21441 let fail = parsed
21442 .declarations()
21443 .iter()
21444 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
21445 .expect("explicit-object member");
21446 assert_eq!(fail.signature(), Some("(const this auto &)"));
21447 let metadata = parsed
21448 .signature_metadata
21449 .get(fail)
21450 .and_then(|metadata| metadata.first())
21451 .expect("explicit-object signature metadata");
21452 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
21453 assert!(
21454 metadata
21455 .callable_arity()
21456 .is_some_and(|arity| arity.accepts(0))
21457 );
21458 }
21459
21460 #[test]
21461 fn trailing_qualifiers_survive_parameter_list_whitespace() {
21462 let source = r#"
21467struct Widget {
21468 bool multiline(int settings, int supprs) const;
21469 bool doublespace(int settings, int supprs) const;
21470 bool noexcept_multiline(int settings, int supprs) noexcept;
21471 bool ref_multiline(int settings, int supprs) &&;
21472};
21473bool
21474Widget::multiline (int settings,
21475 int supprs) const
21476{ return settings + supprs > 0; }
21477bool Widget::doublespace(int settings, int supprs) const { return true; }
21478bool Widget::noexcept_multiline(int settings,
21479 int supprs) noexcept { return true; }
21480bool Widget::ref_multiline(int settings,
21481 int supprs) && { return true; }
21482"#;
21483 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
21484 assert_eq!(
21485 vec!["(int, int) const".to_string()],
21486 identity_signatures(&parsed, "Widget.multiline")
21487 );
21488 assert_eq!(
21489 vec!["(int, int) const".to_string()],
21490 identity_signatures(&parsed, "Widget.doublespace")
21491 );
21492 assert_eq!(
21493 vec!["(int, int) noexcept".to_string()],
21494 identity_signatures(&parsed, "Widget.noexcept_multiline")
21495 );
21496 assert_eq!(
21497 vec!["(int, int) &&".to_string()],
21498 identity_signatures(&parsed, "Widget.ref_multiline")
21499 );
21500 }
21501
21502 #[test]
21503 fn macro_fragmented_plain_class_keeps_following_member_signature() {
21504 let source = r#"
21505struct CString {};
21506class CMessage {
21507public:
21508 CString GetParams(unsigned int index, unsigned int length = -1) const
21509 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
21510 return GetParamsColon(index, length);
21511 }
21512 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
21513};
21514CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
21515 return {};
21516}
21517"#;
21518 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
21519 assert_eq!(
21520 vec!["(unsigned int, unsigned int) const".to_string()],
21521 identity_signatures(&parsed, "CMessage.GetParamsColon")
21522 );
21523 }
21524
21525 #[test]
21526 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
21527 let source = r#"
21528#pragma once
21529#define DEMO_DEPRECATED(message)
21530namespace demo {
21531struct Base {
21532 static int aligned(int value) { return value; }
21533 int legacy(int value) const
21534 DEMO_DEPRECATED("use replacement()") { return value; }
21535 int replacement() const;
21536 void run(int value);
21537};
21538struct OtherBase {
21539 void run(int value);
21540 static int aligned(int value) { return value; }
21541};
21542struct Derived : Base {};
21543struct Override : Base {
21544 void run(int value);
21545 static int aligned(int value) { return value; }
21546};
21547struct RecoveredOverride : Base {
21548 int legacy(int value) const
21549 DEMO_DEPRECATED("use replacement()") { return value; }
21550 void run(int value);
21551};
21552struct Hidden : Base {
21553 void run(int first, int second);
21554 static int aligned(int first, int second) { return first + second; }
21555};
21556struct Ambiguous : Base, OtherBase {};
21557}
21558struct Global {};
21559"#;
21560 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
21561 let declarations = parsed.declarations();
21562 let fq_names = declarations
21563 .iter()
21564 .map(|unit| unit.fq_name())
21565 .collect::<std::collections::BTreeSet<_>>();
21566
21567 for expected in [
21568 "demo.Base",
21569 "demo.Base.aligned",
21570 "demo.Base.legacy",
21571 "demo.Base.replacement",
21572 "demo.Base.run",
21573 "demo.Derived",
21574 "demo.OtherBase",
21575 "demo.Override",
21576 "demo.RecoveredOverride",
21577 "demo.Hidden",
21578 "demo.Ambiguous",
21579 "Global",
21580 ] {
21581 assert!(
21582 fq_names.contains(expected),
21583 "missing {expected} from namespaced macro fragment: {declarations:#?}"
21584 );
21585 }
21586 assert!(
21587 !fq_names.contains("Derived"),
21588 "following class escaped its namespace: {declarations:#?}"
21589 );
21590 assert!(
21591 !fq_names.contains("demo.Global"),
21592 "global class crossed the recovered namespace boundary: {declarations:#?}"
21593 );
21594 }
21595
21596 #[test]
21597 fn trailing_qualifiers_still_separate_genuine_overloads() {
21598 let source = r#"
21601struct Widget {
21602 int* slot(int index);
21603 const int* slot(int index) const;
21604 int log(int severity) &;
21605 int log(int severity) &&;
21606};
21607"#;
21608 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
21609 assert_eq!(
21610 vec!["(int)".to_string(), "(int) const".to_string()],
21611 identity_signatures(&parsed, "Widget.slot")
21612 );
21613 assert_eq!(
21614 vec!["(int) &".to_string(), "(int) &&".to_string()],
21615 identity_signatures(&parsed, "Widget.log")
21616 );
21617 }
21618
21619 #[test]
21620 fn virtual_specifier_is_not_part_of_the_identity_signature() {
21621 let source = r#"
21624struct Base {
21625 virtual void run(int value) const;
21626};
21627struct Widget : Base {
21628 void run(int value) const override;
21629};
21630void Widget::run(int value) const {}
21631"#;
21632 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
21633 assert_eq!(
21634 vec!["(int) const".to_string()],
21635 identity_signatures(&parsed, "Widget.run")
21636 );
21637 }
21638
21639 #[test]
21640 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
21641 let source = r#"
21645struct Widget {
21646 bool value_params(const int settings, const int supprs);
21647 void pointee_const(const int* p);
21648 void pointer_const(int* const p);
21649 void both_const(const int* const p);
21650 void reference_const(const int& p);
21651 void array_const(const int values[4]);
21652};
21653bool Widget::value_params(int settings, int supprs) { return true; }
21654void Widget::pointer_const(int* p) {}
21655void Widget::both_const(const int* p) {}
21656"#;
21657 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
21658 assert_eq!(
21659 vec!["(int, int)".to_string()],
21660 identity_signatures(&parsed, "Widget.value_params")
21661 );
21662 assert_eq!(
21663 vec!["(int *)".to_string()],
21664 identity_signatures(&parsed, "Widget.pointer_const")
21665 );
21666 assert_eq!(
21667 vec!["(const int *)".to_string()],
21668 identity_signatures(&parsed, "Widget.both_const")
21669 );
21670 assert_eq!(
21672 vec!["(const int *)".to_string()],
21673 identity_signatures(&parsed, "Widget.pointee_const")
21674 );
21675 assert_eq!(
21676 vec!["(const int &)".to_string()],
21677 identity_signatures(&parsed, "Widget.reference_const")
21678 );
21679 assert_eq!(
21680 vec!["(const int [4])".to_string()],
21681 identity_signatures(&parsed, "Widget.array_const")
21682 );
21683 }
21684
21685 #[test]
21686 fn top_level_parameter_const_still_separates_pointee_overloads() {
21687 let source = r#"
21688struct Widget {
21689 void take(const int* p);
21690 void take(int* p);
21691};
21692"#;
21693 let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
21694 assert_eq!(
21695 vec!["(const int *)".to_string(), "(int *)".to_string()],
21696 identity_signatures(&parsed, "Widget.take")
21697 );
21698 }
21699
21700 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
21701 let mut parser = tree_sitter::Parser::new();
21702 parser
21703 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21704 .unwrap();
21705 let tree = parser.parse(source, None).unwrap();
21706 let start = source.find(callable_name).expect("callable declaration");
21707 let declarator =
21708 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
21709 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
21710 }
21711
21712 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
21713 let mut shapes = comparable_shapes(source, callable_name);
21714 assert_eq!(1, shapes.len(), "{shapes:?}");
21715 match shapes.remove(0) {
21716 CppComparableSlot::Shape(shape) => shape,
21717 other => panic!("expected a comparable shape, got {other:?}"),
21718 }
21719 }
21720
21721 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
21722 let mut current = shape.root();
21723 loop {
21724 match shape.node(current) {
21725 CppComparableNode::Named { .. } => return shape.node(current),
21726 CppComparableNode::Pointer { inner, .. }
21727 | CppComparableNode::Reference { inner }
21728 | CppComparableNode::Array { inner } => current = *inner,
21729 CppComparableNode::Generic { base, .. } => current = *base,
21730 }
21731 }
21732 }
21733
21734 #[test]
21735 fn comparable_shape_keeps_pointee_const() {
21736 assert_ne!(
21737 sole_comparable_shape("void f(const char* p);", "f("),
21738 sole_comparable_shape("void f(char* p);", "f(")
21739 );
21740 }
21741
21742 #[test]
21743 fn comparable_shape_keeps_inner_pointer_const() {
21744 assert_ne!(
21745 sole_comparable_shape("void f(int** p);", "f("),
21746 sole_comparable_shape("void f(int* const* p);", "f(")
21747 );
21748 }
21749
21750 #[test]
21751 fn comparable_shape_drops_top_level_pointer_const() {
21752 assert_eq!(
21753 sole_comparable_shape("void f(int* const p);", "f("),
21754 sole_comparable_shape("void f(int* p);", "f(")
21755 );
21756 }
21757
21758 #[test]
21759 fn comparable_shape_drops_top_level_base_const() {
21760 assert_eq!(
21761 sole_comparable_shape("void f(const int p);", "f("),
21762 sole_comparable_shape("void f(int p);", "f(")
21763 );
21764 }
21765
21766 #[test]
21767 fn comparable_shape_decays_top_level_array_to_pointer() {
21768 assert_eq!(
21769 sole_comparable_shape("void f(int a[3]);", "f("),
21770 sole_comparable_shape("void f(int* a);", "f(")
21771 );
21772 assert_eq!(
21773 sole_comparable_shape("void f(int* a[3]);", "f("),
21774 sole_comparable_shape("void f(int** a);", "f(")
21775 );
21776 }
21777
21778 #[test]
21779 fn comparable_shape_keeps_array_behind_pointer() {
21780 assert_ne!(
21781 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
21782 sole_comparable_shape("struct S { void f(int** a); };", "f(")
21783 );
21784 }
21785
21786 #[test]
21787 fn comparable_shape_records_written_name_and_lexical_scope() {
21788 let declared =
21789 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
21790 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
21791 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
21792 panic!("named leaf");
21793 };
21794 assert_eq!(["Msg".to_string()].as_slice(), name.path());
21795 assert_eq!(
21796 ["ns".to_string(), "S".to_string()].as_slice(),
21797 name.lexical_scope()
21798 );
21799 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
21800 panic!("named leaf");
21801 };
21802 assert_eq!(
21803 ["ns".to_string(), "Msg".to_string()].as_slice(),
21804 name.path()
21805 );
21806 assert!(name.lexical_scope().is_empty());
21807 assert_ne!(declared, defined);
21808 }
21809
21810 #[test]
21811 fn comparable_shape_marks_sized_primitive_leaf() {
21812 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
21813 let CppComparableNode::Named {
21814 name, primitive, ..
21815 } = comparable_named_leaf(&shape)
21816 else {
21817 panic!("named leaf");
21818 };
21819 assert!(primitive);
21820 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
21821 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
21822 }
21823
21824 #[test]
21825 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
21826 assert_eq!(
21827 vec![CppComparableSlot::Unstructured],
21828 comparable_shapes("void f(void (*cb)(int));", "f(")
21829 );
21830 }
21831
21832 #[test]
21833 fn comparable_shape_reports_ellipsis_slot() {
21834 let shapes = comparable_shapes("void f(int a, ...);", "f(");
21835 assert_eq!(2, shapes.len(), "{shapes:?}");
21836 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
21837 }
21838
21839 #[test]
21840 fn comparable_shape_keeps_template_argument_const() {
21841 assert_ne!(
21842 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
21843 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
21844 );
21845 }
21846
21847 #[test]
21850 fn c_file_mints_aggregate_member_tag_at_file_scope() {
21851 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
21852 let parsed = parse_cpp_declarations(source, "x.c");
21853 let declarations = parsed.declarations();
21854
21855 assert!(
21856 declarations
21857 .iter()
21858 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21859 "expected a file-scope inner tag, got {declarations:?}"
21860 );
21861 assert!(
21862 declarations
21863 .iter()
21864 .all(|unit| unit.fq_name() != "outer$inner"),
21865 "expected no nested identity, got {declarations:?}"
21866 );
21867 assert!(
21868 declarations
21869 .iter()
21870 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
21871 );
21872 assert!(
21874 declarations
21875 .iter()
21876 .any(|unit| unit.fq_name() == "inner.value")
21877 );
21878 assert!(
21879 declarations
21880 .iter()
21881 .any(|unit| unit.fq_name() == "outer.item")
21882 );
21883
21884 let outer = declarations
21885 .iter()
21886 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
21887 .expect("outer");
21888 assert!(
21889 parsed
21890 .children
21891 .get(outer)
21892 .into_iter()
21893 .flatten()
21894 .all(|child| child.fq_name() != "inner"),
21895 "the tag must not hang off the aggregate it is written inside: {:?}",
21896 parsed.children
21897 );
21898 }
21899
21900 #[test]
21904 fn header_and_cpp_files_keep_nested_tag_identity() {
21905 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
21906 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
21907 let parsed = parse_cpp_declarations(source, name);
21908 let declarations = parsed.declarations();
21909 assert!(
21910 declarations
21911 .iter()
21912 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21913 "{name} must keep the nested identity, got {declarations:?}"
21914 );
21915 assert!(
21916 declarations.iter().all(|unit| unit.fq_name() != "inner"),
21917 "{name} must not mint a file-scope tag, got {declarations:?}"
21918 );
21919 assert!(
21920 declarations
21921 .iter()
21922 .any(|unit| unit.fq_name() == "outer$inner.value")
21923 );
21924 }
21925 }
21926
21927 #[test]
21929 fn uppercase_c_extension_keeps_cpp_tag_scope() {
21930 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
21931 let parsed = parse_cpp_declarations(source, "x.C");
21932 assert!(
21933 parsed
21934 .declarations()
21935 .iter()
21936 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
21937 );
21938 }
21939
21940 #[test]
21943 fn c_file_mints_every_nesting_level_at_file_scope() {
21944 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
21945 let parsed = parse_cpp_declarations(source, "z.c");
21946 let declarations = parsed.declarations();
21947
21948 for tag in ["a", "b", "c"] {
21949 assert!(
21950 declarations
21951 .iter()
21952 .any(|unit| unit.is_class() && unit.fq_name() == tag),
21953 "expected a file-scope {tag}, got {declarations:?}"
21954 );
21955 }
21956 assert!(
21957 declarations
21958 .iter()
21959 .all(|unit| !unit.fq_name().contains('$')),
21960 "no level may keep a nested identity, got {declarations:?}"
21961 );
21962 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
21964 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
21965 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
21966 }
21967
21968 #[test]
21971 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
21972 let source = "struct outer { enum color { RED, GREEN } c; };\n";
21973 let parsed = parse_cpp_declarations(source, "e.c");
21974 let declarations = parsed.declarations();
21975
21976 let color = declarations
21977 .iter()
21978 .find(|unit| unit.is_class() && unit.fq_name() == "color")
21979 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
21980 assert!(
21981 declarations
21982 .iter()
21983 .all(|unit| unit.fq_name() != "outer$color")
21984 );
21985 for enumerator in ["color.RED", "color.GREEN"] {
21986 assert!(
21987 declarations.iter().any(|unit| unit.fq_name() == enumerator),
21988 "expected {enumerator}, got {declarations:?}"
21989 );
21990 }
21991 let children = parsed
21992 .children
21993 .get(color)
21994 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
21995 assert!(
21996 ["color.RED", "color.GREEN"]
21997 .iter()
21998 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
21999 "enumerators must hang off their enum: {children:?}"
22000 );
22001 }
22002
22003 #[test]
22004 fn c_file_mints_member_list_union_at_file_scope() {
22005 let source = "struct outer { union inner { int a; float b; } item; };\n";
22006 let parsed = parse_cpp_declarations(source, "u.c");
22007 let declarations = parsed.declarations();
22008 assert!(
22009 declarations
22010 .iter()
22011 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
22012 "expected a file-scope inner union, got {declarations:?}"
22013 );
22014 assert!(
22015 declarations
22016 .iter()
22017 .all(|unit| unit.fq_name() != "outer$inner")
22018 );
22019 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
22020 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
22021 }
22022
22023 #[test]
22026 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
22027 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
22028 let parsed = parse_cpp_declarations(source, "n.c");
22029 let declarations = parsed.declarations();
22030 let inner = declarations
22031 .iter()
22032 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
22033 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
22034 assert_eq!(inner.package_name(), "ns");
22035 assert!(
22036 declarations
22037 .iter()
22038 .all(|unit| unit.fq_name() != "ns.outer$inner")
22039 );
22040 }
22041
22042 #[test]
22047 fn function_local_tags_are_unchanged_in_both_dialects() {
22048 let source =
22049 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
22050 for name in ["y.c", "y.cpp"] {
22051 let parsed = parse_cpp_declarations(source, name);
22052 let declarations = parsed.declarations();
22053 assert!(
22054 declarations
22055 .iter()
22056 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
22057 "{name}: {declarations:?}"
22058 );
22059 for tag in ["localtag", "deeper", "localtag$deeper"] {
22060 assert!(
22061 declarations.iter().all(|unit| unit.fq_name() != tag),
22062 "{name} must not mint {tag}, got {declarations:?}"
22063 );
22064 }
22065 }
22066 }
22067
22068 #[test]
22071 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
22072 let source = "typedef struct { int v; } T;\n";
22073 for name in ["t.c", "t.cpp"] {
22074 let parsed = parse_cpp_declarations(source, name);
22075 let declarations = parsed.declarations();
22076 assert!(
22077 declarations
22078 .iter()
22079 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
22080 "{name}: {declarations:?}"
22081 );
22082 }
22083 }
22084
22085 #[test]
22092 fn anonymous_aggregate_members_are_identical_in_both_dialects() {
22093 let sources = [
22094 "struct outer { union { int a; float b; }; };\n",
22095 "struct outer { struct { int *ops; } sock; };\n",
22096 "void run(void) { struct { int v; } item; }\n",
22097 "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
22098 ];
22099 for source in sources {
22100 let names = |name: &str| {
22101 parse_cpp_declarations(source, name)
22102 .declarations()
22103 .iter()
22104 .map(|unit| unit.fq_name())
22105 .collect::<std::collections::BTreeSet<_>>()
22106 };
22107 let c_names = names("a.c");
22108 let cpp_names = names("a.cpp");
22109 assert_eq!(
22110 c_names, cpp_names,
22111 "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
22112 );
22113 assert!(
22114 c_names
22115 .iter()
22116 .any(|name| name.contains('.') && name != "outer"),
22117 "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
22118 );
22119 }
22120 }
22121
22122 #[test]
22123 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
22124 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
22125 let parsed = parse_cpp_declarations(source, "socket.c");
22126 let declarations = parsed.declarations();
22127 assert_eq!(
22128 declarations
22129 .iter()
22130 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
22131 .count(),
22132 1,
22133 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
22134 );
22135 for expected in [
22136 "PAL_HANDLE",
22137 "PAL_HANDLE.sock",
22138 "PAL_HANDLE$sock",
22139 "PAL_HANDLE$sock.ops",
22140 ] {
22141 assert!(
22142 declarations.iter().any(|unit| unit.fq_name() == expected),
22143 "expected {expected}, got {declarations:?}"
22144 );
22145 }
22146 }
22147
22148 #[test]
22151 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
22152 let source = "class outer { class inner { int v; }; };\n";
22153 let c_parsed = parse_cpp_declarations(source, "k.c");
22154 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
22155 let c_declarations = c_parsed.declarations();
22156 let cpp_declarations = cpp_parsed.declarations();
22157 assert!(
22158 c_declarations
22159 .iter()
22160 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
22161 "{c_declarations:?}"
22162 );
22163 assert_eq!(
22164 c_declarations
22165 .iter()
22166 .map(|unit| unit.fq_name())
22167 .collect::<std::collections::BTreeSet<_>>(),
22168 cpp_declarations
22169 .iter()
22170 .map(|unit| unit.fq_name())
22171 .collect::<std::collections::BTreeSet<_>>()
22172 );
22173 }
22174
22175 fn namespace_forward_scan_agreement(source: &str) -> usize {
22189 let mut parser = tree_sitter::Parser::new();
22190 parser
22191 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22192 .unwrap();
22193 let tree = parser.parse(source, None).unwrap();
22194 let root = tree.root_node();
22195 let ancestry = ParentIndex::new(root);
22196
22197 let mut nodes = Vec::new();
22198 let mut names = std::collections::BTreeSet::new();
22199 let mut cursor = root.walk();
22200 let mut stack = vec![root];
22201 while let Some(node) = stack.pop() {
22202 if matches!(
22203 node.kind(),
22204 "class_specifier" | "struct_specifier" | "union_specifier"
22205 ) && let Some(name) = class_like_name(node, source, &ancestry)
22206 {
22207 names.insert(name);
22208 }
22209 nodes.push(node);
22210 stack.extend(node.named_children(&mut cursor));
22211 }
22212 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
22213 assert!(!names.is_empty(), "fixture declares no class-like name");
22214
22215 let mut answered = 0usize;
22216 for reversed in [false, true] {
22217 let mut scan = CppNamespaceForwardScan::default();
22218 let ordered: Vec<_> = if reversed {
22219 nodes.iter().rev().copied().collect()
22220 } else {
22221 nodes.clone()
22222 };
22223 answered = 0;
22224 for node in ordered {
22225 for name in &names {
22226 scan.advance_to(root, node.start_byte(), source, &ancestry);
22227 let carried = scan.unique_earlier_forward(name, node);
22228 assert_eq!(
22229 carried,
22230 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
22231 "carried-forward scan and prefix scan disagree about {name} at \
22232 {} node starting at byte {} (reversed order: {reversed})",
22233 node.kind(),
22234 node.start_byte()
22235 );
22236 answered += usize::from(carried.is_some());
22237 }
22238 }
22239 }
22240 answered
22241 }
22242
22243 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
22250namespace ns {
22251class Widget;
22252class Gadget;
22253int x = ;
22254}
22255class API Widget {
22256public:
22257 void first();
22258};
22259class API Gadget {
22260public:
22261 void second();
22262};
22263"#;
22264
22265 #[test]
22266 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
22267 assert!(
22268 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
22269 "the fixture must actually reach the namespace-borrow path"
22270 );
22271
22272 for source in [
22277 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
22278 r#"#define API
22279namespace ns {
22280class Widget;
22281class Widget;
22282int x = ;
22283}
22284class API Widget {
22285public:
22286 void method();
22287};
22288"#,
22289 r#"#define API
22290namespace ns {
22291void host() {
22292 class Widget;
22293}
22294int x = ;
22295}
22296class API Widget {
22297public:
22298 void method();
22299};
22300"#,
22301 ] {
22302 assert_eq!(
22303 namespace_forward_scan_agreement(source),
22304 0,
22305 "no borrow is justified here: {source}"
22306 );
22307 }
22308 }
22309
22310 #[test]
22314 fn carried_forward_namespace_scan_folds_each_node_once() {
22315 let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
22316 let mut parser = tree_sitter::Parser::new();
22317 parser
22318 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22319 .unwrap();
22320 let tree = parser.parse(source, None).unwrap();
22321 let root = tree.root_node();
22322 let ancestry = ParentIndex::new(root);
22323
22324 let mut incremental = CppNamespaceForwardScan::default();
22325 for cutoff in 0..=source.len() {
22326 incremental.advance_to(root, cutoff, source, &ancestry);
22327 }
22328 let mut whole = CppNamespaceForwardScan::default();
22329 whole.advance_to(root, source.len(), source, &ancestry);
22330
22331 let mut incremental_shape: Vec<_> = incremental
22332 .forwards
22333 .iter()
22334 .map(|(name, forwards)| {
22335 (
22336 name.clone(),
22337 forwards
22338 .iter()
22339 .map(|forward| (forward.start_byte, forward.package_name.clone()))
22340 .collect::<Vec<_>>(),
22341 )
22342 })
22343 .collect();
22344 let mut whole_shape: Vec<_> = whole
22345 .forwards
22346 .iter()
22347 .map(|(name, forwards)| {
22348 (
22349 name.clone(),
22350 forwards
22351 .iter()
22352 .map(|forward| (forward.start_byte, forward.package_name.clone()))
22353 .collect::<Vec<_>>(),
22354 )
22355 })
22356 .collect();
22357 incremental_shape.sort();
22358 whole_shape.sort();
22359 for (_, forwards) in &mut incremental_shape {
22360 forwards.sort();
22361 }
22362 for (_, forwards) in &mut whole_shape {
22363 forwards.sort();
22364 }
22365
22366 assert!(!whole_shape.is_empty(), "fixture folds no forward");
22367 assert_eq!(
22368 incremental_shape, whole_shape,
22369 "one byte at a time must fold exactly what one whole pass folds"
22370 );
22371 }
22372
22373 fn fragmented_class_reparse_agreement(body: &str) {
22383 for prefix in [
22384 String::new(),
22385 "// leading comment\n".to_string(),
22386 "class Widget : public Base { ".to_string(),
22391 "namespace filler {\n".to_string()
22392 + &"struct Filler { int member; };\n".repeat(200)
22393 + "}\n",
22394 "namespace filler {\n".to_string()
22395 + &"struct Filler { int member; };\n".repeat(200)
22396 + "}\nclass Widget : public Base { ",
22397 ] {
22398 let source = format!("{prefix}{body}");
22399 let start = prefix.len();
22400 let end = source.len();
22401 let region = cpp_reparse_fragmented_class_body(&source, start, end)
22402 .expect("the region reparse must produce a tree");
22403 let padded = cpp_reparse_padded_class_body(&source, start, end)
22404 .expect("the padded reparse must produce a tree");
22405 assert_eq!(
22406 cpp_tree_shape(®ion),
22407 cpp_tree_shape(&padded),
22408 "region and padded reparse disagree at offset {start} of {end} bytes"
22409 );
22410 assert_eq!(
22411 region.root_node().start_byte(),
22412 start,
22413 "the reparsed region keeps its original offsets"
22414 );
22415 }
22416 }
22417
22418 #[test]
22419 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
22420 fragmented_class_reparse_agreement(
22424 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
22425 );
22426 fragmented_class_reparse_agreement(
22427 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
22428 );
22429 fragmented_class_reparse_agreement(
22432 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
22433 );
22434 fragmented_class_reparse_agreement(
22435 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
22436 );
22437 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
22440 }
22441
22442 fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
22443 let mut siblings = Vec::new();
22444 let mut anchor = node;
22445 while let Some(parent) = anchor.parent() {
22446 let at_translation_unit = parent.kind() == "translation_unit";
22447 let mut sibling = anchor.next_named_sibling();
22448 while let Some(current) = sibling {
22449 if at_translation_unit
22450 && (current.kind() == "namespace_definition"
22451 || (current.kind() == "function_definition"
22452 && first_class_like_child(current).is_some()))
22453 {
22454 return siblings;
22455 }
22456 siblings.push(current);
22457 if cpp_is_stray_close_brace(current, source) {
22458 if let Some(semicolon) = current
22459 .next_named_sibling()
22460 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
22461 {
22462 siblings.push(semicolon);
22463 }
22464 return siblings;
22465 }
22466 if current.start_byte() >= node.end_byte()
22467 && matches!(current.kind(), "ERROR" | "labeled_statement")
22468 && cpp_nested_stray_close_brace(current, source).is_some()
22469 {
22470 return siblings;
22471 }
22472 sibling = current.next_named_sibling();
22473 }
22474 anchor = parent;
22475 }
22476 siblings
22477 }
22478
22479 #[test]
22480 fn following_named_siblings_cursor_matches_native_traversal() {
22481 let source = r#"
22485namespace api {
22486class PROJECT_API(2, 0) Widget : public virtual Base {
22487 public:
22488 Widget() {}
22489 void first();
22490};
22491enum After { Value };
22492}
22493"#;
22494 let mut parser = tree_sitter::Parser::new();
22495 parser
22496 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22497 .unwrap();
22498 let tree = parser.parse(source, None).unwrap();
22499 let root = tree.root_node();
22500 assert!(root.has_error(), "fixture must remain damaged");
22501
22502 let ancestry = ParentIndex::new(root);
22503 let mut stack = vec![root];
22504 let mut checked = 0;
22505 let mut damaged = 0;
22506 let mut anonymous = 0;
22507 let mut nonempty_sequences = 0;
22508 let mut named_then_anonymous = 0;
22509 while let Some(node) = stack.pop() {
22510 let expected = native_following_named_siblings(node, source)
22511 .iter()
22512 .map(Node::id)
22513 .collect::<Vec<_>>();
22514 let actual = cpp_following_named_siblings(node, source, &ancestry)
22515 .iter()
22516 .map(Node::id)
22517 .collect::<Vec<_>>();
22518 assert_eq!(
22519 actual,
22520 expected,
22521 "cursor traversal diverged at byte {}",
22522 node.start_byte()
22523 );
22524
22525 let mut cursor = node.walk();
22526 let children = node.children(&mut cursor).collect::<Vec<_>>();
22527 stack.extend(children.iter().copied());
22528 checked += 1;
22529 damaged += usize::from(node.is_error() || node.is_missing());
22530 anonymous += usize::from(!node.is_named());
22531 nonempty_sequences += usize::from(!actual.is_empty());
22532 named_then_anonymous += usize::from(
22533 children
22534 .windows(2)
22535 .any(|pair| pair[0].is_named() && !pair[1].is_named()),
22536 );
22537 }
22538 assert!(checked > 0, "fixture must provide nodes to compare");
22539 assert!(damaged > 0, "fixture must cover damaged nodes");
22540 assert!(anonymous > 0, "fixture must cover anonymous nodes");
22541 assert!(
22542 nonempty_sequences > 0,
22543 "fixture must exercise following named siblings"
22544 );
22545 assert!(
22546 named_then_anonymous > 0,
22547 "fixture must exercise anonymous nodes following a named anchor"
22548 );
22549 }
22550
22551 fn many_enums_and_mixed_declarations() -> String {
22555 let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
22556 for index in 0..40 {
22557 let _ = write!(
22558 source,
22559 "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
22560 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
22561 class Color{index}Like {{ public: int member{index}; }};\n"
22562 );
22563 }
22564 source.push_str("namespace outer {\n");
22565 for index in 0..20 {
22566 let _ = write!(
22567 source,
22568 "enum Shade{index} {{ DARK{index} }};\n\
22569 struct Shade{index}Holder {{ int field{index}; }};\n"
22570 );
22571 }
22572 source.push_str("}\n");
22573 source
22574 }
22575
22576 fn field_owner_index_agreement(source: &str, name: &str) -> usize {
22592 let parsed = parse_cpp_declarations(source, name);
22593 let file = ProjectFile::new(std::env::temp_dir(), name);
22594 let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
22595
22596 let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
22597 declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
22598
22599 let foreign: Vec<CodeUnit> = declarations
22600 .iter()
22601 .filter(|unit| unit.kind() == CodeUnitType::Field)
22602 .map(|unit| {
22603 CodeUnit::new_fq(
22604 elsewhere.clone(),
22605 unit.kind(),
22606 unit.package_name().to_string(),
22607 unit.short_name().to_string(),
22608 unit.fq().clone(),
22609 )
22610 })
22611 .collect();
22612
22613 let mut packages: Vec<String> = declarations
22619 .iter()
22620 .map(|unit| unit.package_name().to_string())
22621 .collect();
22622 packages.push(String::new());
22623 packages.sort();
22624 packages.dedup();
22625 let deeper: Vec<CodeUnit> = packages
22626 .iter()
22627 .map(|package_name| {
22628 CodeUnit::new_fq(
22629 file.clone(),
22630 CodeUnitType::Field,
22631 package_name.clone(),
22632 "SynthOwner.middle.leaf".to_string(),
22633 cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
22634 )
22635 })
22636 .collect();
22637
22638 let mut questions: Vec<(String, String)> = Vec::new();
22642 for unit in declarations.iter().chain(deeper.iter()) {
22643 let package_name = unit.package_name().to_string();
22644 let short_name = unit.short_name();
22645 questions.push((package_name.clone(), short_name.to_string()));
22646 questions.push((package_name.clone(), String::new()));
22647 for (offset, _) in short_name.match_indices('.') {
22648 questions.push((package_name.clone(), short_name[..offset].to_string()));
22649 }
22650 }
22651 questions.sort();
22652 questions.dedup();
22653
22654 let mut index = CppFieldOwnerIndex::default();
22655 let mut recorded: Vec<&CodeUnit> = Vec::new();
22656 let mut answered = 0usize;
22657 for unit in foreign
22658 .iter()
22659 .chain(declarations.iter())
22660 .chain(deeper.iter())
22661 {
22662 index.record(unit, &file);
22663 recorded.push(unit);
22664 for (package_name, owner_short_name) in &questions {
22665 let carried = index.owns_fields(package_name, owner_short_name);
22666 assert_eq!(
22667 carried,
22668 cpp_declarations_hold_owned_fields(
22669 recorded.iter().copied(),
22670 &file,
22671 package_name,
22672 owner_short_name
22673 ),
22674 "the carried field index and the declaration scan disagree about \
22675 {package_name:?}/{owner_short_name:?} after recording {}",
22676 unit.fq_name()
22677 );
22678 answered += usize::from(carried);
22679 }
22680 }
22681
22682 let rebuilt = CppFieldOwnerIndex::of(
22685 foreign
22686 .iter()
22687 .chain(declarations.iter())
22688 .chain(deeper.iter()),
22689 &file,
22690 );
22691 for (package_name, owner_short_name) in &questions {
22692 assert_eq!(
22693 rebuilt.owns_fields(package_name, owner_short_name),
22694 index.owns_fields(package_name, owner_short_name),
22695 "a rebuilt index must answer what the incremental one answers for \
22696 {package_name:?}/{owner_short_name:?}"
22697 );
22698 }
22699 answered
22700 }
22701
22702 #[test]
22711 fn a_replacement_that_removes_children_drops_the_field_index() {
22712 let source =
22713 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
22714 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
22715 let mut names: Vec<_> = parsed
22716 .declarations()
22717 .iter()
22718 .map(|unit| unit.fq_name())
22719 .collect();
22720 names.sort();
22721 assert_eq!(
22722 names,
22723 vec![
22724 "Color".to_string(),
22725 "First".to_string(),
22726 "First.A".to_string()
22727 ],
22728 "the replaced Color owns no field any more"
22729 );
22730 }
22731
22732 #[test]
22741 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
22742 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
22743 BEGIN_NS\n\
22744 namespace demo { struct Widget { void doWork(); }; }\n\
22745 END_NS\n";
22746 let parsed = parse_cpp_declarations(source, "restored.cpp");
22747 let recovered: Vec<String> = parsed
22748 .materialization_records
22749 .iter()
22750 .filter_map(|record| match record {
22751 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
22752 _ => None,
22753 })
22754 .collect();
22755 assert!(
22756 recovered.is_empty(),
22757 "the region declares nothing the file did not already declare: {recovered:?}"
22758 );
22759 let mut names: Vec<String> = parsed
22760 .declarations()
22761 .iter()
22762 .map(|unit| unit.fq_name())
22763 .collect();
22764 names.sort();
22765 assert_eq!(
22766 names,
22767 vec![
22768 "demo".to_string(),
22769 "demo.Widget".to_string(),
22770 "demo.Widget.doWork".to_string(),
22771 ]
22772 );
22773 }
22774
22775 #[test]
22780 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
22781 let mut source = String::new();
22782 for index in 0..4 {
22783 let _ = write!(
22784 source,
22785 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
22786 );
22787 }
22788 source.push_str("void outside() {}\n");
22789 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
22790
22791 let recovered: Vec<(String, (usize, usize))> = parsed
22792 .materialization_records
22793 .iter()
22794 .filter_map(|record| match record {
22795 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
22796 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
22797 }
22798 _ => None,
22799 })
22800 .collect();
22801
22802 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
22803 for index in 0..4 {
22804 let region = format!("namespace demo{index}");
22807 let region_start = source.find(®ion).expect("each region is in the source");
22808 let start = source[..region_start]
22809 .rfind("BEGIN_NS")
22810 .expect("each region opens with a sentinel")
22811 + "BEGIN_NS".len();
22812 let end = start
22813 + source[start..]
22814 .find("END_NS")
22815 .expect("each region closes with a sentinel")
22816 - 1;
22817 let window = (start, end);
22818 for name in [
22819 format!("demo{index}"),
22820 format!("demo{index}.Widget{index}"),
22821 format!("demo{index}.Widget{index}.doWork{index}"),
22822 ] {
22823 expected.push((name, window));
22824 }
22825 }
22826 assert_eq!(
22827 recovered, expected,
22828 "each recovery records its own minted declarations, in order"
22829 );
22830 assert!(
22831 parsed
22832 .declarations()
22833 .iter()
22834 .any(|unit| unit.fq_name() == "outside"),
22835 "the declaration outside every region stays parsed and unrecovered"
22836 );
22837 }
22838
22839 #[test]
22840 fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
22841 assert!(
22842 field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
22843 "the fixture must actually own fields"
22844 );
22845
22846 for (source, name) in [
22852 ("struct S { enum E { V }; };\n", "nested.hpp"),
22853 (
22854 "enum Color { RED };\nstruct Color { int RED; };\n",
22855 "class-like.c",
22856 ),
22857 ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
22858 (
22859 "enum E { V };\nnamespace ns { enum E { V }; }\n",
22860 "repeated.hpp",
22861 ),
22862 ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
22863 ] {
22864 field_owner_index_agreement(source, name);
22865 }
22866 }
22867
22868 fn repeated_class_blocks(blocks: usize) -> String {
22873 let mut source = String::from("namespace demo {\n");
22874 for index in 0..blocks {
22875 let _ = write!(
22876 source,
22877 "\nclass Widget{index} {{\npublic:\n int translate() const {{ return {index}; }}\n int helper(const Widget{index}& other) const {{ return other.translate(); }}\nprivate:\n int field = {index};\n}};\n"
22878 );
22879 }
22880 source.push_str("\n}\n");
22881 source
22882 }
22883
22884 #[test]
22892 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
22893 let mut answers = Vec::new();
22894 let mut visits = Vec::new();
22895 let mut node_counts = Vec::new();
22896 for blocks in [200usize, 400] {
22897 let source = repeated_class_blocks(blocks);
22898 let mut parser = tree_sitter::Parser::new();
22899 parser
22900 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22901 .unwrap();
22902 let tree = parser.parse(&source, None).unwrap();
22903 let start_byte = source.find("class Widget0 ").expect("first class");
22904 let end_byte = start_byte
22905 + source[start_byte..]
22906 .find("};")
22907 .expect("first class terminator")
22908 + "};".len();
22909 let range = Range {
22910 start_byte,
22911 end_byte,
22912 start_line: 0,
22913 end_line: 0,
22914 };
22915 reset_recovered_class_body_node_visits_for_test();
22916 let recovered_export_classes =
22917 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
22918 answers.push(recovered_class_body_at(
22919 &recovered_export_classes,
22920 tree.root_node(),
22921 &source,
22922 "Widget0",
22923 &range,
22924 ));
22925 visits.push(recovered_class_body_node_visits_for_test());
22926 let mut nodes = 0usize;
22927 let mut stack = vec![tree.root_node()];
22928 while let Some(node) = stack.pop() {
22929 nodes += 1;
22930 let mut cursor = node.walk();
22931 stack.extend(node.named_children(&mut cursor));
22932 }
22933 node_counts.push(nodes);
22934 }
22935
22936 assert_eq!(
22937 answers,
22938 vec![None, None],
22939 "no recovered shape claims a plain class"
22940 );
22941 assert_eq!(
22942 visits[0], visits[1],
22943 "the walk must follow the range's own path, so doubling the unrelated \
22944 classes must not change the node count: {visits:?} over trees of \
22945 {node_counts:?} nodes"
22946 );
22947 assert!(
22948 visits[1] * 20 < node_counts[1],
22949 "the walk must stay far below one pass over the tree: {visits:?} over \
22950 trees of {node_counts:?} nodes"
22951 );
22952 }
22953
22954 #[test]
22955 fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
22956 let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
22957 let mut parser = tree_sitter::Parser::new();
22958 parser
22959 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22960 .expect("C++ grammar");
22961 let tree = parser.parse(source, None).expect("fixture tree");
22962 let mut stack = vec![tree.root_node()];
22963 let mut recovered = None;
22964 while let Some(node) = stack.pop() {
22965 if node.kind() == "field_declaration"
22966 && let Some(field) = recovered_function_like_field_declarator(node, source)
22967 {
22968 recovered = Some((node, field.name));
22969 break;
22970 }
22971 let mut cursor = node.walk();
22972 stack.extend(node.named_children(&mut cursor));
22973 }
22974 let (declaration, name) = recovered.unwrap_or_else(|| {
22975 panic!(
22976 "pointer-wrapped macro field was not recovered: {}",
22977 tree.root_node().to_sexp()
22978 )
22979 });
22980 assert_eq!(node_text(name, source), "handshake");
22981 let recovered =
22982 recovered_function_like_field_declarator(declaration, source).expect("recovered field");
22983 assert_eq!(recovered.pointer_depth(), 1);
22984 assert_eq!(
22985 render_cpp_field_signature(declaration, name, source),
22986 "struct handshake * handshake;"
22987 );
22988 }
22989
22990 #[test]
22991 fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
22992 let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
22993 let mut parser = tree_sitter::Parser::new();
22994 parser
22995 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22996 .expect("C++ grammar");
22997 let tree = parser.parse(source, None).expect("fixture tree");
22998 let mut stack = vec![tree.root_node()];
22999 let mut recovered = None;
23000 while let Some(node) = stack.pop() {
23001 if let Some(field) = recovered_pyobject_head_field(node, source) {
23002 recovered = Some((node, field));
23003 break;
23004 }
23005 let mut cursor = node.walk();
23006 stack.extend(node.named_children(&mut cursor));
23007 }
23008 let (declaration, recovered) = recovered.unwrap_or_else(|| {
23009 panic!(
23010 "pointer field after PyObject_HEAD was not recovered: {}",
23011 tree.root_node().to_sexp()
23012 )
23013 });
23014 assert_eq!(node_text(recovered.name, source), "image");
23015 assert_eq!(recovered.pointer_depth(), 1);
23016 assert_eq!(
23017 render_cpp_field_signature(declaration, recovered.declarator, source),
23018 "ImagingObject *image;"
23019 );
23020 }
23021
23022 #[test]
23027 fn callable_metadata_records_c_receiver_contracts_structurally() {
23028 const SOURCE: &str = concat!(
23029 "static long accumulate(const long *lines, int count) { return count; }\n",
23030 "\n",
23031 "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
23032 );
23033 let parsed = parse_cpp_declarations(SOURCE, "shop.c");
23034
23035 assert_eq!(
23036 recorded_callable_modifiers(&parsed),
23037 vec![
23038 "accumulate decl_only=false static=false constructor=false \
23039 visibility=Unknown"
23040 .to_string(),
23041 "total decl_only=false static=false constructor=false visibility=Unknown"
23042 .to_string(),
23043 ],
23044 );
23045 }
23046
23047 #[test]
23060 fn callable_metadata_records_cpp_receiver_contracts_structurally() {
23061 const SOURCE: &str = concat!(
23062 "namespace shop {\n",
23063 "\n",
23064 "class Client {\n",
23065 "public:\n",
23066 " Client();\n",
23067 " void send(int order);\n",
23068 " static int count();\n",
23069 "protected:\n",
23070 " virtual int audit(int n) const { return n; }\n",
23071 "private:\n",
23072 " int tally(int n) { return n; }\n",
23073 "};\n",
23074 "\n",
23075 "struct Ledger {\n",
23076 " void record(int n) { (void)n; }\n",
23077 "};\n",
23078 "\n",
23079 "Client::Client() {}\n",
23080 "void Client::send(int order) { (void)order; }\n",
23081 "int Client::count() { return 0; }\n",
23082 "\n",
23083 "int total(int order) { return order; }\n",
23084 "\n",
23085 "}\n",
23086 );
23087 let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
23088
23089 assert_eq!(
23090 recorded_callable_modifiers(&parsed),
23091 vec![
23092 "shop.Client.Client decl_only=false static=false constructor=true \
23093 visibility=Unknown"
23094 .to_string(),
23095 "shop.Client.Client decl_only=true static=false constructor=true \
23096 visibility=Public"
23097 .to_string(),
23098 "shop.Client.audit decl_only=false static=false constructor=false \
23099 visibility=Protected"
23100 .to_string(),
23101 "shop.Client.count decl_only=false static=false constructor=false \
23102 visibility=Unknown"
23103 .to_string(),
23104 "shop.Client.count decl_only=true static=true constructor=false \
23105 visibility=Public"
23106 .to_string(),
23107 "shop.Client.send decl_only=false static=false constructor=false \
23108 visibility=Unknown"
23109 .to_string(),
23110 "shop.Client.send decl_only=true static=false constructor=false \
23111 visibility=Public"
23112 .to_string(),
23113 "shop.Client.tally decl_only=false static=false constructor=false \
23114 visibility=Private"
23115 .to_string(),
23116 "shop.Ledger.record decl_only=false static=false constructor=false \
23117 visibility=Public"
23118 .to_string(),
23119 "shop.total decl_only=false static=false constructor=false visibility=Unknown"
23120 .to_string(),
23121 ],
23122 );
23123 }
23124
23125 fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
23132 let mut rows = parsed
23133 .signature_metadata
23134 .iter()
23135 .flat_map(|(unit, entries)| {
23136 entries.iter().map(move |metadata| {
23137 assert!(
23138 metadata.callable_modifiers_recorded(),
23139 "{} must record that the walk read its declaration shape",
23140 unit.fq_name()
23141 );
23142 format!(
23143 "{} decl_only={} static={} constructor={} visibility={:?}",
23144 unit.fq_name(),
23145 metadata.is_declaration_only(),
23146 metadata.callable_is_static(),
23147 metadata.callable_is_constructor(),
23148 metadata
23149 .callable_declared_visibility()
23150 .expect("a recorded modifier set states a visibility"),
23151 )
23152 })
23153 })
23154 .collect::<Vec<_>>();
23155 rows.sort();
23156 rows
23157 }
23158}