1use crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::{MacroReplacementField, ObjectMacroReplacement};
9use brokk_bifrost_core::analyzer::common::{
10 node_source_text, parse_source_ranges_with_cancellation, parse_source_region,
11};
12use brokk_bifrost_core::analyzer::fq_name::{
13 FqName, SegmentId, SegmentKind, joined_segments, normalize_joined, segment_interner,
14};
15use brokk_bifrost_core::analyzer::model::{
16 CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
17 CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
18 CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
19 ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
20 StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
21};
22use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
23use brokk_bifrost_core::analyzer::structural::adapter_helpers::node_range;
24use brokk_bifrost_core::analyzer::structural::materialization::{
25 GenerationKind, MaterializationRecord,
26};
27use brokk_bifrost_core::analyzer::tree_walk::{
28 NodeKindIds, ParentIndex, WalkControl, children_iter, named_children_iter,
29 push_children_reversed, push_named_children_reversed, walk_named_tree_preorder,
30};
31use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
32use brokk_bifrost_core::hash::{HashMap, HashSet};
33use regex::Regex;
34use tree_sitter::{Node, Parser, Tree};
35
36fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
38 segment_interner().intern(text, kind)
39}
40
41fn cpp_push_package(fq: &mut FqName, package_name: &str) {
49 for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
50 fq.push(cpp_segment(component, SegmentKind::Package));
51 }
52}
53
54const CPP_PACKAGE_SEPARATOR: &str = "::";
56
57fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
64 let mut first = true;
65 for component in chain.split('$').filter(|c| !c.is_empty()) {
69 let kind = if first {
70 SegmentKind::Type
71 } else {
72 SegmentKind::Nested
73 };
74 fq.push(cpp_segment(component, kind));
75 first = false;
76 }
77}
78
79fn cpp_namespace_fq(full_name: &str) -> FqName {
83 let mut fq = FqName::new();
84 cpp_push_package(&mut fq, full_name);
85 fq
86}
87
88fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
104 let mut components = Vec::new();
105 let mut stack = vec![node];
106 while let Some(current) = stack.pop() {
107 match current.kind() {
108 "namespace_identifier" | "identifier" => {
109 components.push(normalize_cpp_whitespace(node_text(current, source)));
110 }
111 "nested_namespace_specifier" => {
112 for index in (0..current.named_child_count()).rev() {
113 stack.push(
114 current
115 .named_child(index)
116 .expect("index below the node's own named child count"),
117 );
118 }
119 }
120 _ => return cpp_raw_namespace_name_components(node, source),
121 }
122 }
123 if components.iter().any(String::is_empty) {
124 return cpp_raw_namespace_name_components(node, source);
125 }
126 components
127}
128
129fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
143 let start = node
144 .child(0)
145 .filter(|child| !child.is_named() && child.kind() == "::")
146 .map_or(node.start_byte(), |marker| marker.end_byte());
147 let text = normalize_cpp_whitespace(
148 source
149 .get(start..node.end_byte())
150 .expect("namespace name node covers one source range"),
151 );
152 let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
153 if text.is_empty() {
154 return Vec::new();
155 }
156 vec![text]
157}
158
159fn cpp_lexical_namespace_name<'tree>(
165 node: Node<'tree>,
166 source: &str,
167 ancestry: &ParentIndex<'tree>,
168) -> Option<String> {
169 let mut components = Vec::new();
170 let mut ancestor = ancestry.parent(node);
171 while let Some(current) = ancestor {
172 if current.kind() == "namespace_definition" {
173 let name_node = current.child_by_field_name("name")?;
174 let name = normalize_cpp_whitespace(node_text(name_node, source));
175 if name.is_empty() {
176 return None;
177 }
178 components.push(name);
179 }
180 ancestor = ancestry.parent(current);
181 }
182 if components.is_empty() {
183 return None;
184 }
185 components.reverse();
186 Some(
190 normalize_joined(
191 &components.join(CPP_PACKAGE_SEPARATOR),
192 CPP_PACKAGE_SEPARATOR,
193 )
194 .into_owned(),
195 )
196}
197
198fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
204 if parent_short.is_empty() {
205 name.to_string()
206 } else {
207 format!("{parent_short}${name}")
208 }
209}
210
211fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
214 if parent_short.is_empty() {
215 name.to_string()
216 } else {
217 format!("{parent_short}.{name}")
218 }
219}
220
221fn cpp_leaf_fq(
228 package_name: &str,
229 parent: Option<&CodeUnit>,
230 name: &str,
231 kind_if_nested: SegmentKind,
232 kind_if_top: SegmentKind,
233) -> FqName {
234 if let Some(parent) = parent {
235 parent
236 .fq()
237 .clone()
238 .with_pushed(cpp_segment(name, kind_if_nested))
239 } else {
240 let mut fq = FqName::new();
241 cpp_push_package(&mut fq, package_name);
242 fq.push(cpp_segment(name, kind_if_top));
243 fq
244 }
245}
246
247pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
254 let mut fq = FqName::new();
255 cpp_push_package(&mut fq, package_name);
256 match short_name.rsplit_once('.') {
257 Some((owner_chain, member)) => {
258 cpp_push_type_chain(&mut fq, owner_chain);
259 fq.push(cpp_segment(member, SegmentKind::Member));
260 }
261 None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
262 }
263 fq
264}
265
266#[derive(Clone)]
267pub struct ScopeInfo {
268 package_name: String,
269 module: Option<CodeUnit>,
270 class_unit: Option<CodeUnit>,
271 template_signature: Option<String>,
272 template_metadata: Option<CppTemplateMetadata>,
273 declarations_are_fields: bool,
274 recovered_specialization_member_scope: bool,
275 visible_using_namespaces: Vec<String>,
287}
288
289struct CppContainer<'tree> {
290 node: Node<'tree>,
291 scope: ScopeInfo,
292}
293
294struct CppNodeWork<'tree> {
295 node: Node<'tree>,
296 scope: ScopeInfo,
297}
298
299struct CppSiblingsWork<'tree> {
306 children: std::vec::IntoIter<Node<'tree>>,
307 scope: ScopeInfo,
308}
309
310enum CppWork<'tree> {
311 Container(CppContainer<'tree>),
312 Node(CppNodeWork<'tree>),
313 Siblings(CppSiblingsWork<'tree>),
314}
315
316fn class_like_name<'tree>(
317 node: Node<'tree>,
318 source: &str,
319 ancestry: &ParentIndex<'tree>,
320) -> Option<String> {
321 let best = class_like_name_from_children(node, source);
322 if let Some(parent) = ancestry.parent(node)
323 && matches!(
324 parent.kind(),
325 "declaration" | "field_declaration" | "function_definition"
326 )
327 && cpp_body_node(node).is_none()
336 && node
337 .child_by_field_name("name")
338 .map(|name_node| {
339 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
340 })
341 .unwrap_or(false)
342 && let Some(recovered) = exported_class_name_from_node(parent, source)
343 && best.as_deref() != Some(recovered.as_str())
344 {
345 return Some(recovered);
346 }
347 best.or_else(|| {
348 node.child_by_field_name("name")
349 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
350 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
351 })
352}
353
354fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
355 let mut grammar_name = None;
356 if let Some(name_node) = node.child_by_field_name("name") {
357 let name = normalize_cpp_whitespace(node_text(name_node, source));
358 if name.is_empty() {
359 return None;
360 }
361 if !cpp_export_macro_token(&name) {
362 return Some(name);
363 }
364 grammar_name = Some(name);
365 }
366
367 let mut best = None;
368 let mut cursor = node.walk();
369 let mut stack = Vec::new();
370 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
371 if matches!(
372 child.kind(),
373 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
374 ) {
375 break;
376 }
377 stack.push(child);
378 }
379
380 while let Some(current) = stack.pop() {
381 if matches!(current.kind(), "type_identifier" | "identifier") {
382 let name = normalize_cpp_whitespace(node_text(current, source));
383 if !name.is_empty() && !cpp_export_macro_token(&name) {
384 best = Some(name);
385 }
386 continue;
387 }
388
389 push_named_children_reversed(current, &mut stack);
390 }
391 best.or(grammar_name)
392}
393
394pub fn cpp_export_macro_token(token: &str) -> bool {
395 token
396 .chars()
397 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
398}
399
400struct RecoveredExportedClass<'tree> {
401 declaration_node: Node<'tree>,
402 name: String,
403 body: Option<Node<'tree>>,
404 raw_supertypes: Option<Vec<String>>,
405 uses_initializer_body: bool,
406 fragmented_body: Option<FragmentedExportBody>,
411}
412
413struct RecoveredFunctionLikeExportClassPair {
414 name: String,
415 range: Range,
416 raw_supertypes: Option<Vec<String>>,
417 fragmented_body: FragmentedExportBody,
418}
419
420struct RecoveredEmbeddedFunctionLikeExportClass {
421 name: String,
422 range: Range,
423 raw_supertypes: Vec<String>,
424 fragmented_body: FragmentedExportBody,
425}
426
427struct FragmentedExportBody {
433 reparse_start: usize,
434 reparse_end: usize,
435 class_range: Range,
436}
437
438fn recovered_fragmented_export_body(
439 body: Node<'_>,
440 class_range: Range,
441) -> Option<FragmentedExportBody> {
442 let open = body.child(0).filter(|child| child.kind() == "{")?;
443 let close = body
444 .child(body.child_count().saturating_sub(1))
445 .filter(|child| child.kind() == "}" && !child.is_missing());
446 Some(FragmentedExportBody {
447 reparse_start: open.end_byte(),
448 reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
453 class_range,
454 })
455}
456
457struct DisplacedFragmentNamespaceBoundary<'tree> {
458 class_close: Node<'tree>,
459 class_semicolon: Node<'tree>,
460 namespace_items: Vec<Node<'tree>>,
461}
462
463enum FragmentedExportMembers {
468 Complete(Tree),
469 ConditionalConstructor(Tree),
470}
471
472#[derive(Clone, Copy)]
473struct DisplacedMacroClassTail {
474 split_index: usize,
475 class_range: Range,
476}
477
478fn recover_exported_class_declaration<'tree>(
479 node: Node<'tree>,
480 source: &str,
481) -> Option<RecoveredExportedClass<'tree>> {
482 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
483 return Some(recovered);
484 }
485
486 let class_node = first_class_like_child(node)?;
487 if let Some(name_node) = class_node.child_by_field_name("name") {
488 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
489 if cpp_export_macro_token(&class_name) {
490 let mut cursor = node.walk();
494 if node
495 .children_by_field_name("declarator", &mut cursor)
496 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
497 {
498 return None;
499 }
500 } else if has_direct_cpp_declarator(node) {
501 return None;
502 }
503 }
504 let name = exported_class_name_from_node(class_node, source)?;
505 Some(RecoveredExportedClass {
506 declaration_node: class_node,
507 name,
508 body: cpp_body_node(class_node),
509 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
510 .then(|| extract_cpp_supertypes(class_node, source)),
511 uses_initializer_body: false,
512 fragmented_body: None,
513 })
514}
515
516fn recover_malformed_exported_base_class<'tree>(
517 node: Node<'tree>,
518 source: &str,
519) -> Option<RecoveredExportedClass<'tree>> {
520 if node.kind() != "declaration" {
521 return None;
522 }
523 let class_node = node.child_by_field_name("type")?;
524 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
525 return None;
526 }
527 let macro_name = class_node
528 .child_by_field_name("name")
529 .and_then(|name| direct_identifier_name(name, source))?;
530 if !cpp_export_macro_token(¯o_name) {
531 return None;
532 }
533
534 let mut named_cursor = node.walk();
535 let mut named = node.named_children(&mut named_cursor);
536 if named
537 .next()
538 .is_none_or(|child| !same_node(child, class_node))
539 {
540 return None;
541 }
542 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
543 if displaced.kind() != "ERROR" {
544 return None;
545 }
546 let name = displaced_exported_class_name(displaced, source)?;
547
548 let remaining = named.collect::<Vec<_>>();
549 let init = *remaining.last()?;
550 if init.kind() != "init_declarator" {
551 return None;
552 }
553 let final_base = init
554 .child_by_field_name("declarator")
555 .and_then(|base| recovered_malformed_base_name(base, source))?;
556 let body = init.child_by_field_name("value")?;
557 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
561 return None;
562 }
563
564 if remaining[..remaining.len() - 1]
565 .iter()
566 .any(|child| match child.kind() {
567 "qualified_identifier"
568 | "scoped_type_identifier"
569 | "type_identifier"
570 | "identifier" => false,
571 "ERROR" => !is_malformed_inheritance_access(*child, source),
572 _ => true,
573 })
574 {
575 return None;
576 }
577
578 let mut raw_supertypes = Vec::new();
579 for base in &remaining[..remaining.len() - 1] {
580 if base.kind() == "ERROR" {
581 continue;
582 }
583 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
584 }
585 raw_supertypes.push(final_base);
586
587 Some(RecoveredExportedClass {
588 declaration_node: node,
589 name,
590 body: Some(body),
591 raw_supertypes: Some(raw_supertypes),
592 uses_initializer_body: true,
593 fragmented_body: fragmented_export_body_region(node, body, source),
594 })
595}
596
597fn fragmented_export_body_region(
613 node: Node<'_>,
614 body: Node<'_>,
615 source: &str,
616) -> Option<FragmentedExportBody> {
617 let reparse_start = body.start_byte() + 1;
618 let close = direct_close_brace(body)?;
619 if close.end_byte() > close.start_byte() {
620 return Some(FragmentedExportBody {
621 reparse_start,
622 reparse_end: close.start_byte(),
623 class_range: cpp_declaration_range(node),
624 });
625 }
626 let mut sibling = node.next_named_sibling();
629 let displaced_close = loop {
630 let Some(current) = sibling else {
631 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
632 };
633 if cpp_is_stray_close_brace(current, source) {
634 break current;
635 }
636 sibling = current.next_named_sibling();
637 };
638 Some(FragmentedExportBody {
639 reparse_start,
640 reparse_end: displaced_close.start_byte(),
641 class_range: Range {
642 start_byte: node.start_byte(),
643 end_byte: displaced_close.end_byte(),
644 start_line: node.start_position().row + 1,
645 end_line: displaced_close.end_position().row + 1,
646 },
647 })
648}
649
650fn fragmented_export_function_body_region(
658 node: Node<'_>,
659 body: Node<'_>,
660 source: &str,
661 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
662) -> Option<FragmentedExportBody> {
663 let reparse_start = body.start_byte().checked_add(1)?;
664 if let Some(boundary) = displaced_namespace {
665 return Some(FragmentedExportBody {
666 reparse_start,
667 reparse_end: boundary.class_close.start_byte(),
668 class_range: Range {
669 start_byte: node.start_byte(),
670 end_byte: boundary.class_semicolon.end_byte(),
671 start_line: node.start_position().row + 1,
672 end_line: boundary.class_semicolon.end_position().row + 1,
673 },
674 });
675 }
676 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
677 let boundary = fragmented_export_sibling_class_boundary(node, source);
678 let boundary_index = boundary.and_then(|boundary| {
679 siblings
680 .iter()
681 .position(|candidate| same_node(*candidate, boundary))
682 });
683 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
684 let mut sibling_index = 0;
685 while let Some(current) = siblings.get(sibling_index).copied() {
698 if current.kind() == "comment" {
699 sibling_index += 1;
700 continue;
701 }
702 if is_trailing_attribute_macro_sibling(current) {
703 sibling_index += 1;
704 continue;
705 }
706 if cpp_is_stray_semicolon(current, source) {
707 return None;
708 }
709 break;
710 }
711 while let Some(current) = siblings.get(sibling_index).copied() {
712 let next = siblings.get(sibling_index + 1).copied();
713 if cpp_is_stray_close_brace(current, source)
714 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
715 {
716 let semicolon = next.expect("checked above");
717 return Some(FragmentedExportBody {
718 reparse_start,
719 reparse_end: current.start_byte(),
720 class_range: Range {
721 start_byte: node.start_byte(),
722 end_byte: semicolon.end_byte(),
723 start_line: node.start_position().row + 1,
724 end_line: semicolon.end_position().row + 1,
725 },
726 });
727 }
728 if current.start_byte() >= body.end_byte()
735 && let Some(close) = cpp_nested_stray_close_brace(current, source)
736 {
737 return Some(FragmentedExportBody {
738 reparse_start,
739 reparse_end: close.start_byte(),
740 class_range: Range {
741 start_byte: node.start_byte(),
742 end_byte: current.end_byte(),
743 start_line: node.start_position().row + 1,
744 end_line: current.end_position().row + 1,
745 },
746 });
747 }
748 sibling_index += 1;
749 }
750 boundary.map(|boundary| FragmentedExportBody {
751 reparse_start,
752 reparse_end: boundary.start_byte(),
753 class_range: Range {
754 start_byte: node.start_byte(),
755 end_byte: boundary.start_byte(),
756 start_line: node.start_position().row + 1,
757 end_line: boundary.start_position().row + 1,
758 },
759 })
760}
761
762fn fragmented_export_sibling_class_boundary<'tree>(
767 node: Node<'tree>,
768 source: &str,
769) -> Option<Node<'tree>> {
770 let node_parent = node.parent()?;
771 cpp_following_named_siblings(node, source, &ParentIndex::unindexed())
772 .into_iter()
773 .find(|candidate| {
774 recover_exported_class_function_definition(*candidate, source).is_some()
775 && candidate
776 .parent()
777 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
778 })
779}
780
781fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
786 if node.kind() != "expression_statement" {
787 return false;
788 }
789 let mut cursor = node.walk();
790 let mut children = node.named_children(&mut cursor);
791 children
792 .next()
793 .is_some_and(|child| child.kind() == "identifier")
794 && children.next().is_none()
795}
796
797fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
803 let mut stack = vec![node];
804 while let Some(current) = stack.pop() {
805 if cpp_is_stray_close_brace(current, source) {
806 return Some(current);
807 }
808 let mut cursor = current.walk();
809 stack.extend(current.named_children(&mut cursor));
810 }
811 None
812}
813
814fn cpp_following_named_siblings<'tree>(
828 node: Node<'tree>,
829 source: &str,
830 ancestry: &ParentIndex<'tree>,
831) -> Vec<Node<'tree>> {
832 let mut siblings = Vec::new();
833 let mut anchor = node;
834 while let Some(parent) = ancestry.parent(anchor) {
835 let at_translation_unit = parent.kind() == "translation_unit";
836 let mut sibling = anchor.next_named_sibling();
837 while let Some(current) = sibling {
838 if at_translation_unit
839 && (current.kind() == "namespace_definition"
840 || (current.kind() == "function_definition"
841 && first_class_like_child(current).is_some()))
842 {
843 return siblings;
844 }
845 siblings.push(current);
846 if cpp_is_stray_close_brace(current, source) {
847 if let Some(semicolon) = current
848 .next_named_sibling()
849 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
850 {
851 siblings.push(semicolon);
852 }
853 return siblings;
854 }
855 if current.start_byte() >= node.end_byte()
856 && matches!(current.kind(), "ERROR" | "labeled_statement")
857 && cpp_nested_stray_close_brace(current, source).is_some()
858 {
859 return siblings;
860 }
861 sibling = current.next_named_sibling();
862 }
863 anchor = parent;
864 }
865 siblings
866}
867
868fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
869 if node.start_byte() >= class_end {
870 return false;
871 }
872 node.end_byte() <= class_end
873 || cpp_nested_stray_close_brace(node, source)
874 .is_some_and(|close| close.start_byte() == class_end)
875}
876
877struct FragmentedClassRecovery<'tree> {
881 declaration_node: Node<'tree>,
882 name: String,
883 raw_supertypes: Vec<String>,
884 body: FragmentedExportBody,
885}
886
887fn fragmented_class_body<'tree>(
899 node: Node<'tree>,
900 source: &str,
901) -> Option<FragmentedClassRecovery<'tree>> {
902 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
903 return Some(recovered);
904 }
905 let supported_container = node.kind() == "ERROR"
906 || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
907 if !supported_container {
908 return None;
909 }
910 let mut cursor = node.walk();
911 let children = node.children(&mut cursor).collect::<Vec<_>>();
912 if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
913 return Some(recovered);
914 }
915 let keyword = children.first()?;
916 if !matches!(keyword.kind(), "class" | "struct" | "union") {
917 return None;
918 }
919 let name_node = children
920 .iter()
921 .copied()
922 .skip(1)
923 .find(|child| child.is_named())?;
924 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
925 return None;
926 }
927 let name = normalize_cpp_whitespace(node_text(name_node, source));
928 if name.is_empty() || cpp_export_macro_token(&name) {
929 return None;
930 }
931 let open_index = children.iter().position(|child| child.kind() == "{")?;
932 Some(FragmentedClassRecovery {
933 declaration_node: node,
934 name,
935 raw_supertypes: extract_cpp_supertypes(node, source),
936 body: fragmented_displaced_class_body(node, &children, open_index, source)?,
937 })
938}
939
940fn fragmented_export_macro_class_body<'tree>(
951 node: Node<'tree>,
952 children: &[Node<'tree>],
953 source: &str,
954) -> Option<FragmentedClassRecovery<'tree>> {
955 let class_node = *children.first()?;
956 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
957 return None;
958 }
959 class_node
963 .child_by_field_name("name")
964 .and_then(|name| direct_identifier_name(name, source))?;
965 let invocation = *children.get(1)?;
966 if invocation.is_named() || invocation.kind() != "(" {
967 return None;
968 }
969 let open_index = children
970 .iter()
971 .position(|child| !child.is_named() && child.kind() == "{")?;
972 let open = children[open_index];
973 let name_node = recovered_export_head_name(node, open, source)?;
974 let name = normalize_cpp_whitespace(node_text(name_node, source));
975 if name.is_empty() || cpp_export_macro_token(&name) {
976 return None;
977 }
978 Some(FragmentedClassRecovery {
979 declaration_node: node,
980 name,
981 raw_supertypes: recovered_export_head_bases(
982 node,
983 name_node.end_byte(),
984 open.start_byte(),
985 source,
986 ),
987 body: fragmented_displaced_class_body(node, children, open_index, source)
988 .or_else(|| fragmented_container_close_class_body(node, open, source))?,
989 })
990}
991
992fn fragmented_displaced_class_body(
998 node: Node<'_>,
999 children: &[Node<'_>],
1000 open_index: usize,
1001 source: &str,
1002) -> Option<FragmentedExportBody> {
1003 let open = children[open_index];
1004 let nested_class_opens = children[open_index + 1..]
1005 .iter()
1006 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1007 .count();
1008 let mut closes_remaining = 1 + nested_class_opens;
1009 let mut sibling = node.next_named_sibling();
1010 while let Some(candidate) = sibling {
1011 let next = candidate.next_named_sibling();
1012 if cpp_is_stray_close_brace(candidate, source) {
1013 closes_remaining -= 1;
1014 if closes_remaining == 0 {
1015 let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
1016 if open.end_byte() >= candidate.start_byte() {
1017 return None;
1018 }
1019 return Some(FragmentedExportBody {
1020 reparse_start: open.end_byte(),
1021 reparse_end: candidate.start_byte(),
1022 class_range: Range {
1023 start_byte: node.start_byte(),
1024 end_byte: semicolon.end_byte(),
1025 start_line: node.start_position().row + 1,
1026 end_line: semicolon.end_position().row + 1,
1027 },
1028 });
1029 }
1030 }
1031 sibling = next;
1032 }
1033 None
1034}
1035
1036fn fragmented_container_close_class_body(
1044 node: Node<'_>,
1045 open: Node<'_>,
1046 source: &str,
1047) -> Option<FragmentedExportBody> {
1048 let parent = node.parent()?;
1049 if !matches!(
1050 parent.kind(),
1051 "declaration_list" | "field_declaration_list" | "compound_statement"
1052 ) {
1053 return None;
1054 }
1055 let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1056 if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1061 || open.end_byte() >= close.start_byte()
1062 {
1063 return None;
1064 }
1065 Some(FragmentedExportBody {
1066 reparse_start: open.end_byte(),
1067 reparse_end: close.start_byte(),
1068 class_range: Range {
1069 start_byte: node.start_byte(),
1070 end_byte: close.end_byte(),
1071 start_line: node.start_position().row + 1,
1072 end_line: close.end_position().row + 1,
1073 },
1074 })
1075}
1076
1077pub(crate) fn recovered_fragmented_class_has_body(
1078 node: Node<'_>,
1079 source: &str,
1080 expected_name: &str,
1081 expected_range: &Range,
1082) -> bool {
1083 fragmented_class_body(node, source).is_some_and(|recovered| {
1084 recovered.name == expected_name
1085 && recovered.body.class_range.start_byte == expected_range.start_byte
1086 && recovered.body.class_range.end_byte == expected_range.end_byte
1087 })
1088}
1089
1090fn fragmented_plain_class_declaration_body<'tree>(
1097 node: Node<'tree>,
1098 source: &str,
1099) -> Option<FragmentedClassRecovery<'tree>> {
1100 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1101 return None;
1102 }
1103 let class_node = node.child_by_field_name("type")?;
1104 if !matches!(
1105 class_node.kind(),
1106 "class_specifier" | "struct_specifier" | "union_specifier"
1107 ) {
1108 return None;
1109 }
1110 let name_node = class_node.child_by_field_name("name")?;
1111 let name = normalize_cpp_whitespace(node_text(name_node, source));
1112 if name.is_empty() || cpp_export_macro_token(&name) {
1113 return None;
1114 }
1115 let body = cpp_body_node(class_node)?;
1116 if body.kind() != "field_declaration_list" {
1117 return None;
1118 }
1119 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1120 if declarator.start_byte() < class_node.end_byte() {
1121 return None;
1122 }
1123 let mut cursor = node.walk();
1124 node.named_children(&mut cursor).any(|child| {
1125 if child.kind() != "ERROR"
1126 || child.start_byte() < class_node.end_byte()
1127 || child.end_byte() > declarator.start_byte()
1128 {
1129 return false;
1130 }
1131 let mut cursor = child.walk();
1132 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1133 let Some((return_type, attributes)) = components.split_last() else {
1134 return false;
1135 };
1136 matches!(
1137 return_type.kind(),
1138 "identifier"
1139 | "type_identifier"
1140 | "primitive_type"
1141 | "decltype"
1142 | "placeholder_type_specifier"
1143 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1144 && attributes.iter().all(|attribute| {
1145 matches!(attribute.kind(), "identifier" | "type_identifier")
1146 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1147 *attribute, source,
1148 )))
1149 })
1150 })
1151 } else {
1152 let mut cursor = node.walk();
1153 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1154 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1155 if same_node(*candidate_class, class_node)
1156 && continuation.kind() == "identifier"
1157 && node_text(*continuation, source) == "else"
1158 && continuation_body.kind() == "compound_statement"
1159 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1160 && continuation_body
1161 .child(continuation_body.child_count().saturating_sub(1))
1162 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1163 };
1164 if !displaced_member {
1165 return None;
1166 }
1167 let open = body
1168 .children(&mut body.walk())
1169 .find(|child| child.kind() == "{")?;
1170 let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1171 let ordinary_boundary =
1172 siblings
1173 .iter()
1174 .copied()
1175 .enumerate()
1176 .find_map(|(close_index, close)| {
1177 cpp_is_stray_close_brace(close, source)
1178 .then(|| {
1179 siblings
1180 .get(close_index + 1)
1181 .copied()
1182 .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1183 .map(|semicolon| (close, semicolon))
1184 })
1185 .flatten()
1186 });
1187 let (close, semicolon) =
1188 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1189 (boundary.class_close, boundary.class_semicolon)
1190 } else {
1191 ordinary_boundary?
1192 };
1193 if open.end_byte() >= close.start_byte() {
1194 return None;
1195 }
1196 Some(FragmentedClassRecovery {
1197 declaration_node: class_node,
1198 name,
1199 raw_supertypes: extract_cpp_supertypes(class_node, source),
1200 body: FragmentedExportBody {
1201 reparse_start: open.end_byte(),
1202 reparse_end: close.start_byte(),
1203 class_range: Range {
1204 start_byte: class_node.start_byte(),
1205 end_byte: semicolon.end_byte(),
1206 start_line: class_node.start_position().row + 1,
1207 end_line: semicolon.end_position().row + 1,
1208 },
1209 },
1210 })
1211}
1212
1213fn displaced_export_function_namespace_shape<'tree>(
1214 declaration: Node<'tree>,
1215 source: &str,
1216) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1217 let mut nested = Vec::new();
1218 for index in (0..declaration.named_child_count()).rev() {
1219 nested.push(declaration.named_child(index)?);
1220 }
1221 while let Some(current) = nested.pop() {
1222 if recover_exported_class_function_definition(current, source).is_some() {
1228 return None;
1229 }
1230 for index in (0..current.named_child_count()).rev() {
1231 nested.push(current.named_child(index)?);
1232 }
1233 }
1234 let mut same_envelope_sibling = declaration.next_named_sibling();
1235 while let Some(current) = same_envelope_sibling {
1236 if recover_exported_class_function_definition(current, source).is_some() {
1237 return None;
1238 }
1239 same_envelope_sibling = current.next_named_sibling();
1240 }
1241 let declaration_list = declaration.parent()?;
1242 if declaration_list.kind() != "declaration_list" {
1243 return None;
1244 }
1245 let namespace = declaration_list.parent()?;
1246 if namespace.kind() != "namespace_definition"
1247 || namespace.child_by_field_name("body") != Some(declaration_list)
1248 {
1249 return None;
1250 }
1251 let class_close = direct_close_brace(declaration_list)?;
1252 let trailing_semicolon = namespace.next_named_sibling()?;
1253 if trailing_semicolon.kind() != "expression_statement"
1254 || trailing_semicolon.named_child_count() != 0
1255 {
1256 return None;
1257 }
1258 let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1264 let trailing_index = siblings
1265 .iter()
1266 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1267 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1268 recover_exported_class_function_definition(*candidate, source).is_some()
1269 }) {
1270 return None;
1275 }
1276 let mut namespace_items = Vec::new();
1277 let mut nested_fragment_end = 0;
1278 for current in siblings.into_iter().skip(trailing_index + 1) {
1279 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1280 {
1281 return Some(DisplacedFragmentNamespaceBoundary {
1282 class_close,
1283 class_semicolon: trailing_semicolon,
1284 namespace_items,
1285 });
1286 }
1287 if current.start_byte() >= nested_fragment_end
1288 && let Some(recovered) = fragmented_class_body(current, source)
1289 {
1290 nested_fragment_end = recovered.body.class_range.end_byte;
1291 } else if current.start_byte() >= nested_fragment_end
1292 && recover_exported_class_function_definition(current, source).is_some()
1293 && let Some(body) = cpp_body_node(current)
1294 && let Some(fragmented) =
1295 fragmented_export_function_body_region(current, body, source, None)
1296 {
1297 nested_fragment_end = fragmented.class_range.end_byte;
1298 }
1299 namespace_items.push(current);
1300 }
1301 None
1302}
1303
1304fn displaced_fragment_namespace_boundary<'tree>(
1305 declaration: Node<'tree>,
1306 body: Node<'tree>,
1307 source: &str,
1308) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1309 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1310 let reparse_start = body.start_byte() + 1;
1311 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1312 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1313}
1314
1315fn displaced_fragment_namespace_geometry<'tree>(
1321 declaration: Node<'tree>,
1322 source: &str,
1323) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1324 let envelope = declaration
1328 .parent()
1329 .filter(|parent| {
1330 parent.kind() == "template_declaration"
1331 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1332 })
1333 .unwrap_or(declaration);
1334 let declaration_list = envelope.parent()?;
1335 if declaration_list.kind() != "declaration_list" {
1336 return None;
1337 }
1338 let namespace = declaration_list.parent()?;
1339 if namespace.kind() != "namespace_definition"
1340 || namespace.child_by_field_name("body") != Some(declaration_list)
1341 {
1342 return None;
1343 }
1344 let class_close = direct_close_brace(declaration_list)?;
1345 let trailing_semicolon = namespace.next_named_sibling()?;
1346 if trailing_semicolon.kind() != "expression_statement"
1347 || trailing_semicolon.named_child_count() != 0
1348 {
1349 return None;
1350 }
1351 let mut namespace_items = Vec::new();
1352 let mut sibling = trailing_semicolon.next_named_sibling();
1353 let mut nested_fragment_end = 0;
1354 loop {
1355 let current = sibling?;
1356 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1357 {
1358 break;
1359 }
1360 if current.start_byte() >= nested_fragment_end
1361 && let Some(recovered) = fragmented_class_body(current, source)
1362 {
1363 nested_fragment_end = recovered.body.class_range.end_byte;
1364 }
1365 namespace_items.push(current);
1366 sibling = current.next_named_sibling();
1367 }
1368 Some(DisplacedFragmentNamespaceBoundary {
1369 class_close,
1370 class_semicolon: trailing_semicolon,
1371 namespace_items,
1372 })
1373}
1374
1375fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1377 (0..node.child_count())
1378 .filter_map(|index| node.child(index))
1379 .find(|child| !child.is_named() && child.kind() == "}")
1380}
1381
1382fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1385 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1386}
1387
1388fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1399 let bytes = source.as_bytes();
1400 if bytes.get(open_byte) != Some(&b'{') {
1401 return None;
1402 }
1403 let mut depth = 0usize;
1404 let mut i = open_byte;
1405 while i < bytes.len() {
1406 match bytes[i] {
1407 b'{' => depth += 1,
1408 b'}' => {
1409 depth = depth.checked_sub(1)?;
1410 if depth == 0 {
1411 return Some(i);
1412 }
1413 }
1414 b'/' if bytes.get(i + 1) == Some(&b'/') => {
1415 while i < bytes.len() && bytes[i] != b'\n' {
1416 i += 1;
1417 }
1418 continue;
1419 }
1420 b'/' if bytes.get(i + 1) == Some(&b'*') => {
1421 i += 2;
1422 while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1423 i += 1;
1424 }
1425 i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1426 continue;
1427 }
1428 quote @ (b'"' | b'\'') => {
1429 if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1432 return None;
1433 }
1434 i += 1;
1435 while i < bytes.len() && bytes[i] != quote {
1436 i += if bytes[i] == b'\\' { 2 } else { 1 };
1437 }
1438 if i >= bytes.len() {
1439 return None;
1440 }
1441 }
1442 _ => {}
1443 }
1444 i += 1;
1445 }
1446 None
1447}
1448
1449fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1450 let mut name = None;
1451 let mut colon_count = 0;
1452 let mut access_count = 0;
1453 for index in 0..node.child_count() {
1454 let child = node.child(index)?;
1455 match child.kind() {
1456 "identifier" | "type_identifier" if child.is_named() => {
1457 if name.is_some() {
1458 return None;
1459 }
1460 let candidate = normalize_cpp_whitespace(node_text(child, source));
1461 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1462 return None;
1463 }
1464 name = Some(candidate);
1465 }
1466 "template_function" | "template_type" if child.is_named() => {
1467 if name.is_some() {
1468 return None;
1469 }
1470 let candidate = child
1471 .child_by_field_name("name")
1472 .and_then(|name| direct_identifier_name(name, source))?;
1473 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1474 return None;
1475 }
1476 name = Some(candidate);
1477 }
1478 ":" if !child.is_named() => colon_count += 1,
1479 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1480 _ => return None,
1481 }
1482 }
1483 (colon_count == 1 && access_count == 1)
1484 .then_some(name)
1485 .flatten()
1486}
1487
1488fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1489 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1490 return false;
1491 }
1492 node.named_child(0)
1493 .and_then(|child| direct_identifier_name(child, source))
1494 .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1495}
1496
1497fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1498 (0..node.child_count()).any(|index| {
1499 node.child(index)
1500 .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1501 })
1502}
1503
1504fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1505 match node.kind() {
1506 "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1507 recovered_base_atom(node, source)
1508 }
1509 "template_type" | "template_function" => node
1510 .child_by_field_name("name")
1511 .and_then(|name| recovered_malformed_base_name(name, source)),
1512 "ERROR" => None,
1513 "qualified_identifier" | "scoped_type_identifier" => {
1514 let suffix = node
1515 .child_by_field_name("name")
1516 .and_then(|name| recovered_malformed_base_name(name, source))?;
1517 let scope = node
1518 .child_by_field_name("scope")
1519 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1520 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1521 malformed_qualified_prefix(node, source)?
1522 } else {
1523 if malformed_qualified_prefix(node, source).is_some() {
1524 return None;
1525 }
1526 scope
1527 };
1528 Some(format!("{prefix}::{suffix}"))
1529 }
1530 _ => None,
1531 }
1532}
1533
1534fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1535 if !matches!(
1536 node.kind(),
1537 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1538 ) {
1539 return None;
1540 }
1541 let name = normalize_cpp_whitespace(node_text(node, source));
1542 (!name.is_empty()).then_some(name)
1543}
1544
1545fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1546 let mut prefix = None;
1547 let mut cursor = node.walk();
1548 for error in node
1549 .named_children(&mut cursor)
1550 .filter(|child| child.kind() == "ERROR")
1551 {
1552 if prefix.is_some() {
1553 return None;
1554 }
1555 let mut error_cursor = error.walk();
1560 let atoms = error
1561 .named_children(&mut error_cursor)
1562 .map(|child| recovered_base_atom(child, source))
1563 .collect::<Option<Vec<_>>>()?;
1564 let [atom] = atoms
1565 .iter()
1566 .filter(|atom| atom.as_str() != "virtual")
1567 .collect::<Vec<_>>()[..]
1568 else {
1569 return None;
1570 };
1571 prefix = Some(atom.clone());
1572 }
1573 prefix
1574}
1575
1576struct StrandedRun<'tree> {
1580 declarations: Vec<MacroWrappedDeclaration<'tree>>,
1581 complete: bool,
1587}
1588
1589struct MacroWrappedDeclaration<'tree> {
1590 declarator: Node<'tree>,
1591 range: Range,
1592 is_static: bool,
1597}
1598
1599fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1613 declaration_scope_container(node, ancestry).is_some()
1614}
1615
1616fn declaration_scope_container<'tree>(
1621 node: Node<'tree>,
1622 ancestry: &ParentIndex<'tree>,
1623) -> Option<Node<'tree>> {
1624 let mut parent = ancestry.parent(node)?;
1625 loop {
1626 match parent.kind() {
1627 "translation_unit" => return Some(parent),
1628 "declaration_list" => {
1629 return ancestry
1630 .parent(parent)
1631 .is_some_and(|grandparent| {
1632 matches!(
1633 grandparent.kind(),
1634 "namespace_definition" | "linkage_specification"
1635 )
1636 })
1637 .then_some(parent);
1638 }
1639 "ERROR" => match ancestry.parent(parent) {
1640 Some(grandparent) => parent = grandparent,
1641 None => return Some(parent),
1644 },
1645 _ => return None,
1646 }
1647 }
1648}
1649
1650fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1652 node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
1653}
1654
1655fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1660 matches!(
1661 node.kind(),
1662 "identifier"
1663 | "type_identifier"
1664 | "primitive_type"
1665 | "sized_type_specifier"
1666 | "struct_specifier"
1667 | "union_specifier"
1668 | "enum_specifier"
1669 | "type_qualifier"
1670 | "storage_class_specifier"
1671 | "explicit_function_specifier"
1672 | "virtual_function_specifier"
1673 | "qualified_identifier"
1674 | "template_type"
1675 | "dependent_type"
1676 | "placeholder_type_specifier"
1677 )
1678}
1679
1680fn is_macro_argument_error(node: Node<'_>) -> bool {
1686 if node.kind() != "ERROR" {
1687 return false;
1688 }
1689 let mut cursor = node.walk();
1690 node.named_children(&mut cursor).all(|child| {
1691 matches!(
1692 child.kind(),
1693 "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1694 )
1695 })
1696}
1697
1698fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1707 declarator
1708 .next_sibling()
1709 .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1710 .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1711}
1712
1713fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1735 let mut parts = Vec::new();
1736 let mut cursor = node.walk();
1737 for child in node.named_children(&mut cursor) {
1738 if child.kind() == "ERROR" {
1739 let mut error_cursor = child.walk();
1740 parts.extend(child.named_children(&mut error_cursor));
1741 } else {
1742 parts.push(child);
1743 }
1744 }
1745
1746 let mut declarations = Vec::new();
1747 let mut start = None;
1748 let mut is_static = false;
1749 let mut complete = true;
1750 for part in parts {
1751 if part.kind() == "comment" {
1752 continue;
1753 }
1754 if let Some(declarator) = extract_function_declarator(part) {
1755 let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1756 declarations.push(MacroWrappedDeclaration {
1757 declarator,
1758 range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
1759 is_static,
1760 });
1761 is_static = false;
1762 continue;
1763 }
1764 if !is_recovered_declaration_type_part(part) {
1765 complete = false;
1766 break;
1767 }
1768 is_static |= part.kind() == "storage_class_specifier"
1769 && normalize_cpp_whitespace(node_text(part, source)) == "static";
1770 start.get_or_insert(part.start_byte());
1771 }
1772 StrandedRun {
1773 declarations,
1774 complete: complete && start.is_none(),
1775 }
1776}
1777
1778fn macro_wrapped_declarations<'tree>(
1804 envelope: Node<'tree>,
1805 source: &str,
1806 ancestry: &ParentIndex<'tree>,
1807) -> Vec<MacroWrappedDeclaration<'tree>> {
1808 let mut declarations = Vec::new();
1809 if !is_declaration_scope_error(envelope, ancestry) {
1810 return declarations;
1811 }
1812 let mut cursor = envelope.walk();
1813 let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
1814 let [macro_name, arguments @ ..] = children.as_slice() else {
1815 return declarations;
1816 };
1817 if macro_name.kind() != "identifier" {
1818 return declarations;
1819 }
1820 let mut wrapped_declaration_seen = false;
1821 for argument in arguments {
1822 match argument.kind() {
1823 "comment" => {}
1824 "parameter_declaration" => {
1825 let recovered = stranded_declaration_run(*argument, source).declarations;
1826 if recovered.is_empty() {
1827 break;
1828 }
1829 wrapped_declaration_seen = true;
1830 declarations.extend(recovered);
1831 }
1832 "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
1837 _ => break,
1838 }
1839 }
1840 declarations
1841}
1842
1843struct CollapsedMacroDeclarationRun {
1846 invocation_end: usize,
1849 region_end: usize,
1854}
1855
1856struct MacroInvocationTokens<'tree> {
1868 stack: Vec<Node<'tree>>,
1869 frontier: Option<Node<'tree>>,
1879}
1880
1881impl<'tree> MacroInvocationTokens<'tree> {
1882 fn new(node: Node<'tree>) -> Self {
1883 Self {
1884 stack: vec![node],
1885 frontier: Some(node),
1886 }
1887 }
1888}
1889
1890impl<'tree> Iterator for MacroInvocationTokens<'tree> {
1891 type Item = Node<'tree>;
1892
1893 fn next(&mut self) -> Option<Node<'tree>> {
1894 loop {
1895 let Some(node) = self.stack.pop() else {
1896 let sibling = self.frontier?.next_sibling()?;
1897 self.frontier = Some(sibling);
1898 self.stack.push(sibling);
1899 continue;
1900 };
1901 if node.child_count() == 0 {
1902 if node.kind() == "comment" || node.is_missing() {
1903 continue;
1904 }
1905 return Some(node);
1906 }
1907 let mut cursor = node.walk();
1908 let children = node.children(&mut cursor).collect::<Vec<_>>();
1909 self.stack.extend(children.into_iter().rev());
1910 }
1911 }
1912}
1913
1914fn collapsed_macro_declaration_run<'tree>(
1961 node: Node<'tree>,
1962 source: &str,
1963 ancestry: &ParentIndex<'tree>,
1964) -> Option<CollapsedMacroDeclarationRun> {
1965 let container_end = declaration_scope_container(node, ancestry)?.end_byte();
1966 let mut tokens = MacroInvocationTokens::new(node);
1967 let name = tokens.next()?;
1968 if !matches!(name.kind(), "identifier" | "type_identifier")
1969 || !cpp_export_macro_token(node_text(name, source))
1970 {
1971 return None;
1972 }
1973 if tokens.next()?.kind() != "(" {
1974 return None;
1975 }
1976 let mut depth = 1usize;
1977 let invocation_end = loop {
1978 let token = tokens.next()?;
1979 match token.kind() {
1980 "(" => depth += 1,
1981 ";" => return None,
1982 ")" => {
1983 depth -= 1;
1984 if depth == 0 {
1985 let semicolon = tokens.next()?;
1986 if semicolon.kind() != ";" {
1987 return None;
1988 }
1989 break semicolon.end_byte();
1990 }
1991 }
1992 _ => {}
1993 }
1994 };
1995 if invocation_end >= container_end {
2001 return None;
2002 }
2003 let region_end = if node.end_byte() > invocation_end {
2012 container_end
2013 } else {
2014 invocation_end
2015 };
2016 Some(CollapsedMacroDeclarationRun {
2017 invocation_end,
2018 region_end,
2019 })
2020}
2021
2022fn string_attribute_macro_member_declarators<'tree>(
2043 field: Node<'tree>,
2044 source: &str,
2045) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2046 if field.kind() != "field_declaration"
2047 || field
2048 .child_by_field_name("type")
2049 .is_none_or(|type_node| type_node.kind() != "type_identifier")
2050 {
2051 return None;
2052 }
2053 let declarator = field.child_by_field_name("declarator")?;
2054 if declarator.kind() != "parenthesized_declarator" {
2055 return None;
2056 }
2057 let opening = declarator.named_child(0)?;
2058 if opening.kind() != "ERROR"
2059 || opening
2060 .named_child(0)
2061 .is_none_or(|word| word.kind() != "identifier")
2062 {
2063 return None;
2064 }
2065 let declarations = stranded_declaration_run(declarator, source).declarations;
2066 (!declarations.is_empty()).then_some(declarations)
2067}
2068
2069fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2076 let Some(call) = (node.kind() == "expression_statement")
2077 .then(|| node.named_child(0))
2078 .flatten()
2079 .filter(|child| child.kind() == "call_expression")
2080 else {
2081 return false;
2082 };
2083 call.child_by_field_name("function")
2084 .is_some_and(|function| function.kind() == "identifier")
2085 && call
2086 .child_by_field_name("arguments")
2087 .is_some_and(|arguments| {
2088 let mut cursor = arguments.walk();
2089 arguments.named_child_count() > 0
2090 && arguments
2091 .named_children(&mut cursor)
2092 .all(|argument| argument.kind() == "string_literal")
2093 })
2094}
2095
2096fn cpp_access_label_constructor_call_start(
2109 node: Node<'_>,
2110 class_name: &str,
2111 source: &str,
2112) -> Option<usize> {
2113 if node.kind() != "labeled_statement" {
2114 return None;
2115 }
2116 let label = node.named_child(0)?;
2117 if label.kind() != "statement_identifier"
2118 || !matches!(
2119 node_text(label, source).trim(),
2120 "public" | "private" | "protected"
2121 )
2122 {
2123 return None;
2124 }
2125 let mut starts = Vec::new();
2126 let mut stack = vec![node];
2127 while let Some(current) = stack.pop() {
2128 if current.kind() == "call_expression"
2129 && current
2130 .child_by_field_name("function")
2131 .is_some_and(|function| {
2132 function.kind() == "identifier"
2133 && node_text(function, source).trim() == class_name
2134 })
2135 {
2136 starts.push(current.start_byte());
2137 }
2138 let mut cursor = current.walk();
2139 stack.extend(current.named_children(&mut cursor));
2140 }
2141 let [start] = starts.as_slice() else {
2142 return None;
2143 };
2144 Some(*start)
2145}
2146
2147fn cpp_declarator_function_definition<'tree>(
2151 declarator: Node<'tree>,
2152 ancestry: &ParentIndex<'tree>,
2153) -> Option<Node<'tree>> {
2154 let mut current = declarator;
2155 while let Some(parent) = ancestry.parent(current) {
2156 match parent.kind() {
2157 "function_definition" if parent.child_by_field_name("body").is_some() => {
2158 return Some(parent);
2159 }
2160 "pointer_declarator"
2161 | "reference_declarator"
2162 | "parenthesized_declarator"
2163 | "array_declarator" => current = parent,
2164 _ => return None,
2165 }
2166 }
2167 None
2168}
2169
2170fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2174 let mut current = node;
2175 while let Some(parent) = ancestry.parent(current) {
2176 if parent.kind() == "namespace_definition"
2177 && parent.child_by_field_name("body") == Some(current)
2178 {
2179 return true;
2180 }
2181 current = parent;
2182 }
2183 false
2184}
2185
2186pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2200 let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2201 let root = tree.root_node();
2202 let mut cursor = root.walk();
2203 let items = root
2204 .named_children(&mut cursor)
2205 .filter(|child| child.kind() != "comment")
2206 .collect::<Vec<_>>();
2207 let [item] = items.as_slice() else {
2208 return None;
2209 };
2210 if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2211 return None;
2212 }
2213 match item.kind() {
2214 "function_definition" => Some(item.child_by_field_name("body").is_some()),
2215 "declaration" | "field_declaration" => Some(false),
2216 _ => None,
2217 }
2218}
2219
2220pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2232 let ancestry = ParentIndex::unindexed();
2233 !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2234 || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2235}
2236
2237fn recover_exported_class_function_definition<'tree>(
2238 node: Node<'tree>,
2239 source: &str,
2240) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
2241 if node.kind() != "function_definition" {
2242 return None;
2243 }
2244 if let Some(prefix) = node.prev_named_sibling()
2245 && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2246 && recovered.range.end_byte == node.end_byte()
2247 {
2248 return Some((node, recovered.name, recovered.raw_supertypes));
2249 }
2250 let type_node = node.child_by_field_name("type")?;
2251 let declarator = node.child_by_field_name("declarator")?;
2252
2253 if matches!(
2254 type_node.kind(),
2255 "class_specifier" | "struct_specifier" | "union_specifier"
2256 ) {
2257 let type_name = type_node
2258 .child_by_field_name("name")
2259 .and_then(|name| direct_identifier_name(name, source));
2260 let exported_macro_type = type_name
2261 .as_ref()
2262 .is_some_and(|name| cpp_export_macro_token(name));
2263 if exported_macro_type {
2264 let mut cursor = node.walk();
2265 let errors_before_declarator = node
2266 .named_children(&mut cursor)
2267 .filter(|child| {
2268 child.kind() == "ERROR"
2269 && child.start_byte() >= type_node.end_byte()
2270 && child.end_byte() <= declarator.start_byte()
2271 })
2272 .collect::<Vec<_>>();
2273 if let Some(name) = errors_before_declarator
2274 .iter()
2275 .find_map(|error| displaced_exported_class_name(*error, source))
2276 {
2277 let raw_supertypes = errors_before_declarator
2278 .iter()
2279 .any(|error| malformed_inheritance_syntax(*error))
2280 .then(|| recovered_malformed_base_name(declarator, source))
2281 .flatten()
2282 .map(|base| vec![base]);
2283 return Some((node, name, raw_supertypes));
2284 }
2285 if errors_before_declarator
2286 .iter()
2287 .any(|error| malformed_inheritance_syntax(*error))
2288 {
2289 return None;
2290 }
2291 }
2292 if !exported_macro_type
2293 && let Some(name) = type_name
2294 && !cpp_export_macro_token(&name)
2295 && let Some(base) =
2296 recovered_postfix_export_macro_base(node, type_node, declarator, source)
2297 {
2298 return Some((node, name, Some(vec![base])));
2299 }
2300 if let Some(name) = direct_identifier_name(declarator, source)
2301 && exported_macro_type
2302 && !cpp_export_macro_token(&name)
2303 {
2304 let raw_supertypes = exported_macro_type
2305 .then(|| recovered_single_base_after_declarator(node, declarator, source))
2306 .flatten()
2307 .map(|base| vec![base]);
2308 return Some((node, name, raw_supertypes));
2309 }
2310 if declarator.kind() == "parenthesized_declarator"
2311 && type_node
2312 .child_by_field_name("name")
2313 .and_then(|name| direct_identifier_name(name, source))
2314 .is_some_and(|name| cpp_export_macro_token(&name))
2315 {
2316 if let Some((name, base)) =
2317 recovered_function_like_export_class_owner(declarator, source)
2318 {
2319 return Some((node, name, Some(vec![base])));
2320 }
2321 let body_start = node
2322 .child_by_field_name("body")
2323 .map(|body| body.start_byte())
2324 .unwrap_or(node.end_byte());
2325 let mut cursor = node.walk();
2326 if let Some(name) = node
2327 .named_children(&mut cursor)
2328 .filter(|child| {
2329 child.kind() == "ERROR"
2330 && child.start_byte() >= declarator.end_byte()
2331 && child.end_byte() <= body_start
2332 })
2333 .find_map(|error| declarator_name_from_node(error, source))
2334 {
2335 return Some((node, name, None));
2336 }
2337 }
2338 }
2339
2340 let declarator_text = direct_identifier_name(declarator, source)?;
2341 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2342 return None;
2343 }
2344 class_identifier_before_body(node, source).map(|name| (node, name, None))
2345}
2346
2347fn recovered_function_like_export_class_owner(
2348 declarator: Node<'_>,
2349 source: &str,
2350) -> Option<(String, String)> {
2351 if declarator.kind() != "parenthesized_declarator" {
2352 return None;
2353 }
2354 let mut cursor = declarator.walk();
2355 let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2356 let [prefix, base] = children.as_slice() else {
2357 return None;
2358 };
2359 if prefix.kind() != "ERROR"
2360 || !matches!(
2361 base.kind(),
2362 "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2363 )
2364 {
2365 return None;
2366 }
2367 let mut identifiers = Vec::new();
2368 let mut prefix_cursor = prefix.walk();
2369 for child in prefix.named_children(&mut prefix_cursor) {
2370 match child.kind() {
2371 "number_literal" | "string_literal" | "char_literal" => {}
2372 "identifier" | "type_identifier" => {
2373 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2374 }
2375 _ => return None,
2376 }
2377 }
2378 let name = match identifiers.as_slice() {
2379 [name] => name.clone(),
2380 [name, final_token] if final_token == "final" => name.clone(),
2381 _ => return None,
2382 };
2383 if name.is_empty() || cpp_export_macro_token(&name) {
2384 return None;
2385 }
2386 let base = recovered_malformed_base_name(*base, source)?;
2387 Some((name, base))
2388}
2389
2390fn recovered_export_head_bases(
2404 node: Node<'_>,
2405 after: usize,
2406 before: usize,
2407 source: &str,
2408) -> Vec<String> {
2409 let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2410 let mut bases = Vec::new();
2411 let mut cursor = node.walk();
2412 for child in node.named_children(&mut cursor) {
2413 if child.kind() == "ERROR" {
2414 let mut error_cursor = child.walk();
2415 bases.extend(
2416 child
2417 .named_children(&mut error_cursor)
2418 .filter(within)
2419 .filter_map(|part| recovered_malformed_base_name(part, source)),
2420 );
2421 } else if within(&child)
2422 && let Some(base) = recovered_malformed_base_name(child, source)
2423 {
2424 bases.push(base);
2425 }
2426 }
2427 bases.retain(|base| {
2428 !matches!(
2429 base.as_str(),
2430 "final" | "public" | "protected" | "private" | "virtual"
2431 )
2432 });
2433 bases
2434}
2435
2436fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2441 if token.is_named() {
2442 token.kind() == "identifier" && node_text(token, source) == "final"
2443 } else {
2444 token.kind() == "final"
2445 }
2446}
2447
2448fn recovered_export_head_name<'tree>(
2461 node: Node<'tree>,
2462 tail: Node<'tree>,
2463 source: &str,
2464) -> Option<Node<'tree>> {
2465 export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
2466}
2467
2468fn recovered_export_pair_head_name<'tree>(
2475 prefix: Node<'tree>,
2476 sibling: Node<'tree>,
2477 tail: Node<'tree>,
2478 source: &str,
2479) -> Option<Node<'tree>> {
2480 let mut tokens = export_head_tokens(prefix, None);
2481 tokens.extend(export_head_tokens(sibling, Some(tail)));
2482 export_head_name_from_tokens(&tokens, source)
2483}
2484
2485fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
2489 let mut tokens = Vec::new();
2490 let mut cursor = node.walk();
2491 for child in node.children(&mut cursor) {
2492 if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
2493 break;
2494 }
2495 if child.kind() == "ERROR" {
2496 let mut fragment_cursor = child.walk();
2497 tokens.extend(child.children(&mut fragment_cursor));
2498 } else {
2499 tokens.push(child);
2500 }
2501 }
2502 tokens
2503}
2504
2505fn export_head_name_from_tokens<'tree>(
2507 tokens: &[Node<'tree>],
2508 source: &str,
2509) -> Option<Node<'tree>> {
2510 let mut name = None;
2511 for token in tokens.iter().copied() {
2512 if recovered_export_head_final(token, source) || (!token.is_named() && token.kind() == ":")
2513 {
2514 break;
2515 }
2516 if token.is_named()
2517 && !token.is_missing()
2518 && matches!(
2519 token.kind(),
2520 "identifier" | "type_identifier" | "field_identifier"
2521 )
2522 {
2523 name = Some(token);
2524 }
2525 }
2526 name
2527}
2528
2529fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2532 let mut cursor = declaration.walk();
2533 declaration
2534 .named_children(&mut cursor)
2535 .find(|child| child.kind() == "init_declarator")
2536}
2537
2538fn recovered_export_declaration_tail<'tree>(
2546 declaration: Node<'tree>,
2547 head_end: usize,
2548 source: &str,
2549) -> Option<(Vec<String>, Node<'tree>)> {
2550 let init = recovered_export_init_declarator(declaration)?;
2551 let body = init.child_by_field_name("value")?;
2552 if body.kind() != "initializer_list" {
2553 return None;
2554 }
2555 let mut bases = recovered_export_head_bases(declaration, head_end, init.start_byte(), source);
2556 bases.extend(recovered_export_head_bases(
2557 init,
2558 init.start_byte(),
2559 body.start_byte(),
2560 source,
2561 ));
2562 Some((bases, body))
2563}
2564
2565fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
2579 match node.kind() {
2580 "ERROR" => {
2581 let Some(class_node) = first_class_like_child(node) else {
2582 return false;
2583 };
2584 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2585 return false;
2586 }
2587 if class_node
2588 .child_by_field_name("name")
2589 .and_then(|name| direct_identifier_name(name, source))
2590 .is_none()
2591 {
2592 return false;
2593 }
2594 class_node
2595 .next_sibling()
2596 .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
2597 }
2598 "declaration" => {
2599 let mut cursor = node.walk();
2600 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
2601 let Some(keyword) = children
2607 .iter()
2608 .copied()
2609 .flat_map(|child| {
2610 let mut cursor = child.walk();
2611 if child.kind() == "ERROR" {
2612 child.named_children(&mut cursor).collect::<Vec<_>>()
2613 } else {
2614 vec![child]
2615 }
2616 })
2617 .find(|child| {
2618 child.kind() == "identifier"
2619 && matches!(node_text(*child, source), "class" | "struct" | "union")
2620 })
2621 else {
2622 return false;
2623 };
2624 children.last().is_some_and(|init| {
2625 init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
2626 let mut cursor = init.walk();
2627 let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
2628 matches!(parts.as_slice(), [macro_name, arguments]
2629 if matches!(macro_name.kind(), "identifier" | "type_identifier")
2630 && arguments.kind() == "argument_list")
2631 }
2632 })
2633 }
2634 _ => false,
2635 }
2636}
2637
2638fn recover_function_like_export_class_pair(
2639 node: Node<'_>,
2640 source: &str,
2641) -> Option<RecoveredFunctionLikeExportClassPair> {
2642 if !is_function_like_export_class_head(node, source) {
2643 return None;
2644 }
2645 let sibling = node.next_named_sibling()?;
2646 let (name, raw_supertypes, body) = match sibling.kind() {
2647 "compound_statement" => (
2653 recovered_export_pair_head_name(node, sibling, sibling, source)
2654 .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2655 None,
2656 sibling,
2657 ),
2658 "expression_statement" => {
2659 let compound = sibling.named_child(0)?;
2660 if compound.kind() != "compound_literal_expression" {
2661 return None;
2662 }
2663 let body = compound.child_by_field_name("value")?;
2664 if body.kind() != "initializer_list" {
2665 return None;
2666 }
2667 (
2668 compound
2669 .child_by_field_name("type")
2670 .and_then(|name| direct_identifier_name(name, source))?,
2671 None,
2672 body,
2673 )
2674 }
2675 "labeled_statement" => {
2676 let label = sibling.child_by_field_name("label")?;
2677 if label.kind() != "statement_identifier" {
2678 return None;
2679 }
2680 let name = normalize_cpp_whitespace(node_text(label, source));
2681 let declaration = sibling
2682 .named_children(&mut sibling.walk())
2683 .find(|child| child.kind() == "declaration")?;
2684 let access = declaration.child_by_field_name("type")?;
2685 if !matches!(
2686 node_text(access, source),
2687 "public" | "protected" | "private"
2688 ) {
2689 return None;
2690 }
2691 let (bases, body) =
2692 recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
2693 (name, (!bases.is_empty()).then_some(bases), body)
2694 }
2695 "function_definition" => {
2701 let body = sibling.child_by_field_name("body")?;
2702 if body.kind() != "compound_statement" {
2703 return None;
2704 }
2705 let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
2706 let bases = recovered_export_head_bases(
2707 sibling,
2708 name_node.end_byte(),
2709 body.start_byte(),
2710 source,
2711 );
2712 (
2713 normalize_cpp_whitespace(node_text(name_node, source)),
2714 (!bases.is_empty()).then_some(bases),
2715 body,
2716 )
2717 }
2718 "declaration" => {
2722 let init = recovered_export_init_declarator(sibling)?;
2723 let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
2724 let (bases, body) =
2725 recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
2726 (
2727 normalize_cpp_whitespace(node_text(name_node, source)),
2728 (!bases.is_empty()).then_some(bases),
2729 body,
2730 )
2731 }
2732 _ => return None,
2733 };
2734 debug_assert!(
2735 !name.is_empty(),
2736 "a recovered class head names its class by an identifier token"
2737 );
2738 let range = Range {
2739 start_byte: node.start_byte(),
2740 end_byte: sibling.end_byte(),
2741 start_line: node.start_position().row + 1,
2742 end_line: sibling.end_position().row + 1,
2743 };
2744 Some(RecoveredFunctionLikeExportClassPair {
2745 name,
2746 raw_supertypes,
2747 range,
2748 fragmented_body: recovered_fragmented_export_body(body, range)?,
2749 })
2750}
2751
2752fn recover_embedded_function_like_export_classes(
2759 node: Node<'_>,
2760 source: &str,
2761) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
2762 if !node.is_error() {
2763 return Vec::new();
2764 }
2765
2766 let mut nodes = Vec::new();
2767 let mut class_tokens = Vec::new();
2768 let mut stack = vec![node];
2769 while let Some(current) = stack.pop() {
2770 nodes.push(current);
2771 if !current.is_named() && current.kind() == "class" {
2775 class_tokens.push(current);
2776 }
2777 push_children_reversed(current, &mut stack);
2778 }
2779 if class_tokens.is_empty() {
2780 return Vec::new();
2781 }
2782 class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2783 nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2784
2785 let language = node.language();
2788 let class_kind = NodeKindIds::new(&language, "class");
2789 let identifier_kinds = [
2790 NodeKindIds::new(&language, "identifier"),
2791 NodeKindIds::new(&language, "type_identifier"),
2792 NodeKindIds::new(&language, "field_identifier"),
2793 ];
2794 let argument_list_kind = NodeKindIds::new(&language, "argument_list");
2795 let colon_kind = NodeKindIds::new(&language, ":");
2796 let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
2797
2798 let mut recovered = Vec::new();
2799 for class_token in class_tokens {
2800 debug_assert!(class_kind.matches(class_token));
2801 let row = class_token.start_position().row;
2802 let is_identifier = |candidate: &Node<'_>| {
2808 !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
2809 };
2810 let Some(macro_name) = nodes.iter().copied().find(|candidate| {
2811 candidate.start_byte() >= class_token.end_byte()
2812 && candidate.start_position().row == row
2813 && is_identifier(candidate)
2814 }) else {
2815 continue;
2816 };
2817 let Some(arguments) = nodes.iter().copied().find(|candidate| {
2818 argument_list_kind.matches(*candidate)
2819 && candidate.start_byte() >= macro_name.end_byte()
2820 && candidate.start_position().row == row
2821 }) else {
2822 continue;
2823 };
2824 if nodes.iter().any(|candidate| {
2825 is_identifier(candidate)
2826 && candidate.start_byte() >= macro_name.end_byte()
2827 && candidate.end_byte() <= arguments.start_byte()
2828 }) {
2829 continue;
2830 }
2831 let Some(head_end) = nodes.iter().copied().find(|candidate| {
2832 candidate.start_byte() >= arguments.end_byte()
2833 && (recovered_export_head_final(*candidate, source)
2834 || (!candidate.is_named() && colon_kind.matches(*candidate)))
2835 }) else {
2836 continue;
2837 };
2838 let Some(name_node) = nodes.iter().copied().rfind(|candidate| {
2839 is_identifier(candidate)
2840 && candidate.start_byte() >= arguments.end_byte()
2841 && candidate.end_byte() <= head_end.start_byte()
2842 && candidate.start_position().row == row
2843 }) else {
2844 continue;
2845 };
2846 let name = normalize_cpp_whitespace(node_text(name_node, source));
2847 let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
2848 field_initializer_kind.matches(*candidate)
2849 && candidate.start_byte() >= name_node.end_byte()
2850 && candidate
2851 .child_by_field_name("field")
2852 .or_else(|| candidate.named_child(0))
2853 .is_some()
2854 && candidate
2855 .child_by_field_name("value")
2856 .or_else(|| {
2857 let mut cursor = candidate.walk();
2858 candidate
2859 .named_children(&mut cursor)
2860 .find(|child| child.kind() == "initializer_list")
2861 })
2862 .is_some_and(|value| value.kind() == "initializer_list")
2863 }) else {
2864 continue;
2865 };
2866 let has_access = nodes.iter().copied().any(|candidate| {
2867 candidate.start_byte() >= name_node.end_byte()
2868 && candidate.end_byte() <= base_initializer.start_byte()
2869 && matches!(
2870 normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
2871 "public" | "protected" | "private"
2872 )
2873 });
2874 if !has_access {
2875 continue;
2876 }
2877 let Some(base_node) = base_initializer
2878 .child_by_field_name("field")
2879 .or_else(|| base_initializer.named_child(0))
2880 else {
2881 continue;
2882 };
2883 let Some(base) = recovered_malformed_base_name(base_node, source) else {
2884 continue;
2885 };
2886 let body = base_initializer
2887 .child_by_field_name("value")
2888 .or_else(|| {
2889 let mut cursor = base_initializer.walk();
2890 base_initializer
2891 .named_children(&mut cursor)
2892 .find(|child| child.kind() == "initializer_list")
2893 })
2894 .expect("initializer-list value checked above");
2895 let range = Range {
2896 start_byte: class_token.start_byte(),
2897 end_byte: body.end_byte(),
2898 start_line: class_token.start_position().row + 1,
2899 end_line: body.end_position().row + 1,
2900 };
2901 if recovered
2902 .iter()
2903 .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
2904 existing.name == name && existing.range == range
2905 })
2906 {
2907 continue;
2908 }
2909 recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
2910 name,
2911 range,
2912 raw_supertypes: vec![base],
2913 fragmented_body: match recovered_fragmented_export_body(body, range) {
2914 Some(fragmented) => fragmented,
2915 None => continue,
2916 },
2917 });
2918 }
2919 recovered
2920}
2921
2922fn lifted_function_like_export_class_namespace<'tree>(
2923 node: Node<'tree>,
2924 source: &str,
2925 ancestry: &ParentIndex<'tree>,
2926) -> Option<String> {
2927 let mut anchor = node;
2933 let parent = loop {
2934 let parent = ancestry.parent(anchor)?;
2935 if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
2936 break parent;
2937 }
2938 anchor = parent;
2939 };
2940 let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
2941 sibling.start_byte() > anchor.end_byte()
2942 && sibling.kind() == "ERROR"
2943 && sibling.named_child_count() == 0
2944 && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
2945 });
2946 if !has_later_close {
2947 return None;
2948 }
2949 let candidates = parent
2950 .named_children(&mut parent.walk())
2951 .filter(|sibling| {
2952 sibling.kind() == "namespace_definition"
2953 && sibling.has_error()
2954 && sibling.end_byte() < anchor.start_byte()
2955 })
2956 .filter_map(|namespace| {
2957 namespace
2958 .child_by_field_name("name")
2959 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
2960 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
2961 })
2962 .collect::<Vec<_>>();
2963 let [namespace] = candidates.as_slice() else {
2964 return None;
2965 };
2966 Some(namespace.clone())
2967}
2968
2969pub(crate) fn recovered_function_like_export_class_pair_has_body(
2970 node: Node<'_>,
2971 source: &str,
2972 identifier: &str,
2973 range: &Range,
2974) -> bool {
2975 recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
2976 recovered.name == identifier
2977 && recovered.range.start_byte == range.start_byte
2978 && recovered.range.end_byte == range.end_byte
2979 })
2980}
2981
2982#[derive(Default)]
2998pub struct CppRecoveredExportClassIndex {
2999 by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3000}
3001
3002impl CppRecoveredExportClassIndex {
3003 pub fn build(root: Node<'_>, source: &str) -> Self {
3004 let mut by_error_node: HashMap<
3005 (usize, usize),
3006 Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3007 > = HashMap::default();
3008 let mut cursor = root.walk();
3011 let mut stack = vec![root];
3012 while let Some(node) = stack.pop() {
3013 if node.is_error() {
3014 let recovered = recover_embedded_function_like_export_classes(node, source);
3015 if !recovered.is_empty() {
3016 by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3017 }
3018 }
3019 stack.extend(node.named_children(&mut cursor));
3020 }
3021 Self { by_error_node }
3022 }
3023
3024 pub fn approximate_size(&self) -> usize {
3026 self.by_error_node
3027 .values()
3028 .fold(0usize, |total, recovered| {
3029 recovered.iter().fold(
3030 total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3031 |acc, class| {
3032 acc.saturating_add(std::mem::size_of::<
3033 RecoveredEmbeddedFunctionLikeExportClass,
3034 >())
3035 .saturating_add(class.name.len())
3036 .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3037 },
3038 )
3039 })
3040 }
3041
3042 fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3043 self.by_error_node
3044 .get(&(node.start_byte(), node.end_byte()))
3045 .is_some_and(|recovered| {
3046 recovered.iter().any(|class| {
3047 class.name == identifier
3048 && class.range.start_byte == range.start_byte
3049 && class.range.end_byte == range.end_byte
3050 })
3051 })
3052 }
3053}
3054
3055#[cfg(any(test, feature = "test-support"))]
3062thread_local! {
3063 static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3064 const { std::cell::Cell::new(0) };
3065}
3066
3067#[cfg(any(test, feature = "test-support"))]
3071#[doc(hidden)]
3072pub fn recovered_class_body_node_visits_for_test() -> usize {
3073 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3074}
3075
3076#[cfg(any(test, feature = "test-support"))]
3078#[doc(hidden)]
3079pub fn reset_recovered_class_body_node_visits_for_test() {
3080 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3081}
3082
3083#[cfg(any(test, feature = "test-support"))]
3084fn record_recovered_class_body_visit() {
3085 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3086}
3087
3088#[cfg(not(any(test, feature = "test-support")))]
3089fn record_recovered_class_body_visit() {}
3090
3091pub(crate) fn recovered_class_body_at(
3114 recovered_export_classes: &CppRecoveredExportClassIndex,
3115 root: Node<'_>,
3116 source: &str,
3117 identifier: &str,
3118 range: &Range,
3119) -> Option<bool> {
3120 let covers_range_start = |node: &Node<'_>| {
3121 node.start_byte() <= range.start_byte
3122 && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3123 };
3124 let mut stack = vec![root];
3125 let mut saw_forward = false;
3126 while let Some(node) = stack.pop() {
3127 record_recovered_class_body_visit();
3128 if (node.start_byte() == range.start_byte
3132 && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3133 || recovered_export_classes.claims(node, identifier, range)
3134 || (node.start_byte() == range.start_byte
3135 && recovered_fragmented_class_has_body(node, source, identifier, range))
3136 {
3137 return Some(true);
3138 }
3139 if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3140 return Some(true);
3141 }
3142 if node.start_byte() <= range.start_byte
3149 && range.start_byte < node.end_byte()
3150 && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3151 {
3152 if has_body {
3153 return Some(true);
3154 }
3155 saw_forward = true;
3156 continue;
3157 }
3158 let mut cursor = node.walk();
3159 stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3160 }
3161 saw_forward.then_some(false)
3162}
3163
3164fn recovered_collapsed_aggregate_has_body(
3177 node: Node<'_>,
3178 source: &str,
3179 identifier: &str,
3180 range: &Range,
3181) -> bool {
3182 let claims = |head: &CppCollapsedAggregateHead<'_>| {
3183 head.key.start_byte() == range.start_byte
3184 && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3185 };
3186 if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3187 return true;
3188 }
3189 if !node.is_error() {
3190 return false;
3191 }
3192 let mut cursor = node.walk();
3193 let children = node.children(&mut cursor).collect::<Vec<_>>();
3194 children
3195 .iter()
3196 .enumerate()
3197 .filter(|(_, child)| {
3198 child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3199 })
3200 .any(|(index, _)| {
3201 cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3202 })
3203}
3204
3205pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3213 if !matches!(
3214 node.kind(),
3215 "qualified_identifier" | "scoped_type_identifier" | "template_type"
3216 ) {
3217 return false;
3218 }
3219 if let Some(function) = node.parent().filter(|parent| {
3220 parent.kind() == "function_definition"
3221 && parent
3222 .child_by_field_name("declarator")
3223 .is_some_and(|declarator| same_node(declarator, node))
3224 }) {
3225 return recover_exported_class_function_definition(function, source)
3226 .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
3227 }
3228 let Some(initializer) = node.parent().filter(|parent| {
3229 parent.kind() == "init_declarator"
3230 && parent
3231 .child_by_field_name("declarator")
3232 .is_some_and(|declarator| same_node(declarator, node))
3233 }) else {
3234 return false;
3235 };
3236 initializer
3237 .parent()
3238 .filter(|parent| parent.kind() == "declaration")
3239 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3240 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3241}
3242
3243struct CppSentinelReparsedClass<'tree> {
3249 declaration_node: Node<'tree>,
3250 name: String,
3251 body: Node<'tree>,
3252 raw_supertypes: Option<Vec<String>>,
3253}
3254
3255fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3256 let mut cursor = root.walk();
3257 root.named_children(&mut cursor)
3258 .find(|child| child.kind() != "comment")
3259 .filter(|child| child.kind() == "template_declaration")
3260}
3261
3262fn cpp_sentinel_reparsed_class<'tree>(
3263 root: Node<'tree>,
3264 template_node: Option<Node<'tree>>,
3265 source: &str,
3266 ancestry: &ParentIndex<'tree>,
3267) -> Option<CppSentinelReparsedClass<'tree>> {
3268 let container = template_node.unwrap_or(root);
3269 let mut cursor = container.walk();
3270 for child in container.named_children(&mut cursor) {
3271 if matches!(
3272 child.kind(),
3273 "class_specifier" | "struct_specifier" | "union_specifier"
3274 ) {
3275 let name = class_like_name(child, source, ancestry)?;
3276 let body = cpp_body_node(child)?;
3277 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
3278 .then(|| extract_cpp_supertypes(child, source));
3279 return Some(CppSentinelReparsedClass {
3280 declaration_node: child,
3281 name,
3282 body,
3283 raw_supertypes,
3284 });
3285 }
3286 if child.kind() == "declaration"
3287 && let Some(class_node) = first_class_like_child(child)
3288 {
3289 let name = class_like_name(class_node, source, ancestry)?;
3290 let body = cpp_body_node(class_node)?;
3291 let raw_supertypes =
3292 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3293 .then(|| extract_cpp_supertypes(class_node, source));
3294 return Some(CppSentinelReparsedClass {
3295 declaration_node: class_node,
3296 name,
3297 body,
3298 raw_supertypes,
3299 });
3300 }
3301 if child.kind() == "function_definition"
3306 && let Some(class_node) = first_class_like_child(child)
3307 && let Some(body) = cpp_body_node(class_node)
3308 && let Some(name) = class_like_name(class_node, source, ancestry)
3309 {
3310 let raw_supertypes =
3311 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3312 .then(|| extract_cpp_supertypes(class_node, source));
3313 return Some(CppSentinelReparsedClass {
3314 declaration_node: class_node,
3315 name,
3316 body,
3317 raw_supertypes,
3318 });
3319 }
3320 if child.kind() == "function_definition"
3321 && let Some((_, name, raw_supertypes)) =
3322 recover_exported_class_function_definition(child, source)
3323 {
3324 let body = cpp_body_node(child)?;
3325 return Some(CppSentinelReparsedClass {
3326 declaration_node: child,
3327 name,
3328 body,
3329 raw_supertypes,
3330 });
3331 }
3332 }
3333 None
3334}
3335
3336fn recovered_postfix_export_macro_base(
3337 node: Node<'_>,
3338 type_node: Node<'_>,
3339 declarator: Node<'_>,
3340 source: &str,
3341) -> Option<String> {
3342 let mut cursor = node.walk();
3343 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
3344 child.kind() == "ERROR"
3345 && child.start_byte() >= type_node.end_byte()
3346 && child.end_byte() <= declarator.start_byte()
3347 && postfix_export_macro_inheritance(*child, source)
3348 });
3349 malformed_clauses.next()?;
3350 if malformed_clauses.next().is_some() {
3351 return None;
3352 }
3353 recovered_malformed_base_name(declarator, source)
3354}
3355
3356fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
3357 let mut macro_count = 0;
3358 let mut colon_count = 0;
3359 let mut access_count = 0;
3360 for index in 0..node.child_count() {
3361 let Some(child) = node.child(index) else {
3362 return false;
3363 };
3364 match child.kind() {
3365 "identifier" | "type_identifier" if child.is_named() => {
3366 let candidate = normalize_cpp_whitespace(node_text(child, source));
3367 if !cpp_export_macro_token(&candidate) {
3368 return false;
3369 }
3370 macro_count += 1;
3371 }
3372 ":" if !child.is_named() => colon_count += 1,
3373 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
3374 _ => return false,
3375 }
3376 }
3377 macro_count == 1 && colon_count == 1 && access_count == 1
3378}
3379
3380fn recovered_single_base_after_declarator(
3381 node: Node<'_>,
3382 declarator: Node<'_>,
3383 source: &str,
3384) -> Option<String> {
3385 let body_start = node
3386 .child_by_field_name("body")
3387 .map(|body| body.start_byte())
3388 .unwrap_or(node.end_byte());
3389 let mut cursor = node.walk();
3390 let mut bases = node
3391 .named_children(&mut cursor)
3392 .filter(|child| {
3393 child.kind() == "ERROR"
3394 && child.start_byte() >= declarator.end_byte()
3395 && child.end_byte() <= body_start
3396 })
3397 .filter_map(|error| displaced_exported_class_name(error, source));
3398 let base = bases.next()?;
3399 bases.next().is_none().then_some(base)
3400}
3401
3402fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
3403 (0..node.child_count()).any(|index| {
3404 node.child(index)
3405 .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
3406 })
3407}
3408
3409pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3410 recover_exported_class_function_definition(node, source).is_some()
3411}
3412
3413fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3414 matches!(
3415 kind,
3416 "ERROR"
3417 | "preproc_if"
3418 | "preproc_ifdef"
3419 | "preproc_ifndef"
3420 | "preproc_else"
3421 | "preproc_elif"
3422 ) || (kind == "labeled_statement" && in_class_scope)
3423}
3424
3425pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3426 if node.kind() != "declaration" {
3427 return false;
3428 }
3429 let mut ancestor = node.parent();
3430 while let Some(container) = ancestor {
3431 match container.kind() {
3432 "compound_statement" => {
3433 return container.parent().is_some_and(|class_container| {
3434 is_recovered_exported_class_container(class_container, source)
3435 });
3436 }
3437 "template_declaration" | "linkage_specification" | "declaration_list" => {}
3440 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3441 _ => return false,
3442 }
3443 ancestor = container.parent();
3444 }
3445 false
3446}
3447
3448pub fn recovered_exported_class_has_body(
3449 node: Node<'_>,
3450 source: &str,
3451 expected_name: &str,
3452) -> Option<bool> {
3453 match node.kind() {
3454 "function_definition" => {
3455 let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
3456 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3457 }
3458 "declaration" | "field_declaration" => {
3459 let recovered = recover_exported_class_declaration(node, source)?;
3460 (recovered.name == expected_name).then(|| recovered.body.is_some())
3461 }
3462 _ => None,
3463 }
3464}
3465
3466fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3467 let body_start = node
3468 .child_by_field_name("body")
3469 .map(|body| body.start_byte())
3470 .unwrap_or(node.end_byte());
3471 let mut stack = Vec::new();
3472 for index in (0..node.named_child_count()).rev() {
3473 let Some(child) = node.named_child(index) else {
3474 continue;
3475 };
3476 if child.start_byte() >= body_start {
3477 continue;
3478 }
3479 stack.push(child);
3480 }
3481
3482 let mut best = None;
3483 while let Some(current) = stack.pop() {
3484 if matches!(current.kind(), "identifier" | "type_identifier") {
3485 let name = normalize_cpp_whitespace(node_text(current, source));
3486 if !name.is_empty()
3487 && !cpp_export_macro_token(&name)
3488 && !matches!(name.as_str(), "class" | "struct" | "union")
3489 {
3490 best = Some(name);
3491 }
3492 continue;
3493 }
3494
3495 for index in (0..current.named_child_count()).rev() {
3496 if let Some(child) = current.named_child(index)
3497 && child.start_byte() < body_start
3498 {
3499 stack.push(child);
3500 }
3501 }
3502 }
3503 best
3504}
3505
3506fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3507 if node.kind() == "declaration"
3508 && node
3509 .child_by_field_name("type")
3510 .or_else(|| first_class_like_child(node))
3511 .is_some_and(|type_node| {
3512 matches!(
3513 type_node.kind(),
3514 "class_specifier" | "struct_specifier" | "union_specifier"
3515 )
3516 })
3517 && let Some(name) = node
3518 .child_by_field_name("declarator")
3519 .and_then(|declarator| declarator_name_from_node(declarator, source))
3520 && !cpp_export_macro_token(&name)
3521 {
3522 return Some(name);
3523 }
3524
3525 if node.kind() == "function_definition"
3526 && node.child_by_field_name("type").is_some_and(|type_node| {
3527 matches!(
3528 type_node.kind(),
3529 "class_specifier" | "struct_specifier" | "union_specifier"
3530 )
3531 })
3532 && let Some(name) = node
3533 .child_by_field_name("declarator")
3534 .and_then(|declarator| direct_identifier_name(declarator, source))
3535 && !cpp_export_macro_token(&name)
3536 {
3537 return Some(name);
3538 }
3539
3540 let class_node = if matches!(
3541 node.kind(),
3542 "class_specifier" | "struct_specifier" | "union_specifier"
3543 ) {
3544 node
3545 } else {
3546 first_class_like_child(node)?
3547 };
3548 class_like_name_from_children(class_node, source)
3549}
3550
3551fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3552 if !matches!(
3553 node.kind(),
3554 "identifier" | "field_identifier" | "type_identifier"
3555 ) {
3556 return None;
3557 }
3558 let name = normalize_cpp_whitespace(node_text(node, source));
3559 (!name.is_empty()).then_some(name)
3560}
3561
3562fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3563 match node.kind() {
3564 "identifier" | "field_identifier" | "type_identifier" => {
3565 let name = normalize_cpp_whitespace(node_text(node, source));
3566 (!name.is_empty()).then_some(name)
3567 }
3568 _ => {
3569 let mut cursor = node.walk();
3570 node.named_children(&mut cursor)
3571 .find_map(|child| declarator_name_from_node(child, source))
3572 }
3573 }
3574}
3575
3576fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3577 let mut cursor = node.walk();
3578 node.named_children(&mut cursor).find(|child| {
3579 matches!(
3580 child.kind(),
3581 "class_specifier" | "struct_specifier" | "union_specifier"
3582 )
3583 })
3584}
3585
3586fn push_cpp_container_work<'tree>(
3591 node: Node<'tree>,
3592 scope: ScopeInfo,
3593 stack: &mut Vec<CppWork<'tree>>,
3594) {
3595 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3596}
3597
3598fn push_cpp_sibling_range<'tree>(
3602 parent: Node<'tree>,
3603 start_index: usize,
3604 end_index: usize,
3605 scope: ScopeInfo,
3606 stack: &mut Vec<CppWork<'tree>>,
3607) {
3608 let mut cursor = parent.walk();
3609 let children = parent
3610 .named_children(&mut cursor)
3611 .skip(start_index)
3612 .take(end_index.saturating_sub(start_index))
3613 .collect::<Vec<_>>()
3614 .into_iter();
3615 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3616}
3617
3618fn advance_cpp_siblings<'tree>(
3626 mut siblings: CppSiblingsWork<'tree>,
3627 source: &str,
3628 stack: &mut Vec<CppWork<'tree>>,
3629) {
3630 let Some(child) = siblings.children.next() else {
3631 return;
3632 };
3633 let current_scope = siblings.scope.clone();
3634 if let Some(namespace) = cpp_using_namespace_target(child, source) {
3635 siblings.scope.visible_using_namespaces.push(namespace);
3636 }
3637 if !siblings.children.as_slice().is_empty() {
3638 stack.push(CppWork::Siblings(siblings));
3639 }
3640 stack.push(CppWork::Node(CppNodeWork {
3641 node: child,
3642 scope: current_scope,
3643 }));
3644}
3645
3646fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3653 if node.kind() != "using_declaration" {
3654 return None;
3655 }
3656 let mut cursor = node.walk();
3657 let is_namespace_directive = node
3658 .children(&mut cursor)
3659 .any(|child| child.kind() == "namespace");
3660 if !is_namespace_directive {
3661 return None;
3662 }
3663 let target = node.named_child(0)?;
3664 let start = target
3672 .child(0)
3673 .filter(|child| !child.is_named() && child.kind() == "::")
3674 .map_or(target.start_byte(), |marker| marker.end_byte());
3675 let text = normalize_cpp_whitespace(
3676 source
3677 .get(start..target.end_byte())
3678 .expect("using-directive target covers one source range"),
3679 );
3680 (!text.is_empty()).then_some(text)
3681}
3682
3683pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
3696 let mut parser = Parser::new();
3697 if parser
3698 .set_language(&tree_sitter_cpp::LANGUAGE.into())
3699 .is_err()
3700 {
3701 return Vec::new();
3702 }
3703 let Some(tree) = parser.parse(source, None) else {
3704 return Vec::new();
3705 };
3706 let mut namespaces = Vec::new();
3707 let mut seen = std::collections::HashSet::new();
3708 let mut stack = vec![tree.root_node()];
3709 while let Some(node) = stack.pop() {
3710 if let Some(namespace) = cpp_using_namespace_target(node, source)
3711 && seen.insert(namespace.clone())
3712 {
3713 namespaces.push(namespace);
3714 }
3715 let mut cursor = node.walk();
3716 stack.extend(node.named_children(&mut cursor));
3717 }
3718 namespaces
3719}
3720
3721pub struct CppVisitor<'a> {
3722 pub file: &'a ProjectFile,
3723 pub source: &'a str,
3724 pub parsed: &'a mut ParsedFile,
3725 pub c_tag_semantics: bool,
3736 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
3737 pub consumed_fragment_regions: Vec<(usize, usize)>,
3746 pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
3751 pub partitioned_regions: Vec<(Tree, std::ops::Range<usize>, ScopeInfo)>,
3754 pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
3759 pub field_owners: Option<CppFieldOwnerIndex>,
3763 pub recovery_captures: Vec<CppRecoveryCapture>,
3767 pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
3771 pub ambiguous_object_macro_fields: HashSet<String>,
3774}
3775
3776#[derive(Clone, Debug, PartialEq, Eq)]
3783pub enum ObjectMacroFieldEvent {
3784 Define {
3785 name: String,
3786 replacement: ObjectMacroReplacement,
3787 conditional: bool,
3788 },
3789 Undef {
3790 name: String,
3791 conditional: bool,
3792 },
3793}
3794
3795pub fn collect_cpp_object_macro_fields<'tree>(
3799 root: Node<'tree>,
3800 source: &str,
3801) -> HashMap<String, ObjectMacroReplacement> {
3802 let mut fields = HashMap::default();
3803 let mut ambiguous = HashSet::default();
3804 for event in collect_cpp_object_macro_field_events(root, source) {
3805 match event {
3806 ObjectMacroFieldEvent::Define {
3807 name,
3808 replacement: value,
3809 conditional,
3810 } => {
3811 if value.is_empty() {
3812 fields.remove(&name);
3813 if conditional {
3814 ambiguous.insert(name);
3815 } else {
3816 ambiguous.remove(&name);
3817 }
3818 } else if ambiguous.contains(&name) {
3819 } else if let Some(previous) = fields.get(&name) {
3821 if previous != &value {
3822 fields.remove(&name);
3823 ambiguous.insert(name);
3824 }
3825 } else {
3826 fields.insert(name, value);
3827 }
3828 }
3829 ObjectMacroFieldEvent::Undef { name, conditional } => {
3830 fields.remove(&name);
3831 if conditional {
3832 ambiguous.insert(name);
3833 } else {
3834 ambiguous.remove(&name);
3835 }
3836 }
3837 }
3838 }
3839 fields
3840}
3841
3842pub fn collect_cpp_object_macro_field_events<'tree>(
3847 root: Node<'tree>,
3848 source: &str,
3849) -> Vec<ObjectMacroFieldEvent> {
3850 let mut events = Vec::new();
3851 let mut stack = vec![root];
3852 while let Some(node) = stack.pop() {
3853 if node.kind() == "preproc_def"
3854 && let Some(name) = extract_macro_name(node, source)
3855 {
3856 let replacement = object_macro_replacement_of(node, source);
3857 events.push(ObjectMacroFieldEvent::Define {
3858 name,
3859 replacement,
3860 conditional: inside_preprocessor_conditional(node),
3861 });
3862 } else if is_cpp_undef_directive(node, source)
3863 && let Some(argument) = node.child_by_field_name("argument")
3864 {
3865 events.push(ObjectMacroFieldEvent::Undef {
3866 name: node_text(argument, source).trim().to_string(),
3867 conditional: inside_preprocessor_conditional(node),
3868 });
3869 }
3870 let mut cursor = node.walk();
3871 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3872 stack.extend(children.into_iter().rev());
3873 }
3874 events
3875}
3876
3877fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
3878 let mut current = node.parent();
3879 while let Some(parent) = current {
3880 if matches!(
3881 parent.kind(),
3882 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
3883 ) {
3884 return true;
3885 }
3886 current = parent.parent();
3887 }
3888 false
3889}
3890
3891fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
3892 node.kind() == "preproc_call"
3893 && node
3894 .child_by_field_name("directive")
3895 .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
3896}
3897
3898impl<'a> CppVisitor<'a> {
3899 fn add_declaration(
3907 &mut self,
3908 code_unit: CodeUnit,
3909 node: Node<'_>,
3910 parent: Option<CodeUnit>,
3911 top_level: Option<CodeUnit>,
3912 ) {
3913 self.note_declaration(&code_unit);
3914 let source = self.source;
3915 self.parsed
3916 .add_code_unit(code_unit, node, source, parent, top_level);
3917 }
3918
3919 fn add_declaration_with_range(
3921 &mut self,
3922 code_unit: CodeUnit,
3923 range: Range,
3924 parent: Option<CodeUnit>,
3925 top_level: Option<CodeUnit>,
3926 ) {
3927 self.note_declaration(&code_unit);
3928 self.parsed
3929 .add_code_unit_with_range(code_unit, range, parent, top_level);
3930 }
3931
3932 fn replace_declaration_deferred(
3934 &mut self,
3935 code_unit: CodeUnit,
3936 node: Node<'_>,
3937 parent: Option<CodeUnit>,
3938 top_level: Option<CodeUnit>,
3939 ) {
3940 self.note_replaced_declaration(&code_unit);
3941 let source = self.source;
3942 self.parsed
3943 .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
3944 }
3945
3946 fn replace_declaration_with_range_deferred(
3948 &mut self,
3949 code_unit: CodeUnit,
3950 range: Range,
3951 parent: Option<CodeUnit>,
3952 top_level: Option<CodeUnit>,
3953 ) {
3954 self.note_replaced_declaration(&code_unit);
3955 self.parsed
3956 .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
3957 }
3958
3959 fn note_declaration(&mut self, code_unit: &CodeUnit) {
3966 if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
3967 for capture in &mut self.recovery_captures {
3968 if capture.removed_pre_existing.contains(code_unit) {
3969 continue;
3970 }
3971 if capture.created_units.insert(code_unit.clone()) {
3972 capture.created.push(code_unit.clone());
3973 }
3974 }
3975 }
3976 if let Some(field_owners) = self.field_owners.as_mut() {
3977 field_owners.record(code_unit, self.file);
3978 }
3979 }
3980
3981 fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
3990 let removes_children = self.parsed.contains_declaration(code_unit)
3991 && self
3992 .parsed
3993 .children
3994 .get(code_unit)
3995 .is_some_and(|children| !children.is_empty());
3996 if removes_children {
3997 if !self.recovery_captures.is_empty() {
3998 let removed = self.declarations_a_replacement_removes(code_unit);
3999 for capture in &mut self.recovery_captures {
4000 for unit in &removed {
4001 if !capture.created_units.contains(unit) {
4006 capture.removed_pre_existing.insert(unit.clone());
4007 }
4008 }
4009 }
4010 }
4011 self.field_owners = None;
4012 }
4013 self.note_declaration(code_unit);
4014 }
4015
4016 fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
4019 let mut removed = Vec::new();
4020 let mut seen = HashSet::default();
4021 let mut pending: Vec<CodeUnit> = self
4022 .parsed
4023 .children
4024 .get(code_unit)
4025 .cloned()
4026 .unwrap_or_default();
4027 while let Some(unit) = pending.pop() {
4028 if !seen.insert(unit.clone()) {
4029 continue;
4030 }
4031 if let Some(children) = self.parsed.children.get(&unit) {
4032 pending.extend(children.iter().cloned());
4033 }
4034 removed.push(unit);
4035 }
4036 removed
4037 }
4038
4039 fn visit_function_like_export_class_pair<'tree>(
4040 &mut self,
4041 node: Node<'tree>,
4042 scope: &ScopeInfo,
4043 stack: &mut Vec<CppWork<'tree>>,
4044 ancestry: &ParentIndex<'tree>,
4045 ) -> bool {
4046 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
4047 return false;
4048 };
4049 let member_outcome = self
4050 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
4051 let mut displaced = node.next_named_sibling();
4057 while let Some(candidate) = displaced {
4058 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
4059 break;
4060 }
4061 displaced = candidate.next_named_sibling();
4062 }
4063 let class_unit = self.visit_named_class_like_shape(
4064 node,
4065 recovered.name,
4066 None,
4071 true,
4072 Some(recovered.range),
4073 recovered.raw_supertypes,
4074 scope,
4075 stack,
4076 ancestry,
4077 );
4078 self.parsed
4079 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4080 recovery: recovered.range,
4081 unit: class_unit.clone(),
4082 });
4083 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4084 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4085 tree.root_node(),
4086 class_unit.identifier(),
4087 self.source,
4088 )
4089 {
4090 self.visit_recovered_fragment_constructor(
4091 range,
4092 body,
4093 node,
4094 &class_unit,
4095 scope,
4096 ancestry,
4097 );
4098 }
4099 if let Some(outcome) = member_outcome {
4100 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4101 }
4102 self.consumed_fragment_regions
4103 .push((node.start_byte(), recovered.range.end_byte));
4104 true
4105 }
4106
4107 fn visit_embedded_function_like_export_classes<'tree>(
4108 &mut self,
4109 node: Node<'tree>,
4110 scope: &ScopeInfo,
4111 stack: &mut Vec<CppWork<'tree>>,
4112 ancestry: &ParentIndex<'tree>,
4113 ) -> bool {
4114 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
4115 let found = !recovered_classes.is_empty();
4116 for recovered in recovered_classes {
4117 let member_outcome = self.reparse_fragmented_export_class_members(
4118 &recovered.fragmented_body,
4119 &recovered.name,
4120 );
4121 let class_unit = self.visit_named_class_like_shape(
4122 node,
4123 recovered.name,
4124 None,
4125 true,
4126 Some(recovered.range),
4127 Some(recovered.raw_supertypes),
4128 scope,
4129 stack,
4130 ancestry,
4131 );
4132 self.parsed
4133 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4134 recovery: recovered.range,
4135 unit: class_unit.clone(),
4136 });
4137 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4138 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4139 tree.root_node(),
4140 class_unit.identifier(),
4141 self.source,
4142 )
4143 {
4144 self.visit_recovered_fragment_constructor(
4145 range,
4146 body,
4147 node,
4148 &class_unit,
4149 scope,
4150 ancestry,
4151 );
4152 }
4153 if let Some(outcome) = member_outcome {
4154 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4155 }
4156 }
4157 found
4158 }
4159
4160 #[allow(clippy::too_many_arguments)]
4168 pub fn visit_container<'tree>(
4169 &mut self,
4170 node: Node<'tree>,
4171 ancestry: &ParentIndex<'tree>,
4172 package_name: &str,
4173 module: Option<CodeUnit>,
4174 class_unit: Option<CodeUnit>,
4175 template_signature: Option<String>,
4176 visible_using_namespaces: Vec<String>,
4177 ) {
4178 let scope = ScopeInfo {
4179 package_name: package_name.to_string(),
4180 module,
4181 class_unit,
4182 template_signature,
4183 template_metadata: None,
4184 declarations_are_fields: false,
4185 recovered_specialization_member_scope: false,
4186 visible_using_namespaces,
4187 };
4188 if node.is_error() {
4195 self.visit_object_macro_error_classes(node, &scope);
4196 }
4197 self.run_container_work(node, scope, ancestry);
4198 while let Some((tree, range, scope)) = self.partitioned_regions.pop() {
4199 let root = tree.root_node();
4200 let container = root
4201 .descendant_for_byte_range(range.start, range.end)
4202 .expect("the queued container belongs to this tree");
4203 assert_eq!(container.byte_range(), range);
4204 self.run_container_work(container, scope, &ParentIndex::new(root));
4205 }
4206 }
4207
4208 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
4212 self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
4213 }
4214
4215 fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
4220 self.consumed_fragment_regions
4221 .iter()
4222 .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
4223 }
4224
4225 fn run_container_work<'tree>(
4237 &mut self,
4238 node: Node<'tree>,
4239 scope: ScopeInfo,
4240 ancestry: &ParentIndex<'tree>,
4241 ) {
4242 self.drain_cpp_work(
4243 vec![CppWork::Container(CppContainer { node, scope })],
4244 ancestry,
4245 );
4246 }
4247
4248 fn drain_cpp_work<'tree>(
4255 &mut self,
4256 mut stack: Vec<CppWork<'tree>>,
4257 ancestry: &ParentIndex<'tree>,
4258 ) {
4259 while let Some(work) = stack.pop() {
4260 match work {
4261 CppWork::Container(container) => {
4262 push_cpp_container_work(container.node, container.scope, &mut stack);
4263 }
4264 CppWork::Siblings(siblings) => {
4265 advance_cpp_siblings(siblings, self.source, &mut stack);
4266 }
4267 CppWork::Node(work) => {
4268 if self.node_is_inside_consumed_fragment(work.node) {
4269 continue;
4270 }
4271 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
4272 }
4273 }
4274 }
4275 }
4276
4277 fn reparse_fragmented_export_class_members(
4282 &self,
4283 fragmented: &FragmentedExportBody,
4284 class_name: &str,
4285 ) -> Option<FragmentedExportMembers> {
4286 if fragmented.reparse_start >= fragmented.reparse_end {
4287 return None;
4288 }
4289 let tree = cpp_reparse_fragmented_class_body(
4290 self.source,
4291 fragmented.reparse_start,
4292 fragmented.reparse_end,
4293 )?;
4294 if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
4295 return Some(FragmentedExportMembers::Complete(tree));
4296 }
4297 let has_conditional_constructor = {
4298 let root = tree.root_node();
4299 let mut cursor = root.walk();
4300 root.named_children(&mut cursor).any(|child| {
4301 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
4302 })
4303 };
4304 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
4305 }
4306
4307 fn visit_fragmented_export_class_members(
4310 &mut self,
4311 outcome: FragmentedExportMembers,
4312 class_unit: CodeUnit,
4313 scope: &ScopeInfo,
4314 ) -> bool {
4315 let (tree, complete) = match outcome {
4316 FragmentedExportMembers::Complete(tree) => (tree, true),
4317 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
4318 };
4319 let root = tree.root_node();
4320 let class_name = class_unit.identifier().to_string();
4321 let member_scope = ScopeInfo {
4322 package_name: class_unit.package_name().to_string(),
4327 module: scope.module.clone(),
4328 class_unit: Some(class_unit),
4329 template_signature: scope.template_signature.clone(),
4330 template_metadata: None,
4331 declarations_are_fields: true,
4332 recovered_specialization_member_scope: false,
4333 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4334 };
4335 if !complete {
4336 let mut cursor = root.walk();
4342 let constructors = root
4343 .named_children(&mut cursor)
4344 .filter_map(|child| {
4345 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
4346 })
4347 .collect::<Vec<_>>();
4348 let reparsed_ancestry = ParentIndex::new(root);
4351 for constructor in constructors {
4352 let mut stack = Vec::new();
4353 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
4354 while let Some(work) = stack.pop() {
4355 match work {
4356 CppWork::Container(container) => {
4357 push_cpp_container_work(container.node, container.scope, &mut stack);
4358 }
4359 CppWork::Siblings(siblings) => {
4360 advance_cpp_siblings(siblings, self.source, &mut stack);
4361 }
4362 CppWork::Node(work) => {
4363 self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
4364 }
4365 }
4366 }
4367 }
4368 return false;
4369 }
4370 self.run_container_work(root, member_scope, &ParentIndex::new(root));
4372 true
4373 }
4374
4375 fn visit_recovered_fragment_constructor<'tree>(
4376 &mut self,
4377 range: std::ops::Range<usize>,
4378 constructor_body: Node<'tree>,
4379 class_declaration: Node<'tree>,
4380 class_unit: &CodeUnit,
4381 scope: &ScopeInfo,
4382 ancestry: &ParentIndex<'tree>,
4383 ) {
4384 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
4385 return;
4386 };
4387 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
4388 tree.root_node(),
4389 range.start,
4390 class_unit.identifier(),
4391 self.source,
4392 ) else {
4393 return;
4394 };
4395 let member_scope = ScopeInfo {
4396 package_name: class_unit.package_name().to_string(),
4397 module: scope.module.clone(),
4398 class_unit: Some(class_unit.clone()),
4399 template_signature: scope.template_signature.clone(),
4400 template_metadata: None,
4401 declarations_are_fields: true,
4402 recovered_specialization_member_scope: false,
4403 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4404 };
4405 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
4406 else {
4407 return;
4408 };
4409 debug_assert_eq!(function.name, class_unit.identifier());
4410 let code_unit = function.code_unit(self.file.clone());
4411 self.add_declaration_with_range(
4412 code_unit.clone(),
4413 Range {
4414 start_byte: function_declarator.start_byte(),
4415 end_byte: constructor_body.end_byte(),
4416 start_line: function_declarator.start_position().row + 1,
4417 end_line: constructor_body.end_position().row + 1,
4418 },
4419 None,
4420 None,
4421 );
4422 self.parsed.add_signature_with_metadata(
4423 code_unit.clone(),
4424 cpp_signature_metadata(
4425 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
4426 function_declarator,
4427 self.source,
4428 ancestry,
4429 )
4430 .with_declaration_only(false)
4431 .with_callable_linkage(cpp_callable_linkage(
4432 class_declaration,
4433 self.source,
4434 ancestry,
4435 )),
4436 );
4437 self.parsed.add_child(class_unit.clone(), code_unit);
4438 }
4439
4440 fn visit_recovered_fragment_prefix_members<'tree>(
4441 &mut self,
4442 root: Node<'tree>,
4443 constructor_start: usize,
4444 class_unit: &CodeUnit,
4445 scope: &ScopeInfo,
4446 ancestry: &ParentIndex<'tree>,
4447 ) {
4448 let member_scope = ScopeInfo {
4449 package_name: class_unit.package_name().to_string(),
4450 module: scope.module.clone(),
4451 class_unit: Some(class_unit.clone()),
4452 template_signature: scope.template_signature.clone(),
4453 template_metadata: None,
4454 declarations_are_fields: true,
4455 recovered_specialization_member_scope: false,
4456 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4457 };
4458 let mut stack = vec![root];
4459 while let Some(current) = stack.pop() {
4460 if current.kind() == "comment" || current.start_byte() >= constructor_start {
4461 continue;
4462 }
4463 if current.end_byte() <= constructor_start
4464 && current.kind() != "translation_unit"
4465 && current.kind() != "labeled_statement"
4466 && current.kind() != "ERROR"
4467 {
4468 let mut work_stack = Vec::new();
4469 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
4470 while let Some(work) = work_stack.pop() {
4471 match work {
4472 CppWork::Container(container) => {
4473 push_cpp_container_work(
4474 container.node,
4475 container.scope,
4476 &mut work_stack,
4477 );
4478 }
4479 CppWork::Siblings(siblings) => {
4480 advance_cpp_siblings(siblings, self.source, &mut work_stack);
4481 }
4482 CppWork::Node(work) => {
4483 self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
4484 }
4485 }
4486 }
4487 continue;
4488 }
4489 if matches!(
4490 current.kind(),
4491 "translation_unit" | "labeled_statement" | "ERROR"
4492 ) {
4493 let mut cursor = current.walk();
4494 stack.extend(current.named_children(&mut cursor));
4495 }
4496 }
4497 }
4498
4499 fn visit_node<'tree>(
4500 &mut self,
4501 node: Node<'tree>,
4502 scope: &ScopeInfo,
4503 stack: &mut Vec<CppWork<'tree>>,
4504 ancestry: &ParentIndex<'tree>,
4505 ) {
4506 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
4507 self.visit_node(node, &recovered_scope, stack, ancestry);
4508 return;
4509 }
4510 if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
4511 self.visit_node(node, &recovered_scope, stack, ancestry);
4512 return;
4513 }
4514 if node.kind() == "function_definition" && node.has_error() {
4520 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4521 }
4522 if let Some(FragmentedClassRecovery {
4523 declaration_node: class_node,
4524 name,
4525 raw_supertypes,
4526 body: fragmented,
4527 }) = fragmented_class_body(node, self.source)
4528 {
4529 let displaced_namespace_items =
4530 displaced_fragment_namespace_geometry(node, self.source)
4531 .map(|boundary| boundary.namespace_items)
4532 .unwrap_or_default();
4533 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
4534 let mut class_stack = Vec::new();
4535 let parser_visible_body =
4538 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
4539 .then(|| cpp_body_node(class_node))
4540 .flatten();
4541 let class_unit = self.visit_named_class_like_shape(
4542 class_node,
4543 name,
4544 parser_visible_body,
4545 true,
4546 Some(fragmented.class_range),
4547 Some(raw_supertypes),
4548 scope,
4549 &mut class_stack,
4550 ancestry,
4551 );
4552 let member_scope = ScopeInfo {
4553 package_name: class_unit.package_name().to_string(),
4554 module: scope.module.clone(),
4555 class_unit: Some(class_unit.clone()),
4556 template_signature: scope.template_signature.clone(),
4557 template_metadata: None,
4558 declarations_are_fields: true,
4559 recovered_specialization_member_scope: false,
4560 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4561 };
4562 let complete = outcome.is_some_and(|outcome| {
4563 self.visit_fragmented_export_class_members(outcome, class_unit, scope)
4564 });
4565 if complete {
4566 self.consumed_fragment_regions
4567 .push((node.start_byte(), fragmented.class_range.end_byte));
4568 } else {
4569 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
4579 if candidate.start_byte() >= fragmented.reparse_end {
4580 break;
4581 }
4582 if cpp_fragment_sibling_is_class_member(
4583 candidate,
4584 fragmented.reparse_end,
4585 self.source,
4586 ) {
4587 self.recovered_class_sibling_scopes
4588 .insert(candidate.id(), member_scope.clone());
4589 }
4590 }
4591 }
4592 for item in displaced_namespace_items {
4593 self.recovered_class_sibling_scopes
4594 .insert(item.id(), scope.clone());
4595 }
4596 stack.extend(class_stack);
4597 return;
4598 }
4599 if self.visit_folded_aggregate(node, scope) {
4600 return;
4601 }
4602 match node.kind() {
4603 "template_declaration" => {
4604 if let Some(recovered) =
4605 recover_fragmented_preprocessor_class(node, self.source, ancestry)
4606 {
4607 let mut template_scope = scope.clone();
4608 template_scope.template_signature =
4609 cpp_template_signature(node, recovered.declaration_node, self.source);
4610 template_scope.template_metadata =
4611 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
4612 let raw_supertypes =
4613 Some(extract_cpp_supertypes(recovered.class_node, self.source));
4614 let mut class_stack = Vec::new();
4615 let class_unit = self.visit_named_class_like_shape(
4616 recovered.class_node,
4617 recovered.name,
4618 Some(recovered.body),
4619 true,
4620 Some(recovered.range),
4621 raw_supertypes,
4622 &template_scope,
4623 &mut class_stack,
4624 ancestry,
4625 );
4626 self.parsed.record_materialization(
4627 MaterializationRecord::RecoveredDeclaration {
4628 recovery: recovered.range,
4629 unit: class_unit.clone(),
4630 },
4631 );
4632 let member_scope = ScopeInfo {
4633 package_name: template_scope.package_name.clone(),
4634 module: template_scope.module.clone(),
4635 class_unit: Some(class_unit.clone()),
4636 template_signature: template_scope.template_signature.clone(),
4637 template_metadata: None,
4638 declarations_are_fields: true,
4639 recovered_specialization_member_scope: recovered
4640 .class_node
4641 .child_by_field_name("name")
4642 .is_some_and(|name| name.kind() == "template_type"),
4643 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
4644 };
4645 for tail_member in recovered.tail_members.into_iter().rev() {
4646 stack.push(CppWork::Node(CppNodeWork {
4647 node: tail_member,
4648 scope: member_scope.clone(),
4649 }));
4650 }
4651 stack.extend(class_stack);
4652 for sibling in recovered.member_siblings {
4653 self.recovered_class_sibling_scopes
4654 .insert(sibling.id(), member_scope.clone());
4655 }
4656 return;
4657 }
4658 for index in (0..node.named_child_count()).rev() {
4659 let Some(child) = node.named_child(index) else {
4660 continue;
4661 };
4662 if matches!(
4663 child.kind(),
4664 "class_specifier"
4665 | "struct_specifier"
4666 | "union_specifier"
4667 | "enum_specifier"
4668 | "function_definition"
4669 | "declaration"
4670 | "field_declaration"
4671 | "alias_declaration"
4672 | "namespace_definition"
4673 ) {
4674 let mut template_scope = scope.clone();
4675 template_scope.template_signature =
4676 cpp_template_signature(node, child, self.source);
4677 template_scope.template_metadata =
4678 cpp_template_metadata(node, child, self.source, ancestry);
4679 if let Some(recovered) = recover_fragmented_partial_specialization(
4680 node,
4681 child,
4682 self.source,
4683 ancestry,
4684 ) {
4685 let code_unit = self.visit_named_class_like_shape(
4686 recovered.declaration_node,
4687 recovered.name,
4688 None,
4689 true,
4690 Some(recovered.range),
4691 None,
4692 &template_scope,
4693 stack,
4694 ancestry,
4695 );
4696 self.parsed.record_materialization(
4697 MaterializationRecord::RecoveredDeclaration {
4698 recovery: recovered.range,
4699 unit: code_unit.clone(),
4700 },
4701 );
4702 let mut member_scope = template_scope.clone();
4703 member_scope.class_unit = Some(code_unit);
4704 member_scope.declarations_are_fields = true;
4705 member_scope.recovered_specialization_member_scope = true;
4706 for prefix_member in recovered.prefix_members.into_iter().rev() {
4707 stack.push(CppWork::Node(CppNodeWork {
4708 node: prefix_member,
4709 scope: member_scope.clone(),
4710 }));
4711 }
4712 for sibling in recovered.member_siblings {
4713 self.recovered_class_sibling_scopes
4714 .insert(sibling.id(), member_scope.clone());
4715 }
4716 for following in recovered.following_declarations.into_iter().rev() {
4717 stack.push(CppWork::Node(CppNodeWork {
4718 node: following,
4719 scope: scope.clone(),
4720 }));
4721 }
4722 return;
4723 }
4724 stack.push(CppWork::Node(CppNodeWork {
4725 node: child,
4726 scope: template_scope,
4727 }));
4728 }
4729 }
4730 }
4731 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
4732 "linkage_specification" => {
4733 if let Some(body) = cpp_body_node(node) {
4734 stack.push(CppWork::Container(CppContainer {
4735 node: body,
4736 scope: scope.clone(),
4737 }));
4738 } else {
4739 stack.push(CppWork::Container(CppContainer {
4740 node,
4741 scope: scope.clone(),
4742 }));
4743 }
4744 }
4745 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
4746 self.visit_class_like(node, scope, stack, ancestry)
4747 }
4748 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
4749 "ERROR" => {
4756 self.visit_object_macro_error_classes(node, scope);
4757 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4758 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4759 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
4760 return;
4761 }
4762 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4763 return;
4764 }
4765 self.visit_macro_swallowed_function_declarations(node, scope);
4766 self.visit_macro_wrapped_declarations(node, scope, ancestry);
4767 self.visit_stranded_class_members(node, scope, ancestry);
4768 stack.push(CppWork::Container(CppContainer {
4769 node,
4770 scope: scope.clone(),
4771 }));
4772 }
4773 }
4774 "declaration" => {
4775 if node.has_error() {
4776 if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4783 return;
4784 }
4785 self.visit_prototype_macro_declarations(node, scope);
4786 if self.node_is_inside_consumed_fragment(node) {
4787 return;
4794 }
4795 }
4796 if scope.class_unit.is_some()
4797 && scope.declarations_are_fields
4798 && scope.recovered_specialization_member_scope
4799 && let Some(alias_name) =
4800 recovered_using_declaration_alias_name(node, self.source)
4801 {
4802 self.add_type_aliases(node, scope, vec![alias_name], ancestry);
4803 } else {
4804 self.visit_declaration(
4805 node,
4806 scope,
4807 scope.declarations_are_fields,
4808 stack,
4809 ancestry,
4810 )
4811 }
4812 }
4813 "expression_statement" => {
4827 if node.has_error() {
4828 self.visit_prototype_macro_declarations(node, scope);
4829 }
4830 }
4831 "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
4832 "preproc_call" => self.visit_preproc_call(node, scope),
4833 "type_definition" | "alias_declaration" => {
4834 self.visit_type_declaration(node, scope, stack, ancestry)
4835 }
4836 "preproc_def" | "preproc_function_def" => self.visit_macro(node),
4837 "preproc_include" => {}
4843 kind if preserves_declaration_scope_through_wrapper(
4844 kind,
4845 scope.class_unit.is_some(),
4846 ) =>
4847 {
4848 if kind == "labeled_statement" {
4854 self.visit_access_label_constructor(node, scope);
4855 }
4856 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
4857 let mut range = cpp_declaration_range(node);
4858 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
4859 range.end_byte = boundary.end_byte;
4860 range.end_line = boundary.end_line;
4861 }
4862 self.parsed.record_materialization(
4863 MaterializationRecord::ConfigurationConditional { range },
4864 );
4865 if node.has_error() {
4866 let mut candidates = vec![node];
4875 while let Some(candidate) = candidates.pop() {
4876 if candidate.kind() == "ERROR"
4887 && !cpp_is_inside_namespace_body(candidate, ancestry)
4888 && self.visit_function_like_export_class_pair(
4889 candidate, scope, stack, ancestry,
4890 )
4891 {
4892 continue;
4893 }
4894 for index in (0..candidate.named_child_count()).rev() {
4895 candidates.push(
4896 candidate
4897 .named_child(index)
4898 .expect("index below the node's own named child count"),
4899 );
4900 }
4901 }
4902 }
4903 }
4904 stack.push(CppWork::Container(CppContainer {
4905 node,
4906 scope: scope.clone(),
4907 }))
4908 }
4909 _ => {
4915 self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
4916 }
4917 }
4918 }
4919
4920 fn visit_macro_swallowed_function_declarations<'tree>(
4921 &mut self,
4922 envelope: Node<'tree>,
4923 scope: &ScopeInfo,
4924 ) {
4925 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
4926 || envelope.kind() == "ERROR"
4927 && envelope
4928 .parent()
4929 .is_some_and(|parent| parent.kind() == "ERROR")
4930 {
4931 return;
4932 }
4933 let mut stack = (0..envelope.named_child_count())
4934 .filter_map(|index| envelope.named_child(index))
4935 .collect::<Vec<_>>();
4936 while let Some(node) = stack.pop() {
4937 if node.kind() == "function_declarator" {
4938 self.visit_error_swallowed_function_declaration(node, scope);
4939 }
4940 for child in named_children_iter(node) {
4941 stack.push(child);
4942 }
4943 }
4944 }
4945
4946 fn visit_macro_wrapped_declarations<'tree>(
4950 &mut self,
4951 envelope: Node<'tree>,
4952 scope: &ScopeInfo,
4953 ancestry: &ParentIndex<'tree>,
4954 ) {
4955 let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
4956 if recovered.is_empty() {
4957 return;
4958 }
4959 let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
4960 self.record_recovered_declarations(recovery, |visitor| {
4961 for declaration in recovered {
4962 visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
4963 }
4964 });
4965 }
4966
4967 fn visit_collapsed_macro_declaration_run<'tree>(
4994 &mut self,
4995 envelope: Node<'tree>,
4996 scope: &ScopeInfo,
4997 ancestry: &ParentIndex<'tree>,
4998 ) -> bool {
4999 let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
5000 return false;
5001 };
5002 let start = envelope.start_byte();
5003 let end = run.region_end;
5004 let recovery = cpp_recovery_window(self.source, start, end);
5005 self.record_recovered_declarations(recovery, |visitor| {
5006 let mut position = start;
5007 while position < end {
5008 let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
5009 return;
5010 };
5011 let root = tree.root_node();
5012 let ancestry = ParentIndex::new(root);
5015 let mut cursor = root.walk();
5016 let collapsed =
5017 root.named_children(&mut cursor)
5018 .enumerate()
5019 .find_map(|(index, item)| {
5020 collapsed_macro_declaration_run(item, visitor.source, &ancestry)
5021 .map(|run| (index, item, run))
5022 });
5023 let mut stack = Vec::new();
5028 push_cpp_sibling_range(
5029 root,
5030 0,
5031 collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
5032 scope.clone(),
5033 &mut stack,
5034 );
5035 visitor.drain_cpp_work(stack, &ancestry);
5036 let Some((_, item, run)) = collapsed else {
5037 return;
5038 };
5039 if let Some(head) =
5043 cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
5044 {
5045 let head_root = head.root_node();
5046 visitor.run_container_work(
5047 head_root,
5048 scope.clone(),
5049 &ParentIndex::new(head_root),
5050 );
5051 }
5052 assert!(
5053 run.invocation_end > position,
5054 "a collapsed run at {position} must end after the byte the scan resumed \
5055 from, but ended at {}",
5056 run.invocation_end
5057 );
5058 position = run.invocation_end;
5059 }
5060 });
5061 self.consumed_fragment_regions.push((start, end));
5062 true
5063 }
5064
5065 fn visit_stranded_class_members<'tree>(
5075 &mut self,
5076 node: Node<'tree>,
5077 scope: &ScopeInfo,
5078 ancestry: &ParentIndex<'tree>,
5079 ) {
5080 if scope.class_unit.is_none() || !scope.declarations_are_fields {
5081 return;
5082 }
5083 for member in stranded_declaration_run(node, self.source).declarations {
5084 self.add_macro_wrapped_declaration(member, scope, ancestry);
5085 }
5086 }
5087
5088 fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5096 let Some(class_unit) = scope.class_unit.clone() else {
5097 return;
5098 };
5099 if !scope.declarations_are_fields {
5100 return;
5101 }
5102 let class_name = class_unit.identifier().to_string();
5103 let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
5104 else {
5105 return;
5106 };
5107 let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
5108 return;
5109 };
5110 let root = tree.root_node();
5111 let Some(declarator) =
5112 cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
5113 else {
5114 return;
5115 };
5116 let reparsed_ancestry = ParentIndex::new(root);
5117 let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
5118 let range = cpp_declaration_range(definition.unwrap_or(declarator));
5119 let recovery = cpp_recovery_window(self.source, start, node.end_byte());
5120 self.record_recovered_declarations(recovery, |visitor| {
5121 visitor.add_macro_wrapped_declaration(
5122 MacroWrappedDeclaration {
5123 declarator,
5124 range,
5125 is_static: false,
5126 },
5127 scope,
5128 &reparsed_ancestry,
5129 );
5130 });
5131 }
5132
5133 fn add_macro_wrapped_declaration<'tree>(
5134 &mut self,
5135 declaration: MacroWrappedDeclaration<'tree>,
5136 scope: &ScopeInfo,
5137 ancestry: &ParentIndex<'tree>,
5138 ) {
5139 let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
5140 else {
5141 return;
5142 };
5143 let code_unit =
5144 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
5145 if self.parsed.contains_declaration(&code_unit) {
5146 self.parsed
5147 .record_navigation_range(code_unit, declaration.range);
5148 return;
5149 }
5150 self.add_declaration_with_range(code_unit.clone(), declaration.range, None, None);
5151 let signature = normalize_cpp_whitespace(
5152 self.source
5153 .get(declaration.range.start_byte..declaration.range.end_byte)
5154 .expect("a recovered declaration range covers one source range"),
5155 );
5156 let linkage = if declaration.is_static {
5157 CallableLinkage::Internal
5158 } else {
5159 cpp_callable_linkage(declaration.declarator, self.source, ancestry)
5160 };
5161 let declaration_only =
5165 cpp_declarator_function_definition(declaration.declarator, ancestry).is_none();
5166 self.parsed.add_signature_with_metadata(
5167 code_unit.clone(),
5168 cpp_signature_metadata(signature, declaration.declarator, self.source, ancestry)
5169 .with_declaration_only(declaration_only)
5170 .with_callable_linkage(linkage),
5171 );
5172 if let Some(parent) = &scope.class_unit {
5173 self.parsed.add_child(parent.clone(), code_unit);
5174 } else if let Some(module) = &scope.module {
5175 self.parsed.add_child(module.clone(), code_unit);
5176 }
5177 }
5178
5179 fn visit_anonymous_local_aggregates_in_function<'tree>(
5184 &mut self,
5185 function: Node<'tree>,
5186 scope: &ScopeInfo,
5187 stack: &mut Vec<CppWork<'tree>>,
5188 ancestry: &ParentIndex<'tree>,
5189 ) {
5190 let Some(body) = cpp_body_node(function) else {
5191 return;
5192 };
5193 let block_scope;
5200 let scope = if scope.class_unit.is_some() {
5201 block_scope = ScopeInfo {
5202 class_unit: None,
5203 declarations_are_fields: false,
5204 ..scope.clone()
5205 };
5206 &block_scope
5207 } else {
5208 scope
5209 };
5210 let mut pending = vec![body];
5211 while let Some(node) = pending.pop() {
5212 if matches!(node.kind(), "function_definition" | "lambda_expression") {
5213 continue;
5214 }
5215 if node.kind() == "declaration"
5216 && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
5217 {
5218 continue;
5219 }
5220 if matches!(
5221 node.kind(),
5222 "class_specifier" | "struct_specifier" | "union_specifier"
5223 ) {
5224 continue;
5225 }
5226 let mut cursor = node.walk();
5227 let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
5228 children.reverse();
5229 pending.extend(children);
5230 }
5231 }
5232
5233 fn visit_error_swallowed_function_declaration<'tree>(
5234 &mut self,
5235 node: Node<'tree>,
5236 scope: &ScopeInfo,
5237 ) -> bool {
5238 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
5239 return false;
5240 };
5241 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
5242 return false;
5243 };
5244 let root = tree.root_node();
5245 let mut cursor = root.walk();
5246 let declarations = root
5247 .named_children(&mut cursor)
5248 .filter(|child| child.kind() != "comment")
5249 .collect::<Vec<_>>();
5250 let [declaration] = declarations.as_slice() else {
5251 return false;
5252 };
5253 if declaration.kind() != "declaration"
5254 || declaration.has_error()
5255 || declaration.start_byte() != start
5256 || declaration.end_byte() != end
5257 {
5258 return false;
5259 }
5260 let recovery = cpp_recovery_window(self.source, start, end);
5261 let reparsed_ancestry = ParentIndex::new(root);
5263 self.record_recovered_declarations(recovery, |visitor| {
5264 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5265 });
5266 true
5267 }
5268
5269 fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5292 for candidate in cpp_prototype_macro_candidates(node, self.source) {
5293 let start = candidate.run_start;
5294 let end = candidate.semicolon_end;
5295 if self.byte_range_is_inside_consumed_fragment(start, end) {
5296 continue;
5297 }
5298 let Some(tree) = parse_source_ranges_with_cancellation(
5299 &tree_sitter_cpp::LANGUAGE.into(),
5300 self.source,
5301 &candidate.ranges(),
5302 None,
5303 ) else {
5304 continue;
5305 };
5306 let root = tree.root_node();
5307 let mut cursor = root.walk();
5308 let declarations = root
5309 .named_children(&mut cursor)
5310 .filter(|child| child.kind() != "comment")
5311 .collect::<Vec<_>>();
5312 let [declaration] = declarations.as_slice() else {
5313 continue;
5314 };
5315 if declaration.kind() != "declaration"
5316 || declaration.has_error()
5317 || declaration.start_byte() != start
5318 || declaration.end_byte() != end
5319 || declaration
5320 .child_by_field_name("declarator")
5321 .and_then(extract_function_declarator)
5322 .is_none()
5323 {
5324 continue;
5325 }
5326 let recovery = cpp_recovery_window(self.source, start, end);
5327 let reparsed_ancestry = ParentIndex::new(root);
5330 self.record_recovered_declarations(recovery, |visitor| {
5331 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5332 });
5333 self.consumed_fragment_regions.push((start, end));
5334 }
5335 }
5336
5337 fn declare_namespace_levels(
5343 &mut self,
5344 mut package_name: String,
5345 levels: Vec<(String, Range)>,
5346 ) -> (String, Option<CodeUnit>) {
5347 let mut module = None;
5348 for (component, range) in levels {
5349 let full_name = if package_name.is_empty() {
5350 component
5351 } else {
5352 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
5353 };
5354 let level = CodeUnit::new_fq(
5355 self.file.clone(),
5356 CodeUnitType::Module,
5357 "",
5358 full_name.clone(),
5359 cpp_namespace_fq(&full_name),
5360 );
5361 if !self.parsed.contains_declaration(&level) {
5362 self.add_declaration_with_range(level.clone(), range, None, None);
5363 }
5364 package_name = full_name;
5365 module = Some(level);
5366 }
5367 (package_name, module)
5368 }
5369
5370 fn recovered_namespace_scope(
5375 &mut self,
5376 node: Node<'_>,
5377 scope: &ScopeInfo,
5378 ) -> Option<ScopeInfo> {
5379 self.orphaned_namespaces.region_at(node.start_byte())?;
5380 let levels = self
5386 .orphaned_namespaces
5387 .enclosing_namespace_levels(node, self.source);
5388 let package_name = levels
5389 .iter()
5390 .map(|(component, _)| component.as_str())
5391 .collect::<Vec<_>>()
5392 .join(CPP_PACKAGE_SEPARATOR);
5393 if package_name == scope.package_name {
5394 return None;
5395 }
5396 let (package_name, module) = self.declare_namespace_levels(String::new(), levels);
5397 Some(ScopeInfo {
5398 package_name,
5399 module,
5400 class_unit: None,
5405 template_signature: None,
5406 template_metadata: None,
5407 declarations_are_fields: false,
5408 recovered_specialization_member_scope: false,
5409 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5410 })
5411 }
5412
5413 fn visit_namespace<'tree>(
5414 &mut self,
5415 node: Node<'tree>,
5416 scope: &ScopeInfo,
5417 stack: &mut Vec<CppWork<'tree>>,
5418 ancestry: &ParentIndex<'tree>,
5419 ) {
5420 let name_node = node.child_by_field_name("name");
5421 let Some(name_node) = name_node else {
5422 if let Some(body) = cpp_body_node(node) {
5423 stack.push(CppWork::Container(CppContainer {
5424 node: body,
5425 scope: scope.clone(),
5426 }));
5427 }
5428 return;
5429 };
5430 let explicitly_global = name_node
5437 .child(0)
5438 .is_some_and(|child| !child.is_named() && child.kind() == "::");
5439 let components = cpp_namespace_name_components(name_node, self.source);
5440 if components.is_empty() {
5441 return;
5442 }
5443 let package_name = if explicitly_global {
5449 String::new()
5450 } else {
5451 scope.package_name.clone()
5452 };
5453 let definition = node_range(node);
5454 let (package_name, module) = self.declare_namespace_levels(
5455 package_name,
5456 components
5457 .into_iter()
5458 .map(|component| (component, definition))
5459 .collect(),
5460 );
5461
5462 let namespace_scope = ScopeInfo {
5463 package_name,
5464 module,
5465 class_unit: None,
5473 template_signature: scope.template_signature.clone(),
5474 template_metadata: scope.template_metadata.clone(),
5475 declarations_are_fields: false,
5476 recovered_specialization_member_scope: false,
5477 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5478 };
5479 let container = cpp_body_node(node).unwrap_or(node);
5480 let mut candidates = vec![container];
5488 while let Some(candidate) = candidates.pop() {
5489 if matches!(
5490 candidate.kind(),
5491 "ERROR" | "function_definition" | "labeled_statement"
5492 ) && self.visit_embedded_function_like_export_classes(
5493 candidate,
5494 &namespace_scope,
5495 stack,
5496 ancestry,
5497 ) {
5498 continue;
5499 }
5500 for index in (0..candidate.named_child_count()).rev() {
5501 candidates.push(
5502 candidate
5503 .named_child(index)
5504 .expect("index below the node's own named child count"),
5505 );
5506 }
5507 }
5508 stack.push(CppWork::Container(CppContainer {
5509 node: container,
5510 scope: namespace_scope,
5511 }));
5512 }
5513
5514 fn visit_class_like<'tree>(
5515 &mut self,
5516 node: Node<'tree>,
5517 scope: &ScopeInfo,
5518 stack: &mut Vec<CppWork<'tree>>,
5519 ancestry: &ParentIndex<'tree>,
5520 ) {
5521 let Some(name) = class_like_name(node, self.source, ancestry) else {
5522 return;
5523 };
5524 let name = qualified_class_name_chain(node, self.source, scope)
5525 .map(|chain| chain.join("$"))
5526 .unwrap_or(name);
5527 self.visit_named_class_like(node, name, scope, stack, ancestry);
5528 }
5529
5530 fn visit_named_class_like<'tree>(
5531 &mut self,
5532 node: Node<'tree>,
5533 name: String,
5534 scope: &ScopeInfo,
5535 stack: &mut Vec<CppWork<'tree>>,
5536 ancestry: &ParentIndex<'tree>,
5537 ) {
5538 let body = cpp_body_node(node);
5539 let definition_body_present = body.is_some();
5540 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
5541 .then(|| extract_cpp_supertypes(node, self.source));
5542 self.visit_named_class_like_shape(
5543 node,
5544 name,
5545 body,
5546 definition_body_present,
5547 None,
5548 raw_supertypes,
5549 scope,
5550 stack,
5551 ancestry,
5552 );
5553 }
5554
5555 fn mints_tag_at_enclosing_c_scope(
5563 &self,
5564 declaration_node: Node<'_>,
5565 scope: &ScopeInfo,
5566 ancestry: &ParentIndex<'_>,
5567 ) -> bool {
5568 self.c_tag_semantics
5569 && scope.class_unit.is_some()
5570 && class_like_name(declaration_node, self.source, ancestry).is_some()
5571 && matches!(
5572 declaration_node.kind(),
5573 "struct_specifier" | "union_specifier" | "enum_specifier"
5574 )
5575 }
5576
5577 #[allow(clippy::too_many_arguments)]
5578 fn visit_named_class_like_shape<'tree>(
5579 &mut self,
5580 declaration_node: Node<'tree>,
5581 name: String,
5582 body: Option<Node<'tree>>,
5583 definition_body_present: bool,
5584 explicit_range: Option<Range>,
5585 raw_supertypes: Option<Vec<String>>,
5586 scope: &ScopeInfo,
5587 stack: &mut Vec<CppWork<'tree>>,
5588 ancestry: &ParentIndex<'tree>,
5589 ) -> CodeUnit {
5590 let displaced_macro_tail = if explicit_range.is_none() {
5591 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
5592 } else {
5593 None
5594 };
5595 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
5596 let recovered_scope = self.scope_for_recovered_exported_class(
5597 declaration_node,
5598 &name,
5599 definition_body_present,
5600 scope,
5601 ancestry,
5602 );
5603 let c_tag_scope;
5613 let scope =
5614 if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
5615 c_tag_scope = ScopeInfo {
5616 class_unit: None,
5617 ..recovered_scope.clone()
5618 };
5619 &c_tag_scope
5620 } else {
5621 &recovered_scope
5622 };
5623 let short_name = if let Some(parent) = &scope.class_unit {
5624 cpp_join_nested_short(parent.short_name(), &name)
5625 } else {
5626 name.clone()
5627 };
5628 let qualified_chain = if scope.class_unit.is_none() {
5635 qualified_class_name_chain(declaration_node, self.source, scope)
5636 .filter(|chain| chain.join("$") == name)
5637 } else {
5638 None
5639 };
5640 let fq = if let Some(chain) = qualified_chain {
5641 let mut fq = FqName::new();
5642 cpp_push_package(&mut fq, &scope.package_name);
5643 let mut first = true;
5644 for component in chain {
5645 let kind = if first {
5646 SegmentKind::Type
5647 } else {
5648 SegmentKind::Nested
5649 };
5650 fq.push(cpp_segment(&component, kind));
5651 first = false;
5652 }
5653 fq
5654 } else {
5655 cpp_leaf_fq(
5656 &scope.package_name,
5657 scope.class_unit.as_ref(),
5658 &name,
5659 SegmentKind::Nested,
5660 SegmentKind::Type,
5661 )
5662 };
5663 let code_unit = CodeUnit::with_signature_and_fq(
5664 self.file.clone(),
5665 CodeUnitType::Class,
5666 scope.package_name.clone(),
5667 short_name,
5668 scope.template_signature.clone(),
5669 false,
5670 fq,
5671 );
5672 let has_body = definition_body_present;
5673 if !has_body && self.parsed.contains_declaration(&code_unit) {
5674 self.parsed.record_navigation_range(
5675 code_unit.clone(),
5676 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
5677 );
5678 return code_unit;
5679 }
5680 if has_body {
5681 let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
5688 if let Some(range) = explicit_range {
5689 self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
5690 } else {
5691 self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
5692 }
5693 for range in earlier_ranges {
5694 self.parsed.add_declaration_range(&code_unit, range);
5695 }
5696 } else {
5697 self.add_declaration(code_unit.clone(), declaration_node, None, None);
5698 }
5699 if let Some(raw_supertypes) = raw_supertypes {
5700 self.parsed
5701 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
5702 }
5703 self.parsed.add_signature(
5704 code_unit.clone(),
5705 render_cpp_type_signature(
5706 declaration_node,
5707 self.source,
5708 scope.template_signature.as_deref(),
5709 ),
5710 );
5711 if let Some(metadata) = &scope.template_metadata {
5712 let primary_short_name = if let Some(parent) = &scope.class_unit {
5713 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
5714 } else {
5715 metadata.primary_name.clone()
5716 };
5717 let primary_fq_name = CodeUnit::new(
5718 self.file.clone(),
5719 CodeUnitType::Class,
5720 scope.package_name.clone(),
5721 primary_short_name,
5722 )
5723 .fq_name();
5724 let mut metadata = metadata.clone();
5725 metadata.primary_fq_name = primary_fq_name;
5726 self.parsed
5727 .set_cpp_template_metadata(code_unit.clone(), metadata);
5728 }
5729 if let Some(parent) = &scope.class_unit {
5730 self.parsed.add_child(parent.clone(), code_unit.clone());
5731 } else if let Some(module) = &scope.module {
5732 self.parsed.add_child(module.clone(), code_unit.clone());
5733 }
5734
5735 if let Some(body) = body {
5736 let mut nested_scope = scope.clone();
5737 nested_scope.class_unit = Some(code_unit.clone());
5738 nested_scope.template_signature = scope.template_signature.clone();
5739 nested_scope.template_metadata = None;
5744 nested_scope.recovered_specialization_member_scope =
5747 scope.template_metadata.as_ref().is_some_and(|metadata| {
5748 declaration_node.kind() == "function_definition" && metadata.is_specialization()
5749 });
5750 nested_scope.declarations_are_fields =
5751 is_recovered_exported_class_container(declaration_node, self.source)
5752 || nested_scope.recovered_specialization_member_scope;
5753 if let Some(displaced) = displaced_macro_tail {
5754 push_cpp_sibling_range(
5762 body,
5763 displaced.split_index,
5764 usize::MAX,
5765 scope.clone(),
5766 stack,
5767 );
5768 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
5769 } else {
5770 stack.push(CppWork::Container(CppContainer {
5771 node: body,
5772 scope: nested_scope,
5773 }));
5774 }
5775 }
5776 if declaration_node.kind() == "enum_specifier" {
5777 self.visit_enum_enumerators(declaration_node, scope, &code_unit);
5778 if !self.has_enum_enumerator_units(&code_unit) {
5779 self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
5780 }
5781 }
5782 code_unit
5783 }
5784
5785 fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
5793 if self.field_owners.is_none() {
5794 self.field_owners = Some(CppFieldOwnerIndex::of(
5795 self.parsed.declarations().iter(),
5796 self.file,
5797 ));
5798 }
5799 debug_assert_eq!(
5800 parent.source(),
5801 self.file,
5802 "the walk's declarations are declarations of the file it is walking"
5803 );
5804 let carried = self
5805 .field_owners
5806 .as_ref()
5807 .expect("the index was just ensured")
5808 .owns_fields(parent.package_name(), parent.short_name());
5809
5810 #[cfg(debug_assertions)]
5811 assert_eq!(
5812 carried,
5813 cpp_declarations_hold_owned_fields(
5814 self.parsed.declarations(),
5815 self.file,
5816 parent.package_name(),
5817 parent.short_name()
5818 ),
5819 "the carried-forward field index must answer what a fresh declaration scan \
5820 answers for {}",
5821 parent.fq_name()
5822 );
5823
5824 carried
5825 }
5826
5827 fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
5828 walk_named_tree_preorder(node, false, |child| {
5829 if child.kind() != "enumerator" {
5830 return WalkControl::Continue;
5831 }
5832 let Some(name_node) = child.child_by_field_name("name") else {
5833 return WalkControl::Continue;
5834 };
5835 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
5836 if name.is_empty() {
5837 return WalkControl::Continue;
5838 }
5839 let code_unit = CodeUnit::new_fq(
5840 self.file.clone(),
5841 CodeUnitType::Field,
5842 scope.package_name.clone(),
5843 cpp_join_member_short(parent.short_name(), &name),
5844 parent
5845 .fq()
5846 .clone()
5847 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
5848 );
5849 if self.parsed.contains_declaration(&code_unit) {
5850 return WalkControl::Continue;
5851 }
5852 self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
5853 self.parsed.add_signature(
5854 code_unit,
5855 normalize_cpp_whitespace(node_text(child, self.source)),
5856 );
5857 WalkControl::Continue
5858 });
5859 }
5860
5861 fn visit_enum_enumerators_from_text(
5862 &mut self,
5863 node: Node<'_>,
5864 scope: &ScopeInfo,
5865 parent: &CodeUnit,
5866 ) {
5867 let text = node_text(node, self.source);
5868 let Some((_, body)) = text.split_once('{') else {
5869 return;
5870 };
5871 let Some((body, _)) = body.rsplit_once('}') else {
5872 return;
5873 };
5874 for entry in body.split(',') {
5875 let trimmed = entry.trim();
5876 let name = trimmed
5877 .split('=')
5878 .next()
5879 .unwrap_or("")
5880 .split_whitespace()
5881 .next()
5882 .unwrap_or("");
5883 if name.is_empty() {
5884 continue;
5885 }
5886 let code_unit = CodeUnit::new_fq(
5887 self.file.clone(),
5888 CodeUnitType::Field,
5889 scope.package_name.clone(),
5890 cpp_join_member_short(parent.short_name(), name),
5891 parent
5892 .fq()
5893 .clone()
5894 .with_pushed(cpp_segment(name, SegmentKind::Member)),
5895 );
5896 if self.parsed.contains_declaration(&code_unit) {
5897 continue;
5898 }
5899 self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
5900 self.parsed.add_signature(code_unit, trimmed.to_string());
5901 }
5902 }
5903
5904 fn visit_function_definition<'tree>(
5905 &mut self,
5906 node: Node<'tree>,
5907 scope: &ScopeInfo,
5908 stack: &mut Vec<CppWork<'tree>>,
5909 ancestry: &ParentIndex<'tree>,
5910 ) {
5911 if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
5918 return;
5919 }
5920 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
5926 return;
5927 }
5928 if node.has_error() {
5929 self.visit_macro_swallowed_function_declarations(node, scope);
5930 }
5931 if let Some((class_node, name, raw_supertypes)) =
5932 recover_exported_class_function_definition(node, self.source)
5933 {
5934 if let Some(body) = cpp_body_node(node)
5935 && let Some(close) = self
5936 .orphaned_namespaces
5937 .matching_close_brace(body.start_byte())
5938 && close.end_byte < body.end_byte()
5939 {
5940 let class_range = Range {
5941 start_byte: node.start_byte(),
5942 end_byte: close.end_byte,
5943 start_line: node.start_position().row + 1,
5944 end_line: close.end_line,
5945 };
5946 let mut head = vec![node];
5951 let mut keyword = None;
5952 let mut name_node = None;
5953 while let Some(part) = head.pop() {
5954 if part.start_byte() >= body.start_byte() || part.is_missing() {
5955 continue;
5956 }
5957 if matches!(part.kind(), "class" | "struct" | "union") {
5958 keyword = Some(part);
5959 }
5960 if matches!(part.kind(), "identifier" | "type_identifier")
5961 && node_text(part, self.source) == name
5962 {
5963 name_node = Some(part);
5964 }
5965 let mut cursor = part.walk();
5966 head.extend(part.children(&mut cursor));
5967 }
5968 if let (Some(keyword), Some(name_node)) = (keyword, name_node)
5969 && let Some(type_name) = keyword
5970 .parent()
5971 .and_then(|parent| parent.child_by_field_name("name"))
5972 && let Some(tree) = parse_source_ranges_with_cancellation(
5973 &tree_sitter_cpp::LANGUAGE.into(),
5974 self.source,
5975 &[
5976 (keyword.start_byte(), type_name.start_byte()),
5977 (name_node.start_byte(), name_node.end_byte()),
5978 (body.start_byte(), close.end_byte),
5979 ],
5980 None,
5981 )
5982 && let Some(reparsed_class) = tree.root_node().named_child(0)
5983 && let Some(class_body) = cpp_body_node(reparsed_class)
5984 && class_body.start_byte() == body.start_byte()
5985 && class_body.end_byte() == close.end_byte
5986 && let Some(tail) =
5987 cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
5988 {
5989 let class_unit = self.visit_named_class_like_shape(
5990 class_node,
5991 name,
5992 None,
5993 true,
5994 Some(class_range),
5995 raw_supertypes,
5996 scope,
5997 stack,
5998 ancestry,
5999 );
6000 self.parsed.record_materialization(
6001 MaterializationRecord::RecoveredDeclaration {
6002 recovery: class_range,
6003 unit: class_unit.clone(),
6004 },
6005 );
6006 let member_scope = ScopeInfo {
6007 package_name: class_unit.package_name().to_string(),
6008 class_unit: Some(class_unit),
6009 declarations_are_fields: true,
6010 template_metadata: None,
6011 recovered_specialization_member_scope: false,
6012 ..scope.clone()
6013 };
6014 let class_body_range = class_body.byte_range();
6015 let tail_range = tail.root_node().byte_range();
6016 self.partitioned_regions
6017 .push((tail, tail_range, scope.clone()));
6018 self.partitioned_regions
6019 .push((tree, class_body_range, member_scope));
6020 return;
6021 }
6022 }
6023 let body = cpp_body_node(class_node);
6024 let displaced_namespace = cpp_body_node(node)
6025 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
6026 let fragmented = cpp_body_node(node).and_then(|body| {
6027 fragmented_export_function_body_region(
6028 node,
6029 body,
6030 self.source,
6031 displaced_namespace.as_ref(),
6032 )
6033 });
6034 if let Some(fragmented) = fragmented {
6040 if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
6044 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
6045 {
6046 let mut boundary_scope = scope.clone();
6047 for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
6048 if sibling.start_byte() >= boundary.start_byte() {
6049 break;
6050 }
6051 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
6052 boundary_scope.visible_using_namespaces.push(namespace);
6053 }
6054 }
6055 self.recovered_class_sibling_scopes
6056 .insert(boundary.id(), boundary_scope);
6057 }
6058 let mut recovered_constructor = None;
6059 let mut recovered_prefix_tree = None;
6060 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
6061 {
6062 Some(FragmentedExportMembers::Complete(tree)) => {
6063 if let Some(body) = body
6064 && let Some(range) =
6065 cpp_reparsed_synthetic_initializer_constructor_range(
6066 tree.root_node(),
6067 &name,
6068 self.source,
6069 body.end_byte(),
6070 )
6071 {
6072 recovered_constructor = Some(range);
6073 recovered_prefix_tree = Some(tree);
6074 None
6075 } else {
6076 Some(FragmentedExportMembers::Complete(tree))
6077 }
6078 }
6079 outcome => outcome,
6080 };
6081 let mut class_stack = Vec::new();
6082 let class_unit = self.visit_named_class_like_shape(
6083 class_node,
6084 name,
6085 None,
6086 true,
6087 Some(fragmented.class_range),
6088 raw_supertypes,
6089 scope,
6090 &mut class_stack,
6091 ancestry,
6092 );
6093 self.parsed
6094 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6095 recovery: fragmented.class_range,
6096 unit: class_unit.clone(),
6097 });
6098 let complete = outcome.is_some_and(|outcome| {
6099 self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
6100 });
6101 if complete {
6102 self.consumed_fragment_regions
6103 .push((node.start_byte(), fragmented.class_range.end_byte));
6104 } else {
6105 let member_scope = ScopeInfo {
6114 package_name: class_unit.package_name().to_string(),
6115 module: scope.module.clone(),
6116 class_unit: Some(class_unit.clone()),
6117 template_signature: scope.template_signature.clone(),
6118 template_metadata: None,
6119 declarations_are_fields: true,
6120 recovered_specialization_member_scope: false,
6121 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6122 };
6123 for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6124 if candidate.start_byte() >= fragmented.reparse_end {
6125 break;
6126 }
6127 if cpp_fragment_sibling_is_class_member(
6128 candidate,
6129 fragmented.reparse_end,
6130 self.source,
6131 ) {
6132 self.recovered_class_sibling_scopes
6133 .insert(candidate.id(), member_scope.clone());
6134 }
6135 }
6136 if let Some(range) = recovered_constructor
6137 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
6138 {
6139 self.visit_recovered_fragment_prefix_members(
6140 prefix_tree.root_node(),
6141 range.start,
6142 &class_unit,
6143 scope,
6144 ancestry,
6145 );
6146 self.visit_recovered_fragment_constructor(
6147 range,
6148 body,
6149 class_node,
6150 &class_unit,
6151 scope,
6152 ancestry,
6153 );
6154 }
6155 }
6156 if let Some(boundary) = displaced_namespace {
6157 for item in boundary.namespace_items {
6158 self.recovered_class_sibling_scopes
6159 .insert(item.id(), scope.clone());
6160 }
6161 }
6162 stack.extend(class_stack);
6163 return;
6164 }
6165 let mut stack = Vec::new();
6166 let class_unit = self.visit_named_class_like_shape(
6167 class_node,
6168 name,
6169 body,
6170 body.is_some(),
6171 None,
6172 raw_supertypes,
6173 scope,
6174 &mut stack,
6175 ancestry,
6176 );
6177 self.parsed
6178 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6179 recovery: cpp_declaration_range(node),
6180 unit: class_unit,
6181 });
6182 if let Some(body) = body
6189 && let Some(class_close) = self
6190 .orphaned_namespaces
6191 .matching_close_brace(body.start_byte())
6192 && class_close.start_byte < body.end_byte()
6193 {
6194 let split = {
6195 let mut cursor = body.walk();
6196 body.named_children(&mut cursor)
6197 .position(|child| child.start_byte() > class_close.start_byte)
6198 };
6199 if let Some(split) = split {
6200 let seeded = stack.pop();
6205 match seeded {
6206 Some(CppWork::Container(container)) => {
6207 push_cpp_sibling_range(
6208 body,
6209 split,
6210 usize::MAX,
6211 scope.clone(),
6212 &mut stack,
6213 );
6214 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
6215 }
6216 _ => unreachable!("exported-class seed is always one Container"),
6219 }
6220 }
6221 }
6222 while let Some(work) = stack.pop() {
6223 match work {
6224 CppWork::Container(container) => {
6225 push_cpp_container_work(container.node, container.scope, &mut stack);
6226 }
6227 CppWork::Siblings(siblings) => {
6228 advance_cpp_siblings(siblings, self.source, &mut stack);
6229 }
6230 CppWork::Node(work) => {
6231 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
6232 }
6233 }
6234 }
6235 return;
6236 }
6237 let recovered_constraint_constructor =
6238 cpp_recovered_template_macro_constructor(node, self.source);
6239 let declarator = recovered_constraint_constructor
6240 .map(|(declarator, _)| declarator)
6241 .or_else(|| node.child_by_field_name("declarator"));
6242 let Some(declarator) = declarator else {
6243 self.visit_malformed_function_definition_container(node, scope, stack);
6244 return;
6245 };
6246 let Some(function_declarator) = extract_function_declarator(declarator) else {
6247 self.visit_malformed_function_definition_container(node, scope, stack);
6248 return;
6249 };
6250 let function = if let Some((_, callable_name)) =
6251 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
6252 {
6253 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
6254 } else {
6255 extract_function_info(function_declarator, self.source, scope)
6256 };
6257 let Some(mut function) = function else {
6258 self.visit_malformed_function_definition_container(node, scope, stack);
6259 return;
6260 };
6261 if let Some((_, template_parameter)) = recovered_constraint_constructor {
6262 function.signature = format!(
6263 "template <{}>{}",
6264 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
6265 function.signature
6266 );
6267 }
6268 let code_unit = function.code_unit(self.file.clone());
6269 self.add_declaration(code_unit.clone(), node, None, None);
6274 let signature = if recovered_constraint_constructor.is_some() {
6275 normalize_cpp_whitespace(node_text(function_declarator, self.source))
6276 } else {
6277 render_cpp_function_display_signature_from_node(
6278 node,
6279 self.source,
6280 scope.template_signature.as_deref(),
6281 true,
6282 ancestry,
6283 )
6284 };
6285 self.parsed.add_signature_with_metadata(
6286 code_unit.clone(),
6287 cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
6288 .with_declaration_only(false)
6289 .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
6290 );
6291 if let Some(parent) = &scope.class_unit {
6292 self.parsed.add_child(parent.clone(), code_unit);
6293 } else if let Some(module) = &scope.module {
6294 self.parsed.add_child(module.clone(), code_unit);
6295 }
6296 self.visit_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
6297 }
6298
6299 fn scope_for_recovered_exported_class<'tree>(
6304 &mut self,
6305 node: Node<'tree>,
6306 name: &str,
6307 definition_body_present: bool,
6308 scope: &ScopeInfo,
6309 ancestry: &ParentIndex<'tree>,
6310 ) -> ScopeInfo {
6311 if !definition_body_present
6312 || !scope.package_name.is_empty()
6313 || scope.class_unit.is_some()
6314 || !(is_recovered_exported_class_container(node, self.source)
6315 || recover_function_like_export_class_pair(node, self.source).is_some()
6316 || recover_embedded_function_like_export_classes(node, self.source)
6317 .iter()
6318 .any(|recovered| recovered.name == name)
6319 || matches!(node.kind(), "declaration" | "field_declaration")
6320 && recover_exported_class_declaration(node, self.source).is_some()
6321 || matches!(
6322 node.kind(),
6323 "class_specifier" | "struct_specifier" | "union_specifier"
6324 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
6325 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
6326 name_node,
6327 self.source,
6328 )))
6329 }) || ancestry.parent(node).is_some_and(|parent| {
6330 matches!(parent.kind(), "declaration" | "field_declaration")
6331 && recover_exported_class_declaration(parent, self.source).is_some()
6332 || is_recovered_exported_class_container(parent, self.source)
6333 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
6334 {
6335 return scope.clone();
6336 }
6337 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
6338 let Some(package_name) = borrowed_namespace
6339 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
6340 else {
6341 return scope.clone();
6342 };
6343
6344 let module = CodeUnit::new_fq(
6345 self.file.clone(),
6346 CodeUnitType::Module,
6347 "",
6348 package_name.clone(),
6349 cpp_namespace_fq(&package_name),
6350 );
6351 let mut recovered = scope.clone();
6352 recovered.package_name = package_name;
6353 recovered.module = Some(module);
6354 recovered
6355 }
6356
6357 fn unique_earlier_namespace_forward<'tree>(
6366 &mut self,
6367 recovered_node: Node<'tree>,
6368 name: &str,
6369 ancestry: &ParentIndex<'tree>,
6370 ) -> Option<String> {
6371 let mut root = recovered_node;
6372 while let Some(parent) = ancestry.parent(root) {
6373 root = parent;
6374 }
6375 let source = self.source;
6376 let scan = self
6377 .namespace_forward_scans
6378 .entry(CppTreeIdentity::of(root))
6379 .or_default();
6380 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
6381 let borrowed = scan.unique_earlier_forward(name, recovered_node);
6382
6383 #[cfg(debug_assertions)]
6384 assert_eq!(
6385 borrowed,
6386 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
6387 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
6388 for {name} at byte {}",
6389 recovered_node.start_byte()
6390 );
6391
6392 borrowed
6393 }
6394
6395 fn visit_malformed_function_definition_container<'tree>(
6396 &mut self,
6397 node: Node<'tree>,
6398 scope: &ScopeInfo,
6399 stack: &mut Vec<CppWork<'tree>>,
6400 ) {
6401 let Some(body) = cpp_body_node(node) else {
6402 return;
6403 };
6404 if !cpp_contains_namespace_definition(body) {
6405 return;
6406 }
6407 stack.push(CppWork::Container(CppContainer {
6408 node: body,
6409 scope: scope.clone(),
6410 }));
6411 }
6412
6413 fn record_recovered_declarations(
6431 &mut self,
6432 recovery: Range,
6433 reparse_walk: impl FnOnce(&mut Self),
6434 ) {
6435 #[cfg(any(debug_assertions, test))]
6438 let before = self.parsed.declarations().clone();
6439
6440 self.recovery_captures.push(CppRecoveryCapture::default());
6441 reparse_walk(self);
6442 let captured = self
6443 .recovery_captures
6444 .pop()
6445 .expect("the capture this call pushed is the one it pops");
6446
6447 let mut minted: Vec<CodeUnit> = captured
6453 .created
6454 .into_iter()
6455 .filter(|unit| self.parsed.contains_declaration(unit))
6456 .collect();
6457 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6458
6459 #[cfg(any(debug_assertions, test))]
6460 {
6461 let mut rediscovered: Vec<CodeUnit> = self
6462 .parsed
6463 .declarations()
6464 .iter()
6465 .filter(|unit| !before.contains(*unit))
6466 .cloned()
6467 .collect();
6468 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6469 assert_eq!(
6470 minted, rediscovered,
6471 "the captured recovered set must be the declaration delta of the reparse \
6472 walk over {recovery:?}"
6473 );
6474 }
6475
6476 for unit in minted {
6477 self.parsed
6478 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6479 recovery,
6480 unit,
6481 });
6482 }
6483 }
6484
6485 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
6488 let start = self
6489 .parsed
6490 .declaration_ranges(unit)
6491 .first()
6492 .map(|range| range.start_byte)
6493 .unwrap_or(usize::MAX);
6494 (start, unit.fq_name().to_string())
6495 }
6496
6497 fn visit_sentinel_macro_region<'tree>(
6498 &mut self,
6499 node: Node<'tree>,
6500 scope: &ScopeInfo,
6501 stack: &mut Vec<CppWork<'tree>>,
6502 ancestry: &ParentIndex<'tree>,
6503 ) -> bool {
6504 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
6505 return true;
6506 }
6507 if let Some((
6508 reparse_start,
6509 class_start,
6510 body_start,
6511 class_close_start,
6512 class_close_end,
6513 class_close_line,
6514 )) = cpp_sentinel_macro_class_region(node, self.source)
6515 {
6516 let Some(class_tree) =
6517 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
6518 else {
6519 return false;
6520 };
6521 let class_root = class_tree.root_node();
6522 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
6523 let class_ancestry = ParentIndex::new(class_root);
6525 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
6526 class_root,
6527 template_node,
6528 self.source,
6529 &class_ancestry,
6530 ) else {
6531 return false;
6532 };
6533 let class_node = reparsed_class.declaration_node;
6534 let name = reparsed_class.name;
6535 let mut class_scope = scope.clone();
6536 if let Some(template_node) = template_node {
6537 class_scope.template_signature =
6538 cpp_template_signature(template_node, class_node, self.source);
6539 class_scope.template_metadata =
6540 cpp_template_metadata(template_node, class_node, self.source, ancestry);
6541 }
6542 let Some(body_tree) =
6543 cpp_reparse_region_items(self.source, body_start, class_close_start)
6544 else {
6545 return false;
6546 };
6547 let raw_supertypes = reparsed_class.raw_supertypes;
6548 let class_range = Range {
6549 start_byte: class_start,
6550 end_byte: class_close_end,
6551 start_line: class_node.start_position().row + 1,
6552 end_line: class_close_line,
6553 };
6554 let class_scope = self.scope_for_recovered_exported_class(
6555 class_node,
6556 &name,
6557 true,
6558 &class_scope,
6559 ancestry,
6560 );
6561 let mut class_stack = Vec::new();
6562 let class_unit = self.visit_named_class_like_shape(
6563 class_node,
6564 name,
6565 None,
6566 true,
6567 Some(class_range),
6568 raw_supertypes,
6569 &class_scope,
6570 &mut class_stack,
6571 ancestry,
6572 );
6573 self.parsed
6574 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6575 recovery: class_range,
6576 unit: class_unit.clone(),
6577 });
6578 let member_scope = ScopeInfo {
6579 package_name: class_scope.package_name.clone(),
6580 module: class_scope.module.clone(),
6581 class_unit: Some(class_unit),
6582 template_signature: class_scope.template_signature.clone(),
6583 template_metadata: None,
6584 declarations_are_fields: true,
6585 recovered_specialization_member_scope: false,
6586 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
6587 };
6588 let body_root = body_tree.root_node();
6590 self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
6591 self.consumed_fragment_regions
6594 .push((node.start_byte(), class_close_end));
6595 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
6602 stack.push(CppWork::Container(CppContainer {
6603 node,
6604 scope: scope.clone(),
6605 }));
6606 }
6607 return true;
6608 }
6609 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
6610 return false;
6611 };
6612 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
6613 return false;
6614 };
6615 let root = tree.root_node();
6616 if !cpp_reparsed_items_are_indexable(root, self.source) {
6617 return false;
6618 }
6619 let recovery = cpp_recovery_window(self.source, start, end);
6620 let reparsed_ancestry = ParentIndex::new(root);
6622 self.record_recovered_declarations(recovery, |visitor| {
6623 visitor.visit_container(
6624 root,
6625 &reparsed_ancestry,
6626 &scope.package_name,
6627 scope.module.clone(),
6628 scope.class_unit.clone(),
6629 scope.template_signature.clone(),
6630 scope.visible_using_namespaces.clone(),
6631 );
6632 });
6633 if end > node.end_byte() {
6634 self.consumed_fragment_regions
6635 .push((node.start_byte(), end));
6636 } else if node.kind() == "ERROR" && node.end_byte() > end {
6637 self.consumed_fragment_regions
6644 .push((node.start_byte(), end));
6645 stack.push(CppWork::Container(CppContainer {
6646 node,
6647 scope: scope.clone(),
6648 }));
6649 }
6650 true
6651 }
6652
6653 fn visit_nested_namespace_sentinel<'tree>(
6659 &mut self,
6660 node: Node<'tree>,
6661 scope: &ScopeInfo,
6662 ancestry: &ParentIndex<'tree>,
6663 ) -> bool {
6664 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
6665 return false;
6666 };
6667
6668 let mut package_name = scope.package_name.clone();
6669 let mut module = scope.module.clone();
6670 for component in recovered.namespace_components {
6671 package_name = if package_name.is_empty() {
6672 component
6673 } else {
6674 format!("{package_name}::{component}")
6675 };
6676 let namespace_module = CodeUnit::new_fq(
6677 self.file.clone(),
6678 CodeUnitType::Module,
6679 "",
6680 package_name.clone(),
6681 cpp_namespace_fq(&package_name),
6682 );
6683 if !self.parsed.contains_declaration(&namespace_module) {
6684 self.add_declaration(namespace_module.clone(), recovered.function, None, None);
6685 }
6686 module = Some(namespace_module);
6687 }
6688
6689 let recovered_scope = ScopeInfo {
6690 package_name,
6691 module,
6692 class_unit: None,
6703 template_signature: scope.template_signature.clone(),
6704 template_metadata: scope.template_metadata.clone(),
6705 declarations_are_fields: false,
6706 recovered_specialization_member_scope: false,
6707 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6708 };
6709 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
6710 recovered.function,
6711 recovered.body,
6712 self.source,
6713 ancestry,
6714 ) {
6715 let mut class_scope = recovered_scope.clone();
6716 if let Some(template_node) = fragmented.template_node {
6717 class_scope.template_signature =
6718 cpp_template_signature(template_node, fragmented.class_node, self.source);
6719 class_scope.template_metadata = cpp_template_metadata(
6720 template_node,
6721 fragmented.class_node,
6722 self.source,
6723 ancestry,
6724 );
6725 }
6726 if let Some(outcome) = self
6727 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
6728 {
6729 let mut class_stack = Vec::new();
6730 let class_unit = self.visit_named_class_like_shape(
6731 fragmented.class_node,
6732 fragmented.name.clone(),
6733 None,
6734 true,
6735 Some(fragmented.fragmented.class_range),
6736 fragmented.raw_supertypes.clone(),
6737 &class_scope,
6738 &mut class_stack,
6739 ancestry,
6740 );
6741 self.parsed
6742 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6743 recovery: fragmented.fragmented.class_range,
6744 unit: class_unit.clone(),
6745 });
6746 if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
6747 self.consumed_fragment_regions.push((
6748 fragmented.consumed_start,
6749 fragmented.fragmented.class_range.end_byte,
6750 ));
6751 }
6752 }
6753 }
6754 self.run_container_work(recovered.body, recovered_scope, ancestry);
6759 true
6760 }
6761
6762 fn visit_declaration<'tree>(
6763 &mut self,
6764 node: Node<'tree>,
6765 scope: &ScopeInfo,
6766 in_class_body: bool,
6767 stack: &mut Vec<CppWork<'tree>>,
6768 ancestry: &ParentIndex<'tree>,
6769 ) {
6770 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6771 return;
6772 }
6773 if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
6774 return;
6775 }
6776 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
6777 !cpp_active_template_type_parameter(
6778 node,
6779 node_text(declarator, self.source),
6780 self.source,
6781 ancestry,
6782 )
6783 }) {
6784 return;
6785 }
6786 if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
6787 self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
6793 return;
6794 }
6795 if in_class_body
6796 && let Some(parent) = scope.class_unit.as_ref()
6797 && let Some(call) =
6798 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
6799 {
6800 self.visit_recovered_macro_qualified_constructor_definition(
6801 node, call, scope, ancestry,
6802 );
6803 return;
6804 }
6805 if in_class_body
6806 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
6807 {
6808 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
6809 return;
6810 }
6811 if in_class_body
6812 && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
6813 {
6814 for member in members {
6815 self.add_macro_wrapped_declaration(member, scope, ancestry);
6816 }
6817 return;
6818 }
6819 if in_class_body
6820 && let Some(declarators) =
6821 recovered_macro_qualified_field_declarators(node, self.source)
6822 {
6823 for declarator in declarators {
6824 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6825 }
6826 return;
6827 }
6828 let recovered_alias_names = recovered_type_alias_names(node, self.source);
6829 if !recovered_alias_names.is_empty() {
6830 self.add_type_aliases(node, scope, recovered_alias_names, ancestry);
6831 return;
6832 }
6833 if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
6834 return;
6835 }
6836 if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
6837 return;
6838 }
6839
6840 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
6841 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
6842 if let Some(outcome) =
6847 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
6848 {
6849 let consumed_region = (
6850 recovered.declaration_node.end_byte(),
6851 fragmented.class_range.end_byte,
6852 );
6853 let code_unit = self.visit_named_class_like_shape(
6854 recovered.declaration_node,
6855 recovered.name,
6856 None,
6857 true,
6858 Some(fragmented.class_range),
6859 recovered.raw_supertypes,
6860 scope,
6861 stack,
6862 ancestry,
6863 );
6864 self.parsed.record_materialization(
6865 MaterializationRecord::RecoveredDeclaration {
6866 recovery: fragmented.class_range,
6867 unit: code_unit.clone(),
6868 },
6869 );
6870 let consume_fragment =
6871 self.visit_fragmented_export_class_members(outcome, code_unit, scope);
6872 if consume_fragment {
6878 self.consumed_fragment_regions.push(consumed_region);
6879 }
6880 return;
6881 }
6882 }
6883 let uses_initializer_body = recovered.uses_initializer_body;
6884 let definition_body_present = recovered.body.is_some();
6885 let class_unit = self.visit_named_class_like_shape(
6886 recovered.declaration_node,
6887 recovered.name,
6888 recovered.body,
6889 definition_body_present,
6890 None,
6891 recovered.raw_supertypes,
6892 scope,
6893 stack,
6894 ancestry,
6895 );
6896 self.parsed
6897 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6898 recovery: cpp_declaration_range(node),
6899 unit: class_unit,
6900 });
6901 if uses_initializer_body {
6902 return;
6903 }
6904 }
6905
6906 if in_class_body && cpp_unterminated_macro_invocation_field(node) {
6917 return;
6918 }
6919
6920 let mut handled_function = false;
6921 let mut handled_declarator = false;
6922 let mut cursor = node.walk();
6923 for child in node.named_children(&mut cursor) {
6924 if matches!(
6925 child.kind(),
6926 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
6927 ) {
6928 if cpp_body_node(child).is_some() {
6937 self.visit_class_like(child, scope, stack, ancestry);
6938 }
6939 continue;
6940 }
6941 }
6942
6943 let mut cursor = node.walk();
6944 for child in node.children_by_field_name("declarator", &mut cursor) {
6945 if crate::structural::is_recovered_designator_init_declarator(child) {
6946 handled_declarator = true;
6947 continue;
6948 }
6949 if in_class_body
6950 && let Some(field) = recovered_function_like_field_declarator(node, self.source)
6951 {
6952 handled_declarator = true;
6953 self.visit_variable_declaration(node, field.name, scope, true, ancestry);
6954 continue;
6955 }
6956 if let Some(kind) = classify_declarator(child) {
6957 handled_declarator = true;
6958 match kind {
6959 DeclaratorKind::Function(function_declarator) => {
6960 handled_function = true;
6961 self.visit_function_declaration(node, function_declarator, scope, ancestry);
6962 }
6963 DeclaratorKind::Variable(variable_declarator) => {
6964 self.visit_variable_declaration(
6965 node,
6966 variable_declarator,
6967 scope,
6968 in_class_body,
6969 ancestry,
6970 );
6971 }
6972 }
6973 }
6974 }
6975
6976 if !handled_declarator {
6977 let mut cursor = node.walk();
6978 for child in node.named_children(&mut cursor) {
6979 if crate::structural::is_recovered_designator_init_declarator(child) {
6980 handled_declarator = true;
6981 continue;
6982 }
6983 if !is_unfielded_declarator_candidate(child) {
6984 continue;
6985 }
6986 let Some(kind) = classify_declarator(child) else {
6987 continue;
6988 };
6989 handled_declarator = true;
6990 match kind {
6991 DeclaratorKind::Function(function_declarator) => {
6992 handled_function = true;
6993 self.visit_function_declaration(node, function_declarator, scope, ancestry);
6994 }
6995 DeclaratorKind::Variable(variable_declarator) => {
6996 self.visit_variable_declaration(
6997 node,
6998 variable_declarator,
6999 scope,
7000 in_class_body,
7001 ancestry,
7002 );
7003 }
7004 }
7005 }
7006 }
7007
7008 if handled_function {
7009 return;
7010 }
7011
7012 if !handled_declarator {
7013 if in_class_body {
7014 self.visit_class_members_from_declaration(node, scope, ancestry);
7015 } else {
7016 self.visit_global_variables_from_declaration(node, scope, ancestry);
7017 }
7018 }
7019 }
7020
7021 fn visit_anonymous_aggregate_declaration<'tree>(
7035 &mut self,
7036 node: Node<'tree>,
7037 scope: &ScopeInfo,
7038 in_class_body: bool,
7039 stack: &mut Vec<CppWork<'tree>>,
7040 ancestry: &ParentIndex<'tree>,
7041 ) -> bool {
7042 if !in_class_body || scope.class_unit.is_none() {
7043 return false;
7044 }
7045 let Some(aggregate) = node.child_by_field_name("type") else {
7046 return false;
7047 };
7048 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7049 || aggregate.child_by_field_name("name").is_some()
7050 {
7051 return false;
7052 }
7053 let Some(body) = cpp_body_node(aggregate) else {
7054 return false;
7055 };
7056
7057 let mut cursor = node.walk();
7058 let declarators = node
7059 .children_by_field_name("declarator", &mut cursor)
7060 .filter_map(|declarator| match classify_declarator(declarator) {
7061 Some(DeclaratorKind::Variable(variable)) => Some(variable),
7062 Some(DeclaratorKind::Function(_)) | None => None,
7063 })
7064 .collect::<Vec<_>>();
7065 if declarators.is_empty() {
7066 stack.push(CppWork::Container(CppContainer {
7067 node: body,
7068 scope: scope.clone(),
7069 }));
7070 return true;
7071 }
7072
7073 for declarator in declarators {
7074 let Some(name) = extract_variable_name(declarator, self.source) else {
7075 continue;
7076 };
7077 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
7078 self.visit_named_class_like_shape(
7083 aggregate,
7084 name,
7085 Some(body),
7086 true,
7087 Some(cpp_declaration_range(node)),
7088 None,
7089 scope,
7090 stack,
7091 ancestry,
7092 );
7093 }
7094 true
7095 }
7096
7097 fn visit_anonymous_local_aggregate_declaration<'tree>(
7106 &mut self,
7107 node: Node<'tree>,
7108 scope: &ScopeInfo,
7109 stack: &mut Vec<CppWork<'tree>>,
7110 ancestry: &ParentIndex<'tree>,
7111 ) -> bool {
7112 if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
7113 return false;
7114 }
7115 let Some(aggregate) = node.child_by_field_name("type") else {
7116 return false;
7117 };
7118 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7119 || aggregate.child_by_field_name("name").is_some()
7120 {
7121 return false;
7122 }
7123 let Some(body) = cpp_body_node(aggregate) else {
7124 return false;
7125 };
7126 let mut cursor = node.walk();
7127 let declarators = node
7128 .children_by_field_name("declarator", &mut cursor)
7129 .filter_map(|declarator| match classify_declarator(declarator) {
7130 Some(DeclaratorKind::Variable(variable)) => Some(variable),
7131 Some(DeclaratorKind::Function(_)) | None => None,
7132 })
7133 .collect::<Vec<_>>();
7134 if declarators.is_empty() {
7135 return false;
7136 }
7137
7138 for declarator in &declarators {
7139 self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
7140 }
7141 let name = format!("<anonymous:{}>", aggregate.start_byte());
7142 self.visit_named_class_like_shape(
7143 aggregate,
7144 name,
7145 Some(body),
7146 true,
7147 None,
7148 None,
7149 scope,
7150 stack,
7151 ancestry,
7152 );
7153 true
7154 }
7155
7156 fn visit_function_declaration<'tree>(
7157 &mut self,
7158 declaration_node: Node<'tree>,
7159 declarator: Node<'tree>,
7160 scope: &ScopeInfo,
7161 ancestry: &ParentIndex<'tree>,
7162 ) {
7163 let Some(function) = extract_function_info(declarator, self.source, scope) else {
7164 return;
7165 };
7166 let code_unit =
7167 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7168 if self.parsed.contains_declaration(&code_unit) {
7169 self.parsed
7170 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7171 return;
7172 }
7173 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7174 let signature = render_cpp_function_display_signature_from_node(
7175 declaration_node,
7176 self.source,
7177 scope.template_signature.as_deref(),
7178 false,
7179 ancestry,
7180 );
7181 self.parsed.add_signature_with_metadata(
7182 code_unit.clone(),
7183 cpp_signature_metadata(signature, declarator, self.source, ancestry)
7184 .with_declaration_only(true)
7185 .with_callable_linkage(cpp_callable_linkage(
7186 declaration_node,
7187 self.source,
7188 ancestry,
7189 )),
7190 );
7191 if let Some(parent) = &scope.class_unit {
7192 self.parsed.add_child(parent.clone(), code_unit);
7193 } else if let Some(module) = &scope.module {
7194 self.parsed.add_child(module.clone(), code_unit);
7195 }
7196 }
7197
7198 fn visit_recovered_macro_qualified_function_declaration<'tree>(
7199 &mut self,
7200 declaration_node: Node<'tree>,
7201 call: Node<'tree>,
7202 scope: &ScopeInfo,
7203 ancestry: &ParentIndex<'tree>,
7204 ) {
7205 let Some(parent) = &scope.class_unit else {
7206 return;
7207 };
7208 let Some(name_node) = call.child_by_field_name("function") else {
7209 return;
7210 };
7211 let Some(arguments) = call.child_by_field_name("arguments") else {
7212 return;
7213 };
7214 let Some((signature, parameter_labels)) =
7215 recovered_macro_qualified_function_parameters(arguments, self.source)
7216 else {
7217 return;
7218 };
7219 let arity = parameter_labels.len();
7220 let function = FunctionInfo {
7221 package_name: scope.package_name.clone(),
7222 owner: Some(CppMemberOwner::Unit(parent.clone())),
7223 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
7224 signature,
7225 };
7226 if function.name.is_empty() {
7227 return;
7228 }
7229 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7230 if self.parsed.contains_declaration(&code_unit) {
7231 self.parsed
7232 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7233 return;
7234 }
7235 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7236 let signature_label = render_cpp_function_display_signature_from_node(
7237 declaration_node,
7238 self.source,
7239 scope.template_signature.as_deref(),
7240 false,
7241 ancestry,
7242 );
7243 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7244 .with_declaration_only(true)
7245 .with_callable_arity(CallableArity::exact(arity))
7246 .with_callable_linkage(cpp_callable_linkage(
7247 declaration_node,
7248 self.source,
7249 ancestry,
7250 ));
7251 self.parsed
7252 .add_signature_with_metadata(code_unit.clone(), metadata);
7253 self.parsed.add_child(parent.clone(), code_unit);
7254 }
7255
7256 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
7257 &mut self,
7258 declaration_node: Node<'tree>,
7259 call: Node<'tree>,
7260 scope: &ScopeInfo,
7261 ancestry: &ParentIndex<'tree>,
7262 ) {
7263 let Some(parent) = &scope.class_unit else {
7264 return;
7265 };
7266 let Some(arguments) = call.child_by_field_name("arguments") else {
7267 return;
7268 };
7269 let Some((mut signature, parameter_labels)) =
7270 recovered_macro_qualified_function_parameters(arguments, self.source)
7271 else {
7272 return;
7273 };
7274 if let Some(template_signature) = &scope.template_signature {
7275 signature = format!("{template_signature}{signature}");
7276 }
7277 let arity = parameter_labels.len();
7278 let function = FunctionInfo {
7279 package_name: scope.package_name.clone(),
7280 owner: Some(CppMemberOwner::Unit(parent.clone())),
7281 name: parent.identifier().to_string(),
7282 signature,
7283 };
7284 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7285 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7286 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
7287 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7288 .with_declaration_only(false)
7289 .with_callable_arity(CallableArity::exact(arity))
7290 .with_callable_linkage(cpp_callable_linkage(
7291 declaration_node,
7292 self.source,
7293 ancestry,
7294 ));
7295 self.parsed
7296 .add_signature_with_metadata(code_unit.clone(), metadata);
7297 self.parsed.add_child(parent.clone(), code_unit);
7298 }
7299
7300 fn visit_variable_declaration<'tree>(
7301 &mut self,
7302 declaration_node: Node<'tree>,
7303 declarator: Node<'tree>,
7304 scope: &ScopeInfo,
7305 in_class_body: bool,
7306 ancestry: &ParentIndex<'tree>,
7307 ) {
7308 let Some(name) = extract_variable_name(declarator, self.source) else {
7309 return;
7310 };
7311 let parent = if in_class_body {
7312 let Some(parent) = &scope.class_unit else {
7313 return;
7314 };
7315 Some(parent)
7316 } else {
7317 None
7318 };
7319 let short_name = match parent {
7320 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
7321 None => name.clone(),
7322 };
7323 let fq = cpp_leaf_fq(
7324 &scope.package_name,
7325 parent,
7326 &name,
7327 SegmentKind::Member,
7328 SegmentKind::Member,
7329 );
7330 let code_unit = CodeUnit::new_fq(
7331 self.file.clone(),
7332 CodeUnitType::Field,
7333 scope.package_name.clone(),
7334 short_name,
7335 fq,
7336 );
7337 if self.parsed.contains_declaration(&code_unit) {
7338 return;
7339 }
7340 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7341 self.parsed.add_signature_with_metadata(
7342 code_unit.clone(),
7343 SignatureMetadata::new(
7344 render_cpp_field_signature(declaration_node, declarator, self.source),
7345 Vec::new(),
7346 )
7347 .with_cpp_field_linkage(cpp_field_declaration_linkage(
7348 declaration_node,
7349 self.source,
7350 ancestry,
7351 )),
7352 );
7353 if let Some(parent) = &scope.class_unit {
7354 self.parsed.add_child(parent.clone(), code_unit);
7355 } else if let Some(module) = &scope.module {
7356 self.parsed.add_child(module.clone(), code_unit);
7357 }
7358 }
7359
7360 fn visit_class_members_from_declaration<'tree>(
7361 &mut self,
7362 node: Node<'tree>,
7363 scope: &ScopeInfo,
7364 ancestry: &ParentIndex<'tree>,
7365 ) {
7366 let mut cursor = node.walk();
7367 for child in node.named_children(&mut cursor) {
7368 if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
7369 self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
7370 } else if child.kind() == "init_declarator"
7371 && let Some(inner) = child.child_by_field_name("declarator")
7372 {
7373 self.visit_variable_declaration(node, inner, scope, true, ancestry);
7374 } else if matches!(
7375 child.kind(),
7376 "identifier"
7377 | "field_identifier"
7378 | "pointer_declarator"
7379 | "reference_declarator"
7380 | "array_declarator"
7381 | "parenthesized_declarator"
7382 ) {
7383 self.visit_variable_declaration(node, child, scope, true, ancestry);
7384 }
7385 }
7386 }
7387
7388 fn visit_global_variables_from_declaration<'tree>(
7389 &mut self,
7390 node: Node<'tree>,
7391 scope: &ScopeInfo,
7392 ancestry: &ParentIndex<'tree>,
7393 ) {
7394 let mut cursor = node.walk();
7395 for child in node.named_children(&mut cursor) {
7396 if child.kind() == "init_declarator"
7397 && let Some(inner) = child.child_by_field_name("declarator")
7398 {
7399 self.visit_variable_declaration(node, inner, scope, false, ancestry);
7400 } else if matches!(
7401 child.kind(),
7402 "identifier"
7403 | "field_identifier"
7404 | "pointer_declarator"
7405 | "reference_declarator"
7406 | "array_declarator"
7407 | "parenthesized_declarator"
7408 ) {
7409 self.visit_variable_declaration(node, child, scope, false, ancestry);
7410 }
7411 }
7412 }
7413
7414 fn visit_type_declaration<'tree>(
7415 &mut self,
7416 node: Node<'tree>,
7417 scope: &ScopeInfo,
7418 stack: &mut Vec<CppWork<'tree>>,
7419 ancestry: &ParentIndex<'tree>,
7420 ) {
7421 let type_node = node.child_by_field_name("type");
7422 if let Some(type_node) = type_node
7423 && matches!(
7424 type_node.kind(),
7425 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
7426 )
7427 {
7428 self.visit_class_like(type_node, scope, stack, ancestry);
7429 }
7430
7431 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
7432 let range = Range {
7433 start_byte: node.start_byte(),
7434 end_byte: recovered.end_node.end_byte(),
7435 start_line: node.start_position().row + 1,
7436 end_line: recovered.end_node.end_position().row + 1,
7437 };
7438 let signature = self
7439 .source
7440 .get(range.start_byte..range.end_byte)
7441 .map(normalize_cpp_whitespace)
7442 .unwrap_or_default();
7443 self.record_type_aliases(
7444 node,
7445 scope,
7446 vec![recovered.name],
7447 signature,
7448 range,
7449 ancestry,
7450 );
7451 return;
7452 }
7453
7454 let alias_names = match node.kind() {
7455 "alias_declaration" => extract_alias_declaration_name(node, self.source)
7456 .into_iter()
7457 .collect::<Vec<_>>(),
7458 "type_definition" => extract_typedef_alias_names(node, self.source),
7459 _ => Vec::new(),
7460 };
7461 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
7462 (type_node, alias_names.as_slice())
7463 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
7464 && type_node.child_by_field_name("name").is_none()
7465 {
7466 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
7467 } else {
7468 None
7469 };
7470 self.add_type_aliases(node, scope, alias_names, ancestry);
7471 if let Some((body, alias_name)) = anonymous_aggregate {
7472 let signature = normalize_cpp_whitespace(node_text(node, self.source));
7478 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
7479 debug_assert!(self.parsed.contains_declaration(&alias_unit));
7480 let mut nested_scope = scope.clone();
7481 nested_scope.class_unit = Some(alias_unit);
7482 nested_scope.template_signature = scope.template_signature.clone();
7483 nested_scope.template_metadata = None;
7484 nested_scope.declarations_are_fields = false;
7485 nested_scope.recovered_specialization_member_scope = false;
7486 stack.push(CppWork::Container(CppContainer {
7487 node: body,
7488 scope: nested_scope,
7489 }));
7490 }
7491 }
7492
7493 fn add_type_aliases(
7494 &mut self,
7495 node: Node<'_>,
7496 scope: &ScopeInfo,
7497 alias_names: Vec<String>,
7498 ancestry: &ParentIndex<'_>,
7499 ) {
7500 let signature = normalize_cpp_whitespace(node_text(node, self.source));
7501 self.record_type_aliases(
7502 node,
7503 scope,
7504 alias_names,
7505 signature,
7506 cpp_declaration_range(node),
7507 ancestry,
7508 );
7509 }
7510
7511 fn record_type_aliases(
7512 &mut self,
7513 node: Node<'_>,
7514 scope: &ScopeInfo,
7515 alias_names: Vec<String>,
7516 signature: String,
7517 range: Range,
7518 ancestry: &ParentIndex<'_>,
7519 ) {
7520 if signature.is_empty() {
7521 return;
7522 }
7523 let type_name = node
7524 .child_by_field_name("type")
7525 .and_then(|type_node| type_node.child_by_field_name("name"))
7526 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
7527 for alias_name in alias_names {
7528 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
7529 continue;
7530 }
7531 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
7532 self.add_declaration_with_range(code_unit.clone(), range, None, None);
7535 let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
7536 let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
7537 cpp_structured_type_identity(type_node, self.source, &lexical_scope)
7538 });
7539 self.parsed.add_signature_with_metadata(
7540 code_unit.clone(),
7541 SignatureMetadata::new(signature.clone(), Vec::new())
7542 .with_underlying_type_identity(underlying_type_identity),
7543 );
7544 if let Some(metadata) = &scope.template_metadata {
7545 let mut metadata = metadata.clone();
7546 metadata.primary_fq_name = code_unit.fq_name();
7547 self.parsed
7548 .set_cpp_template_metadata(code_unit.clone(), metadata);
7549 }
7550 if let Some(parent) = &scope.class_unit {
7551 self.parsed.add_child(parent.clone(), code_unit.clone());
7552 } else if let Some(module) = &scope.module {
7553 self.parsed.add_child(module.clone(), code_unit.clone());
7554 }
7555 self.parsed.mark_type_alias(code_unit);
7556 }
7557 }
7558
7559 fn type_alias_unit(
7560 &self,
7561 scope: &ScopeInfo,
7562 alias_name: String,
7563 signature: String,
7564 ) -> CodeUnit {
7565 let short_name = if let Some(parent) = &scope.class_unit {
7566 cpp_join_nested_short(parent.short_name(), &alias_name)
7567 } else {
7568 alias_name.clone()
7569 };
7570 let fq = cpp_leaf_fq(
7571 &scope.package_name,
7572 scope.class_unit.as_ref(),
7573 &alias_name,
7574 SegmentKind::Nested,
7575 SegmentKind::Type,
7576 );
7577 CodeUnit::with_signature_and_fq(
7578 self.file.clone(),
7579 CodeUnitType::Class,
7580 scope.package_name.clone(),
7581 short_name,
7582 Some(signature),
7583 false,
7584 fq,
7585 )
7586 }
7587
7588 fn visit_macro(&mut self, node: Node<'_>) {
7589 if let Some(replacement) =
7593 crate::graph::syntax::function_macro_replacement_span(node, self.source)
7594 {
7595 self.consumed_fragment_regions
7596 .push((replacement.start, replacement.end));
7597 }
7598 let Some(name) = extract_macro_name(node, self.source) else {
7599 return;
7600 };
7601 let signature = node_text(node, self.source).trim_end().to_string();
7602 if signature.is_empty() {
7603 return;
7604 }
7605 let fq = cpp_member_fq("", &name);
7606 let code_unit = CodeUnit::with_signature_and_fq(
7613 self.file.clone(),
7614 CodeUnitType::Macro,
7615 "",
7616 name.clone(),
7617 Some(signature.clone()),
7618 false,
7619 fq,
7620 );
7621 if !self.parsed.contains_declaration(&code_unit) {
7622 self.add_declaration(code_unit.clone(), node, None, None);
7623 let name_range = node
7624 .child_by_field_name("name")
7625 .map(cpp_declaration_range)
7626 .unwrap_or_else(|| cpp_declaration_range(node));
7627 self.parsed
7628 .record_materialization(MaterializationRecord::GeneratedDeclaration {
7629 site: cpp_declaration_range(node),
7630 argument: name_range,
7631 kind: GenerationKind::PreprocessorDefinition,
7632 unit: code_unit.clone(),
7633 });
7634 self.parsed.add_signature(code_unit, signature);
7635 }
7636 if node.kind() == "preproc_def" {
7637 update_object_macro_field_environment(
7638 node,
7639 self.source,
7640 &mut self.object_macro_fields,
7641 &mut self.ambiguous_object_macro_fields,
7642 );
7643 } else {
7644 self.object_macro_fields.remove(&name);
7645 self.ambiguous_object_macro_fields.remove(&name);
7646 }
7647 }
7648
7649 fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7650 let Some(directive) = node.child_by_field_name("directive") else {
7651 return;
7652 };
7653 let name = node_text(directive, self.source).trim();
7654 let range = cpp_declaration_range(node);
7655 let fields = object_macro_field_closure(&self.object_macro_fields, name);
7656 self.materialize_object_macro_fields(fields, range, scope);
7657 }
7658
7659 fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7664 if !matches!(node.kind(), "declaration" | "field_declaration") {
7665 return false;
7666 }
7667 if scope.class_unit.is_none() {
7675 return false;
7676 }
7677 let macro_nodes =
7678 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7679 for macro_node in ¯o_nodes {
7680 let name = node_text(*macro_node, self.source).trim();
7681 let fields = object_macro_field_closure(&self.object_macro_fields, name);
7682 self.materialize_object_macro_fields(fields, cpp_declaration_range(*macro_node), scope);
7683 }
7684 let Some(last) = macro_nodes.last() else {
7685 return false;
7686 };
7687 self.record_collapsed_aggregate_fields(
7693 last.end_byte()..node.end_byte(),
7694 node.start_position().row
7695 + 1
7696 + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
7697 scope,
7698 );
7699 true
7700 }
7701
7702 fn materialize_object_macro_fields(
7703 &mut self,
7704 fields: Vec<MacroReplacementField>,
7705 range: Range,
7706 scope: &ScopeInfo,
7707 ) {
7708 let Some(owner) = scope.class_unit.as_ref() else {
7709 return;
7710 };
7711 for field in fields {
7712 let signature = field.declaration.clone();
7713 let mut fq = owner.fq().clone();
7714 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7715 let short_name = if owner.short_name().is_empty() {
7716 field.name.clone()
7717 } else {
7718 format!("{}.{}", owner.short_name(), field.name)
7719 };
7720 let code_unit = CodeUnit::with_signature_and_fq(
7721 self.file.clone(),
7722 CodeUnitType::Field,
7723 owner.package_name().to_string(),
7724 short_name,
7725 Some(field.declaration),
7726 true,
7727 fq,
7728 );
7729 if self.parsed.contains_declaration(&code_unit) {
7730 continue;
7731 }
7732 self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7733 self.parsed.add_signature(code_unit, signature);
7734 }
7735 }
7736
7737 fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7744 let mut cursor = node.walk();
7745 let children = node.children(&mut cursor).collect::<Vec<_>>();
7746 let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember>)>::new();
7747 let mut object_macro_fields = self.object_macro_fields.clone();
7748 let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
7749 let mut open = Vec::<usize>::new();
7750 let mut index = 0;
7751 while index < children.len() {
7752 let keyword = children[index];
7753 if update_object_macro_field_environment(
7754 keyword,
7755 self.source,
7756 &mut object_macro_fields,
7757 &mut ambiguous_object_macro_fields,
7758 ) {
7759 index += 1;
7760 continue;
7761 }
7762 if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
7763 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7764 if !name.is_empty() {
7765 let parent = open
7766 .last()
7767 .and_then(|class| recovered.get(*class))
7768 .map(|(owner, _, _, _)| owner.clone())
7769 .or_else(|| scope.class_unit.clone());
7770 let short_name = parent.as_ref().map_or_else(
7771 || name.clone(),
7772 |parent| cpp_join_nested_short(parent.short_name(), &name),
7773 );
7774 let fq = cpp_leaf_fq(
7775 &scope.package_name,
7776 parent.as_ref(),
7777 &name,
7778 SegmentKind::Nested,
7779 SegmentKind::Type,
7780 );
7781 let owner = CodeUnit::with_signature_and_fq(
7782 self.file.clone(),
7783 CodeUnitType::Class,
7784 scope.package_name.clone(),
7785 short_name,
7786 None,
7787 false,
7788 fq,
7789 );
7790 recovered.push((
7791 owner,
7792 head.key.start_byte(),
7793 head.opening.end_byte(),
7794 Vec::new(),
7795 ));
7796 let class_index = recovered.len() - 1;
7797 match head.folded_members {
7798 None => open.push(class_index),
7802 Some(members) => {
7808 let macro_nodes = object_macro_identifier_nodes_with_environment(
7809 children[index],
7810 self.source,
7811 &mut object_macro_fields,
7812 &mut ambiguous_object_macro_fields,
7813 );
7814 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7815 .iter()
7816 .partition(|node| node.start_byte() < head.key.start_byte());
7817 if let Some(&enclosing) = open.last() {
7818 for macro_node in preceding {
7819 recovered[enclosing]
7820 .3
7821 .push(CppCollapsedMember::MacroFields {
7822 range: cpp_declaration_range(macro_node),
7823 fields: object_macro_field_closure(
7824 &object_macro_fields,
7825 &normalize_cpp_whitespace(node_text(
7826 macro_node,
7827 self.source,
7828 )),
7829 ),
7830 });
7831 }
7832 }
7833 let closing = cpp_collapsed_aggregate_closing_brace(members);
7834 recovered[class_index]
7835 .3
7836 .extend(cpp_collapsed_aggregate_members(
7837 &inner,
7838 head.opening.end_byte()..closing,
7839 head.opening.end_position().row + 1,
7840 self.source,
7841 &object_macro_fields,
7842 ));
7843 recovered[class_index].2 = members.end_byte();
7844 for &open_class in &open {
7845 recovered[open_class].2 =
7846 recovered[open_class].2.max(members.end_byte());
7847 }
7848 }
7849 }
7850 index += head.width;
7851 continue;
7852 }
7853 }
7854 if let Some(&class_index) = open.last()
7855 && children[index].kind() == "field_declaration"
7856 {
7857 let field = children[index];
7858 let macro_nodes = object_macro_identifier_nodes_with_environment(
7859 field,
7860 self.source,
7861 &mut object_macro_fields,
7862 &mut ambiguous_object_macro_fields,
7863 );
7864 recovered[class_index]
7865 .3
7866 .extend(cpp_collapsed_aggregate_members(
7867 ¯o_nodes,
7868 field.start_byte()..field.end_byte(),
7869 field.start_position().row + 1,
7870 self.source,
7871 &object_macro_fields,
7872 ));
7873 let end = field.end_byte();
7874 for &open_class in &open {
7875 recovered[open_class].2 = recovered[open_class].2.max(end);
7876 }
7877 let closes = count_close_brace_nodes(field);
7878 for _ in 0..closes {
7879 if let Some(closed) = open.pop() {
7880 recovered[closed].2 = end;
7881 }
7882 }
7883 }
7884 index += 1;
7885 }
7886
7887 let mut owners = Vec::with_capacity(recovered.len());
7888 for (owner, start, end, members) in recovered {
7889 let parent = owners
7890 .iter()
7891 .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
7892 .cloned()
7893 .or_else(|| scope.class_unit.clone());
7894 self.declare_collapsed_aggregate(owner.clone(), start..end, parent, members, scope);
7895 owners.push(owner);
7896 }
7897 }
7898
7899 fn declare_collapsed_aggregate(
7901 &mut self,
7902 owner: CodeUnit,
7903 span: std::ops::Range<usize>,
7904 parent: Option<CodeUnit>,
7905 members: Vec<CppCollapsedMember>,
7906 scope: &ScopeInfo,
7907 ) {
7908 let range = Range {
7909 start_byte: span.start,
7910 end_byte: span.end,
7911 start_line: self.source.get(..span.start).map_or(1, |source| {
7912 source.bytes().filter(|byte| *byte == b'\n').count() + 1
7913 }),
7914 end_line: self.source.get(..span.end).map_or(1, |source| {
7915 source.bytes().filter(|byte| *byte == b'\n').count() + 1
7916 }),
7917 };
7918 self.add_declaration_with_range(owner.clone(), range, parent, None);
7925 let owner_scope = ScopeInfo {
7926 class_unit: Some(owner),
7927 declarations_are_fields: true,
7928 ..scope.clone()
7929 };
7930 for member in members {
7931 match member {
7932 CppCollapsedMember::MacroFields { range, fields } => {
7933 self.materialize_object_macro_fields(fields, range, &owner_scope);
7934 }
7935 CppCollapsedMember::Declarations { span, start_line } => {
7936 self.record_collapsed_aggregate_fields(span, start_line, &owner_scope);
7937 }
7938 }
7939 }
7940 }
7941
7942 fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7953 if node.parent().is_some_and(|parent| parent.is_error()) {
7954 return false;
7957 }
7958 let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
7959 return false;
7960 };
7961 let members = head
7962 .folded_members
7963 .expect("a folded aggregate head carries its member list");
7964 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7965 if name.is_empty() {
7966 return false;
7967 }
7968 let macro_nodes =
7969 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7970 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7971 .iter()
7972 .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
7973 for macro_node in preceding {
7974 let fields = object_macro_field_closure(
7975 &self.object_macro_fields,
7976 &normalize_cpp_whitespace(node_text(macro_node, self.source)),
7977 );
7978 self.materialize_object_macro_fields(fields, cpp_declaration_range(macro_node), scope);
7979 }
7980 let parent = scope.class_unit.clone();
7981 let short_name = parent.as_ref().map_or_else(
7982 || name.clone(),
7983 |parent| cpp_join_nested_short(parent.short_name(), &name),
7984 );
7985 let fq = cpp_leaf_fq(
7986 &scope.package_name,
7987 parent.as_ref(),
7988 &name,
7989 SegmentKind::Nested,
7990 SegmentKind::Type,
7991 );
7992 let owner = CodeUnit::with_signature_and_fq(
7993 self.file.clone(),
7994 CodeUnitType::Class,
7995 scope.package_name.clone(),
7996 short_name,
7997 None,
7998 false,
7999 fq,
8000 );
8001 let recovered = cpp_collapsed_aggregate_members(
8002 &inner,
8003 head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
8004 head.opening.end_position().row + 1,
8005 self.source,
8006 &self.object_macro_fields,
8007 );
8008 self.declare_collapsed_aggregate(
8009 owner,
8010 head.key.start_byte()..members.end_byte(),
8011 parent,
8012 recovered,
8013 scope,
8014 );
8015 true
8016 }
8017
8018 fn record_collapsed_aggregate_fields(
8023 &mut self,
8024 span: std::ops::Range<usize>,
8025 start_line: usize,
8026 scope: &ScopeInfo,
8027 ) {
8028 let Some(owner) = scope.class_unit.as_ref() else {
8029 return;
8030 };
8031 for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
8032 let range = Range {
8033 start_byte: field.range.start,
8034 end_byte: field.range.end,
8035 start_line: start_line
8036 + cpp_line_breaks_between(self.source, span.start, field.range.start),
8037 end_line: start_line
8038 + cpp_line_breaks_between(self.source, span.start, field.range.end),
8039 };
8040 let mut fq = owner.fq().clone();
8041 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
8042 let short_name = if owner.short_name().is_empty() {
8043 field.name.clone()
8044 } else {
8045 format!("{}.{}", owner.short_name(), field.name)
8046 };
8047 let signature = normalize_cpp_whitespace(&field.declaration);
8048 let code_unit = CodeUnit::with_signature_and_fq(
8049 self.file.clone(),
8050 CodeUnitType::Field,
8051 owner.package_name().to_string(),
8052 short_name,
8053 Some(signature.clone()),
8054 false,
8055 fq,
8056 );
8057 if self.parsed.contains_declaration(&code_unit) {
8058 continue;
8059 }
8060 self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
8061 self.parsed.add_signature(code_unit, signature);
8062 }
8063 }
8064
8065 fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
8066 let Some(_directive) = node.child_by_field_name("directive") else {
8067 return;
8068 };
8069 if is_cpp_undef_directive(node, self.source) {
8070 update_object_macro_field_environment(
8071 node,
8072 self.source,
8073 &mut self.object_macro_fields,
8074 &mut self.ambiguous_object_macro_fields,
8075 );
8076 return;
8077 }
8078 let directly_in_field_list = node
8079 .parent()
8080 .is_some_and(|parent| parent.kind() == "field_declaration_list");
8081 if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
8082 self.visit_object_macro_fields(node, scope);
8083 }
8084 }
8085}
8086
8087fn object_macro_field_closure(
8095 environment: &HashMap<String, ObjectMacroReplacement>,
8096 name: &str,
8097) -> Vec<MacroReplacementField> {
8098 let mut fields = Vec::new();
8099 let mut visited = HashSet::default();
8100 let mut stack = vec![name.to_string()];
8101 while let Some(current) = stack.pop() {
8102 if !visited.insert(current.clone()) {
8103 continue;
8104 }
8105 let Some(replacement) = environment.get(¤t) else {
8106 continue;
8107 };
8108 fields.extend(replacement.fields.iter().cloned());
8109 stack.extend(replacement.nested.iter().rev().cloned());
8110 }
8111 fields
8112}
8113
8114fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
8120 crate::graph::syntax::object_macro_replacement_span(node, source)
8121 .and_then(|span| source.get(span))
8122 .map(crate::graph::syntax::object_macro_replacement)
8123 .unwrap_or_default()
8124}
8125
8126fn update_object_macro_field_environment(
8131 node: Node<'_>,
8132 source: &str,
8133 fields: &mut HashMap<String, ObjectMacroReplacement>,
8134 ambiguous: &mut HashSet<String>,
8135) -> bool {
8136 match node.kind() {
8137 "preproc_def" => {
8138 let Some(name) = extract_macro_name(node, source) else {
8139 return false;
8140 };
8141 let replacement = object_macro_replacement_of(node, source);
8142 if replacement.is_empty() || ambiguous.contains(&name) {
8143 fields.remove(&name);
8144 ambiguous.insert(name);
8145 } else if let Some(previous) = fields.get(&name) {
8146 if previous != &replacement {
8147 fields.remove(&name);
8148 ambiguous.insert(name);
8149 }
8150 } else {
8151 fields.insert(name, replacement);
8152 }
8153 true
8154 }
8155 "preproc_call" if is_cpp_undef_directive(node, source) => {
8156 if let Some(argument) = node.child_by_field_name("argument") {
8157 let name = node_text(argument, source).trim();
8158 fields.remove(name);
8159 if inside_preprocessor_conditional(node) {
8160 ambiguous.insert(name.to_string());
8161 } else {
8162 ambiguous.remove(name);
8163 }
8164 }
8165 true
8166 }
8167 _ => false,
8168 }
8169}
8170
8171fn object_macro_identifier_nodes<'tree>(
8172 node: Node<'tree>,
8173 source: &str,
8174 fields: &HashMap<String, ObjectMacroReplacement>,
8175) -> Vec<Node<'tree>> {
8176 let mut result = Vec::new();
8177 let mut stack = vec![node];
8178 while let Some(current) = stack.pop() {
8179 if matches!(
8180 current.kind(),
8181 "identifier" | "field_identifier" | "type_identifier"
8182 ) && fields.contains_key(node_text(current, source).trim())
8183 {
8184 result.push(current);
8185 }
8186 let mut cursor = current.walk();
8187 let children = current.children(&mut cursor).collect::<Vec<_>>();
8188 stack.extend(children.into_iter().rev());
8189 }
8190 result.sort_by_key(|node| node.start_byte());
8191 result
8192}
8193
8194fn object_macro_identifier_nodes_with_environment<'tree>(
8199 node: Node<'tree>,
8200 source: &str,
8201 fields: &mut HashMap<String, ObjectMacroReplacement>,
8202 ambiguous: &mut HashSet<String>,
8203) -> Vec<Node<'tree>> {
8204 let mut result = Vec::new();
8205 let mut stack = vec![node];
8206 while let Some(current) = stack.pop() {
8207 if update_object_macro_field_environment(current, source, fields, ambiguous) {
8208 continue;
8209 }
8210 if matches!(
8211 current.kind(),
8212 "identifier" | "field_identifier" | "type_identifier"
8213 ) && fields.contains_key(node_text(current, source).trim())
8214 {
8215 result.push(current);
8216 }
8217 let mut cursor = current.walk();
8218 let children = current.children(&mut cursor).collect::<Vec<_>>();
8219 stack.extend(children.into_iter().rev());
8220 }
8221 result.sort_by_key(|node| node.start_byte());
8222 result
8223}
8224
8225enum CppCollapsedMember {
8228 MacroFields {
8231 range: Range,
8232 fields: Vec<MacroReplacementField>,
8233 },
8234 Declarations {
8237 span: std::ops::Range<usize>,
8238 start_line: usize,
8239 },
8240}
8241
8242struct CppCollapsedAggregateHead<'tree> {
8257 key: Node<'tree>,
8258 name: Node<'tree>,
8259 opening: Node<'tree>,
8260 folded_members: Option<Node<'tree>>,
8264 width: usize,
8266}
8267
8268fn cpp_collapsed_aggregate_head<'tree>(
8270 children: &[Node<'tree>],
8271 index: usize,
8272 source: &str,
8273) -> Option<CppCollapsedAggregateHead<'tree>> {
8274 let key = *children.get(index)?;
8275 if matches!(key.kind(), "struct" | "class" | "union") {
8276 let name = *children.get(index + 1)?;
8277 let opening = *children.get(index + 2)?;
8278 if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
8279 return None;
8280 }
8281 return Some(CppCollapsedAggregateHead {
8282 key,
8283 name,
8284 opening,
8285 folded_members: None,
8286 width: 3,
8287 });
8288 }
8289 cpp_folded_aggregate_head(key, source)
8290}
8291
8292fn cpp_folded_aggregate_head<'tree>(
8295 node: Node<'tree>,
8296 source: &str,
8297) -> Option<CppCollapsedAggregateHead<'tree>> {
8298 if !matches!(node.kind(), "declaration" | "field_declaration") {
8299 return None;
8300 }
8301 let mut cursor = node.walk();
8302 let children = node.children(&mut cursor).collect::<Vec<_>>();
8303 let key_index = children.iter().position(|child| {
8304 child.is_error()
8305 && matches!(
8306 node_text(*child, source).trim(),
8307 "struct" | "class" | "union"
8308 )
8309 })?;
8310 let declarator = *children.get(key_index + 1)?;
8311 let (name, members) = if declarator.kind() == "init_declarator" {
8314 (
8315 declarator.child_by_field_name("declarator")?,
8316 declarator.child_by_field_name("value")?,
8317 )
8318 } else {
8319 (declarator, *children.get(key_index + 2)?)
8320 };
8321 if !matches!(
8322 name.kind(),
8323 "field_identifier" | "type_identifier" | "identifier"
8324 ) {
8325 return None;
8326 }
8327 if members.kind() != "initializer_list" {
8328 return None;
8329 }
8330 let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
8331 Some(CppCollapsedAggregateHead {
8332 key: children[key_index],
8333 name,
8334 opening,
8335 folded_members: Some(members),
8336 width: 1,
8337 })
8338}
8339
8340fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
8344 let mut cursor = members.walk();
8345 members
8346 .children(&mut cursor)
8347 .filter(|child| child.kind() == "}" && !child.is_missing())
8348 .last()
8349 .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
8350}
8351
8352fn cpp_collapsed_aggregate_members(
8356 macro_nodes: &[Node<'_>],
8357 region: std::ops::Range<usize>,
8358 region_start_line: usize,
8359 source: &str,
8360 environment: &HashMap<String, ObjectMacroReplacement>,
8361) -> Vec<CppCollapsedMember> {
8362 let mut members = macro_nodes
8363 .iter()
8364 .map(|macro_node| CppCollapsedMember::MacroFields {
8365 range: cpp_declaration_range(*macro_node),
8366 fields: object_macro_field_closure(
8367 environment,
8368 &normalize_cpp_whitespace(node_text(*macro_node, source)),
8369 ),
8370 })
8371 .collect::<Vec<_>>();
8372 let declarations_start = macro_nodes
8376 .last()
8377 .map_or(region.start, |macro_node| macro_node.end_byte());
8378 members.push(CppCollapsedMember::Declarations {
8379 span: declarations_start..region.end,
8380 start_line: region_start_line
8381 + cpp_line_breaks_between(source, region.start, declarations_start),
8382 });
8383 members
8384}
8385
8386fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
8389 source.get(from..to).map_or(0, |slice| {
8390 slice.bytes().filter(|byte| *byte == b'\n').count()
8391 })
8392}
8393
8394fn count_close_brace_nodes(node: Node<'_>) -> usize {
8400 let mut opened = 0usize;
8401 let mut closed = 0usize;
8402 let mut stack = vec![node];
8403 while let Some(current) = stack.pop() {
8404 if !current.is_missing() {
8405 match current.kind() {
8406 "{" => opened += 1,
8407 "}" => closed += 1,
8408 _ => {}
8409 }
8410 }
8411 let mut cursor = current.walk();
8412 stack.extend(current.children(&mut cursor));
8413 }
8414 closed.saturating_sub(opened)
8415}
8416
8417pub fn cpp_field_declaration_linkage<'tree>(
8422 declaration: Node<'tree>,
8423 source: &str,
8424 ancestry: &ParentIndex<'tree>,
8425) -> CppFieldLinkage {
8426 let mut current = ancestry.parent(declaration);
8427 let mut enclosed_by_class = false;
8428 while let Some(node) = current {
8429 if node.kind() == "namespace_definition"
8430 && node
8431 .child_by_field_name("name")
8432 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8433 {
8434 return CppFieldLinkage::Internal;
8435 }
8436 if matches!(
8437 node.kind(),
8438 "class_specifier" | "struct_specifier" | "union_specifier"
8439 ) && node
8440 .child_by_field_name("name")
8441 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8442 {
8443 return CppFieldLinkage::Internal;
8444 }
8445 if matches!(
8446 node.kind(),
8447 "class_specifier" | "struct_specifier" | "union_specifier"
8448 ) {
8449 enclosed_by_class = true;
8450 }
8451 if matches!(node.kind(), "function_definition" | "lambda_expression") {
8452 return CppFieldLinkage::Internal;
8453 }
8454 current = ancestry.parent(node);
8455 }
8456 if enclosed_by_class {
8457 return CppFieldLinkage::External;
8458 }
8459 let mut cursor = declaration.walk();
8460 let mut has_static = false;
8461 let mut has_extern = false;
8462 let mut has_inline = false;
8463 let mut has_const = false;
8464 let mut has_constexpr = false;
8465 for child in declaration.named_children(&mut cursor) {
8466 let text = normalize_cpp_whitespace(node_text(child, source));
8467 match (child.kind(), text.as_str()) {
8468 ("storage_class_specifier", "static") => has_static = true,
8469 ("storage_class_specifier", "extern") => has_extern = true,
8470 ("storage_class_specifier", "inline") => has_inline = true,
8471 ("storage_class_specifier", "constexpr") => has_constexpr = true,
8472 ("type_qualifier", "const") => has_const = true,
8473 ("type_qualifier", "constexpr") => has_constexpr = true,
8474 _ => {}
8475 }
8476 }
8477 if has_static {
8478 CppFieldLinkage::Internal
8479 } else if has_extern || has_inline {
8480 CppFieldLinkage::External
8481 } else if has_const || has_constexpr {
8482 CppFieldLinkage::InternalUnlessExternalPeer
8483 } else {
8484 CppFieldLinkage::External
8485 }
8486}
8487
8488fn cpp_declaration_range(node: Node<'_>) -> Range {
8489 Range {
8490 start_byte: node.start_byte(),
8491 end_byte: node.end_byte(),
8492 start_line: node.start_position().row + 1,
8493 end_line: node.end_position().row + 1,
8494 }
8495}
8496
8497fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
8501 let line_at = |byte: usize| {
8502 source.as_bytes()[..byte]
8503 .iter()
8504 .filter(|&&b| b == b'\n')
8505 .count()
8506 + 1
8507 };
8508 Range {
8509 start_byte,
8510 end_byte,
8511 start_line: line_at(start_byte),
8512 end_line: line_at(end_byte),
8513 }
8514}
8515
8516pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
8523 walk_named_tree_preorder(root, true, |node| {
8524 if node.kind() == "preproc_include" {
8525 let raw = normalize_cpp_whitespace(node_text(node, source));
8526 if !raw.is_empty() {
8527 parsed.imports.push(ImportInfo {
8528 raw_snippet: raw,
8529 is_wildcard: false,
8530 is_global: false,
8531 identifier: None,
8532 alias: None,
8533 path: None,
8534 binder_span: None,
8535 });
8536 }
8537 return WalkControl::SkipChildren;
8538 }
8539 WalkControl::Continue
8540 });
8541}
8542
8543pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
8544 let mut in_block_comment = false;
8545 for line in source.lines() {
8546 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
8547 let trimmed = stripped.trim();
8548 if !looks_like_quoted_include_line(trimmed) {
8549 continue;
8550 }
8551
8552 let raw = normalize_cpp_whitespace(trimmed);
8553 if parsed
8557 .imports
8558 .iter()
8559 .any(|import| import.raw_snippet == raw)
8560 {
8561 continue;
8562 }
8563
8564 parsed.imports.push(ImportInfo {
8565 raw_snippet: raw,
8566 is_wildcard: false,
8567 is_global: false,
8568 identifier: None,
8569 alias: None,
8570 path: None,
8571 binder_span: None,
8572 });
8573 }
8574}
8575
8576fn looks_like_quoted_include_line(line: &str) -> bool {
8577 let Some(rest) = line.trim_start().strip_prefix('#') else {
8578 return false;
8579 };
8580 let Some(rest) = rest.trim_start().strip_prefix("include") else {
8581 return false;
8582 };
8583 rest.trim_start().starts_with('"')
8584}
8585
8586fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
8587 let mut raw = Vec::new();
8588 let mut cursor = node.walk();
8589 for child in node.named_children(&mut cursor) {
8590 if child.kind() == "base_class_clause" {
8591 collect_cpp_base_nodes(child, source, &mut raw);
8592 }
8593 }
8594 raw
8595}
8596
8597fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
8598 walk_named_tree_preorder(node, false, |child| match child.kind() {
8599 "type_identifier" | "qualified_identifier" | "template_type" => {
8600 let text = normalize_cpp_whitespace(node_text(child, source));
8601 if !text.is_empty() {
8602 raw.push(text);
8603 }
8604 WalkControl::SkipChildren
8605 }
8606 _ => WalkControl::Continue,
8607 });
8608}
8609
8610fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
8611 let mut out = String::new();
8612 let chars: Vec<char> = line.chars().collect();
8613 let mut index = 0;
8614 let mut in_string = false;
8615 let mut in_char = false;
8616 let mut escape = false;
8617
8618 while index < chars.len() {
8619 let ch = chars[index];
8620 let next = chars.get(index + 1).copied();
8621
8622 if *in_block_comment {
8623 if ch == '*' && next == Some('/') {
8624 *in_block_comment = false;
8625 index += 2;
8626 } else {
8627 index += 1;
8628 }
8629 continue;
8630 }
8631
8632 if in_string {
8633 out.push(ch);
8634 if escape {
8635 escape = false;
8636 } else if ch == '\\' {
8637 escape = true;
8638 } else if ch == '"' {
8639 in_string = false;
8640 }
8641 index += 1;
8642 continue;
8643 }
8644
8645 if in_char {
8646 out.push(ch);
8647 if escape {
8648 escape = false;
8649 } else if ch == '\\' {
8650 escape = true;
8651 } else if ch == '\'' {
8652 in_char = false;
8653 }
8654 index += 1;
8655 continue;
8656 }
8657
8658 if ch == '/' && next == Some('/') {
8659 break;
8660 }
8661 if ch == '/' && next == Some('*') {
8662 *in_block_comment = true;
8663 index += 2;
8664 continue;
8665 }
8666 if ch == '"' {
8667 in_string = true;
8668 out.push(ch);
8669 index += 1;
8670 continue;
8671 }
8672 if ch == '\'' {
8673 in_char = true;
8674 out.push(ch);
8675 index += 1;
8676 continue;
8677 }
8678
8679 out.push(ch);
8680 index += 1;
8681 }
8682
8683 out
8684}
8685
8686#[derive(Clone)]
8687struct FunctionInfo {
8688 package_name: String,
8689 owner: Option<CppMemberOwner>,
8690 name: String,
8691 signature: String,
8692}
8693
8694#[derive(Clone)]
8701enum CppMemberOwner {
8702 Chain(Vec<String>),
8706 Unit(CodeUnit),
8709}
8710
8711impl CppMemberOwner {
8712 fn short_chain(&self) -> String {
8714 match self {
8715 Self::Chain(chain) => chain.join("$"),
8716 Self::Unit(parent) => parent.short_name().to_string(),
8717 }
8718 }
8719}
8720
8721enum DeclaratorKind<'a> {
8722 Function(Node<'a>),
8723 Variable(Node<'a>),
8724}
8725
8726impl FunctionInfo {
8727 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
8728 self.code_unit_with_synthetic(file, false)
8729 }
8730
8731 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
8732 let short_name = match &self.owner {
8733 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
8734 None => self.name.clone(),
8735 };
8736 let fq = match &self.owner {
8737 Some(CppMemberOwner::Chain(chain)) => {
8738 debug_assert!(
8739 !chain.is_empty(),
8740 "an empty owner chain is no owner; producers return None instead"
8741 );
8742 let mut fq = FqName::new();
8743 cpp_push_package(&mut fq, &self.package_name);
8744 let mut first = true;
8745 for component in chain {
8746 let kind = if first {
8747 SegmentKind::Type
8748 } else {
8749 SegmentKind::Nested
8750 };
8751 fq.push(cpp_segment(component, kind));
8752 first = false;
8753 }
8754 fq.push(cpp_segment(&self.name, SegmentKind::Member));
8755 fq
8756 }
8757 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
8758 .fq()
8759 .clone()
8760 .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
8761 Some(CppMemberOwner::Unit(_)) | None => {
8764 let mut fq = FqName::new();
8765 cpp_push_package(&mut fq, &self.package_name);
8766 fq.push(cpp_segment(&self.name, SegmentKind::Member));
8767 fq
8768 }
8769 };
8770 CodeUnit::with_signature_and_fq(
8771 file,
8772 CodeUnitType::Function,
8773 self.package_name.clone(),
8774 short_name,
8775 Some(self.signature.clone()),
8776 synthetic,
8777 fq,
8778 )
8779 }
8780}
8781
8782fn extract_function_info(
8783 declarator: Node<'_>,
8784 source: &str,
8785 scope: &ScopeInfo,
8786) -> Option<FunctionInfo> {
8787 let parameters_node = declarator.child_by_field_name("parameters")?;
8788 let declarator_name_node = declarator
8789 .child_by_field_name("declarator")
8790 .or_else(|| parameters_node.prev_named_sibling())?;
8791 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
8792}
8793
8794fn extract_function_info_from_name(
8795 declarator: Node<'_>,
8796 declarator_name_node: Node<'_>,
8797 source: &str,
8798 scope: &ScopeInfo,
8799) -> Option<FunctionInfo> {
8800 let parameters_node = declarator.child_by_field_name("parameters")?;
8801 let parameters_text = cpp_parameter_signature(parameters_node, source);
8802 let recovered_specialization_member = scope
8803 .recovered_specialization_member_scope
8804 .then(|| {
8805 let terminal = declarator_name_node
8806 .child_by_field_name("name")
8807 .unwrap_or(declarator_name_node);
8808 let name = canonical_cpp_qualified_component(terminal, source)?.name;
8809 let owner = scope.class_unit.as_ref()?;
8810 Some((
8811 Some(CppMemberOwner::Unit(owner.clone())),
8812 name,
8813 scope.package_name.clone(),
8814 ))
8815 })
8816 .flatten();
8817 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
8818 parts
8819 } else if let Some(parts) =
8820 split_structured_templated_cpp_name(declarator_name_node, source, scope)
8821 {
8822 parts
8823 } else {
8824 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
8825 declarator_name_node,
8826 source,
8827 )?);
8828 if raw_name.is_empty() {
8829 return None;
8830 }
8831 split_cpp_name(&raw_name, scope)
8832 };
8833 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
8834 let mut signature = if suffix.is_empty() {
8835 parameters_text
8836 } else {
8837 format!("{parameters_text} {suffix}")
8838 };
8839 if let Some(template_signature) = &scope.template_signature {
8840 signature = format!("{template_signature}{signature}");
8841 }
8842
8843 Some(FunctionInfo {
8844 package_name,
8845 owner,
8846 name,
8847 signature,
8848 })
8849}
8850
8851fn cpp_macro_displaced_callable_parts<'tree>(
8858 function_declarator: Node<'tree>,
8859 source: &str,
8860 ancestry: &ParentIndex<'tree>,
8861) -> Option<(Node<'tree>, Node<'tree>)> {
8862 let definition = ancestry.parent(function_declarator)?;
8863 if definition.kind() != "function_definition"
8864 || definition.child_by_field_name("declarator") != Some(function_declarator)
8865 || definition
8866 .child_by_field_name("body")
8867 .is_none_or(|body| body.kind() != "compound_statement")
8868 {
8869 return None;
8870 }
8871 let macro_type = definition.child_by_field_name("type")?;
8872 if macro_type.kind() != "type_identifier"
8873 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8874 {
8875 return None;
8876 }
8877
8878 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
8879 if apparent_return_type.kind() == "qualified_identifier"
8880 && let (Some(return_type), Some(callable_name)) = (
8881 apparent_return_type.child_by_field_name("scope"),
8882 apparent_return_type.child_by_field_name("name"),
8883 )
8884 && return_type.kind() == "template_type"
8885 && matches!(callable_name.kind(), "identifier" | "field_identifier")
8886 && (0..apparent_return_type.child_count())
8887 .filter_map(|index| apparent_return_type.child(index))
8888 .any(|child| child.kind() == "::" && child.is_missing())
8889 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
8890 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8891 {
8892 return Some((return_type, callable_name));
8893 }
8894 if !matches!(
8895 apparent_return_type.kind(),
8896 "identifier" | "field_identifier" | "type_identifier"
8897 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
8898 {
8899 return None;
8900 }
8901 let parameters = function_declarator.child_by_field_name("parameters")?;
8902 let mut cursor = function_declarator.walk();
8903 let between = function_declarator
8904 .named_children(&mut cursor)
8905 .filter(|child| child.kind() != "comment")
8906 .filter(|child| {
8907 child.start_byte() >= apparent_return_type.end_byte()
8908 && child.end_byte() <= parameters.start_byte()
8909 && !same_node(*child, apparent_return_type)
8910 && !same_node(*child, parameters)
8911 })
8912 .collect::<Vec<_>>();
8913 let [name_error] = between.as_slice() else {
8914 return None;
8915 };
8916 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
8917 return None;
8918 }
8919 let callable_name = name_error.named_child(0)?;
8920 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
8921 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8922 {
8923 return None;
8924 }
8925 Some((apparent_return_type, callable_name))
8926}
8927
8928fn cpp_declarator_identity_suffix(
8944 declarator: Node<'_>,
8945 parameters_node: Node<'_>,
8946 source: &str,
8947) -> String {
8948 let mut cursor = declarator.walk();
8949 let parts = declarator
8950 .named_children(&mut cursor)
8951 .filter(|child| child.start_byte() >= parameters_node.end_byte())
8952 .filter(|child| {
8953 matches!(
8954 child.kind(),
8955 "type_qualifier"
8956 | "ref_qualifier"
8957 | "noexcept"
8958 | "throw_specifier"
8959 | "trailing_return_type"
8960 | "requires_clause"
8961 )
8962 })
8963 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8964 .filter(|text| !text.is_empty())
8965 .collect::<Vec<_>>();
8966 normalize_cpp_qualifier_suffix(&parts.join(" "))
8967}
8968
8969pub(crate) fn cpp_callable_identity_suffix(
8976 function_declarator: Node<'_>,
8977 source: &str,
8978) -> Option<String> {
8979 let parameters_node = function_declarator.child_by_field_name("parameters")?;
8980 Some(cpp_declarator_identity_suffix(
8981 function_declarator,
8982 parameters_node,
8983 source,
8984 ))
8985}
8986
8987pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
8988 match classify_declarator(node)? {
8989 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
8990 DeclaratorKind::Variable(_) => None,
8991 }
8992}
8993
8994fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
8995 match node.kind() {
8996 "function_declarator" => {
8997 let inner = node
8998 .child_by_field_name("declarator")
8999 .or_else(|| node.child_by_field_name("name"))
9000 .or_else(|| last_named_child(node));
9001 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
9002 Some(DeclaratorKind::Variable(node))
9003 } else {
9004 Some(DeclaratorKind::Function(node))
9005 }
9006 }
9007 "init_declarator"
9008 | "pointer_declarator"
9009 | "reference_declarator"
9010 | "parenthesized_declarator"
9011 | "array_declarator"
9012 | "attributed_declarator"
9013 | "template_function" => node
9014 .child_by_field_name("declarator")
9015 .or_else(|| node.child_by_field_name("name"))
9016 .or_else(|| last_named_child(node))
9017 .and_then(classify_declarator),
9018 "identifier" | "field_identifier" | "qualified_identifier" => {
9019 Some(DeclaratorKind::Variable(node))
9020 }
9021 _ => node
9022 .child_by_field_name("declarator")
9023 .or_else(|| node.child_by_field_name("name"))
9024 .or_else(|| last_named_child(node))
9025 .and_then(classify_declarator),
9026 }
9027}
9028
9029fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
9030 matches!(
9031 node.kind(),
9032 "function_declarator"
9033 | "init_declarator"
9034 | "pointer_declarator"
9035 | "reference_declarator"
9036 | "parenthesized_declarator"
9037 | "array_declarator"
9038 | "attributed_declarator"
9039 | "template_function"
9040 | "identifier"
9041 | "field_identifier"
9042 | "qualified_identifier"
9043 )
9044}
9045
9046fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
9047 let class_like = first_class_like_child(node);
9048 let mut cursor = node.walk();
9049 node.named_children(&mut cursor).any(|child| {
9050 matches!(
9051 child.kind(),
9052 "init_declarator"
9053 | "pointer_declarator"
9054 | "reference_declarator"
9055 | "array_declarator"
9056 | "function_declarator"
9057 | "parenthesized_declarator"
9058 | "attributed_declarator"
9059 ) || matches!(
9060 child.kind(),
9061 "identifier" | "field_identifier" | "qualified_identifier"
9062 ) && class_like.is_none_or(|class_node| {
9063 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
9064 })
9065 })
9066}
9067
9068struct CppNamespaceForward {
9081 name: String,
9082 start_byte: usize,
9083 namespace_end_byte: usize,
9086 package_name: String,
9087}
9088
9089fn cpp_namespace_forward_entry<'tree>(
9095 node: Node<'tree>,
9096 source: &str,
9097 ancestry: &ParentIndex<'tree>,
9098) -> Option<CppNamespaceForward> {
9099 if !matches!(
9100 node.kind(),
9101 "class_specifier" | "struct_specifier" | "union_specifier"
9102 ) || cpp_body_node(node).is_some()
9103 {
9104 return None;
9105 }
9106 let parent = node.parent()?;
9107 if !(parent.kind() == "declaration_list"
9108 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
9109 {
9110 return None;
9111 }
9112 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
9113 if !namespace.has_error() {
9118 return None;
9119 }
9120 Some(CppNamespaceForward {
9121 name: class_like_name(node, source, ancestry)?,
9122 start_byte: node.start_byte(),
9123 namespace_end_byte: namespace.end_byte(),
9124 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
9125 })
9126}
9127
9128fn cpp_namespace_forward_matches_recovery(
9132 forward: &CppNamespaceForward,
9133 recovered_node: Node<'_>,
9134) -> bool {
9135 forward.start_byte < recovered_node.start_byte()
9136 && forward.namespace_end_byte < recovered_node.start_byte()
9137 && malformed_namespace_is_nearest_recovery_region(
9138 forward.namespace_end_byte,
9139 recovered_node,
9140 )
9141}
9142
9143#[derive(Debug, Default)]
9159pub struct CppRecoveryCapture {
9160 created: Vec<CodeUnit>,
9162 created_units: HashSet<CodeUnit>,
9164 removed_pre_existing: HashSet<CodeUnit>,
9166}
9167
9168#[derive(Debug, Default)]
9180pub struct CppFieldOwnerIndex {
9181 owners: HashMap<String, HashSet<String>>,
9183 ownerless_packages: HashSet<String>,
9185}
9186
9187impl CppFieldOwnerIndex {
9188 fn of<'unit>(
9191 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9192 file: &ProjectFile,
9193 ) -> Self {
9194 let mut index = Self::default();
9195 for declaration in declarations {
9196 index.record(declaration, file);
9197 }
9198 index
9199 }
9200
9201 fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
9202 if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
9203 return;
9204 }
9205 let short_name = code_unit.short_name();
9206 let package_name = code_unit.package_name();
9207 if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
9208 self.ownerless_packages.insert(package_name.to_string());
9209 }
9210 if !short_name.contains('.') {
9211 return;
9212 }
9213 if !self.owners.contains_key(package_name) {
9214 self.owners
9215 .insert(package_name.to_string(), HashSet::default());
9216 }
9217 let owners = self
9218 .owners
9219 .get_mut(package_name)
9220 .expect("the package entry was just ensured");
9221 for (offset, _) in short_name.match_indices('.') {
9222 let owner = &short_name[..offset];
9223 if !owners.contains(owner) {
9224 owners.insert(owner.to_string());
9225 }
9226 }
9227 }
9228
9229 fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
9232 if owner_short_name.is_empty() {
9233 self.ownerless_packages.contains(package_name)
9234 } else {
9235 self.owners
9236 .get(package_name)
9237 .is_some_and(|owners| owners.contains(owner_short_name))
9238 }
9239 }
9240}
9241
9242#[cfg(any(debug_assertions, test))]
9246fn cpp_declarations_hold_owned_fields<'unit>(
9247 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9248 file: &ProjectFile,
9249 package_name: &str,
9250 owner_short_name: &str,
9251) -> bool {
9252 let prefix = format!("{owner_short_name}.");
9253 declarations.into_iter().any(|unit| {
9254 unit.kind() == CodeUnitType::Field
9255 && unit.source() == file
9256 && unit.package_name() == package_name
9257 && if owner_short_name.is_empty() {
9258 !unit.short_name().contains(['.', '$'])
9262 } else {
9263 unit.short_name().starts_with(&prefix)
9264 }
9265 })
9266}
9267
9268#[derive(PartialEq, Eq, Hash)]
9276pub struct CppTreeIdentity {
9277 root_id: usize,
9278 start_byte: usize,
9279 end_byte: usize,
9280 kind_id: u16,
9281 child_count: usize,
9282}
9283
9284impl CppTreeIdentity {
9285 fn of(root: Node<'_>) -> Self {
9286 Self {
9287 root_id: root.id(),
9288 start_byte: root.start_byte(),
9289 end_byte: root.end_byte(),
9290 kind_id: root.kind_id(),
9291 child_count: root.child_count(),
9292 }
9293 }
9294}
9295
9296#[derive(Default)]
9310pub struct CppNamespaceForwardScan {
9311 scanned_through: usize,
9313 forwards: HashMap<String, Vec<CppNamespaceForward>>,
9314}
9315
9316impl CppNamespaceForwardScan {
9317 fn advance_to<'tree>(
9324 &mut self,
9325 root: Node<'tree>,
9326 cutoff: usize,
9327 source: &str,
9328 ancestry: &ParentIndex<'tree>,
9329 ) {
9330 if cutoff <= self.scanned_through {
9331 return;
9332 }
9333 let folded_through = self.scanned_through;
9334 let mut cursor = root.walk();
9335 let mut stack = vec![root];
9336 while let Some(current) = stack.pop() {
9337 if (folded_through..cutoff).contains(¤t.start_byte())
9338 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9339 {
9340 self.forwards
9341 .entry(forward.name.clone())
9342 .or_default()
9343 .push(forward);
9344 }
9345 for child in current.named_children(&mut cursor) {
9350 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
9351 stack.push(child);
9352 }
9353 }
9354 }
9355 self.scanned_through = cutoff;
9356 }
9357
9358 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
9362 let mut matching = self
9363 .forwards
9364 .get(name)
9365 .into_iter()
9366 .flatten()
9367 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
9368 let first = matching.next()?;
9369 matching
9370 .next()
9371 .is_none()
9372 .then(|| first.package_name.clone())
9373 }
9374}
9375
9376#[cfg(any(debug_assertions, test))]
9381fn unique_earlier_cpp_namespace_forward<'tree>(
9382 recovered_node: Node<'tree>,
9383 name: &str,
9384 source: &str,
9385 ancestry: &ParentIndex<'tree>,
9386) -> Option<String> {
9387 let mut root = recovered_node;
9388 while let Some(parent) = ancestry.parent(root) {
9389 root = parent;
9390 }
9391
9392 let mut candidates = Vec::new();
9393 let mut stack = vec![root];
9394 while let Some(current) = stack.pop() {
9395 if current.start_byte() < recovered_node.start_byte()
9396 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9397 && forward.name == name
9398 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
9399 {
9400 candidates.push(forward.package_name);
9401 }
9402
9403 let mut cursor = current.walk();
9404 for child in current.named_children(&mut cursor) {
9405 if child.start_byte() < recovered_node.start_byte() {
9406 stack.push(child);
9407 }
9408 }
9409 }
9410
9411 if candidates.len() == 1 {
9412 candidates.pop()
9413 } else {
9414 None
9415 }
9416}
9417
9418fn malformed_namespace_is_nearest_recovery_region(
9419 namespace_end_byte: usize,
9420 recovered_node: Node<'_>,
9421) -> bool {
9422 let mut root = recovered_node;
9423 while let Some(parent) = root.parent() {
9424 root = parent;
9425 }
9426 let mut cursor = root.walk();
9427 root.named_children(&mut cursor)
9428 .filter(|sibling| {
9429 namespace_end_byte <= sibling.start_byte()
9430 && sibling.end_byte() <= recovered_node.start_byte()
9431 })
9432 .all(is_malformed_namespace_recovery_trivia)
9433}
9434
9435fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
9436 matches!(node.kind(), "ERROR" | "comment")
9437 || node.kind().starts_with("preproc_")
9438 || node.kind() == "expression_statement" && node.named_child_count() == 0
9439}
9440
9441fn cpp_namespace_name_for_forward<'tree>(
9445 node: Node<'tree>,
9446 source: &str,
9447 ancestry: &ParentIndex<'tree>,
9448) -> Option<String> {
9449 cpp_namespace_definition_for_forward(node, ancestry)?;
9450 cpp_lexical_namespace_name(node, source, ancestry)
9451}
9452
9453fn cpp_namespace_definition_for_forward<'tree>(
9454 node: Node<'tree>,
9455 ancestry: &ParentIndex<'tree>,
9456) -> Option<Node<'tree>> {
9457 let declaration = ancestry.parent(node)?;
9458 let mut ancestor = ancestry.parent(declaration);
9459 while let Some(current) = ancestor {
9460 if matches!(
9461 current.kind(),
9462 "compound_statement"
9463 | "field_declaration_list"
9464 | "class_specifier"
9465 | "struct_specifier"
9466 | "union_specifier"
9467 | "function_definition"
9468 | "lambda_expression"
9469 ) {
9470 return None;
9471 }
9472 if current.kind() == "namespace_definition" {
9473 return Some(current);
9474 }
9475 ancestor = ancestry.parent(current);
9476 }
9477 None
9478}
9479
9480fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
9481 match node.kind() {
9482 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9483 "parenthesized_declarator" => node
9484 .child_by_field_name("declarator")
9485 .or_else(|| last_named_child(node))
9486 .is_some_and(is_pointer_wrapper_declarator),
9487 "template_function" => node
9488 .child_by_field_name("name")
9489 .is_some_and(is_function_pointer_like_inner_declarator),
9490 _ => false,
9491 }
9492}
9493
9494fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
9495 match node.kind() {
9496 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9497 "parenthesized_declarator" => node
9498 .child_by_field_name("declarator")
9499 .or_else(|| last_named_child(node))
9500 .is_some_and(is_pointer_wrapper_declarator),
9501 _ => false,
9502 }
9503}
9504
9505fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
9506 let cleaned = raw_name.trim_start_matches("template ").trim();
9507 let cleaned = cleaned.trim_start_matches("::");
9514 let parts: Vec<_> = cleaned
9523 .split("::")
9524 .filter(|component| !component.is_empty())
9525 .collect();
9526 if parts.is_empty() {
9527 return (None, cleaned.to_string(), scope.package_name.clone());
9528 }
9529 if parts.len() > 1 {
9530 let name = parts.last().unwrap_or(&cleaned).to_string();
9531 let owner_parts = &parts[..parts.len() - 1];
9532 if let Some(class_unit) = &scope.class_unit {
9533 return (
9536 Some(CppMemberOwner::Unit(class_unit.clone())),
9537 name,
9538 scope.package_name.clone(),
9539 );
9540 }
9541 if !scope.package_name.is_empty() {
9542 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
9557 let owner = (!nested.is_empty()).then(|| {
9558 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
9559 });
9560 return (owner, name, scope.package_name.clone());
9561 }
9562 let (owner, package_name) = if owner_parts.len() > 1 {
9564 (
9576 Some(CppMemberOwner::Chain(vec![
9577 owner_parts.last().unwrap_or(&"").to_string(),
9578 ])),
9579 owner_parts[..owner_parts.len() - 1].join("::"),
9580 )
9581 } else {
9582 (
9594 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
9595 cpp_using_directive_namespace_for_bare_owner(scope),
9596 )
9597 };
9598 return (owner, name, package_name);
9599 }
9600
9601 let package_name = scope.package_name.clone();
9602 let owner = scope
9603 .class_unit
9604 .as_ref()
9605 .map(|parent| CppMemberOwner::Unit(parent.clone()));
9606 (owner, cleaned.to_string(), package_name)
9607}
9608
9609fn strip_redundant_namespace_prefix<'a>(
9623 owner_parts: &'a [&'a str],
9624 package_name: &str,
9625) -> &'a [&'a str] {
9626 if package_name.is_empty() {
9627 return owner_parts;
9628 }
9629 let package_segments: Vec<&str> = package_name.split("::").collect();
9630 let max_prefix = owner_parts.len().min(package_segments.len());
9631 for prefix_len in (1..=max_prefix).rev() {
9632 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
9633 if &owner_parts[..prefix_len] == package_suffix {
9634 return &owner_parts[prefix_len..];
9635 }
9636 }
9637 owner_parts
9638}
9639
9640fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
9651 scope
9652 .visible_using_namespaces
9653 .iter()
9654 .min_by_key(|namespace| namespace.split("::").count())
9655 .cloned()
9656 .unwrap_or_default()
9657}
9658
9659struct CppQualifiedNameComponent {
9660 name: String,
9661 is_template_id: bool,
9662}
9663
9664fn qualified_class_name_chain(
9677 class_node: Node<'_>,
9678 source: &str,
9679 scope: &ScopeInfo,
9680) -> Option<Vec<String>> {
9681 if scope.package_name.is_empty() || scope.class_unit.is_some() {
9682 return None;
9683 }
9684 let name = class_node.child_by_field_name("name")?;
9685 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
9686 if explicitly_global
9687 || components.len() < 2
9688 || components.iter().any(|component| component.is_template_id)
9689 {
9690 return None;
9691 }
9692 let names = components
9693 .iter()
9694 .map(|component| component.name.as_str())
9695 .collect::<Vec<_>>();
9696 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
9697 if class_chain.is_empty() {
9698 return None;
9699 }
9700 Some(class_chain.iter().map(|name| name.to_string()).collect())
9701}
9702
9703fn structured_cpp_qualified_components(
9704 qualified_name: Node<'_>,
9705 source: &str,
9706) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
9707 if qualified_name.kind() != "qualified_identifier" {
9708 return None;
9709 }
9710
9711 let mut components = Vec::new();
9712 let mut current = qualified_name;
9713 let mut explicitly_global = false;
9714 loop {
9715 if current.kind() == "qualified_identifier" {
9716 if let Some(component) = current.child_by_field_name("scope") {
9717 components.push(canonical_cpp_qualified_component(component, source)?);
9718 } else if components.is_empty() {
9719 explicitly_global = true;
9720 } else {
9721 return None;
9722 }
9723 current = current.child_by_field_name("name")?;
9724 } else {
9725 components.push(canonical_cpp_qualified_component(current, source)?);
9726 break;
9727 }
9728 }
9729 Some((components, explicitly_global))
9730}
9731
9732fn split_structured_templated_cpp_name(
9733 declarator_name: Node<'_>,
9734 source: &str,
9735 scope: &ScopeInfo,
9736) -> Option<(Option<CppMemberOwner>, String, String)> {
9737 let (mut components, explicitly_global) =
9738 structured_cpp_qualified_components(declarator_name, source)?;
9739
9740 let terminal = components.pop()?;
9741 let owner_start = components
9742 .iter()
9743 .position(|component| component.is_template_id)?;
9744 let explicit_package = components[..owner_start]
9745 .iter()
9746 .map(|component| component.name.as_str())
9747 .collect::<Vec<_>>()
9748 .join("::");
9749 let explicit_package_is_empty = explicit_package.is_empty();
9750 let package_name = match (
9751 explicitly_global,
9752 scope.package_name.is_empty(),
9753 explicit_package_is_empty,
9754 ) {
9755 (true, _, _) => explicit_package,
9756 (false, _, true) => scope.package_name.clone(),
9757 (false, true, false) => explicit_package,
9758 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
9759 };
9760 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
9766 {
9767 cpp_using_directive_namespace_for_bare_owner(scope)
9768 } else {
9769 package_name
9770 };
9771 let owner_chain = components[owner_start..]
9772 .iter()
9773 .map(|component| component.name.clone())
9774 .collect::<Vec<_>>();
9775 if owner_chain.is_empty() || terminal.name.is_empty() {
9776 return None;
9777 }
9778
9779 Some((
9780 Some(CppMemberOwner::Chain(owner_chain)),
9781 terminal.name,
9782 package_name,
9783 ))
9784}
9785
9786fn canonical_cpp_qualified_component(
9787 mut component: Node<'_>,
9788 source: &str,
9789) -> Option<CppQualifiedNameComponent> {
9790 let mut is_template_id = false;
9791 loop {
9792 match component.kind() {
9793 "template_type" => {
9794 is_template_id = true;
9795 component = component.child_by_field_name("name")?;
9796 }
9797 "dependent_name" => component = component.named_child(0)?,
9798 "identifier"
9799 | "field_identifier"
9800 | "namespace_identifier"
9801 | "type_identifier"
9802 | "operator_name"
9803 | "destructor_name" => {
9804 let name = normalize_cpp_whitespace(node_text(component, source));
9805 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
9806 name,
9807 is_template_id,
9808 });
9809 }
9810 _ => component = component.child_by_field_name("name")?,
9811 }
9812 }
9813}
9814
9815fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
9816 if let Some(name) = macro_decorated_unqualified_name(node) {
9817 return extract_declarator_name(name, source);
9818 }
9819 match node.kind() {
9820 "identifier"
9821 | "field_identifier"
9822 | "type_identifier"
9823 | "operator_name"
9824 | "destructor_name"
9825 | "qualified_identifier" => node_text(node, source).to_string(),
9826 "function_declarator"
9827 | "pointer_declarator"
9828 | "reference_declarator"
9829 | "parenthesized_declarator"
9830 | "array_declarator"
9831 | "template_function" => node
9832 .child_by_field_name("declarator")
9833 .or_else(|| node.child_by_field_name("name"))
9834 .or_else(|| last_named_child(node))
9835 .map(|child| extract_declarator_name(child, source))
9836 .unwrap_or_else(|| node_text(node, source).to_string()),
9837 _ => node
9838 .child_by_field_name("name")
9839 .map(|child| extract_declarator_name(child, source))
9840 .unwrap_or_else(|| node_text(node, source).to_string()),
9841 }
9842}
9843
9844fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
9849 if let Some(name) = macro_decorated_unqualified_name(node) {
9850 return extract_callable_declarator_name(name, source);
9851 }
9852 match node.kind() {
9853 "identifier"
9854 | "field_identifier"
9855 | "type_identifier"
9856 | "operator_name"
9857 | "destructor_name"
9858 | "qualified_identifier" => Some(node_text(node, source).to_string()),
9859 "function_declarator"
9860 | "pointer_declarator"
9861 | "reference_declarator"
9862 | "parenthesized_declarator"
9863 | "array_declarator"
9864 | "template_function" => node
9865 .child_by_field_name("declarator")
9866 .or_else(|| node.child_by_field_name("name"))
9867 .and_then(|child| extract_callable_declarator_name(child, source)),
9868 _ => None,
9869 }
9870}
9871
9872fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
9873 match node.kind() {
9874 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
9875 let name = node_text(node, source).trim().to_string();
9876 (!name.is_empty()).then_some(name)
9877 }
9878 _ => node
9879 .child_by_field_name("declarator")
9880 .or_else(|| node.child_by_field_name("name"))
9881 .or_else(|| last_named_child(node))
9882 .and_then(|child| extract_variable_name(child, source)),
9883 }
9884}
9885
9886#[derive(Clone, Copy)]
9892pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
9893 pub(crate) name: Node<'tree>,
9894 pub(crate) declarator: Node<'tree>,
9895}
9896
9897impl RecoveredFunctionLikeFieldDeclarator<'_> {
9898 pub(crate) fn pointer_depth(self) -> i32 {
9899 let mut depth = 0;
9900 let mut current = self.declarator;
9901 while current.kind() != "function_declarator" {
9902 if current.kind() == "pointer_declarator" {
9903 depth += 1;
9904 }
9905 current = current
9906 .child_by_field_name("declarator")
9907 .expect("recovered field wrapper has an inner declarator");
9908 }
9909 depth
9910 }
9911}
9912
9913fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
9924 if node.kind() != "field_declaration" {
9925 return false;
9926 }
9927 let Some(type_node) = node.child_by_field_name("type") else {
9928 return false;
9929 };
9930 if !matches!(type_node.kind(), "identifier" | "type_identifier") {
9931 return false;
9932 }
9933 let Some(declarator) = node.child_by_field_name("declarator") else {
9934 return false;
9935 };
9936 if declarator.kind() != "parenthesized_declarator" {
9937 return false;
9938 }
9939 let mut invented_terminator = false;
9940 for child in children_iter(node) {
9941 if child.kind() != ";" {
9942 continue;
9943 }
9944 if !child.is_missing() {
9945 return false;
9946 }
9947 invented_terminator = true;
9948 }
9949 invented_terminator
9950}
9951
9952pub(crate) fn recovered_function_like_field_declarator<'tree>(
9953 node: Node<'tree>,
9954 source: &str,
9955) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
9956 if node.kind() != "field_declaration" {
9957 return None;
9958 }
9959 let outer_declarator = node.child_by_field_name("declarator")?;
9960 let mut declarator = outer_declarator;
9961 while matches!(
9962 declarator.kind(),
9963 "pointer_declarator"
9964 | "reference_declarator"
9965 | "array_declarator"
9966 | "parenthesized_declarator"
9967 ) {
9968 declarator = declarator.child_by_field_name("declarator")?;
9969 }
9970 if declarator.kind() != "function_declarator" {
9971 return None;
9972 }
9973 let macro_name = declarator.child_by_field_name("declarator")?;
9974 if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
9975 {
9976 return None;
9977 }
9978 let parameters = declarator.child_by_field_name("parameters")?;
9979 let mut cursor = parameters.walk();
9980 let mut arguments = parameters.named_children(&mut cursor);
9981 let parameter = arguments.next()?;
9982 if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
9983 return None;
9984 }
9985 let name = parameter.child_by_field_name("type").filter(|argument| {
9986 matches!(
9987 argument.kind(),
9988 "identifier" | "field_identifier" | "type_identifier"
9989 )
9990 })?;
9991 Some(RecoveredFunctionLikeFieldDeclarator {
9992 name,
9993 declarator: outer_declarator,
9994 })
9995}
9996
9997fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
9998 let count = node.named_child_count();
9999 if count == 0 {
10000 None
10001 } else {
10002 node.named_child(count - 1)
10003 }
10004}
10005
10006fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
10007 let name_node = node.child_by_field_name("name")?;
10008 let name = normalize_cpp_whitespace(node_text(name_node, source));
10009 (!name.is_empty()).then_some(name)
10010}
10011
10012fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
10013 if node.kind() != "declaration" {
10014 return Vec::new();
10015 }
10016 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
10017 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
10018 }) else {
10019 return Vec::new();
10020 };
10021 let Some(declarator) = node.child_by_field_name("declarator") else {
10022 return Vec::new();
10023 };
10024 if node_text(keyword, source) == "using"
10025 && (declarator.kind() != "init_declarator"
10026 || declarator.child_by_field_name("value").is_none())
10027 {
10028 return Vec::new();
10029 }
10030 if node_text(keyword, source) == "typedef"
10031 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
10032 {
10033 return vec![alias_name];
10034 }
10035 extract_typedef_declarator_name(declarator, source)
10036 .into_iter()
10037 .collect()
10038}
10039
10040fn recovered_typedef_error_alias_name(
10041 declaration: Node<'_>,
10042 declarator: Node<'_>,
10043 source: &str,
10044) -> Option<String> {
10045 if declarator.kind() != "qualified_identifier" {
10055 return None;
10056 }
10057 let mut cursor = declaration.walk();
10058 let mut errors = declaration
10059 .named_children(&mut cursor)
10060 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
10061 let error = errors.next()?;
10062 if errors.next().is_some() || error.named_child_count() != 1 {
10063 return None;
10064 }
10065 let name = error.named_child(0)?;
10066 if !matches!(
10067 name.kind(),
10068 "identifier" | "field_identifier" | "type_identifier"
10069 ) {
10070 return None;
10071 }
10072 let name = normalize_cpp_whitespace(node_text(name, source));
10073 (!name.is_empty()).then_some(name)
10074}
10075
10076fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
10077 if fragmented_parenthesized_typedef_type(node).is_some() {
10081 return Vec::new();
10082 }
10083 let has_function_like_macro_type = node
10084 .child_by_field_name("type")
10085 .filter(|type_node| type_node.kind() == "type_identifier")
10086 .is_some_and(|type_node| {
10087 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
10088 });
10089 let mut names = Vec::new();
10090 let mut cursor = node.walk();
10091 for declarator in node.children_by_field_name("declarator", &mut cursor) {
10092 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
10093 continue;
10094 }
10095 if let Some(name) = extract_typedef_declarator_name(declarator, source)
10096 && !names.contains(&name)
10097 {
10098 names.push(name);
10099 }
10100 }
10101 names
10102}
10103
10104struct RecoveredMacroTypedefAlias<'tree> {
10105 name: String,
10106 end_node: Node<'tree>,
10107}
10108
10109fn recovered_macro_typedef_alias<'tree>(
10113 node: Node<'tree>,
10114 source: &str,
10115) -> Option<RecoveredMacroTypedefAlias<'tree>> {
10116 let type_node = fragmented_parenthesized_typedef_type(node)?;
10117 if type_node.kind() != "type_identifier"
10118 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
10119 {
10120 return None;
10121 }
10122
10123 let end_node = node.next_named_sibling()?;
10124 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
10125 return None;
10126 }
10127 let name_node = end_node.named_child(0)?;
10128 if name_node.kind() != "identifier" {
10129 return None;
10130 }
10131 let has_terminator = (0..end_node.child_count()).any(|index| {
10132 end_node
10133 .child(index)
10134 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
10135 });
10136 if !has_terminator {
10137 return None;
10138 }
10139 let name = normalize_cpp_whitespace(node_text(name_node, source));
10140 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
10141}
10142
10143fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
10144 if node.kind() != "type_definition" {
10145 return None;
10146 }
10147 let mut declarator_cursor = node.walk();
10148 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
10149 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
10150 return None;
10151 }
10152 let has_missing_terminator = (0..node.child_count()).any(|index| {
10153 node.child(index)
10154 .is_some_and(|child| child.kind() == ";" && child.is_missing())
10155 });
10156 if !has_missing_terminator {
10157 return None;
10158 }
10159 node.child_by_field_name("type")
10160}
10161
10162fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
10163 match node.kind() {
10164 "identifier" | "field_identifier" | "type_identifier" => {
10165 let name = normalize_cpp_whitespace(node_text(node, source));
10166 (!name.is_empty()).then_some(name)
10167 }
10168 "qualified_identifier" => node
10169 .child_by_field_name("name")
10170 .and_then(|name| extract_typedef_declarator_name(name, source)),
10171 _ => node
10172 .child_by_field_name("declarator")
10173 .or_else(|| node.child_by_field_name("name"))
10174 .or_else(|| last_named_child(node))
10175 .and_then(|child| extract_typedef_declarator_name(child, source)),
10176 }
10177}
10178
10179fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
10180 let name = node
10181 .child_by_field_name("name")
10182 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10183 .or_else(|| {
10184 let mut cursor = node.walk();
10185 node.named_children(&mut cursor)
10186 .find(|child| {
10187 matches!(
10188 child.kind(),
10189 "identifier" | "field_identifier" | "type_identifier"
10190 )
10191 })
10192 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10193 })?;
10194 (!name.is_empty()).then_some(name)
10195}
10196
10197fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
10198 left.id() == right.id()
10199}
10200
10201fn render_cpp_type_signature(
10202 node: Node<'_>,
10203 source: &str,
10204 template_signature: Option<&str>,
10205) -> String {
10206 let text = normalize_cpp_whitespace(node_text(node, source));
10207 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
10208 let rendered = if head.ends_with(';') {
10209 head.to_string()
10210 } else {
10211 format!("{head} {{")
10212 };
10213 if let Some(template_signature) = template_signature {
10214 format!("template {template_signature} {rendered}")
10215 } else {
10216 rendered
10217 }
10218}
10219
10220fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
10221 if let Some(recovered) = recovered_pyobject_head_field(node, source)
10222 && recovered.declarator == declarator
10223 {
10224 let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
10225 let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
10226 return format!("{type_text} {declarator};");
10227 }
10228 if let Some(recovered) = recovered_function_like_field_declarator(node, source)
10229 && recovered.name == declarator
10230 {
10231 let type_text = node
10232 .child_by_field_name("type")
10233 .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
10234 .unwrap_or_default();
10235 let name = normalize_cpp_whitespace(node_text(recovered.name, source));
10236 let mut prefix = String::new();
10237 let mut suffix = String::new();
10238 let mut current = recovered.declarator;
10239 while current.kind() != "function_declarator" {
10240 match current.kind() {
10241 "pointer_declarator" => prefix.push('*'),
10242 "reference_declarator" => prefix.push('&'),
10243 "array_declarator" => {
10244 let size = current
10245 .child_by_field_name("size")
10246 .map(|size| normalize_cpp_whitespace(node_text(size, source)))
10247 .unwrap_or_default();
10248 suffix.push('[');
10249 suffix.push_str(&size);
10250 suffix.push(']');
10251 }
10252 "parenthesized_declarator" => {}
10253 _ => unreachable!("validated recovered field declarator wrapper"),
10254 }
10255 current = current
10256 .child_by_field_name("declarator")
10257 .expect("recovered field wrapper has an inner declarator");
10258 }
10259 let separator = if prefix.is_empty() { "" } else { " " };
10260 return format!("{type_text} {prefix}{separator}{name}{suffix};");
10261 }
10262 if let Some(signature) =
10263 render_recovered_macro_qualified_field_signature(node, declarator, source)
10264 {
10265 return signature;
10266 }
10267 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
10268 let prefix = cpp_declaration_prefix(node, source);
10269 let name = extract_variable_name(declarator, source).unwrap_or_default();
10270 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
10271 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
10272 || (prefix.ends_with('&') && raw_suffix == "&")
10273 {
10274 String::new()
10275 } else {
10276 raw_suffix
10277 };
10278
10279 let mut rendered = if suffix.is_empty() {
10280 format!("{prefix} {name}")
10281 } else if suffix.starts_with('*') || suffix.starts_with('&') {
10282 format!("{prefix}{suffix} {name}")
10283 } else if suffix.starts_with('[') || suffix.starts_with('(') {
10284 format!("{prefix} {name}{suffix}")
10285 } else {
10286 format!("{prefix} {suffix}{name}")
10287 };
10288 rendered = collapse_cpp_whitespace(&rendered);
10289
10290 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10291 format!("{rendered} = {initializer};")
10292 } else if declaration_text.ends_with(';') {
10293 format!("{rendered};")
10294 } else {
10295 rendered
10296 }
10297}
10298
10299fn render_recovered_macro_qualified_field_signature(
10300 node: Node<'_>,
10301 declarator: Node<'_>,
10302 source: &str,
10303) -> Option<String> {
10304 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
10305 if !recovered
10306 .iter()
10307 .any(|candidate| same_node(*candidate, declarator))
10308 {
10309 return None;
10310 }
10311 let pseudo_declarator = node.child_by_field_name("declarator")?;
10312 let mut cursor = node.walk();
10313 let clause = node
10314 .named_children(&mut cursor)
10315 .find(|child| child.kind() == "bitfield_clause")?;
10316 let mut cursor = clause.walk();
10317 let error = clause
10318 .named_children(&mut cursor)
10319 .find(|child| child.kind() == "ERROR")?;
10320 let qualified_type =
10321 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
10322 let prefix = cpp_declaration_prefix(node, source);
10323 let name = extract_variable_name(declarator, source)?;
10324 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
10325 let mut rendered = if suffix.is_empty() {
10326 format!("{prefix} {qualified_type} {name}")
10327 } else {
10328 format!("{prefix} {qualified_type} {suffix} {name}")
10329 };
10330 rendered = collapse_cpp_whitespace(&rendered);
10331
10332 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
10333 Some(format!(
10334 "{rendered} = {};",
10335 normalize_cpp_whitespace(node_text(initializer, source))
10336 ))
10337 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10338 Some(format!("{rendered} = {initializer};"))
10339 } else {
10340 Some(format!("{rendered};"))
10341 }
10342}
10343
10344fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
10345 match node.kind() {
10346 "pointer_expression" => {
10347 let operator = node
10348 .child_by_field_name("operator")
10349 .or_else(|| node.child(0))
10350 .map(|operator| node_text(operator, source))
10351 .unwrap_or("*");
10352 let argument = node
10353 .child_by_field_name("argument")
10354 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10355 .unwrap_or_default();
10356 format!("{operator}{argument}")
10357 }
10358 "unary_expression" => {
10359 let operator = node
10360 .child_by_field_name("operator")
10361 .or_else(|| node.child(0))
10362 .map(|operator| node_text(operator, source))
10363 .unwrap_or_default();
10364 let argument = node
10365 .child_by_field_name("argument")
10366 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10367 .unwrap_or_default();
10368 format!("{operator}{argument}")
10369 }
10370 "identifier" | "field_identifier" => String::new(),
10371 _ => cpp_declarator_suffix_without_name(node, source),
10372 }
10373}
10374
10375fn recovered_macro_qualified_field_initializer<'tree>(
10376 clause: Node<'tree>,
10377 declarator: Node<'tree>,
10378) -> Option<Node<'tree>> {
10379 let mut stack = vec![clause];
10380 while let Some(current) = stack.pop() {
10381 if current.kind() == "assignment_expression"
10382 && current
10383 .child_by_field_name("left")
10384 .is_some_and(|left| same_node(left, declarator))
10385 {
10386 return current.child_by_field_name("right");
10387 }
10388 let mut cursor = current.walk();
10389 stack.extend(current.named_children(&mut cursor));
10390 }
10391 None
10392}
10393
10394fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
10395 let text = node_text(node, source);
10396 let mut cursor = node.walk();
10397 let first_declarator = node.named_children(&mut cursor).find(|child| {
10398 matches!(
10399 child.kind(),
10400 "init_declarator"
10401 | "identifier"
10402 | "field_identifier"
10403 | "pointer_declarator"
10404 | "reference_declarator"
10405 | "array_declarator"
10406 | "function_declarator"
10407 )
10408 });
10409 let prefix = if let Some(first_declarator) = first_declarator {
10410 let end = first_declarator
10411 .start_byte()
10412 .saturating_sub(node.start_byte());
10413 let mut prefix = text.get(..end).unwrap_or(text).to_string();
10414 let declarator_suffix = match first_declarator.kind() {
10415 "init_declarator" => first_declarator
10416 .child_by_field_name("declarator")
10417 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
10418 .unwrap_or_default(),
10419 _ => cpp_declarator_suffix_without_name(first_declarator, source),
10420 };
10421 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
10422 prefix.push_str(&declarator_suffix);
10423 }
10424 return collapse_cpp_whitespace(&prefix)
10425 .trim_end_matches(',')
10426 .trim_end_matches(';')
10427 .trim()
10428 .to_string();
10429 } else {
10430 text
10431 };
10432 collapse_cpp_whitespace(prefix)
10433 .trim_end_matches(',')
10434 .trim_end_matches(';')
10435 .trim()
10436 .to_string()
10437}
10438
10439fn cpp_preserved_initializer(
10440 declaration_node: Node<'_>,
10441 declarator: Node<'_>,
10442 source: &str,
10443) -> Option<String> {
10444 let name = extract_variable_name(declarator, source)?;
10445 let mut cursor = declaration_node.walk();
10446 for child in declaration_node.named_children(&mut cursor) {
10447 if child.kind() != "init_declarator" {
10448 continue;
10449 }
10450 let Some(inner) = child.child_by_field_name("declarator") else {
10451 continue;
10452 };
10453 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
10454 continue;
10455 }
10456 let value = child.child_by_field_name("value")?;
10457 let kind = value.kind();
10458 if matches!(
10459 kind,
10460 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
10461 ) {
10462 return Some(normalize_cpp_whitespace(node_text(value, source)));
10463 }
10464 break;
10465 }
10466 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
10467 let pattern = format!(
10468 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
10469 regex::escape(&name)
10470 );
10471 Regex::new(&pattern)
10472 .ok()
10473 .and_then(|regex| regex.captures(&declaration_text))
10474 .and_then(|captures| captures.get(1))
10475 .map(|value| value.as_str().to_string())
10476}
10477
10478fn render_cpp_function_display_signature_from_node<'tree>(
10479 node: Node<'tree>,
10480 source: &str,
10481 template_signature: Option<&str>,
10482 has_body: bool,
10483 ancestry: &ParentIndex<'tree>,
10484) -> String {
10485 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
10486 let parent_text = node_text(root, source);
10487 let body_local_start = root
10488 .child_by_field_name("body")
10489 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
10490 .unwrap_or(parent_text.len());
10491 let display = parent_text
10492 .get(..body_local_start)
10493 .unwrap_or(parent_text)
10494 .trim()
10495 .trim();
10496 let display = if let Some(template_signature) = template_signature {
10497 if display.starts_with("template ") {
10498 display.to_string()
10499 } else {
10500 format!("template {template_signature} {display}")
10501 }
10502 } else {
10503 display.to_string()
10504 };
10505 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
10506 if has_body {
10507 format!("{display} {{...}}")
10508 } else {
10509 format!("{display};")
10510 }
10511}
10512
10513fn cpp_template_signature(
10514 template_node: Node<'_>,
10515 declaration_child: Node<'_>,
10516 source: &str,
10517) -> Option<String> {
10518 let text = source
10519 .get(template_node.start_byte()..declaration_child.start_byte())
10520 .unwrap_or("");
10521 let text = normalize_cpp_whitespace(text);
10522 let start = text.find('<')?;
10523 let end = text.rfind('>')?;
10524 if end < start {
10525 return None;
10526 }
10527 Some(text[start..=end].to_string())
10528}
10529
10530struct RecoveredFragmentedPartialSpecialization<'tree> {
10531 declaration_node: Node<'tree>,
10532 name: String,
10533 range: Range,
10534 prefix_members: Vec<Node<'tree>>,
10535 member_siblings: Vec<Node<'tree>>,
10536 following_declarations: Vec<Node<'tree>>,
10537}
10538
10539struct RecoveredFragmentedPreprocessorClass<'tree> {
10540 declaration_node: Node<'tree>,
10541 class_node: Node<'tree>,
10542 body: Node<'tree>,
10543 name: String,
10544 range: Range,
10545 tail_members: Vec<Node<'tree>>,
10546 member_siblings: Vec<Node<'tree>>,
10547}
10548
10549fn recover_fragmented_preprocessor_class<'tree>(
10558 template_node: Node<'tree>,
10559 source: &str,
10560 ancestry: &ParentIndex<'tree>,
10561) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
10562 let alternative = ancestry.parent(template_node)?;
10563 if alternative.kind() != "preproc_else" {
10564 return None;
10565 }
10566 let conditional = alternative.parent()?;
10567 if conditional.kind() != "preproc_if" {
10568 return None;
10569 }
10570 let declaration_node = template_node
10571 .named_children(&mut template_node.walk())
10572 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
10573 let class_node = declaration_node
10574 .named_children(&mut declaration_node.walk())
10575 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
10576 let body = cpp_body_node(class_node)?;
10577 if class_node.end_byte() >= declaration_node.end_byte() {
10578 return None;
10579 }
10580 let name = class_like_name(class_node, source, ancestry)?;
10581 let is_partial_specialization = class_node
10582 .child_by_field_name("name")
10583 .is_some_and(|class_name| class_name.kind() == "template_type");
10584 if is_partial_specialization {
10585 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
10586 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
10587 return None;
10588 }
10589 } else {
10590 if !class_has_displaced_preprocessor_terminator(class_node) {
10591 return None;
10592 }
10593 let matching_other_branch = conditional
10594 .named_children(&mut conditional.walk())
10595 .take_while(|child| !same_node(*child, alternative))
10596 .filter(|child| child.kind() == "template_declaration")
10597 .filter_map(first_class_like_child)
10598 .any(|candidate| {
10599 cpp_body_node(candidate).is_none()
10600 && class_like_name(candidate, source, ancestry).as_deref()
10601 == Some(name.as_str())
10602 });
10603 if !matching_other_branch {
10604 return None;
10605 }
10606 }
10607
10608 let mut tail_members = Vec::new();
10609 let mut saw_class = false;
10610 let mut declaration_cursor = declaration_node.walk();
10611 for child in declaration_node.named_children(&mut declaration_cursor) {
10612 if same_node(child, class_node) {
10613 saw_class = true;
10614 } else if saw_class {
10615 tail_members.push(child);
10616 }
10617 }
10618
10619 let mut member_siblings = Vec::new();
10620 let mut saw_template = false;
10621 let mut terminator = None;
10622 for index in 0..alternative.child_count() {
10623 let Some(child) = alternative.child(index) else {
10624 continue;
10625 };
10626 if same_node(child, template_node) {
10627 saw_template = true;
10628 continue;
10629 }
10630 if !saw_template {
10631 continue;
10632 }
10633 if displaced_fragmented_class_terminator(alternative, index) {
10634 terminator = alternative.child(index + 1);
10635 break;
10636 }
10637 if child.is_named() {
10638 member_siblings.push(child);
10639 }
10640 }
10641 let terminator = terminator?;
10642 Some(RecoveredFragmentedPreprocessorClass {
10643 declaration_node,
10644 class_node,
10645 body,
10646 name,
10647 range: Range {
10648 start_byte: class_node.start_byte(),
10649 end_byte: terminator.end_byte(),
10650 start_line: class_node.start_position().row + 1,
10651 end_line: terminator.end_position().row + 1,
10652 },
10653 tail_members,
10654 member_siblings,
10655 })
10656}
10657
10658fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
10659 (0..class_node.child_count()).any(|index| {
10660 class_node.child(index).is_some_and(|child| {
10661 child.kind() == "ERROR"
10662 && (0..child.child_count()).any(|error_index| {
10663 child
10664 .child(error_index)
10665 .is_some_and(|token| token.kind() == "#endif")
10666 })
10667 })
10668 })
10669}
10670
10671pub fn cpp_displaced_preprocessor_terminator<'tree>(
10680 conditional: Node<'tree>,
10681) -> Option<Node<'tree>> {
10682 if !conditional.has_error() {
10683 return None;
10684 }
10685 let has_concrete_direct_terminator = conditional
10686 .child_count()
10687 .checked_sub(1)
10688 .and_then(|index| conditional.child(index))
10689 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
10690 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
10691 return None;
10695 }
10696 let mut displaced = None;
10702 let mut conditional_depth = 0usize;
10703 let mut stack = children_iter(conditional)
10704 .map(|child| (child, false))
10705 .collect::<Vec<_>>();
10706 stack.reverse();
10707 while let Some((node, inside_error)) = stack.pop() {
10708 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
10709 continue;
10710 }
10711 if node != conditional
10712 && matches!(
10713 node.kind(),
10714 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
10715 )
10716 {
10717 continue;
10718 }
10719 let inside_error = inside_error || node.kind() == "ERROR";
10720 match node.kind() {
10721 "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
10722 conditional_depth += 1;
10723 }
10724 "#endif" if !node.is_missing() => {
10725 if conditional_depth == 0
10726 && inside_error
10727 && displaced
10728 .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
10729 {
10730 displaced = Some(node);
10731 }
10732 conditional_depth = conditional_depth.saturating_sub(1);
10733 }
10734 _ => {}
10735 }
10736 let first_pushed = stack.len();
10737 stack.extend(children_iter(node).map(|child| (child, inside_error)));
10738 stack[first_pushed..].reverse();
10739 }
10740 displaced
10741}
10742
10743#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10756pub struct CppDisplacedPreprocessorBoundary {
10757 pub end_byte: usize,
10758 pub end_line: usize,
10759}
10760
10761pub fn cpp_displaced_preprocessor_boundary(
10762 conditional: Node<'_>,
10763) -> Option<CppDisplacedPreprocessorBoundary> {
10764 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
10765 return Some(CppDisplacedPreprocessorBoundary {
10766 end_byte: terminator.end_byte(),
10767 end_line: terminator.end_position().row + 1,
10768 });
10769 }
10770 if let Some(declaration) = displaced_split_declaration(conditional) {
10771 return Some(CppDisplacedPreprocessorBoundary {
10772 end_byte: declaration.end_byte(),
10773 end_line: declaration.end_position().row + 1,
10774 });
10775 }
10776 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
10777 return Some(CppDisplacedPreprocessorBoundary {
10778 end_byte: terminator.end_byte(),
10779 end_line: terminator.end_position().row + 1,
10780 });
10781 }
10782 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
10783 return Some(CppDisplacedPreprocessorBoundary {
10784 end_byte: terminator.end_byte(),
10785 end_line: terminator.end_position().row + 1,
10786 });
10787 }
10788 None
10789}
10790
10791fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10797 if !conditional.has_error()
10798 || conditional.child_by_field_name("alternative").is_some()
10799 || conditional
10800 .child(conditional.child_count().saturating_sub(1))
10801 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
10802 {
10803 return None;
10804 }
10805 let mut recovered = None;
10806 for nested in named_children_iter(conditional) {
10807 if !matches!(
10808 nested.kind(),
10809 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
10810 ) || nested.child_by_field_name("alternative").is_some()
10811 {
10812 continue;
10813 }
10814 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
10815 continue;
10816 };
10817 if direct.kind() != "#endif" || direct.is_missing() {
10818 continue;
10819 }
10820 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
10821 continue;
10822 };
10823 if displaced.end_byte() >= direct.start_byte() {
10824 continue;
10825 }
10826 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
10827 recovered = Some(direct);
10828 }
10829 }
10830 recovered
10831}
10832
10833fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10834 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
10835 return None;
10836 }
10837 let mut cursor = conditional.walk();
10838 let declarations = conditional
10839 .named_children(&mut cursor)
10840 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
10841 .collect::<Vec<_>>();
10842 let declaration = *declarations.first()?;
10843 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
10844 return None;
10845 }
10846 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
10847 let mut terminator = None;
10848 let mut stack = (0..declaration.child_count())
10849 .filter_map(|index| declaration.child(index))
10850 .filter(|child| child.start_byte() < declarator_start)
10851 .map(|child| (child, false))
10852 .collect::<Vec<_>>();
10853 while let Some((node, inside_error)) = stack.pop() {
10854 let inside_error = inside_error || node.kind() == "ERROR";
10855 if inside_error && node.kind() == "#endif" && !node.is_missing() {
10856 terminator = Some(node);
10857 continue;
10858 }
10859 for index in 0..node.child_count() {
10860 if let Some(child) = node.child(index)
10861 && child.start_byte() < declarator_start
10862 {
10863 stack.push((child, inside_error));
10864 }
10865 }
10866 }
10867 terminator
10868}
10869
10870fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10871 if !conditional.has_error()
10872 || conditional.child_by_field_name("alternative").is_some()
10873 || conditional
10874 .prev_named_sibling()
10875 .filter(|sibling| {
10876 sibling.kind() == "ERROR"
10877 && sibling.child_count() == 1
10878 && sibling
10879 .child(0)
10880 .is_some_and(|child| child.kind() == "typedef")
10881 })
10882 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
10883 .is_none()
10884 {
10885 return None;
10886 }
10887 let mut cursor = conditional.walk();
10888 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
10889 let declaration_index = children
10890 .iter()
10891 .position(|child| child.kind() == "declaration" && child.has_error())?;
10892 let declaration = children[declaration_index];
10893 if !children
10894 .iter()
10895 .skip(declaration_index + 1)
10896 .any(|child| child.end_byte() > declaration.end_byte())
10897 {
10898 return None;
10899 }
10900 let declarator = declaration.child_by_field_name("declarator")?;
10901 let mut error_end = None;
10902 let mut names = Vec::new();
10903 let mut stack = vec![declarator];
10904 while let Some(node) = stack.pop() {
10905 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
10906 error_end =
10907 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
10908 continue;
10909 }
10910 if matches!(node.kind(), "identifier" | "type_identifier") {
10911 names.push(node.start_byte());
10912 }
10913 push_named_children_reversed(node, &mut stack);
10914 }
10915 let error_end = error_end?;
10916 names
10917 .into_iter()
10918 .any(|start| start >= error_end)
10919 .then_some(declaration)
10920}
10921
10922fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
10923 let Some(error) = parent.child(error_index) else {
10924 return false;
10925 };
10926 if error.kind() != "ERROR"
10927 || error.child_count() != 1
10928 || error.child(0).is_none_or(|child| child.kind() != "}")
10929 {
10930 return false;
10931 }
10932 let Some(semicolon) = parent.child(error_index + 1) else {
10933 return false;
10934 };
10935 semicolon.kind() == "expression_statement"
10936 && semicolon.child_count() == 1
10937 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
10938}
10939
10940fn displaced_macro_class_tail(
10946 declaration_node: Node<'_>,
10947 body: Node<'_>,
10948 source: &str,
10949) -> Option<DisplacedMacroClassTail> {
10950 if !matches!(
10951 declaration_node.kind(),
10952 "class_specifier" | "struct_specifier" | "union_specifier"
10953 ) || body.kind() != "field_declaration_list"
10954 {
10955 return None;
10956 }
10957
10958 let child_count = body.named_child_count();
10959 for index in 0..child_count {
10960 let child = body.named_child(index)?;
10961 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
10962 continue;
10963 };
10964 let split_index = index + 1;
10965 if split_index >= child_count {
10966 return None;
10967 }
10968 let mut cursor = body.walk();
10969 if !body
10970 .named_children(&mut cursor)
10971 .skip(split_index)
10972 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
10973 {
10974 return None;
10975 }
10976 return Some(DisplacedMacroClassTail {
10977 split_index,
10978 class_range: Range {
10979 start_byte: declaration_node.start_byte(),
10980 end_byte: terminator.end_byte(),
10981 start_line: declaration_node.start_position().row + 1,
10982 end_line: terminator.end_position().row + 1,
10983 },
10984 });
10985 }
10986 None
10987}
10988
10989fn displaced_macro_field_terminator<'tree>(
10990 field: Node<'tree>,
10991 source: &str,
10992) -> Option<Node<'tree>> {
10993 if field.kind() != "field_declaration" {
10994 return None;
10995 }
10996 let macro_type = field.child_by_field_name("type")?;
10997 if macro_type.kind() != "type_identifier"
10998 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10999 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
11000 {
11001 return None;
11002 }
11003 for index in 0..field.child_count() {
11004 let error = field.child(index)?;
11005 if error.kind() != "ERROR"
11006 || error.child_count() != 1
11007 || error.child(0).is_none_or(|child| child.kind() != "}")
11008 {
11009 continue;
11010 }
11011 let semicolon = field.child(index + 1)?;
11012 if semicolon.kind() == ";" {
11013 return Some(semicolon);
11014 }
11015 }
11016 None
11017}
11018
11019fn recover_fragmented_partial_specialization<'tree>(
11020 template_node: Node<'tree>,
11021 declaration_child: Node<'tree>,
11022 source: &str,
11023 ancestry: &ParentIndex<'tree>,
11024) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
11025 if declaration_child.kind() != "function_definition" {
11026 return None;
11027 }
11028 let class_node = declaration_child.child_by_field_name("type")?;
11029 if !matches!(
11030 class_node.kind(),
11031 "class_specifier" | "struct_specifier" | "union_specifier"
11032 ) || !class_node
11033 .child_by_field_name("name")
11034 .and_then(|name| direct_identifier_name(name, source))
11035 .is_some_and(|name| cpp_export_macro_token(&name))
11036 {
11037 return None;
11038 }
11039 let declarator = declaration_child.child_by_field_name("declarator")?;
11040 if declarator.kind() != "template_function" {
11041 return None;
11042 }
11043 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
11044 if metadata.specialization_arguments.is_empty() {
11045 return None;
11046 }
11047 let body = declaration_child.child_by_field_name("body")?;
11048 if body.kind() != "compound_statement" {
11049 return None;
11050 }
11051 let complete_prefix = body.named_child(0).filter(|first| {
11052 first.kind() == "labeled_statement"
11053 && first.has_error()
11054 && first
11055 .named_child(first.named_child_count().saturating_sub(1))
11056 .is_some_and(recovered_declaration_has_class_terminator)
11057 });
11058 let complete_body = complete_prefix.is_some();
11059 let mut prefix_members = Vec::new();
11060 if let Some(prefix) = complete_prefix {
11061 prefix_members.push(prefix);
11062 } else {
11063 let mut body_cursor = body.walk();
11064 for child in body.named_children(&mut body_cursor) {
11065 if !is_structurally_valid_fragmented_class_prefix_member(child) {
11066 break;
11067 }
11068 prefix_members.push(child);
11069 }
11070 }
11071 let containing_declarations = template_node.parent()?;
11072 if !matches!(
11073 containing_declarations.kind(),
11074 "declaration_list" | "compound_statement"
11075 ) {
11076 return None;
11077 }
11078 let mut member_siblings = Vec::new();
11079 let mut following_declarations = Vec::new();
11080 let terminator;
11081 if complete_body {
11082 terminator = complete_prefix?;
11083 let mut cursor = body.walk();
11084 let mut after_prefix = false;
11085 for child in body.named_children(&mut cursor) {
11086 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
11087 after_prefix = true;
11088 } else if after_prefix {
11089 following_declarations.push(child);
11090 }
11091 }
11092 } else {
11093 let mut found_template = false;
11094 let mut cursor = containing_declarations.walk();
11095 let mut class_terminator = None;
11096 for child in containing_declarations.children(&mut cursor) {
11097 if same_node(child, template_node) {
11098 found_template = true;
11099 continue;
11100 }
11101 if found_template && child.kind() == "}" {
11102 class_terminator = Some(child);
11103 break;
11104 }
11105 if found_template && child.kind() == "namespace_definition" {
11112 return None;
11113 }
11114 if found_template && child.is_named() {
11115 member_siblings.push(child);
11116 }
11117 }
11118 terminator = class_terminator?;
11119 }
11120 let name = format!(
11121 "{}<{}>",
11122 metadata.primary_name,
11123 metadata
11124 .specialization_arguments
11125 .iter()
11126 .map(|argument| argument.text.as_str())
11127 .collect::<Vec<_>>()
11128 .join(", ")
11129 );
11130 Some(RecoveredFragmentedPartialSpecialization {
11131 declaration_node: declaration_child,
11132 name,
11133 range: Range {
11134 start_byte: declaration_child.start_byte(),
11135 end_byte: terminator.end_byte(),
11136 start_line: declaration_child.start_position().row + 1,
11137 end_line: terminator.end_position().row + 1,
11138 },
11139 prefix_members,
11140 member_siblings,
11141 following_declarations,
11142 })
11143}
11144
11145pub fn is_recovered_fragmented_partial_specialization_container(
11152 node: Node<'_>,
11153 source: &str,
11154) -> bool {
11155 let Some(template) = node
11156 .parent()
11157 .filter(|parent| parent.kind() == "template_declaration")
11158 else {
11159 return false;
11160 };
11161 let mut root = template;
11162 while let Some(parent) = root.parent() {
11163 root = parent;
11164 }
11165 recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
11166 .is_some()
11167}
11168
11169fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
11170 if declaration.kind() != "declaration" {
11171 return false;
11172 }
11173 (0..declaration.child_count().saturating_sub(1)).any(|index| {
11178 let Some(error) = declaration.child(index) else {
11179 return false;
11180 };
11181 error.kind() == "ERROR"
11182 && error.child_count() == 1
11183 && error.child(0).is_some_and(|child| child.kind() == "}")
11184 && declaration
11185 .child(index + 1)
11186 .is_some_and(|child| child.kind() == ";")
11187 })
11188}
11189
11190fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
11191 if node.has_error() {
11192 return false;
11193 }
11194 match node.kind() {
11195 "declaration"
11196 | "field_declaration"
11197 | "alias_declaration"
11198 | "type_definition"
11199 | "static_assert_declaration" => true,
11200 "labeled_statement" => node
11201 .named_child(node.named_child_count().saturating_sub(1))
11202 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
11203 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
11204 matches!(
11205 child.kind(),
11206 "declaration"
11207 | "field_declaration"
11208 | "alias_declaration"
11209 | "type_definition"
11210 | "function_definition"
11211 )
11212 }),
11213 _ => false,
11214 }
11215}
11216
11217fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
11218 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
11219 .then(|| node.child_by_field_name("declarator"))
11220 .flatten()
11221 .and_then(|declarator| extract_variable_name(declarator, source))
11222}
11223
11224fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
11225 while let Some(parent) = node.parent() {
11226 if matches!(parent.kind(), "function_definition" | "lambda_expression") {
11227 return true;
11228 }
11229 node = parent;
11230 }
11231 false
11232}
11233
11234fn cpp_template_metadata<'tree>(
11235 template_node: Node<'tree>,
11236 declaration_child: Node<'tree>,
11237 source: &str,
11238 ancestry: &ParentIndex<'tree>,
11239) -> Option<CppTemplateMetadata> {
11240 let parameters_node = template_node.child_by_field_name("parameters")?;
11241 let name_node = cpp_templated_class_name_node(declaration_child)?;
11242 let primary_node = match name_node.kind() {
11243 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
11244 _ => name_node,
11245 };
11246 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
11247 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
11248 return None;
11249 }
11250
11251 let mut parameter_nodes = Vec::new();
11252 let mut parameter_names = Vec::new();
11253 let mut cursor = parameters_node.walk();
11254 for parameter in parameters_node.named_children(&mut cursor) {
11255 if !matches!(
11256 parameter.kind(),
11257 "type_parameter_declaration"
11258 | "optional_type_parameter_declaration"
11259 | "variadic_type_parameter_declaration"
11260 | "template_template_parameter_declaration"
11261 | "parameter_declaration"
11262 | "optional_parameter_declaration"
11263 | "variadic_parameter_declaration"
11264 ) {
11265 continue;
11266 }
11267 let index = parameter_nodes.len();
11268 let name = cpp_template_parameter_name(parameter, source)
11273 .unwrap_or_else(|| format!("<anonymous:{index}>"));
11274 parameter_names.push(name);
11275 parameter_nodes.push(parameter);
11276 }
11277 let parameters = parameter_nodes
11278 .into_iter()
11279 .zip(parameter_names.iter().cloned())
11280 .map(|(parameter, name)| CppTemplateParameterMetadata {
11281 name,
11282 kind: cpp_template_parameter_kind(parameter),
11283 variadic: matches!(
11284 parameter.kind(),
11285 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
11286 ),
11287 default: cpp_template_parameter_default_expression(
11288 parameter,
11289 source,
11290 ¶meter_names,
11291 ancestry,
11292 ),
11293 })
11294 .collect();
11295 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
11296 Vec::new()
11297 } else {
11298 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
11299 .unwrap_or_default()
11300 };
11301 let alias_target = (declaration_child.kind() == "alias_declaration")
11302 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
11303 .flatten();
11304 Some(CppTemplateMetadata {
11305 primary_name,
11306 primary_fq_name: String::new(),
11307 parameters,
11308 specialization_arguments,
11309 alias_target,
11310 })
11311}
11312
11313fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
11314 match node.kind() {
11315 "class_specifier" | "struct_specifier" | "union_specifier" => {
11316 node.child_by_field_name("name")
11317 }
11318 "function_definition" => {
11319 let declarator = node.child_by_field_name("declarator")?;
11320 if matches!(declarator.kind(), "identifier" | "template_function") {
11321 Some(declarator)
11322 } else {
11323 None
11324 }
11325 }
11326 "alias_declaration" => node.child_by_field_name("name"),
11327 _ => None,
11328 }
11329}
11330
11331fn cpp_template_alias_target<'tree>(
11332 alias: Node<'tree>,
11333 source: &str,
11334 parameter_names: &[String],
11335 ancestry: &ParentIndex<'tree>,
11336) -> Option<CppTemplateAliasTargetMetadata> {
11337 let mut type_node = alias.child_by_field_name("type")?;
11338 while type_node.kind() == "type_descriptor" {
11339 type_node = type_node.child_by_field_name("type")?;
11340 }
11341 let global = type_node.child_by_field_name("scope").is_none()
11342 && type_node.child(0).is_some_and(|child| child.kind() == "::");
11343 let mut components = Vec::new();
11344 cpp_template_target_components(type_node, source, &mut components)?;
11345 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
11346 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
11347 components,
11348 global,
11349 arguments,
11350 })
11351}
11352
11353fn cpp_template_target_components(
11354 node: Node<'_>,
11355 source: &str,
11356 out: &mut Vec<String>,
11357) -> Option<()> {
11358 match node.kind() {
11359 "identifier" | "namespace_identifier" | "type_identifier" => {
11360 out.push(node_text(node, source).to_string());
11361 Some(())
11362 }
11363 "template_type" => {
11364 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11365 }
11366 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11367 if let Some(scope) = node.child_by_field_name("scope") {
11368 cpp_template_target_components(scope, source, out)?;
11369 }
11370 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11371 }
11372 _ => None,
11373 }
11374}
11375
11376fn cpp_template_argument_expressions<'tree>(
11377 mut node: Node<'tree>,
11378 source: &str,
11379 parameter_names: &[String],
11380 ancestry: &ParentIndex<'tree>,
11381) -> Option<Vec<CppTemplateExpression>> {
11382 loop {
11383 match node.kind() {
11384 "template_type" | "template_function" => {
11385 let arguments = node.child_by_field_name("arguments")?;
11386 let mut cursor = arguments.walk();
11387 return Some(
11388 arguments
11389 .named_children(&mut cursor)
11390 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
11391 .map(|argument| {
11392 cpp_template_expression(argument, source, parameter_names, ancestry)
11393 })
11394 .collect(),
11395 );
11396 }
11397 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
11398 node = node
11399 .child_by_field_name("name")
11400 .or_else(|| node.child_by_field_name("type"))?;
11401 }
11402 _ => return None,
11403 }
11404 }
11405}
11406
11407fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
11408 let candidate = node
11409 .child_by_field_name("name")
11410 .or_else(|| node.child_by_field_name("declarator"))
11411 .or_else(|| {
11412 let mut cursor = node.walk();
11413 node.named_children(&mut cursor).find(|child| {
11414 matches!(
11415 child.kind(),
11416 "identifier" | "type_identifier" | "field_identifier"
11417 )
11418 })
11419 })?;
11420 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
11421 (!name.is_empty()).then_some(name)
11422}
11423
11424fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
11425 match node.kind() {
11426 "type_parameter_declaration"
11427 | "optional_type_parameter_declaration"
11428 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
11429 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
11430 _ => CppTemplateParameterKind::Value,
11431 }
11432}
11433
11434fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
11435 node.child_by_field_name("default_type")
11436 .or_else(|| node.child_by_field_name("default_value"))
11437}
11438
11439fn cpp_template_parameter_default_expression<'tree>(
11440 parameter: Node<'tree>,
11441 source: &str,
11442 parameter_names: &[String],
11443 ancestry: &ParentIndex<'tree>,
11444) -> Option<CppTemplateExpression> {
11445 let default = cpp_template_parameter_default(parameter)?;
11446 let base = cpp_template_expression(default, source, parameter_names, ancestry);
11447 let Some(pointer_error) = parameter.next_named_sibling() else {
11448 return Some(base);
11449 };
11450 let Some(pointer_declarator) =
11451 recovered_abstract_pointer_declarator_term(pointer_error, source)
11452 else {
11453 return Some(base);
11454 };
11455 Some(CppTemplateExpression {
11456 text: format!(
11457 "{}{}",
11458 base.text,
11459 normalize_cpp_whitespace(node_text(pointer_error, source))
11460 ),
11461 term: CppTemplateTerm::Node {
11462 kind: "type_descriptor".to_string(),
11463 children: vec![base.term, pointer_declarator],
11464 },
11465 })
11466}
11467
11468fn recovered_abstract_pointer_declarator_term(
11469 node: Node<'_>,
11470 source: &str,
11471) -> Option<CppTemplateTerm> {
11472 if node.kind() != "ERROR" || node.child_count() == 0 {
11473 return None;
11474 }
11475 let mut children = Vec::new();
11476 for index in 0..node.child_count() {
11477 let child = node.child(index)?;
11478 if child.kind() != "*" {
11479 return None;
11480 }
11481 children.push(CppTemplateTerm::Atom {
11482 kind: "*".to_string(),
11483 text: normalize_cpp_whitespace(node_text(child, source)),
11484 });
11485 }
11486 Some(CppTemplateTerm::Node {
11487 kind: "abstract_pointer_declarator".to_string(),
11488 children,
11489 })
11490}
11491
11492fn cpp_template_expression<'tree>(
11493 node: Node<'tree>,
11494 source: &str,
11495 parameter_names: &[String],
11496 ancestry: &ParentIndex<'tree>,
11497) -> CppTemplateExpression {
11498 let text = normalize_cpp_whitespace(node_text(node, source));
11499 CppTemplateExpression {
11500 text,
11501 term: cpp_template_term(node, source, parameter_names, ancestry),
11502 }
11503}
11504
11505pub fn cpp_template_term<'tree>(
11506 node: Node<'tree>,
11507 source: &str,
11508 parameter_names: &[String],
11509 ancestry: &ParentIndex<'tree>,
11510) -> CppTemplateTerm {
11511 enum Work<'tree> {
11512 Visit(Node<'tree>),
11513 Build { kind: String, child_count: usize },
11514 }
11515
11516 let mut work = vec![Work::Visit(node)];
11517 let mut terms = Vec::new();
11518 while let Some(next) = work.pop() {
11519 match next {
11520 Work::Visit(current) => {
11521 let text = normalize_cpp_whitespace(node_text(current, source));
11522 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
11523 terms.push(CppTemplateTerm::Parameter(text));
11524 continue;
11525 }
11526 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
11527 let mut cursor = current.walk();
11528 let named = current
11529 .named_children(&mut cursor)
11530 .filter(|child| !child.is_extra() && child.kind() != "comment")
11531 .collect::<Vec<_>>();
11532 if let [child] = named.as_slice() {
11533 work.push(Work::Visit(*child));
11534 continue;
11535 }
11536 }
11537 if current.child_count() == 0 {
11538 terms.push(CppTemplateTerm::Atom {
11539 kind: if matches!(
11540 current.kind(),
11541 "identifier"
11542 | "type_identifier"
11543 | "field_identifier"
11544 | "namespace_identifier"
11545 ) {
11546 "identifier".to_string()
11547 } else {
11548 current.kind().to_string()
11549 },
11550 text,
11551 });
11552 continue;
11553 }
11554 let children = (0..current.child_count())
11555 .filter_map(|index| current.child(index))
11556 .filter(|child| !child.is_extra() && child.kind() != "comment")
11557 .collect::<Vec<_>>();
11558 work.push(Work::Build {
11559 kind: current.kind().to_string(),
11560 child_count: children.len(),
11561 });
11562 work.extend(children.into_iter().rev().map(Work::Visit));
11563 }
11564 Work::Build { kind, child_count } => {
11565 let children = terms.split_off(terms.len() - child_count);
11566 terms.push(CppTemplateTerm::Node { kind, children });
11567 }
11568 }
11569 }
11570 terms.pop().expect("template term traversal emits one root")
11571}
11572
11573fn cpp_template_term_leaf_is_parameter<'tree>(
11574 node: Node<'tree>,
11575 text: &str,
11576 parameter_names: &[String],
11577 ancestry: &ParentIndex<'tree>,
11578) -> bool {
11579 if !parameter_names.iter().any(|parameter| parameter == text) {
11580 return false;
11581 }
11582 !ancestry.parent(node).is_some_and(|parent| {
11583 matches!(
11584 parent.kind(),
11585 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
11586 ) && parent.child_by_field_name("scope").is_some()
11587 && parent.child_by_field_name("name") == Some(node)
11588 })
11589}
11590
11591fn enclosing_cpp_declaration_node<'tree>(
11592 mut node: Node<'tree>,
11593 ancestry: &ParentIndex<'tree>,
11594) -> Option<Node<'tree>> {
11595 loop {
11596 match node.kind() {
11597 "declaration"
11598 | "function_declaration"
11599 | "field_declaration"
11600 | "function_definition" => return Some(node),
11601 _ => node = ancestry.parent(node)?,
11602 }
11603 }
11604}
11605
11606fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
11607 let mut params = Vec::new();
11608 let mut cursor = parameters_node.walk();
11609 for child in parameters_node.children(&mut cursor) {
11610 match child.kind() {
11611 "parameter_declaration" | "optional_parameter_declaration" => {
11612 params.push(cpp_parameter_type(child, source));
11613 }
11614 "variadic_parameter_declaration" => {
11615 params.push(cpp_parameter_type(child, source));
11616 }
11617 "variadic_parameter" | "..." => params.push("...".to_string()),
11618 _ => {}
11619 }
11620 }
11621
11622 if params.is_empty() {
11623 "()".to_string()
11624 } else {
11625 format!("({})", params.join(", "))
11626 }
11627}
11628
11629fn cpp_signature_metadata<'tree>(
11630 signature: String,
11631 function_declarator: Node<'tree>,
11632 source: &str,
11633 ancestry: &ParentIndex<'tree>,
11634) -> SignatureMetadata {
11635 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
11636 let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
11637 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
11638 let return_type_identity =
11639 cpp_callable_return_type_identity(function_declarator, source, ancestry);
11640 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
11641 return enrich(
11642 SignatureMetadata::new(signature, Vec::new())
11643 .with_return_type_text(return_type_text)
11644 .with_return_type_identity(return_type_identity),
11645 );
11646 };
11647 let callable_arity = cpp_callable_arity(parameters_node, source);
11648 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
11649 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
11650 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
11651 let Some(relative_start) = signature
11652 .get(search_from..)
11653 .and_then(|suffix| suffix.find(¶meter_text))
11654 else {
11655 return enrich(
11656 SignatureMetadata::new(signature, Vec::new())
11657 .with_callable_arity(callable_arity)
11658 .with_callable_parameter_types(callable_parameter_types)
11659 .with_return_type_text(return_type_text)
11660 .with_return_type_identity(return_type_identity),
11661 );
11662 };
11663 let parameters_start = search_from + relative_start;
11664 let parameters_end = parameters_start + parameter_text.len();
11665 let mut search_start = parameters_start;
11666 let parameters = cpp_parameter_label_nodes(parameters_node)
11667 .into_iter()
11668 .filter_map(|label_node| {
11669 let label = normalize_cpp_whitespace(node_text(label_node, source));
11670 if label.is_empty() || search_start > parameters_end {
11671 return None;
11672 }
11673 let haystack = signature.get(search_start..parameters_end)?;
11674 let relative_start = haystack.find(&label)?;
11675 let start_byte = search_start + relative_start;
11676 let end_byte = start_byte + label.len();
11677 search_start = end_byte;
11678 Some(ParameterMetadata::new(label, start_byte, end_byte))
11679 })
11680 .collect();
11681 enrich(
11682 SignatureMetadata::new(signature, parameters)
11683 .with_callable_arity(callable_arity)
11684 .with_callable_parameter_types(callable_parameter_types)
11685 .with_return_type_text(return_type_text)
11686 .with_return_type_identity(return_type_identity),
11687 )
11688}
11689
11690fn cpp_callable_is_structural_constructor<'tree>(
11691 function_declarator: Node<'tree>,
11692 source: &str,
11693 ancestry: &ParentIndex<'tree>,
11694) -> bool {
11695 let Some(name_node) = function_declarator
11696 .child_by_field_name("declarator")
11697 .or_else(|| function_declarator.child_by_field_name("name"))
11698 .or_else(|| last_named_child(function_declarator))
11699 else {
11700 return false;
11701 };
11702 let Some(callable_name) = direct_identifier_name(name_node, source) else {
11703 return false;
11704 };
11705
11706 let mut current = ancestry.parent(function_declarator);
11707 while let Some(ancestor) = current {
11708 let owner_name = match ancestor.kind() {
11709 "class_specifier" | "struct_specifier" | "union_specifier" => {
11710 class_like_name(ancestor, source, ancestry)
11711 }
11712 "ERROR" => malformed_class_error_owner_name(ancestor, source),
11713 _ => None,
11714 };
11715 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
11716 return true;
11717 }
11718 current = ancestry.parent(ancestor);
11719 }
11720 false
11721}
11722
11723fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
11733 if node.kind() != "ERROR" {
11734 return None;
11735 }
11736 let keyword = node.child(0)?;
11737 if !matches!(keyword.kind(), "class" | "struct" | "union") {
11738 return None;
11739 }
11740 let name_node = node.child(1)?;
11741 let name = direct_identifier_name(name_node, source)?;
11742 let has_body = (2..node.child_count())
11743 .filter_map(|index| node.child(index))
11744 .any(|child| child.kind() == "{");
11745 has_body.then_some(name)
11746}
11747
11748pub fn cpp_callable_declaration_return_type_identity<'tree>(
11754 callable: Node<'tree>,
11755 source: &str,
11756 ancestry: &ParentIndex<'tree>,
11757) -> Option<StructuredTypeIdentity> {
11758 let declarator = callable
11759 .child_by_field_name("declarator")
11760 .and_then(extract_function_declarator)?;
11761 cpp_callable_return_type_identity(declarator, source, ancestry)
11762}
11763
11764pub(crate) fn cpp_callable_return_type_identity<'tree>(
11765 function_declarator: Node<'tree>,
11766 source: &str,
11767 ancestry: &ParentIndex<'tree>,
11768) -> Option<StructuredTypeIdentity> {
11769 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
11770 return None;
11771 }
11772 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
11773 if let Some((return_type, _)) =
11774 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
11775 {
11776 return cpp_structured_type_identity(return_type, source, &lexical_scope);
11777 }
11778 let mut cursor = function_declarator.walk();
11779 if let Some(trailing) = function_declarator
11780 .named_children(&mut cursor)
11781 .find(|child| child.kind() == "trailing_return_type")
11782 && let Some(type_descriptor) = trailing.named_child(0)
11783 {
11784 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
11785 }
11786
11787 let mut current = function_declarator;
11788 let mut wrappers = Vec::new();
11789 while let Some(parent) = ancestry.parent(current) {
11790 if matches!(
11791 parent.kind(),
11792 "function_definition" | "declaration" | "field_declaration"
11793 ) {
11794 let type_node = parent.child_by_field_name("type")?;
11795 if cpp_export_macro_token(node_text(type_node, source))
11796 && (0..parent.named_child_count()).any(|index| {
11797 parent
11798 .named_child(index)
11799 .is_some_and(|child| child.kind() == "ERROR")
11800 })
11801 {
11802 return None;
11803 }
11804 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
11805 for wrapper in wrappers.into_iter().rev() {
11806 identity = cpp_wrap_structured_type(identity, wrapper)?;
11807 }
11808 return Some(identity);
11809 }
11810 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
11811 || (matches!(
11812 parent.kind(),
11813 "pointer_declarator"
11814 | "reference_declarator"
11815 | "array_declarator"
11816 | "parenthesized_declarator"
11817 ) && parent.named_child_count() == 1
11818 && parent.named_child(0) == Some(current));
11819 if !wraps_current_declarator {
11820 return None;
11821 }
11822 match parent.kind() {
11823 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
11824 "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
11825 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
11826 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
11827 _ => return None,
11828 }
11829 current = parent;
11830 }
11831 None
11832}
11833
11834fn cpp_structured_type_identity(
11835 node: Node<'_>,
11836 source: &str,
11837 lexical_scope: &[String],
11838) -> Option<StructuredTypeIdentity> {
11839 enum Work<'tree> {
11840 Visit(Node<'tree>),
11841 Wrap(CppStructuredTypeWrapper),
11842 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
11843 BuildGeneric { argument_count: usize },
11844 }
11845
11846 let mut work = vec![Work::Visit(node)];
11847 let mut values = Vec::new();
11848 let mut builder = StructuredTypeIdentityBuilder::default();
11849 while let Some(next) = work.pop() {
11850 match next {
11851 Work::Visit(current) => match current.kind() {
11852 "type_descriptor" => {
11853 let type_node = current
11854 .child_by_field_name("type")
11855 .or_else(|| current.named_child(0))?;
11856 let mut wrappers = Vec::new();
11857 let mut cursor = current.walk();
11858 for child in current.named_children(&mut cursor) {
11859 if child.id() != type_node.id() {
11860 wrappers.extend(cpp_structured_declarator_wrappers(child)?);
11861 }
11862 }
11863 work.push(Work::ApplyWrappers(wrappers));
11864 work.push(Work::Visit(type_node));
11865 }
11866 "pointer_declarator" | "abstract_pointer_declarator" => {
11867 let child = current
11868 .child_by_field_name("declarator")
11869 .or_else(|| current.named_child(0))?;
11870 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
11871 work.push(Work::Visit(child));
11872 }
11873 "reference_declarator" => {
11874 let child = current
11875 .child_by_field_name("declarator")
11876 .or_else(|| current.named_child(0))?;
11877 work.push(Work::Wrap(cpp_reference_wrapper(current)?));
11878 work.push(Work::Visit(child));
11879 }
11880 "array_declarator" | "abstract_array_declarator" => {
11881 let child = current
11882 .child_by_field_name("declarator")
11883 .or_else(|| current.named_child(0))?;
11884 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
11885 work.push(Work::Visit(child));
11886 }
11887 "template_type" => {
11888 let name_node = current.child_by_field_name("name")?;
11889 let arguments = current
11890 .child_by_field_name("arguments")
11891 .map(|arguments_node| {
11892 let mut cursor = arguments_node.walk();
11893 arguments_node
11894 .named_children(&mut cursor)
11895 .filter(|child| !child.is_extra() && child.kind() != "comment")
11896 .collect::<Vec<_>>()
11897 })
11898 .unwrap_or_default();
11899 work.push(Work::BuildGeneric {
11900 argument_count: arguments.len(),
11901 });
11902 work.extend(arguments.into_iter().rev().map(Work::Visit));
11903 work.push(Work::Visit(name_node));
11904 }
11905 "qualified_identifier"
11906 | "scoped_identifier"
11907 | "scoped_type_identifier"
11908 | "type_identifier"
11909 | "field_identifier"
11910 | "identifier"
11911 | "namespace_identifier"
11912 | "primitive_type" => {
11913 values.push(builder.named(cpp_structured_named_type(
11914 current,
11915 source,
11916 lexical_scope,
11917 )?)?);
11918 }
11919 _ => {
11920 let child = current.child_by_field_name("type").or_else(|| {
11921 (current.named_child_count() == 1)
11922 .then(|| current.named_child(0))
11923 .flatten()
11924 })?;
11925 work.push(Work::Visit(child));
11926 }
11927 },
11928 Work::Wrap(wrapper) => {
11929 let root = values.pop()?;
11930 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
11931 }
11932 Work::ApplyWrappers(wrappers) => {
11933 let mut root = values.pop()?;
11934 for wrapper in wrappers.into_iter().rev() {
11935 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
11936 }
11937 values.push(root);
11938 }
11939 Work::BuildGeneric { argument_count } => {
11940 let value_count = argument_count.checked_add(1)?;
11941 let start = values.len().checked_sub(value_count)?;
11942 let mut built = values.split_off(start);
11943 let base = built.remove(0);
11944 values.push(builder.generic(base, built)?);
11945 }
11946 }
11947 }
11948 (values.len() == 1)
11949 .then(|| values.pop())
11950 .flatten()
11951 .and_then(|root| builder.finish(root))
11952}
11953
11954fn cpp_structured_named_type(
11955 node: Node<'_>,
11956 source: &str,
11957 lexical_scope: &[String],
11958) -> Option<StructuredTypeName> {
11959 let path = cpp_structured_type_path(node, source)?;
11960 let absolute = node.child_by_field_name("scope").is_none()
11961 && node.child(0).is_some_and(|child| child.kind() == "::");
11962 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
11963}
11964
11965#[derive(Clone, Copy)]
11966enum CppStructuredTypeWrapper {
11967 Pointer,
11968 LvalueReference,
11969 RvalueReference,
11970 Array,
11971}
11972
11973fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
11974 let mut wrappers = Vec::new();
11975 let mut current = node;
11976 loop {
11977 match current.kind() {
11978 "pointer_declarator" | "abstract_pointer_declarator" => {
11979 wrappers.push(CppStructuredTypeWrapper::Pointer)
11980 }
11981 "reference_declarator" | "abstract_reference_declarator" => {
11982 wrappers.push(cpp_reference_wrapper(current)?);
11983 }
11984 "array_declarator" | "abstract_array_declarator" => {
11985 wrappers.push(CppStructuredTypeWrapper::Array)
11986 }
11987 "init_declarator" | "parenthesized_declarator" => {}
11994 _ => break,
11995 }
11996 let Some(child) = current
11997 .child_by_field_name("declarator")
11998 .or_else(|| current.named_child(0))
11999 else {
12000 break;
12001 };
12002 current = child;
12003 }
12004 Some(wrappers)
12005}
12006
12007fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
12008 node.children(&mut node.walk())
12009 .find_map(|child| match child.kind() {
12010 "&" => Some(CppStructuredTypeWrapper::LvalueReference),
12011 "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
12012 _ => None,
12013 })
12014}
12015
12016fn cpp_wrap_structured_type(
12017 identity: StructuredTypeIdentity,
12018 wrapper: CppStructuredTypeWrapper,
12019) -> Option<StructuredTypeIdentity> {
12020 match wrapper {
12021 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
12022 CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
12023 CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
12024 CppStructuredTypeWrapper::Array => identity.wrap_array(),
12025 }
12026}
12027
12028fn cpp_wrap_structured_type_node(
12029 builder: &mut StructuredTypeIdentityBuilder,
12030 inner: StructuredTypeNodeId,
12031 wrapper: CppStructuredTypeWrapper,
12032) -> Option<StructuredTypeNodeId> {
12033 match wrapper {
12034 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
12035 CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
12036 CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
12037 CppStructuredTypeWrapper::Array => builder.array(inner),
12038 }
12039}
12040
12041pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
12048 let mut path = Vec::new();
12049 let mut stack = vec![node];
12050 while let Some(current) = stack.pop() {
12051 match current.kind() {
12052 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
12053 let component = node_text(current, source).to_string();
12054 if component.is_empty() {
12055 return None;
12056 }
12057 path.push(component);
12058 }
12059 "template_type" | "dependent_type" => {
12060 stack.push(current.child_by_field_name("name")?);
12061 }
12062 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12063 stack.push(current.child_by_field_name("name")?);
12064 if let Some(scope) = current.child_by_field_name("scope") {
12065 stack.push(scope);
12066 }
12067 }
12068 _ => return None,
12069 }
12070 }
12071 (!path.is_empty()).then_some(path)
12072}
12073
12074fn cpp_callable_lexical_scope<'tree>(
12075 node: Node<'tree>,
12076 source: &str,
12077 ancestry: &ParentIndex<'tree>,
12078) -> Vec<String> {
12079 let mut groups = Vec::new();
12080 let mut current = ancestry.parent(node);
12081 while let Some(parent) = current {
12082 if matches!(
12083 parent.kind(),
12084 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
12085 ) && let Some(name_node) = parent.child_by_field_name("name")
12086 && let Some(components) = cpp_structured_type_path(name_node, source)
12087 && !components.is_empty()
12088 {
12089 groups.push(components);
12090 }
12091 current = ancestry.parent(parent);
12092 }
12093 groups.reverse();
12094 groups.into_iter().flatten().collect()
12095}
12096
12097fn cpp_callable_dispatch_extensibility<'tree>(
12098 function_declarator: Node<'tree>,
12099 ancestry: &ParentIndex<'tree>,
12100) -> DispatchExtensibility {
12101 let mut declaration = None;
12102 let mut current = Some(function_declarator);
12103 while let Some(node) = current {
12104 match node.kind() {
12105 "template_declaration"
12106 | "preproc_if"
12107 | "preproc_ifdef"
12108 | "preproc_else"
12109 | "preproc_elif"
12110 | "preproc_call"
12111 | "ERROR" => return DispatchExtensibility::Open,
12112 "declaration" | "field_declaration" | "function_definition" => {
12113 declaration.get_or_insert(node);
12114 }
12115 "translation_unit" => break,
12116 _ => {}
12117 }
12118 current = ancestry.parent(node);
12119 }
12120 let Some(declaration) = declaration else {
12121 return DispatchExtensibility::Open;
12122 };
12123
12124 let mut saw_virtual_boundary = false;
12125 let mut stack = vec![declaration];
12126 while let Some(node) = stack.pop() {
12127 match node.kind() {
12128 "compound_statement" | "field_declaration_list" => continue,
12129 "final" | "final_specifier" => return DispatchExtensibility::Closed,
12130 "virtual"
12131 | "override"
12132 | "virtual_specifier"
12133 | "pure_virtual_clause"
12134 | "template_parameter_list"
12135 | "template_method"
12136 | "template_function"
12137 | "ERROR" => saw_virtual_boundary = true,
12138 _ => {}
12139 }
12140 let mut cursor = node.walk();
12141 stack.extend(node.children(&mut cursor));
12142 }
12143
12144 if saw_virtual_boundary {
12145 DispatchExtensibility::Open
12146 } else {
12147 DispatchExtensibility::Closed
12148 }
12149}
12150
12151fn cpp_callable_linkage<'tree>(
12152 declaration: Node<'tree>,
12153 source: &str,
12154 ancestry: &ParentIndex<'tree>,
12155) -> CallableLinkage {
12156 let mut enclosed_by_class = false;
12157 let mut current = ancestry.parent(declaration);
12158 while let Some(node) = current {
12159 if node.kind() == "namespace_definition"
12160 && node
12161 .child_by_field_name("name")
12162 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12163 {
12164 return CallableLinkage::Internal;
12165 }
12166 if matches!(
12167 node.kind(),
12168 "class_specifier" | "struct_specifier" | "union_specifier"
12169 ) {
12170 if node
12171 .child_by_field_name("name")
12172 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12173 {
12174 return CallableLinkage::Internal;
12175 }
12176 enclosed_by_class = true;
12177 }
12178 if node.kind() == "lambda_expression"
12183 || node.kind() == "function_definition"
12184 && !is_recovered_exported_class_container(node, source)
12185 {
12186 return CallableLinkage::Internal;
12187 }
12188 current = ancestry.parent(node);
12189 }
12190
12191 if enclosed_by_class {
12192 return CallableLinkage::External;
12193 }
12194
12195 let mut cursor = declaration.walk();
12196 if declaration.named_children(&mut cursor).any(|child| {
12197 child.kind() == "storage_class_specifier"
12198 && normalize_cpp_whitespace(node_text(child, source)) == "static"
12199 }) {
12200 CallableLinkage::Internal
12201 } else {
12202 CallableLinkage::External
12203 }
12204}
12205
12206fn cpp_callable_return_type_text<'tree>(
12207 function_declarator: Node<'tree>,
12208 source: &str,
12209 ancestry: &ParentIndex<'tree>,
12210) -> Option<String> {
12211 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
12212 return None;
12213 }
12214 if let Some((return_type, _)) =
12215 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
12216 {
12217 let text = normalize_cpp_whitespace(node_text(return_type, source));
12218 return (!text.is_empty()).then_some(text);
12219 }
12220 let mut cursor = function_declarator.walk();
12221 if let Some(trailing) = function_declarator
12222 .named_children(&mut cursor)
12223 .find(|child| child.kind() == "trailing_return_type")
12224 && let Some(type_descriptor) = trailing.named_child(0)
12225 {
12226 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
12227 if !text.is_empty() {
12228 return Some(text);
12229 }
12230 }
12231
12232 let mut current = function_declarator;
12233 let mut indirection = String::new();
12234 while let Some(parent) = ancestry.parent(current) {
12235 if matches!(
12236 parent.kind(),
12237 "function_definition" | "declaration" | "field_declaration"
12238 ) {
12239 let type_node = parent.child_by_field_name("type")?;
12240 if cpp_export_macro_token(node_text(type_node, source))
12241 && (0..parent.named_child_count()).any(|index| {
12242 parent
12243 .named_child(index)
12244 .is_some_and(|child| child.kind() == "ERROR")
12245 })
12246 {
12247 return None;
12252 }
12253 let base = normalize_cpp_whitespace(node_text(type_node, source));
12254 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
12255 }
12256 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
12257 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
12258 && parent.named_child_count() == 1
12259 && parent.named_child(0) == Some(current));
12260 if wraps_current_declarator {
12261 match parent.kind() {
12262 "pointer_declarator" => indirection.push('*'),
12263 "reference_declarator" => {
12264 let reference = parent
12265 .children(&mut parent.walk())
12266 .find(|child| !child.is_named())
12267 .map(|child| node_text(child, source))
12268 .unwrap_or("&");
12269 indirection.push_str(reference);
12270 }
12271 "init_declarator" | "parenthesized_declarator" => {}
12272 _ => return None,
12273 }
12274 current = parent;
12275 continue;
12276 }
12277 return None;
12278 }
12279 None
12280}
12281
12282fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
12283 let mut required = 0;
12284 let mut total = 0;
12285 let mut repeated = false;
12286 let mut cursor = parameters_node.walk();
12287 for child in parameters_node.children(&mut cursor) {
12288 match child.kind() {
12289 "parameter_declaration" => {
12290 if cpp_parameter_is_explicit_object(child, source) {
12291 continue;
12292 }
12293 if child.child_by_field_name("declarator").is_none()
12294 && child
12295 .child_by_field_name("type")
12296 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
12297 {
12298 continue;
12299 }
12300 required += 1;
12301 total += 1;
12302 }
12303 "optional_parameter_declaration" => total += 1,
12304 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12305 repeated = true;
12306 }
12307 _ => {}
12308 }
12309 }
12310 CallableArity::new(required, total, repeated)
12311}
12312
12313fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
12314 parameter
12315 .child_by_field_name("type")
12316 .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
12317 .and_then(|type_node| type_node.child_by_field_name("constraint"))
12318 .is_some_and(|constraint| {
12319 constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
12320 })
12321}
12322
12323#[derive(Clone, Copy)]
12331enum CppParameterSlot<'tree> {
12332 Declared(Node<'tree>),
12333 Ellipsis,
12334}
12335
12336fn cpp_callable_parameter_slots<'tree>(
12337 parameters_node: Node<'tree>,
12338 source: &str,
12339) -> Vec<CppParameterSlot<'tree>> {
12340 let mut slots = Vec::new();
12341 let mut cursor = parameters_node.walk();
12342 for parameter in parameters_node.children(&mut cursor) {
12343 match parameter.kind() {
12344 "parameter_declaration" | "optional_parameter_declaration" => {
12345 if cpp_parameter_is_explicit_object(parameter, source)
12346 || (parameter.child_by_field_name("declarator").is_none()
12347 && parameter
12348 .child_by_field_name("type")
12349 .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
12350 {
12351 continue;
12352 }
12353 slots.push(CppParameterSlot::Declared(parameter));
12354 }
12355 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12356 slots.push(CppParameterSlot::Ellipsis);
12357 }
12358 _ => {}
12359 }
12360 }
12361 slots
12362}
12363
12364fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
12365 cpp_callable_parameter_slots(parameters_node, source)
12366 .into_iter()
12367 .map(|slot| match slot {
12368 CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
12369 CppParameterSlot::Ellipsis => "...".to_string(),
12370 })
12371 .collect()
12372}
12373
12374#[derive(Debug, Clone, PartialEq, Eq)]
12380pub enum CppParameterType {
12381 Structured(StructuredTypeIdentity),
12384 Ellipsis,
12386 Unstructured,
12389}
12390
12391pub fn cpp_callable_parameter_type_identities<'tree>(
12397 function_declarator: Node<'tree>,
12398 source: &str,
12399 ancestry: &ParentIndex<'tree>,
12400) -> Vec<CppParameterType> {
12401 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12402 return Vec::new();
12403 };
12404 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12405 cpp_callable_parameter_slots(parameters_node, source)
12406 .into_iter()
12407 .map(|slot| match slot {
12408 CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
12409 CppParameterSlot::Declared(parameter) => {
12410 cpp_parameter_type_identity(parameter, source, &lexical_scope)
12411 .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
12412 }
12413 })
12414 .collect()
12415}
12416
12417pub fn cpp_declaration_type_identity<'tree>(
12424 declaration: Node<'tree>,
12425 declarator: Node<'tree>,
12426 source: &str,
12427 ancestry: &ParentIndex<'tree>,
12428) -> Option<StructuredTypeIdentity> {
12429 let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
12430 cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
12431}
12432
12433fn cpp_parameter_type_identity(
12434 parameter: Node<'_>,
12435 source: &str,
12436 lexical_scope: &[String],
12437) -> Option<StructuredTypeIdentity> {
12438 cpp_declaration_type_identity_in_scope(
12439 parameter,
12440 cpp_parameter_declarator(parameter),
12441 source,
12442 lexical_scope,
12443 )
12444}
12445
12446fn cpp_declaration_type_identity_in_scope(
12447 declaration: Node<'_>,
12448 declarator: Option<Node<'_>>,
12449 source: &str,
12450 lexical_scope: &[String],
12451) -> Option<StructuredTypeIdentity> {
12452 let type_node = declaration.child_by_field_name("type")?;
12453 let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
12454 if let Some(declarator) = declarator {
12455 for wrapper in cpp_structured_declarator_wrappers(declarator)?
12456 .into_iter()
12457 .rev()
12458 {
12459 identity = cpp_wrap_structured_type(identity, wrapper)?;
12460 }
12461 }
12462 Some(identity)
12463}
12464
12465#[derive(Debug, Clone, PartialEq, Eq)]
12478pub enum CppComparableSlot {
12479 Shape(CppComparableParameter),
12481 Ellipsis,
12483 Unstructured,
12486}
12487
12488#[derive(Debug, Clone, PartialEq, Eq)]
12501pub struct CppComparableParameter {
12502 nodes: Vec<CppComparableNode>,
12503 root: usize,
12504}
12505
12506#[derive(Debug, Clone, PartialEq, Eq)]
12514pub enum CppComparableNode {
12515 Named {
12516 name: StructuredTypeName,
12517 primitive: bool,
12518 konst: bool,
12519 volatil: bool,
12520 },
12521 Pointer {
12522 inner: usize,
12523 konst: bool,
12524 volatil: bool,
12525 },
12526 Reference {
12527 inner: usize,
12528 },
12529 Array {
12530 inner: usize,
12531 },
12532 Generic {
12533 base: usize,
12534 arguments: Vec<usize>,
12535 },
12536}
12537
12538impl CppComparableParameter {
12539 pub fn root(&self) -> usize {
12540 self.root
12541 }
12542
12543 pub fn node(&self, index: usize) -> &CppComparableNode {
12544 &self.nodes[index]
12545 }
12546
12547 fn adjust_parameter_top_level(&mut self) {
12558 let root = self.root;
12559 match &mut self.nodes[root] {
12560 CppComparableNode::Named { konst, volatil, .. }
12561 | CppComparableNode::Pointer { konst, volatil, .. } => {
12562 *konst = false;
12563 *volatil = false;
12564 }
12565 CppComparableNode::Array { inner } => {
12566 let inner = *inner;
12567 self.nodes[root] = CppComparableNode::Pointer {
12568 inner,
12569 konst: false,
12570 volatil: false,
12571 };
12572 }
12573 CppComparableNode::Generic { base, .. } => {
12574 let base = *base;
12575 let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
12576 unreachable!("a comparable generic's base is always a named leaf");
12577 };
12578 *konst = false;
12579 *volatil = false;
12580 }
12581 CppComparableNode::Reference { .. } => {}
12582 }
12583 }
12584}
12585
12586pub fn cpp_comparable_parameter_shapes<'tree>(
12593 function_declarator: Node<'tree>,
12594 source: &str,
12595 ancestry: &ParentIndex<'tree>,
12596) -> Vec<CppComparableSlot> {
12597 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12598 return Vec::new();
12599 };
12600 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12601 cpp_callable_parameter_slots(parameters_node, source)
12602 .into_iter()
12603 .map(|slot| match slot {
12604 CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
12605 CppParameterSlot::Declared(parameter) => {
12606 cpp_comparable_parameter(parameter, source, &lexical_scope)
12607 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
12608 }
12609 })
12610 .collect()
12611}
12612
12613fn cpp_comparable_parameter(
12614 parameter: Node<'_>,
12615 source: &str,
12616 lexical_scope: &[String],
12617) -> Option<CppComparableParameter> {
12618 let type_node = parameter.child_by_field_name("type")?;
12619 let levels = match cpp_parameter_declarator(parameter) {
12620 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12621 None => Vec::new(),
12622 };
12623 let mut shape = cpp_comparable_type_shape(
12624 type_node,
12625 cpp_cv_qualifiers(parameter, source),
12626 levels,
12627 source,
12628 lexical_scope,
12629 )?;
12630 shape.adjust_parameter_top_level();
12631 Some(shape)
12632}
12633
12634fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
12645 let mut qualifiers = CppCvQualifiers::default();
12646 let mut cursor = node.walk();
12647 for child in node.named_children(&mut cursor) {
12648 if child.kind() != "type_qualifier" {
12649 continue;
12650 }
12651 match node_text(child, source) {
12652 "const" => qualifiers.konst = true,
12653 "volatile" => qualifiers.volatil = true,
12654 _ => {}
12655 }
12656 }
12657 qualifiers
12658}
12659
12660#[derive(Clone, Copy, Default)]
12661struct CppCvQualifiers {
12662 konst: bool,
12663 volatil: bool,
12664}
12665
12666impl CppCvQualifiers {
12667 fn union(self, other: Self) -> Self {
12668 Self {
12669 konst: self.konst || other.konst,
12670 volatil: self.volatil || other.volatil,
12671 }
12672 }
12673}
12674
12675#[derive(Clone, Copy)]
12677enum CppComparableLevel {
12678 Pointer { konst: bool, volatil: bool },
12679 Reference,
12680 Array,
12681}
12682
12683fn cpp_comparable_declarator_levels(
12696 declarator: Node<'_>,
12697 source: &str,
12698) -> Option<Vec<CppComparableLevel>> {
12699 let mut levels = Vec::new();
12700 let mut current = declarator;
12701 loop {
12702 match current.kind() {
12703 "pointer_declarator" | "abstract_pointer_declarator" => {
12704 let qualifiers = cpp_cv_qualifiers(current, source);
12705 levels.push(CppComparableLevel::Pointer {
12706 konst: qualifiers.konst,
12707 volatil: qualifiers.volatil,
12708 });
12709 }
12710 "reference_declarator" | "abstract_reference_declarator" => {
12711 levels.push(CppComparableLevel::Reference);
12712 }
12713 "array_declarator" | "abstract_array_declarator" => {
12714 levels.push(CppComparableLevel::Array);
12715 }
12716 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
12717 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
12718 _ => return None,
12719 }
12720 let Some(next) = cpp_nested_declarator(current) else {
12721 return Some(levels);
12722 };
12723 current = next;
12724 }
12725}
12726
12727fn cpp_comparable_type_shape(
12734 type_node: Node<'_>,
12735 qualifiers: CppCvQualifiers,
12736 levels: Vec<CppComparableLevel>,
12737 source: &str,
12738 lexical_scope: &[String],
12739) -> Option<CppComparableParameter> {
12740 enum Work<'tree> {
12741 Visit {
12742 node: Node<'tree>,
12743 qualifiers: CppCvQualifiers,
12744 },
12745 ApplyLevels(Vec<CppComparableLevel>),
12746 BuildGeneric {
12747 argument_count: usize,
12748 },
12749 }
12750
12751 let mut nodes: Vec<CppComparableNode> = Vec::new();
12752 let mut values: Vec<usize> = Vec::new();
12753 let mut work = vec![
12754 Work::ApplyLevels(levels),
12755 Work::Visit {
12756 node: type_node,
12757 qualifiers,
12758 },
12759 ];
12760 while let Some(next) = work.pop() {
12761 match next {
12762 Work::Visit { node, qualifiers } => match node.kind() {
12763 "type_descriptor" => {
12764 let inner_type = node
12765 .child_by_field_name("type")
12766 .or_else(|| node.named_child(0))?;
12767 let mut cursor = node.walk();
12768 let declarator = node.child_by_field_name("declarator").or_else(|| {
12769 node.named_children(&mut cursor).find(|child| {
12770 child.id() != inner_type.id() && child.kind() != "type_qualifier"
12771 })
12772 });
12773 let levels = match declarator {
12774 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12775 None => Vec::new(),
12776 };
12777 work.push(Work::ApplyLevels(levels));
12778 work.push(Work::Visit {
12779 node: inner_type,
12780 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
12781 });
12782 }
12783 "sized_type_specifier" => {
12784 let name = StructuredTypeName::new(
12789 vec![normalize_cpp_whitespace(node_text(node, source))],
12790 lexical_scope.to_vec(),
12791 false,
12792 )?;
12793 values.push(cpp_push_comparable_node(
12794 &mut nodes,
12795 CppComparableNode::Named {
12796 name,
12797 primitive: true,
12798 konst: qualifiers.konst,
12799 volatil: qualifiers.volatil,
12800 },
12801 ));
12802 }
12803 "qualified_identifier"
12804 | "scoped_identifier"
12805 | "scoped_type_identifier"
12806 | "type_identifier"
12807 | "field_identifier"
12808 | "identifier"
12809 | "namespace_identifier"
12810 | "primitive_type"
12811 | "template_type" => {
12812 let name = cpp_structured_named_type(node, source, lexical_scope)?;
12813 values.push(cpp_push_comparable_node(
12814 &mut nodes,
12815 CppComparableNode::Named {
12816 name,
12817 primitive: node.kind() == "primitive_type",
12818 konst: qualifiers.konst,
12819 volatil: qualifiers.volatil,
12820 },
12821 ));
12822 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
12823 let mut cursor = arguments_node.walk();
12824 let arguments = arguments_node
12825 .named_children(&mut cursor)
12826 .filter(|child| !child.is_extra() && child.kind() != "comment")
12827 .collect::<Vec<_>>();
12828 work.push(Work::BuildGeneric {
12829 argument_count: arguments.len(),
12830 });
12831 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
12832 node: argument,
12833 qualifiers: CppCvQualifiers::default(),
12834 }));
12835 }
12836 }
12837 _ => {
12838 let inner = node.child_by_field_name("type").or_else(|| {
12839 (node.named_child_count() == 1)
12840 .then(|| node.named_child(0))
12841 .flatten()
12842 })?;
12843 work.push(Work::Visit {
12844 node: inner,
12845 qualifiers,
12846 });
12847 }
12848 },
12849 Work::ApplyLevels(levels) => {
12850 let mut root = values.pop()?;
12851 for level in levels {
12852 let node = match level {
12853 CppComparableLevel::Pointer { konst, volatil } => {
12854 CppComparableNode::Pointer {
12855 inner: root,
12856 konst,
12857 volatil,
12858 }
12859 }
12860 CppComparableLevel::Reference => {
12861 CppComparableNode::Reference { inner: root }
12862 }
12863 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
12864 };
12865 root = cpp_push_comparable_node(&mut nodes, node);
12866 }
12867 values.push(root);
12868 }
12869 Work::BuildGeneric { argument_count } => {
12870 let value_count = argument_count.checked_add(1)?;
12871 let start = values.len().checked_sub(value_count)?;
12872 let mut built = values.split_off(start);
12873 let base = built.remove(0);
12874 values.push(cpp_push_comparable_node(
12875 &mut nodes,
12876 CppComparableNode::Generic {
12877 base,
12878 arguments: built,
12879 },
12880 ));
12881 }
12882 }
12883 }
12884 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
12885 debug_assert_eq!(
12886 root,
12887 nodes.len().saturating_sub(1),
12888 "comparable nodes are appended in post-order, so the root is the last one"
12889 );
12890 Some(CppComparableParameter { nodes, root })
12891}
12892
12893fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
12894 nodes.push(node);
12895 nodes.len() - 1
12896}
12897
12898fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
12904 let mut current = node;
12905 loop {
12906 match current.kind() {
12907 "template_type" => return current.child_by_field_name("arguments"),
12908 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12909 current = current.child_by_field_name("name")?;
12910 }
12911 _ => return None,
12912 }
12913 }
12914}
12915
12916pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
12922 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
12923 loop {
12924 if matches!(
12925 current.kind(),
12926 "declaration" | "field_declaration" | "function_definition"
12927 ) && let Some(declarator) = current
12928 .child_by_field_name("declarator")
12929 .and_then(extract_function_declarator)
12930 {
12931 return Some(declarator);
12932 }
12933 current = current.parent()?;
12934 }
12935}
12936
12937fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
12938 let mut labels = Vec::new();
12939 let mut cursor = parameters_node.walk();
12940 for child in parameters_node.children(&mut cursor) {
12941 match child.kind() {
12942 "parameter_declaration" | "optional_parameter_declaration" => {
12943 if let Some(name_node) = child
12944 .child_by_field_name("declarator")
12945 .and_then(cpp_declarator_label_node)
12946 {
12947 labels.push(name_node);
12948 } else {
12949 labels.push(child);
12950 }
12951 }
12952 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12953 labels.push(child);
12954 }
12955 _ => {}
12956 }
12957 }
12958 labels
12959}
12960
12961fn cpp_signature_search_start<'tree>(
12962 signature: &str,
12963 function_declarator: Node<'tree>,
12964 source: &str,
12965 ancestry: &ParentIndex<'tree>,
12966) -> usize {
12967 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
12968 return 0;
12969 };
12970 let raw = node_text(enclosing, source);
12971 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
12972 let offset = function_declarator
12973 .start_byte()
12974 .saturating_sub(enclosing.start_byte())
12975 .saturating_sub(leading_trim_bytes);
12976 offset.min(signature.len())
12977}
12978
12979fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
12980 match node.kind() {
12981 "identifier" | "field_identifier" => Some(node),
12982 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
12983 .child_by_field_name("declarator")
12984 .or_else(|| last_named_child(node))
12985 .and_then(cpp_declarator_label_node),
12986 "array_declarator" => node
12987 .child_by_field_name("declarator")
12988 .and_then(cpp_declarator_label_node),
12989 "function_declarator" => node
12990 .child_by_field_name("declarator")
12991 .or_else(|| node.child_by_field_name("name"))
12992 .or_else(|| last_named_child(node))
12993 .and_then(cpp_declarator_label_node),
12994 _ => None,
12995 }
12996}
12997
12998fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
12999 let base_type = parameter
13000 .child_by_field_name("type")
13001 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
13002 .unwrap_or_default();
13003 let declarator = cpp_parameter_declarator(parameter);
13004 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
13011 let mut cursor = parameter.walk();
13012 let qualifiers = parameter
13013 .named_children(&mut cursor)
13014 .filter(|child| child.kind() == "type_qualifier")
13015 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
13016 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
13017 .collect::<Vec<_>>()
13018 .join(" ");
13019 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
13020 (true, _) => base_type,
13021 (_, true) => qualifiers,
13022 (false, false) => format!("{qualifiers} {base_type}"),
13023 };
13024 let declarator_suffix = declarator
13025 .map(|node| cpp_declarator_suffix_without_name(node, source))
13026 .unwrap_or_default();
13027
13028 let combined = if type_text.is_empty() {
13029 declarator_suffix
13030 } else if declarator_suffix.is_empty() {
13031 type_text
13032 } else {
13033 format!("{type_text} {declarator_suffix}")
13034 };
13035 normalize_cpp_type_text(&combined)
13036}
13037
13038fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
13039 parameter.child_by_field_name("declarator").or_else(|| {
13040 let mut cursor = parameter.walk();
13046 parameter
13047 .named_children(&mut cursor)
13048 .find(|child| is_cpp_abstract_declarator(child.kind()))
13049 })
13050}
13051
13052pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
13055 let mut current = Some(declarator);
13056 while let Some(node) = current {
13057 if matches!(
13058 node.kind(),
13059 "pointer_declarator"
13060 | "abstract_pointer_declarator"
13061 | "reference_declarator"
13062 | "abstract_reference_declarator"
13063 | "array_declarator"
13064 | "abstract_array_declarator"
13065 | "function_declarator"
13066 | "abstract_function_declarator"
13067 ) {
13068 return true;
13069 }
13070 current = cpp_nested_declarator(node);
13071 }
13072 false
13073}
13074
13075fn is_cpp_abstract_declarator(kind: &str) -> bool {
13076 matches!(
13077 kind,
13078 "abstract_pointer_declarator"
13079 | "abstract_reference_declarator"
13080 | "abstract_array_declarator"
13081 | "abstract_function_declarator"
13082 | "abstract_parenthesized_declarator"
13083 )
13084}
13085
13086fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
13087 node.child_by_field_name("declarator").or_else(|| {
13088 if is_cpp_abstract_declarator(node.kind()) {
13089 let mut cursor = node.walk();
13090 node.named_children(&mut cursor)
13091 .find(|child| is_cpp_abstract_declarator(child.kind()))
13092 } else {
13093 last_named_child(node)
13097 }
13098 })
13099}
13100
13101fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
13102 match node.kind() {
13103 "identifier" | "field_identifier" => String::new(),
13104 "pointer_declarator" | "abstract_pointer_declarator" => {
13105 let inner = cpp_nested_declarator(node)
13106 .map(|child| cpp_declarator_suffix_without_name(child, source))
13107 .unwrap_or_default();
13108 format!("*{inner}")
13109 }
13110 "reference_declarator" | "abstract_reference_declarator" => {
13111 let inner = cpp_nested_declarator(node)
13112 .map(|child| cpp_declarator_suffix_without_name(child, source))
13113 .unwrap_or_default();
13114 let reference = node
13115 .children(&mut node.walk())
13116 .find(|child| matches!(child.kind(), "&" | "&&"))
13117 .map(|child| node_text(child, source))
13118 .unwrap_or("&");
13119 format!("{reference}{inner}")
13120 }
13121 "array_declarator" | "abstract_array_declarator" => {
13122 let inner = cpp_nested_declarator(node)
13123 .map(|child| cpp_declarator_suffix_without_name(child, source))
13124 .unwrap_or_default();
13125 let size = node
13126 .child_by_field_name("size")
13127 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
13128 .unwrap_or_default();
13129 format!("{inner}[{size}]")
13130 }
13131 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
13132 let inner = cpp_nested_declarator(node);
13133 inner
13134 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
13135 .unwrap_or_default()
13136 }
13137 "function_declarator" | "abstract_function_declarator" => {
13138 let inner = cpp_nested_declarator(node)
13139 .map(|child| cpp_declarator_suffix_without_name(child, source))
13140 .unwrap_or_default();
13141 let params = node
13142 .child_by_field_name("parameters")
13143 .map(|child| cpp_parameter_signature(child, source))
13144 .unwrap_or_else(|| "()".to_string());
13145 format!("{inner}{params}")
13146 }
13147 _ => {
13148 let text = normalize_cpp_whitespace(node_text(node, source));
13149 let name = extract_declarator_name(node, source);
13150 if name.is_empty() {
13151 text
13152 } else {
13153 text.replace(&name, "").trim().to_string()
13154 }
13155 }
13156 }
13157}
13158
13159fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
13160 collapse_cpp_whitespace(
13161 suffix
13162 .trim()
13163 .trim_start_matches("->")
13164 .trim_start_matches('{')
13165 .trim_end_matches(';'),
13166 )
13167}
13168
13169pub fn normalize_cpp_whitespace(value: &str) -> String {
13170 collapse_cpp_whitespace(value)
13171}
13172
13173fn normalize_cpp_type_text(value: &str) -> String {
13174 collapse_cpp_whitespace(value)
13175 .replace(", ", ",")
13176 .replace(" <", "<")
13177 .replace("< ", "<")
13178 .replace(" >", ">")
13179}
13180
13181fn collapse_cpp_whitespace(value: &str) -> String {
13182 let mut result = String::new();
13183 let mut prev_space = false;
13184 for ch in value.chars() {
13185 if ch.is_whitespace() {
13186 if !prev_space {
13187 result.push(' ');
13188 }
13189 prev_space = true;
13190 } else {
13191 result.push(ch);
13192 prev_space = false;
13193 }
13194 }
13195 result.trim().to_string()
13196}
13197
13198pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
13199 node_source_text(node, source)
13200}
13201
13202pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
13203 walk_named_tree_preorder(node, true, |node| {
13204 match node.kind() {
13205 "type_identifier" | "identifier" | "qualified_identifier" => {
13206 let text = node_text(node, source).trim();
13207 if !text.is_empty() {
13208 identifiers.insert(text.to_string());
13209 }
13210 }
13211 _ => {}
13212 }
13213 WalkControl::Continue
13214 });
13215}
13216
13217fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
13218 node.child_by_field_name("body").or_else(|| {
13219 let mut cursor = node.walk();
13220 node.named_children(&mut cursor).find(|child| {
13221 matches!(
13222 child.kind(),
13223 "declaration_list" | "field_declaration_list" | "enumerator_list"
13224 )
13225 })
13226 })
13227}
13228
13229fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
13240 if !matches!(
13241 node.kind(),
13242 "class_specifier" | "struct_specifier" | "union_specifier"
13243 ) {
13244 return None;
13245 }
13246 let body = cpp_body_node(node)?;
13247 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
13248 return None;
13249 }
13250 let open = body.child(0)?;
13251 let close = body.child(body.child_count().checked_sub(1)?)?;
13252 if open.kind() != "{"
13253 || open.is_missing()
13254 || close.kind() != "}"
13255 || close.is_missing()
13256 || close.end_byte() != body.end_byte()
13257 || body.end_byte() > node.end_byte()
13258 || node
13259 .parent()
13260 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
13261 {
13262 return None;
13263 }
13264 Some(close)
13265}
13266
13267fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
13268 if node.kind() == "namespace_definition" {
13269 return true;
13270 }
13271 let mut cursor = node.walk();
13272 node.named_children(&mut cursor)
13273 .any(cpp_contains_namespace_definition)
13274}
13275
13276struct CppNestedNamespaceSentinel<'tree> {
13277 function: Node<'tree>,
13278 body: Node<'tree>,
13279 namespace_components: Vec<String>,
13280}
13281
13282#[derive(Debug, Clone)]
13292pub struct CppSentinelRecoveredOwner {
13293 pub range: Range,
13294 pub owner_name_start_byte: usize,
13298 pub namespace_component_count: usize,
13302 pub scope_components: Vec<String>,
13303}
13304
13305#[derive(Debug, Clone)]
13306pub struct CppSentinelRecoveredClass {
13307 pub namespace_range: Range,
13308 pub namespace_scope_components: Vec<String>,
13309 pub class_range: Range,
13310 pub scope_components: Vec<String>,
13312 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
13316}
13317
13318pub fn cpp_sentinel_recovered_scope_for_node(
13324 node: Node<'_>,
13325 source: &str,
13326 recovered_classes: &[CppSentinelRecoveredClass],
13327) -> Option<Vec<String>> {
13328 let contains =
13329 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
13330 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
13331 for recovered in recovered_classes {
13332 for owner in recovered
13333 .owner_ranges
13334 .iter()
13335 .filter(|owner| contains(owner.range))
13336 {
13337 let replace = best_owner.is_none_or(|existing| {
13338 owner.range.end_byte.saturating_sub(owner.range.start_byte)
13339 < existing
13340 .range
13341 .end_byte
13342 .saturating_sub(existing.range.start_byte)
13343 });
13344 if replace {
13345 best_owner = Some(owner);
13346 }
13347 }
13348 }
13349 if let Some(owner) = best_owner {
13350 let mut scope = owner.scope_components.clone();
13351 if node.start_byte() < owner.owner_name_start_byte {
13352 scope.truncate(owner.namespace_component_count);
13353 }
13354 return Some(scope);
13355 }
13356
13357 let class = recovered_classes
13358 .iter()
13359 .filter(|recovered| contains(recovered.class_range))
13360 .min_by_key(|recovered| {
13361 recovered
13362 .class_range
13363 .end_byte
13364 .saturating_sub(recovered.class_range.start_byte)
13365 });
13366 let class_scope = class.is_some();
13367 let mut scope = if let Some(class) = class {
13368 class.scope_components.clone()
13369 } else {
13370 let namespace = recovered_classes
13371 .iter()
13372 .filter(|recovered| contains(recovered.namespace_range))
13373 .min_by_key(|recovered| {
13374 recovered
13375 .namespace_range
13376 .end_byte
13377 .saturating_sub(recovered.namespace_range.start_byte)
13378 })?;
13379 let mut scope = namespace.namespace_scope_components.clone();
13380 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
13381 let common_prefix = scope
13382 .iter()
13383 .zip(&parser_namespace)
13384 .take_while(|(recovered, parser)| recovered == parser)
13385 .count();
13386 scope.extend(parser_namespace.into_iter().skip(common_prefix));
13387 scope
13388 };
13389 if class_scope {
13390 let mut ancestor_components = Vec::new();
13391 let mut ancestor = node.parent();
13392 while let Some(current) = ancestor {
13393 if matches!(
13394 current.kind(),
13395 "class_specifier" | "struct_specifier" | "union_specifier"
13396 ) && let Some(name) = current.child_by_field_name("name")
13397 && let Some(name_components) = cpp_name_components(name, source)
13398 {
13399 ancestor_components.push(
13400 name_components
13401 .into_iter()
13402 .map(|component| component.name)
13403 .collect::<Vec<_>>(),
13404 );
13405 }
13406 ancestor = current.parent();
13407 }
13408 ancestor_components.reverse();
13409 let base_len = scope.len();
13410 for component in ancestor_components.into_iter().flatten() {
13411 if scope.len() >= base_len && scope.last() == Some(&component) {
13412 continue;
13413 }
13414 scope.push(component);
13415 }
13416 }
13417 Some(scope)
13418}
13419
13420struct CppSentinelFragmentedClassTail<'tree> {
13421 class_node: Node<'tree>,
13422 template_node: Option<Node<'tree>>,
13423 name: String,
13424 raw_supertypes: Option<Vec<String>>,
13425 fragmented: FragmentedExportBody,
13426 consumed_start: usize,
13427}
13428
13429struct CppSentinelFragmentedClassErrorPrefix<'tree> {
13430 name: String,
13431 open: Node<'tree>,
13432 raw_supertypes: Option<Vec<String>>,
13433}
13434
13435struct CppSentinelDirectBodyClassRegion {
13436 namespace_components: Vec<String>,
13437 class_start: usize,
13438 class_start_line: usize,
13439 class_close_end: usize,
13440 class_close_line: usize,
13441 name: String,
13442}
13443
13444fn cpp_sentinel_body_class_candidate<'tree>(
13445 child: Node<'tree>,
13446) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13447 if matches!(
13448 child.kind(),
13449 "class_specifier" | "struct_specifier" | "union_specifier"
13450 ) {
13451 return Some((child, None));
13452 }
13453 if child.kind() != "template_declaration" {
13454 if child.kind() == "declaration" {
13455 return Some((first_class_like_child(child)?, None));
13456 }
13457 return None;
13458 }
13459 let mut cursor = child.walk();
13460 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
13461 if matches!(
13462 candidate.kind(),
13463 "class_specifier" | "struct_specifier" | "union_specifier"
13464 ) {
13465 Some(candidate)
13466 } else if candidate.kind() == "declaration" {
13467 first_class_like_child(candidate)
13468 } else {
13469 None
13470 }
13471 })?;
13472 Some((class_node, Some(child)))
13473}
13474
13475fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
13481 node: Node<'tree>,
13482 source: &str,
13483) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
13484 let name = malformed_class_error_owner_name(node, source)?;
13485 let mut cursor = node.walk();
13486 let children = node.children(&mut cursor).collect::<Vec<_>>();
13487 let keyword = children.first()?;
13488 let open_index = children.iter().position(|child| child.kind() == "{")?;
13489 if children[open_index + 1..]
13490 .iter()
13491 .any(|child| child.kind() == "}")
13492 {
13493 return None;
13494 }
13495 let raw_supertypes =
13496 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
13497 Some(CppSentinelFragmentedClassErrorPrefix {
13498 name,
13499 open: children[open_index],
13500 raw_supertypes,
13501 })
13502}
13503
13504fn cpp_sentinel_direct_body_class_candidate<'tree>(
13505 child: Node<'tree>,
13506) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13507 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
13508 return Some(candidate);
13509 }
13510 if child.kind() != "template_declaration" {
13511 return None;
13512 }
13513 let mut cursor = child.walk();
13514 let wrapper = child
13515 .named_children(&mut cursor)
13516 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
13517 Some((first_class_like_child(wrapper)?, Some(child)))
13518}
13519
13520fn cpp_sentinel_direct_namespace_components(
13521 function: Node<'_>,
13522 body: Node<'_>,
13523 source: &str,
13524) -> Option<Vec<String>> {
13525 let mut cursor = function.walk();
13526 let children = function
13527 .named_children(&mut cursor)
13528 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
13529 .collect::<Vec<_>>();
13530 let sentinel_index = children.iter().rposition(|child| {
13531 direct_identifier_name(*child, source)
13532 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
13533 })?;
13534 let mut identifiers = Vec::new();
13535 let mut stack = children[sentinel_index + 1..]
13536 .iter()
13537 .rev()
13538 .copied()
13539 .collect::<Vec<_>>();
13540 while let Some(current) = stack.pop() {
13541 if let Some(name) = direct_identifier_name(current, source) {
13542 identifiers.push(name);
13543 continue;
13544 }
13545 let mut cursor = current.walk();
13546 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
13547 stack.extend(children.into_iter().rev());
13548 }
13549 let [keyword, namespace] = identifiers.as_slice() else {
13550 return None;
13551 };
13552 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
13553 .then(|| vec![namespace.clone()])
13554}
13555
13556fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
13557 let mut sibling = class_semicolon.next_named_sibling();
13558 let namespace_close = loop {
13559 let Some(current) = sibling else {
13560 return false;
13561 };
13562 sibling = current.next_named_sibling();
13563 if current.kind() != "comment" {
13564 break current;
13565 }
13566 };
13567 if !cpp_is_stray_close_brace(namespace_close, source) {
13568 return false;
13569 }
13570 loop {
13571 let Some(current) = sibling else {
13572 return false;
13573 };
13574 sibling = current.next_named_sibling();
13575 if current.kind() == "comment" {
13576 continue;
13577 }
13578 return direct_identifier_name(current, source)
13579 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
13580 }
13581}
13582
13583fn cpp_sentinel_macro_body_class_region<'tree>(
13584 node: Node<'tree>,
13585 source: &str,
13586 ancestry: &ParentIndex<'tree>,
13587) -> Option<CppSentinelDirectBodyClassRegion> {
13588 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
13589 return None;
13590 };
13591 if node.kind() != "function_definition" || !node.has_error() {
13592 return None;
13593 }
13594 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
13595 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
13596 let mut cursor = body.walk();
13597 let candidates = body
13598 .named_children(&mut cursor)
13599 .filter_map(cpp_sentinel_direct_body_class_candidate)
13600 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
13601 .collect::<Vec<_>>();
13602 let [(class_node, template_node)] = candidates.as_slice() else {
13603 return None;
13604 };
13605 let original_body = cpp_body_node(*class_node)?;
13606 let name = class_like_name(*class_node, source, ancestry)?;
13607 if name.is_empty() || cpp_export_macro_token(&name) {
13608 return None;
13609 }
13610
13611 let mut sibling = node.next_named_sibling();
13612 let (class_close_start, class_close_end, class_close_line) = loop {
13613 let current = sibling?;
13614 let next = current.next_named_sibling();
13615 if cpp_is_stray_close_brace(current, source)
13616 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
13617 {
13618 let semicolon = next.expect("checked above");
13619 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
13620 return None;
13621 }
13622 break (
13623 current.start_byte(),
13624 semicolon.end_byte(),
13625 semicolon.end_position().row + 1,
13626 );
13627 }
13628 sibling = next;
13629 };
13630 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
13631 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
13632 let root = tree.root_node();
13633 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
13634 let reparsed_ancestry = ParentIndex::new(root);
13637 let reparsed =
13638 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
13639 if reparsed.name != name
13640 || reparsed.declaration_node.start_byte() != class_node.start_byte()
13641 || reparsed.body.start_byte() != original_body.start_byte()
13642 || class_close_start <= reparsed.body.end_byte()
13643 || class_close_end <= class_node.end_byte()
13644 {
13645 return None;
13646 }
13647 Some(CppSentinelDirectBodyClassRegion {
13648 namespace_components,
13649 class_start: reparse_start,
13650 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
13651 node.start_position().row + 1
13652 }),
13653 class_close_end,
13654 class_close_line,
13655 name,
13656 })
13657}
13658
13659fn cpp_nested_namespace_sentinel<'tree>(
13671 node: Node<'tree>,
13672 source: &str,
13673 ancestry: &ParentIndex<'tree>,
13674) -> Option<CppNestedNamespaceSentinel<'tree>> {
13675 if !node.has_error() {
13676 return None;
13677 }
13678
13679 let (function, mut namespace_components) = if node.kind() == "ERROR" {
13680 let mut cursor = node.walk();
13681 let functions = node
13682 .named_children(&mut cursor)
13683 .filter(|child| child.kind() == "function_definition")
13684 .collect::<Vec<_>>();
13685 let [function] = functions.as_slice() else {
13686 return None;
13687 };
13688 if !function.has_error() {
13689 return None;
13690 }
13691 let mut cursor = node.walk();
13692 let children = node.children(&mut cursor).collect::<Vec<_>>();
13693 let function_index = children
13694 .iter()
13695 .position(|child| same_node(*child, *function))?;
13696 let [outer_keyword, outer_name, outer_open] =
13697 children.get(function_index.checked_sub(3)?..function_index)?
13698 else {
13699 return None;
13700 };
13701 if outer_keyword.kind() != "namespace"
13702 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
13703 || outer_open.kind() != "{"
13704 {
13705 return None;
13706 }
13707 (
13708 *function,
13709 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
13710 )
13711 } else if node.kind() == "function_definition" {
13712 let declaration_list = node.parent()?;
13713 let namespace = declaration_list.parent()?;
13714 if declaration_list.kind() != "declaration_list"
13715 || namespace.kind() != "namespace_definition"
13716 || namespace.child_by_field_name("body") != Some(declaration_list)
13717 {
13718 return None;
13719 }
13720 (node, Vec::new())
13721 } else {
13722 return None;
13723 };
13724
13725 let mut cursor = function.walk();
13726 let named = function
13727 .named_children(&mut cursor)
13728 .filter(|child| child.kind() != "comment")
13729 .collect::<Vec<_>>();
13730 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
13731 return None;
13732 };
13733 if first_type.kind() != "type_identifier" {
13734 return None;
13735 }
13736 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
13737 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
13738 return None;
13739 }
13740 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
13741 return None;
13742 }
13743 let inner_keyword = inner_error.named_child(0)?;
13744 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
13745 return None;
13746 }
13747 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
13748 return None;
13749 }
13750 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
13751 if inner_name.is_empty() || body.kind() != "compound_statement" {
13752 return None;
13753 }
13754 namespace_components.push(inner_name);
13755
13756 let mut cursor = body.walk();
13757 let has_complete_class = body.named_children(&mut cursor).any(|child| {
13758 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13759 cpp_body_node(class_node).is_some()
13760 && class_like_name(class_node, source, ancestry)
13761 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13762 })
13763 });
13764 if !has_complete_class
13765 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
13766 {
13767 return None;
13768 }
13769
13770 Some(CppNestedNamespaceSentinel {
13771 function,
13772 body: *body,
13773 namespace_components,
13774 })
13775}
13776
13777fn cpp_root_namespace_sentinel<'tree>(
13786 node: Node<'tree>,
13787 source: &str,
13788 ancestry: &ParentIndex<'tree>,
13789) -> Option<CppNestedNamespaceSentinel<'tree>> {
13790 if node.kind() != "function_definition"
13791 || !node.has_error()
13792 || node.parent()?.kind() != "translation_unit"
13793 {
13794 return None;
13795 }
13796 let first_type = node.child_by_field_name("type")?;
13797 let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
13798 if first_type.kind() != "type_identifier"
13799 || sentinel.is_empty()
13800 || !cpp_export_macro_token(&sentinel)
13801 {
13802 return None;
13803 }
13804 let declarator = node.child_by_field_name("declarator")?;
13805 let body = node.child_by_field_name("body")?;
13806 if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
13807 return None;
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 [named_type, named_declarator, named_body] = named.as_slice() else {
13815 return None;
13816 };
13817 if !same_node(*named_type, first_type)
13818 || !same_node(*named_declarator, declarator)
13819 || !same_node(*named_body, body)
13820 {
13821 return None;
13822 }
13823 let mut declarator_components = Vec::new();
13824 let mut valid_components = true;
13825 walk_named_tree_preorder(declarator, true, |component| {
13826 if !matches!(
13827 component.kind(),
13828 "identifier" | "namespace_identifier" | "type_identifier"
13829 ) {
13830 return WalkControl::Continue;
13831 }
13832 let Some(component) = canonical_cpp_qualified_component(component, source) else {
13833 valid_components = false;
13834 return WalkControl::Break;
13835 };
13836 declarator_components.push(component.name);
13837 WalkControl::SkipChildren
13838 });
13839 if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
13840 return None;
13841 }
13842 declarator_components.remove(0);
13843 let namespace_components = declarator_components;
13844 if namespace_components.is_empty()
13845 || namespace_components
13846 .iter()
13847 .any(|component| component.is_empty() || cpp_export_macro_token(component))
13848 {
13849 return None;
13850 }
13851
13852 let mut cursor = body.walk();
13853 let has_complete_class = body.named_children(&mut cursor).any(|child| {
13854 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13855 cpp_body_node(class_node).is_some()
13856 && class_like_name(class_node, source, ancestry)
13857 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13858 })
13859 });
13860 if !has_complete_class
13861 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
13862 {
13863 return None;
13864 }
13865
13866 Some(CppNestedNamespaceSentinel {
13867 function: node,
13868 body,
13869 namespace_components,
13870 })
13871}
13872
13873fn cpp_sentinel_fragmented_class_tail<'tree>(
13882 function: Node<'tree>,
13883 body: Node<'tree>,
13884 source: &str,
13885 ancestry: &ParentIndex<'tree>,
13886) -> Option<CppSentinelFragmentedClassTail<'tree>> {
13887 let mut cursor = body.walk();
13888 let candidates = body
13889 .named_children(&mut cursor)
13890 .filter_map(|child| {
13891 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
13892 let class_body = cpp_body_node(class_node)?;
13893 if !class_node.has_error() {
13894 return None;
13895 }
13896 let name = class_like_name(class_node, source, ancestry)?;
13897 let raw_supertypes =
13898 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
13899 .then(|| extract_cpp_supertypes(class_node, source));
13900 return Some((
13901 class_node,
13902 template_node,
13903 name,
13904 class_body,
13905 class_body.start_byte().checked_add(1)?,
13906 raw_supertypes,
13907 ));
13908 }
13909 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
13910 Some((
13911 child,
13912 None,
13913 prefix.name,
13914 prefix.open,
13915 prefix.open.end_byte(),
13916 prefix.raw_supertypes,
13917 ))
13918 })
13919 .collect::<Vec<_>>();
13920 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
13921 candidates.as_slice()
13922 else {
13923 return None;
13924 };
13925 if name.is_empty() || cpp_export_macro_token(name) {
13926 return None;
13927 }
13928
13929 let (close, semicolon) =
13930 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
13931
13932 let reparse_end = close.start_byte();
13933 if *reparse_start >= reparse_end {
13934 return None;
13935 }
13936 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
13937 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
13938 return None;
13939 }
13940 let class_range = Range {
13941 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13942 end_byte: semicolon.end_byte(),
13943 start_line: template_node.map_or(class_node.start_position().row, |node| {
13944 node.start_position().row
13945 }) + 1,
13946 end_line: semicolon.end_position().row + 1,
13947 };
13948 Some(CppSentinelFragmentedClassTail {
13949 class_node: *class_node,
13950 template_node: *template_node,
13951 name: name.clone(),
13952 raw_supertypes: raw_supertypes.clone(),
13953 fragmented: FragmentedExportBody {
13954 reparse_start: *reparse_start,
13955 reparse_end,
13956 class_range,
13957 },
13958 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13959 })
13960}
13961
13962pub fn cpp_sentinel_recovered_classes(
13971 root: Node<'_>,
13972 source: &str,
13973) -> Vec<CppSentinelRecoveredClass> {
13974 if !root.has_error() {
13975 return Vec::new();
13976 }
13977 let ancestry = ParentIndex::new(root);
13981 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
13982 let mut stack = vec![root];
13983 while let Some(current) = stack.pop() {
13984 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
13985 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
13986 {
13987 let namespace_components = cpp_sentinel_recovered_namespace_components(
13988 recovered.function,
13989 &recovered.namespace_components,
13990 source,
13991 );
13992 let fragmented = cpp_sentinel_fragmented_class_tail(
13993 recovered.function,
13994 recovered.body,
13995 source,
13996 &ancestry,
13997 );
13998 let mut class_candidates = Vec::new();
13999 let mut cursor = recovered.body.walk();
14000 for (class_node, template_node) in recovered
14001 .body
14002 .named_children(&mut cursor)
14003 .filter_map(cpp_sentinel_body_class_candidate)
14004 {
14005 let Some(name) = class_like_name(class_node, source, &ancestry) else {
14006 continue;
14007 };
14008 if name.is_empty() || cpp_export_macro_token(&name) {
14009 continue;
14010 }
14011 let is_fragmented = fragmented
14012 .as_ref()
14013 .is_some_and(|tail| same_node(tail.class_node, class_node));
14014 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
14015 continue;
14016 }
14017 let class_range = if is_fragmented {
14018 fragmented
14019 .as_ref()
14020 .map(|tail| tail.fragmented.class_range)
14021 .expect("fragmented class range is present when class matches")
14022 } else {
14023 cpp_declaration_range(template_node.unwrap_or(class_node))
14024 };
14025 class_candidates.push((class_range, name));
14026 }
14027 if let Some(fragmented) = fragmented
14028 .as_ref()
14029 .filter(|tail| tail.class_node.kind() == "ERROR")
14030 {
14031 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
14032 }
14033
14034 let mut owner_ranges =
14035 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
14036 cpp_sentinel_extend_unique_owner_ranges(
14037 &mut owner_ranges,
14038 cpp_sentinel_recovered_sibling_owner_ranges(
14039 recovered.function,
14040 &namespace_components,
14041 source,
14042 ),
14043 );
14044 for (class_range, name) in class_candidates {
14045 push_cpp_sentinel_recovered_class(
14046 &mut recovered_classes,
14047 cpp_declaration_range(recovered.body),
14048 &namespace_components,
14049 class_range,
14050 name,
14051 &owner_ranges,
14052 );
14053 }
14054
14055 if let Some(declaration_list) = recovered
14056 .function
14057 .parent()
14058 .filter(|parent| parent.kind() == "declaration_list")
14059 {
14060 let outer_namespace =
14061 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
14062 push_cpp_sentinel_sibling_classes(
14063 &mut recovered_classes,
14064 declaration_list,
14065 recovered.function,
14066 &outer_namespace,
14067 source,
14068 &ancestry,
14069 );
14070 }
14071 } else if let Some(region) =
14072 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
14073 {
14074 let namespace_components = cpp_sentinel_recovered_namespace_components(
14075 current,
14076 ®ion.namespace_components,
14077 source,
14078 );
14079 let owner_container = current
14080 .parent()
14081 .filter(|parent| parent.kind() == "declaration_list")
14082 .unwrap_or(current);
14083 let owner_ranges =
14084 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
14085 push_cpp_sentinel_recovered_class(
14086 &mut recovered_classes,
14087 cpp_declaration_range(owner_container),
14088 &namespace_components,
14089 Range {
14090 start_byte: region.class_start,
14091 end_byte: region.class_close_end,
14092 start_line: region.class_start_line,
14093 end_line: region.class_close_line,
14094 },
14095 region.name,
14096 &owner_ranges,
14097 );
14098 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
14099 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
14104 region;
14105 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
14106 continue;
14107 };
14108 let root = tree.root_node();
14109 let template_node = cpp_sentinel_reparsed_leading_template(root);
14110 let reparsed_ancestry = ParentIndex::new(root);
14112 let Some(reparsed_class) =
14113 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
14114 else {
14115 continue;
14116 };
14117 let class_node = reparsed_class.declaration_node;
14118 let name = reparsed_class.name;
14119 let namespace_components =
14120 cpp_sentinel_recovered_namespace_components(current, &[], source);
14121 let owner_container = current
14122 .parent()
14123 .filter(|parent| parent.kind() == "declaration_list")
14124 .unwrap_or(current);
14125 let mut owner_ranges =
14126 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
14127 cpp_sentinel_extend_unique_owner_ranges(
14128 &mut owner_ranges,
14129 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
14130 );
14131 push_cpp_sentinel_recovered_class(
14132 &mut recovered_classes,
14133 cpp_declaration_range(owner_container),
14134 &namespace_components,
14135 Range {
14136 start_byte: class_start,
14137 end_byte: close_end,
14138 start_line: class_node.start_position().row + 1,
14139 end_line: class_node.end_position().row + 1,
14140 },
14141 name,
14142 &owner_ranges,
14143 );
14144 if owner_container.kind() == "declaration_list" {
14145 push_cpp_sentinel_sibling_classes(
14146 &mut recovered_classes,
14147 owner_container,
14148 current,
14149 &namespace_components,
14150 source,
14151 &ancestry,
14152 );
14153 }
14154 }
14155
14156 let mut cursor = current.walk();
14157 stack.extend(current.named_children(&mut cursor));
14158 }
14159 let shadowed = recovered_classes
14165 .iter()
14166 .map(|candidate| {
14167 recovered_classes.iter().any(|container| {
14168 container.class_range.start_byte <= candidate.class_range.start_byte
14169 && container.class_range.end_byte >= candidate.class_range.end_byte
14170 && container.class_range != candidate.class_range
14171 && container.namespace_scope_components.len()
14172 > candidate.namespace_scope_components.len()
14173 && container
14174 .namespace_scope_components
14175 .starts_with(&candidate.namespace_scope_components)
14176 })
14177 })
14178 .collect::<Vec<_>>();
14179 let mut index = 0usize;
14180 recovered_classes.retain(|_| {
14181 let keep = !shadowed[index];
14182 index += 1;
14183 keep
14184 });
14185 recovered_classes
14186}
14187
14188fn push_cpp_sentinel_sibling_classes<'tree>(
14194 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14195 declaration_list: Node<'tree>,
14196 sentinel_node: Node<'tree>,
14197 namespace_components: &[String],
14198 source: &str,
14199 ancestry: &ParentIndex<'tree>,
14200) {
14201 let owner_ranges =
14202 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
14203 let namespace_range = cpp_declaration_range(declaration_list);
14204 let mut cursor = declaration_list.walk();
14205 for (class_node, template_node) in declaration_list
14206 .named_children(&mut cursor)
14207 .filter(|child| !same_node(*child, sentinel_node))
14208 .filter_map(cpp_sentinel_body_class_candidate)
14209 {
14210 let Some(name) = class_like_name(class_node, source, ancestry) else {
14211 continue;
14212 };
14213 if name.is_empty()
14214 || cpp_export_macro_token(&name)
14215 || cpp_complete_class_body_close(class_node).is_none()
14216 {
14217 continue;
14218 }
14219 push_cpp_sentinel_recovered_class(
14220 recovered_classes,
14221 namespace_range,
14222 namespace_components,
14223 cpp_declaration_range(template_node.unwrap_or(class_node)),
14224 name,
14225 &owner_ranges,
14226 );
14227 }
14228}
14229
14230fn push_cpp_sentinel_recovered_class(
14231 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14232 namespace_range: Range,
14233 namespace_components: &[String],
14234 class_range: Range,
14235 name: String,
14236 owner_ranges: &[CppSentinelRecoveredOwner],
14237) {
14238 let mut scope_components = namespace_components.to_vec();
14239 scope_components.push(name);
14240 let owner_ranges = owner_ranges
14241 .iter()
14242 .filter(|owner| owner.scope_components.starts_with(&scope_components))
14243 .cloned()
14244 .collect::<Vec<_>>();
14245 if recovered_classes.iter().any(|existing| {
14246 existing.class_range == class_range && existing.scope_components == scope_components
14247 }) {
14248 return;
14249 }
14250 recovered_classes.push(CppSentinelRecoveredClass {
14251 namespace_range,
14252 namespace_scope_components: namespace_components.to_vec(),
14253 class_range,
14254 scope_components,
14255 owner_ranges,
14256 });
14257}
14258
14259fn cpp_sentinel_recovered_namespace_components(
14260 function: Node<'_>,
14261 recovered_components: &[String],
14262 source: &str,
14263) -> Vec<String> {
14264 let mut ancestor_components = Vec::new();
14265 let mut ancestor = function.parent();
14266 while let Some(current) = ancestor {
14267 if current.kind() == "namespace_definition"
14268 && let Some(name_node) = current.child_by_field_name("name")
14269 && let Some(components) = cpp_name_components(name_node, source)
14270 {
14271 ancestor_components.push(
14272 components
14273 .into_iter()
14274 .map(|component| component.name)
14275 .collect::<Vec<_>>(),
14276 );
14277 }
14278 ancestor = current.parent();
14279 }
14280 ancestor_components.reverse();
14281 let mut ancestors = ancestor_components
14282 .into_iter()
14283 .flatten()
14284 .collect::<Vec<_>>();
14285
14286 let overlap = (0..=ancestors.len().min(recovered_components.len()))
14287 .rev()
14288 .find(|length| {
14289 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
14290 })
14291 .unwrap_or(0);
14292 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
14293 ancestors
14294}
14295
14296fn cpp_sentinel_recovered_owner_ranges(
14297 body: Node<'_>,
14298 namespace_components: &[String],
14299 source: &str,
14300) -> Vec<CppSentinelRecoveredOwner> {
14301 let mut owners = Vec::new();
14302 walk_named_tree_preorder(body, true, |node| {
14303 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14304 });
14305 owners
14306}
14307
14308fn cpp_sentinel_collect_owner_range(
14309 node: Node<'_>,
14310 namespace_components: &[String],
14311 source: &str,
14312 owners: &mut Vec<CppSentinelRecoveredOwner>,
14313) -> WalkControl {
14314 if node.kind() != "function_definition" {
14315 return WalkControl::Continue;
14316 }
14317 let Some(function_declarator) = extract_function_declarator(node) else {
14318 return WalkControl::Continue;
14319 };
14320 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
14321 return WalkControl::Continue;
14322 };
14323 let Some(mut components) = cpp_name_components(name_node, source) else {
14324 return WalkControl::Continue;
14325 };
14326 if components.len() <= 1 {
14327 return WalkControl::Continue;
14328 }
14329 components.pop();
14330 let mut owner_components = components
14331 .into_iter()
14332 .map(|component| component.name)
14333 .collect::<Vec<_>>();
14334 let overlap = (0..=namespace_components.len().min(owner_components.len()))
14335 .rev()
14336 .find(|length| {
14337 owner_components[..*length]
14338 == namespace_components[namespace_components.len().saturating_sub(*length)..]
14339 })
14340 .unwrap_or(0);
14341 let mut scope_components = namespace_components.to_vec();
14342 scope_components.extend(owner_components.drain(overlap..));
14343 if scope_components.len() <= namespace_components.len() {
14344 return WalkControl::Continue;
14345 }
14346 let range = cpp_declaration_range(node);
14347 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
14348 existing.range == range && existing.scope_components == scope_components
14349 }) {
14350 owners.push(CppSentinelRecoveredOwner {
14351 range,
14352 owner_name_start_byte: name_node.start_byte(),
14353 namespace_component_count: namespace_components.len(),
14354 scope_components,
14355 });
14356 }
14357 WalkControl::Continue
14358}
14359
14360fn cpp_sentinel_extend_unique_owner_ranges(
14361 owners: &mut Vec<CppSentinelRecoveredOwner>,
14362 additional: Vec<CppSentinelRecoveredOwner>,
14363) {
14364 for owner in additional {
14365 if !owners.iter().any(|existing| {
14366 existing.range == owner.range && existing.scope_components == owner.scope_components
14367 }) {
14368 owners.push(owner);
14369 }
14370 }
14371}
14372
14373fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
14374 if node.kind() != "ERROR" || node.named_child_count() != 1 {
14375 return false;
14376 }
14377 let Some(end_name) = node.named_child(0) else {
14378 return false;
14379 };
14380 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
14381 return false;
14382 }
14383 let mut cursor = node.walk();
14384 node.children(&mut cursor)
14385 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
14386}
14387
14388fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14392 parent: Node<'_>,
14393 sentinel_node: Node<'_>,
14394 namespace_components: &[String],
14395 source: &str,
14396) -> Vec<CppSentinelRecoveredOwner> {
14397 let mut owners = Vec::new();
14398 let mut after_sentinel = false;
14399 let mut cursor = parent.walk();
14400 for child in parent.named_children(&mut cursor) {
14401 if !after_sentinel {
14402 if same_node(child, sentinel_node) {
14403 after_sentinel = true;
14404 }
14405 continue;
14406 }
14407 walk_named_tree_preorder(child, true, |node| {
14408 if node.kind() == "namespace_definition" {
14409 return WalkControl::SkipChildren;
14410 }
14411 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14412 });
14413 }
14414 owners
14415}
14416
14417fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14421 parent: Node<'_>,
14422 sentinel_node: Node<'_>,
14423 namespace_components: &[String],
14424 source: &str,
14425) -> Option<Vec<CppSentinelRecoveredOwner>> {
14426 let mut owners = Vec::new();
14427 let mut after_namespace = false;
14428 let mut cursor = parent.walk();
14429 for child in parent.named_children(&mut cursor) {
14430 if !after_namespace {
14431 if same_node(child, sentinel_node) {
14432 after_namespace = true;
14433 }
14434 continue;
14435 }
14436 if cpp_sentinel_namespace_end(child, source) {
14437 return Some(owners);
14438 }
14439 walk_named_tree_preorder(child, true, |node| {
14440 if node.kind() == "namespace_definition" {
14441 return WalkControl::SkipChildren;
14442 }
14443 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14444 });
14445 }
14446 None
14447}
14448
14449fn cpp_sentinel_recovered_sibling_owner_ranges(
14450 sentinel_node: Node<'_>,
14451 namespace_components: &[String],
14452 source: &str,
14453) -> Vec<CppSentinelRecoveredOwner> {
14454 let Some(declaration_list) = sentinel_node
14455 .parent()
14456 .filter(|parent| parent.kind() == "declaration_list")
14457 else {
14458 return Vec::new();
14459 };
14460 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14461 declaration_list,
14462 sentinel_node,
14463 namespace_components,
14464 source,
14465 );
14466
14467 let Some(namespace) = declaration_list
14468 .parent()
14469 .filter(|parent| parent.kind() == "namespace_definition")
14470 else {
14471 return owners;
14472 };
14473 let Some(outer_parent) = namespace.parent() else {
14474 return owners;
14475 };
14476 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14477 outer_parent,
14478 namespace,
14479 namespace_components,
14480 source,
14481 ) {
14482 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
14483 }
14484 owners
14485}
14486
14487fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
14488 let mut current = function_declarator.child_by_field_name("declarator")?;
14489 loop {
14490 if let Some(name) = macro_decorated_unqualified_name(current) {
14491 current = name;
14492 continue;
14493 }
14494 if matches!(
14495 current.kind(),
14496 "qualified_identifier"
14497 | "scoped_identifier"
14498 | "scoped_type_identifier"
14499 | "identifier"
14500 | "field_identifier"
14501 | "operator_name"
14502 | "destructor_name"
14503 | "literal_operator_name"
14504 ) {
14505 return Some(current);
14506 }
14507 current = current
14508 .child_by_field_name("declarator")
14509 .or_else(|| current.child_by_field_name("name"))
14510 .or_else(|| last_named_child(current))?;
14511 }
14512}
14513
14514fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
14527 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
14528 return None;
14529 }
14530 let mut cursor = node.walk();
14531 if node
14532 .children(&mut cursor)
14533 .any(|child| child.kind() == "::" && !child.is_missing())
14534 {
14535 return None;
14536 }
14537 node.child_by_field_name("name")
14538}
14539
14540fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
14541 if let Some(name) = macro_decorated_unqualified_name(node) {
14542 return cpp_name_components(name, source);
14543 }
14544 match node.kind() {
14545 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14546 let mut components = match node.child_by_field_name("scope") {
14547 Some(scope) => cpp_name_components(scope, source)?,
14548 None => Vec::new(),
14549 };
14550 let name = node.child_by_field_name("name")?;
14551 components.push(canonical_cpp_qualified_component(name, source)?);
14552 Some(components)
14553 }
14554 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
14555 }
14556}
14557
14558fn cpp_sentinel_fragment_boundary<'tree>(
14559 function: Node<'tree>,
14560 class_node: Node<'tree>,
14561 class_body: Node<'tree>,
14562 source: &str,
14563) -> Option<(Node<'tree>, Node<'tree>)> {
14564 let declaration_list = function.parent()?;
14565 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
14566 return None;
14567 }
14568 let namespace = declaration_list.parent()?;
14569 if namespace.kind() != "namespace_definition"
14570 || namespace.child_by_field_name("body") != Some(declaration_list)
14571 {
14572 return None;
14573 }
14574 let mut cursor = declaration_list.walk();
14575 let closes = declaration_list
14576 .children(&mut cursor)
14577 .filter(|child| {
14578 !child.is_named()
14579 && child.kind() == "}"
14580 && child.start_byte() >= function.end_byte()
14581 && child.start_byte() > class_node.end_byte()
14582 && child.start_byte() > class_body.start_byte()
14583 })
14584 .collect::<Vec<_>>();
14585 let [close] = closes.as_slice() else {
14586 return None;
14587 };
14588 let semicolon = namespace.next_named_sibling()?;
14589 if !cpp_is_stray_semicolon(semicolon, source)
14590 || close.end_byte() != namespace.end_byte()
14591 || semicolon.start_byte() < namespace.end_byte()
14592 {
14593 return None;
14594 }
14595 Some((*close, semicolon))
14596}
14597
14598fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
14624 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
14625 {
14626 return None;
14627 }
14628 let mut declarator_cursor = node.walk();
14634 let preserved_callable = node
14635 .children_by_field_name("declarator", &mut declarator_cursor)
14636 .find_map(extract_function_declarator);
14637 let mut cursor = node.walk();
14645 let first = node
14646 .named_children(&mut cursor)
14647 .find(|child| child.kind() != "comment")?;
14648 if first.kind() != "type_identifier" {
14649 return None;
14650 }
14651 let sentinel = normalize_cpp_whitespace(node_text(first, source));
14652 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
14653 return None;
14654 }
14655 let mut start = first.end_byte();
14662 let mut after_first = false;
14663 let mut cursor = node.walk();
14664 for child in node.named_children(&mut cursor) {
14665 if !after_first {
14666 if same_node(child, first) {
14667 after_first = true;
14668 }
14669 continue;
14670 }
14671 if matches!(child.kind(), "identifier" | "type_identifier")
14672 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
14673 {
14674 start = child.end_byte();
14675 } else {
14676 break;
14677 }
14678 }
14679 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
14688 let mut class_start = None;
14689 let mut template_start = None;
14690 let mut stack = vec![node];
14691 while let Some(current) = stack.pop() {
14692 if current.start_byte() >= prefix_end {
14693 continue;
14694 }
14695 if matches!(
14696 current.kind(),
14697 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
14698 ) {
14699 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
14700 "class" | "struct" | "union" | "enum" => {
14701 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
14702 seen.min(current.start_byte())
14703 }));
14704 }
14705 "template" => {
14706 template_start =
14707 Some(template_start.map_or(current.start_byte(), |seen: usize| {
14708 seen.min(current.start_byte())
14709 }));
14710 }
14711 _ => {}
14712 }
14713 }
14714 let mut cursor = current.walk();
14715 stack.extend(current.children(&mut cursor));
14716 }
14717 if preserved_callable.is_some_and(|callable| {
14718 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
14719 }) {
14720 return None;
14721 }
14722 if let Some(class_start) = class_start {
14723 start = template_start
14724 .filter(|template_start| *template_start < class_start)
14725 .unwrap_or(class_start);
14726 }
14727 Some((start, class_start))
14728}
14729
14730fn cpp_sentinel_macro_class_region<'tree>(
14736 node: Node<'tree>,
14737 source: &str,
14738) -> Option<(usize, usize, usize, usize, usize, usize)> {
14739 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
14740 return None;
14741 };
14742 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
14743 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
14744 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
14745 if class_start >= body_open_start {
14746 return None;
14747 }
14748 let sibling_close = {
14749 let mut sibling = node.next_named_sibling();
14750 let mut found = None;
14751 while let Some(current) = sibling {
14752 let next = current.next_named_sibling();
14753 if cpp_is_stray_close_brace(current, source)
14754 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
14755 {
14756 let semicolon = next.expect("checked above");
14757 found = Some((
14758 current.start_byte(),
14759 semicolon.end_byte(),
14760 semicolon.end_position().row + 1,
14761 ));
14762 break;
14763 }
14764 sibling = next;
14765 }
14766 found
14767 };
14768 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
14781 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
14782 return false;
14783 };
14784 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14785 let reparsed_ancestry = ParentIndex::new(tree.root_node());
14787 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
14788 tree.root_node(),
14789 template_node,
14790 source,
14791 &reparsed_ancestry,
14792 ) else {
14793 return false;
14794 };
14795 let body = reparsed_class.body;
14796 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
14797 });
14798 let (class_close_start, class_close_end, class_close_line) =
14799 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
14800 (class_close_start, class_close_end, class_close_line)
14801 } else {
14802 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
14809 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14810 let reparsed_ancestry = ParentIndex::new(tree.root_node());
14812 let reparsed_class = cpp_sentinel_reparsed_class(
14813 tree.root_node(),
14814 template_node,
14815 source,
14816 &reparsed_ancestry,
14817 )?;
14818 let body = reparsed_class.body;
14819 let class_close_end = body.end_byte();
14820 let class_close_start = class_close_end.checked_sub(1)?;
14821 let class_close_line = body.end_position().row + 1;
14822 (class_close_start, class_close_end, class_close_line)
14823 };
14824 if class_close_start <= class_start {
14825 return None;
14826 }
14827
14828 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
14832 let class_root = tree.root_node();
14833 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
14834 let reparsed_ancestry = ParentIndex::new(class_root);
14836 let reparsed_class =
14837 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
14838 let body = reparsed_class.body;
14839 if body.start_byte() != body_open_start {
14843 return None;
14844 }
14845 let body_start = body.start_byte().checked_add(1)?;
14846 (body_start < class_close_start).then_some((
14847 reparse_start,
14848 class_start,
14849 body_start,
14850 class_close_start,
14851 class_close_end,
14852 class_close_line,
14853 ))
14854}
14855
14856fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
14861 let mut stack = vec![node];
14862 while let Some(current) = stack.pop() {
14863 if current.start_byte() == class_start
14864 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
14865 {
14866 let mut sibling = current.next_sibling();
14867 while let Some(candidate) = sibling {
14868 if candidate.kind() == "{" {
14869 return Some(candidate.start_byte());
14870 }
14871 sibling = candidate.next_sibling();
14872 }
14873 }
14874 let mut cursor = current.walk();
14875 stack.extend(current.children(&mut cursor));
14876 }
14877 None
14878}
14879
14880fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
14891 node.next_named_sibling()
14892 .filter(|sibling| sibling.kind() == "compound_statement")
14893}
14894
14895fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
14896 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
14897 let mut end = if class_start.is_some() {
14898 cpp_macro_prefixed_class_end(source, start)?
14899 } else {
14900 node.end_byte()
14901 };
14902 if class_start.is_none()
14903 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
14904 {
14905 end = end.max(namespace_end);
14906 }
14907 let mut sibling = node.next_named_sibling();
14908 while let Some(current) = sibling {
14909 if !cpp_is_stray_semicolon(current, source) {
14910 break;
14911 }
14912 end = current.end_byte();
14913 sibling = current.next_named_sibling();
14914 }
14915 (start < end).then_some((start, end))
14916}
14917
14918fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
14929 let mut sibling = node.next_sibling();
14930 let keyword = loop {
14931 let candidate = sibling?;
14932 sibling = candidate.next_sibling();
14933 if candidate.kind() != "comment" {
14934 break candidate;
14935 }
14936 };
14937 if keyword.kind() != "namespace" {
14938 return None;
14939 }
14940 let name = loop {
14941 let candidate = sibling?;
14942 sibling = candidate.next_sibling();
14943 if candidate.kind() != "comment" {
14944 break candidate;
14945 }
14946 };
14947 if cpp_namespace_name_components(name, source).is_empty() {
14948 return None;
14949 }
14950 let open = loop {
14951 let candidate = sibling?;
14952 sibling = candidate.next_sibling();
14953 if candidate.kind() != "comment" {
14954 break candidate;
14955 }
14956 };
14957 if open.kind() != "{" {
14958 return None;
14959 }
14960
14961 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
14962 let root = tree.root_node();
14963 let mut cursor = root.walk();
14964 let namespace = root
14965 .named_children(&mut cursor)
14966 .find(|candidate| candidate.kind() != "comment")?;
14967 (namespace.kind() == "namespace_definition"
14968 && namespace.start_byte() == keyword.start_byte()
14969 && namespace.child_by_field_name("body").is_some())
14970 .then_some(namespace.end_byte())
14971}
14972
14973fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
14979 let tree = cpp_reparse_region_items(source, start, source.len())?;
14980 let root = tree.root_node();
14981 let mut cursor = root.walk();
14982 for item in root.named_children(&mut cursor) {
14983 if item.end_byte() <= start || item.kind() == "comment" {
14984 continue;
14985 }
14986 let mut stack = vec![item];
14987 while let Some(current) = stack.pop() {
14988 if matches!(
14989 current.kind(),
14990 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
14991 ) && cpp_body_node(current).is_some()
14992 {
14993 return Some(current.end_byte());
14994 }
14995 let mut cursor = current.walk();
14996 stack.extend(current.named_children(&mut cursor));
14997 }
14998 return None;
15002 }
15003 None
15004}
15005
15006fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
15009 node.kind() == "expression_statement"
15010 && node.named_child_count() == 0
15011 && node_text(node, source).trim() == ";"
15012}
15013
15014#[derive(Clone, Copy)]
15024pub(crate) struct RecoveredPyObjectHeadField<'tree> {
15025 pub(crate) type_node: Node<'tree>,
15026 pub(crate) name: Node<'tree>,
15027 pub(crate) declarator: Node<'tree>,
15028}
15029
15030impl RecoveredPyObjectHeadField<'_> {
15031 pub(crate) fn pointer_depth(self) -> i32 {
15032 let mut depth = 0;
15033 let mut current = self.declarator;
15034 while current != self.name {
15035 debug_assert_eq!(current.kind(), "pointer_declarator");
15036 depth += 1;
15037 current = current
15038 .child_by_field_name("declarator")
15039 .expect("recovered PyObject field pointer has an inner declarator");
15040 }
15041 depth
15042 }
15043}
15044
15045pub(crate) fn recovered_pyobject_head_field<'tree>(
15046 node: Node<'tree>,
15047 source: &str,
15048) -> Option<RecoveredPyObjectHeadField<'tree>> {
15049 if node.kind() != "field_declaration" {
15050 return None;
15051 }
15052 let type_node = node.child_by_field_name("type")?;
15053 if type_node.kind() != "type_identifier"
15054 || node_text(type_node, source).trim() != "PyObject_HEAD"
15055 {
15056 return None;
15057 }
15058 let pseudo_declarator = node.child_by_field_name("declarator")?;
15059 let mut cursor = node.walk();
15060 let errors = node
15061 .named_children(&mut cursor)
15062 .filter(|child| child.kind() == "ERROR")
15063 .collect::<Vec<_>>();
15064 let [error] = errors.as_slice() else {
15065 return None;
15066 };
15067 if error.named_child_count() != 1 {
15068 return None;
15069 }
15070 let error_child = error.named_child(0)?;
15071 if pseudo_declarator.kind() == "field_identifier"
15072 && error.start_byte() >= pseudo_declarator.end_byte()
15073 && error_child.kind() == "identifier"
15074 {
15075 return Some(RecoveredPyObjectHeadField {
15076 type_node: pseudo_declarator,
15077 name: error_child,
15078 declarator: error_child,
15079 });
15080 }
15081 if pseudo_declarator.kind() != "pointer_declarator"
15082 || error.end_byte() > pseudo_declarator.start_byte()
15083 || error_child.kind() != "identifier"
15084 {
15085 return None;
15086 }
15087 let mut name = pseudo_declarator;
15088 while name.kind() == "pointer_declarator" {
15089 name = name.child_by_field_name("declarator")?;
15090 }
15091 (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
15092 type_node: error_child,
15093 name,
15094 declarator: pseudo_declarator,
15095 })
15096}
15097
15098fn recovered_macro_qualified_field_declarators<'tree>(
15107 node: Node<'tree>,
15108 source: &str,
15109) -> Option<Vec<Node<'tree>>> {
15110 if node.kind() != "field_declaration" {
15111 return None;
15112 }
15113 let macro_type = node.child_by_field_name("type")?;
15114 if macro_type.kind() != "type_identifier"
15115 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15116 {
15117 return None;
15118 }
15119 let pseudo_declarator = node.child_by_field_name("declarator")?;
15120 if pseudo_declarator.kind() != "field_identifier" {
15121 return None;
15122 }
15123 let mut cursor = node.walk();
15124 let clause = node
15125 .named_children(&mut cursor)
15126 .find(|child| child.kind() == "bitfield_clause")?;
15127 if !(0..clause.named_child_count()).any(|index| {
15128 clause
15129 .named_child(index)
15130 .is_some_and(|child| child.kind() == "ERROR")
15131 }) {
15132 return None;
15133 }
15134 let mut recovered = Vec::new();
15135 let mut stack = vec![clause];
15136 while let Some(current) = stack.pop() {
15137 if current.kind() == "assignment_expression"
15138 && let Some(left) = current.child_by_field_name("left")
15139 && extract_variable_name(left, source).is_some()
15140 {
15141 recovered.push(left);
15142 break;
15143 }
15144 let mut cursor = current.walk();
15145 stack.extend(current.named_children(&mut cursor));
15146 }
15147 if recovered.is_empty() {
15148 return None;
15149 }
15150 let mut cursor = node.walk();
15151 recovered.extend(
15152 node.children_by_field_name("declarator", &mut cursor)
15153 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
15154 );
15155 Some(recovered)
15156}
15157
15158fn recovered_macro_qualified_constructor_call<'tree>(
15164 node: Node<'tree>,
15165 class_name: &str,
15166 source: &str,
15167) -> Option<Node<'tree>> {
15168 if node.kind() != "field_declaration" {
15169 return None;
15170 }
15171 let macro_type = node.child_by_field_name("type")?;
15172 if macro_type.kind() != "type_identifier"
15173 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15174 {
15175 return None;
15176 }
15177 let mut cursor = node.walk();
15178 let bitfield = node
15179 .named_children(&mut cursor)
15180 .find(|child| child.kind() == "bitfield_clause")?;
15181 let error = bitfield
15182 .named_child(0)
15183 .filter(|child| child.kind() == "ERROR")?;
15184 let mut stack = vec![error];
15185 while let Some(current) = stack.pop() {
15186 if current.kind() == "call_expression"
15187 && current
15188 .child_by_field_name("function")
15189 .is_some_and(|function| node_text(function, source) == class_name)
15190 && current
15191 .child_by_field_name("arguments")
15192 .is_some_and(|arguments| arguments.kind() == "argument_list")
15193 {
15194 return Some(current);
15195 }
15196 let mut cursor = current.walk();
15197 stack.extend(current.named_children(&mut cursor));
15198 }
15199 None
15200}
15201
15202fn recovered_macro_qualified_function_call<'tree>(
15210 node: Node<'tree>,
15211 source: &str,
15212) -> Option<Node<'tree>> {
15213 if node.kind() != "field_declaration" {
15214 return None;
15215 }
15216 let macro_type = node.child_by_field_name("type")?;
15217 if macro_type.kind() != "type_identifier"
15218 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15219 {
15220 return None;
15221 }
15222 let declarator = node.child_by_field_name("declarator")?;
15223 if declarator.kind() != "field_identifier" {
15224 return None;
15225 }
15226 let mut cursor = node.walk();
15227 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15228 if !named.iter().any(|child| {
15229 child.kind() == "storage_class_specifier"
15230 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
15231 }) {
15232 return None;
15233 }
15234 let bitfield = named
15235 .iter()
15236 .find(|child| child.kind() == "bitfield_clause")?;
15237 let mut bitfield_cursor = bitfield.walk();
15238 let payload = bitfield
15239 .named_children(&mut bitfield_cursor)
15240 .collect::<Vec<_>>();
15241 let [displaced_error, call] = payload.as_slice() else {
15242 return None;
15243 };
15244 if displaced_error.kind() != "ERROR"
15245 || displaced_error.named_child_count() != 1
15246 || displaced_error
15247 .named_child(0)
15248 .is_none_or(|child| child.kind() != "identifier")
15249 || call.kind() != "call_expression"
15250 || call
15251 .child_by_field_name("function")
15252 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
15253 || call
15254 .child_by_field_name("arguments")
15255 .is_none_or(|arguments| arguments.kind() != "argument_list")
15256 {
15257 return None;
15258 }
15259 Some(*call)
15260}
15261
15262fn recovered_macro_qualified_function_parameters(
15263 arguments: Node<'_>,
15264 source: &str,
15265) -> Option<(String, Vec<String>)> {
15266 if arguments.kind() != "argument_list" {
15267 return None;
15268 }
15269 let mut cursor = arguments.walk();
15270 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
15271 if named.is_empty() {
15272 return Some(("()".to_string(), Vec::new()));
15273 }
15274 let mut types = Vec::new();
15275 let mut labels = Vec::new();
15276 let mut index = 0;
15277 while index < named.len() {
15278 let parameter_type = named[index];
15279 let parameter_name = named.get(index + 1).copied()?;
15280 if !matches!(
15281 parameter_type.kind(),
15282 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
15283 ) || parameter_name.kind() != "ERROR"
15284 || parameter_name.named_child_count() != 1
15285 || parameter_name
15286 .named_child(0)
15287 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
15288 {
15289 return None;
15290 }
15291 let parameter_name = parameter_name.named_child(0)?;
15292 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
15293 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
15294 index += 2;
15295 }
15296 Some((format!("({})", types.join(", ")), labels))
15297}
15298
15299pub fn recovered_macro_return_type_node<'tree>(
15311 node: Node<'tree>,
15312 source: &str,
15313) -> Option<Node<'tree>> {
15314 if node.kind() != "field_declaration" {
15315 return None;
15316 }
15317 let macro_type = node.child_by_field_name("type")?;
15318 if macro_type.kind() != "type_identifier"
15319 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15320 {
15321 return None;
15322 }
15323 let declarator = node.child_by_field_name("declarator")?;
15324 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
15325 return None;
15326 }
15327 let mut has_missing_semicolon = false;
15328 let mut has_real_semicolon = false;
15329 for child in children_iter(node) {
15330 if child.kind() != ";" {
15331 continue;
15332 }
15333 if child.is_missing() {
15334 has_missing_semicolon = true;
15335 } else {
15336 has_real_semicolon = true;
15337 }
15338 }
15339 if !has_missing_semicolon || has_real_semicolon {
15340 return None;
15341 }
15342 let mut next = node.next_named_sibling();
15343 while next.is_some_and(|sibling| sibling.kind() == "comment") {
15344 next = next.and_then(|sibling| sibling.next_named_sibling());
15345 }
15346 let next = next?;
15347 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
15348 return None;
15349 }
15350 let function_declarator = next.child_by_field_name("declarator")?;
15351 extract_function_declarator(function_declarator).map(|_| declarator)
15352}
15353
15354pub(crate) fn cpp_active_template_type_parameter<'tree>(
15361 node: Node<'tree>,
15362 name: &str,
15363 source: &str,
15364 ancestry: &ParentIndex<'tree>,
15365) -> bool {
15366 let mut ancestor = ancestry.parent(node);
15367 while let Some(current) = ancestor {
15368 if current.kind() == "template_declaration"
15369 && let Some(parameters) = current.child_by_field_name("parameters")
15370 {
15371 let mut cursor = parameters.walk();
15372 if parameters.named_children(&mut cursor).any(|parameter| {
15373 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
15374 && cpp_template_parameter_name(parameter, source)
15375 .is_some_and(|parameter_name| parameter_name == name)
15376 }) {
15377 return true;
15378 }
15379 }
15380 ancestor = ancestry.parent(current);
15381 }
15382 false
15383}
15384
15385fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
15391 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
15392}
15393
15394fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
15395 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
15396 return None;
15397 }
15398 let semicolon = node.next_sibling()?;
15399 if semicolon.kind() != ";" || semicolon.is_missing() {
15400 return None;
15401 }
15402 let row = node.start_position().row;
15403 let mut start = node.start_byte();
15404 let mut sibling = node.prev_sibling();
15405 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
15406 if previous.kind() == ";" {
15407 break;
15408 }
15409 start = previous.start_byte();
15410 sibling = previous.prev_sibling();
15411 }
15412 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
15413}
15414
15415struct PrototypeMacroCandidate {
15419 run_start: usize,
15421 identifier_start: usize,
15424 inner_open_start: usize,
15428 inner_close_end: usize,
15430 outer_close_end: usize,
15432 semicolon_end: usize,
15435}
15436
15437impl PrototypeMacroCandidate {
15438 fn ranges(&self) -> [(usize, usize); 3] {
15444 [
15445 (self.run_start, self.identifier_start),
15446 (self.inner_open_start, self.inner_close_end),
15447 (self.outer_close_end, self.semicolon_end),
15448 ]
15449 }
15450}
15451
15452fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
15454 node.child(node.child_count().checked_sub(1)?)
15455 .filter(|child| child.kind() == ";" && !child.is_missing())
15456}
15457
15458fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
15463 matches!(
15464 node.kind(),
15465 "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
15466 ) && matches!(
15467 normalize_cpp_whitespace(node_text(node, source)).as_str(),
15468 "_" | "__P" | "OF" | "PROTO"
15469 )
15470}
15471
15472fn cpp_prototype_macro_qualified_parts<'tree>(
15476 node: Node<'tree>,
15477 source: &str,
15478) -> Option<(Node<'tree>, Node<'tree>)> {
15479 if node.kind() != "qualified_identifier" {
15480 return None;
15481 }
15482 let declared_name = node
15483 .child_by_field_name("scope")
15484 .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
15485 let macro_name = macro_decorated_unqualified_name(node)?;
15486 cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
15487}
15488
15489fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
15495 if arguments.kind() != "argument_list"
15496 || arguments.named_child_count() != 1
15497 || arguments.child_count() != 3
15498 || arguments
15499 .child(0)
15500 .is_none_or(|open| open.kind() != "(" || open.is_missing())
15501 || arguments
15502 .child(2)
15503 .is_none_or(|close| close.kind() != ")" || close.is_missing())
15504 {
15505 return None;
15506 }
15507 let inner = arguments.named_child(0)?;
15508 let close_index = match inner.kind() {
15509 "parenthesized_expression" => inner.child_count().checked_sub(1)?,
15510 "cast_expression" => inner.child_count().checked_sub(2)?,
15513 _ => return None,
15514 };
15515 (inner
15516 .child(0)
15517 .is_some_and(|open| open.kind() == "(" && !open.is_missing())
15518 && inner
15519 .child(close_index)
15520 .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
15521 .then_some(inner)
15522}
15523
15524fn cpp_prototype_macro_candidate_from_init_declaration(
15525 declaration: Node<'_>,
15526 source: &str,
15527) -> Option<PrototypeMacroCandidate> {
15528 let init = declaration
15529 .child_by_field_name("declarator")
15530 .filter(|declarator| declarator.kind() == "init_declarator")?;
15531 let malformed_declarator = init.child_by_field_name("declarator")?;
15532 let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
15533 cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
15534 } else {
15535 if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
15536 return None;
15537 }
15538 let declared_name_error = init
15539 .prev_named_sibling()
15540 .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
15541 let declared_name = declared_name_error
15542 .named_child(0)
15543 .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
15544 (declared_name, malformed_declarator)
15545 };
15546 let arguments = init
15547 .child_by_field_name("value")
15548 .filter(|value| value.kind() == "argument_list")?;
15549 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15550 let semicolon = cpp_direct_semicolon(declaration)?;
15551 let return_type = declaration.child_by_field_name("type")?;
15552 if return_type.end_byte() > declared_name.start_byte()
15553 || declared_name.end_byte() > macro_name.start_byte()
15554 || macro_name.end_byte() > arguments.start_byte()
15555 || arguments.end_byte() > semicolon.start_byte()
15556 {
15557 return None;
15558 }
15559 Some(PrototypeMacroCandidate {
15560 run_start: declaration.start_byte(),
15561 identifier_start: macro_name.start_byte(),
15562 inner_open_start: inner.start_byte(),
15563 inner_close_end: inner.end_byte(),
15564 outer_close_end: arguments.end_byte(),
15565 semicolon_end: semicolon.end_byte(),
15566 })
15567}
15568
15569fn cpp_prototype_macro_candidate_from_qualified_declaration(
15570 declaration: Node<'_>,
15571 source: &str,
15572) -> Option<PrototypeMacroCandidate> {
15573 let qualified = declaration
15574 .child_by_field_name("declarator")
15575 .filter(|declarator| declarator.kind() == "qualified_identifier")?;
15576 let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15577 let open_error = qualified
15578 .next_named_sibling()
15579 .filter(|next| next.kind() == "ERROR")?;
15580 let close_error = last_named_child(declaration)
15581 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
15582 if open_error.child_count() < 3
15583 || open_error
15584 .child(0)
15585 .is_none_or(|open| open.kind() != "(" || open.is_missing())
15586 || open_error
15587 .child(1)
15588 .is_none_or(|open| open.kind() != "(" || open.is_missing())
15589 || close_error.child_count() != 2
15590 || close_error
15591 .child(0)
15592 .is_none_or(|close| close.kind() != ")" || close.is_missing())
15593 || close_error
15594 .child(1)
15595 .is_none_or(|close| close.kind() != ")" || close.is_missing())
15596 {
15597 return None;
15598 }
15599 let inner_open = open_error.child(1)?;
15600 let inner_close = close_error.child(0)?;
15601 let outer_close = close_error.child(1)?;
15602 let semicolon = cpp_direct_semicolon(declaration)?;
15603 let return_type = declaration.child_by_field_name("type")?;
15604 if return_type.end_byte() > qualified.start_byte()
15605 || macro_name.end_byte() > open_error.start_byte()
15606 || inner_open.start_byte() > inner_close.end_byte()
15607 || inner_close.end_byte() > outer_close.start_byte()
15608 || outer_close.end_byte() > semicolon.start_byte()
15609 {
15610 return None;
15611 }
15612 Some(PrototypeMacroCandidate {
15613 run_start: declaration.start_byte(),
15614 identifier_start: macro_name.start_byte(),
15615 inner_open_start: inner_open.start_byte(),
15616 inner_close_end: inner_close.end_byte(),
15617 outer_close_end: outer_close.end_byte(),
15618 semicolon_end: semicolon.end_byte(),
15619 })
15620}
15621
15622fn cpp_prototype_macro_candidate_from_pointer_expression(
15623 statement: Node<'_>,
15624 source: &str,
15625) -> Option<PrototypeMacroCandidate> {
15626 if statement.kind() != "expression_statement"
15627 || statement.named_child_count() != 1
15628 || !statement.has_error()
15629 {
15630 return None;
15631 }
15632 let expansion = statement
15633 .named_child(0)
15634 .filter(|child| child.kind() == "parameter_pack_expansion")?;
15635 let binary = expansion
15636 .child_by_field_name("pattern")
15637 .filter(|pattern| pattern.kind() == "binary_expression")?;
15638 if binary.child_count() != 3
15639 || binary
15640 .child(1)
15641 .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
15642 || expansion
15643 .child(expansion.child_count().checked_sub(1)?)
15644 .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
15645 {
15646 return None;
15647 }
15648 let return_type = binary.child_by_field_name("left")?;
15649 let call = binary
15650 .child_by_field_name("right")
15651 .filter(|right| right.kind() == "call_expression")?;
15652 let qualified = call.child_by_field_name("function")?;
15653 let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15654 let arguments = call
15655 .child_by_field_name("arguments")
15656 .filter(|arguments| arguments.kind() == "argument_list")?;
15657 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15658 let semicolon = cpp_direct_semicolon(statement)?;
15659 if return_type.end_byte() > declared_name.start_byte()
15660 || macro_name.end_byte() > arguments.start_byte()
15661 || arguments.end_byte() > semicolon.start_byte()
15662 {
15663 return None;
15664 }
15665 Some(PrototypeMacroCandidate {
15666 run_start: statement.start_byte(),
15667 identifier_start: macro_name.start_byte(),
15668 inner_open_start: inner.start_byte(),
15669 inner_close_end: inner.end_byte(),
15670 outer_close_end: arguments.end_byte(),
15671 semicolon_end: semicolon.end_byte(),
15672 })
15673}
15674
15675fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
15680 let candidate = match node.kind() {
15681 "declaration" if node.has_error() => {
15682 cpp_prototype_macro_candidate_from_init_declaration(node, source)
15683 .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
15684 }
15685 "expression_statement" => {
15686 cpp_prototype_macro_candidate_from_pointer_expression(node, source)
15687 }
15688 _ => None,
15689 };
15690 candidate.into_iter().collect()
15691}
15692
15693fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
15694 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
15695 return false;
15696 }
15697 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
15698 return false;
15699 }
15700 let Some(declarator) = (if node.kind() == "function_definition" {
15701 node.child_by_field_name("declarator")
15702 .and_then(extract_function_declarator)
15703 } else {
15704 node.named_child(0)
15705 .filter(|child| child.kind() == "function_declarator")
15706 }) else {
15707 return false;
15708 };
15709 let Some(name) = cpp_function_declarator_name_node(declarator) else {
15710 return false;
15711 };
15712 declarator.start_byte() == node.start_byte()
15713 && name.kind() == "identifier"
15714 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
15715}
15716
15717fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15730 let region = cpp_reparse_region_items(source, start, end);
15731
15732 #[cfg(debug_assertions)]
15733 assert_eq!(
15734 region.as_ref().map(cpp_tree_shape),
15735 cpp_reparse_padded_class_body(source, start, end)
15736 .as_ref()
15737 .map(cpp_tree_shape),
15738 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
15739 prefix produces"
15740 );
15741
15742 region
15743}
15744
15745#[cfg(any(debug_assertions, test))]
15749fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15750 if start >= end {
15751 return None;
15755 }
15756 let bytes = source.as_bytes();
15757 let prefix = bytes.get(..start)?;
15758 let interior = bytes.get(start..end)?;
15759 let mut padded = Vec::with_capacity(end);
15760 padded.extend(
15761 prefix
15762 .iter()
15763 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
15764 );
15765 padded.extend_from_slice(interior);
15766 let padded = String::from_utf8(padded).ok()?;
15767 let mut parser = Parser::new();
15768 parser
15769 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15770 .ok()?;
15771 parser.parse(&padded, None)
15772}
15773
15774#[cfg(any(debug_assertions, test))]
15778fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
15779 let mut shape = Vec::new();
15780 let mut cursor = tree.root_node().walk();
15781 let mut stack = vec![tree.root_node()];
15782 while let Some(node) = stack.pop() {
15783 shape.push((
15784 node.kind(),
15785 node.start_byte(),
15786 node.end_byte(),
15787 node.start_position().row,
15788 node.start_position().column,
15789 node.is_named(),
15790 node.is_missing(),
15791 ));
15792 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
15793 stack.extend(children.into_iter().rev());
15794 }
15795 shape
15796}
15797
15798fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
15819 let mut cursor = root.walk();
15820 let mut saw_item = false;
15821 for child in root.named_children(&mut cursor) {
15822 match child.kind() {
15823 "comment" => {}
15824 "function_definition" => {
15825 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
15826 return false;
15827 }
15828 saw_item = true;
15829 }
15830 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
15831 _ => return false,
15832 }
15833 }
15834 saw_item
15835}
15836
15837fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
15847 if node.kind() != "ERROR" {
15848 return false;
15849 }
15850 let mut stack = Vec::new();
15851 let mut saw_function_declarator = false;
15852 let mut cursor = node.walk();
15853 for child in node.named_children(&mut cursor) {
15854 stack.push(child);
15855 }
15856 while let Some(current) = stack.pop() {
15857 match current.kind() {
15858 "ERROR" => {
15862 let mut cursor = current.walk();
15863 stack.extend(current.named_children(&mut cursor));
15864 }
15865 "function_declarator" => saw_function_declarator = true,
15866 _ => return false,
15867 }
15868 }
15869 saw_function_declarator
15870}
15871
15872fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
15873 if node.kind() != "ERROR" {
15874 return false;
15875 }
15876 let mut cursor = node.walk();
15877 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15878 let [explicit, constructor_error, destructor] = named.as_slice() else {
15879 return false;
15880 };
15881 let Some(constructor) = constructor_error.named_child(0) else {
15882 return false;
15883 };
15884 let Some(constructor_name) =
15885 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
15886 else {
15887 return false;
15888 };
15889 let Some(destructor_name) =
15890 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
15891 else {
15892 return false;
15893 };
15894 let Some(destroyed_type) = destructor_name.named_child(0) else {
15895 return false;
15896 };
15897 explicit.kind() == "explicit_function_specifier"
15898 && constructor_error.kind() == "ERROR"
15899 && constructor_error.named_child_count() == 1
15900 && constructor.kind() == "function_declarator"
15901 && constructor_name.kind() == "identifier"
15902 && destructor.kind() == "function_declarator"
15903 && destructor_name.kind() == "destructor_name"
15904 && destroyed_type.kind() == "identifier"
15905 && node_text(constructor_name, source) == node_text(destroyed_type, source)
15906}
15907
15908fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
15909 if node.kind() != "compound_statement" {
15910 return false;
15911 }
15912 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
15913 return false;
15914 };
15915 if prefix.kind() == "labeled_statement"
15916 && prefix.named_child(0).is_some_and(|label| {
15917 matches!(
15918 node_text(label, source).trim(),
15919 "public" | "private" | "protected"
15920 )
15921 })
15922 {
15923 return prefix.named_children(&mut prefix.walk()).any(|child| {
15924 child.kind() == "declaration"
15925 && child.has_error()
15926 && child
15927 .named_children(&mut child.walk())
15928 .any(cpp_reparsed_member_error_is_indexable)
15929 });
15930 }
15931 prefix.kind() == "declaration"
15936 && prefix.has_error()
15937 && prefix
15938 .named_children(&mut prefix.walk())
15939 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
15940}
15941
15942fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
15943 if !cpp_reparsed_member_error_is_indexable(node) {
15944 return false;
15945 }
15946 let Some(preproc) = node.next_named_sibling() else {
15947 return false;
15948 };
15949 preproc.kind() == "preproc_if"
15950 && preproc.has_error()
15951 && preproc
15952 .named_children(&mut preproc.walk())
15953 .any(|child| child.kind() == "expression_statement" && child.has_error())
15954 && preproc
15955 .next_named_sibling()
15956 .is_some_and(|body| body.kind() == "compound_statement")
15957}
15958
15959fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
15964 if node.kind() != "function_definition" {
15965 return None;
15966 }
15967 let body = node.child_by_field_name("body")?;
15968 if body.kind() != "compound_statement" {
15969 return None;
15970 }
15971 let open = body.child(0)?;
15972 let close = body.child(body.child_count().checked_sub(1)?)?;
15973 if open.kind() != "{"
15974 || open.is_missing()
15975 || close.kind() != "}"
15976 || close.is_missing()
15977 || close.end_byte() != body.end_byte()
15978 || body.end_byte() != node.end_byte()
15979 {
15980 return None;
15981 }
15982 Some(body)
15983}
15984
15985fn cpp_reparsed_member_function_errors_are_in_body(
15986 node: Node<'_>,
15987 body: Node<'_>,
15988 source: &str,
15989) -> bool {
15990 let mut cursor = node.walk();
15991 node.children(&mut cursor).all(|child| {
15992 same_node(child, body)
15993 || cpp_reparsed_member_attribute_error(child, source)
15994 || cpp_reparsed_member_signature_identifier_errors(child)
15995 || (!child.has_error() && !child.is_error() && !child.is_missing())
15996 })
15997}
15998
15999fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
16007 if !node.has_error() && !node.is_error() && !node.is_missing() {
16008 return false;
16009 }
16010 let mut stack = vec![node];
16011 let mut saw_error = false;
16012 while let Some(current) = stack.pop() {
16013 if current.is_missing() {
16014 return false;
16015 }
16016 if current.kind() == "ERROR" {
16017 saw_error = true;
16018 let mut cursor = current.walk();
16019 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
16020 if children
16021 .iter()
16022 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
16023 {
16024 return false;
16025 }
16026 stack.extend(children);
16027 continue;
16028 }
16029 let mut cursor = current.walk();
16030 stack.extend(current.children(&mut cursor));
16031 }
16032 saw_error
16033}
16034
16035fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
16036 node.kind() == "ERROR"
16037 && node.named_child_count() == 1
16038 && node.named_child(0).is_some_and(|attribute| {
16039 attribute.kind() == "identifier"
16040 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
16041 })
16042}
16043
16044fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
16050 let Some(body) = cpp_reparsed_member_function_body(node) else {
16051 return false;
16052 };
16053 let mut cursor = node.walk();
16054 let named = node
16055 .named_children(&mut cursor)
16056 .filter(|child| child.kind() != "comment")
16057 .collect::<Vec<_>>();
16058 let [type_node, error, attribute, body_node] = named.as_slice() else {
16059 return false;
16060 };
16061 if !same_node(*body_node, body)
16062 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16063 || attribute.kind() != "identifier"
16064 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16065 || error.kind() != "ERROR"
16066 || error.named_child_count() != 1
16067 {
16068 return false;
16069 }
16070 error
16071 .named_child(0)
16072 .is_some_and(cpp_reparsed_attribute_callable_declarator)
16073}
16074
16075fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
16076 cpp_structured_type_path(node, source).is_some()
16077 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
16078}
16079
16080fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16081 let Some(body) = cpp_reparsed_member_function_body(node) else {
16082 return false;
16083 };
16084 let mut cursor = node.walk();
16085 let named = node
16086 .named_children(&mut cursor)
16087 .filter(|child| child.kind() != "comment")
16088 .collect::<Vec<_>>();
16089 let [friend, return_error, declarator, body_node] = named.as_slice() else {
16090 return false;
16091 };
16092 let Some(return_type) = return_error.named_child(0) else {
16093 return false;
16094 };
16095 same_node(*body_node, body)
16096 && friend.kind() == "type_identifier"
16097 && node_text(*friend, source) == "friend"
16098 && return_error.kind() == "ERROR"
16099 && return_error.named_child_count() == 1
16100 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16101 && extract_function_declarator(*declarator)
16102 .and_then(cpp_function_declarator_name_node)
16103 .is_some()
16104}
16105
16106fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16107 let Some(body) = cpp_reparsed_member_function_body(node) else {
16108 return false;
16109 };
16110 let mut cursor = node.walk();
16111 let named = node
16112 .named_children(&mut cursor)
16113 .filter(|child| child.kind() != "comment")
16114 .collect::<Vec<_>>();
16115 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
16116 return false;
16117 };
16118 let Some(return_type) = return_error.named_child(0) else {
16119 return false;
16120 };
16121 same_node(*body_node, body)
16122 && prefix
16123 .iter()
16124 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
16125 && attribute.kind() == "type_identifier"
16126 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16127 && return_error.kind() == "ERROR"
16128 && return_error.named_child_count() == 1
16129 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16130 && extract_function_declarator(*declarator)
16131 .and_then(cpp_function_declarator_name_node)
16132 .is_some()
16133}
16134
16135fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16141 let Some(body) = cpp_reparsed_member_function_body(node) else {
16142 return false;
16143 };
16144 let mut cursor = node.walk();
16145 let named = node
16146 .named_children(&mut cursor)
16147 .filter(|child| child.kind() != "comment")
16148 .collect::<Vec<_>>();
16149 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
16150 return false;
16151 };
16152 let Some(template_name) = template_type.child_by_field_name("name") else {
16153 return false;
16154 };
16155 let Some(arguments) = template_type.child_by_field_name("arguments") else {
16156 return false;
16157 };
16158 let Some(return_type) = return_error.named_child(0) else {
16159 return false;
16160 };
16161 let mut cursor = template_type.walk();
16162 let template_errors = template_type
16163 .named_children(&mut cursor)
16164 .filter(|child| child.kind() == "ERROR")
16165 .collect::<Vec<_>>();
16166 let [comment_error] = template_errors.as_slice() else {
16167 return false;
16168 };
16169 let mut cursor = comment_error.walk();
16170 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
16171 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
16172 return false;
16173 };
16174 same_node(*body_node, body)
16175 && template_type.kind() == "template_type"
16176 && template_name.kind() == "type_identifier"
16177 && matches!(
16178 node_text(template_name, source).trim(),
16179 "public" | "private" | "protected"
16180 )
16181 && arguments.kind() == "template_argument_list"
16182 && arguments.named_child_count() > 0
16183 && !arguments.has_error()
16184 && !colon.is_named()
16185 && colon.kind() == ":"
16186 && comments.iter().all(|child| child.kind() == "comment")
16187 && !template_keyword.is_named()
16188 && template_keyword.kind() == "template"
16189 && return_error.kind() == "ERROR"
16190 && return_error.named_child_count() == 1
16191 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16192 && extract_function_declarator(*declarator)
16193 .and_then(cpp_function_declarator_name_node)
16194 .is_some()
16195}
16196
16197fn cpp_reparsed_preprocessor_constructor<'tree>(
16203 node: Node<'tree>,
16204 class_name: &str,
16205 source: &str,
16206) -> Option<Node<'tree>> {
16207 if node.kind() != "labeled_statement" {
16208 return None;
16209 }
16210 let mut cursor = node.walk();
16211 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16212 let [label, directive_error, declaration] = named.as_slice() else {
16213 return None;
16214 };
16215 if label.kind() != "statement_identifier"
16216 || !matches!(
16217 node_text(*label, source),
16218 "public" | "private" | "protected"
16219 )
16220 || directive_error.kind() != "ERROR"
16221 || directive_error.child_count() != 1
16222 || directive_error
16223 .child(0)
16224 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
16225 || declaration.kind() != "declaration"
16226 || declaration.named_child_count() != 2
16227 {
16228 return None;
16229 }
16230 let apparent_type = declaration.child_by_field_name("type")?;
16231 if apparent_type.kind() != "type_identifier"
16232 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
16233 {
16234 return None;
16235 }
16236 let declarator = declaration.child_by_field_name("declarator")?;
16237 let function = extract_function_declarator(declarator)?;
16238 let name = cpp_function_declarator_name_node(function)?;
16239 (node_text(name, source) == class_name).then_some(*declaration)
16240}
16241
16242fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
16243 if extract_function_declarator(node)
16244 .and_then(cpp_function_declarator_name_node)
16245 .is_some()
16246 {
16247 return true;
16248 }
16249 node.kind() == "init_declarator"
16250 && node
16251 .child_by_field_name("declarator")
16252 .is_some_and(|declarator| declarator.kind() == "identifier")
16253 && node
16254 .child_by_field_name("value")
16255 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
16256}
16257
16258fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
16263 if node.kind() != "ERROR" || node.named_child_count() != 3 {
16264 return false;
16265 }
16266 let mut cursor = node.walk();
16267 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16268 let [type_node, function_declarator, attribute] = named.as_slice() else {
16269 return false;
16270 };
16271 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16272 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
16273 || attribute.kind() != "identifier"
16274 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16275 {
16276 return false;
16277 }
16278 let Some(preproc) =
16279 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16280 else {
16281 return false;
16282 };
16283 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
16284 .filter(|sibling| sibling.kind() == "compound_statement")
16285 else {
16286 return false;
16287 };
16288 let Some(open) = body.child(0) else {
16289 return false;
16290 };
16291 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
16292 return false;
16293 };
16294 let Some(condition) = preproc.child_by_field_name("condition") else {
16295 return false;
16296 };
16297 let mut cursor = preproc.walk();
16298 let payload = preproc
16299 .named_children(&mut cursor)
16300 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
16301 .collect::<Vec<_>>();
16302 let [requires_statement] = payload.as_slice() else {
16303 return false;
16304 };
16305 let requires_clause = requires_statement.named_child(0);
16306
16307 open.kind() == "{"
16308 && !open.is_missing()
16309 && close.kind() == "}"
16310 && !close.is_missing()
16311 && close.end_byte() == body.end_byte()
16312 && requires_statement.kind() == "expression_statement"
16313 && requires_statement.named_child_count() == 1
16314 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
16315}
16316
16317fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16318 let mut sibling = node.next_named_sibling();
16319 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16320 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
16321 }
16322 sibling
16323}
16324
16325fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16326 let mut sibling = node.prev_named_sibling();
16327 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16328 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
16329 }
16330 sibling
16331}
16332
16333fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
16334 let Some(preproc) =
16335 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16336 else {
16337 return false;
16338 };
16339 let Some(error) =
16340 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
16341 else {
16342 return false;
16343 };
16344 cpp_reparsed_attribute_requires_error(error, source)
16345}
16346
16347fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
16348 node: Node<'tree>,
16349 source: &str,
16350) -> Option<Node<'tree>> {
16351 if node.kind() != "ERROR" {
16352 return None;
16353 }
16354 let mut cursor = node.walk();
16355 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16356 let [parameter, macro_name, message] = named.as_slice() else {
16357 return None;
16358 };
16359 let parameter_name = parameter.named_child(0)?;
16360 (parameter.kind() == "type_parameter_declaration"
16361 && parameter_name.kind() == "type_identifier"
16362 && macro_name.kind() == "type_identifier"
16363 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16364 && message.kind() == "string_literal")
16365 .then_some(parameter_name)
16366}
16367
16368fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
16373 node: Node<'tree>,
16374 source: &str,
16375) -> Option<Node<'tree>> {
16376 if node.kind() != "ERROR" {
16377 return None;
16378 }
16379 let mut cursor = node.walk();
16380 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16381 let [parameter, macro_name, message, constraint] = named.as_slice() else {
16382 return None;
16383 };
16384 let parameter_name = parameter.named_child(0)?;
16385 let constraint_scope = constraint.child_by_field_name("scope")?;
16386 let constraint_template = constraint.child_by_field_name("name")?;
16387 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16388 let mut argument_cursor = constraint_arguments.walk();
16389 let constraint_types = constraint_arguments
16390 .named_children(&mut argument_cursor)
16391 .collect::<Vec<_>>();
16392 if parameter.kind() != "type_parameter_declaration"
16393 || parameter_name.kind() != "type_identifier"
16394 || macro_name.kind() != "type_identifier"
16395 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16396 || message.kind() != "string_literal"
16397 || constraint.kind() != "qualified_identifier"
16398 || constraint_scope.kind() != "namespace_identifier"
16399 || !matches!(
16400 constraint_template.kind(),
16401 "template_function" | "template_type"
16402 )
16403 || !matches!(constraint_types.as_slice(), [left, right]
16404 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16405 || constraint_arguments.has_error()
16406 {
16407 return None;
16408 }
16409 let parameter_text = node_text(parameter_name, source);
16410 let mut stack = constraint_types;
16411 while let Some(current) = stack.pop() {
16412 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
16413 return Some(parameter_name);
16414 }
16415 let mut cursor = current.walk();
16416 stack.extend(current.named_children(&mut cursor));
16417 }
16418 None
16419}
16420
16421fn cpp_reparsed_template_macro_companion_is_indexable(
16422 node: Node<'_>,
16423 parameter_name: Node<'_>,
16424 source: &str,
16425) -> bool {
16426 let Some(body) = cpp_reparsed_member_function_body(node) else {
16427 return false;
16428 };
16429 let mut cursor = node.walk();
16430 let named = node
16431 .named_children(&mut cursor)
16432 .filter(|child| child.kind() != "comment")
16433 .collect::<Vec<_>>();
16434 let [
16435 constraint,
16436 close_error,
16437 storage,
16438 return_error,
16439 declarator,
16440 body_node,
16441 ] = named.as_slice()
16442 else {
16443 return false;
16444 };
16445 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
16446 return false;
16447 };
16448 let Some(constraint_template) = constraint.child_by_field_name("name") else {
16449 return false;
16450 };
16451 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
16452 return false;
16453 };
16454 let Some(return_type) = return_error.named_child(0) else {
16455 return false;
16456 };
16457 let mut cursor = constraint_arguments.walk();
16458 let constraint_types = constraint_arguments
16459 .named_children(&mut cursor)
16460 .collect::<Vec<_>>();
16461 same_node(*body_node, body)
16462 && constraint.kind() == "qualified_identifier"
16463 && constraint_scope.kind() == "namespace_identifier"
16464 && constraint_template.kind() == "template_type"
16465 && matches!(constraint_types.as_slice(), [left, right]
16466 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16467 && !constraint_arguments.has_error()
16468 && close_error.kind() == "ERROR"
16469 && close_error.named_child_count() == 0
16470 && storage.kind() == "storage_class_specifier"
16471 && return_error.kind() == "ERROR"
16472 && return_error.named_child_count() == 1
16473 && return_type.kind() == "identifier"
16474 && node_text(return_type, source) == node_text(parameter_name, source)
16475 && extract_function_declarator(*declarator)
16476 .and_then(cpp_function_declarator_name_node)
16477 .is_some()
16478}
16479
16480fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
16481 node: Node<'tree>,
16482 parameter_name: Node<'_>,
16483 source: &str,
16484) -> Option<Node<'tree>> {
16485 let body = cpp_reparsed_member_function_body(node)?;
16486 let constraint = node.child_by_field_name("type")?;
16487 let constraint_template = constraint.child_by_field_name("name")?;
16488 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16489 let mut argument_cursor = constraint_arguments.walk();
16490 let constraint_types = constraint_arguments
16491 .named_children(&mut argument_cursor)
16492 .collect::<Vec<_>>();
16493 if constraint.kind() != "qualified_identifier"
16494 || constraint_template.kind() != "template_type"
16495 || !matches!(constraint_types.as_slice(), [left, right]
16496 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16497 || constraint_arguments.has_error()
16498 || node
16499 .child_by_field_name("body")
16500 .is_none_or(|candidate| !same_node(candidate, body))
16501 {
16502 return None;
16503 }
16504
16505 let mut cursor = node.walk();
16506 let recovery_errors = node
16507 .named_children(&mut cursor)
16508 .filter(|child| child.kind() == "ERROR")
16509 .collect::<Vec<_>>();
16510 if !recovery_errors
16511 .iter()
16512 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
16513 || !recovery_errors.iter().all(|error| {
16514 error.named_child_count() == 0
16515 || cpp_reparsed_constraint_macro_error(*error, source)
16516 || (error.named_child_count() == 1
16517 && error
16518 .named_child(0)
16519 .is_some_and(|child| child.kind() == "function_declarator"))
16520 })
16521 {
16522 return None;
16523 }
16524
16525 let parameter_text = node_text(parameter_name, source);
16526 let mut declarators = node
16527 .child_by_field_name("declarator")
16528 .and_then(extract_function_declarator)
16529 .into_iter()
16530 .collect::<Vec<_>>();
16531 for error in recovery_errors {
16532 let mut stack = vec![error];
16533 while let Some(current) = stack.pop() {
16534 if current.kind() == "function_declarator" {
16535 declarators.push(current);
16536 }
16537 let mut cursor = current.walk();
16538 stack.extend(current.named_children(&mut cursor));
16539 }
16540 }
16541 declarators.into_iter().find(|declarator| {
16542 cpp_function_declarator_name_node(*declarator)
16543 .is_some_and(|name| name.kind() == "identifier")
16544 && declarator
16545 .child_by_field_name("parameters")
16546 .is_some_and(|parameters| {
16547 parameters
16548 .named_children(&mut parameters.walk())
16549 .filter_map(|parameter| parameter.child_by_field_name("type"))
16550 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
16551 })
16552 })
16553}
16554
16555fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
16556 node: Node<'_>,
16557 parameter_name: Node<'_>,
16558 source: &str,
16559) -> bool {
16560 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
16561}
16562
16563fn cpp_reparsed_template_macro_function_companion_is_indexable(
16564 node: Node<'_>,
16565 parameter_name: Node<'_>,
16566 source: &str,
16567) -> bool {
16568 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
16569 return false;
16570 }
16571 let Some(return_type) = node.child_by_field_name("type") else {
16572 return false;
16573 };
16574 let Some(function_declarator) = node
16575 .child_by_field_name("declarator")
16576 .and_then(extract_function_declarator)
16577 else {
16578 return false;
16579 };
16580 if cpp_function_declarator_name_node(function_declarator).is_none()
16581 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
16582 {
16583 return false;
16584 }
16585 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
16586 return false;
16587 };
16588 let parameter_text = node_text(parameter_name, source);
16589 parameters
16590 .named_children(&mut parameters.walk())
16591 .any(|parameter| {
16592 parameter
16593 .child_by_field_name("type")
16594 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
16595 })
16596}
16597
16598fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
16599 if node.kind() != "ERROR" {
16600 return false;
16601 }
16602 let mut stack = vec![node];
16603 while let Some(current) = stack.pop() {
16604 let macro_shape = match current.kind() {
16605 "call_expression" => current
16606 .child_by_field_name("function")
16607 .zip(current.child_by_field_name("arguments")),
16608 "init_declarator" => current
16609 .child_by_field_name("declarator")
16610 .zip(current.child_by_field_name("value")),
16611 _ => None,
16612 };
16613 if let Some((name, arguments)) = macro_shape
16614 && name.kind() == "identifier"
16615 && arguments.kind() == "argument_list"
16616 && arguments.named_child_count() >= 2
16617 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
16618 {
16619 return true;
16620 }
16621 let mut cursor = current.walk();
16622 stack.extend(current.named_children(&mut cursor));
16623 }
16624 false
16625}
16626
16627fn cpp_recovered_template_macro_constructor<'tree>(
16628 node: Node<'tree>,
16629 source: &str,
16630) -> Option<(Node<'tree>, Node<'tree>)> {
16631 let mut prefix = node.prev_named_sibling()?;
16632 while prefix.kind() == "comment" {
16633 prefix = prefix.prev_named_sibling()?;
16634 }
16635 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
16636 let parameter = parameter_name
16637 .parent()
16638 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
16639 let declarator =
16640 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
16641 Some((declarator, parameter))
16642}
16643
16644fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
16645 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
16646 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16647 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
16648 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
16649 function,
16650 parameter_name,
16651 source,
16652 )
16653 });
16654 }
16655 let Some(parameter_name) =
16656 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
16657 else {
16658 return false;
16659 };
16660 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16661 cpp_reparsed_template_macro_function_companion_is_indexable(
16662 function,
16663 parameter_name,
16664 source,
16665 )
16666 })
16667}
16668
16669fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16670 let function_name = node
16671 .child_by_field_name("declarator")
16672 .and_then(extract_function_declarator)
16673 .and_then(cpp_function_declarator_name_node);
16674 if let Some(body) = cpp_reparsed_member_function_body(node)
16675 && function_name.is_some()
16676 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
16677 {
16678 return true;
16679 }
16680 cpp_reparsed_attribute_member_function(node, source)
16681 || cpp_reparsed_friend_function_is_indexable(node, source)
16682 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
16683 || cpp_reparsed_access_template_function_is_indexable(node, source)
16684 || cpp_recovered_template_macro_constructor(node, source).is_some()
16685}
16686
16687fn cpp_reparsed_macro_attribute_member_sequence(
16694 children: &[Node<'_>],
16695 index: usize,
16696 source: &str,
16697) -> bool {
16698 let Some(prefix) = children.get(index).copied() else {
16699 return false;
16700 };
16701 let declaration = if prefix.kind() == "labeled_statement" {
16702 prefix
16703 .named_child(prefix.named_child_count().saturating_sub(1))
16704 .filter(|child| child.kind() == "declaration")
16705 } else {
16706 (prefix.kind() == "declaration").then_some(prefix)
16707 };
16708 let Some(declaration) = declaration else {
16709 return false;
16710 };
16711 if !declaration.has_error()
16712 || declaration
16713 .child_by_field_name("declarator")
16714 .and_then(extract_function_declarator)
16715 .and_then(cpp_function_declarator_name_node)
16716 .is_none()
16717 {
16718 return false;
16719 }
16720 let Some(attribute_statement) = children.get(index + 1).copied() else {
16721 return false;
16722 };
16723 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
16724 .then(|| attribute_statement.named_child(0))
16725 .flatten()
16726 .filter(|child| child.kind() == "call_expression")
16727 else {
16728 return false;
16729 };
16730 let Some(attribute_name) = attribute_call
16731 .child_by_field_name("function")
16732 .filter(|function| function.kind() == "identifier")
16733 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
16734 else {
16735 return false;
16736 };
16737 if !cpp_export_macro_token(&attribute_name) {
16738 return false;
16739 }
16740 let Some(body) = children.get(index + 2).copied() else {
16741 return false;
16742 };
16743 body.kind() == "compound_statement"
16744 && body.child(0).is_some_and(|open| open.kind() == "{")
16745 && body
16746 .child(body.child_count().saturating_sub(1))
16747 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
16748 && declaration.end_byte() <= attribute_statement.start_byte()
16749 && attribute_statement.end_byte() <= body.start_byte()
16750}
16751
16752fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
16760 if node.kind() != "ERROR" {
16761 return false;
16762 }
16763 let run = stranded_declaration_run(node, source);
16764 run.complete && !run.declarations.is_empty()
16765}
16766
16767fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
16768 let mut cursor = root.walk();
16769 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
16770 let mut saw_member = false;
16771 let mut index = 0;
16772 while index < children.len() {
16773 let child = children[index];
16774 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
16775 saw_member = true;
16776 index += 3;
16777 continue;
16778 }
16779 if let Some(recovered) = fragmented_class_body(child, source) {
16780 let Some(tree) = cpp_reparse_fragmented_class_body(
16781 source,
16782 recovered.body.reparse_start,
16783 recovered.body.reparse_end,
16784 ) else {
16785 return false;
16786 };
16787 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
16788 return false;
16789 }
16790 saw_member = true;
16791 index += 1;
16792 while index < children.len()
16793 && children[index].end_byte() <= recovered.body.class_range.end_byte
16794 {
16795 index += 1;
16796 }
16797 continue;
16798 }
16799 match child.kind() {
16800 "comment" => {}
16801 "labeled_statement" => saw_member = true,
16802 "function_definition" => {
16803 if child.has_error()
16804 && !cpp_reparsed_member_function_is_indexable(child, source)
16805 && cpp_sentinel_macro_region(child, source).is_none()
16806 {
16807 return false;
16808 }
16809 saw_member = true;
16810 }
16811 "expression_statement" if is_string_attribute_macro_statement(child) => {}
16815 "ERROR"
16816 if (cpp_reparsed_member_error_is_indexable(child)
16817 || cpp_reparsed_adjacent_copy_control_error(child, source)
16818 || cpp_reparsed_stranded_member_error(child, source))
16819 && (child
16820 .next_named_sibling()
16821 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
16822 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
16823 {
16824 saw_member = true;
16825 }
16826 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
16827 saw_member = true;
16828 }
16829 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
16830 saw_member = true;
16831 }
16832 "expression_statement"
16833 if cpp_is_stray_semicolon(child, source)
16834 && child.prev_named_sibling().is_some_and(|error| {
16835 cpp_reparsed_member_error_is_indexable(error)
16836 || cpp_reparsed_adjacent_copy_control_error(error, source)
16837 || cpp_reparsed_stranded_member_error(error, source)
16838 }) =>
16839 {
16840 saw_member = true;
16841 }
16842 "compound_statement"
16843 if cpp_reparsed_constructor_body_is_indexable(child, source)
16844 || cpp_reparsed_attribute_requires_body(child, source) =>
16845 {
16846 saw_member = true;
16847 }
16848 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
16849 _ => return false,
16850 }
16851 index += 1;
16852 }
16853 saw_member
16854}
16855
16856fn cpp_reparsed_synthetic_initializer_constructor_range(
16864 root: Node<'_>,
16865 class_name: &str,
16866 source: &str,
16867 constructor_end: usize,
16868) -> Option<std::ops::Range<usize>> {
16869 let mut stack = {
16870 let mut cursor = root.walk();
16871 root.named_children(&mut cursor).collect::<Vec<_>>()
16872 };
16873 while let Some(current) = stack.pop() {
16874 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
16875 current,
16876 class_name,
16877 source,
16878 constructor_end,
16879 ) {
16880 return Some(range);
16881 }
16882 if current.kind() == "ERROR" {
16883 let mut cursor = current.walk();
16884 stack.extend(current.named_children(&mut cursor));
16885 }
16886 }
16887 None
16888}
16889
16890fn cpp_reparsed_merged_inline_constructor<'tree>(
16897 root: Node<'tree>,
16898 class_name: &str,
16899 source: &str,
16900) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
16901 let mut stack = vec![root];
16902 while let Some(current) = stack.pop() {
16903 if current.kind() != "labeled_statement" {
16904 let mut cursor = current.walk();
16905 stack.extend(current.named_children(&mut cursor));
16906 continue;
16907 }
16908 let declaration = current
16909 .named_children(&mut current.walk())
16910 .find(|child| child.kind() == "declaration")?;
16911 if declaration
16912 .child_by_field_name("type")
16913 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
16914 {
16915 continue;
16916 }
16917 let following = declaration
16918 .child_by_field_name("declarator")
16919 .and_then(extract_function_declarator)
16920 .and_then(cpp_function_declarator_name_node);
16921 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
16922 continue;
16923 }
16924 let mut declaration_cursor = declaration.walk();
16925 let Some(error) = declaration
16926 .named_children(&mut declaration_cursor)
16927 .find(|child| child.kind() == "ERROR")
16928 else {
16929 continue;
16930 };
16931 let mut error_cursor = error.walk();
16932 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
16933 let Some(constructor) = error_children.iter().copied().find(|child| {
16934 child.kind() == "function_declarator"
16935 && cpp_function_declarator_name_node(*child)
16936 .is_some_and(|name| node_text(name, source).trim() == class_name)
16937 }) else {
16938 continue;
16939 };
16940 let Some(body) = error_children.iter().copied().find_map(|child| {
16941 (child.kind() == "init_declarator")
16942 .then(|| child.child_by_field_name("value"))
16943 .flatten()
16944 .filter(|value| value.kind() == "initializer_list")
16945 }) else {
16946 continue;
16947 };
16948 if constructor.end_byte() > body.start_byte() {
16949 continue;
16950 }
16951 return Some((constructor.start_byte()..body.end_byte(), body));
16952 }
16953 None
16954}
16955
16956fn cpp_reparsed_synthetic_initializer_constructor(
16957 node: Node<'_>,
16958 class_name: &str,
16959 source: &str,
16960 constructor_end: usize,
16961) -> Option<std::ops::Range<usize>> {
16962 if node.kind() != "labeled_statement" {
16963 return None;
16964 }
16965 let mut cursor = node.walk();
16966 let named = node
16967 .named_children(&mut cursor)
16968 .filter(|child| child.kind() != "comment")
16969 .collect::<Vec<_>>();
16970 let label = named.first()?;
16971 if label.kind() != "statement_identifier"
16972 || !matches!(
16973 node_text(*label, source).trim(),
16974 "public" | "private" | "protected"
16975 )
16976 {
16977 return None;
16978 }
16979 let call_error_index = named.iter().position(|child| {
16980 if child.kind() != "ERROR" {
16981 return false;
16982 }
16983 let mut stack = vec![*child];
16984 while let Some(current) = stack.pop() {
16985 if current.kind() == "call_expression"
16986 && current
16987 .child_by_field_name("function")
16988 .is_some_and(|function| {
16989 function.kind() == "identifier"
16990 && node_text(function, source).trim() == class_name
16991 })
16992 {
16993 return true;
16994 }
16995 let mut cursor = current.walk();
16996 stack.extend(current.named_children(&mut cursor));
16997 }
16998 false
16999 })?;
17000 let constructor_call = {
17001 let mut stack = vec![named[call_error_index]];
17002 let mut found = None;
17003 while let Some(current) = stack.pop() {
17004 if current.kind() == "call_expression"
17005 && current
17006 .child_by_field_name("function")
17007 .is_some_and(|function| {
17008 function.kind() == "identifier"
17009 && node_text(function, source).trim() == class_name
17010 })
17011 {
17012 found = Some(current);
17013 break;
17014 }
17015 let mut cursor = current.walk();
17016 stack.extend(current.named_children(&mut cursor));
17017 }
17018 found
17019 };
17020 let constructor_call = constructor_call?;
17021 named.iter().skip(call_error_index + 1).find(|child| {
17022 child.kind() == "declaration" && child.has_error() && {
17023 let mut cursor = child.walk();
17024 child.named_children(&mut cursor).any(|declarator| {
17025 declarator.kind() == "init_declarator"
17026 && declarator
17027 .child_by_field_name("declarator")
17028 .is_some_and(|declarator| declarator.kind() == "function_declarator")
17029 && declarator
17030 .child_by_field_name("value")
17031 .is_some_and(|value| value.kind() == "initializer_list")
17032 })
17033 }
17034 })?;
17035 Some(constructor_call.start_byte()..constructor_end)
17036}
17037
17038fn cpp_reparsed_exact_constructor_declarator<'tree>(
17039 root: Node<'tree>,
17040 start: usize,
17041 class_name: &str,
17042 source: &str,
17043) -> Option<Node<'tree>> {
17044 let mut candidate = None;
17045 let mut stack = vec![root];
17046 while let Some(current) = stack.pop() {
17047 if current.kind() == "function_declarator"
17048 && current.start_byte() == start
17049 && cpp_function_declarator_name_node(current)
17050 .is_some_and(|name| node_text(name, source).trim() == class_name)
17051 {
17052 if candidate.is_some() {
17053 return None;
17054 }
17055 candidate = Some(current);
17056 continue;
17057 }
17058 let mut cursor = current.walk();
17059 stack.extend(current.named_children(&mut cursor));
17060 }
17061 candidate
17062}
17063
17064fn cpp_is_indexable_item_kind(kind: &str) -> bool {
17065 matches!(
17066 kind,
17067 "namespace_definition"
17068 | "class_specifier"
17069 | "struct_specifier"
17070 | "union_specifier"
17071 | "enum_specifier"
17072 | "function_definition"
17073 | "template_declaration"
17074 | "declaration"
17075 | "field_declaration"
17076 | "alias_declaration"
17077 | "static_assert_declaration"
17078 | "type_definition"
17079 | "using_declaration"
17080 | "linkage_specification"
17081 | "preproc_def"
17082 | "preproc_function_def"
17083 | "preproc_include"
17084 | "preproc_if"
17085 | "preproc_ifdef"
17086 | "preproc_call"
17087 )
17088}
17089
17090#[cfg(test)]
17091mod tests {
17092 use super::*;
17093 use crate::adapter::parse_cpp_file;
17094 use brokk_bifrost_core::analyzer::parsed_file::{
17095 finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
17096 start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
17097 };
17098 use std::fmt::Write;
17099
17100 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
17101 let mut parser = tree_sitter::Parser::new();
17102 parser
17103 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17104 .unwrap();
17105 let tree = parser.parse(source, None).unwrap();
17106 let file = ProjectFile::new(std::env::temp_dir(), name);
17107 parse_cpp_file(&file, source, &tree)
17108 }
17109
17110 #[test]
17111 fn displaced_terminator_ignores_nested_initializer_endif() {
17112 let source = "#ifdef ENABLE_ITEMS\n\
17113struct Item { int reg; };\n\
17114static const struct Item items[] = {{1}};\n\
17115static const struct Item extras[] = {\n\
17116 {0},\n\
17117#ifndef REDUCED\n\
17118 {1},\n\
17119#endif\n\
17120};\n\
17121int read_item(int i) { return items[i].reg; }\n\
17122#endif\n";
17123 let mut parser = tree_sitter::Parser::new();
17124 parser
17125 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17126 .expect("C++ grammar");
17127 let tree = parser.parse(source, None).expect("fixture tree");
17128 let conditional = tree
17129 .root_node()
17130 .named_child(0)
17131 .filter(|node| node.kind() == "preproc_ifdef")
17132 .expect("outer conditional");
17133
17134 assert!(conditional.has_error());
17135 assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
17136 assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
17137 }
17138
17139 #[test]
17140 fn pyobject_head_field_recovery_publishes_only_the_real_member() {
17141 let source = "struct Image { PyObject_HEAD Imaging image; };";
17142 let parsed = parse_cpp_declarations(source, "image.h");
17143 let names = parsed
17144 .declarations()
17145 .iter()
17146 .map(|unit| unit.fq_name())
17147 .collect::<Vec<_>>();
17148
17149 assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
17150 assert!(
17151 names.iter().all(|name| name != "Image.Imaging"),
17152 "the pseudo-declarator must not become a field: {names:#?}"
17153 );
17154
17155 let pointer = parse_cpp_declarations(
17156 "struct Image { PyObject_HEAD Imaging *image; };",
17157 "image-pointer.h",
17158 );
17159 let pointer_names = pointer
17160 .declarations()
17161 .iter()
17162 .map(|unit| unit.fq_name())
17163 .collect::<Vec<_>>();
17164 assert!(
17165 pointer_names.iter().any(|name| name == "Image.image"),
17166 "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
17167 );
17168 assert!(
17169 pointer_names.iter().all(|name| name != "Image.Imaging"),
17170 "the pointer recovery error must not become a field: {pointer_names:#?}"
17171 );
17172
17173 let unrelated_macro = parse_cpp_declarations(
17174 "struct Image { OTHER_HEAD Imaging other; };",
17175 "image-near-miss.h",
17176 );
17177 let unrelated_names = unrelated_macro
17178 .declarations()
17179 .iter()
17180 .map(|unit| unit.fq_name())
17181 .collect::<Vec<_>>();
17182 assert!(
17183 unrelated_names.iter().all(|name| name != "Image.other"),
17184 "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
17185 );
17186 }
17187
17188 #[test]
17189 fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
17190 let source = r#"namespace testing {
17191namespace internal {
17192 namespace detail {
17193 class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
17194 public:
17195 int value() const { return count_ + 1; }
17196 private:
17197 int count_;
17198 };
17199 class GTEST_API_ [[nodiscard]] OtherReporter {
17200 public:
17201 int value() const { return count_ + 2; }
17202 private:
17203 int count_;
17204 };
17205 }
17206
17207 template <typename T>
17208 void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
17209
17210 class TailReporter {};
17211}
17212}
17213"#;
17214 let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
17215 let declarations = parsed.declarations();
17216 let mut parser = tree_sitter::Parser::new();
17217 parser
17218 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17219 .unwrap();
17220 let tree = parser.parse(source, None).unwrap();
17221 let tail_start = source.find("TailReporter").expect("tail class");
17222 let tail_node = tree
17223 .root_node()
17224 .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
17225 .expect("tail class AST node");
17226 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
17227 assert!(
17228 tree.root_node().has_error(),
17229 "the malformed class must exercise recovery"
17230 );
17231 assert!(index.region_at(tail_start).is_some());
17232 assert_eq!(
17233 index.enclosing_namespace_components(tail_node, source),
17234 ["testing", "internal"]
17235 );
17236 let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
17237 let mut recovered_parsed = ParsedFile::new(String::new());
17238 let class_unit = CodeUnit::new_fq(
17239 file.clone(),
17240 CodeUnitType::Class,
17241 "testing",
17242 "ScopedFakeTestPartResultReporter",
17243 cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
17244 );
17245 let scope = ScopeInfo {
17246 package_name: "testing".to_string(),
17247 module: None,
17248 class_unit: Some(class_unit),
17249 template_signature: Some("<typename T>".to_string()),
17250 template_metadata: Some(CppTemplateMetadata {
17251 primary_name: "ScopedFakeTestPartResultReporter".to_string(),
17252 primary_fq_name: String::new(),
17253 parameters: Vec::new(),
17254 specialization_arguments: Vec::new(),
17255 alias_target: None,
17256 }),
17257 declarations_are_fields: true,
17258 recovered_specialization_member_scope: true,
17259 visible_using_namespaces: Vec::new(),
17260 };
17261 let mut visitor = CppVisitor {
17262 file: &file,
17263 source,
17264 parsed: &mut recovered_parsed,
17265 c_tag_semantics: false,
17266 recovered_class_sibling_scopes: HashMap::default(),
17267 consumed_fragment_regions: Vec::new(),
17268 orphaned_namespaces: &index,
17269 partitioned_regions: Vec::new(),
17270 namespace_forward_scans: HashMap::default(),
17271 field_owners: None,
17272 recovery_captures: Vec::new(),
17273 object_macro_fields: HashMap::default(),
17274 ambiguous_object_macro_fields: HashSet::default(),
17275 };
17276 let recovered = visitor
17277 .recovered_namespace_scope(tail_node, &scope)
17278 .expect("the tail must use the stolen namespace scope");
17279 assert_eq!(recovered.package_name, "testing::internal");
17280 assert!(
17281 recovered.class_unit.is_none(),
17282 "recovered namespace declarations cannot retain the malformed class owner"
17283 );
17284 assert!(recovered.template_signature.is_none());
17285 assert!(recovered.template_metadata.is_none());
17286 assert!(!recovered.declarations_are_fields);
17287 assert!(!recovered.recovered_specialization_member_scope);
17288 assert!(
17289 declarations
17290 .iter()
17291 .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
17292 "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
17293 );
17294 assert!(
17295 declarations
17296 .iter()
17297 .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
17298 "the recovered free function remains in its namespace: {declarations:#?}"
17299 );
17300 assert!(
17301 declarations
17302 .iter()
17303 .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
17304 "the independent nested class keeps its ordinary class owner: {declarations:#?}"
17305 );
17306 assert!(
17307 declarations.iter().all(|unit| {
17308 !unit
17309 .short_name()
17310 .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
17311 }),
17312 "recovered namespace declarations must not retain a class owner: {declarations:#?}"
17313 );
17314 assert!(
17315 declarations
17316 .iter()
17317 .all(|unit| !unit.identifier().is_empty()),
17318 "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
17319 );
17320 }
17321
17322 #[test]
17323 fn object_like_field_macros_materialize_owner_specific_declarations() {
17324 let source = r#"#define PUBLIC_FIELDS int public_value;
17325#define PRIVATE_FIELDS int private_value;
17326#define NOT_A_FIELD_LIST not a declaration
17327
17328struct First {
17329 PUBLIC_FIELDS
17330 PRIVATE_FIELDS
17331};
17332struct Second {
17333 PUBLIC_FIELDS
17334 NOT_A_FIELD_LIST
17335};
17336#undef PUBLIC_FIELDS
17337struct Third {
17338 PUBLIC_FIELDS
17339};
17340"#;
17341 let parsed = parse_cpp_declarations(source, "macro-fields.c");
17342 let fields = parsed
17343 .declarations()
17344 .iter()
17345 .filter(|unit| unit.is_field())
17346 .map(|unit| unit.fq_name())
17347 .collect::<Vec<_>>();
17348
17349 assert!(
17350 fields.contains(&"First.public_value".to_string()),
17351 "{fields:?}"
17352 );
17353 assert!(
17354 fields.contains(&"First.private_value".to_string()),
17355 "{fields:?}"
17356 );
17357 assert!(
17358 fields.contains(&"Second.public_value".to_string()),
17359 "{fields:?}"
17360 );
17361 assert!(
17362 !fields.iter().any(|field| field.contains("not_a_field")),
17363 "malformed macro must fail closed: {fields:?}"
17364 );
17365 assert!(
17366 !fields.iter().any(|field| field.starts_with("Third.")),
17367 "undefined macro must fail closed: {fields:?}"
17368 );
17369 }
17370
17371 #[test]
17372 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
17373 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
17374 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
17375 let mut macros = parsed
17376 .declarations()
17377 .iter()
17378 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
17379 .collect::<Vec<_>>();
17380 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
17381
17382 assert_eq!(macros.len(), 2, "{macros:#?}");
17383 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
17384 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
17385 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
17386 assert_eq!(
17387 parsed.declaration_ranges(macros[1])[0].start_byte,
17388 source.rfind("#define VALUE 2").expect("second definition")
17389 );
17390 }
17391
17392 #[test]
17393 fn identifies_export_macro_class_base_displaced_into_declarator() {
17394 let source = r#"#define PROJECT_API_
17395namespace project {
17396namespace internal {
17397template <typename T>
17398class Base {};
17399}
17400template <typename T>
17401class Wrapper;
17402template <>
17403class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
17404}
17405"#;
17406 let mut parser = tree_sitter::Parser::new();
17407 parser
17408 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17409 .unwrap();
17410 let tree = parser.parse(source, None).unwrap();
17411 let start = source.find("internal::Base<int>").expect("base");
17412 let mut base = tree
17413 .root_node()
17414 .descendant_for_byte_range(start, start + 8)
17415 .expect("base syntax");
17416 while base.kind() != "qualified_identifier" {
17417 base = base.parent().expect("qualified base ancestor");
17418 }
17419 assert!(
17420 is_recovered_exported_class_base_type_node(base, source),
17421 "{}",
17422 tree.root_node().to_sexp()
17423 );
17424 }
17425
17426 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
17427 let mut identities = parsed
17428 .declarations()
17429 .iter()
17430 .filter(|unit| unit.is_function())
17431 .map(|unit| {
17432 (
17433 unit.fq_name(),
17434 unit.signature().unwrap_or_default().to_string(),
17435 )
17436 })
17437 .collect::<Vec<_>>();
17438 identities.sort();
17439 identities
17440 }
17441
17442 #[test]
17449 fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
17450 let cases = [
17451 (
17452 "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
17453 "pg_typemap_fit_to_result",
17454 "(VALUE, VALUE)",
17455 ),
17456 (
17457 "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
17458 "pg_typemap_result_value",
17459 "(t_typemap *, VALUE, int, int)",
17460 ),
17461 (
17462 "void pg_typemap_mark _(( void * ));",
17463 "pg_typemap_mark",
17464 "(void *)",
17465 ),
17466 (
17467 "void init_pg_type_map _(( void ));",
17468 "init_pg_type_map",
17469 "(void)",
17470 ),
17471 ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
17472 (
17473 "extern VALUE pg_extern _(( VALUE, VALUE ));",
17474 "pg_extern",
17475 "(VALUE, VALUE)",
17476 ),
17477 (
17478 "size_t pg_typemap_memsize _(( const void * ));",
17479 "pg_typemap_memsize",
17480 "(const void *)",
17481 ),
17482 (
17483 "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
17484 "pg_wrap_socket_io",
17485 "(int, VALUE, VALUE *, int *)",
17486 ),
17487 ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
17488 ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
17489 ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
17490 ];
17491
17492 for (index, (source, name, signature)) in cases.into_iter().enumerate() {
17493 let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
17494 assert_eq!(
17495 function_identities(&parsed),
17496 vec![(name.to_string(), signature.to_string())],
17497 "{source}: {:#?}",
17498 parsed.declarations()
17499 );
17500 assert!(
17501 parsed.declarations().iter().all(|unit| !unit.is_field()),
17502 "{source} must not retain the malformed field: {:#?}",
17503 parsed.declarations()
17504 );
17505 }
17506 }
17507
17508 #[test]
17517 fn c_prototype_macro_recovers_pointer_return_expression_statements() {
17518 let cases = [
17519 (
17520 "PGconn *pg_get_pgconn _(( VALUE ));",
17521 "pg_get_pgconn",
17522 "(VALUE)",
17523 ),
17524 (
17525 "PGresult* pgresult_get _(( VALUE ));",
17526 "pgresult_get",
17527 "(VALUE)",
17528 ),
17529 ];
17530 for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
17531 let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
17532 let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
17533 assert_eq!(
17534 function_identities(&parsed),
17535 vec![(name.to_string(), signature.to_string())],
17536 "{source}: {:#?}",
17537 parsed.declarations()
17538 );
17539 assert_eq!(
17540 parsed
17541 .declarations()
17542 .iter()
17543 .filter(|unit| unit.is_field())
17544 .map(|unit| unit.fq_name())
17545 .collect::<Vec<_>>(),
17546 vec!["rb_mPG".to_string()],
17547 "{source}: {:#?}",
17548 parsed.declarations()
17549 );
17550 }
17551 }
17552
17553 #[test]
17561 fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
17562 let source = r#"VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
17563VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
17564int pg_typemap_fit_to_copy_get _(( VALUE ));
17565VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
17566t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
17567VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
17568void pg_typemap_mark _(( void * ));
17569size_t pg_typemap_memsize _(( const void * ));
17570void pg_typemap_compact _(( void * ));
17571
17572PGconn *pg_get_pgconn _(( VALUE ));
17573t_pg_connection *pg_get_connection _(( VALUE ));
17574VALUE pgconn_block _(( int, VALUE *, VALUE ));
17575#ifdef __GNUC__
17576__attribute__((format(printf, 3, 4)))
17577#endif
17578NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
17579VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
17580void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
17581
17582
17583VALUE pg_new_result _(( PGresult *, VALUE ));
17584VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
17585PGresult* pgresult_get _(( VALUE ));
17586VALUE pg_result_check _(( VALUE ));
17587VALUE pg_result_clear _(( VALUE ));
17588VALUE pg_tuple_new _(( VALUE, int ));
17589
17590/*
17591 * Fetch the data pointer for the result object
17592 */
17593static inline t_pg_result *
17594pgresult_get_this( VALUE self )
17595{
17596 return RTYPEDDATA_DATA(self);
17597}
17598
17599
17600rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
17601const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
17602rb_encoding *pg_conn_enc_get _(( PGconn * ));
17603
17604"#;
17605 let parsed = parse_cpp_declarations(source, "pg.h");
17606 assert!(
17607 function_identities(&parsed)
17608 .iter()
17609 .any(|(name, signature)| name == "pg_typemap_result_value"
17610 && signature == "(t_typemap *, VALUE, int, int)"),
17611 "the real issue witness must be a Function with its C signature: {:#?}",
17612 parsed.declarations()
17613 );
17614 assert!(
17615 parsed
17616 .declarations()
17617 .iter()
17618 .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
17619 "the issue witness must not leave its return type as a Field name: {:#?}",
17620 parsed.declarations()
17621 );
17622 }
17623
17624 #[test]
17631 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
17632 let source = r#"class SIMD_4x26 final {
17633 public:
17634 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
17635 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
17636 int m_v;
17637};
17638"#;
17639 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
17640 assert_eq!(
17641 function_identities(&parsed),
17642 vec![
17643 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
17644 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
17645 ],
17646 "{:#?}",
17647 parsed.declarations()
17648 );
17649 }
17650
17651 #[test]
17656 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
17657 let source = r#"#include <cstdint>
17658struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
17659 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
17660 struct llama_model * model,
17661 struct llama_context_params params),
17662 "use llama_init_from_model instead");
17663 LLAMA_API int32_t llama_tokenize(
17664 const struct llama_vocab * vocab,
17665 const char * text,
17666 bool parse_special);
17667 LLAMA_API int32_t llama_other(int a);
17668"#;
17669 let parsed = parse_cpp_declarations(source, "llama.h");
17670 assert_eq!(
17671 function_identities(&parsed),
17672 vec![
17673 (
17674 "llama_new_context_with_model".to_string(),
17675 "(struct llama_model *, struct llama_context_params)".to_string()
17676 ),
17677 ("llama_other".to_string(), "(int)".to_string()),
17678 (
17679 "llama_tokenize".to_string(),
17680 "(const struct llama_vocab *, const char *, bool)".to_string()
17681 ),
17682 ],
17683 "{:#?}",
17684 parsed.declarations()
17685 );
17686
17687 for (name, expected) in [
17690 (
17691 "llama_new_context_with_model",
17692 "LLAMA_API struct llama_context * llama_new_context_with_model(",
17693 ),
17694 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
17695 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
17696 ] {
17697 let unit = parsed
17698 .declarations()
17699 .iter()
17700 .find(|unit| unit.is_function() && unit.fq_name() == name)
17701 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
17702 let [range] = parsed.declaration_ranges(unit) else {
17703 panic!("{name} must have exactly one range");
17704 };
17705 let text = &source[range.start_byte..range.end_byte];
17706 assert!(
17707 text.starts_with(expected),
17708 "{name} range is {text:?}, expected it to start with {expected:?}"
17709 );
17710 assert!(
17711 text.ends_with(')') || text.ends_with(';'),
17712 "{name}: {text:?}"
17713 );
17714 }
17715 }
17716
17717 #[test]
17731 fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
17732 let source = r#" LIBRARY_DEPRECATED(
17733 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17734 "use library_init_from_file_with_params instead"
17735 );
17736 LIBRARY_DEPRECATED(
17737 LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
17738 "use library_init_from_buffer_with_params instead"
17739 );
17740 LIBRARY_DEPRECATED(
17741 LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
17742 "use library_init_with_params instead"
17743 );
17744"#;
17745 let mut parser = tree_sitter::Parser::new();
17746 parser
17747 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17748 .expect("C++ grammar");
17749 let tree = parser.parse(source, None).expect("C++ tree");
17750 let root = tree.root_node();
17751
17752 let mut cursor = root.walk();
17756 let items = root.children(&mut cursor).collect::<Vec<_>>();
17757 let [first, hint_statement, rest @ ..] = items.as_slice() else {
17758 panic!("{}", root.to_sexp());
17759 };
17760 assert_eq!(first.kind(), "ERROR");
17761 assert_eq!(hint_statement.kind(), "expression_statement");
17762 assert!(
17763 node_text(*first, source).ends_with(','),
17764 "the first invocation's own `)` and `;` are the statement's, not its own: {}",
17765 root.to_sexp()
17766 );
17767 let swallowing = rest
17768 .iter()
17769 .find(|item| item.kind() == "ERROR")
17770 .unwrap_or_else(|| panic!("{}", root.to_sexp()));
17771 let mut swallowing_cursor = swallowing.walk();
17772 let flattened = swallowing
17773 .children(&mut swallowing_cursor)
17774 .map(|child| child.kind())
17775 .collect::<Vec<_>>();
17776 assert_eq!(
17777 flattened,
17778 vec![
17779 "identifier",
17780 "(",
17781 "parameter_declaration",
17782 ",",
17783 "ERROR",
17784 "type_identifier",
17785 "(",
17786 "parameter_declaration",
17787 ",",
17788 "\"",
17789 "identifier",
17790 "identifier",
17791 "identifier",
17792 "\"",
17793 ")",
17794 ],
17795 "the third invocation must be flattened into the second one's node: {}",
17796 root.to_sexp()
17797 );
17798
17799 let ancestry = ParentIndex::new(root);
17801 let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
17802 .expect("the first invocation");
17803 assert_eq!(
17804 &source[..first_run.invocation_end],
17805 &source[..source.find("instead\"\n );").expect("first hint")
17806 + "instead\"\n );".len()]
17807 );
17808 assert_eq!(
17809 first_run.region_end, first_run.invocation_end,
17810 "the first invocation swallowed nothing, so the recovery owns only its own bytes"
17811 );
17812 let swallowing_run = collapsed_macro_declaration_run(*swallowing, source, &ancestry)
17813 .expect("the second invocation");
17814 assert!(
17815 swallowing_run.invocation_end < swallowing.end_byte(),
17816 "the second invocation swallowed the third"
17817 );
17818 assert_eq!(
17819 swallowing_run.region_end,
17820 root.end_byte(),
17821 "a swallowing invocation owns the region to the close of its declaration scope"
17822 );
17823
17824 let parsed = parse_cpp_declarations(source, "library.h");
17825 assert_eq!(
17826 function_identities(&parsed),
17827 vec![
17828 (
17829 "library_init".to_string(),
17830 "(struct library_model_loader *)".to_string()
17831 ),
17832 (
17833 "library_init_from_buffer".to_string(),
17834 "(void *, size_t)".to_string()
17835 ),
17836 (
17837 "library_init_from_file".to_string(),
17838 "(const char *)".to_string()
17839 ),
17840 ],
17841 "{:#?}",
17842 parsed.declarations()
17843 );
17844 }
17845
17846 #[test]
17852 fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
17853 let source = r#"LIBRARY_DEPRECATED(
17854 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17855 "use library_init_from_file_with_params instead"
17856 );"#;
17857 let mut parser = tree_sitter::Parser::new();
17858 parser
17859 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17860 .expect("C++ grammar");
17861 let tree = parser.parse(source, None).expect("C++ tree");
17862 let root = tree.root_node();
17863 let head = root.named_child(0).expect("the invocation");
17864 let ancestry = ParentIndex::new(root);
17865 assert!(
17866 collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
17867 "{}",
17868 root.to_sexp()
17869 );
17870 assert!(
17871 !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
17872 "the ordinary reader must be the one that has it: {}",
17873 root.to_sexp()
17874 );
17875 let unindexed = ParentIndex::unindexed();
17878 assert!(
17879 collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
17880 "indexed and unindexed climbs must agree"
17881 );
17882 assert_eq!(
17883 macro_wrapped_declarations(head, source, &ancestry).len(),
17884 macro_wrapped_declarations(head, source, &unindexed).len(),
17885 "indexed and unindexed climbs must agree"
17886 );
17887 }
17888
17889 #[test]
17894 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
17895 for source in [
17896 "int before;\nFOO(1, 2);\nint after;\n",
17897 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
17898 "int before;\nMACRO(int a, int b);\nint after;\n",
17899 "DECLARE_HANDLE(HWND);\nint after;\n",
17900 ] {
17901 let parsed = parse_cpp_declarations(source, "macro-call.h");
17902 assert_eq!(
17903 function_identities(&parsed),
17904 Vec::new(),
17905 "{source:?} must declare no function: {:#?}",
17906 parsed.declarations()
17907 );
17908 }
17909 }
17910
17911 #[test]
17916 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
17917 let source = r#"#include <string_view>
17918namespace Botan {
17919class DL_Group final {
17920 public:
17921 DL_Group() = default;
17922 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17923 DL_Group(std::string_view pem, int format);
17924 size_t get_p() const;
17925};
17926}
17927"#;
17928 let parsed = parse_cpp_declarations(source, "dl_group.h");
17929 assert_eq!(
17930 function_identities(&parsed),
17931 vec![
17932 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
17933 (
17934 "Botan.DL_Group.DL_Group".to_string(),
17935 "(std::string_view)".to_string()
17936 ),
17937 (
17938 "Botan.DL_Group.DL_Group".to_string(),
17939 "(std::string_view, int)".to_string()
17940 ),
17941 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
17942 ],
17943 "{:#?}",
17944 parsed.declarations()
17945 );
17946 }
17947
17948 #[test]
17953 fn an_export_macro_class_keeps_its_string_attribute_members() {
17954 let source = r#"#include <string_view>
17955namespace Botan {
17956class BOTAN_PUBLIC_API(2, 0) DL_Group final {
17957 public:
17958 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17959 DL_Group(std::string_view pem, int format);
17960 size_t get_p() const;
17961};
17962}
17963"#;
17964 let parsed = parse_cpp_declarations(source, "dl_group.h");
17965 assert!(
17966 parsed
17967 .declarations()
17968 .iter()
17969 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
17970 "{:#?}",
17971 parsed.declarations()
17972 );
17973 assert_eq!(
17974 function_identities(&parsed),
17975 vec![
17976 (
17977 "Botan.DL_Group.DL_Group".to_string(),
17978 "(std::string_view)".to_string()
17979 ),
17980 (
17981 "Botan.DL_Group.DL_Group".to_string(),
17982 "(std::string_view, int)".to_string()
17983 ),
17984 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
17985 ],
17986 "{:#?}",
17987 parsed.declarations()
17988 );
17989 }
17990
17991 #[test]
17997 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
17998 let source = r#"namespace Botan {
17999class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
18000 public:
18001 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
18002 XMSS_Parameters(int oid, int len);
18003 size_t len() const;
18004
18005 private:
18006 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
18007 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
18008
18009 int m_oid;
18010 int m_wots_oid;
18011 size_t m_element_size;
18012 size_t m_tree_height;
18013};
18014}
18015"#;
18016 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
18017 let constructors = function_identities(&parsed)
18018 .into_iter()
18019 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
18020 .map(|(_, signature)| signature)
18021 .collect::<Vec<_>>();
18022 assert_eq!(
18023 constructors,
18024 vec![
18025 "()".to_string(),
18026 "(int, int)".to_string(),
18027 "(int, int, size_t, size_t)".to_string(),
18028 ],
18029 "{:#?}",
18030 parsed.declarations()
18031 );
18032 }
18033
18034 #[test]
18038 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
18039 let source = r#"namespace shell {
18040struct Outer {
18041 struct Inner {
18042 Inner(int v);
18043 void run(int v);
18044 };
18045};
18046Outer::Inner::Inner(int v) {}
18047void Outer::Inner::run(int v) {}
18048}
18049"#;
18050 let parsed = parse_cpp_declarations(source, "outer.cpp");
18051 let names = function_identities(&parsed)
18052 .into_iter()
18053 .map(|(fq_name, _)| fq_name)
18054 .collect::<Vec<_>>();
18055 assert!(
18056 names
18057 .iter()
18058 .all(|name| name.starts_with("shell.Outer$Inner.")),
18059 "{names:#?}"
18060 );
18061 }
18062
18063 #[test]
18064 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
18065 let source = r#"namespace control {
18066template <typename T>
18067class AnySpan;
18068template <typename T>
18069class ABSL_ATTRIBUTE_VIEW AnySpan {
18070 public:
18071 int begin() const;
18072};
18073}
18074
18075namespace absl {
18076ABSL_NAMESPACE_BEGIN
18077template <typename T>
18078class ABSL_ATTRIBUTE_VIEW Span {
18079 public:
18080 int begin() const;
18081 int back() const;
18082};
18083
18084int begin();
18085int back();
18086}
18087"#;
18088 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
18089 let declarations = parsed.declarations();
18090 assert!(
18091 declarations
18092 .iter()
18093 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
18094 );
18095 for method in ["begin", "back"] {
18096 assert!(declarations.iter().any(|unit| {
18097 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
18098 }));
18099 assert!(
18100 declarations.iter().any(|unit| {
18101 unit.is_function() && unit.fq_name() == format!("absl.{method}")
18102 })
18103 );
18104 }
18105 assert!(
18106 declarations
18107 .iter()
18108 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
18109 );
18110 assert!(
18111 declarations
18112 .iter()
18113 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
18114 );
18115 assert!(
18116 declarations
18117 .iter()
18118 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
18119 );
18120 }
18121
18122 #[test]
18123 fn explicit_global_member_definition_has_canonical_package_boundary() {
18124 let source = r#"
18125namespace arangodb::aql {
18126class ExecutionPlan {
18127 public:
18128 template<class... Args> Node* createNode(Args&&... args);
18129};
18130}
18131
18132template<class... Args>
18133Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
18134"#;
18135 let parsed = parse_cpp_declarations(source, "global-member.cpp");
18136
18137 assert!(parsed.declarations().iter().any(|unit| {
18138 unit.is_function()
18139 && unit.package_name() == "arangodb::aql"
18140 && unit.short_name() == "ExecutionPlan.createNode"
18141 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
18142 }));
18143 }
18144
18145 #[test]
18146 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
18147 let source = r#"
18148#ifndef TINYXML2_INCLUDED
18149#define TINYXML2_INCLUDED
18150namespace tinyxml2 {
18151class TINYXML2_LIB XMLUtil {
18152 public:
18153 static const char* SkipWhiteSpace(const char* p) {
18154 while (*p) {
18155 if (*p == ' ') {
18156 ++p;
18157 }
18158 }
18159 return p;
18160 }
18161 static bool StringEqual(const char* p, const char* q) {
18162 return p == q;
18163 }
18164 class TINYXML2_LIB Helper {
18165 public:
18166 void Touch();
18167 };
18168 static void ToStr(int value, char* buffer);
18169 private:
18170 static const char* writeBoolTrue;
18171};
18172
18173class TINYXML2_LIB XMLNode {
18174 public:
18175 virtual XMLNode* ShallowClone() const = 0;
18176 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
18177};
18178}
18179#endif
18180"#;
18181 let mut parser = tree_sitter::Parser::new();
18182 parser
18183 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18184 .unwrap();
18185 let tree = parser.parse(source, None).unwrap();
18186 let mut boundary_found = false;
18187 walk_named_tree_preorder(tree.root_node(), true, |node| {
18188 if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
18189 && name == "XMLUtil"
18190 {
18191 boundary_found = fragmented_export_sibling_class_boundary(node, source)
18192 .and_then(|boundary| {
18193 recover_exported_class_function_definition(boundary, source)
18194 })
18195 .is_some_and(|(_, name, _)| name == "XMLNode");
18196 }
18197 WalkControl::Continue
18198 });
18199 assert!(
18200 boundary_found,
18201 "fixture must exercise the recovered sibling boundary"
18202 );
18203
18204 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
18205 assert!(
18206 parsed
18207 .declarations()
18208 .iter()
18209 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
18210 "{:#?}",
18211 parsed.declarations()
18212 );
18213 assert!(
18214 parsed
18215 .declarations()
18216 .iter()
18217 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
18218 "{:#?}",
18219 parsed.declarations()
18220 );
18221 assert!(parsed.declarations().iter().any(|unit| {
18222 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
18223 }));
18224 assert!(
18225 parsed
18226 .declarations()
18227 .iter()
18228 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
18229 );
18230 assert!(
18231 parsed
18232 .declarations()
18233 .iter()
18234 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
18235 );
18236 }
18237
18238 #[test]
18239 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
18240 let parsed = parse_cpp_declarations(
18244 r#"
18245namespace cwg311 {
18246namespace X { namespace Y {} }
18247namespace ::cwg311::X {}
18248}
18249"#,
18250 "explicit-global-namespace.cpp",
18251 );
18252
18253 assert!(parsed.declarations().iter().any(|unit| {
18254 unit.kind() == CodeUnitType::Module
18255 && unit.short_name() == "cwg311::X"
18256 && unit.fq_name() == "cwg311::X"
18257 }));
18258 assert!(
18259 parsed
18260 .declarations()
18261 .iter()
18262 .all(|unit| !unit.short_name().contains("::::")),
18263 "recovered namespace names must not retain empty scope components: {:#?}",
18264 parsed.declarations()
18265 );
18266 }
18267
18268 #[test]
18269 fn repeated_scope_separator_does_not_create_empty_function_owner() {
18270 let scope = ScopeInfo {
18271 package_name: "X".to_string(),
18272 module: None,
18273 class_unit: None,
18274 template_signature: None,
18275 template_metadata: None,
18276 declarations_are_fields: false,
18277 recovered_specialization_member_scope: false,
18278 visible_using_namespaces: Vec::new(),
18279 };
18280
18281 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
18282
18283 assert!(owner.is_none());
18284 assert_eq!(name, "doit");
18285 assert_eq!(package, "X");
18286 }
18287
18288 #[test]
18289 fn trailing_decltype_expression_is_not_a_function_declarator() {
18290 let source = r#"
18291namespace boost { namespace detail {
18292#if ! defined(BOOST_NO_SFINAE_EXPR) && \
18293 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
18294 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
18295#define BOOST_THREAD_PROVIDES_INVOKE
18296#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
18297template <class Fp, class A0, class ...Args>
18298inline auto
18299invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
18300 BOOST_THREAD_RV_REF(Args) ...args)
18301 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
18302{
18303 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
18304}
18305#endif
18306#endif
18307}}
18308"#;
18309 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
18310
18311 assert!(
18312 parsed
18313 .declarations()
18314 .iter()
18315 .all(|unit| unit.short_name() != ".*f")
18316 );
18317 }
18318
18319 fn find_class_named<'tree>(
18320 root: Node<'tree>,
18321 source: &str,
18322 expected_name: &str,
18323 ) -> Option<Node<'tree>> {
18324 let mut stack = vec![root];
18325 while let Some(node) = stack.pop() {
18326 if node.kind() == "class_specifier"
18327 && node
18328 .child_by_field_name("name")
18329 .is_some_and(|name| node_text(name, source) == expected_name)
18330 {
18331 return Some(node);
18332 }
18333 let mut cursor = node.walk();
18334 stack.extend(node.named_children(&mut cursor));
18335 }
18336 None
18337 }
18338
18339 #[test]
18340 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
18341 let source = r#"EXPORT void definition(struct Value value) {}
18342EXPORT void prototype(struct Value value);
18343"#;
18344 let mut parser = tree_sitter::Parser::new();
18345 parser
18346 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18347 .unwrap();
18348 let tree = parser.parse(source, None).unwrap();
18349 let root = tree.root_node();
18350 let mut cursor = root.walk();
18351 let callables = root
18352 .named_children(&mut cursor)
18353 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
18354 .collect::<Vec<_>>();
18355
18356 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
18357 for callable in callables {
18358 assert!(callable.has_error(), "fixture must exercise error recovery");
18359 assert!(
18360 cpp_sentinel_macro_parts(callable, source).is_none(),
18361 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
18362 );
18363 }
18364 }
18365
18366 #[test]
18367 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
18368 let source = r#"namespace absl {
18369ABSL_NAMESPACE_BEGIN
18370// Generate a floating-point variate conforming to a Beta distribution:
18371template <typename RealType = double>
18372class beta_distribution {
18373 public:
18374 using result_type = RealType;
18375
18376
18377 beta_distribution() : beta_distribution(1) {}
18378
18379 explicit beta_distribution(result_type alpha, result_type beta = 1)
18380 : param_(alpha, beta) {}
18381
18382 explicit beta_distribution(const param_type& p) : param_(p) {}
18383
18384 void reset() {}
18385
18386 // Generating functions
18387 template <typename URBG>
18388 result_type operator()(URBG& g) { // NOLINT(runtime/references)
18389 return (*this)(g, param_);
18390 }
18391
18392};
18393ABSL_NAMESPACE_END
18394} // namespace absl
18395"#;
18396 let mut parser = tree_sitter::Parser::new();
18397 parser
18398 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18399 .unwrap();
18400 let tree = parser.parse(source, None).unwrap();
18401 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18402 let body = namespace
18403 .child_by_field_name("body")
18404 .expect("fixture namespace body");
18405 let sentinel = body.named_child(0).expect("sentinel envelope");
18406 let callable = sentinel
18407 .child_by_field_name("declarator")
18408 .and_then(extract_function_declarator)
18409 .and_then(cpp_function_declarator_name_node)
18410 .expect("preserved callable name");
18411
18412 assert_eq!(sentinel.kind(), "function_definition");
18413 assert_eq!(callable.kind(), "operator_name");
18414 assert!(
18415 cpp_sentinel_macro_parts(sentinel, source).is_some(),
18416 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
18417 );
18418 }
18419
18420 #[test]
18421 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
18422 let source = r#"namespace absl {
18423ABSL_NAMESPACE_BEGIN
18424// absl::discrete_distribution
18425//
18426// A discrete distribution produces random integers i, where 0 <= i < n
18427template <typename IntType = int>
18428class discrete_distribution {
18429 public:
18430 using result_type = IntType;
18431 class param_type {
18432 public:
18433 param_type() { init(); }
18434 template <typename InputIterator>
18435 explicit param_type(InputIterator begin, InputIterator end)
18436 : p_(begin, end) {
18437 init();
18438 }
18439 };
18440 discrete_distribution() : param_() {}
18441 explicit discrete_distribution(const param_type& p) : param_(p) {}
18442};
18443ABSL_NAMESPACE_END
18444} // namespace absl
18445"#;
18446 let mut parser = tree_sitter::Parser::new();
18447 parser
18448 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18449 .unwrap();
18450 let tree = parser.parse(source, None).unwrap();
18451 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18452 let body = namespace
18453 .child_by_field_name("body")
18454 .expect("fixture namespace body");
18455 let sentinel = body.named_child(0).expect("sentinel envelope");
18456 let callable = sentinel
18457 .child_by_field_name("declarator")
18458 .and_then(extract_function_declarator)
18459 .and_then(cpp_function_declarator_name_node)
18460 .expect("preserved callable name");
18461
18462 assert_eq!(sentinel.kind(), "function_definition");
18463 assert_eq!(callable.kind(), "identifier");
18464 assert!(
18465 cpp_sentinel_macro_parts(sentinel, source).is_some(),
18466 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
18467 );
18468 }
18469
18470 #[test]
18471 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
18472 let source = r#"
18473#define CPPCHECKLIB
18474class Library {
18475 struct Container {
18476 CPPCHECKLIB static std::string toString(Yield yield);
18477 CPPCHECKLIB static std::string toString(Action action);
18478 };
18479};
18480"#;
18481 let mut parser = tree_sitter::Parser::new();
18482 parser
18483 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18484 .unwrap();
18485 let tree = parser.parse(source, None).unwrap();
18486 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
18487 let parsed = parse_cpp_file(&file, source, &tree);
18488 assert!(
18489 parsed
18490 .declarations()
18491 .iter()
18492 .all(|unit| unit.fq_name() != "Library$Container.std"),
18493 "the qualified return-type namespace must not become a field: {:#?}",
18494 parsed.declarations()
18495 );
18496 for expected in ["(Yield)", "(Action)"] {
18497 assert!(
18498 parsed.declarations().iter().any(|unit| {
18499 unit.is_function()
18500 && unit.fq_name() == "Library$Container.toString"
18501 && unit.signature() == Some(expected)
18502 }),
18503 "recovered toString overload {expected} is missing: {:#?}",
18504 parsed.declarations()
18505 );
18506 }
18507 }
18508
18509 #[test]
18510 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
18511 let source = r#"
18512#define SIMPLECPP_LIB
18513namespace simplecpp {
18514using TokenString = std::string;
18515struct Location { int line{}; };
18516class SIMPLECPP_LIB Token {
18517 TokenString prefix;
18518 void prefix_method() {}
18519 public:
18520 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18521 whitespaceahead(wsahead), location(loc), string(s)
18522 // The comment must not hide the constructor body from recovery.
18523 {
18524 flags();
18525 }
18526 TokenString string;
18527 bool whitespaceahead;
18528 Location location;
18529 Token *previous{};
18530 private:
18531 void flags() {
18532 whitespaceahead = true;
18533 }
18534};
18535}
18536"#;
18537 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
18538
18539 let location_fields = parsed
18540 .declarations()
18541 .iter()
18542 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
18543 .collect::<Vec<_>>();
18544 assert_eq!(
18545 location_fields.len(),
18546 1,
18547 "location should have one class-owned declaration: {:#?}",
18548 parsed.declarations()
18549 );
18550 assert!(
18551 location_fields[0].is_field(),
18552 "location has wrong kind: {:#?}",
18553 parsed.declarations()
18554 );
18555 assert!(
18556 parsed.declarations().iter().all(|unit| {
18557 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
18558 })
18559 );
18560 assert!(
18561 parsed.declarations().iter().all(|unit| {
18562 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18563 })
18564 );
18565 assert!(
18566 parsed
18567 .declarations()
18568 .iter()
18569 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
18570 );
18571 assert!(
18572 parsed
18573 .declarations()
18574 .iter()
18575 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
18576 "the recovered class must retain its constructor: {:#?}",
18577 parsed.declarations()
18578 );
18579 assert!(
18580 parsed
18581 .declarations()
18582 .iter()
18583 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
18584 );
18585 assert!(parsed.declarations().iter().any(|unit| {
18586 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
18587 }));
18588 let constructor = parsed
18589 .declarations()
18590 .iter()
18591 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
18592 .expect("recovered constructor");
18593 let constructor_start = source.find("Token(const").expect("constructor start");
18594 let constructor_end = source
18595 .get(
18596 ..source
18597 .find(" TokenString string;")
18598 .expect("constructor end"),
18599 )
18600 .expect("constructor slice")
18601 .trim_end()
18602 .len();
18603 assert!(
18604 parsed
18605 .navigation_ranges
18606 .get(constructor)
18607 .is_some_and(|ranges| {
18608 ranges.iter().any(|range| {
18609 range.start_byte == constructor_start && range.end_byte == constructor_end
18610 })
18611 }),
18612 "constructor navigation must span the full body: {:#?}",
18613 parsed.navigation_ranges
18614 );
18615 assert_eq!(
18616 parsed
18617 .signature_metadata
18618 .get(constructor)
18619 .and_then(|metadata| metadata.first())
18620 .and_then(SignatureMetadata::callable_linkage),
18621 Some(CallableLinkage::External)
18622 );
18623 let token_class = parsed
18624 .declarations()
18625 .iter()
18626 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
18627 .expect("recovered Token class");
18628 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
18629 assert!(
18630 parsed
18631 .navigation_ranges
18632 .get(token_class)
18633 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
18634 "class navigation must include the terminating semicolon: {:#?}",
18635 parsed.navigation_ranges
18636 );
18637 }
18638
18639 #[test]
18640 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
18641 let source = r#"
18642#define SIMPLECPP_LIB
18643namespace simplecpp {
18644using TokenString = std::string;
18645class Macro;
18646struct Location {
18647 unsigned int fileIndex{};
18648 unsigned int line{};
18649 unsigned int col{};
18650};
18651struct Output {
18652 int type;
18653};
18654class SIMPLECPP_LIB Token {
18655 public:
18656 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18657 whitespaceahead(wsahead), location(loc), string(s) {
18658 flags();
18659 }
18660 Token(const Token &tok) :
18661 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
18662 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
18663 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
18664 Token &operator=(const Token &tok) = delete;
18665 const TokenString& str() const { return string; }
18666 void setstr(const std::string &s) { string = s; flags(); }
18667 bool isOneOf(const char ops[]) const;
18668 TokenString macro;
18669 char op;
18670 bool comment;
18671 bool name;
18672 bool number;
18673 bool whitespaceahead;
18674 Location location;
18675 Token *previous{};
18676 Token *next{};
18677 private:
18678 void flags() {
18679 name = !string.empty();
18680 comment = false;
18681 number = false;
18682 op = 0;
18683 }
18684 TokenString string;
18685};
18686}
18687struct Following {
18688 int type;
18689};
18690class SIMPLECPP_LIB Later {
18691 public:
18692 Later(int value) : value(value) {}
18693 int value;
18694};
18695"#;
18696 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
18697 assert!(
18698 parsed
18699 .declarations()
18700 .iter()
18701 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
18702 );
18703 assert!(
18704 !parsed
18705 .declarations()
18706 .iter()
18707 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
18708 );
18709 assert!(
18710 parsed
18711 .declarations()
18712 .iter()
18713 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
18714 );
18715 assert!(
18716 !parsed
18717 .declarations()
18718 .iter()
18719 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18720 );
18721 assert!(
18722 parsed
18723 .declarations()
18724 .iter()
18725 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
18726 );
18727 assert!(
18728 parsed
18729 .declarations()
18730 .iter()
18731 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
18732 );
18733 assert!(
18734 parsed
18735 .declarations()
18736 .iter()
18737 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
18738 );
18739 assert!(
18740 parsed
18741 .declarations()
18742 .iter()
18743 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
18744 );
18745 assert!(
18746 parsed
18747 .declarations()
18748 .iter()
18749 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
18750 );
18751 assert!(
18752 parsed
18753 .declarations()
18754 .iter()
18755 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
18756 );
18757 assert!(parsed.declarations().iter().all(|unit| {
18758 !matches!(
18759 unit.fq_name().as_str(),
18760 "simplecpp.Token.Following" | "simplecpp.Token.Later"
18761 )
18762 }));
18763 assert!(
18764 !parsed
18765 .declarations()
18766 .iter()
18767 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
18768 "the following struct must remain outside the recovered Token class"
18769 );
18770 }
18771
18772 #[test]
18773 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
18774 let source = r#"
18775#define SIMPLECPP_LIB
18776namespace {
18777namespace simplecpp {
18778using TokenString = std::string;
18779struct Location { int line{}; };
18780class SIMPLECPP_LIB HiddenToken {
18781 public:
18782 HiddenToken(const TokenString &s, const Location &loc) :
18783 location(loc), string(s) {
18784 flags();
18785 }
18786 TokenString string;
18787 Location location;
18788 HiddenToken *previous{};
18789 private:
18790 void flags() {}
18791};
18792}
18793}
18794"#;
18795 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
18796 let constructor = parsed
18797 .declarations()
18798 .iter()
18799 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
18800 .expect("recovered anonymous-namespace constructor");
18801 assert_eq!(
18802 parsed
18803 .signature_metadata
18804 .get(constructor)
18805 .and_then(|metadata| metadata.first())
18806 .and_then(SignatureMetadata::callable_linkage),
18807 Some(CallableLinkage::Internal)
18808 );
18809 }
18810
18811 #[test]
18812 fn macro_qualified_static_field_keeps_real_declarator() {
18813 let source = r#"#define JSON_INLINE_VARIABLE
18814struct Reader {
18815static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
18816static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
18817static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
18818};"#;
18819 let mut parser = tree_sitter::Parser::new();
18820 parser
18821 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18822 .unwrap();
18823 let tree = parser.parse(source, None).unwrap();
18824 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
18825 let parsed = parse_cpp_file(&file, source, &tree);
18826 for expected in [
18827 "Reader.npos",
18828 "Reader.other",
18829 "Reader.pointer",
18830 "Reader.reference",
18831 ] {
18832 assert!(
18833 parsed
18834 .declarations()
18835 .iter()
18836 .any(|unit| unit.is_field() && unit.fq_name() == expected),
18837 "real macro-decorated field {expected} is missing: {:#?}",
18838 parsed.declarations()
18839 );
18840 }
18841 assert!(
18842 parsed
18843 .declarations()
18844 .iter()
18845 .all(|unit| unit.fq_name() != "Reader.std"),
18846 "qualified type prefix became a pseudo-field: {:#?}",
18847 parsed.declarations()
18848 );
18849 let root = tree.root_node();
18850 let mut stack = vec![root];
18851 let mut signatures = Vec::new();
18852 while let Some(current) = stack.pop() {
18853 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
18854 {
18855 signatures.extend(
18856 declarators
18857 .into_iter()
18858 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
18859 );
18860 }
18861 let mut cursor = current.walk();
18862 stack.extend(current.named_children(&mut cursor));
18863 }
18864 signatures.sort();
18865 assert_eq!(
18866 signatures,
18867 [
18868 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
18869 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
18870 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
18871 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
18872 ]
18873 );
18874 }
18875
18876 fn member_function_linkage(source: &str) -> CallableLinkage {
18877 let mut parser = tree_sitter::Parser::new();
18878 parser
18879 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18880 .unwrap();
18881 let tree = parser.parse(source, None).unwrap();
18882 let ancestry = ParentIndex::new(tree.root_node());
18883 let mut stack = vec![tree.root_node()];
18884 while let Some(node) = stack.pop() {
18885 if node.kind() == "function_definition" {
18886 let mut current = node.parent();
18887 while let Some(parent) = current {
18888 if matches!(
18889 parent.kind(),
18890 "class_specifier" | "struct_specifier" | "union_specifier"
18891 ) {
18892 return cpp_callable_linkage(node, source, &ancestry);
18893 }
18894 current = parent.parent();
18895 }
18896 }
18897 let mut cursor = node.walk();
18898 stack.extend(node.named_children(&mut cursor));
18899 }
18900 panic!("fixture has no member function definition");
18901 }
18902
18903 #[test]
18904 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
18905 assert_eq!(
18906 member_function_linkage("struct Named { int method() { return 1; } };"),
18907 CallableLinkage::External
18908 );
18909 assert_eq!(
18910 member_function_linkage(
18911 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
18912 ),
18913 CallableLinkage::Internal
18914 );
18915 assert_eq!(
18916 member_function_linkage("struct { int method() { return 1; } } instance;"),
18917 CallableLinkage::Internal
18918 );
18919 assert_eq!(
18920 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
18921 CallableLinkage::Internal
18922 );
18923 }
18924
18925 #[test]
18926 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
18927 let source = r#"
18928#ifndef PROTON_VALUE_HPP
18929#define PROTON_VALUE_HPP
18930namespace proton {
18931namespace internal {
18932class value_base {
18933 protected:
18934 internal::data& data();
18935 internal::data data_;
18936 friend class codec::encoder;
18937 friend class codec::decoder;
18938};
18939}
18940class value : public internal::value_base, private internal::comparable<value> {
18941 private:
18942 template<class T, class U=void> struct assignable :
18943 public std::enable_if<codec::is_encodable<T>::value, U> {};
18944 template<class U> struct assignable<value, U> {};
18945 public:
18946 PN_CPP_EXTERN value();
18947 PN_CPP_EXTERN value(const value&);
18948 PN_CPP_EXTERN value& operator=(const value&);
18949 PN_CPP_EXTERN value(value&&);
18950 PN_CPP_EXTERN value& operator=(value&&);
18951 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
18952 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
18953 codec::encoder e(*this);
18954 e << x;
18955 return *this;
18956 }
18957 PN_CPP_EXTERN type_id type() const;
18958 PN_CPP_EXTERN bool empty() const;
18959 PN_CPP_EXTERN void clear();
18960 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
18961 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
18962 friend PN_CPP_EXTERN void swap(value&, value&);
18963 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
18964 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
18965 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
18966 value(pn_data_t* d);
18967 void reset(pn_data_t* d = 0);
18968};
18969}
18970#endif
18971"#;
18972 let mut parser = tree_sitter::Parser::new();
18973 parser
18974 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18975 .unwrap();
18976 let tree = parser.parse(source, None).unwrap();
18977 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
18978 let parsed = parse_cpp_file(&file, source, &tree);
18979 let macro_constructors = parsed
18980 .signature_metadata
18981 .iter()
18982 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
18983 .flat_map(|(_, metadata)| metadata)
18984 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
18985 .collect::<Vec<_>>();
18986
18987 assert_eq!(
18988 macro_constructors.len(),
18989 3,
18990 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
18991 parsed.declarations()
18992 );
18993 assert!(
18994 macro_constructors.iter().all(|metadata| {
18995 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
18996 }),
18997 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
18998 );
18999 }
19000
19001 #[test]
19002 fn recovered_export_class_typedef_uses_displaced_alias_name() {
19003 let source = r#"
19004namespace spi {
19005class Filter {
19006public:
19007 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
19008};
19009}
19010namespace filter {
19011class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
19012{
19013public:
19014 typedef spi::Filter BASE_CLASS;
19015 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
19016 BEGIN_LOG4CXX_CAST_MAP()
19017 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
19018 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
19019 END_LOG4CXX_CAST_MAP()
19020 FilterDecision decide() const;
19021};
19022}
19023"#;
19024 let mut parser = tree_sitter::Parser::new();
19025 parser
19026 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19027 .unwrap();
19028 let tree = parser.parse(source, None).unwrap();
19029 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
19030 let parsed = parse_cpp_file(&file, source, &tree);
19031 assert!(
19032 parsed.declarations().iter().any(|unit| {
19033 unit.is_class()
19034 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
19035 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
19036 }),
19037 "the displaced typedef alias must retain its declared name: {:#?}",
19038 parsed.declarations()
19039 );
19040 assert!(
19041 parsed
19042 .declarations()
19043 .iter()
19044 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
19045 "the qualified underlying type must not become a false nested alias: {:#?}",
19046 parsed.declarations()
19047 );
19048 }
19049
19050 #[test]
19051 fn exported_single_base_recovery_uses_displaced_class_name() {
19052 let source = r#"
19053class CORE_EXPORT QgsPoint : public AbstractGeometry
19054{
19055 Q_GADGET
19056
19057 Q_PROPERTY( double x READ x WRITE setX )
19058 Q_PROPERTY( double y READ y WRITE setY )
19059 Q_PROPERTY( double z READ z WRITE setZ )
19060 Q_PROPERTY( double m READ m WRITE setM )
19061
19062 public:
19063#ifndef SIP_RUN
19064 QgsPoint(
19065 double x = std::numeric_limits<double>::quiet_NaN(),
19066 double y = std::numeric_limits<double>::quiet_NaN(),
19067 double z = std::numeric_limits<double>::quiet_NaN(),
19068 double m = std::numeric_limits<double>::quiet_NaN(),
19069 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
19070 );
19071#else
19072 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 )];
19073 % MethodCode
19074 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
19075 {
19076 int state;
19077 sipIsErr = 0;
19078 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
19079 if ( !sipIsErr )
19080 {
19081 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
19082 }
19083 sipReleaseType( p, sipType_QgsPointXY, state );
19084 }
19085 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
19086 {
19087 int state;
19088 sipIsErr = 0;
19089
19090 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
19091 if ( !sipIsErr )
19092 {
19093 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
19094 }
19095 sipReleaseType( p, sipType_QPointF, state );
19096 }
19097 else if (
19098 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
19099 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
19100 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
19101 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
19102 {
19103 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
19104 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
19105 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
19106 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
19107 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
19108 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
19109 }
19110 else // Invalid ctor arguments
19111 {
19112 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
19113 sipIsErr = 1;
19114 }
19115 % End
19116#endif
19117
19118 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
19119 explicit QgsPoint( QPointF p ) SIP_SKIP;
19120 explicit QgsPoint(
19121 Qgis::WkbType wkbType,
19122 double x = std::numeric_limits<double>::quiet_NaN(),
19123 double y = std::numeric_limits<double>::quiet_NaN(),
19124 double z = std::numeric_limits<double>::quiet_NaN(),
19125 double m = std::numeric_limits<double>::quiet_NaN()
19126 ) SIP_SKIP;
19127 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19128 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
19129 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19130#ifndef SIP_RUN
19131 private:
19132 bool fuzzyHelper(
19133 double epsilon,
19134 const AbstractGeometry &other,
19135 bool is3DFlag,
19136 bool isMeasureFlag
19137 ) const
19138 {
19139 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
19140 }
19141#endif
19142};
19143class Ordinary : public Base { public: Ordinary(); };
19144class API_EXPORT Plain { public: Plain(); };
19145class API_EXPORT : public Base {};
19146class
19147PN_CPP_CLASS_EXTERN Sender : public Link {
19148 Sender();
19149 struct impl;
19150 struct impl& get_impl() const;
19151};
19152class thread_ctx_t {};
19153class ctx_t ZMQ_FINAL : public thread_ctx_t {
19154 bool start();
19155};
19156"#;
19157 let mut parser = tree_sitter::Parser::new();
19158 parser
19159 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19160 .unwrap();
19161 let tree = parser.parse(source, None).unwrap();
19162 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
19163 let parsed = parse_cpp_file(&file, source, &tree);
19164 let declarations = parsed.declarations();
19165
19166 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
19167 assert!(
19168 declarations
19169 .iter()
19170 .any(|unit| unit.is_class() && unit.fq_name() == expected),
19171 "missing recovered class {expected}: {declarations:#?}"
19172 );
19173 }
19174 let qgs_point = declarations
19175 .iter()
19176 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
19177 .expect("recovered QgsPoint class");
19178 assert_eq!(
19179 parsed.raw_supertypes.get(qgs_point),
19180 Some(&vec!["AbstractGeometry".to_string()]),
19181 "single-base export recovery must retain its displaced base"
19182 );
19183 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
19184 assert!(
19185 parsed
19186 .navigation_ranges
19187 .get(qgs_point)
19188 .is_some_and(|ranges| {
19189 !ranges.is_empty()
19190 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
19191 }),
19192 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
19193 parsed.navigation_ranges.get(qgs_point)
19194 );
19195 let sender = declarations
19196 .iter()
19197 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
19198 .expect("recovered Sender class");
19199 assert_eq!(
19200 parsed.raw_supertypes.get(sender),
19201 Some(&vec!["Link".to_string()]),
19202 "post-declarator export recovery must retain its displaced base"
19203 );
19204 let recovered_member = declarations
19205 .iter()
19206 .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
19207 .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
19208 assert_eq!(
19209 parsed
19210 .signature_metadata
19211 .get(recovered_member)
19212 .and_then(|metadata| metadata.first())
19213 .and_then(SignatureMetadata::callable_linkage),
19214 Some(CallableLinkage::External),
19215 "a named recovered class's members have external linkage"
19216 );
19217 let ctx = declarations
19218 .iter()
19219 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
19220 .expect("recovered ctx_t class");
19221 assert_eq!(
19222 parsed.raw_supertypes.get(ctx),
19223 Some(&vec!["thread_ctx_t".to_string()]),
19224 "postfix export-macro recovery must retain its displaced base"
19225 );
19226 assert!(
19227 declarations.iter().any(|unit| {
19228 unit.is_function()
19229 && unit.fq_name() == "QgsPoint.QgsPoint"
19230 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
19231 }),
19232 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
19233 );
19234 assert!(
19235 declarations.iter().all(|unit| {
19236 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
19237 }),
19238 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
19239 );
19240 }
19241
19242 #[test]
19243 fn function_like_export_macro_classes_keep_names_and_base_edges() {
19244 let source = r#"
19248namespace api {
19249class PROJECT_PUBLIC_API(2, 0) Prelude {
19250 public:
19251 Prelude();
19252};
19253class PROJECT_PUBLIC_API(2, 0) Base {
19254 public:
19255 Base(int value);
19256};
19257class PROJECT_PUBLIC_API(2, 0) Mixin {
19258 public:
19259 Mixin();
19260};
19261class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
19262 public:
19263 Adopted(int value);
19264};
19265class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
19266 public:
19267 Derived(int value);
19268};
19269class PROJECT_PUBLIC_API(2, 0) Solo final {
19270 public:
19271 Solo();
19272};
19273class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
19274 public:
19275 Blended(int value);
19276};
19277class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
19278 public:
19279 Woven(int value);
19280};
19281} // namespace api
19282"#;
19283 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
19284 let declarations = parsed.declarations();
19285 let class_named = |name: &str| {
19286 declarations
19287 .iter()
19288 .find(|unit| unit.is_class() && unit.fq_name() == name)
19289 .unwrap_or_else(|| {
19290 panic!("missing function-like export macro class {name}: {declarations:#?}")
19291 })
19292 };
19293 let base = class_named("api.Base");
19294 class_named("api.Prelude");
19295 class_named("api.Mixin");
19296
19297 assert_eq!(
19298 parsed.raw_supertypes.get(class_named("api.Adopted")),
19299 Some(&vec!["Base".to_string()])
19300 );
19301 assert_eq!(
19302 parsed.raw_supertypes.get(class_named("api.Derived")),
19303 Some(&vec!["Base".to_string()])
19304 );
19305 assert_eq!(
19306 parsed.raw_supertypes.get(class_named("api.Solo")),
19307 None,
19308 "a final class without a base list must not invent a supertype"
19309 );
19310 assert_eq!(
19311 parsed.raw_supertypes.get(class_named("api.Blended")),
19312 Some(&vec!["Base".to_string(), "Mixin".to_string()])
19313 );
19314 assert_eq!(
19315 parsed.raw_supertypes.get(class_named("api.Woven")),
19316 Some(&vec!["Base".to_string(), "Mixin".to_string()])
19317 );
19318 assert!(
19319 declarations
19320 .iter()
19321 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19322 "the export macro must not become a declaration: {declarations:#?}"
19323 );
19324 assert!(
19325 declarations.iter().all(|unit| !matches!(
19326 unit.identifier(),
19327 "final" | "public" | "protected" | "private"
19328 )),
19329 "the head specifiers must not become declarations: {declarations:#?}"
19330 );
19331 assert!(
19332 parsed
19333 .navigation_ranges
19334 .get(base)
19335 .is_some_and(|ranges| !ranges.is_empty()),
19336 "the recovered base must retain a navigable declaration range"
19337 );
19338 }
19339
19340 #[test]
19341 fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
19342 let source = r#"
19348namespace api {
19349class PROJECT_PUBLIC_API(2, 0) Base {
19350 public:
19351 Base();
19352};
19353class PROJECT_PUBLIC_API(2, 0) Mixin {
19354 public:
19355 Mixin();
19356};
19357class PROJECT_PUBLIC_API(2, 0) Name {
19358 public:
19359 Name();
19360};
19361class PROJECT_PUBLIC_API(2, 0) X509_CA final {
19362 public:
19363 X509_CA();
19364};
19365class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
19366 public:
19367 HSS_LMS_KEY();
19368};
19369class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
19370 public:
19371 GOST_3410();
19372};
19373class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
19374 public:
19375 PKCS11_RSA();
19376};
19377class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
19378 public:
19379 Plain();
19380};
19381class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
19382 public:
19383 Decorated();
19384};
19385class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
19386 public:
19387 Layered();
19388};
19389} // namespace api
19390"#;
19391 let parsed = parse_cpp_declarations(source, "positional-export.hpp");
19392 let declarations = parsed.declarations();
19393 let class_named = |name: &str| {
19394 declarations
19395 .iter()
19396 .find(|unit| unit.is_class() && unit.fq_name() == name)
19397 .unwrap_or_else(|| {
19398 panic!("missing function-like export macro class {name}: {declarations:#?}")
19399 })
19400 };
19401 for (name, bases) in [
19402 ("api.Name", None),
19403 ("api.X509_CA", None),
19404 ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
19405 ("api.GOST_3410", Some(vec!["Base"])),
19406 ("api.PKCS11_RSA", None),
19407 ("api.Plain", None),
19408 ("api.Decorated", Some(vec!["Base"])),
19409 ("api.Layered", Some(vec!["Base", "Mixin"])),
19410 ] {
19411 let expected =
19412 bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
19413 assert_eq!(
19414 parsed.raw_supertypes.get(class_named(name)),
19415 expected.as_ref(),
19416 "{name}"
19417 );
19418 }
19419 assert!(
19420 declarations.iter().all(|unit| !matches!(
19421 unit.identifier(),
19422 "PROJECT_PUBLIC_API"
19423 | "OTHER_MACRO"
19424 | "FIRST_MACRO"
19425 | "SECOND_MACRO"
19426 | "final"
19427 | "public"
19428 )),
19429 "macros and head specifiers must not become declarations: {declarations:#?}"
19430 );
19431 }
19432
19433 #[test]
19434 fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
19435 let source = r#"
19441namespace api {
19442
19443/**
19444* Doc comment
19445*/
19446class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
19447 public:
19448 /**
19449 * Construct from a point.
19450 */
19451 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
19452
19453#if defined(PROJECT_HAS_LEGACY_POINT)
19454 /**
19455 * Construct from a legacy point.
19456 */
19457 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
19458#endif
19459
19460 std::string algo_name() const override;
19461
19462 AlgorithmIdentifier algorithm_identifier() const override;
19463};
19464
19465}
19466"#;
19467 let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
19468 let declarations = parsed.declarations();
19469 let class = declarations
19470 .iter()
19471 .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
19472 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19473 assert_eq!(
19474 parsed.raw_supertypes.get(class),
19475 Some(&vec!["BaseKey".to_string()]),
19476 "the virtual base is the class's base: {declarations:#?}"
19477 );
19478 for member in [
19479 "api.VirtualBased.algo_name",
19480 "api.VirtualBased.algorithm_identifier",
19481 ] {
19482 assert!(
19483 declarations
19484 .iter()
19485 .any(|unit| unit.is_function() && unit.fq_name() == member),
19486 "{member} must be owned by the recovered class: {declarations:#?}"
19487 );
19488 }
19489 assert!(
19490 declarations
19491 .iter()
19492 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19493 "an unrecovered head must not mint a macro-named class: {declarations:#?}"
19494 );
19495 }
19496
19497 #[test]
19498 fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
19499 let source = r#"
19506namespace api {
19507
19508DIAGNOSTIC_PUSH
19509DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
19510
19511class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
19512 public virtual api::Inner::Key {
19513 public:
19514 std::string algo_name() const override;
19515};
19516
19517DIAGNOSTIC_POP
19518
19519}
19520"#;
19521 let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
19522 let declarations = parsed.declarations();
19523 let class = declarations
19524 .iter()
19525 .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
19526 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19527 assert_eq!(
19528 parsed.raw_supertypes.get(class),
19529 Some(&vec![
19530 "api::Outer::Key".to_string(),
19531 "api::Inner::Key".to_string()
19532 ]),
19533 "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
19534 );
19535 assert!(
19536 declarations
19537 .iter()
19538 .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
19539 "the member is owned by the recovered class: {declarations:#?}"
19540 );
19541 assert!(
19542 declarations
19543 .iter()
19544 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19545 "the macro invocation must not mint a declaration: {declarations:#?}"
19546 );
19547 }
19548
19549 #[test]
19550 fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
19551 let fixture = |head: &str, name: &str| {
19554 format!(
19555 r#"
19556namespace api {{
19557class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
19558 public:
19559 /** Return a descriptive string. */
19560 const char* what() const noexcept override {{ return m_msg.c_str(); }}
19561
19562 /** Return the type of error. */
19563 virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
19564
19565 /** Return an associated error code. */
19566 virtual int error_code() const noexcept {{ return 0; }}
19567
19568 /** Avoid throwing the base directly. */
19569 explicit Exception(std::string_view msg);
19570
19571 /** Avoid throwing the base directly. */
19572 Exception(const char* prefix, std::string_view msg);
19573
19574 /** Avoid throwing the base directly. */
19575 Exception(std::string_view msg, const std::exception& e);
19576
19577 private:
19578 std::string m_msg;
19579}};
19580
19581class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
19582 public:
19583 explicit {name}(std::string_view msg);
19584
19585 explicit {name}(std::string_view msg, std::string_view where);
19586
19587 {name}(std::string_view msg, const std::exception& e);
19588
19589 ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
19590}};
19591}} // namespace api
19592"#
19593 )
19594 };
19595 for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
19596 let source = fixture(head, name);
19597 let mut parser = Parser::new();
19598 parser
19599 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19600 .expect("set C++ grammar");
19601 let tree = parser.parse(&source, None).expect("parse fixture");
19602 let mut embedded = Vec::new();
19603 let mut stack = vec![tree.root_node()];
19604 while let Some(node) = stack.pop() {
19605 embedded.extend(
19606 recover_embedded_function_like_export_classes(node, &source)
19607 .into_iter()
19608 .map(|recovered| (recovered.name, recovered.raw_supertypes)),
19609 );
19610 let mut cursor = node.walk();
19611 stack.extend(node.named_children(&mut cursor));
19612 }
19613 assert!(
19614 embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
19615 "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
19616 tree.root_node().to_sexp()
19617 );
19618 assert!(
19619 embedded
19620 .iter()
19621 .all(|(recovered, _)| recovered != "OTHER_MACRO"),
19622 "{head}: the object-like macro is not a class: {embedded:#?}"
19623 );
19624
19625 let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
19626 let declarations = parsed.declarations();
19627 let class = declarations
19628 .iter()
19629 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
19630 .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
19631 assert_eq!(
19632 parsed.raw_supertypes.get(class),
19633 Some(&vec!["Exception".to_string()]),
19634 "{head}"
19635 );
19636 assert!(
19637 declarations
19638 .iter()
19639 .all(|unit| unit.identifier() != "OTHER_MACRO"),
19640 "{head}: the object-like macro must not become a declaration: {declarations:#?}"
19641 );
19642 }
19643 }
19644
19645 #[test]
19646 fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
19647 let source = r#"
19657namespace api {
19658class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
19659 public virtual Botan::EC_PublicKey {
19660 public:
19661 std::string algo_name() const override { return "ECDSA"; }
19662};
19663} // namespace api
19664"#;
19665 let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
19666 let declarations = parsed.declarations();
19667 assert!(
19668 declarations
19669 .iter()
19670 .all(|unit| !unit.identifier().is_empty()),
19671 "no declaration may carry an empty name: {declarations:#?}"
19672 );
19673 assert!(
19674 declarations
19675 .iter()
19676 .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
19677 "macros and head specifiers must not become declarations: {declarations:#?}"
19678 );
19679 let class = declarations
19680 .iter()
19681 .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
19682 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19683 assert_eq!(
19684 parsed.raw_supertypes.get(class),
19685 Some(&vec![
19686 "Botan::TPM2::PublicKey".to_string(),
19687 "Botan::EC_PublicKey".to_string()
19688 ]),
19689 "both qualified virtual bases are bases: {declarations:#?}"
19690 );
19691 assert!(
19692 declarations
19693 .iter()
19694 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19695 "the head must not mint a macro-named class: {declarations:#?}"
19696 );
19697 }
19698
19699 #[test]
19700 fn function_like_export_class_survives_a_preceding_malformed_body() {
19701 let source = r#"
19702namespace api {
19703class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
19704 public:
19705 /** Return a descriptive string. */
19706 const char* what() const noexcept override { return m_msg.c_str(); }
19707
19708 /** Return the type of error. */
19709 virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
19710
19711 /** Return an associated error code. */
19712 virtual int error_code() const noexcept { return 0; }
19713
19714 /** Avoid throwing the base directly. */
19715 explicit Exception(std::string_view msg);
19716
19717 /** Avoid throwing the base directly. */
19718 Exception(const char* prefix, std::string_view msg);
19719
19720 /** Avoid throwing the base directly. */
19721 Exception(std::string_view msg, const std::exception& e);
19722
19723 private:
19724 std::string m_msg;
19725};
19726
19727class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
19728 public:
19729 explicit Invalid_Argument(std::string_view msg);
19730
19731 explicit Invalid_Argument(std::string_view msg, std::string_view where);
19732
19733 Invalid_Argument(std::string_view msg, const std::exception& e);
19734
19735 ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
19736};
19737} // namespace api
19738"#;
19739 let mut parser = Parser::new();
19740 parser
19741 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19742 .expect("set C++ grammar");
19743 let tree = parser.parse(source, None).expect("parse fixture");
19744 let mut stack = vec![tree.root_node()];
19745 let mut saw_embedded_shape = false;
19746 while let Some(node) = stack.pop() {
19747 saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
19748 .iter()
19749 .any(|recovered| recovered.name == "Invalid_Argument");
19750 let mut cursor = node.walk();
19751 stack.extend(node.named_children(&mut cursor));
19752 }
19753 assert!(
19754 saw_embedded_shape,
19755 "fixture must retain the embedded error geometry: {}",
19756 tree.root_node().to_sexp()
19757 );
19758
19759 let parsed = parse_cpp_file(
19760 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
19761 source,
19762 &tree,
19763 );
19764 let declarations = parsed.declarations();
19765 let exception = declarations
19766 .iter()
19767 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
19768 .expect("qualified-base export class");
19769 let invalid = declarations
19770 .iter()
19771 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
19772 .expect("class embedded in the preceding malformed body");
19773
19774 assert_eq!(
19775 parsed.raw_supertypes.get(exception),
19776 Some(&vec!["std::exception".to_string()])
19777 );
19778 assert_eq!(
19779 parsed.raw_supertypes.get(invalid),
19780 Some(&vec!["Exception".to_string()])
19781 );
19782 assert!(
19783 parsed.materialization_records.iter().any(|record| matches!(
19784 record,
19785 MaterializationRecord::RecoveredDeclaration { unit, .. }
19786 if unit == invalid
19787 )),
19788 "the embedded class must retain recovery provenance: {:#?}",
19789 parsed.materialization_records
19790 );
19791 }
19792
19793 #[test]
19794 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
19795 let source = r#"
19796public:
19797 explicit Lookup_Error(std::string_view err) : Exception(err) {}
19798
19799 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
19800"#;
19801 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
19802 .expect("reparse merged constructor body");
19803 let (range, body) =
19804 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
19805 .unwrap_or_else(|| {
19806 panic!(
19807 "the merged constructor must retain its structured declarator/body: {}",
19808 tree.root_node().to_sexp()
19809 )
19810 });
19811 assert_eq!(
19812 source.get(range).expect("constructor range"),
19813 "Lookup_Error(std::string_view err) : Exception(err) {}"
19814 );
19815 assert_eq!(node_text(body, source), "{}");
19816 }
19817
19818 #[test]
19819 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
19820 let positive_source =
19821 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
19822 let mut parser = tree_sitter::Parser::new();
19823 parser
19824 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19825 .unwrap();
19826 let positive_tree = parser.parse(positive_source, None).unwrap();
19827 assert!(cpp_reparsed_members_are_indexable(
19828 positive_tree.root_node(),
19829 positive_source
19830 ));
19831
19832 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
19833 let negative_tree = parser.parse(negative_source, None).unwrap();
19834 assert!(!cpp_reparsed_members_are_indexable(
19835 negative_tree.root_node(),
19836 negative_source
19837 ));
19838 }
19839
19840 #[test]
19841 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
19842 let copy_control_source = r#"
19843public:
19844 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
19845 explicit Token(const Token* tok);
19846 ~Token();
19847 Token* astOperand1() { return nullptr; }
19848"#;
19849 let constraint_source = r#"
19850private:
19851 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19852 static T *tokAtImpl(T *tok, int index) {
19853 return tok;
19854 }
19855
19856 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19857 static T *linkAtImpl(T *tok, int index) {
19858 return tok;
19859 }
19860
19861public:
19862 int late() const { return 1; }
19863"#;
19864 let mut parser = tree_sitter::Parser::new();
19865 parser
19866 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19867 .unwrap();
19868 let copy_control_tree = parser
19869 .parse(copy_control_source, None)
19870 .expect("parse copy-control fixture");
19871 assert!(
19872 copy_control_tree.root_node().has_error(),
19873 "fixture must exercise adjacent copy-control recovery"
19874 );
19875 assert!(
19876 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
19877 "a complete late getter must remain recoverable after adjacent copy-control declarations"
19878 );
19879 let mut cursor = copy_control_tree.root_node().walk();
19880 assert!(
19881 copy_control_tree
19882 .root_node()
19883 .named_children(&mut cursor)
19884 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
19885 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
19886 copy_control_tree.root_node().to_sexp()
19887 );
19888 let constraint_tree = parser
19889 .parse(constraint_source, None)
19890 .expect("parse constraint-macro fixture");
19891 assert!(constraint_tree.root_node().has_error());
19892 assert!(
19893 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
19894 "complete constraint-macro members must not hide a later ordinary member"
19895 );
19896 let mut cursor = constraint_tree.root_node().walk();
19897 assert!(
19898 constraint_tree
19899 .root_node()
19900 .named_children(&mut cursor)
19901 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
19902 child,
19903 constraint_source
19904 )),
19905 "fixture must retain the split constraint-macro prefix/function geometry"
19906 );
19907 }
19908
19909 #[test]
19910 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
19911 let source = r#"
19912struct Analyzer {
19913 struct Action {
19914 Action() = default;
19915 Action(const Action&) = default;
19916 Action& operator=(const Action& rhs) & = default;
19917
19918 template<class T,
19919 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
19920 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
19921 // NOLINTNEXTLINE(google-explicit-constructor)
19922 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
19923 {}
19924
19925 enum : std::uint16_t { None = 0, Read = (1 << 0) };
19926 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
19927
19928 private:
19929 unsigned int mFlag{};
19930 };
19931
19932 enum class Direction : unsigned char { Forward, Reverse };
19933 virtual Action analyze(Direction d) const = 0;
19934};
19935"#;
19936 let mut parser = tree_sitter::Parser::new();
19937 parser
19938 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19939 .unwrap();
19940 let tree = parser.parse(source, None).unwrap();
19941 assert!(tree.root_node().has_error());
19942 let root = tree.root_node();
19943 let outer = root
19944 .named_children(&mut root.walk())
19945 .find(|child| child.kind() == "ERROR")
19946 .expect("fragmented Analyzer prefix");
19947 let outer_recovered =
19948 fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
19949 assert_eq!(outer_recovered.name, "Analyzer");
19950 let outer_tree = cpp_reparse_fragmented_class_body(
19951 source,
19952 outer_recovered.body.reparse_start,
19953 outer_recovered.body.reparse_end,
19954 )
19955 .expect("reparse Analyzer body");
19956 let outer_root = outer_tree.root_node();
19957 let action_prefix = outer_root
19958 .named_children(&mut outer_root.walk())
19959 .find(|child| child.kind() == "ERROR")
19960 .expect("fragmented Action prefix");
19961 let action_recovered = fragmented_class_body(action_prefix, source)
19962 .expect("structured Action fragment boundary");
19963 assert_eq!(action_recovered.name, "Action");
19964 let action_tree = cpp_reparse_fragmented_class_body(
19965 source,
19966 action_recovered.body.reparse_start,
19967 action_recovered.body.reparse_end,
19968 )
19969 .expect("reparse Action body");
19970 let action_root = action_tree.root_node();
19971 let macro_prefix = action_root
19972 .named_children(&mut action_root.walk())
19973 .find(|child| child.kind() == "ERROR")
19974 .expect("constraint macro prefix");
19975 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
19976 .expect("structured template macro prefix");
19977 let macro_companion =
19978 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
19979 assert!(
19980 cpp_reparsed_template_macro_constructor_companion_is_indexable(
19981 macro_companion,
19982 macro_parameter,
19983 source,
19984 ),
19985 "split constrained constructor must be admitted: {}",
19986 macro_companion.to_sexp()
19987 );
19988 assert!(
19989 cpp_reparsed_members_are_indexable(action_root, source),
19990 "complete Action body must pass the recovery gate: {}",
19991 action_tree.root_node().to_sexp()
19992 );
19993 assert!(
19994 cpp_reparsed_members_are_indexable(outer_root, source),
19995 "complete Analyzer body must pass the recovery gate: {}",
19996 outer_tree.root_node().to_sexp()
19997 );
19998 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
19999 let parsed = parse_cpp_file(&file, source, &tree);
20000 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
20001 assert!(
20002 parsed
20003 .declarations()
20004 .iter()
20005 .any(|unit| unit.fq_name() == expected),
20006 "missing recovered declaration {expected}: {:#?}",
20007 parsed.declarations()
20008 );
20009 }
20010 assert!(
20011 parsed
20012 .declarations()
20013 .iter()
20014 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
20015 "nested members must not remain flattened: {:#?}",
20016 parsed.declarations()
20017 );
20018 }
20019
20020 #[test]
20021 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
20022 let source = r#"
20023raw_hash_set& operator=(raw_hash_set&& that) {
20024 return move_assign(
20025 std::move(that),
20026 typename AllocTraits::propagate_on_container_move_assignment());
20027}
20028
20029iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
20030 return {};
20031}
20032
20033void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
20034
20035iterator insert(const_iterator hint, value_type&& value)
20036 ABSL_ATTRIBUTE_LIFETIME_BOUND {
20037 return {};
20038}
20039
20040friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
20041 return left.size() == right.size();
20042}
20043
20044static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
20045 return static_cast<slot_type*>(buffer);
20046}
20047
20048protected:
20049// Included-range recovery can attach this comment to the template prefix.
20050template <class K>
20051void AssertOnFind([[maybe_unused]] const K& key) {
20052 Check(key);
20053}
20054"#;
20055 let mut parser = tree_sitter::Parser::new();
20056 parser
20057 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20058 .unwrap();
20059 let tree = parser.parse(source, None).unwrap();
20060 assert!(
20061 tree.root_node().has_error(),
20062 "the fixture must exercise tree-sitter's errorful member shapes"
20063 );
20064 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
20065
20066 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
20067 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
20068 assert!(!cpp_reparsed_members_are_indexable(
20069 incomplete_tree.root_node(),
20070 incomplete_source
20071 ));
20072
20073 let outside_error_source = "int foo() stray_attribute {}\n";
20074 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
20075 assert!(outside_error_tree.root_node().has_error());
20076 assert!(!cpp_reparsed_members_are_indexable(
20077 outside_error_tree.root_node(),
20078 outside_error_source
20079 ));
20080
20081 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
20082 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
20083 assert!(!cpp_reparsed_members_are_indexable(
20084 variable_initializer_tree.root_node(),
20085 variable_initializer_source
20086 ));
20087 }
20088
20089 #[test]
20090 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
20091 let positive_source = r#"
20092std::pair<iterator, bool> insert(init_type&& value)
20093 ABSL_ATTRIBUTE_LIFETIME_BOUND
20094#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
20095 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
20096#endif
20097{
20098 return emplace(std::move(value));
20099}
20100"#;
20101 let mut parser = tree_sitter::Parser::new();
20102 parser
20103 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20104 .unwrap();
20105 let positive_tree = parser.parse(positive_source, None).unwrap();
20106 assert!(
20107 positive_tree.root_node().has_error(),
20108 "the fixture must exercise the split attribute/requires shape"
20109 );
20110 assert!(cpp_reparsed_members_are_indexable(
20111 positive_tree.root_node(),
20112 positive_source
20113 ));
20114
20115 let template_return_source = r#"
20116pair<int> insert(init_type&& value)
20117 ABSL_ATTRIBUTE_LIFETIME_BOUND
20118#if LANGUAGE_LEVEL >= 202002L
20119 requires(!Predicate<init_type>::value)
20120#endif
20121// Attributes and the function body may be separated by comments.
20122{
20123 return {};
20124}
20125"#;
20126 let template_return_tree = parser.parse(template_return_source, None).unwrap();
20127 assert!(
20128 cpp_reparsed_members_are_indexable(
20129 template_return_tree.root_node(),
20130 template_return_source
20131 ),
20132 "template-return attribute/requires tree: {}",
20133 template_return_tree.root_node().to_sexp()
20134 );
20135
20136 let no_body_source = r#"
20137std::pair<iterator, bool> insert(init_type&& value)
20138 ABSL_ATTRIBUTE_LIFETIME_BOUND
20139#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
20140 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
20141#endif
20142+ 0;
20143"#;
20144 let no_body_tree = parser.parse(no_body_source, None).unwrap();
20145 assert!(!cpp_reparsed_members_are_indexable(
20146 no_body_tree.root_node(),
20147 no_body_source
20148 ));
20149
20150 let extra_payload_source = r#"
20151pair<int> insert(init_type&& value)
20152 ABSL_ATTRIBUTE_LIFETIME_BOUND
20153#if LANGUAGE_LEVEL >= 202002L
20154 int unrelated;
20155 requires(Predicate<init_type>::value)
20156#endif
20157{
20158 return {};
20159}
20160"#;
20161 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
20162 assert!(!cpp_reparsed_members_are_indexable(
20163 extra_payload_tree.root_node(),
20164 extra_payload_source
20165 ));
20166
20167 let variable_initializer_source = r#"
20168int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
20169#if LANGUAGE_LEVEL >= 202002L
20170 requires(true)
20171#endif
20172{
20173 bad;
20174}
20175"#;
20176 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
20177 assert!(!cpp_reparsed_members_are_indexable(
20178 variable_initializer_tree.root_node(),
20179 variable_initializer_source
20180 ));
20181 }
20182
20183 #[test]
20184 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
20185 let source = r#"namespace absl {
20186ABSL_NAMESPACE_BEGIN namespace container_internal {
20187
20188class raw_hash_set : public Base {
20189 public:
20190 using value_type = int;
20191
20192 template <class U,
20193 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
20194 void insert(U value) { (void)value; }
20195
20196 struct InsertSlot {
20197 raw_hash_set& s;
20198 };
20199};
20200
20201}
20202ABSL_NAMESPACE_END
20203}"#;
20204 let mut parser = tree_sitter::Parser::new();
20205 parser
20206 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20207 .unwrap();
20208 let tree = parser.parse(source, None).unwrap();
20209 let root = tree.root_node();
20210 let outer_namespace = root
20211 .named_children(&mut root.walk())
20212 .find(|child| child.kind() == "namespace_definition")
20213 .expect("outer absl namespace");
20214 let declaration_list = outer_namespace
20215 .child_by_field_name("body")
20216 .expect("outer namespace body");
20217 let sentinel_function = declaration_list
20218 .named_children(&mut declaration_list.walk())
20219 .find(|child| child.kind() == "function_definition")
20220 .expect("malformed namespace sentinel function");
20221 let ancestry = ParentIndex::new(root);
20222 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
20223 .expect("structured nested namespace sentinel");
20224 let fragmented =
20225 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
20226 .expect("fragmented raw_hash_set class");
20227 assert_eq!(fragmented.class_node.kind(), "ERROR");
20228 assert_eq!(fragmented.name, "raw_hash_set");
20229 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
20230
20231 let outer_scope =
20232 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
20233 let mut outer_siblings = Vec::new();
20234 push_cpp_sentinel_sibling_classes(
20235 &mut outer_siblings,
20236 declaration_list,
20237 sentinel.function,
20238 &outer_scope,
20239 source,
20240 &ancestry,
20241 );
20242 let [outer_shadow] = outer_siblings.as_slice() else {
20243 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
20244 };
20245 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
20246 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
20247
20248 let field = " raw_hash_set& s;";
20249 let start = source.find(field).expect("InsertSlot field") + 4;
20250 let node = root
20251 .descendant_for_byte_range(start, start + "raw_hash_set".len())
20252 .expect("raw_hash_set type node");
20253 let recovered = cpp_sentinel_recovered_classes(root, source);
20254 let [deep_class] = recovered.as_slice() else {
20255 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
20256 };
20257 assert_eq!(
20258 deep_class.namespace_scope_components,
20259 vec!["absl", "container_internal"]
20260 );
20261 assert_eq!(
20262 deep_class.scope_components,
20263 vec!["absl", "container_internal", "raw_hash_set"]
20264 );
20265 assert!(
20266 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
20267 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
20268 );
20269
20270 assert_eq!(
20271 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
20272 Some(vec![
20273 "absl".to_string(),
20274 "container_internal".to_string(),
20275 "raw_hash_set".to_string(),
20276 "InsertSlot".to_string(),
20277 ])
20278 );
20279
20280 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
20281 let parsed = parse_cpp_file(&file, source, &tree);
20282 let raw_hash_set = parsed
20283 .declarations()
20284 .iter()
20285 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
20286 .expect("recovered raw_hash_set class");
20287 assert_eq!(
20288 raw_hash_set.fq_name(),
20289 "absl::container_internal.raw_hash_set",
20290 "the recovered declaration must publish under the deeper sentinel namespace"
20291 );
20292 assert_eq!(
20293 parsed.raw_supertypes.get(raw_hash_set),
20294 Some(&vec!["Base".to_string()]),
20295 "the structured base clause on the fragmented ERROR prefix must survive publication"
20296 );
20297 assert!(
20298 parsed.materialization_records.iter().any(|record| matches!(
20299 record,
20300 MaterializationRecord::RecoveredDeclaration { recovery, unit }
20301 if unit == raw_hash_set && *recovery == deep_class.class_range
20302 )),
20303 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
20304 parsed.materialization_records
20305 );
20306 }
20307
20308 #[test]
20335 fn the_parent_index_answers_what_tree_sitter_answers() {
20336 const SHAPES: [&str; 5] = [
20337 "namespace outer { namespace inner { struct Tag { int field; }; } }",
20338 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
20339 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
20340 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
20341 "class API Broken : public First, public Second {\n void member();\n",
20342 ];
20343 for source in SHAPES {
20344 let mut parser = tree_sitter::Parser::new();
20345 parser
20346 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20347 .unwrap();
20348 let tree = parser.parse(source, None).unwrap();
20349 let root = tree.root_node();
20350 let ancestry = ParentIndex::new(root);
20351 let mut nodes = 0usize;
20352 let mut stack = vec![root];
20353 while let Some(node) = stack.pop() {
20354 nodes += 1;
20355 assert_eq!(
20356 node.parent().map(|parent| parent.id()),
20357 ancestry.parent(node).map(|parent| parent.id()),
20358 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
20359 );
20360 let mut cursor = node.walk();
20361 stack.extend(node.children(&mut cursor));
20362 }
20363 assert!(nodes > 1, "{source:?} produced no tree to compare");
20364 }
20365 }
20366
20367 #[test]
20374 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
20375 const DEPTH: usize = 64;
20376 let mut source = String::new();
20377 for level in 0..DEPTH {
20378 writeln!(source, "namespace n{level} {{").unwrap();
20379 }
20380 source.push_str("int deepest(int value);\n");
20381 for _ in 0..DEPTH {
20382 source.push_str("}\n");
20383 }
20384
20385 let mut parser = tree_sitter::Parser::new();
20386 parser
20387 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20388 .unwrap();
20389 let tree = parser.parse(&source, None).unwrap();
20390 let root = tree.root_node();
20391 let ancestry = ParentIndex::new(root);
20392 let mut function_declarator = None;
20393 walk_named_tree_preorder(root, true, |node| {
20394 if node.kind() == "function_declarator" {
20395 function_declarator = Some(node);
20396 WalkControl::Break
20397 } else {
20398 WalkControl::Continue
20399 }
20400 });
20401 let function_declarator = function_declarator.expect("deepest function declarator");
20402 let ancestor_count =
20403 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
20404
20405 ancestry.reset_parent_query_count_for_test();
20406 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
20407 assert_eq!(DEPTH, lexical_scope.len());
20408 assert_eq!(
20409 ancestor_count + 1,
20410 ancestry.parent_query_count_for_test(),
20411 "lexical-scope ancestry bypassed the parent index"
20412 );
20413
20414 ancestry.reset_parent_query_count_for_test();
20415 assert_eq!(
20416 DispatchExtensibility::Closed,
20417 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
20418 );
20419 assert_eq!(
20420 ancestor_count,
20421 ancestry.parent_query_count_for_test(),
20422 "dispatch ancestry bypassed the parent index"
20423 );
20424
20425 ancestry.reset_parent_query_count_for_test();
20426 assert_eq!(
20427 CallableLinkage::External,
20428 cpp_callable_linkage(function_declarator, &source, &ancestry)
20429 );
20430 assert_eq!(
20431 ancestor_count + 1,
20432 ancestry.parent_query_count_for_test(),
20433 "linkage ancestry bypassed the parent index"
20434 );
20435
20436 ancestry.reset_parent_query_count_for_test();
20437 assert!(!cpp_callable_is_structural_constructor(
20438 function_declarator,
20439 &source,
20440 &ancestry
20441 ));
20442 assert_eq!(
20443 ancestor_count + 1,
20444 ancestry.parent_query_count_for_test(),
20445 "constructor ancestry bypassed the parent index"
20446 );
20447 }
20448
20449 #[test]
20454 fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
20455 for aggregates in [64usize, 512] {
20456 let mut source =
20457 String::from("typedef unsigned long long u64;\nnamespace generated {\n");
20458 for index in 0..aggregates {
20459 writeln!(source, "struct tag{index};").unwrap();
20460 }
20461 for index in (0..aggregates).rev() {
20462 writeln!(
20463 source,
20464 "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
20465 )
20466 .unwrap();
20467 }
20468 source.push_str("}\n");
20469
20470 start_code_unit_removal_scan_probe();
20471 let parsed = parse_cpp_declarations(&source, "vmlinux.h");
20472 let scanned = finish_code_unit_removal_scan_probe();
20473
20474 let expected_names: Vec<String> = (0..aggregates)
20475 .rev()
20476 .map(|index| format!("tag{index}"))
20477 .collect();
20478 let top_level_names: Vec<String> = parsed
20479 .top_level_declarations
20480 .iter()
20481 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20482 .map(|unit| unit.short_name().to_string())
20483 .collect();
20484 let namespace = parsed
20485 .declarations()
20486 .iter()
20487 .find(|unit| {
20488 unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
20489 })
20490 .expect("generated namespace should be declared");
20491 let child_names: Vec<String> = parsed.children[namespace]
20492 .iter()
20493 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20494 .map(|unit| unit.short_name().to_string())
20495 .collect();
20496 assert_eq!(
20497 aggregates,
20498 parsed
20499 .declarations()
20500 .iter()
20501 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20502 .count(),
20503 "every aggregate must still be declared at {aggregates} aggregates"
20504 );
20505 assert_eq!(expected_names, top_level_names);
20506 assert_eq!(expected_names, child_names);
20507 assert_eq!(
20508 0, scanned,
20509 "replacing {aggregates} forward declarations must compact their shared lists once"
20510 );
20511 }
20512 }
20513
20514 #[test]
20515 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
20516 const DISTINCT_PER_KIND: usize = 64;
20517 let mut source = String::new();
20518 for index in 0..DISTINCT_PER_KIND {
20519 writeln!(source, "typedef int Alias{index};").unwrap();
20520 }
20521 writeln!(source, "typedef long Alias0;").unwrap();
20522 for index in 0..DISTINCT_PER_KIND {
20523 writeln!(source, "#define MACRO_{index} {index}").unwrap();
20524 }
20525 writeln!(source, "#define MACRO_0 duplicate").unwrap();
20526 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
20527
20528 let mut parser = tree_sitter::Parser::new();
20529 parser
20530 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20531 .unwrap();
20532 let tree = parser.parse(&source, None).unwrap();
20533 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
20534
20535 start_declaration_identity_comparison_probe();
20536 let parsed = parse_cpp_file(&file, &source, &tree);
20537 let comparisons = finish_declaration_identity_comparison_probe();
20538
20539 assert_eq!(
20540 DISTINCT_PER_KIND + 1,
20541 parsed
20542 .declarations()
20543 .iter()
20544 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
20545 .count(),
20546 "every physical typedef alias declaration must be retained so \
20547 conditional branch guards stay available to the resolver"
20548 );
20549 assert_eq!(
20550 DISTINCT_PER_KIND + 1,
20551 parsed
20552 .declarations()
20553 .iter()
20554 .filter(|unit| {
20555 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
20556 })
20557 .count(),
20558 "distinct macro redefinitions must remain available to temporal lookup"
20559 );
20560 assert_eq!(
20561 2,
20562 parsed
20563 .declarations()
20564 .iter()
20565 .filter(|unit| {
20566 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
20567 })
20568 .count(),
20569 "function overloads must remain distinct"
20570 );
20571
20572 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
20573 assert!(
20574 comparisons <= dedup_inputs * 4,
20575 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
20576 );
20577 }
20578
20579 #[test]
20580 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
20581 let source = r#"namespace absl {
20582ABSL_NAMESPACE_BEGIN namespace container_internal {
20583template <typename T>
20584class broken {
20585 public:
20586 using value_type = T;
20587 T operator->() const { return &operator*(); }
20588 using alias = value_type;
20589};
20590}
20591}
20592"#;
20593 let mut parser = tree_sitter::Parser::new();
20594 parser
20595 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20596 .unwrap();
20597 let tree = parser.parse(source, None).unwrap();
20598 let broken = find_class_named(tree.root_node(), source, "broken")
20599 .expect("the positive fixture must expose the broken class node");
20600 assert!(
20601 broken.has_error(),
20602 "the positive fixture must retain an internal parser error"
20603 );
20604 assert!(
20605 cpp_complete_class_body_close(broken).is_some(),
20606 "the positive fixture must expose a real class body close"
20607 );
20608 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20609 assert!(
20610 recovered.iter().any(|class| {
20611 class.scope_components == ["absl", "container_internal", "broken"]
20612 }),
20613 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
20614 );
20615 }
20616
20617 #[test]
20618 fn sentinel_recovery_keeps_members_after_nested_body_close() {
20619 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
20620NLOHMANN_BASIC_JSON_TPL_DECLARATION
20621class basic_json {
20622 private:
20623 union storage {
20624 int value;
20625 } data;
20626 public:
20627 using late_alias = int;
20628 late_alias value() const;
20629};
20630NLOHMANN_JSON_NAMESPACE_END
20631"#;
20632 let mut parser = tree_sitter::Parser::new();
20633 parser
20634 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20635 .unwrap();
20636 let tree = parser.parse(source, None).unwrap();
20637 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20638 let basic_json = recovered
20639 .iter()
20640 .find(|class| {
20641 class
20642 .scope_components
20643 .last()
20644 .is_some_and(|name| name == "basic_json")
20645 })
20646 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
20647 let late_alias = source
20648 .find("late_alias value")
20649 .expect("late alias reference");
20650 assert!(
20651 basic_json.class_range.start_byte < late_alias
20652 && late_alias < basic_json.class_range.end_byte,
20653 "the recovered class range must include members after a nested close: {basic_json:#?}"
20654 );
20655 }
20656
20657 #[test]
20658 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
20659 let source = r#"namespace absl {
20660ABSL_NAMESPACE_BEGIN namespace container_internal {
20661template <typename T>
20662class broken {
20663 public:
20664 using value_type = T;
20665 T operator->() const { return &operator*(); }
20666}
20667}
20668"#;
20669 let mut parser = tree_sitter::Parser::new();
20670 parser
20671 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20672 .unwrap();
20673 let tree = parser.parse(source, None).unwrap();
20674 let broken = find_class_named(tree.root_node(), source, "broken")
20675 .expect("the negative fixture must expose the malformed class node");
20676 assert!(
20677 broken.has_error(),
20678 "the negative fixture must retain a parser error"
20679 );
20680 assert!(
20681 cpp_complete_class_body_close(broken).is_none(),
20682 "the malformed class must not expose a real body close"
20683 );
20684 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20685 assert!(
20686 recovered
20687 .iter()
20688 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
20689 "an incomplete class must not borrow the namespace close: {recovered:#?}"
20690 );
20691 }
20692
20693 #[test]
20694 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
20695 let source = r#"namespace absl {
20696ABSL_NAMESPACE_BEGIN namespace container_internal {
20697template <typename T>
20698struct broken {
20699 using value_type = T;
20700};
20701}
20702
20703#ifdef OWNER_DEF
20704template <typename T>
20705typename broken<T>::value_type broken<T>::method() {
20706 value_type value{};
20707 return value;
20708}
20709#endif
20710
20711namespace sibling {
20712template <typename T>
20713typename broken<T>::value_type broken<T>::other() {
20714 value_type value{};
20715 return value;
20716}
20717}
20718
20719ABSL_NAMESPACE_END
20720}
20721"#;
20722 let mut parser = tree_sitter::Parser::new();
20723 parser
20724 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20725 .unwrap();
20726 let tree = parser.parse(source, None).unwrap();
20727 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20728 let broken = recovered
20729 .iter()
20730 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20731 .expect("the sentinel class must be recovered");
20732 let method_start = source
20733 .find("typename broken<T>::value_type broken<T>::method()")
20734 .expect("guarded sibling owner");
20735 let method_end = source[method_start..]
20736 .find("\n}")
20737 .map(|offset| method_start + offset + 2)
20738 .expect("guarded sibling owner close");
20739 assert!(
20740 broken
20741 .owner_ranges
20742 .iter()
20743 .any(|owner| owner.range.start_byte <= method_start
20744 && method_end <= owner.range.end_byte),
20745 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
20746 );
20747 let sibling_start = source
20748 .find("typename broken<T>::value_type broken<T>::other()")
20749 .expect("nested namespace sibling owner");
20750 assert!(
20751 broken
20752 .owner_ranges
20753 .iter()
20754 .all(|owner| owner.range.start_byte > sibling_start
20755 || owner.range.end_byte <= sibling_start),
20756 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
20757 );
20758 }
20759
20760 #[test]
20761 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
20762 let source = r#"#ifdef OUTER
20763namespace absl {
20764ABSL_NAMESPACE_BEGIN namespace container_internal {
20765template <typename T>
20766struct broken {
20767 using value_type = T;
20768};
20769}
20770}
20771
20772#ifdef OWNER_DEF
20773template <typename T>
20774typename broken<T>::value_type broken<T>::method() {
20775 value_type value{};
20776 return value;
20777}
20778#endif
20779#endif
20780"#;
20781 let mut parser = tree_sitter::Parser::new();
20782 parser
20783 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20784 .unwrap();
20785 let tree = parser.parse(source, None).unwrap();
20786 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20787 let broken = recovered
20788 .iter()
20789 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20790 .expect("the sentinel class must be recovered");
20791 let method_start = source
20792 .find("typename broken<T>::value_type broken<T>::method()")
20793 .expect("outer sibling owner");
20794 assert!(
20795 broken
20796 .owner_ranges
20797 .iter()
20798 .all(|owner| owner.range.start_byte > method_start
20799 || owner.range.end_byte <= method_start),
20800 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
20801 );
20802 }
20803
20804 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
20806 let mut signatures = parsed
20807 .declarations()
20808 .iter()
20809 .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
20810 .filter_map(|unit| unit.signature().map(str::to_string))
20811 .collect::<Vec<_>>();
20812 signatures.sort();
20813 signatures.dedup();
20814 signatures
20815 }
20816
20817 #[test]
20818 fn callable_parameter_types_come_from_the_ast_parameter_list() {
20819 let source = r#"
20820template <typename T, ENABLE_BYTES(T)>
20821Vec256<T> DupOdd(Vec256<T> value) { return value; }
20822
20823struct Visitor {
20824 void fail(this auto const& self) {}
20825};
20826"#;
20827 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
20828 let dup_odd = parsed
20829 .declarations()
20830 .iter()
20831 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
20832 .expect("DupOdd declaration");
20833 assert_eq!(
20834 dup_odd.signature(),
20835 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
20836 );
20837 assert_eq!(
20838 parsed
20839 .signature_metadata
20840 .get(dup_odd)
20841 .and_then(|metadata| metadata.first())
20842 .and_then(SignatureMetadata::callable_parameter_types),
20843 Some(["Vec256<T>".to_string()].as_slice())
20844 );
20845
20846 let fail = parsed
20847 .declarations()
20848 .iter()
20849 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
20850 .expect("explicit-object member");
20851 assert_eq!(fail.signature(), Some("(const this auto &)"));
20852 let metadata = parsed
20853 .signature_metadata
20854 .get(fail)
20855 .and_then(|metadata| metadata.first())
20856 .expect("explicit-object signature metadata");
20857 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
20858 assert!(
20859 metadata
20860 .callable_arity()
20861 .is_some_and(|arity| arity.accepts(0))
20862 );
20863 }
20864
20865 #[test]
20866 fn trailing_qualifiers_survive_parameter_list_whitespace() {
20867 let source = r#"
20872struct Widget {
20873 bool multiline(int settings, int supprs) const;
20874 bool doublespace(int settings, int supprs) const;
20875 bool noexcept_multiline(int settings, int supprs) noexcept;
20876 bool ref_multiline(int settings, int supprs) &&;
20877};
20878bool
20879Widget::multiline (int settings,
20880 int supprs) const
20881{ return settings + supprs > 0; }
20882bool Widget::doublespace(int settings, int supprs) const { return true; }
20883bool Widget::noexcept_multiline(int settings,
20884 int supprs) noexcept { return true; }
20885bool Widget::ref_multiline(int settings,
20886 int supprs) && { return true; }
20887"#;
20888 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
20889 assert_eq!(
20890 vec!["(int, int) const".to_string()],
20891 identity_signatures(&parsed, "Widget.multiline")
20892 );
20893 assert_eq!(
20894 vec!["(int, int) const".to_string()],
20895 identity_signatures(&parsed, "Widget.doublespace")
20896 );
20897 assert_eq!(
20898 vec!["(int, int) noexcept".to_string()],
20899 identity_signatures(&parsed, "Widget.noexcept_multiline")
20900 );
20901 assert_eq!(
20902 vec!["(int, int) &&".to_string()],
20903 identity_signatures(&parsed, "Widget.ref_multiline")
20904 );
20905 }
20906
20907 #[test]
20908 fn macro_fragmented_plain_class_keeps_following_member_signature() {
20909 let source = r#"
20910struct CString {};
20911class CMessage {
20912public:
20913 CString GetParams(unsigned int index, unsigned int length = -1) const
20914 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
20915 return GetParamsColon(index, length);
20916 }
20917 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
20918};
20919CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
20920 return {};
20921}
20922"#;
20923 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
20924 assert_eq!(
20925 vec!["(unsigned int, unsigned int) const".to_string()],
20926 identity_signatures(&parsed, "CMessage.GetParamsColon")
20927 );
20928 }
20929
20930 #[test]
20931 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
20932 let source = r#"
20933#pragma once
20934#define DEMO_DEPRECATED(message)
20935namespace demo {
20936struct Base {
20937 static int aligned(int value) { return value; }
20938 int legacy(int value) const
20939 DEMO_DEPRECATED("use replacement()") { return value; }
20940 int replacement() const;
20941 void run(int value);
20942};
20943struct OtherBase {
20944 void run(int value);
20945 static int aligned(int value) { return value; }
20946};
20947struct Derived : Base {};
20948struct Override : Base {
20949 void run(int value);
20950 static int aligned(int value) { return value; }
20951};
20952struct RecoveredOverride : Base {
20953 int legacy(int value) const
20954 DEMO_DEPRECATED("use replacement()") { return value; }
20955 void run(int value);
20956};
20957struct Hidden : Base {
20958 void run(int first, int second);
20959 static int aligned(int first, int second) { return first + second; }
20960};
20961struct Ambiguous : Base, OtherBase {};
20962}
20963struct Global {};
20964"#;
20965 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
20966 let declarations = parsed.declarations();
20967 let fq_names = declarations
20968 .iter()
20969 .map(|unit| unit.fq_name())
20970 .collect::<std::collections::BTreeSet<_>>();
20971
20972 for expected in [
20973 "demo.Base",
20974 "demo.Base.aligned",
20975 "demo.Base.legacy",
20976 "demo.Base.replacement",
20977 "demo.Base.run",
20978 "demo.Derived",
20979 "demo.OtherBase",
20980 "demo.Override",
20981 "demo.RecoveredOverride",
20982 "demo.Hidden",
20983 "demo.Ambiguous",
20984 "Global",
20985 ] {
20986 assert!(
20987 fq_names.contains(expected),
20988 "missing {expected} from namespaced macro fragment: {declarations:#?}"
20989 );
20990 }
20991 assert!(
20992 !fq_names.contains("Derived"),
20993 "following class escaped its namespace: {declarations:#?}"
20994 );
20995 assert!(
20996 !fq_names.contains("demo.Global"),
20997 "global class crossed the recovered namespace boundary: {declarations:#?}"
20998 );
20999 }
21000
21001 #[test]
21002 fn trailing_qualifiers_still_separate_genuine_overloads() {
21003 let source = r#"
21006struct Widget {
21007 int* slot(int index);
21008 const int* slot(int index) const;
21009 int log(int severity) &;
21010 int log(int severity) &&;
21011};
21012"#;
21013 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
21014 assert_eq!(
21015 vec!["(int)".to_string(), "(int) const".to_string()],
21016 identity_signatures(&parsed, "Widget.slot")
21017 );
21018 assert_eq!(
21019 vec!["(int) &".to_string(), "(int) &&".to_string()],
21020 identity_signatures(&parsed, "Widget.log")
21021 );
21022 }
21023
21024 #[test]
21025 fn virtual_specifier_is_not_part_of_the_identity_signature() {
21026 let source = r#"
21029struct Base {
21030 virtual void run(int value) const;
21031};
21032struct Widget : Base {
21033 void run(int value) const override;
21034};
21035void Widget::run(int value) const {}
21036"#;
21037 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
21038 assert_eq!(
21039 vec!["(int) const".to_string()],
21040 identity_signatures(&parsed, "Widget.run")
21041 );
21042 }
21043
21044 #[test]
21045 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
21046 let source = r#"
21050struct Widget {
21051 bool value_params(const int settings, const int supprs);
21052 void pointee_const(const int* p);
21053 void pointer_const(int* const p);
21054 void both_const(const int* const p);
21055 void reference_const(const int& p);
21056 void array_const(const int values[4]);
21057};
21058bool Widget::value_params(int settings, int supprs) { return true; }
21059void Widget::pointer_const(int* p) {}
21060void Widget::both_const(const int* p) {}
21061"#;
21062 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
21063 assert_eq!(
21064 vec!["(int, int)".to_string()],
21065 identity_signatures(&parsed, "Widget.value_params")
21066 );
21067 assert_eq!(
21068 vec!["(int *)".to_string()],
21069 identity_signatures(&parsed, "Widget.pointer_const")
21070 );
21071 assert_eq!(
21072 vec!["(const int *)".to_string()],
21073 identity_signatures(&parsed, "Widget.both_const")
21074 );
21075 assert_eq!(
21077 vec!["(const int *)".to_string()],
21078 identity_signatures(&parsed, "Widget.pointee_const")
21079 );
21080 assert_eq!(
21081 vec!["(const int &)".to_string()],
21082 identity_signatures(&parsed, "Widget.reference_const")
21083 );
21084 assert_eq!(
21085 vec!["(const int [4])".to_string()],
21086 identity_signatures(&parsed, "Widget.array_const")
21087 );
21088 }
21089
21090 #[test]
21091 fn top_level_parameter_const_still_separates_pointee_overloads() {
21092 let source = r#"
21093struct Widget {
21094 void take(const int* p);
21095 void take(int* p);
21096};
21097"#;
21098 let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
21099 assert_eq!(
21100 vec!["(const int *)".to_string(), "(int *)".to_string()],
21101 identity_signatures(&parsed, "Widget.take")
21102 );
21103 }
21104
21105 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
21106 let mut parser = tree_sitter::Parser::new();
21107 parser
21108 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21109 .unwrap();
21110 let tree = parser.parse(source, None).unwrap();
21111 let start = source.find(callable_name).expect("callable declaration");
21112 let declarator =
21113 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
21114 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
21115 }
21116
21117 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
21118 let mut shapes = comparable_shapes(source, callable_name);
21119 assert_eq!(1, shapes.len(), "{shapes:?}");
21120 match shapes.remove(0) {
21121 CppComparableSlot::Shape(shape) => shape,
21122 other => panic!("expected a comparable shape, got {other:?}"),
21123 }
21124 }
21125
21126 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
21127 let mut current = shape.root();
21128 loop {
21129 match shape.node(current) {
21130 CppComparableNode::Named { .. } => return shape.node(current),
21131 CppComparableNode::Pointer { inner, .. }
21132 | CppComparableNode::Reference { inner }
21133 | CppComparableNode::Array { inner } => current = *inner,
21134 CppComparableNode::Generic { base, .. } => current = *base,
21135 }
21136 }
21137 }
21138
21139 #[test]
21140 fn comparable_shape_keeps_pointee_const() {
21141 assert_ne!(
21142 sole_comparable_shape("void f(const char* p);", "f("),
21143 sole_comparable_shape("void f(char* p);", "f(")
21144 );
21145 }
21146
21147 #[test]
21148 fn comparable_shape_keeps_inner_pointer_const() {
21149 assert_ne!(
21150 sole_comparable_shape("void f(int** p);", "f("),
21151 sole_comparable_shape("void f(int* const* p);", "f(")
21152 );
21153 }
21154
21155 #[test]
21156 fn comparable_shape_drops_top_level_pointer_const() {
21157 assert_eq!(
21158 sole_comparable_shape("void f(int* const p);", "f("),
21159 sole_comparable_shape("void f(int* p);", "f(")
21160 );
21161 }
21162
21163 #[test]
21164 fn comparable_shape_drops_top_level_base_const() {
21165 assert_eq!(
21166 sole_comparable_shape("void f(const int p);", "f("),
21167 sole_comparable_shape("void f(int p);", "f(")
21168 );
21169 }
21170
21171 #[test]
21172 fn comparable_shape_decays_top_level_array_to_pointer() {
21173 assert_eq!(
21174 sole_comparable_shape("void f(int a[3]);", "f("),
21175 sole_comparable_shape("void f(int* a);", "f(")
21176 );
21177 assert_eq!(
21178 sole_comparable_shape("void f(int* a[3]);", "f("),
21179 sole_comparable_shape("void f(int** a);", "f(")
21180 );
21181 }
21182
21183 #[test]
21184 fn comparable_shape_keeps_array_behind_pointer() {
21185 assert_ne!(
21186 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
21187 sole_comparable_shape("struct S { void f(int** a); };", "f(")
21188 );
21189 }
21190
21191 #[test]
21192 fn comparable_shape_records_written_name_and_lexical_scope() {
21193 let declared =
21194 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
21195 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
21196 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
21197 panic!("named leaf");
21198 };
21199 assert_eq!(["Msg".to_string()].as_slice(), name.path());
21200 assert_eq!(
21201 ["ns".to_string(), "S".to_string()].as_slice(),
21202 name.lexical_scope()
21203 );
21204 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
21205 panic!("named leaf");
21206 };
21207 assert_eq!(
21208 ["ns".to_string(), "Msg".to_string()].as_slice(),
21209 name.path()
21210 );
21211 assert!(name.lexical_scope().is_empty());
21212 assert_ne!(declared, defined);
21213 }
21214
21215 #[test]
21216 fn comparable_shape_marks_sized_primitive_leaf() {
21217 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
21218 let CppComparableNode::Named {
21219 name, primitive, ..
21220 } = comparable_named_leaf(&shape)
21221 else {
21222 panic!("named leaf");
21223 };
21224 assert!(primitive);
21225 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
21226 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
21227 }
21228
21229 #[test]
21230 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
21231 assert_eq!(
21232 vec![CppComparableSlot::Unstructured],
21233 comparable_shapes("void f(void (*cb)(int));", "f(")
21234 );
21235 }
21236
21237 #[test]
21238 fn comparable_shape_reports_ellipsis_slot() {
21239 let shapes = comparable_shapes("void f(int a, ...);", "f(");
21240 assert_eq!(2, shapes.len(), "{shapes:?}");
21241 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
21242 }
21243
21244 #[test]
21245 fn comparable_shape_keeps_template_argument_const() {
21246 assert_ne!(
21247 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
21248 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
21249 );
21250 }
21251
21252 #[test]
21255 fn c_file_mints_aggregate_member_tag_at_file_scope() {
21256 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
21257 let parsed = parse_cpp_declarations(source, "x.c");
21258 let declarations = parsed.declarations();
21259
21260 assert!(
21261 declarations
21262 .iter()
21263 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21264 "expected a file-scope inner tag, got {declarations:?}"
21265 );
21266 assert!(
21267 declarations
21268 .iter()
21269 .all(|unit| unit.fq_name() != "outer$inner"),
21270 "expected no nested identity, got {declarations:?}"
21271 );
21272 assert!(
21273 declarations
21274 .iter()
21275 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
21276 );
21277 assert!(
21279 declarations
21280 .iter()
21281 .any(|unit| unit.fq_name() == "inner.value")
21282 );
21283 assert!(
21284 declarations
21285 .iter()
21286 .any(|unit| unit.fq_name() == "outer.item")
21287 );
21288
21289 let outer = declarations
21290 .iter()
21291 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
21292 .expect("outer");
21293 assert!(
21294 parsed
21295 .children
21296 .get(outer)
21297 .into_iter()
21298 .flatten()
21299 .all(|child| child.fq_name() != "inner"),
21300 "the tag must not hang off the aggregate it is written inside: {:?}",
21301 parsed.children
21302 );
21303 }
21304
21305 #[test]
21309 fn header_and_cpp_files_keep_nested_tag_identity() {
21310 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
21311 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
21312 let parsed = parse_cpp_declarations(source, name);
21313 let declarations = parsed.declarations();
21314 assert!(
21315 declarations
21316 .iter()
21317 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21318 "{name} must keep the nested identity, got {declarations:?}"
21319 );
21320 assert!(
21321 declarations.iter().all(|unit| unit.fq_name() != "inner"),
21322 "{name} must not mint a file-scope tag, got {declarations:?}"
21323 );
21324 assert!(
21325 declarations
21326 .iter()
21327 .any(|unit| unit.fq_name() == "outer$inner.value")
21328 );
21329 }
21330 }
21331
21332 #[test]
21334 fn uppercase_c_extension_keeps_cpp_tag_scope() {
21335 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
21336 let parsed = parse_cpp_declarations(source, "x.C");
21337 assert!(
21338 parsed
21339 .declarations()
21340 .iter()
21341 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
21342 );
21343 }
21344
21345 #[test]
21348 fn c_file_mints_every_nesting_level_at_file_scope() {
21349 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
21350 let parsed = parse_cpp_declarations(source, "z.c");
21351 let declarations = parsed.declarations();
21352
21353 for tag in ["a", "b", "c"] {
21354 assert!(
21355 declarations
21356 .iter()
21357 .any(|unit| unit.is_class() && unit.fq_name() == tag),
21358 "expected a file-scope {tag}, got {declarations:?}"
21359 );
21360 }
21361 assert!(
21362 declarations
21363 .iter()
21364 .all(|unit| !unit.fq_name().contains('$')),
21365 "no level may keep a nested identity, got {declarations:?}"
21366 );
21367 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
21369 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
21370 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
21371 }
21372
21373 #[test]
21376 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
21377 let source = "struct outer { enum color { RED, GREEN } c; };\n";
21378 let parsed = parse_cpp_declarations(source, "e.c");
21379 let declarations = parsed.declarations();
21380
21381 let color = declarations
21382 .iter()
21383 .find(|unit| unit.is_class() && unit.fq_name() == "color")
21384 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
21385 assert!(
21386 declarations
21387 .iter()
21388 .all(|unit| unit.fq_name() != "outer$color")
21389 );
21390 for enumerator in ["color.RED", "color.GREEN"] {
21391 assert!(
21392 declarations.iter().any(|unit| unit.fq_name() == enumerator),
21393 "expected {enumerator}, got {declarations:?}"
21394 );
21395 }
21396 let children = parsed
21397 .children
21398 .get(color)
21399 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
21400 assert!(
21401 ["color.RED", "color.GREEN"]
21402 .iter()
21403 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
21404 "enumerators must hang off their enum: {children:?}"
21405 );
21406 }
21407
21408 #[test]
21409 fn c_file_mints_member_list_union_at_file_scope() {
21410 let source = "struct outer { union inner { int a; float b; } item; };\n";
21411 let parsed = parse_cpp_declarations(source, "u.c");
21412 let declarations = parsed.declarations();
21413 assert!(
21414 declarations
21415 .iter()
21416 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21417 "expected a file-scope inner union, got {declarations:?}"
21418 );
21419 assert!(
21420 declarations
21421 .iter()
21422 .all(|unit| unit.fq_name() != "outer$inner")
21423 );
21424 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
21425 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
21426 }
21427
21428 #[test]
21431 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
21432 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
21433 let parsed = parse_cpp_declarations(source, "n.c");
21434 let declarations = parsed.declarations();
21435 let inner = declarations
21436 .iter()
21437 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
21438 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
21439 assert_eq!(inner.package_name(), "ns");
21440 assert!(
21441 declarations
21442 .iter()
21443 .all(|unit| unit.fq_name() != "ns.outer$inner")
21444 );
21445 }
21446
21447 #[test]
21452 fn function_local_tags_are_unchanged_in_both_dialects() {
21453 let source =
21454 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
21455 for name in ["y.c", "y.cpp"] {
21456 let parsed = parse_cpp_declarations(source, name);
21457 let declarations = parsed.declarations();
21458 assert!(
21459 declarations
21460 .iter()
21461 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
21462 "{name}: {declarations:?}"
21463 );
21464 for tag in ["localtag", "deeper", "localtag$deeper"] {
21465 assert!(
21466 declarations.iter().all(|unit| unit.fq_name() != tag),
21467 "{name} must not mint {tag}, got {declarations:?}"
21468 );
21469 }
21470 }
21471 }
21472
21473 #[test]
21476 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
21477 let source = "typedef struct { int v; } T;\n";
21478 for name in ["t.c", "t.cpp"] {
21479 let parsed = parse_cpp_declarations(source, name);
21480 let declarations = parsed.declarations();
21481 assert!(
21482 declarations
21483 .iter()
21484 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
21485 "{name}: {declarations:?}"
21486 );
21487 }
21488 }
21489
21490 #[test]
21497 fn anonymous_aggregate_members_are_identical_in_both_dialects() {
21498 let sources = [
21499 "struct outer { union { int a; float b; }; };\n",
21500 "struct outer { struct { int *ops; } sock; };\n",
21501 "void run(void) { struct { int v; } item; }\n",
21502 "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
21503 ];
21504 for source in sources {
21505 let names = |name: &str| {
21506 parse_cpp_declarations(source, name)
21507 .declarations()
21508 .iter()
21509 .map(|unit| unit.fq_name())
21510 .collect::<std::collections::BTreeSet<_>>()
21511 };
21512 let c_names = names("a.c");
21513 let cpp_names = names("a.cpp");
21514 assert_eq!(
21515 c_names, cpp_names,
21516 "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
21517 );
21518 assert!(
21519 c_names
21520 .iter()
21521 .any(|name| name.contains('.') && name != "outer"),
21522 "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
21523 );
21524 }
21525 }
21526
21527 #[test]
21528 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
21529 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
21530 let parsed = parse_cpp_declarations(source, "socket.c");
21531 let declarations = parsed.declarations();
21532 assert_eq!(
21533 declarations
21534 .iter()
21535 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
21536 .count(),
21537 1,
21538 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
21539 );
21540 for expected in [
21541 "PAL_HANDLE",
21542 "PAL_HANDLE.sock",
21543 "PAL_HANDLE$sock",
21544 "PAL_HANDLE$sock.ops",
21545 ] {
21546 assert!(
21547 declarations.iter().any(|unit| unit.fq_name() == expected),
21548 "expected {expected}, got {declarations:?}"
21549 );
21550 }
21551 }
21552
21553 #[test]
21556 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
21557 let source = "class outer { class inner { int v; }; };\n";
21558 let c_parsed = parse_cpp_declarations(source, "k.c");
21559 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
21560 let c_declarations = c_parsed.declarations();
21561 let cpp_declarations = cpp_parsed.declarations();
21562 assert!(
21563 c_declarations
21564 .iter()
21565 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21566 "{c_declarations:?}"
21567 );
21568 assert_eq!(
21569 c_declarations
21570 .iter()
21571 .map(|unit| unit.fq_name())
21572 .collect::<std::collections::BTreeSet<_>>(),
21573 cpp_declarations
21574 .iter()
21575 .map(|unit| unit.fq_name())
21576 .collect::<std::collections::BTreeSet<_>>()
21577 );
21578 }
21579
21580 fn namespace_forward_scan_agreement(source: &str) -> usize {
21594 let mut parser = tree_sitter::Parser::new();
21595 parser
21596 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21597 .unwrap();
21598 let tree = parser.parse(source, None).unwrap();
21599 let root = tree.root_node();
21600 let ancestry = ParentIndex::new(root);
21601
21602 let mut nodes = Vec::new();
21603 let mut names = std::collections::BTreeSet::new();
21604 let mut cursor = root.walk();
21605 let mut stack = vec![root];
21606 while let Some(node) = stack.pop() {
21607 if matches!(
21608 node.kind(),
21609 "class_specifier" | "struct_specifier" | "union_specifier"
21610 ) && let Some(name) = class_like_name(node, source, &ancestry)
21611 {
21612 names.insert(name);
21613 }
21614 nodes.push(node);
21615 stack.extend(node.named_children(&mut cursor));
21616 }
21617 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
21618 assert!(!names.is_empty(), "fixture declares no class-like name");
21619
21620 let mut answered = 0usize;
21621 for reversed in [false, true] {
21622 let mut scan = CppNamespaceForwardScan::default();
21623 let ordered: Vec<_> = if reversed {
21624 nodes.iter().rev().copied().collect()
21625 } else {
21626 nodes.clone()
21627 };
21628 answered = 0;
21629 for node in ordered {
21630 for name in &names {
21631 scan.advance_to(root, node.start_byte(), source, &ancestry);
21632 let carried = scan.unique_earlier_forward(name, node);
21633 assert_eq!(
21634 carried,
21635 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
21636 "carried-forward scan and prefix scan disagree about {name} at \
21637 {} node starting at byte {} (reversed order: {reversed})",
21638 node.kind(),
21639 node.start_byte()
21640 );
21641 answered += usize::from(carried.is_some());
21642 }
21643 }
21644 }
21645 answered
21646 }
21647
21648 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
21655namespace ns {
21656class Widget;
21657class Gadget;
21658int x = ;
21659}
21660class API Widget {
21661public:
21662 void first();
21663};
21664class API Gadget {
21665public:
21666 void second();
21667};
21668"#;
21669
21670 #[test]
21671 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
21672 assert!(
21673 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
21674 "the fixture must actually reach the namespace-borrow path"
21675 );
21676
21677 for source in [
21682 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
21683 r#"#define API
21684namespace ns {
21685class Widget;
21686class Widget;
21687int x = ;
21688}
21689class API Widget {
21690public:
21691 void method();
21692};
21693"#,
21694 r#"#define API
21695namespace ns {
21696void host() {
21697 class Widget;
21698}
21699int x = ;
21700}
21701class API Widget {
21702public:
21703 void method();
21704};
21705"#,
21706 ] {
21707 assert_eq!(
21708 namespace_forward_scan_agreement(source),
21709 0,
21710 "no borrow is justified here: {source}"
21711 );
21712 }
21713 }
21714
21715 #[test]
21719 fn carried_forward_namespace_scan_folds_each_node_once() {
21720 let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
21721 let mut parser = tree_sitter::Parser::new();
21722 parser
21723 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21724 .unwrap();
21725 let tree = parser.parse(source, None).unwrap();
21726 let root = tree.root_node();
21727 let ancestry = ParentIndex::new(root);
21728
21729 let mut incremental = CppNamespaceForwardScan::default();
21730 for cutoff in 0..=source.len() {
21731 incremental.advance_to(root, cutoff, source, &ancestry);
21732 }
21733 let mut whole = CppNamespaceForwardScan::default();
21734 whole.advance_to(root, source.len(), source, &ancestry);
21735
21736 let mut incremental_shape: Vec<_> = incremental
21737 .forwards
21738 .iter()
21739 .map(|(name, forwards)| {
21740 (
21741 name.clone(),
21742 forwards
21743 .iter()
21744 .map(|forward| (forward.start_byte, forward.package_name.clone()))
21745 .collect::<Vec<_>>(),
21746 )
21747 })
21748 .collect();
21749 let mut whole_shape: Vec<_> = whole
21750 .forwards
21751 .iter()
21752 .map(|(name, forwards)| {
21753 (
21754 name.clone(),
21755 forwards
21756 .iter()
21757 .map(|forward| (forward.start_byte, forward.package_name.clone()))
21758 .collect::<Vec<_>>(),
21759 )
21760 })
21761 .collect();
21762 incremental_shape.sort();
21763 whole_shape.sort();
21764 for (_, forwards) in &mut incremental_shape {
21765 forwards.sort();
21766 }
21767 for (_, forwards) in &mut whole_shape {
21768 forwards.sort();
21769 }
21770
21771 assert!(!whole_shape.is_empty(), "fixture folds no forward");
21772 assert_eq!(
21773 incremental_shape, whole_shape,
21774 "one byte at a time must fold exactly what one whole pass folds"
21775 );
21776 }
21777
21778 fn fragmented_class_reparse_agreement(body: &str) {
21788 for prefix in [
21789 String::new(),
21790 "// leading comment\n".to_string(),
21791 "class Widget : public Base { ".to_string(),
21796 "namespace filler {\n".to_string()
21797 + &"struct Filler { int member; };\n".repeat(200)
21798 + "}\n",
21799 "namespace filler {\n".to_string()
21800 + &"struct Filler { int member; };\n".repeat(200)
21801 + "}\nclass Widget : public Base { ",
21802 ] {
21803 let source = format!("{prefix}{body}");
21804 let start = prefix.len();
21805 let end = source.len();
21806 let region = cpp_reparse_fragmented_class_body(&source, start, end)
21807 .expect("the region reparse must produce a tree");
21808 let padded = cpp_reparse_padded_class_body(&source, start, end)
21809 .expect("the padded reparse must produce a tree");
21810 assert_eq!(
21811 cpp_tree_shape(®ion),
21812 cpp_tree_shape(&padded),
21813 "region and padded reparse disagree at offset {start} of {end} bytes"
21814 );
21815 assert_eq!(
21816 region.root_node().start_byte(),
21817 start,
21818 "the reparsed region keeps its original offsets"
21819 );
21820 }
21821 }
21822
21823 #[test]
21824 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
21825 fragmented_class_reparse_agreement(
21829 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
21830 );
21831 fragmented_class_reparse_agreement(
21832 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
21833 );
21834 fragmented_class_reparse_agreement(
21837 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
21838 );
21839 fragmented_class_reparse_agreement(
21840 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
21841 );
21842 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
21845 }
21846
21847 fn many_enums_and_mixed_declarations() -> String {
21851 let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
21852 for index in 0..40 {
21853 let _ = write!(
21854 source,
21855 "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
21856 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
21857 class Color{index}Like {{ public: int member{index}; }};\n"
21858 );
21859 }
21860 source.push_str("namespace outer {\n");
21861 for index in 0..20 {
21862 let _ = write!(
21863 source,
21864 "enum Shade{index} {{ DARK{index} }};\n\
21865 struct Shade{index}Holder {{ int field{index}; }};\n"
21866 );
21867 }
21868 source.push_str("}\n");
21869 source
21870 }
21871
21872 fn field_owner_index_agreement(source: &str, name: &str) -> usize {
21888 let parsed = parse_cpp_declarations(source, name);
21889 let file = ProjectFile::new(std::env::temp_dir(), name);
21890 let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
21891
21892 let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
21893 declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
21894
21895 let foreign: Vec<CodeUnit> = declarations
21896 .iter()
21897 .filter(|unit| unit.kind() == CodeUnitType::Field)
21898 .map(|unit| {
21899 CodeUnit::new_fq(
21900 elsewhere.clone(),
21901 unit.kind(),
21902 unit.package_name().to_string(),
21903 unit.short_name().to_string(),
21904 unit.fq().clone(),
21905 )
21906 })
21907 .collect();
21908
21909 let mut packages: Vec<String> = declarations
21915 .iter()
21916 .map(|unit| unit.package_name().to_string())
21917 .collect();
21918 packages.push(String::new());
21919 packages.sort();
21920 packages.dedup();
21921 let deeper: Vec<CodeUnit> = packages
21922 .iter()
21923 .map(|package_name| {
21924 CodeUnit::new_fq(
21925 file.clone(),
21926 CodeUnitType::Field,
21927 package_name.clone(),
21928 "SynthOwner.middle.leaf".to_string(),
21929 cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
21930 )
21931 })
21932 .collect();
21933
21934 let mut questions: Vec<(String, String)> = Vec::new();
21938 for unit in declarations.iter().chain(deeper.iter()) {
21939 let package_name = unit.package_name().to_string();
21940 let short_name = unit.short_name();
21941 questions.push((package_name.clone(), short_name.to_string()));
21942 questions.push((package_name.clone(), String::new()));
21943 for (offset, _) in short_name.match_indices('.') {
21944 questions.push((package_name.clone(), short_name[..offset].to_string()));
21945 }
21946 }
21947 questions.sort();
21948 questions.dedup();
21949
21950 let mut index = CppFieldOwnerIndex::default();
21951 let mut recorded: Vec<&CodeUnit> = Vec::new();
21952 let mut answered = 0usize;
21953 for unit in foreign
21954 .iter()
21955 .chain(declarations.iter())
21956 .chain(deeper.iter())
21957 {
21958 index.record(unit, &file);
21959 recorded.push(unit);
21960 for (package_name, owner_short_name) in &questions {
21961 let carried = index.owns_fields(package_name, owner_short_name);
21962 assert_eq!(
21963 carried,
21964 cpp_declarations_hold_owned_fields(
21965 recorded.iter().copied(),
21966 &file,
21967 package_name,
21968 owner_short_name
21969 ),
21970 "the carried field index and the declaration scan disagree about \
21971 {package_name:?}/{owner_short_name:?} after recording {}",
21972 unit.fq_name()
21973 );
21974 answered += usize::from(carried);
21975 }
21976 }
21977
21978 let rebuilt = CppFieldOwnerIndex::of(
21981 foreign
21982 .iter()
21983 .chain(declarations.iter())
21984 .chain(deeper.iter()),
21985 &file,
21986 );
21987 for (package_name, owner_short_name) in &questions {
21988 assert_eq!(
21989 rebuilt.owns_fields(package_name, owner_short_name),
21990 index.owns_fields(package_name, owner_short_name),
21991 "a rebuilt index must answer what the incremental one answers for \
21992 {package_name:?}/{owner_short_name:?}"
21993 );
21994 }
21995 answered
21996 }
21997
21998 #[test]
22007 fn a_replacement_that_removes_children_drops_the_field_index() {
22008 let source =
22009 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
22010 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
22011 let mut names: Vec<_> = parsed
22012 .declarations()
22013 .iter()
22014 .map(|unit| unit.fq_name())
22015 .collect();
22016 names.sort();
22017 assert_eq!(
22018 names,
22019 vec![
22020 "Color".to_string(),
22021 "First".to_string(),
22022 "First.A".to_string()
22023 ],
22024 "the replaced Color owns no field any more"
22025 );
22026 }
22027
22028 #[test]
22037 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
22038 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
22039 BEGIN_NS\n\
22040 namespace demo { struct Widget { void doWork(); }; }\n\
22041 END_NS\n";
22042 let parsed = parse_cpp_declarations(source, "restored.cpp");
22043 let recovered: Vec<String> = parsed
22044 .materialization_records
22045 .iter()
22046 .filter_map(|record| match record {
22047 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
22048 _ => None,
22049 })
22050 .collect();
22051 assert!(
22052 recovered.is_empty(),
22053 "the region declares nothing the file did not already declare: {recovered:?}"
22054 );
22055 let mut names: Vec<String> = parsed
22056 .declarations()
22057 .iter()
22058 .map(|unit| unit.fq_name())
22059 .collect();
22060 names.sort();
22061 assert_eq!(
22062 names,
22063 vec![
22064 "demo".to_string(),
22065 "demo.Widget".to_string(),
22066 "demo.Widget.doWork".to_string(),
22067 ]
22068 );
22069 }
22070
22071 #[test]
22076 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
22077 let mut source = String::new();
22078 for index in 0..4 {
22079 let _ = write!(
22080 source,
22081 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
22082 );
22083 }
22084 source.push_str("void outside() {}\n");
22085 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
22086
22087 let recovered: Vec<(String, (usize, usize))> = parsed
22088 .materialization_records
22089 .iter()
22090 .filter_map(|record| match record {
22091 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
22092 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
22093 }
22094 _ => None,
22095 })
22096 .collect();
22097
22098 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
22099 for index in 0..4 {
22100 let region = format!("namespace demo{index}");
22103 let region_start = source.find(®ion).expect("each region is in the source");
22104 let start = source[..region_start]
22105 .rfind("BEGIN_NS")
22106 .expect("each region opens with a sentinel")
22107 + "BEGIN_NS".len();
22108 let end = start
22109 + source[start..]
22110 .find("END_NS")
22111 .expect("each region closes with a sentinel")
22112 - 1;
22113 let window = (start, end);
22114 for name in [
22115 format!("demo{index}"),
22116 format!("demo{index}.Widget{index}"),
22117 format!("demo{index}.Widget{index}.doWork{index}"),
22118 ] {
22119 expected.push((name, window));
22120 }
22121 }
22122 assert_eq!(
22123 recovered, expected,
22124 "each recovery records its own minted declarations, in order"
22125 );
22126 assert!(
22127 parsed
22128 .declarations()
22129 .iter()
22130 .any(|unit| unit.fq_name() == "outside"),
22131 "the declaration outside every region stays parsed and unrecovered"
22132 );
22133 }
22134
22135 #[test]
22136 fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
22137 assert!(
22138 field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
22139 "the fixture must actually own fields"
22140 );
22141
22142 for (source, name) in [
22148 ("struct S { enum E { V }; };\n", "nested.hpp"),
22149 (
22150 "enum Color { RED };\nstruct Color { int RED; };\n",
22151 "class-like.c",
22152 ),
22153 ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
22154 (
22155 "enum E { V };\nnamespace ns { enum E { V }; }\n",
22156 "repeated.hpp",
22157 ),
22158 ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
22159 ] {
22160 field_owner_index_agreement(source, name);
22161 }
22162 }
22163
22164 fn repeated_class_blocks(blocks: usize) -> String {
22169 let mut source = String::from("namespace demo {\n");
22170 for index in 0..blocks {
22171 let _ = write!(
22172 source,
22173 "\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"
22174 );
22175 }
22176 source.push_str("\n}\n");
22177 source
22178 }
22179
22180 #[test]
22188 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
22189 let mut answers = Vec::new();
22190 let mut visits = Vec::new();
22191 let mut node_counts = Vec::new();
22192 for blocks in [200usize, 400] {
22193 let source = repeated_class_blocks(blocks);
22194 let mut parser = tree_sitter::Parser::new();
22195 parser
22196 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22197 .unwrap();
22198 let tree = parser.parse(&source, None).unwrap();
22199 let start_byte = source.find("class Widget0 ").expect("first class");
22200 let end_byte = start_byte
22201 + source[start_byte..]
22202 .find("};")
22203 .expect("first class terminator")
22204 + "};".len();
22205 let range = Range {
22206 start_byte,
22207 end_byte,
22208 start_line: 0,
22209 end_line: 0,
22210 };
22211 reset_recovered_class_body_node_visits_for_test();
22212 let recovered_export_classes =
22213 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
22214 answers.push(recovered_class_body_at(
22215 &recovered_export_classes,
22216 tree.root_node(),
22217 &source,
22218 "Widget0",
22219 &range,
22220 ));
22221 visits.push(recovered_class_body_node_visits_for_test());
22222 let mut nodes = 0usize;
22223 let mut stack = vec![tree.root_node()];
22224 while let Some(node) = stack.pop() {
22225 nodes += 1;
22226 let mut cursor = node.walk();
22227 stack.extend(node.named_children(&mut cursor));
22228 }
22229 node_counts.push(nodes);
22230 }
22231
22232 assert_eq!(
22233 answers,
22234 vec![None, None],
22235 "no recovered shape claims a plain class"
22236 );
22237 assert_eq!(
22238 visits[0], visits[1],
22239 "the walk must follow the range's own path, so doubling the unrelated \
22240 classes must not change the node count: {visits:?} over trees of \
22241 {node_counts:?} nodes"
22242 );
22243 assert!(
22244 visits[1] * 20 < node_counts[1],
22245 "the walk must stay far below one pass over the tree: {visits:?} over \
22246 trees of {node_counts:?} nodes"
22247 );
22248 }
22249
22250 #[test]
22251 fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
22252 let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
22253 let mut parser = tree_sitter::Parser::new();
22254 parser
22255 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22256 .expect("C++ grammar");
22257 let tree = parser.parse(source, None).expect("fixture tree");
22258 let mut stack = vec![tree.root_node()];
22259 let mut recovered = None;
22260 while let Some(node) = stack.pop() {
22261 if node.kind() == "field_declaration"
22262 && let Some(field) = recovered_function_like_field_declarator(node, source)
22263 {
22264 recovered = Some((node, field.name));
22265 break;
22266 }
22267 let mut cursor = node.walk();
22268 stack.extend(node.named_children(&mut cursor));
22269 }
22270 let (declaration, name) = recovered.unwrap_or_else(|| {
22271 panic!(
22272 "pointer-wrapped macro field was not recovered: {}",
22273 tree.root_node().to_sexp()
22274 )
22275 });
22276 assert_eq!(node_text(name, source), "handshake");
22277 let recovered =
22278 recovered_function_like_field_declarator(declaration, source).expect("recovered field");
22279 assert_eq!(recovered.pointer_depth(), 1);
22280 assert_eq!(
22281 render_cpp_field_signature(declaration, name, source),
22282 "struct handshake * handshake;"
22283 );
22284 }
22285
22286 #[test]
22287 fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
22288 let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
22289 let mut parser = tree_sitter::Parser::new();
22290 parser
22291 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22292 .expect("C++ grammar");
22293 let tree = parser.parse(source, None).expect("fixture tree");
22294 let mut stack = vec![tree.root_node()];
22295 let mut recovered = None;
22296 while let Some(node) = stack.pop() {
22297 if let Some(field) = recovered_pyobject_head_field(node, source) {
22298 recovered = Some((node, field));
22299 break;
22300 }
22301 let mut cursor = node.walk();
22302 stack.extend(node.named_children(&mut cursor));
22303 }
22304 let (declaration, recovered) = recovered.unwrap_or_else(|| {
22305 panic!(
22306 "pointer field after PyObject_HEAD was not recovered: {}",
22307 tree.root_node().to_sexp()
22308 )
22309 });
22310 assert_eq!(node_text(recovered.name, source), "image");
22311 assert_eq!(recovered.pointer_depth(), 1);
22312 assert_eq!(
22313 render_cpp_field_signature(declaration, recovered.declarator, source),
22314 "ImagingObject *image;"
22315 );
22316 }
22317}