1use brokk_bifrost_core::analyzer::common::{node_source_text, parse_source_region};
8use brokk_bifrost_core::analyzer::fq_name::{
9 FqName, SegmentId, SegmentKind, joined_segments, normalize_joined, segment_interner,
10};
11use brokk_bifrost_core::analyzer::model::{
12 CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
13 CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
14 CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
15 ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
16 StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
17};
18use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
19use brokk_bifrost_core::analyzer::structural::materialization::{
20 GenerationKind, MaterializationRecord,
21};
22use brokk_bifrost_core::analyzer::tree_walk::{ParentIndex, WalkControl, walk_named_tree_preorder};
23use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
24use brokk_bifrost_core::hash::{HashMap, HashSet};
25use regex::Regex;
26use tree_sitter::{Node, Parser, Tree};
27
28fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
30 segment_interner().intern(text, kind)
31}
32
33fn cpp_push_package(fq: &mut FqName, package_name: &str) {
41 for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
42 fq.push(cpp_segment(component, SegmentKind::Package));
43 }
44}
45
46const CPP_PACKAGE_SEPARATOR: &str = "::";
48
49fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
56 let mut first = true;
57 for component in chain.split('$').filter(|c| !c.is_empty()) {
61 let kind = if first {
62 SegmentKind::Type
63 } else {
64 SegmentKind::Nested
65 };
66 fq.push(cpp_segment(component, kind));
67 first = false;
68 }
69}
70
71fn cpp_namespace_fq(full_name: &str) -> FqName {
75 let mut fq = FqName::new();
76 cpp_push_package(&mut fq, full_name);
77 fq
78}
79
80fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
96 let mut components = Vec::new();
97 let mut stack = vec![node];
98 while let Some(current) = stack.pop() {
99 match current.kind() {
100 "namespace_identifier" | "identifier" => {
101 components.push(normalize_cpp_whitespace(node_text(current, source)));
102 }
103 "nested_namespace_specifier" => {
104 for index in (0..current.named_child_count()).rev() {
105 stack.push(
106 current
107 .named_child(index)
108 .expect("index below the node's own named child count"),
109 );
110 }
111 }
112 _ => return cpp_raw_namespace_name_components(node, source),
113 }
114 }
115 if components.iter().any(String::is_empty) {
116 return cpp_raw_namespace_name_components(node, source);
117 }
118 components
119}
120
121fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
135 let start = node
136 .child(0)
137 .filter(|child| !child.is_named() && child.kind() == "::")
138 .map_or(node.start_byte(), |marker| marker.end_byte());
139 let text = normalize_cpp_whitespace(
140 source
141 .get(start..node.end_byte())
142 .expect("namespace name node covers one source range"),
143 );
144 let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
145 if text.is_empty() {
146 return Vec::new();
147 }
148 vec![text]
149}
150
151fn cpp_lexical_namespace_name<'tree>(
157 node: Node<'tree>,
158 source: &str,
159 ancestry: &ParentIndex<'tree>,
160) -> Option<String> {
161 let mut components = Vec::new();
162 let mut ancestor = ancestry.parent(node);
163 while let Some(current) = ancestor {
164 if current.kind() == "namespace_definition" {
165 let name_node = current.child_by_field_name("name")?;
166 let name = normalize_cpp_whitespace(node_text(name_node, source));
167 if name.is_empty() {
168 return None;
169 }
170 components.push(name);
171 }
172 ancestor = ancestry.parent(current);
173 }
174 if components.is_empty() {
175 return None;
176 }
177 components.reverse();
178 Some(
182 normalize_joined(
183 &components.join(CPP_PACKAGE_SEPARATOR),
184 CPP_PACKAGE_SEPARATOR,
185 )
186 .into_owned(),
187 )
188}
189
190fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
196 if parent_short.is_empty() {
197 name.to_string()
198 } else {
199 format!("{parent_short}${name}")
200 }
201}
202
203fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
206 if parent_short.is_empty() {
207 name.to_string()
208 } else {
209 format!("{parent_short}.{name}")
210 }
211}
212
213fn cpp_leaf_fq(
220 package_name: &str,
221 parent: Option<&CodeUnit>,
222 name: &str,
223 kind_if_nested: SegmentKind,
224 kind_if_top: SegmentKind,
225) -> FqName {
226 if let Some(parent) = parent {
227 parent
228 .fq()
229 .clone()
230 .with_pushed(cpp_segment(name, kind_if_nested))
231 } else {
232 let mut fq = FqName::new();
233 cpp_push_package(&mut fq, package_name);
234 fq.push(cpp_segment(name, kind_if_top));
235 fq
236 }
237}
238
239pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
246 let mut fq = FqName::new();
247 cpp_push_package(&mut fq, package_name);
248 match short_name.rsplit_once('.') {
249 Some((owner_chain, member)) => {
250 cpp_push_type_chain(&mut fq, owner_chain);
251 fq.push(cpp_segment(member, SegmentKind::Member));
252 }
253 None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
254 }
255 fq
256}
257
258#[derive(Clone)]
259pub struct ScopeInfo {
260 package_name: String,
261 module: Option<CodeUnit>,
262 class_unit: Option<CodeUnit>,
263 template_signature: Option<String>,
264 template_metadata: Option<CppTemplateMetadata>,
265 declarations_are_fields: bool,
266 recovered_specialization_member_scope: bool,
267 visible_using_namespaces: Vec<String>,
279}
280
281struct CppContainer<'tree> {
282 node: Node<'tree>,
283 scope: ScopeInfo,
284}
285
286struct CppNodeWork<'tree> {
287 node: Node<'tree>,
288 scope: ScopeInfo,
289}
290
291struct CppSiblingsWork<'tree> {
298 children: std::vec::IntoIter<Node<'tree>>,
299 scope: ScopeInfo,
300}
301
302enum CppWork<'tree> {
303 Container(CppContainer<'tree>),
304 Node(CppNodeWork<'tree>),
305 Siblings(CppSiblingsWork<'tree>),
306}
307
308fn class_like_name<'tree>(
309 node: Node<'tree>,
310 source: &str,
311 ancestry: &ParentIndex<'tree>,
312) -> Option<String> {
313 let best = class_like_name_from_children(node, source);
314 if let Some(parent) = ancestry.parent(node)
315 && matches!(
316 parent.kind(),
317 "declaration" | "field_declaration" | "function_definition"
318 )
319 && cpp_body_node(node).is_none()
328 && node
329 .child_by_field_name("name")
330 .map(|name_node| {
331 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
332 })
333 .unwrap_or(false)
334 && let Some(recovered) = exported_class_name_from_node(parent, source)
335 && best.as_deref() != Some(recovered.as_str())
336 {
337 return Some(recovered);
338 }
339 best.or_else(|| {
340 node.child_by_field_name("name")
341 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
342 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
343 })
344}
345
346fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
347 let mut grammar_name = None;
348 if let Some(name_node) = node.child_by_field_name("name") {
349 let name = normalize_cpp_whitespace(node_text(name_node, source));
350 if name.is_empty() {
351 return None;
352 }
353 if !cpp_export_macro_token(&name) {
354 return Some(name);
355 }
356 grammar_name = Some(name);
357 }
358
359 let mut best = None;
360 let mut cursor = node.walk();
361 let mut stack = Vec::new();
362 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
363 if matches!(
364 child.kind(),
365 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
366 ) {
367 break;
368 }
369 stack.push(child);
370 }
371
372 while let Some(current) = stack.pop() {
373 if matches!(current.kind(), "type_identifier" | "identifier") {
374 let name = normalize_cpp_whitespace(node_text(current, source));
375 if !name.is_empty() && !cpp_export_macro_token(&name) {
376 best = Some(name);
377 }
378 continue;
379 }
380
381 for index in (0..current.named_child_count()).rev() {
382 if let Some(child) = current.named_child(index) {
383 stack.push(child);
384 }
385 }
386 }
387 best.or(grammar_name)
388}
389
390pub fn cpp_export_macro_token(token: &str) -> bool {
391 token
392 .chars()
393 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
394}
395
396struct RecoveredExportedClass<'tree> {
397 declaration_node: Node<'tree>,
398 name: String,
399 body: Option<Node<'tree>>,
400 raw_supertypes: Option<Vec<String>>,
401 uses_initializer_body: bool,
402 fragmented_body: Option<FragmentedExportBody>,
407}
408
409struct RecoveredFunctionLikeExportClassPair {
410 name: String,
411 range: Range,
412 raw_supertypes: Option<Vec<String>>,
413 fragmented_body: FragmentedExportBody,
414}
415
416struct RecoveredEmbeddedFunctionLikeExportClass {
417 name: String,
418 range: Range,
419 raw_supertypes: Vec<String>,
420 fragmented_body: FragmentedExportBody,
421}
422
423struct FragmentedExportBody {
429 reparse_start: usize,
430 reparse_end: usize,
431 class_range: Range,
432}
433
434fn recovered_fragmented_export_body(
435 body: Node<'_>,
436 class_range: Range,
437) -> Option<FragmentedExportBody> {
438 let open = body.child(0).filter(|child| child.kind() == "{")?;
439 let close = body
440 .child(body.child_count().saturating_sub(1))
441 .filter(|child| child.kind() == "}" && !child.is_missing());
442 Some(FragmentedExportBody {
443 reparse_start: open.end_byte(),
444 reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
449 class_range,
450 })
451}
452
453struct DisplacedFragmentNamespaceBoundary<'tree> {
454 class_close: Node<'tree>,
455 class_semicolon: Node<'tree>,
456 namespace_items: Vec<Node<'tree>>,
457}
458
459enum FragmentedExportMembers {
464 Complete(Tree),
465 ConditionalConstructor(Tree),
466}
467
468#[derive(Clone, Copy)]
469struct DisplacedMacroClassTail {
470 split_index: usize,
471 class_range: Range,
472}
473
474fn recover_exported_class_declaration<'tree>(
475 node: Node<'tree>,
476 source: &str,
477) -> Option<RecoveredExportedClass<'tree>> {
478 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
479 return Some(recovered);
480 }
481
482 let class_node = first_class_like_child(node)?;
483 if let Some(name_node) = class_node.child_by_field_name("name") {
484 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
485 if cpp_export_macro_token(&class_name) {
486 let mut cursor = node.walk();
490 if node
491 .children_by_field_name("declarator", &mut cursor)
492 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
493 {
494 return None;
495 }
496 } else if has_direct_cpp_declarator(node) {
497 return None;
498 }
499 }
500 let name = exported_class_name_from_node(class_node, source)?;
501 Some(RecoveredExportedClass {
502 declaration_node: class_node,
503 name,
504 body: cpp_body_node(class_node),
505 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
506 .then(|| extract_cpp_supertypes(class_node, source)),
507 uses_initializer_body: false,
508 fragmented_body: None,
509 })
510}
511
512fn recover_malformed_exported_base_class<'tree>(
513 node: Node<'tree>,
514 source: &str,
515) -> Option<RecoveredExportedClass<'tree>> {
516 if node.kind() != "declaration" {
517 return None;
518 }
519 let class_node = node.child_by_field_name("type")?;
520 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
521 return None;
522 }
523 let macro_name = class_node
524 .child_by_field_name("name")
525 .and_then(|name| direct_identifier_name(name, source))?;
526 if !cpp_export_macro_token(¯o_name) {
527 return None;
528 }
529
530 let mut named_cursor = node.walk();
531 let mut named = node.named_children(&mut named_cursor);
532 if named
533 .next()
534 .is_none_or(|child| !same_node(child, class_node))
535 {
536 return None;
537 }
538 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
539 if displaced.kind() != "ERROR" {
540 return None;
541 }
542 let name = displaced_exported_class_name(displaced, source)?;
543
544 let remaining = named.collect::<Vec<_>>();
545 let init = *remaining.last()?;
546 if init.kind() != "init_declarator" {
547 return None;
548 }
549 let final_base = init
550 .child_by_field_name("declarator")
551 .and_then(|base| recovered_malformed_base_name(base, source))?;
552 let body = init.child_by_field_name("value")?;
553 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
557 return None;
558 }
559
560 if remaining[..remaining.len() - 1]
561 .iter()
562 .any(|child| match child.kind() {
563 "qualified_identifier"
564 | "scoped_type_identifier"
565 | "type_identifier"
566 | "identifier" => false,
567 "ERROR" => !is_malformed_inheritance_access(*child, source),
568 _ => true,
569 })
570 {
571 return None;
572 }
573
574 let mut raw_supertypes = Vec::new();
575 for base in &remaining[..remaining.len() - 1] {
576 if base.kind() == "ERROR" {
577 continue;
578 }
579 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
580 }
581 raw_supertypes.push(final_base);
582
583 Some(RecoveredExportedClass {
584 declaration_node: node,
585 name,
586 body: Some(body),
587 raw_supertypes: Some(raw_supertypes),
588 uses_initializer_body: true,
589 fragmented_body: fragmented_export_body_region(node, body, source),
590 })
591}
592
593fn fragmented_export_body_region(
609 node: Node<'_>,
610 body: Node<'_>,
611 source: &str,
612) -> Option<FragmentedExportBody> {
613 let reparse_start = body.start_byte() + 1;
614 let close = direct_close_brace(body)?;
615 if close.end_byte() > close.start_byte() {
616 return Some(FragmentedExportBody {
617 reparse_start,
618 reparse_end: close.start_byte(),
619 class_range: cpp_declaration_range(node),
620 });
621 }
622 let mut sibling = node.next_named_sibling();
625 let displaced_close = loop {
626 let Some(current) = sibling else {
627 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
628 };
629 if cpp_is_stray_close_brace(current, source) {
630 break current;
631 }
632 sibling = current.next_named_sibling();
633 };
634 Some(FragmentedExportBody {
635 reparse_start,
636 reparse_end: displaced_close.start_byte(),
637 class_range: Range {
638 start_byte: node.start_byte(),
639 end_byte: displaced_close.end_byte(),
640 start_line: node.start_position().row + 1,
641 end_line: displaced_close.end_position().row + 1,
642 },
643 })
644}
645
646fn fragmented_export_function_body_region(
654 node: Node<'_>,
655 body: Node<'_>,
656 source: &str,
657 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
658) -> Option<FragmentedExportBody> {
659 let reparse_start = body.start_byte().checked_add(1)?;
660 if let Some(boundary) = displaced_namespace {
661 return Some(FragmentedExportBody {
662 reparse_start,
663 reparse_end: boundary.class_close.start_byte(),
664 class_range: Range {
665 start_byte: node.start_byte(),
666 end_byte: boundary.class_semicolon.end_byte(),
667 start_line: node.start_position().row + 1,
668 end_line: boundary.class_semicolon.end_position().row + 1,
669 },
670 });
671 }
672 let siblings = cpp_following_named_siblings(node, source);
673 let boundary = fragmented_export_sibling_class_boundary(node, source);
674 let boundary_index = boundary.and_then(|boundary| {
675 siblings
676 .iter()
677 .position(|candidate| same_node(*candidate, boundary))
678 });
679 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
680 let mut sibling_index = 0;
681 while let Some(current) = siblings.get(sibling_index).copied() {
694 if current.kind() == "comment" {
695 sibling_index += 1;
696 continue;
697 }
698 if is_trailing_attribute_macro_sibling(current) {
699 sibling_index += 1;
700 continue;
701 }
702 if cpp_is_stray_semicolon(current, source) {
703 return None;
704 }
705 break;
706 }
707 while let Some(current) = siblings.get(sibling_index).copied() {
708 let next = siblings.get(sibling_index + 1).copied();
709 if cpp_is_stray_close_brace(current, source)
710 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
711 {
712 let semicolon = next.expect("checked above");
713 return Some(FragmentedExportBody {
714 reparse_start,
715 reparse_end: current.start_byte(),
716 class_range: Range {
717 start_byte: node.start_byte(),
718 end_byte: semicolon.end_byte(),
719 start_line: node.start_position().row + 1,
720 end_line: semicolon.end_position().row + 1,
721 },
722 });
723 }
724 if current.start_byte() >= body.end_byte()
731 && let Some(close) = cpp_nested_stray_close_brace(current, source)
732 {
733 return Some(FragmentedExportBody {
734 reparse_start,
735 reparse_end: close.start_byte(),
736 class_range: Range {
737 start_byte: node.start_byte(),
738 end_byte: current.end_byte(),
739 start_line: node.start_position().row + 1,
740 end_line: current.end_position().row + 1,
741 },
742 });
743 }
744 sibling_index += 1;
745 }
746 boundary.map(|boundary| FragmentedExportBody {
747 reparse_start,
748 reparse_end: boundary.start_byte(),
749 class_range: Range {
750 start_byte: node.start_byte(),
751 end_byte: boundary.start_byte(),
752 start_line: node.start_position().row + 1,
753 end_line: boundary.start_position().row + 1,
754 },
755 })
756}
757
758fn fragmented_export_sibling_class_boundary<'tree>(
763 node: Node<'tree>,
764 source: &str,
765) -> Option<Node<'tree>> {
766 let node_parent = node.parent()?;
767 cpp_following_named_siblings(node, source)
768 .into_iter()
769 .find(|candidate| {
770 recover_exported_class_function_definition(*candidate, source).is_some()
771 && candidate
772 .parent()
773 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
774 })
775}
776
777fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
782 if node.kind() != "expression_statement" {
783 return false;
784 }
785 let mut cursor = node.walk();
786 let mut children = node.named_children(&mut cursor);
787 children
788 .next()
789 .is_some_and(|child| child.kind() == "identifier")
790 && children.next().is_none()
791}
792
793fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
799 let mut stack = vec![node];
800 while let Some(current) = stack.pop() {
801 if cpp_is_stray_close_brace(current, source) {
802 return Some(current);
803 }
804 let mut cursor = current.walk();
805 stack.extend(current.named_children(&mut cursor));
806 }
807 None
808}
809
810fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
815 let mut siblings = Vec::new();
816 let mut anchor = node;
817 while let Some(parent) = anchor.parent() {
818 let at_translation_unit = parent.kind() == "translation_unit";
819 let mut sibling = anchor.next_named_sibling();
820 while let Some(current) = sibling {
821 if at_translation_unit
822 && (current.kind() == "namespace_definition"
823 || (current.kind() == "function_definition"
824 && first_class_like_child(current).is_some()))
825 {
826 return siblings;
827 }
828 siblings.push(current);
829 if cpp_is_stray_close_brace(current, source) {
830 if let Some(semicolon) = current
831 .next_named_sibling()
832 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
833 {
834 siblings.push(semicolon);
835 }
836 return siblings;
837 }
838 if current.start_byte() >= node.end_byte()
839 && matches!(current.kind(), "ERROR" | "labeled_statement")
840 && cpp_nested_stray_close_brace(current, source).is_some()
841 {
842 return siblings;
843 }
844 sibling = current.next_named_sibling();
845 }
846 anchor = parent;
847 }
848 siblings
849}
850
851fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
852 if node.start_byte() >= class_end {
853 return false;
854 }
855 node.end_byte() <= class_end
856 || cpp_nested_stray_close_brace(node, source)
857 .is_some_and(|close| close.start_byte() == class_end)
858}
859
860fn fragmented_plain_class_body<'tree>(
867 node: Node<'tree>,
868 source: &str,
869) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
870 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
871 return Some(recovered);
872 }
873 let supported_container = node.kind() == "ERROR"
874 || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
875 if !supported_container {
876 return None;
877 }
878 let mut cursor = node.walk();
879 let children = node.children(&mut cursor).collect::<Vec<_>>();
880 let keyword = children.first()?;
881 if !matches!(keyword.kind(), "class" | "struct" | "union") {
882 return None;
883 }
884 let name_node = children
885 .iter()
886 .copied()
887 .skip(1)
888 .find(|child| child.is_named())?;
889 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
890 return None;
891 }
892 let name = normalize_cpp_whitespace(node_text(name_node, source));
893 if name.is_empty() || cpp_export_macro_token(&name) {
894 return None;
895 }
896 let open_index = children.iter().position(|child| child.kind() == "{")?;
897 let open = children[open_index];
898 let nested_class_opens = children[open_index + 1..]
899 .iter()
900 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
901 .count();
902 let mut closes_remaining = 1 + nested_class_opens;
903 let mut sibling = node.next_named_sibling();
904 while let Some(candidate) = sibling {
905 let next = candidate.next_named_sibling();
906 if cpp_is_stray_close_brace(candidate, source) {
907 closes_remaining -= 1;
908 if closes_remaining == 0 {
909 let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
910 if open.end_byte() >= candidate.start_byte() {
911 return None;
912 }
913 return Some((
914 node,
915 name,
916 FragmentedExportBody {
917 reparse_start: open.end_byte(),
918 reparse_end: candidate.start_byte(),
919 class_range: Range {
920 start_byte: node.start_byte(),
921 end_byte: semicolon.end_byte(),
922 start_line: node.start_position().row + 1,
923 end_line: semicolon.end_position().row + 1,
924 },
925 },
926 ));
927 }
928 }
929 sibling = next;
930 }
931 None
932}
933
934pub(crate) fn recovered_fragmented_plain_class_has_body(
935 node: Node<'_>,
936 source: &str,
937 expected_name: &str,
938 expected_range: &Range,
939) -> bool {
940 fragmented_plain_class_body(node, source).is_some_and(|(_, name, fragmented)| {
941 name == expected_name
942 && fragmented.class_range.start_byte == expected_range.start_byte
943 && fragmented.class_range.end_byte == expected_range.end_byte
944 })
945}
946
947fn fragmented_plain_class_declaration_body<'tree>(
954 node: Node<'tree>,
955 source: &str,
956) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
957 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
958 return None;
959 }
960 let class_node = node.child_by_field_name("type")?;
961 if !matches!(
962 class_node.kind(),
963 "class_specifier" | "struct_specifier" | "union_specifier"
964 ) {
965 return None;
966 }
967 let name_node = class_node.child_by_field_name("name")?;
968 let name = normalize_cpp_whitespace(node_text(name_node, source));
969 if name.is_empty() || cpp_export_macro_token(&name) {
970 return None;
971 }
972 let body = cpp_body_node(class_node)?;
973 if body.kind() != "field_declaration_list" {
974 return None;
975 }
976 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
977 if declarator.start_byte() < class_node.end_byte() {
978 return None;
979 }
980 let mut cursor = node.walk();
981 node.named_children(&mut cursor).any(|child| {
982 if child.kind() != "ERROR"
983 || child.start_byte() < class_node.end_byte()
984 || child.end_byte() > declarator.start_byte()
985 {
986 return false;
987 }
988 let mut cursor = child.walk();
989 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
990 let Some((return_type, attributes)) = components.split_last() else {
991 return false;
992 };
993 matches!(
994 return_type.kind(),
995 "identifier"
996 | "type_identifier"
997 | "primitive_type"
998 | "decltype"
999 | "placeholder_type_specifier"
1000 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1001 && attributes.iter().all(|attribute| {
1002 matches!(attribute.kind(), "identifier" | "type_identifier")
1003 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1004 *attribute, source,
1005 )))
1006 })
1007 })
1008 } else {
1009 let mut cursor = node.walk();
1010 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1011 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1012 if same_node(*candidate_class, class_node)
1013 && continuation.kind() == "identifier"
1014 && node_text(*continuation, source) == "else"
1015 && continuation_body.kind() == "compound_statement"
1016 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1017 && continuation_body
1018 .child(continuation_body.child_count().saturating_sub(1))
1019 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1020 };
1021 if !displaced_member {
1022 return None;
1023 }
1024 let open = body
1025 .children(&mut body.walk())
1026 .find(|child| child.kind() == "{")?;
1027 let siblings = cpp_following_named_siblings(node, source);
1028 let ordinary_boundary =
1029 siblings
1030 .iter()
1031 .copied()
1032 .enumerate()
1033 .find_map(|(close_index, close)| {
1034 cpp_is_stray_close_brace(close, source)
1035 .then(|| {
1036 siblings
1037 .get(close_index + 1)
1038 .copied()
1039 .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1040 .map(|semicolon| (close, semicolon))
1041 })
1042 .flatten()
1043 });
1044 let (close, semicolon) =
1045 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1046 (boundary.class_close, boundary.class_semicolon)
1047 } else {
1048 ordinary_boundary?
1049 };
1050 if open.end_byte() >= close.start_byte() {
1051 return None;
1052 }
1053 Some((
1054 class_node,
1055 name,
1056 FragmentedExportBody {
1057 reparse_start: open.end_byte(),
1058 reparse_end: close.start_byte(),
1059 class_range: Range {
1060 start_byte: class_node.start_byte(),
1061 end_byte: semicolon.end_byte(),
1062 start_line: class_node.start_position().row + 1,
1063 end_line: semicolon.end_position().row + 1,
1064 },
1065 },
1066 ))
1067}
1068
1069fn displaced_export_function_namespace_shape<'tree>(
1070 declaration: Node<'tree>,
1071 source: &str,
1072) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1073 let mut nested = Vec::new();
1074 for index in (0..declaration.named_child_count()).rev() {
1075 nested.push(declaration.named_child(index)?);
1076 }
1077 while let Some(current) = nested.pop() {
1078 if recover_exported_class_function_definition(current, source).is_some() {
1084 return None;
1085 }
1086 for index in (0..current.named_child_count()).rev() {
1087 nested.push(current.named_child(index)?);
1088 }
1089 }
1090 let mut same_envelope_sibling = declaration.next_named_sibling();
1091 while let Some(current) = same_envelope_sibling {
1092 if recover_exported_class_function_definition(current, source).is_some() {
1093 return None;
1094 }
1095 same_envelope_sibling = current.next_named_sibling();
1096 }
1097 let declaration_list = declaration.parent()?;
1098 if declaration_list.kind() != "declaration_list" {
1099 return None;
1100 }
1101 let namespace = declaration_list.parent()?;
1102 if namespace.kind() != "namespace_definition"
1103 || namespace.child_by_field_name("body") != Some(declaration_list)
1104 {
1105 return None;
1106 }
1107 let class_close = direct_close_brace(declaration_list)?;
1108 let trailing_semicolon = namespace.next_named_sibling()?;
1109 if trailing_semicolon.kind() != "expression_statement"
1110 || trailing_semicolon.named_child_count() != 0
1111 {
1112 return None;
1113 }
1114 let siblings = cpp_following_named_siblings(namespace, source);
1120 let trailing_index = siblings
1121 .iter()
1122 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1123 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1124 recover_exported_class_function_definition(*candidate, source).is_some()
1125 }) {
1126 return None;
1131 }
1132 let mut namespace_items = Vec::new();
1133 let mut nested_fragment_end = 0;
1134 for current in siblings.into_iter().skip(trailing_index + 1) {
1135 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1136 {
1137 return Some(DisplacedFragmentNamespaceBoundary {
1138 class_close,
1139 class_semicolon: trailing_semicolon,
1140 namespace_items,
1141 });
1142 }
1143 if current.start_byte() >= nested_fragment_end
1144 && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1145 {
1146 nested_fragment_end = fragmented.class_range.end_byte;
1147 } else if current.start_byte() >= nested_fragment_end
1148 && recover_exported_class_function_definition(current, source).is_some()
1149 && let Some(body) = cpp_body_node(current)
1150 && let Some(fragmented) =
1151 fragmented_export_function_body_region(current, body, source, None)
1152 {
1153 nested_fragment_end = fragmented.class_range.end_byte;
1154 }
1155 namespace_items.push(current);
1156 }
1157 None
1158}
1159
1160fn displaced_fragment_namespace_boundary<'tree>(
1161 declaration: Node<'tree>,
1162 body: Node<'tree>,
1163 source: &str,
1164) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1165 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1166 let reparse_start = body.start_byte() + 1;
1167 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1168 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1169}
1170
1171fn displaced_fragment_namespace_geometry<'tree>(
1177 declaration: Node<'tree>,
1178 source: &str,
1179) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1180 let envelope = declaration
1184 .parent()
1185 .filter(|parent| {
1186 parent.kind() == "template_declaration"
1187 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1188 })
1189 .unwrap_or(declaration);
1190 let declaration_list = envelope.parent()?;
1191 if declaration_list.kind() != "declaration_list" {
1192 return None;
1193 }
1194 let namespace = declaration_list.parent()?;
1195 if namespace.kind() != "namespace_definition"
1196 || namespace.child_by_field_name("body") != Some(declaration_list)
1197 {
1198 return None;
1199 }
1200 let class_close = direct_close_brace(declaration_list)?;
1201 let trailing_semicolon = namespace.next_named_sibling()?;
1202 if trailing_semicolon.kind() != "expression_statement"
1203 || trailing_semicolon.named_child_count() != 0
1204 {
1205 return None;
1206 }
1207 let mut namespace_items = Vec::new();
1208 let mut sibling = trailing_semicolon.next_named_sibling();
1209 let mut nested_fragment_end = 0;
1210 loop {
1211 let current = sibling?;
1212 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1213 {
1214 break;
1215 }
1216 if current.start_byte() >= nested_fragment_end
1217 && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1218 {
1219 nested_fragment_end = fragmented.class_range.end_byte;
1220 }
1221 namespace_items.push(current);
1222 sibling = current.next_named_sibling();
1223 }
1224 Some(DisplacedFragmentNamespaceBoundary {
1225 class_close,
1226 class_semicolon: trailing_semicolon,
1227 namespace_items,
1228 })
1229}
1230
1231fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1233 (0..node.child_count())
1234 .filter_map(|index| node.child(index))
1235 .find(|child| !child.is_named() && child.kind() == "}")
1236}
1237
1238fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1241 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1242}
1243
1244fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1255 let bytes = source.as_bytes();
1256 if bytes.get(open_byte) != Some(&b'{') {
1257 return None;
1258 }
1259 let mut depth = 0usize;
1260 let mut i = open_byte;
1261 while i < bytes.len() {
1262 match bytes[i] {
1263 b'{' => depth += 1,
1264 b'}' => {
1265 depth = depth.checked_sub(1)?;
1266 if depth == 0 {
1267 return Some(i);
1268 }
1269 }
1270 b'/' if bytes.get(i + 1) == Some(&b'/') => {
1271 while i < bytes.len() && bytes[i] != b'\n' {
1272 i += 1;
1273 }
1274 continue;
1275 }
1276 b'/' if bytes.get(i + 1) == Some(&b'*') => {
1277 i += 2;
1278 while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1279 i += 1;
1280 }
1281 i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1282 continue;
1283 }
1284 quote @ (b'"' | b'\'') => {
1285 if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1288 return None;
1289 }
1290 i += 1;
1291 while i < bytes.len() && bytes[i] != quote {
1292 i += if bytes[i] == b'\\' { 2 } else { 1 };
1293 }
1294 if i >= bytes.len() {
1295 return None;
1296 }
1297 }
1298 _ => {}
1299 }
1300 i += 1;
1301 }
1302 None
1303}
1304
1305fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1306 let mut name = None;
1307 let mut colon_count = 0;
1308 let mut access_count = 0;
1309 for index in 0..node.child_count() {
1310 let child = node.child(index)?;
1311 match child.kind() {
1312 "identifier" | "type_identifier" if child.is_named() => {
1313 if name.is_some() {
1314 return None;
1315 }
1316 let candidate = normalize_cpp_whitespace(node_text(child, source));
1317 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1318 return None;
1319 }
1320 name = Some(candidate);
1321 }
1322 "template_function" | "template_type" if child.is_named() => {
1323 if name.is_some() {
1324 return None;
1325 }
1326 let candidate = child
1327 .child_by_field_name("name")
1328 .and_then(|name| direct_identifier_name(name, source))?;
1329 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1330 return None;
1331 }
1332 name = Some(candidate);
1333 }
1334 ":" if !child.is_named() => colon_count += 1,
1335 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1336 _ => return None,
1337 }
1338 }
1339 (colon_count == 1 && access_count == 1)
1340 .then_some(name)
1341 .flatten()
1342}
1343
1344fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1345 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1346 return false;
1347 }
1348 node.named_child(0)
1349 .and_then(|child| direct_identifier_name(child, source))
1350 .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1351}
1352
1353fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1354 (0..node.child_count()).any(|index| {
1355 node.child(index)
1356 .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1357 })
1358}
1359
1360fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1361 match node.kind() {
1362 "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1363 recovered_base_atom(node, source)
1364 }
1365 "template_type" | "template_function" => node
1366 .child_by_field_name("name")
1367 .and_then(|name| recovered_malformed_base_name(name, source)),
1368 "ERROR" => None,
1369 "qualified_identifier" | "scoped_type_identifier" => {
1370 let suffix = node
1371 .child_by_field_name("name")
1372 .and_then(|name| recovered_malformed_base_name(name, source))?;
1373 let scope = node
1374 .child_by_field_name("scope")
1375 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1376 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1377 malformed_qualified_prefix(node, source)?
1378 } else {
1379 if malformed_qualified_prefix(node, source).is_some() {
1380 return None;
1381 }
1382 scope
1383 };
1384 Some(format!("{prefix}::{suffix}"))
1385 }
1386 _ => None,
1387 }
1388}
1389
1390fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1391 if !matches!(
1392 node.kind(),
1393 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1394 ) {
1395 return None;
1396 }
1397 let name = normalize_cpp_whitespace(node_text(node, source));
1398 (!name.is_empty()).then_some(name)
1399}
1400
1401fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1402 let mut prefix = None;
1403 let mut cursor = node.walk();
1404 for error in node
1405 .named_children(&mut cursor)
1406 .filter(|child| child.kind() == "ERROR")
1407 {
1408 if error.named_child_count() != 1 || prefix.is_some() {
1409 return None;
1410 }
1411 prefix = error
1412 .named_child(0)
1413 .and_then(|child| recovered_base_atom(child, source));
1414 prefix.as_ref()?;
1415 }
1416 prefix
1417}
1418
1419fn recover_exported_class_function_definition<'tree>(
1420 node: Node<'tree>,
1421 source: &str,
1422) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
1423 if node.kind() != "function_definition" {
1424 return None;
1425 }
1426 if let Some(prefix) = node.prev_named_sibling()
1427 && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
1428 && recovered.range.end_byte == node.end_byte()
1429 {
1430 return Some((node, recovered.name, recovered.raw_supertypes));
1431 }
1432 let type_node = node.child_by_field_name("type")?;
1433 let declarator = node.child_by_field_name("declarator")?;
1434
1435 if matches!(
1436 type_node.kind(),
1437 "class_specifier" | "struct_specifier" | "union_specifier"
1438 ) {
1439 let type_name = type_node
1440 .child_by_field_name("name")
1441 .and_then(|name| direct_identifier_name(name, source));
1442 let exported_macro_type = type_name
1443 .as_ref()
1444 .is_some_and(|name| cpp_export_macro_token(name));
1445 if exported_macro_type {
1446 let mut cursor = node.walk();
1447 let errors_before_declarator = node
1448 .named_children(&mut cursor)
1449 .filter(|child| {
1450 child.kind() == "ERROR"
1451 && child.start_byte() >= type_node.end_byte()
1452 && child.end_byte() <= declarator.start_byte()
1453 })
1454 .collect::<Vec<_>>();
1455 if let Some(name) = errors_before_declarator
1456 .iter()
1457 .find_map(|error| displaced_exported_class_name(*error, source))
1458 {
1459 let raw_supertypes = errors_before_declarator
1460 .iter()
1461 .any(|error| malformed_inheritance_syntax(*error))
1462 .then(|| recovered_malformed_base_name(declarator, source))
1463 .flatten()
1464 .map(|base| vec![base]);
1465 return Some((node, name, raw_supertypes));
1466 }
1467 if errors_before_declarator
1468 .iter()
1469 .any(|error| malformed_inheritance_syntax(*error))
1470 {
1471 return None;
1472 }
1473 }
1474 if !exported_macro_type
1475 && let Some(name) = type_name
1476 && !cpp_export_macro_token(&name)
1477 && let Some(base) =
1478 recovered_postfix_export_macro_base(node, type_node, declarator, source)
1479 {
1480 return Some((node, name, Some(vec![base])));
1481 }
1482 if let Some(name) = direct_identifier_name(declarator, source)
1483 && exported_macro_type
1484 && !cpp_export_macro_token(&name)
1485 {
1486 let raw_supertypes = exported_macro_type
1487 .then(|| recovered_single_base_after_declarator(node, declarator, source))
1488 .flatten()
1489 .map(|base| vec![base]);
1490 return Some((node, name, raw_supertypes));
1491 }
1492 if declarator.kind() == "parenthesized_declarator"
1493 && type_node
1494 .child_by_field_name("name")
1495 .and_then(|name| direct_identifier_name(name, source))
1496 .is_some_and(|name| cpp_export_macro_token(&name))
1497 {
1498 if let Some((name, base)) =
1499 recovered_function_like_export_class_owner(declarator, source)
1500 {
1501 return Some((node, name, Some(vec![base])));
1502 }
1503 let body_start = node
1504 .child_by_field_name("body")
1505 .map(|body| body.start_byte())
1506 .unwrap_or(node.end_byte());
1507 let mut cursor = node.walk();
1508 if let Some(name) = node
1509 .named_children(&mut cursor)
1510 .filter(|child| {
1511 child.kind() == "ERROR"
1512 && child.start_byte() >= declarator.end_byte()
1513 && child.end_byte() <= body_start
1514 })
1515 .find_map(|error| declarator_name_from_node(error, source))
1516 {
1517 return Some((node, name, None));
1518 }
1519 }
1520 }
1521
1522 let declarator_text = direct_identifier_name(declarator, source)?;
1523 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
1524 return None;
1525 }
1526 class_identifier_before_body(node, source).map(|name| (node, name, None))
1527}
1528
1529fn recovered_function_like_export_class_owner(
1530 declarator: Node<'_>,
1531 source: &str,
1532) -> Option<(String, String)> {
1533 if declarator.kind() != "parenthesized_declarator" {
1534 return None;
1535 }
1536 let mut cursor = declarator.walk();
1537 let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
1538 let [prefix, base] = children.as_slice() else {
1539 return None;
1540 };
1541 if prefix.kind() != "ERROR"
1542 || !matches!(
1543 base.kind(),
1544 "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
1545 )
1546 {
1547 return None;
1548 }
1549 let mut identifiers = Vec::new();
1550 let mut prefix_cursor = prefix.walk();
1551 for child in prefix.named_children(&mut prefix_cursor) {
1552 match child.kind() {
1553 "number_literal" | "string_literal" | "char_literal" => {}
1554 "identifier" | "type_identifier" => {
1555 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
1556 }
1557 _ => return None,
1558 }
1559 }
1560 let name = match identifiers.as_slice() {
1561 [name] => name.clone(),
1562 [name, final_token] if final_token == "final" => name.clone(),
1563 _ => return None,
1564 };
1565 if name.is_empty() || cpp_export_macro_token(&name) {
1566 return None;
1567 }
1568 let base = recovered_malformed_base_name(*base, source)?;
1569 Some((name, base))
1570}
1571
1572fn recover_function_like_export_class_pair(
1573 node: Node<'_>,
1574 source: &str,
1575) -> Option<RecoveredFunctionLikeExportClassPair> {
1576 if node.kind() != "ERROR" {
1577 return None;
1578 }
1579 let class_node = first_class_like_child(node)?;
1580 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1581 return None;
1582 }
1583 let macro_name = class_node
1584 .child_by_field_name("name")
1585 .and_then(|name| direct_identifier_name(name, source))?;
1586 if !cpp_export_macro_token(¯o_name) {
1587 return None;
1588 }
1589 let sibling = node.next_named_sibling()?;
1590 let (name, raw_supertypes, body) = match sibling.kind() {
1591 "expression_statement" => {
1592 let compound = sibling.named_child(0)?;
1593 if compound.kind() != "compound_literal_expression" {
1594 return None;
1595 }
1596 let body = compound.child_by_field_name("value")?;
1597 if body.kind() != "initializer_list" {
1598 return None;
1599 }
1600 (
1601 compound
1602 .child_by_field_name("type")
1603 .and_then(|name| direct_identifier_name(name, source))?,
1604 None,
1605 body,
1606 )
1607 }
1608 "labeled_statement" => {
1609 let label = sibling.child_by_field_name("label")?;
1610 if label.kind() != "statement_identifier" {
1611 return None;
1612 }
1613 let name = normalize_cpp_whitespace(node_text(label, source));
1614 let declaration = sibling
1615 .named_children(&mut sibling.walk())
1616 .find(|child| child.kind() == "declaration")?;
1617 let access = declaration.child_by_field_name("type")?;
1618 if !matches!(
1619 node_text(access, source),
1620 "public" | "protected" | "private"
1621 ) {
1622 return None;
1623 }
1624 let init = declaration.child_by_field_name("declarator")?;
1625 if init.kind() != "init_declarator" {
1626 return None;
1627 }
1628 let body = init.child_by_field_name("value")?;
1629 if body.kind() != "initializer_list" {
1630 return None;
1631 }
1632 let base = init
1633 .child_by_field_name("declarator")
1634 .and_then(|base| recovered_malformed_base_name(base, source))?;
1635 (name, Some(vec![base]), body)
1636 }
1637 "function_definition" => {
1638 let type_node = sibling.child_by_field_name("type")?;
1639 let body = sibling.child_by_field_name("body")?;
1640 if body.kind() != "compound_statement" {
1641 return None;
1642 }
1643 let name = direct_identifier_name(type_node, source)?;
1644 let mut bases = Vec::new();
1645 let mut sibling_cursor = sibling.walk();
1646 for child in sibling.named_children(&mut sibling_cursor) {
1647 if same_node(child, type_node) || same_node(child, body) {
1648 continue;
1649 }
1650 if child.kind() == "ERROR" {
1651 let mut error_cursor = child.walk();
1652 bases.extend(
1653 child
1654 .named_children(&mut error_cursor)
1655 .filter_map(|part| recovered_malformed_base_name(part, source)),
1656 );
1657 } else if let Some(base) = recovered_malformed_base_name(child, source) {
1658 bases.push(base);
1659 }
1660 }
1661 bases.retain(|base| {
1662 !matches!(base.as_str(), "final" | "public" | "protected" | "private")
1663 });
1664 let [base] = bases.as_slice() else {
1665 return None;
1666 };
1667 (name, Some(vec![base.clone()]), body)
1668 }
1669 _ => return None,
1670 };
1671 if name.is_empty() || cpp_export_macro_token(&name) {
1672 return None;
1673 }
1674 let range = Range {
1675 start_byte: node.start_byte(),
1676 end_byte: sibling.end_byte(),
1677 start_line: node.start_position().row + 1,
1678 end_line: sibling.end_position().row + 1,
1679 };
1680 Some(RecoveredFunctionLikeExportClassPair {
1681 name,
1682 raw_supertypes,
1683 range,
1684 fragmented_body: recovered_fragmented_export_body(body, range)?,
1685 })
1686}
1687
1688fn recover_embedded_function_like_export_classes(
1695 node: Node<'_>,
1696 source: &str,
1697) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
1698 if node.kind() != "ERROR" {
1699 return Vec::new();
1700 }
1701
1702 let mut nodes = Vec::new();
1703 let mut stack = vec![node];
1704 while let Some(current) = stack.pop() {
1705 nodes.push(current);
1706 for index in (0..current.child_count()).rev() {
1707 stack.push(
1708 current
1709 .child(index)
1710 .expect("index below the node's own child count"),
1711 );
1712 }
1713 }
1714 nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
1715
1716 let mut recovered = Vec::new();
1717 for class_token in nodes
1718 .iter()
1719 .copied()
1720 .filter(|child| !child.is_named() && child.kind() == "class")
1721 {
1722 let row = class_token.start_position().row;
1723 let Some(macro_name) = nodes.iter().copied().find(|candidate| {
1724 candidate.start_byte() >= class_token.end_byte()
1725 && candidate.start_position().row == row
1726 && matches!(
1727 candidate.kind(),
1728 "identifier" | "type_identifier" | "field_identifier"
1729 )
1730 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*candidate, source)))
1731 }) else {
1732 continue;
1733 };
1734 let Some(arguments) = nodes.iter().copied().find(|candidate| {
1735 candidate.kind() == "argument_list"
1736 && candidate.start_byte() >= macro_name.end_byte()
1737 && candidate.start_position().row == row
1738 }) else {
1739 continue;
1740 };
1741 let Some(name_node) = nodes.iter().copied().find(|candidate| {
1742 candidate.kind() == "identifier"
1743 && candidate.start_byte() >= arguments.end_byte()
1744 && candidate.start_position().row == row
1745 }) else {
1746 continue;
1747 };
1748 let name = normalize_cpp_whitespace(node_text(name_node, source));
1749 if name.is_empty() || cpp_export_macro_token(&name) {
1750 continue;
1751 }
1752 let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
1753 candidate.kind() == "field_initializer"
1754 && candidate.start_byte() >= name_node.end_byte()
1755 && candidate
1756 .child_by_field_name("field")
1757 .or_else(|| candidate.named_child(0))
1758 .is_some()
1759 && candidate
1760 .child_by_field_name("value")
1761 .or_else(|| {
1762 let mut cursor = candidate.walk();
1763 candidate
1764 .named_children(&mut cursor)
1765 .find(|child| child.kind() == "initializer_list")
1766 })
1767 .is_some_and(|value| value.kind() == "initializer_list")
1768 }) else {
1769 continue;
1770 };
1771 let has_access = nodes.iter().copied().any(|candidate| {
1772 candidate.start_byte() >= name_node.end_byte()
1773 && candidate.end_byte() <= base_initializer.start_byte()
1774 && matches!(
1775 normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
1776 "public" | "protected" | "private"
1777 )
1778 });
1779 if !has_access {
1780 continue;
1781 }
1782 let Some(base_node) = base_initializer
1783 .child_by_field_name("field")
1784 .or_else(|| base_initializer.named_child(0))
1785 else {
1786 continue;
1787 };
1788 let Some(base) = recovered_malformed_base_name(base_node, source) else {
1789 continue;
1790 };
1791 let body = base_initializer
1792 .child_by_field_name("value")
1793 .or_else(|| {
1794 let mut cursor = base_initializer.walk();
1795 base_initializer
1796 .named_children(&mut cursor)
1797 .find(|child| child.kind() == "initializer_list")
1798 })
1799 .expect("initializer-list value checked above");
1800 let range = Range {
1801 start_byte: class_token.start_byte(),
1802 end_byte: body.end_byte(),
1803 start_line: class_token.start_position().row + 1,
1804 end_line: body.end_position().row + 1,
1805 };
1806 if recovered
1807 .iter()
1808 .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
1809 existing.name == name && existing.range == range
1810 })
1811 {
1812 continue;
1813 }
1814 recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
1815 name,
1816 range,
1817 raw_supertypes: vec![base],
1818 fragmented_body: match recovered_fragmented_export_body(body, range) {
1819 Some(fragmented) => fragmented,
1820 None => continue,
1821 },
1822 });
1823 }
1824 recovered
1825}
1826
1827fn lifted_function_like_export_class_namespace<'tree>(
1828 node: Node<'tree>,
1829 source: &str,
1830 ancestry: &ParentIndex<'tree>,
1831) -> Option<String> {
1832 let mut anchor = node;
1838 let parent = loop {
1839 let parent = ancestry.parent(anchor)?;
1840 if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
1841 break parent;
1842 }
1843 anchor = parent;
1844 };
1845 let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
1846 sibling.start_byte() > anchor.end_byte()
1847 && sibling.kind() == "ERROR"
1848 && sibling.named_child_count() == 0
1849 && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
1850 });
1851 if !has_later_close {
1852 return None;
1853 }
1854 let candidates = parent
1855 .named_children(&mut parent.walk())
1856 .filter(|sibling| {
1857 sibling.kind() == "namespace_definition"
1858 && sibling.has_error()
1859 && sibling.end_byte() < anchor.start_byte()
1860 })
1861 .filter_map(|namespace| {
1862 namespace
1863 .child_by_field_name("name")
1864 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
1865 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
1866 })
1867 .collect::<Vec<_>>();
1868 let [namespace] = candidates.as_slice() else {
1869 return None;
1870 };
1871 Some(namespace.clone())
1872}
1873
1874pub(crate) fn recovered_function_like_export_class_pair_has_body(
1875 node: Node<'_>,
1876 source: &str,
1877 identifier: &str,
1878 range: &Range,
1879) -> bool {
1880 recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
1881 recovered.name == identifier
1882 && recovered.range.start_byte == range.start_byte
1883 && recovered.range.end_byte == range.end_byte
1884 })
1885}
1886
1887pub(crate) fn recovered_embedded_function_like_export_class_has_body(
1888 node: Node<'_>,
1889 source: &str,
1890 identifier: &str,
1891 range: &Range,
1892) -> bool {
1893 recover_embedded_function_like_export_classes(node, source)
1894 .into_iter()
1895 .any(|recovered| {
1896 recovered.name == identifier
1897 && recovered.range.start_byte == range.start_byte
1898 && recovered.range.end_byte == range.end_byte
1899 })
1900}
1901
1902pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
1910 if !matches!(
1911 node.kind(),
1912 "qualified_identifier" | "scoped_type_identifier" | "template_type"
1913 ) {
1914 return false;
1915 }
1916 if let Some(function) = node.parent().filter(|parent| {
1917 parent.kind() == "function_definition"
1918 && parent
1919 .child_by_field_name("declarator")
1920 .is_some_and(|declarator| same_node(declarator, node))
1921 }) {
1922 return recover_exported_class_function_definition(function, source)
1923 .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
1924 }
1925 let Some(initializer) = node.parent().filter(|parent| {
1926 parent.kind() == "init_declarator"
1927 && parent
1928 .child_by_field_name("declarator")
1929 .is_some_and(|declarator| same_node(declarator, node))
1930 }) else {
1931 return false;
1932 };
1933 initializer
1934 .parent()
1935 .filter(|parent| parent.kind() == "declaration")
1936 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
1937 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
1938}
1939
1940struct CppSentinelReparsedClass<'tree> {
1946 declaration_node: Node<'tree>,
1947 name: String,
1948 body: Node<'tree>,
1949 raw_supertypes: Option<Vec<String>>,
1950}
1951
1952fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
1953 let mut cursor = root.walk();
1954 root.named_children(&mut cursor)
1955 .find(|child| child.kind() != "comment")
1956 .filter(|child| child.kind() == "template_declaration")
1957}
1958
1959fn cpp_sentinel_reparsed_class<'tree>(
1960 root: Node<'tree>,
1961 template_node: Option<Node<'tree>>,
1962 source: &str,
1963 ancestry: &ParentIndex<'tree>,
1964) -> Option<CppSentinelReparsedClass<'tree>> {
1965 let container = template_node.unwrap_or(root);
1966 let mut cursor = container.walk();
1967 for child in container.named_children(&mut cursor) {
1968 if matches!(
1969 child.kind(),
1970 "class_specifier" | "struct_specifier" | "union_specifier"
1971 ) {
1972 let name = class_like_name(child, source, ancestry)?;
1973 let body = cpp_body_node(child)?;
1974 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
1975 .then(|| extract_cpp_supertypes(child, source));
1976 return Some(CppSentinelReparsedClass {
1977 declaration_node: child,
1978 name,
1979 body,
1980 raw_supertypes,
1981 });
1982 }
1983 if child.kind() == "declaration"
1984 && let Some(class_node) = first_class_like_child(child)
1985 {
1986 let name = class_like_name(class_node, source, ancestry)?;
1987 let body = cpp_body_node(class_node)?;
1988 let raw_supertypes =
1989 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1990 .then(|| extract_cpp_supertypes(class_node, source));
1991 return Some(CppSentinelReparsedClass {
1992 declaration_node: class_node,
1993 name,
1994 body,
1995 raw_supertypes,
1996 });
1997 }
1998 if child.kind() == "function_definition"
2003 && let Some(class_node) = first_class_like_child(child)
2004 && let Some(body) = cpp_body_node(class_node)
2005 && let Some(name) = class_like_name(class_node, source, ancestry)
2006 {
2007 let raw_supertypes =
2008 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
2009 .then(|| extract_cpp_supertypes(class_node, source));
2010 return Some(CppSentinelReparsedClass {
2011 declaration_node: class_node,
2012 name,
2013 body,
2014 raw_supertypes,
2015 });
2016 }
2017 if child.kind() == "function_definition"
2018 && let Some((_, name, raw_supertypes)) =
2019 recover_exported_class_function_definition(child, source)
2020 {
2021 let body = cpp_body_node(child)?;
2022 return Some(CppSentinelReparsedClass {
2023 declaration_node: child,
2024 name,
2025 body,
2026 raw_supertypes,
2027 });
2028 }
2029 }
2030 None
2031}
2032
2033fn recovered_postfix_export_macro_base(
2034 node: Node<'_>,
2035 type_node: Node<'_>,
2036 declarator: Node<'_>,
2037 source: &str,
2038) -> Option<String> {
2039 let mut cursor = node.walk();
2040 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
2041 child.kind() == "ERROR"
2042 && child.start_byte() >= type_node.end_byte()
2043 && child.end_byte() <= declarator.start_byte()
2044 && postfix_export_macro_inheritance(*child, source)
2045 });
2046 malformed_clauses.next()?;
2047 if malformed_clauses.next().is_some() {
2048 return None;
2049 }
2050 recovered_malformed_base_name(declarator, source)
2051}
2052
2053fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
2054 let mut macro_count = 0;
2055 let mut colon_count = 0;
2056 let mut access_count = 0;
2057 for index in 0..node.child_count() {
2058 let Some(child) = node.child(index) else {
2059 return false;
2060 };
2061 match child.kind() {
2062 "identifier" | "type_identifier" if child.is_named() => {
2063 let candidate = normalize_cpp_whitespace(node_text(child, source));
2064 if !cpp_export_macro_token(&candidate) {
2065 return false;
2066 }
2067 macro_count += 1;
2068 }
2069 ":" if !child.is_named() => colon_count += 1,
2070 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
2071 _ => return false,
2072 }
2073 }
2074 macro_count == 1 && colon_count == 1 && access_count == 1
2075}
2076
2077fn recovered_single_base_after_declarator(
2078 node: Node<'_>,
2079 declarator: Node<'_>,
2080 source: &str,
2081) -> Option<String> {
2082 let body_start = node
2083 .child_by_field_name("body")
2084 .map(|body| body.start_byte())
2085 .unwrap_or(node.end_byte());
2086 let mut cursor = node.walk();
2087 let mut bases = node
2088 .named_children(&mut cursor)
2089 .filter(|child| {
2090 child.kind() == "ERROR"
2091 && child.start_byte() >= declarator.end_byte()
2092 && child.end_byte() <= body_start
2093 })
2094 .filter_map(|error| displaced_exported_class_name(error, source));
2095 let base = bases.next()?;
2096 bases.next().is_none().then_some(base)
2097}
2098
2099fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
2100 (0..node.child_count()).any(|index| {
2101 node.child(index)
2102 .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
2103 })
2104}
2105
2106pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
2107 recover_exported_class_function_definition(node, source).is_some()
2108}
2109
2110fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
2111 matches!(
2112 kind,
2113 "ERROR"
2114 | "preproc_if"
2115 | "preproc_ifdef"
2116 | "preproc_ifndef"
2117 | "preproc_else"
2118 | "preproc_elif"
2119 ) || (kind == "labeled_statement" && in_class_scope)
2120}
2121
2122pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
2123 if node.kind() != "declaration" {
2124 return false;
2125 }
2126 let mut ancestor = node.parent();
2127 while let Some(container) = ancestor {
2128 match container.kind() {
2129 "compound_statement" => {
2130 return container.parent().is_some_and(|class_container| {
2131 is_recovered_exported_class_container(class_container, source)
2132 });
2133 }
2134 "template_declaration" | "linkage_specification" | "declaration_list" => {}
2137 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
2138 _ => return false,
2139 }
2140 ancestor = container.parent();
2141 }
2142 false
2143}
2144
2145pub fn recovered_exported_class_has_body(
2146 node: Node<'_>,
2147 source: &str,
2148 expected_name: &str,
2149) -> Option<bool> {
2150 match node.kind() {
2151 "function_definition" => {
2152 let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
2153 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
2154 }
2155 "declaration" | "field_declaration" => {
2156 let recovered = recover_exported_class_declaration(node, source)?;
2157 (recovered.name == expected_name).then(|| recovered.body.is_some())
2158 }
2159 _ => None,
2160 }
2161}
2162
2163fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
2164 let body_start = node
2165 .child_by_field_name("body")
2166 .map(|body| body.start_byte())
2167 .unwrap_or(node.end_byte());
2168 let mut stack = Vec::new();
2169 for index in (0..node.named_child_count()).rev() {
2170 let Some(child) = node.named_child(index) else {
2171 continue;
2172 };
2173 if child.start_byte() >= body_start {
2174 continue;
2175 }
2176 stack.push(child);
2177 }
2178
2179 let mut best = None;
2180 while let Some(current) = stack.pop() {
2181 if matches!(current.kind(), "identifier" | "type_identifier") {
2182 let name = normalize_cpp_whitespace(node_text(current, source));
2183 if !name.is_empty()
2184 && !cpp_export_macro_token(&name)
2185 && !matches!(name.as_str(), "class" | "struct" | "union")
2186 {
2187 best = Some(name);
2188 }
2189 continue;
2190 }
2191
2192 for index in (0..current.named_child_count()).rev() {
2193 if let Some(child) = current.named_child(index)
2194 && child.start_byte() < body_start
2195 {
2196 stack.push(child);
2197 }
2198 }
2199 }
2200 best
2201}
2202
2203fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
2204 if node.kind() == "declaration"
2205 && node
2206 .child_by_field_name("type")
2207 .or_else(|| first_class_like_child(node))
2208 .is_some_and(|type_node| {
2209 matches!(
2210 type_node.kind(),
2211 "class_specifier" | "struct_specifier" | "union_specifier"
2212 )
2213 })
2214 && let Some(name) = node
2215 .child_by_field_name("declarator")
2216 .and_then(|declarator| declarator_name_from_node(declarator, source))
2217 && !cpp_export_macro_token(&name)
2218 {
2219 return Some(name);
2220 }
2221
2222 if node.kind() == "function_definition"
2223 && node.child_by_field_name("type").is_some_and(|type_node| {
2224 matches!(
2225 type_node.kind(),
2226 "class_specifier" | "struct_specifier" | "union_specifier"
2227 )
2228 })
2229 && let Some(name) = node
2230 .child_by_field_name("declarator")
2231 .and_then(|declarator| direct_identifier_name(declarator, source))
2232 && !cpp_export_macro_token(&name)
2233 {
2234 return Some(name);
2235 }
2236
2237 let class_node = if matches!(
2238 node.kind(),
2239 "class_specifier" | "struct_specifier" | "union_specifier"
2240 ) {
2241 node
2242 } else {
2243 first_class_like_child(node)?
2244 };
2245 class_like_name_from_children(class_node, source)
2246}
2247
2248fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
2249 if !matches!(
2250 node.kind(),
2251 "identifier" | "field_identifier" | "type_identifier"
2252 ) {
2253 return None;
2254 }
2255 let name = normalize_cpp_whitespace(node_text(node, source));
2256 (!name.is_empty()).then_some(name)
2257}
2258
2259fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
2260 match node.kind() {
2261 "identifier" | "field_identifier" | "type_identifier" => {
2262 let name = normalize_cpp_whitespace(node_text(node, source));
2263 (!name.is_empty()).then_some(name)
2264 }
2265 _ => {
2266 let mut cursor = node.walk();
2267 node.named_children(&mut cursor)
2268 .find_map(|child| declarator_name_from_node(child, source))
2269 }
2270 }
2271}
2272
2273fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
2274 let mut cursor = node.walk();
2275 node.named_children(&mut cursor).find(|child| {
2276 matches!(
2277 child.kind(),
2278 "class_specifier" | "struct_specifier" | "union_specifier"
2279 )
2280 })
2281}
2282
2283fn push_cpp_container_work<'tree>(
2288 node: Node<'tree>,
2289 scope: ScopeInfo,
2290 stack: &mut Vec<CppWork<'tree>>,
2291) {
2292 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
2293}
2294
2295fn push_cpp_sibling_range<'tree>(
2299 parent: Node<'tree>,
2300 start_index: usize,
2301 end_index: usize,
2302 scope: ScopeInfo,
2303 stack: &mut Vec<CppWork<'tree>>,
2304) {
2305 let mut cursor = parent.walk();
2306 let children = parent
2307 .named_children(&mut cursor)
2308 .skip(start_index)
2309 .take(end_index.saturating_sub(start_index))
2310 .collect::<Vec<_>>()
2311 .into_iter();
2312 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
2313}
2314
2315fn advance_cpp_siblings<'tree>(
2323 mut siblings: CppSiblingsWork<'tree>,
2324 source: &str,
2325 stack: &mut Vec<CppWork<'tree>>,
2326) {
2327 let Some(child) = siblings.children.next() else {
2328 return;
2329 };
2330 let current_scope = siblings.scope.clone();
2331 if let Some(namespace) = cpp_using_namespace_target(child, source) {
2332 siblings.scope.visible_using_namespaces.push(namespace);
2333 }
2334 if !siblings.children.as_slice().is_empty() {
2335 stack.push(CppWork::Siblings(siblings));
2336 }
2337 stack.push(CppWork::Node(CppNodeWork {
2338 node: child,
2339 scope: current_scope,
2340 }));
2341}
2342
2343fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
2350 if node.kind() != "using_declaration" {
2351 return None;
2352 }
2353 let mut cursor = node.walk();
2354 let is_namespace_directive = node
2355 .children(&mut cursor)
2356 .any(|child| child.kind() == "namespace");
2357 if !is_namespace_directive {
2358 return None;
2359 }
2360 let target = node.named_child(0)?;
2361 let start = target
2369 .child(0)
2370 .filter(|child| !child.is_named() && child.kind() == "::")
2371 .map_or(target.start_byte(), |marker| marker.end_byte());
2372 let text = normalize_cpp_whitespace(
2373 source
2374 .get(start..target.end_byte())
2375 .expect("using-directive target covers one source range"),
2376 );
2377 (!text.is_empty()).then_some(text)
2378}
2379
2380pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
2393 let mut parser = Parser::new();
2394 if parser
2395 .set_language(&tree_sitter_cpp::LANGUAGE.into())
2396 .is_err()
2397 {
2398 return Vec::new();
2399 }
2400 let Some(tree) = parser.parse(source, None) else {
2401 return Vec::new();
2402 };
2403 let mut namespaces = Vec::new();
2404 let mut seen = std::collections::HashSet::new();
2405 let mut stack = vec![tree.root_node()];
2406 while let Some(node) = stack.pop() {
2407 if let Some(namespace) = cpp_using_namespace_target(node, source)
2408 && seen.insert(namespace.clone())
2409 {
2410 namespaces.push(namespace);
2411 }
2412 let mut cursor = node.walk();
2413 stack.extend(node.named_children(&mut cursor));
2414 }
2415 namespaces
2416}
2417
2418pub struct CppVisitor<'a> {
2419 pub file: &'a ProjectFile,
2420 pub source: &'a str,
2421 pub parsed: &'a mut ParsedFile,
2422 pub c_tag_semantics: bool,
2433 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
2434 pub consumed_fragment_regions: Vec<(usize, usize)>,
2443 pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
2448 pub field_owners: Option<CppFieldOwnerIndex>,
2452 pub recovery_captures: Vec<CppRecoveryCapture>,
2456}
2457
2458impl<'a> CppVisitor<'a> {
2459 fn add_declaration(
2467 &mut self,
2468 code_unit: CodeUnit,
2469 node: Node<'_>,
2470 parent: Option<CodeUnit>,
2471 top_level: Option<CodeUnit>,
2472 ) {
2473 self.note_declaration(&code_unit);
2474 let source = self.source;
2475 self.parsed
2476 .add_code_unit(code_unit, node, source, parent, top_level);
2477 }
2478
2479 fn add_declaration_with_range(
2481 &mut self,
2482 code_unit: CodeUnit,
2483 range: Range,
2484 parent: Option<CodeUnit>,
2485 top_level: Option<CodeUnit>,
2486 ) {
2487 self.note_declaration(&code_unit);
2488 self.parsed
2489 .add_code_unit_with_range(code_unit, range, parent, top_level);
2490 }
2491
2492 fn replace_declaration_deferred(
2494 &mut self,
2495 code_unit: CodeUnit,
2496 node: Node<'_>,
2497 parent: Option<CodeUnit>,
2498 top_level: Option<CodeUnit>,
2499 ) {
2500 self.note_replaced_declaration(&code_unit);
2501 let source = self.source;
2502 self.parsed
2503 .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
2504 }
2505
2506 fn replace_declaration_with_range_deferred(
2508 &mut self,
2509 code_unit: CodeUnit,
2510 range: Range,
2511 parent: Option<CodeUnit>,
2512 top_level: Option<CodeUnit>,
2513 ) {
2514 self.note_replaced_declaration(&code_unit);
2515 self.parsed
2516 .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
2517 }
2518
2519 fn note_declaration(&mut self, code_unit: &CodeUnit) {
2526 if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
2527 for capture in &mut self.recovery_captures {
2528 if capture.removed_pre_existing.contains(code_unit) {
2529 continue;
2530 }
2531 if capture.created_units.insert(code_unit.clone()) {
2532 capture.created.push(code_unit.clone());
2533 }
2534 }
2535 }
2536 if let Some(field_owners) = self.field_owners.as_mut() {
2537 field_owners.record(code_unit, self.file);
2538 }
2539 }
2540
2541 fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
2550 let removes_children = self.parsed.contains_declaration(code_unit)
2551 && self
2552 .parsed
2553 .children
2554 .get(code_unit)
2555 .is_some_and(|children| !children.is_empty());
2556 if removes_children {
2557 if !self.recovery_captures.is_empty() {
2558 let removed = self.declarations_a_replacement_removes(code_unit);
2559 for capture in &mut self.recovery_captures {
2560 for unit in &removed {
2561 if !capture.created_units.contains(unit) {
2566 capture.removed_pre_existing.insert(unit.clone());
2567 }
2568 }
2569 }
2570 }
2571 self.field_owners = None;
2572 }
2573 self.note_declaration(code_unit);
2574 }
2575
2576 fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
2579 let mut removed = Vec::new();
2580 let mut seen = HashSet::default();
2581 let mut pending: Vec<CodeUnit> = self
2582 .parsed
2583 .children
2584 .get(code_unit)
2585 .cloned()
2586 .unwrap_or_default();
2587 while let Some(unit) = pending.pop() {
2588 if !seen.insert(unit.clone()) {
2589 continue;
2590 }
2591 if let Some(children) = self.parsed.children.get(&unit) {
2592 pending.extend(children.iter().cloned());
2593 }
2594 removed.push(unit);
2595 }
2596 removed
2597 }
2598
2599 fn visit_function_like_export_class_pair<'tree>(
2600 &mut self,
2601 node: Node<'tree>,
2602 scope: &ScopeInfo,
2603 stack: &mut Vec<CppWork<'tree>>,
2604 ancestry: &ParentIndex<'tree>,
2605 ) -> bool {
2606 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
2607 return false;
2608 };
2609 let member_outcome = self
2610 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
2611 let mut displaced = node.next_named_sibling();
2617 while let Some(candidate) = displaced {
2618 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
2619 break;
2620 }
2621 displaced = candidate.next_named_sibling();
2622 }
2623 let class_unit = self.visit_named_class_like_shape(
2624 node,
2625 recovered.name,
2626 None,
2631 true,
2632 Some(recovered.range),
2633 recovered.raw_supertypes,
2634 scope,
2635 stack,
2636 ancestry,
2637 );
2638 self.parsed
2639 .record_materialization(MaterializationRecord::RecoveredDeclaration {
2640 recovery: recovered.range,
2641 unit: class_unit.clone(),
2642 });
2643 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
2644 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
2645 tree.root_node(),
2646 class_unit.identifier(),
2647 self.source,
2648 )
2649 {
2650 self.visit_recovered_fragment_constructor(
2651 range,
2652 body,
2653 node,
2654 &class_unit,
2655 scope,
2656 ancestry,
2657 );
2658 }
2659 if let Some(outcome) = member_outcome {
2660 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
2661 }
2662 self.consumed_fragment_regions
2663 .push((node.start_byte(), recovered.range.end_byte));
2664 true
2665 }
2666
2667 fn visit_embedded_function_like_export_classes<'tree>(
2668 &mut self,
2669 node: Node<'tree>,
2670 scope: &ScopeInfo,
2671 stack: &mut Vec<CppWork<'tree>>,
2672 ancestry: &ParentIndex<'tree>,
2673 ) -> bool {
2674 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
2675 let found = !recovered_classes.is_empty();
2676 for recovered in recovered_classes {
2677 let member_outcome = self.reparse_fragmented_export_class_members(
2678 &recovered.fragmented_body,
2679 &recovered.name,
2680 );
2681 let class_unit = self.visit_named_class_like_shape(
2682 node,
2683 recovered.name,
2684 None,
2685 true,
2686 Some(recovered.range),
2687 Some(recovered.raw_supertypes),
2688 scope,
2689 stack,
2690 ancestry,
2691 );
2692 self.parsed
2693 .record_materialization(MaterializationRecord::RecoveredDeclaration {
2694 recovery: recovered.range,
2695 unit: class_unit.clone(),
2696 });
2697 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
2698 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
2699 tree.root_node(),
2700 class_unit.identifier(),
2701 self.source,
2702 )
2703 {
2704 self.visit_recovered_fragment_constructor(
2705 range,
2706 body,
2707 node,
2708 &class_unit,
2709 scope,
2710 ancestry,
2711 );
2712 }
2713 if let Some(outcome) = member_outcome {
2714 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
2715 }
2716 }
2717 found
2718 }
2719
2720 #[allow(clippy::too_many_arguments)]
2728 pub fn visit_container<'tree>(
2729 &mut self,
2730 node: Node<'tree>,
2731 ancestry: &ParentIndex<'tree>,
2732 package_name: &str,
2733 module: Option<CodeUnit>,
2734 class_unit: Option<CodeUnit>,
2735 template_signature: Option<String>,
2736 visible_using_namespaces: Vec<String>,
2737 ) {
2738 let scope = ScopeInfo {
2739 package_name: package_name.to_string(),
2740 module,
2741 class_unit,
2742 template_signature,
2743 template_metadata: None,
2744 declarations_are_fields: false,
2745 recovered_specialization_member_scope: false,
2746 visible_using_namespaces,
2747 };
2748 self.run_container_work(node, scope, ancestry);
2749 }
2750
2751 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
2755 self.consumed_fragment_regions
2756 .iter()
2757 .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
2758 }
2759
2760 fn run_container_work<'tree>(
2772 &mut self,
2773 node: Node<'tree>,
2774 scope: ScopeInfo,
2775 ancestry: &ParentIndex<'tree>,
2776 ) {
2777 let mut stack = vec![CppWork::Container(CppContainer { node, scope })];
2778 while let Some(work) = stack.pop() {
2779 match work {
2780 CppWork::Container(container) => {
2781 push_cpp_container_work(container.node, container.scope, &mut stack);
2782 }
2783 CppWork::Siblings(siblings) => {
2784 advance_cpp_siblings(siblings, self.source, &mut stack);
2785 }
2786 CppWork::Node(work) => {
2787 if self.node_is_inside_consumed_fragment(work.node) {
2788 continue;
2789 }
2790 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
2791 }
2792 }
2793 }
2794 }
2795
2796 fn reparse_fragmented_export_class_members(
2801 &self,
2802 fragmented: &FragmentedExportBody,
2803 class_name: &str,
2804 ) -> Option<FragmentedExportMembers> {
2805 if fragmented.reparse_start >= fragmented.reparse_end {
2806 return None;
2807 }
2808 let tree = cpp_reparse_fragmented_class_body(
2809 self.source,
2810 fragmented.reparse_start,
2811 fragmented.reparse_end,
2812 )?;
2813 if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
2814 return Some(FragmentedExportMembers::Complete(tree));
2815 }
2816 let has_conditional_constructor = {
2817 let root = tree.root_node();
2818 let mut cursor = root.walk();
2819 root.named_children(&mut cursor).any(|child| {
2820 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
2821 })
2822 };
2823 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
2824 }
2825
2826 fn visit_fragmented_export_class_members(
2829 &mut self,
2830 outcome: FragmentedExportMembers,
2831 class_unit: CodeUnit,
2832 scope: &ScopeInfo,
2833 ) -> bool {
2834 let (tree, complete) = match outcome {
2835 FragmentedExportMembers::Complete(tree) => (tree, true),
2836 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
2837 };
2838 let root = tree.root_node();
2839 let class_name = class_unit.identifier().to_string();
2840 let member_scope = ScopeInfo {
2841 package_name: class_unit.package_name().to_string(),
2846 module: scope.module.clone(),
2847 class_unit: Some(class_unit),
2848 template_signature: scope.template_signature.clone(),
2849 template_metadata: None,
2850 declarations_are_fields: true,
2851 recovered_specialization_member_scope: false,
2852 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2853 };
2854 if !complete {
2855 let mut cursor = root.walk();
2861 let constructors = root
2862 .named_children(&mut cursor)
2863 .filter_map(|child| {
2864 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
2865 })
2866 .collect::<Vec<_>>();
2867 let reparsed_ancestry = ParentIndex::new(root);
2870 for constructor in constructors {
2871 let mut stack = Vec::new();
2872 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
2873 while let Some(work) = stack.pop() {
2874 match work {
2875 CppWork::Container(container) => {
2876 push_cpp_container_work(container.node, container.scope, &mut stack);
2877 }
2878 CppWork::Siblings(siblings) => {
2879 advance_cpp_siblings(siblings, self.source, &mut stack);
2880 }
2881 CppWork::Node(work) => {
2882 self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
2883 }
2884 }
2885 }
2886 }
2887 return false;
2888 }
2889 self.run_container_work(root, member_scope, &ParentIndex::new(root));
2891 true
2892 }
2893
2894 fn visit_recovered_fragment_constructor<'tree>(
2895 &mut self,
2896 range: std::ops::Range<usize>,
2897 constructor_body: Node<'tree>,
2898 class_declaration: Node<'tree>,
2899 class_unit: &CodeUnit,
2900 scope: &ScopeInfo,
2901 ancestry: &ParentIndex<'tree>,
2902 ) {
2903 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
2904 return;
2905 };
2906 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
2907 tree.root_node(),
2908 range.start,
2909 class_unit.identifier(),
2910 self.source,
2911 ) else {
2912 return;
2913 };
2914 let member_scope = ScopeInfo {
2915 package_name: class_unit.package_name().to_string(),
2916 module: scope.module.clone(),
2917 class_unit: Some(class_unit.clone()),
2918 template_signature: scope.template_signature.clone(),
2919 template_metadata: None,
2920 declarations_are_fields: true,
2921 recovered_specialization_member_scope: false,
2922 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2923 };
2924 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
2925 else {
2926 return;
2927 };
2928 debug_assert_eq!(function.name, class_unit.identifier());
2929 let code_unit = function.code_unit(self.file.clone());
2930 self.add_declaration_with_range(
2931 code_unit.clone(),
2932 Range {
2933 start_byte: function_declarator.start_byte(),
2934 end_byte: constructor_body.end_byte(),
2935 start_line: function_declarator.start_position().row + 1,
2936 end_line: constructor_body.end_position().row + 1,
2937 },
2938 None,
2939 None,
2940 );
2941 self.parsed.add_signature_with_metadata(
2942 code_unit.clone(),
2943 cpp_signature_metadata(
2944 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
2945 function_declarator,
2946 self.source,
2947 ancestry,
2948 )
2949 .with_declaration_only(false)
2950 .with_callable_linkage(cpp_callable_linkage(
2951 class_declaration,
2952 self.source,
2953 ancestry,
2954 )),
2955 );
2956 self.parsed.add_child(class_unit.clone(), code_unit);
2957 }
2958
2959 fn visit_recovered_fragment_prefix_members<'tree>(
2960 &mut self,
2961 root: Node<'tree>,
2962 constructor_start: usize,
2963 class_unit: &CodeUnit,
2964 scope: &ScopeInfo,
2965 ancestry: &ParentIndex<'tree>,
2966 ) {
2967 let member_scope = ScopeInfo {
2968 package_name: class_unit.package_name().to_string(),
2969 module: scope.module.clone(),
2970 class_unit: Some(class_unit.clone()),
2971 template_signature: scope.template_signature.clone(),
2972 template_metadata: None,
2973 declarations_are_fields: true,
2974 recovered_specialization_member_scope: false,
2975 visible_using_namespaces: scope.visible_using_namespaces.clone(),
2976 };
2977 let mut stack = vec![root];
2978 while let Some(current) = stack.pop() {
2979 if current.kind() == "comment" || current.start_byte() >= constructor_start {
2980 continue;
2981 }
2982 if current.end_byte() <= constructor_start
2983 && current.kind() != "translation_unit"
2984 && current.kind() != "labeled_statement"
2985 && current.kind() != "ERROR"
2986 {
2987 let mut work_stack = Vec::new();
2988 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
2989 while let Some(work) = work_stack.pop() {
2990 match work {
2991 CppWork::Container(container) => {
2992 push_cpp_container_work(
2993 container.node,
2994 container.scope,
2995 &mut work_stack,
2996 );
2997 }
2998 CppWork::Siblings(siblings) => {
2999 advance_cpp_siblings(siblings, self.source, &mut work_stack);
3000 }
3001 CppWork::Node(work) => {
3002 self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
3003 }
3004 }
3005 }
3006 continue;
3007 }
3008 if matches!(
3009 current.kind(),
3010 "translation_unit" | "labeled_statement" | "ERROR"
3011 ) {
3012 let mut cursor = current.walk();
3013 stack.extend(current.named_children(&mut cursor));
3014 }
3015 }
3016 }
3017
3018 fn visit_node<'tree>(
3019 &mut self,
3020 node: Node<'tree>,
3021 scope: &ScopeInfo,
3022 stack: &mut Vec<CppWork<'tree>>,
3023 ancestry: &ParentIndex<'tree>,
3024 ) {
3025 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
3026 self.visit_node(node, &recovered_scope, stack, ancestry);
3027 return;
3028 }
3029 if node.kind() == "function_definition" && node.has_error() {
3035 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
3036 }
3037 if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
3038 {
3039 let displaced_namespace_items =
3040 displaced_fragment_namespace_geometry(node, self.source)
3041 .map(|boundary| boundary.namespace_items)
3042 .unwrap_or_default();
3043 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
3044 let mut class_stack = Vec::new();
3045 let parser_visible_body =
3048 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
3049 .then(|| cpp_body_node(class_node))
3050 .flatten();
3051 let class_unit = self.visit_named_class_like_shape(
3052 class_node,
3053 name,
3054 parser_visible_body,
3055 true,
3056 Some(fragmented.class_range),
3057 Some(extract_cpp_supertypes(class_node, self.source)),
3058 scope,
3059 &mut class_stack,
3060 ancestry,
3061 );
3062 let member_scope = ScopeInfo {
3063 package_name: class_unit.package_name().to_string(),
3064 module: scope.module.clone(),
3065 class_unit: Some(class_unit.clone()),
3066 template_signature: scope.template_signature.clone(),
3067 template_metadata: None,
3068 declarations_are_fields: true,
3069 recovered_specialization_member_scope: false,
3070 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3071 };
3072 let complete = outcome.is_some_and(|outcome| {
3073 self.visit_fragmented_export_class_members(outcome, class_unit, scope)
3074 });
3075 if complete {
3076 self.consumed_fragment_regions
3077 .push((node.start_byte(), fragmented.class_range.end_byte));
3078 } else {
3079 for candidate in cpp_following_named_siblings(node, self.source) {
3089 if candidate.start_byte() >= fragmented.reparse_end {
3090 break;
3091 }
3092 if cpp_fragment_sibling_is_class_member(
3093 candidate,
3094 fragmented.reparse_end,
3095 self.source,
3096 ) {
3097 self.recovered_class_sibling_scopes
3098 .insert(candidate.id(), member_scope.clone());
3099 }
3100 }
3101 }
3102 for item in displaced_namespace_items {
3103 self.recovered_class_sibling_scopes
3104 .insert(item.id(), scope.clone());
3105 }
3106 stack.extend(class_stack);
3107 return;
3108 }
3109 match node.kind() {
3110 "template_declaration" => {
3111 if let Some(recovered) =
3112 recover_fragmented_preprocessor_class(node, self.source, ancestry)
3113 {
3114 let mut template_scope = scope.clone();
3115 template_scope.template_signature =
3116 cpp_template_signature(node, recovered.declaration_node, self.source);
3117 template_scope.template_metadata =
3118 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
3119 let raw_supertypes =
3120 Some(extract_cpp_supertypes(recovered.class_node, self.source));
3121 let mut class_stack = Vec::new();
3122 let class_unit = self.visit_named_class_like_shape(
3123 recovered.class_node,
3124 recovered.name,
3125 Some(recovered.body),
3126 true,
3127 Some(recovered.range),
3128 raw_supertypes,
3129 &template_scope,
3130 &mut class_stack,
3131 ancestry,
3132 );
3133 self.parsed.record_materialization(
3134 MaterializationRecord::RecoveredDeclaration {
3135 recovery: recovered.range,
3136 unit: class_unit.clone(),
3137 },
3138 );
3139 let member_scope = ScopeInfo {
3140 package_name: template_scope.package_name.clone(),
3141 module: template_scope.module.clone(),
3142 class_unit: Some(class_unit.clone()),
3143 template_signature: template_scope.template_signature.clone(),
3144 template_metadata: None,
3145 declarations_are_fields: true,
3146 recovered_specialization_member_scope: recovered
3147 .class_node
3148 .child_by_field_name("name")
3149 .is_some_and(|name| name.kind() == "template_type"),
3150 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
3151 };
3152 for tail_member in recovered.tail_members.into_iter().rev() {
3153 stack.push(CppWork::Node(CppNodeWork {
3154 node: tail_member,
3155 scope: member_scope.clone(),
3156 }));
3157 }
3158 stack.extend(class_stack);
3159 for sibling in recovered.member_siblings {
3160 self.recovered_class_sibling_scopes
3161 .insert(sibling.id(), member_scope.clone());
3162 }
3163 return;
3164 }
3165 for index in (0..node.named_child_count()).rev() {
3166 let Some(child) = node.named_child(index) else {
3167 continue;
3168 };
3169 if matches!(
3170 child.kind(),
3171 "class_specifier"
3172 | "struct_specifier"
3173 | "union_specifier"
3174 | "enum_specifier"
3175 | "function_definition"
3176 | "declaration"
3177 | "field_declaration"
3178 | "alias_declaration"
3179 | "namespace_definition"
3180 ) {
3181 let mut template_scope = scope.clone();
3182 template_scope.template_signature =
3183 cpp_template_signature(node, child, self.source);
3184 template_scope.template_metadata =
3185 cpp_template_metadata(node, child, self.source, ancestry);
3186 if let Some(recovered) = recover_fragmented_partial_specialization(
3187 node,
3188 child,
3189 self.source,
3190 ancestry,
3191 ) {
3192 let code_unit = self.visit_named_class_like_shape(
3193 recovered.declaration_node,
3194 recovered.name,
3195 None,
3196 true,
3197 Some(recovered.range),
3198 None,
3199 &template_scope,
3200 stack,
3201 ancestry,
3202 );
3203 self.parsed.record_materialization(
3204 MaterializationRecord::RecoveredDeclaration {
3205 recovery: recovered.range,
3206 unit: code_unit.clone(),
3207 },
3208 );
3209 let mut member_scope = template_scope.clone();
3210 member_scope.class_unit = Some(code_unit);
3211 member_scope.declarations_are_fields = true;
3212 member_scope.recovered_specialization_member_scope = true;
3213 for prefix_member in recovered.prefix_members.into_iter().rev() {
3214 stack.push(CppWork::Node(CppNodeWork {
3215 node: prefix_member,
3216 scope: member_scope.clone(),
3217 }));
3218 }
3219 for sibling in recovered.member_siblings {
3220 self.recovered_class_sibling_scopes
3221 .insert(sibling.id(), member_scope.clone());
3222 }
3223 for following in recovered.following_declarations.into_iter().rev() {
3224 stack.push(CppWork::Node(CppNodeWork {
3225 node: following,
3226 scope: scope.clone(),
3227 }));
3228 }
3229 return;
3230 }
3231 stack.push(CppWork::Node(CppNodeWork {
3232 node: child,
3233 scope: template_scope,
3234 }));
3235 }
3236 }
3237 }
3238 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
3239 "linkage_specification" => {
3240 if let Some(body) = cpp_body_node(node) {
3241 stack.push(CppWork::Container(CppContainer {
3242 node: body,
3243 scope: scope.clone(),
3244 }));
3245 } else {
3246 stack.push(CppWork::Container(CppContainer {
3247 node,
3248 scope: scope.clone(),
3249 }));
3250 }
3251 }
3252 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
3253 self.visit_class_like(node, scope, stack, ancestry)
3254 }
3255 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
3256 "ERROR" => {
3263 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
3264 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
3265 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
3266 return;
3267 }
3268 self.visit_macro_swallowed_function_declarations(node, scope);
3269 stack.push(CppWork::Container(CppContainer {
3270 node,
3271 scope: scope.clone(),
3272 }));
3273 }
3274 }
3275 "declaration" => {
3276 if scope.class_unit.is_some()
3277 && scope.declarations_are_fields
3278 && scope.recovered_specialization_member_scope
3279 && let Some(alias_name) =
3280 recovered_using_declaration_alias_name(node, self.source)
3281 {
3282 self.add_type_aliases(node, scope, vec![alias_name]);
3283 } else {
3284 self.visit_declaration(
3285 node,
3286 scope,
3287 scope.declarations_are_fields,
3288 stack,
3289 ancestry,
3290 )
3291 }
3292 }
3293 "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
3294 "type_definition" | "alias_declaration" => {
3295 self.visit_type_declaration(node, scope, stack, ancestry)
3296 }
3297 "preproc_def" | "preproc_function_def" => self.visit_macro(node),
3298 "preproc_include" => {}
3304 kind if preserves_declaration_scope_through_wrapper(
3305 kind,
3306 scope.class_unit.is_some(),
3307 ) =>
3308 {
3309 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
3315 let mut range = cpp_declaration_range(node);
3316 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
3317 range.end_byte = boundary.end_byte;
3318 range.end_line = boundary.end_line;
3319 }
3320 self.parsed.record_materialization(
3321 MaterializationRecord::ConfigurationConditional { range },
3322 );
3323 if node.has_error() {
3324 let mut candidates = vec![node];
3333 while let Some(candidate) = candidates.pop() {
3334 if candidate.kind() == "ERROR"
3335 && self.visit_function_like_export_class_pair(
3336 candidate, scope, stack, ancestry,
3337 )
3338 {
3339 continue;
3340 }
3341 for index in (0..candidate.named_child_count()).rev() {
3342 candidates.push(
3343 candidate
3344 .named_child(index)
3345 .expect("index below the node's own named child count"),
3346 );
3347 }
3348 }
3349 }
3350 }
3351 stack.push(CppWork::Container(CppContainer {
3352 node,
3353 scope: scope.clone(),
3354 }))
3355 }
3356 _ => {}
3357 }
3358 }
3359
3360 fn visit_macro_swallowed_function_declarations<'tree>(
3361 &mut self,
3362 envelope: Node<'tree>,
3363 scope: &ScopeInfo,
3364 ) {
3365 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
3366 || envelope.kind() == "ERROR"
3367 && envelope
3368 .parent()
3369 .is_some_and(|parent| parent.kind() == "ERROR")
3370 {
3371 return;
3372 }
3373 let mut stack = (0..envelope.named_child_count())
3374 .filter_map(|index| envelope.named_child(index))
3375 .collect::<Vec<_>>();
3376 while let Some(node) = stack.pop() {
3377 if node.kind() == "function_declarator" {
3378 self.visit_error_swallowed_function_declaration(node, scope);
3379 }
3380 for index in 0..node.named_child_count() {
3381 if let Some(child) = node.named_child(index) {
3382 stack.push(child);
3383 }
3384 }
3385 }
3386 }
3387
3388 fn visit_error_swallowed_function_declaration<'tree>(
3389 &mut self,
3390 node: Node<'tree>,
3391 scope: &ScopeInfo,
3392 ) -> bool {
3393 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
3394 return false;
3395 };
3396 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
3397 return false;
3398 };
3399 let root = tree.root_node();
3400 let mut cursor = root.walk();
3401 let declarations = root
3402 .named_children(&mut cursor)
3403 .filter(|child| child.kind() != "comment")
3404 .collect::<Vec<_>>();
3405 let [declaration] = declarations.as_slice() else {
3406 return false;
3407 };
3408 if declaration.kind() != "declaration"
3409 || declaration.has_error()
3410 || declaration.start_byte() != start
3411 || declaration.end_byte() != end
3412 {
3413 return false;
3414 }
3415 let recovery = cpp_recovery_window(self.source, start, end);
3416 let reparsed_ancestry = ParentIndex::new(root);
3418 self.record_recovered_declarations(recovery, |visitor| {
3419 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
3420 });
3421 true
3422 }
3423
3424 fn visit_namespace<'tree>(
3425 &mut self,
3426 node: Node<'tree>,
3427 scope: &ScopeInfo,
3428 stack: &mut Vec<CppWork<'tree>>,
3429 ancestry: &ParentIndex<'tree>,
3430 ) {
3431 let name_node = node.child_by_field_name("name");
3432 let Some(name_node) = name_node else {
3433 if let Some(body) = cpp_body_node(node) {
3434 stack.push(CppWork::Container(CppContainer {
3435 node: body,
3436 scope: scope.clone(),
3437 }));
3438 }
3439 return;
3440 };
3441 let explicitly_global = name_node
3448 .child(0)
3449 .is_some_and(|child| !child.is_named() && child.kind() == "::");
3450 let components = cpp_namespace_name_components(name_node, self.source);
3451 if components.is_empty() {
3452 return;
3453 }
3454 let mut package_name = if explicitly_global {
3460 String::new()
3461 } else {
3462 scope.package_name.clone()
3463 };
3464 let mut module = None;
3465 for component in components {
3466 let full_name = if package_name.is_empty() {
3467 component
3468 } else {
3469 format!("{package_name}::{component}")
3470 };
3471 let level = CodeUnit::new_fq(
3472 self.file.clone(),
3473 CodeUnitType::Module,
3474 "",
3475 full_name.clone(),
3476 cpp_namespace_fq(&full_name),
3477 );
3478 if !self.parsed.contains_declaration(&level) {
3479 self.add_declaration(level.clone(), node, None, None);
3480 }
3481 package_name = full_name;
3482 module = Some(level);
3483 }
3484
3485 let namespace_scope = ScopeInfo {
3486 package_name,
3487 module,
3488 class_unit: None,
3496 template_signature: scope.template_signature.clone(),
3497 template_metadata: scope.template_metadata.clone(),
3498 declarations_are_fields: false,
3499 recovered_specialization_member_scope: false,
3500 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3501 };
3502 let container = cpp_body_node(node).unwrap_or(node);
3503 let mut candidates = vec![container];
3511 while let Some(candidate) = candidates.pop() {
3512 if matches!(
3513 candidate.kind(),
3514 "ERROR" | "function_definition" | "labeled_statement"
3515 ) && self.visit_embedded_function_like_export_classes(
3516 candidate,
3517 &namespace_scope,
3518 stack,
3519 ancestry,
3520 ) {
3521 continue;
3522 }
3523 for index in (0..candidate.named_child_count()).rev() {
3524 candidates.push(
3525 candidate
3526 .named_child(index)
3527 .expect("index below the node's own named child count"),
3528 );
3529 }
3530 }
3531 stack.push(CppWork::Container(CppContainer {
3532 node: container,
3533 scope: namespace_scope,
3534 }));
3535 }
3536
3537 fn visit_class_like<'tree>(
3538 &mut self,
3539 node: Node<'tree>,
3540 scope: &ScopeInfo,
3541 stack: &mut Vec<CppWork<'tree>>,
3542 ancestry: &ParentIndex<'tree>,
3543 ) {
3544 let Some(name) = class_like_name(node, self.source, ancestry) else {
3545 return;
3546 };
3547 let name = qualified_class_name_chain(node, self.source, scope)
3548 .map(|chain| chain.join("$"))
3549 .unwrap_or(name);
3550 self.visit_named_class_like(node, name, scope, stack, ancestry);
3551 }
3552
3553 fn visit_named_class_like<'tree>(
3554 &mut self,
3555 node: Node<'tree>,
3556 name: String,
3557 scope: &ScopeInfo,
3558 stack: &mut Vec<CppWork<'tree>>,
3559 ancestry: &ParentIndex<'tree>,
3560 ) {
3561 let body = cpp_body_node(node);
3562 let definition_body_present = body.is_some();
3563 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
3564 .then(|| extract_cpp_supertypes(node, self.source));
3565 self.visit_named_class_like_shape(
3566 node,
3567 name,
3568 body,
3569 definition_body_present,
3570 None,
3571 raw_supertypes,
3572 scope,
3573 stack,
3574 ancestry,
3575 );
3576 }
3577
3578 fn mints_tag_at_enclosing_c_scope(
3586 &self,
3587 declaration_node: Node<'_>,
3588 scope: &ScopeInfo,
3589 ancestry: &ParentIndex<'_>,
3590 ) -> bool {
3591 self.c_tag_semantics
3592 && scope.class_unit.is_some()
3593 && class_like_name(declaration_node, self.source, ancestry).is_some()
3594 && matches!(
3595 declaration_node.kind(),
3596 "struct_specifier" | "union_specifier" | "enum_specifier"
3597 )
3598 }
3599
3600 #[allow(clippy::too_many_arguments)]
3601 fn visit_named_class_like_shape<'tree>(
3602 &mut self,
3603 declaration_node: Node<'tree>,
3604 name: String,
3605 body: Option<Node<'tree>>,
3606 definition_body_present: bool,
3607 explicit_range: Option<Range>,
3608 raw_supertypes: Option<Vec<String>>,
3609 scope: &ScopeInfo,
3610 stack: &mut Vec<CppWork<'tree>>,
3611 ancestry: &ParentIndex<'tree>,
3612 ) -> CodeUnit {
3613 let displaced_macro_tail = if explicit_range.is_none() {
3614 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
3615 } else {
3616 None
3617 };
3618 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
3619 let recovered_scope = self.scope_for_recovered_exported_class(
3620 declaration_node,
3621 &name,
3622 definition_body_present,
3623 scope,
3624 ancestry,
3625 );
3626 let c_tag_scope;
3636 let scope =
3637 if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
3638 c_tag_scope = ScopeInfo {
3639 class_unit: None,
3640 ..recovered_scope.clone()
3641 };
3642 &c_tag_scope
3643 } else {
3644 &recovered_scope
3645 };
3646 let short_name = if let Some(parent) = &scope.class_unit {
3647 cpp_join_nested_short(parent.short_name(), &name)
3648 } else {
3649 name.clone()
3650 };
3651 let qualified_chain = if scope.class_unit.is_none() {
3658 qualified_class_name_chain(declaration_node, self.source, scope)
3659 .filter(|chain| chain.join("$") == name)
3660 } else {
3661 None
3662 };
3663 let fq = if let Some(chain) = qualified_chain {
3664 let mut fq = FqName::new();
3665 cpp_push_package(&mut fq, &scope.package_name);
3666 let mut first = true;
3667 for component in chain {
3668 let kind = if first {
3669 SegmentKind::Type
3670 } else {
3671 SegmentKind::Nested
3672 };
3673 fq.push(cpp_segment(&component, kind));
3674 first = false;
3675 }
3676 fq
3677 } else {
3678 cpp_leaf_fq(
3679 &scope.package_name,
3680 scope.class_unit.as_ref(),
3681 &name,
3682 SegmentKind::Nested,
3683 SegmentKind::Type,
3684 )
3685 };
3686 let code_unit = CodeUnit::with_signature_and_fq(
3687 self.file.clone(),
3688 CodeUnitType::Class,
3689 scope.package_name.clone(),
3690 short_name,
3691 scope.template_signature.clone(),
3692 false,
3693 fq,
3694 );
3695 let has_body = definition_body_present;
3696 if !has_body && self.parsed.contains_declaration(&code_unit) {
3697 self.parsed.record_navigation_range(
3698 code_unit.clone(),
3699 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
3700 );
3701 return code_unit;
3702 }
3703 if has_body {
3704 if let Some(range) = explicit_range {
3705 self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
3706 } else {
3707 self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
3708 }
3709 } else {
3710 self.add_declaration(code_unit.clone(), declaration_node, None, None);
3711 }
3712 if let Some(raw_supertypes) = raw_supertypes {
3713 self.parsed
3714 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
3715 }
3716 self.parsed.add_signature(
3717 code_unit.clone(),
3718 render_cpp_type_signature(
3719 declaration_node,
3720 self.source,
3721 scope.template_signature.as_deref(),
3722 ),
3723 );
3724 if let Some(metadata) = &scope.template_metadata {
3725 let primary_short_name = if let Some(parent) = &scope.class_unit {
3726 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
3727 } else {
3728 metadata.primary_name.clone()
3729 };
3730 let primary_fq_name = CodeUnit::new(
3731 self.file.clone(),
3732 CodeUnitType::Class,
3733 scope.package_name.clone(),
3734 primary_short_name,
3735 )
3736 .fq_name();
3737 let mut metadata = metadata.clone();
3738 metadata.primary_fq_name = primary_fq_name;
3739 self.parsed
3740 .set_cpp_template_metadata(code_unit.clone(), metadata);
3741 }
3742 if let Some(parent) = &scope.class_unit {
3743 self.parsed.add_child(parent.clone(), code_unit.clone());
3744 } else if let Some(module) = &scope.module {
3745 self.parsed.add_child(module.clone(), code_unit.clone());
3746 }
3747
3748 if let Some(body) = body {
3749 let mut nested_scope = scope.clone();
3750 nested_scope.class_unit = Some(code_unit.clone());
3751 nested_scope.template_signature = scope.template_signature.clone();
3752 nested_scope.template_metadata = None;
3757 nested_scope.recovered_specialization_member_scope =
3760 scope.template_metadata.as_ref().is_some_and(|metadata| {
3761 declaration_node.kind() == "function_definition" && metadata.is_specialization()
3762 });
3763 nested_scope.declarations_are_fields =
3764 is_recovered_exported_class_container(declaration_node, self.source)
3765 || nested_scope.recovered_specialization_member_scope;
3766 if let Some(displaced) = displaced_macro_tail {
3767 push_cpp_sibling_range(
3775 body,
3776 displaced.split_index,
3777 usize::MAX,
3778 scope.clone(),
3779 stack,
3780 );
3781 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
3782 } else {
3783 stack.push(CppWork::Container(CppContainer {
3784 node: body,
3785 scope: nested_scope,
3786 }));
3787 }
3788 }
3789 if declaration_node.kind() == "enum_specifier" {
3790 self.visit_enum_enumerators(declaration_node, scope, &code_unit);
3791 if !self.has_enum_enumerator_units(&code_unit) {
3792 self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
3793 }
3794 }
3795 code_unit
3796 }
3797
3798 fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
3806 if self.field_owners.is_none() {
3807 self.field_owners = Some(CppFieldOwnerIndex::of(
3808 self.parsed.declarations().iter(),
3809 self.file,
3810 ));
3811 }
3812 debug_assert_eq!(
3813 parent.source(),
3814 self.file,
3815 "the walk's declarations are declarations of the file it is walking"
3816 );
3817 let carried = self
3818 .field_owners
3819 .as_ref()
3820 .expect("the index was just ensured")
3821 .owns_fields(parent.package_name(), parent.short_name());
3822
3823 #[cfg(debug_assertions)]
3824 assert_eq!(
3825 carried,
3826 cpp_declarations_hold_owned_fields(
3827 self.parsed.declarations(),
3828 self.file,
3829 parent.package_name(),
3830 parent.short_name()
3831 ),
3832 "the carried-forward field index must answer what a fresh declaration scan \
3833 answers for {}",
3834 parent.fq_name()
3835 );
3836
3837 carried
3838 }
3839
3840 fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
3841 walk_named_tree_preorder(node, false, |child| {
3842 if child.kind() != "enumerator" {
3843 return WalkControl::Continue;
3844 }
3845 let Some(name_node) = child.child_by_field_name("name") else {
3846 return WalkControl::Continue;
3847 };
3848 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
3849 if name.is_empty() {
3850 return WalkControl::Continue;
3851 }
3852 let code_unit = CodeUnit::new_fq(
3853 self.file.clone(),
3854 CodeUnitType::Field,
3855 scope.package_name.clone(),
3856 cpp_join_member_short(parent.short_name(), &name),
3857 parent
3858 .fq()
3859 .clone()
3860 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
3861 );
3862 if self.parsed.contains_declaration(&code_unit) {
3863 return WalkControl::Continue;
3864 }
3865 self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
3866 self.parsed.add_signature(
3867 code_unit,
3868 normalize_cpp_whitespace(node_text(child, self.source)),
3869 );
3870 WalkControl::Continue
3871 });
3872 }
3873
3874 fn visit_enum_enumerators_from_text(
3875 &mut self,
3876 node: Node<'_>,
3877 scope: &ScopeInfo,
3878 parent: &CodeUnit,
3879 ) {
3880 let text = node_text(node, self.source);
3881 let Some((_, body)) = text.split_once('{') else {
3882 return;
3883 };
3884 let Some((body, _)) = body.rsplit_once('}') else {
3885 return;
3886 };
3887 for entry in body.split(',') {
3888 let trimmed = entry.trim();
3889 let name = trimmed
3890 .split('=')
3891 .next()
3892 .unwrap_or("")
3893 .split_whitespace()
3894 .next()
3895 .unwrap_or("");
3896 if name.is_empty() {
3897 continue;
3898 }
3899 let code_unit = CodeUnit::new_fq(
3900 self.file.clone(),
3901 CodeUnitType::Field,
3902 scope.package_name.clone(),
3903 cpp_join_member_short(parent.short_name(), name),
3904 parent
3905 .fq()
3906 .clone()
3907 .with_pushed(cpp_segment(name, SegmentKind::Member)),
3908 );
3909 if self.parsed.contains_declaration(&code_unit) {
3910 continue;
3911 }
3912 self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
3913 self.parsed.add_signature(code_unit, trimmed.to_string());
3914 }
3915 }
3916
3917 fn visit_function_definition<'tree>(
3918 &mut self,
3919 node: Node<'tree>,
3920 scope: &ScopeInfo,
3921 stack: &mut Vec<CppWork<'tree>>,
3922 ancestry: &ParentIndex<'tree>,
3923 ) {
3924 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
3930 return;
3931 }
3932 if node.has_error() {
3933 self.visit_macro_swallowed_function_declarations(node, scope);
3934 }
3935 if let Some((class_node, name, raw_supertypes)) =
3936 recover_exported_class_function_definition(node, self.source)
3937 {
3938 let body = cpp_body_node(class_node);
3939 let displaced_namespace = cpp_body_node(node)
3940 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
3941 let fragmented = cpp_body_node(node).and_then(|body| {
3942 fragmented_export_function_body_region(
3943 node,
3944 body,
3945 self.source,
3946 displaced_namespace.as_ref(),
3947 )
3948 });
3949 if let Some(fragmented) = fragmented {
3955 if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
3959 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
3960 {
3961 let mut boundary_scope = scope.clone();
3962 for sibling in cpp_following_named_siblings(node, self.source) {
3963 if sibling.start_byte() >= boundary.start_byte() {
3964 break;
3965 }
3966 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
3967 boundary_scope.visible_using_namespaces.push(namespace);
3968 }
3969 }
3970 self.recovered_class_sibling_scopes
3971 .insert(boundary.id(), boundary_scope);
3972 }
3973 let mut recovered_constructor = None;
3974 let mut recovered_prefix_tree = None;
3975 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
3976 {
3977 Some(FragmentedExportMembers::Complete(tree)) => {
3978 if let Some(body) = body
3979 && let Some(range) =
3980 cpp_reparsed_synthetic_initializer_constructor_range(
3981 tree.root_node(),
3982 &name,
3983 self.source,
3984 body.end_byte(),
3985 )
3986 {
3987 recovered_constructor = Some(range);
3988 recovered_prefix_tree = Some(tree);
3989 None
3990 } else {
3991 Some(FragmentedExportMembers::Complete(tree))
3992 }
3993 }
3994 outcome => outcome,
3995 };
3996 let mut class_stack = Vec::new();
3997 let class_unit = self.visit_named_class_like_shape(
3998 class_node,
3999 name,
4000 None,
4001 true,
4002 Some(fragmented.class_range),
4003 raw_supertypes,
4004 scope,
4005 &mut class_stack,
4006 ancestry,
4007 );
4008 self.parsed
4009 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4010 recovery: fragmented.class_range,
4011 unit: class_unit.clone(),
4012 });
4013 let complete = outcome.is_some_and(|outcome| {
4014 self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
4015 });
4016 if complete {
4017 self.consumed_fragment_regions
4018 .push((node.start_byte(), fragmented.class_range.end_byte));
4019 } else {
4020 let member_scope = ScopeInfo {
4029 package_name: class_unit.package_name().to_string(),
4030 module: scope.module.clone(),
4031 class_unit: Some(class_unit.clone()),
4032 template_signature: scope.template_signature.clone(),
4033 template_metadata: None,
4034 declarations_are_fields: true,
4035 recovered_specialization_member_scope: false,
4036 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4037 };
4038 for candidate in cpp_following_named_siblings(node, self.source) {
4039 if candidate.start_byte() >= fragmented.reparse_end {
4040 break;
4041 }
4042 if cpp_fragment_sibling_is_class_member(
4043 candidate,
4044 fragmented.reparse_end,
4045 self.source,
4046 ) {
4047 self.recovered_class_sibling_scopes
4048 .insert(candidate.id(), member_scope.clone());
4049 }
4050 }
4051 if let Some(range) = recovered_constructor
4052 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
4053 {
4054 self.visit_recovered_fragment_prefix_members(
4055 prefix_tree.root_node(),
4056 range.start,
4057 &class_unit,
4058 scope,
4059 ancestry,
4060 );
4061 self.visit_recovered_fragment_constructor(
4062 range,
4063 body,
4064 class_node,
4065 &class_unit,
4066 scope,
4067 ancestry,
4068 );
4069 }
4070 }
4071 if let Some(boundary) = displaced_namespace {
4072 for item in boundary.namespace_items {
4073 self.recovered_class_sibling_scopes
4074 .insert(item.id(), scope.clone());
4075 }
4076 }
4077 stack.extend(class_stack);
4078 return;
4079 }
4080 let mut stack = Vec::new();
4081 let class_unit = self.visit_named_class_like_shape(
4082 class_node,
4083 name,
4084 body,
4085 body.is_some(),
4086 None,
4087 raw_supertypes,
4088 scope,
4089 &mut stack,
4090 ancestry,
4091 );
4092 self.parsed
4093 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4094 recovery: cpp_declaration_range(node),
4095 unit: class_unit,
4096 });
4097 if let Some(body) = body
4104 && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
4105 && class_close < body.end_byte()
4106 {
4107 let split = {
4108 let mut cursor = body.walk();
4109 body.named_children(&mut cursor)
4110 .position(|child| child.start_byte() > class_close)
4111 };
4112 if let Some(split) = split {
4113 let seeded = stack.pop();
4118 match seeded {
4119 Some(CppWork::Container(container)) => {
4120 push_cpp_sibling_range(
4121 body,
4122 split,
4123 usize::MAX,
4124 scope.clone(),
4125 &mut stack,
4126 );
4127 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
4128 }
4129 _ => unreachable!("exported-class seed is always one Container"),
4132 }
4133 }
4134 }
4135 while let Some(work) = stack.pop() {
4136 match work {
4137 CppWork::Container(container) => {
4138 push_cpp_container_work(container.node, container.scope, &mut stack);
4139 }
4140 CppWork::Siblings(siblings) => {
4141 advance_cpp_siblings(siblings, self.source, &mut stack);
4142 }
4143 CppWork::Node(work) => {
4144 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
4145 }
4146 }
4147 }
4148 return;
4149 }
4150 let recovered_constraint_constructor =
4151 cpp_recovered_template_macro_constructor(node, self.source);
4152 let declarator = recovered_constraint_constructor
4153 .map(|(declarator, _)| declarator)
4154 .or_else(|| node.child_by_field_name("declarator"));
4155 let Some(declarator) = declarator else {
4156 self.visit_malformed_function_definition_container(node, scope, stack);
4157 return;
4158 };
4159 let Some(function_declarator) = extract_function_declarator(declarator) else {
4160 self.visit_malformed_function_definition_container(node, scope, stack);
4161 return;
4162 };
4163 let function = if let Some((_, callable_name)) =
4164 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
4165 {
4166 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
4167 } else {
4168 extract_function_info(function_declarator, self.source, scope)
4169 };
4170 let Some(mut function) = function else {
4171 self.visit_malformed_function_definition_container(node, scope, stack);
4172 return;
4173 };
4174 if let Some((_, template_parameter)) = recovered_constraint_constructor {
4175 function.signature = format!(
4176 "template <{}>{}",
4177 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
4178 function.signature
4179 );
4180 }
4181 let code_unit = function.code_unit(self.file.clone());
4182 self.add_declaration(code_unit.clone(), node, None, None);
4187 let signature = if recovered_constraint_constructor.is_some() {
4188 normalize_cpp_whitespace(node_text(function_declarator, self.source))
4189 } else {
4190 render_cpp_function_display_signature_from_node(
4191 node,
4192 self.source,
4193 scope.template_signature.as_deref(),
4194 true,
4195 ancestry,
4196 )
4197 };
4198 self.parsed.add_signature_with_metadata(
4199 code_unit.clone(),
4200 cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
4201 .with_declaration_only(false)
4202 .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
4203 );
4204 if let Some(parent) = &scope.class_unit {
4205 self.parsed.add_child(parent.clone(), code_unit);
4206 } else if let Some(module) = &scope.module {
4207 self.parsed.add_child(module.clone(), code_unit);
4208 }
4209 }
4210
4211 fn scope_for_recovered_exported_class<'tree>(
4216 &mut self,
4217 node: Node<'tree>,
4218 name: &str,
4219 definition_body_present: bool,
4220 scope: &ScopeInfo,
4221 ancestry: &ParentIndex<'tree>,
4222 ) -> ScopeInfo {
4223 if !definition_body_present
4224 || !scope.package_name.is_empty()
4225 || scope.class_unit.is_some()
4226 || !(is_recovered_exported_class_container(node, self.source)
4227 || recover_function_like_export_class_pair(node, self.source).is_some()
4228 || recover_embedded_function_like_export_classes(node, self.source)
4229 .iter()
4230 .any(|recovered| recovered.name == name)
4231 || matches!(node.kind(), "declaration" | "field_declaration")
4232 && recover_exported_class_declaration(node, self.source).is_some()
4233 || matches!(
4234 node.kind(),
4235 "class_specifier" | "struct_specifier" | "union_specifier"
4236 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
4237 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
4238 name_node,
4239 self.source,
4240 )))
4241 }) || ancestry.parent(node).is_some_and(|parent| {
4242 matches!(parent.kind(), "declaration" | "field_declaration")
4243 && recover_exported_class_declaration(parent, self.source).is_some()
4244 || is_recovered_exported_class_container(parent, self.source)
4245 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
4246 {
4247 return scope.clone();
4248 }
4249 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
4250 let Some(package_name) = borrowed_namespace
4251 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
4252 else {
4253 return scope.clone();
4254 };
4255
4256 let module = CodeUnit::new_fq(
4257 self.file.clone(),
4258 CodeUnitType::Module,
4259 "",
4260 package_name.clone(),
4261 cpp_namespace_fq(&package_name),
4262 );
4263 let mut recovered = scope.clone();
4264 recovered.package_name = package_name;
4265 recovered.module = Some(module);
4266 recovered
4267 }
4268
4269 fn unique_earlier_namespace_forward<'tree>(
4278 &mut self,
4279 recovered_node: Node<'tree>,
4280 name: &str,
4281 ancestry: &ParentIndex<'tree>,
4282 ) -> Option<String> {
4283 let mut root = recovered_node;
4284 while let Some(parent) = ancestry.parent(root) {
4285 root = parent;
4286 }
4287 let source = self.source;
4288 let scan = self
4289 .namespace_forward_scans
4290 .entry(CppTreeIdentity::of(root))
4291 .or_default();
4292 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
4293 let borrowed = scan.unique_earlier_forward(name, recovered_node);
4294
4295 #[cfg(debug_assertions)]
4296 assert_eq!(
4297 borrowed,
4298 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
4299 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
4300 for {name} at byte {}",
4301 recovered_node.start_byte()
4302 );
4303
4304 borrowed
4305 }
4306
4307 fn visit_malformed_function_definition_container<'tree>(
4308 &mut self,
4309 node: Node<'tree>,
4310 scope: &ScopeInfo,
4311 stack: &mut Vec<CppWork<'tree>>,
4312 ) {
4313 let Some(body) = cpp_body_node(node) else {
4314 return;
4315 };
4316 if !cpp_contains_namespace_definition(body) {
4317 return;
4318 }
4319 stack.push(CppWork::Container(CppContainer {
4320 node: body,
4321 scope: scope.clone(),
4322 }));
4323 }
4324
4325 fn record_recovered_declarations(
4343 &mut self,
4344 recovery: Range,
4345 reparse_walk: impl FnOnce(&mut Self),
4346 ) {
4347 #[cfg(any(debug_assertions, test))]
4350 let before = self.parsed.declarations().clone();
4351
4352 self.recovery_captures.push(CppRecoveryCapture::default());
4353 reparse_walk(self);
4354 let captured = self
4355 .recovery_captures
4356 .pop()
4357 .expect("the capture this call pushed is the one it pops");
4358
4359 let mut minted: Vec<CodeUnit> = captured
4365 .created
4366 .into_iter()
4367 .filter(|unit| self.parsed.contains_declaration(unit))
4368 .collect();
4369 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
4370
4371 #[cfg(any(debug_assertions, test))]
4372 {
4373 let mut rediscovered: Vec<CodeUnit> = self
4374 .parsed
4375 .declarations()
4376 .iter()
4377 .filter(|unit| !before.contains(*unit))
4378 .cloned()
4379 .collect();
4380 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
4381 assert_eq!(
4382 minted, rediscovered,
4383 "the captured recovered set must be the declaration delta of the reparse \
4384 walk over {recovery:?}"
4385 );
4386 }
4387
4388 for unit in minted {
4389 self.parsed
4390 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4391 recovery,
4392 unit,
4393 });
4394 }
4395 }
4396
4397 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
4400 let start = self
4401 .parsed
4402 .declaration_ranges(unit)
4403 .first()
4404 .map(|range| range.start_byte)
4405 .unwrap_or(usize::MAX);
4406 (start, unit.fq_name().to_string())
4407 }
4408
4409 fn visit_sentinel_macro_region<'tree>(
4410 &mut self,
4411 node: Node<'tree>,
4412 scope: &ScopeInfo,
4413 stack: &mut Vec<CppWork<'tree>>,
4414 ancestry: &ParentIndex<'tree>,
4415 ) -> bool {
4416 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
4417 return true;
4418 }
4419 if let Some((
4420 reparse_start,
4421 class_start,
4422 body_start,
4423 class_close_start,
4424 class_close_end,
4425 class_close_line,
4426 )) = cpp_sentinel_macro_class_region(node, self.source)
4427 {
4428 let Some(class_tree) =
4429 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
4430 else {
4431 return false;
4432 };
4433 let class_root = class_tree.root_node();
4434 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
4435 let class_ancestry = ParentIndex::new(class_root);
4437 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
4438 class_root,
4439 template_node,
4440 self.source,
4441 &class_ancestry,
4442 ) else {
4443 return false;
4444 };
4445 let class_node = reparsed_class.declaration_node;
4446 let name = reparsed_class.name;
4447 let mut class_scope = scope.clone();
4448 if let Some(template_node) = template_node {
4449 class_scope.template_signature =
4450 cpp_template_signature(template_node, class_node, self.source);
4451 class_scope.template_metadata =
4452 cpp_template_metadata(template_node, class_node, self.source, ancestry);
4453 }
4454 let Some(body_tree) =
4455 cpp_reparse_region_items(self.source, body_start, class_close_start)
4456 else {
4457 return false;
4458 };
4459 let raw_supertypes = reparsed_class.raw_supertypes;
4460 let class_range = Range {
4461 start_byte: class_start,
4462 end_byte: class_close_end,
4463 start_line: class_node.start_position().row + 1,
4464 end_line: class_close_line,
4465 };
4466 let class_scope = self.scope_for_recovered_exported_class(
4467 class_node,
4468 &name,
4469 true,
4470 &class_scope,
4471 ancestry,
4472 );
4473 let mut class_stack = Vec::new();
4474 let class_unit = self.visit_named_class_like_shape(
4475 class_node,
4476 name,
4477 None,
4478 true,
4479 Some(class_range),
4480 raw_supertypes,
4481 &class_scope,
4482 &mut class_stack,
4483 ancestry,
4484 );
4485 self.parsed
4486 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4487 recovery: class_range,
4488 unit: class_unit.clone(),
4489 });
4490 let member_scope = ScopeInfo {
4491 package_name: class_scope.package_name.clone(),
4492 module: class_scope.module.clone(),
4493 class_unit: Some(class_unit),
4494 template_signature: class_scope.template_signature.clone(),
4495 template_metadata: None,
4496 declarations_are_fields: true,
4497 recovered_specialization_member_scope: false,
4498 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
4499 };
4500 let body_root = body_tree.root_node();
4502 self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
4503 self.consumed_fragment_regions
4506 .push((node.start_byte(), class_close_end));
4507 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
4514 stack.push(CppWork::Container(CppContainer {
4515 node,
4516 scope: scope.clone(),
4517 }));
4518 }
4519 return true;
4520 }
4521 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
4522 return false;
4523 };
4524 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
4525 return false;
4526 };
4527 let root = tree.root_node();
4528 if !cpp_reparsed_items_are_indexable(root, self.source) {
4529 return false;
4530 }
4531 let recovery = cpp_recovery_window(self.source, start, end);
4532 let reparsed_ancestry = ParentIndex::new(root);
4534 self.record_recovered_declarations(recovery, |visitor| {
4535 visitor.visit_container(
4536 root,
4537 &reparsed_ancestry,
4538 &scope.package_name,
4539 scope.module.clone(),
4540 scope.class_unit.clone(),
4541 scope.template_signature.clone(),
4542 scope.visible_using_namespaces.clone(),
4543 );
4544 });
4545 if end > node.end_byte() {
4546 self.consumed_fragment_regions
4547 .push((node.start_byte(), end));
4548 } else if node.kind() == "ERROR" && node.end_byte() > end {
4549 self.consumed_fragment_regions
4556 .push((node.start_byte(), end));
4557 stack.push(CppWork::Container(CppContainer {
4558 node,
4559 scope: scope.clone(),
4560 }));
4561 }
4562 true
4563 }
4564
4565 fn visit_nested_namespace_sentinel<'tree>(
4571 &mut self,
4572 node: Node<'tree>,
4573 scope: &ScopeInfo,
4574 ancestry: &ParentIndex<'tree>,
4575 ) -> bool {
4576 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
4577 return false;
4578 };
4579
4580 let mut package_name = scope.package_name.clone();
4581 let mut module = scope.module.clone();
4582 for component in recovered.namespace_components {
4583 package_name = if package_name.is_empty() {
4584 component
4585 } else {
4586 format!("{package_name}::{component}")
4587 };
4588 let namespace_module = CodeUnit::new_fq(
4589 self.file.clone(),
4590 CodeUnitType::Module,
4591 "",
4592 package_name.clone(),
4593 cpp_namespace_fq(&package_name),
4594 );
4595 if !self.parsed.contains_declaration(&namespace_module) {
4596 self.add_declaration(namespace_module.clone(), recovered.function, None, None);
4597 }
4598 module = Some(namespace_module);
4599 }
4600
4601 let recovered_scope = ScopeInfo {
4602 package_name,
4603 module,
4604 class_unit: scope.class_unit.clone(),
4605 template_signature: scope.template_signature.clone(),
4606 template_metadata: scope.template_metadata.clone(),
4607 declarations_are_fields: false,
4608 recovered_specialization_member_scope: false,
4609 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4610 };
4611 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
4612 recovered.function,
4613 recovered.body,
4614 self.source,
4615 ancestry,
4616 ) {
4617 let mut class_scope = recovered_scope.clone();
4618 if let Some(template_node) = fragmented.template_node {
4619 class_scope.template_signature =
4620 cpp_template_signature(template_node, fragmented.class_node, self.source);
4621 class_scope.template_metadata = cpp_template_metadata(
4622 template_node,
4623 fragmented.class_node,
4624 self.source,
4625 ancestry,
4626 );
4627 }
4628 if let Some(outcome) = self
4629 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
4630 {
4631 let mut class_stack = Vec::new();
4632 let class_unit = self.visit_named_class_like_shape(
4633 fragmented.class_node,
4634 fragmented.name.clone(),
4635 None,
4636 true,
4637 Some(fragmented.fragmented.class_range),
4638 fragmented.raw_supertypes.clone(),
4639 &class_scope,
4640 &mut class_stack,
4641 ancestry,
4642 );
4643 self.parsed
4644 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4645 recovery: fragmented.fragmented.class_range,
4646 unit: class_unit.clone(),
4647 });
4648 if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
4649 self.consumed_fragment_regions.push((
4650 fragmented.consumed_start,
4651 fragmented.fragmented.class_range.end_byte,
4652 ));
4653 }
4654 }
4655 }
4656 self.run_container_work(recovered.body, recovered_scope, ancestry);
4661 true
4662 }
4663
4664 fn visit_declaration<'tree>(
4665 &mut self,
4666 node: Node<'tree>,
4667 scope: &ScopeInfo,
4668 in_class_body: bool,
4669 stack: &mut Vec<CppWork<'tree>>,
4670 ancestry: &ParentIndex<'tree>,
4671 ) {
4672 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4673 return;
4674 }
4675 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
4676 !cpp_active_template_type_parameter(
4677 node,
4678 node_text(declarator, self.source),
4679 self.source,
4680 ancestry,
4681 )
4682 }) {
4683 return;
4684 }
4685 if in_class_body
4686 && let Some(parent) = scope.class_unit.as_ref()
4687 && let Some(call) =
4688 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
4689 {
4690 self.visit_recovered_macro_qualified_constructor_definition(
4691 node, call, scope, ancestry,
4692 );
4693 return;
4694 }
4695 if in_class_body
4696 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
4697 {
4698 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
4699 return;
4700 }
4701 if in_class_body
4702 && let Some(declarators) =
4703 recovered_macro_qualified_field_declarators(node, self.source)
4704 {
4705 for declarator in declarators {
4706 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
4707 }
4708 return;
4709 }
4710 let recovered_alias_names = recovered_type_alias_names(node, self.source);
4711 if !recovered_alias_names.is_empty() {
4712 self.add_type_aliases(node, scope, recovered_alias_names);
4713 return;
4714 }
4715 if self.visit_c_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry)
4716 {
4717 return;
4718 }
4719
4720 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
4721 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
4722 if let Some(outcome) =
4727 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
4728 {
4729 let consumed_region = (
4730 recovered.declaration_node.end_byte(),
4731 fragmented.class_range.end_byte,
4732 );
4733 let code_unit = self.visit_named_class_like_shape(
4734 recovered.declaration_node,
4735 recovered.name,
4736 None,
4737 true,
4738 Some(fragmented.class_range),
4739 recovered.raw_supertypes,
4740 scope,
4741 stack,
4742 ancestry,
4743 );
4744 self.parsed.record_materialization(
4745 MaterializationRecord::RecoveredDeclaration {
4746 recovery: fragmented.class_range,
4747 unit: code_unit.clone(),
4748 },
4749 );
4750 let consume_fragment =
4751 self.visit_fragmented_export_class_members(outcome, code_unit, scope);
4752 if consume_fragment {
4758 self.consumed_fragment_regions.push(consumed_region);
4759 }
4760 return;
4761 }
4762 }
4763 let uses_initializer_body = recovered.uses_initializer_body;
4764 let definition_body_present = recovered.body.is_some();
4765 let class_unit = self.visit_named_class_like_shape(
4766 recovered.declaration_node,
4767 recovered.name,
4768 recovered.body,
4769 definition_body_present,
4770 None,
4771 recovered.raw_supertypes,
4772 scope,
4773 stack,
4774 ancestry,
4775 );
4776 self.parsed
4777 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4778 recovery: cpp_declaration_range(node),
4779 unit: class_unit,
4780 });
4781 if uses_initializer_body {
4782 return;
4783 }
4784 }
4785
4786 let mut handled_function = false;
4787 let mut handled_declarator = false;
4788 let mut cursor = node.walk();
4789 for child in node.named_children(&mut cursor) {
4790 if matches!(
4791 child.kind(),
4792 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
4793 ) {
4794 if cpp_body_node(child).is_some() {
4803 self.visit_class_like(child, scope, stack, ancestry);
4804 }
4805 continue;
4806 }
4807 }
4808
4809 let mut cursor = node.walk();
4810 for child in node.children_by_field_name("declarator", &mut cursor) {
4811 if crate::structural::is_recovered_designator_init_declarator(child) {
4812 handled_declarator = true;
4813 continue;
4814 }
4815 if let Some(kind) = classify_declarator(child) {
4816 handled_declarator = true;
4817 match kind {
4818 DeclaratorKind::Function(function_declarator) => {
4819 handled_function = true;
4820 self.visit_function_declaration(node, function_declarator, scope, ancestry);
4821 }
4822 DeclaratorKind::Variable(variable_declarator) => {
4823 self.visit_variable_declaration(
4824 node,
4825 variable_declarator,
4826 scope,
4827 in_class_body,
4828 ancestry,
4829 );
4830 }
4831 }
4832 }
4833 }
4834
4835 if !handled_declarator {
4836 let mut cursor = node.walk();
4837 for child in node.named_children(&mut cursor) {
4838 if crate::structural::is_recovered_designator_init_declarator(child) {
4839 handled_declarator = true;
4840 continue;
4841 }
4842 if !is_unfielded_declarator_candidate(child) {
4843 continue;
4844 }
4845 let Some(kind) = classify_declarator(child) else {
4846 continue;
4847 };
4848 handled_declarator = true;
4849 match kind {
4850 DeclaratorKind::Function(function_declarator) => {
4851 handled_function = true;
4852 self.visit_function_declaration(node, function_declarator, scope, ancestry);
4853 }
4854 DeclaratorKind::Variable(variable_declarator) => {
4855 self.visit_variable_declaration(
4856 node,
4857 variable_declarator,
4858 scope,
4859 in_class_body,
4860 ancestry,
4861 );
4862 }
4863 }
4864 }
4865 }
4866
4867 if handled_function {
4868 return;
4869 }
4870
4871 if !handled_declarator {
4872 if in_class_body {
4873 self.visit_class_members_from_declaration(node, scope, ancestry);
4874 } else {
4875 self.visit_global_variables_from_declaration(node, scope, ancestry);
4876 }
4877 }
4878 }
4879
4880 fn visit_c_anonymous_aggregate_declaration<'tree>(
4889 &mut self,
4890 node: Node<'tree>,
4891 scope: &ScopeInfo,
4892 in_class_body: bool,
4893 stack: &mut Vec<CppWork<'tree>>,
4894 ancestry: &ParentIndex<'tree>,
4895 ) -> bool {
4896 if !self.c_tag_semantics || !in_class_body || scope.class_unit.is_none() {
4897 return false;
4898 }
4899 let Some(aggregate) = node.child_by_field_name("type") else {
4900 return false;
4901 };
4902 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
4903 || aggregate.child_by_field_name("name").is_some()
4904 {
4905 return false;
4906 }
4907 let Some(body) = cpp_body_node(aggregate) else {
4908 return false;
4909 };
4910
4911 let mut cursor = node.walk();
4912 let declarators = node
4913 .children_by_field_name("declarator", &mut cursor)
4914 .filter_map(|declarator| match classify_declarator(declarator) {
4915 Some(DeclaratorKind::Variable(variable)) => Some(variable),
4916 Some(DeclaratorKind::Function(_)) | None => None,
4917 })
4918 .collect::<Vec<_>>();
4919 if declarators.is_empty() {
4920 stack.push(CppWork::Container(CppContainer {
4921 node: body,
4922 scope: scope.clone(),
4923 }));
4924 return true;
4925 }
4926
4927 for declarator in declarators {
4928 let Some(name) = extract_variable_name(declarator, self.source) else {
4929 continue;
4930 };
4931 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
4932 self.visit_named_class_like_shape(
4933 aggregate,
4934 name,
4935 Some(body),
4936 true,
4937 None,
4938 None,
4939 scope,
4940 stack,
4941 ancestry,
4942 );
4943 }
4944 true
4945 }
4946
4947 fn visit_function_declaration<'tree>(
4948 &mut self,
4949 declaration_node: Node<'tree>,
4950 declarator: Node<'tree>,
4951 scope: &ScopeInfo,
4952 ancestry: &ParentIndex<'tree>,
4953 ) {
4954 let Some(function) = extract_function_info(declarator, self.source, scope) else {
4955 return;
4956 };
4957 let code_unit =
4958 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
4959 if self.parsed.contains_declaration(&code_unit) {
4960 self.parsed
4961 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
4962 return;
4963 }
4964 self.add_declaration(code_unit.clone(), declaration_node, None, None);
4965 let signature = render_cpp_function_display_signature_from_node(
4966 declaration_node,
4967 self.source,
4968 scope.template_signature.as_deref(),
4969 false,
4970 ancestry,
4971 );
4972 self.parsed.add_signature_with_metadata(
4973 code_unit.clone(),
4974 cpp_signature_metadata(signature, declarator, self.source, ancestry)
4975 .with_declaration_only(true)
4976 .with_callable_linkage(cpp_callable_linkage(
4977 declaration_node,
4978 self.source,
4979 ancestry,
4980 )),
4981 );
4982 if let Some(parent) = &scope.class_unit {
4983 self.parsed.add_child(parent.clone(), code_unit);
4984 } else if let Some(module) = &scope.module {
4985 self.parsed.add_child(module.clone(), code_unit);
4986 }
4987 }
4988
4989 fn visit_recovered_macro_qualified_function_declaration<'tree>(
4990 &mut self,
4991 declaration_node: Node<'tree>,
4992 call: Node<'tree>,
4993 scope: &ScopeInfo,
4994 ancestry: &ParentIndex<'tree>,
4995 ) {
4996 let Some(parent) = &scope.class_unit else {
4997 return;
4998 };
4999 let Some(name_node) = call.child_by_field_name("function") else {
5000 return;
5001 };
5002 let Some(arguments) = call.child_by_field_name("arguments") else {
5003 return;
5004 };
5005 let Some((signature, parameter_labels)) =
5006 recovered_macro_qualified_function_parameters(arguments, self.source)
5007 else {
5008 return;
5009 };
5010 let arity = parameter_labels.len();
5011 let function = FunctionInfo {
5012 package_name: scope.package_name.clone(),
5013 owner: Some(CppMemberOwner::Unit(parent.clone())),
5014 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
5015 signature,
5016 };
5017 if function.name.is_empty() {
5018 return;
5019 }
5020 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
5021 if self.parsed.contains_declaration(&code_unit) {
5022 self.parsed
5023 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
5024 return;
5025 }
5026 self.add_declaration(code_unit.clone(), declaration_node, None, None);
5027 let signature_label = render_cpp_function_display_signature_from_node(
5028 declaration_node,
5029 self.source,
5030 scope.template_signature.as_deref(),
5031 false,
5032 ancestry,
5033 );
5034 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
5035 .with_declaration_only(true)
5036 .with_callable_arity(CallableArity::exact(arity))
5037 .with_callable_linkage(cpp_callable_linkage(
5038 declaration_node,
5039 self.source,
5040 ancestry,
5041 ));
5042 self.parsed
5043 .add_signature_with_metadata(code_unit.clone(), metadata);
5044 self.parsed.add_child(parent.clone(), code_unit);
5045 }
5046
5047 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
5048 &mut self,
5049 declaration_node: Node<'tree>,
5050 call: Node<'tree>,
5051 scope: &ScopeInfo,
5052 ancestry: &ParentIndex<'tree>,
5053 ) {
5054 let Some(parent) = &scope.class_unit else {
5055 return;
5056 };
5057 let Some(arguments) = call.child_by_field_name("arguments") else {
5058 return;
5059 };
5060 let Some((mut signature, parameter_labels)) =
5061 recovered_macro_qualified_function_parameters(arguments, self.source)
5062 else {
5063 return;
5064 };
5065 if let Some(template_signature) = &scope.template_signature {
5066 signature = format!("{template_signature}{signature}");
5067 }
5068 let arity = parameter_labels.len();
5069 let function = FunctionInfo {
5070 package_name: scope.package_name.clone(),
5071 owner: Some(CppMemberOwner::Unit(parent.clone())),
5072 name: parent.identifier().to_string(),
5073 signature,
5074 };
5075 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
5076 self.add_declaration(code_unit.clone(), declaration_node, None, None);
5077 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
5078 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
5079 .with_declaration_only(false)
5080 .with_callable_arity(CallableArity::exact(arity))
5081 .with_callable_linkage(cpp_callable_linkage(
5082 declaration_node,
5083 self.source,
5084 ancestry,
5085 ));
5086 self.parsed
5087 .add_signature_with_metadata(code_unit.clone(), metadata);
5088 self.parsed.add_child(parent.clone(), code_unit);
5089 }
5090
5091 fn visit_variable_declaration<'tree>(
5092 &mut self,
5093 declaration_node: Node<'tree>,
5094 declarator: Node<'tree>,
5095 scope: &ScopeInfo,
5096 in_class_body: bool,
5097 ancestry: &ParentIndex<'tree>,
5098 ) {
5099 let Some(name) = extract_variable_name(declarator, self.source) else {
5100 return;
5101 };
5102 let parent = if in_class_body {
5103 let Some(parent) = &scope.class_unit else {
5104 return;
5105 };
5106 Some(parent)
5107 } else {
5108 None
5109 };
5110 let short_name = match parent {
5111 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
5112 None => name.clone(),
5113 };
5114 let fq = cpp_leaf_fq(
5115 &scope.package_name,
5116 parent,
5117 &name,
5118 SegmentKind::Member,
5119 SegmentKind::Member,
5120 );
5121 let code_unit = CodeUnit::new_fq(
5122 self.file.clone(),
5123 CodeUnitType::Field,
5124 scope.package_name.clone(),
5125 short_name,
5126 fq,
5127 );
5128 if self.parsed.contains_declaration(&code_unit) {
5129 return;
5130 }
5131 self.add_declaration(code_unit.clone(), declaration_node, None, None);
5132 self.parsed.add_signature_with_metadata(
5133 code_unit.clone(),
5134 SignatureMetadata::new(
5135 render_cpp_field_signature(declaration_node, declarator, self.source),
5136 Vec::new(),
5137 )
5138 .with_cpp_field_linkage(cpp_field_declaration_linkage(
5139 declaration_node,
5140 self.source,
5141 ancestry,
5142 )),
5143 );
5144 if let Some(parent) = &scope.class_unit {
5145 self.parsed.add_child(parent.clone(), code_unit);
5146 } else if let Some(module) = &scope.module {
5147 self.parsed.add_child(module.clone(), code_unit);
5148 }
5149 }
5150
5151 fn visit_class_members_from_declaration<'tree>(
5152 &mut self,
5153 node: Node<'tree>,
5154 scope: &ScopeInfo,
5155 ancestry: &ParentIndex<'tree>,
5156 ) {
5157 let mut cursor = node.walk();
5158 for child in node.named_children(&mut cursor) {
5159 if child.kind() == "init_declarator"
5160 && let Some(inner) = child.child_by_field_name("declarator")
5161 {
5162 self.visit_variable_declaration(node, inner, scope, true, ancestry);
5163 } else if matches!(
5164 child.kind(),
5165 "identifier"
5166 | "field_identifier"
5167 | "pointer_declarator"
5168 | "reference_declarator"
5169 | "array_declarator"
5170 | "parenthesized_declarator"
5171 ) {
5172 self.visit_variable_declaration(node, child, scope, true, ancestry);
5173 }
5174 }
5175 }
5176
5177 fn visit_global_variables_from_declaration<'tree>(
5178 &mut self,
5179 node: Node<'tree>,
5180 scope: &ScopeInfo,
5181 ancestry: &ParentIndex<'tree>,
5182 ) {
5183 let mut cursor = node.walk();
5184 for child in node.named_children(&mut cursor) {
5185 if child.kind() == "init_declarator"
5186 && let Some(inner) = child.child_by_field_name("declarator")
5187 {
5188 self.visit_variable_declaration(node, inner, scope, false, ancestry);
5189 } else if matches!(
5190 child.kind(),
5191 "identifier"
5192 | "field_identifier"
5193 | "pointer_declarator"
5194 | "reference_declarator"
5195 | "array_declarator"
5196 | "parenthesized_declarator"
5197 ) {
5198 self.visit_variable_declaration(node, child, scope, false, ancestry);
5199 }
5200 }
5201 }
5202
5203 fn visit_type_declaration<'tree>(
5204 &mut self,
5205 node: Node<'tree>,
5206 scope: &ScopeInfo,
5207 stack: &mut Vec<CppWork<'tree>>,
5208 ancestry: &ParentIndex<'tree>,
5209 ) {
5210 let type_node = node.child_by_field_name("type");
5211 if let Some(type_node) = type_node
5212 && matches!(
5213 type_node.kind(),
5214 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
5215 )
5216 {
5217 self.visit_class_like(type_node, scope, stack, ancestry);
5218 }
5219
5220 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
5221 let range = Range {
5222 start_byte: node.start_byte(),
5223 end_byte: recovered.end_node.end_byte(),
5224 start_line: node.start_position().row + 1,
5225 end_line: recovered.end_node.end_position().row + 1,
5226 };
5227 let signature = self
5228 .source
5229 .get(range.start_byte..range.end_byte)
5230 .map(normalize_cpp_whitespace)
5231 .unwrap_or_default();
5232 self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
5233 return;
5234 }
5235
5236 let alias_names = match node.kind() {
5237 "alias_declaration" => extract_alias_declaration_name(node, self.source)
5238 .into_iter()
5239 .collect::<Vec<_>>(),
5240 "type_definition" => extract_typedef_alias_names(node, self.source),
5241 _ => Vec::new(),
5242 };
5243 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
5244 (type_node, alias_names.as_slice())
5245 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
5246 && type_node.child_by_field_name("name").is_none()
5247 {
5248 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
5249 } else {
5250 None
5251 };
5252 self.add_type_aliases(node, scope, alias_names);
5253 if let Some((body, alias_name)) = anonymous_aggregate {
5254 let signature = normalize_cpp_whitespace(node_text(node, self.source));
5260 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
5261 debug_assert!(self.parsed.contains_declaration(&alias_unit));
5262 let mut nested_scope = scope.clone();
5263 nested_scope.class_unit = Some(alias_unit);
5264 nested_scope.template_signature = scope.template_signature.clone();
5265 nested_scope.template_metadata = None;
5266 nested_scope.declarations_are_fields = false;
5267 nested_scope.recovered_specialization_member_scope = false;
5268 stack.push(CppWork::Container(CppContainer {
5269 node: body,
5270 scope: nested_scope,
5271 }));
5272 }
5273 }
5274
5275 fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
5276 let signature = normalize_cpp_whitespace(node_text(node, self.source));
5277 self.record_type_aliases(
5278 node,
5279 scope,
5280 alias_names,
5281 signature,
5282 cpp_declaration_range(node),
5283 );
5284 }
5285
5286 fn record_type_aliases(
5287 &mut self,
5288 node: Node<'_>,
5289 scope: &ScopeInfo,
5290 alias_names: Vec<String>,
5291 signature: String,
5292 range: Range,
5293 ) {
5294 if signature.is_empty() {
5295 return;
5296 }
5297 let type_name = node
5298 .child_by_field_name("type")
5299 .and_then(|type_node| type_node.child_by_field_name("name"))
5300 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
5301 for alias_name in alias_names {
5302 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
5303 continue;
5304 }
5305 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
5306 self.add_declaration_with_range(code_unit.clone(), range, None, None);
5309 self.parsed
5310 .add_signature(code_unit.clone(), signature.clone());
5311 if let Some(metadata) = &scope.template_metadata {
5312 let mut metadata = metadata.clone();
5313 metadata.primary_fq_name = code_unit.fq_name();
5314 self.parsed
5315 .set_cpp_template_metadata(code_unit.clone(), metadata);
5316 }
5317 if let Some(parent) = &scope.class_unit {
5318 self.parsed.add_child(parent.clone(), code_unit.clone());
5319 } else if let Some(module) = &scope.module {
5320 self.parsed.add_child(module.clone(), code_unit.clone());
5321 }
5322 self.parsed.mark_type_alias(code_unit);
5323 }
5324 }
5325
5326 fn type_alias_unit(
5327 &self,
5328 scope: &ScopeInfo,
5329 alias_name: String,
5330 signature: String,
5331 ) -> CodeUnit {
5332 let short_name = if let Some(parent) = &scope.class_unit {
5333 cpp_join_nested_short(parent.short_name(), &alias_name)
5334 } else {
5335 alias_name.clone()
5336 };
5337 let fq = cpp_leaf_fq(
5338 &scope.package_name,
5339 scope.class_unit.as_ref(),
5340 &alias_name,
5341 SegmentKind::Nested,
5342 SegmentKind::Type,
5343 );
5344 CodeUnit::with_signature_and_fq(
5345 self.file.clone(),
5346 CodeUnitType::Class,
5347 scope.package_name.clone(),
5348 short_name,
5349 Some(signature),
5350 false,
5351 fq,
5352 )
5353 }
5354
5355 fn visit_macro(&mut self, node: Node<'_>) {
5356 let Some(name) = extract_macro_name(node, self.source) else {
5357 return;
5358 };
5359 let signature = node_text(node, self.source).trim_end().to_string();
5360 if signature.is_empty() {
5361 return;
5362 }
5363 let fq = cpp_member_fq("", &name);
5364 let code_unit = CodeUnit::with_signature_and_fq(
5371 self.file.clone(),
5372 CodeUnitType::Macro,
5373 "",
5374 name,
5375 Some(signature.clone()),
5376 false,
5377 fq,
5378 );
5379 if self.parsed.contains_declaration(&code_unit) {
5380 return;
5381 }
5382 self.add_declaration(code_unit.clone(), node, None, None);
5383 let name_range = node
5384 .child_by_field_name("name")
5385 .map(cpp_declaration_range)
5386 .unwrap_or_else(|| cpp_declaration_range(node));
5387 self.parsed
5388 .record_materialization(MaterializationRecord::GeneratedDeclaration {
5389 site: cpp_declaration_range(node),
5390 argument: name_range,
5391 kind: GenerationKind::PreprocessorDefinition,
5392 unit: code_unit.clone(),
5393 });
5394 self.parsed.add_signature(code_unit, signature);
5395 }
5396}
5397
5398pub fn cpp_field_declaration_linkage<'tree>(
5403 declaration: Node<'tree>,
5404 source: &str,
5405 ancestry: &ParentIndex<'tree>,
5406) -> CppFieldLinkage {
5407 let mut current = ancestry.parent(declaration);
5408 let mut enclosed_by_class = false;
5409 while let Some(node) = current {
5410 if node.kind() == "namespace_definition"
5411 && node
5412 .child_by_field_name("name")
5413 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
5414 {
5415 return CppFieldLinkage::Internal;
5416 }
5417 if matches!(
5418 node.kind(),
5419 "class_specifier" | "struct_specifier" | "union_specifier"
5420 ) && node
5421 .child_by_field_name("name")
5422 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
5423 {
5424 return CppFieldLinkage::Internal;
5425 }
5426 if matches!(
5427 node.kind(),
5428 "class_specifier" | "struct_specifier" | "union_specifier"
5429 ) {
5430 enclosed_by_class = true;
5431 }
5432 if matches!(node.kind(), "function_definition" | "lambda_expression") {
5433 return CppFieldLinkage::Internal;
5434 }
5435 current = ancestry.parent(node);
5436 }
5437 if enclosed_by_class {
5438 return CppFieldLinkage::External;
5439 }
5440 let mut cursor = declaration.walk();
5441 let mut has_static = false;
5442 let mut has_extern = false;
5443 let mut has_inline = false;
5444 let mut has_const = false;
5445 let mut has_constexpr = false;
5446 for child in declaration.named_children(&mut cursor) {
5447 let text = normalize_cpp_whitespace(node_text(child, source));
5448 match (child.kind(), text.as_str()) {
5449 ("storage_class_specifier", "static") => has_static = true,
5450 ("storage_class_specifier", "extern") => has_extern = true,
5451 ("storage_class_specifier", "inline") => has_inline = true,
5452 ("storage_class_specifier", "constexpr") => has_constexpr = true,
5453 ("type_qualifier", "const") => has_const = true,
5454 ("type_qualifier", "constexpr") => has_constexpr = true,
5455 _ => {}
5456 }
5457 }
5458 if has_static {
5459 CppFieldLinkage::Internal
5460 } else if has_extern || has_inline {
5461 CppFieldLinkage::External
5462 } else if has_const || has_constexpr {
5463 CppFieldLinkage::InternalUnlessExternalPeer
5464 } else {
5465 CppFieldLinkage::External
5466 }
5467}
5468
5469fn cpp_declaration_range(node: Node<'_>) -> Range {
5470 Range {
5471 start_byte: node.start_byte(),
5472 end_byte: node.end_byte(),
5473 start_line: node.start_position().row + 1,
5474 end_line: node.end_position().row + 1,
5475 }
5476}
5477
5478fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
5482 let line_at = |byte: usize| {
5483 source.as_bytes()[..byte]
5484 .iter()
5485 .filter(|&&b| b == b'\n')
5486 .count()
5487 + 1
5488 };
5489 Range {
5490 start_byte,
5491 end_byte,
5492 start_line: line_at(start_byte),
5493 end_line: line_at(end_byte),
5494 }
5495}
5496
5497pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
5504 walk_named_tree_preorder(root, true, |node| {
5505 if node.kind() == "preproc_include" {
5506 let raw = normalize_cpp_whitespace(node_text(node, source));
5507 if !raw.is_empty() {
5508 parsed.imports.push(ImportInfo {
5509 raw_snippet: raw,
5510 is_wildcard: false,
5511 is_global: false,
5512 identifier: None,
5513 alias: None,
5514 path: None,
5515 binder_span: None,
5516 });
5517 }
5518 return WalkControl::SkipChildren;
5519 }
5520 WalkControl::Continue
5521 });
5522}
5523
5524pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
5525 let mut in_block_comment = false;
5526 for line in source.lines() {
5527 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
5528 let trimmed = stripped.trim();
5529 if !looks_like_quoted_include_line(trimmed) {
5530 continue;
5531 }
5532
5533 let raw = normalize_cpp_whitespace(trimmed);
5534 if parsed
5538 .imports
5539 .iter()
5540 .any(|import| import.raw_snippet == raw)
5541 {
5542 continue;
5543 }
5544
5545 parsed.imports.push(ImportInfo {
5546 raw_snippet: raw,
5547 is_wildcard: false,
5548 is_global: false,
5549 identifier: None,
5550 alias: None,
5551 path: None,
5552 binder_span: None,
5553 });
5554 }
5555}
5556
5557fn looks_like_quoted_include_line(line: &str) -> bool {
5558 let Some(rest) = line.trim_start().strip_prefix('#') else {
5559 return false;
5560 };
5561 let Some(rest) = rest.trim_start().strip_prefix("include") else {
5562 return false;
5563 };
5564 rest.trim_start().starts_with('"')
5565}
5566
5567fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
5568 let mut raw = Vec::new();
5569 let mut cursor = node.walk();
5570 for child in node.named_children(&mut cursor) {
5571 if child.kind() == "base_class_clause" {
5572 collect_cpp_base_nodes(child, source, &mut raw);
5573 }
5574 }
5575 raw
5576}
5577
5578fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
5579 walk_named_tree_preorder(node, false, |child| match child.kind() {
5580 "type_identifier" | "qualified_identifier" | "template_type" => {
5581 let text = normalize_cpp_whitespace(node_text(child, source));
5582 if !text.is_empty() {
5583 raw.push(text);
5584 }
5585 WalkControl::SkipChildren
5586 }
5587 _ => WalkControl::Continue,
5588 });
5589}
5590
5591fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
5592 let mut out = String::new();
5593 let chars: Vec<char> = line.chars().collect();
5594 let mut index = 0;
5595 let mut in_string = false;
5596 let mut in_char = false;
5597 let mut escape = false;
5598
5599 while index < chars.len() {
5600 let ch = chars[index];
5601 let next = chars.get(index + 1).copied();
5602
5603 if *in_block_comment {
5604 if ch == '*' && next == Some('/') {
5605 *in_block_comment = false;
5606 index += 2;
5607 } else {
5608 index += 1;
5609 }
5610 continue;
5611 }
5612
5613 if in_string {
5614 out.push(ch);
5615 if escape {
5616 escape = false;
5617 } else if ch == '\\' {
5618 escape = true;
5619 } else if ch == '"' {
5620 in_string = false;
5621 }
5622 index += 1;
5623 continue;
5624 }
5625
5626 if in_char {
5627 out.push(ch);
5628 if escape {
5629 escape = false;
5630 } else if ch == '\\' {
5631 escape = true;
5632 } else if ch == '\'' {
5633 in_char = false;
5634 }
5635 index += 1;
5636 continue;
5637 }
5638
5639 if ch == '/' && next == Some('/') {
5640 break;
5641 }
5642 if ch == '/' && next == Some('*') {
5643 *in_block_comment = true;
5644 index += 2;
5645 continue;
5646 }
5647 if ch == '"' {
5648 in_string = true;
5649 out.push(ch);
5650 index += 1;
5651 continue;
5652 }
5653 if ch == '\'' {
5654 in_char = true;
5655 out.push(ch);
5656 index += 1;
5657 continue;
5658 }
5659
5660 out.push(ch);
5661 index += 1;
5662 }
5663
5664 out
5665}
5666
5667#[derive(Clone)]
5668struct FunctionInfo {
5669 package_name: String,
5670 owner: Option<CppMemberOwner>,
5671 name: String,
5672 signature: String,
5673}
5674
5675#[derive(Clone)]
5682enum CppMemberOwner {
5683 Chain(Vec<String>),
5687 Unit(CodeUnit),
5690}
5691
5692impl CppMemberOwner {
5693 fn short_chain(&self) -> String {
5695 match self {
5696 Self::Chain(chain) => chain.join("$"),
5697 Self::Unit(parent) => parent.short_name().to_string(),
5698 }
5699 }
5700}
5701
5702enum DeclaratorKind<'a> {
5703 Function(Node<'a>),
5704 Variable(Node<'a>),
5705}
5706
5707impl FunctionInfo {
5708 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
5709 self.code_unit_with_synthetic(file, false)
5710 }
5711
5712 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
5713 let short_name = match &self.owner {
5714 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
5715 None => self.name.clone(),
5716 };
5717 let fq = match &self.owner {
5718 Some(CppMemberOwner::Chain(chain)) => {
5719 debug_assert!(
5720 !chain.is_empty(),
5721 "an empty owner chain is no owner; producers return None instead"
5722 );
5723 let mut fq = FqName::new();
5724 cpp_push_package(&mut fq, &self.package_name);
5725 let mut first = true;
5726 for component in chain {
5727 let kind = if first {
5728 SegmentKind::Type
5729 } else {
5730 SegmentKind::Nested
5731 };
5732 fq.push(cpp_segment(component, kind));
5733 first = false;
5734 }
5735 fq.push(cpp_segment(&self.name, SegmentKind::Member));
5736 fq
5737 }
5738 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
5739 .fq()
5740 .clone()
5741 .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
5742 Some(CppMemberOwner::Unit(_)) | None => {
5745 let mut fq = FqName::new();
5746 cpp_push_package(&mut fq, &self.package_name);
5747 fq.push(cpp_segment(&self.name, SegmentKind::Member));
5748 fq
5749 }
5750 };
5751 CodeUnit::with_signature_and_fq(
5752 file,
5753 CodeUnitType::Function,
5754 self.package_name.clone(),
5755 short_name,
5756 Some(self.signature.clone()),
5757 synthetic,
5758 fq,
5759 )
5760 }
5761}
5762
5763fn extract_function_info(
5764 declarator: Node<'_>,
5765 source: &str,
5766 scope: &ScopeInfo,
5767) -> Option<FunctionInfo> {
5768 let parameters_node = declarator.child_by_field_name("parameters")?;
5769 let declarator_name_node = declarator
5770 .child_by_field_name("declarator")
5771 .or_else(|| parameters_node.prev_named_sibling())?;
5772 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
5773}
5774
5775fn extract_function_info_from_name(
5776 declarator: Node<'_>,
5777 declarator_name_node: Node<'_>,
5778 source: &str,
5779 scope: &ScopeInfo,
5780) -> Option<FunctionInfo> {
5781 let parameters_node = declarator.child_by_field_name("parameters")?;
5782 let parameters_text = cpp_parameter_signature(parameters_node, source);
5783 let recovered_specialization_member = scope
5784 .recovered_specialization_member_scope
5785 .then(|| {
5786 let terminal = declarator_name_node
5787 .child_by_field_name("name")
5788 .unwrap_or(declarator_name_node);
5789 let name = canonical_cpp_qualified_component(terminal, source)?.name;
5790 let owner = scope.class_unit.as_ref()?;
5791 Some((
5792 Some(CppMemberOwner::Unit(owner.clone())),
5793 name,
5794 scope.package_name.clone(),
5795 ))
5796 })
5797 .flatten();
5798 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
5799 parts
5800 } else if let Some(parts) =
5801 split_structured_templated_cpp_name(declarator_name_node, source, scope)
5802 {
5803 parts
5804 } else {
5805 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
5806 declarator_name_node,
5807 source,
5808 )?);
5809 if raw_name.is_empty() {
5810 return None;
5811 }
5812 split_cpp_name(&raw_name, scope)
5813 };
5814 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
5815 let mut signature = if suffix.is_empty() {
5816 parameters_text
5817 } else {
5818 format!("{parameters_text} {suffix}")
5819 };
5820 if let Some(template_signature) = &scope.template_signature {
5821 signature = format!("{template_signature}{signature}");
5822 }
5823
5824 Some(FunctionInfo {
5825 package_name,
5826 owner,
5827 name,
5828 signature,
5829 })
5830}
5831
5832fn cpp_macro_displaced_callable_parts<'tree>(
5839 function_declarator: Node<'tree>,
5840 source: &str,
5841 ancestry: &ParentIndex<'tree>,
5842) -> Option<(Node<'tree>, Node<'tree>)> {
5843 let definition = ancestry.parent(function_declarator)?;
5844 if definition.kind() != "function_definition"
5845 || definition.child_by_field_name("declarator") != Some(function_declarator)
5846 || definition
5847 .child_by_field_name("body")
5848 .is_none_or(|body| body.kind() != "compound_statement")
5849 {
5850 return None;
5851 }
5852 let macro_type = definition.child_by_field_name("type")?;
5853 if macro_type.kind() != "type_identifier"
5854 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
5855 {
5856 return None;
5857 }
5858
5859 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
5860 if apparent_return_type.kind() == "qualified_identifier"
5861 && let (Some(return_type), Some(callable_name)) = (
5862 apparent_return_type.child_by_field_name("scope"),
5863 apparent_return_type.child_by_field_name("name"),
5864 )
5865 && return_type.kind() == "template_type"
5866 && matches!(callable_name.kind(), "identifier" | "field_identifier")
5867 && (0..apparent_return_type.child_count())
5868 .filter_map(|index| apparent_return_type.child(index))
5869 .any(|child| child.kind() == "::" && child.is_missing())
5870 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
5871 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
5872 {
5873 return Some((return_type, callable_name));
5874 }
5875 if !matches!(
5876 apparent_return_type.kind(),
5877 "identifier" | "field_identifier" | "type_identifier"
5878 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
5879 {
5880 return None;
5881 }
5882 let parameters = function_declarator.child_by_field_name("parameters")?;
5883 let mut cursor = function_declarator.walk();
5884 let between = function_declarator
5885 .named_children(&mut cursor)
5886 .filter(|child| child.kind() != "comment")
5887 .filter(|child| {
5888 child.start_byte() >= apparent_return_type.end_byte()
5889 && child.end_byte() <= parameters.start_byte()
5890 && !same_node(*child, apparent_return_type)
5891 && !same_node(*child, parameters)
5892 })
5893 .collect::<Vec<_>>();
5894 let [name_error] = between.as_slice() else {
5895 return None;
5896 };
5897 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
5898 return None;
5899 }
5900 let callable_name = name_error.named_child(0)?;
5901 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
5902 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
5903 {
5904 return None;
5905 }
5906 Some((apparent_return_type, callable_name))
5907}
5908
5909fn cpp_declarator_identity_suffix(
5925 declarator: Node<'_>,
5926 parameters_node: Node<'_>,
5927 source: &str,
5928) -> String {
5929 let mut cursor = declarator.walk();
5930 let parts = declarator
5931 .named_children(&mut cursor)
5932 .filter(|child| child.start_byte() >= parameters_node.end_byte())
5933 .filter(|child| {
5934 matches!(
5935 child.kind(),
5936 "type_qualifier"
5937 | "ref_qualifier"
5938 | "noexcept"
5939 | "throw_specifier"
5940 | "trailing_return_type"
5941 | "requires_clause"
5942 )
5943 })
5944 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
5945 .filter(|text| !text.is_empty())
5946 .collect::<Vec<_>>();
5947 normalize_cpp_qualifier_suffix(&parts.join(" "))
5948}
5949
5950pub(crate) fn cpp_callable_identity_suffix(
5957 function_declarator: Node<'_>,
5958 source: &str,
5959) -> Option<String> {
5960 let parameters_node = function_declarator.child_by_field_name("parameters")?;
5961 Some(cpp_declarator_identity_suffix(
5962 function_declarator,
5963 parameters_node,
5964 source,
5965 ))
5966}
5967
5968fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
5969 match classify_declarator(node)? {
5970 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
5971 DeclaratorKind::Variable(_) => None,
5972 }
5973}
5974
5975fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
5976 match node.kind() {
5977 "function_declarator" => {
5978 let inner = node
5979 .child_by_field_name("declarator")
5980 .or_else(|| node.child_by_field_name("name"))
5981 .or_else(|| last_named_child(node));
5982 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
5983 Some(DeclaratorKind::Variable(node))
5984 } else {
5985 Some(DeclaratorKind::Function(node))
5986 }
5987 }
5988 "init_declarator"
5989 | "pointer_declarator"
5990 | "reference_declarator"
5991 | "parenthesized_declarator"
5992 | "array_declarator"
5993 | "attributed_declarator"
5994 | "template_function" => node
5995 .child_by_field_name("declarator")
5996 .or_else(|| node.child_by_field_name("name"))
5997 .or_else(|| last_named_child(node))
5998 .and_then(classify_declarator),
5999 "identifier" | "field_identifier" | "qualified_identifier" => {
6000 Some(DeclaratorKind::Variable(node))
6001 }
6002 _ => node
6003 .child_by_field_name("declarator")
6004 .or_else(|| node.child_by_field_name("name"))
6005 .or_else(|| last_named_child(node))
6006 .and_then(classify_declarator),
6007 }
6008}
6009
6010fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
6011 matches!(
6012 node.kind(),
6013 "function_declarator"
6014 | "init_declarator"
6015 | "pointer_declarator"
6016 | "reference_declarator"
6017 | "parenthesized_declarator"
6018 | "array_declarator"
6019 | "attributed_declarator"
6020 | "template_function"
6021 | "identifier"
6022 | "field_identifier"
6023 | "qualified_identifier"
6024 )
6025}
6026
6027fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
6028 let class_like = first_class_like_child(node);
6029 let mut cursor = node.walk();
6030 node.named_children(&mut cursor).any(|child| {
6031 matches!(
6032 child.kind(),
6033 "init_declarator"
6034 | "pointer_declarator"
6035 | "reference_declarator"
6036 | "array_declarator"
6037 | "function_declarator"
6038 | "parenthesized_declarator"
6039 | "attributed_declarator"
6040 ) || matches!(
6041 child.kind(),
6042 "identifier" | "field_identifier" | "qualified_identifier"
6043 ) && class_like.is_none_or(|class_node| {
6044 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
6045 })
6046 })
6047}
6048
6049struct CppNamespaceForward {
6062 name: String,
6063 start_byte: usize,
6064 namespace_end_byte: usize,
6067 package_name: String,
6068}
6069
6070fn cpp_namespace_forward_entry<'tree>(
6076 node: Node<'tree>,
6077 source: &str,
6078 ancestry: &ParentIndex<'tree>,
6079) -> Option<CppNamespaceForward> {
6080 if !matches!(
6081 node.kind(),
6082 "class_specifier" | "struct_specifier" | "union_specifier"
6083 ) || cpp_body_node(node).is_some()
6084 {
6085 return None;
6086 }
6087 let parent = node.parent()?;
6088 if !(parent.kind() == "declaration_list"
6089 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
6090 {
6091 return None;
6092 }
6093 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
6094 if !namespace.has_error() {
6099 return None;
6100 }
6101 Some(CppNamespaceForward {
6102 name: class_like_name(node, source, ancestry)?,
6103 start_byte: node.start_byte(),
6104 namespace_end_byte: namespace.end_byte(),
6105 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
6106 })
6107}
6108
6109fn cpp_namespace_forward_matches_recovery(
6113 forward: &CppNamespaceForward,
6114 recovered_node: Node<'_>,
6115) -> bool {
6116 forward.start_byte < recovered_node.start_byte()
6117 && forward.namespace_end_byte < recovered_node.start_byte()
6118 && malformed_namespace_is_nearest_recovery_region(
6119 forward.namespace_end_byte,
6120 recovered_node,
6121 )
6122}
6123
6124#[derive(Debug, Default)]
6140pub struct CppRecoveryCapture {
6141 created: Vec<CodeUnit>,
6143 created_units: HashSet<CodeUnit>,
6145 removed_pre_existing: HashSet<CodeUnit>,
6147}
6148
6149#[derive(Debug, Default)]
6161pub struct CppFieldOwnerIndex {
6162 owners: HashMap<String, HashSet<String>>,
6164 ownerless_packages: HashSet<String>,
6166}
6167
6168impl CppFieldOwnerIndex {
6169 fn of<'unit>(
6172 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
6173 file: &ProjectFile,
6174 ) -> Self {
6175 let mut index = Self::default();
6176 for declaration in declarations {
6177 index.record(declaration, file);
6178 }
6179 index
6180 }
6181
6182 fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
6183 if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
6184 return;
6185 }
6186 let short_name = code_unit.short_name();
6187 let package_name = code_unit.package_name();
6188 if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
6189 self.ownerless_packages.insert(package_name.to_string());
6190 }
6191 if !short_name.contains('.') {
6192 return;
6193 }
6194 if !self.owners.contains_key(package_name) {
6195 self.owners
6196 .insert(package_name.to_string(), HashSet::default());
6197 }
6198 let owners = self
6199 .owners
6200 .get_mut(package_name)
6201 .expect("the package entry was just ensured");
6202 for (offset, _) in short_name.match_indices('.') {
6203 let owner = &short_name[..offset];
6204 if !owners.contains(owner) {
6205 owners.insert(owner.to_string());
6206 }
6207 }
6208 }
6209
6210 fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
6213 if owner_short_name.is_empty() {
6214 self.ownerless_packages.contains(package_name)
6215 } else {
6216 self.owners
6217 .get(package_name)
6218 .is_some_and(|owners| owners.contains(owner_short_name))
6219 }
6220 }
6221}
6222
6223#[cfg(any(debug_assertions, test))]
6227fn cpp_declarations_hold_owned_fields<'unit>(
6228 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
6229 file: &ProjectFile,
6230 package_name: &str,
6231 owner_short_name: &str,
6232) -> bool {
6233 let prefix = format!("{owner_short_name}.");
6234 declarations.into_iter().any(|unit| {
6235 unit.kind() == CodeUnitType::Field
6236 && unit.source() == file
6237 && unit.package_name() == package_name
6238 && if owner_short_name.is_empty() {
6239 !unit.short_name().contains(['.', '$'])
6243 } else {
6244 unit.short_name().starts_with(&prefix)
6245 }
6246 })
6247}
6248
6249#[derive(PartialEq, Eq, Hash)]
6257pub struct CppTreeIdentity {
6258 root_id: usize,
6259 start_byte: usize,
6260 end_byte: usize,
6261 kind_id: u16,
6262 child_count: usize,
6263}
6264
6265impl CppTreeIdentity {
6266 fn of(root: Node<'_>) -> Self {
6267 Self {
6268 root_id: root.id(),
6269 start_byte: root.start_byte(),
6270 end_byte: root.end_byte(),
6271 kind_id: root.kind_id(),
6272 child_count: root.child_count(),
6273 }
6274 }
6275}
6276
6277#[derive(Default)]
6291pub struct CppNamespaceForwardScan {
6292 scanned_through: usize,
6294 forwards: HashMap<String, Vec<CppNamespaceForward>>,
6295}
6296
6297impl CppNamespaceForwardScan {
6298 fn advance_to<'tree>(
6305 &mut self,
6306 root: Node<'tree>,
6307 cutoff: usize,
6308 source: &str,
6309 ancestry: &ParentIndex<'tree>,
6310 ) {
6311 if cutoff <= self.scanned_through {
6312 return;
6313 }
6314 let folded_through = self.scanned_through;
6315 let mut cursor = root.walk();
6316 let mut stack = vec![root];
6317 while let Some(current) = stack.pop() {
6318 if (folded_through..cutoff).contains(¤t.start_byte())
6319 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
6320 {
6321 self.forwards
6322 .entry(forward.name.clone())
6323 .or_default()
6324 .push(forward);
6325 }
6326 for child in current.named_children(&mut cursor) {
6331 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
6332 stack.push(child);
6333 }
6334 }
6335 }
6336 self.scanned_through = cutoff;
6337 }
6338
6339 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
6343 let mut matching = self
6344 .forwards
6345 .get(name)
6346 .into_iter()
6347 .flatten()
6348 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
6349 let first = matching.next()?;
6350 matching
6351 .next()
6352 .is_none()
6353 .then(|| first.package_name.clone())
6354 }
6355}
6356
6357#[cfg(any(debug_assertions, test))]
6362fn unique_earlier_cpp_namespace_forward<'tree>(
6363 recovered_node: Node<'tree>,
6364 name: &str,
6365 source: &str,
6366 ancestry: &ParentIndex<'tree>,
6367) -> Option<String> {
6368 let mut root = recovered_node;
6369 while let Some(parent) = ancestry.parent(root) {
6370 root = parent;
6371 }
6372
6373 let mut candidates = Vec::new();
6374 let mut stack = vec![root];
6375 while let Some(current) = stack.pop() {
6376 if current.start_byte() < recovered_node.start_byte()
6377 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
6378 && forward.name == name
6379 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
6380 {
6381 candidates.push(forward.package_name);
6382 }
6383
6384 let mut cursor = current.walk();
6385 for child in current.named_children(&mut cursor) {
6386 if child.start_byte() < recovered_node.start_byte() {
6387 stack.push(child);
6388 }
6389 }
6390 }
6391
6392 if candidates.len() == 1 {
6393 candidates.pop()
6394 } else {
6395 None
6396 }
6397}
6398
6399fn malformed_namespace_is_nearest_recovery_region(
6400 namespace_end_byte: usize,
6401 recovered_node: Node<'_>,
6402) -> bool {
6403 let mut root = recovered_node;
6404 while let Some(parent) = root.parent() {
6405 root = parent;
6406 }
6407 let mut cursor = root.walk();
6408 root.named_children(&mut cursor)
6409 .filter(|sibling| {
6410 namespace_end_byte <= sibling.start_byte()
6411 && sibling.end_byte() <= recovered_node.start_byte()
6412 })
6413 .all(is_malformed_namespace_recovery_trivia)
6414}
6415
6416fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
6417 matches!(node.kind(), "ERROR" | "comment")
6418 || node.kind().starts_with("preproc_")
6419 || node.kind() == "expression_statement" && node.named_child_count() == 0
6420}
6421
6422fn cpp_namespace_name_for_forward<'tree>(
6426 node: Node<'tree>,
6427 source: &str,
6428 ancestry: &ParentIndex<'tree>,
6429) -> Option<String> {
6430 cpp_namespace_definition_for_forward(node, ancestry)?;
6431 cpp_lexical_namespace_name(node, source, ancestry)
6432}
6433
6434fn cpp_namespace_definition_for_forward<'tree>(
6435 node: Node<'tree>,
6436 ancestry: &ParentIndex<'tree>,
6437) -> Option<Node<'tree>> {
6438 let declaration = ancestry.parent(node)?;
6439 let mut ancestor = ancestry.parent(declaration);
6440 while let Some(current) = ancestor {
6441 if matches!(
6442 current.kind(),
6443 "compound_statement"
6444 | "field_declaration_list"
6445 | "class_specifier"
6446 | "struct_specifier"
6447 | "union_specifier"
6448 | "function_definition"
6449 | "lambda_expression"
6450 ) {
6451 return None;
6452 }
6453 if current.kind() == "namespace_definition" {
6454 return Some(current);
6455 }
6456 ancestor = ancestry.parent(current);
6457 }
6458 None
6459}
6460
6461fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
6462 match node.kind() {
6463 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
6464 "parenthesized_declarator" => node
6465 .child_by_field_name("declarator")
6466 .or_else(|| last_named_child(node))
6467 .is_some_and(is_pointer_wrapper_declarator),
6468 "template_function" => node
6469 .child_by_field_name("name")
6470 .is_some_and(is_function_pointer_like_inner_declarator),
6471 _ => false,
6472 }
6473}
6474
6475fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
6476 match node.kind() {
6477 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
6478 "parenthesized_declarator" => node
6479 .child_by_field_name("declarator")
6480 .or_else(|| last_named_child(node))
6481 .is_some_and(is_pointer_wrapper_declarator),
6482 _ => false,
6483 }
6484}
6485
6486fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
6487 let cleaned = raw_name.trim_start_matches("template ").trim();
6488 let cleaned = cleaned.trim_start_matches("::");
6495 let parts: Vec<_> = cleaned
6504 .split("::")
6505 .filter(|component| !component.is_empty())
6506 .collect();
6507 if parts.is_empty() {
6508 return (None, cleaned.to_string(), scope.package_name.clone());
6509 }
6510 if parts.len() > 1 {
6511 let name = parts.last().unwrap_or(&cleaned).to_string();
6512 let owner_parts = &parts[..parts.len() - 1];
6513 if let Some(class_unit) = &scope.class_unit {
6514 return (
6517 Some(CppMemberOwner::Unit(class_unit.clone())),
6518 name,
6519 scope.package_name.clone(),
6520 );
6521 }
6522 if !scope.package_name.is_empty() {
6523 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
6538 let owner = (!nested.is_empty()).then(|| {
6539 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
6540 });
6541 return (owner, name, scope.package_name.clone());
6542 }
6543 let (owner, package_name) = if owner_parts.len() > 1 {
6545 (
6557 Some(CppMemberOwner::Chain(vec![
6558 owner_parts.last().unwrap_or(&"").to_string(),
6559 ])),
6560 owner_parts[..owner_parts.len() - 1].join("::"),
6561 )
6562 } else {
6563 (
6575 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
6576 cpp_using_directive_namespace_for_bare_owner(scope),
6577 )
6578 };
6579 return (owner, name, package_name);
6580 }
6581
6582 let package_name = scope.package_name.clone();
6583 let owner = scope
6584 .class_unit
6585 .as_ref()
6586 .map(|parent| CppMemberOwner::Unit(parent.clone()));
6587 (owner, cleaned.to_string(), package_name)
6588}
6589
6590fn strip_redundant_namespace_prefix<'a>(
6604 owner_parts: &'a [&'a str],
6605 package_name: &str,
6606) -> &'a [&'a str] {
6607 if package_name.is_empty() {
6608 return owner_parts;
6609 }
6610 let package_segments: Vec<&str> = package_name.split("::").collect();
6611 let max_prefix = owner_parts.len().min(package_segments.len());
6612 for prefix_len in (1..=max_prefix).rev() {
6613 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
6614 if &owner_parts[..prefix_len] == package_suffix {
6615 return &owner_parts[prefix_len..];
6616 }
6617 }
6618 owner_parts
6619}
6620
6621fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
6632 scope
6633 .visible_using_namespaces
6634 .iter()
6635 .min_by_key(|namespace| namespace.split("::").count())
6636 .cloned()
6637 .unwrap_or_default()
6638}
6639
6640struct CppQualifiedNameComponent {
6641 name: String,
6642 is_template_id: bool,
6643}
6644
6645fn qualified_class_name_chain(
6658 class_node: Node<'_>,
6659 source: &str,
6660 scope: &ScopeInfo,
6661) -> Option<Vec<String>> {
6662 if scope.package_name.is_empty() || scope.class_unit.is_some() {
6663 return None;
6664 }
6665 let name = class_node.child_by_field_name("name")?;
6666 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
6667 if explicitly_global
6668 || components.len() < 2
6669 || components.iter().any(|component| component.is_template_id)
6670 {
6671 return None;
6672 }
6673 let names = components
6674 .iter()
6675 .map(|component| component.name.as_str())
6676 .collect::<Vec<_>>();
6677 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
6678 if class_chain.is_empty() {
6679 return None;
6680 }
6681 Some(class_chain.iter().map(|name| name.to_string()).collect())
6682}
6683
6684fn structured_cpp_qualified_components(
6685 qualified_name: Node<'_>,
6686 source: &str,
6687) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
6688 if qualified_name.kind() != "qualified_identifier" {
6689 return None;
6690 }
6691
6692 let mut components = Vec::new();
6693 let mut current = qualified_name;
6694 let mut explicitly_global = false;
6695 loop {
6696 if current.kind() == "qualified_identifier" {
6697 if let Some(component) = current.child_by_field_name("scope") {
6698 components.push(canonical_cpp_qualified_component(component, source)?);
6699 } else if components.is_empty() {
6700 explicitly_global = true;
6701 } else {
6702 return None;
6703 }
6704 current = current.child_by_field_name("name")?;
6705 } else {
6706 components.push(canonical_cpp_qualified_component(current, source)?);
6707 break;
6708 }
6709 }
6710 Some((components, explicitly_global))
6711}
6712
6713fn split_structured_templated_cpp_name(
6714 declarator_name: Node<'_>,
6715 source: &str,
6716 scope: &ScopeInfo,
6717) -> Option<(Option<CppMemberOwner>, String, String)> {
6718 let (mut components, explicitly_global) =
6719 structured_cpp_qualified_components(declarator_name, source)?;
6720
6721 let terminal = components.pop()?;
6722 let owner_start = components
6723 .iter()
6724 .position(|component| component.is_template_id)?;
6725 let explicit_package = components[..owner_start]
6726 .iter()
6727 .map(|component| component.name.as_str())
6728 .collect::<Vec<_>>()
6729 .join("::");
6730 let explicit_package_is_empty = explicit_package.is_empty();
6731 let package_name = match (
6732 explicitly_global,
6733 scope.package_name.is_empty(),
6734 explicit_package_is_empty,
6735 ) {
6736 (true, _, _) => explicit_package,
6737 (false, _, true) => scope.package_name.clone(),
6738 (false, true, false) => explicit_package,
6739 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
6740 };
6741 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
6747 {
6748 cpp_using_directive_namespace_for_bare_owner(scope)
6749 } else {
6750 package_name
6751 };
6752 let owner_chain = components[owner_start..]
6753 .iter()
6754 .map(|component| component.name.clone())
6755 .collect::<Vec<_>>();
6756 if owner_chain.is_empty() || terminal.name.is_empty() {
6757 return None;
6758 }
6759
6760 Some((
6761 Some(CppMemberOwner::Chain(owner_chain)),
6762 terminal.name,
6763 package_name,
6764 ))
6765}
6766
6767fn canonical_cpp_qualified_component(
6768 mut component: Node<'_>,
6769 source: &str,
6770) -> Option<CppQualifiedNameComponent> {
6771 let mut is_template_id = false;
6772 loop {
6773 match component.kind() {
6774 "template_type" => {
6775 is_template_id = true;
6776 component = component.child_by_field_name("name")?;
6777 }
6778 "dependent_name" => component = component.named_child(0)?,
6779 "identifier"
6780 | "field_identifier"
6781 | "namespace_identifier"
6782 | "type_identifier"
6783 | "operator_name"
6784 | "destructor_name" => {
6785 let name = normalize_cpp_whitespace(node_text(component, source));
6786 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
6787 name,
6788 is_template_id,
6789 });
6790 }
6791 _ => component = component.child_by_field_name("name")?,
6792 }
6793 }
6794}
6795
6796fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
6797 match node.kind() {
6798 "identifier"
6799 | "field_identifier"
6800 | "type_identifier"
6801 | "operator_name"
6802 | "destructor_name"
6803 | "qualified_identifier" => node_text(node, source).to_string(),
6804 "function_declarator"
6805 | "pointer_declarator"
6806 | "reference_declarator"
6807 | "parenthesized_declarator"
6808 | "array_declarator"
6809 | "template_function" => node
6810 .child_by_field_name("declarator")
6811 .or_else(|| node.child_by_field_name("name"))
6812 .or_else(|| last_named_child(node))
6813 .map(|child| extract_declarator_name(child, source))
6814 .unwrap_or_else(|| node_text(node, source).to_string()),
6815 _ => node
6816 .child_by_field_name("name")
6817 .map(|child| extract_declarator_name(child, source))
6818 .unwrap_or_else(|| node_text(node, source).to_string()),
6819 }
6820}
6821
6822fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
6827 match node.kind() {
6828 "identifier"
6829 | "field_identifier"
6830 | "type_identifier"
6831 | "operator_name"
6832 | "destructor_name"
6833 | "qualified_identifier" => Some(node_text(node, source).to_string()),
6834 "function_declarator"
6835 | "pointer_declarator"
6836 | "reference_declarator"
6837 | "parenthesized_declarator"
6838 | "array_declarator"
6839 | "template_function" => node
6840 .child_by_field_name("declarator")
6841 .or_else(|| node.child_by_field_name("name"))
6842 .and_then(|child| extract_callable_declarator_name(child, source)),
6843 _ => None,
6844 }
6845}
6846
6847fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
6848 match node.kind() {
6849 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
6850 let name = node_text(node, source).trim().to_string();
6851 (!name.is_empty()).then_some(name)
6852 }
6853 _ => node
6854 .child_by_field_name("declarator")
6855 .or_else(|| node.child_by_field_name("name"))
6856 .or_else(|| last_named_child(node))
6857 .and_then(|child| extract_variable_name(child, source)),
6858 }
6859}
6860
6861fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
6862 let count = node.named_child_count();
6863 if count == 0 {
6864 None
6865 } else {
6866 node.named_child(count - 1)
6867 }
6868}
6869
6870fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
6871 let name_node = node.child_by_field_name("name")?;
6872 let name = normalize_cpp_whitespace(node_text(name_node, source));
6873 (!name.is_empty()).then_some(name)
6874}
6875
6876fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
6877 if node.kind() != "declaration" {
6878 return Vec::new();
6879 }
6880 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
6881 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
6882 }) else {
6883 return Vec::new();
6884 };
6885 let Some(declarator) = node.child_by_field_name("declarator") else {
6886 return Vec::new();
6887 };
6888 if node_text(keyword, source) == "using"
6889 && (declarator.kind() != "init_declarator"
6890 || declarator.child_by_field_name("value").is_none())
6891 {
6892 return Vec::new();
6893 }
6894 if node_text(keyword, source) == "typedef"
6895 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
6896 {
6897 return vec![alias_name];
6898 }
6899 extract_typedef_declarator_name(declarator, source)
6900 .into_iter()
6901 .collect()
6902}
6903
6904fn recovered_typedef_error_alias_name(
6905 declaration: Node<'_>,
6906 declarator: Node<'_>,
6907 source: &str,
6908) -> Option<String> {
6909 if declarator.kind() != "qualified_identifier" {
6919 return None;
6920 }
6921 let mut cursor = declaration.walk();
6922 let mut errors = declaration
6923 .named_children(&mut cursor)
6924 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
6925 let error = errors.next()?;
6926 if errors.next().is_some() || error.named_child_count() != 1 {
6927 return None;
6928 }
6929 let name = error.named_child(0)?;
6930 if !matches!(
6931 name.kind(),
6932 "identifier" | "field_identifier" | "type_identifier"
6933 ) {
6934 return None;
6935 }
6936 let name = normalize_cpp_whitespace(node_text(name, source));
6937 (!name.is_empty()).then_some(name)
6938}
6939
6940fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
6941 if fragmented_parenthesized_typedef_type(node).is_some() {
6945 return Vec::new();
6946 }
6947 let has_function_like_macro_type = node
6948 .child_by_field_name("type")
6949 .filter(|type_node| type_node.kind() == "type_identifier")
6950 .is_some_and(|type_node| {
6951 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
6952 });
6953 let mut names = Vec::new();
6954 let mut cursor = node.walk();
6955 for declarator in node.children_by_field_name("declarator", &mut cursor) {
6956 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
6957 continue;
6958 }
6959 if let Some(name) = extract_typedef_declarator_name(declarator, source)
6960 && !names.contains(&name)
6961 {
6962 names.push(name);
6963 }
6964 }
6965 names
6966}
6967
6968struct RecoveredMacroTypedefAlias<'tree> {
6969 name: String,
6970 end_node: Node<'tree>,
6971}
6972
6973fn recovered_macro_typedef_alias<'tree>(
6977 node: Node<'tree>,
6978 source: &str,
6979) -> Option<RecoveredMacroTypedefAlias<'tree>> {
6980 let type_node = fragmented_parenthesized_typedef_type(node)?;
6981 if type_node.kind() != "type_identifier"
6982 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
6983 {
6984 return None;
6985 }
6986
6987 let end_node = node.next_named_sibling()?;
6988 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
6989 return None;
6990 }
6991 let name_node = end_node.named_child(0)?;
6992 if name_node.kind() != "identifier" {
6993 return None;
6994 }
6995 let has_terminator = (0..end_node.child_count()).any(|index| {
6996 end_node
6997 .child(index)
6998 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
6999 });
7000 if !has_terminator {
7001 return None;
7002 }
7003 let name = normalize_cpp_whitespace(node_text(name_node, source));
7004 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
7005}
7006
7007fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
7008 if node.kind() != "type_definition" {
7009 return None;
7010 }
7011 let mut declarator_cursor = node.walk();
7012 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
7013 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
7014 return None;
7015 }
7016 let has_missing_terminator = (0..node.child_count()).any(|index| {
7017 node.child(index)
7018 .is_some_and(|child| child.kind() == ";" && child.is_missing())
7019 });
7020 if !has_missing_terminator {
7021 return None;
7022 }
7023 node.child_by_field_name("type")
7024}
7025
7026fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
7027 match node.kind() {
7028 "identifier" | "field_identifier" | "type_identifier" => {
7029 let name = normalize_cpp_whitespace(node_text(node, source));
7030 (!name.is_empty()).then_some(name)
7031 }
7032 "qualified_identifier" => node
7033 .child_by_field_name("name")
7034 .and_then(|name| extract_typedef_declarator_name(name, source)),
7035 _ => node
7036 .child_by_field_name("declarator")
7037 .or_else(|| node.child_by_field_name("name"))
7038 .or_else(|| last_named_child(node))
7039 .and_then(|child| extract_typedef_declarator_name(child, source)),
7040 }
7041}
7042
7043fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
7044 let name = node
7045 .child_by_field_name("name")
7046 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
7047 .or_else(|| {
7048 let mut cursor = node.walk();
7049 node.named_children(&mut cursor)
7050 .find(|child| {
7051 matches!(
7052 child.kind(),
7053 "identifier" | "field_identifier" | "type_identifier"
7054 )
7055 })
7056 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
7057 })?;
7058 (!name.is_empty()).then_some(name)
7059}
7060
7061fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
7062 left.id() == right.id()
7063}
7064
7065fn render_cpp_type_signature(
7066 node: Node<'_>,
7067 source: &str,
7068 template_signature: Option<&str>,
7069) -> String {
7070 let text = normalize_cpp_whitespace(node_text(node, source));
7071 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
7072 let rendered = if head.ends_with(';') {
7073 head.to_string()
7074 } else {
7075 format!("{head} {{")
7076 };
7077 if let Some(template_signature) = template_signature {
7078 format!("template {template_signature} {rendered}")
7079 } else {
7080 rendered
7081 }
7082}
7083
7084fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
7085 if let Some(signature) =
7086 render_recovered_macro_qualified_field_signature(node, declarator, source)
7087 {
7088 return signature;
7089 }
7090 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
7091 let prefix = cpp_declaration_prefix(node, source);
7092 let name = extract_variable_name(declarator, source).unwrap_or_default();
7093 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
7094 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
7095 || (prefix.ends_with('&') && raw_suffix == "&")
7096 {
7097 String::new()
7098 } else {
7099 raw_suffix
7100 };
7101
7102 let mut rendered = if suffix.is_empty() {
7103 format!("{prefix} {name}")
7104 } else if suffix.starts_with('*') || suffix.starts_with('&') {
7105 format!("{prefix}{suffix} {name}")
7106 } else if suffix.starts_with('[') || suffix.starts_with('(') {
7107 format!("{prefix} {name}{suffix}")
7108 } else {
7109 format!("{prefix} {suffix}{name}")
7110 };
7111 rendered = collapse_cpp_whitespace(&rendered);
7112
7113 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
7114 format!("{rendered} = {initializer};")
7115 } else if declaration_text.ends_with(';') {
7116 format!("{rendered};")
7117 } else {
7118 rendered
7119 }
7120}
7121
7122fn render_recovered_macro_qualified_field_signature(
7123 node: Node<'_>,
7124 declarator: Node<'_>,
7125 source: &str,
7126) -> Option<String> {
7127 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
7128 if !recovered
7129 .iter()
7130 .any(|candidate| same_node(*candidate, declarator))
7131 {
7132 return None;
7133 }
7134 let pseudo_declarator = node.child_by_field_name("declarator")?;
7135 let mut cursor = node.walk();
7136 let clause = node
7137 .named_children(&mut cursor)
7138 .find(|child| child.kind() == "bitfield_clause")?;
7139 let mut cursor = clause.walk();
7140 let error = clause
7141 .named_children(&mut cursor)
7142 .find(|child| child.kind() == "ERROR")?;
7143 let qualified_type =
7144 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
7145 let prefix = cpp_declaration_prefix(node, source);
7146 let name = extract_variable_name(declarator, source)?;
7147 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
7148 let mut rendered = if suffix.is_empty() {
7149 format!("{prefix} {qualified_type} {name}")
7150 } else {
7151 format!("{prefix} {qualified_type} {suffix} {name}")
7152 };
7153 rendered = collapse_cpp_whitespace(&rendered);
7154
7155 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
7156 Some(format!(
7157 "{rendered} = {};",
7158 normalize_cpp_whitespace(node_text(initializer, source))
7159 ))
7160 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
7161 Some(format!("{rendered} = {initializer};"))
7162 } else {
7163 Some(format!("{rendered};"))
7164 }
7165}
7166
7167fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
7168 match node.kind() {
7169 "pointer_expression" => {
7170 let operator = node
7171 .child_by_field_name("operator")
7172 .or_else(|| node.child(0))
7173 .map(|operator| node_text(operator, source))
7174 .unwrap_or("*");
7175 let argument = node
7176 .child_by_field_name("argument")
7177 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
7178 .unwrap_or_default();
7179 format!("{operator}{argument}")
7180 }
7181 "unary_expression" => {
7182 let operator = node
7183 .child_by_field_name("operator")
7184 .or_else(|| node.child(0))
7185 .map(|operator| node_text(operator, source))
7186 .unwrap_or_default();
7187 let argument = node
7188 .child_by_field_name("argument")
7189 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
7190 .unwrap_or_default();
7191 format!("{operator}{argument}")
7192 }
7193 "identifier" | "field_identifier" => String::new(),
7194 _ => cpp_declarator_suffix_without_name(node, source),
7195 }
7196}
7197
7198fn recovered_macro_qualified_field_initializer<'tree>(
7199 clause: Node<'tree>,
7200 declarator: Node<'tree>,
7201) -> Option<Node<'tree>> {
7202 let mut stack = vec![clause];
7203 while let Some(current) = stack.pop() {
7204 if current.kind() == "assignment_expression"
7205 && current
7206 .child_by_field_name("left")
7207 .is_some_and(|left| same_node(left, declarator))
7208 {
7209 return current.child_by_field_name("right");
7210 }
7211 let mut cursor = current.walk();
7212 stack.extend(current.named_children(&mut cursor));
7213 }
7214 None
7215}
7216
7217fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
7218 let text = node_text(node, source);
7219 let mut cursor = node.walk();
7220 let first_declarator = node.named_children(&mut cursor).find(|child| {
7221 matches!(
7222 child.kind(),
7223 "init_declarator"
7224 | "identifier"
7225 | "field_identifier"
7226 | "pointer_declarator"
7227 | "reference_declarator"
7228 | "array_declarator"
7229 | "function_declarator"
7230 )
7231 });
7232 let prefix = if let Some(first_declarator) = first_declarator {
7233 let end = first_declarator
7234 .start_byte()
7235 .saturating_sub(node.start_byte());
7236 let mut prefix = text.get(..end).unwrap_or(text).to_string();
7237 let declarator_suffix = match first_declarator.kind() {
7238 "init_declarator" => first_declarator
7239 .child_by_field_name("declarator")
7240 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
7241 .unwrap_or_default(),
7242 _ => cpp_declarator_suffix_without_name(first_declarator, source),
7243 };
7244 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
7245 prefix.push_str(&declarator_suffix);
7246 }
7247 return collapse_cpp_whitespace(&prefix)
7248 .trim_end_matches(',')
7249 .trim_end_matches(';')
7250 .trim()
7251 .to_string();
7252 } else {
7253 text
7254 };
7255 collapse_cpp_whitespace(prefix)
7256 .trim_end_matches(',')
7257 .trim_end_matches(';')
7258 .trim()
7259 .to_string()
7260}
7261
7262fn cpp_preserved_initializer(
7263 declaration_node: Node<'_>,
7264 declarator: Node<'_>,
7265 source: &str,
7266) -> Option<String> {
7267 let name = extract_variable_name(declarator, source)?;
7268 let mut cursor = declaration_node.walk();
7269 for child in declaration_node.named_children(&mut cursor) {
7270 if child.kind() != "init_declarator" {
7271 continue;
7272 }
7273 let Some(inner) = child.child_by_field_name("declarator") else {
7274 continue;
7275 };
7276 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
7277 continue;
7278 }
7279 let value = child.child_by_field_name("value")?;
7280 let kind = value.kind();
7281 if matches!(
7282 kind,
7283 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
7284 ) {
7285 return Some(normalize_cpp_whitespace(node_text(value, source)));
7286 }
7287 break;
7288 }
7289 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
7290 let pattern = format!(
7291 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
7292 regex::escape(&name)
7293 );
7294 Regex::new(&pattern)
7295 .ok()
7296 .and_then(|regex| regex.captures(&declaration_text))
7297 .and_then(|captures| captures.get(1))
7298 .map(|value| value.as_str().to_string())
7299}
7300
7301fn render_cpp_function_display_signature_from_node<'tree>(
7302 node: Node<'tree>,
7303 source: &str,
7304 template_signature: Option<&str>,
7305 has_body: bool,
7306 ancestry: &ParentIndex<'tree>,
7307) -> String {
7308 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
7309 let parent_text = node_text(root, source);
7310 let body_local_start = root
7311 .child_by_field_name("body")
7312 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
7313 .unwrap_or(parent_text.len());
7314 let display = parent_text
7315 .get(..body_local_start)
7316 .unwrap_or(parent_text)
7317 .trim()
7318 .trim();
7319 let display = if let Some(template_signature) = template_signature {
7320 if display.starts_with("template ") {
7321 display.to_string()
7322 } else {
7323 format!("template {template_signature} {display}")
7324 }
7325 } else {
7326 display.to_string()
7327 };
7328 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
7329 if has_body {
7330 format!("{display} {{...}}")
7331 } else {
7332 format!("{display};")
7333 }
7334}
7335
7336fn cpp_template_signature(
7337 template_node: Node<'_>,
7338 declaration_child: Node<'_>,
7339 source: &str,
7340) -> Option<String> {
7341 let text = source
7342 .get(template_node.start_byte()..declaration_child.start_byte())
7343 .unwrap_or("");
7344 let text = normalize_cpp_whitespace(text);
7345 let start = text.find('<')?;
7346 let end = text.rfind('>')?;
7347 if end < start {
7348 return None;
7349 }
7350 Some(text[start..=end].to_string())
7351}
7352
7353struct RecoveredFragmentedPartialSpecialization<'tree> {
7354 declaration_node: Node<'tree>,
7355 name: String,
7356 range: Range,
7357 prefix_members: Vec<Node<'tree>>,
7358 member_siblings: Vec<Node<'tree>>,
7359 following_declarations: Vec<Node<'tree>>,
7360}
7361
7362struct RecoveredFragmentedPreprocessorClass<'tree> {
7363 declaration_node: Node<'tree>,
7364 class_node: Node<'tree>,
7365 body: Node<'tree>,
7366 name: String,
7367 range: Range,
7368 tail_members: Vec<Node<'tree>>,
7369 member_siblings: Vec<Node<'tree>>,
7370}
7371
7372fn recover_fragmented_preprocessor_class<'tree>(
7381 template_node: Node<'tree>,
7382 source: &str,
7383 ancestry: &ParentIndex<'tree>,
7384) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
7385 let alternative = ancestry.parent(template_node)?;
7386 if alternative.kind() != "preproc_else" {
7387 return None;
7388 }
7389 let conditional = alternative.parent()?;
7390 if conditional.kind() != "preproc_if" {
7391 return None;
7392 }
7393 let declaration_node = template_node
7394 .named_children(&mut template_node.walk())
7395 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
7396 let class_node = declaration_node
7397 .named_children(&mut declaration_node.walk())
7398 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
7399 let body = cpp_body_node(class_node)?;
7400 if class_node.end_byte() >= declaration_node.end_byte() {
7401 return None;
7402 }
7403 let name = class_like_name(class_node, source, ancestry)?;
7404 let is_partial_specialization = class_node
7405 .child_by_field_name("name")
7406 .is_some_and(|class_name| class_name.kind() == "template_type");
7407 if is_partial_specialization {
7408 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
7409 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
7410 return None;
7411 }
7412 } else {
7413 if !class_has_displaced_preprocessor_terminator(class_node) {
7414 return None;
7415 }
7416 let matching_other_branch = conditional
7417 .named_children(&mut conditional.walk())
7418 .take_while(|child| !same_node(*child, alternative))
7419 .filter(|child| child.kind() == "template_declaration")
7420 .filter_map(first_class_like_child)
7421 .any(|candidate| {
7422 cpp_body_node(candidate).is_none()
7423 && class_like_name(candidate, source, ancestry).as_deref()
7424 == Some(name.as_str())
7425 });
7426 if !matching_other_branch {
7427 return None;
7428 }
7429 }
7430
7431 let mut tail_members = Vec::new();
7432 let mut saw_class = false;
7433 let mut declaration_cursor = declaration_node.walk();
7434 for child in declaration_node.named_children(&mut declaration_cursor) {
7435 if same_node(child, class_node) {
7436 saw_class = true;
7437 } else if saw_class {
7438 tail_members.push(child);
7439 }
7440 }
7441
7442 let mut member_siblings = Vec::new();
7443 let mut saw_template = false;
7444 let mut terminator = None;
7445 for index in 0..alternative.child_count() {
7446 let Some(child) = alternative.child(index) else {
7447 continue;
7448 };
7449 if same_node(child, template_node) {
7450 saw_template = true;
7451 continue;
7452 }
7453 if !saw_template {
7454 continue;
7455 }
7456 if displaced_fragmented_class_terminator(alternative, index) {
7457 terminator = alternative.child(index + 1);
7458 break;
7459 }
7460 if child.is_named() {
7461 member_siblings.push(child);
7462 }
7463 }
7464 let terminator = terminator?;
7465 Some(RecoveredFragmentedPreprocessorClass {
7466 declaration_node,
7467 class_node,
7468 body,
7469 name,
7470 range: Range {
7471 start_byte: class_node.start_byte(),
7472 end_byte: terminator.end_byte(),
7473 start_line: class_node.start_position().row + 1,
7474 end_line: terminator.end_position().row + 1,
7475 },
7476 tail_members,
7477 member_siblings,
7478 })
7479}
7480
7481fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
7482 (0..class_node.child_count()).any(|index| {
7483 class_node.child(index).is_some_and(|child| {
7484 child.kind() == "ERROR"
7485 && (0..child.child_count()).any(|error_index| {
7486 child
7487 .child(error_index)
7488 .is_some_and(|token| token.kind() == "#endif")
7489 })
7490 })
7491 })
7492}
7493
7494pub fn cpp_displaced_preprocessor_terminator<'tree>(
7503 conditional: Node<'tree>,
7504) -> Option<Node<'tree>> {
7505 if !conditional.has_error() {
7506 return None;
7507 }
7508 let has_concrete_direct_terminator = conditional
7509 .child_count()
7510 .checked_sub(1)
7511 .and_then(|index| conditional.child(index))
7512 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
7513 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
7514 return None;
7518 }
7519 let mut displaced = None;
7520 let mut stack = (0..conditional.child_count())
7521 .filter_map(|index| conditional.child(index))
7522 .map(|child| (child, false))
7523 .collect::<Vec<_>>();
7524 while let Some((node, inside_error)) = stack.pop() {
7525 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
7526 continue;
7527 }
7528 if node.kind() == "#endif" && !node.is_missing() && inside_error {
7529 if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
7530 displaced = Some(node);
7531 }
7532 continue;
7533 }
7534 if node != conditional
7535 && matches!(
7536 node.kind(),
7537 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
7538 )
7539 {
7540 continue;
7541 }
7542 let inside_error = inside_error || node.kind() == "ERROR";
7543 for index in 0..node.child_count() {
7544 if let Some(child) = node.child(index) {
7545 stack.push((child, inside_error));
7546 }
7547 }
7548 }
7549 displaced
7550}
7551
7552#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7565pub struct CppDisplacedPreprocessorBoundary {
7566 pub end_byte: usize,
7567 pub end_line: usize,
7568}
7569
7570pub fn cpp_displaced_preprocessor_boundary(
7571 conditional: Node<'_>,
7572) -> Option<CppDisplacedPreprocessorBoundary> {
7573 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
7574 return Some(CppDisplacedPreprocessorBoundary {
7575 end_byte: terminator.end_byte(),
7576 end_line: terminator.end_position().row + 1,
7577 });
7578 }
7579 if let Some(declaration) = displaced_split_declaration(conditional) {
7580 return Some(CppDisplacedPreprocessorBoundary {
7581 end_byte: declaration.end_byte(),
7582 end_line: declaration.end_position().row + 1,
7583 });
7584 }
7585 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
7586 return Some(CppDisplacedPreprocessorBoundary {
7587 end_byte: terminator.end_byte(),
7588 end_line: terminator.end_position().row + 1,
7589 });
7590 }
7591 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
7592 return Some(CppDisplacedPreprocessorBoundary {
7593 end_byte: terminator.end_byte(),
7594 end_line: terminator.end_position().row + 1,
7595 });
7596 }
7597 None
7598}
7599
7600fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
7606 if !conditional.has_error()
7607 || conditional.child_by_field_name("alternative").is_some()
7608 || conditional
7609 .child(conditional.child_count().saturating_sub(1))
7610 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
7611 {
7612 return None;
7613 }
7614 let mut recovered = None;
7615 for index in 0..conditional.named_child_count() {
7616 let Some(nested) = conditional.named_child(index) else {
7617 continue;
7618 };
7619 if !matches!(
7620 nested.kind(),
7621 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
7622 ) || nested.child_by_field_name("alternative").is_some()
7623 {
7624 continue;
7625 }
7626 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
7627 continue;
7628 };
7629 if direct.kind() != "#endif" || direct.is_missing() {
7630 continue;
7631 }
7632 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
7633 continue;
7634 };
7635 if displaced.end_byte() >= direct.start_byte() {
7636 continue;
7637 }
7638 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
7639 recovered = Some(direct);
7640 }
7641 }
7642 recovered
7643}
7644
7645fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
7646 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
7647 return None;
7648 }
7649 let mut cursor = conditional.walk();
7650 let declarations = conditional
7651 .named_children(&mut cursor)
7652 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
7653 .collect::<Vec<_>>();
7654 let declaration = *declarations.first()?;
7655 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
7656 return None;
7657 }
7658 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
7659 let mut terminator = None;
7660 let mut stack = (0..declaration.child_count())
7661 .filter_map(|index| declaration.child(index))
7662 .filter(|child| child.start_byte() < declarator_start)
7663 .map(|child| (child, false))
7664 .collect::<Vec<_>>();
7665 while let Some((node, inside_error)) = stack.pop() {
7666 let inside_error = inside_error || node.kind() == "ERROR";
7667 if inside_error && node.kind() == "#endif" && !node.is_missing() {
7668 terminator = Some(node);
7669 continue;
7670 }
7671 for index in 0..node.child_count() {
7672 if let Some(child) = node.child(index)
7673 && child.start_byte() < declarator_start
7674 {
7675 stack.push((child, inside_error));
7676 }
7677 }
7678 }
7679 terminator
7680}
7681
7682fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
7683 if !conditional.has_error()
7684 || conditional.child_by_field_name("alternative").is_some()
7685 || conditional
7686 .prev_named_sibling()
7687 .filter(|sibling| {
7688 sibling.kind() == "ERROR"
7689 && sibling.child_count() == 1
7690 && sibling
7691 .child(0)
7692 .is_some_and(|child| child.kind() == "typedef")
7693 })
7694 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
7695 .is_none()
7696 {
7697 return None;
7698 }
7699 let mut cursor = conditional.walk();
7700 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
7701 let declaration_index = children
7702 .iter()
7703 .position(|child| child.kind() == "declaration" && child.has_error())?;
7704 let declaration = children[declaration_index];
7705 if !children
7706 .iter()
7707 .skip(declaration_index + 1)
7708 .any(|child| child.end_byte() > declaration.end_byte())
7709 {
7710 return None;
7711 }
7712 let declarator = declaration.child_by_field_name("declarator")?;
7713 let mut error_end = None;
7714 let mut names = Vec::new();
7715 let mut stack = vec![declarator];
7716 while let Some(node) = stack.pop() {
7717 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
7718 error_end =
7719 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
7720 continue;
7721 }
7722 if matches!(node.kind(), "identifier" | "type_identifier") {
7723 names.push(node.start_byte());
7724 }
7725 for index in (0..node.named_child_count()).rev() {
7726 if let Some(child) = node.named_child(index) {
7727 stack.push(child);
7728 }
7729 }
7730 }
7731 let error_end = error_end?;
7732 names
7733 .into_iter()
7734 .any(|start| start >= error_end)
7735 .then_some(declaration)
7736}
7737
7738fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
7739 let Some(error) = parent.child(error_index) else {
7740 return false;
7741 };
7742 if error.kind() != "ERROR"
7743 || error.child_count() != 1
7744 || error.child(0).is_none_or(|child| child.kind() != "}")
7745 {
7746 return false;
7747 }
7748 let Some(semicolon) = parent.child(error_index + 1) else {
7749 return false;
7750 };
7751 semicolon.kind() == "expression_statement"
7752 && semicolon.child_count() == 1
7753 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
7754}
7755
7756fn displaced_macro_class_tail(
7762 declaration_node: Node<'_>,
7763 body: Node<'_>,
7764 source: &str,
7765) -> Option<DisplacedMacroClassTail> {
7766 if !matches!(
7767 declaration_node.kind(),
7768 "class_specifier" | "struct_specifier" | "union_specifier"
7769 ) || body.kind() != "field_declaration_list"
7770 {
7771 return None;
7772 }
7773
7774 let child_count = body.named_child_count();
7775 for index in 0..child_count {
7776 let child = body.named_child(index)?;
7777 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
7778 continue;
7779 };
7780 let split_index = index + 1;
7781 if split_index >= child_count {
7782 return None;
7783 }
7784 let mut cursor = body.walk();
7785 if !body
7786 .named_children(&mut cursor)
7787 .skip(split_index)
7788 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
7789 {
7790 return None;
7791 }
7792 return Some(DisplacedMacroClassTail {
7793 split_index,
7794 class_range: Range {
7795 start_byte: declaration_node.start_byte(),
7796 end_byte: terminator.end_byte(),
7797 start_line: declaration_node.start_position().row + 1,
7798 end_line: terminator.end_position().row + 1,
7799 },
7800 });
7801 }
7802 None
7803}
7804
7805fn displaced_macro_field_terminator<'tree>(
7806 field: Node<'tree>,
7807 source: &str,
7808) -> Option<Node<'tree>> {
7809 if field.kind() != "field_declaration" {
7810 return None;
7811 }
7812 let macro_type = field.child_by_field_name("type")?;
7813 if macro_type.kind() != "type_identifier"
7814 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
7815 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
7816 {
7817 return None;
7818 }
7819 for index in 0..field.child_count() {
7820 let error = field.child(index)?;
7821 if error.kind() != "ERROR"
7822 || error.child_count() != 1
7823 || error.child(0).is_none_or(|child| child.kind() != "}")
7824 {
7825 continue;
7826 }
7827 let semicolon = field.child(index + 1)?;
7828 if semicolon.kind() == ";" {
7829 return Some(semicolon);
7830 }
7831 }
7832 None
7833}
7834
7835fn recover_fragmented_partial_specialization<'tree>(
7836 template_node: Node<'tree>,
7837 declaration_child: Node<'tree>,
7838 source: &str,
7839 ancestry: &ParentIndex<'tree>,
7840) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
7841 if declaration_child.kind() != "function_definition" {
7842 return None;
7843 }
7844 let class_node = declaration_child.child_by_field_name("type")?;
7845 if !matches!(
7846 class_node.kind(),
7847 "class_specifier" | "struct_specifier" | "union_specifier"
7848 ) || !class_node
7849 .child_by_field_name("name")
7850 .and_then(|name| direct_identifier_name(name, source))
7851 .is_some_and(|name| cpp_export_macro_token(&name))
7852 {
7853 return None;
7854 }
7855 let declarator = declaration_child.child_by_field_name("declarator")?;
7856 if declarator.kind() != "template_function" {
7857 return None;
7858 }
7859 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
7860 if metadata.specialization_arguments.is_empty() {
7861 return None;
7862 }
7863 let body = declaration_child.child_by_field_name("body")?;
7864 if body.kind() != "compound_statement" {
7865 return None;
7866 }
7867 let complete_prefix = body.named_child(0).filter(|first| {
7868 first.kind() == "labeled_statement"
7869 && first.has_error()
7870 && first
7871 .named_child(first.named_child_count().saturating_sub(1))
7872 .is_some_and(recovered_declaration_has_class_terminator)
7873 });
7874 let complete_body = complete_prefix.is_some();
7875 let mut prefix_members = Vec::new();
7876 if let Some(prefix) = complete_prefix {
7877 prefix_members.push(prefix);
7878 } else {
7879 let mut body_cursor = body.walk();
7880 for child in body.named_children(&mut body_cursor) {
7881 if !is_structurally_valid_fragmented_class_prefix_member(child) {
7882 break;
7883 }
7884 prefix_members.push(child);
7885 }
7886 }
7887 let containing_declarations = template_node.parent()?;
7888 if !matches!(
7889 containing_declarations.kind(),
7890 "declaration_list" | "compound_statement"
7891 ) {
7892 return None;
7893 }
7894 let mut member_siblings = Vec::new();
7895 let mut following_declarations = Vec::new();
7896 let terminator;
7897 if complete_body {
7898 terminator = complete_prefix?;
7899 let mut cursor = body.walk();
7900 let mut after_prefix = false;
7901 for child in body.named_children(&mut cursor) {
7902 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
7903 after_prefix = true;
7904 } else if after_prefix {
7905 following_declarations.push(child);
7906 }
7907 }
7908 } else {
7909 let mut found_template = false;
7910 let mut cursor = containing_declarations.walk();
7911 let mut class_terminator = None;
7912 for child in containing_declarations.children(&mut cursor) {
7913 if same_node(child, template_node) {
7914 found_template = true;
7915 continue;
7916 }
7917 if found_template && child.kind() == "}" {
7918 class_terminator = Some(child);
7919 break;
7920 }
7921 if found_template && child.kind() == "namespace_definition" {
7928 return None;
7929 }
7930 if found_template && child.is_named() {
7931 member_siblings.push(child);
7932 }
7933 }
7934 terminator = class_terminator?;
7935 }
7936 let name = format!(
7937 "{}<{}>",
7938 metadata.primary_name,
7939 metadata
7940 .specialization_arguments
7941 .iter()
7942 .map(|argument| argument.text.as_str())
7943 .collect::<Vec<_>>()
7944 .join(", ")
7945 );
7946 Some(RecoveredFragmentedPartialSpecialization {
7947 declaration_node: declaration_child,
7948 name,
7949 range: Range {
7950 start_byte: declaration_child.start_byte(),
7951 end_byte: terminator.end_byte(),
7952 start_line: declaration_child.start_position().row + 1,
7953 end_line: terminator.end_position().row + 1,
7954 },
7955 prefix_members,
7956 member_siblings,
7957 following_declarations,
7958 })
7959}
7960
7961fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
7962 if declaration.kind() != "declaration" {
7963 return false;
7964 }
7965 (0..declaration.child_count().saturating_sub(1)).any(|index| {
7970 let Some(error) = declaration.child(index) else {
7971 return false;
7972 };
7973 error.kind() == "ERROR"
7974 && error.child_count() == 1
7975 && error.child(0).is_some_and(|child| child.kind() == "}")
7976 && declaration
7977 .child(index + 1)
7978 .is_some_and(|child| child.kind() == ";")
7979 })
7980}
7981
7982fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
7983 if node.has_error() {
7984 return false;
7985 }
7986 match node.kind() {
7987 "declaration"
7988 | "field_declaration"
7989 | "alias_declaration"
7990 | "type_definition"
7991 | "static_assert_declaration" => true,
7992 "labeled_statement" => node
7993 .named_child(node.named_child_count().saturating_sub(1))
7994 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
7995 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
7996 matches!(
7997 child.kind(),
7998 "declaration"
7999 | "field_declaration"
8000 | "alias_declaration"
8001 | "type_definition"
8002 | "function_definition"
8003 )
8004 }),
8005 _ => false,
8006 }
8007}
8008
8009fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
8010 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
8011 .then(|| node.child_by_field_name("declarator"))
8012 .flatten()
8013 .and_then(|declarator| extract_variable_name(declarator, source))
8014}
8015
8016fn cpp_template_metadata<'tree>(
8017 template_node: Node<'tree>,
8018 declaration_child: Node<'tree>,
8019 source: &str,
8020 ancestry: &ParentIndex<'tree>,
8021) -> Option<CppTemplateMetadata> {
8022 let parameters_node = template_node.child_by_field_name("parameters")?;
8023 let name_node = cpp_templated_class_name_node(declaration_child)?;
8024 let primary_node = match name_node.kind() {
8025 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
8026 _ => name_node,
8027 };
8028 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
8029 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
8030 return None;
8031 }
8032
8033 let mut parameter_nodes = Vec::new();
8034 let mut parameter_names = Vec::new();
8035 let mut cursor = parameters_node.walk();
8036 for parameter in parameters_node.named_children(&mut cursor) {
8037 if !matches!(
8038 parameter.kind(),
8039 "type_parameter_declaration"
8040 | "optional_type_parameter_declaration"
8041 | "variadic_type_parameter_declaration"
8042 | "template_template_parameter_declaration"
8043 | "parameter_declaration"
8044 | "optional_parameter_declaration"
8045 | "variadic_parameter_declaration"
8046 ) {
8047 continue;
8048 }
8049 let index = parameter_nodes.len();
8050 let name = cpp_template_parameter_name(parameter, source)
8055 .unwrap_or_else(|| format!("<anonymous:{index}>"));
8056 parameter_names.push(name);
8057 parameter_nodes.push(parameter);
8058 }
8059 let parameters = parameter_nodes
8060 .into_iter()
8061 .zip(parameter_names.iter().cloned())
8062 .map(|(parameter, name)| CppTemplateParameterMetadata {
8063 name,
8064 kind: cpp_template_parameter_kind(parameter),
8065 variadic: matches!(
8066 parameter.kind(),
8067 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
8068 ),
8069 default: cpp_template_parameter_default_expression(
8070 parameter,
8071 source,
8072 ¶meter_names,
8073 ancestry,
8074 ),
8075 })
8076 .collect();
8077 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
8078 Vec::new()
8079 } else {
8080 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
8081 .unwrap_or_default()
8082 };
8083 let alias_target = (declaration_child.kind() == "alias_declaration")
8084 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
8085 .flatten();
8086 Some(CppTemplateMetadata {
8087 primary_name,
8088 primary_fq_name: String::new(),
8089 parameters,
8090 specialization_arguments,
8091 alias_target,
8092 })
8093}
8094
8095fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
8096 match node.kind() {
8097 "class_specifier" | "struct_specifier" | "union_specifier" => {
8098 node.child_by_field_name("name")
8099 }
8100 "function_definition" => {
8101 let declarator = node.child_by_field_name("declarator")?;
8102 if matches!(declarator.kind(), "identifier" | "template_function") {
8103 Some(declarator)
8104 } else {
8105 None
8106 }
8107 }
8108 "alias_declaration" => node.child_by_field_name("name"),
8109 _ => None,
8110 }
8111}
8112
8113fn cpp_template_alias_target<'tree>(
8114 alias: Node<'tree>,
8115 source: &str,
8116 parameter_names: &[String],
8117 ancestry: &ParentIndex<'tree>,
8118) -> Option<CppTemplateAliasTargetMetadata> {
8119 let mut type_node = alias.child_by_field_name("type")?;
8120 while type_node.kind() == "type_descriptor" {
8121 type_node = type_node.child_by_field_name("type")?;
8122 }
8123 let global = type_node.child_by_field_name("scope").is_none()
8124 && type_node.child(0).is_some_and(|child| child.kind() == "::");
8125 let mut components = Vec::new();
8126 cpp_template_target_components(type_node, source, &mut components)?;
8127 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
8128 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
8129 components,
8130 global,
8131 arguments,
8132 })
8133}
8134
8135fn cpp_template_target_components(
8136 node: Node<'_>,
8137 source: &str,
8138 out: &mut Vec<String>,
8139) -> Option<()> {
8140 match node.kind() {
8141 "identifier" | "namespace_identifier" | "type_identifier" => {
8142 out.push(node_text(node, source).to_string());
8143 Some(())
8144 }
8145 "template_type" => {
8146 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
8147 }
8148 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
8149 if let Some(scope) = node.child_by_field_name("scope") {
8150 cpp_template_target_components(scope, source, out)?;
8151 }
8152 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
8153 }
8154 _ => None,
8155 }
8156}
8157
8158fn cpp_template_argument_expressions<'tree>(
8159 mut node: Node<'tree>,
8160 source: &str,
8161 parameter_names: &[String],
8162 ancestry: &ParentIndex<'tree>,
8163) -> Option<Vec<CppTemplateExpression>> {
8164 loop {
8165 match node.kind() {
8166 "template_type" | "template_function" => {
8167 let arguments = node.child_by_field_name("arguments")?;
8168 let mut cursor = arguments.walk();
8169 return Some(
8170 arguments
8171 .named_children(&mut cursor)
8172 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
8173 .map(|argument| {
8174 cpp_template_expression(argument, source, parameter_names, ancestry)
8175 })
8176 .collect(),
8177 );
8178 }
8179 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
8180 node = node
8181 .child_by_field_name("name")
8182 .or_else(|| node.child_by_field_name("type"))?;
8183 }
8184 _ => return None,
8185 }
8186 }
8187}
8188
8189fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
8190 let candidate = node
8191 .child_by_field_name("name")
8192 .or_else(|| node.child_by_field_name("declarator"))
8193 .or_else(|| {
8194 let mut cursor = node.walk();
8195 node.named_children(&mut cursor).find(|child| {
8196 matches!(
8197 child.kind(),
8198 "identifier" | "type_identifier" | "field_identifier"
8199 )
8200 })
8201 })?;
8202 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
8203 (!name.is_empty()).then_some(name)
8204}
8205
8206fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
8207 match node.kind() {
8208 "type_parameter_declaration"
8209 | "optional_type_parameter_declaration"
8210 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
8211 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
8212 _ => CppTemplateParameterKind::Value,
8213 }
8214}
8215
8216fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
8217 node.child_by_field_name("default_type")
8218 .or_else(|| node.child_by_field_name("default_value"))
8219}
8220
8221fn cpp_template_parameter_default_expression<'tree>(
8222 parameter: Node<'tree>,
8223 source: &str,
8224 parameter_names: &[String],
8225 ancestry: &ParentIndex<'tree>,
8226) -> Option<CppTemplateExpression> {
8227 let default = cpp_template_parameter_default(parameter)?;
8228 let base = cpp_template_expression(default, source, parameter_names, ancestry);
8229 let Some(pointer_error) = parameter.next_named_sibling() else {
8230 return Some(base);
8231 };
8232 let Some(pointer_declarator) =
8233 recovered_abstract_pointer_declarator_term(pointer_error, source)
8234 else {
8235 return Some(base);
8236 };
8237 Some(CppTemplateExpression {
8238 text: format!(
8239 "{}{}",
8240 base.text,
8241 normalize_cpp_whitespace(node_text(pointer_error, source))
8242 ),
8243 term: CppTemplateTerm::Node {
8244 kind: "type_descriptor".to_string(),
8245 children: vec![base.term, pointer_declarator],
8246 },
8247 })
8248}
8249
8250fn recovered_abstract_pointer_declarator_term(
8251 node: Node<'_>,
8252 source: &str,
8253) -> Option<CppTemplateTerm> {
8254 if node.kind() != "ERROR" || node.child_count() == 0 {
8255 return None;
8256 }
8257 let mut children = Vec::new();
8258 for index in 0..node.child_count() {
8259 let child = node.child(index)?;
8260 if child.kind() != "*" {
8261 return None;
8262 }
8263 children.push(CppTemplateTerm::Atom {
8264 kind: "*".to_string(),
8265 text: normalize_cpp_whitespace(node_text(child, source)),
8266 });
8267 }
8268 Some(CppTemplateTerm::Node {
8269 kind: "abstract_pointer_declarator".to_string(),
8270 children,
8271 })
8272}
8273
8274fn cpp_template_expression<'tree>(
8275 node: Node<'tree>,
8276 source: &str,
8277 parameter_names: &[String],
8278 ancestry: &ParentIndex<'tree>,
8279) -> CppTemplateExpression {
8280 let text = normalize_cpp_whitespace(node_text(node, source));
8281 CppTemplateExpression {
8282 text,
8283 term: cpp_template_term(node, source, parameter_names, ancestry),
8284 }
8285}
8286
8287pub fn cpp_template_term<'tree>(
8288 node: Node<'tree>,
8289 source: &str,
8290 parameter_names: &[String],
8291 ancestry: &ParentIndex<'tree>,
8292) -> CppTemplateTerm {
8293 enum Work<'tree> {
8294 Visit(Node<'tree>),
8295 Build { kind: String, child_count: usize },
8296 }
8297
8298 let mut work = vec![Work::Visit(node)];
8299 let mut terms = Vec::new();
8300 while let Some(next) = work.pop() {
8301 match next {
8302 Work::Visit(current) => {
8303 let text = normalize_cpp_whitespace(node_text(current, source));
8304 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
8305 terms.push(CppTemplateTerm::Parameter(text));
8306 continue;
8307 }
8308 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
8309 let mut cursor = current.walk();
8310 let named = current
8311 .named_children(&mut cursor)
8312 .filter(|child| !child.is_extra() && child.kind() != "comment")
8313 .collect::<Vec<_>>();
8314 if let [child] = named.as_slice() {
8315 work.push(Work::Visit(*child));
8316 continue;
8317 }
8318 }
8319 if current.child_count() == 0 {
8320 terms.push(CppTemplateTerm::Atom {
8321 kind: if matches!(
8322 current.kind(),
8323 "identifier"
8324 | "type_identifier"
8325 | "field_identifier"
8326 | "namespace_identifier"
8327 ) {
8328 "identifier".to_string()
8329 } else {
8330 current.kind().to_string()
8331 },
8332 text,
8333 });
8334 continue;
8335 }
8336 let children = (0..current.child_count())
8337 .filter_map(|index| current.child(index))
8338 .filter(|child| !child.is_extra() && child.kind() != "comment")
8339 .collect::<Vec<_>>();
8340 work.push(Work::Build {
8341 kind: current.kind().to_string(),
8342 child_count: children.len(),
8343 });
8344 work.extend(children.into_iter().rev().map(Work::Visit));
8345 }
8346 Work::Build { kind, child_count } => {
8347 let children = terms.split_off(terms.len() - child_count);
8348 terms.push(CppTemplateTerm::Node { kind, children });
8349 }
8350 }
8351 }
8352 terms.pop().expect("template term traversal emits one root")
8353}
8354
8355fn cpp_template_term_leaf_is_parameter<'tree>(
8356 node: Node<'tree>,
8357 text: &str,
8358 parameter_names: &[String],
8359 ancestry: &ParentIndex<'tree>,
8360) -> bool {
8361 if !parameter_names.iter().any(|parameter| parameter == text) {
8362 return false;
8363 }
8364 !ancestry.parent(node).is_some_and(|parent| {
8365 matches!(
8366 parent.kind(),
8367 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
8368 ) && parent.child_by_field_name("scope").is_some()
8369 && parent.child_by_field_name("name") == Some(node)
8370 })
8371}
8372
8373fn enclosing_cpp_declaration_node<'tree>(
8374 mut node: Node<'tree>,
8375 ancestry: &ParentIndex<'tree>,
8376) -> Option<Node<'tree>> {
8377 loop {
8378 match node.kind() {
8379 "declaration"
8380 | "function_declaration"
8381 | "field_declaration"
8382 | "function_definition" => return Some(node),
8383 _ => node = ancestry.parent(node)?,
8384 }
8385 }
8386}
8387
8388fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
8389 let mut params = Vec::new();
8390 let mut cursor = parameters_node.walk();
8391 for child in parameters_node.children(&mut cursor) {
8392 match child.kind() {
8393 "parameter_declaration" | "optional_parameter_declaration" => {
8394 params.push(cpp_parameter_type(child, source));
8395 }
8396 "variadic_parameter_declaration" => {
8397 params.push(cpp_parameter_type(child, source));
8398 }
8399 "variadic_parameter" | "..." => params.push("...".to_string()),
8400 _ => {}
8401 }
8402 }
8403
8404 if params.is_empty() {
8405 "()".to_string()
8406 } else {
8407 format!("({})", params.join(", "))
8408 }
8409}
8410
8411fn cpp_signature_metadata<'tree>(
8412 signature: String,
8413 function_declarator: Node<'tree>,
8414 source: &str,
8415 ancestry: &ParentIndex<'tree>,
8416) -> SignatureMetadata {
8417 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
8418 let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
8419 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
8420 let return_type_identity =
8421 cpp_callable_return_type_identity(function_declarator, source, ancestry);
8422 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
8423 return enrich(
8424 SignatureMetadata::new(signature, Vec::new())
8425 .with_return_type_text(return_type_text)
8426 .with_return_type_identity(return_type_identity),
8427 );
8428 };
8429 let callable_arity = cpp_callable_arity(parameters_node, source);
8430 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
8431 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
8432 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
8433 let Some(relative_start) = signature
8434 .get(search_from..)
8435 .and_then(|suffix| suffix.find(¶meter_text))
8436 else {
8437 return enrich(
8438 SignatureMetadata::new(signature, Vec::new())
8439 .with_callable_arity(callable_arity)
8440 .with_callable_parameter_types(callable_parameter_types)
8441 .with_return_type_text(return_type_text)
8442 .with_return_type_identity(return_type_identity),
8443 );
8444 };
8445 let parameters_start = search_from + relative_start;
8446 let parameters_end = parameters_start + parameter_text.len();
8447 let mut search_start = parameters_start;
8448 let parameters = cpp_parameter_label_nodes(parameters_node)
8449 .into_iter()
8450 .filter_map(|label_node| {
8451 let label = normalize_cpp_whitespace(node_text(label_node, source));
8452 if label.is_empty() || search_start > parameters_end {
8453 return None;
8454 }
8455 let haystack = signature.get(search_start..parameters_end)?;
8456 let relative_start = haystack.find(&label)?;
8457 let start_byte = search_start + relative_start;
8458 let end_byte = start_byte + label.len();
8459 search_start = end_byte;
8460 Some(ParameterMetadata::new(label, start_byte, end_byte))
8461 })
8462 .collect();
8463 enrich(
8464 SignatureMetadata::new(signature, parameters)
8465 .with_callable_arity(callable_arity)
8466 .with_callable_parameter_types(callable_parameter_types)
8467 .with_return_type_text(return_type_text)
8468 .with_return_type_identity(return_type_identity),
8469 )
8470}
8471
8472fn cpp_callable_is_structural_constructor<'tree>(
8473 function_declarator: Node<'tree>,
8474 source: &str,
8475 ancestry: &ParentIndex<'tree>,
8476) -> bool {
8477 let Some(name_node) = function_declarator
8478 .child_by_field_name("declarator")
8479 .or_else(|| function_declarator.child_by_field_name("name"))
8480 .or_else(|| last_named_child(function_declarator))
8481 else {
8482 return false;
8483 };
8484 let Some(callable_name) = direct_identifier_name(name_node, source) else {
8485 return false;
8486 };
8487
8488 let mut current = ancestry.parent(function_declarator);
8489 while let Some(ancestor) = current {
8490 let owner_name = match ancestor.kind() {
8491 "class_specifier" | "struct_specifier" | "union_specifier" => {
8492 class_like_name(ancestor, source, ancestry)
8493 }
8494 "ERROR" => malformed_class_error_owner_name(ancestor, source),
8495 _ => None,
8496 };
8497 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
8498 return true;
8499 }
8500 current = ancestry.parent(ancestor);
8501 }
8502 false
8503}
8504
8505fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
8515 if node.kind() != "ERROR" {
8516 return None;
8517 }
8518 let keyword = node.child(0)?;
8519 if !matches!(keyword.kind(), "class" | "struct" | "union") {
8520 return None;
8521 }
8522 let name_node = node.child(1)?;
8523 let name = direct_identifier_name(name_node, source)?;
8524 let has_body = (2..node.child_count())
8525 .filter_map(|index| node.child(index))
8526 .any(|child| child.kind() == "{");
8527 has_body.then_some(name)
8528}
8529
8530fn cpp_callable_return_type_identity<'tree>(
8531 function_declarator: Node<'tree>,
8532 source: &str,
8533 ancestry: &ParentIndex<'tree>,
8534) -> Option<StructuredTypeIdentity> {
8535 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
8536 return None;
8537 }
8538 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
8539 if let Some((return_type, _)) =
8540 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
8541 {
8542 return cpp_structured_type_identity(return_type, source, &lexical_scope);
8543 }
8544 let mut cursor = function_declarator.walk();
8545 if let Some(trailing) = function_declarator
8546 .named_children(&mut cursor)
8547 .find(|child| child.kind() == "trailing_return_type")
8548 && let Some(type_descriptor) = trailing.named_child(0)
8549 {
8550 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
8551 }
8552
8553 let mut current = function_declarator;
8554 let mut wrappers = Vec::new();
8555 while let Some(parent) = ancestry.parent(current) {
8556 if matches!(
8557 parent.kind(),
8558 "function_definition" | "declaration" | "field_declaration"
8559 ) {
8560 let type_node = parent.child_by_field_name("type")?;
8561 if cpp_export_macro_token(node_text(type_node, source))
8562 && (0..parent.named_child_count()).any(|index| {
8563 parent
8564 .named_child(index)
8565 .is_some_and(|child| child.kind() == "ERROR")
8566 })
8567 {
8568 return None;
8569 }
8570 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
8571 for wrapper in wrappers.into_iter().rev() {
8572 identity = cpp_wrap_structured_type(identity, wrapper)?;
8573 }
8574 return Some(identity);
8575 }
8576 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
8577 || (matches!(
8578 parent.kind(),
8579 "pointer_declarator"
8580 | "reference_declarator"
8581 | "array_declarator"
8582 | "parenthesized_declarator"
8583 ) && parent.named_child_count() == 1
8584 && parent.named_child(0) == Some(current));
8585 if !wraps_current_declarator {
8586 return None;
8587 }
8588 match parent.kind() {
8589 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
8590 "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
8591 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
8592 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
8593 _ => return None,
8594 }
8595 current = parent;
8596 }
8597 None
8598}
8599
8600fn cpp_structured_type_identity(
8601 node: Node<'_>,
8602 source: &str,
8603 lexical_scope: &[String],
8604) -> Option<StructuredTypeIdentity> {
8605 enum Work<'tree> {
8606 Visit(Node<'tree>),
8607 Wrap(CppStructuredTypeWrapper),
8608 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
8609 BuildGeneric { argument_count: usize },
8610 }
8611
8612 let mut work = vec![Work::Visit(node)];
8613 let mut values = Vec::new();
8614 let mut builder = StructuredTypeIdentityBuilder::default();
8615 while let Some(next) = work.pop() {
8616 match next {
8617 Work::Visit(current) => match current.kind() {
8618 "type_descriptor" => {
8619 let type_node = current
8620 .child_by_field_name("type")
8621 .or_else(|| current.named_child(0))?;
8622 let mut wrappers = Vec::new();
8623 let mut cursor = current.walk();
8624 for child in current.named_children(&mut cursor) {
8625 if child.id() != type_node.id() {
8626 wrappers.extend(cpp_structured_declarator_wrappers(child));
8627 }
8628 }
8629 work.push(Work::ApplyWrappers(wrappers));
8630 work.push(Work::Visit(type_node));
8631 }
8632 "pointer_declarator" | "abstract_pointer_declarator" => {
8633 let child = current
8634 .child_by_field_name("declarator")
8635 .or_else(|| current.named_child(0))?;
8636 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
8637 work.push(Work::Visit(child));
8638 }
8639 "reference_declarator" => {
8640 let child = current
8641 .child_by_field_name("declarator")
8642 .or_else(|| current.named_child(0))?;
8643 work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
8644 work.push(Work::Visit(child));
8645 }
8646 "array_declarator" | "abstract_array_declarator" => {
8647 let child = current
8648 .child_by_field_name("declarator")
8649 .or_else(|| current.named_child(0))?;
8650 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
8651 work.push(Work::Visit(child));
8652 }
8653 "template_type" => {
8654 let name_node = current.child_by_field_name("name")?;
8655 let arguments = current
8656 .child_by_field_name("arguments")
8657 .map(|arguments_node| {
8658 let mut cursor = arguments_node.walk();
8659 arguments_node
8660 .named_children(&mut cursor)
8661 .filter(|child| !child.is_extra() && child.kind() != "comment")
8662 .collect::<Vec<_>>()
8663 })
8664 .unwrap_or_default();
8665 work.push(Work::BuildGeneric {
8666 argument_count: arguments.len(),
8667 });
8668 work.extend(arguments.into_iter().rev().map(Work::Visit));
8669 work.push(Work::Visit(name_node));
8670 }
8671 "qualified_identifier"
8672 | "scoped_identifier"
8673 | "scoped_type_identifier"
8674 | "type_identifier"
8675 | "field_identifier"
8676 | "identifier"
8677 | "namespace_identifier"
8678 | "primitive_type" => {
8679 values.push(builder.named(cpp_structured_named_type(
8680 current,
8681 source,
8682 lexical_scope,
8683 )?)?);
8684 }
8685 _ => {
8686 let child = current.child_by_field_name("type").or_else(|| {
8687 (current.named_child_count() == 1)
8688 .then(|| current.named_child(0))
8689 .flatten()
8690 })?;
8691 work.push(Work::Visit(child));
8692 }
8693 },
8694 Work::Wrap(wrapper) => {
8695 let root = values.pop()?;
8696 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
8697 }
8698 Work::ApplyWrappers(wrappers) => {
8699 let mut root = values.pop()?;
8700 for wrapper in wrappers.into_iter().rev() {
8701 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
8702 }
8703 values.push(root);
8704 }
8705 Work::BuildGeneric { argument_count } => {
8706 let value_count = argument_count.checked_add(1)?;
8707 let start = values.len().checked_sub(value_count)?;
8708 let mut built = values.split_off(start);
8709 let base = built.remove(0);
8710 values.push(builder.generic(base, built)?);
8711 }
8712 }
8713 }
8714 (values.len() == 1)
8715 .then(|| values.pop())
8716 .flatten()
8717 .and_then(|root| builder.finish(root))
8718}
8719
8720fn cpp_structured_named_type(
8721 node: Node<'_>,
8722 source: &str,
8723 lexical_scope: &[String],
8724) -> Option<StructuredTypeName> {
8725 let path = cpp_structured_type_path(node, source)?;
8726 let absolute = node.child_by_field_name("scope").is_none()
8727 && node.child(0).is_some_and(|child| child.kind() == "::");
8728 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
8729}
8730
8731#[derive(Clone, Copy)]
8732enum CppStructuredTypeWrapper {
8733 Pointer,
8734 Reference,
8735 Array,
8736}
8737
8738fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
8739 let mut wrappers = Vec::new();
8740 let mut current = node;
8741 loop {
8742 match current.kind() {
8743 "pointer_declarator" | "abstract_pointer_declarator" => {
8744 wrappers.push(CppStructuredTypeWrapper::Pointer)
8745 }
8746 "reference_declarator" | "abstract_reference_declarator" => {
8747 wrappers.push(CppStructuredTypeWrapper::Reference)
8748 }
8749 "array_declarator" | "abstract_array_declarator" => {
8750 wrappers.push(CppStructuredTypeWrapper::Array)
8751 }
8752 _ => break,
8753 }
8754 let Some(child) = current
8755 .child_by_field_name("declarator")
8756 .or_else(|| current.named_child(0))
8757 else {
8758 break;
8759 };
8760 current = child;
8761 }
8762 wrappers
8763}
8764
8765fn cpp_wrap_structured_type(
8766 identity: StructuredTypeIdentity,
8767 wrapper: CppStructuredTypeWrapper,
8768) -> Option<StructuredTypeIdentity> {
8769 match wrapper {
8770 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
8771 CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
8772 CppStructuredTypeWrapper::Array => identity.wrap_array(),
8773 }
8774}
8775
8776fn cpp_wrap_structured_type_node(
8777 builder: &mut StructuredTypeIdentityBuilder,
8778 inner: StructuredTypeNodeId,
8779 wrapper: CppStructuredTypeWrapper,
8780) -> Option<StructuredTypeNodeId> {
8781 match wrapper {
8782 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
8783 CppStructuredTypeWrapper::Reference => builder.reference(inner),
8784 CppStructuredTypeWrapper::Array => builder.array(inner),
8785 }
8786}
8787
8788fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
8789 let mut path = Vec::new();
8790 let mut stack = vec![node];
8791 while let Some(current) = stack.pop() {
8792 match current.kind() {
8793 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
8794 let component = node_text(current, source).to_string();
8795 if component.is_empty() {
8796 return None;
8797 }
8798 path.push(component);
8799 }
8800 "template_type" | "dependent_type" => {
8801 stack.push(current.child_by_field_name("name")?);
8802 }
8803 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
8804 stack.push(current.child_by_field_name("name")?);
8805 if let Some(scope) = current.child_by_field_name("scope") {
8806 stack.push(scope);
8807 }
8808 }
8809 _ => return None,
8810 }
8811 }
8812 (!path.is_empty()).then_some(path)
8813}
8814
8815fn cpp_callable_lexical_scope<'tree>(
8816 node: Node<'tree>,
8817 source: &str,
8818 ancestry: &ParentIndex<'tree>,
8819) -> Vec<String> {
8820 let mut groups = Vec::new();
8821 let mut current = ancestry.parent(node);
8822 while let Some(parent) = current {
8823 if matches!(
8824 parent.kind(),
8825 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
8826 ) && let Some(name_node) = parent.child_by_field_name("name")
8827 && let Some(components) = cpp_structured_type_path(name_node, source)
8828 && !components.is_empty()
8829 {
8830 groups.push(components);
8831 }
8832 current = ancestry.parent(parent);
8833 }
8834 groups.reverse();
8835 groups.into_iter().flatten().collect()
8836}
8837
8838fn cpp_callable_dispatch_extensibility<'tree>(
8839 function_declarator: Node<'tree>,
8840 ancestry: &ParentIndex<'tree>,
8841) -> DispatchExtensibility {
8842 let mut declaration = None;
8843 let mut current = Some(function_declarator);
8844 while let Some(node) = current {
8845 match node.kind() {
8846 "template_declaration"
8847 | "preproc_if"
8848 | "preproc_ifdef"
8849 | "preproc_else"
8850 | "preproc_elif"
8851 | "preproc_call"
8852 | "ERROR" => return DispatchExtensibility::Open,
8853 "declaration" | "field_declaration" | "function_definition" => {
8854 declaration.get_or_insert(node);
8855 }
8856 "translation_unit" => break,
8857 _ => {}
8858 }
8859 current = ancestry.parent(node);
8860 }
8861 let Some(declaration) = declaration else {
8862 return DispatchExtensibility::Open;
8863 };
8864
8865 let mut saw_virtual_boundary = false;
8866 let mut stack = vec![declaration];
8867 while let Some(node) = stack.pop() {
8868 match node.kind() {
8869 "compound_statement" | "field_declaration_list" => continue,
8870 "final" | "final_specifier" => return DispatchExtensibility::Closed,
8871 "virtual"
8872 | "override"
8873 | "virtual_specifier"
8874 | "pure_virtual_clause"
8875 | "template_parameter_list"
8876 | "template_method"
8877 | "template_function"
8878 | "ERROR" => saw_virtual_boundary = true,
8879 _ => {}
8880 }
8881 let mut cursor = node.walk();
8882 stack.extend(node.children(&mut cursor));
8883 }
8884
8885 if saw_virtual_boundary {
8886 DispatchExtensibility::Open
8887 } else {
8888 DispatchExtensibility::Closed
8889 }
8890}
8891
8892fn cpp_callable_linkage<'tree>(
8893 declaration: Node<'tree>,
8894 source: &str,
8895 ancestry: &ParentIndex<'tree>,
8896) -> CallableLinkage {
8897 let mut enclosed_by_class = false;
8898 let mut current = ancestry.parent(declaration);
8899 while let Some(node) = current {
8900 if node.kind() == "namespace_definition"
8901 && node
8902 .child_by_field_name("name")
8903 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8904 {
8905 return CallableLinkage::Internal;
8906 }
8907 if matches!(
8908 node.kind(),
8909 "class_specifier" | "struct_specifier" | "union_specifier"
8910 ) {
8911 if node
8912 .child_by_field_name("name")
8913 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8914 {
8915 return CallableLinkage::Internal;
8916 }
8917 enclosed_by_class = true;
8918 }
8919 if matches!(node.kind(), "function_definition" | "lambda_expression") {
8920 return CallableLinkage::Internal;
8921 }
8922 current = ancestry.parent(node);
8923 }
8924
8925 if enclosed_by_class {
8926 return CallableLinkage::External;
8927 }
8928
8929 let mut cursor = declaration.walk();
8930 if declaration.named_children(&mut cursor).any(|child| {
8931 child.kind() == "storage_class_specifier"
8932 && normalize_cpp_whitespace(node_text(child, source)) == "static"
8933 }) {
8934 CallableLinkage::Internal
8935 } else {
8936 CallableLinkage::External
8937 }
8938}
8939
8940fn cpp_callable_return_type_text<'tree>(
8941 function_declarator: Node<'tree>,
8942 source: &str,
8943 ancestry: &ParentIndex<'tree>,
8944) -> Option<String> {
8945 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
8946 return None;
8947 }
8948 if let Some((return_type, _)) =
8949 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
8950 {
8951 let text = normalize_cpp_whitespace(node_text(return_type, source));
8952 return (!text.is_empty()).then_some(text);
8953 }
8954 let mut cursor = function_declarator.walk();
8955 if let Some(trailing) = function_declarator
8956 .named_children(&mut cursor)
8957 .find(|child| child.kind() == "trailing_return_type")
8958 && let Some(type_descriptor) = trailing.named_child(0)
8959 {
8960 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
8961 if !text.is_empty() {
8962 return Some(text);
8963 }
8964 }
8965
8966 let mut current = function_declarator;
8967 let mut indirection = String::new();
8968 while let Some(parent) = ancestry.parent(current) {
8969 if matches!(
8970 parent.kind(),
8971 "function_definition" | "declaration" | "field_declaration"
8972 ) {
8973 let type_node = parent.child_by_field_name("type")?;
8974 if cpp_export_macro_token(node_text(type_node, source))
8975 && (0..parent.named_child_count()).any(|index| {
8976 parent
8977 .named_child(index)
8978 .is_some_and(|child| child.kind() == "ERROR")
8979 })
8980 {
8981 return None;
8986 }
8987 let base = normalize_cpp_whitespace(node_text(type_node, source));
8988 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
8989 }
8990 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
8991 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
8992 && parent.named_child_count() == 1
8993 && parent.named_child(0) == Some(current));
8994 if wraps_current_declarator {
8995 match parent.kind() {
8996 "pointer_declarator" => indirection.push('*'),
8997 "reference_declarator" => {
8998 let reference = parent
8999 .children(&mut parent.walk())
9000 .find(|child| !child.is_named())
9001 .map(|child| node_text(child, source))
9002 .unwrap_or("&");
9003 indirection.push_str(reference);
9004 }
9005 "init_declarator" | "parenthesized_declarator" => {}
9006 _ => return None,
9007 }
9008 current = parent;
9009 continue;
9010 }
9011 return None;
9012 }
9013 None
9014}
9015
9016fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
9017 let mut required = 0;
9018 let mut total = 0;
9019 let mut repeated = false;
9020 let mut cursor = parameters_node.walk();
9021 for child in parameters_node.children(&mut cursor) {
9022 match child.kind() {
9023 "parameter_declaration" => {
9024 if cpp_parameter_is_explicit_object(child, source) {
9025 continue;
9026 }
9027 if child.child_by_field_name("declarator").is_none()
9028 && child
9029 .child_by_field_name("type")
9030 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
9031 {
9032 continue;
9033 }
9034 required += 1;
9035 total += 1;
9036 }
9037 "optional_parameter_declaration" => total += 1,
9038 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
9039 repeated = true;
9040 }
9041 _ => {}
9042 }
9043 }
9044 CallableArity::new(required, total, repeated)
9045}
9046
9047fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
9048 parameter
9049 .child_by_field_name("type")
9050 .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
9051 .and_then(|type_node| type_node.child_by_field_name("constraint"))
9052 .is_some_and(|constraint| {
9053 constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
9054 })
9055}
9056
9057#[derive(Clone, Copy)]
9065enum CppParameterSlot<'tree> {
9066 Declared(Node<'tree>),
9067 Ellipsis,
9068}
9069
9070fn cpp_callable_parameter_slots<'tree>(
9071 parameters_node: Node<'tree>,
9072 source: &str,
9073) -> Vec<CppParameterSlot<'tree>> {
9074 let mut slots = Vec::new();
9075 let mut cursor = parameters_node.walk();
9076 for parameter in parameters_node.children(&mut cursor) {
9077 match parameter.kind() {
9078 "parameter_declaration" | "optional_parameter_declaration" => {
9079 if cpp_parameter_is_explicit_object(parameter, source)
9080 || (parameter.child_by_field_name("declarator").is_none()
9081 && parameter
9082 .child_by_field_name("type")
9083 .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
9084 {
9085 continue;
9086 }
9087 slots.push(CppParameterSlot::Declared(parameter));
9088 }
9089 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
9090 slots.push(CppParameterSlot::Ellipsis);
9091 }
9092 _ => {}
9093 }
9094 }
9095 slots
9096}
9097
9098fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
9099 cpp_callable_parameter_slots(parameters_node, source)
9100 .into_iter()
9101 .map(|slot| match slot {
9102 CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
9103 CppParameterSlot::Ellipsis => "...".to_string(),
9104 })
9105 .collect()
9106}
9107
9108#[derive(Debug, Clone, PartialEq, Eq)]
9114pub enum CppParameterType {
9115 Structured(StructuredTypeIdentity),
9118 Ellipsis,
9120 Unstructured,
9123}
9124
9125pub fn cpp_callable_parameter_type_identities<'tree>(
9131 function_declarator: Node<'tree>,
9132 source: &str,
9133 ancestry: &ParentIndex<'tree>,
9134) -> Vec<CppParameterType> {
9135 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
9136 return Vec::new();
9137 };
9138 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
9139 cpp_callable_parameter_slots(parameters_node, source)
9140 .into_iter()
9141 .map(|slot| match slot {
9142 CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
9143 CppParameterSlot::Declared(parameter) => {
9144 cpp_parameter_type_identity(parameter, source, &lexical_scope)
9145 .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
9146 }
9147 })
9148 .collect()
9149}
9150
9151fn cpp_parameter_type_identity(
9152 parameter: Node<'_>,
9153 source: &str,
9154 lexical_scope: &[String],
9155) -> Option<StructuredTypeIdentity> {
9156 let type_node = parameter.child_by_field_name("type")?;
9157 let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
9158 if let Some(declarator) = cpp_parameter_declarator(parameter) {
9159 for wrapper in cpp_structured_declarator_wrappers(declarator)
9160 .into_iter()
9161 .rev()
9162 {
9163 identity = cpp_wrap_structured_type(identity, wrapper)?;
9164 }
9165 }
9166 Some(identity)
9167}
9168
9169#[derive(Debug, Clone, PartialEq, Eq)]
9182pub enum CppComparableSlot {
9183 Shape(CppComparableParameter),
9185 Ellipsis,
9187 Unstructured,
9190}
9191
9192#[derive(Debug, Clone, PartialEq, Eq)]
9205pub struct CppComparableParameter {
9206 nodes: Vec<CppComparableNode>,
9207 root: usize,
9208}
9209
9210#[derive(Debug, Clone, PartialEq, Eq)]
9218pub enum CppComparableNode {
9219 Named {
9220 name: StructuredTypeName,
9221 primitive: bool,
9222 konst: bool,
9223 volatil: bool,
9224 },
9225 Pointer {
9226 inner: usize,
9227 konst: bool,
9228 volatil: bool,
9229 },
9230 Reference {
9231 inner: usize,
9232 },
9233 Array {
9234 inner: usize,
9235 },
9236 Generic {
9237 base: usize,
9238 arguments: Vec<usize>,
9239 },
9240}
9241
9242impl CppComparableParameter {
9243 pub fn root(&self) -> usize {
9244 self.root
9245 }
9246
9247 pub fn node(&self, index: usize) -> &CppComparableNode {
9248 &self.nodes[index]
9249 }
9250
9251 fn adjust_parameter_top_level(&mut self) {
9262 let root = self.root;
9263 match &mut self.nodes[root] {
9264 CppComparableNode::Named { konst, volatil, .. }
9265 | CppComparableNode::Pointer { konst, volatil, .. } => {
9266 *konst = false;
9267 *volatil = false;
9268 }
9269 CppComparableNode::Array { inner } => {
9270 let inner = *inner;
9271 self.nodes[root] = CppComparableNode::Pointer {
9272 inner,
9273 konst: false,
9274 volatil: false,
9275 };
9276 }
9277 CppComparableNode::Generic { base, .. } => {
9278 let base = *base;
9279 let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
9280 unreachable!("a comparable generic's base is always a named leaf");
9281 };
9282 *konst = false;
9283 *volatil = false;
9284 }
9285 CppComparableNode::Reference { .. } => {}
9286 }
9287 }
9288}
9289
9290pub fn cpp_comparable_parameter_shapes<'tree>(
9297 function_declarator: Node<'tree>,
9298 source: &str,
9299 ancestry: &ParentIndex<'tree>,
9300) -> Vec<CppComparableSlot> {
9301 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
9302 return Vec::new();
9303 };
9304 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
9305 cpp_callable_parameter_slots(parameters_node, source)
9306 .into_iter()
9307 .map(|slot| match slot {
9308 CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
9309 CppParameterSlot::Declared(parameter) => {
9310 cpp_comparable_parameter(parameter, source, &lexical_scope)
9311 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
9312 }
9313 })
9314 .collect()
9315}
9316
9317fn cpp_comparable_parameter(
9318 parameter: Node<'_>,
9319 source: &str,
9320 lexical_scope: &[String],
9321) -> Option<CppComparableParameter> {
9322 let type_node = parameter.child_by_field_name("type")?;
9323 let levels = match cpp_parameter_declarator(parameter) {
9324 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
9325 None => Vec::new(),
9326 };
9327 let mut shape = cpp_comparable_type_shape(
9328 type_node,
9329 cpp_cv_qualifiers(parameter, source),
9330 levels,
9331 source,
9332 lexical_scope,
9333 )?;
9334 shape.adjust_parameter_top_level();
9335 Some(shape)
9336}
9337
9338fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
9349 let mut qualifiers = CppCvQualifiers::default();
9350 let mut cursor = node.walk();
9351 for child in node.named_children(&mut cursor) {
9352 if child.kind() != "type_qualifier" {
9353 continue;
9354 }
9355 match node_text(child, source) {
9356 "const" => qualifiers.konst = true,
9357 "volatile" => qualifiers.volatil = true,
9358 _ => {}
9359 }
9360 }
9361 qualifiers
9362}
9363
9364#[derive(Clone, Copy, Default)]
9365struct CppCvQualifiers {
9366 konst: bool,
9367 volatil: bool,
9368}
9369
9370impl CppCvQualifiers {
9371 fn union(self, other: Self) -> Self {
9372 Self {
9373 konst: self.konst || other.konst,
9374 volatil: self.volatil || other.volatil,
9375 }
9376 }
9377}
9378
9379#[derive(Clone, Copy)]
9381enum CppComparableLevel {
9382 Pointer { konst: bool, volatil: bool },
9383 Reference,
9384 Array,
9385}
9386
9387fn cpp_comparable_declarator_levels(
9400 declarator: Node<'_>,
9401 source: &str,
9402) -> Option<Vec<CppComparableLevel>> {
9403 let mut levels = Vec::new();
9404 let mut current = declarator;
9405 loop {
9406 match current.kind() {
9407 "pointer_declarator" | "abstract_pointer_declarator" => {
9408 let qualifiers = cpp_cv_qualifiers(current, source);
9409 levels.push(CppComparableLevel::Pointer {
9410 konst: qualifiers.konst,
9411 volatil: qualifiers.volatil,
9412 });
9413 }
9414 "reference_declarator" | "abstract_reference_declarator" => {
9415 levels.push(CppComparableLevel::Reference);
9416 }
9417 "array_declarator" | "abstract_array_declarator" => {
9418 levels.push(CppComparableLevel::Array);
9419 }
9420 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
9421 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
9422 _ => return None,
9423 }
9424 let Some(next) = cpp_nested_declarator(current) else {
9425 return Some(levels);
9426 };
9427 current = next;
9428 }
9429}
9430
9431fn cpp_comparable_type_shape(
9438 type_node: Node<'_>,
9439 qualifiers: CppCvQualifiers,
9440 levels: Vec<CppComparableLevel>,
9441 source: &str,
9442 lexical_scope: &[String],
9443) -> Option<CppComparableParameter> {
9444 enum Work<'tree> {
9445 Visit {
9446 node: Node<'tree>,
9447 qualifiers: CppCvQualifiers,
9448 },
9449 ApplyLevels(Vec<CppComparableLevel>),
9450 BuildGeneric {
9451 argument_count: usize,
9452 },
9453 }
9454
9455 let mut nodes: Vec<CppComparableNode> = Vec::new();
9456 let mut values: Vec<usize> = Vec::new();
9457 let mut work = vec![
9458 Work::ApplyLevels(levels),
9459 Work::Visit {
9460 node: type_node,
9461 qualifiers,
9462 },
9463 ];
9464 while let Some(next) = work.pop() {
9465 match next {
9466 Work::Visit { node, qualifiers } => match node.kind() {
9467 "type_descriptor" => {
9468 let inner_type = node
9469 .child_by_field_name("type")
9470 .or_else(|| node.named_child(0))?;
9471 let mut cursor = node.walk();
9472 let declarator = node.child_by_field_name("declarator").or_else(|| {
9473 node.named_children(&mut cursor).find(|child| {
9474 child.id() != inner_type.id() && child.kind() != "type_qualifier"
9475 })
9476 });
9477 let levels = match declarator {
9478 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
9479 None => Vec::new(),
9480 };
9481 work.push(Work::ApplyLevels(levels));
9482 work.push(Work::Visit {
9483 node: inner_type,
9484 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
9485 });
9486 }
9487 "sized_type_specifier" => {
9488 let name = StructuredTypeName::new(
9493 vec![normalize_cpp_whitespace(node_text(node, source))],
9494 lexical_scope.to_vec(),
9495 false,
9496 )?;
9497 values.push(cpp_push_comparable_node(
9498 &mut nodes,
9499 CppComparableNode::Named {
9500 name,
9501 primitive: true,
9502 konst: qualifiers.konst,
9503 volatil: qualifiers.volatil,
9504 },
9505 ));
9506 }
9507 "qualified_identifier"
9508 | "scoped_identifier"
9509 | "scoped_type_identifier"
9510 | "type_identifier"
9511 | "field_identifier"
9512 | "identifier"
9513 | "namespace_identifier"
9514 | "primitive_type"
9515 | "template_type" => {
9516 let name = cpp_structured_named_type(node, source, lexical_scope)?;
9517 values.push(cpp_push_comparable_node(
9518 &mut nodes,
9519 CppComparableNode::Named {
9520 name,
9521 primitive: node.kind() == "primitive_type",
9522 konst: qualifiers.konst,
9523 volatil: qualifiers.volatil,
9524 },
9525 ));
9526 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
9527 let mut cursor = arguments_node.walk();
9528 let arguments = arguments_node
9529 .named_children(&mut cursor)
9530 .filter(|child| !child.is_extra() && child.kind() != "comment")
9531 .collect::<Vec<_>>();
9532 work.push(Work::BuildGeneric {
9533 argument_count: arguments.len(),
9534 });
9535 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
9536 node: argument,
9537 qualifiers: CppCvQualifiers::default(),
9538 }));
9539 }
9540 }
9541 _ => {
9542 let inner = node.child_by_field_name("type").or_else(|| {
9543 (node.named_child_count() == 1)
9544 .then(|| node.named_child(0))
9545 .flatten()
9546 })?;
9547 work.push(Work::Visit {
9548 node: inner,
9549 qualifiers,
9550 });
9551 }
9552 },
9553 Work::ApplyLevels(levels) => {
9554 let mut root = values.pop()?;
9555 for level in levels {
9556 let node = match level {
9557 CppComparableLevel::Pointer { konst, volatil } => {
9558 CppComparableNode::Pointer {
9559 inner: root,
9560 konst,
9561 volatil,
9562 }
9563 }
9564 CppComparableLevel::Reference => {
9565 CppComparableNode::Reference { inner: root }
9566 }
9567 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
9568 };
9569 root = cpp_push_comparable_node(&mut nodes, node);
9570 }
9571 values.push(root);
9572 }
9573 Work::BuildGeneric { argument_count } => {
9574 let value_count = argument_count.checked_add(1)?;
9575 let start = values.len().checked_sub(value_count)?;
9576 let mut built = values.split_off(start);
9577 let base = built.remove(0);
9578 values.push(cpp_push_comparable_node(
9579 &mut nodes,
9580 CppComparableNode::Generic {
9581 base,
9582 arguments: built,
9583 },
9584 ));
9585 }
9586 }
9587 }
9588 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
9589 debug_assert_eq!(
9590 root,
9591 nodes.len().saturating_sub(1),
9592 "comparable nodes are appended in post-order, so the root is the last one"
9593 );
9594 Some(CppComparableParameter { nodes, root })
9595}
9596
9597fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
9598 nodes.push(node);
9599 nodes.len() - 1
9600}
9601
9602fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
9608 let mut current = node;
9609 loop {
9610 match current.kind() {
9611 "template_type" => return current.child_by_field_name("arguments"),
9612 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
9613 current = current.child_by_field_name("name")?;
9614 }
9615 _ => return None,
9616 }
9617 }
9618}
9619
9620pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
9626 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
9627 loop {
9628 if matches!(
9629 current.kind(),
9630 "declaration" | "field_declaration" | "function_definition"
9631 ) && let Some(declarator) = current
9632 .child_by_field_name("declarator")
9633 .and_then(extract_function_declarator)
9634 {
9635 return Some(declarator);
9636 }
9637 current = current.parent()?;
9638 }
9639}
9640
9641fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
9642 let mut labels = Vec::new();
9643 let mut cursor = parameters_node.walk();
9644 for child in parameters_node.children(&mut cursor) {
9645 match child.kind() {
9646 "parameter_declaration" | "optional_parameter_declaration" => {
9647 if let Some(name_node) = child
9648 .child_by_field_name("declarator")
9649 .and_then(cpp_declarator_label_node)
9650 {
9651 labels.push(name_node);
9652 } else {
9653 labels.push(child);
9654 }
9655 }
9656 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
9657 labels.push(child);
9658 }
9659 _ => {}
9660 }
9661 }
9662 labels
9663}
9664
9665fn cpp_signature_search_start<'tree>(
9666 signature: &str,
9667 function_declarator: Node<'tree>,
9668 source: &str,
9669 ancestry: &ParentIndex<'tree>,
9670) -> usize {
9671 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
9672 return 0;
9673 };
9674 let raw = node_text(enclosing, source);
9675 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
9676 let offset = function_declarator
9677 .start_byte()
9678 .saturating_sub(enclosing.start_byte())
9679 .saturating_sub(leading_trim_bytes);
9680 offset.min(signature.len())
9681}
9682
9683fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
9684 match node.kind() {
9685 "identifier" | "field_identifier" => Some(node),
9686 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
9687 .child_by_field_name("declarator")
9688 .or_else(|| last_named_child(node))
9689 .and_then(cpp_declarator_label_node),
9690 "array_declarator" => node
9691 .child_by_field_name("declarator")
9692 .and_then(cpp_declarator_label_node),
9693 "function_declarator" => node
9694 .child_by_field_name("declarator")
9695 .or_else(|| node.child_by_field_name("name"))
9696 .or_else(|| last_named_child(node))
9697 .and_then(cpp_declarator_label_node),
9698 _ => None,
9699 }
9700}
9701
9702fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
9703 let base_type = parameter
9704 .child_by_field_name("type")
9705 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
9706 .unwrap_or_default();
9707 let declarator = cpp_parameter_declarator(parameter);
9708 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
9715 let mut cursor = parameter.walk();
9716 let qualifiers = parameter
9717 .named_children(&mut cursor)
9718 .filter(|child| child.kind() == "type_qualifier")
9719 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
9720 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
9721 .collect::<Vec<_>>()
9722 .join(" ");
9723 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
9724 (true, _) => base_type,
9725 (_, true) => qualifiers,
9726 (false, false) => format!("{qualifiers} {base_type}"),
9727 };
9728 let declarator_suffix = declarator
9729 .map(|node| cpp_declarator_suffix_without_name(node, source))
9730 .unwrap_or_default();
9731
9732 let combined = if type_text.is_empty() {
9733 declarator_suffix
9734 } else if declarator_suffix.is_empty() {
9735 type_text
9736 } else {
9737 format!("{type_text} {declarator_suffix}")
9738 };
9739 normalize_cpp_type_text(&combined)
9740}
9741
9742fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
9743 parameter.child_by_field_name("declarator").or_else(|| {
9744 let mut cursor = parameter.walk();
9750 parameter
9751 .named_children(&mut cursor)
9752 .find(|child| is_cpp_abstract_declarator(child.kind()))
9753 })
9754}
9755
9756pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
9759 let mut current = Some(declarator);
9760 while let Some(node) = current {
9761 if matches!(
9762 node.kind(),
9763 "pointer_declarator"
9764 | "abstract_pointer_declarator"
9765 | "reference_declarator"
9766 | "abstract_reference_declarator"
9767 | "array_declarator"
9768 | "abstract_array_declarator"
9769 | "function_declarator"
9770 | "abstract_function_declarator"
9771 ) {
9772 return true;
9773 }
9774 current = cpp_nested_declarator(node);
9775 }
9776 false
9777}
9778
9779fn is_cpp_abstract_declarator(kind: &str) -> bool {
9780 matches!(
9781 kind,
9782 "abstract_pointer_declarator"
9783 | "abstract_reference_declarator"
9784 | "abstract_array_declarator"
9785 | "abstract_function_declarator"
9786 | "abstract_parenthesized_declarator"
9787 )
9788}
9789
9790fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
9791 node.child_by_field_name("declarator").or_else(|| {
9792 if is_cpp_abstract_declarator(node.kind()) {
9793 let mut cursor = node.walk();
9794 node.named_children(&mut cursor)
9795 .find(|child| is_cpp_abstract_declarator(child.kind()))
9796 } else {
9797 last_named_child(node)
9801 }
9802 })
9803}
9804
9805fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
9806 match node.kind() {
9807 "identifier" | "field_identifier" => String::new(),
9808 "pointer_declarator" | "abstract_pointer_declarator" => {
9809 let inner = cpp_nested_declarator(node)
9810 .map(|child| cpp_declarator_suffix_without_name(child, source))
9811 .unwrap_or_default();
9812 format!("*{inner}")
9813 }
9814 "reference_declarator" | "abstract_reference_declarator" => {
9815 let inner = cpp_nested_declarator(node)
9816 .map(|child| cpp_declarator_suffix_without_name(child, source))
9817 .unwrap_or_default();
9818 let reference = node
9819 .children(&mut node.walk())
9820 .find(|child| matches!(child.kind(), "&" | "&&"))
9821 .map(|child| node_text(child, source))
9822 .unwrap_or("&");
9823 format!("{reference}{inner}")
9824 }
9825 "array_declarator" | "abstract_array_declarator" => {
9826 let inner = cpp_nested_declarator(node)
9827 .map(|child| cpp_declarator_suffix_without_name(child, source))
9828 .unwrap_or_default();
9829 let size = node
9830 .child_by_field_name("size")
9831 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
9832 .unwrap_or_default();
9833 format!("{inner}[{size}]")
9834 }
9835 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
9836 let inner = cpp_nested_declarator(node);
9837 inner
9838 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
9839 .unwrap_or_default()
9840 }
9841 "function_declarator" | "abstract_function_declarator" => {
9842 let inner = cpp_nested_declarator(node)
9843 .map(|child| cpp_declarator_suffix_without_name(child, source))
9844 .unwrap_or_default();
9845 let params = node
9846 .child_by_field_name("parameters")
9847 .map(|child| cpp_parameter_signature(child, source))
9848 .unwrap_or_else(|| "()".to_string());
9849 format!("{inner}{params}")
9850 }
9851 _ => {
9852 let text = normalize_cpp_whitespace(node_text(node, source));
9853 let name = extract_declarator_name(node, source);
9854 if name.is_empty() {
9855 text
9856 } else {
9857 text.replace(&name, "").trim().to_string()
9858 }
9859 }
9860 }
9861}
9862
9863fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
9864 collapse_cpp_whitespace(
9865 suffix
9866 .trim()
9867 .trim_start_matches("->")
9868 .trim_start_matches('{')
9869 .trim_end_matches(';'),
9870 )
9871}
9872
9873pub fn normalize_cpp_whitespace(value: &str) -> String {
9874 collapse_cpp_whitespace(value)
9875}
9876
9877fn normalize_cpp_type_text(value: &str) -> String {
9878 collapse_cpp_whitespace(value)
9879 .replace(", ", ",")
9880 .replace(" <", "<")
9881 .replace("< ", "<")
9882 .replace(" >", ">")
9883}
9884
9885fn collapse_cpp_whitespace(value: &str) -> String {
9886 let mut result = String::new();
9887 let mut prev_space = false;
9888 for ch in value.chars() {
9889 if ch.is_whitespace() {
9890 if !prev_space {
9891 result.push(' ');
9892 }
9893 prev_space = true;
9894 } else {
9895 result.push(ch);
9896 prev_space = false;
9897 }
9898 }
9899 result.trim().to_string()
9900}
9901
9902pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
9903 node_source_text(node, source)
9904}
9905
9906pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
9907 walk_named_tree_preorder(node, true, |node| {
9908 match node.kind() {
9909 "type_identifier" | "identifier" | "qualified_identifier" => {
9910 let text = node_text(node, source).trim();
9911 if !text.is_empty() {
9912 identifiers.insert(text.to_string());
9913 }
9914 }
9915 _ => {}
9916 }
9917 WalkControl::Continue
9918 });
9919}
9920
9921fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
9922 node.child_by_field_name("body").or_else(|| {
9923 let mut cursor = node.walk();
9924 node.named_children(&mut cursor).find(|child| {
9925 matches!(
9926 child.kind(),
9927 "declaration_list" | "field_declaration_list" | "enumerator_list"
9928 )
9929 })
9930 })
9931}
9932
9933fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
9944 if !matches!(
9945 node.kind(),
9946 "class_specifier" | "struct_specifier" | "union_specifier"
9947 ) {
9948 return None;
9949 }
9950 let body = cpp_body_node(node)?;
9951 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
9952 return None;
9953 }
9954 let open = body.child(0)?;
9955 let close = body.child(body.child_count().checked_sub(1)?)?;
9956 if open.kind() != "{"
9957 || open.is_missing()
9958 || close.kind() != "}"
9959 || close.is_missing()
9960 || close.end_byte() != body.end_byte()
9961 || body.end_byte() > node.end_byte()
9962 || node
9963 .parent()
9964 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
9965 {
9966 return None;
9967 }
9968 Some(close)
9969}
9970
9971fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
9972 if node.kind() == "namespace_definition" {
9973 return true;
9974 }
9975 let mut cursor = node.walk();
9976 node.named_children(&mut cursor)
9977 .any(cpp_contains_namespace_definition)
9978}
9979
9980struct CppNestedNamespaceSentinel<'tree> {
9981 function: Node<'tree>,
9982 body: Node<'tree>,
9983 namespace_components: Vec<String>,
9984}
9985
9986#[derive(Debug, Clone)]
9996pub struct CppSentinelRecoveredOwner {
9997 pub range: Range,
9998 pub owner_name_start_byte: usize,
10002 pub namespace_component_count: usize,
10006 pub scope_components: Vec<String>,
10007}
10008
10009#[derive(Debug, Clone)]
10010pub struct CppSentinelRecoveredClass {
10011 pub namespace_range: Range,
10012 pub namespace_scope_components: Vec<String>,
10013 pub class_range: Range,
10014 pub scope_components: Vec<String>,
10016 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
10020}
10021
10022pub fn cpp_sentinel_recovered_scope_for_node(
10028 node: Node<'_>,
10029 source: &str,
10030 recovered_classes: &[CppSentinelRecoveredClass],
10031) -> Option<Vec<String>> {
10032 let contains =
10033 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
10034 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
10035 for recovered in recovered_classes {
10036 for owner in recovered
10037 .owner_ranges
10038 .iter()
10039 .filter(|owner| contains(owner.range))
10040 {
10041 let replace = best_owner.is_none_or(|existing| {
10042 owner.range.end_byte.saturating_sub(owner.range.start_byte)
10043 < existing
10044 .range
10045 .end_byte
10046 .saturating_sub(existing.range.start_byte)
10047 });
10048 if replace {
10049 best_owner = Some(owner);
10050 }
10051 }
10052 }
10053 if let Some(owner) = best_owner {
10054 let mut scope = owner.scope_components.clone();
10055 if node.start_byte() < owner.owner_name_start_byte {
10056 scope.truncate(owner.namespace_component_count);
10057 }
10058 return Some(scope);
10059 }
10060
10061 let class = recovered_classes
10062 .iter()
10063 .filter(|recovered| contains(recovered.class_range))
10064 .min_by_key(|recovered| {
10065 recovered
10066 .class_range
10067 .end_byte
10068 .saturating_sub(recovered.class_range.start_byte)
10069 });
10070 let class_scope = class.is_some();
10071 let mut scope = if let Some(class) = class {
10072 class.scope_components.clone()
10073 } else {
10074 let namespace = recovered_classes
10075 .iter()
10076 .filter(|recovered| contains(recovered.namespace_range))
10077 .min_by_key(|recovered| {
10078 recovered
10079 .namespace_range
10080 .end_byte
10081 .saturating_sub(recovered.namespace_range.start_byte)
10082 })?;
10083 let mut scope = namespace.namespace_scope_components.clone();
10084 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
10085 let common_prefix = scope
10086 .iter()
10087 .zip(&parser_namespace)
10088 .take_while(|(recovered, parser)| recovered == parser)
10089 .count();
10090 scope.extend(parser_namespace.into_iter().skip(common_prefix));
10091 scope
10092 };
10093 if class_scope {
10094 let mut ancestor_components = Vec::new();
10095 let mut ancestor = node.parent();
10096 while let Some(current) = ancestor {
10097 if matches!(
10098 current.kind(),
10099 "class_specifier" | "struct_specifier" | "union_specifier"
10100 ) && let Some(name) = current.child_by_field_name("name")
10101 && let Some(name_components) = cpp_name_components(name, source)
10102 {
10103 ancestor_components.push(
10104 name_components
10105 .into_iter()
10106 .map(|component| component.name)
10107 .collect::<Vec<_>>(),
10108 );
10109 }
10110 ancestor = current.parent();
10111 }
10112 ancestor_components.reverse();
10113 let base_len = scope.len();
10114 for component in ancestor_components.into_iter().flatten() {
10115 if scope.len() >= base_len && scope.last() == Some(&component) {
10116 continue;
10117 }
10118 scope.push(component);
10119 }
10120 }
10121 Some(scope)
10122}
10123
10124struct CppSentinelFragmentedClassTail<'tree> {
10125 class_node: Node<'tree>,
10126 template_node: Option<Node<'tree>>,
10127 name: String,
10128 raw_supertypes: Option<Vec<String>>,
10129 fragmented: FragmentedExportBody,
10130 consumed_start: usize,
10131}
10132
10133struct CppSentinelFragmentedClassErrorPrefix<'tree> {
10134 name: String,
10135 open: Node<'tree>,
10136 raw_supertypes: Option<Vec<String>>,
10137}
10138
10139struct CppSentinelDirectBodyClassRegion {
10140 namespace_components: Vec<String>,
10141 class_start: usize,
10142 class_start_line: usize,
10143 class_close_end: usize,
10144 class_close_line: usize,
10145 name: String,
10146}
10147
10148fn cpp_sentinel_body_class_candidate<'tree>(
10149 child: Node<'tree>,
10150) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
10151 if matches!(
10152 child.kind(),
10153 "class_specifier" | "struct_specifier" | "union_specifier"
10154 ) {
10155 return Some((child, None));
10156 }
10157 if child.kind() != "template_declaration" {
10158 if child.kind() == "declaration" {
10159 return Some((first_class_like_child(child)?, None));
10160 }
10161 return None;
10162 }
10163 let mut cursor = child.walk();
10164 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
10165 if matches!(
10166 candidate.kind(),
10167 "class_specifier" | "struct_specifier" | "union_specifier"
10168 ) {
10169 Some(candidate)
10170 } else if candidate.kind() == "declaration" {
10171 first_class_like_child(candidate)
10172 } else {
10173 None
10174 }
10175 })?;
10176 Some((class_node, Some(child)))
10177}
10178
10179fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
10185 node: Node<'tree>,
10186 source: &str,
10187) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
10188 let name = malformed_class_error_owner_name(node, source)?;
10189 let mut cursor = node.walk();
10190 let children = node.children(&mut cursor).collect::<Vec<_>>();
10191 let keyword = children.first()?;
10192 let open_index = children.iter().position(|child| child.kind() == "{")?;
10193 if children[open_index + 1..]
10194 .iter()
10195 .any(|child| child.kind() == "}")
10196 {
10197 return None;
10198 }
10199 let raw_supertypes =
10200 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
10201 Some(CppSentinelFragmentedClassErrorPrefix {
10202 name,
10203 open: children[open_index],
10204 raw_supertypes,
10205 })
10206}
10207
10208fn cpp_sentinel_direct_body_class_candidate<'tree>(
10209 child: Node<'tree>,
10210) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
10211 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
10212 return Some(candidate);
10213 }
10214 if child.kind() != "template_declaration" {
10215 return None;
10216 }
10217 let mut cursor = child.walk();
10218 let wrapper = child
10219 .named_children(&mut cursor)
10220 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
10221 Some((first_class_like_child(wrapper)?, Some(child)))
10222}
10223
10224fn cpp_sentinel_direct_namespace_components(
10225 function: Node<'_>,
10226 body: Node<'_>,
10227 source: &str,
10228) -> Option<Vec<String>> {
10229 let mut cursor = function.walk();
10230 let children = function
10231 .named_children(&mut cursor)
10232 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
10233 .collect::<Vec<_>>();
10234 let sentinel_index = children.iter().rposition(|child| {
10235 direct_identifier_name(*child, source)
10236 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
10237 })?;
10238 let mut identifiers = Vec::new();
10239 let mut stack = children[sentinel_index + 1..]
10240 .iter()
10241 .rev()
10242 .copied()
10243 .collect::<Vec<_>>();
10244 while let Some(current) = stack.pop() {
10245 if let Some(name) = direct_identifier_name(current, source) {
10246 identifiers.push(name);
10247 continue;
10248 }
10249 let mut cursor = current.walk();
10250 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
10251 stack.extend(children.into_iter().rev());
10252 }
10253 let [keyword, namespace] = identifiers.as_slice() else {
10254 return None;
10255 };
10256 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
10257 .then(|| vec![namespace.clone()])
10258}
10259
10260fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
10261 let mut sibling = class_semicolon.next_named_sibling();
10262 let namespace_close = loop {
10263 let Some(current) = sibling else {
10264 return false;
10265 };
10266 sibling = current.next_named_sibling();
10267 if current.kind() != "comment" {
10268 break current;
10269 }
10270 };
10271 if !cpp_is_stray_close_brace(namespace_close, source) {
10272 return false;
10273 }
10274 loop {
10275 let Some(current) = sibling else {
10276 return false;
10277 };
10278 sibling = current.next_named_sibling();
10279 if current.kind() == "comment" {
10280 continue;
10281 }
10282 return direct_identifier_name(current, source)
10283 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
10284 }
10285}
10286
10287fn cpp_sentinel_macro_body_class_region<'tree>(
10288 node: Node<'tree>,
10289 source: &str,
10290 ancestry: &ParentIndex<'tree>,
10291) -> Option<CppSentinelDirectBodyClassRegion> {
10292 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
10293 return None;
10294 };
10295 if node.kind() != "function_definition" || !node.has_error() {
10296 return None;
10297 }
10298 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
10299 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
10300 let mut cursor = body.walk();
10301 let candidates = body
10302 .named_children(&mut cursor)
10303 .filter_map(cpp_sentinel_direct_body_class_candidate)
10304 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
10305 .collect::<Vec<_>>();
10306 let [(class_node, template_node)] = candidates.as_slice() else {
10307 return None;
10308 };
10309 let original_body = cpp_body_node(*class_node)?;
10310 let name = class_like_name(*class_node, source, ancestry)?;
10311 if name.is_empty() || cpp_export_macro_token(&name) {
10312 return None;
10313 }
10314
10315 let mut sibling = node.next_named_sibling();
10316 let (class_close_start, class_close_end, class_close_line) = loop {
10317 let current = sibling?;
10318 let next = current.next_named_sibling();
10319 if cpp_is_stray_close_brace(current, source)
10320 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
10321 {
10322 let semicolon = next.expect("checked above");
10323 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
10324 return None;
10325 }
10326 break (
10327 current.start_byte(),
10328 semicolon.end_byte(),
10329 semicolon.end_position().row + 1,
10330 );
10331 }
10332 sibling = next;
10333 };
10334 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
10335 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
10336 let root = tree.root_node();
10337 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
10338 let reparsed_ancestry = ParentIndex::new(root);
10341 let reparsed =
10342 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
10343 if reparsed.name != name
10344 || reparsed.declaration_node.start_byte() != class_node.start_byte()
10345 || reparsed.body.start_byte() != original_body.start_byte()
10346 || class_close_start <= reparsed.body.end_byte()
10347 || class_close_end <= class_node.end_byte()
10348 {
10349 return None;
10350 }
10351 Some(CppSentinelDirectBodyClassRegion {
10352 namespace_components,
10353 class_start: reparse_start,
10354 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
10355 node.start_position().row + 1
10356 }),
10357 class_close_end,
10358 class_close_line,
10359 name,
10360 })
10361}
10362
10363fn cpp_nested_namespace_sentinel<'tree>(
10375 node: Node<'tree>,
10376 source: &str,
10377 ancestry: &ParentIndex<'tree>,
10378) -> Option<CppNestedNamespaceSentinel<'tree>> {
10379 if !node.has_error() {
10380 return None;
10381 }
10382
10383 let (function, mut namespace_components) = if node.kind() == "ERROR" {
10384 let mut cursor = node.walk();
10385 let functions = node
10386 .named_children(&mut cursor)
10387 .filter(|child| child.kind() == "function_definition")
10388 .collect::<Vec<_>>();
10389 let [function] = functions.as_slice() else {
10390 return None;
10391 };
10392 if !function.has_error() {
10393 return None;
10394 }
10395 let mut cursor = node.walk();
10396 let children = node.children(&mut cursor).collect::<Vec<_>>();
10397 let function_index = children
10398 .iter()
10399 .position(|child| same_node(*child, *function))?;
10400 let [outer_keyword, outer_name, outer_open] =
10401 children.get(function_index.checked_sub(3)?..function_index)?
10402 else {
10403 return None;
10404 };
10405 if outer_keyword.kind() != "namespace"
10406 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
10407 || outer_open.kind() != "{"
10408 {
10409 return None;
10410 }
10411 (
10412 *function,
10413 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
10414 )
10415 } else if node.kind() == "function_definition" {
10416 let declaration_list = node.parent()?;
10417 let namespace = declaration_list.parent()?;
10418 if declaration_list.kind() != "declaration_list"
10419 || namespace.kind() != "namespace_definition"
10420 || namespace.child_by_field_name("body") != Some(declaration_list)
10421 {
10422 return None;
10423 }
10424 (node, Vec::new())
10425 } else {
10426 return None;
10427 };
10428
10429 let mut cursor = function.walk();
10430 let named = function
10431 .named_children(&mut cursor)
10432 .filter(|child| child.kind() != "comment")
10433 .collect::<Vec<_>>();
10434 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
10435 return None;
10436 };
10437 if first_type.kind() != "type_identifier" {
10438 return None;
10439 }
10440 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
10441 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
10442 return None;
10443 }
10444 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
10445 return None;
10446 }
10447 let inner_keyword = inner_error.named_child(0)?;
10448 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
10449 return None;
10450 }
10451 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
10452 return None;
10453 }
10454 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
10455 if inner_name.is_empty() || body.kind() != "compound_statement" {
10456 return None;
10457 }
10458 namespace_components.push(inner_name);
10459
10460 let mut cursor = body.walk();
10461 let has_complete_class = body.named_children(&mut cursor).any(|child| {
10462 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
10463 cpp_body_node(class_node).is_some()
10464 && class_like_name(class_node, source, ancestry)
10465 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
10466 })
10467 });
10468 if !has_complete_class
10469 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
10470 {
10471 return None;
10472 }
10473
10474 Some(CppNestedNamespaceSentinel {
10475 function,
10476 body: *body,
10477 namespace_components,
10478 })
10479}
10480
10481fn cpp_root_namespace_sentinel<'tree>(
10490 node: Node<'tree>,
10491 source: &str,
10492 ancestry: &ParentIndex<'tree>,
10493) -> Option<CppNestedNamespaceSentinel<'tree>> {
10494 if node.kind() != "function_definition"
10495 || !node.has_error()
10496 || node.parent()?.kind() != "translation_unit"
10497 {
10498 return None;
10499 }
10500 let first_type = node.child_by_field_name("type")?;
10501 let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
10502 if first_type.kind() != "type_identifier"
10503 || sentinel.is_empty()
10504 || !cpp_export_macro_token(&sentinel)
10505 {
10506 return None;
10507 }
10508 let declarator = node.child_by_field_name("declarator")?;
10509 let body = node.child_by_field_name("body")?;
10510 if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
10511 return None;
10512 }
10513 let mut cursor = node.walk();
10514 let named = node
10515 .named_children(&mut cursor)
10516 .filter(|child| child.kind() != "comment")
10517 .collect::<Vec<_>>();
10518 let [named_type, named_declarator, named_body] = named.as_slice() else {
10519 return None;
10520 };
10521 if !same_node(*named_type, first_type)
10522 || !same_node(*named_declarator, declarator)
10523 || !same_node(*named_body, body)
10524 {
10525 return None;
10526 }
10527 let mut declarator_components = Vec::new();
10528 let mut valid_components = true;
10529 walk_named_tree_preorder(declarator, true, |component| {
10530 if !matches!(
10531 component.kind(),
10532 "identifier" | "namespace_identifier" | "type_identifier"
10533 ) {
10534 return WalkControl::Continue;
10535 }
10536 let Some(component) = canonical_cpp_qualified_component(component, source) else {
10537 valid_components = false;
10538 return WalkControl::Break;
10539 };
10540 declarator_components.push(component.name);
10541 WalkControl::SkipChildren
10542 });
10543 if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
10544 return None;
10545 }
10546 declarator_components.remove(0);
10547 let namespace_components = declarator_components;
10548 if namespace_components.is_empty()
10549 || namespace_components
10550 .iter()
10551 .any(|component| component.is_empty() || cpp_export_macro_token(component))
10552 {
10553 return None;
10554 }
10555
10556 let mut cursor = body.walk();
10557 let has_complete_class = body.named_children(&mut cursor).any(|child| {
10558 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
10559 cpp_body_node(class_node).is_some()
10560 && class_like_name(class_node, source, ancestry)
10561 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
10562 })
10563 });
10564 if !has_complete_class
10565 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
10566 {
10567 return None;
10568 }
10569
10570 Some(CppNestedNamespaceSentinel {
10571 function: node,
10572 body,
10573 namespace_components,
10574 })
10575}
10576
10577fn cpp_sentinel_fragmented_class_tail<'tree>(
10586 function: Node<'tree>,
10587 body: Node<'tree>,
10588 source: &str,
10589 ancestry: &ParentIndex<'tree>,
10590) -> Option<CppSentinelFragmentedClassTail<'tree>> {
10591 let mut cursor = body.walk();
10592 let candidates = body
10593 .named_children(&mut cursor)
10594 .filter_map(|child| {
10595 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
10596 let class_body = cpp_body_node(class_node)?;
10597 if !class_node.has_error() {
10598 return None;
10599 }
10600 let name = class_like_name(class_node, source, ancestry)?;
10601 let raw_supertypes =
10602 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
10603 .then(|| extract_cpp_supertypes(class_node, source));
10604 return Some((
10605 class_node,
10606 template_node,
10607 name,
10608 class_body,
10609 class_body.start_byte().checked_add(1)?,
10610 raw_supertypes,
10611 ));
10612 }
10613 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
10614 Some((
10615 child,
10616 None,
10617 prefix.name,
10618 prefix.open,
10619 prefix.open.end_byte(),
10620 prefix.raw_supertypes,
10621 ))
10622 })
10623 .collect::<Vec<_>>();
10624 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
10625 candidates.as_slice()
10626 else {
10627 return None;
10628 };
10629 if name.is_empty() || cpp_export_macro_token(name) {
10630 return None;
10631 }
10632
10633 let (close, semicolon) =
10634 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
10635
10636 let reparse_end = close.start_byte();
10637 if *reparse_start >= reparse_end {
10638 return None;
10639 }
10640 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
10641 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
10642 return None;
10643 }
10644 let class_range = Range {
10645 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
10646 end_byte: semicolon.end_byte(),
10647 start_line: template_node.map_or(class_node.start_position().row, |node| {
10648 node.start_position().row
10649 }) + 1,
10650 end_line: semicolon.end_position().row + 1,
10651 };
10652 Some(CppSentinelFragmentedClassTail {
10653 class_node: *class_node,
10654 template_node: *template_node,
10655 name: name.clone(),
10656 raw_supertypes: raw_supertypes.clone(),
10657 fragmented: FragmentedExportBody {
10658 reparse_start: *reparse_start,
10659 reparse_end,
10660 class_range,
10661 },
10662 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
10663 })
10664}
10665
10666pub fn cpp_sentinel_recovered_classes(
10675 root: Node<'_>,
10676 source: &str,
10677) -> Vec<CppSentinelRecoveredClass> {
10678 if !root.has_error() {
10679 return Vec::new();
10680 }
10681 let ancestry = ParentIndex::new(root);
10685 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
10686 let mut stack = vec![root];
10687 while let Some(current) = stack.pop() {
10688 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
10689 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
10690 {
10691 let namespace_components = cpp_sentinel_recovered_namespace_components(
10692 recovered.function,
10693 &recovered.namespace_components,
10694 source,
10695 );
10696 let fragmented = cpp_sentinel_fragmented_class_tail(
10697 recovered.function,
10698 recovered.body,
10699 source,
10700 &ancestry,
10701 );
10702 let mut class_candidates = Vec::new();
10703 let mut cursor = recovered.body.walk();
10704 for (class_node, template_node) in recovered
10705 .body
10706 .named_children(&mut cursor)
10707 .filter_map(cpp_sentinel_body_class_candidate)
10708 {
10709 let Some(name) = class_like_name(class_node, source, &ancestry) else {
10710 continue;
10711 };
10712 if name.is_empty() || cpp_export_macro_token(&name) {
10713 continue;
10714 }
10715 let is_fragmented = fragmented
10716 .as_ref()
10717 .is_some_and(|tail| same_node(tail.class_node, class_node));
10718 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
10719 continue;
10720 }
10721 let class_range = if is_fragmented {
10722 fragmented
10723 .as_ref()
10724 .map(|tail| tail.fragmented.class_range)
10725 .expect("fragmented class range is present when class matches")
10726 } else {
10727 cpp_declaration_range(template_node.unwrap_or(class_node))
10728 };
10729 class_candidates.push((class_range, name));
10730 }
10731 if let Some(fragmented) = fragmented
10732 .as_ref()
10733 .filter(|tail| tail.class_node.kind() == "ERROR")
10734 {
10735 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
10736 }
10737
10738 let mut owner_ranges =
10739 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
10740 cpp_sentinel_extend_unique_owner_ranges(
10741 &mut owner_ranges,
10742 cpp_sentinel_recovered_sibling_owner_ranges(
10743 recovered.function,
10744 &namespace_components,
10745 source,
10746 ),
10747 );
10748 for (class_range, name) in class_candidates {
10749 push_cpp_sentinel_recovered_class(
10750 &mut recovered_classes,
10751 cpp_declaration_range(recovered.body),
10752 &namespace_components,
10753 class_range,
10754 name,
10755 &owner_ranges,
10756 );
10757 }
10758
10759 if let Some(declaration_list) = recovered
10760 .function
10761 .parent()
10762 .filter(|parent| parent.kind() == "declaration_list")
10763 {
10764 let outer_namespace =
10765 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
10766 push_cpp_sentinel_sibling_classes(
10767 &mut recovered_classes,
10768 declaration_list,
10769 recovered.function,
10770 &outer_namespace,
10771 source,
10772 &ancestry,
10773 );
10774 }
10775 } else if let Some(region) =
10776 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
10777 {
10778 let namespace_components = cpp_sentinel_recovered_namespace_components(
10779 current,
10780 ®ion.namespace_components,
10781 source,
10782 );
10783 let owner_container = current
10784 .parent()
10785 .filter(|parent| parent.kind() == "declaration_list")
10786 .unwrap_or(current);
10787 let owner_ranges =
10788 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
10789 push_cpp_sentinel_recovered_class(
10790 &mut recovered_classes,
10791 cpp_declaration_range(owner_container),
10792 &namespace_components,
10793 Range {
10794 start_byte: region.class_start,
10795 end_byte: region.class_close_end,
10796 start_line: region.class_start_line,
10797 end_line: region.class_close_line,
10798 },
10799 region.name,
10800 &owner_ranges,
10801 );
10802 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
10803 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
10808 region;
10809 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
10810 continue;
10811 };
10812 let root = tree.root_node();
10813 let template_node = cpp_sentinel_reparsed_leading_template(root);
10814 let reparsed_ancestry = ParentIndex::new(root);
10816 let Some(reparsed_class) =
10817 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
10818 else {
10819 continue;
10820 };
10821 let class_node = reparsed_class.declaration_node;
10822 let name = reparsed_class.name;
10823 let namespace_components =
10824 cpp_sentinel_recovered_namespace_components(current, &[], source);
10825 let owner_container = current
10826 .parent()
10827 .filter(|parent| parent.kind() == "declaration_list")
10828 .unwrap_or(current);
10829 let mut owner_ranges =
10830 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
10831 cpp_sentinel_extend_unique_owner_ranges(
10832 &mut owner_ranges,
10833 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
10834 );
10835 push_cpp_sentinel_recovered_class(
10836 &mut recovered_classes,
10837 cpp_declaration_range(owner_container),
10838 &namespace_components,
10839 Range {
10840 start_byte: class_start,
10841 end_byte: close_end,
10842 start_line: class_node.start_position().row + 1,
10843 end_line: class_node.end_position().row + 1,
10844 },
10845 name,
10846 &owner_ranges,
10847 );
10848 if owner_container.kind() == "declaration_list" {
10849 push_cpp_sentinel_sibling_classes(
10850 &mut recovered_classes,
10851 owner_container,
10852 current,
10853 &namespace_components,
10854 source,
10855 &ancestry,
10856 );
10857 }
10858 }
10859
10860 let mut cursor = current.walk();
10861 stack.extend(current.named_children(&mut cursor));
10862 }
10863 let shadowed = recovered_classes
10869 .iter()
10870 .map(|candidate| {
10871 recovered_classes.iter().any(|container| {
10872 container.class_range.start_byte <= candidate.class_range.start_byte
10873 && container.class_range.end_byte >= candidate.class_range.end_byte
10874 && container.class_range != candidate.class_range
10875 && container.namespace_scope_components.len()
10876 > candidate.namespace_scope_components.len()
10877 && container
10878 .namespace_scope_components
10879 .starts_with(&candidate.namespace_scope_components)
10880 })
10881 })
10882 .collect::<Vec<_>>();
10883 let mut index = 0usize;
10884 recovered_classes.retain(|_| {
10885 let keep = !shadowed[index];
10886 index += 1;
10887 keep
10888 });
10889 recovered_classes
10890}
10891
10892fn push_cpp_sentinel_sibling_classes<'tree>(
10898 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
10899 declaration_list: Node<'tree>,
10900 sentinel_node: Node<'tree>,
10901 namespace_components: &[String],
10902 source: &str,
10903 ancestry: &ParentIndex<'tree>,
10904) {
10905 let owner_ranges =
10906 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
10907 let namespace_range = cpp_declaration_range(declaration_list);
10908 let mut cursor = declaration_list.walk();
10909 for (class_node, template_node) in declaration_list
10910 .named_children(&mut cursor)
10911 .filter(|child| !same_node(*child, sentinel_node))
10912 .filter_map(cpp_sentinel_body_class_candidate)
10913 {
10914 let Some(name) = class_like_name(class_node, source, ancestry) else {
10915 continue;
10916 };
10917 if name.is_empty()
10918 || cpp_export_macro_token(&name)
10919 || cpp_complete_class_body_close(class_node).is_none()
10920 {
10921 continue;
10922 }
10923 push_cpp_sentinel_recovered_class(
10924 recovered_classes,
10925 namespace_range,
10926 namespace_components,
10927 cpp_declaration_range(template_node.unwrap_or(class_node)),
10928 name,
10929 &owner_ranges,
10930 );
10931 }
10932}
10933
10934fn push_cpp_sentinel_recovered_class(
10935 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
10936 namespace_range: Range,
10937 namespace_components: &[String],
10938 class_range: Range,
10939 name: String,
10940 owner_ranges: &[CppSentinelRecoveredOwner],
10941) {
10942 let mut scope_components = namespace_components.to_vec();
10943 scope_components.push(name);
10944 let owner_ranges = owner_ranges
10945 .iter()
10946 .filter(|owner| owner.scope_components.starts_with(&scope_components))
10947 .cloned()
10948 .collect::<Vec<_>>();
10949 if recovered_classes.iter().any(|existing| {
10950 existing.class_range == class_range && existing.scope_components == scope_components
10951 }) {
10952 return;
10953 }
10954 recovered_classes.push(CppSentinelRecoveredClass {
10955 namespace_range,
10956 namespace_scope_components: namespace_components.to_vec(),
10957 class_range,
10958 scope_components,
10959 owner_ranges,
10960 });
10961}
10962
10963fn cpp_sentinel_recovered_namespace_components(
10964 function: Node<'_>,
10965 recovered_components: &[String],
10966 source: &str,
10967) -> Vec<String> {
10968 let mut ancestor_components = Vec::new();
10969 let mut ancestor = function.parent();
10970 while let Some(current) = ancestor {
10971 if current.kind() == "namespace_definition"
10972 && let Some(name_node) = current.child_by_field_name("name")
10973 && let Some(components) = cpp_name_components(name_node, source)
10974 {
10975 ancestor_components.push(
10976 components
10977 .into_iter()
10978 .map(|component| component.name)
10979 .collect::<Vec<_>>(),
10980 );
10981 }
10982 ancestor = current.parent();
10983 }
10984 ancestor_components.reverse();
10985 let mut ancestors = ancestor_components
10986 .into_iter()
10987 .flatten()
10988 .collect::<Vec<_>>();
10989
10990 let overlap = (0..=ancestors.len().min(recovered_components.len()))
10991 .rev()
10992 .find(|length| {
10993 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
10994 })
10995 .unwrap_or(0);
10996 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
10997 ancestors
10998}
10999
11000fn cpp_sentinel_recovered_owner_ranges(
11001 body: Node<'_>,
11002 namespace_components: &[String],
11003 source: &str,
11004) -> Vec<CppSentinelRecoveredOwner> {
11005 let mut owners = Vec::new();
11006 walk_named_tree_preorder(body, true, |node| {
11007 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
11008 });
11009 owners
11010}
11011
11012fn cpp_sentinel_collect_owner_range(
11013 node: Node<'_>,
11014 namespace_components: &[String],
11015 source: &str,
11016 owners: &mut Vec<CppSentinelRecoveredOwner>,
11017) -> WalkControl {
11018 if node.kind() != "function_definition" {
11019 return WalkControl::Continue;
11020 }
11021 let Some(function_declarator) = extract_function_declarator(node) else {
11022 return WalkControl::Continue;
11023 };
11024 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
11025 return WalkControl::Continue;
11026 };
11027 let Some(mut components) = cpp_name_components(name_node, source) else {
11028 return WalkControl::Continue;
11029 };
11030 if components.len() <= 1 {
11031 return WalkControl::Continue;
11032 }
11033 components.pop();
11034 let mut owner_components = components
11035 .into_iter()
11036 .map(|component| component.name)
11037 .collect::<Vec<_>>();
11038 let overlap = (0..=namespace_components.len().min(owner_components.len()))
11039 .rev()
11040 .find(|length| {
11041 owner_components[..*length]
11042 == namespace_components[namespace_components.len().saturating_sub(*length)..]
11043 })
11044 .unwrap_or(0);
11045 let mut scope_components = namespace_components.to_vec();
11046 scope_components.extend(owner_components.drain(overlap..));
11047 if scope_components.len() <= namespace_components.len() {
11048 return WalkControl::Continue;
11049 }
11050 let range = cpp_declaration_range(node);
11051 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
11052 existing.range == range && existing.scope_components == scope_components
11053 }) {
11054 owners.push(CppSentinelRecoveredOwner {
11055 range,
11056 owner_name_start_byte: name_node.start_byte(),
11057 namespace_component_count: namespace_components.len(),
11058 scope_components,
11059 });
11060 }
11061 WalkControl::Continue
11062}
11063
11064fn cpp_sentinel_extend_unique_owner_ranges(
11065 owners: &mut Vec<CppSentinelRecoveredOwner>,
11066 additional: Vec<CppSentinelRecoveredOwner>,
11067) {
11068 for owner in additional {
11069 if !owners.iter().any(|existing| {
11070 existing.range == owner.range && existing.scope_components == owner.scope_components
11071 }) {
11072 owners.push(owner);
11073 }
11074 }
11075}
11076
11077fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
11078 if node.kind() != "ERROR" || node.named_child_count() != 1 {
11079 return false;
11080 }
11081 let Some(end_name) = node.named_child(0) else {
11082 return false;
11083 };
11084 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
11085 return false;
11086 }
11087 let mut cursor = node.walk();
11088 node.children(&mut cursor)
11089 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
11090}
11091
11092fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
11096 parent: Node<'_>,
11097 sentinel_node: Node<'_>,
11098 namespace_components: &[String],
11099 source: &str,
11100) -> Vec<CppSentinelRecoveredOwner> {
11101 let mut owners = Vec::new();
11102 let mut after_sentinel = false;
11103 let mut cursor = parent.walk();
11104 for child in parent.named_children(&mut cursor) {
11105 if !after_sentinel {
11106 if same_node(child, sentinel_node) {
11107 after_sentinel = true;
11108 }
11109 continue;
11110 }
11111 walk_named_tree_preorder(child, true, |node| {
11112 if node.kind() == "namespace_definition" {
11113 return WalkControl::SkipChildren;
11114 }
11115 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
11116 });
11117 }
11118 owners
11119}
11120
11121fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
11125 parent: Node<'_>,
11126 sentinel_node: Node<'_>,
11127 namespace_components: &[String],
11128 source: &str,
11129) -> Option<Vec<CppSentinelRecoveredOwner>> {
11130 let mut owners = Vec::new();
11131 let mut after_namespace = false;
11132 let mut cursor = parent.walk();
11133 for child in parent.named_children(&mut cursor) {
11134 if !after_namespace {
11135 if same_node(child, sentinel_node) {
11136 after_namespace = true;
11137 }
11138 continue;
11139 }
11140 if cpp_sentinel_namespace_end(child, source) {
11141 return Some(owners);
11142 }
11143 walk_named_tree_preorder(child, true, |node| {
11144 if node.kind() == "namespace_definition" {
11145 return WalkControl::SkipChildren;
11146 }
11147 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
11148 });
11149 }
11150 None
11151}
11152
11153fn cpp_sentinel_recovered_sibling_owner_ranges(
11154 sentinel_node: Node<'_>,
11155 namespace_components: &[String],
11156 source: &str,
11157) -> Vec<CppSentinelRecoveredOwner> {
11158 let Some(declaration_list) = sentinel_node
11159 .parent()
11160 .filter(|parent| parent.kind() == "declaration_list")
11161 else {
11162 return Vec::new();
11163 };
11164 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
11165 declaration_list,
11166 sentinel_node,
11167 namespace_components,
11168 source,
11169 );
11170
11171 let Some(namespace) = declaration_list
11172 .parent()
11173 .filter(|parent| parent.kind() == "namespace_definition")
11174 else {
11175 return owners;
11176 };
11177 let Some(outer_parent) = namespace.parent() else {
11178 return owners;
11179 };
11180 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
11181 outer_parent,
11182 namespace,
11183 namespace_components,
11184 source,
11185 ) {
11186 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
11187 }
11188 owners
11189}
11190
11191fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
11192 let mut current = function_declarator.child_by_field_name("declarator")?;
11193 loop {
11194 if matches!(
11195 current.kind(),
11196 "qualified_identifier"
11197 | "scoped_identifier"
11198 | "scoped_type_identifier"
11199 | "identifier"
11200 | "field_identifier"
11201 | "operator_name"
11202 | "destructor_name"
11203 | "literal_operator_name"
11204 ) {
11205 return Some(current);
11206 }
11207 current = current
11208 .child_by_field_name("declarator")
11209 .or_else(|| current.child_by_field_name("name"))
11210 .or_else(|| last_named_child(current))?;
11211 }
11212}
11213
11214fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
11215 match node.kind() {
11216 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11217 let mut components = match node.child_by_field_name("scope") {
11218 Some(scope) => cpp_name_components(scope, source)?,
11219 None => Vec::new(),
11220 };
11221 let name = node.child_by_field_name("name")?;
11222 components.push(canonical_cpp_qualified_component(name, source)?);
11223 Some(components)
11224 }
11225 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
11226 }
11227}
11228
11229fn cpp_sentinel_fragment_boundary<'tree>(
11230 function: Node<'tree>,
11231 class_node: Node<'tree>,
11232 class_body: Node<'tree>,
11233 source: &str,
11234) -> Option<(Node<'tree>, Node<'tree>)> {
11235 let declaration_list = function.parent()?;
11236 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
11237 return None;
11238 }
11239 let namespace = declaration_list.parent()?;
11240 if namespace.kind() != "namespace_definition"
11241 || namespace.child_by_field_name("body") != Some(declaration_list)
11242 {
11243 return None;
11244 }
11245 let mut cursor = declaration_list.walk();
11246 let closes = declaration_list
11247 .children(&mut cursor)
11248 .filter(|child| {
11249 !child.is_named()
11250 && child.kind() == "}"
11251 && child.start_byte() >= function.end_byte()
11252 && child.start_byte() > class_node.end_byte()
11253 && child.start_byte() > class_body.start_byte()
11254 })
11255 .collect::<Vec<_>>();
11256 let [close] = closes.as_slice() else {
11257 return None;
11258 };
11259 let semicolon = namespace.next_named_sibling()?;
11260 if !cpp_is_stray_semicolon(semicolon, source)
11261 || close.end_byte() != namespace.end_byte()
11262 || semicolon.start_byte() < namespace.end_byte()
11263 {
11264 return None;
11265 }
11266 Some((*close, semicolon))
11267}
11268
11269fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
11295 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
11296 {
11297 return None;
11298 }
11299 let mut declarator_cursor = node.walk();
11305 let preserved_callable = node
11306 .children_by_field_name("declarator", &mut declarator_cursor)
11307 .find_map(extract_function_declarator);
11308 let mut cursor = node.walk();
11316 let first = node
11317 .named_children(&mut cursor)
11318 .find(|child| child.kind() != "comment")?;
11319 if first.kind() != "type_identifier" {
11320 return None;
11321 }
11322 let sentinel = normalize_cpp_whitespace(node_text(first, source));
11323 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
11324 return None;
11325 }
11326 let mut start = first.end_byte();
11333 let mut after_first = false;
11334 let mut cursor = node.walk();
11335 for child in node.named_children(&mut cursor) {
11336 if !after_first {
11337 if same_node(child, first) {
11338 after_first = true;
11339 }
11340 continue;
11341 }
11342 if matches!(child.kind(), "identifier" | "type_identifier")
11343 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
11344 {
11345 start = child.end_byte();
11346 } else {
11347 break;
11348 }
11349 }
11350 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
11359 let mut class_start = None;
11360 let mut template_start = None;
11361 let mut stack = vec![node];
11362 while let Some(current) = stack.pop() {
11363 if current.start_byte() >= prefix_end {
11364 continue;
11365 }
11366 if matches!(
11367 current.kind(),
11368 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
11369 ) {
11370 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
11371 "class" | "struct" | "union" | "enum" => {
11372 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
11373 seen.min(current.start_byte())
11374 }));
11375 }
11376 "template" => {
11377 template_start =
11378 Some(template_start.map_or(current.start_byte(), |seen: usize| {
11379 seen.min(current.start_byte())
11380 }));
11381 }
11382 _ => {}
11383 }
11384 }
11385 let mut cursor = current.walk();
11386 stack.extend(current.children(&mut cursor));
11387 }
11388 if preserved_callable.is_some_and(|callable| {
11389 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
11390 }) {
11391 return None;
11392 }
11393 if let Some(class_start) = class_start {
11394 start = template_start
11395 .filter(|template_start| *template_start < class_start)
11396 .unwrap_or(class_start);
11397 }
11398 Some((start, class_start))
11399}
11400
11401fn cpp_sentinel_macro_class_region<'tree>(
11407 node: Node<'tree>,
11408 source: &str,
11409) -> Option<(usize, usize, usize, usize, usize, usize)> {
11410 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
11411 return None;
11412 };
11413 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
11414 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
11415 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
11416 if class_start >= body_open_start {
11417 return None;
11418 }
11419 let sibling_close = {
11420 let mut sibling = node.next_named_sibling();
11421 let mut found = None;
11422 while let Some(current) = sibling {
11423 let next = current.next_named_sibling();
11424 if cpp_is_stray_close_brace(current, source)
11425 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
11426 {
11427 let semicolon = next.expect("checked above");
11428 found = Some((
11429 current.start_byte(),
11430 semicolon.end_byte(),
11431 semicolon.end_position().row + 1,
11432 ));
11433 break;
11434 }
11435 sibling = next;
11436 }
11437 found
11438 };
11439 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
11452 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
11453 return false;
11454 };
11455 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
11456 let reparsed_ancestry = ParentIndex::new(tree.root_node());
11458 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
11459 tree.root_node(),
11460 template_node,
11461 source,
11462 &reparsed_ancestry,
11463 ) else {
11464 return false;
11465 };
11466 let body = reparsed_class.body;
11467 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
11468 });
11469 let (class_close_start, class_close_end, class_close_line) =
11470 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
11471 (class_close_start, class_close_end, class_close_line)
11472 } else {
11473 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
11480 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
11481 let reparsed_ancestry = ParentIndex::new(tree.root_node());
11483 let reparsed_class = cpp_sentinel_reparsed_class(
11484 tree.root_node(),
11485 template_node,
11486 source,
11487 &reparsed_ancestry,
11488 )?;
11489 let body = reparsed_class.body;
11490 let class_close_end = body.end_byte();
11491 let class_close_start = class_close_end.checked_sub(1)?;
11492 let class_close_line = body.end_position().row + 1;
11493 (class_close_start, class_close_end, class_close_line)
11494 };
11495 if class_close_start <= class_start {
11496 return None;
11497 }
11498
11499 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
11503 let class_root = tree.root_node();
11504 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
11505 let reparsed_ancestry = ParentIndex::new(class_root);
11507 let reparsed_class =
11508 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
11509 let body = reparsed_class.body;
11510 if body.start_byte() != body_open_start {
11514 return None;
11515 }
11516 let body_start = body.start_byte().checked_add(1)?;
11517 (body_start < class_close_start).then_some((
11518 reparse_start,
11519 class_start,
11520 body_start,
11521 class_close_start,
11522 class_close_end,
11523 class_close_line,
11524 ))
11525}
11526
11527fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
11532 let mut stack = vec![node];
11533 while let Some(current) = stack.pop() {
11534 if current.start_byte() == class_start
11535 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
11536 {
11537 let mut sibling = current.next_sibling();
11538 while let Some(candidate) = sibling {
11539 if candidate.kind() == "{" {
11540 return Some(candidate.start_byte());
11541 }
11542 sibling = candidate.next_sibling();
11543 }
11544 }
11545 let mut cursor = current.walk();
11546 stack.extend(current.children(&mut cursor));
11547 }
11548 None
11549}
11550
11551fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
11562 node.next_named_sibling()
11563 .filter(|sibling| sibling.kind() == "compound_statement")
11564}
11565
11566fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
11567 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
11568 let mut end = if class_start.is_some() {
11569 cpp_macro_prefixed_class_end(source, start)?
11570 } else {
11571 node.end_byte()
11572 };
11573 if class_start.is_none()
11574 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
11575 {
11576 end = end.max(namespace_end);
11577 }
11578 let mut sibling = node.next_named_sibling();
11579 while let Some(current) = sibling {
11580 if !cpp_is_stray_semicolon(current, source) {
11581 break;
11582 }
11583 end = current.end_byte();
11584 sibling = current.next_named_sibling();
11585 }
11586 (start < end).then_some((start, end))
11587}
11588
11589fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
11600 let mut sibling = node.next_sibling();
11601 let keyword = loop {
11602 let candidate = sibling?;
11603 sibling = candidate.next_sibling();
11604 if candidate.kind() != "comment" {
11605 break candidate;
11606 }
11607 };
11608 if keyword.kind() != "namespace" {
11609 return None;
11610 }
11611 let name = loop {
11612 let candidate = sibling?;
11613 sibling = candidate.next_sibling();
11614 if candidate.kind() != "comment" {
11615 break candidate;
11616 }
11617 };
11618 if cpp_namespace_name_components(name, source).is_empty() {
11619 return None;
11620 }
11621 let open = loop {
11622 let candidate = sibling?;
11623 sibling = candidate.next_sibling();
11624 if candidate.kind() != "comment" {
11625 break candidate;
11626 }
11627 };
11628 if open.kind() != "{" {
11629 return None;
11630 }
11631
11632 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
11633 let root = tree.root_node();
11634 let mut cursor = root.walk();
11635 let namespace = root
11636 .named_children(&mut cursor)
11637 .find(|candidate| candidate.kind() != "comment")?;
11638 (namespace.kind() == "namespace_definition"
11639 && namespace.start_byte() == keyword.start_byte()
11640 && namespace.child_by_field_name("body").is_some())
11641 .then_some(namespace.end_byte())
11642}
11643
11644fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
11650 let tree = cpp_reparse_region_items(source, start, source.len())?;
11651 let root = tree.root_node();
11652 let mut cursor = root.walk();
11653 for item in root.named_children(&mut cursor) {
11654 if item.end_byte() <= start || item.kind() == "comment" {
11655 continue;
11656 }
11657 let mut stack = vec![item];
11658 while let Some(current) = stack.pop() {
11659 if matches!(
11660 current.kind(),
11661 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
11662 ) && cpp_body_node(current).is_some()
11663 {
11664 return Some(current.end_byte());
11665 }
11666 let mut cursor = current.walk();
11667 stack.extend(current.named_children(&mut cursor));
11668 }
11669 return None;
11673 }
11674 None
11675}
11676
11677fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
11680 node.kind() == "expression_statement"
11681 && node.named_child_count() == 0
11682 && node_text(node, source).trim() == ";"
11683}
11684
11685fn recovered_macro_qualified_field_declarators<'tree>(
11694 node: Node<'tree>,
11695 source: &str,
11696) -> Option<Vec<Node<'tree>>> {
11697 if node.kind() != "field_declaration" {
11698 return None;
11699 }
11700 let macro_type = node.child_by_field_name("type")?;
11701 if macro_type.kind() != "type_identifier"
11702 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11703 {
11704 return None;
11705 }
11706 let pseudo_declarator = node.child_by_field_name("declarator")?;
11707 if pseudo_declarator.kind() != "field_identifier" {
11708 return None;
11709 }
11710 let mut cursor = node.walk();
11711 let clause = node
11712 .named_children(&mut cursor)
11713 .find(|child| child.kind() == "bitfield_clause")?;
11714 if !(0..clause.named_child_count()).any(|index| {
11715 clause
11716 .named_child(index)
11717 .is_some_and(|child| child.kind() == "ERROR")
11718 }) {
11719 return None;
11720 }
11721 let mut recovered = Vec::new();
11722 let mut stack = vec![clause];
11723 while let Some(current) = stack.pop() {
11724 if current.kind() == "assignment_expression"
11725 && let Some(left) = current.child_by_field_name("left")
11726 && extract_variable_name(left, source).is_some()
11727 {
11728 recovered.push(left);
11729 break;
11730 }
11731 let mut cursor = current.walk();
11732 stack.extend(current.named_children(&mut cursor));
11733 }
11734 if recovered.is_empty() {
11735 return None;
11736 }
11737 let mut cursor = node.walk();
11738 recovered.extend(
11739 node.children_by_field_name("declarator", &mut cursor)
11740 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
11741 );
11742 Some(recovered)
11743}
11744
11745fn recovered_macro_qualified_constructor_call<'tree>(
11751 node: Node<'tree>,
11752 class_name: &str,
11753 source: &str,
11754) -> Option<Node<'tree>> {
11755 if node.kind() != "field_declaration" {
11756 return None;
11757 }
11758 let macro_type = node.child_by_field_name("type")?;
11759 if macro_type.kind() != "type_identifier"
11760 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11761 {
11762 return None;
11763 }
11764 let mut cursor = node.walk();
11765 let bitfield = node
11766 .named_children(&mut cursor)
11767 .find(|child| child.kind() == "bitfield_clause")?;
11768 let error = bitfield
11769 .named_child(0)
11770 .filter(|child| child.kind() == "ERROR")?;
11771 let mut stack = vec![error];
11772 while let Some(current) = stack.pop() {
11773 if current.kind() == "call_expression"
11774 && current
11775 .child_by_field_name("function")
11776 .is_some_and(|function| node_text(function, source) == class_name)
11777 && current
11778 .child_by_field_name("arguments")
11779 .is_some_and(|arguments| arguments.kind() == "argument_list")
11780 {
11781 return Some(current);
11782 }
11783 let mut cursor = current.walk();
11784 stack.extend(current.named_children(&mut cursor));
11785 }
11786 None
11787}
11788
11789fn recovered_macro_qualified_function_call<'tree>(
11797 node: Node<'tree>,
11798 source: &str,
11799) -> Option<Node<'tree>> {
11800 if node.kind() != "field_declaration" {
11801 return None;
11802 }
11803 let macro_type = node.child_by_field_name("type")?;
11804 if macro_type.kind() != "type_identifier"
11805 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11806 {
11807 return None;
11808 }
11809 let declarator = node.child_by_field_name("declarator")?;
11810 if declarator.kind() != "field_identifier" {
11811 return None;
11812 }
11813 let mut cursor = node.walk();
11814 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11815 if !named.iter().any(|child| {
11816 child.kind() == "storage_class_specifier"
11817 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
11818 }) {
11819 return None;
11820 }
11821 let bitfield = named
11822 .iter()
11823 .find(|child| child.kind() == "bitfield_clause")?;
11824 let mut bitfield_cursor = bitfield.walk();
11825 let payload = bitfield
11826 .named_children(&mut bitfield_cursor)
11827 .collect::<Vec<_>>();
11828 let [displaced_error, call] = payload.as_slice() else {
11829 return None;
11830 };
11831 if displaced_error.kind() != "ERROR"
11832 || displaced_error.named_child_count() != 1
11833 || displaced_error
11834 .named_child(0)
11835 .is_none_or(|child| child.kind() != "identifier")
11836 || call.kind() != "call_expression"
11837 || call
11838 .child_by_field_name("function")
11839 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
11840 || call
11841 .child_by_field_name("arguments")
11842 .is_none_or(|arguments| arguments.kind() != "argument_list")
11843 {
11844 return None;
11845 }
11846 Some(*call)
11847}
11848
11849fn recovered_macro_qualified_function_parameters(
11850 arguments: Node<'_>,
11851 source: &str,
11852) -> Option<(String, Vec<String>)> {
11853 if arguments.kind() != "argument_list" {
11854 return None;
11855 }
11856 let mut cursor = arguments.walk();
11857 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
11858 if named.is_empty() {
11859 return Some(("()".to_string(), Vec::new()));
11860 }
11861 let mut types = Vec::new();
11862 let mut labels = Vec::new();
11863 let mut index = 0;
11864 while index < named.len() {
11865 let parameter_type = named[index];
11866 let parameter_name = named.get(index + 1).copied()?;
11867 if !matches!(
11868 parameter_type.kind(),
11869 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
11870 ) || parameter_name.kind() != "ERROR"
11871 || parameter_name.named_child_count() != 1
11872 || parameter_name
11873 .named_child(0)
11874 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
11875 {
11876 return None;
11877 }
11878 let parameter_name = parameter_name.named_child(0)?;
11879 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
11880 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
11881 index += 2;
11882 }
11883 Some((format!("({})", types.join(", ")), labels))
11884}
11885
11886pub fn recovered_macro_return_type_node<'tree>(
11898 node: Node<'tree>,
11899 source: &str,
11900) -> Option<Node<'tree>> {
11901 if node.kind() != "field_declaration" {
11902 return None;
11903 }
11904 let macro_type = node.child_by_field_name("type")?;
11905 if macro_type.kind() != "type_identifier"
11906 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11907 {
11908 return None;
11909 }
11910 let declarator = node.child_by_field_name("declarator")?;
11911 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
11912 return None;
11913 }
11914 let mut has_missing_semicolon = false;
11915 let mut has_real_semicolon = false;
11916 for index in 0..node.child_count() {
11917 let Some(child) = node.child(index) else {
11918 continue;
11919 };
11920 if child.kind() != ";" {
11921 continue;
11922 }
11923 if child.is_missing() {
11924 has_missing_semicolon = true;
11925 } else {
11926 has_real_semicolon = true;
11927 }
11928 }
11929 if !has_missing_semicolon || has_real_semicolon {
11930 return None;
11931 }
11932 let mut next = node.next_named_sibling();
11933 while next.is_some_and(|sibling| sibling.kind() == "comment") {
11934 next = next.and_then(|sibling| sibling.next_named_sibling());
11935 }
11936 let next = next?;
11937 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
11938 return None;
11939 }
11940 let function_declarator = next.child_by_field_name("declarator")?;
11941 extract_function_declarator(function_declarator).map(|_| declarator)
11942}
11943
11944pub(crate) fn cpp_active_template_type_parameter<'tree>(
11951 node: Node<'tree>,
11952 name: &str,
11953 source: &str,
11954 ancestry: &ParentIndex<'tree>,
11955) -> bool {
11956 let mut ancestor = ancestry.parent(node);
11957 while let Some(current) = ancestor {
11958 if current.kind() == "template_declaration"
11959 && let Some(parameters) = current.child_by_field_name("parameters")
11960 {
11961 let mut cursor = parameters.walk();
11962 if parameters.named_children(&mut cursor).any(|parameter| {
11963 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
11964 && cpp_template_parameter_name(parameter, source)
11965 .is_some_and(|parameter_name| parameter_name == name)
11966 }) {
11967 return true;
11968 }
11969 }
11970 ancestor = ancestry.parent(current);
11971 }
11972 false
11973}
11974
11975fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
11981 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
11982}
11983
11984fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
11985 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
11986 return None;
11987 }
11988 let semicolon = node.next_sibling()?;
11989 if semicolon.kind() != ";" || semicolon.is_missing() {
11990 return None;
11991 }
11992 let row = node.start_position().row;
11993 let mut start = node.start_byte();
11994 let mut sibling = node.prev_sibling();
11995 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
11996 if previous.kind() == ";" {
11997 break;
11998 }
11999 start = previous.start_byte();
12000 sibling = previous.prev_sibling();
12001 }
12002 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
12003}
12004
12005fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
12006 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
12007 return false;
12008 }
12009 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
12010 return false;
12011 }
12012 let Some(declarator) = (if node.kind() == "function_definition" {
12013 node.child_by_field_name("declarator")
12014 .and_then(extract_function_declarator)
12015 } else {
12016 node.named_child(0)
12017 .filter(|child| child.kind() == "function_declarator")
12018 }) else {
12019 return false;
12020 };
12021 let Some(name) = cpp_function_declarator_name_node(declarator) else {
12022 return false;
12023 };
12024 declarator.start_byte() == node.start_byte()
12025 && name.kind() == "identifier"
12026 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
12027}
12028
12029fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
12042 let region = cpp_reparse_region_items(source, start, end);
12043
12044 #[cfg(debug_assertions)]
12045 assert_eq!(
12046 region.as_ref().map(cpp_tree_shape),
12047 cpp_reparse_padded_class_body(source, start, end)
12048 .as_ref()
12049 .map(cpp_tree_shape),
12050 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
12051 prefix produces"
12052 );
12053
12054 region
12055}
12056
12057#[cfg(any(debug_assertions, test))]
12061fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
12062 if start >= end {
12063 return None;
12067 }
12068 let bytes = source.as_bytes();
12069 let prefix = bytes.get(..start)?;
12070 let interior = bytes.get(start..end)?;
12071 let mut padded = Vec::with_capacity(end);
12072 padded.extend(
12073 prefix
12074 .iter()
12075 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
12076 );
12077 padded.extend_from_slice(interior);
12078 let padded = String::from_utf8(padded).ok()?;
12079 let mut parser = Parser::new();
12080 parser
12081 .set_language(&tree_sitter_cpp::LANGUAGE.into())
12082 .ok()?;
12083 parser.parse(&padded, None)
12084}
12085
12086#[cfg(any(debug_assertions, test))]
12090fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
12091 let mut shape = Vec::new();
12092 let mut cursor = tree.root_node().walk();
12093 let mut stack = vec![tree.root_node()];
12094 while let Some(node) = stack.pop() {
12095 shape.push((
12096 node.kind(),
12097 node.start_byte(),
12098 node.end_byte(),
12099 node.start_position().row,
12100 node.start_position().column,
12101 node.is_named(),
12102 node.is_missing(),
12103 ));
12104 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
12105 stack.extend(children.into_iter().rev());
12106 }
12107 shape
12108}
12109
12110fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
12131 let mut cursor = root.walk();
12132 let mut saw_item = false;
12133 for child in root.named_children(&mut cursor) {
12134 match child.kind() {
12135 "comment" => {}
12136 "function_definition" => {
12137 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
12138 return false;
12139 }
12140 saw_item = true;
12141 }
12142 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
12143 _ => return false,
12144 }
12145 }
12146 saw_item
12147}
12148
12149fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
12159 if node.kind() != "ERROR" {
12160 return false;
12161 }
12162 let mut stack = Vec::new();
12163 let mut saw_function_declarator = false;
12164 let mut cursor = node.walk();
12165 for child in node.named_children(&mut cursor) {
12166 stack.push(child);
12167 }
12168 while let Some(current) = stack.pop() {
12169 match current.kind() {
12170 "ERROR" => {
12174 let mut cursor = current.walk();
12175 stack.extend(current.named_children(&mut cursor));
12176 }
12177 "function_declarator" => saw_function_declarator = true,
12178 _ => return false,
12179 }
12180 }
12181 saw_function_declarator
12182}
12183
12184fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
12185 if node.kind() != "ERROR" {
12186 return false;
12187 }
12188 let mut cursor = node.walk();
12189 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12190 let [explicit, constructor_error, destructor] = named.as_slice() else {
12191 return false;
12192 };
12193 let Some(constructor) = constructor_error.named_child(0) else {
12194 return false;
12195 };
12196 let Some(constructor_name) =
12197 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
12198 else {
12199 return false;
12200 };
12201 let Some(destructor_name) =
12202 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
12203 else {
12204 return false;
12205 };
12206 let Some(destroyed_type) = destructor_name.named_child(0) else {
12207 return false;
12208 };
12209 explicit.kind() == "explicit_function_specifier"
12210 && constructor_error.kind() == "ERROR"
12211 && constructor_error.named_child_count() == 1
12212 && constructor.kind() == "function_declarator"
12213 && constructor_name.kind() == "identifier"
12214 && destructor.kind() == "function_declarator"
12215 && destructor_name.kind() == "destructor_name"
12216 && destroyed_type.kind() == "identifier"
12217 && node_text(constructor_name, source) == node_text(destroyed_type, source)
12218}
12219
12220fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
12221 if node.kind() != "compound_statement" {
12222 return false;
12223 }
12224 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
12225 return false;
12226 };
12227 if prefix.kind() == "labeled_statement"
12228 && prefix.named_child(0).is_some_and(|label| {
12229 matches!(
12230 node_text(label, source).trim(),
12231 "public" | "private" | "protected"
12232 )
12233 })
12234 {
12235 return prefix.named_children(&mut prefix.walk()).any(|child| {
12236 child.kind() == "declaration"
12237 && child.has_error()
12238 && child
12239 .named_children(&mut child.walk())
12240 .any(cpp_reparsed_member_error_is_indexable)
12241 });
12242 }
12243 prefix.kind() == "declaration"
12248 && prefix.has_error()
12249 && prefix
12250 .named_children(&mut prefix.walk())
12251 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
12252}
12253
12254fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
12255 if !cpp_reparsed_member_error_is_indexable(node) {
12256 return false;
12257 }
12258 let Some(preproc) = node.next_named_sibling() else {
12259 return false;
12260 };
12261 preproc.kind() == "preproc_if"
12262 && preproc.has_error()
12263 && preproc
12264 .named_children(&mut preproc.walk())
12265 .any(|child| child.kind() == "expression_statement" && child.has_error())
12266 && preproc
12267 .next_named_sibling()
12268 .is_some_and(|body| body.kind() == "compound_statement")
12269}
12270
12271fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
12276 if node.kind() != "function_definition" {
12277 return None;
12278 }
12279 let body = node.child_by_field_name("body")?;
12280 if body.kind() != "compound_statement" {
12281 return None;
12282 }
12283 let open = body.child(0)?;
12284 let close = body.child(body.child_count().checked_sub(1)?)?;
12285 if open.kind() != "{"
12286 || open.is_missing()
12287 || close.kind() != "}"
12288 || close.is_missing()
12289 || close.end_byte() != body.end_byte()
12290 || body.end_byte() != node.end_byte()
12291 {
12292 return None;
12293 }
12294 Some(body)
12295}
12296
12297fn cpp_reparsed_member_function_errors_are_in_body(
12298 node: Node<'_>,
12299 body: Node<'_>,
12300 source: &str,
12301) -> bool {
12302 let mut cursor = node.walk();
12303 node.children(&mut cursor).all(|child| {
12304 same_node(child, body)
12305 || cpp_reparsed_member_attribute_error(child, source)
12306 || cpp_reparsed_member_signature_identifier_errors(child)
12307 || (!child.has_error() && !child.is_error() && !child.is_missing())
12308 })
12309}
12310
12311fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
12319 if !node.has_error() && !node.is_error() && !node.is_missing() {
12320 return false;
12321 }
12322 let mut stack = vec![node];
12323 let mut saw_error = false;
12324 while let Some(current) = stack.pop() {
12325 if current.is_missing() {
12326 return false;
12327 }
12328 if current.kind() == "ERROR" {
12329 saw_error = true;
12330 let mut cursor = current.walk();
12331 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
12332 if children
12333 .iter()
12334 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
12335 {
12336 return false;
12337 }
12338 stack.extend(children);
12339 continue;
12340 }
12341 let mut cursor = current.walk();
12342 stack.extend(current.children(&mut cursor));
12343 }
12344 saw_error
12345}
12346
12347fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
12348 node.kind() == "ERROR"
12349 && node.named_child_count() == 1
12350 && node.named_child(0).is_some_and(|attribute| {
12351 attribute.kind() == "identifier"
12352 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
12353 })
12354}
12355
12356fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
12362 let Some(body) = cpp_reparsed_member_function_body(node) else {
12363 return false;
12364 };
12365 let mut cursor = node.walk();
12366 let named = node
12367 .named_children(&mut cursor)
12368 .filter(|child| child.kind() != "comment")
12369 .collect::<Vec<_>>();
12370 let [type_node, error, attribute, body_node] = named.as_slice() else {
12371 return false;
12372 };
12373 if !same_node(*body_node, body)
12374 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
12375 || attribute.kind() != "identifier"
12376 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
12377 || error.kind() != "ERROR"
12378 || error.named_child_count() != 1
12379 {
12380 return false;
12381 }
12382 error
12383 .named_child(0)
12384 .is_some_and(cpp_reparsed_attribute_callable_declarator)
12385}
12386
12387fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
12388 cpp_structured_type_path(node, source).is_some()
12389 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
12390}
12391
12392fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
12393 let Some(body) = cpp_reparsed_member_function_body(node) else {
12394 return false;
12395 };
12396 let mut cursor = node.walk();
12397 let named = node
12398 .named_children(&mut cursor)
12399 .filter(|child| child.kind() != "comment")
12400 .collect::<Vec<_>>();
12401 let [friend, return_error, declarator, body_node] = named.as_slice() else {
12402 return false;
12403 };
12404 let Some(return_type) = return_error.named_child(0) else {
12405 return false;
12406 };
12407 same_node(*body_node, body)
12408 && friend.kind() == "type_identifier"
12409 && node_text(*friend, source) == "friend"
12410 && return_error.kind() == "ERROR"
12411 && return_error.named_child_count() == 1
12412 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
12413 && extract_function_declarator(*declarator)
12414 .and_then(cpp_function_declarator_name_node)
12415 .is_some()
12416}
12417
12418fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
12419 let Some(body) = cpp_reparsed_member_function_body(node) else {
12420 return false;
12421 };
12422 let mut cursor = node.walk();
12423 let named = node
12424 .named_children(&mut cursor)
12425 .filter(|child| child.kind() != "comment")
12426 .collect::<Vec<_>>();
12427 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
12428 return false;
12429 };
12430 let Some(return_type) = return_error.named_child(0) else {
12431 return false;
12432 };
12433 same_node(*body_node, body)
12434 && prefix
12435 .iter()
12436 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
12437 && attribute.kind() == "type_identifier"
12438 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
12439 && return_error.kind() == "ERROR"
12440 && return_error.named_child_count() == 1
12441 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
12442 && extract_function_declarator(*declarator)
12443 .and_then(cpp_function_declarator_name_node)
12444 .is_some()
12445}
12446
12447fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
12453 let Some(body) = cpp_reparsed_member_function_body(node) else {
12454 return false;
12455 };
12456 let mut cursor = node.walk();
12457 let named = node
12458 .named_children(&mut cursor)
12459 .filter(|child| child.kind() != "comment")
12460 .collect::<Vec<_>>();
12461 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
12462 return false;
12463 };
12464 let Some(template_name) = template_type.child_by_field_name("name") else {
12465 return false;
12466 };
12467 let Some(arguments) = template_type.child_by_field_name("arguments") else {
12468 return false;
12469 };
12470 let Some(return_type) = return_error.named_child(0) else {
12471 return false;
12472 };
12473 let mut cursor = template_type.walk();
12474 let template_errors = template_type
12475 .named_children(&mut cursor)
12476 .filter(|child| child.kind() == "ERROR")
12477 .collect::<Vec<_>>();
12478 let [comment_error] = template_errors.as_slice() else {
12479 return false;
12480 };
12481 let mut cursor = comment_error.walk();
12482 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
12483 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
12484 return false;
12485 };
12486 same_node(*body_node, body)
12487 && template_type.kind() == "template_type"
12488 && template_name.kind() == "type_identifier"
12489 && matches!(
12490 node_text(template_name, source).trim(),
12491 "public" | "private" | "protected"
12492 )
12493 && arguments.kind() == "template_argument_list"
12494 && arguments.named_child_count() > 0
12495 && !arguments.has_error()
12496 && !colon.is_named()
12497 && colon.kind() == ":"
12498 && comments.iter().all(|child| child.kind() == "comment")
12499 && !template_keyword.is_named()
12500 && template_keyword.kind() == "template"
12501 && return_error.kind() == "ERROR"
12502 && return_error.named_child_count() == 1
12503 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
12504 && extract_function_declarator(*declarator)
12505 .and_then(cpp_function_declarator_name_node)
12506 .is_some()
12507}
12508
12509fn cpp_reparsed_preprocessor_constructor<'tree>(
12515 node: Node<'tree>,
12516 class_name: &str,
12517 source: &str,
12518) -> Option<Node<'tree>> {
12519 if node.kind() != "labeled_statement" {
12520 return None;
12521 }
12522 let mut cursor = node.walk();
12523 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12524 let [label, directive_error, declaration] = named.as_slice() else {
12525 return None;
12526 };
12527 if label.kind() != "statement_identifier"
12528 || !matches!(
12529 node_text(*label, source),
12530 "public" | "private" | "protected"
12531 )
12532 || directive_error.kind() != "ERROR"
12533 || directive_error.child_count() != 1
12534 || directive_error
12535 .child(0)
12536 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
12537 || declaration.kind() != "declaration"
12538 || declaration.named_child_count() != 2
12539 {
12540 return None;
12541 }
12542 let apparent_type = declaration.child_by_field_name("type")?;
12543 if apparent_type.kind() != "type_identifier"
12544 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
12545 {
12546 return None;
12547 }
12548 let declarator = declaration.child_by_field_name("declarator")?;
12549 let function = extract_function_declarator(declarator)?;
12550 let name = cpp_function_declarator_name_node(function)?;
12551 (node_text(name, source) == class_name).then_some(*declaration)
12552}
12553
12554fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
12555 if extract_function_declarator(node)
12556 .and_then(cpp_function_declarator_name_node)
12557 .is_some()
12558 {
12559 return true;
12560 }
12561 node.kind() == "init_declarator"
12562 && node
12563 .child_by_field_name("declarator")
12564 .is_some_and(|declarator| declarator.kind() == "identifier")
12565 && node
12566 .child_by_field_name("value")
12567 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
12568}
12569
12570fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
12575 if node.kind() != "ERROR" || node.named_child_count() != 3 {
12576 return false;
12577 }
12578 let mut cursor = node.walk();
12579 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12580 let [type_node, function_declarator, attribute] = named.as_slice() else {
12581 return false;
12582 };
12583 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
12584 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
12585 || attribute.kind() != "identifier"
12586 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
12587 {
12588 return false;
12589 }
12590 let Some(preproc) =
12591 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
12592 else {
12593 return false;
12594 };
12595 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
12596 .filter(|sibling| sibling.kind() == "compound_statement")
12597 else {
12598 return false;
12599 };
12600 let Some(open) = body.child(0) else {
12601 return false;
12602 };
12603 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
12604 return false;
12605 };
12606 let Some(condition) = preproc.child_by_field_name("condition") else {
12607 return false;
12608 };
12609 let mut cursor = preproc.walk();
12610 let payload = preproc
12611 .named_children(&mut cursor)
12612 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
12613 .collect::<Vec<_>>();
12614 let [requires_statement] = payload.as_slice() else {
12615 return false;
12616 };
12617 let requires_clause = requires_statement.named_child(0);
12618
12619 open.kind() == "{"
12620 && !open.is_missing()
12621 && close.kind() == "}"
12622 && !close.is_missing()
12623 && close.end_byte() == body.end_byte()
12624 && requires_statement.kind() == "expression_statement"
12625 && requires_statement.named_child_count() == 1
12626 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
12627}
12628
12629fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
12630 let mut sibling = node.next_named_sibling();
12631 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
12632 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
12633 }
12634 sibling
12635}
12636
12637fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
12638 let mut sibling = node.prev_named_sibling();
12639 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
12640 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
12641 }
12642 sibling
12643}
12644
12645fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
12646 let Some(preproc) =
12647 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
12648 else {
12649 return false;
12650 };
12651 let Some(error) =
12652 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
12653 else {
12654 return false;
12655 };
12656 cpp_reparsed_attribute_requires_error(error, source)
12657}
12658
12659fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
12660 node: Node<'tree>,
12661 source: &str,
12662) -> Option<Node<'tree>> {
12663 if node.kind() != "ERROR" {
12664 return None;
12665 }
12666 let mut cursor = node.walk();
12667 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12668 let [parameter, macro_name, message] = named.as_slice() else {
12669 return None;
12670 };
12671 let parameter_name = parameter.named_child(0)?;
12672 (parameter.kind() == "type_parameter_declaration"
12673 && parameter_name.kind() == "type_identifier"
12674 && macro_name.kind() == "type_identifier"
12675 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
12676 && message.kind() == "string_literal")
12677 .then_some(parameter_name)
12678}
12679
12680fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
12685 node: Node<'tree>,
12686 source: &str,
12687) -> Option<Node<'tree>> {
12688 if node.kind() != "ERROR" {
12689 return None;
12690 }
12691 let mut cursor = node.walk();
12692 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12693 let [parameter, macro_name, message, constraint] = named.as_slice() else {
12694 return None;
12695 };
12696 let parameter_name = parameter.named_child(0)?;
12697 let constraint_scope = constraint.child_by_field_name("scope")?;
12698 let constraint_template = constraint.child_by_field_name("name")?;
12699 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
12700 let mut argument_cursor = constraint_arguments.walk();
12701 let constraint_types = constraint_arguments
12702 .named_children(&mut argument_cursor)
12703 .collect::<Vec<_>>();
12704 if parameter.kind() != "type_parameter_declaration"
12705 || parameter_name.kind() != "type_identifier"
12706 || macro_name.kind() != "type_identifier"
12707 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
12708 || message.kind() != "string_literal"
12709 || constraint.kind() != "qualified_identifier"
12710 || constraint_scope.kind() != "namespace_identifier"
12711 || !matches!(
12712 constraint_template.kind(),
12713 "template_function" | "template_type"
12714 )
12715 || !matches!(constraint_types.as_slice(), [left, right]
12716 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
12717 || constraint_arguments.has_error()
12718 {
12719 return None;
12720 }
12721 let parameter_text = node_text(parameter_name, source);
12722 let mut stack = constraint_types;
12723 while let Some(current) = stack.pop() {
12724 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
12725 return Some(parameter_name);
12726 }
12727 let mut cursor = current.walk();
12728 stack.extend(current.named_children(&mut cursor));
12729 }
12730 None
12731}
12732
12733fn cpp_reparsed_template_macro_companion_is_indexable(
12734 node: Node<'_>,
12735 parameter_name: Node<'_>,
12736 source: &str,
12737) -> bool {
12738 let Some(body) = cpp_reparsed_member_function_body(node) else {
12739 return false;
12740 };
12741 let mut cursor = node.walk();
12742 let named = node
12743 .named_children(&mut cursor)
12744 .filter(|child| child.kind() != "comment")
12745 .collect::<Vec<_>>();
12746 let [
12747 constraint,
12748 close_error,
12749 storage,
12750 return_error,
12751 declarator,
12752 body_node,
12753 ] = named.as_slice()
12754 else {
12755 return false;
12756 };
12757 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
12758 return false;
12759 };
12760 let Some(constraint_template) = constraint.child_by_field_name("name") else {
12761 return false;
12762 };
12763 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
12764 return false;
12765 };
12766 let Some(return_type) = return_error.named_child(0) else {
12767 return false;
12768 };
12769 let mut cursor = constraint_arguments.walk();
12770 let constraint_types = constraint_arguments
12771 .named_children(&mut cursor)
12772 .collect::<Vec<_>>();
12773 same_node(*body_node, body)
12774 && constraint.kind() == "qualified_identifier"
12775 && constraint_scope.kind() == "namespace_identifier"
12776 && constraint_template.kind() == "template_type"
12777 && matches!(constraint_types.as_slice(), [left, right]
12778 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
12779 && !constraint_arguments.has_error()
12780 && close_error.kind() == "ERROR"
12781 && close_error.named_child_count() == 0
12782 && storage.kind() == "storage_class_specifier"
12783 && return_error.kind() == "ERROR"
12784 && return_error.named_child_count() == 1
12785 && return_type.kind() == "identifier"
12786 && node_text(return_type, source) == node_text(parameter_name, source)
12787 && extract_function_declarator(*declarator)
12788 .and_then(cpp_function_declarator_name_node)
12789 .is_some()
12790}
12791
12792fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
12793 node: Node<'tree>,
12794 parameter_name: Node<'_>,
12795 source: &str,
12796) -> Option<Node<'tree>> {
12797 let body = cpp_reparsed_member_function_body(node)?;
12798 let constraint = node.child_by_field_name("type")?;
12799 let constraint_template = constraint.child_by_field_name("name")?;
12800 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
12801 let mut argument_cursor = constraint_arguments.walk();
12802 let constraint_types = constraint_arguments
12803 .named_children(&mut argument_cursor)
12804 .collect::<Vec<_>>();
12805 if constraint.kind() != "qualified_identifier"
12806 || constraint_template.kind() != "template_type"
12807 || !matches!(constraint_types.as_slice(), [left, right]
12808 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
12809 || constraint_arguments.has_error()
12810 || node
12811 .child_by_field_name("body")
12812 .is_none_or(|candidate| !same_node(candidate, body))
12813 {
12814 return None;
12815 }
12816
12817 let mut cursor = node.walk();
12818 let recovery_errors = node
12819 .named_children(&mut cursor)
12820 .filter(|child| child.kind() == "ERROR")
12821 .collect::<Vec<_>>();
12822 if !recovery_errors
12823 .iter()
12824 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
12825 || !recovery_errors.iter().all(|error| {
12826 error.named_child_count() == 0
12827 || cpp_reparsed_constraint_macro_error(*error, source)
12828 || (error.named_child_count() == 1
12829 && error
12830 .named_child(0)
12831 .is_some_and(|child| child.kind() == "function_declarator"))
12832 })
12833 {
12834 return None;
12835 }
12836
12837 let parameter_text = node_text(parameter_name, source);
12838 let mut declarators = node
12839 .child_by_field_name("declarator")
12840 .and_then(extract_function_declarator)
12841 .into_iter()
12842 .collect::<Vec<_>>();
12843 for error in recovery_errors {
12844 let mut stack = vec![error];
12845 while let Some(current) = stack.pop() {
12846 if current.kind() == "function_declarator" {
12847 declarators.push(current);
12848 }
12849 let mut cursor = current.walk();
12850 stack.extend(current.named_children(&mut cursor));
12851 }
12852 }
12853 declarators.into_iter().find(|declarator| {
12854 cpp_function_declarator_name_node(*declarator)
12855 .is_some_and(|name| name.kind() == "identifier")
12856 && declarator
12857 .child_by_field_name("parameters")
12858 .is_some_and(|parameters| {
12859 parameters
12860 .named_children(&mut parameters.walk())
12861 .filter_map(|parameter| parameter.child_by_field_name("type"))
12862 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
12863 })
12864 })
12865}
12866
12867fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
12868 node: Node<'_>,
12869 parameter_name: Node<'_>,
12870 source: &str,
12871) -> bool {
12872 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
12873}
12874
12875fn cpp_reparsed_template_macro_function_companion_is_indexable(
12876 node: Node<'_>,
12877 parameter_name: Node<'_>,
12878 source: &str,
12879) -> bool {
12880 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
12881 return false;
12882 }
12883 let Some(return_type) = node.child_by_field_name("type") else {
12884 return false;
12885 };
12886 let Some(function_declarator) = node
12887 .child_by_field_name("declarator")
12888 .and_then(extract_function_declarator)
12889 else {
12890 return false;
12891 };
12892 if cpp_function_declarator_name_node(function_declarator).is_none()
12893 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
12894 {
12895 return false;
12896 }
12897 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
12898 return false;
12899 };
12900 let parameter_text = node_text(parameter_name, source);
12901 parameters
12902 .named_children(&mut parameters.walk())
12903 .any(|parameter| {
12904 parameter
12905 .child_by_field_name("type")
12906 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
12907 })
12908}
12909
12910fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
12911 if node.kind() != "ERROR" {
12912 return false;
12913 }
12914 let mut stack = vec![node];
12915 while let Some(current) = stack.pop() {
12916 let macro_shape = match current.kind() {
12917 "call_expression" => current
12918 .child_by_field_name("function")
12919 .zip(current.child_by_field_name("arguments")),
12920 "init_declarator" => current
12921 .child_by_field_name("declarator")
12922 .zip(current.child_by_field_name("value")),
12923 _ => None,
12924 };
12925 if let Some((name, arguments)) = macro_shape
12926 && name.kind() == "identifier"
12927 && arguments.kind() == "argument_list"
12928 && arguments.named_child_count() >= 2
12929 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
12930 {
12931 return true;
12932 }
12933 let mut cursor = current.walk();
12934 stack.extend(current.named_children(&mut cursor));
12935 }
12936 false
12937}
12938
12939fn cpp_recovered_template_macro_constructor<'tree>(
12940 node: Node<'tree>,
12941 source: &str,
12942) -> Option<(Node<'tree>, Node<'tree>)> {
12943 let mut prefix = node.prev_named_sibling()?;
12944 while prefix.kind() == "comment" {
12945 prefix = prefix.prev_named_sibling()?;
12946 }
12947 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
12948 let parameter = parameter_name
12949 .parent()
12950 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
12951 let declarator =
12952 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
12953 Some((declarator, parameter))
12954}
12955
12956fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
12957 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
12958 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
12959 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
12960 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
12961 function,
12962 parameter_name,
12963 source,
12964 )
12965 });
12966 }
12967 let Some(parameter_name) =
12968 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
12969 else {
12970 return false;
12971 };
12972 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
12973 cpp_reparsed_template_macro_function_companion_is_indexable(
12974 function,
12975 parameter_name,
12976 source,
12977 )
12978 })
12979}
12980
12981fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
12982 let function_name = node
12983 .child_by_field_name("declarator")
12984 .and_then(extract_function_declarator)
12985 .and_then(cpp_function_declarator_name_node);
12986 if let Some(body) = cpp_reparsed_member_function_body(node)
12987 && function_name.is_some()
12988 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
12989 {
12990 return true;
12991 }
12992 cpp_reparsed_attribute_member_function(node, source)
12993 || cpp_reparsed_friend_function_is_indexable(node, source)
12994 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
12995 || cpp_reparsed_access_template_function_is_indexable(node, source)
12996 || cpp_recovered_template_macro_constructor(node, source).is_some()
12997}
12998
12999fn cpp_reparsed_macro_attribute_member_sequence(
13006 children: &[Node<'_>],
13007 index: usize,
13008 source: &str,
13009) -> bool {
13010 let Some(prefix) = children.get(index).copied() else {
13011 return false;
13012 };
13013 let declaration = if prefix.kind() == "labeled_statement" {
13014 prefix
13015 .named_child(prefix.named_child_count().saturating_sub(1))
13016 .filter(|child| child.kind() == "declaration")
13017 } else {
13018 (prefix.kind() == "declaration").then_some(prefix)
13019 };
13020 let Some(declaration) = declaration else {
13021 return false;
13022 };
13023 if !declaration.has_error()
13024 || declaration
13025 .child_by_field_name("declarator")
13026 .and_then(extract_function_declarator)
13027 .and_then(cpp_function_declarator_name_node)
13028 .is_none()
13029 {
13030 return false;
13031 }
13032 let Some(attribute_statement) = children.get(index + 1).copied() else {
13033 return false;
13034 };
13035 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
13036 .then(|| attribute_statement.named_child(0))
13037 .flatten()
13038 .filter(|child| child.kind() == "call_expression")
13039 else {
13040 return false;
13041 };
13042 let Some(attribute_name) = attribute_call
13043 .child_by_field_name("function")
13044 .filter(|function| function.kind() == "identifier")
13045 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
13046 else {
13047 return false;
13048 };
13049 if !cpp_export_macro_token(&attribute_name) {
13050 return false;
13051 }
13052 let Some(body) = children.get(index + 2).copied() else {
13053 return false;
13054 };
13055 body.kind() == "compound_statement"
13056 && body.child(0).is_some_and(|open| open.kind() == "{")
13057 && body
13058 .child(body.child_count().saturating_sub(1))
13059 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
13060 && declaration.end_byte() <= attribute_statement.start_byte()
13061 && attribute_statement.end_byte() <= body.start_byte()
13062}
13063
13064fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
13065 let mut cursor = root.walk();
13066 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
13067 let mut saw_member = false;
13068 let mut index = 0;
13069 while index < children.len() {
13070 let child = children[index];
13071 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
13072 saw_member = true;
13073 index += 3;
13074 continue;
13075 }
13076 if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
13077 let Some(tree) = cpp_reparse_fragmented_class_body(
13078 source,
13079 fragmented.reparse_start,
13080 fragmented.reparse_end,
13081 ) else {
13082 return false;
13083 };
13084 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
13085 return false;
13086 }
13087 saw_member = true;
13088 index += 1;
13089 while index < children.len()
13090 && children[index].end_byte() <= fragmented.class_range.end_byte
13091 {
13092 index += 1;
13093 }
13094 continue;
13095 }
13096 match child.kind() {
13097 "comment" => {}
13098 "labeled_statement" => saw_member = true,
13099 "function_definition" => {
13100 if child.has_error()
13101 && !cpp_reparsed_member_function_is_indexable(child, source)
13102 && cpp_sentinel_macro_region(child, source).is_none()
13103 {
13104 return false;
13105 }
13106 saw_member = true;
13107 }
13108 "ERROR"
13109 if (cpp_reparsed_member_error_is_indexable(child)
13110 || cpp_reparsed_adjacent_copy_control_error(child, source))
13111 && (child
13112 .next_named_sibling()
13113 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
13114 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
13115 {
13116 saw_member = true;
13117 }
13118 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
13119 saw_member = true;
13120 }
13121 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
13122 saw_member = true;
13123 }
13124 "expression_statement"
13125 if cpp_is_stray_semicolon(child, source)
13126 && child.prev_named_sibling().is_some_and(|error| {
13127 cpp_reparsed_member_error_is_indexable(error)
13128 || cpp_reparsed_adjacent_copy_control_error(error, source)
13129 }) =>
13130 {
13131 saw_member = true;
13132 }
13133 "compound_statement"
13134 if cpp_reparsed_constructor_body_is_indexable(child, source)
13135 || cpp_reparsed_attribute_requires_body(child, source) =>
13136 {
13137 saw_member = true;
13138 }
13139 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
13140 _ => return false,
13141 }
13142 index += 1;
13143 }
13144 saw_member
13145}
13146
13147fn cpp_reparsed_synthetic_initializer_constructor_range(
13155 root: Node<'_>,
13156 class_name: &str,
13157 source: &str,
13158 constructor_end: usize,
13159) -> Option<std::ops::Range<usize>> {
13160 let mut stack = {
13161 let mut cursor = root.walk();
13162 root.named_children(&mut cursor).collect::<Vec<_>>()
13163 };
13164 while let Some(current) = stack.pop() {
13165 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
13166 current,
13167 class_name,
13168 source,
13169 constructor_end,
13170 ) {
13171 return Some(range);
13172 }
13173 if current.kind() == "ERROR" {
13174 let mut cursor = current.walk();
13175 stack.extend(current.named_children(&mut cursor));
13176 }
13177 }
13178 None
13179}
13180
13181fn cpp_reparsed_merged_inline_constructor<'tree>(
13188 root: Node<'tree>,
13189 class_name: &str,
13190 source: &str,
13191) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
13192 let mut stack = vec![root];
13193 while let Some(current) = stack.pop() {
13194 if current.kind() != "labeled_statement" {
13195 let mut cursor = current.walk();
13196 stack.extend(current.named_children(&mut cursor));
13197 continue;
13198 }
13199 let declaration = current
13200 .named_children(&mut current.walk())
13201 .find(|child| child.kind() == "declaration")?;
13202 if declaration
13203 .child_by_field_name("type")
13204 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
13205 {
13206 continue;
13207 }
13208 let following = declaration
13209 .child_by_field_name("declarator")
13210 .and_then(extract_function_declarator)
13211 .and_then(cpp_function_declarator_name_node);
13212 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
13213 continue;
13214 }
13215 let mut declaration_cursor = declaration.walk();
13216 let Some(error) = declaration
13217 .named_children(&mut declaration_cursor)
13218 .find(|child| child.kind() == "ERROR")
13219 else {
13220 continue;
13221 };
13222 let mut error_cursor = error.walk();
13223 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
13224 let Some(constructor) = error_children.iter().copied().find(|child| {
13225 child.kind() == "function_declarator"
13226 && cpp_function_declarator_name_node(*child)
13227 .is_some_and(|name| node_text(name, source).trim() == class_name)
13228 }) else {
13229 continue;
13230 };
13231 let Some(body) = error_children.iter().copied().find_map(|child| {
13232 (child.kind() == "init_declarator")
13233 .then(|| child.child_by_field_name("value"))
13234 .flatten()
13235 .filter(|value| value.kind() == "initializer_list")
13236 }) else {
13237 continue;
13238 };
13239 if constructor.end_byte() > body.start_byte() {
13240 continue;
13241 }
13242 return Some((constructor.start_byte()..body.end_byte(), body));
13243 }
13244 None
13245}
13246
13247fn cpp_reparsed_synthetic_initializer_constructor(
13248 node: Node<'_>,
13249 class_name: &str,
13250 source: &str,
13251 constructor_end: usize,
13252) -> Option<std::ops::Range<usize>> {
13253 if node.kind() != "labeled_statement" {
13254 return None;
13255 }
13256 let mut cursor = node.walk();
13257 let named = node
13258 .named_children(&mut cursor)
13259 .filter(|child| child.kind() != "comment")
13260 .collect::<Vec<_>>();
13261 let label = named.first()?;
13262 if label.kind() != "statement_identifier"
13263 || !matches!(
13264 node_text(*label, source).trim(),
13265 "public" | "private" | "protected"
13266 )
13267 {
13268 return None;
13269 }
13270 let call_error_index = named.iter().position(|child| {
13271 if child.kind() != "ERROR" {
13272 return false;
13273 }
13274 let mut stack = vec![*child];
13275 while let Some(current) = stack.pop() {
13276 if current.kind() == "call_expression"
13277 && current
13278 .child_by_field_name("function")
13279 .is_some_and(|function| {
13280 function.kind() == "identifier"
13281 && node_text(function, source).trim() == class_name
13282 })
13283 {
13284 return true;
13285 }
13286 let mut cursor = current.walk();
13287 stack.extend(current.named_children(&mut cursor));
13288 }
13289 false
13290 })?;
13291 let constructor_call = {
13292 let mut stack = vec![named[call_error_index]];
13293 let mut found = None;
13294 while let Some(current) = stack.pop() {
13295 if current.kind() == "call_expression"
13296 && current
13297 .child_by_field_name("function")
13298 .is_some_and(|function| {
13299 function.kind() == "identifier"
13300 && node_text(function, source).trim() == class_name
13301 })
13302 {
13303 found = Some(current);
13304 break;
13305 }
13306 let mut cursor = current.walk();
13307 stack.extend(current.named_children(&mut cursor));
13308 }
13309 found
13310 };
13311 let constructor_call = constructor_call?;
13312 named.iter().skip(call_error_index + 1).find(|child| {
13313 child.kind() == "declaration" && child.has_error() && {
13314 let mut cursor = child.walk();
13315 child.named_children(&mut cursor).any(|declarator| {
13316 declarator.kind() == "init_declarator"
13317 && declarator
13318 .child_by_field_name("declarator")
13319 .is_some_and(|declarator| declarator.kind() == "function_declarator")
13320 && declarator
13321 .child_by_field_name("value")
13322 .is_some_and(|value| value.kind() == "initializer_list")
13323 })
13324 }
13325 })?;
13326 Some(constructor_call.start_byte()..constructor_end)
13327}
13328
13329fn cpp_reparsed_exact_constructor_declarator<'tree>(
13330 root: Node<'tree>,
13331 start: usize,
13332 class_name: &str,
13333 source: &str,
13334) -> Option<Node<'tree>> {
13335 let mut candidate = None;
13336 let mut stack = vec![root];
13337 while let Some(current) = stack.pop() {
13338 if current.kind() == "function_declarator"
13339 && current.start_byte() == start
13340 && cpp_function_declarator_name_node(current)
13341 .is_some_and(|name| node_text(name, source).trim() == class_name)
13342 {
13343 if candidate.is_some() {
13344 return None;
13345 }
13346 candidate = Some(current);
13347 continue;
13348 }
13349 let mut cursor = current.walk();
13350 stack.extend(current.named_children(&mut cursor));
13351 }
13352 candidate
13353}
13354
13355fn cpp_is_indexable_item_kind(kind: &str) -> bool {
13356 matches!(
13357 kind,
13358 "namespace_definition"
13359 | "class_specifier"
13360 | "struct_specifier"
13361 | "union_specifier"
13362 | "enum_specifier"
13363 | "function_definition"
13364 | "template_declaration"
13365 | "declaration"
13366 | "field_declaration"
13367 | "alias_declaration"
13368 | "static_assert_declaration"
13369 | "type_definition"
13370 | "using_declaration"
13371 | "linkage_specification"
13372 | "preproc_def"
13373 | "preproc_function_def"
13374 | "preproc_include"
13375 | "preproc_if"
13376 | "preproc_ifdef"
13377 | "preproc_call"
13378 )
13379}
13380
13381#[cfg(test)]
13382mod tests {
13383 use super::*;
13384 use crate::adapter::parse_cpp_file;
13385 use brokk_bifrost_core::analyzer::parsed_file::{
13386 finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
13387 start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
13388 };
13389 use std::fmt::Write;
13390
13391 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
13392 let mut parser = tree_sitter::Parser::new();
13393 parser
13394 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13395 .unwrap();
13396 let tree = parser.parse(source, None).unwrap();
13397 let file = ProjectFile::new(std::env::temp_dir(), name);
13398 parse_cpp_file(&file, source, &tree)
13399 }
13400
13401 #[test]
13402 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
13403 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
13404 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
13405 let mut macros = parsed
13406 .declarations()
13407 .iter()
13408 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
13409 .collect::<Vec<_>>();
13410 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
13411
13412 assert_eq!(macros.len(), 2, "{macros:#?}");
13413 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
13414 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
13415 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
13416 assert_eq!(
13417 parsed.declaration_ranges(macros[1])[0].start_byte,
13418 source.rfind("#define VALUE 2").expect("second definition")
13419 );
13420 }
13421
13422 #[test]
13423 fn identifies_export_macro_class_base_displaced_into_declarator() {
13424 let source = r#"#define PROJECT_API_
13425namespace project {
13426namespace internal {
13427template <typename T>
13428class Base {};
13429}
13430template <typename T>
13431class Wrapper;
13432template <>
13433class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
13434}
13435"#;
13436 let mut parser = tree_sitter::Parser::new();
13437 parser
13438 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13439 .unwrap();
13440 let tree = parser.parse(source, None).unwrap();
13441 let start = source.find("internal::Base<int>").expect("base");
13442 let mut base = tree
13443 .root_node()
13444 .descendant_for_byte_range(start, start + 8)
13445 .expect("base syntax");
13446 while base.kind() != "qualified_identifier" {
13447 base = base.parent().expect("qualified base ancestor");
13448 }
13449 assert!(
13450 is_recovered_exported_class_base_type_node(base, source),
13451 "{}",
13452 tree.root_node().to_sexp()
13453 );
13454 }
13455
13456 #[test]
13457 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
13458 let source = r#"namespace control {
13459template <typename T>
13460class AnySpan;
13461template <typename T>
13462class ABSL_ATTRIBUTE_VIEW AnySpan {
13463 public:
13464 int begin() const;
13465};
13466}
13467
13468namespace absl {
13469ABSL_NAMESPACE_BEGIN
13470template <typename T>
13471class ABSL_ATTRIBUTE_VIEW Span {
13472 public:
13473 int begin() const;
13474 int back() const;
13475};
13476
13477int begin();
13478int back();
13479}
13480"#;
13481 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
13482 let declarations = parsed.declarations();
13483 assert!(
13484 declarations
13485 .iter()
13486 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
13487 );
13488 for method in ["begin", "back"] {
13489 assert!(declarations.iter().any(|unit| {
13490 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
13491 }));
13492 assert!(
13493 declarations.iter().any(|unit| {
13494 unit.is_function() && unit.fq_name() == format!("absl.{method}")
13495 })
13496 );
13497 }
13498 assert!(
13499 declarations
13500 .iter()
13501 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
13502 );
13503 assert!(
13504 declarations
13505 .iter()
13506 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
13507 );
13508 assert!(
13509 declarations
13510 .iter()
13511 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
13512 );
13513 }
13514
13515 #[test]
13516 fn explicit_global_member_definition_has_canonical_package_boundary() {
13517 let source = r#"
13518namespace arangodb::aql {
13519class ExecutionPlan {
13520 public:
13521 template<class... Args> Node* createNode(Args&&... args);
13522};
13523}
13524
13525template<class... Args>
13526Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
13527"#;
13528 let parsed = parse_cpp_declarations(source, "global-member.cpp");
13529
13530 assert!(parsed.declarations().iter().any(|unit| {
13531 unit.is_function()
13532 && unit.package_name() == "arangodb::aql"
13533 && unit.short_name() == "ExecutionPlan.createNode"
13534 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
13535 }));
13536 }
13537
13538 #[test]
13539 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
13540 let source = r#"
13541#ifndef TINYXML2_INCLUDED
13542#define TINYXML2_INCLUDED
13543namespace tinyxml2 {
13544class TINYXML2_LIB XMLUtil {
13545 public:
13546 static const char* SkipWhiteSpace(const char* p) {
13547 while (*p) {
13548 if (*p == ' ') {
13549 ++p;
13550 }
13551 }
13552 return p;
13553 }
13554 static bool StringEqual(const char* p, const char* q) {
13555 return p == q;
13556 }
13557 class TINYXML2_LIB Helper {
13558 public:
13559 void Touch();
13560 };
13561 static void ToStr(int value, char* buffer);
13562 private:
13563 static const char* writeBoolTrue;
13564};
13565
13566class TINYXML2_LIB XMLNode {
13567 public:
13568 virtual XMLNode* ShallowClone() const = 0;
13569 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
13570};
13571}
13572#endif
13573"#;
13574 let mut parser = tree_sitter::Parser::new();
13575 parser
13576 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13577 .unwrap();
13578 let tree = parser.parse(source, None).unwrap();
13579 let mut boundary_found = false;
13580 walk_named_tree_preorder(tree.root_node(), true, |node| {
13581 if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
13582 && name == "XMLUtil"
13583 {
13584 boundary_found = fragmented_export_sibling_class_boundary(node, source)
13585 .and_then(|boundary| {
13586 recover_exported_class_function_definition(boundary, source)
13587 })
13588 .is_some_and(|(_, name, _)| name == "XMLNode");
13589 }
13590 WalkControl::Continue
13591 });
13592 assert!(
13593 boundary_found,
13594 "fixture must exercise the recovered sibling boundary"
13595 );
13596
13597 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
13598 assert!(
13599 parsed
13600 .declarations()
13601 .iter()
13602 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
13603 "{:#?}",
13604 parsed.declarations()
13605 );
13606 assert!(
13607 parsed
13608 .declarations()
13609 .iter()
13610 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
13611 "{:#?}",
13612 parsed.declarations()
13613 );
13614 assert!(parsed.declarations().iter().any(|unit| {
13615 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
13616 }));
13617 assert!(
13618 parsed
13619 .declarations()
13620 .iter()
13621 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
13622 );
13623 assert!(
13624 parsed
13625 .declarations()
13626 .iter()
13627 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
13628 );
13629 }
13630
13631 #[test]
13632 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
13633 let parsed = parse_cpp_declarations(
13637 r#"
13638namespace cwg311 {
13639namespace X { namespace Y {} }
13640namespace ::cwg311::X {}
13641}
13642"#,
13643 "explicit-global-namespace.cpp",
13644 );
13645
13646 assert!(parsed.declarations().iter().any(|unit| {
13647 unit.kind() == CodeUnitType::Module
13648 && unit.short_name() == "cwg311::X"
13649 && unit.fq_name() == "cwg311::X"
13650 }));
13651 assert!(
13652 parsed
13653 .declarations()
13654 .iter()
13655 .all(|unit| !unit.short_name().contains("::::")),
13656 "recovered namespace names must not retain empty scope components: {:#?}",
13657 parsed.declarations()
13658 );
13659 }
13660
13661 #[test]
13662 fn repeated_scope_separator_does_not_create_empty_function_owner() {
13663 let scope = ScopeInfo {
13664 package_name: "X".to_string(),
13665 module: None,
13666 class_unit: None,
13667 template_signature: None,
13668 template_metadata: None,
13669 declarations_are_fields: false,
13670 recovered_specialization_member_scope: false,
13671 visible_using_namespaces: Vec::new(),
13672 };
13673
13674 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
13675
13676 assert!(owner.is_none());
13677 assert_eq!(name, "doit");
13678 assert_eq!(package, "X");
13679 }
13680
13681 #[test]
13682 fn trailing_decltype_expression_is_not_a_function_declarator() {
13683 let source = r#"
13684namespace boost { namespace detail {
13685#if ! defined(BOOST_NO_SFINAE_EXPR) && \
13686 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
13687 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
13688#define BOOST_THREAD_PROVIDES_INVOKE
13689#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
13690template <class Fp, class A0, class ...Args>
13691inline auto
13692invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
13693 BOOST_THREAD_RV_REF(Args) ...args)
13694 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
13695{
13696 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
13697}
13698#endif
13699#endif
13700}}
13701"#;
13702 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
13703
13704 assert!(
13705 parsed
13706 .declarations()
13707 .iter()
13708 .all(|unit| unit.short_name() != ".*f")
13709 );
13710 }
13711
13712 fn find_class_named<'tree>(
13713 root: Node<'tree>,
13714 source: &str,
13715 expected_name: &str,
13716 ) -> Option<Node<'tree>> {
13717 let mut stack = vec![root];
13718 while let Some(node) = stack.pop() {
13719 if node.kind() == "class_specifier"
13720 && node
13721 .child_by_field_name("name")
13722 .is_some_and(|name| node_text(name, source) == expected_name)
13723 {
13724 return Some(node);
13725 }
13726 let mut cursor = node.walk();
13727 stack.extend(node.named_children(&mut cursor));
13728 }
13729 None
13730 }
13731
13732 #[test]
13733 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
13734 let source = r#"EXPORT void definition(struct Value value) {}
13735EXPORT void prototype(struct Value value);
13736"#;
13737 let mut parser = tree_sitter::Parser::new();
13738 parser
13739 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13740 .unwrap();
13741 let tree = parser.parse(source, None).unwrap();
13742 let root = tree.root_node();
13743 let mut cursor = root.walk();
13744 let callables = root
13745 .named_children(&mut cursor)
13746 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
13747 .collect::<Vec<_>>();
13748
13749 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
13750 for callable in callables {
13751 assert!(callable.has_error(), "fixture must exercise error recovery");
13752 assert!(
13753 cpp_sentinel_macro_parts(callable, source).is_none(),
13754 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
13755 );
13756 }
13757 }
13758
13759 #[test]
13760 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
13761 let source = r#"namespace absl {
13762ABSL_NAMESPACE_BEGIN
13763// Generate a floating-point variate conforming to a Beta distribution:
13764template <typename RealType = double>
13765class beta_distribution {
13766 public:
13767 using result_type = RealType;
13768
13769
13770 beta_distribution() : beta_distribution(1) {}
13771
13772 explicit beta_distribution(result_type alpha, result_type beta = 1)
13773 : param_(alpha, beta) {}
13774
13775 explicit beta_distribution(const param_type& p) : param_(p) {}
13776
13777 void reset() {}
13778
13779 // Generating functions
13780 template <typename URBG>
13781 result_type operator()(URBG& g) { // NOLINT(runtime/references)
13782 return (*this)(g, param_);
13783 }
13784
13785};
13786ABSL_NAMESPACE_END
13787} // namespace absl
13788"#;
13789 let mut parser = tree_sitter::Parser::new();
13790 parser
13791 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13792 .unwrap();
13793 let tree = parser.parse(source, None).unwrap();
13794 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
13795 let body = namespace
13796 .child_by_field_name("body")
13797 .expect("fixture namespace body");
13798 let sentinel = body.named_child(0).expect("sentinel envelope");
13799 let callable = sentinel
13800 .child_by_field_name("declarator")
13801 .and_then(extract_function_declarator)
13802 .and_then(cpp_function_declarator_name_node)
13803 .expect("preserved callable name");
13804
13805 assert_eq!(sentinel.kind(), "function_definition");
13806 assert_eq!(callable.kind(), "operator_name");
13807 assert!(
13808 cpp_sentinel_macro_parts(sentinel, source).is_some(),
13809 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
13810 );
13811 }
13812
13813 #[test]
13814 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
13815 let source = r#"namespace absl {
13816ABSL_NAMESPACE_BEGIN
13817// absl::discrete_distribution
13818//
13819// A discrete distribution produces random integers i, where 0 <= i < n
13820template <typename IntType = int>
13821class discrete_distribution {
13822 public:
13823 using result_type = IntType;
13824 class param_type {
13825 public:
13826 param_type() { init(); }
13827 template <typename InputIterator>
13828 explicit param_type(InputIterator begin, InputIterator end)
13829 : p_(begin, end) {
13830 init();
13831 }
13832 };
13833 discrete_distribution() : param_() {}
13834 explicit discrete_distribution(const param_type& p) : param_(p) {}
13835};
13836ABSL_NAMESPACE_END
13837} // namespace absl
13838"#;
13839 let mut parser = tree_sitter::Parser::new();
13840 parser
13841 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13842 .unwrap();
13843 let tree = parser.parse(source, None).unwrap();
13844 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
13845 let body = namespace
13846 .child_by_field_name("body")
13847 .expect("fixture namespace body");
13848 let sentinel = body.named_child(0).expect("sentinel envelope");
13849 let callable = sentinel
13850 .child_by_field_name("declarator")
13851 .and_then(extract_function_declarator)
13852 .and_then(cpp_function_declarator_name_node)
13853 .expect("preserved callable name");
13854
13855 assert_eq!(sentinel.kind(), "function_definition");
13856 assert_eq!(callable.kind(), "identifier");
13857 assert!(
13858 cpp_sentinel_macro_parts(sentinel, source).is_some(),
13859 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
13860 );
13861 }
13862
13863 #[test]
13864 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
13865 let source = r#"
13866#define CPPCHECKLIB
13867class Library {
13868 struct Container {
13869 CPPCHECKLIB static std::string toString(Yield yield);
13870 CPPCHECKLIB static std::string toString(Action action);
13871 };
13872};
13873"#;
13874 let mut parser = tree_sitter::Parser::new();
13875 parser
13876 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13877 .unwrap();
13878 let tree = parser.parse(source, None).unwrap();
13879 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
13880 let parsed = parse_cpp_file(&file, source, &tree);
13881 assert!(
13882 parsed
13883 .declarations()
13884 .iter()
13885 .all(|unit| unit.fq_name() != "Library$Container.std"),
13886 "the qualified return-type namespace must not become a field: {:#?}",
13887 parsed.declarations()
13888 );
13889 for expected in ["(Yield)", "(Action)"] {
13890 assert!(
13891 parsed.declarations().iter().any(|unit| {
13892 unit.is_function()
13893 && unit.fq_name() == "Library$Container.toString"
13894 && unit.signature() == Some(expected)
13895 }),
13896 "recovered toString overload {expected} is missing: {:#?}",
13897 parsed.declarations()
13898 );
13899 }
13900 }
13901
13902 #[test]
13903 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
13904 let source = r#"
13905#define SIMPLECPP_LIB
13906namespace simplecpp {
13907using TokenString = std::string;
13908struct Location { int line{}; };
13909class SIMPLECPP_LIB Token {
13910 TokenString prefix;
13911 void prefix_method() {}
13912 public:
13913 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
13914 whitespaceahead(wsahead), location(loc), string(s)
13915 // The comment must not hide the constructor body from recovery.
13916 {
13917 flags();
13918 }
13919 TokenString string;
13920 bool whitespaceahead;
13921 Location location;
13922 Token *previous{};
13923 private:
13924 void flags() {
13925 whitespaceahead = true;
13926 }
13927};
13928}
13929"#;
13930 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
13931
13932 let location_fields = parsed
13933 .declarations()
13934 .iter()
13935 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
13936 .collect::<Vec<_>>();
13937 assert_eq!(
13938 location_fields.len(),
13939 1,
13940 "location should have one class-owned declaration: {:#?}",
13941 parsed.declarations()
13942 );
13943 assert!(
13944 location_fields[0].is_field(),
13945 "location has wrong kind: {:#?}",
13946 parsed.declarations()
13947 );
13948 assert!(
13949 parsed.declarations().iter().all(|unit| {
13950 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
13951 })
13952 );
13953 assert!(
13954 parsed.declarations().iter().all(|unit| {
13955 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
13956 })
13957 );
13958 assert!(
13959 parsed
13960 .declarations()
13961 .iter()
13962 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
13963 );
13964 assert!(
13965 parsed
13966 .declarations()
13967 .iter()
13968 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
13969 "the recovered class must retain its constructor: {:#?}",
13970 parsed.declarations()
13971 );
13972 assert!(
13973 parsed
13974 .declarations()
13975 .iter()
13976 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
13977 );
13978 assert!(parsed.declarations().iter().any(|unit| {
13979 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
13980 }));
13981 let constructor = parsed
13982 .declarations()
13983 .iter()
13984 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
13985 .expect("recovered constructor");
13986 let constructor_start = source.find("Token(const").expect("constructor start");
13987 let constructor_end = source
13988 .get(
13989 ..source
13990 .find(" TokenString string;")
13991 .expect("constructor end"),
13992 )
13993 .expect("constructor slice")
13994 .trim_end()
13995 .len();
13996 assert!(
13997 parsed
13998 .navigation_ranges
13999 .get(constructor)
14000 .is_some_and(|ranges| {
14001 ranges.iter().any(|range| {
14002 range.start_byte == constructor_start && range.end_byte == constructor_end
14003 })
14004 }),
14005 "constructor navigation must span the full body: {:#?}",
14006 parsed.navigation_ranges
14007 );
14008 assert_eq!(
14009 parsed
14010 .signature_metadata
14011 .get(constructor)
14012 .and_then(|metadata| metadata.first())
14013 .and_then(SignatureMetadata::callable_linkage),
14014 Some(CallableLinkage::External)
14015 );
14016 let token_class = parsed
14017 .declarations()
14018 .iter()
14019 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
14020 .expect("recovered Token class");
14021 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
14022 assert!(
14023 parsed
14024 .navigation_ranges
14025 .get(token_class)
14026 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
14027 "class navigation must include the terminating semicolon: {:#?}",
14028 parsed.navigation_ranges
14029 );
14030 }
14031
14032 #[test]
14033 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
14034 let source = r#"
14035#define SIMPLECPP_LIB
14036namespace simplecpp {
14037using TokenString = std::string;
14038class Macro;
14039struct Location {
14040 unsigned int fileIndex{};
14041 unsigned int line{};
14042 unsigned int col{};
14043};
14044struct Output {
14045 int type;
14046};
14047class SIMPLECPP_LIB Token {
14048 public:
14049 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
14050 whitespaceahead(wsahead), location(loc), string(s) {
14051 flags();
14052 }
14053 Token(const Token &tok) :
14054 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
14055 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
14056 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
14057 Token &operator=(const Token &tok) = delete;
14058 const TokenString& str() const { return string; }
14059 void setstr(const std::string &s) { string = s; flags(); }
14060 bool isOneOf(const char ops[]) const;
14061 TokenString macro;
14062 char op;
14063 bool comment;
14064 bool name;
14065 bool number;
14066 bool whitespaceahead;
14067 Location location;
14068 Token *previous{};
14069 Token *next{};
14070 private:
14071 void flags() {
14072 name = !string.empty();
14073 comment = false;
14074 number = false;
14075 op = 0;
14076 }
14077 TokenString string;
14078};
14079}
14080struct Following {
14081 int type;
14082};
14083class SIMPLECPP_LIB Later {
14084 public:
14085 Later(int value) : value(value) {}
14086 int value;
14087};
14088"#;
14089 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
14090 assert!(
14091 parsed
14092 .declarations()
14093 .iter()
14094 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
14095 );
14096 assert!(
14097 !parsed
14098 .declarations()
14099 .iter()
14100 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
14101 );
14102 assert!(
14103 parsed
14104 .declarations()
14105 .iter()
14106 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
14107 );
14108 assert!(
14109 !parsed
14110 .declarations()
14111 .iter()
14112 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
14113 );
14114 assert!(
14115 parsed
14116 .declarations()
14117 .iter()
14118 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
14119 );
14120 assert!(
14121 parsed
14122 .declarations()
14123 .iter()
14124 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
14125 );
14126 assert!(
14127 parsed
14128 .declarations()
14129 .iter()
14130 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
14131 );
14132 assert!(
14133 parsed
14134 .declarations()
14135 .iter()
14136 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
14137 );
14138 assert!(
14139 parsed
14140 .declarations()
14141 .iter()
14142 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
14143 );
14144 assert!(
14145 parsed
14146 .declarations()
14147 .iter()
14148 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
14149 );
14150 assert!(parsed.declarations().iter().all(|unit| {
14151 !matches!(
14152 unit.fq_name().as_str(),
14153 "simplecpp.Token.Following" | "simplecpp.Token.Later"
14154 )
14155 }));
14156 assert!(
14157 !parsed
14158 .declarations()
14159 .iter()
14160 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
14161 "the following struct must remain outside the recovered Token class"
14162 );
14163 }
14164
14165 #[test]
14166 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
14167 let source = r#"
14168#define SIMPLECPP_LIB
14169namespace {
14170namespace simplecpp {
14171using TokenString = std::string;
14172struct Location { int line{}; };
14173class SIMPLECPP_LIB HiddenToken {
14174 public:
14175 HiddenToken(const TokenString &s, const Location &loc) :
14176 location(loc), string(s) {
14177 flags();
14178 }
14179 TokenString string;
14180 Location location;
14181 HiddenToken *previous{};
14182 private:
14183 void flags() {}
14184};
14185}
14186}
14187"#;
14188 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
14189 let constructor = parsed
14190 .declarations()
14191 .iter()
14192 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
14193 .expect("recovered anonymous-namespace constructor");
14194 assert_eq!(
14195 parsed
14196 .signature_metadata
14197 .get(constructor)
14198 .and_then(|metadata| metadata.first())
14199 .and_then(SignatureMetadata::callable_linkage),
14200 Some(CallableLinkage::Internal)
14201 );
14202 }
14203
14204 #[test]
14205 fn macro_qualified_static_field_keeps_real_declarator() {
14206 let source = r#"#define JSON_INLINE_VARIABLE
14207struct Reader {
14208static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
14209static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
14210static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
14211};"#;
14212 let mut parser = tree_sitter::Parser::new();
14213 parser
14214 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14215 .unwrap();
14216 let tree = parser.parse(source, None).unwrap();
14217 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
14218 let parsed = parse_cpp_file(&file, source, &tree);
14219 for expected in [
14220 "Reader.npos",
14221 "Reader.other",
14222 "Reader.pointer",
14223 "Reader.reference",
14224 ] {
14225 assert!(
14226 parsed
14227 .declarations()
14228 .iter()
14229 .any(|unit| unit.is_field() && unit.fq_name() == expected),
14230 "real macro-decorated field {expected} is missing: {:#?}",
14231 parsed.declarations()
14232 );
14233 }
14234 assert!(
14235 parsed
14236 .declarations()
14237 .iter()
14238 .all(|unit| unit.fq_name() != "Reader.std"),
14239 "qualified type prefix became a pseudo-field: {:#?}",
14240 parsed.declarations()
14241 );
14242 let root = tree.root_node();
14243 let mut stack = vec![root];
14244 let mut signatures = Vec::new();
14245 while let Some(current) = stack.pop() {
14246 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
14247 {
14248 signatures.extend(
14249 declarators
14250 .into_iter()
14251 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
14252 );
14253 }
14254 let mut cursor = current.walk();
14255 stack.extend(current.named_children(&mut cursor));
14256 }
14257 signatures.sort();
14258 assert_eq!(
14259 signatures,
14260 [
14261 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
14262 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
14263 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
14264 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
14265 ]
14266 );
14267 }
14268
14269 fn member_function_linkage(source: &str) -> CallableLinkage {
14270 let mut parser = tree_sitter::Parser::new();
14271 parser
14272 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14273 .unwrap();
14274 let tree = parser.parse(source, None).unwrap();
14275 let ancestry = ParentIndex::new(tree.root_node());
14276 let mut stack = vec![tree.root_node()];
14277 while let Some(node) = stack.pop() {
14278 if node.kind() == "function_definition" {
14279 let mut current = node.parent();
14280 while let Some(parent) = current {
14281 if matches!(
14282 parent.kind(),
14283 "class_specifier" | "struct_specifier" | "union_specifier"
14284 ) {
14285 return cpp_callable_linkage(node, source, &ancestry);
14286 }
14287 current = parent.parent();
14288 }
14289 }
14290 let mut cursor = node.walk();
14291 stack.extend(node.named_children(&mut cursor));
14292 }
14293 panic!("fixture has no member function definition");
14294 }
14295
14296 #[test]
14297 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
14298 assert_eq!(
14299 member_function_linkage("struct Named { int method() { return 1; } };"),
14300 CallableLinkage::External
14301 );
14302 assert_eq!(
14303 member_function_linkage(
14304 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
14305 ),
14306 CallableLinkage::Internal
14307 );
14308 assert_eq!(
14309 member_function_linkage("struct { int method() { return 1; } } instance;"),
14310 CallableLinkage::Internal
14311 );
14312 assert_eq!(
14313 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
14314 CallableLinkage::Internal
14315 );
14316 }
14317
14318 #[test]
14319 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
14320 let source = r#"
14321#ifndef PROTON_VALUE_HPP
14322#define PROTON_VALUE_HPP
14323namespace proton {
14324namespace internal {
14325class value_base {
14326 protected:
14327 internal::data& data();
14328 internal::data data_;
14329 friend class codec::encoder;
14330 friend class codec::decoder;
14331};
14332}
14333class value : public internal::value_base, private internal::comparable<value> {
14334 private:
14335 template<class T, class U=void> struct assignable :
14336 public std::enable_if<codec::is_encodable<T>::value, U> {};
14337 template<class U> struct assignable<value, U> {};
14338 public:
14339 PN_CPP_EXTERN value();
14340 PN_CPP_EXTERN value(const value&);
14341 PN_CPP_EXTERN value& operator=(const value&);
14342 PN_CPP_EXTERN value(value&&);
14343 PN_CPP_EXTERN value& operator=(value&&);
14344 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
14345 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
14346 codec::encoder e(*this);
14347 e << x;
14348 return *this;
14349 }
14350 PN_CPP_EXTERN type_id type() const;
14351 PN_CPP_EXTERN bool empty() const;
14352 PN_CPP_EXTERN void clear();
14353 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
14354 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
14355 friend PN_CPP_EXTERN void swap(value&, value&);
14356 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
14357 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
14358 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
14359 value(pn_data_t* d);
14360 void reset(pn_data_t* d = 0);
14361};
14362}
14363#endif
14364"#;
14365 let mut parser = tree_sitter::Parser::new();
14366 parser
14367 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14368 .unwrap();
14369 let tree = parser.parse(source, None).unwrap();
14370 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
14371 let parsed = parse_cpp_file(&file, source, &tree);
14372 let macro_constructors = parsed
14373 .signature_metadata
14374 .iter()
14375 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
14376 .flat_map(|(_, metadata)| metadata)
14377 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
14378 .collect::<Vec<_>>();
14379
14380 assert_eq!(
14381 macro_constructors.len(),
14382 3,
14383 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
14384 parsed.declarations()
14385 );
14386 assert!(
14387 macro_constructors.iter().all(|metadata| {
14388 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
14389 }),
14390 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
14391 );
14392 }
14393
14394 #[test]
14395 fn recovered_export_class_typedef_uses_displaced_alias_name() {
14396 let source = r#"
14397namespace spi {
14398class Filter {
14399public:
14400 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
14401};
14402}
14403namespace filter {
14404class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
14405{
14406public:
14407 typedef spi::Filter BASE_CLASS;
14408 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
14409 BEGIN_LOG4CXX_CAST_MAP()
14410 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
14411 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
14412 END_LOG4CXX_CAST_MAP()
14413 FilterDecision decide() const;
14414};
14415}
14416"#;
14417 let mut parser = tree_sitter::Parser::new();
14418 parser
14419 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14420 .unwrap();
14421 let tree = parser.parse(source, None).unwrap();
14422 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
14423 let parsed = parse_cpp_file(&file, source, &tree);
14424 assert!(
14425 parsed.declarations().iter().any(|unit| {
14426 unit.is_class()
14427 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
14428 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
14429 }),
14430 "the displaced typedef alias must retain its declared name: {:#?}",
14431 parsed.declarations()
14432 );
14433 assert!(
14434 parsed
14435 .declarations()
14436 .iter()
14437 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
14438 "the qualified underlying type must not become a false nested alias: {:#?}",
14439 parsed.declarations()
14440 );
14441 }
14442
14443 #[test]
14444 fn exported_single_base_recovery_uses_displaced_class_name() {
14445 let source = r#"
14446class CORE_EXPORT QgsPoint : public AbstractGeometry
14447{
14448 Q_GADGET
14449
14450 Q_PROPERTY( double x READ x WRITE setX )
14451 Q_PROPERTY( double y READ y WRITE setY )
14452 Q_PROPERTY( double z READ z WRITE setZ )
14453 Q_PROPERTY( double m READ m WRITE setM )
14454
14455 public:
14456#ifndef SIP_RUN
14457 QgsPoint(
14458 double x = std::numeric_limits<double>::quiet_NaN(),
14459 double y = std::numeric_limits<double>::quiet_NaN(),
14460 double z = std::numeric_limits<double>::quiet_NaN(),
14461 double m = std::numeric_limits<double>::quiet_NaN(),
14462 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
14463 );
14464#else
14465 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 )];
14466 % MethodCode
14467 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
14468 {
14469 int state;
14470 sipIsErr = 0;
14471 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
14472 if ( !sipIsErr )
14473 {
14474 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
14475 }
14476 sipReleaseType( p, sipType_QgsPointXY, state );
14477 }
14478 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
14479 {
14480 int state;
14481 sipIsErr = 0;
14482
14483 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
14484 if ( !sipIsErr )
14485 {
14486 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
14487 }
14488 sipReleaseType( p, sipType_QPointF, state );
14489 }
14490 else if (
14491 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
14492 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
14493 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
14494 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
14495 {
14496 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
14497 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
14498 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
14499 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
14500 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
14501 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
14502 }
14503 else // Invalid ctor arguments
14504 {
14505 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
14506 sipIsErr = 1;
14507 }
14508 % End
14509#endif
14510
14511 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
14512 explicit QgsPoint( QPointF p ) SIP_SKIP;
14513 explicit QgsPoint(
14514 Qgis::WkbType wkbType,
14515 double x = std::numeric_limits<double>::quiet_NaN(),
14516 double y = std::numeric_limits<double>::quiet_NaN(),
14517 double z = std::numeric_limits<double>::quiet_NaN(),
14518 double m = std::numeric_limits<double>::quiet_NaN()
14519 ) SIP_SKIP;
14520 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
14521 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
14522 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
14523#ifndef SIP_RUN
14524 private:
14525 bool fuzzyHelper(
14526 double epsilon,
14527 const AbstractGeometry &other,
14528 bool is3DFlag,
14529 bool isMeasureFlag
14530 ) const
14531 {
14532 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
14533 }
14534#endif
14535};
14536class Ordinary : public Base { public: Ordinary(); };
14537class API_EXPORT Plain { public: Plain(); };
14538class API_EXPORT : public Base {};
14539class
14540PN_CPP_CLASS_EXTERN Sender : public Link {
14541 Sender();
14542};
14543class thread_ctx_t {};
14544class ctx_t ZMQ_FINAL : public thread_ctx_t {
14545 bool start();
14546};
14547"#;
14548 let mut parser = tree_sitter::Parser::new();
14549 parser
14550 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14551 .unwrap();
14552 let tree = parser.parse(source, None).unwrap();
14553 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
14554 let parsed = parse_cpp_file(&file, source, &tree);
14555 let declarations = parsed.declarations();
14556
14557 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
14558 assert!(
14559 declarations
14560 .iter()
14561 .any(|unit| unit.is_class() && unit.fq_name() == expected),
14562 "missing recovered class {expected}: {declarations:#?}"
14563 );
14564 }
14565 let qgs_point = declarations
14566 .iter()
14567 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
14568 .expect("recovered QgsPoint class");
14569 assert_eq!(
14570 parsed.raw_supertypes.get(qgs_point),
14571 Some(&vec!["AbstractGeometry".to_string()]),
14572 "single-base export recovery must retain its displaced base"
14573 );
14574 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
14575 assert!(
14576 parsed
14577 .navigation_ranges
14578 .get(qgs_point)
14579 .is_some_and(|ranges| {
14580 !ranges.is_empty()
14581 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
14582 }),
14583 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
14584 parsed.navigation_ranges.get(qgs_point)
14585 );
14586 let sender = declarations
14587 .iter()
14588 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
14589 .expect("recovered Sender class");
14590 assert_eq!(
14591 parsed.raw_supertypes.get(sender),
14592 Some(&vec!["Link".to_string()]),
14593 "post-declarator export recovery must retain its displaced base"
14594 );
14595 let ctx = declarations
14596 .iter()
14597 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
14598 .expect("recovered ctx_t class");
14599 assert_eq!(
14600 parsed.raw_supertypes.get(ctx),
14601 Some(&vec!["thread_ctx_t".to_string()]),
14602 "postfix export-macro recovery must retain its displaced base"
14603 );
14604 assert!(
14605 declarations.iter().any(|unit| {
14606 unit.is_function()
14607 && unit.fq_name() == "QgsPoint.QgsPoint"
14608 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
14609 }),
14610 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
14611 );
14612 assert!(
14613 declarations.iter().all(|unit| {
14614 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
14615 }),
14616 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
14617 );
14618 }
14619
14620 #[test]
14621 fn function_like_export_macro_classes_keep_names_and_base_edges() {
14622 let source = r#"
14623namespace api {
14624class PROJECT_PUBLIC_API(2, 0) Prelude {
14625 public:
14626 Prelude();
14627};
14628class PROJECT_PUBLIC_API(2, 0) Base {
14629 public:
14630 Base(int value);
14631};
14632class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
14633 public:
14634 Derived(int value);
14635};
14636} // namespace api
14637"#;
14638 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
14639 let declarations = parsed.declarations();
14640 let base = declarations
14641 .iter()
14642 .find(|unit| unit.is_class() && unit.fq_name() == "api.Base")
14643 .expect("function-like export macro base class");
14644 let derived = declarations
14645 .iter()
14646 .find(|unit| unit.is_class() && unit.fq_name() == "api.Derived")
14647 .expect("function-like export macro derived class");
14648
14649 assert_eq!(
14650 parsed.raw_supertypes.get(derived),
14651 Some(&vec!["Base".to_string()])
14652 );
14653 assert!(
14654 declarations
14655 .iter()
14656 .all(|unit| unit.fq_name() != "PROJECT_PUBLIC_API"),
14657 "the export macro must not become a declaration: {declarations:#?}"
14658 );
14659 assert!(
14660 parsed
14661 .navigation_ranges
14662 .get(base)
14663 .is_some_and(|ranges| !ranges.is_empty()),
14664 "the recovered base must retain a navigable declaration range"
14665 );
14666 }
14667
14668 #[test]
14669 fn function_like_export_class_survives_a_preceding_malformed_body() {
14670 let source = r#"
14671namespace api {
14672class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
14673 public:
14674 /** Return a descriptive string. */
14675 const char* what() const noexcept override { return m_msg.c_str(); }
14676
14677 /** Return the type of error. */
14678 virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
14679
14680 /** Return an associated error code. */
14681 virtual int error_code() const noexcept { return 0; }
14682
14683 /** Avoid throwing the base directly. */
14684 explicit Exception(std::string_view msg);
14685
14686 /** Avoid throwing the base directly. */
14687 Exception(const char* prefix, std::string_view msg);
14688
14689 /** Avoid throwing the base directly. */
14690 Exception(std::string_view msg, const std::exception& e);
14691
14692 private:
14693 std::string m_msg;
14694};
14695
14696class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
14697 public:
14698 explicit Invalid_Argument(std::string_view msg);
14699
14700 explicit Invalid_Argument(std::string_view msg, std::string_view where);
14701
14702 Invalid_Argument(std::string_view msg, const std::exception& e);
14703
14704 ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
14705};
14706} // namespace api
14707"#;
14708 let mut parser = Parser::new();
14709 parser
14710 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14711 .expect("set C++ grammar");
14712 let tree = parser.parse(source, None).expect("parse fixture");
14713 let mut stack = vec![tree.root_node()];
14714 let mut saw_embedded_shape = false;
14715 while let Some(node) = stack.pop() {
14716 saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
14717 .iter()
14718 .any(|recovered| recovered.name == "Invalid_Argument");
14719 let mut cursor = node.walk();
14720 stack.extend(node.named_children(&mut cursor));
14721 }
14722 assert!(
14723 saw_embedded_shape,
14724 "fixture must retain the embedded error geometry: {}",
14725 tree.root_node().to_sexp()
14726 );
14727
14728 let parsed = parse_cpp_file(
14729 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
14730 source,
14731 &tree,
14732 );
14733 let declarations = parsed.declarations();
14734 let exception = declarations
14735 .iter()
14736 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
14737 .expect("qualified-base export class");
14738 let invalid = declarations
14739 .iter()
14740 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
14741 .expect("class embedded in the preceding malformed body");
14742
14743 assert_eq!(
14744 parsed.raw_supertypes.get(exception),
14745 Some(&vec!["std::exception".to_string()])
14746 );
14747 assert_eq!(
14748 parsed.raw_supertypes.get(invalid),
14749 Some(&vec!["Exception".to_string()])
14750 );
14751 assert!(
14752 parsed.materialization_records.iter().any(|record| matches!(
14753 record,
14754 MaterializationRecord::RecoveredDeclaration { unit, .. }
14755 if unit == invalid
14756 )),
14757 "the embedded class must retain recovery provenance: {:#?}",
14758 parsed.materialization_records
14759 );
14760 }
14761
14762 #[test]
14763 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
14764 let source = r#"
14765public:
14766 explicit Lookup_Error(std::string_view err) : Exception(err) {}
14767
14768 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
14769"#;
14770 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
14771 .expect("reparse merged constructor body");
14772 let (range, body) =
14773 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
14774 .unwrap_or_else(|| {
14775 panic!(
14776 "the merged constructor must retain its structured declarator/body: {}",
14777 tree.root_node().to_sexp()
14778 )
14779 });
14780 assert_eq!(
14781 source.get(range).expect("constructor range"),
14782 "Lookup_Error(std::string_view err) : Exception(err) {}"
14783 );
14784 assert_eq!(node_text(body, source), "{}");
14785 }
14786
14787 #[test]
14788 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
14789 let positive_source =
14790 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
14791 let mut parser = tree_sitter::Parser::new();
14792 parser
14793 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14794 .unwrap();
14795 let positive_tree = parser.parse(positive_source, None).unwrap();
14796 assert!(cpp_reparsed_members_are_indexable(
14797 positive_tree.root_node(),
14798 positive_source
14799 ));
14800
14801 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
14802 let negative_tree = parser.parse(negative_source, None).unwrap();
14803 assert!(!cpp_reparsed_members_are_indexable(
14804 negative_tree.root_node(),
14805 negative_source
14806 ));
14807 }
14808
14809 #[test]
14810 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
14811 let copy_control_source = r#"
14812public:
14813 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
14814 explicit Token(const Token* tok);
14815 ~Token();
14816 Token* astOperand1() { return nullptr; }
14817"#;
14818 let constraint_source = r#"
14819private:
14820 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
14821 static T *tokAtImpl(T *tok, int index) {
14822 return tok;
14823 }
14824
14825 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
14826 static T *linkAtImpl(T *tok, int index) {
14827 return tok;
14828 }
14829
14830public:
14831 int late() const { return 1; }
14832"#;
14833 let mut parser = tree_sitter::Parser::new();
14834 parser
14835 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14836 .unwrap();
14837 let copy_control_tree = parser
14838 .parse(copy_control_source, None)
14839 .expect("parse copy-control fixture");
14840 assert!(
14841 copy_control_tree.root_node().has_error(),
14842 "fixture must exercise adjacent copy-control recovery"
14843 );
14844 assert!(
14845 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
14846 "a complete late getter must remain recoverable after adjacent copy-control declarations"
14847 );
14848 let mut cursor = copy_control_tree.root_node().walk();
14849 assert!(
14850 copy_control_tree
14851 .root_node()
14852 .named_children(&mut cursor)
14853 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
14854 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
14855 copy_control_tree.root_node().to_sexp()
14856 );
14857 let constraint_tree = parser
14858 .parse(constraint_source, None)
14859 .expect("parse constraint-macro fixture");
14860 assert!(constraint_tree.root_node().has_error());
14861 assert!(
14862 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
14863 "complete constraint-macro members must not hide a later ordinary member"
14864 );
14865 let mut cursor = constraint_tree.root_node().walk();
14866 assert!(
14867 constraint_tree
14868 .root_node()
14869 .named_children(&mut cursor)
14870 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
14871 child,
14872 constraint_source
14873 )),
14874 "fixture must retain the split constraint-macro prefix/function geometry"
14875 );
14876 }
14877
14878 #[test]
14879 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
14880 let source = r#"
14881struct Analyzer {
14882 struct Action {
14883 Action() = default;
14884 Action(const Action&) = default;
14885 Action& operator=(const Action& rhs) & = default;
14886
14887 template<class T,
14888 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
14889 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
14890 // NOLINTNEXTLINE(google-explicit-constructor)
14891 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
14892 {}
14893
14894 enum : std::uint16_t { None = 0, Read = (1 << 0) };
14895 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
14896
14897 private:
14898 unsigned int mFlag{};
14899 };
14900
14901 enum class Direction : unsigned char { Forward, Reverse };
14902 virtual Action analyze(Direction d) const = 0;
14903};
14904"#;
14905 let mut parser = tree_sitter::Parser::new();
14906 parser
14907 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14908 .unwrap();
14909 let tree = parser.parse(source, None).unwrap();
14910 assert!(tree.root_node().has_error());
14911 let root = tree.root_node();
14912 let outer = root
14913 .named_children(&mut root.walk())
14914 .find(|child| child.kind() == "ERROR")
14915 .expect("fragmented Analyzer prefix");
14916 let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
14917 .expect("structured Analyzer fragment boundary");
14918 assert_eq!(outer_name, "Analyzer");
14919 let outer_tree = cpp_reparse_fragmented_class_body(
14920 source,
14921 outer_fragment.reparse_start,
14922 outer_fragment.reparse_end,
14923 )
14924 .expect("reparse Analyzer body");
14925 let outer_root = outer_tree.root_node();
14926 let action_prefix = outer_root
14927 .named_children(&mut outer_root.walk())
14928 .find(|child| child.kind() == "ERROR")
14929 .expect("fragmented Action prefix");
14930 let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
14931 .expect("structured Action fragment boundary");
14932 assert_eq!(action_name, "Action");
14933 let action_tree = cpp_reparse_fragmented_class_body(
14934 source,
14935 action_fragment.reparse_start,
14936 action_fragment.reparse_end,
14937 )
14938 .expect("reparse Action body");
14939 let action_root = action_tree.root_node();
14940 let macro_prefix = action_root
14941 .named_children(&mut action_root.walk())
14942 .find(|child| child.kind() == "ERROR")
14943 .expect("constraint macro prefix");
14944 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
14945 .expect("structured template macro prefix");
14946 let macro_companion =
14947 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
14948 assert!(
14949 cpp_reparsed_template_macro_constructor_companion_is_indexable(
14950 macro_companion,
14951 macro_parameter,
14952 source,
14953 ),
14954 "split constrained constructor must be admitted: {}",
14955 macro_companion.to_sexp()
14956 );
14957 assert!(
14958 cpp_reparsed_members_are_indexable(action_root, source),
14959 "complete Action body must pass the recovery gate: {}",
14960 action_tree.root_node().to_sexp()
14961 );
14962 assert!(
14963 cpp_reparsed_members_are_indexable(outer_root, source),
14964 "complete Analyzer body must pass the recovery gate: {}",
14965 outer_tree.root_node().to_sexp()
14966 );
14967 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
14968 let parsed = parse_cpp_file(&file, source, &tree);
14969 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
14970 assert!(
14971 parsed
14972 .declarations()
14973 .iter()
14974 .any(|unit| unit.fq_name() == expected),
14975 "missing recovered declaration {expected}: {:#?}",
14976 parsed.declarations()
14977 );
14978 }
14979 assert!(
14980 parsed
14981 .declarations()
14982 .iter()
14983 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
14984 "nested members must not remain flattened: {:#?}",
14985 parsed.declarations()
14986 );
14987 }
14988
14989 #[test]
14990 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
14991 let source = r#"
14992raw_hash_set& operator=(raw_hash_set&& that) {
14993 return move_assign(
14994 std::move(that),
14995 typename AllocTraits::propagate_on_container_move_assignment());
14996}
14997
14998iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
14999 return {};
15000}
15001
15002void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
15003
15004iterator insert(const_iterator hint, value_type&& value)
15005 ABSL_ATTRIBUTE_LIFETIME_BOUND {
15006 return {};
15007}
15008
15009friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
15010 return left.size() == right.size();
15011}
15012
15013static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
15014 return static_cast<slot_type*>(buffer);
15015}
15016
15017protected:
15018// Included-range recovery can attach this comment to the template prefix.
15019template <class K>
15020void AssertOnFind([[maybe_unused]] const K& key) {
15021 Check(key);
15022}
15023"#;
15024 let mut parser = tree_sitter::Parser::new();
15025 parser
15026 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15027 .unwrap();
15028 let tree = parser.parse(source, None).unwrap();
15029 assert!(
15030 tree.root_node().has_error(),
15031 "the fixture must exercise tree-sitter's errorful member shapes"
15032 );
15033 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
15034
15035 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
15036 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
15037 assert!(!cpp_reparsed_members_are_indexable(
15038 incomplete_tree.root_node(),
15039 incomplete_source
15040 ));
15041
15042 let outside_error_source = "int foo() stray_attribute {}\n";
15043 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
15044 assert!(outside_error_tree.root_node().has_error());
15045 assert!(!cpp_reparsed_members_are_indexable(
15046 outside_error_tree.root_node(),
15047 outside_error_source
15048 ));
15049
15050 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
15051 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
15052 assert!(!cpp_reparsed_members_are_indexable(
15053 variable_initializer_tree.root_node(),
15054 variable_initializer_source
15055 ));
15056 }
15057
15058 #[test]
15059 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
15060 let positive_source = r#"
15061std::pair<iterator, bool> insert(init_type&& value)
15062 ABSL_ATTRIBUTE_LIFETIME_BOUND
15063#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
15064 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
15065#endif
15066{
15067 return emplace(std::move(value));
15068}
15069"#;
15070 let mut parser = tree_sitter::Parser::new();
15071 parser
15072 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15073 .unwrap();
15074 let positive_tree = parser.parse(positive_source, None).unwrap();
15075 assert!(
15076 positive_tree.root_node().has_error(),
15077 "the fixture must exercise the split attribute/requires shape"
15078 );
15079 assert!(cpp_reparsed_members_are_indexable(
15080 positive_tree.root_node(),
15081 positive_source
15082 ));
15083
15084 let template_return_source = r#"
15085pair<int> insert(init_type&& value)
15086 ABSL_ATTRIBUTE_LIFETIME_BOUND
15087#if LANGUAGE_LEVEL >= 202002L
15088 requires(!Predicate<init_type>::value)
15089#endif
15090// Attributes and the function body may be separated by comments.
15091{
15092 return {};
15093}
15094"#;
15095 let template_return_tree = parser.parse(template_return_source, None).unwrap();
15096 assert!(
15097 cpp_reparsed_members_are_indexable(
15098 template_return_tree.root_node(),
15099 template_return_source
15100 ),
15101 "template-return attribute/requires tree: {}",
15102 template_return_tree.root_node().to_sexp()
15103 );
15104
15105 let no_body_source = r#"
15106std::pair<iterator, bool> insert(init_type&& value)
15107 ABSL_ATTRIBUTE_LIFETIME_BOUND
15108#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
15109 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
15110#endif
15111+ 0;
15112"#;
15113 let no_body_tree = parser.parse(no_body_source, None).unwrap();
15114 assert!(!cpp_reparsed_members_are_indexable(
15115 no_body_tree.root_node(),
15116 no_body_source
15117 ));
15118
15119 let extra_payload_source = r#"
15120pair<int> insert(init_type&& value)
15121 ABSL_ATTRIBUTE_LIFETIME_BOUND
15122#if LANGUAGE_LEVEL >= 202002L
15123 int unrelated;
15124 requires(Predicate<init_type>::value)
15125#endif
15126{
15127 return {};
15128}
15129"#;
15130 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
15131 assert!(!cpp_reparsed_members_are_indexable(
15132 extra_payload_tree.root_node(),
15133 extra_payload_source
15134 ));
15135
15136 let variable_initializer_source = r#"
15137int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
15138#if LANGUAGE_LEVEL >= 202002L
15139 requires(true)
15140#endif
15141{
15142 bad;
15143}
15144"#;
15145 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
15146 assert!(!cpp_reparsed_members_are_indexable(
15147 variable_initializer_tree.root_node(),
15148 variable_initializer_source
15149 ));
15150 }
15151
15152 #[test]
15153 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
15154 let source = r#"namespace absl {
15155ABSL_NAMESPACE_BEGIN namespace container_internal {
15156
15157class raw_hash_set : public Base {
15158 public:
15159 using value_type = int;
15160
15161 template <class U,
15162 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
15163 void insert(U value) { (void)value; }
15164
15165 struct InsertSlot {
15166 raw_hash_set& s;
15167 };
15168};
15169
15170}
15171ABSL_NAMESPACE_END
15172}"#;
15173 let mut parser = tree_sitter::Parser::new();
15174 parser
15175 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15176 .unwrap();
15177 let tree = parser.parse(source, None).unwrap();
15178 let root = tree.root_node();
15179 let outer_namespace = root
15180 .named_children(&mut root.walk())
15181 .find(|child| child.kind() == "namespace_definition")
15182 .expect("outer absl namespace");
15183 let declaration_list = outer_namespace
15184 .child_by_field_name("body")
15185 .expect("outer namespace body");
15186 let sentinel_function = declaration_list
15187 .named_children(&mut declaration_list.walk())
15188 .find(|child| child.kind() == "function_definition")
15189 .expect("malformed namespace sentinel function");
15190 let ancestry = ParentIndex::new(root);
15191 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
15192 .expect("structured nested namespace sentinel");
15193 let fragmented =
15194 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
15195 .expect("fragmented raw_hash_set class");
15196 assert_eq!(fragmented.class_node.kind(), "ERROR");
15197 assert_eq!(fragmented.name, "raw_hash_set");
15198 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
15199
15200 let outer_scope =
15201 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
15202 let mut outer_siblings = Vec::new();
15203 push_cpp_sentinel_sibling_classes(
15204 &mut outer_siblings,
15205 declaration_list,
15206 sentinel.function,
15207 &outer_scope,
15208 source,
15209 &ancestry,
15210 );
15211 let [outer_shadow] = outer_siblings.as_slice() else {
15212 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
15213 };
15214 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
15215 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
15216
15217 let field = " raw_hash_set& s;";
15218 let start = source.find(field).expect("InsertSlot field") + 4;
15219 let node = root
15220 .descendant_for_byte_range(start, start + "raw_hash_set".len())
15221 .expect("raw_hash_set type node");
15222 let recovered = cpp_sentinel_recovered_classes(root, source);
15223 let [deep_class] = recovered.as_slice() else {
15224 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
15225 };
15226 assert_eq!(
15227 deep_class.namespace_scope_components,
15228 vec!["absl", "container_internal"]
15229 );
15230 assert_eq!(
15231 deep_class.scope_components,
15232 vec!["absl", "container_internal", "raw_hash_set"]
15233 );
15234 assert!(
15235 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
15236 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
15237 );
15238
15239 assert_eq!(
15240 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
15241 Some(vec![
15242 "absl".to_string(),
15243 "container_internal".to_string(),
15244 "raw_hash_set".to_string(),
15245 "InsertSlot".to_string(),
15246 ])
15247 );
15248
15249 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
15250 let parsed = parse_cpp_file(&file, source, &tree);
15251 let raw_hash_set = parsed
15252 .declarations()
15253 .iter()
15254 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
15255 .expect("recovered raw_hash_set class");
15256 assert_eq!(
15257 raw_hash_set.fq_name(),
15258 "absl::container_internal.raw_hash_set",
15259 "the recovered declaration must publish under the deeper sentinel namespace"
15260 );
15261 assert_eq!(
15262 parsed.raw_supertypes.get(raw_hash_set),
15263 Some(&vec!["Base".to_string()]),
15264 "the structured base clause on the fragmented ERROR prefix must survive publication"
15265 );
15266 assert!(
15267 parsed.materialization_records.iter().any(|record| matches!(
15268 record,
15269 MaterializationRecord::RecoveredDeclaration { recovery, unit }
15270 if unit == raw_hash_set && *recovery == deep_class.class_range
15271 )),
15272 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
15273 parsed.materialization_records
15274 );
15275 }
15276
15277 #[test]
15304 fn the_parent_index_answers_what_tree_sitter_answers() {
15305 const SHAPES: [&str; 5] = [
15306 "namespace outer { namespace inner { struct Tag { int field; }; } }",
15307 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
15308 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
15309 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
15310 "class API Broken : public First, public Second {\n void member();\n",
15311 ];
15312 for source in SHAPES {
15313 let mut parser = tree_sitter::Parser::new();
15314 parser
15315 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15316 .unwrap();
15317 let tree = parser.parse(source, None).unwrap();
15318 let root = tree.root_node();
15319 let ancestry = ParentIndex::new(root);
15320 let mut nodes = 0usize;
15321 let mut stack = vec![root];
15322 while let Some(node) = stack.pop() {
15323 nodes += 1;
15324 assert_eq!(
15325 node.parent().map(|parent| parent.id()),
15326 ancestry.parent(node).map(|parent| parent.id()),
15327 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
15328 );
15329 let mut cursor = node.walk();
15330 stack.extend(node.children(&mut cursor));
15331 }
15332 assert!(nodes > 1, "{source:?} produced no tree to compare");
15333 }
15334 }
15335
15336 #[test]
15343 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
15344 const DEPTH: usize = 64;
15345 let mut source = String::new();
15346 for level in 0..DEPTH {
15347 writeln!(source, "namespace n{level} {{").unwrap();
15348 }
15349 source.push_str("int deepest(int value);\n");
15350 for _ in 0..DEPTH {
15351 source.push_str("}\n");
15352 }
15353
15354 let mut parser = tree_sitter::Parser::new();
15355 parser
15356 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15357 .unwrap();
15358 let tree = parser.parse(&source, None).unwrap();
15359 let root = tree.root_node();
15360 let ancestry = ParentIndex::new(root);
15361 let mut function_declarator = None;
15362 walk_named_tree_preorder(root, true, |node| {
15363 if node.kind() == "function_declarator" {
15364 function_declarator = Some(node);
15365 WalkControl::Break
15366 } else {
15367 WalkControl::Continue
15368 }
15369 });
15370 let function_declarator = function_declarator.expect("deepest function declarator");
15371 let ancestor_count =
15372 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
15373
15374 ancestry.reset_parent_query_count_for_test();
15375 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
15376 assert_eq!(DEPTH, lexical_scope.len());
15377 assert_eq!(
15378 ancestor_count + 1,
15379 ancestry.parent_query_count_for_test(),
15380 "lexical-scope ancestry bypassed the parent index"
15381 );
15382
15383 ancestry.reset_parent_query_count_for_test();
15384 assert_eq!(
15385 DispatchExtensibility::Closed,
15386 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
15387 );
15388 assert_eq!(
15389 ancestor_count,
15390 ancestry.parent_query_count_for_test(),
15391 "dispatch ancestry bypassed the parent index"
15392 );
15393
15394 ancestry.reset_parent_query_count_for_test();
15395 assert_eq!(
15396 CallableLinkage::External,
15397 cpp_callable_linkage(function_declarator, &source, &ancestry)
15398 );
15399 assert_eq!(
15400 ancestor_count + 1,
15401 ancestry.parent_query_count_for_test(),
15402 "linkage ancestry bypassed the parent index"
15403 );
15404
15405 ancestry.reset_parent_query_count_for_test();
15406 assert!(!cpp_callable_is_structural_constructor(
15407 function_declarator,
15408 &source,
15409 &ancestry
15410 ));
15411 assert_eq!(
15412 ancestor_count + 1,
15413 ancestry.parent_query_count_for_test(),
15414 "constructor ancestry bypassed the parent index"
15415 );
15416 }
15417
15418 #[test]
15423 fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
15424 for aggregates in [64usize, 512] {
15425 let mut source =
15426 String::from("typedef unsigned long long u64;\nnamespace generated {\n");
15427 for index in 0..aggregates {
15428 writeln!(source, "struct tag{index};").unwrap();
15429 }
15430 for index in (0..aggregates).rev() {
15431 writeln!(
15432 source,
15433 "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
15434 )
15435 .unwrap();
15436 }
15437 source.push_str("}\n");
15438
15439 start_code_unit_removal_scan_probe();
15440 let parsed = parse_cpp_declarations(&source, "vmlinux.h");
15441 let scanned = finish_code_unit_removal_scan_probe();
15442
15443 let expected_names: Vec<String> = (0..aggregates)
15444 .rev()
15445 .map(|index| format!("tag{index}"))
15446 .collect();
15447 let top_level_names: Vec<String> = parsed
15448 .top_level_declarations
15449 .iter()
15450 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
15451 .map(|unit| unit.short_name().to_string())
15452 .collect();
15453 let namespace = parsed
15454 .declarations()
15455 .iter()
15456 .find(|unit| {
15457 unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
15458 })
15459 .expect("generated namespace should be declared");
15460 let child_names: Vec<String> = parsed.children[namespace]
15461 .iter()
15462 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
15463 .map(|unit| unit.short_name().to_string())
15464 .collect();
15465 assert_eq!(
15466 aggregates,
15467 parsed
15468 .declarations()
15469 .iter()
15470 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
15471 .count(),
15472 "every aggregate must still be declared at {aggregates} aggregates"
15473 );
15474 assert_eq!(expected_names, top_level_names);
15475 assert_eq!(expected_names, child_names);
15476 assert_eq!(
15477 0, scanned,
15478 "replacing {aggregates} forward declarations must compact their shared lists once"
15479 );
15480 }
15481 }
15482
15483 #[test]
15484 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
15485 const DISTINCT_PER_KIND: usize = 64;
15486 let mut source = String::new();
15487 for index in 0..DISTINCT_PER_KIND {
15488 writeln!(source, "typedef int Alias{index};").unwrap();
15489 }
15490 writeln!(source, "typedef long Alias0;").unwrap();
15491 for index in 0..DISTINCT_PER_KIND {
15492 writeln!(source, "#define MACRO_{index} {index}").unwrap();
15493 }
15494 writeln!(source, "#define MACRO_0 duplicate").unwrap();
15495 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
15496
15497 let mut parser = tree_sitter::Parser::new();
15498 parser
15499 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15500 .unwrap();
15501 let tree = parser.parse(&source, None).unwrap();
15502 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
15503
15504 start_declaration_identity_comparison_probe();
15505 let parsed = parse_cpp_file(&file, &source, &tree);
15506 let comparisons = finish_declaration_identity_comparison_probe();
15507
15508 assert_eq!(
15509 DISTINCT_PER_KIND + 1,
15510 parsed
15511 .declarations()
15512 .iter()
15513 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
15514 .count(),
15515 "every physical typedef alias declaration must be retained so \
15516 conditional branch guards stay available to the resolver"
15517 );
15518 assert_eq!(
15519 DISTINCT_PER_KIND + 1,
15520 parsed
15521 .declarations()
15522 .iter()
15523 .filter(|unit| {
15524 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
15525 })
15526 .count(),
15527 "distinct macro redefinitions must remain available to temporal lookup"
15528 );
15529 assert_eq!(
15530 2,
15531 parsed
15532 .declarations()
15533 .iter()
15534 .filter(|unit| {
15535 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
15536 })
15537 .count(),
15538 "function overloads must remain distinct"
15539 );
15540
15541 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
15542 assert!(
15543 comparisons <= dedup_inputs * 4,
15544 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
15545 );
15546 }
15547
15548 #[test]
15549 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
15550 let source = r#"namespace absl {
15551ABSL_NAMESPACE_BEGIN namespace container_internal {
15552template <typename T>
15553class broken {
15554 public:
15555 using value_type = T;
15556 T operator->() const { return &operator*(); }
15557 using alias = value_type;
15558};
15559}
15560}
15561"#;
15562 let mut parser = tree_sitter::Parser::new();
15563 parser
15564 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15565 .unwrap();
15566 let tree = parser.parse(source, None).unwrap();
15567 let broken = find_class_named(tree.root_node(), source, "broken")
15568 .expect("the positive fixture must expose the broken class node");
15569 assert!(
15570 broken.has_error(),
15571 "the positive fixture must retain an internal parser error"
15572 );
15573 assert!(
15574 cpp_complete_class_body_close(broken).is_some(),
15575 "the positive fixture must expose a real class body close"
15576 );
15577 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15578 assert!(
15579 recovered.iter().any(|class| {
15580 class.scope_components == ["absl", "container_internal", "broken"]
15581 }),
15582 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
15583 );
15584 }
15585
15586 #[test]
15587 fn sentinel_recovery_keeps_members_after_nested_body_close() {
15588 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
15589NLOHMANN_BASIC_JSON_TPL_DECLARATION
15590class basic_json {
15591 private:
15592 union storage {
15593 int value;
15594 } data;
15595 public:
15596 using late_alias = int;
15597 late_alias value() const;
15598};
15599NLOHMANN_JSON_NAMESPACE_END
15600"#;
15601 let mut parser = tree_sitter::Parser::new();
15602 parser
15603 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15604 .unwrap();
15605 let tree = parser.parse(source, None).unwrap();
15606 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15607 let basic_json = recovered
15608 .iter()
15609 .find(|class| {
15610 class
15611 .scope_components
15612 .last()
15613 .is_some_and(|name| name == "basic_json")
15614 })
15615 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
15616 let late_alias = source
15617 .find("late_alias value")
15618 .expect("late alias reference");
15619 assert!(
15620 basic_json.class_range.start_byte < late_alias
15621 && late_alias < basic_json.class_range.end_byte,
15622 "the recovered class range must include members after a nested close: {basic_json:#?}"
15623 );
15624 }
15625
15626 #[test]
15627 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
15628 let source = r#"namespace absl {
15629ABSL_NAMESPACE_BEGIN namespace container_internal {
15630template <typename T>
15631class broken {
15632 public:
15633 using value_type = T;
15634 T operator->() const { return &operator*(); }
15635}
15636}
15637"#;
15638 let mut parser = tree_sitter::Parser::new();
15639 parser
15640 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15641 .unwrap();
15642 let tree = parser.parse(source, None).unwrap();
15643 let broken = find_class_named(tree.root_node(), source, "broken")
15644 .expect("the negative fixture must expose the malformed class node");
15645 assert!(
15646 broken.has_error(),
15647 "the negative fixture must retain a parser error"
15648 );
15649 assert!(
15650 cpp_complete_class_body_close(broken).is_none(),
15651 "the malformed class must not expose a real body close"
15652 );
15653 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15654 assert!(
15655 recovered
15656 .iter()
15657 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
15658 "an incomplete class must not borrow the namespace close: {recovered:#?}"
15659 );
15660 }
15661
15662 #[test]
15663 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
15664 let source = r#"namespace absl {
15665ABSL_NAMESPACE_BEGIN namespace container_internal {
15666template <typename T>
15667struct broken {
15668 using value_type = T;
15669};
15670}
15671
15672#ifdef OWNER_DEF
15673template <typename T>
15674typename broken<T>::value_type broken<T>::method() {
15675 value_type value{};
15676 return value;
15677}
15678#endif
15679
15680namespace sibling {
15681template <typename T>
15682typename broken<T>::value_type broken<T>::other() {
15683 value_type value{};
15684 return value;
15685}
15686}
15687
15688ABSL_NAMESPACE_END
15689}
15690"#;
15691 let mut parser = tree_sitter::Parser::new();
15692 parser
15693 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15694 .unwrap();
15695 let tree = parser.parse(source, None).unwrap();
15696 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15697 let broken = recovered
15698 .iter()
15699 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
15700 .expect("the sentinel class must be recovered");
15701 let method_start = source
15702 .find("typename broken<T>::value_type broken<T>::method()")
15703 .expect("guarded sibling owner");
15704 let method_end = source[method_start..]
15705 .find("\n}")
15706 .map(|offset| method_start + offset + 2)
15707 .expect("guarded sibling owner close");
15708 assert!(
15709 broken
15710 .owner_ranges
15711 .iter()
15712 .any(|owner| owner.range.start_byte <= method_start
15713 && method_end <= owner.range.end_byte),
15714 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
15715 );
15716 let sibling_start = source
15717 .find("typename broken<T>::value_type broken<T>::other()")
15718 .expect("nested namespace sibling owner");
15719 assert!(
15720 broken
15721 .owner_ranges
15722 .iter()
15723 .all(|owner| owner.range.start_byte > sibling_start
15724 || owner.range.end_byte <= sibling_start),
15725 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
15726 );
15727 }
15728
15729 #[test]
15730 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
15731 let source = r#"#ifdef OUTER
15732namespace absl {
15733ABSL_NAMESPACE_BEGIN namespace container_internal {
15734template <typename T>
15735struct broken {
15736 using value_type = T;
15737};
15738}
15739}
15740
15741#ifdef OWNER_DEF
15742template <typename T>
15743typename broken<T>::value_type broken<T>::method() {
15744 value_type value{};
15745 return value;
15746}
15747#endif
15748#endif
15749"#;
15750 let mut parser = tree_sitter::Parser::new();
15751 parser
15752 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15753 .unwrap();
15754 let tree = parser.parse(source, None).unwrap();
15755 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
15756 let broken = recovered
15757 .iter()
15758 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
15759 .expect("the sentinel class must be recovered");
15760 let method_start = source
15761 .find("typename broken<T>::value_type broken<T>::method()")
15762 .expect("outer sibling owner");
15763 assert!(
15764 broken
15765 .owner_ranges
15766 .iter()
15767 .all(|owner| owner.range.start_byte > method_start
15768 || owner.range.end_byte <= method_start),
15769 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
15770 );
15771 }
15772
15773 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
15775 let mut signatures = parsed
15776 .declarations()
15777 .iter()
15778 .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
15779 .filter_map(|unit| unit.signature().map(str::to_string))
15780 .collect::<Vec<_>>();
15781 signatures.sort();
15782 signatures.dedup();
15783 signatures
15784 }
15785
15786 #[test]
15787 fn callable_parameter_types_come_from_the_ast_parameter_list() {
15788 let source = r#"
15789template <typename T, ENABLE_BYTES(T)>
15790Vec256<T> DupOdd(Vec256<T> value) { return value; }
15791
15792struct Visitor {
15793 void fail(this auto const& self) {}
15794};
15795"#;
15796 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
15797 let dup_odd = parsed
15798 .declarations()
15799 .iter()
15800 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
15801 .expect("DupOdd declaration");
15802 assert_eq!(
15803 dup_odd.signature(),
15804 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
15805 );
15806 assert_eq!(
15807 parsed
15808 .signature_metadata
15809 .get(dup_odd)
15810 .and_then(|metadata| metadata.first())
15811 .and_then(SignatureMetadata::callable_parameter_types),
15812 Some(["Vec256<T>".to_string()].as_slice())
15813 );
15814
15815 let fail = parsed
15816 .declarations()
15817 .iter()
15818 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
15819 .expect("explicit-object member");
15820 assert_eq!(fail.signature(), Some("(const this auto &)"));
15821 let metadata = parsed
15822 .signature_metadata
15823 .get(fail)
15824 .and_then(|metadata| metadata.first())
15825 .expect("explicit-object signature metadata");
15826 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
15827 assert!(
15828 metadata
15829 .callable_arity()
15830 .is_some_and(|arity| arity.accepts(0))
15831 );
15832 }
15833
15834 #[test]
15835 fn trailing_qualifiers_survive_parameter_list_whitespace() {
15836 let source = r#"
15841struct Widget {
15842 bool multiline(int settings, int supprs) const;
15843 bool doublespace(int settings, int supprs) const;
15844 bool noexcept_multiline(int settings, int supprs) noexcept;
15845 bool ref_multiline(int settings, int supprs) &&;
15846};
15847bool
15848Widget::multiline (int settings,
15849 int supprs) const
15850{ return settings + supprs > 0; }
15851bool Widget::doublespace(int settings, int supprs) const { return true; }
15852bool Widget::noexcept_multiline(int settings,
15853 int supprs) noexcept { return true; }
15854bool Widget::ref_multiline(int settings,
15855 int supprs) && { return true; }
15856"#;
15857 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
15858 assert_eq!(
15859 vec!["(int, int) const".to_string()],
15860 identity_signatures(&parsed, "Widget.multiline")
15861 );
15862 assert_eq!(
15863 vec!["(int, int) const".to_string()],
15864 identity_signatures(&parsed, "Widget.doublespace")
15865 );
15866 assert_eq!(
15867 vec!["(int, int) noexcept".to_string()],
15868 identity_signatures(&parsed, "Widget.noexcept_multiline")
15869 );
15870 assert_eq!(
15871 vec!["(int, int) &&".to_string()],
15872 identity_signatures(&parsed, "Widget.ref_multiline")
15873 );
15874 }
15875
15876 #[test]
15877 fn macro_fragmented_plain_class_keeps_following_member_signature() {
15878 let source = r#"
15879struct CString {};
15880class CMessage {
15881public:
15882 CString GetParams(unsigned int index, unsigned int length = -1) const
15883 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
15884 return GetParamsColon(index, length);
15885 }
15886 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
15887};
15888CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
15889 return {};
15890}
15891"#;
15892 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
15893 assert_eq!(
15894 vec!["(unsigned int, unsigned int) const".to_string()],
15895 identity_signatures(&parsed, "CMessage.GetParamsColon")
15896 );
15897 }
15898
15899 #[test]
15900 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
15901 let source = r#"
15902#pragma once
15903#define DEMO_DEPRECATED(message)
15904namespace demo {
15905struct Base {
15906 static int aligned(int value) { return value; }
15907 int legacy(int value) const
15908 DEMO_DEPRECATED("use replacement()") { return value; }
15909 int replacement() const;
15910 void run(int value);
15911};
15912struct OtherBase {
15913 void run(int value);
15914 static int aligned(int value) { return value; }
15915};
15916struct Derived : Base {};
15917struct Override : Base {
15918 void run(int value);
15919 static int aligned(int value) { return value; }
15920};
15921struct RecoveredOverride : Base {
15922 int legacy(int value) const
15923 DEMO_DEPRECATED("use replacement()") { return value; }
15924 void run(int value);
15925};
15926struct Hidden : Base {
15927 void run(int first, int second);
15928 static int aligned(int first, int second) { return first + second; }
15929};
15930struct Ambiguous : Base, OtherBase {};
15931}
15932struct Global {};
15933"#;
15934 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
15935 let declarations = parsed.declarations();
15936 let fq_names = declarations
15937 .iter()
15938 .map(|unit| unit.fq_name())
15939 .collect::<std::collections::BTreeSet<_>>();
15940
15941 for expected in [
15942 "demo.Base",
15943 "demo.Base.aligned",
15944 "demo.Base.legacy",
15945 "demo.Base.replacement",
15946 "demo.Base.run",
15947 "demo.Derived",
15948 "demo.OtherBase",
15949 "demo.Override",
15950 "demo.RecoveredOverride",
15951 "demo.Hidden",
15952 "demo.Ambiguous",
15953 "Global",
15954 ] {
15955 assert!(
15956 fq_names.contains(expected),
15957 "missing {expected} from namespaced macro fragment: {declarations:#?}"
15958 );
15959 }
15960 assert!(
15961 !fq_names.contains("Derived"),
15962 "following class escaped its namespace: {declarations:#?}"
15963 );
15964 assert!(
15965 !fq_names.contains("demo.Global"),
15966 "global class crossed the recovered namespace boundary: {declarations:#?}"
15967 );
15968 }
15969
15970 #[test]
15971 fn trailing_qualifiers_still_separate_genuine_overloads() {
15972 let source = r#"
15975struct Widget {
15976 int* slot(int index);
15977 const int* slot(int index) const;
15978 int log(int severity) &;
15979 int log(int severity) &&;
15980};
15981"#;
15982 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
15983 assert_eq!(
15984 vec!["(int)".to_string(), "(int) const".to_string()],
15985 identity_signatures(&parsed, "Widget.slot")
15986 );
15987 assert_eq!(
15988 vec!["(int) &".to_string(), "(int) &&".to_string()],
15989 identity_signatures(&parsed, "Widget.log")
15990 );
15991 }
15992
15993 #[test]
15994 fn virtual_specifier_is_not_part_of_the_identity_signature() {
15995 let source = r#"
15998struct Base {
15999 virtual void run(int value) const;
16000};
16001struct Widget : Base {
16002 void run(int value) const override;
16003};
16004void Widget::run(int value) const {}
16005"#;
16006 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
16007 assert_eq!(
16008 vec!["(int) const".to_string()],
16009 identity_signatures(&parsed, "Widget.run")
16010 );
16011 }
16012
16013 #[test]
16014 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
16015 let source = r#"
16019struct Widget {
16020 bool value_params(const int settings, const int supprs);
16021 void pointee_const(const int* p);
16022 void pointer_const(int* const p);
16023 void both_const(const int* const p);
16024 void reference_const(const int& p);
16025 void array_const(const int values[4]);
16026};
16027bool Widget::value_params(int settings, int supprs) { return true; }
16028void Widget::pointer_const(int* p) {}
16029void Widget::both_const(const int* p) {}
16030"#;
16031 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
16032 assert_eq!(
16033 vec!["(int, int)".to_string()],
16034 identity_signatures(&parsed, "Widget.value_params")
16035 );
16036 assert_eq!(
16037 vec!["(int *)".to_string()],
16038 identity_signatures(&parsed, "Widget.pointer_const")
16039 );
16040 assert_eq!(
16041 vec!["(const int *)".to_string()],
16042 identity_signatures(&parsed, "Widget.both_const")
16043 );
16044 assert_eq!(
16046 vec!["(const int *)".to_string()],
16047 identity_signatures(&parsed, "Widget.pointee_const")
16048 );
16049 assert_eq!(
16050 vec!["(const int &)".to_string()],
16051 identity_signatures(&parsed, "Widget.reference_const")
16052 );
16053 assert_eq!(
16054 vec!["(const int [4])".to_string()],
16055 identity_signatures(&parsed, "Widget.array_const")
16056 );
16057 }
16058
16059 #[test]
16060 fn top_level_parameter_const_still_separates_pointee_overloads() {
16061 let source = r#"
16062struct Widget {
16063 void take(const int* p);
16064 void take(int* p);
16065};
16066"#;
16067 let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
16068 assert_eq!(
16069 vec!["(const int *)".to_string(), "(int *)".to_string()],
16070 identity_signatures(&parsed, "Widget.take")
16071 );
16072 }
16073
16074 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
16075 let mut parser = tree_sitter::Parser::new();
16076 parser
16077 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16078 .unwrap();
16079 let tree = parser.parse(source, None).unwrap();
16080 let start = source.find(callable_name).expect("callable declaration");
16081 let declarator =
16082 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
16083 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
16084 }
16085
16086 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
16087 let mut shapes = comparable_shapes(source, callable_name);
16088 assert_eq!(1, shapes.len(), "{shapes:?}");
16089 match shapes.remove(0) {
16090 CppComparableSlot::Shape(shape) => shape,
16091 other => panic!("expected a comparable shape, got {other:?}"),
16092 }
16093 }
16094
16095 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
16096 let mut current = shape.root();
16097 loop {
16098 match shape.node(current) {
16099 CppComparableNode::Named { .. } => return shape.node(current),
16100 CppComparableNode::Pointer { inner, .. }
16101 | CppComparableNode::Reference { inner }
16102 | CppComparableNode::Array { inner } => current = *inner,
16103 CppComparableNode::Generic { base, .. } => current = *base,
16104 }
16105 }
16106 }
16107
16108 #[test]
16109 fn comparable_shape_keeps_pointee_const() {
16110 assert_ne!(
16111 sole_comparable_shape("void f(const char* p);", "f("),
16112 sole_comparable_shape("void f(char* p);", "f(")
16113 );
16114 }
16115
16116 #[test]
16117 fn comparable_shape_keeps_inner_pointer_const() {
16118 assert_ne!(
16119 sole_comparable_shape("void f(int** p);", "f("),
16120 sole_comparable_shape("void f(int* const* p);", "f(")
16121 );
16122 }
16123
16124 #[test]
16125 fn comparable_shape_drops_top_level_pointer_const() {
16126 assert_eq!(
16127 sole_comparable_shape("void f(int* const p);", "f("),
16128 sole_comparable_shape("void f(int* p);", "f(")
16129 );
16130 }
16131
16132 #[test]
16133 fn comparable_shape_drops_top_level_base_const() {
16134 assert_eq!(
16135 sole_comparable_shape("void f(const int p);", "f("),
16136 sole_comparable_shape("void f(int p);", "f(")
16137 );
16138 }
16139
16140 #[test]
16141 fn comparable_shape_decays_top_level_array_to_pointer() {
16142 assert_eq!(
16143 sole_comparable_shape("void f(int a[3]);", "f("),
16144 sole_comparable_shape("void f(int* a);", "f(")
16145 );
16146 assert_eq!(
16147 sole_comparable_shape("void f(int* a[3]);", "f("),
16148 sole_comparable_shape("void f(int** a);", "f(")
16149 );
16150 }
16151
16152 #[test]
16153 fn comparable_shape_keeps_array_behind_pointer() {
16154 assert_ne!(
16155 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
16156 sole_comparable_shape("struct S { void f(int** a); };", "f(")
16157 );
16158 }
16159
16160 #[test]
16161 fn comparable_shape_records_written_name_and_lexical_scope() {
16162 let declared =
16163 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
16164 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
16165 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
16166 panic!("named leaf");
16167 };
16168 assert_eq!(["Msg".to_string()].as_slice(), name.path());
16169 assert_eq!(
16170 ["ns".to_string(), "S".to_string()].as_slice(),
16171 name.lexical_scope()
16172 );
16173 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
16174 panic!("named leaf");
16175 };
16176 assert_eq!(
16177 ["ns".to_string(), "Msg".to_string()].as_slice(),
16178 name.path()
16179 );
16180 assert!(name.lexical_scope().is_empty());
16181 assert_ne!(declared, defined);
16182 }
16183
16184 #[test]
16185 fn comparable_shape_marks_sized_primitive_leaf() {
16186 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
16187 let CppComparableNode::Named {
16188 name, primitive, ..
16189 } = comparable_named_leaf(&shape)
16190 else {
16191 panic!("named leaf");
16192 };
16193 assert!(primitive);
16194 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
16195 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
16196 }
16197
16198 #[test]
16199 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
16200 assert_eq!(
16201 vec![CppComparableSlot::Unstructured],
16202 comparable_shapes("void f(void (*cb)(int));", "f(")
16203 );
16204 }
16205
16206 #[test]
16207 fn comparable_shape_reports_ellipsis_slot() {
16208 let shapes = comparable_shapes("void f(int a, ...);", "f(");
16209 assert_eq!(2, shapes.len(), "{shapes:?}");
16210 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
16211 }
16212
16213 #[test]
16214 fn comparable_shape_keeps_template_argument_const() {
16215 assert_ne!(
16216 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
16217 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
16218 );
16219 }
16220
16221 #[test]
16224 fn c_file_mints_aggregate_member_tag_at_file_scope() {
16225 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
16226 let parsed = parse_cpp_declarations(source, "x.c");
16227 let declarations = parsed.declarations();
16228
16229 assert!(
16230 declarations
16231 .iter()
16232 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
16233 "expected a file-scope inner tag, got {declarations:?}"
16234 );
16235 assert!(
16236 declarations
16237 .iter()
16238 .all(|unit| unit.fq_name() != "outer$inner"),
16239 "expected no nested identity, got {declarations:?}"
16240 );
16241 assert!(
16242 declarations
16243 .iter()
16244 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
16245 );
16246 assert!(
16248 declarations
16249 .iter()
16250 .any(|unit| unit.fq_name() == "inner.value")
16251 );
16252 assert!(
16253 declarations
16254 .iter()
16255 .any(|unit| unit.fq_name() == "outer.item")
16256 );
16257
16258 let outer = declarations
16259 .iter()
16260 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
16261 .expect("outer");
16262 assert!(
16263 parsed
16264 .children
16265 .get(outer)
16266 .into_iter()
16267 .flatten()
16268 .all(|child| child.fq_name() != "inner"),
16269 "the tag must not hang off the aggregate it is written inside: {:?}",
16270 parsed.children
16271 );
16272 }
16273
16274 #[test]
16278 fn header_and_cpp_files_keep_nested_tag_identity() {
16279 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
16280 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
16281 let parsed = parse_cpp_declarations(source, name);
16282 let declarations = parsed.declarations();
16283 assert!(
16284 declarations
16285 .iter()
16286 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
16287 "{name} must keep the nested identity, got {declarations:?}"
16288 );
16289 assert!(
16290 declarations.iter().all(|unit| unit.fq_name() != "inner"),
16291 "{name} must not mint a file-scope tag, got {declarations:?}"
16292 );
16293 assert!(
16294 declarations
16295 .iter()
16296 .any(|unit| unit.fq_name() == "outer$inner.value")
16297 );
16298 }
16299 }
16300
16301 #[test]
16303 fn uppercase_c_extension_keeps_cpp_tag_scope() {
16304 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
16305 let parsed = parse_cpp_declarations(source, "x.C");
16306 assert!(
16307 parsed
16308 .declarations()
16309 .iter()
16310 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
16311 );
16312 }
16313
16314 #[test]
16317 fn c_file_mints_every_nesting_level_at_file_scope() {
16318 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
16319 let parsed = parse_cpp_declarations(source, "z.c");
16320 let declarations = parsed.declarations();
16321
16322 for tag in ["a", "b", "c"] {
16323 assert!(
16324 declarations
16325 .iter()
16326 .any(|unit| unit.is_class() && unit.fq_name() == tag),
16327 "expected a file-scope {tag}, got {declarations:?}"
16328 );
16329 }
16330 assert!(
16331 declarations
16332 .iter()
16333 .all(|unit| !unit.fq_name().contains('$')),
16334 "no level may keep a nested identity, got {declarations:?}"
16335 );
16336 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
16338 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
16339 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
16340 }
16341
16342 #[test]
16345 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
16346 let source = "struct outer { enum color { RED, GREEN } c; };\n";
16347 let parsed = parse_cpp_declarations(source, "e.c");
16348 let declarations = parsed.declarations();
16349
16350 let color = declarations
16351 .iter()
16352 .find(|unit| unit.is_class() && unit.fq_name() == "color")
16353 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
16354 assert!(
16355 declarations
16356 .iter()
16357 .all(|unit| unit.fq_name() != "outer$color")
16358 );
16359 for enumerator in ["color.RED", "color.GREEN"] {
16360 assert!(
16361 declarations.iter().any(|unit| unit.fq_name() == enumerator),
16362 "expected {enumerator}, got {declarations:?}"
16363 );
16364 }
16365 let children = parsed
16366 .children
16367 .get(color)
16368 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
16369 assert!(
16370 ["color.RED", "color.GREEN"]
16371 .iter()
16372 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
16373 "enumerators must hang off their enum: {children:?}"
16374 );
16375 }
16376
16377 #[test]
16378 fn c_file_mints_member_list_union_at_file_scope() {
16379 let source = "struct outer { union inner { int a; float b; } item; };\n";
16380 let parsed = parse_cpp_declarations(source, "u.c");
16381 let declarations = parsed.declarations();
16382 assert!(
16383 declarations
16384 .iter()
16385 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
16386 "expected a file-scope inner union, got {declarations:?}"
16387 );
16388 assert!(
16389 declarations
16390 .iter()
16391 .all(|unit| unit.fq_name() != "outer$inner")
16392 );
16393 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
16394 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
16395 }
16396
16397 #[test]
16400 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
16401 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
16402 let parsed = parse_cpp_declarations(source, "n.c");
16403 let declarations = parsed.declarations();
16404 let inner = declarations
16405 .iter()
16406 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
16407 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
16408 assert_eq!(inner.package_name(), "ns");
16409 assert!(
16410 declarations
16411 .iter()
16412 .all(|unit| unit.fq_name() != "ns.outer$inner")
16413 );
16414 }
16415
16416 #[test]
16421 fn function_local_tags_are_unchanged_in_both_dialects() {
16422 let source =
16423 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
16424 for name in ["y.c", "y.cpp"] {
16425 let parsed = parse_cpp_declarations(source, name);
16426 let declarations = parsed.declarations();
16427 assert!(
16428 declarations
16429 .iter()
16430 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
16431 "{name}: {declarations:?}"
16432 );
16433 for tag in ["localtag", "deeper", "localtag$deeper"] {
16434 assert!(
16435 declarations.iter().all(|unit| unit.fq_name() != tag),
16436 "{name} must not mint {tag}, got {declarations:?}"
16437 );
16438 }
16439 }
16440 }
16441
16442 #[test]
16445 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
16446 let source = "typedef struct { int v; } T;\n";
16447 for name in ["t.c", "t.cpp"] {
16448 let parsed = parse_cpp_declarations(source, name);
16449 let declarations = parsed.declarations();
16450 assert!(
16451 declarations
16452 .iter()
16453 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
16454 "{name}: {declarations:?}"
16455 );
16456 }
16457 }
16458
16459 #[test]
16460 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
16461 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
16462 let parsed = parse_cpp_declarations(source, "socket.c");
16463 let declarations = parsed.declarations();
16464 assert_eq!(
16465 declarations
16466 .iter()
16467 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
16468 .count(),
16469 1,
16470 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
16471 );
16472 for expected in [
16473 "PAL_HANDLE",
16474 "PAL_HANDLE.sock",
16475 "PAL_HANDLE$sock",
16476 "PAL_HANDLE$sock.ops",
16477 ] {
16478 assert!(
16479 declarations.iter().any(|unit| unit.fq_name() == expected),
16480 "expected {expected}, got {declarations:?}"
16481 );
16482 }
16483 }
16484
16485 #[test]
16488 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
16489 let source = "class outer { class inner { int v; }; };\n";
16490 let c_parsed = parse_cpp_declarations(source, "k.c");
16491 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
16492 let c_declarations = c_parsed.declarations();
16493 let cpp_declarations = cpp_parsed.declarations();
16494 assert!(
16495 c_declarations
16496 .iter()
16497 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
16498 "{c_declarations:?}"
16499 );
16500 assert_eq!(
16501 c_declarations
16502 .iter()
16503 .map(|unit| unit.fq_name())
16504 .collect::<std::collections::BTreeSet<_>>(),
16505 cpp_declarations
16506 .iter()
16507 .map(|unit| unit.fq_name())
16508 .collect::<std::collections::BTreeSet<_>>()
16509 );
16510 }
16511
16512 fn namespace_forward_scan_agreement(source: &str) -> usize {
16526 let mut parser = tree_sitter::Parser::new();
16527 parser
16528 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16529 .unwrap();
16530 let tree = parser.parse(source, None).unwrap();
16531 let root = tree.root_node();
16532 let ancestry = ParentIndex::new(root);
16533
16534 let mut nodes = Vec::new();
16535 let mut names = std::collections::BTreeSet::new();
16536 let mut cursor = root.walk();
16537 let mut stack = vec![root];
16538 while let Some(node) = stack.pop() {
16539 if matches!(
16540 node.kind(),
16541 "class_specifier" | "struct_specifier" | "union_specifier"
16542 ) && let Some(name) = class_like_name(node, source, &ancestry)
16543 {
16544 names.insert(name);
16545 }
16546 nodes.push(node);
16547 stack.extend(node.named_children(&mut cursor));
16548 }
16549 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
16550 assert!(!names.is_empty(), "fixture declares no class-like name");
16551
16552 let mut answered = 0usize;
16553 for reversed in [false, true] {
16554 let mut scan = CppNamespaceForwardScan::default();
16555 let ordered: Vec<_> = if reversed {
16556 nodes.iter().rev().copied().collect()
16557 } else {
16558 nodes.clone()
16559 };
16560 answered = 0;
16561 for node in ordered {
16562 for name in &names {
16563 scan.advance_to(root, node.start_byte(), source, &ancestry);
16564 let carried = scan.unique_earlier_forward(name, node);
16565 assert_eq!(
16566 carried,
16567 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
16568 "carried-forward scan and prefix scan disagree about {name} at \
16569 {} node starting at byte {} (reversed order: {reversed})",
16570 node.kind(),
16571 node.start_byte()
16572 );
16573 answered += usize::from(carried.is_some());
16574 }
16575 }
16576 }
16577 answered
16578 }
16579
16580 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
16587namespace ns {
16588class Widget;
16589class Gadget;
16590int x = ;
16591}
16592class API Widget {
16593public:
16594 void first();
16595};
16596class API Gadget {
16597public:
16598 void second();
16599};
16600"#;
16601
16602 #[test]
16603 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
16604 assert!(
16605 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
16606 "the fixture must actually reach the namespace-borrow path"
16607 );
16608
16609 for source in [
16614 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
16615 r#"#define API
16616namespace ns {
16617class Widget;
16618class Widget;
16619int x = ;
16620}
16621class API Widget {
16622public:
16623 void method();
16624};
16625"#,
16626 r#"#define API
16627namespace ns {
16628void host() {
16629 class Widget;
16630}
16631int x = ;
16632}
16633class API Widget {
16634public:
16635 void method();
16636};
16637"#,
16638 ] {
16639 assert_eq!(
16640 namespace_forward_scan_agreement(source),
16641 0,
16642 "no borrow is justified here: {source}"
16643 );
16644 }
16645 }
16646
16647 #[test]
16651 fn carried_forward_namespace_scan_folds_each_node_once() {
16652 let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
16653 let mut parser = tree_sitter::Parser::new();
16654 parser
16655 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16656 .unwrap();
16657 let tree = parser.parse(source, None).unwrap();
16658 let root = tree.root_node();
16659 let ancestry = ParentIndex::new(root);
16660
16661 let mut incremental = CppNamespaceForwardScan::default();
16662 for cutoff in 0..=source.len() {
16663 incremental.advance_to(root, cutoff, source, &ancestry);
16664 }
16665 let mut whole = CppNamespaceForwardScan::default();
16666 whole.advance_to(root, source.len(), source, &ancestry);
16667
16668 let mut incremental_shape: Vec<_> = incremental
16669 .forwards
16670 .iter()
16671 .map(|(name, forwards)| {
16672 (
16673 name.clone(),
16674 forwards
16675 .iter()
16676 .map(|forward| (forward.start_byte, forward.package_name.clone()))
16677 .collect::<Vec<_>>(),
16678 )
16679 })
16680 .collect();
16681 let mut whole_shape: Vec<_> = whole
16682 .forwards
16683 .iter()
16684 .map(|(name, forwards)| {
16685 (
16686 name.clone(),
16687 forwards
16688 .iter()
16689 .map(|forward| (forward.start_byte, forward.package_name.clone()))
16690 .collect::<Vec<_>>(),
16691 )
16692 })
16693 .collect();
16694 incremental_shape.sort();
16695 whole_shape.sort();
16696 for (_, forwards) in &mut incremental_shape {
16697 forwards.sort();
16698 }
16699 for (_, forwards) in &mut whole_shape {
16700 forwards.sort();
16701 }
16702
16703 assert!(!whole_shape.is_empty(), "fixture folds no forward");
16704 assert_eq!(
16705 incremental_shape, whole_shape,
16706 "one byte at a time must fold exactly what one whole pass folds"
16707 );
16708 }
16709
16710 fn fragmented_class_reparse_agreement(body: &str) {
16720 for prefix in [
16721 String::new(),
16722 "// leading comment\n".to_string(),
16723 "class Widget : public Base { ".to_string(),
16728 "namespace filler {\n".to_string()
16729 + &"struct Filler { int member; };\n".repeat(200)
16730 + "}\n",
16731 "namespace filler {\n".to_string()
16732 + &"struct Filler { int member; };\n".repeat(200)
16733 + "}\nclass Widget : public Base { ",
16734 ] {
16735 let source = format!("{prefix}{body}");
16736 let start = prefix.len();
16737 let end = source.len();
16738 let region = cpp_reparse_fragmented_class_body(&source, start, end)
16739 .expect("the region reparse must produce a tree");
16740 let padded = cpp_reparse_padded_class_body(&source, start, end)
16741 .expect("the padded reparse must produce a tree");
16742 assert_eq!(
16743 cpp_tree_shape(®ion),
16744 cpp_tree_shape(&padded),
16745 "region and padded reparse disagree at offset {start} of {end} bytes"
16746 );
16747 assert_eq!(
16748 region.root_node().start_byte(),
16749 start,
16750 "the reparsed region keeps its original offsets"
16751 );
16752 }
16753 }
16754
16755 #[test]
16756 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
16757 fragmented_class_reparse_agreement(
16761 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
16762 );
16763 fragmented_class_reparse_agreement(
16764 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
16765 );
16766 fragmented_class_reparse_agreement(
16769 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
16770 );
16771 fragmented_class_reparse_agreement(
16772 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
16773 );
16774 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
16777 }
16778
16779 fn many_enums_and_mixed_declarations() -> String {
16783 let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
16784 for index in 0..40 {
16785 let _ = write!(
16786 source,
16787 "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
16788 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
16789 class Color{index}Like {{ public: int member{index}; }};\n"
16790 );
16791 }
16792 source.push_str("namespace outer {\n");
16793 for index in 0..20 {
16794 let _ = write!(
16795 source,
16796 "enum Shade{index} {{ DARK{index} }};\n\
16797 struct Shade{index}Holder {{ int field{index}; }};\n"
16798 );
16799 }
16800 source.push_str("}\n");
16801 source
16802 }
16803
16804 fn field_owner_index_agreement(source: &str, name: &str) -> usize {
16820 let parsed = parse_cpp_declarations(source, name);
16821 let file = ProjectFile::new(std::env::temp_dir(), name);
16822 let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
16823
16824 let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
16825 declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
16826
16827 let foreign: Vec<CodeUnit> = declarations
16828 .iter()
16829 .filter(|unit| unit.kind() == CodeUnitType::Field)
16830 .map(|unit| {
16831 CodeUnit::new_fq(
16832 elsewhere.clone(),
16833 unit.kind(),
16834 unit.package_name().to_string(),
16835 unit.short_name().to_string(),
16836 unit.fq().clone(),
16837 )
16838 })
16839 .collect();
16840
16841 let mut packages: Vec<String> = declarations
16847 .iter()
16848 .map(|unit| unit.package_name().to_string())
16849 .collect();
16850 packages.push(String::new());
16851 packages.sort();
16852 packages.dedup();
16853 let deeper: Vec<CodeUnit> = packages
16854 .iter()
16855 .map(|package_name| {
16856 CodeUnit::new_fq(
16857 file.clone(),
16858 CodeUnitType::Field,
16859 package_name.clone(),
16860 "SynthOwner.middle.leaf".to_string(),
16861 cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
16862 )
16863 })
16864 .collect();
16865
16866 let mut questions: Vec<(String, String)> = Vec::new();
16870 for unit in declarations.iter().chain(deeper.iter()) {
16871 let package_name = unit.package_name().to_string();
16872 let short_name = unit.short_name();
16873 questions.push((package_name.clone(), short_name.to_string()));
16874 questions.push((package_name.clone(), String::new()));
16875 for (offset, _) in short_name.match_indices('.') {
16876 questions.push((package_name.clone(), short_name[..offset].to_string()));
16877 }
16878 }
16879 questions.sort();
16880 questions.dedup();
16881
16882 let mut index = CppFieldOwnerIndex::default();
16883 let mut recorded: Vec<&CodeUnit> = Vec::new();
16884 let mut answered = 0usize;
16885 for unit in foreign
16886 .iter()
16887 .chain(declarations.iter())
16888 .chain(deeper.iter())
16889 {
16890 index.record(unit, &file);
16891 recorded.push(unit);
16892 for (package_name, owner_short_name) in &questions {
16893 let carried = index.owns_fields(package_name, owner_short_name);
16894 assert_eq!(
16895 carried,
16896 cpp_declarations_hold_owned_fields(
16897 recorded.iter().copied(),
16898 &file,
16899 package_name,
16900 owner_short_name
16901 ),
16902 "the carried field index and the declaration scan disagree about \
16903 {package_name:?}/{owner_short_name:?} after recording {}",
16904 unit.fq_name()
16905 );
16906 answered += usize::from(carried);
16907 }
16908 }
16909
16910 let rebuilt = CppFieldOwnerIndex::of(
16913 foreign
16914 .iter()
16915 .chain(declarations.iter())
16916 .chain(deeper.iter()),
16917 &file,
16918 );
16919 for (package_name, owner_short_name) in &questions {
16920 assert_eq!(
16921 rebuilt.owns_fields(package_name, owner_short_name),
16922 index.owns_fields(package_name, owner_short_name),
16923 "a rebuilt index must answer what the incremental one answers for \
16924 {package_name:?}/{owner_short_name:?}"
16925 );
16926 }
16927 answered
16928 }
16929
16930 #[test]
16939 fn a_replacement_that_removes_children_drops_the_field_index() {
16940 let source =
16941 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
16942 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
16943 let mut names: Vec<_> = parsed
16944 .declarations()
16945 .iter()
16946 .map(|unit| unit.fq_name())
16947 .collect();
16948 names.sort();
16949 assert_eq!(
16950 names,
16951 vec![
16952 "Color".to_string(),
16953 "First".to_string(),
16954 "First.A".to_string()
16955 ],
16956 "the replaced Color owns no field any more"
16957 );
16958 }
16959
16960 #[test]
16969 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
16970 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
16971 BEGIN_NS\n\
16972 namespace demo { struct Widget { void doWork(); }; }\n\
16973 END_NS\n";
16974 let parsed = parse_cpp_declarations(source, "restored.cpp");
16975 let recovered: Vec<String> = parsed
16976 .materialization_records
16977 .iter()
16978 .filter_map(|record| match record {
16979 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
16980 _ => None,
16981 })
16982 .collect();
16983 assert!(
16984 recovered.is_empty(),
16985 "the region declares nothing the file did not already declare: {recovered:?}"
16986 );
16987 let mut names: Vec<String> = parsed
16988 .declarations()
16989 .iter()
16990 .map(|unit| unit.fq_name())
16991 .collect();
16992 names.sort();
16993 assert_eq!(
16994 names,
16995 vec![
16996 "demo".to_string(),
16997 "demo.Widget".to_string(),
16998 "demo.Widget.doWork".to_string(),
16999 ]
17000 );
17001 }
17002
17003 #[test]
17008 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
17009 let mut source = String::new();
17010 for index in 0..4 {
17011 let _ = write!(
17012 source,
17013 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
17014 );
17015 }
17016 source.push_str("void outside() {}\n");
17017 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
17018
17019 let recovered: Vec<(String, (usize, usize))> = parsed
17020 .materialization_records
17021 .iter()
17022 .filter_map(|record| match record {
17023 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
17024 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
17025 }
17026 _ => None,
17027 })
17028 .collect();
17029
17030 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
17031 for index in 0..4 {
17032 let region = format!("namespace demo{index}");
17035 let region_start = source.find(®ion).expect("each region is in the source");
17036 let start = source[..region_start]
17037 .rfind("BEGIN_NS")
17038 .expect("each region opens with a sentinel")
17039 + "BEGIN_NS".len();
17040 let end = start
17041 + source[start..]
17042 .find("END_NS")
17043 .expect("each region closes with a sentinel")
17044 - 1;
17045 let window = (start, end);
17046 for name in [
17047 format!("demo{index}"),
17048 format!("demo{index}.Widget{index}"),
17049 format!("demo{index}.Widget{index}.doWork{index}"),
17050 ] {
17051 expected.push((name, window));
17052 }
17053 }
17054 assert_eq!(
17055 recovered, expected,
17056 "each recovery records its own minted declarations, in order"
17057 );
17058 assert!(
17059 parsed
17060 .declarations()
17061 .iter()
17062 .any(|unit| unit.fq_name() == "outside"),
17063 "the declaration outside every region stays parsed and unrecovered"
17064 );
17065 }
17066
17067 #[test]
17068 fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
17069 assert!(
17070 field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
17071 "the fixture must actually own fields"
17072 );
17073
17074 for (source, name) in [
17080 ("struct S { enum E { V }; };\n", "nested.hpp"),
17081 (
17082 "enum Color { RED };\nstruct Color { int RED; };\n",
17083 "class-like.c",
17084 ),
17085 ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
17086 (
17087 "enum E { V };\nnamespace ns { enum E { V }; }\n",
17088 "repeated.hpp",
17089 ),
17090 ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
17091 ] {
17092 field_owner_index_agreement(source, name);
17093 }
17094 }
17095}