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
1419struct StrandedRun<'tree> {
1423 declarations: Vec<MacroWrappedDeclaration<'tree>>,
1424 complete: bool,
1430}
1431
1432struct MacroWrappedDeclaration<'tree> {
1433 declarator: Node<'tree>,
1434 range: Range,
1435 is_static: bool,
1440}
1441
1442fn is_declaration_scope_position(node: Node<'_>) -> bool {
1445 let Some(parent) = node.parent() else {
1446 return false;
1447 };
1448 match parent.kind() {
1449 "translation_unit" => true,
1450 "declaration_list" => parent.parent().is_some_and(|grandparent| {
1451 matches!(
1452 grandparent.kind(),
1453 "namespace_definition" | "linkage_specification"
1454 )
1455 }),
1456 _ => false,
1457 }
1458}
1459
1460fn is_declaration_scope_error(node: Node<'_>) -> bool {
1462 node.kind() == "ERROR" && is_declaration_scope_position(node)
1463}
1464
1465fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1470 matches!(
1471 node.kind(),
1472 "identifier"
1473 | "type_identifier"
1474 | "primitive_type"
1475 | "sized_type_specifier"
1476 | "struct_specifier"
1477 | "union_specifier"
1478 | "enum_specifier"
1479 | "type_qualifier"
1480 | "storage_class_specifier"
1481 | "explicit_function_specifier"
1482 | "virtual_function_specifier"
1483 | "qualified_identifier"
1484 | "template_type"
1485 | "dependent_type"
1486 | "placeholder_type_specifier"
1487 )
1488}
1489
1490fn is_macro_argument_error(node: Node<'_>) -> bool {
1496 if node.kind() != "ERROR" {
1497 return false;
1498 }
1499 let mut cursor = node.walk();
1500 node.named_children(&mut cursor).all(|child| {
1501 matches!(
1502 child.kind(),
1503 "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1504 )
1505 })
1506}
1507
1508fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1517 declarator
1518 .next_sibling()
1519 .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1520 .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1521}
1522
1523fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1545 let mut parts = Vec::new();
1546 let mut cursor = node.walk();
1547 for child in node.named_children(&mut cursor) {
1548 if child.kind() == "ERROR" {
1549 let mut error_cursor = child.walk();
1550 parts.extend(child.named_children(&mut error_cursor));
1551 } else {
1552 parts.push(child);
1553 }
1554 }
1555
1556 let mut declarations = Vec::new();
1557 let mut start = None;
1558 let mut is_static = false;
1559 let mut complete = true;
1560 for part in parts {
1561 if part.kind() == "comment" {
1562 continue;
1563 }
1564 if let Some(declarator) = extract_function_declarator(part) {
1565 let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1566 declarations.push(MacroWrappedDeclaration {
1567 declarator,
1568 range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
1569 is_static,
1570 });
1571 is_static = false;
1572 continue;
1573 }
1574 if !is_recovered_declaration_type_part(part) {
1575 complete = false;
1576 break;
1577 }
1578 is_static |= part.kind() == "storage_class_specifier"
1579 && normalize_cpp_whitespace(node_text(part, source)) == "static";
1580 start.get_or_insert(part.start_byte());
1581 }
1582 StrandedRun {
1583 declarations,
1584 complete: complete && start.is_none(),
1585 }
1586}
1587
1588fn macro_wrapped_declarations<'tree>(
1614 envelope: Node<'tree>,
1615 source: &str,
1616) -> Vec<MacroWrappedDeclaration<'tree>> {
1617 let mut declarations = Vec::new();
1618 if !is_declaration_scope_error(envelope) {
1619 return declarations;
1620 }
1621 let mut cursor = envelope.walk();
1622 let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
1623 let [macro_name, arguments @ ..] = children.as_slice() else {
1624 return declarations;
1625 };
1626 if macro_name.kind() != "identifier" {
1627 return declarations;
1628 }
1629 let mut wrapped_declaration_seen = false;
1630 for argument in arguments {
1631 match argument.kind() {
1632 "comment" => {}
1633 "parameter_declaration" => {
1634 let recovered = stranded_declaration_run(*argument, source).declarations;
1635 if recovered.is_empty() {
1636 break;
1637 }
1638 wrapped_declaration_seen = true;
1639 declarations.extend(recovered);
1640 }
1641 "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
1646 _ => break,
1647 }
1648 }
1649 declarations
1650}
1651
1652struct CollapsedMacroDeclarationRun {
1655 invocation_end: usize,
1658}
1659
1660fn collapsed_macro_declaration_run(
1692 node: Node<'_>,
1693 source: &str,
1694) -> Option<CollapsedMacroDeclarationRun> {
1695 if !matches!(node.kind(), "function_definition" | "ERROR")
1696 || !is_declaration_scope_position(node)
1697 {
1698 return None;
1699 }
1700 let head = if node.kind() == "function_definition" {
1701 if node.child_by_field_name("type").is_some() {
1704 return None;
1705 }
1706 node.child_by_field_name("declarator")?
1707 } else {
1708 node.named_child(0)?
1709 };
1710 let mut invocation = extract_function_declarator(head)?;
1711 if invocation.start_byte() != node.start_byte() {
1712 return None;
1713 }
1714 while let Some(inner) = invocation
1718 .child_by_field_name("declarator")
1719 .filter(|inner| inner.kind() == "function_declarator")
1720 {
1721 invocation = inner;
1722 }
1723 let name = invocation.child_by_field_name("declarator")?;
1724 if name.kind() != "identifier"
1725 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
1726 {
1727 return None;
1728 }
1729 let arguments = invocation.child_by_field_name("parameters")?;
1730 let mut cursor = arguments.walk();
1731 let mut children = arguments
1732 .children(&mut cursor)
1733 .filter(|child| child.kind() != "comment");
1734 if children.next()?.kind() != "(" || children.next()?.kind() != "parameter_declaration" {
1735 return None;
1736 }
1737 let hint = children.find(|child| child.kind() != ",")?;
1744 if hint.kind() != "ERROR" {
1745 return None;
1746 }
1747 let mut hint_cursor = hint.walk();
1748 let parts = hint.children(&mut hint_cursor).collect::<Vec<_>>();
1749 let invocation_end = parts.windows(2).find_map(|pair| {
1750 let [close, semicolon] = pair else {
1751 return None;
1752 };
1753 (close.kind() == ")"
1754 && !close.is_missing()
1755 && semicolon.kind() == ";"
1756 && !semicolon.is_missing())
1757 .then(|| semicolon.end_byte())
1758 })?;
1759 (invocation_end < node.end_byte()).then_some(CollapsedMacroDeclarationRun { invocation_end })
1761}
1762
1763fn string_attribute_macro_member_declarators<'tree>(
1784 field: Node<'tree>,
1785 source: &str,
1786) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
1787 if field.kind() != "field_declaration"
1788 || field
1789 .child_by_field_name("type")
1790 .is_none_or(|type_node| type_node.kind() != "type_identifier")
1791 {
1792 return None;
1793 }
1794 let declarator = field.child_by_field_name("declarator")?;
1795 if declarator.kind() != "parenthesized_declarator" {
1796 return None;
1797 }
1798 let opening = declarator.named_child(0)?;
1799 if opening.kind() != "ERROR"
1800 || opening
1801 .named_child(0)
1802 .is_none_or(|word| word.kind() != "identifier")
1803 {
1804 return None;
1805 }
1806 let declarations = stranded_declaration_run(declarator, source).declarations;
1807 (!declarations.is_empty()).then_some(declarations)
1808}
1809
1810fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
1817 let Some(call) = (node.kind() == "expression_statement")
1818 .then(|| node.named_child(0))
1819 .flatten()
1820 .filter(|child| child.kind() == "call_expression")
1821 else {
1822 return false;
1823 };
1824 call.child_by_field_name("function")
1825 .is_some_and(|function| function.kind() == "identifier")
1826 && call
1827 .child_by_field_name("arguments")
1828 .is_some_and(|arguments| {
1829 let mut cursor = arguments.walk();
1830 arguments.named_child_count() > 0
1831 && arguments
1832 .named_children(&mut cursor)
1833 .all(|argument| argument.kind() == "string_literal")
1834 })
1835}
1836
1837fn cpp_access_label_constructor_call_start(
1850 node: Node<'_>,
1851 class_name: &str,
1852 source: &str,
1853) -> Option<usize> {
1854 if node.kind() != "labeled_statement" {
1855 return None;
1856 }
1857 let label = node.named_child(0)?;
1858 if label.kind() != "statement_identifier"
1859 || !matches!(
1860 node_text(label, source).trim(),
1861 "public" | "private" | "protected"
1862 )
1863 {
1864 return None;
1865 }
1866 let mut starts = Vec::new();
1867 let mut stack = vec![node];
1868 while let Some(current) = stack.pop() {
1869 if current.kind() == "call_expression"
1870 && current
1871 .child_by_field_name("function")
1872 .is_some_and(|function| {
1873 function.kind() == "identifier"
1874 && node_text(function, source).trim() == class_name
1875 })
1876 {
1877 starts.push(current.start_byte());
1878 }
1879 let mut cursor = current.walk();
1880 stack.extend(current.named_children(&mut cursor));
1881 }
1882 let [start] = starts.as_slice() else {
1883 return None;
1884 };
1885 Some(*start)
1886}
1887
1888fn cpp_declarator_function_definition<'tree>(
1892 declarator: Node<'tree>,
1893 ancestry: &ParentIndex<'tree>,
1894) -> Option<Node<'tree>> {
1895 let mut current = declarator;
1896 while let Some(parent) = ancestry.parent(current) {
1897 match parent.kind() {
1898 "function_definition" if parent.child_by_field_name("body").is_some() => {
1899 return Some(parent);
1900 }
1901 "pointer_declarator"
1902 | "reference_declarator"
1903 | "parenthesized_declarator"
1904 | "array_declarator" => current = parent,
1905 _ => return None,
1906 }
1907 }
1908 None
1909}
1910
1911fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1915 let mut current = node;
1916 while let Some(parent) = ancestry.parent(current) {
1917 if parent.kind() == "namespace_definition"
1918 && parent.child_by_field_name("body") == Some(current)
1919 {
1920 return true;
1921 }
1922 current = parent;
1923 }
1924 false
1925}
1926
1927pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
1941 let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
1942 let root = tree.root_node();
1943 let mut cursor = root.walk();
1944 let items = root
1945 .named_children(&mut cursor)
1946 .filter(|child| child.kind() != "comment")
1947 .collect::<Vec<_>>();
1948 let [item] = items.as_slice() else {
1949 return None;
1950 };
1951 if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
1952 return None;
1953 }
1954 match item.kind() {
1955 "function_definition" => Some(item.child_by_field_name("body").is_some()),
1956 "declaration" | "field_declaration" => Some(false),
1957 _ => None,
1958 }
1959}
1960
1961pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
1973 !macro_wrapped_declarations(node, source).is_empty()
1974 || collapsed_macro_declaration_run(node, source).is_some()
1975}
1976
1977fn recover_exported_class_function_definition<'tree>(
1978 node: Node<'tree>,
1979 source: &str,
1980) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
1981 if node.kind() != "function_definition" {
1982 return None;
1983 }
1984 if let Some(prefix) = node.prev_named_sibling()
1985 && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
1986 && recovered.range.end_byte == node.end_byte()
1987 {
1988 return Some((node, recovered.name, recovered.raw_supertypes));
1989 }
1990 let type_node = node.child_by_field_name("type")?;
1991 let declarator = node.child_by_field_name("declarator")?;
1992
1993 if matches!(
1994 type_node.kind(),
1995 "class_specifier" | "struct_specifier" | "union_specifier"
1996 ) {
1997 let type_name = type_node
1998 .child_by_field_name("name")
1999 .and_then(|name| direct_identifier_name(name, source));
2000 let exported_macro_type = type_name
2001 .as_ref()
2002 .is_some_and(|name| cpp_export_macro_token(name));
2003 if exported_macro_type {
2004 let mut cursor = node.walk();
2005 let errors_before_declarator = node
2006 .named_children(&mut cursor)
2007 .filter(|child| {
2008 child.kind() == "ERROR"
2009 && child.start_byte() >= type_node.end_byte()
2010 && child.end_byte() <= declarator.start_byte()
2011 })
2012 .collect::<Vec<_>>();
2013 if let Some(name) = errors_before_declarator
2014 .iter()
2015 .find_map(|error| displaced_exported_class_name(*error, source))
2016 {
2017 let raw_supertypes = errors_before_declarator
2018 .iter()
2019 .any(|error| malformed_inheritance_syntax(*error))
2020 .then(|| recovered_malformed_base_name(declarator, source))
2021 .flatten()
2022 .map(|base| vec![base]);
2023 return Some((node, name, raw_supertypes));
2024 }
2025 if errors_before_declarator
2026 .iter()
2027 .any(|error| malformed_inheritance_syntax(*error))
2028 {
2029 return None;
2030 }
2031 }
2032 if !exported_macro_type
2033 && let Some(name) = type_name
2034 && !cpp_export_macro_token(&name)
2035 && let Some(base) =
2036 recovered_postfix_export_macro_base(node, type_node, declarator, source)
2037 {
2038 return Some((node, name, Some(vec![base])));
2039 }
2040 if let Some(name) = direct_identifier_name(declarator, source)
2041 && exported_macro_type
2042 && !cpp_export_macro_token(&name)
2043 {
2044 let raw_supertypes = exported_macro_type
2045 .then(|| recovered_single_base_after_declarator(node, declarator, source))
2046 .flatten()
2047 .map(|base| vec![base]);
2048 return Some((node, name, raw_supertypes));
2049 }
2050 if declarator.kind() == "parenthesized_declarator"
2051 && type_node
2052 .child_by_field_name("name")
2053 .and_then(|name| direct_identifier_name(name, source))
2054 .is_some_and(|name| cpp_export_macro_token(&name))
2055 {
2056 if let Some((name, base)) =
2057 recovered_function_like_export_class_owner(declarator, source)
2058 {
2059 return Some((node, name, Some(vec![base])));
2060 }
2061 let body_start = node
2062 .child_by_field_name("body")
2063 .map(|body| body.start_byte())
2064 .unwrap_or(node.end_byte());
2065 let mut cursor = node.walk();
2066 if let Some(name) = node
2067 .named_children(&mut cursor)
2068 .filter(|child| {
2069 child.kind() == "ERROR"
2070 && child.start_byte() >= declarator.end_byte()
2071 && child.end_byte() <= body_start
2072 })
2073 .find_map(|error| declarator_name_from_node(error, source))
2074 {
2075 return Some((node, name, None));
2076 }
2077 }
2078 }
2079
2080 let declarator_text = direct_identifier_name(declarator, source)?;
2081 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2082 return None;
2083 }
2084 class_identifier_before_body(node, source).map(|name| (node, name, None))
2085}
2086
2087fn recovered_function_like_export_class_owner(
2088 declarator: Node<'_>,
2089 source: &str,
2090) -> Option<(String, String)> {
2091 if declarator.kind() != "parenthesized_declarator" {
2092 return None;
2093 }
2094 let mut cursor = declarator.walk();
2095 let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2096 let [prefix, base] = children.as_slice() else {
2097 return None;
2098 };
2099 if prefix.kind() != "ERROR"
2100 || !matches!(
2101 base.kind(),
2102 "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2103 )
2104 {
2105 return None;
2106 }
2107 let mut identifiers = Vec::new();
2108 let mut prefix_cursor = prefix.walk();
2109 for child in prefix.named_children(&mut prefix_cursor) {
2110 match child.kind() {
2111 "number_literal" | "string_literal" | "char_literal" => {}
2112 "identifier" | "type_identifier" => {
2113 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2114 }
2115 _ => return None,
2116 }
2117 }
2118 let name = match identifiers.as_slice() {
2119 [name] => name.clone(),
2120 [name, final_token] if final_token == "final" => name.clone(),
2121 _ => return None,
2122 };
2123 if name.is_empty() || cpp_export_macro_token(&name) {
2124 return None;
2125 }
2126 let base = recovered_malformed_base_name(*base, source)?;
2127 Some((name, base))
2128}
2129
2130fn recovered_export_head_bases(node: Node<'_>, skip: &[Node<'_>], source: &str) -> Vec<String> {
2137 let mut bases = Vec::new();
2138 let mut cursor = node.walk();
2139 for child in node.named_children(&mut cursor) {
2140 if skip.iter().any(|skipped| same_node(child, *skipped)) {
2141 continue;
2142 }
2143 if child.kind() == "ERROR" {
2144 let mut error_cursor = child.walk();
2145 bases.extend(
2146 child
2147 .named_children(&mut error_cursor)
2148 .filter_map(|part| recovered_malformed_base_name(part, source)),
2149 );
2150 } else if let Some(base) = recovered_malformed_base_name(child, source) {
2151 bases.push(base);
2152 }
2153 }
2154 bases.retain(|base| !matches!(base.as_str(), "final" | "public" | "protected" | "private"));
2155 bases
2156}
2157
2158fn recovered_export_declaration_tail<'tree>(
2165 declaration: Node<'tree>,
2166 head: Node<'tree>,
2167 source: &str,
2168) -> Option<(Vec<String>, Node<'tree>)> {
2169 let mut cursor = declaration.walk();
2170 let init = declaration
2171 .named_children(&mut cursor)
2172 .find(|child| child.kind() == "init_declarator")?;
2173 let body = init.child_by_field_name("value")?;
2174 if body.kind() != "initializer_list" {
2175 return None;
2176 }
2177 let mut bases = recovered_export_head_bases(declaration, &[head, init], source);
2178 bases.extend(recovered_export_head_bases(init, &[body], source));
2179 Some((bases, body))
2180}
2181
2182fn recover_function_like_export_class_pair(
2183 node: Node<'_>,
2184 source: &str,
2185) -> Option<RecoveredFunctionLikeExportClassPair> {
2186 if node.kind() != "ERROR" {
2187 return None;
2188 }
2189 let class_node = first_class_like_child(node)?;
2190 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2191 return None;
2192 }
2193 let macro_name = class_node
2194 .child_by_field_name("name")
2195 .and_then(|name| direct_identifier_name(name, source))?;
2196 if !cpp_export_macro_token(¯o_name) {
2197 return None;
2198 }
2199 let sibling = node.next_named_sibling()?;
2200 let (name, raw_supertypes, body) = match sibling.kind() {
2201 "expression_statement" => {
2202 let compound = sibling.named_child(0)?;
2203 if compound.kind() != "compound_literal_expression" {
2204 return None;
2205 }
2206 let body = compound.child_by_field_name("value")?;
2207 if body.kind() != "initializer_list" {
2208 return None;
2209 }
2210 (
2211 compound
2212 .child_by_field_name("type")
2213 .and_then(|name| direct_identifier_name(name, source))?,
2214 None,
2215 body,
2216 )
2217 }
2218 "labeled_statement" => {
2219 let label = sibling.child_by_field_name("label")?;
2220 if label.kind() != "statement_identifier" {
2221 return None;
2222 }
2223 let name = normalize_cpp_whitespace(node_text(label, source));
2224 let declaration = sibling
2225 .named_children(&mut sibling.walk())
2226 .find(|child| child.kind() == "declaration")?;
2227 let access = declaration.child_by_field_name("type")?;
2228 if !matches!(
2229 node_text(access, source),
2230 "public" | "protected" | "private"
2231 ) {
2232 return None;
2233 }
2234 let (bases, body) = recovered_export_declaration_tail(declaration, access, source)?;
2235 (name, (!bases.is_empty()).then_some(bases), body)
2236 }
2237 "function_definition" => {
2242 let type_node = sibling.child_by_field_name("type")?;
2243 let body = sibling.child_by_field_name("body")?;
2244 if body.kind() != "compound_statement" {
2245 return None;
2246 }
2247 let name = direct_identifier_name(type_node, source)?;
2248 let bases = recovered_export_head_bases(sibling, &[type_node, body], source);
2249 (name, (!bases.is_empty()).then_some(bases), body)
2250 }
2251 "declaration" => {
2255 let type_node = sibling.child_by_field_name("type")?;
2256 let name = direct_identifier_name(type_node, source)?;
2257 let (bases, body) = recovered_export_declaration_tail(sibling, type_node, source)?;
2258 (name, (!bases.is_empty()).then_some(bases), body)
2259 }
2260 _ => return None,
2261 };
2262 if name.is_empty() || cpp_export_macro_token(&name) {
2263 return None;
2264 }
2265 let range = Range {
2266 start_byte: node.start_byte(),
2267 end_byte: sibling.end_byte(),
2268 start_line: node.start_position().row + 1,
2269 end_line: sibling.end_position().row + 1,
2270 };
2271 Some(RecoveredFunctionLikeExportClassPair {
2272 name,
2273 raw_supertypes,
2274 range,
2275 fragmented_body: recovered_fragmented_export_body(body, range)?,
2276 })
2277}
2278
2279fn recover_embedded_function_like_export_classes(
2286 node: Node<'_>,
2287 source: &str,
2288) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
2289 if node.kind() != "ERROR" {
2290 return Vec::new();
2291 }
2292
2293 let mut nodes = Vec::new();
2294 let mut stack = vec![node];
2295 while let Some(current) = stack.pop() {
2296 nodes.push(current);
2297 for index in (0..current.child_count()).rev() {
2298 stack.push(
2299 current
2300 .child(index)
2301 .expect("index below the node's own child count"),
2302 );
2303 }
2304 }
2305 nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2306
2307 let mut recovered = Vec::new();
2308 for class_token in nodes
2309 .iter()
2310 .copied()
2311 .filter(|child| !child.is_named() && child.kind() == "class")
2312 {
2313 let row = class_token.start_position().row;
2314 let Some(macro_name) = nodes.iter().copied().find(|candidate| {
2315 candidate.start_byte() >= class_token.end_byte()
2316 && candidate.start_position().row == row
2317 && matches!(
2318 candidate.kind(),
2319 "identifier" | "type_identifier" | "field_identifier"
2320 )
2321 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*candidate, source)))
2322 }) else {
2323 continue;
2324 };
2325 let Some(arguments) = nodes.iter().copied().find(|candidate| {
2326 candidate.kind() == "argument_list"
2327 && candidate.start_byte() >= macro_name.end_byte()
2328 && candidate.start_position().row == row
2329 }) else {
2330 continue;
2331 };
2332 let Some(name_node) = nodes.iter().copied().find(|candidate| {
2333 candidate.kind() == "identifier"
2334 && candidate.start_byte() >= arguments.end_byte()
2335 && candidate.start_position().row == row
2336 }) else {
2337 continue;
2338 };
2339 let name = normalize_cpp_whitespace(node_text(name_node, source));
2340 if name.is_empty() || cpp_export_macro_token(&name) {
2341 continue;
2342 }
2343 let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
2344 candidate.kind() == "field_initializer"
2345 && candidate.start_byte() >= name_node.end_byte()
2346 && candidate
2347 .child_by_field_name("field")
2348 .or_else(|| candidate.named_child(0))
2349 .is_some()
2350 && candidate
2351 .child_by_field_name("value")
2352 .or_else(|| {
2353 let mut cursor = candidate.walk();
2354 candidate
2355 .named_children(&mut cursor)
2356 .find(|child| child.kind() == "initializer_list")
2357 })
2358 .is_some_and(|value| value.kind() == "initializer_list")
2359 }) else {
2360 continue;
2361 };
2362 let has_access = nodes.iter().copied().any(|candidate| {
2363 candidate.start_byte() >= name_node.end_byte()
2364 && candidate.end_byte() <= base_initializer.start_byte()
2365 && matches!(
2366 normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
2367 "public" | "protected" | "private"
2368 )
2369 });
2370 if !has_access {
2371 continue;
2372 }
2373 let Some(base_node) = base_initializer
2374 .child_by_field_name("field")
2375 .or_else(|| base_initializer.named_child(0))
2376 else {
2377 continue;
2378 };
2379 let Some(base) = recovered_malformed_base_name(base_node, source) else {
2380 continue;
2381 };
2382 let body = base_initializer
2383 .child_by_field_name("value")
2384 .or_else(|| {
2385 let mut cursor = base_initializer.walk();
2386 base_initializer
2387 .named_children(&mut cursor)
2388 .find(|child| child.kind() == "initializer_list")
2389 })
2390 .expect("initializer-list value checked above");
2391 let range = Range {
2392 start_byte: class_token.start_byte(),
2393 end_byte: body.end_byte(),
2394 start_line: class_token.start_position().row + 1,
2395 end_line: body.end_position().row + 1,
2396 };
2397 if recovered
2398 .iter()
2399 .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
2400 existing.name == name && existing.range == range
2401 })
2402 {
2403 continue;
2404 }
2405 recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
2406 name,
2407 range,
2408 raw_supertypes: vec![base],
2409 fragmented_body: match recovered_fragmented_export_body(body, range) {
2410 Some(fragmented) => fragmented,
2411 None => continue,
2412 },
2413 });
2414 }
2415 recovered
2416}
2417
2418fn lifted_function_like_export_class_namespace<'tree>(
2419 node: Node<'tree>,
2420 source: &str,
2421 ancestry: &ParentIndex<'tree>,
2422) -> Option<String> {
2423 let mut anchor = node;
2429 let parent = loop {
2430 let parent = ancestry.parent(anchor)?;
2431 if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
2432 break parent;
2433 }
2434 anchor = parent;
2435 };
2436 let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
2437 sibling.start_byte() > anchor.end_byte()
2438 && sibling.kind() == "ERROR"
2439 && sibling.named_child_count() == 0
2440 && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
2441 });
2442 if !has_later_close {
2443 return None;
2444 }
2445 let candidates = parent
2446 .named_children(&mut parent.walk())
2447 .filter(|sibling| {
2448 sibling.kind() == "namespace_definition"
2449 && sibling.has_error()
2450 && sibling.end_byte() < anchor.start_byte()
2451 })
2452 .filter_map(|namespace| {
2453 namespace
2454 .child_by_field_name("name")
2455 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
2456 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
2457 })
2458 .collect::<Vec<_>>();
2459 let [namespace] = candidates.as_slice() else {
2460 return None;
2461 };
2462 Some(namespace.clone())
2463}
2464
2465pub(crate) fn recovered_function_like_export_class_pair_has_body(
2466 node: Node<'_>,
2467 source: &str,
2468 identifier: &str,
2469 range: &Range,
2470) -> bool {
2471 recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
2472 recovered.name == identifier
2473 && recovered.range.start_byte == range.start_byte
2474 && recovered.range.end_byte == range.end_byte
2475 })
2476}
2477
2478#[derive(Default)]
2494pub struct CppRecoveredExportClassIndex {
2495 by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
2496}
2497
2498impl CppRecoveredExportClassIndex {
2499 pub fn build(root: Node<'_>, source: &str) -> Self {
2500 let mut by_error_node: HashMap<
2501 (usize, usize),
2502 Vec<RecoveredEmbeddedFunctionLikeExportClass>,
2503 > = HashMap::default();
2504 let mut stack = vec![root];
2505 while let Some(node) = stack.pop() {
2506 if node.kind() == "ERROR" {
2507 let recovered = recover_embedded_function_like_export_classes(node, source);
2508 if !recovered.is_empty() {
2509 by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
2510 }
2511 }
2512 let mut cursor = node.walk();
2513 stack.extend(node.named_children(&mut cursor));
2514 }
2515 Self { by_error_node }
2516 }
2517
2518 pub fn approximate_size(&self) -> usize {
2520 self.by_error_node
2521 .values()
2522 .fold(0usize, |total, recovered| {
2523 recovered.iter().fold(
2524 total.saturating_add(std::mem::size_of::<(usize, usize)>()),
2525 |acc, class| {
2526 acc.saturating_add(std::mem::size_of::<
2527 RecoveredEmbeddedFunctionLikeExportClass,
2528 >())
2529 .saturating_add(class.name.len())
2530 .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
2531 },
2532 )
2533 })
2534 }
2535
2536 fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
2537 self.by_error_node
2538 .get(&(node.start_byte(), node.end_byte()))
2539 .is_some_and(|recovered| {
2540 recovered.iter().any(|class| {
2541 class.name == identifier
2542 && class.range.start_byte == range.start_byte
2543 && class.range.end_byte == range.end_byte
2544 })
2545 })
2546 }
2547}
2548
2549#[cfg(any(test, feature = "test-support"))]
2556thread_local! {
2557 static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
2558 const { std::cell::Cell::new(0) };
2559}
2560
2561#[cfg(any(test, feature = "test-support"))]
2565#[doc(hidden)]
2566pub fn recovered_class_body_node_visits_for_test() -> usize {
2567 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
2568}
2569
2570#[cfg(any(test, feature = "test-support"))]
2572#[doc(hidden)]
2573pub fn reset_recovered_class_body_node_visits_for_test() {
2574 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
2575}
2576
2577#[cfg(any(test, feature = "test-support"))]
2578fn record_recovered_class_body_visit() {
2579 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
2580}
2581
2582#[cfg(not(any(test, feature = "test-support")))]
2583fn record_recovered_class_body_visit() {}
2584
2585pub(crate) fn recovered_class_body_at(
2608 recovered_export_classes: &CppRecoveredExportClassIndex,
2609 root: Node<'_>,
2610 source: &str,
2611 identifier: &str,
2612 range: &Range,
2613) -> Option<bool> {
2614 let covers_range_start = |node: &Node<'_>| {
2615 node.start_byte() <= range.start_byte
2616 && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
2617 };
2618 let mut stack = vec![root];
2619 let mut saw_forward = false;
2620 while let Some(node) = stack.pop() {
2621 record_recovered_class_body_visit();
2622 if (node.start_byte() == range.start_byte
2626 && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
2627 || recovered_export_classes.claims(node, identifier, range)
2628 || (node.start_byte() == range.start_byte
2629 && recovered_fragmented_plain_class_has_body(node, source, identifier, range))
2630 {
2631 return Some(true);
2632 }
2633 if node.start_byte() <= range.start_byte
2640 && range.start_byte < node.end_byte()
2641 && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
2642 {
2643 if has_body {
2644 return Some(true);
2645 }
2646 saw_forward = true;
2647 continue;
2648 }
2649 let mut cursor = node.walk();
2650 stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
2651 }
2652 saw_forward.then_some(false)
2653}
2654
2655pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
2663 if !matches!(
2664 node.kind(),
2665 "qualified_identifier" | "scoped_type_identifier" | "template_type"
2666 ) {
2667 return false;
2668 }
2669 if let Some(function) = node.parent().filter(|parent| {
2670 parent.kind() == "function_definition"
2671 && parent
2672 .child_by_field_name("declarator")
2673 .is_some_and(|declarator| same_node(declarator, node))
2674 }) {
2675 return recover_exported_class_function_definition(function, source)
2676 .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
2677 }
2678 let Some(initializer) = node.parent().filter(|parent| {
2679 parent.kind() == "init_declarator"
2680 && parent
2681 .child_by_field_name("declarator")
2682 .is_some_and(|declarator| same_node(declarator, node))
2683 }) else {
2684 return false;
2685 };
2686 initializer
2687 .parent()
2688 .filter(|parent| parent.kind() == "declaration")
2689 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
2690 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
2691}
2692
2693struct CppSentinelReparsedClass<'tree> {
2699 declaration_node: Node<'tree>,
2700 name: String,
2701 body: Node<'tree>,
2702 raw_supertypes: Option<Vec<String>>,
2703}
2704
2705fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
2706 let mut cursor = root.walk();
2707 root.named_children(&mut cursor)
2708 .find(|child| child.kind() != "comment")
2709 .filter(|child| child.kind() == "template_declaration")
2710}
2711
2712fn cpp_sentinel_reparsed_class<'tree>(
2713 root: Node<'tree>,
2714 template_node: Option<Node<'tree>>,
2715 source: &str,
2716 ancestry: &ParentIndex<'tree>,
2717) -> Option<CppSentinelReparsedClass<'tree>> {
2718 let container = template_node.unwrap_or(root);
2719 let mut cursor = container.walk();
2720 for child in container.named_children(&mut cursor) {
2721 if matches!(
2722 child.kind(),
2723 "class_specifier" | "struct_specifier" | "union_specifier"
2724 ) {
2725 let name = class_like_name(child, source, ancestry)?;
2726 let body = cpp_body_node(child)?;
2727 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
2728 .then(|| extract_cpp_supertypes(child, source));
2729 return Some(CppSentinelReparsedClass {
2730 declaration_node: child,
2731 name,
2732 body,
2733 raw_supertypes,
2734 });
2735 }
2736 if child.kind() == "declaration"
2737 && let Some(class_node) = first_class_like_child(child)
2738 {
2739 let name = class_like_name(class_node, source, ancestry)?;
2740 let body = cpp_body_node(class_node)?;
2741 let raw_supertypes =
2742 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
2743 .then(|| extract_cpp_supertypes(class_node, source));
2744 return Some(CppSentinelReparsedClass {
2745 declaration_node: class_node,
2746 name,
2747 body,
2748 raw_supertypes,
2749 });
2750 }
2751 if child.kind() == "function_definition"
2756 && let Some(class_node) = first_class_like_child(child)
2757 && let Some(body) = cpp_body_node(class_node)
2758 && let Some(name) = class_like_name(class_node, source, ancestry)
2759 {
2760 let raw_supertypes =
2761 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
2762 .then(|| extract_cpp_supertypes(class_node, source));
2763 return Some(CppSentinelReparsedClass {
2764 declaration_node: class_node,
2765 name,
2766 body,
2767 raw_supertypes,
2768 });
2769 }
2770 if child.kind() == "function_definition"
2771 && let Some((_, name, raw_supertypes)) =
2772 recover_exported_class_function_definition(child, source)
2773 {
2774 let body = cpp_body_node(child)?;
2775 return Some(CppSentinelReparsedClass {
2776 declaration_node: child,
2777 name,
2778 body,
2779 raw_supertypes,
2780 });
2781 }
2782 }
2783 None
2784}
2785
2786fn recovered_postfix_export_macro_base(
2787 node: Node<'_>,
2788 type_node: Node<'_>,
2789 declarator: Node<'_>,
2790 source: &str,
2791) -> Option<String> {
2792 let mut cursor = node.walk();
2793 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
2794 child.kind() == "ERROR"
2795 && child.start_byte() >= type_node.end_byte()
2796 && child.end_byte() <= declarator.start_byte()
2797 && postfix_export_macro_inheritance(*child, source)
2798 });
2799 malformed_clauses.next()?;
2800 if malformed_clauses.next().is_some() {
2801 return None;
2802 }
2803 recovered_malformed_base_name(declarator, source)
2804}
2805
2806fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
2807 let mut macro_count = 0;
2808 let mut colon_count = 0;
2809 let mut access_count = 0;
2810 for index in 0..node.child_count() {
2811 let Some(child) = node.child(index) else {
2812 return false;
2813 };
2814 match child.kind() {
2815 "identifier" | "type_identifier" if child.is_named() => {
2816 let candidate = normalize_cpp_whitespace(node_text(child, source));
2817 if !cpp_export_macro_token(&candidate) {
2818 return false;
2819 }
2820 macro_count += 1;
2821 }
2822 ":" if !child.is_named() => colon_count += 1,
2823 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
2824 _ => return false,
2825 }
2826 }
2827 macro_count == 1 && colon_count == 1 && access_count == 1
2828}
2829
2830fn recovered_single_base_after_declarator(
2831 node: Node<'_>,
2832 declarator: Node<'_>,
2833 source: &str,
2834) -> Option<String> {
2835 let body_start = node
2836 .child_by_field_name("body")
2837 .map(|body| body.start_byte())
2838 .unwrap_or(node.end_byte());
2839 let mut cursor = node.walk();
2840 let mut bases = node
2841 .named_children(&mut cursor)
2842 .filter(|child| {
2843 child.kind() == "ERROR"
2844 && child.start_byte() >= declarator.end_byte()
2845 && child.end_byte() <= body_start
2846 })
2847 .filter_map(|error| displaced_exported_class_name(error, source));
2848 let base = bases.next()?;
2849 bases.next().is_none().then_some(base)
2850}
2851
2852fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
2853 (0..node.child_count()).any(|index| {
2854 node.child(index)
2855 .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
2856 })
2857}
2858
2859pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
2860 recover_exported_class_function_definition(node, source).is_some()
2861}
2862
2863fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
2864 matches!(
2865 kind,
2866 "ERROR"
2867 | "preproc_if"
2868 | "preproc_ifdef"
2869 | "preproc_ifndef"
2870 | "preproc_else"
2871 | "preproc_elif"
2872 ) || (kind == "labeled_statement" && in_class_scope)
2873}
2874
2875pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
2876 if node.kind() != "declaration" {
2877 return false;
2878 }
2879 let mut ancestor = node.parent();
2880 while let Some(container) = ancestor {
2881 match container.kind() {
2882 "compound_statement" => {
2883 return container.parent().is_some_and(|class_container| {
2884 is_recovered_exported_class_container(class_container, source)
2885 });
2886 }
2887 "template_declaration" | "linkage_specification" | "declaration_list" => {}
2890 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
2891 _ => return false,
2892 }
2893 ancestor = container.parent();
2894 }
2895 false
2896}
2897
2898pub fn recovered_exported_class_has_body(
2899 node: Node<'_>,
2900 source: &str,
2901 expected_name: &str,
2902) -> Option<bool> {
2903 match node.kind() {
2904 "function_definition" => {
2905 let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
2906 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
2907 }
2908 "declaration" | "field_declaration" => {
2909 let recovered = recover_exported_class_declaration(node, source)?;
2910 (recovered.name == expected_name).then(|| recovered.body.is_some())
2911 }
2912 _ => None,
2913 }
2914}
2915
2916fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
2917 let body_start = node
2918 .child_by_field_name("body")
2919 .map(|body| body.start_byte())
2920 .unwrap_or(node.end_byte());
2921 let mut stack = Vec::new();
2922 for index in (0..node.named_child_count()).rev() {
2923 let Some(child) = node.named_child(index) else {
2924 continue;
2925 };
2926 if child.start_byte() >= body_start {
2927 continue;
2928 }
2929 stack.push(child);
2930 }
2931
2932 let mut best = None;
2933 while let Some(current) = stack.pop() {
2934 if matches!(current.kind(), "identifier" | "type_identifier") {
2935 let name = normalize_cpp_whitespace(node_text(current, source));
2936 if !name.is_empty()
2937 && !cpp_export_macro_token(&name)
2938 && !matches!(name.as_str(), "class" | "struct" | "union")
2939 {
2940 best = Some(name);
2941 }
2942 continue;
2943 }
2944
2945 for index in (0..current.named_child_count()).rev() {
2946 if let Some(child) = current.named_child(index)
2947 && child.start_byte() < body_start
2948 {
2949 stack.push(child);
2950 }
2951 }
2952 }
2953 best
2954}
2955
2956fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
2957 if node.kind() == "declaration"
2958 && node
2959 .child_by_field_name("type")
2960 .or_else(|| first_class_like_child(node))
2961 .is_some_and(|type_node| {
2962 matches!(
2963 type_node.kind(),
2964 "class_specifier" | "struct_specifier" | "union_specifier"
2965 )
2966 })
2967 && let Some(name) = node
2968 .child_by_field_name("declarator")
2969 .and_then(|declarator| declarator_name_from_node(declarator, source))
2970 && !cpp_export_macro_token(&name)
2971 {
2972 return Some(name);
2973 }
2974
2975 if node.kind() == "function_definition"
2976 && node.child_by_field_name("type").is_some_and(|type_node| {
2977 matches!(
2978 type_node.kind(),
2979 "class_specifier" | "struct_specifier" | "union_specifier"
2980 )
2981 })
2982 && let Some(name) = node
2983 .child_by_field_name("declarator")
2984 .and_then(|declarator| direct_identifier_name(declarator, source))
2985 && !cpp_export_macro_token(&name)
2986 {
2987 return Some(name);
2988 }
2989
2990 let class_node = if matches!(
2991 node.kind(),
2992 "class_specifier" | "struct_specifier" | "union_specifier"
2993 ) {
2994 node
2995 } else {
2996 first_class_like_child(node)?
2997 };
2998 class_like_name_from_children(class_node, source)
2999}
3000
3001fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3002 if !matches!(
3003 node.kind(),
3004 "identifier" | "field_identifier" | "type_identifier"
3005 ) {
3006 return None;
3007 }
3008 let name = normalize_cpp_whitespace(node_text(node, source));
3009 (!name.is_empty()).then_some(name)
3010}
3011
3012fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3013 match node.kind() {
3014 "identifier" | "field_identifier" | "type_identifier" => {
3015 let name = normalize_cpp_whitespace(node_text(node, source));
3016 (!name.is_empty()).then_some(name)
3017 }
3018 _ => {
3019 let mut cursor = node.walk();
3020 node.named_children(&mut cursor)
3021 .find_map(|child| declarator_name_from_node(child, source))
3022 }
3023 }
3024}
3025
3026fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3027 let mut cursor = node.walk();
3028 node.named_children(&mut cursor).find(|child| {
3029 matches!(
3030 child.kind(),
3031 "class_specifier" | "struct_specifier" | "union_specifier"
3032 )
3033 })
3034}
3035
3036fn push_cpp_container_work<'tree>(
3041 node: Node<'tree>,
3042 scope: ScopeInfo,
3043 stack: &mut Vec<CppWork<'tree>>,
3044) {
3045 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3046}
3047
3048fn push_cpp_sibling_range<'tree>(
3052 parent: Node<'tree>,
3053 start_index: usize,
3054 end_index: usize,
3055 scope: ScopeInfo,
3056 stack: &mut Vec<CppWork<'tree>>,
3057) {
3058 let mut cursor = parent.walk();
3059 let children = parent
3060 .named_children(&mut cursor)
3061 .skip(start_index)
3062 .take(end_index.saturating_sub(start_index))
3063 .collect::<Vec<_>>()
3064 .into_iter();
3065 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3066}
3067
3068fn advance_cpp_siblings<'tree>(
3076 mut siblings: CppSiblingsWork<'tree>,
3077 source: &str,
3078 stack: &mut Vec<CppWork<'tree>>,
3079) {
3080 let Some(child) = siblings.children.next() else {
3081 return;
3082 };
3083 let current_scope = siblings.scope.clone();
3084 if let Some(namespace) = cpp_using_namespace_target(child, source) {
3085 siblings.scope.visible_using_namespaces.push(namespace);
3086 }
3087 if !siblings.children.as_slice().is_empty() {
3088 stack.push(CppWork::Siblings(siblings));
3089 }
3090 stack.push(CppWork::Node(CppNodeWork {
3091 node: child,
3092 scope: current_scope,
3093 }));
3094}
3095
3096fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3103 if node.kind() != "using_declaration" {
3104 return None;
3105 }
3106 let mut cursor = node.walk();
3107 let is_namespace_directive = node
3108 .children(&mut cursor)
3109 .any(|child| child.kind() == "namespace");
3110 if !is_namespace_directive {
3111 return None;
3112 }
3113 let target = node.named_child(0)?;
3114 let start = target
3122 .child(0)
3123 .filter(|child| !child.is_named() && child.kind() == "::")
3124 .map_or(target.start_byte(), |marker| marker.end_byte());
3125 let text = normalize_cpp_whitespace(
3126 source
3127 .get(start..target.end_byte())
3128 .expect("using-directive target covers one source range"),
3129 );
3130 (!text.is_empty()).then_some(text)
3131}
3132
3133pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
3146 let mut parser = Parser::new();
3147 if parser
3148 .set_language(&tree_sitter_cpp::LANGUAGE.into())
3149 .is_err()
3150 {
3151 return Vec::new();
3152 }
3153 let Some(tree) = parser.parse(source, None) else {
3154 return Vec::new();
3155 };
3156 let mut namespaces = Vec::new();
3157 let mut seen = std::collections::HashSet::new();
3158 let mut stack = vec![tree.root_node()];
3159 while let Some(node) = stack.pop() {
3160 if let Some(namespace) = cpp_using_namespace_target(node, source)
3161 && seen.insert(namespace.clone())
3162 {
3163 namespaces.push(namespace);
3164 }
3165 let mut cursor = node.walk();
3166 stack.extend(node.named_children(&mut cursor));
3167 }
3168 namespaces
3169}
3170
3171pub struct CppVisitor<'a> {
3172 pub file: &'a ProjectFile,
3173 pub source: &'a str,
3174 pub parsed: &'a mut ParsedFile,
3175 pub c_tag_semantics: bool,
3186 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
3187 pub consumed_fragment_regions: Vec<(usize, usize)>,
3196 pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
3201 pub field_owners: Option<CppFieldOwnerIndex>,
3205 pub recovery_captures: Vec<CppRecoveryCapture>,
3209}
3210
3211impl<'a> CppVisitor<'a> {
3212 fn add_declaration(
3220 &mut self,
3221 code_unit: CodeUnit,
3222 node: Node<'_>,
3223 parent: Option<CodeUnit>,
3224 top_level: Option<CodeUnit>,
3225 ) {
3226 self.note_declaration(&code_unit);
3227 let source = self.source;
3228 self.parsed
3229 .add_code_unit(code_unit, node, source, parent, top_level);
3230 }
3231
3232 fn add_declaration_with_range(
3234 &mut self,
3235 code_unit: CodeUnit,
3236 range: Range,
3237 parent: Option<CodeUnit>,
3238 top_level: Option<CodeUnit>,
3239 ) {
3240 self.note_declaration(&code_unit);
3241 self.parsed
3242 .add_code_unit_with_range(code_unit, range, parent, top_level);
3243 }
3244
3245 fn replace_declaration_deferred(
3247 &mut self,
3248 code_unit: CodeUnit,
3249 node: Node<'_>,
3250 parent: Option<CodeUnit>,
3251 top_level: Option<CodeUnit>,
3252 ) {
3253 self.note_replaced_declaration(&code_unit);
3254 let source = self.source;
3255 self.parsed
3256 .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
3257 }
3258
3259 fn replace_declaration_with_range_deferred(
3261 &mut self,
3262 code_unit: CodeUnit,
3263 range: Range,
3264 parent: Option<CodeUnit>,
3265 top_level: Option<CodeUnit>,
3266 ) {
3267 self.note_replaced_declaration(&code_unit);
3268 self.parsed
3269 .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
3270 }
3271
3272 fn note_declaration(&mut self, code_unit: &CodeUnit) {
3279 if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
3280 for capture in &mut self.recovery_captures {
3281 if capture.removed_pre_existing.contains(code_unit) {
3282 continue;
3283 }
3284 if capture.created_units.insert(code_unit.clone()) {
3285 capture.created.push(code_unit.clone());
3286 }
3287 }
3288 }
3289 if let Some(field_owners) = self.field_owners.as_mut() {
3290 field_owners.record(code_unit, self.file);
3291 }
3292 }
3293
3294 fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
3303 let removes_children = self.parsed.contains_declaration(code_unit)
3304 && self
3305 .parsed
3306 .children
3307 .get(code_unit)
3308 .is_some_and(|children| !children.is_empty());
3309 if removes_children {
3310 if !self.recovery_captures.is_empty() {
3311 let removed = self.declarations_a_replacement_removes(code_unit);
3312 for capture in &mut self.recovery_captures {
3313 for unit in &removed {
3314 if !capture.created_units.contains(unit) {
3319 capture.removed_pre_existing.insert(unit.clone());
3320 }
3321 }
3322 }
3323 }
3324 self.field_owners = None;
3325 }
3326 self.note_declaration(code_unit);
3327 }
3328
3329 fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
3332 let mut removed = Vec::new();
3333 let mut seen = HashSet::default();
3334 let mut pending: Vec<CodeUnit> = self
3335 .parsed
3336 .children
3337 .get(code_unit)
3338 .cloned()
3339 .unwrap_or_default();
3340 while let Some(unit) = pending.pop() {
3341 if !seen.insert(unit.clone()) {
3342 continue;
3343 }
3344 if let Some(children) = self.parsed.children.get(&unit) {
3345 pending.extend(children.iter().cloned());
3346 }
3347 removed.push(unit);
3348 }
3349 removed
3350 }
3351
3352 fn visit_function_like_export_class_pair<'tree>(
3353 &mut self,
3354 node: Node<'tree>,
3355 scope: &ScopeInfo,
3356 stack: &mut Vec<CppWork<'tree>>,
3357 ancestry: &ParentIndex<'tree>,
3358 ) -> bool {
3359 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
3360 return false;
3361 };
3362 let member_outcome = self
3363 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
3364 let mut displaced = node.next_named_sibling();
3370 while let Some(candidate) = displaced {
3371 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
3372 break;
3373 }
3374 displaced = candidate.next_named_sibling();
3375 }
3376 let class_unit = self.visit_named_class_like_shape(
3377 node,
3378 recovered.name,
3379 None,
3384 true,
3385 Some(recovered.range),
3386 recovered.raw_supertypes,
3387 scope,
3388 stack,
3389 ancestry,
3390 );
3391 self.parsed
3392 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3393 recovery: recovered.range,
3394 unit: class_unit.clone(),
3395 });
3396 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
3397 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
3398 tree.root_node(),
3399 class_unit.identifier(),
3400 self.source,
3401 )
3402 {
3403 self.visit_recovered_fragment_constructor(
3404 range,
3405 body,
3406 node,
3407 &class_unit,
3408 scope,
3409 ancestry,
3410 );
3411 }
3412 if let Some(outcome) = member_outcome {
3413 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
3414 }
3415 self.consumed_fragment_regions
3416 .push((node.start_byte(), recovered.range.end_byte));
3417 true
3418 }
3419
3420 fn visit_embedded_function_like_export_classes<'tree>(
3421 &mut self,
3422 node: Node<'tree>,
3423 scope: &ScopeInfo,
3424 stack: &mut Vec<CppWork<'tree>>,
3425 ancestry: &ParentIndex<'tree>,
3426 ) -> bool {
3427 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
3428 let found = !recovered_classes.is_empty();
3429 for recovered in recovered_classes {
3430 let member_outcome = self.reparse_fragmented_export_class_members(
3431 &recovered.fragmented_body,
3432 &recovered.name,
3433 );
3434 let class_unit = self.visit_named_class_like_shape(
3435 node,
3436 recovered.name,
3437 None,
3438 true,
3439 Some(recovered.range),
3440 Some(recovered.raw_supertypes),
3441 scope,
3442 stack,
3443 ancestry,
3444 );
3445 self.parsed
3446 .record_materialization(MaterializationRecord::RecoveredDeclaration {
3447 recovery: recovered.range,
3448 unit: class_unit.clone(),
3449 });
3450 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
3451 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
3452 tree.root_node(),
3453 class_unit.identifier(),
3454 self.source,
3455 )
3456 {
3457 self.visit_recovered_fragment_constructor(
3458 range,
3459 body,
3460 node,
3461 &class_unit,
3462 scope,
3463 ancestry,
3464 );
3465 }
3466 if let Some(outcome) = member_outcome {
3467 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
3468 }
3469 }
3470 found
3471 }
3472
3473 #[allow(clippy::too_many_arguments)]
3481 pub fn visit_container<'tree>(
3482 &mut self,
3483 node: Node<'tree>,
3484 ancestry: &ParentIndex<'tree>,
3485 package_name: &str,
3486 module: Option<CodeUnit>,
3487 class_unit: Option<CodeUnit>,
3488 template_signature: Option<String>,
3489 visible_using_namespaces: Vec<String>,
3490 ) {
3491 let scope = ScopeInfo {
3492 package_name: package_name.to_string(),
3493 module,
3494 class_unit,
3495 template_signature,
3496 template_metadata: None,
3497 declarations_are_fields: false,
3498 recovered_specialization_member_scope: false,
3499 visible_using_namespaces,
3500 };
3501 self.run_container_work(node, scope, ancestry);
3502 }
3503
3504 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
3508 self.consumed_fragment_regions
3509 .iter()
3510 .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
3511 }
3512
3513 fn run_container_work<'tree>(
3525 &mut self,
3526 node: Node<'tree>,
3527 scope: ScopeInfo,
3528 ancestry: &ParentIndex<'tree>,
3529 ) {
3530 self.drain_cpp_work(
3531 vec![CppWork::Container(CppContainer { node, scope })],
3532 ancestry,
3533 );
3534 }
3535
3536 fn drain_cpp_work<'tree>(
3543 &mut self,
3544 mut stack: Vec<CppWork<'tree>>,
3545 ancestry: &ParentIndex<'tree>,
3546 ) {
3547 while let Some(work) = stack.pop() {
3548 match work {
3549 CppWork::Container(container) => {
3550 push_cpp_container_work(container.node, container.scope, &mut stack);
3551 }
3552 CppWork::Siblings(siblings) => {
3553 advance_cpp_siblings(siblings, self.source, &mut stack);
3554 }
3555 CppWork::Node(work) => {
3556 if self.node_is_inside_consumed_fragment(work.node) {
3557 continue;
3558 }
3559 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
3560 }
3561 }
3562 }
3563 }
3564
3565 fn reparse_fragmented_export_class_members(
3570 &self,
3571 fragmented: &FragmentedExportBody,
3572 class_name: &str,
3573 ) -> Option<FragmentedExportMembers> {
3574 if fragmented.reparse_start >= fragmented.reparse_end {
3575 return None;
3576 }
3577 let tree = cpp_reparse_fragmented_class_body(
3578 self.source,
3579 fragmented.reparse_start,
3580 fragmented.reparse_end,
3581 )?;
3582 if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
3583 return Some(FragmentedExportMembers::Complete(tree));
3584 }
3585 let has_conditional_constructor = {
3586 let root = tree.root_node();
3587 let mut cursor = root.walk();
3588 root.named_children(&mut cursor).any(|child| {
3589 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
3590 })
3591 };
3592 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
3593 }
3594
3595 fn visit_fragmented_export_class_members(
3598 &mut self,
3599 outcome: FragmentedExportMembers,
3600 class_unit: CodeUnit,
3601 scope: &ScopeInfo,
3602 ) -> bool {
3603 let (tree, complete) = match outcome {
3604 FragmentedExportMembers::Complete(tree) => (tree, true),
3605 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
3606 };
3607 let root = tree.root_node();
3608 let class_name = class_unit.identifier().to_string();
3609 let member_scope = ScopeInfo {
3610 package_name: class_unit.package_name().to_string(),
3615 module: scope.module.clone(),
3616 class_unit: Some(class_unit),
3617 template_signature: scope.template_signature.clone(),
3618 template_metadata: None,
3619 declarations_are_fields: true,
3620 recovered_specialization_member_scope: false,
3621 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3622 };
3623 if !complete {
3624 let mut cursor = root.walk();
3630 let constructors = root
3631 .named_children(&mut cursor)
3632 .filter_map(|child| {
3633 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
3634 })
3635 .collect::<Vec<_>>();
3636 let reparsed_ancestry = ParentIndex::new(root);
3639 for constructor in constructors {
3640 let mut stack = Vec::new();
3641 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
3642 while let Some(work) = stack.pop() {
3643 match work {
3644 CppWork::Container(container) => {
3645 push_cpp_container_work(container.node, container.scope, &mut stack);
3646 }
3647 CppWork::Siblings(siblings) => {
3648 advance_cpp_siblings(siblings, self.source, &mut stack);
3649 }
3650 CppWork::Node(work) => {
3651 self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
3652 }
3653 }
3654 }
3655 }
3656 return false;
3657 }
3658 self.run_container_work(root, member_scope, &ParentIndex::new(root));
3660 true
3661 }
3662
3663 fn visit_recovered_fragment_constructor<'tree>(
3664 &mut self,
3665 range: std::ops::Range<usize>,
3666 constructor_body: Node<'tree>,
3667 class_declaration: Node<'tree>,
3668 class_unit: &CodeUnit,
3669 scope: &ScopeInfo,
3670 ancestry: &ParentIndex<'tree>,
3671 ) {
3672 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
3673 return;
3674 };
3675 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
3676 tree.root_node(),
3677 range.start,
3678 class_unit.identifier(),
3679 self.source,
3680 ) else {
3681 return;
3682 };
3683 let member_scope = ScopeInfo {
3684 package_name: class_unit.package_name().to_string(),
3685 module: scope.module.clone(),
3686 class_unit: Some(class_unit.clone()),
3687 template_signature: scope.template_signature.clone(),
3688 template_metadata: None,
3689 declarations_are_fields: true,
3690 recovered_specialization_member_scope: false,
3691 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3692 };
3693 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
3694 else {
3695 return;
3696 };
3697 debug_assert_eq!(function.name, class_unit.identifier());
3698 let code_unit = function.code_unit(self.file.clone());
3699 self.add_declaration_with_range(
3700 code_unit.clone(),
3701 Range {
3702 start_byte: function_declarator.start_byte(),
3703 end_byte: constructor_body.end_byte(),
3704 start_line: function_declarator.start_position().row + 1,
3705 end_line: constructor_body.end_position().row + 1,
3706 },
3707 None,
3708 None,
3709 );
3710 self.parsed.add_signature_with_metadata(
3711 code_unit.clone(),
3712 cpp_signature_metadata(
3713 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
3714 function_declarator,
3715 self.source,
3716 ancestry,
3717 )
3718 .with_declaration_only(false)
3719 .with_callable_linkage(cpp_callable_linkage(
3720 class_declaration,
3721 self.source,
3722 ancestry,
3723 )),
3724 );
3725 self.parsed.add_child(class_unit.clone(), code_unit);
3726 }
3727
3728 fn visit_recovered_fragment_prefix_members<'tree>(
3729 &mut self,
3730 root: Node<'tree>,
3731 constructor_start: usize,
3732 class_unit: &CodeUnit,
3733 scope: &ScopeInfo,
3734 ancestry: &ParentIndex<'tree>,
3735 ) {
3736 let member_scope = ScopeInfo {
3737 package_name: class_unit.package_name().to_string(),
3738 module: scope.module.clone(),
3739 class_unit: Some(class_unit.clone()),
3740 template_signature: scope.template_signature.clone(),
3741 template_metadata: None,
3742 declarations_are_fields: true,
3743 recovered_specialization_member_scope: false,
3744 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3745 };
3746 let mut stack = vec![root];
3747 while let Some(current) = stack.pop() {
3748 if current.kind() == "comment" || current.start_byte() >= constructor_start {
3749 continue;
3750 }
3751 if current.end_byte() <= constructor_start
3752 && current.kind() != "translation_unit"
3753 && current.kind() != "labeled_statement"
3754 && current.kind() != "ERROR"
3755 {
3756 let mut work_stack = Vec::new();
3757 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
3758 while let Some(work) = work_stack.pop() {
3759 match work {
3760 CppWork::Container(container) => {
3761 push_cpp_container_work(
3762 container.node,
3763 container.scope,
3764 &mut work_stack,
3765 );
3766 }
3767 CppWork::Siblings(siblings) => {
3768 advance_cpp_siblings(siblings, self.source, &mut work_stack);
3769 }
3770 CppWork::Node(work) => {
3771 self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
3772 }
3773 }
3774 }
3775 continue;
3776 }
3777 if matches!(
3778 current.kind(),
3779 "translation_unit" | "labeled_statement" | "ERROR"
3780 ) {
3781 let mut cursor = current.walk();
3782 stack.extend(current.named_children(&mut cursor));
3783 }
3784 }
3785 }
3786
3787 fn visit_node<'tree>(
3788 &mut self,
3789 node: Node<'tree>,
3790 scope: &ScopeInfo,
3791 stack: &mut Vec<CppWork<'tree>>,
3792 ancestry: &ParentIndex<'tree>,
3793 ) {
3794 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
3795 self.visit_node(node, &recovered_scope, stack, ancestry);
3796 return;
3797 }
3798 if node.kind() == "function_definition" && node.has_error() {
3804 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
3805 }
3806 if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
3807 {
3808 let displaced_namespace_items =
3809 displaced_fragment_namespace_geometry(node, self.source)
3810 .map(|boundary| boundary.namespace_items)
3811 .unwrap_or_default();
3812 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
3813 let mut class_stack = Vec::new();
3814 let parser_visible_body =
3817 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
3818 .then(|| cpp_body_node(class_node))
3819 .flatten();
3820 let class_unit = self.visit_named_class_like_shape(
3821 class_node,
3822 name,
3823 parser_visible_body,
3824 true,
3825 Some(fragmented.class_range),
3826 Some(extract_cpp_supertypes(class_node, self.source)),
3827 scope,
3828 &mut class_stack,
3829 ancestry,
3830 );
3831 let member_scope = ScopeInfo {
3832 package_name: class_unit.package_name().to_string(),
3833 module: scope.module.clone(),
3834 class_unit: Some(class_unit.clone()),
3835 template_signature: scope.template_signature.clone(),
3836 template_metadata: None,
3837 declarations_are_fields: true,
3838 recovered_specialization_member_scope: false,
3839 visible_using_namespaces: scope.visible_using_namespaces.clone(),
3840 };
3841 let complete = outcome.is_some_and(|outcome| {
3842 self.visit_fragmented_export_class_members(outcome, class_unit, scope)
3843 });
3844 if complete {
3845 self.consumed_fragment_regions
3846 .push((node.start_byte(), fragmented.class_range.end_byte));
3847 } else {
3848 for candidate in cpp_following_named_siblings(node, self.source) {
3858 if candidate.start_byte() >= fragmented.reparse_end {
3859 break;
3860 }
3861 if cpp_fragment_sibling_is_class_member(
3862 candidate,
3863 fragmented.reparse_end,
3864 self.source,
3865 ) {
3866 self.recovered_class_sibling_scopes
3867 .insert(candidate.id(), member_scope.clone());
3868 }
3869 }
3870 }
3871 for item in displaced_namespace_items {
3872 self.recovered_class_sibling_scopes
3873 .insert(item.id(), scope.clone());
3874 }
3875 stack.extend(class_stack);
3876 return;
3877 }
3878 match node.kind() {
3879 "template_declaration" => {
3880 if let Some(recovered) =
3881 recover_fragmented_preprocessor_class(node, self.source, ancestry)
3882 {
3883 let mut template_scope = scope.clone();
3884 template_scope.template_signature =
3885 cpp_template_signature(node, recovered.declaration_node, self.source);
3886 template_scope.template_metadata =
3887 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
3888 let raw_supertypes =
3889 Some(extract_cpp_supertypes(recovered.class_node, self.source));
3890 let mut class_stack = Vec::new();
3891 let class_unit = self.visit_named_class_like_shape(
3892 recovered.class_node,
3893 recovered.name,
3894 Some(recovered.body),
3895 true,
3896 Some(recovered.range),
3897 raw_supertypes,
3898 &template_scope,
3899 &mut class_stack,
3900 ancestry,
3901 );
3902 self.parsed.record_materialization(
3903 MaterializationRecord::RecoveredDeclaration {
3904 recovery: recovered.range,
3905 unit: class_unit.clone(),
3906 },
3907 );
3908 let member_scope = ScopeInfo {
3909 package_name: template_scope.package_name.clone(),
3910 module: template_scope.module.clone(),
3911 class_unit: Some(class_unit.clone()),
3912 template_signature: template_scope.template_signature.clone(),
3913 template_metadata: None,
3914 declarations_are_fields: true,
3915 recovered_specialization_member_scope: recovered
3916 .class_node
3917 .child_by_field_name("name")
3918 .is_some_and(|name| name.kind() == "template_type"),
3919 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
3920 };
3921 for tail_member in recovered.tail_members.into_iter().rev() {
3922 stack.push(CppWork::Node(CppNodeWork {
3923 node: tail_member,
3924 scope: member_scope.clone(),
3925 }));
3926 }
3927 stack.extend(class_stack);
3928 for sibling in recovered.member_siblings {
3929 self.recovered_class_sibling_scopes
3930 .insert(sibling.id(), member_scope.clone());
3931 }
3932 return;
3933 }
3934 for index in (0..node.named_child_count()).rev() {
3935 let Some(child) = node.named_child(index) else {
3936 continue;
3937 };
3938 if matches!(
3939 child.kind(),
3940 "class_specifier"
3941 | "struct_specifier"
3942 | "union_specifier"
3943 | "enum_specifier"
3944 | "function_definition"
3945 | "declaration"
3946 | "field_declaration"
3947 | "alias_declaration"
3948 | "namespace_definition"
3949 ) {
3950 let mut template_scope = scope.clone();
3951 template_scope.template_signature =
3952 cpp_template_signature(node, child, self.source);
3953 template_scope.template_metadata =
3954 cpp_template_metadata(node, child, self.source, ancestry);
3955 if let Some(recovered) = recover_fragmented_partial_specialization(
3956 node,
3957 child,
3958 self.source,
3959 ancestry,
3960 ) {
3961 let code_unit = self.visit_named_class_like_shape(
3962 recovered.declaration_node,
3963 recovered.name,
3964 None,
3965 true,
3966 Some(recovered.range),
3967 None,
3968 &template_scope,
3969 stack,
3970 ancestry,
3971 );
3972 self.parsed.record_materialization(
3973 MaterializationRecord::RecoveredDeclaration {
3974 recovery: recovered.range,
3975 unit: code_unit.clone(),
3976 },
3977 );
3978 let mut member_scope = template_scope.clone();
3979 member_scope.class_unit = Some(code_unit);
3980 member_scope.declarations_are_fields = true;
3981 member_scope.recovered_specialization_member_scope = true;
3982 for prefix_member in recovered.prefix_members.into_iter().rev() {
3983 stack.push(CppWork::Node(CppNodeWork {
3984 node: prefix_member,
3985 scope: member_scope.clone(),
3986 }));
3987 }
3988 for sibling in recovered.member_siblings {
3989 self.recovered_class_sibling_scopes
3990 .insert(sibling.id(), member_scope.clone());
3991 }
3992 for following in recovered.following_declarations.into_iter().rev() {
3993 stack.push(CppWork::Node(CppNodeWork {
3994 node: following,
3995 scope: scope.clone(),
3996 }));
3997 }
3998 return;
3999 }
4000 stack.push(CppWork::Node(CppNodeWork {
4001 node: child,
4002 scope: template_scope,
4003 }));
4004 }
4005 }
4006 }
4007 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
4008 "linkage_specification" => {
4009 if let Some(body) = cpp_body_node(node) {
4010 stack.push(CppWork::Container(CppContainer {
4011 node: body,
4012 scope: scope.clone(),
4013 }));
4014 } else {
4015 stack.push(CppWork::Container(CppContainer {
4016 node,
4017 scope: scope.clone(),
4018 }));
4019 }
4020 }
4021 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
4022 self.visit_class_like(node, scope, stack, ancestry)
4023 }
4024 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
4025 "ERROR" => {
4032 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4033 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4034 if self.visit_collapsed_macro_declaration_run(node, scope) {
4035 return;
4036 }
4037 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4038 return;
4039 }
4040 self.visit_macro_swallowed_function_declarations(node, scope);
4041 self.visit_macro_wrapped_declarations(node, scope, ancestry);
4042 self.visit_stranded_class_members(node, scope, ancestry);
4043 stack.push(CppWork::Container(CppContainer {
4044 node,
4045 scope: scope.clone(),
4046 }));
4047 }
4048 }
4049 "declaration" => {
4050 if scope.class_unit.is_some()
4051 && scope.declarations_are_fields
4052 && scope.recovered_specialization_member_scope
4053 && let Some(alias_name) =
4054 recovered_using_declaration_alias_name(node, self.source)
4055 {
4056 self.add_type_aliases(node, scope, vec![alias_name]);
4057 } else {
4058 self.visit_declaration(
4059 node,
4060 scope,
4061 scope.declarations_are_fields,
4062 stack,
4063 ancestry,
4064 )
4065 }
4066 }
4067 "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
4068 "type_definition" | "alias_declaration" => {
4069 self.visit_type_declaration(node, scope, stack, ancestry)
4070 }
4071 "preproc_def" | "preproc_function_def" => self.visit_macro(node),
4072 "preproc_include" => {}
4078 kind if preserves_declaration_scope_through_wrapper(
4079 kind,
4080 scope.class_unit.is_some(),
4081 ) =>
4082 {
4083 if kind == "labeled_statement" {
4089 self.visit_access_label_constructor(node, scope);
4090 }
4091 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
4092 let mut range = cpp_declaration_range(node);
4093 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
4094 range.end_byte = boundary.end_byte;
4095 range.end_line = boundary.end_line;
4096 }
4097 self.parsed.record_materialization(
4098 MaterializationRecord::ConfigurationConditional { range },
4099 );
4100 if node.has_error() {
4101 let mut candidates = vec![node];
4110 while let Some(candidate) = candidates.pop() {
4111 if candidate.kind() == "ERROR"
4122 && !cpp_is_inside_namespace_body(candidate, ancestry)
4123 && self.visit_function_like_export_class_pair(
4124 candidate, scope, stack, ancestry,
4125 )
4126 {
4127 continue;
4128 }
4129 for index in (0..candidate.named_child_count()).rev() {
4130 candidates.push(
4131 candidate
4132 .named_child(index)
4133 .expect("index below the node's own named child count"),
4134 );
4135 }
4136 }
4137 }
4138 }
4139 stack.push(CppWork::Container(CppContainer {
4140 node,
4141 scope: scope.clone(),
4142 }))
4143 }
4144 _ => {}
4145 }
4146 }
4147
4148 fn visit_macro_swallowed_function_declarations<'tree>(
4149 &mut self,
4150 envelope: Node<'tree>,
4151 scope: &ScopeInfo,
4152 ) {
4153 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
4154 || envelope.kind() == "ERROR"
4155 && envelope
4156 .parent()
4157 .is_some_and(|parent| parent.kind() == "ERROR")
4158 {
4159 return;
4160 }
4161 let mut stack = (0..envelope.named_child_count())
4162 .filter_map(|index| envelope.named_child(index))
4163 .collect::<Vec<_>>();
4164 while let Some(node) = stack.pop() {
4165 if node.kind() == "function_declarator" {
4166 self.visit_error_swallowed_function_declaration(node, scope);
4167 }
4168 for index in 0..node.named_child_count() {
4169 if let Some(child) = node.named_child(index) {
4170 stack.push(child);
4171 }
4172 }
4173 }
4174 }
4175
4176 fn visit_macro_wrapped_declarations<'tree>(
4180 &mut self,
4181 envelope: Node<'tree>,
4182 scope: &ScopeInfo,
4183 ancestry: &ParentIndex<'tree>,
4184 ) {
4185 let recovered = macro_wrapped_declarations(envelope, self.source);
4186 if recovered.is_empty() {
4187 return;
4188 }
4189 let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
4190 self.record_recovered_declarations(recovery, |visitor| {
4191 for declaration in recovered {
4192 visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
4193 }
4194 });
4195 }
4196
4197 fn visit_collapsed_macro_declaration_run(
4217 &mut self,
4218 envelope: Node<'_>,
4219 scope: &ScopeInfo,
4220 ) -> bool {
4221 if collapsed_macro_declaration_run(envelope, self.source).is_none() {
4222 return false;
4223 }
4224 let start = envelope.start_byte();
4225 let end = envelope.end_byte();
4226 let recovery = cpp_recovery_window(self.source, start, end);
4227 self.record_recovered_declarations(recovery, |visitor| {
4228 let mut position = start;
4229 while position < end {
4230 let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
4231 return;
4232 };
4233 let root = tree.root_node();
4234 let ancestry = ParentIndex::new(root);
4237 let mut cursor = root.walk();
4238 let collapsed =
4239 root.named_children(&mut cursor)
4240 .enumerate()
4241 .find_map(|(index, item)| {
4242 collapsed_macro_declaration_run(item, visitor.source)
4243 .map(|run| (index, item, run))
4244 });
4245 let mut stack = Vec::new();
4250 push_cpp_sibling_range(
4251 root,
4252 0,
4253 collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
4254 scope.clone(),
4255 &mut stack,
4256 );
4257 visitor.drain_cpp_work(stack, &ancestry);
4258 let Some((_, item, run)) = collapsed else {
4259 return;
4260 };
4261 if let Some(head) =
4265 cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
4266 {
4267 let head_root = head.root_node();
4268 visitor.run_container_work(
4269 head_root,
4270 scope.clone(),
4271 &ParentIndex::new(head_root),
4272 );
4273 }
4274 assert!(
4275 run.invocation_end > position,
4276 "a collapsed run at {position} must end after the byte the scan resumed \
4277 from, but ended at {}",
4278 run.invocation_end
4279 );
4280 position = run.invocation_end;
4281 }
4282 });
4283 true
4284 }
4285
4286 fn visit_stranded_class_members<'tree>(
4296 &mut self,
4297 node: Node<'tree>,
4298 scope: &ScopeInfo,
4299 ancestry: &ParentIndex<'tree>,
4300 ) {
4301 if scope.class_unit.is_none() || !scope.declarations_are_fields {
4302 return;
4303 }
4304 for member in stranded_declaration_run(node, self.source).declarations {
4305 self.add_macro_wrapped_declaration(member, scope, ancestry);
4306 }
4307 }
4308
4309 fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
4317 let Some(class_unit) = scope.class_unit.clone() else {
4318 return;
4319 };
4320 if !scope.declarations_are_fields {
4321 return;
4322 }
4323 let class_name = class_unit.identifier().to_string();
4324 let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
4325 else {
4326 return;
4327 };
4328 let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
4329 return;
4330 };
4331 let root = tree.root_node();
4332 let Some(declarator) =
4333 cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
4334 else {
4335 return;
4336 };
4337 let reparsed_ancestry = ParentIndex::new(root);
4338 let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
4339 let range = cpp_declaration_range(definition.unwrap_or(declarator));
4340 let recovery = cpp_recovery_window(self.source, start, node.end_byte());
4341 self.record_recovered_declarations(recovery, |visitor| {
4342 visitor.add_macro_wrapped_declaration(
4343 MacroWrappedDeclaration {
4344 declarator,
4345 range,
4346 is_static: false,
4347 },
4348 scope,
4349 &reparsed_ancestry,
4350 );
4351 });
4352 }
4353
4354 fn add_macro_wrapped_declaration<'tree>(
4355 &mut self,
4356 declaration: MacroWrappedDeclaration<'tree>,
4357 scope: &ScopeInfo,
4358 ancestry: &ParentIndex<'tree>,
4359 ) {
4360 let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
4361 else {
4362 return;
4363 };
4364 let code_unit =
4365 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
4366 if self.parsed.contains_declaration(&code_unit) {
4367 self.parsed
4368 .record_navigation_range(code_unit, declaration.range);
4369 return;
4370 }
4371 self.add_declaration_with_range(code_unit.clone(), declaration.range, None, None);
4372 let signature = normalize_cpp_whitespace(
4373 self.source
4374 .get(declaration.range.start_byte..declaration.range.end_byte)
4375 .expect("a recovered declaration range covers one source range"),
4376 );
4377 let linkage = if declaration.is_static {
4378 CallableLinkage::Internal
4379 } else {
4380 cpp_callable_linkage(declaration.declarator, self.source, ancestry)
4381 };
4382 let declaration_only =
4386 cpp_declarator_function_definition(declaration.declarator, ancestry).is_none();
4387 self.parsed.add_signature_with_metadata(
4388 code_unit.clone(),
4389 cpp_signature_metadata(signature, declaration.declarator, self.source, ancestry)
4390 .with_declaration_only(declaration_only)
4391 .with_callable_linkage(linkage),
4392 );
4393 if let Some(parent) = &scope.class_unit {
4394 self.parsed.add_child(parent.clone(), code_unit);
4395 } else if let Some(module) = &scope.module {
4396 self.parsed.add_child(module.clone(), code_unit);
4397 }
4398 }
4399
4400 fn visit_error_swallowed_function_declaration<'tree>(
4401 &mut self,
4402 node: Node<'tree>,
4403 scope: &ScopeInfo,
4404 ) -> bool {
4405 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
4406 return false;
4407 };
4408 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
4409 return false;
4410 };
4411 let root = tree.root_node();
4412 let mut cursor = root.walk();
4413 let declarations = root
4414 .named_children(&mut cursor)
4415 .filter(|child| child.kind() != "comment")
4416 .collect::<Vec<_>>();
4417 let [declaration] = declarations.as_slice() else {
4418 return false;
4419 };
4420 if declaration.kind() != "declaration"
4421 || declaration.has_error()
4422 || declaration.start_byte() != start
4423 || declaration.end_byte() != end
4424 {
4425 return false;
4426 }
4427 let recovery = cpp_recovery_window(self.source, start, end);
4428 let reparsed_ancestry = ParentIndex::new(root);
4430 self.record_recovered_declarations(recovery, |visitor| {
4431 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
4432 });
4433 true
4434 }
4435
4436 fn visit_namespace<'tree>(
4437 &mut self,
4438 node: Node<'tree>,
4439 scope: &ScopeInfo,
4440 stack: &mut Vec<CppWork<'tree>>,
4441 ancestry: &ParentIndex<'tree>,
4442 ) {
4443 let name_node = node.child_by_field_name("name");
4444 let Some(name_node) = name_node else {
4445 if let Some(body) = cpp_body_node(node) {
4446 stack.push(CppWork::Container(CppContainer {
4447 node: body,
4448 scope: scope.clone(),
4449 }));
4450 }
4451 return;
4452 };
4453 let explicitly_global = name_node
4460 .child(0)
4461 .is_some_and(|child| !child.is_named() && child.kind() == "::");
4462 let components = cpp_namespace_name_components(name_node, self.source);
4463 if components.is_empty() {
4464 return;
4465 }
4466 let mut package_name = if explicitly_global {
4472 String::new()
4473 } else {
4474 scope.package_name.clone()
4475 };
4476 let mut module = None;
4477 for component in components {
4478 let full_name = if package_name.is_empty() {
4479 component
4480 } else {
4481 format!("{package_name}::{component}")
4482 };
4483 let level = CodeUnit::new_fq(
4484 self.file.clone(),
4485 CodeUnitType::Module,
4486 "",
4487 full_name.clone(),
4488 cpp_namespace_fq(&full_name),
4489 );
4490 if !self.parsed.contains_declaration(&level) {
4491 self.add_declaration(level.clone(), node, None, None);
4492 }
4493 package_name = full_name;
4494 module = Some(level);
4495 }
4496
4497 let namespace_scope = ScopeInfo {
4498 package_name,
4499 module,
4500 class_unit: None,
4508 template_signature: scope.template_signature.clone(),
4509 template_metadata: scope.template_metadata.clone(),
4510 declarations_are_fields: false,
4511 recovered_specialization_member_scope: false,
4512 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4513 };
4514 let container = cpp_body_node(node).unwrap_or(node);
4515 let mut candidates = vec![container];
4523 while let Some(candidate) = candidates.pop() {
4524 if matches!(
4525 candidate.kind(),
4526 "ERROR" | "function_definition" | "labeled_statement"
4527 ) && self.visit_embedded_function_like_export_classes(
4528 candidate,
4529 &namespace_scope,
4530 stack,
4531 ancestry,
4532 ) {
4533 continue;
4534 }
4535 for index in (0..candidate.named_child_count()).rev() {
4536 candidates.push(
4537 candidate
4538 .named_child(index)
4539 .expect("index below the node's own named child count"),
4540 );
4541 }
4542 }
4543 stack.push(CppWork::Container(CppContainer {
4544 node: container,
4545 scope: namespace_scope,
4546 }));
4547 }
4548
4549 fn visit_class_like<'tree>(
4550 &mut self,
4551 node: Node<'tree>,
4552 scope: &ScopeInfo,
4553 stack: &mut Vec<CppWork<'tree>>,
4554 ancestry: &ParentIndex<'tree>,
4555 ) {
4556 let Some(name) = class_like_name(node, self.source, ancestry) else {
4557 return;
4558 };
4559 let name = qualified_class_name_chain(node, self.source, scope)
4560 .map(|chain| chain.join("$"))
4561 .unwrap_or(name);
4562 self.visit_named_class_like(node, name, scope, stack, ancestry);
4563 }
4564
4565 fn visit_named_class_like<'tree>(
4566 &mut self,
4567 node: Node<'tree>,
4568 name: String,
4569 scope: &ScopeInfo,
4570 stack: &mut Vec<CppWork<'tree>>,
4571 ancestry: &ParentIndex<'tree>,
4572 ) {
4573 let body = cpp_body_node(node);
4574 let definition_body_present = body.is_some();
4575 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
4576 .then(|| extract_cpp_supertypes(node, self.source));
4577 self.visit_named_class_like_shape(
4578 node,
4579 name,
4580 body,
4581 definition_body_present,
4582 None,
4583 raw_supertypes,
4584 scope,
4585 stack,
4586 ancestry,
4587 );
4588 }
4589
4590 fn mints_tag_at_enclosing_c_scope(
4598 &self,
4599 declaration_node: Node<'_>,
4600 scope: &ScopeInfo,
4601 ancestry: &ParentIndex<'_>,
4602 ) -> bool {
4603 self.c_tag_semantics
4604 && scope.class_unit.is_some()
4605 && class_like_name(declaration_node, self.source, ancestry).is_some()
4606 && matches!(
4607 declaration_node.kind(),
4608 "struct_specifier" | "union_specifier" | "enum_specifier"
4609 )
4610 }
4611
4612 #[allow(clippy::too_many_arguments)]
4613 fn visit_named_class_like_shape<'tree>(
4614 &mut self,
4615 declaration_node: Node<'tree>,
4616 name: String,
4617 body: Option<Node<'tree>>,
4618 definition_body_present: bool,
4619 explicit_range: Option<Range>,
4620 raw_supertypes: Option<Vec<String>>,
4621 scope: &ScopeInfo,
4622 stack: &mut Vec<CppWork<'tree>>,
4623 ancestry: &ParentIndex<'tree>,
4624 ) -> CodeUnit {
4625 let displaced_macro_tail = if explicit_range.is_none() {
4626 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
4627 } else {
4628 None
4629 };
4630 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
4631 let recovered_scope = self.scope_for_recovered_exported_class(
4632 declaration_node,
4633 &name,
4634 definition_body_present,
4635 scope,
4636 ancestry,
4637 );
4638 let c_tag_scope;
4648 let scope =
4649 if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
4650 c_tag_scope = ScopeInfo {
4651 class_unit: None,
4652 ..recovered_scope.clone()
4653 };
4654 &c_tag_scope
4655 } else {
4656 &recovered_scope
4657 };
4658 let short_name = if let Some(parent) = &scope.class_unit {
4659 cpp_join_nested_short(parent.short_name(), &name)
4660 } else {
4661 name.clone()
4662 };
4663 let qualified_chain = if scope.class_unit.is_none() {
4670 qualified_class_name_chain(declaration_node, self.source, scope)
4671 .filter(|chain| chain.join("$") == name)
4672 } else {
4673 None
4674 };
4675 let fq = if let Some(chain) = qualified_chain {
4676 let mut fq = FqName::new();
4677 cpp_push_package(&mut fq, &scope.package_name);
4678 let mut first = true;
4679 for component in chain {
4680 let kind = if first {
4681 SegmentKind::Type
4682 } else {
4683 SegmentKind::Nested
4684 };
4685 fq.push(cpp_segment(&component, kind));
4686 first = false;
4687 }
4688 fq
4689 } else {
4690 cpp_leaf_fq(
4691 &scope.package_name,
4692 scope.class_unit.as_ref(),
4693 &name,
4694 SegmentKind::Nested,
4695 SegmentKind::Type,
4696 )
4697 };
4698 let code_unit = CodeUnit::with_signature_and_fq(
4699 self.file.clone(),
4700 CodeUnitType::Class,
4701 scope.package_name.clone(),
4702 short_name,
4703 scope.template_signature.clone(),
4704 false,
4705 fq,
4706 );
4707 let has_body = definition_body_present;
4708 if !has_body && self.parsed.contains_declaration(&code_unit) {
4709 self.parsed.record_navigation_range(
4710 code_unit.clone(),
4711 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
4712 );
4713 return code_unit;
4714 }
4715 if has_body {
4716 if let Some(range) = explicit_range {
4717 self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
4718 } else {
4719 self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
4720 }
4721 } else {
4722 self.add_declaration(code_unit.clone(), declaration_node, None, None);
4723 }
4724 if let Some(raw_supertypes) = raw_supertypes {
4725 self.parsed
4726 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
4727 }
4728 self.parsed.add_signature(
4729 code_unit.clone(),
4730 render_cpp_type_signature(
4731 declaration_node,
4732 self.source,
4733 scope.template_signature.as_deref(),
4734 ),
4735 );
4736 if let Some(metadata) = &scope.template_metadata {
4737 let primary_short_name = if let Some(parent) = &scope.class_unit {
4738 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
4739 } else {
4740 metadata.primary_name.clone()
4741 };
4742 let primary_fq_name = CodeUnit::new(
4743 self.file.clone(),
4744 CodeUnitType::Class,
4745 scope.package_name.clone(),
4746 primary_short_name,
4747 )
4748 .fq_name();
4749 let mut metadata = metadata.clone();
4750 metadata.primary_fq_name = primary_fq_name;
4751 self.parsed
4752 .set_cpp_template_metadata(code_unit.clone(), metadata);
4753 }
4754 if let Some(parent) = &scope.class_unit {
4755 self.parsed.add_child(parent.clone(), code_unit.clone());
4756 } else if let Some(module) = &scope.module {
4757 self.parsed.add_child(module.clone(), code_unit.clone());
4758 }
4759
4760 if let Some(body) = body {
4761 let mut nested_scope = scope.clone();
4762 nested_scope.class_unit = Some(code_unit.clone());
4763 nested_scope.template_signature = scope.template_signature.clone();
4764 nested_scope.template_metadata = None;
4769 nested_scope.recovered_specialization_member_scope =
4772 scope.template_metadata.as_ref().is_some_and(|metadata| {
4773 declaration_node.kind() == "function_definition" && metadata.is_specialization()
4774 });
4775 nested_scope.declarations_are_fields =
4776 is_recovered_exported_class_container(declaration_node, self.source)
4777 || nested_scope.recovered_specialization_member_scope;
4778 if let Some(displaced) = displaced_macro_tail {
4779 push_cpp_sibling_range(
4787 body,
4788 displaced.split_index,
4789 usize::MAX,
4790 scope.clone(),
4791 stack,
4792 );
4793 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
4794 } else {
4795 stack.push(CppWork::Container(CppContainer {
4796 node: body,
4797 scope: nested_scope,
4798 }));
4799 }
4800 }
4801 if declaration_node.kind() == "enum_specifier" {
4802 self.visit_enum_enumerators(declaration_node, scope, &code_unit);
4803 if !self.has_enum_enumerator_units(&code_unit) {
4804 self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
4805 }
4806 }
4807 code_unit
4808 }
4809
4810 fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
4818 if self.field_owners.is_none() {
4819 self.field_owners = Some(CppFieldOwnerIndex::of(
4820 self.parsed.declarations().iter(),
4821 self.file,
4822 ));
4823 }
4824 debug_assert_eq!(
4825 parent.source(),
4826 self.file,
4827 "the walk's declarations are declarations of the file it is walking"
4828 );
4829 let carried = self
4830 .field_owners
4831 .as_ref()
4832 .expect("the index was just ensured")
4833 .owns_fields(parent.package_name(), parent.short_name());
4834
4835 #[cfg(debug_assertions)]
4836 assert_eq!(
4837 carried,
4838 cpp_declarations_hold_owned_fields(
4839 self.parsed.declarations(),
4840 self.file,
4841 parent.package_name(),
4842 parent.short_name()
4843 ),
4844 "the carried-forward field index must answer what a fresh declaration scan \
4845 answers for {}",
4846 parent.fq_name()
4847 );
4848
4849 carried
4850 }
4851
4852 fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
4853 walk_named_tree_preorder(node, false, |child| {
4854 if child.kind() != "enumerator" {
4855 return WalkControl::Continue;
4856 }
4857 let Some(name_node) = child.child_by_field_name("name") else {
4858 return WalkControl::Continue;
4859 };
4860 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
4861 if name.is_empty() {
4862 return WalkControl::Continue;
4863 }
4864 let code_unit = CodeUnit::new_fq(
4865 self.file.clone(),
4866 CodeUnitType::Field,
4867 scope.package_name.clone(),
4868 cpp_join_member_short(parent.short_name(), &name),
4869 parent
4870 .fq()
4871 .clone()
4872 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
4873 );
4874 if self.parsed.contains_declaration(&code_unit) {
4875 return WalkControl::Continue;
4876 }
4877 self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
4878 self.parsed.add_signature(
4879 code_unit,
4880 normalize_cpp_whitespace(node_text(child, self.source)),
4881 );
4882 WalkControl::Continue
4883 });
4884 }
4885
4886 fn visit_enum_enumerators_from_text(
4887 &mut self,
4888 node: Node<'_>,
4889 scope: &ScopeInfo,
4890 parent: &CodeUnit,
4891 ) {
4892 let text = node_text(node, self.source);
4893 let Some((_, body)) = text.split_once('{') else {
4894 return;
4895 };
4896 let Some((body, _)) = body.rsplit_once('}') else {
4897 return;
4898 };
4899 for entry in body.split(',') {
4900 let trimmed = entry.trim();
4901 let name = trimmed
4902 .split('=')
4903 .next()
4904 .unwrap_or("")
4905 .split_whitespace()
4906 .next()
4907 .unwrap_or("");
4908 if name.is_empty() {
4909 continue;
4910 }
4911 let code_unit = CodeUnit::new_fq(
4912 self.file.clone(),
4913 CodeUnitType::Field,
4914 scope.package_name.clone(),
4915 cpp_join_member_short(parent.short_name(), name),
4916 parent
4917 .fq()
4918 .clone()
4919 .with_pushed(cpp_segment(name, SegmentKind::Member)),
4920 );
4921 if self.parsed.contains_declaration(&code_unit) {
4922 continue;
4923 }
4924 self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
4925 self.parsed.add_signature(code_unit, trimmed.to_string());
4926 }
4927 }
4928
4929 fn visit_function_definition<'tree>(
4930 &mut self,
4931 node: Node<'tree>,
4932 scope: &ScopeInfo,
4933 stack: &mut Vec<CppWork<'tree>>,
4934 ancestry: &ParentIndex<'tree>,
4935 ) {
4936 if self.visit_collapsed_macro_declaration_run(node, scope) {
4943 return;
4944 }
4945 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4951 return;
4952 }
4953 if node.has_error() {
4954 self.visit_macro_swallowed_function_declarations(node, scope);
4955 }
4956 if let Some((class_node, name, raw_supertypes)) =
4957 recover_exported_class_function_definition(node, self.source)
4958 {
4959 let body = cpp_body_node(class_node);
4960 let displaced_namespace = cpp_body_node(node)
4961 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
4962 let fragmented = cpp_body_node(node).and_then(|body| {
4963 fragmented_export_function_body_region(
4964 node,
4965 body,
4966 self.source,
4967 displaced_namespace.as_ref(),
4968 )
4969 });
4970 if let Some(fragmented) = fragmented {
4976 if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
4980 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
4981 {
4982 let mut boundary_scope = scope.clone();
4983 for sibling in cpp_following_named_siblings(node, self.source) {
4984 if sibling.start_byte() >= boundary.start_byte() {
4985 break;
4986 }
4987 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
4988 boundary_scope.visible_using_namespaces.push(namespace);
4989 }
4990 }
4991 self.recovered_class_sibling_scopes
4992 .insert(boundary.id(), boundary_scope);
4993 }
4994 let mut recovered_constructor = None;
4995 let mut recovered_prefix_tree = None;
4996 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
4997 {
4998 Some(FragmentedExportMembers::Complete(tree)) => {
4999 if let Some(body) = body
5000 && let Some(range) =
5001 cpp_reparsed_synthetic_initializer_constructor_range(
5002 tree.root_node(),
5003 &name,
5004 self.source,
5005 body.end_byte(),
5006 )
5007 {
5008 recovered_constructor = Some(range);
5009 recovered_prefix_tree = Some(tree);
5010 None
5011 } else {
5012 Some(FragmentedExportMembers::Complete(tree))
5013 }
5014 }
5015 outcome => outcome,
5016 };
5017 let mut class_stack = Vec::new();
5018 let class_unit = self.visit_named_class_like_shape(
5019 class_node,
5020 name,
5021 None,
5022 true,
5023 Some(fragmented.class_range),
5024 raw_supertypes,
5025 scope,
5026 &mut class_stack,
5027 ancestry,
5028 );
5029 self.parsed
5030 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5031 recovery: fragmented.class_range,
5032 unit: class_unit.clone(),
5033 });
5034 let complete = outcome.is_some_and(|outcome| {
5035 self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
5036 });
5037 if complete {
5038 self.consumed_fragment_regions
5039 .push((node.start_byte(), fragmented.class_range.end_byte));
5040 } else {
5041 let member_scope = ScopeInfo {
5050 package_name: class_unit.package_name().to_string(),
5051 module: scope.module.clone(),
5052 class_unit: Some(class_unit.clone()),
5053 template_signature: scope.template_signature.clone(),
5054 template_metadata: None,
5055 declarations_are_fields: true,
5056 recovered_specialization_member_scope: false,
5057 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5058 };
5059 for candidate in cpp_following_named_siblings(node, self.source) {
5060 if candidate.start_byte() >= fragmented.reparse_end {
5061 break;
5062 }
5063 if cpp_fragment_sibling_is_class_member(
5064 candidate,
5065 fragmented.reparse_end,
5066 self.source,
5067 ) {
5068 self.recovered_class_sibling_scopes
5069 .insert(candidate.id(), member_scope.clone());
5070 }
5071 }
5072 if let Some(range) = recovered_constructor
5073 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
5074 {
5075 self.visit_recovered_fragment_prefix_members(
5076 prefix_tree.root_node(),
5077 range.start,
5078 &class_unit,
5079 scope,
5080 ancestry,
5081 );
5082 self.visit_recovered_fragment_constructor(
5083 range,
5084 body,
5085 class_node,
5086 &class_unit,
5087 scope,
5088 ancestry,
5089 );
5090 }
5091 }
5092 if let Some(boundary) = displaced_namespace {
5093 for item in boundary.namespace_items {
5094 self.recovered_class_sibling_scopes
5095 .insert(item.id(), scope.clone());
5096 }
5097 }
5098 stack.extend(class_stack);
5099 return;
5100 }
5101 let mut stack = Vec::new();
5102 let class_unit = self.visit_named_class_like_shape(
5103 class_node,
5104 name,
5105 body,
5106 body.is_some(),
5107 None,
5108 raw_supertypes,
5109 scope,
5110 &mut stack,
5111 ancestry,
5112 );
5113 self.parsed
5114 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5115 recovery: cpp_declaration_range(node),
5116 unit: class_unit,
5117 });
5118 if let Some(body) = body
5125 && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
5126 && class_close < body.end_byte()
5127 {
5128 let split = {
5129 let mut cursor = body.walk();
5130 body.named_children(&mut cursor)
5131 .position(|child| child.start_byte() > class_close)
5132 };
5133 if let Some(split) = split {
5134 let seeded = stack.pop();
5139 match seeded {
5140 Some(CppWork::Container(container)) => {
5141 push_cpp_sibling_range(
5142 body,
5143 split,
5144 usize::MAX,
5145 scope.clone(),
5146 &mut stack,
5147 );
5148 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
5149 }
5150 _ => unreachable!("exported-class seed is always one Container"),
5153 }
5154 }
5155 }
5156 while let Some(work) = stack.pop() {
5157 match work {
5158 CppWork::Container(container) => {
5159 push_cpp_container_work(container.node, container.scope, &mut stack);
5160 }
5161 CppWork::Siblings(siblings) => {
5162 advance_cpp_siblings(siblings, self.source, &mut stack);
5163 }
5164 CppWork::Node(work) => {
5165 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
5166 }
5167 }
5168 }
5169 return;
5170 }
5171 let recovered_constraint_constructor =
5172 cpp_recovered_template_macro_constructor(node, self.source);
5173 let declarator = recovered_constraint_constructor
5174 .map(|(declarator, _)| declarator)
5175 .or_else(|| node.child_by_field_name("declarator"));
5176 let Some(declarator) = declarator else {
5177 self.visit_malformed_function_definition_container(node, scope, stack);
5178 return;
5179 };
5180 let Some(function_declarator) = extract_function_declarator(declarator) else {
5181 self.visit_malformed_function_definition_container(node, scope, stack);
5182 return;
5183 };
5184 let function = if let Some((_, callable_name)) =
5185 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
5186 {
5187 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
5188 } else {
5189 extract_function_info(function_declarator, self.source, scope)
5190 };
5191 let Some(mut function) = function else {
5192 self.visit_malformed_function_definition_container(node, scope, stack);
5193 return;
5194 };
5195 if let Some((_, template_parameter)) = recovered_constraint_constructor {
5196 function.signature = format!(
5197 "template <{}>{}",
5198 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
5199 function.signature
5200 );
5201 }
5202 let code_unit = function.code_unit(self.file.clone());
5203 self.add_declaration(code_unit.clone(), node, None, None);
5208 let signature = if recovered_constraint_constructor.is_some() {
5209 normalize_cpp_whitespace(node_text(function_declarator, self.source))
5210 } else {
5211 render_cpp_function_display_signature_from_node(
5212 node,
5213 self.source,
5214 scope.template_signature.as_deref(),
5215 true,
5216 ancestry,
5217 )
5218 };
5219 self.parsed.add_signature_with_metadata(
5220 code_unit.clone(),
5221 cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
5222 .with_declaration_only(false)
5223 .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
5224 );
5225 if let Some(parent) = &scope.class_unit {
5226 self.parsed.add_child(parent.clone(), code_unit);
5227 } else if let Some(module) = &scope.module {
5228 self.parsed.add_child(module.clone(), code_unit);
5229 }
5230 }
5231
5232 fn scope_for_recovered_exported_class<'tree>(
5237 &mut self,
5238 node: Node<'tree>,
5239 name: &str,
5240 definition_body_present: bool,
5241 scope: &ScopeInfo,
5242 ancestry: &ParentIndex<'tree>,
5243 ) -> ScopeInfo {
5244 if !definition_body_present
5245 || !scope.package_name.is_empty()
5246 || scope.class_unit.is_some()
5247 || !(is_recovered_exported_class_container(node, self.source)
5248 || recover_function_like_export_class_pair(node, self.source).is_some()
5249 || recover_embedded_function_like_export_classes(node, self.source)
5250 .iter()
5251 .any(|recovered| recovered.name == name)
5252 || matches!(node.kind(), "declaration" | "field_declaration")
5253 && recover_exported_class_declaration(node, self.source).is_some()
5254 || matches!(
5255 node.kind(),
5256 "class_specifier" | "struct_specifier" | "union_specifier"
5257 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
5258 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
5259 name_node,
5260 self.source,
5261 )))
5262 }) || ancestry.parent(node).is_some_and(|parent| {
5263 matches!(parent.kind(), "declaration" | "field_declaration")
5264 && recover_exported_class_declaration(parent, self.source).is_some()
5265 || is_recovered_exported_class_container(parent, self.source)
5266 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
5267 {
5268 return scope.clone();
5269 }
5270 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
5271 let Some(package_name) = borrowed_namespace
5272 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
5273 else {
5274 return scope.clone();
5275 };
5276
5277 let module = CodeUnit::new_fq(
5278 self.file.clone(),
5279 CodeUnitType::Module,
5280 "",
5281 package_name.clone(),
5282 cpp_namespace_fq(&package_name),
5283 );
5284 let mut recovered = scope.clone();
5285 recovered.package_name = package_name;
5286 recovered.module = Some(module);
5287 recovered
5288 }
5289
5290 fn unique_earlier_namespace_forward<'tree>(
5299 &mut self,
5300 recovered_node: Node<'tree>,
5301 name: &str,
5302 ancestry: &ParentIndex<'tree>,
5303 ) -> Option<String> {
5304 let mut root = recovered_node;
5305 while let Some(parent) = ancestry.parent(root) {
5306 root = parent;
5307 }
5308 let source = self.source;
5309 let scan = self
5310 .namespace_forward_scans
5311 .entry(CppTreeIdentity::of(root))
5312 .or_default();
5313 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
5314 let borrowed = scan.unique_earlier_forward(name, recovered_node);
5315
5316 #[cfg(debug_assertions)]
5317 assert_eq!(
5318 borrowed,
5319 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
5320 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
5321 for {name} at byte {}",
5322 recovered_node.start_byte()
5323 );
5324
5325 borrowed
5326 }
5327
5328 fn visit_malformed_function_definition_container<'tree>(
5329 &mut self,
5330 node: Node<'tree>,
5331 scope: &ScopeInfo,
5332 stack: &mut Vec<CppWork<'tree>>,
5333 ) {
5334 let Some(body) = cpp_body_node(node) else {
5335 return;
5336 };
5337 if !cpp_contains_namespace_definition(body) {
5338 return;
5339 }
5340 stack.push(CppWork::Container(CppContainer {
5341 node: body,
5342 scope: scope.clone(),
5343 }));
5344 }
5345
5346 fn record_recovered_declarations(
5364 &mut self,
5365 recovery: Range,
5366 reparse_walk: impl FnOnce(&mut Self),
5367 ) {
5368 #[cfg(any(debug_assertions, test))]
5371 let before = self.parsed.declarations().clone();
5372
5373 self.recovery_captures.push(CppRecoveryCapture::default());
5374 reparse_walk(self);
5375 let captured = self
5376 .recovery_captures
5377 .pop()
5378 .expect("the capture this call pushed is the one it pops");
5379
5380 let mut minted: Vec<CodeUnit> = captured
5386 .created
5387 .into_iter()
5388 .filter(|unit| self.parsed.contains_declaration(unit))
5389 .collect();
5390 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
5391
5392 #[cfg(any(debug_assertions, test))]
5393 {
5394 let mut rediscovered: Vec<CodeUnit> = self
5395 .parsed
5396 .declarations()
5397 .iter()
5398 .filter(|unit| !before.contains(*unit))
5399 .cloned()
5400 .collect();
5401 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
5402 assert_eq!(
5403 minted, rediscovered,
5404 "the captured recovered set must be the declaration delta of the reparse \
5405 walk over {recovery:?}"
5406 );
5407 }
5408
5409 for unit in minted {
5410 self.parsed
5411 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5412 recovery,
5413 unit,
5414 });
5415 }
5416 }
5417
5418 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
5421 let start = self
5422 .parsed
5423 .declaration_ranges(unit)
5424 .first()
5425 .map(|range| range.start_byte)
5426 .unwrap_or(usize::MAX);
5427 (start, unit.fq_name().to_string())
5428 }
5429
5430 fn visit_sentinel_macro_region<'tree>(
5431 &mut self,
5432 node: Node<'tree>,
5433 scope: &ScopeInfo,
5434 stack: &mut Vec<CppWork<'tree>>,
5435 ancestry: &ParentIndex<'tree>,
5436 ) -> bool {
5437 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
5438 return true;
5439 }
5440 if let Some((
5441 reparse_start,
5442 class_start,
5443 body_start,
5444 class_close_start,
5445 class_close_end,
5446 class_close_line,
5447 )) = cpp_sentinel_macro_class_region(node, self.source)
5448 {
5449 let Some(class_tree) =
5450 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
5451 else {
5452 return false;
5453 };
5454 let class_root = class_tree.root_node();
5455 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
5456 let class_ancestry = ParentIndex::new(class_root);
5458 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
5459 class_root,
5460 template_node,
5461 self.source,
5462 &class_ancestry,
5463 ) else {
5464 return false;
5465 };
5466 let class_node = reparsed_class.declaration_node;
5467 let name = reparsed_class.name;
5468 let mut class_scope = scope.clone();
5469 if let Some(template_node) = template_node {
5470 class_scope.template_signature =
5471 cpp_template_signature(template_node, class_node, self.source);
5472 class_scope.template_metadata =
5473 cpp_template_metadata(template_node, class_node, self.source, ancestry);
5474 }
5475 let Some(body_tree) =
5476 cpp_reparse_region_items(self.source, body_start, class_close_start)
5477 else {
5478 return false;
5479 };
5480 let raw_supertypes = reparsed_class.raw_supertypes;
5481 let class_range = Range {
5482 start_byte: class_start,
5483 end_byte: class_close_end,
5484 start_line: class_node.start_position().row + 1,
5485 end_line: class_close_line,
5486 };
5487 let class_scope = self.scope_for_recovered_exported_class(
5488 class_node,
5489 &name,
5490 true,
5491 &class_scope,
5492 ancestry,
5493 );
5494 let mut class_stack = Vec::new();
5495 let class_unit = self.visit_named_class_like_shape(
5496 class_node,
5497 name,
5498 None,
5499 true,
5500 Some(class_range),
5501 raw_supertypes,
5502 &class_scope,
5503 &mut class_stack,
5504 ancestry,
5505 );
5506 self.parsed
5507 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5508 recovery: class_range,
5509 unit: class_unit.clone(),
5510 });
5511 let member_scope = ScopeInfo {
5512 package_name: class_scope.package_name.clone(),
5513 module: class_scope.module.clone(),
5514 class_unit: Some(class_unit),
5515 template_signature: class_scope.template_signature.clone(),
5516 template_metadata: None,
5517 declarations_are_fields: true,
5518 recovered_specialization_member_scope: false,
5519 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
5520 };
5521 let body_root = body_tree.root_node();
5523 self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
5524 self.consumed_fragment_regions
5527 .push((node.start_byte(), class_close_end));
5528 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
5535 stack.push(CppWork::Container(CppContainer {
5536 node,
5537 scope: scope.clone(),
5538 }));
5539 }
5540 return true;
5541 }
5542 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
5543 return false;
5544 };
5545 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
5546 return false;
5547 };
5548 let root = tree.root_node();
5549 if !cpp_reparsed_items_are_indexable(root, self.source) {
5550 return false;
5551 }
5552 let recovery = cpp_recovery_window(self.source, start, end);
5553 let reparsed_ancestry = ParentIndex::new(root);
5555 self.record_recovered_declarations(recovery, |visitor| {
5556 visitor.visit_container(
5557 root,
5558 &reparsed_ancestry,
5559 &scope.package_name,
5560 scope.module.clone(),
5561 scope.class_unit.clone(),
5562 scope.template_signature.clone(),
5563 scope.visible_using_namespaces.clone(),
5564 );
5565 });
5566 if end > node.end_byte() {
5567 self.consumed_fragment_regions
5568 .push((node.start_byte(), end));
5569 } else if node.kind() == "ERROR" && node.end_byte() > end {
5570 self.consumed_fragment_regions
5577 .push((node.start_byte(), end));
5578 stack.push(CppWork::Container(CppContainer {
5579 node,
5580 scope: scope.clone(),
5581 }));
5582 }
5583 true
5584 }
5585
5586 fn visit_nested_namespace_sentinel<'tree>(
5592 &mut self,
5593 node: Node<'tree>,
5594 scope: &ScopeInfo,
5595 ancestry: &ParentIndex<'tree>,
5596 ) -> bool {
5597 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
5598 return false;
5599 };
5600
5601 let mut package_name = scope.package_name.clone();
5602 let mut module = scope.module.clone();
5603 for component in recovered.namespace_components {
5604 package_name = if package_name.is_empty() {
5605 component
5606 } else {
5607 format!("{package_name}::{component}")
5608 };
5609 let namespace_module = CodeUnit::new_fq(
5610 self.file.clone(),
5611 CodeUnitType::Module,
5612 "",
5613 package_name.clone(),
5614 cpp_namespace_fq(&package_name),
5615 );
5616 if !self.parsed.contains_declaration(&namespace_module) {
5617 self.add_declaration(namespace_module.clone(), recovered.function, None, None);
5618 }
5619 module = Some(namespace_module);
5620 }
5621
5622 let recovered_scope = ScopeInfo {
5623 package_name,
5624 module,
5625 class_unit: scope.class_unit.clone(),
5626 template_signature: scope.template_signature.clone(),
5627 template_metadata: scope.template_metadata.clone(),
5628 declarations_are_fields: false,
5629 recovered_specialization_member_scope: false,
5630 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5631 };
5632 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
5633 recovered.function,
5634 recovered.body,
5635 self.source,
5636 ancestry,
5637 ) {
5638 let mut class_scope = recovered_scope.clone();
5639 if let Some(template_node) = fragmented.template_node {
5640 class_scope.template_signature =
5641 cpp_template_signature(template_node, fragmented.class_node, self.source);
5642 class_scope.template_metadata = cpp_template_metadata(
5643 template_node,
5644 fragmented.class_node,
5645 self.source,
5646 ancestry,
5647 );
5648 }
5649 if let Some(outcome) = self
5650 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
5651 {
5652 let mut class_stack = Vec::new();
5653 let class_unit = self.visit_named_class_like_shape(
5654 fragmented.class_node,
5655 fragmented.name.clone(),
5656 None,
5657 true,
5658 Some(fragmented.fragmented.class_range),
5659 fragmented.raw_supertypes.clone(),
5660 &class_scope,
5661 &mut class_stack,
5662 ancestry,
5663 );
5664 self.parsed
5665 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5666 recovery: fragmented.fragmented.class_range,
5667 unit: class_unit.clone(),
5668 });
5669 if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
5670 self.consumed_fragment_regions.push((
5671 fragmented.consumed_start,
5672 fragmented.fragmented.class_range.end_byte,
5673 ));
5674 }
5675 }
5676 }
5677 self.run_container_work(recovered.body, recovered_scope, ancestry);
5682 true
5683 }
5684
5685 fn visit_declaration<'tree>(
5686 &mut self,
5687 node: Node<'tree>,
5688 scope: &ScopeInfo,
5689 in_class_body: bool,
5690 stack: &mut Vec<CppWork<'tree>>,
5691 ancestry: &ParentIndex<'tree>,
5692 ) {
5693 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
5694 return;
5695 }
5696 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
5697 !cpp_active_template_type_parameter(
5698 node,
5699 node_text(declarator, self.source),
5700 self.source,
5701 ancestry,
5702 )
5703 }) {
5704 return;
5705 }
5706 if in_class_body
5707 && let Some(parent) = scope.class_unit.as_ref()
5708 && let Some(call) =
5709 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
5710 {
5711 self.visit_recovered_macro_qualified_constructor_definition(
5712 node, call, scope, ancestry,
5713 );
5714 return;
5715 }
5716 if in_class_body
5717 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
5718 {
5719 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
5720 return;
5721 }
5722 if in_class_body
5723 && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
5724 {
5725 for member in members {
5726 self.add_macro_wrapped_declaration(member, scope, ancestry);
5727 }
5728 return;
5729 }
5730 if in_class_body
5731 && let Some(declarators) =
5732 recovered_macro_qualified_field_declarators(node, self.source)
5733 {
5734 for declarator in declarators {
5735 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
5736 }
5737 return;
5738 }
5739 let recovered_alias_names = recovered_type_alias_names(node, self.source);
5740 if !recovered_alias_names.is_empty() {
5741 self.add_type_aliases(node, scope, recovered_alias_names);
5742 return;
5743 }
5744 if self.visit_c_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry)
5745 {
5746 return;
5747 }
5748
5749 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
5750 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
5751 if let Some(outcome) =
5756 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
5757 {
5758 let consumed_region = (
5759 recovered.declaration_node.end_byte(),
5760 fragmented.class_range.end_byte,
5761 );
5762 let code_unit = self.visit_named_class_like_shape(
5763 recovered.declaration_node,
5764 recovered.name,
5765 None,
5766 true,
5767 Some(fragmented.class_range),
5768 recovered.raw_supertypes,
5769 scope,
5770 stack,
5771 ancestry,
5772 );
5773 self.parsed.record_materialization(
5774 MaterializationRecord::RecoveredDeclaration {
5775 recovery: fragmented.class_range,
5776 unit: code_unit.clone(),
5777 },
5778 );
5779 let consume_fragment =
5780 self.visit_fragmented_export_class_members(outcome, code_unit, scope);
5781 if consume_fragment {
5787 self.consumed_fragment_regions.push(consumed_region);
5788 }
5789 return;
5790 }
5791 }
5792 let uses_initializer_body = recovered.uses_initializer_body;
5793 let definition_body_present = recovered.body.is_some();
5794 let class_unit = self.visit_named_class_like_shape(
5795 recovered.declaration_node,
5796 recovered.name,
5797 recovered.body,
5798 definition_body_present,
5799 None,
5800 recovered.raw_supertypes,
5801 scope,
5802 stack,
5803 ancestry,
5804 );
5805 self.parsed
5806 .record_materialization(MaterializationRecord::RecoveredDeclaration {
5807 recovery: cpp_declaration_range(node),
5808 unit: class_unit,
5809 });
5810 if uses_initializer_body {
5811 return;
5812 }
5813 }
5814
5815 let mut handled_function = false;
5816 let mut handled_declarator = false;
5817 let mut cursor = node.walk();
5818 for child in node.named_children(&mut cursor) {
5819 if matches!(
5820 child.kind(),
5821 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
5822 ) {
5823 if cpp_body_node(child).is_some() {
5832 self.visit_class_like(child, scope, stack, ancestry);
5833 }
5834 continue;
5835 }
5836 }
5837
5838 let mut cursor = node.walk();
5839 for child in node.children_by_field_name("declarator", &mut cursor) {
5840 if crate::structural::is_recovered_designator_init_declarator(child) {
5841 handled_declarator = true;
5842 continue;
5843 }
5844 if let Some(kind) = classify_declarator(child) {
5845 handled_declarator = true;
5846 match kind {
5847 DeclaratorKind::Function(function_declarator) => {
5848 handled_function = true;
5849 self.visit_function_declaration(node, function_declarator, scope, ancestry);
5850 }
5851 DeclaratorKind::Variable(variable_declarator) => {
5852 self.visit_variable_declaration(
5853 node,
5854 variable_declarator,
5855 scope,
5856 in_class_body,
5857 ancestry,
5858 );
5859 }
5860 }
5861 }
5862 }
5863
5864 if !handled_declarator {
5865 let mut cursor = node.walk();
5866 for child in node.named_children(&mut cursor) {
5867 if crate::structural::is_recovered_designator_init_declarator(child) {
5868 handled_declarator = true;
5869 continue;
5870 }
5871 if !is_unfielded_declarator_candidate(child) {
5872 continue;
5873 }
5874 let Some(kind) = classify_declarator(child) else {
5875 continue;
5876 };
5877 handled_declarator = true;
5878 match kind {
5879 DeclaratorKind::Function(function_declarator) => {
5880 handled_function = true;
5881 self.visit_function_declaration(node, function_declarator, scope, ancestry);
5882 }
5883 DeclaratorKind::Variable(variable_declarator) => {
5884 self.visit_variable_declaration(
5885 node,
5886 variable_declarator,
5887 scope,
5888 in_class_body,
5889 ancestry,
5890 );
5891 }
5892 }
5893 }
5894 }
5895
5896 if handled_function {
5897 return;
5898 }
5899
5900 if !handled_declarator {
5901 if in_class_body {
5902 self.visit_class_members_from_declaration(node, scope, ancestry);
5903 } else {
5904 self.visit_global_variables_from_declaration(node, scope, ancestry);
5905 }
5906 }
5907 }
5908
5909 fn visit_c_anonymous_aggregate_declaration<'tree>(
5918 &mut self,
5919 node: Node<'tree>,
5920 scope: &ScopeInfo,
5921 in_class_body: bool,
5922 stack: &mut Vec<CppWork<'tree>>,
5923 ancestry: &ParentIndex<'tree>,
5924 ) -> bool {
5925 if !self.c_tag_semantics || !in_class_body || scope.class_unit.is_none() {
5926 return false;
5927 }
5928 let Some(aggregate) = node.child_by_field_name("type") else {
5929 return false;
5930 };
5931 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
5932 || aggregate.child_by_field_name("name").is_some()
5933 {
5934 return false;
5935 }
5936 let Some(body) = cpp_body_node(aggregate) else {
5937 return false;
5938 };
5939
5940 let mut cursor = node.walk();
5941 let declarators = node
5942 .children_by_field_name("declarator", &mut cursor)
5943 .filter_map(|declarator| match classify_declarator(declarator) {
5944 Some(DeclaratorKind::Variable(variable)) => Some(variable),
5945 Some(DeclaratorKind::Function(_)) | None => None,
5946 })
5947 .collect::<Vec<_>>();
5948 if declarators.is_empty() {
5949 stack.push(CppWork::Container(CppContainer {
5950 node: body,
5951 scope: scope.clone(),
5952 }));
5953 return true;
5954 }
5955
5956 for declarator in declarators {
5957 let Some(name) = extract_variable_name(declarator, self.source) else {
5958 continue;
5959 };
5960 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
5961 self.visit_named_class_like_shape(
5962 aggregate,
5963 name,
5964 Some(body),
5965 true,
5966 None,
5967 None,
5968 scope,
5969 stack,
5970 ancestry,
5971 );
5972 }
5973 true
5974 }
5975
5976 fn visit_function_declaration<'tree>(
5977 &mut self,
5978 declaration_node: Node<'tree>,
5979 declarator: Node<'tree>,
5980 scope: &ScopeInfo,
5981 ancestry: &ParentIndex<'tree>,
5982 ) {
5983 let Some(function) = extract_function_info(declarator, self.source, scope) else {
5984 return;
5985 };
5986 let code_unit =
5987 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
5988 if self.parsed.contains_declaration(&code_unit) {
5989 self.parsed
5990 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
5991 return;
5992 }
5993 self.add_declaration(code_unit.clone(), declaration_node, None, None);
5994 let signature = render_cpp_function_display_signature_from_node(
5995 declaration_node,
5996 self.source,
5997 scope.template_signature.as_deref(),
5998 false,
5999 ancestry,
6000 );
6001 self.parsed.add_signature_with_metadata(
6002 code_unit.clone(),
6003 cpp_signature_metadata(signature, declarator, self.source, ancestry)
6004 .with_declaration_only(true)
6005 .with_callable_linkage(cpp_callable_linkage(
6006 declaration_node,
6007 self.source,
6008 ancestry,
6009 )),
6010 );
6011 if let Some(parent) = &scope.class_unit {
6012 self.parsed.add_child(parent.clone(), code_unit);
6013 } else if let Some(module) = &scope.module {
6014 self.parsed.add_child(module.clone(), code_unit);
6015 }
6016 }
6017
6018 fn visit_recovered_macro_qualified_function_declaration<'tree>(
6019 &mut self,
6020 declaration_node: Node<'tree>,
6021 call: Node<'tree>,
6022 scope: &ScopeInfo,
6023 ancestry: &ParentIndex<'tree>,
6024 ) {
6025 let Some(parent) = &scope.class_unit else {
6026 return;
6027 };
6028 let Some(name_node) = call.child_by_field_name("function") else {
6029 return;
6030 };
6031 let Some(arguments) = call.child_by_field_name("arguments") else {
6032 return;
6033 };
6034 let Some((signature, parameter_labels)) =
6035 recovered_macro_qualified_function_parameters(arguments, self.source)
6036 else {
6037 return;
6038 };
6039 let arity = parameter_labels.len();
6040 let function = FunctionInfo {
6041 package_name: scope.package_name.clone(),
6042 owner: Some(CppMemberOwner::Unit(parent.clone())),
6043 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
6044 signature,
6045 };
6046 if function.name.is_empty() {
6047 return;
6048 }
6049 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6050 if self.parsed.contains_declaration(&code_unit) {
6051 self.parsed
6052 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
6053 return;
6054 }
6055 self.add_declaration(code_unit.clone(), declaration_node, None, None);
6056 let signature_label = render_cpp_function_display_signature_from_node(
6057 declaration_node,
6058 self.source,
6059 scope.template_signature.as_deref(),
6060 false,
6061 ancestry,
6062 );
6063 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
6064 .with_declaration_only(true)
6065 .with_callable_arity(CallableArity::exact(arity))
6066 .with_callable_linkage(cpp_callable_linkage(
6067 declaration_node,
6068 self.source,
6069 ancestry,
6070 ));
6071 self.parsed
6072 .add_signature_with_metadata(code_unit.clone(), metadata);
6073 self.parsed.add_child(parent.clone(), code_unit);
6074 }
6075
6076 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
6077 &mut self,
6078 declaration_node: Node<'tree>,
6079 call: Node<'tree>,
6080 scope: &ScopeInfo,
6081 ancestry: &ParentIndex<'tree>,
6082 ) {
6083 let Some(parent) = &scope.class_unit else {
6084 return;
6085 };
6086 let Some(arguments) = call.child_by_field_name("arguments") else {
6087 return;
6088 };
6089 let Some((mut signature, parameter_labels)) =
6090 recovered_macro_qualified_function_parameters(arguments, self.source)
6091 else {
6092 return;
6093 };
6094 if let Some(template_signature) = &scope.template_signature {
6095 signature = format!("{template_signature}{signature}");
6096 }
6097 let arity = parameter_labels.len();
6098 let function = FunctionInfo {
6099 package_name: scope.package_name.clone(),
6100 owner: Some(CppMemberOwner::Unit(parent.clone())),
6101 name: parent.identifier().to_string(),
6102 signature,
6103 };
6104 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6105 self.add_declaration(code_unit.clone(), declaration_node, None, None);
6106 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
6107 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
6108 .with_declaration_only(false)
6109 .with_callable_arity(CallableArity::exact(arity))
6110 .with_callable_linkage(cpp_callable_linkage(
6111 declaration_node,
6112 self.source,
6113 ancestry,
6114 ));
6115 self.parsed
6116 .add_signature_with_metadata(code_unit.clone(), metadata);
6117 self.parsed.add_child(parent.clone(), code_unit);
6118 }
6119
6120 fn visit_variable_declaration<'tree>(
6121 &mut self,
6122 declaration_node: Node<'tree>,
6123 declarator: Node<'tree>,
6124 scope: &ScopeInfo,
6125 in_class_body: bool,
6126 ancestry: &ParentIndex<'tree>,
6127 ) {
6128 let Some(name) = extract_variable_name(declarator, self.source) else {
6129 return;
6130 };
6131 let parent = if in_class_body {
6132 let Some(parent) = &scope.class_unit else {
6133 return;
6134 };
6135 Some(parent)
6136 } else {
6137 None
6138 };
6139 let short_name = match parent {
6140 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
6141 None => name.clone(),
6142 };
6143 let fq = cpp_leaf_fq(
6144 &scope.package_name,
6145 parent,
6146 &name,
6147 SegmentKind::Member,
6148 SegmentKind::Member,
6149 );
6150 let code_unit = CodeUnit::new_fq(
6151 self.file.clone(),
6152 CodeUnitType::Field,
6153 scope.package_name.clone(),
6154 short_name,
6155 fq,
6156 );
6157 if self.parsed.contains_declaration(&code_unit) {
6158 return;
6159 }
6160 self.add_declaration(code_unit.clone(), declaration_node, None, None);
6161 self.parsed.add_signature_with_metadata(
6162 code_unit.clone(),
6163 SignatureMetadata::new(
6164 render_cpp_field_signature(declaration_node, declarator, self.source),
6165 Vec::new(),
6166 )
6167 .with_cpp_field_linkage(cpp_field_declaration_linkage(
6168 declaration_node,
6169 self.source,
6170 ancestry,
6171 )),
6172 );
6173 if let Some(parent) = &scope.class_unit {
6174 self.parsed.add_child(parent.clone(), code_unit);
6175 } else if let Some(module) = &scope.module {
6176 self.parsed.add_child(module.clone(), code_unit);
6177 }
6178 }
6179
6180 fn visit_class_members_from_declaration<'tree>(
6181 &mut self,
6182 node: Node<'tree>,
6183 scope: &ScopeInfo,
6184 ancestry: &ParentIndex<'tree>,
6185 ) {
6186 let mut cursor = node.walk();
6187 for child in node.named_children(&mut cursor) {
6188 if child.kind() == "init_declarator"
6189 && let Some(inner) = child.child_by_field_name("declarator")
6190 {
6191 self.visit_variable_declaration(node, inner, scope, true, ancestry);
6192 } else if matches!(
6193 child.kind(),
6194 "identifier"
6195 | "field_identifier"
6196 | "pointer_declarator"
6197 | "reference_declarator"
6198 | "array_declarator"
6199 | "parenthesized_declarator"
6200 ) {
6201 self.visit_variable_declaration(node, child, scope, true, ancestry);
6202 }
6203 }
6204 }
6205
6206 fn visit_global_variables_from_declaration<'tree>(
6207 &mut self,
6208 node: Node<'tree>,
6209 scope: &ScopeInfo,
6210 ancestry: &ParentIndex<'tree>,
6211 ) {
6212 let mut cursor = node.walk();
6213 for child in node.named_children(&mut cursor) {
6214 if child.kind() == "init_declarator"
6215 && let Some(inner) = child.child_by_field_name("declarator")
6216 {
6217 self.visit_variable_declaration(node, inner, scope, false, ancestry);
6218 } else if matches!(
6219 child.kind(),
6220 "identifier"
6221 | "field_identifier"
6222 | "pointer_declarator"
6223 | "reference_declarator"
6224 | "array_declarator"
6225 | "parenthesized_declarator"
6226 ) {
6227 self.visit_variable_declaration(node, child, scope, false, ancestry);
6228 }
6229 }
6230 }
6231
6232 fn visit_type_declaration<'tree>(
6233 &mut self,
6234 node: Node<'tree>,
6235 scope: &ScopeInfo,
6236 stack: &mut Vec<CppWork<'tree>>,
6237 ancestry: &ParentIndex<'tree>,
6238 ) {
6239 let type_node = node.child_by_field_name("type");
6240 if let Some(type_node) = type_node
6241 && matches!(
6242 type_node.kind(),
6243 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
6244 )
6245 {
6246 self.visit_class_like(type_node, scope, stack, ancestry);
6247 }
6248
6249 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
6250 let range = Range {
6251 start_byte: node.start_byte(),
6252 end_byte: recovered.end_node.end_byte(),
6253 start_line: node.start_position().row + 1,
6254 end_line: recovered.end_node.end_position().row + 1,
6255 };
6256 let signature = self
6257 .source
6258 .get(range.start_byte..range.end_byte)
6259 .map(normalize_cpp_whitespace)
6260 .unwrap_or_default();
6261 self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
6262 return;
6263 }
6264
6265 let alias_names = match node.kind() {
6266 "alias_declaration" => extract_alias_declaration_name(node, self.source)
6267 .into_iter()
6268 .collect::<Vec<_>>(),
6269 "type_definition" => extract_typedef_alias_names(node, self.source),
6270 _ => Vec::new(),
6271 };
6272 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
6273 (type_node, alias_names.as_slice())
6274 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
6275 && type_node.child_by_field_name("name").is_none()
6276 {
6277 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
6278 } else {
6279 None
6280 };
6281 self.add_type_aliases(node, scope, alias_names);
6282 if let Some((body, alias_name)) = anonymous_aggregate {
6283 let signature = normalize_cpp_whitespace(node_text(node, self.source));
6289 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
6290 debug_assert!(self.parsed.contains_declaration(&alias_unit));
6291 let mut nested_scope = scope.clone();
6292 nested_scope.class_unit = Some(alias_unit);
6293 nested_scope.template_signature = scope.template_signature.clone();
6294 nested_scope.template_metadata = None;
6295 nested_scope.declarations_are_fields = false;
6296 nested_scope.recovered_specialization_member_scope = false;
6297 stack.push(CppWork::Container(CppContainer {
6298 node: body,
6299 scope: nested_scope,
6300 }));
6301 }
6302 }
6303
6304 fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
6305 let signature = normalize_cpp_whitespace(node_text(node, self.source));
6306 self.record_type_aliases(
6307 node,
6308 scope,
6309 alias_names,
6310 signature,
6311 cpp_declaration_range(node),
6312 );
6313 }
6314
6315 fn record_type_aliases(
6316 &mut self,
6317 node: Node<'_>,
6318 scope: &ScopeInfo,
6319 alias_names: Vec<String>,
6320 signature: String,
6321 range: Range,
6322 ) {
6323 if signature.is_empty() {
6324 return;
6325 }
6326 let type_name = node
6327 .child_by_field_name("type")
6328 .and_then(|type_node| type_node.child_by_field_name("name"))
6329 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
6330 for alias_name in alias_names {
6331 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
6332 continue;
6333 }
6334 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
6335 self.add_declaration_with_range(code_unit.clone(), range, None, None);
6338 self.parsed
6339 .add_signature(code_unit.clone(), signature.clone());
6340 if let Some(metadata) = &scope.template_metadata {
6341 let mut metadata = metadata.clone();
6342 metadata.primary_fq_name = code_unit.fq_name();
6343 self.parsed
6344 .set_cpp_template_metadata(code_unit.clone(), metadata);
6345 }
6346 if let Some(parent) = &scope.class_unit {
6347 self.parsed.add_child(parent.clone(), code_unit.clone());
6348 } else if let Some(module) = &scope.module {
6349 self.parsed.add_child(module.clone(), code_unit.clone());
6350 }
6351 self.parsed.mark_type_alias(code_unit);
6352 }
6353 }
6354
6355 fn type_alias_unit(
6356 &self,
6357 scope: &ScopeInfo,
6358 alias_name: String,
6359 signature: String,
6360 ) -> CodeUnit {
6361 let short_name = if let Some(parent) = &scope.class_unit {
6362 cpp_join_nested_short(parent.short_name(), &alias_name)
6363 } else {
6364 alias_name.clone()
6365 };
6366 let fq = cpp_leaf_fq(
6367 &scope.package_name,
6368 scope.class_unit.as_ref(),
6369 &alias_name,
6370 SegmentKind::Nested,
6371 SegmentKind::Type,
6372 );
6373 CodeUnit::with_signature_and_fq(
6374 self.file.clone(),
6375 CodeUnitType::Class,
6376 scope.package_name.clone(),
6377 short_name,
6378 Some(signature),
6379 false,
6380 fq,
6381 )
6382 }
6383
6384 fn visit_macro(&mut self, node: Node<'_>) {
6385 let Some(name) = extract_macro_name(node, self.source) else {
6386 return;
6387 };
6388 let signature = node_text(node, self.source).trim_end().to_string();
6389 if signature.is_empty() {
6390 return;
6391 }
6392 let fq = cpp_member_fq("", &name);
6393 let code_unit = CodeUnit::with_signature_and_fq(
6400 self.file.clone(),
6401 CodeUnitType::Macro,
6402 "",
6403 name,
6404 Some(signature.clone()),
6405 false,
6406 fq,
6407 );
6408 if self.parsed.contains_declaration(&code_unit) {
6409 return;
6410 }
6411 self.add_declaration(code_unit.clone(), node, None, None);
6412 let name_range = node
6413 .child_by_field_name("name")
6414 .map(cpp_declaration_range)
6415 .unwrap_or_else(|| cpp_declaration_range(node));
6416 self.parsed
6417 .record_materialization(MaterializationRecord::GeneratedDeclaration {
6418 site: cpp_declaration_range(node),
6419 argument: name_range,
6420 kind: GenerationKind::PreprocessorDefinition,
6421 unit: code_unit.clone(),
6422 });
6423 self.parsed.add_signature(code_unit, signature);
6424 }
6425}
6426
6427pub fn cpp_field_declaration_linkage<'tree>(
6432 declaration: Node<'tree>,
6433 source: &str,
6434 ancestry: &ParentIndex<'tree>,
6435) -> CppFieldLinkage {
6436 let mut current = ancestry.parent(declaration);
6437 let mut enclosed_by_class = false;
6438 while let Some(node) = current {
6439 if node.kind() == "namespace_definition"
6440 && node
6441 .child_by_field_name("name")
6442 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6443 {
6444 return CppFieldLinkage::Internal;
6445 }
6446 if matches!(
6447 node.kind(),
6448 "class_specifier" | "struct_specifier" | "union_specifier"
6449 ) && node
6450 .child_by_field_name("name")
6451 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6452 {
6453 return CppFieldLinkage::Internal;
6454 }
6455 if matches!(
6456 node.kind(),
6457 "class_specifier" | "struct_specifier" | "union_specifier"
6458 ) {
6459 enclosed_by_class = true;
6460 }
6461 if matches!(node.kind(), "function_definition" | "lambda_expression") {
6462 return CppFieldLinkage::Internal;
6463 }
6464 current = ancestry.parent(node);
6465 }
6466 if enclosed_by_class {
6467 return CppFieldLinkage::External;
6468 }
6469 let mut cursor = declaration.walk();
6470 let mut has_static = false;
6471 let mut has_extern = false;
6472 let mut has_inline = false;
6473 let mut has_const = false;
6474 let mut has_constexpr = false;
6475 for child in declaration.named_children(&mut cursor) {
6476 let text = normalize_cpp_whitespace(node_text(child, source));
6477 match (child.kind(), text.as_str()) {
6478 ("storage_class_specifier", "static") => has_static = true,
6479 ("storage_class_specifier", "extern") => has_extern = true,
6480 ("storage_class_specifier", "inline") => has_inline = true,
6481 ("storage_class_specifier", "constexpr") => has_constexpr = true,
6482 ("type_qualifier", "const") => has_const = true,
6483 ("type_qualifier", "constexpr") => has_constexpr = true,
6484 _ => {}
6485 }
6486 }
6487 if has_static {
6488 CppFieldLinkage::Internal
6489 } else if has_extern || has_inline {
6490 CppFieldLinkage::External
6491 } else if has_const || has_constexpr {
6492 CppFieldLinkage::InternalUnlessExternalPeer
6493 } else {
6494 CppFieldLinkage::External
6495 }
6496}
6497
6498fn cpp_declaration_range(node: Node<'_>) -> Range {
6499 Range {
6500 start_byte: node.start_byte(),
6501 end_byte: node.end_byte(),
6502 start_line: node.start_position().row + 1,
6503 end_line: node.end_position().row + 1,
6504 }
6505}
6506
6507fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
6511 let line_at = |byte: usize| {
6512 source.as_bytes()[..byte]
6513 .iter()
6514 .filter(|&&b| b == b'\n')
6515 .count()
6516 + 1
6517 };
6518 Range {
6519 start_byte,
6520 end_byte,
6521 start_line: line_at(start_byte),
6522 end_line: line_at(end_byte),
6523 }
6524}
6525
6526pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
6533 walk_named_tree_preorder(root, true, |node| {
6534 if node.kind() == "preproc_include" {
6535 let raw = normalize_cpp_whitespace(node_text(node, source));
6536 if !raw.is_empty() {
6537 parsed.imports.push(ImportInfo {
6538 raw_snippet: raw,
6539 is_wildcard: false,
6540 is_global: false,
6541 identifier: None,
6542 alias: None,
6543 path: None,
6544 binder_span: None,
6545 });
6546 }
6547 return WalkControl::SkipChildren;
6548 }
6549 WalkControl::Continue
6550 });
6551}
6552
6553pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
6554 let mut in_block_comment = false;
6555 for line in source.lines() {
6556 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
6557 let trimmed = stripped.trim();
6558 if !looks_like_quoted_include_line(trimmed) {
6559 continue;
6560 }
6561
6562 let raw = normalize_cpp_whitespace(trimmed);
6563 if parsed
6567 .imports
6568 .iter()
6569 .any(|import| import.raw_snippet == raw)
6570 {
6571 continue;
6572 }
6573
6574 parsed.imports.push(ImportInfo {
6575 raw_snippet: raw,
6576 is_wildcard: false,
6577 is_global: false,
6578 identifier: None,
6579 alias: None,
6580 path: None,
6581 binder_span: None,
6582 });
6583 }
6584}
6585
6586fn looks_like_quoted_include_line(line: &str) -> bool {
6587 let Some(rest) = line.trim_start().strip_prefix('#') else {
6588 return false;
6589 };
6590 let Some(rest) = rest.trim_start().strip_prefix("include") else {
6591 return false;
6592 };
6593 rest.trim_start().starts_with('"')
6594}
6595
6596fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
6597 let mut raw = Vec::new();
6598 let mut cursor = node.walk();
6599 for child in node.named_children(&mut cursor) {
6600 if child.kind() == "base_class_clause" {
6601 collect_cpp_base_nodes(child, source, &mut raw);
6602 }
6603 }
6604 raw
6605}
6606
6607fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
6608 walk_named_tree_preorder(node, false, |child| match child.kind() {
6609 "type_identifier" | "qualified_identifier" | "template_type" => {
6610 let text = normalize_cpp_whitespace(node_text(child, source));
6611 if !text.is_empty() {
6612 raw.push(text);
6613 }
6614 WalkControl::SkipChildren
6615 }
6616 _ => WalkControl::Continue,
6617 });
6618}
6619
6620fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
6621 let mut out = String::new();
6622 let chars: Vec<char> = line.chars().collect();
6623 let mut index = 0;
6624 let mut in_string = false;
6625 let mut in_char = false;
6626 let mut escape = false;
6627
6628 while index < chars.len() {
6629 let ch = chars[index];
6630 let next = chars.get(index + 1).copied();
6631
6632 if *in_block_comment {
6633 if ch == '*' && next == Some('/') {
6634 *in_block_comment = false;
6635 index += 2;
6636 } else {
6637 index += 1;
6638 }
6639 continue;
6640 }
6641
6642 if in_string {
6643 out.push(ch);
6644 if escape {
6645 escape = false;
6646 } else if ch == '\\' {
6647 escape = true;
6648 } else if ch == '"' {
6649 in_string = false;
6650 }
6651 index += 1;
6652 continue;
6653 }
6654
6655 if in_char {
6656 out.push(ch);
6657 if escape {
6658 escape = false;
6659 } else if ch == '\\' {
6660 escape = true;
6661 } else if ch == '\'' {
6662 in_char = false;
6663 }
6664 index += 1;
6665 continue;
6666 }
6667
6668 if ch == '/' && next == Some('/') {
6669 break;
6670 }
6671 if ch == '/' && next == Some('*') {
6672 *in_block_comment = true;
6673 index += 2;
6674 continue;
6675 }
6676 if ch == '"' {
6677 in_string = true;
6678 out.push(ch);
6679 index += 1;
6680 continue;
6681 }
6682 if ch == '\'' {
6683 in_char = true;
6684 out.push(ch);
6685 index += 1;
6686 continue;
6687 }
6688
6689 out.push(ch);
6690 index += 1;
6691 }
6692
6693 out
6694}
6695
6696#[derive(Clone)]
6697struct FunctionInfo {
6698 package_name: String,
6699 owner: Option<CppMemberOwner>,
6700 name: String,
6701 signature: String,
6702}
6703
6704#[derive(Clone)]
6711enum CppMemberOwner {
6712 Chain(Vec<String>),
6716 Unit(CodeUnit),
6719}
6720
6721impl CppMemberOwner {
6722 fn short_chain(&self) -> String {
6724 match self {
6725 Self::Chain(chain) => chain.join("$"),
6726 Self::Unit(parent) => parent.short_name().to_string(),
6727 }
6728 }
6729}
6730
6731enum DeclaratorKind<'a> {
6732 Function(Node<'a>),
6733 Variable(Node<'a>),
6734}
6735
6736impl FunctionInfo {
6737 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
6738 self.code_unit_with_synthetic(file, false)
6739 }
6740
6741 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
6742 let short_name = match &self.owner {
6743 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
6744 None => self.name.clone(),
6745 };
6746 let fq = match &self.owner {
6747 Some(CppMemberOwner::Chain(chain)) => {
6748 debug_assert!(
6749 !chain.is_empty(),
6750 "an empty owner chain is no owner; producers return None instead"
6751 );
6752 let mut fq = FqName::new();
6753 cpp_push_package(&mut fq, &self.package_name);
6754 let mut first = true;
6755 for component in chain {
6756 let kind = if first {
6757 SegmentKind::Type
6758 } else {
6759 SegmentKind::Nested
6760 };
6761 fq.push(cpp_segment(component, kind));
6762 first = false;
6763 }
6764 fq.push(cpp_segment(&self.name, SegmentKind::Member));
6765 fq
6766 }
6767 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
6768 .fq()
6769 .clone()
6770 .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
6771 Some(CppMemberOwner::Unit(_)) | None => {
6774 let mut fq = FqName::new();
6775 cpp_push_package(&mut fq, &self.package_name);
6776 fq.push(cpp_segment(&self.name, SegmentKind::Member));
6777 fq
6778 }
6779 };
6780 CodeUnit::with_signature_and_fq(
6781 file,
6782 CodeUnitType::Function,
6783 self.package_name.clone(),
6784 short_name,
6785 Some(self.signature.clone()),
6786 synthetic,
6787 fq,
6788 )
6789 }
6790}
6791
6792fn extract_function_info(
6793 declarator: Node<'_>,
6794 source: &str,
6795 scope: &ScopeInfo,
6796) -> Option<FunctionInfo> {
6797 let parameters_node = declarator.child_by_field_name("parameters")?;
6798 let declarator_name_node = declarator
6799 .child_by_field_name("declarator")
6800 .or_else(|| parameters_node.prev_named_sibling())?;
6801 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
6802}
6803
6804fn extract_function_info_from_name(
6805 declarator: Node<'_>,
6806 declarator_name_node: Node<'_>,
6807 source: &str,
6808 scope: &ScopeInfo,
6809) -> Option<FunctionInfo> {
6810 let parameters_node = declarator.child_by_field_name("parameters")?;
6811 let parameters_text = cpp_parameter_signature(parameters_node, source);
6812 let recovered_specialization_member = scope
6813 .recovered_specialization_member_scope
6814 .then(|| {
6815 let terminal = declarator_name_node
6816 .child_by_field_name("name")
6817 .unwrap_or(declarator_name_node);
6818 let name = canonical_cpp_qualified_component(terminal, source)?.name;
6819 let owner = scope.class_unit.as_ref()?;
6820 Some((
6821 Some(CppMemberOwner::Unit(owner.clone())),
6822 name,
6823 scope.package_name.clone(),
6824 ))
6825 })
6826 .flatten();
6827 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
6828 parts
6829 } else if let Some(parts) =
6830 split_structured_templated_cpp_name(declarator_name_node, source, scope)
6831 {
6832 parts
6833 } else {
6834 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
6835 declarator_name_node,
6836 source,
6837 )?);
6838 if raw_name.is_empty() {
6839 return None;
6840 }
6841 split_cpp_name(&raw_name, scope)
6842 };
6843 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
6844 let mut signature = if suffix.is_empty() {
6845 parameters_text
6846 } else {
6847 format!("{parameters_text} {suffix}")
6848 };
6849 if let Some(template_signature) = &scope.template_signature {
6850 signature = format!("{template_signature}{signature}");
6851 }
6852
6853 Some(FunctionInfo {
6854 package_name,
6855 owner,
6856 name,
6857 signature,
6858 })
6859}
6860
6861fn cpp_macro_displaced_callable_parts<'tree>(
6868 function_declarator: Node<'tree>,
6869 source: &str,
6870 ancestry: &ParentIndex<'tree>,
6871) -> Option<(Node<'tree>, Node<'tree>)> {
6872 let definition = ancestry.parent(function_declarator)?;
6873 if definition.kind() != "function_definition"
6874 || definition.child_by_field_name("declarator") != Some(function_declarator)
6875 || definition
6876 .child_by_field_name("body")
6877 .is_none_or(|body| body.kind() != "compound_statement")
6878 {
6879 return None;
6880 }
6881 let macro_type = definition.child_by_field_name("type")?;
6882 if macro_type.kind() != "type_identifier"
6883 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
6884 {
6885 return None;
6886 }
6887
6888 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
6889 if apparent_return_type.kind() == "qualified_identifier"
6890 && let (Some(return_type), Some(callable_name)) = (
6891 apparent_return_type.child_by_field_name("scope"),
6892 apparent_return_type.child_by_field_name("name"),
6893 )
6894 && return_type.kind() == "template_type"
6895 && matches!(callable_name.kind(), "identifier" | "field_identifier")
6896 && (0..apparent_return_type.child_count())
6897 .filter_map(|index| apparent_return_type.child(index))
6898 .any(|child| child.kind() == "::" && child.is_missing())
6899 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
6900 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
6901 {
6902 return Some((return_type, callable_name));
6903 }
6904 if !matches!(
6905 apparent_return_type.kind(),
6906 "identifier" | "field_identifier" | "type_identifier"
6907 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
6908 {
6909 return None;
6910 }
6911 let parameters = function_declarator.child_by_field_name("parameters")?;
6912 let mut cursor = function_declarator.walk();
6913 let between = function_declarator
6914 .named_children(&mut cursor)
6915 .filter(|child| child.kind() != "comment")
6916 .filter(|child| {
6917 child.start_byte() >= apparent_return_type.end_byte()
6918 && child.end_byte() <= parameters.start_byte()
6919 && !same_node(*child, apparent_return_type)
6920 && !same_node(*child, parameters)
6921 })
6922 .collect::<Vec<_>>();
6923 let [name_error] = between.as_slice() else {
6924 return None;
6925 };
6926 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
6927 return None;
6928 }
6929 let callable_name = name_error.named_child(0)?;
6930 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
6931 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
6932 {
6933 return None;
6934 }
6935 Some((apparent_return_type, callable_name))
6936}
6937
6938fn cpp_declarator_identity_suffix(
6954 declarator: Node<'_>,
6955 parameters_node: Node<'_>,
6956 source: &str,
6957) -> String {
6958 let mut cursor = declarator.walk();
6959 let parts = declarator
6960 .named_children(&mut cursor)
6961 .filter(|child| child.start_byte() >= parameters_node.end_byte())
6962 .filter(|child| {
6963 matches!(
6964 child.kind(),
6965 "type_qualifier"
6966 | "ref_qualifier"
6967 | "noexcept"
6968 | "throw_specifier"
6969 | "trailing_return_type"
6970 | "requires_clause"
6971 )
6972 })
6973 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
6974 .filter(|text| !text.is_empty())
6975 .collect::<Vec<_>>();
6976 normalize_cpp_qualifier_suffix(&parts.join(" "))
6977}
6978
6979pub(crate) fn cpp_callable_identity_suffix(
6986 function_declarator: Node<'_>,
6987 source: &str,
6988) -> Option<String> {
6989 let parameters_node = function_declarator.child_by_field_name("parameters")?;
6990 Some(cpp_declarator_identity_suffix(
6991 function_declarator,
6992 parameters_node,
6993 source,
6994 ))
6995}
6996
6997pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
6998 match classify_declarator(node)? {
6999 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
7000 DeclaratorKind::Variable(_) => None,
7001 }
7002}
7003
7004fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
7005 match node.kind() {
7006 "function_declarator" => {
7007 let inner = node
7008 .child_by_field_name("declarator")
7009 .or_else(|| node.child_by_field_name("name"))
7010 .or_else(|| last_named_child(node));
7011 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
7012 Some(DeclaratorKind::Variable(node))
7013 } else {
7014 Some(DeclaratorKind::Function(node))
7015 }
7016 }
7017 "init_declarator"
7018 | "pointer_declarator"
7019 | "reference_declarator"
7020 | "parenthesized_declarator"
7021 | "array_declarator"
7022 | "attributed_declarator"
7023 | "template_function" => node
7024 .child_by_field_name("declarator")
7025 .or_else(|| node.child_by_field_name("name"))
7026 .or_else(|| last_named_child(node))
7027 .and_then(classify_declarator),
7028 "identifier" | "field_identifier" | "qualified_identifier" => {
7029 Some(DeclaratorKind::Variable(node))
7030 }
7031 _ => node
7032 .child_by_field_name("declarator")
7033 .or_else(|| node.child_by_field_name("name"))
7034 .or_else(|| last_named_child(node))
7035 .and_then(classify_declarator),
7036 }
7037}
7038
7039fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
7040 matches!(
7041 node.kind(),
7042 "function_declarator"
7043 | "init_declarator"
7044 | "pointer_declarator"
7045 | "reference_declarator"
7046 | "parenthesized_declarator"
7047 | "array_declarator"
7048 | "attributed_declarator"
7049 | "template_function"
7050 | "identifier"
7051 | "field_identifier"
7052 | "qualified_identifier"
7053 )
7054}
7055
7056fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
7057 let class_like = first_class_like_child(node);
7058 let mut cursor = node.walk();
7059 node.named_children(&mut cursor).any(|child| {
7060 matches!(
7061 child.kind(),
7062 "init_declarator"
7063 | "pointer_declarator"
7064 | "reference_declarator"
7065 | "array_declarator"
7066 | "function_declarator"
7067 | "parenthesized_declarator"
7068 | "attributed_declarator"
7069 ) || matches!(
7070 child.kind(),
7071 "identifier" | "field_identifier" | "qualified_identifier"
7072 ) && class_like.is_none_or(|class_node| {
7073 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
7074 })
7075 })
7076}
7077
7078struct CppNamespaceForward {
7091 name: String,
7092 start_byte: usize,
7093 namespace_end_byte: usize,
7096 package_name: String,
7097}
7098
7099fn cpp_namespace_forward_entry<'tree>(
7105 node: Node<'tree>,
7106 source: &str,
7107 ancestry: &ParentIndex<'tree>,
7108) -> Option<CppNamespaceForward> {
7109 if !matches!(
7110 node.kind(),
7111 "class_specifier" | "struct_specifier" | "union_specifier"
7112 ) || cpp_body_node(node).is_some()
7113 {
7114 return None;
7115 }
7116 let parent = node.parent()?;
7117 if !(parent.kind() == "declaration_list"
7118 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
7119 {
7120 return None;
7121 }
7122 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
7123 if !namespace.has_error() {
7128 return None;
7129 }
7130 Some(CppNamespaceForward {
7131 name: class_like_name(node, source, ancestry)?,
7132 start_byte: node.start_byte(),
7133 namespace_end_byte: namespace.end_byte(),
7134 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
7135 })
7136}
7137
7138fn cpp_namespace_forward_matches_recovery(
7142 forward: &CppNamespaceForward,
7143 recovered_node: Node<'_>,
7144) -> bool {
7145 forward.start_byte < recovered_node.start_byte()
7146 && forward.namespace_end_byte < recovered_node.start_byte()
7147 && malformed_namespace_is_nearest_recovery_region(
7148 forward.namespace_end_byte,
7149 recovered_node,
7150 )
7151}
7152
7153#[derive(Debug, Default)]
7169pub struct CppRecoveryCapture {
7170 created: Vec<CodeUnit>,
7172 created_units: HashSet<CodeUnit>,
7174 removed_pre_existing: HashSet<CodeUnit>,
7176}
7177
7178#[derive(Debug, Default)]
7190pub struct CppFieldOwnerIndex {
7191 owners: HashMap<String, HashSet<String>>,
7193 ownerless_packages: HashSet<String>,
7195}
7196
7197impl CppFieldOwnerIndex {
7198 fn of<'unit>(
7201 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
7202 file: &ProjectFile,
7203 ) -> Self {
7204 let mut index = Self::default();
7205 for declaration in declarations {
7206 index.record(declaration, file);
7207 }
7208 index
7209 }
7210
7211 fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
7212 if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
7213 return;
7214 }
7215 let short_name = code_unit.short_name();
7216 let package_name = code_unit.package_name();
7217 if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
7218 self.ownerless_packages.insert(package_name.to_string());
7219 }
7220 if !short_name.contains('.') {
7221 return;
7222 }
7223 if !self.owners.contains_key(package_name) {
7224 self.owners
7225 .insert(package_name.to_string(), HashSet::default());
7226 }
7227 let owners = self
7228 .owners
7229 .get_mut(package_name)
7230 .expect("the package entry was just ensured");
7231 for (offset, _) in short_name.match_indices('.') {
7232 let owner = &short_name[..offset];
7233 if !owners.contains(owner) {
7234 owners.insert(owner.to_string());
7235 }
7236 }
7237 }
7238
7239 fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
7242 if owner_short_name.is_empty() {
7243 self.ownerless_packages.contains(package_name)
7244 } else {
7245 self.owners
7246 .get(package_name)
7247 .is_some_and(|owners| owners.contains(owner_short_name))
7248 }
7249 }
7250}
7251
7252#[cfg(any(debug_assertions, test))]
7256fn cpp_declarations_hold_owned_fields<'unit>(
7257 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
7258 file: &ProjectFile,
7259 package_name: &str,
7260 owner_short_name: &str,
7261) -> bool {
7262 let prefix = format!("{owner_short_name}.");
7263 declarations.into_iter().any(|unit| {
7264 unit.kind() == CodeUnitType::Field
7265 && unit.source() == file
7266 && unit.package_name() == package_name
7267 && if owner_short_name.is_empty() {
7268 !unit.short_name().contains(['.', '$'])
7272 } else {
7273 unit.short_name().starts_with(&prefix)
7274 }
7275 })
7276}
7277
7278#[derive(PartialEq, Eq, Hash)]
7286pub struct CppTreeIdentity {
7287 root_id: usize,
7288 start_byte: usize,
7289 end_byte: usize,
7290 kind_id: u16,
7291 child_count: usize,
7292}
7293
7294impl CppTreeIdentity {
7295 fn of(root: Node<'_>) -> Self {
7296 Self {
7297 root_id: root.id(),
7298 start_byte: root.start_byte(),
7299 end_byte: root.end_byte(),
7300 kind_id: root.kind_id(),
7301 child_count: root.child_count(),
7302 }
7303 }
7304}
7305
7306#[derive(Default)]
7320pub struct CppNamespaceForwardScan {
7321 scanned_through: usize,
7323 forwards: HashMap<String, Vec<CppNamespaceForward>>,
7324}
7325
7326impl CppNamespaceForwardScan {
7327 fn advance_to<'tree>(
7334 &mut self,
7335 root: Node<'tree>,
7336 cutoff: usize,
7337 source: &str,
7338 ancestry: &ParentIndex<'tree>,
7339 ) {
7340 if cutoff <= self.scanned_through {
7341 return;
7342 }
7343 let folded_through = self.scanned_through;
7344 let mut cursor = root.walk();
7345 let mut stack = vec![root];
7346 while let Some(current) = stack.pop() {
7347 if (folded_through..cutoff).contains(¤t.start_byte())
7348 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
7349 {
7350 self.forwards
7351 .entry(forward.name.clone())
7352 .or_default()
7353 .push(forward);
7354 }
7355 for child in current.named_children(&mut cursor) {
7360 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
7361 stack.push(child);
7362 }
7363 }
7364 }
7365 self.scanned_through = cutoff;
7366 }
7367
7368 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
7372 let mut matching = self
7373 .forwards
7374 .get(name)
7375 .into_iter()
7376 .flatten()
7377 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
7378 let first = matching.next()?;
7379 matching
7380 .next()
7381 .is_none()
7382 .then(|| first.package_name.clone())
7383 }
7384}
7385
7386#[cfg(any(debug_assertions, test))]
7391fn unique_earlier_cpp_namespace_forward<'tree>(
7392 recovered_node: Node<'tree>,
7393 name: &str,
7394 source: &str,
7395 ancestry: &ParentIndex<'tree>,
7396) -> Option<String> {
7397 let mut root = recovered_node;
7398 while let Some(parent) = ancestry.parent(root) {
7399 root = parent;
7400 }
7401
7402 let mut candidates = Vec::new();
7403 let mut stack = vec![root];
7404 while let Some(current) = stack.pop() {
7405 if current.start_byte() < recovered_node.start_byte()
7406 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
7407 && forward.name == name
7408 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
7409 {
7410 candidates.push(forward.package_name);
7411 }
7412
7413 let mut cursor = current.walk();
7414 for child in current.named_children(&mut cursor) {
7415 if child.start_byte() < recovered_node.start_byte() {
7416 stack.push(child);
7417 }
7418 }
7419 }
7420
7421 if candidates.len() == 1 {
7422 candidates.pop()
7423 } else {
7424 None
7425 }
7426}
7427
7428fn malformed_namespace_is_nearest_recovery_region(
7429 namespace_end_byte: usize,
7430 recovered_node: Node<'_>,
7431) -> bool {
7432 let mut root = recovered_node;
7433 while let Some(parent) = root.parent() {
7434 root = parent;
7435 }
7436 let mut cursor = root.walk();
7437 root.named_children(&mut cursor)
7438 .filter(|sibling| {
7439 namespace_end_byte <= sibling.start_byte()
7440 && sibling.end_byte() <= recovered_node.start_byte()
7441 })
7442 .all(is_malformed_namespace_recovery_trivia)
7443}
7444
7445fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
7446 matches!(node.kind(), "ERROR" | "comment")
7447 || node.kind().starts_with("preproc_")
7448 || node.kind() == "expression_statement" && node.named_child_count() == 0
7449}
7450
7451fn cpp_namespace_name_for_forward<'tree>(
7455 node: Node<'tree>,
7456 source: &str,
7457 ancestry: &ParentIndex<'tree>,
7458) -> Option<String> {
7459 cpp_namespace_definition_for_forward(node, ancestry)?;
7460 cpp_lexical_namespace_name(node, source, ancestry)
7461}
7462
7463fn cpp_namespace_definition_for_forward<'tree>(
7464 node: Node<'tree>,
7465 ancestry: &ParentIndex<'tree>,
7466) -> Option<Node<'tree>> {
7467 let declaration = ancestry.parent(node)?;
7468 let mut ancestor = ancestry.parent(declaration);
7469 while let Some(current) = ancestor {
7470 if matches!(
7471 current.kind(),
7472 "compound_statement"
7473 | "field_declaration_list"
7474 | "class_specifier"
7475 | "struct_specifier"
7476 | "union_specifier"
7477 | "function_definition"
7478 | "lambda_expression"
7479 ) {
7480 return None;
7481 }
7482 if current.kind() == "namespace_definition" {
7483 return Some(current);
7484 }
7485 ancestor = ancestry.parent(current);
7486 }
7487 None
7488}
7489
7490fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
7491 match node.kind() {
7492 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
7493 "parenthesized_declarator" => node
7494 .child_by_field_name("declarator")
7495 .or_else(|| last_named_child(node))
7496 .is_some_and(is_pointer_wrapper_declarator),
7497 "template_function" => node
7498 .child_by_field_name("name")
7499 .is_some_and(is_function_pointer_like_inner_declarator),
7500 _ => false,
7501 }
7502}
7503
7504fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
7505 match node.kind() {
7506 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
7507 "parenthesized_declarator" => node
7508 .child_by_field_name("declarator")
7509 .or_else(|| last_named_child(node))
7510 .is_some_and(is_pointer_wrapper_declarator),
7511 _ => false,
7512 }
7513}
7514
7515fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
7516 let cleaned = raw_name.trim_start_matches("template ").trim();
7517 let cleaned = cleaned.trim_start_matches("::");
7524 let parts: Vec<_> = cleaned
7533 .split("::")
7534 .filter(|component| !component.is_empty())
7535 .collect();
7536 if parts.is_empty() {
7537 return (None, cleaned.to_string(), scope.package_name.clone());
7538 }
7539 if parts.len() > 1 {
7540 let name = parts.last().unwrap_or(&cleaned).to_string();
7541 let owner_parts = &parts[..parts.len() - 1];
7542 if let Some(class_unit) = &scope.class_unit {
7543 return (
7546 Some(CppMemberOwner::Unit(class_unit.clone())),
7547 name,
7548 scope.package_name.clone(),
7549 );
7550 }
7551 if !scope.package_name.is_empty() {
7552 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
7567 let owner = (!nested.is_empty()).then(|| {
7568 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
7569 });
7570 return (owner, name, scope.package_name.clone());
7571 }
7572 let (owner, package_name) = if owner_parts.len() > 1 {
7574 (
7586 Some(CppMemberOwner::Chain(vec![
7587 owner_parts.last().unwrap_or(&"").to_string(),
7588 ])),
7589 owner_parts[..owner_parts.len() - 1].join("::"),
7590 )
7591 } else {
7592 (
7604 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
7605 cpp_using_directive_namespace_for_bare_owner(scope),
7606 )
7607 };
7608 return (owner, name, package_name);
7609 }
7610
7611 let package_name = scope.package_name.clone();
7612 let owner = scope
7613 .class_unit
7614 .as_ref()
7615 .map(|parent| CppMemberOwner::Unit(parent.clone()));
7616 (owner, cleaned.to_string(), package_name)
7617}
7618
7619fn strip_redundant_namespace_prefix<'a>(
7633 owner_parts: &'a [&'a str],
7634 package_name: &str,
7635) -> &'a [&'a str] {
7636 if package_name.is_empty() {
7637 return owner_parts;
7638 }
7639 let package_segments: Vec<&str> = package_name.split("::").collect();
7640 let max_prefix = owner_parts.len().min(package_segments.len());
7641 for prefix_len in (1..=max_prefix).rev() {
7642 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
7643 if &owner_parts[..prefix_len] == package_suffix {
7644 return &owner_parts[prefix_len..];
7645 }
7646 }
7647 owner_parts
7648}
7649
7650fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
7661 scope
7662 .visible_using_namespaces
7663 .iter()
7664 .min_by_key(|namespace| namespace.split("::").count())
7665 .cloned()
7666 .unwrap_or_default()
7667}
7668
7669struct CppQualifiedNameComponent {
7670 name: String,
7671 is_template_id: bool,
7672}
7673
7674fn qualified_class_name_chain(
7687 class_node: Node<'_>,
7688 source: &str,
7689 scope: &ScopeInfo,
7690) -> Option<Vec<String>> {
7691 if scope.package_name.is_empty() || scope.class_unit.is_some() {
7692 return None;
7693 }
7694 let name = class_node.child_by_field_name("name")?;
7695 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
7696 if explicitly_global
7697 || components.len() < 2
7698 || components.iter().any(|component| component.is_template_id)
7699 {
7700 return None;
7701 }
7702 let names = components
7703 .iter()
7704 .map(|component| component.name.as_str())
7705 .collect::<Vec<_>>();
7706 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
7707 if class_chain.is_empty() {
7708 return None;
7709 }
7710 Some(class_chain.iter().map(|name| name.to_string()).collect())
7711}
7712
7713fn structured_cpp_qualified_components(
7714 qualified_name: Node<'_>,
7715 source: &str,
7716) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
7717 if qualified_name.kind() != "qualified_identifier" {
7718 return None;
7719 }
7720
7721 let mut components = Vec::new();
7722 let mut current = qualified_name;
7723 let mut explicitly_global = false;
7724 loop {
7725 if current.kind() == "qualified_identifier" {
7726 if let Some(component) = current.child_by_field_name("scope") {
7727 components.push(canonical_cpp_qualified_component(component, source)?);
7728 } else if components.is_empty() {
7729 explicitly_global = true;
7730 } else {
7731 return None;
7732 }
7733 current = current.child_by_field_name("name")?;
7734 } else {
7735 components.push(canonical_cpp_qualified_component(current, source)?);
7736 break;
7737 }
7738 }
7739 Some((components, explicitly_global))
7740}
7741
7742fn split_structured_templated_cpp_name(
7743 declarator_name: Node<'_>,
7744 source: &str,
7745 scope: &ScopeInfo,
7746) -> Option<(Option<CppMemberOwner>, String, String)> {
7747 let (mut components, explicitly_global) =
7748 structured_cpp_qualified_components(declarator_name, source)?;
7749
7750 let terminal = components.pop()?;
7751 let owner_start = components
7752 .iter()
7753 .position(|component| component.is_template_id)?;
7754 let explicit_package = components[..owner_start]
7755 .iter()
7756 .map(|component| component.name.as_str())
7757 .collect::<Vec<_>>()
7758 .join("::");
7759 let explicit_package_is_empty = explicit_package.is_empty();
7760 let package_name = match (
7761 explicitly_global,
7762 scope.package_name.is_empty(),
7763 explicit_package_is_empty,
7764 ) {
7765 (true, _, _) => explicit_package,
7766 (false, _, true) => scope.package_name.clone(),
7767 (false, true, false) => explicit_package,
7768 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
7769 };
7770 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
7776 {
7777 cpp_using_directive_namespace_for_bare_owner(scope)
7778 } else {
7779 package_name
7780 };
7781 let owner_chain = components[owner_start..]
7782 .iter()
7783 .map(|component| component.name.clone())
7784 .collect::<Vec<_>>();
7785 if owner_chain.is_empty() || terminal.name.is_empty() {
7786 return None;
7787 }
7788
7789 Some((
7790 Some(CppMemberOwner::Chain(owner_chain)),
7791 terminal.name,
7792 package_name,
7793 ))
7794}
7795
7796fn canonical_cpp_qualified_component(
7797 mut component: Node<'_>,
7798 source: &str,
7799) -> Option<CppQualifiedNameComponent> {
7800 let mut is_template_id = false;
7801 loop {
7802 match component.kind() {
7803 "template_type" => {
7804 is_template_id = true;
7805 component = component.child_by_field_name("name")?;
7806 }
7807 "dependent_name" => component = component.named_child(0)?,
7808 "identifier"
7809 | "field_identifier"
7810 | "namespace_identifier"
7811 | "type_identifier"
7812 | "operator_name"
7813 | "destructor_name" => {
7814 let name = normalize_cpp_whitespace(node_text(component, source));
7815 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
7816 name,
7817 is_template_id,
7818 });
7819 }
7820 _ => component = component.child_by_field_name("name")?,
7821 }
7822 }
7823}
7824
7825fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
7826 if let Some(name) = macro_decorated_unqualified_name(node) {
7827 return extract_declarator_name(name, source);
7828 }
7829 match node.kind() {
7830 "identifier"
7831 | "field_identifier"
7832 | "type_identifier"
7833 | "operator_name"
7834 | "destructor_name"
7835 | "qualified_identifier" => node_text(node, source).to_string(),
7836 "function_declarator"
7837 | "pointer_declarator"
7838 | "reference_declarator"
7839 | "parenthesized_declarator"
7840 | "array_declarator"
7841 | "template_function" => node
7842 .child_by_field_name("declarator")
7843 .or_else(|| node.child_by_field_name("name"))
7844 .or_else(|| last_named_child(node))
7845 .map(|child| extract_declarator_name(child, source))
7846 .unwrap_or_else(|| node_text(node, source).to_string()),
7847 _ => node
7848 .child_by_field_name("name")
7849 .map(|child| extract_declarator_name(child, source))
7850 .unwrap_or_else(|| node_text(node, source).to_string()),
7851 }
7852}
7853
7854fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
7859 if let Some(name) = macro_decorated_unqualified_name(node) {
7860 return extract_callable_declarator_name(name, source);
7861 }
7862 match node.kind() {
7863 "identifier"
7864 | "field_identifier"
7865 | "type_identifier"
7866 | "operator_name"
7867 | "destructor_name"
7868 | "qualified_identifier" => Some(node_text(node, source).to_string()),
7869 "function_declarator"
7870 | "pointer_declarator"
7871 | "reference_declarator"
7872 | "parenthesized_declarator"
7873 | "array_declarator"
7874 | "template_function" => node
7875 .child_by_field_name("declarator")
7876 .or_else(|| node.child_by_field_name("name"))
7877 .and_then(|child| extract_callable_declarator_name(child, source)),
7878 _ => None,
7879 }
7880}
7881
7882fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
7883 match node.kind() {
7884 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
7885 let name = node_text(node, source).trim().to_string();
7886 (!name.is_empty()).then_some(name)
7887 }
7888 _ => node
7889 .child_by_field_name("declarator")
7890 .or_else(|| node.child_by_field_name("name"))
7891 .or_else(|| last_named_child(node))
7892 .and_then(|child| extract_variable_name(child, source)),
7893 }
7894}
7895
7896fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
7897 let count = node.named_child_count();
7898 if count == 0 {
7899 None
7900 } else {
7901 node.named_child(count - 1)
7902 }
7903}
7904
7905fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
7906 let name_node = node.child_by_field_name("name")?;
7907 let name = normalize_cpp_whitespace(node_text(name_node, source));
7908 (!name.is_empty()).then_some(name)
7909}
7910
7911fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
7912 if node.kind() != "declaration" {
7913 return Vec::new();
7914 }
7915 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
7916 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
7917 }) else {
7918 return Vec::new();
7919 };
7920 let Some(declarator) = node.child_by_field_name("declarator") else {
7921 return Vec::new();
7922 };
7923 if node_text(keyword, source) == "using"
7924 && (declarator.kind() != "init_declarator"
7925 || declarator.child_by_field_name("value").is_none())
7926 {
7927 return Vec::new();
7928 }
7929 if node_text(keyword, source) == "typedef"
7930 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
7931 {
7932 return vec![alias_name];
7933 }
7934 extract_typedef_declarator_name(declarator, source)
7935 .into_iter()
7936 .collect()
7937}
7938
7939fn recovered_typedef_error_alias_name(
7940 declaration: Node<'_>,
7941 declarator: Node<'_>,
7942 source: &str,
7943) -> Option<String> {
7944 if declarator.kind() != "qualified_identifier" {
7954 return None;
7955 }
7956 let mut cursor = declaration.walk();
7957 let mut errors = declaration
7958 .named_children(&mut cursor)
7959 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
7960 let error = errors.next()?;
7961 if errors.next().is_some() || error.named_child_count() != 1 {
7962 return None;
7963 }
7964 let name = error.named_child(0)?;
7965 if !matches!(
7966 name.kind(),
7967 "identifier" | "field_identifier" | "type_identifier"
7968 ) {
7969 return None;
7970 }
7971 let name = normalize_cpp_whitespace(node_text(name, source));
7972 (!name.is_empty()).then_some(name)
7973}
7974
7975fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
7976 if fragmented_parenthesized_typedef_type(node).is_some() {
7980 return Vec::new();
7981 }
7982 let has_function_like_macro_type = node
7983 .child_by_field_name("type")
7984 .filter(|type_node| type_node.kind() == "type_identifier")
7985 .is_some_and(|type_node| {
7986 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
7987 });
7988 let mut names = Vec::new();
7989 let mut cursor = node.walk();
7990 for declarator in node.children_by_field_name("declarator", &mut cursor) {
7991 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
7992 continue;
7993 }
7994 if let Some(name) = extract_typedef_declarator_name(declarator, source)
7995 && !names.contains(&name)
7996 {
7997 names.push(name);
7998 }
7999 }
8000 names
8001}
8002
8003struct RecoveredMacroTypedefAlias<'tree> {
8004 name: String,
8005 end_node: Node<'tree>,
8006}
8007
8008fn recovered_macro_typedef_alias<'tree>(
8012 node: Node<'tree>,
8013 source: &str,
8014) -> Option<RecoveredMacroTypedefAlias<'tree>> {
8015 let type_node = fragmented_parenthesized_typedef_type(node)?;
8016 if type_node.kind() != "type_identifier"
8017 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
8018 {
8019 return None;
8020 }
8021
8022 let end_node = node.next_named_sibling()?;
8023 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
8024 return None;
8025 }
8026 let name_node = end_node.named_child(0)?;
8027 if name_node.kind() != "identifier" {
8028 return None;
8029 }
8030 let has_terminator = (0..end_node.child_count()).any(|index| {
8031 end_node
8032 .child(index)
8033 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
8034 });
8035 if !has_terminator {
8036 return None;
8037 }
8038 let name = normalize_cpp_whitespace(node_text(name_node, source));
8039 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
8040}
8041
8042fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
8043 if node.kind() != "type_definition" {
8044 return None;
8045 }
8046 let mut declarator_cursor = node.walk();
8047 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
8048 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
8049 return None;
8050 }
8051 let has_missing_terminator = (0..node.child_count()).any(|index| {
8052 node.child(index)
8053 .is_some_and(|child| child.kind() == ";" && child.is_missing())
8054 });
8055 if !has_missing_terminator {
8056 return None;
8057 }
8058 node.child_by_field_name("type")
8059}
8060
8061fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
8062 match node.kind() {
8063 "identifier" | "field_identifier" | "type_identifier" => {
8064 let name = normalize_cpp_whitespace(node_text(node, source));
8065 (!name.is_empty()).then_some(name)
8066 }
8067 "qualified_identifier" => node
8068 .child_by_field_name("name")
8069 .and_then(|name| extract_typedef_declarator_name(name, source)),
8070 _ => node
8071 .child_by_field_name("declarator")
8072 .or_else(|| node.child_by_field_name("name"))
8073 .or_else(|| last_named_child(node))
8074 .and_then(|child| extract_typedef_declarator_name(child, source)),
8075 }
8076}
8077
8078fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
8079 let name = node
8080 .child_by_field_name("name")
8081 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
8082 .or_else(|| {
8083 let mut cursor = node.walk();
8084 node.named_children(&mut cursor)
8085 .find(|child| {
8086 matches!(
8087 child.kind(),
8088 "identifier" | "field_identifier" | "type_identifier"
8089 )
8090 })
8091 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
8092 })?;
8093 (!name.is_empty()).then_some(name)
8094}
8095
8096fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
8097 left.id() == right.id()
8098}
8099
8100fn render_cpp_type_signature(
8101 node: Node<'_>,
8102 source: &str,
8103 template_signature: Option<&str>,
8104) -> String {
8105 let text = normalize_cpp_whitespace(node_text(node, source));
8106 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
8107 let rendered = if head.ends_with(';') {
8108 head.to_string()
8109 } else {
8110 format!("{head} {{")
8111 };
8112 if let Some(template_signature) = template_signature {
8113 format!("template {template_signature} {rendered}")
8114 } else {
8115 rendered
8116 }
8117}
8118
8119fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
8120 if let Some(signature) =
8121 render_recovered_macro_qualified_field_signature(node, declarator, source)
8122 {
8123 return signature;
8124 }
8125 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
8126 let prefix = cpp_declaration_prefix(node, source);
8127 let name = extract_variable_name(declarator, source).unwrap_or_default();
8128 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
8129 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
8130 || (prefix.ends_with('&') && raw_suffix == "&")
8131 {
8132 String::new()
8133 } else {
8134 raw_suffix
8135 };
8136
8137 let mut rendered = if suffix.is_empty() {
8138 format!("{prefix} {name}")
8139 } else if suffix.starts_with('*') || suffix.starts_with('&') {
8140 format!("{prefix}{suffix} {name}")
8141 } else if suffix.starts_with('[') || suffix.starts_with('(') {
8142 format!("{prefix} {name}{suffix}")
8143 } else {
8144 format!("{prefix} {suffix}{name}")
8145 };
8146 rendered = collapse_cpp_whitespace(&rendered);
8147
8148 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
8149 format!("{rendered} = {initializer};")
8150 } else if declaration_text.ends_with(';') {
8151 format!("{rendered};")
8152 } else {
8153 rendered
8154 }
8155}
8156
8157fn render_recovered_macro_qualified_field_signature(
8158 node: Node<'_>,
8159 declarator: Node<'_>,
8160 source: &str,
8161) -> Option<String> {
8162 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
8163 if !recovered
8164 .iter()
8165 .any(|candidate| same_node(*candidate, declarator))
8166 {
8167 return None;
8168 }
8169 let pseudo_declarator = node.child_by_field_name("declarator")?;
8170 let mut cursor = node.walk();
8171 let clause = node
8172 .named_children(&mut cursor)
8173 .find(|child| child.kind() == "bitfield_clause")?;
8174 let mut cursor = clause.walk();
8175 let error = clause
8176 .named_children(&mut cursor)
8177 .find(|child| child.kind() == "ERROR")?;
8178 let qualified_type =
8179 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
8180 let prefix = cpp_declaration_prefix(node, source);
8181 let name = extract_variable_name(declarator, source)?;
8182 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
8183 let mut rendered = if suffix.is_empty() {
8184 format!("{prefix} {qualified_type} {name}")
8185 } else {
8186 format!("{prefix} {qualified_type} {suffix} {name}")
8187 };
8188 rendered = collapse_cpp_whitespace(&rendered);
8189
8190 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
8191 Some(format!(
8192 "{rendered} = {};",
8193 normalize_cpp_whitespace(node_text(initializer, source))
8194 ))
8195 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
8196 Some(format!("{rendered} = {initializer};"))
8197 } else {
8198 Some(format!("{rendered};"))
8199 }
8200}
8201
8202fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
8203 match node.kind() {
8204 "pointer_expression" => {
8205 let operator = node
8206 .child_by_field_name("operator")
8207 .or_else(|| node.child(0))
8208 .map(|operator| node_text(operator, source))
8209 .unwrap_or("*");
8210 let argument = node
8211 .child_by_field_name("argument")
8212 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
8213 .unwrap_or_default();
8214 format!("{operator}{argument}")
8215 }
8216 "unary_expression" => {
8217 let operator = node
8218 .child_by_field_name("operator")
8219 .or_else(|| node.child(0))
8220 .map(|operator| node_text(operator, source))
8221 .unwrap_or_default();
8222 let argument = node
8223 .child_by_field_name("argument")
8224 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
8225 .unwrap_or_default();
8226 format!("{operator}{argument}")
8227 }
8228 "identifier" | "field_identifier" => String::new(),
8229 _ => cpp_declarator_suffix_without_name(node, source),
8230 }
8231}
8232
8233fn recovered_macro_qualified_field_initializer<'tree>(
8234 clause: Node<'tree>,
8235 declarator: Node<'tree>,
8236) -> Option<Node<'tree>> {
8237 let mut stack = vec![clause];
8238 while let Some(current) = stack.pop() {
8239 if current.kind() == "assignment_expression"
8240 && current
8241 .child_by_field_name("left")
8242 .is_some_and(|left| same_node(left, declarator))
8243 {
8244 return current.child_by_field_name("right");
8245 }
8246 let mut cursor = current.walk();
8247 stack.extend(current.named_children(&mut cursor));
8248 }
8249 None
8250}
8251
8252fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
8253 let text = node_text(node, source);
8254 let mut cursor = node.walk();
8255 let first_declarator = node.named_children(&mut cursor).find(|child| {
8256 matches!(
8257 child.kind(),
8258 "init_declarator"
8259 | "identifier"
8260 | "field_identifier"
8261 | "pointer_declarator"
8262 | "reference_declarator"
8263 | "array_declarator"
8264 | "function_declarator"
8265 )
8266 });
8267 let prefix = if let Some(first_declarator) = first_declarator {
8268 let end = first_declarator
8269 .start_byte()
8270 .saturating_sub(node.start_byte());
8271 let mut prefix = text.get(..end).unwrap_or(text).to_string();
8272 let declarator_suffix = match first_declarator.kind() {
8273 "init_declarator" => first_declarator
8274 .child_by_field_name("declarator")
8275 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
8276 .unwrap_or_default(),
8277 _ => cpp_declarator_suffix_without_name(first_declarator, source),
8278 };
8279 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
8280 prefix.push_str(&declarator_suffix);
8281 }
8282 return collapse_cpp_whitespace(&prefix)
8283 .trim_end_matches(',')
8284 .trim_end_matches(';')
8285 .trim()
8286 .to_string();
8287 } else {
8288 text
8289 };
8290 collapse_cpp_whitespace(prefix)
8291 .trim_end_matches(',')
8292 .trim_end_matches(';')
8293 .trim()
8294 .to_string()
8295}
8296
8297fn cpp_preserved_initializer(
8298 declaration_node: Node<'_>,
8299 declarator: Node<'_>,
8300 source: &str,
8301) -> Option<String> {
8302 let name = extract_variable_name(declarator, source)?;
8303 let mut cursor = declaration_node.walk();
8304 for child in declaration_node.named_children(&mut cursor) {
8305 if child.kind() != "init_declarator" {
8306 continue;
8307 }
8308 let Some(inner) = child.child_by_field_name("declarator") else {
8309 continue;
8310 };
8311 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
8312 continue;
8313 }
8314 let value = child.child_by_field_name("value")?;
8315 let kind = value.kind();
8316 if matches!(
8317 kind,
8318 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
8319 ) {
8320 return Some(normalize_cpp_whitespace(node_text(value, source)));
8321 }
8322 break;
8323 }
8324 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
8325 let pattern = format!(
8326 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
8327 regex::escape(&name)
8328 );
8329 Regex::new(&pattern)
8330 .ok()
8331 .and_then(|regex| regex.captures(&declaration_text))
8332 .and_then(|captures| captures.get(1))
8333 .map(|value| value.as_str().to_string())
8334}
8335
8336fn render_cpp_function_display_signature_from_node<'tree>(
8337 node: Node<'tree>,
8338 source: &str,
8339 template_signature: Option<&str>,
8340 has_body: bool,
8341 ancestry: &ParentIndex<'tree>,
8342) -> String {
8343 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
8344 let parent_text = node_text(root, source);
8345 let body_local_start = root
8346 .child_by_field_name("body")
8347 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
8348 .unwrap_or(parent_text.len());
8349 let display = parent_text
8350 .get(..body_local_start)
8351 .unwrap_or(parent_text)
8352 .trim()
8353 .trim();
8354 let display = if let Some(template_signature) = template_signature {
8355 if display.starts_with("template ") {
8356 display.to_string()
8357 } else {
8358 format!("template {template_signature} {display}")
8359 }
8360 } else {
8361 display.to_string()
8362 };
8363 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
8364 if has_body {
8365 format!("{display} {{...}}")
8366 } else {
8367 format!("{display};")
8368 }
8369}
8370
8371fn cpp_template_signature(
8372 template_node: Node<'_>,
8373 declaration_child: Node<'_>,
8374 source: &str,
8375) -> Option<String> {
8376 let text = source
8377 .get(template_node.start_byte()..declaration_child.start_byte())
8378 .unwrap_or("");
8379 let text = normalize_cpp_whitespace(text);
8380 let start = text.find('<')?;
8381 let end = text.rfind('>')?;
8382 if end < start {
8383 return None;
8384 }
8385 Some(text[start..=end].to_string())
8386}
8387
8388struct RecoveredFragmentedPartialSpecialization<'tree> {
8389 declaration_node: Node<'tree>,
8390 name: String,
8391 range: Range,
8392 prefix_members: Vec<Node<'tree>>,
8393 member_siblings: Vec<Node<'tree>>,
8394 following_declarations: Vec<Node<'tree>>,
8395}
8396
8397struct RecoveredFragmentedPreprocessorClass<'tree> {
8398 declaration_node: Node<'tree>,
8399 class_node: Node<'tree>,
8400 body: Node<'tree>,
8401 name: String,
8402 range: Range,
8403 tail_members: Vec<Node<'tree>>,
8404 member_siblings: Vec<Node<'tree>>,
8405}
8406
8407fn recover_fragmented_preprocessor_class<'tree>(
8416 template_node: Node<'tree>,
8417 source: &str,
8418 ancestry: &ParentIndex<'tree>,
8419) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
8420 let alternative = ancestry.parent(template_node)?;
8421 if alternative.kind() != "preproc_else" {
8422 return None;
8423 }
8424 let conditional = alternative.parent()?;
8425 if conditional.kind() != "preproc_if" {
8426 return None;
8427 }
8428 let declaration_node = template_node
8429 .named_children(&mut template_node.walk())
8430 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
8431 let class_node = declaration_node
8432 .named_children(&mut declaration_node.walk())
8433 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
8434 let body = cpp_body_node(class_node)?;
8435 if class_node.end_byte() >= declaration_node.end_byte() {
8436 return None;
8437 }
8438 let name = class_like_name(class_node, source, ancestry)?;
8439 let is_partial_specialization = class_node
8440 .child_by_field_name("name")
8441 .is_some_and(|class_name| class_name.kind() == "template_type");
8442 if is_partial_specialization {
8443 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
8444 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
8445 return None;
8446 }
8447 } else {
8448 if !class_has_displaced_preprocessor_terminator(class_node) {
8449 return None;
8450 }
8451 let matching_other_branch = conditional
8452 .named_children(&mut conditional.walk())
8453 .take_while(|child| !same_node(*child, alternative))
8454 .filter(|child| child.kind() == "template_declaration")
8455 .filter_map(first_class_like_child)
8456 .any(|candidate| {
8457 cpp_body_node(candidate).is_none()
8458 && class_like_name(candidate, source, ancestry).as_deref()
8459 == Some(name.as_str())
8460 });
8461 if !matching_other_branch {
8462 return None;
8463 }
8464 }
8465
8466 let mut tail_members = Vec::new();
8467 let mut saw_class = false;
8468 let mut declaration_cursor = declaration_node.walk();
8469 for child in declaration_node.named_children(&mut declaration_cursor) {
8470 if same_node(child, class_node) {
8471 saw_class = true;
8472 } else if saw_class {
8473 tail_members.push(child);
8474 }
8475 }
8476
8477 let mut member_siblings = Vec::new();
8478 let mut saw_template = false;
8479 let mut terminator = None;
8480 for index in 0..alternative.child_count() {
8481 let Some(child) = alternative.child(index) else {
8482 continue;
8483 };
8484 if same_node(child, template_node) {
8485 saw_template = true;
8486 continue;
8487 }
8488 if !saw_template {
8489 continue;
8490 }
8491 if displaced_fragmented_class_terminator(alternative, index) {
8492 terminator = alternative.child(index + 1);
8493 break;
8494 }
8495 if child.is_named() {
8496 member_siblings.push(child);
8497 }
8498 }
8499 let terminator = terminator?;
8500 Some(RecoveredFragmentedPreprocessorClass {
8501 declaration_node,
8502 class_node,
8503 body,
8504 name,
8505 range: Range {
8506 start_byte: class_node.start_byte(),
8507 end_byte: terminator.end_byte(),
8508 start_line: class_node.start_position().row + 1,
8509 end_line: terminator.end_position().row + 1,
8510 },
8511 tail_members,
8512 member_siblings,
8513 })
8514}
8515
8516fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
8517 (0..class_node.child_count()).any(|index| {
8518 class_node.child(index).is_some_and(|child| {
8519 child.kind() == "ERROR"
8520 && (0..child.child_count()).any(|error_index| {
8521 child
8522 .child(error_index)
8523 .is_some_and(|token| token.kind() == "#endif")
8524 })
8525 })
8526 })
8527}
8528
8529pub fn cpp_displaced_preprocessor_terminator<'tree>(
8538 conditional: Node<'tree>,
8539) -> Option<Node<'tree>> {
8540 if !conditional.has_error() {
8541 return None;
8542 }
8543 let has_concrete_direct_terminator = conditional
8544 .child_count()
8545 .checked_sub(1)
8546 .and_then(|index| conditional.child(index))
8547 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
8548 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
8549 return None;
8553 }
8554 let mut displaced = None;
8555 let mut stack = (0..conditional.child_count())
8556 .filter_map(|index| conditional.child(index))
8557 .map(|child| (child, false))
8558 .collect::<Vec<_>>();
8559 while let Some((node, inside_error)) = stack.pop() {
8560 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
8561 continue;
8562 }
8563 if node.kind() == "#endif" && !node.is_missing() && inside_error {
8564 if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
8565 displaced = Some(node);
8566 }
8567 continue;
8568 }
8569 if node != conditional
8570 && matches!(
8571 node.kind(),
8572 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
8573 )
8574 {
8575 continue;
8576 }
8577 let inside_error = inside_error || node.kind() == "ERROR";
8578 for index in 0..node.child_count() {
8579 if let Some(child) = node.child(index) {
8580 stack.push((child, inside_error));
8581 }
8582 }
8583 }
8584 displaced
8585}
8586
8587#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8600pub struct CppDisplacedPreprocessorBoundary {
8601 pub end_byte: usize,
8602 pub end_line: usize,
8603}
8604
8605pub fn cpp_displaced_preprocessor_boundary(
8606 conditional: Node<'_>,
8607) -> Option<CppDisplacedPreprocessorBoundary> {
8608 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
8609 return Some(CppDisplacedPreprocessorBoundary {
8610 end_byte: terminator.end_byte(),
8611 end_line: terminator.end_position().row + 1,
8612 });
8613 }
8614 if let Some(declaration) = displaced_split_declaration(conditional) {
8615 return Some(CppDisplacedPreprocessorBoundary {
8616 end_byte: declaration.end_byte(),
8617 end_line: declaration.end_position().row + 1,
8618 });
8619 }
8620 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
8621 return Some(CppDisplacedPreprocessorBoundary {
8622 end_byte: terminator.end_byte(),
8623 end_line: terminator.end_position().row + 1,
8624 });
8625 }
8626 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
8627 return Some(CppDisplacedPreprocessorBoundary {
8628 end_byte: terminator.end_byte(),
8629 end_line: terminator.end_position().row + 1,
8630 });
8631 }
8632 None
8633}
8634
8635fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
8641 if !conditional.has_error()
8642 || conditional.child_by_field_name("alternative").is_some()
8643 || conditional
8644 .child(conditional.child_count().saturating_sub(1))
8645 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
8646 {
8647 return None;
8648 }
8649 let mut recovered = None;
8650 for index in 0..conditional.named_child_count() {
8651 let Some(nested) = conditional.named_child(index) else {
8652 continue;
8653 };
8654 if !matches!(
8655 nested.kind(),
8656 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
8657 ) || nested.child_by_field_name("alternative").is_some()
8658 {
8659 continue;
8660 }
8661 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
8662 continue;
8663 };
8664 if direct.kind() != "#endif" || direct.is_missing() {
8665 continue;
8666 }
8667 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
8668 continue;
8669 };
8670 if displaced.end_byte() >= direct.start_byte() {
8671 continue;
8672 }
8673 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
8674 recovered = Some(direct);
8675 }
8676 }
8677 recovered
8678}
8679
8680fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
8681 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
8682 return None;
8683 }
8684 let mut cursor = conditional.walk();
8685 let declarations = conditional
8686 .named_children(&mut cursor)
8687 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
8688 .collect::<Vec<_>>();
8689 let declaration = *declarations.first()?;
8690 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
8691 return None;
8692 }
8693 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
8694 let mut terminator = None;
8695 let mut stack = (0..declaration.child_count())
8696 .filter_map(|index| declaration.child(index))
8697 .filter(|child| child.start_byte() < declarator_start)
8698 .map(|child| (child, false))
8699 .collect::<Vec<_>>();
8700 while let Some((node, inside_error)) = stack.pop() {
8701 let inside_error = inside_error || node.kind() == "ERROR";
8702 if inside_error && node.kind() == "#endif" && !node.is_missing() {
8703 terminator = Some(node);
8704 continue;
8705 }
8706 for index in 0..node.child_count() {
8707 if let Some(child) = node.child(index)
8708 && child.start_byte() < declarator_start
8709 {
8710 stack.push((child, inside_error));
8711 }
8712 }
8713 }
8714 terminator
8715}
8716
8717fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
8718 if !conditional.has_error()
8719 || conditional.child_by_field_name("alternative").is_some()
8720 || conditional
8721 .prev_named_sibling()
8722 .filter(|sibling| {
8723 sibling.kind() == "ERROR"
8724 && sibling.child_count() == 1
8725 && sibling
8726 .child(0)
8727 .is_some_and(|child| child.kind() == "typedef")
8728 })
8729 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
8730 .is_none()
8731 {
8732 return None;
8733 }
8734 let mut cursor = conditional.walk();
8735 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
8736 let declaration_index = children
8737 .iter()
8738 .position(|child| child.kind() == "declaration" && child.has_error())?;
8739 let declaration = children[declaration_index];
8740 if !children
8741 .iter()
8742 .skip(declaration_index + 1)
8743 .any(|child| child.end_byte() > declaration.end_byte())
8744 {
8745 return None;
8746 }
8747 let declarator = declaration.child_by_field_name("declarator")?;
8748 let mut error_end = None;
8749 let mut names = Vec::new();
8750 let mut stack = vec![declarator];
8751 while let Some(node) = stack.pop() {
8752 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
8753 error_end =
8754 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
8755 continue;
8756 }
8757 if matches!(node.kind(), "identifier" | "type_identifier") {
8758 names.push(node.start_byte());
8759 }
8760 for index in (0..node.named_child_count()).rev() {
8761 if let Some(child) = node.named_child(index) {
8762 stack.push(child);
8763 }
8764 }
8765 }
8766 let error_end = error_end?;
8767 names
8768 .into_iter()
8769 .any(|start| start >= error_end)
8770 .then_some(declaration)
8771}
8772
8773fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
8774 let Some(error) = parent.child(error_index) else {
8775 return false;
8776 };
8777 if error.kind() != "ERROR"
8778 || error.child_count() != 1
8779 || error.child(0).is_none_or(|child| child.kind() != "}")
8780 {
8781 return false;
8782 }
8783 let Some(semicolon) = parent.child(error_index + 1) else {
8784 return false;
8785 };
8786 semicolon.kind() == "expression_statement"
8787 && semicolon.child_count() == 1
8788 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
8789}
8790
8791fn displaced_macro_class_tail(
8797 declaration_node: Node<'_>,
8798 body: Node<'_>,
8799 source: &str,
8800) -> Option<DisplacedMacroClassTail> {
8801 if !matches!(
8802 declaration_node.kind(),
8803 "class_specifier" | "struct_specifier" | "union_specifier"
8804 ) || body.kind() != "field_declaration_list"
8805 {
8806 return None;
8807 }
8808
8809 let child_count = body.named_child_count();
8810 for index in 0..child_count {
8811 let child = body.named_child(index)?;
8812 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
8813 continue;
8814 };
8815 let split_index = index + 1;
8816 if split_index >= child_count {
8817 return None;
8818 }
8819 let mut cursor = body.walk();
8820 if !body
8821 .named_children(&mut cursor)
8822 .skip(split_index)
8823 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
8824 {
8825 return None;
8826 }
8827 return Some(DisplacedMacroClassTail {
8828 split_index,
8829 class_range: Range {
8830 start_byte: declaration_node.start_byte(),
8831 end_byte: terminator.end_byte(),
8832 start_line: declaration_node.start_position().row + 1,
8833 end_line: terminator.end_position().row + 1,
8834 },
8835 });
8836 }
8837 None
8838}
8839
8840fn displaced_macro_field_terminator<'tree>(
8841 field: Node<'tree>,
8842 source: &str,
8843) -> Option<Node<'tree>> {
8844 if field.kind() != "field_declaration" {
8845 return None;
8846 }
8847 let macro_type = field.child_by_field_name("type")?;
8848 if macro_type.kind() != "type_identifier"
8849 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8850 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
8851 {
8852 return None;
8853 }
8854 for index in 0..field.child_count() {
8855 let error = field.child(index)?;
8856 if error.kind() != "ERROR"
8857 || error.child_count() != 1
8858 || error.child(0).is_none_or(|child| child.kind() != "}")
8859 {
8860 continue;
8861 }
8862 let semicolon = field.child(index + 1)?;
8863 if semicolon.kind() == ";" {
8864 return Some(semicolon);
8865 }
8866 }
8867 None
8868}
8869
8870fn recover_fragmented_partial_specialization<'tree>(
8871 template_node: Node<'tree>,
8872 declaration_child: Node<'tree>,
8873 source: &str,
8874 ancestry: &ParentIndex<'tree>,
8875) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
8876 if declaration_child.kind() != "function_definition" {
8877 return None;
8878 }
8879 let class_node = declaration_child.child_by_field_name("type")?;
8880 if !matches!(
8881 class_node.kind(),
8882 "class_specifier" | "struct_specifier" | "union_specifier"
8883 ) || !class_node
8884 .child_by_field_name("name")
8885 .and_then(|name| direct_identifier_name(name, source))
8886 .is_some_and(|name| cpp_export_macro_token(&name))
8887 {
8888 return None;
8889 }
8890 let declarator = declaration_child.child_by_field_name("declarator")?;
8891 if declarator.kind() != "template_function" {
8892 return None;
8893 }
8894 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
8895 if metadata.specialization_arguments.is_empty() {
8896 return None;
8897 }
8898 let body = declaration_child.child_by_field_name("body")?;
8899 if body.kind() != "compound_statement" {
8900 return None;
8901 }
8902 let complete_prefix = body.named_child(0).filter(|first| {
8903 first.kind() == "labeled_statement"
8904 && first.has_error()
8905 && first
8906 .named_child(first.named_child_count().saturating_sub(1))
8907 .is_some_and(recovered_declaration_has_class_terminator)
8908 });
8909 let complete_body = complete_prefix.is_some();
8910 let mut prefix_members = Vec::new();
8911 if let Some(prefix) = complete_prefix {
8912 prefix_members.push(prefix);
8913 } else {
8914 let mut body_cursor = body.walk();
8915 for child in body.named_children(&mut body_cursor) {
8916 if !is_structurally_valid_fragmented_class_prefix_member(child) {
8917 break;
8918 }
8919 prefix_members.push(child);
8920 }
8921 }
8922 let containing_declarations = template_node.parent()?;
8923 if !matches!(
8924 containing_declarations.kind(),
8925 "declaration_list" | "compound_statement"
8926 ) {
8927 return None;
8928 }
8929 let mut member_siblings = Vec::new();
8930 let mut following_declarations = Vec::new();
8931 let terminator;
8932 if complete_body {
8933 terminator = complete_prefix?;
8934 let mut cursor = body.walk();
8935 let mut after_prefix = false;
8936 for child in body.named_children(&mut cursor) {
8937 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
8938 after_prefix = true;
8939 } else if after_prefix {
8940 following_declarations.push(child);
8941 }
8942 }
8943 } else {
8944 let mut found_template = false;
8945 let mut cursor = containing_declarations.walk();
8946 let mut class_terminator = None;
8947 for child in containing_declarations.children(&mut cursor) {
8948 if same_node(child, template_node) {
8949 found_template = true;
8950 continue;
8951 }
8952 if found_template && child.kind() == "}" {
8953 class_terminator = Some(child);
8954 break;
8955 }
8956 if found_template && child.kind() == "namespace_definition" {
8963 return None;
8964 }
8965 if found_template && child.is_named() {
8966 member_siblings.push(child);
8967 }
8968 }
8969 terminator = class_terminator?;
8970 }
8971 let name = format!(
8972 "{}<{}>",
8973 metadata.primary_name,
8974 metadata
8975 .specialization_arguments
8976 .iter()
8977 .map(|argument| argument.text.as_str())
8978 .collect::<Vec<_>>()
8979 .join(", ")
8980 );
8981 Some(RecoveredFragmentedPartialSpecialization {
8982 declaration_node: declaration_child,
8983 name,
8984 range: Range {
8985 start_byte: declaration_child.start_byte(),
8986 end_byte: terminator.end_byte(),
8987 start_line: declaration_child.start_position().row + 1,
8988 end_line: terminator.end_position().row + 1,
8989 },
8990 prefix_members,
8991 member_siblings,
8992 following_declarations,
8993 })
8994}
8995
8996fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
8997 if declaration.kind() != "declaration" {
8998 return false;
8999 }
9000 (0..declaration.child_count().saturating_sub(1)).any(|index| {
9005 let Some(error) = declaration.child(index) else {
9006 return false;
9007 };
9008 error.kind() == "ERROR"
9009 && error.child_count() == 1
9010 && error.child(0).is_some_and(|child| child.kind() == "}")
9011 && declaration
9012 .child(index + 1)
9013 .is_some_and(|child| child.kind() == ";")
9014 })
9015}
9016
9017fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
9018 if node.has_error() {
9019 return false;
9020 }
9021 match node.kind() {
9022 "declaration"
9023 | "field_declaration"
9024 | "alias_declaration"
9025 | "type_definition"
9026 | "static_assert_declaration" => true,
9027 "labeled_statement" => node
9028 .named_child(node.named_child_count().saturating_sub(1))
9029 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
9030 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
9031 matches!(
9032 child.kind(),
9033 "declaration"
9034 | "field_declaration"
9035 | "alias_declaration"
9036 | "type_definition"
9037 | "function_definition"
9038 )
9039 }),
9040 _ => false,
9041 }
9042}
9043
9044fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
9045 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
9046 .then(|| node.child_by_field_name("declarator"))
9047 .flatten()
9048 .and_then(|declarator| extract_variable_name(declarator, source))
9049}
9050
9051fn cpp_template_metadata<'tree>(
9052 template_node: Node<'tree>,
9053 declaration_child: Node<'tree>,
9054 source: &str,
9055 ancestry: &ParentIndex<'tree>,
9056) -> Option<CppTemplateMetadata> {
9057 let parameters_node = template_node.child_by_field_name("parameters")?;
9058 let name_node = cpp_templated_class_name_node(declaration_child)?;
9059 let primary_node = match name_node.kind() {
9060 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
9061 _ => name_node,
9062 };
9063 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
9064 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
9065 return None;
9066 }
9067
9068 let mut parameter_nodes = Vec::new();
9069 let mut parameter_names = Vec::new();
9070 let mut cursor = parameters_node.walk();
9071 for parameter in parameters_node.named_children(&mut cursor) {
9072 if !matches!(
9073 parameter.kind(),
9074 "type_parameter_declaration"
9075 | "optional_type_parameter_declaration"
9076 | "variadic_type_parameter_declaration"
9077 | "template_template_parameter_declaration"
9078 | "parameter_declaration"
9079 | "optional_parameter_declaration"
9080 | "variadic_parameter_declaration"
9081 ) {
9082 continue;
9083 }
9084 let index = parameter_nodes.len();
9085 let name = cpp_template_parameter_name(parameter, source)
9090 .unwrap_or_else(|| format!("<anonymous:{index}>"));
9091 parameter_names.push(name);
9092 parameter_nodes.push(parameter);
9093 }
9094 let parameters = parameter_nodes
9095 .into_iter()
9096 .zip(parameter_names.iter().cloned())
9097 .map(|(parameter, name)| CppTemplateParameterMetadata {
9098 name,
9099 kind: cpp_template_parameter_kind(parameter),
9100 variadic: matches!(
9101 parameter.kind(),
9102 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
9103 ),
9104 default: cpp_template_parameter_default_expression(
9105 parameter,
9106 source,
9107 ¶meter_names,
9108 ancestry,
9109 ),
9110 })
9111 .collect();
9112 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
9113 Vec::new()
9114 } else {
9115 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
9116 .unwrap_or_default()
9117 };
9118 let alias_target = (declaration_child.kind() == "alias_declaration")
9119 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
9120 .flatten();
9121 Some(CppTemplateMetadata {
9122 primary_name,
9123 primary_fq_name: String::new(),
9124 parameters,
9125 specialization_arguments,
9126 alias_target,
9127 })
9128}
9129
9130fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
9131 match node.kind() {
9132 "class_specifier" | "struct_specifier" | "union_specifier" => {
9133 node.child_by_field_name("name")
9134 }
9135 "function_definition" => {
9136 let declarator = node.child_by_field_name("declarator")?;
9137 if matches!(declarator.kind(), "identifier" | "template_function") {
9138 Some(declarator)
9139 } else {
9140 None
9141 }
9142 }
9143 "alias_declaration" => node.child_by_field_name("name"),
9144 _ => None,
9145 }
9146}
9147
9148fn cpp_template_alias_target<'tree>(
9149 alias: Node<'tree>,
9150 source: &str,
9151 parameter_names: &[String],
9152 ancestry: &ParentIndex<'tree>,
9153) -> Option<CppTemplateAliasTargetMetadata> {
9154 let mut type_node = alias.child_by_field_name("type")?;
9155 while type_node.kind() == "type_descriptor" {
9156 type_node = type_node.child_by_field_name("type")?;
9157 }
9158 let global = type_node.child_by_field_name("scope").is_none()
9159 && type_node.child(0).is_some_and(|child| child.kind() == "::");
9160 let mut components = Vec::new();
9161 cpp_template_target_components(type_node, source, &mut components)?;
9162 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
9163 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
9164 components,
9165 global,
9166 arguments,
9167 })
9168}
9169
9170fn cpp_template_target_components(
9171 node: Node<'_>,
9172 source: &str,
9173 out: &mut Vec<String>,
9174) -> Option<()> {
9175 match node.kind() {
9176 "identifier" | "namespace_identifier" | "type_identifier" => {
9177 out.push(node_text(node, source).to_string());
9178 Some(())
9179 }
9180 "template_type" => {
9181 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
9182 }
9183 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
9184 if let Some(scope) = node.child_by_field_name("scope") {
9185 cpp_template_target_components(scope, source, out)?;
9186 }
9187 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
9188 }
9189 _ => None,
9190 }
9191}
9192
9193fn cpp_template_argument_expressions<'tree>(
9194 mut node: Node<'tree>,
9195 source: &str,
9196 parameter_names: &[String],
9197 ancestry: &ParentIndex<'tree>,
9198) -> Option<Vec<CppTemplateExpression>> {
9199 loop {
9200 match node.kind() {
9201 "template_type" | "template_function" => {
9202 let arguments = node.child_by_field_name("arguments")?;
9203 let mut cursor = arguments.walk();
9204 return Some(
9205 arguments
9206 .named_children(&mut cursor)
9207 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
9208 .map(|argument| {
9209 cpp_template_expression(argument, source, parameter_names, ancestry)
9210 })
9211 .collect(),
9212 );
9213 }
9214 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
9215 node = node
9216 .child_by_field_name("name")
9217 .or_else(|| node.child_by_field_name("type"))?;
9218 }
9219 _ => return None,
9220 }
9221 }
9222}
9223
9224fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
9225 let candidate = node
9226 .child_by_field_name("name")
9227 .or_else(|| node.child_by_field_name("declarator"))
9228 .or_else(|| {
9229 let mut cursor = node.walk();
9230 node.named_children(&mut cursor).find(|child| {
9231 matches!(
9232 child.kind(),
9233 "identifier" | "type_identifier" | "field_identifier"
9234 )
9235 })
9236 })?;
9237 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
9238 (!name.is_empty()).then_some(name)
9239}
9240
9241fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
9242 match node.kind() {
9243 "type_parameter_declaration"
9244 | "optional_type_parameter_declaration"
9245 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
9246 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
9247 _ => CppTemplateParameterKind::Value,
9248 }
9249}
9250
9251fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
9252 node.child_by_field_name("default_type")
9253 .or_else(|| node.child_by_field_name("default_value"))
9254}
9255
9256fn cpp_template_parameter_default_expression<'tree>(
9257 parameter: Node<'tree>,
9258 source: &str,
9259 parameter_names: &[String],
9260 ancestry: &ParentIndex<'tree>,
9261) -> Option<CppTemplateExpression> {
9262 let default = cpp_template_parameter_default(parameter)?;
9263 let base = cpp_template_expression(default, source, parameter_names, ancestry);
9264 let Some(pointer_error) = parameter.next_named_sibling() else {
9265 return Some(base);
9266 };
9267 let Some(pointer_declarator) =
9268 recovered_abstract_pointer_declarator_term(pointer_error, source)
9269 else {
9270 return Some(base);
9271 };
9272 Some(CppTemplateExpression {
9273 text: format!(
9274 "{}{}",
9275 base.text,
9276 normalize_cpp_whitespace(node_text(pointer_error, source))
9277 ),
9278 term: CppTemplateTerm::Node {
9279 kind: "type_descriptor".to_string(),
9280 children: vec![base.term, pointer_declarator],
9281 },
9282 })
9283}
9284
9285fn recovered_abstract_pointer_declarator_term(
9286 node: Node<'_>,
9287 source: &str,
9288) -> Option<CppTemplateTerm> {
9289 if node.kind() != "ERROR" || node.child_count() == 0 {
9290 return None;
9291 }
9292 let mut children = Vec::new();
9293 for index in 0..node.child_count() {
9294 let child = node.child(index)?;
9295 if child.kind() != "*" {
9296 return None;
9297 }
9298 children.push(CppTemplateTerm::Atom {
9299 kind: "*".to_string(),
9300 text: normalize_cpp_whitespace(node_text(child, source)),
9301 });
9302 }
9303 Some(CppTemplateTerm::Node {
9304 kind: "abstract_pointer_declarator".to_string(),
9305 children,
9306 })
9307}
9308
9309fn cpp_template_expression<'tree>(
9310 node: Node<'tree>,
9311 source: &str,
9312 parameter_names: &[String],
9313 ancestry: &ParentIndex<'tree>,
9314) -> CppTemplateExpression {
9315 let text = normalize_cpp_whitespace(node_text(node, source));
9316 CppTemplateExpression {
9317 text,
9318 term: cpp_template_term(node, source, parameter_names, ancestry),
9319 }
9320}
9321
9322pub fn cpp_template_term<'tree>(
9323 node: Node<'tree>,
9324 source: &str,
9325 parameter_names: &[String],
9326 ancestry: &ParentIndex<'tree>,
9327) -> CppTemplateTerm {
9328 enum Work<'tree> {
9329 Visit(Node<'tree>),
9330 Build { kind: String, child_count: usize },
9331 }
9332
9333 let mut work = vec![Work::Visit(node)];
9334 let mut terms = Vec::new();
9335 while let Some(next) = work.pop() {
9336 match next {
9337 Work::Visit(current) => {
9338 let text = normalize_cpp_whitespace(node_text(current, source));
9339 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
9340 terms.push(CppTemplateTerm::Parameter(text));
9341 continue;
9342 }
9343 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
9344 let mut cursor = current.walk();
9345 let named = current
9346 .named_children(&mut cursor)
9347 .filter(|child| !child.is_extra() && child.kind() != "comment")
9348 .collect::<Vec<_>>();
9349 if let [child] = named.as_slice() {
9350 work.push(Work::Visit(*child));
9351 continue;
9352 }
9353 }
9354 if current.child_count() == 0 {
9355 terms.push(CppTemplateTerm::Atom {
9356 kind: if matches!(
9357 current.kind(),
9358 "identifier"
9359 | "type_identifier"
9360 | "field_identifier"
9361 | "namespace_identifier"
9362 ) {
9363 "identifier".to_string()
9364 } else {
9365 current.kind().to_string()
9366 },
9367 text,
9368 });
9369 continue;
9370 }
9371 let children = (0..current.child_count())
9372 .filter_map(|index| current.child(index))
9373 .filter(|child| !child.is_extra() && child.kind() != "comment")
9374 .collect::<Vec<_>>();
9375 work.push(Work::Build {
9376 kind: current.kind().to_string(),
9377 child_count: children.len(),
9378 });
9379 work.extend(children.into_iter().rev().map(Work::Visit));
9380 }
9381 Work::Build { kind, child_count } => {
9382 let children = terms.split_off(terms.len() - child_count);
9383 terms.push(CppTemplateTerm::Node { kind, children });
9384 }
9385 }
9386 }
9387 terms.pop().expect("template term traversal emits one root")
9388}
9389
9390fn cpp_template_term_leaf_is_parameter<'tree>(
9391 node: Node<'tree>,
9392 text: &str,
9393 parameter_names: &[String],
9394 ancestry: &ParentIndex<'tree>,
9395) -> bool {
9396 if !parameter_names.iter().any(|parameter| parameter == text) {
9397 return false;
9398 }
9399 !ancestry.parent(node).is_some_and(|parent| {
9400 matches!(
9401 parent.kind(),
9402 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
9403 ) && parent.child_by_field_name("scope").is_some()
9404 && parent.child_by_field_name("name") == Some(node)
9405 })
9406}
9407
9408fn enclosing_cpp_declaration_node<'tree>(
9409 mut node: Node<'tree>,
9410 ancestry: &ParentIndex<'tree>,
9411) -> Option<Node<'tree>> {
9412 loop {
9413 match node.kind() {
9414 "declaration"
9415 | "function_declaration"
9416 | "field_declaration"
9417 | "function_definition" => return Some(node),
9418 _ => node = ancestry.parent(node)?,
9419 }
9420 }
9421}
9422
9423fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
9424 let mut params = Vec::new();
9425 let mut cursor = parameters_node.walk();
9426 for child in parameters_node.children(&mut cursor) {
9427 match child.kind() {
9428 "parameter_declaration" | "optional_parameter_declaration" => {
9429 params.push(cpp_parameter_type(child, source));
9430 }
9431 "variadic_parameter_declaration" => {
9432 params.push(cpp_parameter_type(child, source));
9433 }
9434 "variadic_parameter" | "..." => params.push("...".to_string()),
9435 _ => {}
9436 }
9437 }
9438
9439 if params.is_empty() {
9440 "()".to_string()
9441 } else {
9442 format!("({})", params.join(", "))
9443 }
9444}
9445
9446fn cpp_signature_metadata<'tree>(
9447 signature: String,
9448 function_declarator: Node<'tree>,
9449 source: &str,
9450 ancestry: &ParentIndex<'tree>,
9451) -> SignatureMetadata {
9452 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
9453 let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
9454 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
9455 let return_type_identity =
9456 cpp_callable_return_type_identity(function_declarator, source, ancestry);
9457 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
9458 return enrich(
9459 SignatureMetadata::new(signature, Vec::new())
9460 .with_return_type_text(return_type_text)
9461 .with_return_type_identity(return_type_identity),
9462 );
9463 };
9464 let callable_arity = cpp_callable_arity(parameters_node, source);
9465 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
9466 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
9467 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
9468 let Some(relative_start) = signature
9469 .get(search_from..)
9470 .and_then(|suffix| suffix.find(¶meter_text))
9471 else {
9472 return enrich(
9473 SignatureMetadata::new(signature, Vec::new())
9474 .with_callable_arity(callable_arity)
9475 .with_callable_parameter_types(callable_parameter_types)
9476 .with_return_type_text(return_type_text)
9477 .with_return_type_identity(return_type_identity),
9478 );
9479 };
9480 let parameters_start = search_from + relative_start;
9481 let parameters_end = parameters_start + parameter_text.len();
9482 let mut search_start = parameters_start;
9483 let parameters = cpp_parameter_label_nodes(parameters_node)
9484 .into_iter()
9485 .filter_map(|label_node| {
9486 let label = normalize_cpp_whitespace(node_text(label_node, source));
9487 if label.is_empty() || search_start > parameters_end {
9488 return None;
9489 }
9490 let haystack = signature.get(search_start..parameters_end)?;
9491 let relative_start = haystack.find(&label)?;
9492 let start_byte = search_start + relative_start;
9493 let end_byte = start_byte + label.len();
9494 search_start = end_byte;
9495 Some(ParameterMetadata::new(label, start_byte, end_byte))
9496 })
9497 .collect();
9498 enrich(
9499 SignatureMetadata::new(signature, parameters)
9500 .with_callable_arity(callable_arity)
9501 .with_callable_parameter_types(callable_parameter_types)
9502 .with_return_type_text(return_type_text)
9503 .with_return_type_identity(return_type_identity),
9504 )
9505}
9506
9507fn cpp_callable_is_structural_constructor<'tree>(
9508 function_declarator: Node<'tree>,
9509 source: &str,
9510 ancestry: &ParentIndex<'tree>,
9511) -> bool {
9512 let Some(name_node) = function_declarator
9513 .child_by_field_name("declarator")
9514 .or_else(|| function_declarator.child_by_field_name("name"))
9515 .or_else(|| last_named_child(function_declarator))
9516 else {
9517 return false;
9518 };
9519 let Some(callable_name) = direct_identifier_name(name_node, source) else {
9520 return false;
9521 };
9522
9523 let mut current = ancestry.parent(function_declarator);
9524 while let Some(ancestor) = current {
9525 let owner_name = match ancestor.kind() {
9526 "class_specifier" | "struct_specifier" | "union_specifier" => {
9527 class_like_name(ancestor, source, ancestry)
9528 }
9529 "ERROR" => malformed_class_error_owner_name(ancestor, source),
9530 _ => None,
9531 };
9532 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
9533 return true;
9534 }
9535 current = ancestry.parent(ancestor);
9536 }
9537 false
9538}
9539
9540fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
9550 if node.kind() != "ERROR" {
9551 return None;
9552 }
9553 let keyword = node.child(0)?;
9554 if !matches!(keyword.kind(), "class" | "struct" | "union") {
9555 return None;
9556 }
9557 let name_node = node.child(1)?;
9558 let name = direct_identifier_name(name_node, source)?;
9559 let has_body = (2..node.child_count())
9560 .filter_map(|index| node.child(index))
9561 .any(|child| child.kind() == "{");
9562 has_body.then_some(name)
9563}
9564
9565fn cpp_callable_return_type_identity<'tree>(
9566 function_declarator: Node<'tree>,
9567 source: &str,
9568 ancestry: &ParentIndex<'tree>,
9569) -> Option<StructuredTypeIdentity> {
9570 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
9571 return None;
9572 }
9573 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
9574 if let Some((return_type, _)) =
9575 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
9576 {
9577 return cpp_structured_type_identity(return_type, source, &lexical_scope);
9578 }
9579 let mut cursor = function_declarator.walk();
9580 if let Some(trailing) = function_declarator
9581 .named_children(&mut cursor)
9582 .find(|child| child.kind() == "trailing_return_type")
9583 && let Some(type_descriptor) = trailing.named_child(0)
9584 {
9585 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
9586 }
9587
9588 let mut current = function_declarator;
9589 let mut wrappers = Vec::new();
9590 while let Some(parent) = ancestry.parent(current) {
9591 if matches!(
9592 parent.kind(),
9593 "function_definition" | "declaration" | "field_declaration"
9594 ) {
9595 let type_node = parent.child_by_field_name("type")?;
9596 if cpp_export_macro_token(node_text(type_node, source))
9597 && (0..parent.named_child_count()).any(|index| {
9598 parent
9599 .named_child(index)
9600 .is_some_and(|child| child.kind() == "ERROR")
9601 })
9602 {
9603 return None;
9604 }
9605 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
9606 for wrapper in wrappers.into_iter().rev() {
9607 identity = cpp_wrap_structured_type(identity, wrapper)?;
9608 }
9609 return Some(identity);
9610 }
9611 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
9612 || (matches!(
9613 parent.kind(),
9614 "pointer_declarator"
9615 | "reference_declarator"
9616 | "array_declarator"
9617 | "parenthesized_declarator"
9618 ) && parent.named_child_count() == 1
9619 && parent.named_child(0) == Some(current));
9620 if !wraps_current_declarator {
9621 return None;
9622 }
9623 match parent.kind() {
9624 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
9625 "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
9626 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
9627 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
9628 _ => return None,
9629 }
9630 current = parent;
9631 }
9632 None
9633}
9634
9635fn cpp_structured_type_identity(
9636 node: Node<'_>,
9637 source: &str,
9638 lexical_scope: &[String],
9639) -> Option<StructuredTypeIdentity> {
9640 enum Work<'tree> {
9641 Visit(Node<'tree>),
9642 Wrap(CppStructuredTypeWrapper),
9643 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
9644 BuildGeneric { argument_count: usize },
9645 }
9646
9647 let mut work = vec![Work::Visit(node)];
9648 let mut values = Vec::new();
9649 let mut builder = StructuredTypeIdentityBuilder::default();
9650 while let Some(next) = work.pop() {
9651 match next {
9652 Work::Visit(current) => match current.kind() {
9653 "type_descriptor" => {
9654 let type_node = current
9655 .child_by_field_name("type")
9656 .or_else(|| current.named_child(0))?;
9657 let mut wrappers = Vec::new();
9658 let mut cursor = current.walk();
9659 for child in current.named_children(&mut cursor) {
9660 if child.id() != type_node.id() {
9661 wrappers.extend(cpp_structured_declarator_wrappers(child));
9662 }
9663 }
9664 work.push(Work::ApplyWrappers(wrappers));
9665 work.push(Work::Visit(type_node));
9666 }
9667 "pointer_declarator" | "abstract_pointer_declarator" => {
9668 let child = current
9669 .child_by_field_name("declarator")
9670 .or_else(|| current.named_child(0))?;
9671 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
9672 work.push(Work::Visit(child));
9673 }
9674 "reference_declarator" => {
9675 let child = current
9676 .child_by_field_name("declarator")
9677 .or_else(|| current.named_child(0))?;
9678 work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
9679 work.push(Work::Visit(child));
9680 }
9681 "array_declarator" | "abstract_array_declarator" => {
9682 let child = current
9683 .child_by_field_name("declarator")
9684 .or_else(|| current.named_child(0))?;
9685 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
9686 work.push(Work::Visit(child));
9687 }
9688 "template_type" => {
9689 let name_node = current.child_by_field_name("name")?;
9690 let arguments = current
9691 .child_by_field_name("arguments")
9692 .map(|arguments_node| {
9693 let mut cursor = arguments_node.walk();
9694 arguments_node
9695 .named_children(&mut cursor)
9696 .filter(|child| !child.is_extra() && child.kind() != "comment")
9697 .collect::<Vec<_>>()
9698 })
9699 .unwrap_or_default();
9700 work.push(Work::BuildGeneric {
9701 argument_count: arguments.len(),
9702 });
9703 work.extend(arguments.into_iter().rev().map(Work::Visit));
9704 work.push(Work::Visit(name_node));
9705 }
9706 "qualified_identifier"
9707 | "scoped_identifier"
9708 | "scoped_type_identifier"
9709 | "type_identifier"
9710 | "field_identifier"
9711 | "identifier"
9712 | "namespace_identifier"
9713 | "primitive_type" => {
9714 values.push(builder.named(cpp_structured_named_type(
9715 current,
9716 source,
9717 lexical_scope,
9718 )?)?);
9719 }
9720 _ => {
9721 let child = current.child_by_field_name("type").or_else(|| {
9722 (current.named_child_count() == 1)
9723 .then(|| current.named_child(0))
9724 .flatten()
9725 })?;
9726 work.push(Work::Visit(child));
9727 }
9728 },
9729 Work::Wrap(wrapper) => {
9730 let root = values.pop()?;
9731 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
9732 }
9733 Work::ApplyWrappers(wrappers) => {
9734 let mut root = values.pop()?;
9735 for wrapper in wrappers.into_iter().rev() {
9736 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
9737 }
9738 values.push(root);
9739 }
9740 Work::BuildGeneric { argument_count } => {
9741 let value_count = argument_count.checked_add(1)?;
9742 let start = values.len().checked_sub(value_count)?;
9743 let mut built = values.split_off(start);
9744 let base = built.remove(0);
9745 values.push(builder.generic(base, built)?);
9746 }
9747 }
9748 }
9749 (values.len() == 1)
9750 .then(|| values.pop())
9751 .flatten()
9752 .and_then(|root| builder.finish(root))
9753}
9754
9755fn cpp_structured_named_type(
9756 node: Node<'_>,
9757 source: &str,
9758 lexical_scope: &[String],
9759) -> Option<StructuredTypeName> {
9760 let path = cpp_structured_type_path(node, source)?;
9761 let absolute = node.child_by_field_name("scope").is_none()
9762 && node.child(0).is_some_and(|child| child.kind() == "::");
9763 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
9764}
9765
9766#[derive(Clone, Copy)]
9767enum CppStructuredTypeWrapper {
9768 Pointer,
9769 Reference,
9770 Array,
9771}
9772
9773fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
9774 let mut wrappers = Vec::new();
9775 let mut current = node;
9776 loop {
9777 match current.kind() {
9778 "pointer_declarator" | "abstract_pointer_declarator" => {
9779 wrappers.push(CppStructuredTypeWrapper::Pointer)
9780 }
9781 "reference_declarator" | "abstract_reference_declarator" => {
9782 wrappers.push(CppStructuredTypeWrapper::Reference)
9783 }
9784 "array_declarator" | "abstract_array_declarator" => {
9785 wrappers.push(CppStructuredTypeWrapper::Array)
9786 }
9787 _ => break,
9788 }
9789 let Some(child) = current
9790 .child_by_field_name("declarator")
9791 .or_else(|| current.named_child(0))
9792 else {
9793 break;
9794 };
9795 current = child;
9796 }
9797 wrappers
9798}
9799
9800fn cpp_wrap_structured_type(
9801 identity: StructuredTypeIdentity,
9802 wrapper: CppStructuredTypeWrapper,
9803) -> Option<StructuredTypeIdentity> {
9804 match wrapper {
9805 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
9806 CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
9807 CppStructuredTypeWrapper::Array => identity.wrap_array(),
9808 }
9809}
9810
9811fn cpp_wrap_structured_type_node(
9812 builder: &mut StructuredTypeIdentityBuilder,
9813 inner: StructuredTypeNodeId,
9814 wrapper: CppStructuredTypeWrapper,
9815) -> Option<StructuredTypeNodeId> {
9816 match wrapper {
9817 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
9818 CppStructuredTypeWrapper::Reference => builder.reference(inner),
9819 CppStructuredTypeWrapper::Array => builder.array(inner),
9820 }
9821}
9822
9823fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
9824 let mut path = Vec::new();
9825 let mut stack = vec![node];
9826 while let Some(current) = stack.pop() {
9827 match current.kind() {
9828 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
9829 let component = node_text(current, source).to_string();
9830 if component.is_empty() {
9831 return None;
9832 }
9833 path.push(component);
9834 }
9835 "template_type" | "dependent_type" => {
9836 stack.push(current.child_by_field_name("name")?);
9837 }
9838 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
9839 stack.push(current.child_by_field_name("name")?);
9840 if let Some(scope) = current.child_by_field_name("scope") {
9841 stack.push(scope);
9842 }
9843 }
9844 _ => return None,
9845 }
9846 }
9847 (!path.is_empty()).then_some(path)
9848}
9849
9850fn cpp_callable_lexical_scope<'tree>(
9851 node: Node<'tree>,
9852 source: &str,
9853 ancestry: &ParentIndex<'tree>,
9854) -> Vec<String> {
9855 let mut groups = Vec::new();
9856 let mut current = ancestry.parent(node);
9857 while let Some(parent) = current {
9858 if matches!(
9859 parent.kind(),
9860 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
9861 ) && let Some(name_node) = parent.child_by_field_name("name")
9862 && let Some(components) = cpp_structured_type_path(name_node, source)
9863 && !components.is_empty()
9864 {
9865 groups.push(components);
9866 }
9867 current = ancestry.parent(parent);
9868 }
9869 groups.reverse();
9870 groups.into_iter().flatten().collect()
9871}
9872
9873fn cpp_callable_dispatch_extensibility<'tree>(
9874 function_declarator: Node<'tree>,
9875 ancestry: &ParentIndex<'tree>,
9876) -> DispatchExtensibility {
9877 let mut declaration = None;
9878 let mut current = Some(function_declarator);
9879 while let Some(node) = current {
9880 match node.kind() {
9881 "template_declaration"
9882 | "preproc_if"
9883 | "preproc_ifdef"
9884 | "preproc_else"
9885 | "preproc_elif"
9886 | "preproc_call"
9887 | "ERROR" => return DispatchExtensibility::Open,
9888 "declaration" | "field_declaration" | "function_definition" => {
9889 declaration.get_or_insert(node);
9890 }
9891 "translation_unit" => break,
9892 _ => {}
9893 }
9894 current = ancestry.parent(node);
9895 }
9896 let Some(declaration) = declaration else {
9897 return DispatchExtensibility::Open;
9898 };
9899
9900 let mut saw_virtual_boundary = false;
9901 let mut stack = vec![declaration];
9902 while let Some(node) = stack.pop() {
9903 match node.kind() {
9904 "compound_statement" | "field_declaration_list" => continue,
9905 "final" | "final_specifier" => return DispatchExtensibility::Closed,
9906 "virtual"
9907 | "override"
9908 | "virtual_specifier"
9909 | "pure_virtual_clause"
9910 | "template_parameter_list"
9911 | "template_method"
9912 | "template_function"
9913 | "ERROR" => saw_virtual_boundary = true,
9914 _ => {}
9915 }
9916 let mut cursor = node.walk();
9917 stack.extend(node.children(&mut cursor));
9918 }
9919
9920 if saw_virtual_boundary {
9921 DispatchExtensibility::Open
9922 } else {
9923 DispatchExtensibility::Closed
9924 }
9925}
9926
9927fn cpp_callable_linkage<'tree>(
9928 declaration: Node<'tree>,
9929 source: &str,
9930 ancestry: &ParentIndex<'tree>,
9931) -> CallableLinkage {
9932 let mut enclosed_by_class = false;
9933 let mut current = ancestry.parent(declaration);
9934 while let Some(node) = current {
9935 if node.kind() == "namespace_definition"
9936 && node
9937 .child_by_field_name("name")
9938 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
9939 {
9940 return CallableLinkage::Internal;
9941 }
9942 if matches!(
9943 node.kind(),
9944 "class_specifier" | "struct_specifier" | "union_specifier"
9945 ) {
9946 if node
9947 .child_by_field_name("name")
9948 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
9949 {
9950 return CallableLinkage::Internal;
9951 }
9952 enclosed_by_class = true;
9953 }
9954 if matches!(node.kind(), "function_definition" | "lambda_expression") {
9955 return CallableLinkage::Internal;
9956 }
9957 current = ancestry.parent(node);
9958 }
9959
9960 if enclosed_by_class {
9961 return CallableLinkage::External;
9962 }
9963
9964 let mut cursor = declaration.walk();
9965 if declaration.named_children(&mut cursor).any(|child| {
9966 child.kind() == "storage_class_specifier"
9967 && normalize_cpp_whitespace(node_text(child, source)) == "static"
9968 }) {
9969 CallableLinkage::Internal
9970 } else {
9971 CallableLinkage::External
9972 }
9973}
9974
9975fn cpp_callable_return_type_text<'tree>(
9976 function_declarator: Node<'tree>,
9977 source: &str,
9978 ancestry: &ParentIndex<'tree>,
9979) -> Option<String> {
9980 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
9981 return None;
9982 }
9983 if let Some((return_type, _)) =
9984 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
9985 {
9986 let text = normalize_cpp_whitespace(node_text(return_type, source));
9987 return (!text.is_empty()).then_some(text);
9988 }
9989 let mut cursor = function_declarator.walk();
9990 if let Some(trailing) = function_declarator
9991 .named_children(&mut cursor)
9992 .find(|child| child.kind() == "trailing_return_type")
9993 && let Some(type_descriptor) = trailing.named_child(0)
9994 {
9995 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
9996 if !text.is_empty() {
9997 return Some(text);
9998 }
9999 }
10000
10001 let mut current = function_declarator;
10002 let mut indirection = String::new();
10003 while let Some(parent) = ancestry.parent(current) {
10004 if matches!(
10005 parent.kind(),
10006 "function_definition" | "declaration" | "field_declaration"
10007 ) {
10008 let type_node = parent.child_by_field_name("type")?;
10009 if cpp_export_macro_token(node_text(type_node, source))
10010 && (0..parent.named_child_count()).any(|index| {
10011 parent
10012 .named_child(index)
10013 .is_some_and(|child| child.kind() == "ERROR")
10014 })
10015 {
10016 return None;
10021 }
10022 let base = normalize_cpp_whitespace(node_text(type_node, source));
10023 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
10024 }
10025 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
10026 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
10027 && parent.named_child_count() == 1
10028 && parent.named_child(0) == Some(current));
10029 if wraps_current_declarator {
10030 match parent.kind() {
10031 "pointer_declarator" => indirection.push('*'),
10032 "reference_declarator" => {
10033 let reference = parent
10034 .children(&mut parent.walk())
10035 .find(|child| !child.is_named())
10036 .map(|child| node_text(child, source))
10037 .unwrap_or("&");
10038 indirection.push_str(reference);
10039 }
10040 "init_declarator" | "parenthesized_declarator" => {}
10041 _ => return None,
10042 }
10043 current = parent;
10044 continue;
10045 }
10046 return None;
10047 }
10048 None
10049}
10050
10051fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
10052 let mut required = 0;
10053 let mut total = 0;
10054 let mut repeated = false;
10055 let mut cursor = parameters_node.walk();
10056 for child in parameters_node.children(&mut cursor) {
10057 match child.kind() {
10058 "parameter_declaration" => {
10059 if cpp_parameter_is_explicit_object(child, source) {
10060 continue;
10061 }
10062 if child.child_by_field_name("declarator").is_none()
10063 && child
10064 .child_by_field_name("type")
10065 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
10066 {
10067 continue;
10068 }
10069 required += 1;
10070 total += 1;
10071 }
10072 "optional_parameter_declaration" => total += 1,
10073 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
10074 repeated = true;
10075 }
10076 _ => {}
10077 }
10078 }
10079 CallableArity::new(required, total, repeated)
10080}
10081
10082fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
10083 parameter
10084 .child_by_field_name("type")
10085 .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
10086 .and_then(|type_node| type_node.child_by_field_name("constraint"))
10087 .is_some_and(|constraint| {
10088 constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
10089 })
10090}
10091
10092#[derive(Clone, Copy)]
10100enum CppParameterSlot<'tree> {
10101 Declared(Node<'tree>),
10102 Ellipsis,
10103}
10104
10105fn cpp_callable_parameter_slots<'tree>(
10106 parameters_node: Node<'tree>,
10107 source: &str,
10108) -> Vec<CppParameterSlot<'tree>> {
10109 let mut slots = Vec::new();
10110 let mut cursor = parameters_node.walk();
10111 for parameter in parameters_node.children(&mut cursor) {
10112 match parameter.kind() {
10113 "parameter_declaration" | "optional_parameter_declaration" => {
10114 if cpp_parameter_is_explicit_object(parameter, source)
10115 || (parameter.child_by_field_name("declarator").is_none()
10116 && parameter
10117 .child_by_field_name("type")
10118 .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
10119 {
10120 continue;
10121 }
10122 slots.push(CppParameterSlot::Declared(parameter));
10123 }
10124 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
10125 slots.push(CppParameterSlot::Ellipsis);
10126 }
10127 _ => {}
10128 }
10129 }
10130 slots
10131}
10132
10133fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
10134 cpp_callable_parameter_slots(parameters_node, source)
10135 .into_iter()
10136 .map(|slot| match slot {
10137 CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
10138 CppParameterSlot::Ellipsis => "...".to_string(),
10139 })
10140 .collect()
10141}
10142
10143#[derive(Debug, Clone, PartialEq, Eq)]
10149pub enum CppParameterType {
10150 Structured(StructuredTypeIdentity),
10153 Ellipsis,
10155 Unstructured,
10158}
10159
10160pub fn cpp_callable_parameter_type_identities<'tree>(
10166 function_declarator: Node<'tree>,
10167 source: &str,
10168 ancestry: &ParentIndex<'tree>,
10169) -> Vec<CppParameterType> {
10170 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
10171 return Vec::new();
10172 };
10173 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
10174 cpp_callable_parameter_slots(parameters_node, source)
10175 .into_iter()
10176 .map(|slot| match slot {
10177 CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
10178 CppParameterSlot::Declared(parameter) => {
10179 cpp_parameter_type_identity(parameter, source, &lexical_scope)
10180 .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
10181 }
10182 })
10183 .collect()
10184}
10185
10186fn cpp_parameter_type_identity(
10187 parameter: Node<'_>,
10188 source: &str,
10189 lexical_scope: &[String],
10190) -> Option<StructuredTypeIdentity> {
10191 let type_node = parameter.child_by_field_name("type")?;
10192 let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
10193 if let Some(declarator) = cpp_parameter_declarator(parameter) {
10194 for wrapper in cpp_structured_declarator_wrappers(declarator)
10195 .into_iter()
10196 .rev()
10197 {
10198 identity = cpp_wrap_structured_type(identity, wrapper)?;
10199 }
10200 }
10201 Some(identity)
10202}
10203
10204#[derive(Debug, Clone, PartialEq, Eq)]
10217pub enum CppComparableSlot {
10218 Shape(CppComparableParameter),
10220 Ellipsis,
10222 Unstructured,
10225}
10226
10227#[derive(Debug, Clone, PartialEq, Eq)]
10240pub struct CppComparableParameter {
10241 nodes: Vec<CppComparableNode>,
10242 root: usize,
10243}
10244
10245#[derive(Debug, Clone, PartialEq, Eq)]
10253pub enum CppComparableNode {
10254 Named {
10255 name: StructuredTypeName,
10256 primitive: bool,
10257 konst: bool,
10258 volatil: bool,
10259 },
10260 Pointer {
10261 inner: usize,
10262 konst: bool,
10263 volatil: bool,
10264 },
10265 Reference {
10266 inner: usize,
10267 },
10268 Array {
10269 inner: usize,
10270 },
10271 Generic {
10272 base: usize,
10273 arguments: Vec<usize>,
10274 },
10275}
10276
10277impl CppComparableParameter {
10278 pub fn root(&self) -> usize {
10279 self.root
10280 }
10281
10282 pub fn node(&self, index: usize) -> &CppComparableNode {
10283 &self.nodes[index]
10284 }
10285
10286 fn adjust_parameter_top_level(&mut self) {
10297 let root = self.root;
10298 match &mut self.nodes[root] {
10299 CppComparableNode::Named { konst, volatil, .. }
10300 | CppComparableNode::Pointer { konst, volatil, .. } => {
10301 *konst = false;
10302 *volatil = false;
10303 }
10304 CppComparableNode::Array { inner } => {
10305 let inner = *inner;
10306 self.nodes[root] = CppComparableNode::Pointer {
10307 inner,
10308 konst: false,
10309 volatil: false,
10310 };
10311 }
10312 CppComparableNode::Generic { base, .. } => {
10313 let base = *base;
10314 let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
10315 unreachable!("a comparable generic's base is always a named leaf");
10316 };
10317 *konst = false;
10318 *volatil = false;
10319 }
10320 CppComparableNode::Reference { .. } => {}
10321 }
10322 }
10323}
10324
10325pub fn cpp_comparable_parameter_shapes<'tree>(
10332 function_declarator: Node<'tree>,
10333 source: &str,
10334 ancestry: &ParentIndex<'tree>,
10335) -> Vec<CppComparableSlot> {
10336 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
10337 return Vec::new();
10338 };
10339 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
10340 cpp_callable_parameter_slots(parameters_node, source)
10341 .into_iter()
10342 .map(|slot| match slot {
10343 CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
10344 CppParameterSlot::Declared(parameter) => {
10345 cpp_comparable_parameter(parameter, source, &lexical_scope)
10346 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
10347 }
10348 })
10349 .collect()
10350}
10351
10352fn cpp_comparable_parameter(
10353 parameter: Node<'_>,
10354 source: &str,
10355 lexical_scope: &[String],
10356) -> Option<CppComparableParameter> {
10357 let type_node = parameter.child_by_field_name("type")?;
10358 let levels = match cpp_parameter_declarator(parameter) {
10359 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
10360 None => Vec::new(),
10361 };
10362 let mut shape = cpp_comparable_type_shape(
10363 type_node,
10364 cpp_cv_qualifiers(parameter, source),
10365 levels,
10366 source,
10367 lexical_scope,
10368 )?;
10369 shape.adjust_parameter_top_level();
10370 Some(shape)
10371}
10372
10373fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
10384 let mut qualifiers = CppCvQualifiers::default();
10385 let mut cursor = node.walk();
10386 for child in node.named_children(&mut cursor) {
10387 if child.kind() != "type_qualifier" {
10388 continue;
10389 }
10390 match node_text(child, source) {
10391 "const" => qualifiers.konst = true,
10392 "volatile" => qualifiers.volatil = true,
10393 _ => {}
10394 }
10395 }
10396 qualifiers
10397}
10398
10399#[derive(Clone, Copy, Default)]
10400struct CppCvQualifiers {
10401 konst: bool,
10402 volatil: bool,
10403}
10404
10405impl CppCvQualifiers {
10406 fn union(self, other: Self) -> Self {
10407 Self {
10408 konst: self.konst || other.konst,
10409 volatil: self.volatil || other.volatil,
10410 }
10411 }
10412}
10413
10414#[derive(Clone, Copy)]
10416enum CppComparableLevel {
10417 Pointer { konst: bool, volatil: bool },
10418 Reference,
10419 Array,
10420}
10421
10422fn cpp_comparable_declarator_levels(
10435 declarator: Node<'_>,
10436 source: &str,
10437) -> Option<Vec<CppComparableLevel>> {
10438 let mut levels = Vec::new();
10439 let mut current = declarator;
10440 loop {
10441 match current.kind() {
10442 "pointer_declarator" | "abstract_pointer_declarator" => {
10443 let qualifiers = cpp_cv_qualifiers(current, source);
10444 levels.push(CppComparableLevel::Pointer {
10445 konst: qualifiers.konst,
10446 volatil: qualifiers.volatil,
10447 });
10448 }
10449 "reference_declarator" | "abstract_reference_declarator" => {
10450 levels.push(CppComparableLevel::Reference);
10451 }
10452 "array_declarator" | "abstract_array_declarator" => {
10453 levels.push(CppComparableLevel::Array);
10454 }
10455 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
10456 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
10457 _ => return None,
10458 }
10459 let Some(next) = cpp_nested_declarator(current) else {
10460 return Some(levels);
10461 };
10462 current = next;
10463 }
10464}
10465
10466fn cpp_comparable_type_shape(
10473 type_node: Node<'_>,
10474 qualifiers: CppCvQualifiers,
10475 levels: Vec<CppComparableLevel>,
10476 source: &str,
10477 lexical_scope: &[String],
10478) -> Option<CppComparableParameter> {
10479 enum Work<'tree> {
10480 Visit {
10481 node: Node<'tree>,
10482 qualifiers: CppCvQualifiers,
10483 },
10484 ApplyLevels(Vec<CppComparableLevel>),
10485 BuildGeneric {
10486 argument_count: usize,
10487 },
10488 }
10489
10490 let mut nodes: Vec<CppComparableNode> = Vec::new();
10491 let mut values: Vec<usize> = Vec::new();
10492 let mut work = vec![
10493 Work::ApplyLevels(levels),
10494 Work::Visit {
10495 node: type_node,
10496 qualifiers,
10497 },
10498 ];
10499 while let Some(next) = work.pop() {
10500 match next {
10501 Work::Visit { node, qualifiers } => match node.kind() {
10502 "type_descriptor" => {
10503 let inner_type = node
10504 .child_by_field_name("type")
10505 .or_else(|| node.named_child(0))?;
10506 let mut cursor = node.walk();
10507 let declarator = node.child_by_field_name("declarator").or_else(|| {
10508 node.named_children(&mut cursor).find(|child| {
10509 child.id() != inner_type.id() && child.kind() != "type_qualifier"
10510 })
10511 });
10512 let levels = match declarator {
10513 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
10514 None => Vec::new(),
10515 };
10516 work.push(Work::ApplyLevels(levels));
10517 work.push(Work::Visit {
10518 node: inner_type,
10519 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
10520 });
10521 }
10522 "sized_type_specifier" => {
10523 let name = StructuredTypeName::new(
10528 vec![normalize_cpp_whitespace(node_text(node, source))],
10529 lexical_scope.to_vec(),
10530 false,
10531 )?;
10532 values.push(cpp_push_comparable_node(
10533 &mut nodes,
10534 CppComparableNode::Named {
10535 name,
10536 primitive: true,
10537 konst: qualifiers.konst,
10538 volatil: qualifiers.volatil,
10539 },
10540 ));
10541 }
10542 "qualified_identifier"
10543 | "scoped_identifier"
10544 | "scoped_type_identifier"
10545 | "type_identifier"
10546 | "field_identifier"
10547 | "identifier"
10548 | "namespace_identifier"
10549 | "primitive_type"
10550 | "template_type" => {
10551 let name = cpp_structured_named_type(node, source, lexical_scope)?;
10552 values.push(cpp_push_comparable_node(
10553 &mut nodes,
10554 CppComparableNode::Named {
10555 name,
10556 primitive: node.kind() == "primitive_type",
10557 konst: qualifiers.konst,
10558 volatil: qualifiers.volatil,
10559 },
10560 ));
10561 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
10562 let mut cursor = arguments_node.walk();
10563 let arguments = arguments_node
10564 .named_children(&mut cursor)
10565 .filter(|child| !child.is_extra() && child.kind() != "comment")
10566 .collect::<Vec<_>>();
10567 work.push(Work::BuildGeneric {
10568 argument_count: arguments.len(),
10569 });
10570 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
10571 node: argument,
10572 qualifiers: CppCvQualifiers::default(),
10573 }));
10574 }
10575 }
10576 _ => {
10577 let inner = node.child_by_field_name("type").or_else(|| {
10578 (node.named_child_count() == 1)
10579 .then(|| node.named_child(0))
10580 .flatten()
10581 })?;
10582 work.push(Work::Visit {
10583 node: inner,
10584 qualifiers,
10585 });
10586 }
10587 },
10588 Work::ApplyLevels(levels) => {
10589 let mut root = values.pop()?;
10590 for level in levels {
10591 let node = match level {
10592 CppComparableLevel::Pointer { konst, volatil } => {
10593 CppComparableNode::Pointer {
10594 inner: root,
10595 konst,
10596 volatil,
10597 }
10598 }
10599 CppComparableLevel::Reference => {
10600 CppComparableNode::Reference { inner: root }
10601 }
10602 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
10603 };
10604 root = cpp_push_comparable_node(&mut nodes, node);
10605 }
10606 values.push(root);
10607 }
10608 Work::BuildGeneric { argument_count } => {
10609 let value_count = argument_count.checked_add(1)?;
10610 let start = values.len().checked_sub(value_count)?;
10611 let mut built = values.split_off(start);
10612 let base = built.remove(0);
10613 values.push(cpp_push_comparable_node(
10614 &mut nodes,
10615 CppComparableNode::Generic {
10616 base,
10617 arguments: built,
10618 },
10619 ));
10620 }
10621 }
10622 }
10623 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
10624 debug_assert_eq!(
10625 root,
10626 nodes.len().saturating_sub(1),
10627 "comparable nodes are appended in post-order, so the root is the last one"
10628 );
10629 Some(CppComparableParameter { nodes, root })
10630}
10631
10632fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
10633 nodes.push(node);
10634 nodes.len() - 1
10635}
10636
10637fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
10643 let mut current = node;
10644 loop {
10645 match current.kind() {
10646 "template_type" => return current.child_by_field_name("arguments"),
10647 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
10648 current = current.child_by_field_name("name")?;
10649 }
10650 _ => return None,
10651 }
10652 }
10653}
10654
10655pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
10661 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
10662 loop {
10663 if matches!(
10664 current.kind(),
10665 "declaration" | "field_declaration" | "function_definition"
10666 ) && let Some(declarator) = current
10667 .child_by_field_name("declarator")
10668 .and_then(extract_function_declarator)
10669 {
10670 return Some(declarator);
10671 }
10672 current = current.parent()?;
10673 }
10674}
10675
10676fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
10677 let mut labels = Vec::new();
10678 let mut cursor = parameters_node.walk();
10679 for child in parameters_node.children(&mut cursor) {
10680 match child.kind() {
10681 "parameter_declaration" | "optional_parameter_declaration" => {
10682 if let Some(name_node) = child
10683 .child_by_field_name("declarator")
10684 .and_then(cpp_declarator_label_node)
10685 {
10686 labels.push(name_node);
10687 } else {
10688 labels.push(child);
10689 }
10690 }
10691 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
10692 labels.push(child);
10693 }
10694 _ => {}
10695 }
10696 }
10697 labels
10698}
10699
10700fn cpp_signature_search_start<'tree>(
10701 signature: &str,
10702 function_declarator: Node<'tree>,
10703 source: &str,
10704 ancestry: &ParentIndex<'tree>,
10705) -> usize {
10706 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
10707 return 0;
10708 };
10709 let raw = node_text(enclosing, source);
10710 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
10711 let offset = function_declarator
10712 .start_byte()
10713 .saturating_sub(enclosing.start_byte())
10714 .saturating_sub(leading_trim_bytes);
10715 offset.min(signature.len())
10716}
10717
10718fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
10719 match node.kind() {
10720 "identifier" | "field_identifier" => Some(node),
10721 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
10722 .child_by_field_name("declarator")
10723 .or_else(|| last_named_child(node))
10724 .and_then(cpp_declarator_label_node),
10725 "array_declarator" => node
10726 .child_by_field_name("declarator")
10727 .and_then(cpp_declarator_label_node),
10728 "function_declarator" => node
10729 .child_by_field_name("declarator")
10730 .or_else(|| node.child_by_field_name("name"))
10731 .or_else(|| last_named_child(node))
10732 .and_then(cpp_declarator_label_node),
10733 _ => None,
10734 }
10735}
10736
10737fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
10738 let base_type = parameter
10739 .child_by_field_name("type")
10740 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
10741 .unwrap_or_default();
10742 let declarator = cpp_parameter_declarator(parameter);
10743 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
10750 let mut cursor = parameter.walk();
10751 let qualifiers = parameter
10752 .named_children(&mut cursor)
10753 .filter(|child| child.kind() == "type_qualifier")
10754 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
10755 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
10756 .collect::<Vec<_>>()
10757 .join(" ");
10758 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
10759 (true, _) => base_type,
10760 (_, true) => qualifiers,
10761 (false, false) => format!("{qualifiers} {base_type}"),
10762 };
10763 let declarator_suffix = declarator
10764 .map(|node| cpp_declarator_suffix_without_name(node, source))
10765 .unwrap_or_default();
10766
10767 let combined = if type_text.is_empty() {
10768 declarator_suffix
10769 } else if declarator_suffix.is_empty() {
10770 type_text
10771 } else {
10772 format!("{type_text} {declarator_suffix}")
10773 };
10774 normalize_cpp_type_text(&combined)
10775}
10776
10777fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
10778 parameter.child_by_field_name("declarator").or_else(|| {
10779 let mut cursor = parameter.walk();
10785 parameter
10786 .named_children(&mut cursor)
10787 .find(|child| is_cpp_abstract_declarator(child.kind()))
10788 })
10789}
10790
10791pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
10794 let mut current = Some(declarator);
10795 while let Some(node) = current {
10796 if matches!(
10797 node.kind(),
10798 "pointer_declarator"
10799 | "abstract_pointer_declarator"
10800 | "reference_declarator"
10801 | "abstract_reference_declarator"
10802 | "array_declarator"
10803 | "abstract_array_declarator"
10804 | "function_declarator"
10805 | "abstract_function_declarator"
10806 ) {
10807 return true;
10808 }
10809 current = cpp_nested_declarator(node);
10810 }
10811 false
10812}
10813
10814fn is_cpp_abstract_declarator(kind: &str) -> bool {
10815 matches!(
10816 kind,
10817 "abstract_pointer_declarator"
10818 | "abstract_reference_declarator"
10819 | "abstract_array_declarator"
10820 | "abstract_function_declarator"
10821 | "abstract_parenthesized_declarator"
10822 )
10823}
10824
10825fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
10826 node.child_by_field_name("declarator").or_else(|| {
10827 if is_cpp_abstract_declarator(node.kind()) {
10828 let mut cursor = node.walk();
10829 node.named_children(&mut cursor)
10830 .find(|child| is_cpp_abstract_declarator(child.kind()))
10831 } else {
10832 last_named_child(node)
10836 }
10837 })
10838}
10839
10840fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
10841 match node.kind() {
10842 "identifier" | "field_identifier" => String::new(),
10843 "pointer_declarator" | "abstract_pointer_declarator" => {
10844 let inner = cpp_nested_declarator(node)
10845 .map(|child| cpp_declarator_suffix_without_name(child, source))
10846 .unwrap_or_default();
10847 format!("*{inner}")
10848 }
10849 "reference_declarator" | "abstract_reference_declarator" => {
10850 let inner = cpp_nested_declarator(node)
10851 .map(|child| cpp_declarator_suffix_without_name(child, source))
10852 .unwrap_or_default();
10853 let reference = node
10854 .children(&mut node.walk())
10855 .find(|child| matches!(child.kind(), "&" | "&&"))
10856 .map(|child| node_text(child, source))
10857 .unwrap_or("&");
10858 format!("{reference}{inner}")
10859 }
10860 "array_declarator" | "abstract_array_declarator" => {
10861 let inner = cpp_nested_declarator(node)
10862 .map(|child| cpp_declarator_suffix_without_name(child, source))
10863 .unwrap_or_default();
10864 let size = node
10865 .child_by_field_name("size")
10866 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
10867 .unwrap_or_default();
10868 format!("{inner}[{size}]")
10869 }
10870 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
10871 let inner = cpp_nested_declarator(node);
10872 inner
10873 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
10874 .unwrap_or_default()
10875 }
10876 "function_declarator" | "abstract_function_declarator" => {
10877 let inner = cpp_nested_declarator(node)
10878 .map(|child| cpp_declarator_suffix_without_name(child, source))
10879 .unwrap_or_default();
10880 let params = node
10881 .child_by_field_name("parameters")
10882 .map(|child| cpp_parameter_signature(child, source))
10883 .unwrap_or_else(|| "()".to_string());
10884 format!("{inner}{params}")
10885 }
10886 _ => {
10887 let text = normalize_cpp_whitespace(node_text(node, source));
10888 let name = extract_declarator_name(node, source);
10889 if name.is_empty() {
10890 text
10891 } else {
10892 text.replace(&name, "").trim().to_string()
10893 }
10894 }
10895 }
10896}
10897
10898fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
10899 collapse_cpp_whitespace(
10900 suffix
10901 .trim()
10902 .trim_start_matches("->")
10903 .trim_start_matches('{')
10904 .trim_end_matches(';'),
10905 )
10906}
10907
10908pub fn normalize_cpp_whitespace(value: &str) -> String {
10909 collapse_cpp_whitespace(value)
10910}
10911
10912fn normalize_cpp_type_text(value: &str) -> String {
10913 collapse_cpp_whitespace(value)
10914 .replace(", ", ",")
10915 .replace(" <", "<")
10916 .replace("< ", "<")
10917 .replace(" >", ">")
10918}
10919
10920fn collapse_cpp_whitespace(value: &str) -> String {
10921 let mut result = String::new();
10922 let mut prev_space = false;
10923 for ch in value.chars() {
10924 if ch.is_whitespace() {
10925 if !prev_space {
10926 result.push(' ');
10927 }
10928 prev_space = true;
10929 } else {
10930 result.push(ch);
10931 prev_space = false;
10932 }
10933 }
10934 result.trim().to_string()
10935}
10936
10937pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
10938 node_source_text(node, source)
10939}
10940
10941pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
10942 walk_named_tree_preorder(node, true, |node| {
10943 match node.kind() {
10944 "type_identifier" | "identifier" | "qualified_identifier" => {
10945 let text = node_text(node, source).trim();
10946 if !text.is_empty() {
10947 identifiers.insert(text.to_string());
10948 }
10949 }
10950 _ => {}
10951 }
10952 WalkControl::Continue
10953 });
10954}
10955
10956fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
10957 node.child_by_field_name("body").or_else(|| {
10958 let mut cursor = node.walk();
10959 node.named_children(&mut cursor).find(|child| {
10960 matches!(
10961 child.kind(),
10962 "declaration_list" | "field_declaration_list" | "enumerator_list"
10963 )
10964 })
10965 })
10966}
10967
10968fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
10979 if !matches!(
10980 node.kind(),
10981 "class_specifier" | "struct_specifier" | "union_specifier"
10982 ) {
10983 return None;
10984 }
10985 let body = cpp_body_node(node)?;
10986 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
10987 return None;
10988 }
10989 let open = body.child(0)?;
10990 let close = body.child(body.child_count().checked_sub(1)?)?;
10991 if open.kind() != "{"
10992 || open.is_missing()
10993 || close.kind() != "}"
10994 || close.is_missing()
10995 || close.end_byte() != body.end_byte()
10996 || body.end_byte() > node.end_byte()
10997 || node
10998 .parent()
10999 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
11000 {
11001 return None;
11002 }
11003 Some(close)
11004}
11005
11006fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
11007 if node.kind() == "namespace_definition" {
11008 return true;
11009 }
11010 let mut cursor = node.walk();
11011 node.named_children(&mut cursor)
11012 .any(cpp_contains_namespace_definition)
11013}
11014
11015struct CppNestedNamespaceSentinel<'tree> {
11016 function: Node<'tree>,
11017 body: Node<'tree>,
11018 namespace_components: Vec<String>,
11019}
11020
11021#[derive(Debug, Clone)]
11031pub struct CppSentinelRecoveredOwner {
11032 pub range: Range,
11033 pub owner_name_start_byte: usize,
11037 pub namespace_component_count: usize,
11041 pub scope_components: Vec<String>,
11042}
11043
11044#[derive(Debug, Clone)]
11045pub struct CppSentinelRecoveredClass {
11046 pub namespace_range: Range,
11047 pub namespace_scope_components: Vec<String>,
11048 pub class_range: Range,
11049 pub scope_components: Vec<String>,
11051 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
11055}
11056
11057pub fn cpp_sentinel_recovered_scope_for_node(
11063 node: Node<'_>,
11064 source: &str,
11065 recovered_classes: &[CppSentinelRecoveredClass],
11066) -> Option<Vec<String>> {
11067 let contains =
11068 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
11069 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
11070 for recovered in recovered_classes {
11071 for owner in recovered
11072 .owner_ranges
11073 .iter()
11074 .filter(|owner| contains(owner.range))
11075 {
11076 let replace = best_owner.is_none_or(|existing| {
11077 owner.range.end_byte.saturating_sub(owner.range.start_byte)
11078 < existing
11079 .range
11080 .end_byte
11081 .saturating_sub(existing.range.start_byte)
11082 });
11083 if replace {
11084 best_owner = Some(owner);
11085 }
11086 }
11087 }
11088 if let Some(owner) = best_owner {
11089 let mut scope = owner.scope_components.clone();
11090 if node.start_byte() < owner.owner_name_start_byte {
11091 scope.truncate(owner.namespace_component_count);
11092 }
11093 return Some(scope);
11094 }
11095
11096 let class = recovered_classes
11097 .iter()
11098 .filter(|recovered| contains(recovered.class_range))
11099 .min_by_key(|recovered| {
11100 recovered
11101 .class_range
11102 .end_byte
11103 .saturating_sub(recovered.class_range.start_byte)
11104 });
11105 let class_scope = class.is_some();
11106 let mut scope = if let Some(class) = class {
11107 class.scope_components.clone()
11108 } else {
11109 let namespace = recovered_classes
11110 .iter()
11111 .filter(|recovered| contains(recovered.namespace_range))
11112 .min_by_key(|recovered| {
11113 recovered
11114 .namespace_range
11115 .end_byte
11116 .saturating_sub(recovered.namespace_range.start_byte)
11117 })?;
11118 let mut scope = namespace.namespace_scope_components.clone();
11119 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
11120 let common_prefix = scope
11121 .iter()
11122 .zip(&parser_namespace)
11123 .take_while(|(recovered, parser)| recovered == parser)
11124 .count();
11125 scope.extend(parser_namespace.into_iter().skip(common_prefix));
11126 scope
11127 };
11128 if class_scope {
11129 let mut ancestor_components = Vec::new();
11130 let mut ancestor = node.parent();
11131 while let Some(current) = ancestor {
11132 if matches!(
11133 current.kind(),
11134 "class_specifier" | "struct_specifier" | "union_specifier"
11135 ) && let Some(name) = current.child_by_field_name("name")
11136 && let Some(name_components) = cpp_name_components(name, source)
11137 {
11138 ancestor_components.push(
11139 name_components
11140 .into_iter()
11141 .map(|component| component.name)
11142 .collect::<Vec<_>>(),
11143 );
11144 }
11145 ancestor = current.parent();
11146 }
11147 ancestor_components.reverse();
11148 let base_len = scope.len();
11149 for component in ancestor_components.into_iter().flatten() {
11150 if scope.len() >= base_len && scope.last() == Some(&component) {
11151 continue;
11152 }
11153 scope.push(component);
11154 }
11155 }
11156 Some(scope)
11157}
11158
11159struct CppSentinelFragmentedClassTail<'tree> {
11160 class_node: Node<'tree>,
11161 template_node: Option<Node<'tree>>,
11162 name: String,
11163 raw_supertypes: Option<Vec<String>>,
11164 fragmented: FragmentedExportBody,
11165 consumed_start: usize,
11166}
11167
11168struct CppSentinelFragmentedClassErrorPrefix<'tree> {
11169 name: String,
11170 open: Node<'tree>,
11171 raw_supertypes: Option<Vec<String>>,
11172}
11173
11174struct CppSentinelDirectBodyClassRegion {
11175 namespace_components: Vec<String>,
11176 class_start: usize,
11177 class_start_line: usize,
11178 class_close_end: usize,
11179 class_close_line: usize,
11180 name: String,
11181}
11182
11183fn cpp_sentinel_body_class_candidate<'tree>(
11184 child: Node<'tree>,
11185) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
11186 if matches!(
11187 child.kind(),
11188 "class_specifier" | "struct_specifier" | "union_specifier"
11189 ) {
11190 return Some((child, None));
11191 }
11192 if child.kind() != "template_declaration" {
11193 if child.kind() == "declaration" {
11194 return Some((first_class_like_child(child)?, None));
11195 }
11196 return None;
11197 }
11198 let mut cursor = child.walk();
11199 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
11200 if matches!(
11201 candidate.kind(),
11202 "class_specifier" | "struct_specifier" | "union_specifier"
11203 ) {
11204 Some(candidate)
11205 } else if candidate.kind() == "declaration" {
11206 first_class_like_child(candidate)
11207 } else {
11208 None
11209 }
11210 })?;
11211 Some((class_node, Some(child)))
11212}
11213
11214fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
11220 node: Node<'tree>,
11221 source: &str,
11222) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
11223 let name = malformed_class_error_owner_name(node, source)?;
11224 let mut cursor = node.walk();
11225 let children = node.children(&mut cursor).collect::<Vec<_>>();
11226 let keyword = children.first()?;
11227 let open_index = children.iter().position(|child| child.kind() == "{")?;
11228 if children[open_index + 1..]
11229 .iter()
11230 .any(|child| child.kind() == "}")
11231 {
11232 return None;
11233 }
11234 let raw_supertypes =
11235 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
11236 Some(CppSentinelFragmentedClassErrorPrefix {
11237 name,
11238 open: children[open_index],
11239 raw_supertypes,
11240 })
11241}
11242
11243fn cpp_sentinel_direct_body_class_candidate<'tree>(
11244 child: Node<'tree>,
11245) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
11246 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
11247 return Some(candidate);
11248 }
11249 if child.kind() != "template_declaration" {
11250 return None;
11251 }
11252 let mut cursor = child.walk();
11253 let wrapper = child
11254 .named_children(&mut cursor)
11255 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
11256 Some((first_class_like_child(wrapper)?, Some(child)))
11257}
11258
11259fn cpp_sentinel_direct_namespace_components(
11260 function: Node<'_>,
11261 body: Node<'_>,
11262 source: &str,
11263) -> Option<Vec<String>> {
11264 let mut cursor = function.walk();
11265 let children = function
11266 .named_children(&mut cursor)
11267 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
11268 .collect::<Vec<_>>();
11269 let sentinel_index = children.iter().rposition(|child| {
11270 direct_identifier_name(*child, source)
11271 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
11272 })?;
11273 let mut identifiers = Vec::new();
11274 let mut stack = children[sentinel_index + 1..]
11275 .iter()
11276 .rev()
11277 .copied()
11278 .collect::<Vec<_>>();
11279 while let Some(current) = stack.pop() {
11280 if let Some(name) = direct_identifier_name(current, source) {
11281 identifiers.push(name);
11282 continue;
11283 }
11284 let mut cursor = current.walk();
11285 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
11286 stack.extend(children.into_iter().rev());
11287 }
11288 let [keyword, namespace] = identifiers.as_slice() else {
11289 return None;
11290 };
11291 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
11292 .then(|| vec![namespace.clone()])
11293}
11294
11295fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
11296 let mut sibling = class_semicolon.next_named_sibling();
11297 let namespace_close = loop {
11298 let Some(current) = sibling else {
11299 return false;
11300 };
11301 sibling = current.next_named_sibling();
11302 if current.kind() != "comment" {
11303 break current;
11304 }
11305 };
11306 if !cpp_is_stray_close_brace(namespace_close, source) {
11307 return false;
11308 }
11309 loop {
11310 let Some(current) = sibling else {
11311 return false;
11312 };
11313 sibling = current.next_named_sibling();
11314 if current.kind() == "comment" {
11315 continue;
11316 }
11317 return direct_identifier_name(current, source)
11318 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
11319 }
11320}
11321
11322fn cpp_sentinel_macro_body_class_region<'tree>(
11323 node: Node<'tree>,
11324 source: &str,
11325 ancestry: &ParentIndex<'tree>,
11326) -> Option<CppSentinelDirectBodyClassRegion> {
11327 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
11328 return None;
11329 };
11330 if node.kind() != "function_definition" || !node.has_error() {
11331 return None;
11332 }
11333 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
11334 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
11335 let mut cursor = body.walk();
11336 let candidates = body
11337 .named_children(&mut cursor)
11338 .filter_map(cpp_sentinel_direct_body_class_candidate)
11339 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
11340 .collect::<Vec<_>>();
11341 let [(class_node, template_node)] = candidates.as_slice() else {
11342 return None;
11343 };
11344 let original_body = cpp_body_node(*class_node)?;
11345 let name = class_like_name(*class_node, source, ancestry)?;
11346 if name.is_empty() || cpp_export_macro_token(&name) {
11347 return None;
11348 }
11349
11350 let mut sibling = node.next_named_sibling();
11351 let (class_close_start, class_close_end, class_close_line) = loop {
11352 let current = sibling?;
11353 let next = current.next_named_sibling();
11354 if cpp_is_stray_close_brace(current, source)
11355 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
11356 {
11357 let semicolon = next.expect("checked above");
11358 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
11359 return None;
11360 }
11361 break (
11362 current.start_byte(),
11363 semicolon.end_byte(),
11364 semicolon.end_position().row + 1,
11365 );
11366 }
11367 sibling = next;
11368 };
11369 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
11370 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
11371 let root = tree.root_node();
11372 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
11373 let reparsed_ancestry = ParentIndex::new(root);
11376 let reparsed =
11377 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
11378 if reparsed.name != name
11379 || reparsed.declaration_node.start_byte() != class_node.start_byte()
11380 || reparsed.body.start_byte() != original_body.start_byte()
11381 || class_close_start <= reparsed.body.end_byte()
11382 || class_close_end <= class_node.end_byte()
11383 {
11384 return None;
11385 }
11386 Some(CppSentinelDirectBodyClassRegion {
11387 namespace_components,
11388 class_start: reparse_start,
11389 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
11390 node.start_position().row + 1
11391 }),
11392 class_close_end,
11393 class_close_line,
11394 name,
11395 })
11396}
11397
11398fn cpp_nested_namespace_sentinel<'tree>(
11410 node: Node<'tree>,
11411 source: &str,
11412 ancestry: &ParentIndex<'tree>,
11413) -> Option<CppNestedNamespaceSentinel<'tree>> {
11414 if !node.has_error() {
11415 return None;
11416 }
11417
11418 let (function, mut namespace_components) = if node.kind() == "ERROR" {
11419 let mut cursor = node.walk();
11420 let functions = node
11421 .named_children(&mut cursor)
11422 .filter(|child| child.kind() == "function_definition")
11423 .collect::<Vec<_>>();
11424 let [function] = functions.as_slice() else {
11425 return None;
11426 };
11427 if !function.has_error() {
11428 return None;
11429 }
11430 let mut cursor = node.walk();
11431 let children = node.children(&mut cursor).collect::<Vec<_>>();
11432 let function_index = children
11433 .iter()
11434 .position(|child| same_node(*child, *function))?;
11435 let [outer_keyword, outer_name, outer_open] =
11436 children.get(function_index.checked_sub(3)?..function_index)?
11437 else {
11438 return None;
11439 };
11440 if outer_keyword.kind() != "namespace"
11441 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
11442 || outer_open.kind() != "{"
11443 {
11444 return None;
11445 }
11446 (
11447 *function,
11448 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
11449 )
11450 } else if node.kind() == "function_definition" {
11451 let declaration_list = node.parent()?;
11452 let namespace = declaration_list.parent()?;
11453 if declaration_list.kind() != "declaration_list"
11454 || namespace.kind() != "namespace_definition"
11455 || namespace.child_by_field_name("body") != Some(declaration_list)
11456 {
11457 return None;
11458 }
11459 (node, Vec::new())
11460 } else {
11461 return None;
11462 };
11463
11464 let mut cursor = function.walk();
11465 let named = function
11466 .named_children(&mut cursor)
11467 .filter(|child| child.kind() != "comment")
11468 .collect::<Vec<_>>();
11469 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
11470 return None;
11471 };
11472 if first_type.kind() != "type_identifier" {
11473 return None;
11474 }
11475 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
11476 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
11477 return None;
11478 }
11479 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
11480 return None;
11481 }
11482 let inner_keyword = inner_error.named_child(0)?;
11483 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
11484 return None;
11485 }
11486 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
11487 return None;
11488 }
11489 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
11490 if inner_name.is_empty() || body.kind() != "compound_statement" {
11491 return None;
11492 }
11493 namespace_components.push(inner_name);
11494
11495 let mut cursor = body.walk();
11496 let has_complete_class = body.named_children(&mut cursor).any(|child| {
11497 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
11498 cpp_body_node(class_node).is_some()
11499 && class_like_name(class_node, source, ancestry)
11500 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
11501 })
11502 });
11503 if !has_complete_class
11504 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
11505 {
11506 return None;
11507 }
11508
11509 Some(CppNestedNamespaceSentinel {
11510 function,
11511 body: *body,
11512 namespace_components,
11513 })
11514}
11515
11516fn cpp_root_namespace_sentinel<'tree>(
11525 node: Node<'tree>,
11526 source: &str,
11527 ancestry: &ParentIndex<'tree>,
11528) -> Option<CppNestedNamespaceSentinel<'tree>> {
11529 if node.kind() != "function_definition"
11530 || !node.has_error()
11531 || node.parent()?.kind() != "translation_unit"
11532 {
11533 return None;
11534 }
11535 let first_type = node.child_by_field_name("type")?;
11536 let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
11537 if first_type.kind() != "type_identifier"
11538 || sentinel.is_empty()
11539 || !cpp_export_macro_token(&sentinel)
11540 {
11541 return None;
11542 }
11543 let declarator = node.child_by_field_name("declarator")?;
11544 let body = node.child_by_field_name("body")?;
11545 if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
11546 return None;
11547 }
11548 let mut cursor = node.walk();
11549 let named = node
11550 .named_children(&mut cursor)
11551 .filter(|child| child.kind() != "comment")
11552 .collect::<Vec<_>>();
11553 let [named_type, named_declarator, named_body] = named.as_slice() else {
11554 return None;
11555 };
11556 if !same_node(*named_type, first_type)
11557 || !same_node(*named_declarator, declarator)
11558 || !same_node(*named_body, body)
11559 {
11560 return None;
11561 }
11562 let mut declarator_components = Vec::new();
11563 let mut valid_components = true;
11564 walk_named_tree_preorder(declarator, true, |component| {
11565 if !matches!(
11566 component.kind(),
11567 "identifier" | "namespace_identifier" | "type_identifier"
11568 ) {
11569 return WalkControl::Continue;
11570 }
11571 let Some(component) = canonical_cpp_qualified_component(component, source) else {
11572 valid_components = false;
11573 return WalkControl::Break;
11574 };
11575 declarator_components.push(component.name);
11576 WalkControl::SkipChildren
11577 });
11578 if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
11579 return None;
11580 }
11581 declarator_components.remove(0);
11582 let namespace_components = declarator_components;
11583 if namespace_components.is_empty()
11584 || namespace_components
11585 .iter()
11586 .any(|component| component.is_empty() || cpp_export_macro_token(component))
11587 {
11588 return None;
11589 }
11590
11591 let mut cursor = body.walk();
11592 let has_complete_class = body.named_children(&mut cursor).any(|child| {
11593 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
11594 cpp_body_node(class_node).is_some()
11595 && class_like_name(class_node, source, ancestry)
11596 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
11597 })
11598 });
11599 if !has_complete_class
11600 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
11601 {
11602 return None;
11603 }
11604
11605 Some(CppNestedNamespaceSentinel {
11606 function: node,
11607 body,
11608 namespace_components,
11609 })
11610}
11611
11612fn cpp_sentinel_fragmented_class_tail<'tree>(
11621 function: Node<'tree>,
11622 body: Node<'tree>,
11623 source: &str,
11624 ancestry: &ParentIndex<'tree>,
11625) -> Option<CppSentinelFragmentedClassTail<'tree>> {
11626 let mut cursor = body.walk();
11627 let candidates = body
11628 .named_children(&mut cursor)
11629 .filter_map(|child| {
11630 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
11631 let class_body = cpp_body_node(class_node)?;
11632 if !class_node.has_error() {
11633 return None;
11634 }
11635 let name = class_like_name(class_node, source, ancestry)?;
11636 let raw_supertypes =
11637 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
11638 .then(|| extract_cpp_supertypes(class_node, source));
11639 return Some((
11640 class_node,
11641 template_node,
11642 name,
11643 class_body,
11644 class_body.start_byte().checked_add(1)?,
11645 raw_supertypes,
11646 ));
11647 }
11648 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
11649 Some((
11650 child,
11651 None,
11652 prefix.name,
11653 prefix.open,
11654 prefix.open.end_byte(),
11655 prefix.raw_supertypes,
11656 ))
11657 })
11658 .collect::<Vec<_>>();
11659 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
11660 candidates.as_slice()
11661 else {
11662 return None;
11663 };
11664 if name.is_empty() || cpp_export_macro_token(name) {
11665 return None;
11666 }
11667
11668 let (close, semicolon) =
11669 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
11670
11671 let reparse_end = close.start_byte();
11672 if *reparse_start >= reparse_end {
11673 return None;
11674 }
11675 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
11676 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
11677 return None;
11678 }
11679 let class_range = Range {
11680 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
11681 end_byte: semicolon.end_byte(),
11682 start_line: template_node.map_or(class_node.start_position().row, |node| {
11683 node.start_position().row
11684 }) + 1,
11685 end_line: semicolon.end_position().row + 1,
11686 };
11687 Some(CppSentinelFragmentedClassTail {
11688 class_node: *class_node,
11689 template_node: *template_node,
11690 name: name.clone(),
11691 raw_supertypes: raw_supertypes.clone(),
11692 fragmented: FragmentedExportBody {
11693 reparse_start: *reparse_start,
11694 reparse_end,
11695 class_range,
11696 },
11697 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
11698 })
11699}
11700
11701pub fn cpp_sentinel_recovered_classes(
11710 root: Node<'_>,
11711 source: &str,
11712) -> Vec<CppSentinelRecoveredClass> {
11713 if !root.has_error() {
11714 return Vec::new();
11715 }
11716 let ancestry = ParentIndex::new(root);
11720 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
11721 let mut stack = vec![root];
11722 while let Some(current) = stack.pop() {
11723 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
11724 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
11725 {
11726 let namespace_components = cpp_sentinel_recovered_namespace_components(
11727 recovered.function,
11728 &recovered.namespace_components,
11729 source,
11730 );
11731 let fragmented = cpp_sentinel_fragmented_class_tail(
11732 recovered.function,
11733 recovered.body,
11734 source,
11735 &ancestry,
11736 );
11737 let mut class_candidates = Vec::new();
11738 let mut cursor = recovered.body.walk();
11739 for (class_node, template_node) in recovered
11740 .body
11741 .named_children(&mut cursor)
11742 .filter_map(cpp_sentinel_body_class_candidate)
11743 {
11744 let Some(name) = class_like_name(class_node, source, &ancestry) else {
11745 continue;
11746 };
11747 if name.is_empty() || cpp_export_macro_token(&name) {
11748 continue;
11749 }
11750 let is_fragmented = fragmented
11751 .as_ref()
11752 .is_some_and(|tail| same_node(tail.class_node, class_node));
11753 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
11754 continue;
11755 }
11756 let class_range = if is_fragmented {
11757 fragmented
11758 .as_ref()
11759 .map(|tail| tail.fragmented.class_range)
11760 .expect("fragmented class range is present when class matches")
11761 } else {
11762 cpp_declaration_range(template_node.unwrap_or(class_node))
11763 };
11764 class_candidates.push((class_range, name));
11765 }
11766 if let Some(fragmented) = fragmented
11767 .as_ref()
11768 .filter(|tail| tail.class_node.kind() == "ERROR")
11769 {
11770 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
11771 }
11772
11773 let mut owner_ranges =
11774 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
11775 cpp_sentinel_extend_unique_owner_ranges(
11776 &mut owner_ranges,
11777 cpp_sentinel_recovered_sibling_owner_ranges(
11778 recovered.function,
11779 &namespace_components,
11780 source,
11781 ),
11782 );
11783 for (class_range, name) in class_candidates {
11784 push_cpp_sentinel_recovered_class(
11785 &mut recovered_classes,
11786 cpp_declaration_range(recovered.body),
11787 &namespace_components,
11788 class_range,
11789 name,
11790 &owner_ranges,
11791 );
11792 }
11793
11794 if let Some(declaration_list) = recovered
11795 .function
11796 .parent()
11797 .filter(|parent| parent.kind() == "declaration_list")
11798 {
11799 let outer_namespace =
11800 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
11801 push_cpp_sentinel_sibling_classes(
11802 &mut recovered_classes,
11803 declaration_list,
11804 recovered.function,
11805 &outer_namespace,
11806 source,
11807 &ancestry,
11808 );
11809 }
11810 } else if let Some(region) =
11811 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
11812 {
11813 let namespace_components = cpp_sentinel_recovered_namespace_components(
11814 current,
11815 ®ion.namespace_components,
11816 source,
11817 );
11818 let owner_container = current
11819 .parent()
11820 .filter(|parent| parent.kind() == "declaration_list")
11821 .unwrap_or(current);
11822 let owner_ranges =
11823 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
11824 push_cpp_sentinel_recovered_class(
11825 &mut recovered_classes,
11826 cpp_declaration_range(owner_container),
11827 &namespace_components,
11828 Range {
11829 start_byte: region.class_start,
11830 end_byte: region.class_close_end,
11831 start_line: region.class_start_line,
11832 end_line: region.class_close_line,
11833 },
11834 region.name,
11835 &owner_ranges,
11836 );
11837 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
11838 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
11843 region;
11844 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
11845 continue;
11846 };
11847 let root = tree.root_node();
11848 let template_node = cpp_sentinel_reparsed_leading_template(root);
11849 let reparsed_ancestry = ParentIndex::new(root);
11851 let Some(reparsed_class) =
11852 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
11853 else {
11854 continue;
11855 };
11856 let class_node = reparsed_class.declaration_node;
11857 let name = reparsed_class.name;
11858 let namespace_components =
11859 cpp_sentinel_recovered_namespace_components(current, &[], source);
11860 let owner_container = current
11861 .parent()
11862 .filter(|parent| parent.kind() == "declaration_list")
11863 .unwrap_or(current);
11864 let mut owner_ranges =
11865 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
11866 cpp_sentinel_extend_unique_owner_ranges(
11867 &mut owner_ranges,
11868 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
11869 );
11870 push_cpp_sentinel_recovered_class(
11871 &mut recovered_classes,
11872 cpp_declaration_range(owner_container),
11873 &namespace_components,
11874 Range {
11875 start_byte: class_start,
11876 end_byte: close_end,
11877 start_line: class_node.start_position().row + 1,
11878 end_line: class_node.end_position().row + 1,
11879 },
11880 name,
11881 &owner_ranges,
11882 );
11883 if owner_container.kind() == "declaration_list" {
11884 push_cpp_sentinel_sibling_classes(
11885 &mut recovered_classes,
11886 owner_container,
11887 current,
11888 &namespace_components,
11889 source,
11890 &ancestry,
11891 );
11892 }
11893 }
11894
11895 let mut cursor = current.walk();
11896 stack.extend(current.named_children(&mut cursor));
11897 }
11898 let shadowed = recovered_classes
11904 .iter()
11905 .map(|candidate| {
11906 recovered_classes.iter().any(|container| {
11907 container.class_range.start_byte <= candidate.class_range.start_byte
11908 && container.class_range.end_byte >= candidate.class_range.end_byte
11909 && container.class_range != candidate.class_range
11910 && container.namespace_scope_components.len()
11911 > candidate.namespace_scope_components.len()
11912 && container
11913 .namespace_scope_components
11914 .starts_with(&candidate.namespace_scope_components)
11915 })
11916 })
11917 .collect::<Vec<_>>();
11918 let mut index = 0usize;
11919 recovered_classes.retain(|_| {
11920 let keep = !shadowed[index];
11921 index += 1;
11922 keep
11923 });
11924 recovered_classes
11925}
11926
11927fn push_cpp_sentinel_sibling_classes<'tree>(
11933 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
11934 declaration_list: Node<'tree>,
11935 sentinel_node: Node<'tree>,
11936 namespace_components: &[String],
11937 source: &str,
11938 ancestry: &ParentIndex<'tree>,
11939) {
11940 let owner_ranges =
11941 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
11942 let namespace_range = cpp_declaration_range(declaration_list);
11943 let mut cursor = declaration_list.walk();
11944 for (class_node, template_node) in declaration_list
11945 .named_children(&mut cursor)
11946 .filter(|child| !same_node(*child, sentinel_node))
11947 .filter_map(cpp_sentinel_body_class_candidate)
11948 {
11949 let Some(name) = class_like_name(class_node, source, ancestry) else {
11950 continue;
11951 };
11952 if name.is_empty()
11953 || cpp_export_macro_token(&name)
11954 || cpp_complete_class_body_close(class_node).is_none()
11955 {
11956 continue;
11957 }
11958 push_cpp_sentinel_recovered_class(
11959 recovered_classes,
11960 namespace_range,
11961 namespace_components,
11962 cpp_declaration_range(template_node.unwrap_or(class_node)),
11963 name,
11964 &owner_ranges,
11965 );
11966 }
11967}
11968
11969fn push_cpp_sentinel_recovered_class(
11970 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
11971 namespace_range: Range,
11972 namespace_components: &[String],
11973 class_range: Range,
11974 name: String,
11975 owner_ranges: &[CppSentinelRecoveredOwner],
11976) {
11977 let mut scope_components = namespace_components.to_vec();
11978 scope_components.push(name);
11979 let owner_ranges = owner_ranges
11980 .iter()
11981 .filter(|owner| owner.scope_components.starts_with(&scope_components))
11982 .cloned()
11983 .collect::<Vec<_>>();
11984 if recovered_classes.iter().any(|existing| {
11985 existing.class_range == class_range && existing.scope_components == scope_components
11986 }) {
11987 return;
11988 }
11989 recovered_classes.push(CppSentinelRecoveredClass {
11990 namespace_range,
11991 namespace_scope_components: namespace_components.to_vec(),
11992 class_range,
11993 scope_components,
11994 owner_ranges,
11995 });
11996}
11997
11998fn cpp_sentinel_recovered_namespace_components(
11999 function: Node<'_>,
12000 recovered_components: &[String],
12001 source: &str,
12002) -> Vec<String> {
12003 let mut ancestor_components = Vec::new();
12004 let mut ancestor = function.parent();
12005 while let Some(current) = ancestor {
12006 if current.kind() == "namespace_definition"
12007 && let Some(name_node) = current.child_by_field_name("name")
12008 && let Some(components) = cpp_name_components(name_node, source)
12009 {
12010 ancestor_components.push(
12011 components
12012 .into_iter()
12013 .map(|component| component.name)
12014 .collect::<Vec<_>>(),
12015 );
12016 }
12017 ancestor = current.parent();
12018 }
12019 ancestor_components.reverse();
12020 let mut ancestors = ancestor_components
12021 .into_iter()
12022 .flatten()
12023 .collect::<Vec<_>>();
12024
12025 let overlap = (0..=ancestors.len().min(recovered_components.len()))
12026 .rev()
12027 .find(|length| {
12028 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
12029 })
12030 .unwrap_or(0);
12031 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
12032 ancestors
12033}
12034
12035fn cpp_sentinel_recovered_owner_ranges(
12036 body: Node<'_>,
12037 namespace_components: &[String],
12038 source: &str,
12039) -> Vec<CppSentinelRecoveredOwner> {
12040 let mut owners = Vec::new();
12041 walk_named_tree_preorder(body, true, |node| {
12042 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
12043 });
12044 owners
12045}
12046
12047fn cpp_sentinel_collect_owner_range(
12048 node: Node<'_>,
12049 namespace_components: &[String],
12050 source: &str,
12051 owners: &mut Vec<CppSentinelRecoveredOwner>,
12052) -> WalkControl {
12053 if node.kind() != "function_definition" {
12054 return WalkControl::Continue;
12055 }
12056 let Some(function_declarator) = extract_function_declarator(node) else {
12057 return WalkControl::Continue;
12058 };
12059 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
12060 return WalkControl::Continue;
12061 };
12062 let Some(mut components) = cpp_name_components(name_node, source) else {
12063 return WalkControl::Continue;
12064 };
12065 if components.len() <= 1 {
12066 return WalkControl::Continue;
12067 }
12068 components.pop();
12069 let mut owner_components = components
12070 .into_iter()
12071 .map(|component| component.name)
12072 .collect::<Vec<_>>();
12073 let overlap = (0..=namespace_components.len().min(owner_components.len()))
12074 .rev()
12075 .find(|length| {
12076 owner_components[..*length]
12077 == namespace_components[namespace_components.len().saturating_sub(*length)..]
12078 })
12079 .unwrap_or(0);
12080 let mut scope_components = namespace_components.to_vec();
12081 scope_components.extend(owner_components.drain(overlap..));
12082 if scope_components.len() <= namespace_components.len() {
12083 return WalkControl::Continue;
12084 }
12085 let range = cpp_declaration_range(node);
12086 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
12087 existing.range == range && existing.scope_components == scope_components
12088 }) {
12089 owners.push(CppSentinelRecoveredOwner {
12090 range,
12091 owner_name_start_byte: name_node.start_byte(),
12092 namespace_component_count: namespace_components.len(),
12093 scope_components,
12094 });
12095 }
12096 WalkControl::Continue
12097}
12098
12099fn cpp_sentinel_extend_unique_owner_ranges(
12100 owners: &mut Vec<CppSentinelRecoveredOwner>,
12101 additional: Vec<CppSentinelRecoveredOwner>,
12102) {
12103 for owner in additional {
12104 if !owners.iter().any(|existing| {
12105 existing.range == owner.range && existing.scope_components == owner.scope_components
12106 }) {
12107 owners.push(owner);
12108 }
12109 }
12110}
12111
12112fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
12113 if node.kind() != "ERROR" || node.named_child_count() != 1 {
12114 return false;
12115 }
12116 let Some(end_name) = node.named_child(0) else {
12117 return false;
12118 };
12119 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
12120 return false;
12121 }
12122 let mut cursor = node.walk();
12123 node.children(&mut cursor)
12124 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
12125}
12126
12127fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
12131 parent: Node<'_>,
12132 sentinel_node: Node<'_>,
12133 namespace_components: &[String],
12134 source: &str,
12135) -> Vec<CppSentinelRecoveredOwner> {
12136 let mut owners = Vec::new();
12137 let mut after_sentinel = false;
12138 let mut cursor = parent.walk();
12139 for child in parent.named_children(&mut cursor) {
12140 if !after_sentinel {
12141 if same_node(child, sentinel_node) {
12142 after_sentinel = true;
12143 }
12144 continue;
12145 }
12146 walk_named_tree_preorder(child, true, |node| {
12147 if node.kind() == "namespace_definition" {
12148 return WalkControl::SkipChildren;
12149 }
12150 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
12151 });
12152 }
12153 owners
12154}
12155
12156fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
12160 parent: Node<'_>,
12161 sentinel_node: Node<'_>,
12162 namespace_components: &[String],
12163 source: &str,
12164) -> Option<Vec<CppSentinelRecoveredOwner>> {
12165 let mut owners = Vec::new();
12166 let mut after_namespace = false;
12167 let mut cursor = parent.walk();
12168 for child in parent.named_children(&mut cursor) {
12169 if !after_namespace {
12170 if same_node(child, sentinel_node) {
12171 after_namespace = true;
12172 }
12173 continue;
12174 }
12175 if cpp_sentinel_namespace_end(child, source) {
12176 return Some(owners);
12177 }
12178 walk_named_tree_preorder(child, true, |node| {
12179 if node.kind() == "namespace_definition" {
12180 return WalkControl::SkipChildren;
12181 }
12182 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
12183 });
12184 }
12185 None
12186}
12187
12188fn cpp_sentinel_recovered_sibling_owner_ranges(
12189 sentinel_node: Node<'_>,
12190 namespace_components: &[String],
12191 source: &str,
12192) -> Vec<CppSentinelRecoveredOwner> {
12193 let Some(declaration_list) = sentinel_node
12194 .parent()
12195 .filter(|parent| parent.kind() == "declaration_list")
12196 else {
12197 return Vec::new();
12198 };
12199 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
12200 declaration_list,
12201 sentinel_node,
12202 namespace_components,
12203 source,
12204 );
12205
12206 let Some(namespace) = declaration_list
12207 .parent()
12208 .filter(|parent| parent.kind() == "namespace_definition")
12209 else {
12210 return owners;
12211 };
12212 let Some(outer_parent) = namespace.parent() else {
12213 return owners;
12214 };
12215 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
12216 outer_parent,
12217 namespace,
12218 namespace_components,
12219 source,
12220 ) {
12221 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
12222 }
12223 owners
12224}
12225
12226fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
12227 let mut current = function_declarator.child_by_field_name("declarator")?;
12228 loop {
12229 if let Some(name) = macro_decorated_unqualified_name(current) {
12230 current = name;
12231 continue;
12232 }
12233 if matches!(
12234 current.kind(),
12235 "qualified_identifier"
12236 | "scoped_identifier"
12237 | "scoped_type_identifier"
12238 | "identifier"
12239 | "field_identifier"
12240 | "operator_name"
12241 | "destructor_name"
12242 | "literal_operator_name"
12243 ) {
12244 return Some(current);
12245 }
12246 current = current
12247 .child_by_field_name("declarator")
12248 .or_else(|| current.child_by_field_name("name"))
12249 .or_else(|| last_named_child(current))?;
12250 }
12251}
12252
12253fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
12266 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
12267 return None;
12268 }
12269 let mut cursor = node.walk();
12270 if node
12271 .children(&mut cursor)
12272 .any(|child| child.kind() == "::" && !child.is_missing())
12273 {
12274 return None;
12275 }
12276 node.child_by_field_name("name")
12277}
12278
12279fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
12280 if let Some(name) = macro_decorated_unqualified_name(node) {
12281 return cpp_name_components(name, source);
12282 }
12283 match node.kind() {
12284 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12285 let mut components = match node.child_by_field_name("scope") {
12286 Some(scope) => cpp_name_components(scope, source)?,
12287 None => Vec::new(),
12288 };
12289 let name = node.child_by_field_name("name")?;
12290 components.push(canonical_cpp_qualified_component(name, source)?);
12291 Some(components)
12292 }
12293 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
12294 }
12295}
12296
12297fn cpp_sentinel_fragment_boundary<'tree>(
12298 function: Node<'tree>,
12299 class_node: Node<'tree>,
12300 class_body: Node<'tree>,
12301 source: &str,
12302) -> Option<(Node<'tree>, Node<'tree>)> {
12303 let declaration_list = function.parent()?;
12304 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
12305 return None;
12306 }
12307 let namespace = declaration_list.parent()?;
12308 if namespace.kind() != "namespace_definition"
12309 || namespace.child_by_field_name("body") != Some(declaration_list)
12310 {
12311 return None;
12312 }
12313 let mut cursor = declaration_list.walk();
12314 let closes = declaration_list
12315 .children(&mut cursor)
12316 .filter(|child| {
12317 !child.is_named()
12318 && child.kind() == "}"
12319 && child.start_byte() >= function.end_byte()
12320 && child.start_byte() > class_node.end_byte()
12321 && child.start_byte() > class_body.start_byte()
12322 })
12323 .collect::<Vec<_>>();
12324 let [close] = closes.as_slice() else {
12325 return None;
12326 };
12327 let semicolon = namespace.next_named_sibling()?;
12328 if !cpp_is_stray_semicolon(semicolon, source)
12329 || close.end_byte() != namespace.end_byte()
12330 || semicolon.start_byte() < namespace.end_byte()
12331 {
12332 return None;
12333 }
12334 Some((*close, semicolon))
12335}
12336
12337fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
12363 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
12364 {
12365 return None;
12366 }
12367 let mut declarator_cursor = node.walk();
12373 let preserved_callable = node
12374 .children_by_field_name("declarator", &mut declarator_cursor)
12375 .find_map(extract_function_declarator);
12376 let mut cursor = node.walk();
12384 let first = node
12385 .named_children(&mut cursor)
12386 .find(|child| child.kind() != "comment")?;
12387 if first.kind() != "type_identifier" {
12388 return None;
12389 }
12390 let sentinel = normalize_cpp_whitespace(node_text(first, source));
12391 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
12392 return None;
12393 }
12394 let mut start = first.end_byte();
12401 let mut after_first = false;
12402 let mut cursor = node.walk();
12403 for child in node.named_children(&mut cursor) {
12404 if !after_first {
12405 if same_node(child, first) {
12406 after_first = true;
12407 }
12408 continue;
12409 }
12410 if matches!(child.kind(), "identifier" | "type_identifier")
12411 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
12412 {
12413 start = child.end_byte();
12414 } else {
12415 break;
12416 }
12417 }
12418 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
12427 let mut class_start = None;
12428 let mut template_start = None;
12429 let mut stack = vec![node];
12430 while let Some(current) = stack.pop() {
12431 if current.start_byte() >= prefix_end {
12432 continue;
12433 }
12434 if matches!(
12435 current.kind(),
12436 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
12437 ) {
12438 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
12439 "class" | "struct" | "union" | "enum" => {
12440 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
12441 seen.min(current.start_byte())
12442 }));
12443 }
12444 "template" => {
12445 template_start =
12446 Some(template_start.map_or(current.start_byte(), |seen: usize| {
12447 seen.min(current.start_byte())
12448 }));
12449 }
12450 _ => {}
12451 }
12452 }
12453 let mut cursor = current.walk();
12454 stack.extend(current.children(&mut cursor));
12455 }
12456 if preserved_callable.is_some_and(|callable| {
12457 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
12458 }) {
12459 return None;
12460 }
12461 if let Some(class_start) = class_start {
12462 start = template_start
12463 .filter(|template_start| *template_start < class_start)
12464 .unwrap_or(class_start);
12465 }
12466 Some((start, class_start))
12467}
12468
12469fn cpp_sentinel_macro_class_region<'tree>(
12475 node: Node<'tree>,
12476 source: &str,
12477) -> Option<(usize, usize, usize, usize, usize, usize)> {
12478 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
12479 return None;
12480 };
12481 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
12482 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
12483 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
12484 if class_start >= body_open_start {
12485 return None;
12486 }
12487 let sibling_close = {
12488 let mut sibling = node.next_named_sibling();
12489 let mut found = None;
12490 while let Some(current) = sibling {
12491 let next = current.next_named_sibling();
12492 if cpp_is_stray_close_brace(current, source)
12493 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
12494 {
12495 let semicolon = next.expect("checked above");
12496 found = Some((
12497 current.start_byte(),
12498 semicolon.end_byte(),
12499 semicolon.end_position().row + 1,
12500 ));
12501 break;
12502 }
12503 sibling = next;
12504 }
12505 found
12506 };
12507 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
12520 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
12521 return false;
12522 };
12523 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
12524 let reparsed_ancestry = ParentIndex::new(tree.root_node());
12526 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
12527 tree.root_node(),
12528 template_node,
12529 source,
12530 &reparsed_ancestry,
12531 ) else {
12532 return false;
12533 };
12534 let body = reparsed_class.body;
12535 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
12536 });
12537 let (class_close_start, class_close_end, class_close_line) =
12538 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
12539 (class_close_start, class_close_end, class_close_line)
12540 } else {
12541 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
12548 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
12549 let reparsed_ancestry = ParentIndex::new(tree.root_node());
12551 let reparsed_class = cpp_sentinel_reparsed_class(
12552 tree.root_node(),
12553 template_node,
12554 source,
12555 &reparsed_ancestry,
12556 )?;
12557 let body = reparsed_class.body;
12558 let class_close_end = body.end_byte();
12559 let class_close_start = class_close_end.checked_sub(1)?;
12560 let class_close_line = body.end_position().row + 1;
12561 (class_close_start, class_close_end, class_close_line)
12562 };
12563 if class_close_start <= class_start {
12564 return None;
12565 }
12566
12567 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
12571 let class_root = tree.root_node();
12572 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
12573 let reparsed_ancestry = ParentIndex::new(class_root);
12575 let reparsed_class =
12576 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
12577 let body = reparsed_class.body;
12578 if body.start_byte() != body_open_start {
12582 return None;
12583 }
12584 let body_start = body.start_byte().checked_add(1)?;
12585 (body_start < class_close_start).then_some((
12586 reparse_start,
12587 class_start,
12588 body_start,
12589 class_close_start,
12590 class_close_end,
12591 class_close_line,
12592 ))
12593}
12594
12595fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
12600 let mut stack = vec![node];
12601 while let Some(current) = stack.pop() {
12602 if current.start_byte() == class_start
12603 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
12604 {
12605 let mut sibling = current.next_sibling();
12606 while let Some(candidate) = sibling {
12607 if candidate.kind() == "{" {
12608 return Some(candidate.start_byte());
12609 }
12610 sibling = candidate.next_sibling();
12611 }
12612 }
12613 let mut cursor = current.walk();
12614 stack.extend(current.children(&mut cursor));
12615 }
12616 None
12617}
12618
12619fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
12630 node.next_named_sibling()
12631 .filter(|sibling| sibling.kind() == "compound_statement")
12632}
12633
12634fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
12635 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
12636 let mut end = if class_start.is_some() {
12637 cpp_macro_prefixed_class_end(source, start)?
12638 } else {
12639 node.end_byte()
12640 };
12641 if class_start.is_none()
12642 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
12643 {
12644 end = end.max(namespace_end);
12645 }
12646 let mut sibling = node.next_named_sibling();
12647 while let Some(current) = sibling {
12648 if !cpp_is_stray_semicolon(current, source) {
12649 break;
12650 }
12651 end = current.end_byte();
12652 sibling = current.next_named_sibling();
12653 }
12654 (start < end).then_some((start, end))
12655}
12656
12657fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
12668 let mut sibling = node.next_sibling();
12669 let keyword = loop {
12670 let candidate = sibling?;
12671 sibling = candidate.next_sibling();
12672 if candidate.kind() != "comment" {
12673 break candidate;
12674 }
12675 };
12676 if keyword.kind() != "namespace" {
12677 return None;
12678 }
12679 let name = loop {
12680 let candidate = sibling?;
12681 sibling = candidate.next_sibling();
12682 if candidate.kind() != "comment" {
12683 break candidate;
12684 }
12685 };
12686 if cpp_namespace_name_components(name, source).is_empty() {
12687 return None;
12688 }
12689 let open = loop {
12690 let candidate = sibling?;
12691 sibling = candidate.next_sibling();
12692 if candidate.kind() != "comment" {
12693 break candidate;
12694 }
12695 };
12696 if open.kind() != "{" {
12697 return None;
12698 }
12699
12700 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
12701 let root = tree.root_node();
12702 let mut cursor = root.walk();
12703 let namespace = root
12704 .named_children(&mut cursor)
12705 .find(|candidate| candidate.kind() != "comment")?;
12706 (namespace.kind() == "namespace_definition"
12707 && namespace.start_byte() == keyword.start_byte()
12708 && namespace.child_by_field_name("body").is_some())
12709 .then_some(namespace.end_byte())
12710}
12711
12712fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
12718 let tree = cpp_reparse_region_items(source, start, source.len())?;
12719 let root = tree.root_node();
12720 let mut cursor = root.walk();
12721 for item in root.named_children(&mut cursor) {
12722 if item.end_byte() <= start || item.kind() == "comment" {
12723 continue;
12724 }
12725 let mut stack = vec![item];
12726 while let Some(current) = stack.pop() {
12727 if matches!(
12728 current.kind(),
12729 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
12730 ) && cpp_body_node(current).is_some()
12731 {
12732 return Some(current.end_byte());
12733 }
12734 let mut cursor = current.walk();
12735 stack.extend(current.named_children(&mut cursor));
12736 }
12737 return None;
12741 }
12742 None
12743}
12744
12745fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
12748 node.kind() == "expression_statement"
12749 && node.named_child_count() == 0
12750 && node_text(node, source).trim() == ";"
12751}
12752
12753fn recovered_macro_qualified_field_declarators<'tree>(
12762 node: Node<'tree>,
12763 source: &str,
12764) -> Option<Vec<Node<'tree>>> {
12765 if node.kind() != "field_declaration" {
12766 return None;
12767 }
12768 let macro_type = node.child_by_field_name("type")?;
12769 if macro_type.kind() != "type_identifier"
12770 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12771 {
12772 return None;
12773 }
12774 let pseudo_declarator = node.child_by_field_name("declarator")?;
12775 if pseudo_declarator.kind() != "field_identifier" {
12776 return None;
12777 }
12778 let mut cursor = node.walk();
12779 let clause = node
12780 .named_children(&mut cursor)
12781 .find(|child| child.kind() == "bitfield_clause")?;
12782 if !(0..clause.named_child_count()).any(|index| {
12783 clause
12784 .named_child(index)
12785 .is_some_and(|child| child.kind() == "ERROR")
12786 }) {
12787 return None;
12788 }
12789 let mut recovered = Vec::new();
12790 let mut stack = vec![clause];
12791 while let Some(current) = stack.pop() {
12792 if current.kind() == "assignment_expression"
12793 && let Some(left) = current.child_by_field_name("left")
12794 && extract_variable_name(left, source).is_some()
12795 {
12796 recovered.push(left);
12797 break;
12798 }
12799 let mut cursor = current.walk();
12800 stack.extend(current.named_children(&mut cursor));
12801 }
12802 if recovered.is_empty() {
12803 return None;
12804 }
12805 let mut cursor = node.walk();
12806 recovered.extend(
12807 node.children_by_field_name("declarator", &mut cursor)
12808 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
12809 );
12810 Some(recovered)
12811}
12812
12813fn recovered_macro_qualified_constructor_call<'tree>(
12819 node: Node<'tree>,
12820 class_name: &str,
12821 source: &str,
12822) -> Option<Node<'tree>> {
12823 if node.kind() != "field_declaration" {
12824 return None;
12825 }
12826 let macro_type = node.child_by_field_name("type")?;
12827 if macro_type.kind() != "type_identifier"
12828 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12829 {
12830 return None;
12831 }
12832 let mut cursor = node.walk();
12833 let bitfield = node
12834 .named_children(&mut cursor)
12835 .find(|child| child.kind() == "bitfield_clause")?;
12836 let error = bitfield
12837 .named_child(0)
12838 .filter(|child| child.kind() == "ERROR")?;
12839 let mut stack = vec![error];
12840 while let Some(current) = stack.pop() {
12841 if current.kind() == "call_expression"
12842 && current
12843 .child_by_field_name("function")
12844 .is_some_and(|function| node_text(function, source) == class_name)
12845 && current
12846 .child_by_field_name("arguments")
12847 .is_some_and(|arguments| arguments.kind() == "argument_list")
12848 {
12849 return Some(current);
12850 }
12851 let mut cursor = current.walk();
12852 stack.extend(current.named_children(&mut cursor));
12853 }
12854 None
12855}
12856
12857fn recovered_macro_qualified_function_call<'tree>(
12865 node: Node<'tree>,
12866 source: &str,
12867) -> Option<Node<'tree>> {
12868 if node.kind() != "field_declaration" {
12869 return None;
12870 }
12871 let macro_type = node.child_by_field_name("type")?;
12872 if macro_type.kind() != "type_identifier"
12873 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12874 {
12875 return None;
12876 }
12877 let declarator = node.child_by_field_name("declarator")?;
12878 if declarator.kind() != "field_identifier" {
12879 return None;
12880 }
12881 let mut cursor = node.walk();
12882 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
12883 if !named.iter().any(|child| {
12884 child.kind() == "storage_class_specifier"
12885 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
12886 }) {
12887 return None;
12888 }
12889 let bitfield = named
12890 .iter()
12891 .find(|child| child.kind() == "bitfield_clause")?;
12892 let mut bitfield_cursor = bitfield.walk();
12893 let payload = bitfield
12894 .named_children(&mut bitfield_cursor)
12895 .collect::<Vec<_>>();
12896 let [displaced_error, call] = payload.as_slice() else {
12897 return None;
12898 };
12899 if displaced_error.kind() != "ERROR"
12900 || displaced_error.named_child_count() != 1
12901 || displaced_error
12902 .named_child(0)
12903 .is_none_or(|child| child.kind() != "identifier")
12904 || call.kind() != "call_expression"
12905 || call
12906 .child_by_field_name("function")
12907 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
12908 || call
12909 .child_by_field_name("arguments")
12910 .is_none_or(|arguments| arguments.kind() != "argument_list")
12911 {
12912 return None;
12913 }
12914 Some(*call)
12915}
12916
12917fn recovered_macro_qualified_function_parameters(
12918 arguments: Node<'_>,
12919 source: &str,
12920) -> Option<(String, Vec<String>)> {
12921 if arguments.kind() != "argument_list" {
12922 return None;
12923 }
12924 let mut cursor = arguments.walk();
12925 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
12926 if named.is_empty() {
12927 return Some(("()".to_string(), Vec::new()));
12928 }
12929 let mut types = Vec::new();
12930 let mut labels = Vec::new();
12931 let mut index = 0;
12932 while index < named.len() {
12933 let parameter_type = named[index];
12934 let parameter_name = named.get(index + 1).copied()?;
12935 if !matches!(
12936 parameter_type.kind(),
12937 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
12938 ) || parameter_name.kind() != "ERROR"
12939 || parameter_name.named_child_count() != 1
12940 || parameter_name
12941 .named_child(0)
12942 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
12943 {
12944 return None;
12945 }
12946 let parameter_name = parameter_name.named_child(0)?;
12947 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
12948 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
12949 index += 2;
12950 }
12951 Some((format!("({})", types.join(", ")), labels))
12952}
12953
12954pub fn recovered_macro_return_type_node<'tree>(
12966 node: Node<'tree>,
12967 source: &str,
12968) -> Option<Node<'tree>> {
12969 if node.kind() != "field_declaration" {
12970 return None;
12971 }
12972 let macro_type = node.child_by_field_name("type")?;
12973 if macro_type.kind() != "type_identifier"
12974 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12975 {
12976 return None;
12977 }
12978 let declarator = node.child_by_field_name("declarator")?;
12979 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
12980 return None;
12981 }
12982 let mut has_missing_semicolon = false;
12983 let mut has_real_semicolon = false;
12984 for index in 0..node.child_count() {
12985 let Some(child) = node.child(index) else {
12986 continue;
12987 };
12988 if child.kind() != ";" {
12989 continue;
12990 }
12991 if child.is_missing() {
12992 has_missing_semicolon = true;
12993 } else {
12994 has_real_semicolon = true;
12995 }
12996 }
12997 if !has_missing_semicolon || has_real_semicolon {
12998 return None;
12999 }
13000 let mut next = node.next_named_sibling();
13001 while next.is_some_and(|sibling| sibling.kind() == "comment") {
13002 next = next.and_then(|sibling| sibling.next_named_sibling());
13003 }
13004 let next = next?;
13005 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
13006 return None;
13007 }
13008 let function_declarator = next.child_by_field_name("declarator")?;
13009 extract_function_declarator(function_declarator).map(|_| declarator)
13010}
13011
13012pub(crate) fn cpp_active_template_type_parameter<'tree>(
13019 node: Node<'tree>,
13020 name: &str,
13021 source: &str,
13022 ancestry: &ParentIndex<'tree>,
13023) -> bool {
13024 let mut ancestor = ancestry.parent(node);
13025 while let Some(current) = ancestor {
13026 if current.kind() == "template_declaration"
13027 && let Some(parameters) = current.child_by_field_name("parameters")
13028 {
13029 let mut cursor = parameters.walk();
13030 if parameters.named_children(&mut cursor).any(|parameter| {
13031 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
13032 && cpp_template_parameter_name(parameter, source)
13033 .is_some_and(|parameter_name| parameter_name == name)
13034 }) {
13035 return true;
13036 }
13037 }
13038 ancestor = ancestry.parent(current);
13039 }
13040 false
13041}
13042
13043fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
13049 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
13050}
13051
13052fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
13053 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
13054 return None;
13055 }
13056 let semicolon = node.next_sibling()?;
13057 if semicolon.kind() != ";" || semicolon.is_missing() {
13058 return None;
13059 }
13060 let row = node.start_position().row;
13061 let mut start = node.start_byte();
13062 let mut sibling = node.prev_sibling();
13063 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
13064 if previous.kind() == ";" {
13065 break;
13066 }
13067 start = previous.start_byte();
13068 sibling = previous.prev_sibling();
13069 }
13070 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
13071}
13072
13073fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
13074 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
13075 return false;
13076 }
13077 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
13078 return false;
13079 }
13080 let Some(declarator) = (if node.kind() == "function_definition" {
13081 node.child_by_field_name("declarator")
13082 .and_then(extract_function_declarator)
13083 } else {
13084 node.named_child(0)
13085 .filter(|child| child.kind() == "function_declarator")
13086 }) else {
13087 return false;
13088 };
13089 let Some(name) = cpp_function_declarator_name_node(declarator) else {
13090 return false;
13091 };
13092 declarator.start_byte() == node.start_byte()
13093 && name.kind() == "identifier"
13094 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
13095}
13096
13097fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
13110 let region = cpp_reparse_region_items(source, start, end);
13111
13112 #[cfg(debug_assertions)]
13113 assert_eq!(
13114 region.as_ref().map(cpp_tree_shape),
13115 cpp_reparse_padded_class_body(source, start, end)
13116 .as_ref()
13117 .map(cpp_tree_shape),
13118 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
13119 prefix produces"
13120 );
13121
13122 region
13123}
13124
13125#[cfg(any(debug_assertions, test))]
13129fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
13130 if start >= end {
13131 return None;
13135 }
13136 let bytes = source.as_bytes();
13137 let prefix = bytes.get(..start)?;
13138 let interior = bytes.get(start..end)?;
13139 let mut padded = Vec::with_capacity(end);
13140 padded.extend(
13141 prefix
13142 .iter()
13143 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
13144 );
13145 padded.extend_from_slice(interior);
13146 let padded = String::from_utf8(padded).ok()?;
13147 let mut parser = Parser::new();
13148 parser
13149 .set_language(&tree_sitter_cpp::LANGUAGE.into())
13150 .ok()?;
13151 parser.parse(&padded, None)
13152}
13153
13154#[cfg(any(debug_assertions, test))]
13158fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
13159 let mut shape = Vec::new();
13160 let mut cursor = tree.root_node().walk();
13161 let mut stack = vec![tree.root_node()];
13162 while let Some(node) = stack.pop() {
13163 shape.push((
13164 node.kind(),
13165 node.start_byte(),
13166 node.end_byte(),
13167 node.start_position().row,
13168 node.start_position().column,
13169 node.is_named(),
13170 node.is_missing(),
13171 ));
13172 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
13173 stack.extend(children.into_iter().rev());
13174 }
13175 shape
13176}
13177
13178fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
13199 let mut cursor = root.walk();
13200 let mut saw_item = false;
13201 for child in root.named_children(&mut cursor) {
13202 match child.kind() {
13203 "comment" => {}
13204 "function_definition" => {
13205 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
13206 return false;
13207 }
13208 saw_item = true;
13209 }
13210 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
13211 _ => return false,
13212 }
13213 }
13214 saw_item
13215}
13216
13217fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
13227 if node.kind() != "ERROR" {
13228 return false;
13229 }
13230 let mut stack = Vec::new();
13231 let mut saw_function_declarator = false;
13232 let mut cursor = node.walk();
13233 for child in node.named_children(&mut cursor) {
13234 stack.push(child);
13235 }
13236 while let Some(current) = stack.pop() {
13237 match current.kind() {
13238 "ERROR" => {
13242 let mut cursor = current.walk();
13243 stack.extend(current.named_children(&mut cursor));
13244 }
13245 "function_declarator" => saw_function_declarator = true,
13246 _ => return false,
13247 }
13248 }
13249 saw_function_declarator
13250}
13251
13252fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
13253 if node.kind() != "ERROR" {
13254 return false;
13255 }
13256 let mut cursor = node.walk();
13257 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
13258 let [explicit, constructor_error, destructor] = named.as_slice() else {
13259 return false;
13260 };
13261 let Some(constructor) = constructor_error.named_child(0) else {
13262 return false;
13263 };
13264 let Some(constructor_name) =
13265 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
13266 else {
13267 return false;
13268 };
13269 let Some(destructor_name) =
13270 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
13271 else {
13272 return false;
13273 };
13274 let Some(destroyed_type) = destructor_name.named_child(0) else {
13275 return false;
13276 };
13277 explicit.kind() == "explicit_function_specifier"
13278 && constructor_error.kind() == "ERROR"
13279 && constructor_error.named_child_count() == 1
13280 && constructor.kind() == "function_declarator"
13281 && constructor_name.kind() == "identifier"
13282 && destructor.kind() == "function_declarator"
13283 && destructor_name.kind() == "destructor_name"
13284 && destroyed_type.kind() == "identifier"
13285 && node_text(constructor_name, source) == node_text(destroyed_type, source)
13286}
13287
13288fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
13289 if node.kind() != "compound_statement" {
13290 return false;
13291 }
13292 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
13293 return false;
13294 };
13295 if prefix.kind() == "labeled_statement"
13296 && prefix.named_child(0).is_some_and(|label| {
13297 matches!(
13298 node_text(label, source).trim(),
13299 "public" | "private" | "protected"
13300 )
13301 })
13302 {
13303 return prefix.named_children(&mut prefix.walk()).any(|child| {
13304 child.kind() == "declaration"
13305 && child.has_error()
13306 && child
13307 .named_children(&mut child.walk())
13308 .any(cpp_reparsed_member_error_is_indexable)
13309 });
13310 }
13311 prefix.kind() == "declaration"
13316 && prefix.has_error()
13317 && prefix
13318 .named_children(&mut prefix.walk())
13319 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
13320}
13321
13322fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
13323 if !cpp_reparsed_member_error_is_indexable(node) {
13324 return false;
13325 }
13326 let Some(preproc) = node.next_named_sibling() else {
13327 return false;
13328 };
13329 preproc.kind() == "preproc_if"
13330 && preproc.has_error()
13331 && preproc
13332 .named_children(&mut preproc.walk())
13333 .any(|child| child.kind() == "expression_statement" && child.has_error())
13334 && preproc
13335 .next_named_sibling()
13336 .is_some_and(|body| body.kind() == "compound_statement")
13337}
13338
13339fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
13344 if node.kind() != "function_definition" {
13345 return None;
13346 }
13347 let body = node.child_by_field_name("body")?;
13348 if body.kind() != "compound_statement" {
13349 return None;
13350 }
13351 let open = body.child(0)?;
13352 let close = body.child(body.child_count().checked_sub(1)?)?;
13353 if open.kind() != "{"
13354 || open.is_missing()
13355 || close.kind() != "}"
13356 || close.is_missing()
13357 || close.end_byte() != body.end_byte()
13358 || body.end_byte() != node.end_byte()
13359 {
13360 return None;
13361 }
13362 Some(body)
13363}
13364
13365fn cpp_reparsed_member_function_errors_are_in_body(
13366 node: Node<'_>,
13367 body: Node<'_>,
13368 source: &str,
13369) -> bool {
13370 let mut cursor = node.walk();
13371 node.children(&mut cursor).all(|child| {
13372 same_node(child, body)
13373 || cpp_reparsed_member_attribute_error(child, source)
13374 || cpp_reparsed_member_signature_identifier_errors(child)
13375 || (!child.has_error() && !child.is_error() && !child.is_missing())
13376 })
13377}
13378
13379fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
13387 if !node.has_error() && !node.is_error() && !node.is_missing() {
13388 return false;
13389 }
13390 let mut stack = vec![node];
13391 let mut saw_error = false;
13392 while let Some(current) = stack.pop() {
13393 if current.is_missing() {
13394 return false;
13395 }
13396 if current.kind() == "ERROR" {
13397 saw_error = true;
13398 let mut cursor = current.walk();
13399 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
13400 if children
13401 .iter()
13402 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
13403 {
13404 return false;
13405 }
13406 stack.extend(children);
13407 continue;
13408 }
13409 let mut cursor = current.walk();
13410 stack.extend(current.children(&mut cursor));
13411 }
13412 saw_error
13413}
13414
13415fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
13416 node.kind() == "ERROR"
13417 && node.named_child_count() == 1
13418 && node.named_child(0).is_some_and(|attribute| {
13419 attribute.kind() == "identifier"
13420 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
13421 })
13422}
13423
13424fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
13430 let Some(body) = cpp_reparsed_member_function_body(node) else {
13431 return false;
13432 };
13433 let mut cursor = node.walk();
13434 let named = node
13435 .named_children(&mut cursor)
13436 .filter(|child| child.kind() != "comment")
13437 .collect::<Vec<_>>();
13438 let [type_node, error, attribute, body_node] = named.as_slice() else {
13439 return false;
13440 };
13441 if !same_node(*body_node, body)
13442 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
13443 || attribute.kind() != "identifier"
13444 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
13445 || error.kind() != "ERROR"
13446 || error.named_child_count() != 1
13447 {
13448 return false;
13449 }
13450 error
13451 .named_child(0)
13452 .is_some_and(cpp_reparsed_attribute_callable_declarator)
13453}
13454
13455fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
13456 cpp_structured_type_path(node, source).is_some()
13457 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
13458}
13459
13460fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
13461 let Some(body) = cpp_reparsed_member_function_body(node) else {
13462 return false;
13463 };
13464 let mut cursor = node.walk();
13465 let named = node
13466 .named_children(&mut cursor)
13467 .filter(|child| child.kind() != "comment")
13468 .collect::<Vec<_>>();
13469 let [friend, return_error, declarator, body_node] = named.as_slice() else {
13470 return false;
13471 };
13472 let Some(return_type) = return_error.named_child(0) else {
13473 return false;
13474 };
13475 same_node(*body_node, body)
13476 && friend.kind() == "type_identifier"
13477 && node_text(*friend, source) == "friend"
13478 && return_error.kind() == "ERROR"
13479 && return_error.named_child_count() == 1
13480 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
13481 && extract_function_declarator(*declarator)
13482 .and_then(cpp_function_declarator_name_node)
13483 .is_some()
13484}
13485
13486fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
13487 let Some(body) = cpp_reparsed_member_function_body(node) else {
13488 return false;
13489 };
13490 let mut cursor = node.walk();
13491 let named = node
13492 .named_children(&mut cursor)
13493 .filter(|child| child.kind() != "comment")
13494 .collect::<Vec<_>>();
13495 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
13496 return false;
13497 };
13498 let Some(return_type) = return_error.named_child(0) else {
13499 return false;
13500 };
13501 same_node(*body_node, body)
13502 && prefix
13503 .iter()
13504 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
13505 && attribute.kind() == "type_identifier"
13506 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
13507 && return_error.kind() == "ERROR"
13508 && return_error.named_child_count() == 1
13509 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
13510 && extract_function_declarator(*declarator)
13511 .and_then(cpp_function_declarator_name_node)
13512 .is_some()
13513}
13514
13515fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
13521 let Some(body) = cpp_reparsed_member_function_body(node) else {
13522 return false;
13523 };
13524 let mut cursor = node.walk();
13525 let named = node
13526 .named_children(&mut cursor)
13527 .filter(|child| child.kind() != "comment")
13528 .collect::<Vec<_>>();
13529 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
13530 return false;
13531 };
13532 let Some(template_name) = template_type.child_by_field_name("name") else {
13533 return false;
13534 };
13535 let Some(arguments) = template_type.child_by_field_name("arguments") else {
13536 return false;
13537 };
13538 let Some(return_type) = return_error.named_child(0) else {
13539 return false;
13540 };
13541 let mut cursor = template_type.walk();
13542 let template_errors = template_type
13543 .named_children(&mut cursor)
13544 .filter(|child| child.kind() == "ERROR")
13545 .collect::<Vec<_>>();
13546 let [comment_error] = template_errors.as_slice() else {
13547 return false;
13548 };
13549 let mut cursor = comment_error.walk();
13550 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
13551 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
13552 return false;
13553 };
13554 same_node(*body_node, body)
13555 && template_type.kind() == "template_type"
13556 && template_name.kind() == "type_identifier"
13557 && matches!(
13558 node_text(template_name, source).trim(),
13559 "public" | "private" | "protected"
13560 )
13561 && arguments.kind() == "template_argument_list"
13562 && arguments.named_child_count() > 0
13563 && !arguments.has_error()
13564 && !colon.is_named()
13565 && colon.kind() == ":"
13566 && comments.iter().all(|child| child.kind() == "comment")
13567 && !template_keyword.is_named()
13568 && template_keyword.kind() == "template"
13569 && return_error.kind() == "ERROR"
13570 && return_error.named_child_count() == 1
13571 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
13572 && extract_function_declarator(*declarator)
13573 .and_then(cpp_function_declarator_name_node)
13574 .is_some()
13575}
13576
13577fn cpp_reparsed_preprocessor_constructor<'tree>(
13583 node: Node<'tree>,
13584 class_name: &str,
13585 source: &str,
13586) -> Option<Node<'tree>> {
13587 if node.kind() != "labeled_statement" {
13588 return None;
13589 }
13590 let mut cursor = node.walk();
13591 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
13592 let [label, directive_error, declaration] = named.as_slice() else {
13593 return None;
13594 };
13595 if label.kind() != "statement_identifier"
13596 || !matches!(
13597 node_text(*label, source),
13598 "public" | "private" | "protected"
13599 )
13600 || directive_error.kind() != "ERROR"
13601 || directive_error.child_count() != 1
13602 || directive_error
13603 .child(0)
13604 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
13605 || declaration.kind() != "declaration"
13606 || declaration.named_child_count() != 2
13607 {
13608 return None;
13609 }
13610 let apparent_type = declaration.child_by_field_name("type")?;
13611 if apparent_type.kind() != "type_identifier"
13612 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
13613 {
13614 return None;
13615 }
13616 let declarator = declaration.child_by_field_name("declarator")?;
13617 let function = extract_function_declarator(declarator)?;
13618 let name = cpp_function_declarator_name_node(function)?;
13619 (node_text(name, source) == class_name).then_some(*declaration)
13620}
13621
13622fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
13623 if extract_function_declarator(node)
13624 .and_then(cpp_function_declarator_name_node)
13625 .is_some()
13626 {
13627 return true;
13628 }
13629 node.kind() == "init_declarator"
13630 && node
13631 .child_by_field_name("declarator")
13632 .is_some_and(|declarator| declarator.kind() == "identifier")
13633 && node
13634 .child_by_field_name("value")
13635 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
13636}
13637
13638fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
13643 if node.kind() != "ERROR" || node.named_child_count() != 3 {
13644 return false;
13645 }
13646 let mut cursor = node.walk();
13647 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
13648 let [type_node, function_declarator, attribute] = named.as_slice() else {
13649 return false;
13650 };
13651 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
13652 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
13653 || attribute.kind() != "identifier"
13654 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
13655 {
13656 return false;
13657 }
13658 let Some(preproc) =
13659 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
13660 else {
13661 return false;
13662 };
13663 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
13664 .filter(|sibling| sibling.kind() == "compound_statement")
13665 else {
13666 return false;
13667 };
13668 let Some(open) = body.child(0) else {
13669 return false;
13670 };
13671 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
13672 return false;
13673 };
13674 let Some(condition) = preproc.child_by_field_name("condition") else {
13675 return false;
13676 };
13677 let mut cursor = preproc.walk();
13678 let payload = preproc
13679 .named_children(&mut cursor)
13680 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
13681 .collect::<Vec<_>>();
13682 let [requires_statement] = payload.as_slice() else {
13683 return false;
13684 };
13685 let requires_clause = requires_statement.named_child(0);
13686
13687 open.kind() == "{"
13688 && !open.is_missing()
13689 && close.kind() == "}"
13690 && !close.is_missing()
13691 && close.end_byte() == body.end_byte()
13692 && requires_statement.kind() == "expression_statement"
13693 && requires_statement.named_child_count() == 1
13694 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
13695}
13696
13697fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
13698 let mut sibling = node.next_named_sibling();
13699 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
13700 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
13701 }
13702 sibling
13703}
13704
13705fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
13706 let mut sibling = node.prev_named_sibling();
13707 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
13708 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
13709 }
13710 sibling
13711}
13712
13713fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
13714 let Some(preproc) =
13715 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
13716 else {
13717 return false;
13718 };
13719 let Some(error) =
13720 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
13721 else {
13722 return false;
13723 };
13724 cpp_reparsed_attribute_requires_error(error, source)
13725}
13726
13727fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
13728 node: Node<'tree>,
13729 source: &str,
13730) -> Option<Node<'tree>> {
13731 if node.kind() != "ERROR" {
13732 return None;
13733 }
13734 let mut cursor = node.walk();
13735 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
13736 let [parameter, macro_name, message] = named.as_slice() else {
13737 return None;
13738 };
13739 let parameter_name = parameter.named_child(0)?;
13740 (parameter.kind() == "type_parameter_declaration"
13741 && parameter_name.kind() == "type_identifier"
13742 && macro_name.kind() == "type_identifier"
13743 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
13744 && message.kind() == "string_literal")
13745 .then_some(parameter_name)
13746}
13747
13748fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
13753 node: Node<'tree>,
13754 source: &str,
13755) -> Option<Node<'tree>> {
13756 if node.kind() != "ERROR" {
13757 return None;
13758 }
13759 let mut cursor = node.walk();
13760 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
13761 let [parameter, macro_name, message, constraint] = named.as_slice() else {
13762 return None;
13763 };
13764 let parameter_name = parameter.named_child(0)?;
13765 let constraint_scope = constraint.child_by_field_name("scope")?;
13766 let constraint_template = constraint.child_by_field_name("name")?;
13767 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
13768 let mut argument_cursor = constraint_arguments.walk();
13769 let constraint_types = constraint_arguments
13770 .named_children(&mut argument_cursor)
13771 .collect::<Vec<_>>();
13772 if parameter.kind() != "type_parameter_declaration"
13773 || parameter_name.kind() != "type_identifier"
13774 || macro_name.kind() != "type_identifier"
13775 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
13776 || message.kind() != "string_literal"
13777 || constraint.kind() != "qualified_identifier"
13778 || constraint_scope.kind() != "namespace_identifier"
13779 || !matches!(
13780 constraint_template.kind(),
13781 "template_function" | "template_type"
13782 )
13783 || !matches!(constraint_types.as_slice(), [left, right]
13784 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
13785 || constraint_arguments.has_error()
13786 {
13787 return None;
13788 }
13789 let parameter_text = node_text(parameter_name, source);
13790 let mut stack = constraint_types;
13791 while let Some(current) = stack.pop() {
13792 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
13793 return Some(parameter_name);
13794 }
13795 let mut cursor = current.walk();
13796 stack.extend(current.named_children(&mut cursor));
13797 }
13798 None
13799}
13800
13801fn cpp_reparsed_template_macro_companion_is_indexable(
13802 node: Node<'_>,
13803 parameter_name: Node<'_>,
13804 source: &str,
13805) -> bool {
13806 let Some(body) = cpp_reparsed_member_function_body(node) else {
13807 return false;
13808 };
13809 let mut cursor = node.walk();
13810 let named = node
13811 .named_children(&mut cursor)
13812 .filter(|child| child.kind() != "comment")
13813 .collect::<Vec<_>>();
13814 let [
13815 constraint,
13816 close_error,
13817 storage,
13818 return_error,
13819 declarator,
13820 body_node,
13821 ] = named.as_slice()
13822 else {
13823 return false;
13824 };
13825 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
13826 return false;
13827 };
13828 let Some(constraint_template) = constraint.child_by_field_name("name") else {
13829 return false;
13830 };
13831 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
13832 return false;
13833 };
13834 let Some(return_type) = return_error.named_child(0) else {
13835 return false;
13836 };
13837 let mut cursor = constraint_arguments.walk();
13838 let constraint_types = constraint_arguments
13839 .named_children(&mut cursor)
13840 .collect::<Vec<_>>();
13841 same_node(*body_node, body)
13842 && constraint.kind() == "qualified_identifier"
13843 && constraint_scope.kind() == "namespace_identifier"
13844 && constraint_template.kind() == "template_type"
13845 && matches!(constraint_types.as_slice(), [left, right]
13846 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
13847 && !constraint_arguments.has_error()
13848 && close_error.kind() == "ERROR"
13849 && close_error.named_child_count() == 0
13850 && storage.kind() == "storage_class_specifier"
13851 && return_error.kind() == "ERROR"
13852 && return_error.named_child_count() == 1
13853 && return_type.kind() == "identifier"
13854 && node_text(return_type, source) == node_text(parameter_name, source)
13855 && extract_function_declarator(*declarator)
13856 .and_then(cpp_function_declarator_name_node)
13857 .is_some()
13858}
13859
13860fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
13861 node: Node<'tree>,
13862 parameter_name: Node<'_>,
13863 source: &str,
13864) -> Option<Node<'tree>> {
13865 let body = cpp_reparsed_member_function_body(node)?;
13866 let constraint = node.child_by_field_name("type")?;
13867 let constraint_template = constraint.child_by_field_name("name")?;
13868 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
13869 let mut argument_cursor = constraint_arguments.walk();
13870 let constraint_types = constraint_arguments
13871 .named_children(&mut argument_cursor)
13872 .collect::<Vec<_>>();
13873 if constraint.kind() != "qualified_identifier"
13874 || constraint_template.kind() != "template_type"
13875 || !matches!(constraint_types.as_slice(), [left, right]
13876 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
13877 || constraint_arguments.has_error()
13878 || node
13879 .child_by_field_name("body")
13880 .is_none_or(|candidate| !same_node(candidate, body))
13881 {
13882 return None;
13883 }
13884
13885 let mut cursor = node.walk();
13886 let recovery_errors = node
13887 .named_children(&mut cursor)
13888 .filter(|child| child.kind() == "ERROR")
13889 .collect::<Vec<_>>();
13890 if !recovery_errors
13891 .iter()
13892 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
13893 || !recovery_errors.iter().all(|error| {
13894 error.named_child_count() == 0
13895 || cpp_reparsed_constraint_macro_error(*error, source)
13896 || (error.named_child_count() == 1
13897 && error
13898 .named_child(0)
13899 .is_some_and(|child| child.kind() == "function_declarator"))
13900 })
13901 {
13902 return None;
13903 }
13904
13905 let parameter_text = node_text(parameter_name, source);
13906 let mut declarators = node
13907 .child_by_field_name("declarator")
13908 .and_then(extract_function_declarator)
13909 .into_iter()
13910 .collect::<Vec<_>>();
13911 for error in recovery_errors {
13912 let mut stack = vec![error];
13913 while let Some(current) = stack.pop() {
13914 if current.kind() == "function_declarator" {
13915 declarators.push(current);
13916 }
13917 let mut cursor = current.walk();
13918 stack.extend(current.named_children(&mut cursor));
13919 }
13920 }
13921 declarators.into_iter().find(|declarator| {
13922 cpp_function_declarator_name_node(*declarator)
13923 .is_some_and(|name| name.kind() == "identifier")
13924 && declarator
13925 .child_by_field_name("parameters")
13926 .is_some_and(|parameters| {
13927 parameters
13928 .named_children(&mut parameters.walk())
13929 .filter_map(|parameter| parameter.child_by_field_name("type"))
13930 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
13931 })
13932 })
13933}
13934
13935fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
13936 node: Node<'_>,
13937 parameter_name: Node<'_>,
13938 source: &str,
13939) -> bool {
13940 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
13941}
13942
13943fn cpp_reparsed_template_macro_function_companion_is_indexable(
13944 node: Node<'_>,
13945 parameter_name: Node<'_>,
13946 source: &str,
13947) -> bool {
13948 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
13949 return false;
13950 }
13951 let Some(return_type) = node.child_by_field_name("type") else {
13952 return false;
13953 };
13954 let Some(function_declarator) = node
13955 .child_by_field_name("declarator")
13956 .and_then(extract_function_declarator)
13957 else {
13958 return false;
13959 };
13960 if cpp_function_declarator_name_node(function_declarator).is_none()
13961 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
13962 {
13963 return false;
13964 }
13965 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
13966 return false;
13967 };
13968 let parameter_text = node_text(parameter_name, source);
13969 parameters
13970 .named_children(&mut parameters.walk())
13971 .any(|parameter| {
13972 parameter
13973 .child_by_field_name("type")
13974 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
13975 })
13976}
13977
13978fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
13979 if node.kind() != "ERROR" {
13980 return false;
13981 }
13982 let mut stack = vec![node];
13983 while let Some(current) = stack.pop() {
13984 let macro_shape = match current.kind() {
13985 "call_expression" => current
13986 .child_by_field_name("function")
13987 .zip(current.child_by_field_name("arguments")),
13988 "init_declarator" => current
13989 .child_by_field_name("declarator")
13990 .zip(current.child_by_field_name("value")),
13991 _ => None,
13992 };
13993 if let Some((name, arguments)) = macro_shape
13994 && name.kind() == "identifier"
13995 && arguments.kind() == "argument_list"
13996 && arguments.named_child_count() >= 2
13997 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
13998 {
13999 return true;
14000 }
14001 let mut cursor = current.walk();
14002 stack.extend(current.named_children(&mut cursor));
14003 }
14004 false
14005}
14006
14007fn cpp_recovered_template_macro_constructor<'tree>(
14008 node: Node<'tree>,
14009 source: &str,
14010) -> Option<(Node<'tree>, Node<'tree>)> {
14011 let mut prefix = node.prev_named_sibling()?;
14012 while prefix.kind() == "comment" {
14013 prefix = prefix.prev_named_sibling()?;
14014 }
14015 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
14016 let parameter = parameter_name
14017 .parent()
14018 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
14019 let declarator =
14020 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
14021 Some((declarator, parameter))
14022}
14023
14024fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
14025 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
14026 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
14027 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
14028 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
14029 function,
14030 parameter_name,
14031 source,
14032 )
14033 });
14034 }
14035 let Some(parameter_name) =
14036 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
14037 else {
14038 return false;
14039 };
14040 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
14041 cpp_reparsed_template_macro_function_companion_is_indexable(
14042 function,
14043 parameter_name,
14044 source,
14045 )
14046 })
14047}
14048
14049fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
14050 let function_name = node
14051 .child_by_field_name("declarator")
14052 .and_then(extract_function_declarator)
14053 .and_then(cpp_function_declarator_name_node);
14054 if let Some(body) = cpp_reparsed_member_function_body(node)
14055 && function_name.is_some()
14056 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
14057 {
14058 return true;
14059 }
14060 cpp_reparsed_attribute_member_function(node, source)
14061 || cpp_reparsed_friend_function_is_indexable(node, source)
14062 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
14063 || cpp_reparsed_access_template_function_is_indexable(node, source)
14064 || cpp_recovered_template_macro_constructor(node, source).is_some()
14065}
14066
14067fn cpp_reparsed_macro_attribute_member_sequence(
14074 children: &[Node<'_>],
14075 index: usize,
14076 source: &str,
14077) -> bool {
14078 let Some(prefix) = children.get(index).copied() else {
14079 return false;
14080 };
14081 let declaration = if prefix.kind() == "labeled_statement" {
14082 prefix
14083 .named_child(prefix.named_child_count().saturating_sub(1))
14084 .filter(|child| child.kind() == "declaration")
14085 } else {
14086 (prefix.kind() == "declaration").then_some(prefix)
14087 };
14088 let Some(declaration) = declaration else {
14089 return false;
14090 };
14091 if !declaration.has_error()
14092 || declaration
14093 .child_by_field_name("declarator")
14094 .and_then(extract_function_declarator)
14095 .and_then(cpp_function_declarator_name_node)
14096 .is_none()
14097 {
14098 return false;
14099 }
14100 let Some(attribute_statement) = children.get(index + 1).copied() else {
14101 return false;
14102 };
14103 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
14104 .then(|| attribute_statement.named_child(0))
14105 .flatten()
14106 .filter(|child| child.kind() == "call_expression")
14107 else {
14108 return false;
14109 };
14110 let Some(attribute_name) = attribute_call
14111 .child_by_field_name("function")
14112 .filter(|function| function.kind() == "identifier")
14113 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
14114 else {
14115 return false;
14116 };
14117 if !cpp_export_macro_token(&attribute_name) {
14118 return false;
14119 }
14120 let Some(body) = children.get(index + 2).copied() else {
14121 return false;
14122 };
14123 body.kind() == "compound_statement"
14124 && body.child(0).is_some_and(|open| open.kind() == "{")
14125 && body
14126 .child(body.child_count().saturating_sub(1))
14127 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
14128 && declaration.end_byte() <= attribute_statement.start_byte()
14129 && attribute_statement.end_byte() <= body.start_byte()
14130}
14131
14132fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
14140 if node.kind() != "ERROR" {
14141 return false;
14142 }
14143 let run = stranded_declaration_run(node, source);
14144 run.complete && !run.declarations.is_empty()
14145}
14146
14147fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
14148 let mut cursor = root.walk();
14149 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
14150 let mut saw_member = false;
14151 let mut index = 0;
14152 while index < children.len() {
14153 let child = children[index];
14154 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
14155 saw_member = true;
14156 index += 3;
14157 continue;
14158 }
14159 if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
14160 let Some(tree) = cpp_reparse_fragmented_class_body(
14161 source,
14162 fragmented.reparse_start,
14163 fragmented.reparse_end,
14164 ) else {
14165 return false;
14166 };
14167 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
14168 return false;
14169 }
14170 saw_member = true;
14171 index += 1;
14172 while index < children.len()
14173 && children[index].end_byte() <= fragmented.class_range.end_byte
14174 {
14175 index += 1;
14176 }
14177 continue;
14178 }
14179 match child.kind() {
14180 "comment" => {}
14181 "labeled_statement" => saw_member = true,
14182 "function_definition" => {
14183 if child.has_error()
14184 && !cpp_reparsed_member_function_is_indexable(child, source)
14185 && cpp_sentinel_macro_region(child, source).is_none()
14186 {
14187 return false;
14188 }
14189 saw_member = true;
14190 }
14191 "expression_statement" if is_string_attribute_macro_statement(child) => {}
14195 "ERROR"
14196 if (cpp_reparsed_member_error_is_indexable(child)
14197 || cpp_reparsed_adjacent_copy_control_error(child, source)
14198 || cpp_reparsed_stranded_member_error(child, source))
14199 && (child
14200 .next_named_sibling()
14201 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
14202 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
14203 {
14204 saw_member = true;
14205 }
14206 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
14207 saw_member = true;
14208 }
14209 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
14210 saw_member = true;
14211 }
14212 "expression_statement"
14213 if cpp_is_stray_semicolon(child, source)
14214 && child.prev_named_sibling().is_some_and(|error| {
14215 cpp_reparsed_member_error_is_indexable(error)
14216 || cpp_reparsed_adjacent_copy_control_error(error, source)
14217 || cpp_reparsed_stranded_member_error(error, source)
14218 }) =>
14219 {
14220 saw_member = true;
14221 }
14222 "compound_statement"
14223 if cpp_reparsed_constructor_body_is_indexable(child, source)
14224 || cpp_reparsed_attribute_requires_body(child, source) =>
14225 {
14226 saw_member = true;
14227 }
14228 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
14229 _ => return false,
14230 }
14231 index += 1;
14232 }
14233 saw_member
14234}
14235
14236fn cpp_reparsed_synthetic_initializer_constructor_range(
14244 root: Node<'_>,
14245 class_name: &str,
14246 source: &str,
14247 constructor_end: usize,
14248) -> Option<std::ops::Range<usize>> {
14249 let mut stack = {
14250 let mut cursor = root.walk();
14251 root.named_children(&mut cursor).collect::<Vec<_>>()
14252 };
14253 while let Some(current) = stack.pop() {
14254 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
14255 current,
14256 class_name,
14257 source,
14258 constructor_end,
14259 ) {
14260 return Some(range);
14261 }
14262 if current.kind() == "ERROR" {
14263 let mut cursor = current.walk();
14264 stack.extend(current.named_children(&mut cursor));
14265 }
14266 }
14267 None
14268}
14269
14270fn cpp_reparsed_merged_inline_constructor<'tree>(
14277 root: Node<'tree>,
14278 class_name: &str,
14279 source: &str,
14280) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
14281 let mut stack = vec![root];
14282 while let Some(current) = stack.pop() {
14283 if current.kind() != "labeled_statement" {
14284 let mut cursor = current.walk();
14285 stack.extend(current.named_children(&mut cursor));
14286 continue;
14287 }
14288 let declaration = current
14289 .named_children(&mut current.walk())
14290 .find(|child| child.kind() == "declaration")?;
14291 if declaration
14292 .child_by_field_name("type")
14293 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
14294 {
14295 continue;
14296 }
14297 let following = declaration
14298 .child_by_field_name("declarator")
14299 .and_then(extract_function_declarator)
14300 .and_then(cpp_function_declarator_name_node);
14301 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
14302 continue;
14303 }
14304 let mut declaration_cursor = declaration.walk();
14305 let Some(error) = declaration
14306 .named_children(&mut declaration_cursor)
14307 .find(|child| child.kind() == "ERROR")
14308 else {
14309 continue;
14310 };
14311 let mut error_cursor = error.walk();
14312 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
14313 let Some(constructor) = error_children.iter().copied().find(|child| {
14314 child.kind() == "function_declarator"
14315 && cpp_function_declarator_name_node(*child)
14316 .is_some_and(|name| node_text(name, source).trim() == class_name)
14317 }) else {
14318 continue;
14319 };
14320 let Some(body) = error_children.iter().copied().find_map(|child| {
14321 (child.kind() == "init_declarator")
14322 .then(|| child.child_by_field_name("value"))
14323 .flatten()
14324 .filter(|value| value.kind() == "initializer_list")
14325 }) else {
14326 continue;
14327 };
14328 if constructor.end_byte() > body.start_byte() {
14329 continue;
14330 }
14331 return Some((constructor.start_byte()..body.end_byte(), body));
14332 }
14333 None
14334}
14335
14336fn cpp_reparsed_synthetic_initializer_constructor(
14337 node: Node<'_>,
14338 class_name: &str,
14339 source: &str,
14340 constructor_end: usize,
14341) -> Option<std::ops::Range<usize>> {
14342 if node.kind() != "labeled_statement" {
14343 return None;
14344 }
14345 let mut cursor = node.walk();
14346 let named = node
14347 .named_children(&mut cursor)
14348 .filter(|child| child.kind() != "comment")
14349 .collect::<Vec<_>>();
14350 let label = named.first()?;
14351 if label.kind() != "statement_identifier"
14352 || !matches!(
14353 node_text(*label, source).trim(),
14354 "public" | "private" | "protected"
14355 )
14356 {
14357 return None;
14358 }
14359 let call_error_index = named.iter().position(|child| {
14360 if child.kind() != "ERROR" {
14361 return false;
14362 }
14363 let mut stack = vec![*child];
14364 while let Some(current) = stack.pop() {
14365 if current.kind() == "call_expression"
14366 && current
14367 .child_by_field_name("function")
14368 .is_some_and(|function| {
14369 function.kind() == "identifier"
14370 && node_text(function, source).trim() == class_name
14371 })
14372 {
14373 return true;
14374 }
14375 let mut cursor = current.walk();
14376 stack.extend(current.named_children(&mut cursor));
14377 }
14378 false
14379 })?;
14380 let constructor_call = {
14381 let mut stack = vec![named[call_error_index]];
14382 let mut found = None;
14383 while let Some(current) = stack.pop() {
14384 if current.kind() == "call_expression"
14385 && current
14386 .child_by_field_name("function")
14387 .is_some_and(|function| {
14388 function.kind() == "identifier"
14389 && node_text(function, source).trim() == class_name
14390 })
14391 {
14392 found = Some(current);
14393 break;
14394 }
14395 let mut cursor = current.walk();
14396 stack.extend(current.named_children(&mut cursor));
14397 }
14398 found
14399 };
14400 let constructor_call = constructor_call?;
14401 named.iter().skip(call_error_index + 1).find(|child| {
14402 child.kind() == "declaration" && child.has_error() && {
14403 let mut cursor = child.walk();
14404 child.named_children(&mut cursor).any(|declarator| {
14405 declarator.kind() == "init_declarator"
14406 && declarator
14407 .child_by_field_name("declarator")
14408 .is_some_and(|declarator| declarator.kind() == "function_declarator")
14409 && declarator
14410 .child_by_field_name("value")
14411 .is_some_and(|value| value.kind() == "initializer_list")
14412 })
14413 }
14414 })?;
14415 Some(constructor_call.start_byte()..constructor_end)
14416}
14417
14418fn cpp_reparsed_exact_constructor_declarator<'tree>(
14419 root: Node<'tree>,
14420 start: usize,
14421 class_name: &str,
14422 source: &str,
14423) -> Option<Node<'tree>> {
14424 let mut candidate = None;
14425 let mut stack = vec![root];
14426 while let Some(current) = stack.pop() {
14427 if current.kind() == "function_declarator"
14428 && current.start_byte() == start
14429 && cpp_function_declarator_name_node(current)
14430 .is_some_and(|name| node_text(name, source).trim() == class_name)
14431 {
14432 if candidate.is_some() {
14433 return None;
14434 }
14435 candidate = Some(current);
14436 continue;
14437 }
14438 let mut cursor = current.walk();
14439 stack.extend(current.named_children(&mut cursor));
14440 }
14441 candidate
14442}
14443
14444fn cpp_is_indexable_item_kind(kind: &str) -> bool {
14445 matches!(
14446 kind,
14447 "namespace_definition"
14448 | "class_specifier"
14449 | "struct_specifier"
14450 | "union_specifier"
14451 | "enum_specifier"
14452 | "function_definition"
14453 | "template_declaration"
14454 | "declaration"
14455 | "field_declaration"
14456 | "alias_declaration"
14457 | "static_assert_declaration"
14458 | "type_definition"
14459 | "using_declaration"
14460 | "linkage_specification"
14461 | "preproc_def"
14462 | "preproc_function_def"
14463 | "preproc_include"
14464 | "preproc_if"
14465 | "preproc_ifdef"
14466 | "preproc_call"
14467 )
14468}
14469
14470#[cfg(test)]
14471mod tests {
14472 use super::*;
14473 use crate::adapter::parse_cpp_file;
14474 use brokk_bifrost_core::analyzer::parsed_file::{
14475 finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
14476 start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
14477 };
14478 use std::fmt::Write;
14479
14480 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
14481 let mut parser = tree_sitter::Parser::new();
14482 parser
14483 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14484 .unwrap();
14485 let tree = parser.parse(source, None).unwrap();
14486 let file = ProjectFile::new(std::env::temp_dir(), name);
14487 parse_cpp_file(&file, source, &tree)
14488 }
14489
14490 #[test]
14491 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
14492 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
14493 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
14494 let mut macros = parsed
14495 .declarations()
14496 .iter()
14497 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
14498 .collect::<Vec<_>>();
14499 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
14500
14501 assert_eq!(macros.len(), 2, "{macros:#?}");
14502 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
14503 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
14504 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
14505 assert_eq!(
14506 parsed.declaration_ranges(macros[1])[0].start_byte,
14507 source.rfind("#define VALUE 2").expect("second definition")
14508 );
14509 }
14510
14511 #[test]
14512 fn identifies_export_macro_class_base_displaced_into_declarator() {
14513 let source = r#"#define PROJECT_API_
14514namespace project {
14515namespace internal {
14516template <typename T>
14517class Base {};
14518}
14519template <typename T>
14520class Wrapper;
14521template <>
14522class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
14523}
14524"#;
14525 let mut parser = tree_sitter::Parser::new();
14526 parser
14527 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14528 .unwrap();
14529 let tree = parser.parse(source, None).unwrap();
14530 let start = source.find("internal::Base<int>").expect("base");
14531 let mut base = tree
14532 .root_node()
14533 .descendant_for_byte_range(start, start + 8)
14534 .expect("base syntax");
14535 while base.kind() != "qualified_identifier" {
14536 base = base.parent().expect("qualified base ancestor");
14537 }
14538 assert!(
14539 is_recovered_exported_class_base_type_node(base, source),
14540 "{}",
14541 tree.root_node().to_sexp()
14542 );
14543 }
14544
14545 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
14546 let mut identities = parsed
14547 .declarations()
14548 .iter()
14549 .filter(|unit| unit.is_function())
14550 .map(|unit| {
14551 (
14552 unit.fq_name(),
14553 unit.signature().unwrap_or_default().to_string(),
14554 )
14555 })
14556 .collect::<Vec<_>>();
14557 identities.sort();
14558 identities
14559 }
14560
14561 #[test]
14568 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
14569 let source = r#"class SIMD_4x26 final {
14570 public:
14571 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
14572 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
14573 int m_v;
14574};
14575"#;
14576 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
14577 assert_eq!(
14578 function_identities(&parsed),
14579 vec![
14580 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
14581 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
14582 ],
14583 "{:#?}",
14584 parsed.declarations()
14585 );
14586 }
14587
14588 #[test]
14593 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
14594 let source = r#"#include <cstdint>
14595struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
14596 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
14597 struct llama_model * model,
14598 struct llama_context_params params),
14599 "use llama_init_from_model instead");
14600 LLAMA_API int32_t llama_tokenize(
14601 const struct llama_vocab * vocab,
14602 const char * text,
14603 bool parse_special);
14604 LLAMA_API int32_t llama_other(int a);
14605"#;
14606 let parsed = parse_cpp_declarations(source, "llama.h");
14607 assert_eq!(
14608 function_identities(&parsed),
14609 vec![
14610 (
14611 "llama_new_context_with_model".to_string(),
14612 "(struct llama_model *, struct llama_context_params)".to_string()
14613 ),
14614 ("llama_other".to_string(), "(int)".to_string()),
14615 (
14616 "llama_tokenize".to_string(),
14617 "(const struct llama_vocab *, const char *, bool)".to_string()
14618 ),
14619 ],
14620 "{:#?}",
14621 parsed.declarations()
14622 );
14623
14624 for (name, expected) in [
14627 (
14628 "llama_new_context_with_model",
14629 "LLAMA_API struct llama_context * llama_new_context_with_model(",
14630 ),
14631 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
14632 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
14633 ] {
14634 let unit = parsed
14635 .declarations()
14636 .iter()
14637 .find(|unit| unit.is_function() && unit.fq_name() == name)
14638 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
14639 let [range] = parsed.declaration_ranges(unit) else {
14640 panic!("{name} must have exactly one range");
14641 };
14642 let text = &source[range.start_byte..range.end_byte];
14643 assert!(
14644 text.starts_with(expected),
14645 "{name} range is {text:?}, expected it to start with {expected:?}"
14646 );
14647 assert!(
14648 text.ends_with(')') || text.ends_with(';'),
14649 "{name}: {text:?}"
14650 );
14651 }
14652 }
14653
14654 #[test]
14659 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
14660 for source in [
14661 "int before;\nFOO(1, 2);\nint after;\n",
14662 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
14663 "int before;\nMACRO(int a, int b);\nint after;\n",
14664 "DECLARE_HANDLE(HWND);\nint after;\n",
14665 ] {
14666 let parsed = parse_cpp_declarations(source, "macro-call.h");
14667 assert_eq!(
14668 function_identities(&parsed),
14669 Vec::new(),
14670 "{source:?} must declare no function: {:#?}",
14671 parsed.declarations()
14672 );
14673 }
14674 }
14675
14676 #[test]
14681 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
14682 let source = r#"#include <string_view>
14683namespace Botan {
14684class DL_Group final {
14685 public:
14686 DL_Group() = default;
14687 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
14688 DL_Group(std::string_view pem, int format);
14689 size_t get_p() const;
14690};
14691}
14692"#;
14693 let parsed = parse_cpp_declarations(source, "dl_group.h");
14694 assert_eq!(
14695 function_identities(&parsed),
14696 vec![
14697 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
14698 (
14699 "Botan.DL_Group.DL_Group".to_string(),
14700 "(std::string_view)".to_string()
14701 ),
14702 (
14703 "Botan.DL_Group.DL_Group".to_string(),
14704 "(std::string_view, int)".to_string()
14705 ),
14706 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
14707 ],
14708 "{:#?}",
14709 parsed.declarations()
14710 );
14711 }
14712
14713 #[test]
14718 fn an_export_macro_class_keeps_its_string_attribute_members() {
14719 let source = r#"#include <string_view>
14720namespace Botan {
14721class BOTAN_PUBLIC_API(2, 0) DL_Group final {
14722 public:
14723 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
14724 DL_Group(std::string_view pem, int format);
14725 size_t get_p() const;
14726};
14727}
14728"#;
14729 let parsed = parse_cpp_declarations(source, "dl_group.h");
14730 assert!(
14731 parsed
14732 .declarations()
14733 .iter()
14734 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
14735 "{:#?}",
14736 parsed.declarations()
14737 );
14738 assert_eq!(
14739 function_identities(&parsed),
14740 vec![
14741 (
14742 "Botan.DL_Group.DL_Group".to_string(),
14743 "(std::string_view)".to_string()
14744 ),
14745 (
14746 "Botan.DL_Group.DL_Group".to_string(),
14747 "(std::string_view, int)".to_string()
14748 ),
14749 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
14750 ],
14751 "{:#?}",
14752 parsed.declarations()
14753 );
14754 }
14755
14756 #[test]
14762 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
14763 let source = r#"namespace Botan {
14764class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
14765 public:
14766 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
14767 XMSS_Parameters(int oid, int len);
14768 size_t len() const;
14769
14770 private:
14771 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
14772 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
14773
14774 int m_oid;
14775 int m_wots_oid;
14776 size_t m_element_size;
14777 size_t m_tree_height;
14778};
14779}
14780"#;
14781 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
14782 let constructors = function_identities(&parsed)
14783 .into_iter()
14784 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
14785 .map(|(_, signature)| signature)
14786 .collect::<Vec<_>>();
14787 assert_eq!(
14788 constructors,
14789 vec![
14790 "()".to_string(),
14791 "(int, int)".to_string(),
14792 "(int, int, size_t, size_t)".to_string(),
14793 ],
14794 "{:#?}",
14795 parsed.declarations()
14796 );
14797 }
14798
14799 #[test]
14803 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
14804 let source = r#"namespace shell {
14805struct Outer {
14806 struct Inner {
14807 Inner(int v);
14808 void run(int v);
14809 };
14810};
14811Outer::Inner::Inner(int v) {}
14812void Outer::Inner::run(int v) {}
14813}
14814"#;
14815 let parsed = parse_cpp_declarations(source, "outer.cpp");
14816 let names = function_identities(&parsed)
14817 .into_iter()
14818 .map(|(fq_name, _)| fq_name)
14819 .collect::<Vec<_>>();
14820 assert!(
14821 names
14822 .iter()
14823 .all(|name| name.starts_with("shell.Outer$Inner.")),
14824 "{names:#?}"
14825 );
14826 }
14827
14828 #[test]
14829 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
14830 let source = r#"namespace control {
14831template <typename T>
14832class AnySpan;
14833template <typename T>
14834class ABSL_ATTRIBUTE_VIEW AnySpan {
14835 public:
14836 int begin() const;
14837};
14838}
14839
14840namespace absl {
14841ABSL_NAMESPACE_BEGIN
14842template <typename T>
14843class ABSL_ATTRIBUTE_VIEW Span {
14844 public:
14845 int begin() const;
14846 int back() const;
14847};
14848
14849int begin();
14850int back();
14851}
14852"#;
14853 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
14854 let declarations = parsed.declarations();
14855 assert!(
14856 declarations
14857 .iter()
14858 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
14859 );
14860 for method in ["begin", "back"] {
14861 assert!(declarations.iter().any(|unit| {
14862 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
14863 }));
14864 assert!(
14865 declarations.iter().any(|unit| {
14866 unit.is_function() && unit.fq_name() == format!("absl.{method}")
14867 })
14868 );
14869 }
14870 assert!(
14871 declarations
14872 .iter()
14873 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
14874 );
14875 assert!(
14876 declarations
14877 .iter()
14878 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
14879 );
14880 assert!(
14881 declarations
14882 .iter()
14883 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
14884 );
14885 }
14886
14887 #[test]
14888 fn explicit_global_member_definition_has_canonical_package_boundary() {
14889 let source = r#"
14890namespace arangodb::aql {
14891class ExecutionPlan {
14892 public:
14893 template<class... Args> Node* createNode(Args&&... args);
14894};
14895}
14896
14897template<class... Args>
14898Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
14899"#;
14900 let parsed = parse_cpp_declarations(source, "global-member.cpp");
14901
14902 assert!(parsed.declarations().iter().any(|unit| {
14903 unit.is_function()
14904 && unit.package_name() == "arangodb::aql"
14905 && unit.short_name() == "ExecutionPlan.createNode"
14906 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
14907 }));
14908 }
14909
14910 #[test]
14911 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
14912 let source = r#"
14913#ifndef TINYXML2_INCLUDED
14914#define TINYXML2_INCLUDED
14915namespace tinyxml2 {
14916class TINYXML2_LIB XMLUtil {
14917 public:
14918 static const char* SkipWhiteSpace(const char* p) {
14919 while (*p) {
14920 if (*p == ' ') {
14921 ++p;
14922 }
14923 }
14924 return p;
14925 }
14926 static bool StringEqual(const char* p, const char* q) {
14927 return p == q;
14928 }
14929 class TINYXML2_LIB Helper {
14930 public:
14931 void Touch();
14932 };
14933 static void ToStr(int value, char* buffer);
14934 private:
14935 static const char* writeBoolTrue;
14936};
14937
14938class TINYXML2_LIB XMLNode {
14939 public:
14940 virtual XMLNode* ShallowClone() const = 0;
14941 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
14942};
14943}
14944#endif
14945"#;
14946 let mut parser = tree_sitter::Parser::new();
14947 parser
14948 .set_language(&tree_sitter_cpp::LANGUAGE.into())
14949 .unwrap();
14950 let tree = parser.parse(source, None).unwrap();
14951 let mut boundary_found = false;
14952 walk_named_tree_preorder(tree.root_node(), true, |node| {
14953 if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
14954 && name == "XMLUtil"
14955 {
14956 boundary_found = fragmented_export_sibling_class_boundary(node, source)
14957 .and_then(|boundary| {
14958 recover_exported_class_function_definition(boundary, source)
14959 })
14960 .is_some_and(|(_, name, _)| name == "XMLNode");
14961 }
14962 WalkControl::Continue
14963 });
14964 assert!(
14965 boundary_found,
14966 "fixture must exercise the recovered sibling boundary"
14967 );
14968
14969 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
14970 assert!(
14971 parsed
14972 .declarations()
14973 .iter()
14974 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
14975 "{:#?}",
14976 parsed.declarations()
14977 );
14978 assert!(
14979 parsed
14980 .declarations()
14981 .iter()
14982 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
14983 "{:#?}",
14984 parsed.declarations()
14985 );
14986 assert!(parsed.declarations().iter().any(|unit| {
14987 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
14988 }));
14989 assert!(
14990 parsed
14991 .declarations()
14992 .iter()
14993 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
14994 );
14995 assert!(
14996 parsed
14997 .declarations()
14998 .iter()
14999 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
15000 );
15001 }
15002
15003 #[test]
15004 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
15005 let parsed = parse_cpp_declarations(
15009 r#"
15010namespace cwg311 {
15011namespace X { namespace Y {} }
15012namespace ::cwg311::X {}
15013}
15014"#,
15015 "explicit-global-namespace.cpp",
15016 );
15017
15018 assert!(parsed.declarations().iter().any(|unit| {
15019 unit.kind() == CodeUnitType::Module
15020 && unit.short_name() == "cwg311::X"
15021 && unit.fq_name() == "cwg311::X"
15022 }));
15023 assert!(
15024 parsed
15025 .declarations()
15026 .iter()
15027 .all(|unit| !unit.short_name().contains("::::")),
15028 "recovered namespace names must not retain empty scope components: {:#?}",
15029 parsed.declarations()
15030 );
15031 }
15032
15033 #[test]
15034 fn repeated_scope_separator_does_not_create_empty_function_owner() {
15035 let scope = ScopeInfo {
15036 package_name: "X".to_string(),
15037 module: None,
15038 class_unit: None,
15039 template_signature: None,
15040 template_metadata: None,
15041 declarations_are_fields: false,
15042 recovered_specialization_member_scope: false,
15043 visible_using_namespaces: Vec::new(),
15044 };
15045
15046 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
15047
15048 assert!(owner.is_none());
15049 assert_eq!(name, "doit");
15050 assert_eq!(package, "X");
15051 }
15052
15053 #[test]
15054 fn trailing_decltype_expression_is_not_a_function_declarator() {
15055 let source = r#"
15056namespace boost { namespace detail {
15057#if ! defined(BOOST_NO_SFINAE_EXPR) && \
15058 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
15059 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
15060#define BOOST_THREAD_PROVIDES_INVOKE
15061#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
15062template <class Fp, class A0, class ...Args>
15063inline auto
15064invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
15065 BOOST_THREAD_RV_REF(Args) ...args)
15066 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
15067{
15068 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
15069}
15070#endif
15071#endif
15072}}
15073"#;
15074 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
15075
15076 assert!(
15077 parsed
15078 .declarations()
15079 .iter()
15080 .all(|unit| unit.short_name() != ".*f")
15081 );
15082 }
15083
15084 fn find_class_named<'tree>(
15085 root: Node<'tree>,
15086 source: &str,
15087 expected_name: &str,
15088 ) -> Option<Node<'tree>> {
15089 let mut stack = vec![root];
15090 while let Some(node) = stack.pop() {
15091 if node.kind() == "class_specifier"
15092 && node
15093 .child_by_field_name("name")
15094 .is_some_and(|name| node_text(name, source) == expected_name)
15095 {
15096 return Some(node);
15097 }
15098 let mut cursor = node.walk();
15099 stack.extend(node.named_children(&mut cursor));
15100 }
15101 None
15102 }
15103
15104 #[test]
15105 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
15106 let source = r#"EXPORT void definition(struct Value value) {}
15107EXPORT void prototype(struct Value value);
15108"#;
15109 let mut parser = tree_sitter::Parser::new();
15110 parser
15111 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15112 .unwrap();
15113 let tree = parser.parse(source, None).unwrap();
15114 let root = tree.root_node();
15115 let mut cursor = root.walk();
15116 let callables = root
15117 .named_children(&mut cursor)
15118 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
15119 .collect::<Vec<_>>();
15120
15121 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
15122 for callable in callables {
15123 assert!(callable.has_error(), "fixture must exercise error recovery");
15124 assert!(
15125 cpp_sentinel_macro_parts(callable, source).is_none(),
15126 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
15127 );
15128 }
15129 }
15130
15131 #[test]
15132 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
15133 let source = r#"namespace absl {
15134ABSL_NAMESPACE_BEGIN
15135// Generate a floating-point variate conforming to a Beta distribution:
15136template <typename RealType = double>
15137class beta_distribution {
15138 public:
15139 using result_type = RealType;
15140
15141
15142 beta_distribution() : beta_distribution(1) {}
15143
15144 explicit beta_distribution(result_type alpha, result_type beta = 1)
15145 : param_(alpha, beta) {}
15146
15147 explicit beta_distribution(const param_type& p) : param_(p) {}
15148
15149 void reset() {}
15150
15151 // Generating functions
15152 template <typename URBG>
15153 result_type operator()(URBG& g) { // NOLINT(runtime/references)
15154 return (*this)(g, param_);
15155 }
15156
15157};
15158ABSL_NAMESPACE_END
15159} // namespace absl
15160"#;
15161 let mut parser = tree_sitter::Parser::new();
15162 parser
15163 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15164 .unwrap();
15165 let tree = parser.parse(source, None).unwrap();
15166 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
15167 let body = namespace
15168 .child_by_field_name("body")
15169 .expect("fixture namespace body");
15170 let sentinel = body.named_child(0).expect("sentinel envelope");
15171 let callable = sentinel
15172 .child_by_field_name("declarator")
15173 .and_then(extract_function_declarator)
15174 .and_then(cpp_function_declarator_name_node)
15175 .expect("preserved callable name");
15176
15177 assert_eq!(sentinel.kind(), "function_definition");
15178 assert_eq!(callable.kind(), "operator_name");
15179 assert!(
15180 cpp_sentinel_macro_parts(sentinel, source).is_some(),
15181 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
15182 );
15183 }
15184
15185 #[test]
15186 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
15187 let source = r#"namespace absl {
15188ABSL_NAMESPACE_BEGIN
15189// absl::discrete_distribution
15190//
15191// A discrete distribution produces random integers i, where 0 <= i < n
15192template <typename IntType = int>
15193class discrete_distribution {
15194 public:
15195 using result_type = IntType;
15196 class param_type {
15197 public:
15198 param_type() { init(); }
15199 template <typename InputIterator>
15200 explicit param_type(InputIterator begin, InputIterator end)
15201 : p_(begin, end) {
15202 init();
15203 }
15204 };
15205 discrete_distribution() : param_() {}
15206 explicit discrete_distribution(const param_type& p) : param_(p) {}
15207};
15208ABSL_NAMESPACE_END
15209} // namespace absl
15210"#;
15211 let mut parser = tree_sitter::Parser::new();
15212 parser
15213 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15214 .unwrap();
15215 let tree = parser.parse(source, None).unwrap();
15216 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
15217 let body = namespace
15218 .child_by_field_name("body")
15219 .expect("fixture namespace body");
15220 let sentinel = body.named_child(0).expect("sentinel envelope");
15221 let callable = sentinel
15222 .child_by_field_name("declarator")
15223 .and_then(extract_function_declarator)
15224 .and_then(cpp_function_declarator_name_node)
15225 .expect("preserved callable name");
15226
15227 assert_eq!(sentinel.kind(), "function_definition");
15228 assert_eq!(callable.kind(), "identifier");
15229 assert!(
15230 cpp_sentinel_macro_parts(sentinel, source).is_some(),
15231 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
15232 );
15233 }
15234
15235 #[test]
15236 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
15237 let source = r#"
15238#define CPPCHECKLIB
15239class Library {
15240 struct Container {
15241 CPPCHECKLIB static std::string toString(Yield yield);
15242 CPPCHECKLIB static std::string toString(Action action);
15243 };
15244};
15245"#;
15246 let mut parser = tree_sitter::Parser::new();
15247 parser
15248 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15249 .unwrap();
15250 let tree = parser.parse(source, None).unwrap();
15251 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
15252 let parsed = parse_cpp_file(&file, source, &tree);
15253 assert!(
15254 parsed
15255 .declarations()
15256 .iter()
15257 .all(|unit| unit.fq_name() != "Library$Container.std"),
15258 "the qualified return-type namespace must not become a field: {:#?}",
15259 parsed.declarations()
15260 );
15261 for expected in ["(Yield)", "(Action)"] {
15262 assert!(
15263 parsed.declarations().iter().any(|unit| {
15264 unit.is_function()
15265 && unit.fq_name() == "Library$Container.toString"
15266 && unit.signature() == Some(expected)
15267 }),
15268 "recovered toString overload {expected} is missing: {:#?}",
15269 parsed.declarations()
15270 );
15271 }
15272 }
15273
15274 #[test]
15275 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
15276 let source = r#"
15277#define SIMPLECPP_LIB
15278namespace simplecpp {
15279using TokenString = std::string;
15280struct Location { int line{}; };
15281class SIMPLECPP_LIB Token {
15282 TokenString prefix;
15283 void prefix_method() {}
15284 public:
15285 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
15286 whitespaceahead(wsahead), location(loc), string(s)
15287 // The comment must not hide the constructor body from recovery.
15288 {
15289 flags();
15290 }
15291 TokenString string;
15292 bool whitespaceahead;
15293 Location location;
15294 Token *previous{};
15295 private:
15296 void flags() {
15297 whitespaceahead = true;
15298 }
15299};
15300}
15301"#;
15302 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
15303
15304 let location_fields = parsed
15305 .declarations()
15306 .iter()
15307 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
15308 .collect::<Vec<_>>();
15309 assert_eq!(
15310 location_fields.len(),
15311 1,
15312 "location should have one class-owned declaration: {:#?}",
15313 parsed.declarations()
15314 );
15315 assert!(
15316 location_fields[0].is_field(),
15317 "location has wrong kind: {:#?}",
15318 parsed.declarations()
15319 );
15320 assert!(
15321 parsed.declarations().iter().all(|unit| {
15322 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
15323 })
15324 );
15325 assert!(
15326 parsed.declarations().iter().all(|unit| {
15327 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
15328 })
15329 );
15330 assert!(
15331 parsed
15332 .declarations()
15333 .iter()
15334 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
15335 );
15336 assert!(
15337 parsed
15338 .declarations()
15339 .iter()
15340 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
15341 "the recovered class must retain its constructor: {:#?}",
15342 parsed.declarations()
15343 );
15344 assert!(
15345 parsed
15346 .declarations()
15347 .iter()
15348 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
15349 );
15350 assert!(parsed.declarations().iter().any(|unit| {
15351 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
15352 }));
15353 let constructor = parsed
15354 .declarations()
15355 .iter()
15356 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
15357 .expect("recovered constructor");
15358 let constructor_start = source.find("Token(const").expect("constructor start");
15359 let constructor_end = source
15360 .get(
15361 ..source
15362 .find(" TokenString string;")
15363 .expect("constructor end"),
15364 )
15365 .expect("constructor slice")
15366 .trim_end()
15367 .len();
15368 assert!(
15369 parsed
15370 .navigation_ranges
15371 .get(constructor)
15372 .is_some_and(|ranges| {
15373 ranges.iter().any(|range| {
15374 range.start_byte == constructor_start && range.end_byte == constructor_end
15375 })
15376 }),
15377 "constructor navigation must span the full body: {:#?}",
15378 parsed.navigation_ranges
15379 );
15380 assert_eq!(
15381 parsed
15382 .signature_metadata
15383 .get(constructor)
15384 .and_then(|metadata| metadata.first())
15385 .and_then(SignatureMetadata::callable_linkage),
15386 Some(CallableLinkage::External)
15387 );
15388 let token_class = parsed
15389 .declarations()
15390 .iter()
15391 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
15392 .expect("recovered Token class");
15393 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
15394 assert!(
15395 parsed
15396 .navigation_ranges
15397 .get(token_class)
15398 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
15399 "class navigation must include the terminating semicolon: {:#?}",
15400 parsed.navigation_ranges
15401 );
15402 }
15403
15404 #[test]
15405 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
15406 let source = r#"
15407#define SIMPLECPP_LIB
15408namespace simplecpp {
15409using TokenString = std::string;
15410class Macro;
15411struct Location {
15412 unsigned int fileIndex{};
15413 unsigned int line{};
15414 unsigned int col{};
15415};
15416struct Output {
15417 int type;
15418};
15419class SIMPLECPP_LIB Token {
15420 public:
15421 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
15422 whitespaceahead(wsahead), location(loc), string(s) {
15423 flags();
15424 }
15425 Token(const Token &tok) :
15426 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
15427 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
15428 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
15429 Token &operator=(const Token &tok) = delete;
15430 const TokenString& str() const { return string; }
15431 void setstr(const std::string &s) { string = s; flags(); }
15432 bool isOneOf(const char ops[]) const;
15433 TokenString macro;
15434 char op;
15435 bool comment;
15436 bool name;
15437 bool number;
15438 bool whitespaceahead;
15439 Location location;
15440 Token *previous{};
15441 Token *next{};
15442 private:
15443 void flags() {
15444 name = !string.empty();
15445 comment = false;
15446 number = false;
15447 op = 0;
15448 }
15449 TokenString string;
15450};
15451}
15452struct Following {
15453 int type;
15454};
15455class SIMPLECPP_LIB Later {
15456 public:
15457 Later(int value) : value(value) {}
15458 int value;
15459};
15460"#;
15461 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
15462 assert!(
15463 parsed
15464 .declarations()
15465 .iter()
15466 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
15467 );
15468 assert!(
15469 !parsed
15470 .declarations()
15471 .iter()
15472 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
15473 );
15474 assert!(
15475 parsed
15476 .declarations()
15477 .iter()
15478 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
15479 );
15480 assert!(
15481 !parsed
15482 .declarations()
15483 .iter()
15484 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
15485 );
15486 assert!(
15487 parsed
15488 .declarations()
15489 .iter()
15490 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
15491 );
15492 assert!(
15493 parsed
15494 .declarations()
15495 .iter()
15496 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
15497 );
15498 assert!(
15499 parsed
15500 .declarations()
15501 .iter()
15502 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
15503 );
15504 assert!(
15505 parsed
15506 .declarations()
15507 .iter()
15508 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
15509 );
15510 assert!(
15511 parsed
15512 .declarations()
15513 .iter()
15514 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
15515 );
15516 assert!(
15517 parsed
15518 .declarations()
15519 .iter()
15520 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
15521 );
15522 assert!(parsed.declarations().iter().all(|unit| {
15523 !matches!(
15524 unit.fq_name().as_str(),
15525 "simplecpp.Token.Following" | "simplecpp.Token.Later"
15526 )
15527 }));
15528 assert!(
15529 !parsed
15530 .declarations()
15531 .iter()
15532 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
15533 "the following struct must remain outside the recovered Token class"
15534 );
15535 }
15536
15537 #[test]
15538 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
15539 let source = r#"
15540#define SIMPLECPP_LIB
15541namespace {
15542namespace simplecpp {
15543using TokenString = std::string;
15544struct Location { int line{}; };
15545class SIMPLECPP_LIB HiddenToken {
15546 public:
15547 HiddenToken(const TokenString &s, const Location &loc) :
15548 location(loc), string(s) {
15549 flags();
15550 }
15551 TokenString string;
15552 Location location;
15553 HiddenToken *previous{};
15554 private:
15555 void flags() {}
15556};
15557}
15558}
15559"#;
15560 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
15561 let constructor = parsed
15562 .declarations()
15563 .iter()
15564 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
15565 .expect("recovered anonymous-namespace constructor");
15566 assert_eq!(
15567 parsed
15568 .signature_metadata
15569 .get(constructor)
15570 .and_then(|metadata| metadata.first())
15571 .and_then(SignatureMetadata::callable_linkage),
15572 Some(CallableLinkage::Internal)
15573 );
15574 }
15575
15576 #[test]
15577 fn macro_qualified_static_field_keeps_real_declarator() {
15578 let source = r#"#define JSON_INLINE_VARIABLE
15579struct Reader {
15580static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
15581static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
15582static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
15583};"#;
15584 let mut parser = tree_sitter::Parser::new();
15585 parser
15586 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15587 .unwrap();
15588 let tree = parser.parse(source, None).unwrap();
15589 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
15590 let parsed = parse_cpp_file(&file, source, &tree);
15591 for expected in [
15592 "Reader.npos",
15593 "Reader.other",
15594 "Reader.pointer",
15595 "Reader.reference",
15596 ] {
15597 assert!(
15598 parsed
15599 .declarations()
15600 .iter()
15601 .any(|unit| unit.is_field() && unit.fq_name() == expected),
15602 "real macro-decorated field {expected} is missing: {:#?}",
15603 parsed.declarations()
15604 );
15605 }
15606 assert!(
15607 parsed
15608 .declarations()
15609 .iter()
15610 .all(|unit| unit.fq_name() != "Reader.std"),
15611 "qualified type prefix became a pseudo-field: {:#?}",
15612 parsed.declarations()
15613 );
15614 let root = tree.root_node();
15615 let mut stack = vec![root];
15616 let mut signatures = Vec::new();
15617 while let Some(current) = stack.pop() {
15618 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
15619 {
15620 signatures.extend(
15621 declarators
15622 .into_iter()
15623 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
15624 );
15625 }
15626 let mut cursor = current.walk();
15627 stack.extend(current.named_children(&mut cursor));
15628 }
15629 signatures.sort();
15630 assert_eq!(
15631 signatures,
15632 [
15633 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
15634 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
15635 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
15636 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
15637 ]
15638 );
15639 }
15640
15641 fn member_function_linkage(source: &str) -> CallableLinkage {
15642 let mut parser = tree_sitter::Parser::new();
15643 parser
15644 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15645 .unwrap();
15646 let tree = parser.parse(source, None).unwrap();
15647 let ancestry = ParentIndex::new(tree.root_node());
15648 let mut stack = vec![tree.root_node()];
15649 while let Some(node) = stack.pop() {
15650 if node.kind() == "function_definition" {
15651 let mut current = node.parent();
15652 while let Some(parent) = current {
15653 if matches!(
15654 parent.kind(),
15655 "class_specifier" | "struct_specifier" | "union_specifier"
15656 ) {
15657 return cpp_callable_linkage(node, source, &ancestry);
15658 }
15659 current = parent.parent();
15660 }
15661 }
15662 let mut cursor = node.walk();
15663 stack.extend(node.named_children(&mut cursor));
15664 }
15665 panic!("fixture has no member function definition");
15666 }
15667
15668 #[test]
15669 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
15670 assert_eq!(
15671 member_function_linkage("struct Named { int method() { return 1; } };"),
15672 CallableLinkage::External
15673 );
15674 assert_eq!(
15675 member_function_linkage(
15676 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
15677 ),
15678 CallableLinkage::Internal
15679 );
15680 assert_eq!(
15681 member_function_linkage("struct { int method() { return 1; } } instance;"),
15682 CallableLinkage::Internal
15683 );
15684 assert_eq!(
15685 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
15686 CallableLinkage::Internal
15687 );
15688 }
15689
15690 #[test]
15691 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
15692 let source = r#"
15693#ifndef PROTON_VALUE_HPP
15694#define PROTON_VALUE_HPP
15695namespace proton {
15696namespace internal {
15697class value_base {
15698 protected:
15699 internal::data& data();
15700 internal::data data_;
15701 friend class codec::encoder;
15702 friend class codec::decoder;
15703};
15704}
15705class value : public internal::value_base, private internal::comparable<value> {
15706 private:
15707 template<class T, class U=void> struct assignable :
15708 public std::enable_if<codec::is_encodable<T>::value, U> {};
15709 template<class U> struct assignable<value, U> {};
15710 public:
15711 PN_CPP_EXTERN value();
15712 PN_CPP_EXTERN value(const value&);
15713 PN_CPP_EXTERN value& operator=(const value&);
15714 PN_CPP_EXTERN value(value&&);
15715 PN_CPP_EXTERN value& operator=(value&&);
15716 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
15717 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
15718 codec::encoder e(*this);
15719 e << x;
15720 return *this;
15721 }
15722 PN_CPP_EXTERN type_id type() const;
15723 PN_CPP_EXTERN bool empty() const;
15724 PN_CPP_EXTERN void clear();
15725 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
15726 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
15727 friend PN_CPP_EXTERN void swap(value&, value&);
15728 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
15729 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
15730 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
15731 value(pn_data_t* d);
15732 void reset(pn_data_t* d = 0);
15733};
15734}
15735#endif
15736"#;
15737 let mut parser = tree_sitter::Parser::new();
15738 parser
15739 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15740 .unwrap();
15741 let tree = parser.parse(source, None).unwrap();
15742 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
15743 let parsed = parse_cpp_file(&file, source, &tree);
15744 let macro_constructors = parsed
15745 .signature_metadata
15746 .iter()
15747 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
15748 .flat_map(|(_, metadata)| metadata)
15749 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
15750 .collect::<Vec<_>>();
15751
15752 assert_eq!(
15753 macro_constructors.len(),
15754 3,
15755 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
15756 parsed.declarations()
15757 );
15758 assert!(
15759 macro_constructors.iter().all(|metadata| {
15760 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
15761 }),
15762 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
15763 );
15764 }
15765
15766 #[test]
15767 fn recovered_export_class_typedef_uses_displaced_alias_name() {
15768 let source = r#"
15769namespace spi {
15770class Filter {
15771public:
15772 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
15773};
15774}
15775namespace filter {
15776class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
15777{
15778public:
15779 typedef spi::Filter BASE_CLASS;
15780 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
15781 BEGIN_LOG4CXX_CAST_MAP()
15782 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
15783 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
15784 END_LOG4CXX_CAST_MAP()
15785 FilterDecision decide() const;
15786};
15787}
15788"#;
15789 let mut parser = tree_sitter::Parser::new();
15790 parser
15791 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15792 .unwrap();
15793 let tree = parser.parse(source, None).unwrap();
15794 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
15795 let parsed = parse_cpp_file(&file, source, &tree);
15796 assert!(
15797 parsed.declarations().iter().any(|unit| {
15798 unit.is_class()
15799 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
15800 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
15801 }),
15802 "the displaced typedef alias must retain its declared name: {:#?}",
15803 parsed.declarations()
15804 );
15805 assert!(
15806 parsed
15807 .declarations()
15808 .iter()
15809 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
15810 "the qualified underlying type must not become a false nested alias: {:#?}",
15811 parsed.declarations()
15812 );
15813 }
15814
15815 #[test]
15816 fn exported_single_base_recovery_uses_displaced_class_name() {
15817 let source = r#"
15818class CORE_EXPORT QgsPoint : public AbstractGeometry
15819{
15820 Q_GADGET
15821
15822 Q_PROPERTY( double x READ x WRITE setX )
15823 Q_PROPERTY( double y READ y WRITE setY )
15824 Q_PROPERTY( double z READ z WRITE setZ )
15825 Q_PROPERTY( double m READ m WRITE setM )
15826
15827 public:
15828#ifndef SIP_RUN
15829 QgsPoint(
15830 double x = std::numeric_limits<double>::quiet_NaN(),
15831 double y = std::numeric_limits<double>::quiet_NaN(),
15832 double z = std::numeric_limits<double>::quiet_NaN(),
15833 double m = std::numeric_limits<double>::quiet_NaN(),
15834 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
15835 );
15836#else
15837 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 )];
15838 % MethodCode
15839 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
15840 {
15841 int state;
15842 sipIsErr = 0;
15843 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
15844 if ( !sipIsErr )
15845 {
15846 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
15847 }
15848 sipReleaseType( p, sipType_QgsPointXY, state );
15849 }
15850 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
15851 {
15852 int state;
15853 sipIsErr = 0;
15854
15855 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
15856 if ( !sipIsErr )
15857 {
15858 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
15859 }
15860 sipReleaseType( p, sipType_QPointF, state );
15861 }
15862 else if (
15863 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
15864 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
15865 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
15866 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
15867 {
15868 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
15869 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
15870 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
15871 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
15872 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
15873 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
15874 }
15875 else // Invalid ctor arguments
15876 {
15877 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
15878 sipIsErr = 1;
15879 }
15880 % End
15881#endif
15882
15883 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
15884 explicit QgsPoint( QPointF p ) SIP_SKIP;
15885 explicit QgsPoint(
15886 Qgis::WkbType wkbType,
15887 double x = std::numeric_limits<double>::quiet_NaN(),
15888 double y = std::numeric_limits<double>::quiet_NaN(),
15889 double z = std::numeric_limits<double>::quiet_NaN(),
15890 double m = std::numeric_limits<double>::quiet_NaN()
15891 ) SIP_SKIP;
15892 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
15893 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
15894 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
15895#ifndef SIP_RUN
15896 private:
15897 bool fuzzyHelper(
15898 double epsilon,
15899 const AbstractGeometry &other,
15900 bool is3DFlag,
15901 bool isMeasureFlag
15902 ) const
15903 {
15904 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
15905 }
15906#endif
15907};
15908class Ordinary : public Base { public: Ordinary(); };
15909class API_EXPORT Plain { public: Plain(); };
15910class API_EXPORT : public Base {};
15911class
15912PN_CPP_CLASS_EXTERN Sender : public Link {
15913 Sender();
15914};
15915class thread_ctx_t {};
15916class ctx_t ZMQ_FINAL : public thread_ctx_t {
15917 bool start();
15918};
15919"#;
15920 let mut parser = tree_sitter::Parser::new();
15921 parser
15922 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15923 .unwrap();
15924 let tree = parser.parse(source, None).unwrap();
15925 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
15926 let parsed = parse_cpp_file(&file, source, &tree);
15927 let declarations = parsed.declarations();
15928
15929 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
15930 assert!(
15931 declarations
15932 .iter()
15933 .any(|unit| unit.is_class() && unit.fq_name() == expected),
15934 "missing recovered class {expected}: {declarations:#?}"
15935 );
15936 }
15937 let qgs_point = declarations
15938 .iter()
15939 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
15940 .expect("recovered QgsPoint class");
15941 assert_eq!(
15942 parsed.raw_supertypes.get(qgs_point),
15943 Some(&vec!["AbstractGeometry".to_string()]),
15944 "single-base export recovery must retain its displaced base"
15945 );
15946 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
15947 assert!(
15948 parsed
15949 .navigation_ranges
15950 .get(qgs_point)
15951 .is_some_and(|ranges| {
15952 !ranges.is_empty()
15953 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
15954 }),
15955 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
15956 parsed.navigation_ranges.get(qgs_point)
15957 );
15958 let sender = declarations
15959 .iter()
15960 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
15961 .expect("recovered Sender class");
15962 assert_eq!(
15963 parsed.raw_supertypes.get(sender),
15964 Some(&vec!["Link".to_string()]),
15965 "post-declarator export recovery must retain its displaced base"
15966 );
15967 let ctx = declarations
15968 .iter()
15969 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
15970 .expect("recovered ctx_t class");
15971 assert_eq!(
15972 parsed.raw_supertypes.get(ctx),
15973 Some(&vec!["thread_ctx_t".to_string()]),
15974 "postfix export-macro recovery must retain its displaced base"
15975 );
15976 assert!(
15977 declarations.iter().any(|unit| {
15978 unit.is_function()
15979 && unit.fq_name() == "QgsPoint.QgsPoint"
15980 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
15981 }),
15982 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
15983 );
15984 assert!(
15985 declarations.iter().all(|unit| {
15986 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
15987 }),
15988 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
15989 );
15990 }
15991
15992 #[test]
15993 fn function_like_export_macro_classes_keep_names_and_base_edges() {
15994 let source = r#"
15998namespace api {
15999class PROJECT_PUBLIC_API(2, 0) Prelude {
16000 public:
16001 Prelude();
16002};
16003class PROJECT_PUBLIC_API(2, 0) Base {
16004 public:
16005 Base(int value);
16006};
16007class PROJECT_PUBLIC_API(2, 0) Mixin {
16008 public:
16009 Mixin();
16010};
16011class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
16012 public:
16013 Adopted(int value);
16014};
16015class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
16016 public:
16017 Derived(int value);
16018};
16019class PROJECT_PUBLIC_API(2, 0) Solo final {
16020 public:
16021 Solo();
16022};
16023class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
16024 public:
16025 Blended(int value);
16026};
16027class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
16028 public:
16029 Woven(int value);
16030};
16031} // namespace api
16032"#;
16033 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
16034 let declarations = parsed.declarations();
16035 let class_named = |name: &str| {
16036 declarations
16037 .iter()
16038 .find(|unit| unit.is_class() && unit.fq_name() == name)
16039 .unwrap_or_else(|| {
16040 panic!("missing function-like export macro class {name}: {declarations:#?}")
16041 })
16042 };
16043 let base = class_named("api.Base");
16044 class_named("api.Prelude");
16045 class_named("api.Mixin");
16046
16047 assert_eq!(
16048 parsed.raw_supertypes.get(class_named("api.Adopted")),
16049 Some(&vec!["Base".to_string()])
16050 );
16051 assert_eq!(
16052 parsed.raw_supertypes.get(class_named("api.Derived")),
16053 Some(&vec!["Base".to_string()])
16054 );
16055 assert_eq!(
16056 parsed.raw_supertypes.get(class_named("api.Solo")),
16057 None,
16058 "a final class without a base list must not invent a supertype"
16059 );
16060 assert_eq!(
16061 parsed.raw_supertypes.get(class_named("api.Blended")),
16062 Some(&vec!["Base".to_string(), "Mixin".to_string()])
16063 );
16064 assert_eq!(
16065 parsed.raw_supertypes.get(class_named("api.Woven")),
16066 Some(&vec!["Base".to_string(), "Mixin".to_string()])
16067 );
16068 assert!(
16069 declarations
16070 .iter()
16071 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
16072 "the export macro must not become a declaration: {declarations:#?}"
16073 );
16074 assert!(
16075 declarations.iter().all(|unit| !matches!(
16076 unit.identifier(),
16077 "final" | "public" | "protected" | "private"
16078 )),
16079 "the head specifiers must not become declarations: {declarations:#?}"
16080 );
16081 assert!(
16082 parsed
16083 .navigation_ranges
16084 .get(base)
16085 .is_some_and(|ranges| !ranges.is_empty()),
16086 "the recovered base must retain a navigable declaration range"
16087 );
16088 }
16089
16090 #[test]
16091 fn function_like_export_class_survives_a_preceding_malformed_body() {
16092 let source = r#"
16093namespace api {
16094class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
16095 public:
16096 /** Return a descriptive string. */
16097 const char* what() const noexcept override { return m_msg.c_str(); }
16098
16099 /** Return the type of error. */
16100 virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
16101
16102 /** Return an associated error code. */
16103 virtual int error_code() const noexcept { return 0; }
16104
16105 /** Avoid throwing the base directly. */
16106 explicit Exception(std::string_view msg);
16107
16108 /** Avoid throwing the base directly. */
16109 Exception(const char* prefix, std::string_view msg);
16110
16111 /** Avoid throwing the base directly. */
16112 Exception(std::string_view msg, const std::exception& e);
16113
16114 private:
16115 std::string m_msg;
16116};
16117
16118class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
16119 public:
16120 explicit Invalid_Argument(std::string_view msg);
16121
16122 explicit Invalid_Argument(std::string_view msg, std::string_view where);
16123
16124 Invalid_Argument(std::string_view msg, const std::exception& e);
16125
16126 ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
16127};
16128} // namespace api
16129"#;
16130 let mut parser = Parser::new();
16131 parser
16132 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16133 .expect("set C++ grammar");
16134 let tree = parser.parse(source, None).expect("parse fixture");
16135 let mut stack = vec![tree.root_node()];
16136 let mut saw_embedded_shape = false;
16137 while let Some(node) = stack.pop() {
16138 saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
16139 .iter()
16140 .any(|recovered| recovered.name == "Invalid_Argument");
16141 let mut cursor = node.walk();
16142 stack.extend(node.named_children(&mut cursor));
16143 }
16144 assert!(
16145 saw_embedded_shape,
16146 "fixture must retain the embedded error geometry: {}",
16147 tree.root_node().to_sexp()
16148 );
16149
16150 let parsed = parse_cpp_file(
16151 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
16152 source,
16153 &tree,
16154 );
16155 let declarations = parsed.declarations();
16156 let exception = declarations
16157 .iter()
16158 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
16159 .expect("qualified-base export class");
16160 let invalid = declarations
16161 .iter()
16162 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
16163 .expect("class embedded in the preceding malformed body");
16164
16165 assert_eq!(
16166 parsed.raw_supertypes.get(exception),
16167 Some(&vec!["std::exception".to_string()])
16168 );
16169 assert_eq!(
16170 parsed.raw_supertypes.get(invalid),
16171 Some(&vec!["Exception".to_string()])
16172 );
16173 assert!(
16174 parsed.materialization_records.iter().any(|record| matches!(
16175 record,
16176 MaterializationRecord::RecoveredDeclaration { unit, .. }
16177 if unit == invalid
16178 )),
16179 "the embedded class must retain recovery provenance: {:#?}",
16180 parsed.materialization_records
16181 );
16182 }
16183
16184 #[test]
16185 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
16186 let source = r#"
16187public:
16188 explicit Lookup_Error(std::string_view err) : Exception(err) {}
16189
16190 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
16191"#;
16192 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
16193 .expect("reparse merged constructor body");
16194 let (range, body) =
16195 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
16196 .unwrap_or_else(|| {
16197 panic!(
16198 "the merged constructor must retain its structured declarator/body: {}",
16199 tree.root_node().to_sexp()
16200 )
16201 });
16202 assert_eq!(
16203 source.get(range).expect("constructor range"),
16204 "Lookup_Error(std::string_view err) : Exception(err) {}"
16205 );
16206 assert_eq!(node_text(body, source), "{}");
16207 }
16208
16209 #[test]
16210 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
16211 let positive_source =
16212 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
16213 let mut parser = tree_sitter::Parser::new();
16214 parser
16215 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16216 .unwrap();
16217 let positive_tree = parser.parse(positive_source, None).unwrap();
16218 assert!(cpp_reparsed_members_are_indexable(
16219 positive_tree.root_node(),
16220 positive_source
16221 ));
16222
16223 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
16224 let negative_tree = parser.parse(negative_source, None).unwrap();
16225 assert!(!cpp_reparsed_members_are_indexable(
16226 negative_tree.root_node(),
16227 negative_source
16228 ));
16229 }
16230
16231 #[test]
16232 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
16233 let copy_control_source = r#"
16234public:
16235 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
16236 explicit Token(const Token* tok);
16237 ~Token();
16238 Token* astOperand1() { return nullptr; }
16239"#;
16240 let constraint_source = r#"
16241private:
16242 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
16243 static T *tokAtImpl(T *tok, int index) {
16244 return tok;
16245 }
16246
16247 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
16248 static T *linkAtImpl(T *tok, int index) {
16249 return tok;
16250 }
16251
16252public:
16253 int late() const { return 1; }
16254"#;
16255 let mut parser = tree_sitter::Parser::new();
16256 parser
16257 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16258 .unwrap();
16259 let copy_control_tree = parser
16260 .parse(copy_control_source, None)
16261 .expect("parse copy-control fixture");
16262 assert!(
16263 copy_control_tree.root_node().has_error(),
16264 "fixture must exercise adjacent copy-control recovery"
16265 );
16266 assert!(
16267 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
16268 "a complete late getter must remain recoverable after adjacent copy-control declarations"
16269 );
16270 let mut cursor = copy_control_tree.root_node().walk();
16271 assert!(
16272 copy_control_tree
16273 .root_node()
16274 .named_children(&mut cursor)
16275 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
16276 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
16277 copy_control_tree.root_node().to_sexp()
16278 );
16279 let constraint_tree = parser
16280 .parse(constraint_source, None)
16281 .expect("parse constraint-macro fixture");
16282 assert!(constraint_tree.root_node().has_error());
16283 assert!(
16284 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
16285 "complete constraint-macro members must not hide a later ordinary member"
16286 );
16287 let mut cursor = constraint_tree.root_node().walk();
16288 assert!(
16289 constraint_tree
16290 .root_node()
16291 .named_children(&mut cursor)
16292 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
16293 child,
16294 constraint_source
16295 )),
16296 "fixture must retain the split constraint-macro prefix/function geometry"
16297 );
16298 }
16299
16300 #[test]
16301 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
16302 let source = r#"
16303struct Analyzer {
16304 struct Action {
16305 Action() = default;
16306 Action(const Action&) = default;
16307 Action& operator=(const Action& rhs) & = default;
16308
16309 template<class T,
16310 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
16311 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
16312 // NOLINTNEXTLINE(google-explicit-constructor)
16313 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
16314 {}
16315
16316 enum : std::uint16_t { None = 0, Read = (1 << 0) };
16317 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
16318
16319 private:
16320 unsigned int mFlag{};
16321 };
16322
16323 enum class Direction : unsigned char { Forward, Reverse };
16324 virtual Action analyze(Direction d) const = 0;
16325};
16326"#;
16327 let mut parser = tree_sitter::Parser::new();
16328 parser
16329 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16330 .unwrap();
16331 let tree = parser.parse(source, None).unwrap();
16332 assert!(tree.root_node().has_error());
16333 let root = tree.root_node();
16334 let outer = root
16335 .named_children(&mut root.walk())
16336 .find(|child| child.kind() == "ERROR")
16337 .expect("fragmented Analyzer prefix");
16338 let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
16339 .expect("structured Analyzer fragment boundary");
16340 assert_eq!(outer_name, "Analyzer");
16341 let outer_tree = cpp_reparse_fragmented_class_body(
16342 source,
16343 outer_fragment.reparse_start,
16344 outer_fragment.reparse_end,
16345 )
16346 .expect("reparse Analyzer body");
16347 let outer_root = outer_tree.root_node();
16348 let action_prefix = outer_root
16349 .named_children(&mut outer_root.walk())
16350 .find(|child| child.kind() == "ERROR")
16351 .expect("fragmented Action prefix");
16352 let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
16353 .expect("structured Action fragment boundary");
16354 assert_eq!(action_name, "Action");
16355 let action_tree = cpp_reparse_fragmented_class_body(
16356 source,
16357 action_fragment.reparse_start,
16358 action_fragment.reparse_end,
16359 )
16360 .expect("reparse Action body");
16361 let action_root = action_tree.root_node();
16362 let macro_prefix = action_root
16363 .named_children(&mut action_root.walk())
16364 .find(|child| child.kind() == "ERROR")
16365 .expect("constraint macro prefix");
16366 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
16367 .expect("structured template macro prefix");
16368 let macro_companion =
16369 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
16370 assert!(
16371 cpp_reparsed_template_macro_constructor_companion_is_indexable(
16372 macro_companion,
16373 macro_parameter,
16374 source,
16375 ),
16376 "split constrained constructor must be admitted: {}",
16377 macro_companion.to_sexp()
16378 );
16379 assert!(
16380 cpp_reparsed_members_are_indexable(action_root, source),
16381 "complete Action body must pass the recovery gate: {}",
16382 action_tree.root_node().to_sexp()
16383 );
16384 assert!(
16385 cpp_reparsed_members_are_indexable(outer_root, source),
16386 "complete Analyzer body must pass the recovery gate: {}",
16387 outer_tree.root_node().to_sexp()
16388 );
16389 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
16390 let parsed = parse_cpp_file(&file, source, &tree);
16391 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
16392 assert!(
16393 parsed
16394 .declarations()
16395 .iter()
16396 .any(|unit| unit.fq_name() == expected),
16397 "missing recovered declaration {expected}: {:#?}",
16398 parsed.declarations()
16399 );
16400 }
16401 assert!(
16402 parsed
16403 .declarations()
16404 .iter()
16405 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
16406 "nested members must not remain flattened: {:#?}",
16407 parsed.declarations()
16408 );
16409 }
16410
16411 #[test]
16412 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
16413 let source = r#"
16414raw_hash_set& operator=(raw_hash_set&& that) {
16415 return move_assign(
16416 std::move(that),
16417 typename AllocTraits::propagate_on_container_move_assignment());
16418}
16419
16420iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
16421 return {};
16422}
16423
16424void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
16425
16426iterator insert(const_iterator hint, value_type&& value)
16427 ABSL_ATTRIBUTE_LIFETIME_BOUND {
16428 return {};
16429}
16430
16431friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
16432 return left.size() == right.size();
16433}
16434
16435static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
16436 return static_cast<slot_type*>(buffer);
16437}
16438
16439protected:
16440// Included-range recovery can attach this comment to the template prefix.
16441template <class K>
16442void AssertOnFind([[maybe_unused]] const K& key) {
16443 Check(key);
16444}
16445"#;
16446 let mut parser = tree_sitter::Parser::new();
16447 parser
16448 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16449 .unwrap();
16450 let tree = parser.parse(source, None).unwrap();
16451 assert!(
16452 tree.root_node().has_error(),
16453 "the fixture must exercise tree-sitter's errorful member shapes"
16454 );
16455 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
16456
16457 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
16458 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
16459 assert!(!cpp_reparsed_members_are_indexable(
16460 incomplete_tree.root_node(),
16461 incomplete_source
16462 ));
16463
16464 let outside_error_source = "int foo() stray_attribute {}\n";
16465 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
16466 assert!(outside_error_tree.root_node().has_error());
16467 assert!(!cpp_reparsed_members_are_indexable(
16468 outside_error_tree.root_node(),
16469 outside_error_source
16470 ));
16471
16472 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
16473 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
16474 assert!(!cpp_reparsed_members_are_indexable(
16475 variable_initializer_tree.root_node(),
16476 variable_initializer_source
16477 ));
16478 }
16479
16480 #[test]
16481 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
16482 let positive_source = r#"
16483std::pair<iterator, bool> insert(init_type&& value)
16484 ABSL_ATTRIBUTE_LIFETIME_BOUND
16485#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
16486 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
16487#endif
16488{
16489 return emplace(std::move(value));
16490}
16491"#;
16492 let mut parser = tree_sitter::Parser::new();
16493 parser
16494 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16495 .unwrap();
16496 let positive_tree = parser.parse(positive_source, None).unwrap();
16497 assert!(
16498 positive_tree.root_node().has_error(),
16499 "the fixture must exercise the split attribute/requires shape"
16500 );
16501 assert!(cpp_reparsed_members_are_indexable(
16502 positive_tree.root_node(),
16503 positive_source
16504 ));
16505
16506 let template_return_source = r#"
16507pair<int> insert(init_type&& value)
16508 ABSL_ATTRIBUTE_LIFETIME_BOUND
16509#if LANGUAGE_LEVEL >= 202002L
16510 requires(!Predicate<init_type>::value)
16511#endif
16512// Attributes and the function body may be separated by comments.
16513{
16514 return {};
16515}
16516"#;
16517 let template_return_tree = parser.parse(template_return_source, None).unwrap();
16518 assert!(
16519 cpp_reparsed_members_are_indexable(
16520 template_return_tree.root_node(),
16521 template_return_source
16522 ),
16523 "template-return attribute/requires tree: {}",
16524 template_return_tree.root_node().to_sexp()
16525 );
16526
16527 let no_body_source = r#"
16528std::pair<iterator, bool> insert(init_type&& value)
16529 ABSL_ATTRIBUTE_LIFETIME_BOUND
16530#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
16531 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
16532#endif
16533+ 0;
16534"#;
16535 let no_body_tree = parser.parse(no_body_source, None).unwrap();
16536 assert!(!cpp_reparsed_members_are_indexable(
16537 no_body_tree.root_node(),
16538 no_body_source
16539 ));
16540
16541 let extra_payload_source = r#"
16542pair<int> insert(init_type&& value)
16543 ABSL_ATTRIBUTE_LIFETIME_BOUND
16544#if LANGUAGE_LEVEL >= 202002L
16545 int unrelated;
16546 requires(Predicate<init_type>::value)
16547#endif
16548{
16549 return {};
16550}
16551"#;
16552 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
16553 assert!(!cpp_reparsed_members_are_indexable(
16554 extra_payload_tree.root_node(),
16555 extra_payload_source
16556 ));
16557
16558 let variable_initializer_source = r#"
16559int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
16560#if LANGUAGE_LEVEL >= 202002L
16561 requires(true)
16562#endif
16563{
16564 bad;
16565}
16566"#;
16567 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
16568 assert!(!cpp_reparsed_members_are_indexable(
16569 variable_initializer_tree.root_node(),
16570 variable_initializer_source
16571 ));
16572 }
16573
16574 #[test]
16575 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
16576 let source = r#"namespace absl {
16577ABSL_NAMESPACE_BEGIN namespace container_internal {
16578
16579class raw_hash_set : public Base {
16580 public:
16581 using value_type = int;
16582
16583 template <class U,
16584 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
16585 void insert(U value) { (void)value; }
16586
16587 struct InsertSlot {
16588 raw_hash_set& s;
16589 };
16590};
16591
16592}
16593ABSL_NAMESPACE_END
16594}"#;
16595 let mut parser = tree_sitter::Parser::new();
16596 parser
16597 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16598 .unwrap();
16599 let tree = parser.parse(source, None).unwrap();
16600 let root = tree.root_node();
16601 let outer_namespace = root
16602 .named_children(&mut root.walk())
16603 .find(|child| child.kind() == "namespace_definition")
16604 .expect("outer absl namespace");
16605 let declaration_list = outer_namespace
16606 .child_by_field_name("body")
16607 .expect("outer namespace body");
16608 let sentinel_function = declaration_list
16609 .named_children(&mut declaration_list.walk())
16610 .find(|child| child.kind() == "function_definition")
16611 .expect("malformed namespace sentinel function");
16612 let ancestry = ParentIndex::new(root);
16613 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
16614 .expect("structured nested namespace sentinel");
16615 let fragmented =
16616 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
16617 .expect("fragmented raw_hash_set class");
16618 assert_eq!(fragmented.class_node.kind(), "ERROR");
16619 assert_eq!(fragmented.name, "raw_hash_set");
16620 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
16621
16622 let outer_scope =
16623 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
16624 let mut outer_siblings = Vec::new();
16625 push_cpp_sentinel_sibling_classes(
16626 &mut outer_siblings,
16627 declaration_list,
16628 sentinel.function,
16629 &outer_scope,
16630 source,
16631 &ancestry,
16632 );
16633 let [outer_shadow] = outer_siblings.as_slice() else {
16634 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
16635 };
16636 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
16637 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
16638
16639 let field = " raw_hash_set& s;";
16640 let start = source.find(field).expect("InsertSlot field") + 4;
16641 let node = root
16642 .descendant_for_byte_range(start, start + "raw_hash_set".len())
16643 .expect("raw_hash_set type node");
16644 let recovered = cpp_sentinel_recovered_classes(root, source);
16645 let [deep_class] = recovered.as_slice() else {
16646 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
16647 };
16648 assert_eq!(
16649 deep_class.namespace_scope_components,
16650 vec!["absl", "container_internal"]
16651 );
16652 assert_eq!(
16653 deep_class.scope_components,
16654 vec!["absl", "container_internal", "raw_hash_set"]
16655 );
16656 assert!(
16657 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
16658 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
16659 );
16660
16661 assert_eq!(
16662 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
16663 Some(vec![
16664 "absl".to_string(),
16665 "container_internal".to_string(),
16666 "raw_hash_set".to_string(),
16667 "InsertSlot".to_string(),
16668 ])
16669 );
16670
16671 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
16672 let parsed = parse_cpp_file(&file, source, &tree);
16673 let raw_hash_set = parsed
16674 .declarations()
16675 .iter()
16676 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
16677 .expect("recovered raw_hash_set class");
16678 assert_eq!(
16679 raw_hash_set.fq_name(),
16680 "absl::container_internal.raw_hash_set",
16681 "the recovered declaration must publish under the deeper sentinel namespace"
16682 );
16683 assert_eq!(
16684 parsed.raw_supertypes.get(raw_hash_set),
16685 Some(&vec!["Base".to_string()]),
16686 "the structured base clause on the fragmented ERROR prefix must survive publication"
16687 );
16688 assert!(
16689 parsed.materialization_records.iter().any(|record| matches!(
16690 record,
16691 MaterializationRecord::RecoveredDeclaration { recovery, unit }
16692 if unit == raw_hash_set && *recovery == deep_class.class_range
16693 )),
16694 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
16695 parsed.materialization_records
16696 );
16697 }
16698
16699 #[test]
16726 fn the_parent_index_answers_what_tree_sitter_answers() {
16727 const SHAPES: [&str; 5] = [
16728 "namespace outer { namespace inner { struct Tag { int field; }; } }",
16729 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
16730 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
16731 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
16732 "class API Broken : public First, public Second {\n void member();\n",
16733 ];
16734 for source in SHAPES {
16735 let mut parser = tree_sitter::Parser::new();
16736 parser
16737 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16738 .unwrap();
16739 let tree = parser.parse(source, None).unwrap();
16740 let root = tree.root_node();
16741 let ancestry = ParentIndex::new(root);
16742 let mut nodes = 0usize;
16743 let mut stack = vec![root];
16744 while let Some(node) = stack.pop() {
16745 nodes += 1;
16746 assert_eq!(
16747 node.parent().map(|parent| parent.id()),
16748 ancestry.parent(node).map(|parent| parent.id()),
16749 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
16750 );
16751 let mut cursor = node.walk();
16752 stack.extend(node.children(&mut cursor));
16753 }
16754 assert!(nodes > 1, "{source:?} produced no tree to compare");
16755 }
16756 }
16757
16758 #[test]
16765 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
16766 const DEPTH: usize = 64;
16767 let mut source = String::new();
16768 for level in 0..DEPTH {
16769 writeln!(source, "namespace n{level} {{").unwrap();
16770 }
16771 source.push_str("int deepest(int value);\n");
16772 for _ in 0..DEPTH {
16773 source.push_str("}\n");
16774 }
16775
16776 let mut parser = tree_sitter::Parser::new();
16777 parser
16778 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16779 .unwrap();
16780 let tree = parser.parse(&source, None).unwrap();
16781 let root = tree.root_node();
16782 let ancestry = ParentIndex::new(root);
16783 let mut function_declarator = None;
16784 walk_named_tree_preorder(root, true, |node| {
16785 if node.kind() == "function_declarator" {
16786 function_declarator = Some(node);
16787 WalkControl::Break
16788 } else {
16789 WalkControl::Continue
16790 }
16791 });
16792 let function_declarator = function_declarator.expect("deepest function declarator");
16793 let ancestor_count =
16794 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
16795
16796 ancestry.reset_parent_query_count_for_test();
16797 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
16798 assert_eq!(DEPTH, lexical_scope.len());
16799 assert_eq!(
16800 ancestor_count + 1,
16801 ancestry.parent_query_count_for_test(),
16802 "lexical-scope ancestry bypassed the parent index"
16803 );
16804
16805 ancestry.reset_parent_query_count_for_test();
16806 assert_eq!(
16807 DispatchExtensibility::Closed,
16808 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
16809 );
16810 assert_eq!(
16811 ancestor_count,
16812 ancestry.parent_query_count_for_test(),
16813 "dispatch ancestry bypassed the parent index"
16814 );
16815
16816 ancestry.reset_parent_query_count_for_test();
16817 assert_eq!(
16818 CallableLinkage::External,
16819 cpp_callable_linkage(function_declarator, &source, &ancestry)
16820 );
16821 assert_eq!(
16822 ancestor_count + 1,
16823 ancestry.parent_query_count_for_test(),
16824 "linkage ancestry bypassed the parent index"
16825 );
16826
16827 ancestry.reset_parent_query_count_for_test();
16828 assert!(!cpp_callable_is_structural_constructor(
16829 function_declarator,
16830 &source,
16831 &ancestry
16832 ));
16833 assert_eq!(
16834 ancestor_count + 1,
16835 ancestry.parent_query_count_for_test(),
16836 "constructor ancestry bypassed the parent index"
16837 );
16838 }
16839
16840 #[test]
16845 fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
16846 for aggregates in [64usize, 512] {
16847 let mut source =
16848 String::from("typedef unsigned long long u64;\nnamespace generated {\n");
16849 for index in 0..aggregates {
16850 writeln!(source, "struct tag{index};").unwrap();
16851 }
16852 for index in (0..aggregates).rev() {
16853 writeln!(
16854 source,
16855 "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
16856 )
16857 .unwrap();
16858 }
16859 source.push_str("}\n");
16860
16861 start_code_unit_removal_scan_probe();
16862 let parsed = parse_cpp_declarations(&source, "vmlinux.h");
16863 let scanned = finish_code_unit_removal_scan_probe();
16864
16865 let expected_names: Vec<String> = (0..aggregates)
16866 .rev()
16867 .map(|index| format!("tag{index}"))
16868 .collect();
16869 let top_level_names: Vec<String> = parsed
16870 .top_level_declarations
16871 .iter()
16872 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
16873 .map(|unit| unit.short_name().to_string())
16874 .collect();
16875 let namespace = parsed
16876 .declarations()
16877 .iter()
16878 .find(|unit| {
16879 unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
16880 })
16881 .expect("generated namespace should be declared");
16882 let child_names: Vec<String> = parsed.children[namespace]
16883 .iter()
16884 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
16885 .map(|unit| unit.short_name().to_string())
16886 .collect();
16887 assert_eq!(
16888 aggregates,
16889 parsed
16890 .declarations()
16891 .iter()
16892 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
16893 .count(),
16894 "every aggregate must still be declared at {aggregates} aggregates"
16895 );
16896 assert_eq!(expected_names, top_level_names);
16897 assert_eq!(expected_names, child_names);
16898 assert_eq!(
16899 0, scanned,
16900 "replacing {aggregates} forward declarations must compact their shared lists once"
16901 );
16902 }
16903 }
16904
16905 #[test]
16906 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
16907 const DISTINCT_PER_KIND: usize = 64;
16908 let mut source = String::new();
16909 for index in 0..DISTINCT_PER_KIND {
16910 writeln!(source, "typedef int Alias{index};").unwrap();
16911 }
16912 writeln!(source, "typedef long Alias0;").unwrap();
16913 for index in 0..DISTINCT_PER_KIND {
16914 writeln!(source, "#define MACRO_{index} {index}").unwrap();
16915 }
16916 writeln!(source, "#define MACRO_0 duplicate").unwrap();
16917 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
16918
16919 let mut parser = tree_sitter::Parser::new();
16920 parser
16921 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16922 .unwrap();
16923 let tree = parser.parse(&source, None).unwrap();
16924 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
16925
16926 start_declaration_identity_comparison_probe();
16927 let parsed = parse_cpp_file(&file, &source, &tree);
16928 let comparisons = finish_declaration_identity_comparison_probe();
16929
16930 assert_eq!(
16931 DISTINCT_PER_KIND + 1,
16932 parsed
16933 .declarations()
16934 .iter()
16935 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
16936 .count(),
16937 "every physical typedef alias declaration must be retained so \
16938 conditional branch guards stay available to the resolver"
16939 );
16940 assert_eq!(
16941 DISTINCT_PER_KIND + 1,
16942 parsed
16943 .declarations()
16944 .iter()
16945 .filter(|unit| {
16946 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
16947 })
16948 .count(),
16949 "distinct macro redefinitions must remain available to temporal lookup"
16950 );
16951 assert_eq!(
16952 2,
16953 parsed
16954 .declarations()
16955 .iter()
16956 .filter(|unit| {
16957 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
16958 })
16959 .count(),
16960 "function overloads must remain distinct"
16961 );
16962
16963 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
16964 assert!(
16965 comparisons <= dedup_inputs * 4,
16966 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
16967 );
16968 }
16969
16970 #[test]
16971 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
16972 let source = r#"namespace absl {
16973ABSL_NAMESPACE_BEGIN namespace container_internal {
16974template <typename T>
16975class broken {
16976 public:
16977 using value_type = T;
16978 T operator->() const { return &operator*(); }
16979 using alias = value_type;
16980};
16981}
16982}
16983"#;
16984 let mut parser = tree_sitter::Parser::new();
16985 parser
16986 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16987 .unwrap();
16988 let tree = parser.parse(source, None).unwrap();
16989 let broken = find_class_named(tree.root_node(), source, "broken")
16990 .expect("the positive fixture must expose the broken class node");
16991 assert!(
16992 broken.has_error(),
16993 "the positive fixture must retain an internal parser error"
16994 );
16995 assert!(
16996 cpp_complete_class_body_close(broken).is_some(),
16997 "the positive fixture must expose a real class body close"
16998 );
16999 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
17000 assert!(
17001 recovered.iter().any(|class| {
17002 class.scope_components == ["absl", "container_internal", "broken"]
17003 }),
17004 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
17005 );
17006 }
17007
17008 #[test]
17009 fn sentinel_recovery_keeps_members_after_nested_body_close() {
17010 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
17011NLOHMANN_BASIC_JSON_TPL_DECLARATION
17012class basic_json {
17013 private:
17014 union storage {
17015 int value;
17016 } data;
17017 public:
17018 using late_alias = int;
17019 late_alias value() const;
17020};
17021NLOHMANN_JSON_NAMESPACE_END
17022"#;
17023 let mut parser = tree_sitter::Parser::new();
17024 parser
17025 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17026 .unwrap();
17027 let tree = parser.parse(source, None).unwrap();
17028 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
17029 let basic_json = recovered
17030 .iter()
17031 .find(|class| {
17032 class
17033 .scope_components
17034 .last()
17035 .is_some_and(|name| name == "basic_json")
17036 })
17037 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
17038 let late_alias = source
17039 .find("late_alias value")
17040 .expect("late alias reference");
17041 assert!(
17042 basic_json.class_range.start_byte < late_alias
17043 && late_alias < basic_json.class_range.end_byte,
17044 "the recovered class range must include members after a nested close: {basic_json:#?}"
17045 );
17046 }
17047
17048 #[test]
17049 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
17050 let source = r#"namespace absl {
17051ABSL_NAMESPACE_BEGIN namespace container_internal {
17052template <typename T>
17053class broken {
17054 public:
17055 using value_type = T;
17056 T operator->() const { return &operator*(); }
17057}
17058}
17059"#;
17060 let mut parser = tree_sitter::Parser::new();
17061 parser
17062 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17063 .unwrap();
17064 let tree = parser.parse(source, None).unwrap();
17065 let broken = find_class_named(tree.root_node(), source, "broken")
17066 .expect("the negative fixture must expose the malformed class node");
17067 assert!(
17068 broken.has_error(),
17069 "the negative fixture must retain a parser error"
17070 );
17071 assert!(
17072 cpp_complete_class_body_close(broken).is_none(),
17073 "the malformed class must not expose a real body close"
17074 );
17075 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
17076 assert!(
17077 recovered
17078 .iter()
17079 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
17080 "an incomplete class must not borrow the namespace close: {recovered:#?}"
17081 );
17082 }
17083
17084 #[test]
17085 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
17086 let source = r#"namespace absl {
17087ABSL_NAMESPACE_BEGIN namespace container_internal {
17088template <typename T>
17089struct broken {
17090 using value_type = T;
17091};
17092}
17093
17094#ifdef OWNER_DEF
17095template <typename T>
17096typename broken<T>::value_type broken<T>::method() {
17097 value_type value{};
17098 return value;
17099}
17100#endif
17101
17102namespace sibling {
17103template <typename T>
17104typename broken<T>::value_type broken<T>::other() {
17105 value_type value{};
17106 return value;
17107}
17108}
17109
17110ABSL_NAMESPACE_END
17111}
17112"#;
17113 let mut parser = tree_sitter::Parser::new();
17114 parser
17115 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17116 .unwrap();
17117 let tree = parser.parse(source, None).unwrap();
17118 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
17119 let broken = recovered
17120 .iter()
17121 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
17122 .expect("the sentinel class must be recovered");
17123 let method_start = source
17124 .find("typename broken<T>::value_type broken<T>::method()")
17125 .expect("guarded sibling owner");
17126 let method_end = source[method_start..]
17127 .find("\n}")
17128 .map(|offset| method_start + offset + 2)
17129 .expect("guarded sibling owner close");
17130 assert!(
17131 broken
17132 .owner_ranges
17133 .iter()
17134 .any(|owner| owner.range.start_byte <= method_start
17135 && method_end <= owner.range.end_byte),
17136 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
17137 );
17138 let sibling_start = source
17139 .find("typename broken<T>::value_type broken<T>::other()")
17140 .expect("nested namespace sibling owner");
17141 assert!(
17142 broken
17143 .owner_ranges
17144 .iter()
17145 .all(|owner| owner.range.start_byte > sibling_start
17146 || owner.range.end_byte <= sibling_start),
17147 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
17148 );
17149 }
17150
17151 #[test]
17152 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
17153 let source = r#"#ifdef OUTER
17154namespace absl {
17155ABSL_NAMESPACE_BEGIN namespace container_internal {
17156template <typename T>
17157struct broken {
17158 using value_type = T;
17159};
17160}
17161}
17162
17163#ifdef OWNER_DEF
17164template <typename T>
17165typename broken<T>::value_type broken<T>::method() {
17166 value_type value{};
17167 return value;
17168}
17169#endif
17170#endif
17171"#;
17172 let mut parser = tree_sitter::Parser::new();
17173 parser
17174 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17175 .unwrap();
17176 let tree = parser.parse(source, None).unwrap();
17177 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
17178 let broken = recovered
17179 .iter()
17180 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
17181 .expect("the sentinel class must be recovered");
17182 let method_start = source
17183 .find("typename broken<T>::value_type broken<T>::method()")
17184 .expect("outer sibling owner");
17185 assert!(
17186 broken
17187 .owner_ranges
17188 .iter()
17189 .all(|owner| owner.range.start_byte > method_start
17190 || owner.range.end_byte <= method_start),
17191 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
17192 );
17193 }
17194
17195 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
17197 let mut signatures = parsed
17198 .declarations()
17199 .iter()
17200 .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
17201 .filter_map(|unit| unit.signature().map(str::to_string))
17202 .collect::<Vec<_>>();
17203 signatures.sort();
17204 signatures.dedup();
17205 signatures
17206 }
17207
17208 #[test]
17209 fn callable_parameter_types_come_from_the_ast_parameter_list() {
17210 let source = r#"
17211template <typename T, ENABLE_BYTES(T)>
17212Vec256<T> DupOdd(Vec256<T> value) { return value; }
17213
17214struct Visitor {
17215 void fail(this auto const& self) {}
17216};
17217"#;
17218 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
17219 let dup_odd = parsed
17220 .declarations()
17221 .iter()
17222 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
17223 .expect("DupOdd declaration");
17224 assert_eq!(
17225 dup_odd.signature(),
17226 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
17227 );
17228 assert_eq!(
17229 parsed
17230 .signature_metadata
17231 .get(dup_odd)
17232 .and_then(|metadata| metadata.first())
17233 .and_then(SignatureMetadata::callable_parameter_types),
17234 Some(["Vec256<T>".to_string()].as_slice())
17235 );
17236
17237 let fail = parsed
17238 .declarations()
17239 .iter()
17240 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
17241 .expect("explicit-object member");
17242 assert_eq!(fail.signature(), Some("(const this auto &)"));
17243 let metadata = parsed
17244 .signature_metadata
17245 .get(fail)
17246 .and_then(|metadata| metadata.first())
17247 .expect("explicit-object signature metadata");
17248 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
17249 assert!(
17250 metadata
17251 .callable_arity()
17252 .is_some_and(|arity| arity.accepts(0))
17253 );
17254 }
17255
17256 #[test]
17257 fn trailing_qualifiers_survive_parameter_list_whitespace() {
17258 let source = r#"
17263struct Widget {
17264 bool multiline(int settings, int supprs) const;
17265 bool doublespace(int settings, int supprs) const;
17266 bool noexcept_multiline(int settings, int supprs) noexcept;
17267 bool ref_multiline(int settings, int supprs) &&;
17268};
17269bool
17270Widget::multiline (int settings,
17271 int supprs) const
17272{ return settings + supprs > 0; }
17273bool Widget::doublespace(int settings, int supprs) const { return true; }
17274bool Widget::noexcept_multiline(int settings,
17275 int supprs) noexcept { return true; }
17276bool Widget::ref_multiline(int settings,
17277 int supprs) && { return true; }
17278"#;
17279 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
17280 assert_eq!(
17281 vec!["(int, int) const".to_string()],
17282 identity_signatures(&parsed, "Widget.multiline")
17283 );
17284 assert_eq!(
17285 vec!["(int, int) const".to_string()],
17286 identity_signatures(&parsed, "Widget.doublespace")
17287 );
17288 assert_eq!(
17289 vec!["(int, int) noexcept".to_string()],
17290 identity_signatures(&parsed, "Widget.noexcept_multiline")
17291 );
17292 assert_eq!(
17293 vec!["(int, int) &&".to_string()],
17294 identity_signatures(&parsed, "Widget.ref_multiline")
17295 );
17296 }
17297
17298 #[test]
17299 fn macro_fragmented_plain_class_keeps_following_member_signature() {
17300 let source = r#"
17301struct CString {};
17302class CMessage {
17303public:
17304 CString GetParams(unsigned int index, unsigned int length = -1) const
17305 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
17306 return GetParamsColon(index, length);
17307 }
17308 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
17309};
17310CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
17311 return {};
17312}
17313"#;
17314 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
17315 assert_eq!(
17316 vec!["(unsigned int, unsigned int) const".to_string()],
17317 identity_signatures(&parsed, "CMessage.GetParamsColon")
17318 );
17319 }
17320
17321 #[test]
17322 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
17323 let source = r#"
17324#pragma once
17325#define DEMO_DEPRECATED(message)
17326namespace demo {
17327struct Base {
17328 static int aligned(int value) { return value; }
17329 int legacy(int value) const
17330 DEMO_DEPRECATED("use replacement()") { return value; }
17331 int replacement() const;
17332 void run(int value);
17333};
17334struct OtherBase {
17335 void run(int value);
17336 static int aligned(int value) { return value; }
17337};
17338struct Derived : Base {};
17339struct Override : Base {
17340 void run(int value);
17341 static int aligned(int value) { return value; }
17342};
17343struct RecoveredOverride : Base {
17344 int legacy(int value) const
17345 DEMO_DEPRECATED("use replacement()") { return value; }
17346 void run(int value);
17347};
17348struct Hidden : Base {
17349 void run(int first, int second);
17350 static int aligned(int first, int second) { return first + second; }
17351};
17352struct Ambiguous : Base, OtherBase {};
17353}
17354struct Global {};
17355"#;
17356 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
17357 let declarations = parsed.declarations();
17358 let fq_names = declarations
17359 .iter()
17360 .map(|unit| unit.fq_name())
17361 .collect::<std::collections::BTreeSet<_>>();
17362
17363 for expected in [
17364 "demo.Base",
17365 "demo.Base.aligned",
17366 "demo.Base.legacy",
17367 "demo.Base.replacement",
17368 "demo.Base.run",
17369 "demo.Derived",
17370 "demo.OtherBase",
17371 "demo.Override",
17372 "demo.RecoveredOverride",
17373 "demo.Hidden",
17374 "demo.Ambiguous",
17375 "Global",
17376 ] {
17377 assert!(
17378 fq_names.contains(expected),
17379 "missing {expected} from namespaced macro fragment: {declarations:#?}"
17380 );
17381 }
17382 assert!(
17383 !fq_names.contains("Derived"),
17384 "following class escaped its namespace: {declarations:#?}"
17385 );
17386 assert!(
17387 !fq_names.contains("demo.Global"),
17388 "global class crossed the recovered namespace boundary: {declarations:#?}"
17389 );
17390 }
17391
17392 #[test]
17393 fn trailing_qualifiers_still_separate_genuine_overloads() {
17394 let source = r#"
17397struct Widget {
17398 int* slot(int index);
17399 const int* slot(int index) const;
17400 int log(int severity) &;
17401 int log(int severity) &&;
17402};
17403"#;
17404 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
17405 assert_eq!(
17406 vec!["(int)".to_string(), "(int) const".to_string()],
17407 identity_signatures(&parsed, "Widget.slot")
17408 );
17409 assert_eq!(
17410 vec!["(int) &".to_string(), "(int) &&".to_string()],
17411 identity_signatures(&parsed, "Widget.log")
17412 );
17413 }
17414
17415 #[test]
17416 fn virtual_specifier_is_not_part_of_the_identity_signature() {
17417 let source = r#"
17420struct Base {
17421 virtual void run(int value) const;
17422};
17423struct Widget : Base {
17424 void run(int value) const override;
17425};
17426void Widget::run(int value) const {}
17427"#;
17428 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
17429 assert_eq!(
17430 vec!["(int) const".to_string()],
17431 identity_signatures(&parsed, "Widget.run")
17432 );
17433 }
17434
17435 #[test]
17436 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
17437 let source = r#"
17441struct Widget {
17442 bool value_params(const int settings, const int supprs);
17443 void pointee_const(const int* p);
17444 void pointer_const(int* const p);
17445 void both_const(const int* const p);
17446 void reference_const(const int& p);
17447 void array_const(const int values[4]);
17448};
17449bool Widget::value_params(int settings, int supprs) { return true; }
17450void Widget::pointer_const(int* p) {}
17451void Widget::both_const(const int* p) {}
17452"#;
17453 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
17454 assert_eq!(
17455 vec!["(int, int)".to_string()],
17456 identity_signatures(&parsed, "Widget.value_params")
17457 );
17458 assert_eq!(
17459 vec!["(int *)".to_string()],
17460 identity_signatures(&parsed, "Widget.pointer_const")
17461 );
17462 assert_eq!(
17463 vec!["(const int *)".to_string()],
17464 identity_signatures(&parsed, "Widget.both_const")
17465 );
17466 assert_eq!(
17468 vec!["(const int *)".to_string()],
17469 identity_signatures(&parsed, "Widget.pointee_const")
17470 );
17471 assert_eq!(
17472 vec!["(const int &)".to_string()],
17473 identity_signatures(&parsed, "Widget.reference_const")
17474 );
17475 assert_eq!(
17476 vec!["(const int [4])".to_string()],
17477 identity_signatures(&parsed, "Widget.array_const")
17478 );
17479 }
17480
17481 #[test]
17482 fn top_level_parameter_const_still_separates_pointee_overloads() {
17483 let source = r#"
17484struct Widget {
17485 void take(const int* p);
17486 void take(int* p);
17487};
17488"#;
17489 let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
17490 assert_eq!(
17491 vec!["(const int *)".to_string(), "(int *)".to_string()],
17492 identity_signatures(&parsed, "Widget.take")
17493 );
17494 }
17495
17496 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
17497 let mut parser = tree_sitter::Parser::new();
17498 parser
17499 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17500 .unwrap();
17501 let tree = parser.parse(source, None).unwrap();
17502 let start = source.find(callable_name).expect("callable declaration");
17503 let declarator =
17504 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
17505 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
17506 }
17507
17508 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
17509 let mut shapes = comparable_shapes(source, callable_name);
17510 assert_eq!(1, shapes.len(), "{shapes:?}");
17511 match shapes.remove(0) {
17512 CppComparableSlot::Shape(shape) => shape,
17513 other => panic!("expected a comparable shape, got {other:?}"),
17514 }
17515 }
17516
17517 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
17518 let mut current = shape.root();
17519 loop {
17520 match shape.node(current) {
17521 CppComparableNode::Named { .. } => return shape.node(current),
17522 CppComparableNode::Pointer { inner, .. }
17523 | CppComparableNode::Reference { inner }
17524 | CppComparableNode::Array { inner } => current = *inner,
17525 CppComparableNode::Generic { base, .. } => current = *base,
17526 }
17527 }
17528 }
17529
17530 #[test]
17531 fn comparable_shape_keeps_pointee_const() {
17532 assert_ne!(
17533 sole_comparable_shape("void f(const char* p);", "f("),
17534 sole_comparable_shape("void f(char* p);", "f(")
17535 );
17536 }
17537
17538 #[test]
17539 fn comparable_shape_keeps_inner_pointer_const() {
17540 assert_ne!(
17541 sole_comparable_shape("void f(int** p);", "f("),
17542 sole_comparable_shape("void f(int* const* p);", "f(")
17543 );
17544 }
17545
17546 #[test]
17547 fn comparable_shape_drops_top_level_pointer_const() {
17548 assert_eq!(
17549 sole_comparable_shape("void f(int* const p);", "f("),
17550 sole_comparable_shape("void f(int* p);", "f(")
17551 );
17552 }
17553
17554 #[test]
17555 fn comparable_shape_drops_top_level_base_const() {
17556 assert_eq!(
17557 sole_comparable_shape("void f(const int p);", "f("),
17558 sole_comparable_shape("void f(int p);", "f(")
17559 );
17560 }
17561
17562 #[test]
17563 fn comparable_shape_decays_top_level_array_to_pointer() {
17564 assert_eq!(
17565 sole_comparable_shape("void f(int a[3]);", "f("),
17566 sole_comparable_shape("void f(int* a);", "f(")
17567 );
17568 assert_eq!(
17569 sole_comparable_shape("void f(int* a[3]);", "f("),
17570 sole_comparable_shape("void f(int** a);", "f(")
17571 );
17572 }
17573
17574 #[test]
17575 fn comparable_shape_keeps_array_behind_pointer() {
17576 assert_ne!(
17577 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
17578 sole_comparable_shape("struct S { void f(int** a); };", "f(")
17579 );
17580 }
17581
17582 #[test]
17583 fn comparable_shape_records_written_name_and_lexical_scope() {
17584 let declared =
17585 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
17586 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
17587 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
17588 panic!("named leaf");
17589 };
17590 assert_eq!(["Msg".to_string()].as_slice(), name.path());
17591 assert_eq!(
17592 ["ns".to_string(), "S".to_string()].as_slice(),
17593 name.lexical_scope()
17594 );
17595 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
17596 panic!("named leaf");
17597 };
17598 assert_eq!(
17599 ["ns".to_string(), "Msg".to_string()].as_slice(),
17600 name.path()
17601 );
17602 assert!(name.lexical_scope().is_empty());
17603 assert_ne!(declared, defined);
17604 }
17605
17606 #[test]
17607 fn comparable_shape_marks_sized_primitive_leaf() {
17608 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
17609 let CppComparableNode::Named {
17610 name, primitive, ..
17611 } = comparable_named_leaf(&shape)
17612 else {
17613 panic!("named leaf");
17614 };
17615 assert!(primitive);
17616 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
17617 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
17618 }
17619
17620 #[test]
17621 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
17622 assert_eq!(
17623 vec![CppComparableSlot::Unstructured],
17624 comparable_shapes("void f(void (*cb)(int));", "f(")
17625 );
17626 }
17627
17628 #[test]
17629 fn comparable_shape_reports_ellipsis_slot() {
17630 let shapes = comparable_shapes("void f(int a, ...);", "f(");
17631 assert_eq!(2, shapes.len(), "{shapes:?}");
17632 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
17633 }
17634
17635 #[test]
17636 fn comparable_shape_keeps_template_argument_const() {
17637 assert_ne!(
17638 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
17639 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
17640 );
17641 }
17642
17643 #[test]
17646 fn c_file_mints_aggregate_member_tag_at_file_scope() {
17647 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
17648 let parsed = parse_cpp_declarations(source, "x.c");
17649 let declarations = parsed.declarations();
17650
17651 assert!(
17652 declarations
17653 .iter()
17654 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
17655 "expected a file-scope inner tag, got {declarations:?}"
17656 );
17657 assert!(
17658 declarations
17659 .iter()
17660 .all(|unit| unit.fq_name() != "outer$inner"),
17661 "expected no nested identity, got {declarations:?}"
17662 );
17663 assert!(
17664 declarations
17665 .iter()
17666 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
17667 );
17668 assert!(
17670 declarations
17671 .iter()
17672 .any(|unit| unit.fq_name() == "inner.value")
17673 );
17674 assert!(
17675 declarations
17676 .iter()
17677 .any(|unit| unit.fq_name() == "outer.item")
17678 );
17679
17680 let outer = declarations
17681 .iter()
17682 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
17683 .expect("outer");
17684 assert!(
17685 parsed
17686 .children
17687 .get(outer)
17688 .into_iter()
17689 .flatten()
17690 .all(|child| child.fq_name() != "inner"),
17691 "the tag must not hang off the aggregate it is written inside: {:?}",
17692 parsed.children
17693 );
17694 }
17695
17696 #[test]
17700 fn header_and_cpp_files_keep_nested_tag_identity() {
17701 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
17702 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
17703 let parsed = parse_cpp_declarations(source, name);
17704 let declarations = parsed.declarations();
17705 assert!(
17706 declarations
17707 .iter()
17708 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
17709 "{name} must keep the nested identity, got {declarations:?}"
17710 );
17711 assert!(
17712 declarations.iter().all(|unit| unit.fq_name() != "inner"),
17713 "{name} must not mint a file-scope tag, got {declarations:?}"
17714 );
17715 assert!(
17716 declarations
17717 .iter()
17718 .any(|unit| unit.fq_name() == "outer$inner.value")
17719 );
17720 }
17721 }
17722
17723 #[test]
17725 fn uppercase_c_extension_keeps_cpp_tag_scope() {
17726 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
17727 let parsed = parse_cpp_declarations(source, "x.C");
17728 assert!(
17729 parsed
17730 .declarations()
17731 .iter()
17732 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
17733 );
17734 }
17735
17736 #[test]
17739 fn c_file_mints_every_nesting_level_at_file_scope() {
17740 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
17741 let parsed = parse_cpp_declarations(source, "z.c");
17742 let declarations = parsed.declarations();
17743
17744 for tag in ["a", "b", "c"] {
17745 assert!(
17746 declarations
17747 .iter()
17748 .any(|unit| unit.is_class() && unit.fq_name() == tag),
17749 "expected a file-scope {tag}, got {declarations:?}"
17750 );
17751 }
17752 assert!(
17753 declarations
17754 .iter()
17755 .all(|unit| !unit.fq_name().contains('$')),
17756 "no level may keep a nested identity, got {declarations:?}"
17757 );
17758 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
17760 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
17761 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
17762 }
17763
17764 #[test]
17767 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
17768 let source = "struct outer { enum color { RED, GREEN } c; };\n";
17769 let parsed = parse_cpp_declarations(source, "e.c");
17770 let declarations = parsed.declarations();
17771
17772 let color = declarations
17773 .iter()
17774 .find(|unit| unit.is_class() && unit.fq_name() == "color")
17775 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
17776 assert!(
17777 declarations
17778 .iter()
17779 .all(|unit| unit.fq_name() != "outer$color")
17780 );
17781 for enumerator in ["color.RED", "color.GREEN"] {
17782 assert!(
17783 declarations.iter().any(|unit| unit.fq_name() == enumerator),
17784 "expected {enumerator}, got {declarations:?}"
17785 );
17786 }
17787 let children = parsed
17788 .children
17789 .get(color)
17790 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
17791 assert!(
17792 ["color.RED", "color.GREEN"]
17793 .iter()
17794 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
17795 "enumerators must hang off their enum: {children:?}"
17796 );
17797 }
17798
17799 #[test]
17800 fn c_file_mints_member_list_union_at_file_scope() {
17801 let source = "struct outer { union inner { int a; float b; } item; };\n";
17802 let parsed = parse_cpp_declarations(source, "u.c");
17803 let declarations = parsed.declarations();
17804 assert!(
17805 declarations
17806 .iter()
17807 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
17808 "expected a file-scope inner union, got {declarations:?}"
17809 );
17810 assert!(
17811 declarations
17812 .iter()
17813 .all(|unit| unit.fq_name() != "outer$inner")
17814 );
17815 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
17816 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
17817 }
17818
17819 #[test]
17822 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
17823 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
17824 let parsed = parse_cpp_declarations(source, "n.c");
17825 let declarations = parsed.declarations();
17826 let inner = declarations
17827 .iter()
17828 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
17829 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
17830 assert_eq!(inner.package_name(), "ns");
17831 assert!(
17832 declarations
17833 .iter()
17834 .all(|unit| unit.fq_name() != "ns.outer$inner")
17835 );
17836 }
17837
17838 #[test]
17843 fn function_local_tags_are_unchanged_in_both_dialects() {
17844 let source =
17845 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
17846 for name in ["y.c", "y.cpp"] {
17847 let parsed = parse_cpp_declarations(source, name);
17848 let declarations = parsed.declarations();
17849 assert!(
17850 declarations
17851 .iter()
17852 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
17853 "{name}: {declarations:?}"
17854 );
17855 for tag in ["localtag", "deeper", "localtag$deeper"] {
17856 assert!(
17857 declarations.iter().all(|unit| unit.fq_name() != tag),
17858 "{name} must not mint {tag}, got {declarations:?}"
17859 );
17860 }
17861 }
17862 }
17863
17864 #[test]
17867 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
17868 let source = "typedef struct { int v; } T;\n";
17869 for name in ["t.c", "t.cpp"] {
17870 let parsed = parse_cpp_declarations(source, name);
17871 let declarations = parsed.declarations();
17872 assert!(
17873 declarations
17874 .iter()
17875 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
17876 "{name}: {declarations:?}"
17877 );
17878 }
17879 }
17880
17881 #[test]
17882 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
17883 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
17884 let parsed = parse_cpp_declarations(source, "socket.c");
17885 let declarations = parsed.declarations();
17886 assert_eq!(
17887 declarations
17888 .iter()
17889 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
17890 .count(),
17891 1,
17892 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
17893 );
17894 for expected in [
17895 "PAL_HANDLE",
17896 "PAL_HANDLE.sock",
17897 "PAL_HANDLE$sock",
17898 "PAL_HANDLE$sock.ops",
17899 ] {
17900 assert!(
17901 declarations.iter().any(|unit| unit.fq_name() == expected),
17902 "expected {expected}, got {declarations:?}"
17903 );
17904 }
17905 }
17906
17907 #[test]
17910 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
17911 let source = "class outer { class inner { int v; }; };\n";
17912 let c_parsed = parse_cpp_declarations(source, "k.c");
17913 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
17914 let c_declarations = c_parsed.declarations();
17915 let cpp_declarations = cpp_parsed.declarations();
17916 assert!(
17917 c_declarations
17918 .iter()
17919 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
17920 "{c_declarations:?}"
17921 );
17922 assert_eq!(
17923 c_declarations
17924 .iter()
17925 .map(|unit| unit.fq_name())
17926 .collect::<std::collections::BTreeSet<_>>(),
17927 cpp_declarations
17928 .iter()
17929 .map(|unit| unit.fq_name())
17930 .collect::<std::collections::BTreeSet<_>>()
17931 );
17932 }
17933
17934 fn namespace_forward_scan_agreement(source: &str) -> usize {
17948 let mut parser = tree_sitter::Parser::new();
17949 parser
17950 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17951 .unwrap();
17952 let tree = parser.parse(source, None).unwrap();
17953 let root = tree.root_node();
17954 let ancestry = ParentIndex::new(root);
17955
17956 let mut nodes = Vec::new();
17957 let mut names = std::collections::BTreeSet::new();
17958 let mut cursor = root.walk();
17959 let mut stack = vec![root];
17960 while let Some(node) = stack.pop() {
17961 if matches!(
17962 node.kind(),
17963 "class_specifier" | "struct_specifier" | "union_specifier"
17964 ) && let Some(name) = class_like_name(node, source, &ancestry)
17965 {
17966 names.insert(name);
17967 }
17968 nodes.push(node);
17969 stack.extend(node.named_children(&mut cursor));
17970 }
17971 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
17972 assert!(!names.is_empty(), "fixture declares no class-like name");
17973
17974 let mut answered = 0usize;
17975 for reversed in [false, true] {
17976 let mut scan = CppNamespaceForwardScan::default();
17977 let ordered: Vec<_> = if reversed {
17978 nodes.iter().rev().copied().collect()
17979 } else {
17980 nodes.clone()
17981 };
17982 answered = 0;
17983 for node in ordered {
17984 for name in &names {
17985 scan.advance_to(root, node.start_byte(), source, &ancestry);
17986 let carried = scan.unique_earlier_forward(name, node);
17987 assert_eq!(
17988 carried,
17989 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
17990 "carried-forward scan and prefix scan disagree about {name} at \
17991 {} node starting at byte {} (reversed order: {reversed})",
17992 node.kind(),
17993 node.start_byte()
17994 );
17995 answered += usize::from(carried.is_some());
17996 }
17997 }
17998 }
17999 answered
18000 }
18001
18002 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
18009namespace ns {
18010class Widget;
18011class Gadget;
18012int x = ;
18013}
18014class API Widget {
18015public:
18016 void first();
18017};
18018class API Gadget {
18019public:
18020 void second();
18021};
18022"#;
18023
18024 #[test]
18025 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
18026 assert!(
18027 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
18028 "the fixture must actually reach the namespace-borrow path"
18029 );
18030
18031 for source in [
18036 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
18037 r#"#define API
18038namespace ns {
18039class Widget;
18040class Widget;
18041int x = ;
18042}
18043class API Widget {
18044public:
18045 void method();
18046};
18047"#,
18048 r#"#define API
18049namespace ns {
18050void host() {
18051 class Widget;
18052}
18053int x = ;
18054}
18055class API Widget {
18056public:
18057 void method();
18058};
18059"#,
18060 ] {
18061 assert_eq!(
18062 namespace_forward_scan_agreement(source),
18063 0,
18064 "no borrow is justified here: {source}"
18065 );
18066 }
18067 }
18068
18069 #[test]
18073 fn carried_forward_namespace_scan_folds_each_node_once() {
18074 let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
18075 let mut parser = tree_sitter::Parser::new();
18076 parser
18077 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18078 .unwrap();
18079 let tree = parser.parse(source, None).unwrap();
18080 let root = tree.root_node();
18081 let ancestry = ParentIndex::new(root);
18082
18083 let mut incremental = CppNamespaceForwardScan::default();
18084 for cutoff in 0..=source.len() {
18085 incremental.advance_to(root, cutoff, source, &ancestry);
18086 }
18087 let mut whole = CppNamespaceForwardScan::default();
18088 whole.advance_to(root, source.len(), source, &ancestry);
18089
18090 let mut incremental_shape: Vec<_> = incremental
18091 .forwards
18092 .iter()
18093 .map(|(name, forwards)| {
18094 (
18095 name.clone(),
18096 forwards
18097 .iter()
18098 .map(|forward| (forward.start_byte, forward.package_name.clone()))
18099 .collect::<Vec<_>>(),
18100 )
18101 })
18102 .collect();
18103 let mut whole_shape: Vec<_> = whole
18104 .forwards
18105 .iter()
18106 .map(|(name, forwards)| {
18107 (
18108 name.clone(),
18109 forwards
18110 .iter()
18111 .map(|forward| (forward.start_byte, forward.package_name.clone()))
18112 .collect::<Vec<_>>(),
18113 )
18114 })
18115 .collect();
18116 incremental_shape.sort();
18117 whole_shape.sort();
18118 for (_, forwards) in &mut incremental_shape {
18119 forwards.sort();
18120 }
18121 for (_, forwards) in &mut whole_shape {
18122 forwards.sort();
18123 }
18124
18125 assert!(!whole_shape.is_empty(), "fixture folds no forward");
18126 assert_eq!(
18127 incremental_shape, whole_shape,
18128 "one byte at a time must fold exactly what one whole pass folds"
18129 );
18130 }
18131
18132 fn fragmented_class_reparse_agreement(body: &str) {
18142 for prefix in [
18143 String::new(),
18144 "// leading comment\n".to_string(),
18145 "class Widget : public Base { ".to_string(),
18150 "namespace filler {\n".to_string()
18151 + &"struct Filler { int member; };\n".repeat(200)
18152 + "}\n",
18153 "namespace filler {\n".to_string()
18154 + &"struct Filler { int member; };\n".repeat(200)
18155 + "}\nclass Widget : public Base { ",
18156 ] {
18157 let source = format!("{prefix}{body}");
18158 let start = prefix.len();
18159 let end = source.len();
18160 let region = cpp_reparse_fragmented_class_body(&source, start, end)
18161 .expect("the region reparse must produce a tree");
18162 let padded = cpp_reparse_padded_class_body(&source, start, end)
18163 .expect("the padded reparse must produce a tree");
18164 assert_eq!(
18165 cpp_tree_shape(®ion),
18166 cpp_tree_shape(&padded),
18167 "region and padded reparse disagree at offset {start} of {end} bytes"
18168 );
18169 assert_eq!(
18170 region.root_node().start_byte(),
18171 start,
18172 "the reparsed region keeps its original offsets"
18173 );
18174 }
18175 }
18176
18177 #[test]
18178 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
18179 fragmented_class_reparse_agreement(
18183 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
18184 );
18185 fragmented_class_reparse_agreement(
18186 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
18187 );
18188 fragmented_class_reparse_agreement(
18191 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
18192 );
18193 fragmented_class_reparse_agreement(
18194 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
18195 );
18196 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
18199 }
18200
18201 fn many_enums_and_mixed_declarations() -> String {
18205 let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
18206 for index in 0..40 {
18207 let _ = write!(
18208 source,
18209 "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
18210 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
18211 class Color{index}Like {{ public: int member{index}; }};\n"
18212 );
18213 }
18214 source.push_str("namespace outer {\n");
18215 for index in 0..20 {
18216 let _ = write!(
18217 source,
18218 "enum Shade{index} {{ DARK{index} }};\n\
18219 struct Shade{index}Holder {{ int field{index}; }};\n"
18220 );
18221 }
18222 source.push_str("}\n");
18223 source
18224 }
18225
18226 fn field_owner_index_agreement(source: &str, name: &str) -> usize {
18242 let parsed = parse_cpp_declarations(source, name);
18243 let file = ProjectFile::new(std::env::temp_dir(), name);
18244 let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
18245
18246 let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
18247 declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
18248
18249 let foreign: Vec<CodeUnit> = declarations
18250 .iter()
18251 .filter(|unit| unit.kind() == CodeUnitType::Field)
18252 .map(|unit| {
18253 CodeUnit::new_fq(
18254 elsewhere.clone(),
18255 unit.kind(),
18256 unit.package_name().to_string(),
18257 unit.short_name().to_string(),
18258 unit.fq().clone(),
18259 )
18260 })
18261 .collect();
18262
18263 let mut packages: Vec<String> = declarations
18269 .iter()
18270 .map(|unit| unit.package_name().to_string())
18271 .collect();
18272 packages.push(String::new());
18273 packages.sort();
18274 packages.dedup();
18275 let deeper: Vec<CodeUnit> = packages
18276 .iter()
18277 .map(|package_name| {
18278 CodeUnit::new_fq(
18279 file.clone(),
18280 CodeUnitType::Field,
18281 package_name.clone(),
18282 "SynthOwner.middle.leaf".to_string(),
18283 cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
18284 )
18285 })
18286 .collect();
18287
18288 let mut questions: Vec<(String, String)> = Vec::new();
18292 for unit in declarations.iter().chain(deeper.iter()) {
18293 let package_name = unit.package_name().to_string();
18294 let short_name = unit.short_name();
18295 questions.push((package_name.clone(), short_name.to_string()));
18296 questions.push((package_name.clone(), String::new()));
18297 for (offset, _) in short_name.match_indices('.') {
18298 questions.push((package_name.clone(), short_name[..offset].to_string()));
18299 }
18300 }
18301 questions.sort();
18302 questions.dedup();
18303
18304 let mut index = CppFieldOwnerIndex::default();
18305 let mut recorded: Vec<&CodeUnit> = Vec::new();
18306 let mut answered = 0usize;
18307 for unit in foreign
18308 .iter()
18309 .chain(declarations.iter())
18310 .chain(deeper.iter())
18311 {
18312 index.record(unit, &file);
18313 recorded.push(unit);
18314 for (package_name, owner_short_name) in &questions {
18315 let carried = index.owns_fields(package_name, owner_short_name);
18316 assert_eq!(
18317 carried,
18318 cpp_declarations_hold_owned_fields(
18319 recorded.iter().copied(),
18320 &file,
18321 package_name,
18322 owner_short_name
18323 ),
18324 "the carried field index and the declaration scan disagree about \
18325 {package_name:?}/{owner_short_name:?} after recording {}",
18326 unit.fq_name()
18327 );
18328 answered += usize::from(carried);
18329 }
18330 }
18331
18332 let rebuilt = CppFieldOwnerIndex::of(
18335 foreign
18336 .iter()
18337 .chain(declarations.iter())
18338 .chain(deeper.iter()),
18339 &file,
18340 );
18341 for (package_name, owner_short_name) in &questions {
18342 assert_eq!(
18343 rebuilt.owns_fields(package_name, owner_short_name),
18344 index.owns_fields(package_name, owner_short_name),
18345 "a rebuilt index must answer what the incremental one answers for \
18346 {package_name:?}/{owner_short_name:?}"
18347 );
18348 }
18349 answered
18350 }
18351
18352 #[test]
18361 fn a_replacement_that_removes_children_drops_the_field_index() {
18362 let source =
18363 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
18364 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
18365 let mut names: Vec<_> = parsed
18366 .declarations()
18367 .iter()
18368 .map(|unit| unit.fq_name())
18369 .collect();
18370 names.sort();
18371 assert_eq!(
18372 names,
18373 vec![
18374 "Color".to_string(),
18375 "First".to_string(),
18376 "First.A".to_string()
18377 ],
18378 "the replaced Color owns no field any more"
18379 );
18380 }
18381
18382 #[test]
18391 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
18392 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
18393 BEGIN_NS\n\
18394 namespace demo { struct Widget { void doWork(); }; }\n\
18395 END_NS\n";
18396 let parsed = parse_cpp_declarations(source, "restored.cpp");
18397 let recovered: Vec<String> = parsed
18398 .materialization_records
18399 .iter()
18400 .filter_map(|record| match record {
18401 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
18402 _ => None,
18403 })
18404 .collect();
18405 assert!(
18406 recovered.is_empty(),
18407 "the region declares nothing the file did not already declare: {recovered:?}"
18408 );
18409 let mut names: Vec<String> = parsed
18410 .declarations()
18411 .iter()
18412 .map(|unit| unit.fq_name())
18413 .collect();
18414 names.sort();
18415 assert_eq!(
18416 names,
18417 vec![
18418 "demo".to_string(),
18419 "demo.Widget".to_string(),
18420 "demo.Widget.doWork".to_string(),
18421 ]
18422 );
18423 }
18424
18425 #[test]
18430 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
18431 let mut source = String::new();
18432 for index in 0..4 {
18433 let _ = write!(
18434 source,
18435 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
18436 );
18437 }
18438 source.push_str("void outside() {}\n");
18439 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
18440
18441 let recovered: Vec<(String, (usize, usize))> = parsed
18442 .materialization_records
18443 .iter()
18444 .filter_map(|record| match record {
18445 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
18446 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
18447 }
18448 _ => None,
18449 })
18450 .collect();
18451
18452 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
18453 for index in 0..4 {
18454 let region = format!("namespace demo{index}");
18457 let region_start = source.find(®ion).expect("each region is in the source");
18458 let start = source[..region_start]
18459 .rfind("BEGIN_NS")
18460 .expect("each region opens with a sentinel")
18461 + "BEGIN_NS".len();
18462 let end = start
18463 + source[start..]
18464 .find("END_NS")
18465 .expect("each region closes with a sentinel")
18466 - 1;
18467 let window = (start, end);
18468 for name in [
18469 format!("demo{index}"),
18470 format!("demo{index}.Widget{index}"),
18471 format!("demo{index}.Widget{index}.doWork{index}"),
18472 ] {
18473 expected.push((name, window));
18474 }
18475 }
18476 assert_eq!(
18477 recovered, expected,
18478 "each recovery records its own minted declarations, in order"
18479 );
18480 assert!(
18481 parsed
18482 .declarations()
18483 .iter()
18484 .any(|unit| unit.fq_name() == "outside"),
18485 "the declaration outside every region stays parsed and unrecovered"
18486 );
18487 }
18488
18489 #[test]
18490 fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
18491 assert!(
18492 field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
18493 "the fixture must actually own fields"
18494 );
18495
18496 for (source, name) in [
18502 ("struct S { enum E { V }; };\n", "nested.hpp"),
18503 (
18504 "enum Color { RED };\nstruct Color { int RED; };\n",
18505 "class-like.c",
18506 ),
18507 ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
18508 (
18509 "enum E { V };\nnamespace ns { enum E { V }; }\n",
18510 "repeated.hpp",
18511 ),
18512 ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
18513 ] {
18514 field_owner_index_agreement(source, name);
18515 }
18516 }
18517
18518 fn repeated_class_blocks(blocks: usize) -> String {
18523 let mut source = String::from("namespace demo {\n");
18524 for index in 0..blocks {
18525 let _ = write!(
18526 source,
18527 "\nclass Widget{index} {{\npublic:\n int translate() const {{ return {index}; }}\n int helper(const Widget{index}& other) const {{ return other.translate(); }}\nprivate:\n int field = {index};\n}};\n"
18528 );
18529 }
18530 source.push_str("\n}\n");
18531 source
18532 }
18533
18534 #[test]
18542 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
18543 let mut answers = Vec::new();
18544 let mut visits = Vec::new();
18545 let mut node_counts = Vec::new();
18546 for blocks in [200usize, 400] {
18547 let source = repeated_class_blocks(blocks);
18548 let mut parser = tree_sitter::Parser::new();
18549 parser
18550 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18551 .unwrap();
18552 let tree = parser.parse(&source, None).unwrap();
18553 let start_byte = source.find("class Widget0 ").expect("first class");
18554 let end_byte = start_byte
18555 + source[start_byte..]
18556 .find("};")
18557 .expect("first class terminator")
18558 + "};".len();
18559 let range = Range {
18560 start_byte,
18561 end_byte,
18562 start_line: 0,
18563 end_line: 0,
18564 };
18565 reset_recovered_class_body_node_visits_for_test();
18566 let recovered_export_classes =
18567 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
18568 answers.push(recovered_class_body_at(
18569 &recovered_export_classes,
18570 tree.root_node(),
18571 &source,
18572 "Widget0",
18573 &range,
18574 ));
18575 visits.push(recovered_class_body_node_visits_for_test());
18576 let mut nodes = 0usize;
18577 let mut stack = vec![tree.root_node()];
18578 while let Some(node) = stack.pop() {
18579 nodes += 1;
18580 let mut cursor = node.walk();
18581 stack.extend(node.named_children(&mut cursor));
18582 }
18583 node_counts.push(nodes);
18584 }
18585
18586 assert_eq!(
18587 answers,
18588 vec![None, None],
18589 "no recovered shape claims a plain class"
18590 );
18591 assert_eq!(
18592 visits[0], visits[1],
18593 "the walk must follow the range's own path, so doubling the unrelated \
18594 classes must not change the node count: {visits:?} over trees of \
18595 {node_counts:?} nodes"
18596 );
18597 assert!(
18598 visits[1] * 20 < node_counts[1],
18599 "the walk must stay far below one pass over the tree: {visits:?} over \
18600 trees of {node_counts:?} nodes"
18601 );
18602 }
18603}