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::materialization::{
24 GenerationKind, MaterializationRecord,
25};
26use brokk_bifrost_core::analyzer::tree_walk::{
27 NodeKindIds, ParentIndex, WalkControl, children_iter, named_children_iter,
28 push_children_reversed, push_named_children_reversed, walk_named_tree_preorder,
29};
30use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
31use brokk_bifrost_core::hash::{HashMap, HashSet};
32use regex::Regex;
33use tree_sitter::{Node, Parser, Tree};
34
35fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
37 segment_interner().intern(text, kind)
38}
39
40fn cpp_push_package(fq: &mut FqName, package_name: &str) {
48 for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
49 fq.push(cpp_segment(component, SegmentKind::Package));
50 }
51}
52
53const CPP_PACKAGE_SEPARATOR: &str = "::";
55
56fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
63 let mut first = true;
64 for component in chain.split('$').filter(|c| !c.is_empty()) {
68 let kind = if first {
69 SegmentKind::Type
70 } else {
71 SegmentKind::Nested
72 };
73 fq.push(cpp_segment(component, kind));
74 first = false;
75 }
76}
77
78fn cpp_namespace_fq(full_name: &str) -> FqName {
82 let mut fq = FqName::new();
83 cpp_push_package(&mut fq, full_name);
84 fq
85}
86
87fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
103 let mut components = Vec::new();
104 let mut stack = vec![node];
105 while let Some(current) = stack.pop() {
106 match current.kind() {
107 "namespace_identifier" | "identifier" => {
108 components.push(normalize_cpp_whitespace(node_text(current, source)));
109 }
110 "nested_namespace_specifier" => {
111 for index in (0..current.named_child_count()).rev() {
112 stack.push(
113 current
114 .named_child(index)
115 .expect("index below the node's own named child count"),
116 );
117 }
118 }
119 _ => return cpp_raw_namespace_name_components(node, source),
120 }
121 }
122 if components.iter().any(String::is_empty) {
123 return cpp_raw_namespace_name_components(node, source);
124 }
125 components
126}
127
128fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
142 let start = node
143 .child(0)
144 .filter(|child| !child.is_named() && child.kind() == "::")
145 .map_or(node.start_byte(), |marker| marker.end_byte());
146 let text = normalize_cpp_whitespace(
147 source
148 .get(start..node.end_byte())
149 .expect("namespace name node covers one source range"),
150 );
151 let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
152 if text.is_empty() {
153 return Vec::new();
154 }
155 vec![text]
156}
157
158fn cpp_lexical_namespace_name<'tree>(
164 node: Node<'tree>,
165 source: &str,
166 ancestry: &ParentIndex<'tree>,
167) -> Option<String> {
168 let mut components = Vec::new();
169 let mut ancestor = ancestry.parent(node);
170 while let Some(current) = ancestor {
171 if current.kind() == "namespace_definition" {
172 let name_node = current.child_by_field_name("name")?;
173 let name = normalize_cpp_whitespace(node_text(name_node, source));
174 if name.is_empty() {
175 return None;
176 }
177 components.push(name);
178 }
179 ancestor = ancestry.parent(current);
180 }
181 if components.is_empty() {
182 return None;
183 }
184 components.reverse();
185 Some(
189 normalize_joined(
190 &components.join(CPP_PACKAGE_SEPARATOR),
191 CPP_PACKAGE_SEPARATOR,
192 )
193 .into_owned(),
194 )
195}
196
197fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
203 if parent_short.is_empty() {
204 name.to_string()
205 } else {
206 format!("{parent_short}${name}")
207 }
208}
209
210fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
213 if parent_short.is_empty() {
214 name.to_string()
215 } else {
216 format!("{parent_short}.{name}")
217 }
218}
219
220fn cpp_leaf_fq(
227 package_name: &str,
228 parent: Option<&CodeUnit>,
229 name: &str,
230 kind_if_nested: SegmentKind,
231 kind_if_top: SegmentKind,
232) -> FqName {
233 if let Some(parent) = parent {
234 parent
235 .fq()
236 .clone()
237 .with_pushed(cpp_segment(name, kind_if_nested))
238 } else {
239 let mut fq = FqName::new();
240 cpp_push_package(&mut fq, package_name);
241 fq.push(cpp_segment(name, kind_if_top));
242 fq
243 }
244}
245
246pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
253 let mut fq = FqName::new();
254 cpp_push_package(&mut fq, package_name);
255 match short_name.rsplit_once('.') {
256 Some((owner_chain, member)) => {
257 cpp_push_type_chain(&mut fq, owner_chain);
258 fq.push(cpp_segment(member, SegmentKind::Member));
259 }
260 None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
261 }
262 fq
263}
264
265#[derive(Clone)]
266pub struct ScopeInfo {
267 package_name: String,
268 module: Option<CodeUnit>,
269 class_unit: Option<CodeUnit>,
270 template_signature: Option<String>,
271 template_metadata: Option<CppTemplateMetadata>,
272 declarations_are_fields: bool,
273 recovered_specialization_member_scope: bool,
274 visible_using_namespaces: Vec<String>,
286}
287
288struct CppContainer<'tree> {
289 node: Node<'tree>,
290 scope: ScopeInfo,
291}
292
293struct CppNodeWork<'tree> {
294 node: Node<'tree>,
295 scope: ScopeInfo,
296}
297
298struct CppSiblingsWork<'tree> {
305 children: std::vec::IntoIter<Node<'tree>>,
306 scope: ScopeInfo,
307}
308
309enum CppWork<'tree> {
310 Container(CppContainer<'tree>),
311 Node(CppNodeWork<'tree>),
312 Siblings(CppSiblingsWork<'tree>),
313}
314
315fn class_like_name<'tree>(
316 node: Node<'tree>,
317 source: &str,
318 ancestry: &ParentIndex<'tree>,
319) -> Option<String> {
320 let best = class_like_name_from_children(node, source);
321 if let Some(parent) = ancestry.parent(node)
322 && matches!(
323 parent.kind(),
324 "declaration" | "field_declaration" | "function_definition"
325 )
326 && cpp_body_node(node).is_none()
335 && node
336 .child_by_field_name("name")
337 .map(|name_node| {
338 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
339 })
340 .unwrap_or(false)
341 && let Some(recovered) = exported_class_name_from_node(parent, source)
342 && best.as_deref() != Some(recovered.as_str())
343 {
344 return Some(recovered);
345 }
346 best.or_else(|| {
347 node.child_by_field_name("name")
348 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
349 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
350 })
351}
352
353fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
354 let mut grammar_name = None;
355 if let Some(name_node) = node.child_by_field_name("name") {
356 let name = normalize_cpp_whitespace(node_text(name_node, source));
357 if name.is_empty() {
358 return None;
359 }
360 if !cpp_export_macro_token(&name) {
361 return Some(name);
362 }
363 grammar_name = Some(name);
364 }
365
366 let mut best = None;
367 let mut cursor = node.walk();
368 let mut stack = Vec::new();
369 for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
370 if matches!(
371 child.kind(),
372 "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
373 ) {
374 break;
375 }
376 stack.push(child);
377 }
378
379 while let Some(current) = stack.pop() {
380 if matches!(current.kind(), "type_identifier" | "identifier") {
381 let name = normalize_cpp_whitespace(node_text(current, source));
382 if !name.is_empty() && !cpp_export_macro_token(&name) {
383 best = Some(name);
384 }
385 continue;
386 }
387
388 push_named_children_reversed(current, &mut stack);
389 }
390 best.or(grammar_name)
391}
392
393pub fn cpp_export_macro_token(token: &str) -> bool {
394 token
395 .chars()
396 .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
397}
398
399struct RecoveredExportedClass<'tree> {
400 declaration_node: Node<'tree>,
401 name: String,
402 body: Option<Node<'tree>>,
403 raw_supertypes: Option<Vec<String>>,
404 uses_initializer_body: bool,
405 fragmented_body: Option<FragmentedExportBody>,
410}
411
412struct RecoveredFunctionLikeExportClassPair {
413 name: String,
414 range: Range,
415 raw_supertypes: Option<Vec<String>>,
416 fragmented_body: FragmentedExportBody,
417}
418
419struct RecoveredEmbeddedFunctionLikeExportClass {
420 name: String,
421 range: Range,
422 raw_supertypes: Vec<String>,
423 fragmented_body: FragmentedExportBody,
424}
425
426struct FragmentedExportBody {
432 reparse_start: usize,
433 reparse_end: usize,
434 class_range: Range,
435}
436
437fn recovered_fragmented_export_body(
438 body: Node<'_>,
439 class_range: Range,
440) -> Option<FragmentedExportBody> {
441 let open = body.child(0).filter(|child| child.kind() == "{")?;
442 let close = body
443 .child(body.child_count().saturating_sub(1))
444 .filter(|child| child.kind() == "}" && !child.is_missing());
445 Some(FragmentedExportBody {
446 reparse_start: open.end_byte(),
447 reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
452 class_range,
453 })
454}
455
456struct DisplacedFragmentNamespaceBoundary<'tree> {
457 class_close: Node<'tree>,
458 class_semicolon: Node<'tree>,
459 namespace_items: Vec<Node<'tree>>,
460}
461
462enum FragmentedExportMembers {
467 Complete(Tree),
468 ConditionalConstructor(Tree),
469}
470
471#[derive(Clone, Copy)]
472struct DisplacedMacroClassTail {
473 split_index: usize,
474 class_range: Range,
475}
476
477fn recover_exported_class_declaration<'tree>(
478 node: Node<'tree>,
479 source: &str,
480) -> Option<RecoveredExportedClass<'tree>> {
481 if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
482 return Some(recovered);
483 }
484
485 let class_node = first_class_like_child(node)?;
486 if let Some(name_node) = class_node.child_by_field_name("name") {
487 let class_name = normalize_cpp_whitespace(node_text(name_node, source));
488 if cpp_export_macro_token(&class_name) {
489 let mut cursor = node.walk();
493 if node
494 .children_by_field_name("declarator", &mut cursor)
495 .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
496 {
497 return None;
498 }
499 } else if has_direct_cpp_declarator(node) {
500 return None;
501 }
502 }
503 let name = exported_class_name_from_node(class_node, source)?;
504 Some(RecoveredExportedClass {
505 declaration_node: class_node,
506 name,
507 body: cpp_body_node(class_node),
508 raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
509 .then(|| extract_cpp_supertypes(class_node, source)),
510 uses_initializer_body: false,
511 fragmented_body: None,
512 })
513}
514
515fn recover_malformed_exported_base_class<'tree>(
516 node: Node<'tree>,
517 source: &str,
518) -> Option<RecoveredExportedClass<'tree>> {
519 if node.kind() != "declaration" {
520 return None;
521 }
522 let class_node = node.child_by_field_name("type")?;
523 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
524 return None;
525 }
526 let macro_name = class_node
527 .child_by_field_name("name")
528 .and_then(|name| direct_identifier_name(name, source))?;
529 if !cpp_export_macro_token(¯o_name) {
530 return None;
531 }
532
533 let mut named_cursor = node.walk();
534 let mut named = node.named_children(&mut named_cursor);
535 if named
536 .next()
537 .is_none_or(|child| !same_node(child, class_node))
538 {
539 return None;
540 }
541 let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
542 if displaced.kind() != "ERROR" {
543 return None;
544 }
545 let name = displaced_exported_class_name(displaced, source)?;
546
547 let remaining = named.collect::<Vec<_>>();
548 let init = *remaining.last()?;
549 if init.kind() != "init_declarator" {
550 return None;
551 }
552 let final_base = init
553 .child_by_field_name("declarator")
554 .and_then(|base| recovered_malformed_base_name(base, source))?;
555 let body = init.child_by_field_name("value")?;
556 if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
560 return None;
561 }
562
563 if remaining[..remaining.len() - 1]
564 .iter()
565 .any(|child| match child.kind() {
566 "qualified_identifier"
567 | "scoped_type_identifier"
568 | "type_identifier"
569 | "identifier" => false,
570 "ERROR" => !is_malformed_inheritance_access(*child, source),
571 _ => true,
572 })
573 {
574 return None;
575 }
576
577 let mut raw_supertypes = Vec::new();
578 for base in &remaining[..remaining.len() - 1] {
579 if base.kind() == "ERROR" {
580 continue;
581 }
582 raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
583 }
584 raw_supertypes.push(final_base);
585
586 Some(RecoveredExportedClass {
587 declaration_node: node,
588 name,
589 body: Some(body),
590 raw_supertypes: Some(raw_supertypes),
591 uses_initializer_body: true,
592 fragmented_body: fragmented_export_body_region(node, body, source),
593 })
594}
595
596fn fragmented_export_body_region(
612 node: Node<'_>,
613 body: Node<'_>,
614 source: &str,
615) -> Option<FragmentedExportBody> {
616 let reparse_start = body.start_byte() + 1;
617 let close = direct_close_brace(body)?;
618 if close.end_byte() > close.start_byte() {
619 return Some(FragmentedExportBody {
620 reparse_start,
621 reparse_end: close.start_byte(),
622 class_range: cpp_declaration_range(node),
623 });
624 }
625 let mut sibling = node.next_named_sibling();
628 let displaced_close = loop {
629 let Some(current) = sibling else {
630 break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
631 };
632 if cpp_is_stray_close_brace(current, source) {
633 break current;
634 }
635 sibling = current.next_named_sibling();
636 };
637 Some(FragmentedExportBody {
638 reparse_start,
639 reparse_end: displaced_close.start_byte(),
640 class_range: Range {
641 start_byte: node.start_byte(),
642 end_byte: displaced_close.end_byte(),
643 start_line: node.start_position().row + 1,
644 end_line: displaced_close.end_position().row + 1,
645 },
646 })
647}
648
649fn fragmented_export_function_body_region(
657 node: Node<'_>,
658 body: Node<'_>,
659 source: &str,
660 displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
661) -> Option<FragmentedExportBody> {
662 let reparse_start = body.start_byte().checked_add(1)?;
663 if let Some(boundary) = displaced_namespace {
664 return Some(FragmentedExportBody {
665 reparse_start,
666 reparse_end: boundary.class_close.start_byte(),
667 class_range: Range {
668 start_byte: node.start_byte(),
669 end_byte: boundary.class_semicolon.end_byte(),
670 start_line: node.start_position().row + 1,
671 end_line: boundary.class_semicolon.end_position().row + 1,
672 },
673 });
674 }
675 let siblings = cpp_following_named_siblings(node, source);
676 let boundary = fragmented_export_sibling_class_boundary(node, source);
677 let boundary_index = boundary.and_then(|boundary| {
678 siblings
679 .iter()
680 .position(|candidate| same_node(*candidate, boundary))
681 });
682 let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
683 let mut sibling_index = 0;
684 while let Some(current) = siblings.get(sibling_index).copied() {
697 if current.kind() == "comment" {
698 sibling_index += 1;
699 continue;
700 }
701 if is_trailing_attribute_macro_sibling(current) {
702 sibling_index += 1;
703 continue;
704 }
705 if cpp_is_stray_semicolon(current, source) {
706 return None;
707 }
708 break;
709 }
710 while let Some(current) = siblings.get(sibling_index).copied() {
711 let next = siblings.get(sibling_index + 1).copied();
712 if cpp_is_stray_close_brace(current, source)
713 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
714 {
715 let semicolon = next.expect("checked above");
716 return Some(FragmentedExportBody {
717 reparse_start,
718 reparse_end: current.start_byte(),
719 class_range: Range {
720 start_byte: node.start_byte(),
721 end_byte: semicolon.end_byte(),
722 start_line: node.start_position().row + 1,
723 end_line: semicolon.end_position().row + 1,
724 },
725 });
726 }
727 if current.start_byte() >= body.end_byte()
734 && let Some(close) = cpp_nested_stray_close_brace(current, source)
735 {
736 return Some(FragmentedExportBody {
737 reparse_start,
738 reparse_end: close.start_byte(),
739 class_range: Range {
740 start_byte: node.start_byte(),
741 end_byte: current.end_byte(),
742 start_line: node.start_position().row + 1,
743 end_line: current.end_position().row + 1,
744 },
745 });
746 }
747 sibling_index += 1;
748 }
749 boundary.map(|boundary| FragmentedExportBody {
750 reparse_start,
751 reparse_end: boundary.start_byte(),
752 class_range: Range {
753 start_byte: node.start_byte(),
754 end_byte: boundary.start_byte(),
755 start_line: node.start_position().row + 1,
756 end_line: boundary.start_position().row + 1,
757 },
758 })
759}
760
761fn fragmented_export_sibling_class_boundary<'tree>(
766 node: Node<'tree>,
767 source: &str,
768) -> Option<Node<'tree>> {
769 let node_parent = node.parent()?;
770 cpp_following_named_siblings(node, source)
771 .into_iter()
772 .find(|candidate| {
773 recover_exported_class_function_definition(*candidate, source).is_some()
774 && candidate
775 .parent()
776 .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
777 })
778}
779
780fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
785 if node.kind() != "expression_statement" {
786 return false;
787 }
788 let mut cursor = node.walk();
789 let mut children = node.named_children(&mut cursor);
790 children
791 .next()
792 .is_some_and(|child| child.kind() == "identifier")
793 && children.next().is_none()
794}
795
796fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
802 let mut stack = vec![node];
803 while let Some(current) = stack.pop() {
804 if cpp_is_stray_close_brace(current, source) {
805 return Some(current);
806 }
807 let mut cursor = current.walk();
808 stack.extend(current.named_children(&mut cursor));
809 }
810 None
811}
812
813fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
818 let mut siblings = Vec::new();
819 let mut anchor = node;
820 while let Some(parent) = anchor.parent() {
821 let at_translation_unit = parent.kind() == "translation_unit";
822 let mut sibling = anchor.next_named_sibling();
823 while let Some(current) = sibling {
824 if at_translation_unit
825 && (current.kind() == "namespace_definition"
826 || (current.kind() == "function_definition"
827 && first_class_like_child(current).is_some()))
828 {
829 return siblings;
830 }
831 siblings.push(current);
832 if cpp_is_stray_close_brace(current, source) {
833 if let Some(semicolon) = current
834 .next_named_sibling()
835 .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
836 {
837 siblings.push(semicolon);
838 }
839 return siblings;
840 }
841 if current.start_byte() >= node.end_byte()
842 && matches!(current.kind(), "ERROR" | "labeled_statement")
843 && cpp_nested_stray_close_brace(current, source).is_some()
844 {
845 return siblings;
846 }
847 sibling = current.next_named_sibling();
848 }
849 anchor = parent;
850 }
851 siblings
852}
853
854fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
855 if node.start_byte() >= class_end {
856 return false;
857 }
858 node.end_byte() <= class_end
859 || cpp_nested_stray_close_brace(node, source)
860 .is_some_and(|close| close.start_byte() == class_end)
861}
862
863struct FragmentedClassRecovery<'tree> {
867 declaration_node: Node<'tree>,
868 name: String,
869 raw_supertypes: Vec<String>,
870 body: FragmentedExportBody,
871}
872
873fn fragmented_class_body<'tree>(
885 node: Node<'tree>,
886 source: &str,
887) -> Option<FragmentedClassRecovery<'tree>> {
888 if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
889 return Some(recovered);
890 }
891 let supported_container = node.kind() == "ERROR"
892 || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
893 if !supported_container {
894 return None;
895 }
896 let mut cursor = node.walk();
897 let children = node.children(&mut cursor).collect::<Vec<_>>();
898 if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
899 return Some(recovered);
900 }
901 let keyword = children.first()?;
902 if !matches!(keyword.kind(), "class" | "struct" | "union") {
903 return None;
904 }
905 let name_node = children
906 .iter()
907 .copied()
908 .skip(1)
909 .find(|child| child.is_named())?;
910 if !matches!(name_node.kind(), "type_identifier" | "identifier") {
911 return None;
912 }
913 let name = normalize_cpp_whitespace(node_text(name_node, source));
914 if name.is_empty() || cpp_export_macro_token(&name) {
915 return None;
916 }
917 let open_index = children.iter().position(|child| child.kind() == "{")?;
918 Some(FragmentedClassRecovery {
919 declaration_node: node,
920 name,
921 raw_supertypes: extract_cpp_supertypes(node, source),
922 body: fragmented_displaced_class_body(node, &children, open_index, source)?,
923 })
924}
925
926fn fragmented_export_macro_class_body<'tree>(
937 node: Node<'tree>,
938 children: &[Node<'tree>],
939 source: &str,
940) -> Option<FragmentedClassRecovery<'tree>> {
941 let class_node = *children.first()?;
942 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
943 return None;
944 }
945 class_node
949 .child_by_field_name("name")
950 .and_then(|name| direct_identifier_name(name, source))?;
951 let invocation = *children.get(1)?;
952 if invocation.is_named() || invocation.kind() != "(" {
953 return None;
954 }
955 let open_index = children
956 .iter()
957 .position(|child| !child.is_named() && child.kind() == "{")?;
958 let open = children[open_index];
959 let name_node = recovered_export_head_name(node, open, source)?;
960 let name = normalize_cpp_whitespace(node_text(name_node, source));
961 if name.is_empty() || cpp_export_macro_token(&name) {
962 return None;
963 }
964 Some(FragmentedClassRecovery {
965 declaration_node: node,
966 name,
967 raw_supertypes: recovered_export_head_bases(
968 node,
969 name_node.end_byte(),
970 open.start_byte(),
971 source,
972 ),
973 body: fragmented_displaced_class_body(node, children, open_index, source)
974 .or_else(|| fragmented_container_close_class_body(node, open, source))?,
975 })
976}
977
978fn fragmented_displaced_class_body(
984 node: Node<'_>,
985 children: &[Node<'_>],
986 open_index: usize,
987 source: &str,
988) -> Option<FragmentedExportBody> {
989 let open = children[open_index];
990 let nested_class_opens = children[open_index + 1..]
991 .iter()
992 .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
993 .count();
994 let mut closes_remaining = 1 + nested_class_opens;
995 let mut sibling = node.next_named_sibling();
996 while let Some(candidate) = sibling {
997 let next = candidate.next_named_sibling();
998 if cpp_is_stray_close_brace(candidate, source) {
999 closes_remaining -= 1;
1000 if closes_remaining == 0 {
1001 let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
1002 if open.end_byte() >= candidate.start_byte() {
1003 return None;
1004 }
1005 return Some(FragmentedExportBody {
1006 reparse_start: open.end_byte(),
1007 reparse_end: candidate.start_byte(),
1008 class_range: Range {
1009 start_byte: node.start_byte(),
1010 end_byte: semicolon.end_byte(),
1011 start_line: node.start_position().row + 1,
1012 end_line: semicolon.end_position().row + 1,
1013 },
1014 });
1015 }
1016 }
1017 sibling = next;
1018 }
1019 None
1020}
1021
1022fn fragmented_container_close_class_body(
1030 node: Node<'_>,
1031 open: Node<'_>,
1032 source: &str,
1033) -> Option<FragmentedExportBody> {
1034 let parent = node.parent()?;
1035 if !matches!(
1036 parent.kind(),
1037 "declaration_list" | "field_declaration_list" | "compound_statement"
1038 ) {
1039 return None;
1040 }
1041 let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1042 if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1047 || open.end_byte() >= close.start_byte()
1048 {
1049 return None;
1050 }
1051 Some(FragmentedExportBody {
1052 reparse_start: open.end_byte(),
1053 reparse_end: close.start_byte(),
1054 class_range: Range {
1055 start_byte: node.start_byte(),
1056 end_byte: close.end_byte(),
1057 start_line: node.start_position().row + 1,
1058 end_line: close.end_position().row + 1,
1059 },
1060 })
1061}
1062
1063pub(crate) fn recovered_fragmented_class_has_body(
1064 node: Node<'_>,
1065 source: &str,
1066 expected_name: &str,
1067 expected_range: &Range,
1068) -> bool {
1069 fragmented_class_body(node, source).is_some_and(|recovered| {
1070 recovered.name == expected_name
1071 && recovered.body.class_range.start_byte == expected_range.start_byte
1072 && recovered.body.class_range.end_byte == expected_range.end_byte
1073 })
1074}
1075
1076fn fragmented_plain_class_declaration_body<'tree>(
1083 node: Node<'tree>,
1084 source: &str,
1085) -> Option<FragmentedClassRecovery<'tree>> {
1086 if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1087 return None;
1088 }
1089 let class_node = node.child_by_field_name("type")?;
1090 if !matches!(
1091 class_node.kind(),
1092 "class_specifier" | "struct_specifier" | "union_specifier"
1093 ) {
1094 return None;
1095 }
1096 let name_node = class_node.child_by_field_name("name")?;
1097 let name = normalize_cpp_whitespace(node_text(name_node, source));
1098 if name.is_empty() || cpp_export_macro_token(&name) {
1099 return None;
1100 }
1101 let body = cpp_body_node(class_node)?;
1102 if body.kind() != "field_declaration_list" {
1103 return None;
1104 }
1105 let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1106 if declarator.start_byte() < class_node.end_byte() {
1107 return None;
1108 }
1109 let mut cursor = node.walk();
1110 node.named_children(&mut cursor).any(|child| {
1111 if child.kind() != "ERROR"
1112 || child.start_byte() < class_node.end_byte()
1113 || child.end_byte() > declarator.start_byte()
1114 {
1115 return false;
1116 }
1117 let mut cursor = child.walk();
1118 let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1119 let Some((return_type, attributes)) = components.split_last() else {
1120 return false;
1121 };
1122 matches!(
1123 return_type.kind(),
1124 "identifier"
1125 | "type_identifier"
1126 | "primitive_type"
1127 | "decltype"
1128 | "placeholder_type_specifier"
1129 ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1130 && attributes.iter().all(|attribute| {
1131 matches!(attribute.kind(), "identifier" | "type_identifier")
1132 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1133 *attribute, source,
1134 )))
1135 })
1136 })
1137 } else {
1138 let mut cursor = node.walk();
1139 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1140 matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1141 if same_node(*candidate_class, class_node)
1142 && continuation.kind() == "identifier"
1143 && node_text(*continuation, source) == "else"
1144 && continuation_body.kind() == "compound_statement"
1145 && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1146 && continuation_body
1147 .child(continuation_body.child_count().saturating_sub(1))
1148 .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1149 };
1150 if !displaced_member {
1151 return None;
1152 }
1153 let open = body
1154 .children(&mut body.walk())
1155 .find(|child| child.kind() == "{")?;
1156 let siblings = cpp_following_named_siblings(node, source);
1157 let ordinary_boundary =
1158 siblings
1159 .iter()
1160 .copied()
1161 .enumerate()
1162 .find_map(|(close_index, close)| {
1163 cpp_is_stray_close_brace(close, source)
1164 .then(|| {
1165 siblings
1166 .get(close_index + 1)
1167 .copied()
1168 .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1169 .map(|semicolon| (close, semicolon))
1170 })
1171 .flatten()
1172 });
1173 let (close, semicolon) =
1174 if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1175 (boundary.class_close, boundary.class_semicolon)
1176 } else {
1177 ordinary_boundary?
1178 };
1179 if open.end_byte() >= close.start_byte() {
1180 return None;
1181 }
1182 Some(FragmentedClassRecovery {
1183 declaration_node: class_node,
1184 name,
1185 raw_supertypes: extract_cpp_supertypes(class_node, source),
1186 body: FragmentedExportBody {
1187 reparse_start: open.end_byte(),
1188 reparse_end: close.start_byte(),
1189 class_range: Range {
1190 start_byte: class_node.start_byte(),
1191 end_byte: semicolon.end_byte(),
1192 start_line: class_node.start_position().row + 1,
1193 end_line: semicolon.end_position().row + 1,
1194 },
1195 },
1196 })
1197}
1198
1199fn displaced_export_function_namespace_shape<'tree>(
1200 declaration: Node<'tree>,
1201 source: &str,
1202) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1203 let mut nested = Vec::new();
1204 for index in (0..declaration.named_child_count()).rev() {
1205 nested.push(declaration.named_child(index)?);
1206 }
1207 while let Some(current) = nested.pop() {
1208 if recover_exported_class_function_definition(current, source).is_some() {
1214 return None;
1215 }
1216 for index in (0..current.named_child_count()).rev() {
1217 nested.push(current.named_child(index)?);
1218 }
1219 }
1220 let mut same_envelope_sibling = declaration.next_named_sibling();
1221 while let Some(current) = same_envelope_sibling {
1222 if recover_exported_class_function_definition(current, source).is_some() {
1223 return None;
1224 }
1225 same_envelope_sibling = current.next_named_sibling();
1226 }
1227 let declaration_list = declaration.parent()?;
1228 if declaration_list.kind() != "declaration_list" {
1229 return None;
1230 }
1231 let namespace = declaration_list.parent()?;
1232 if namespace.kind() != "namespace_definition"
1233 || namespace.child_by_field_name("body") != Some(declaration_list)
1234 {
1235 return None;
1236 }
1237 let class_close = direct_close_brace(declaration_list)?;
1238 let trailing_semicolon = namespace.next_named_sibling()?;
1239 if trailing_semicolon.kind() != "expression_statement"
1240 || trailing_semicolon.named_child_count() != 0
1241 {
1242 return None;
1243 }
1244 let siblings = cpp_following_named_siblings(namespace, source);
1250 let trailing_index = siblings
1251 .iter()
1252 .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1253 if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1254 recover_exported_class_function_definition(*candidate, source).is_some()
1255 }) {
1256 return None;
1261 }
1262 let mut namespace_items = Vec::new();
1263 let mut nested_fragment_end = 0;
1264 for current in siblings.into_iter().skip(trailing_index + 1) {
1265 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1266 {
1267 return Some(DisplacedFragmentNamespaceBoundary {
1268 class_close,
1269 class_semicolon: trailing_semicolon,
1270 namespace_items,
1271 });
1272 }
1273 if current.start_byte() >= nested_fragment_end
1274 && let Some(recovered) = fragmented_class_body(current, source)
1275 {
1276 nested_fragment_end = recovered.body.class_range.end_byte;
1277 } else if current.start_byte() >= nested_fragment_end
1278 && recover_exported_class_function_definition(current, source).is_some()
1279 && let Some(body) = cpp_body_node(current)
1280 && let Some(fragmented) =
1281 fragmented_export_function_body_region(current, body, source, None)
1282 {
1283 nested_fragment_end = fragmented.class_range.end_byte;
1284 }
1285 namespace_items.push(current);
1286 }
1287 None
1288}
1289
1290fn displaced_fragment_namespace_boundary<'tree>(
1291 declaration: Node<'tree>,
1292 body: Node<'tree>,
1293 source: &str,
1294) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1295 let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1296 let reparse_start = body.start_byte() + 1;
1297 let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1298 cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1299}
1300
1301fn displaced_fragment_namespace_geometry<'tree>(
1307 declaration: Node<'tree>,
1308 source: &str,
1309) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1310 let envelope = declaration
1314 .parent()
1315 .filter(|parent| {
1316 parent.kind() == "template_declaration"
1317 && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1318 })
1319 .unwrap_or(declaration);
1320 let declaration_list = envelope.parent()?;
1321 if declaration_list.kind() != "declaration_list" {
1322 return None;
1323 }
1324 let namespace = declaration_list.parent()?;
1325 if namespace.kind() != "namespace_definition"
1326 || namespace.child_by_field_name("body") != Some(declaration_list)
1327 {
1328 return None;
1329 }
1330 let class_close = direct_close_brace(declaration_list)?;
1331 let trailing_semicolon = namespace.next_named_sibling()?;
1332 if trailing_semicolon.kind() != "expression_statement"
1333 || trailing_semicolon.named_child_count() != 0
1334 {
1335 return None;
1336 }
1337 let mut namespace_items = Vec::new();
1338 let mut sibling = trailing_semicolon.next_named_sibling();
1339 let mut nested_fragment_end = 0;
1340 loop {
1341 let current = sibling?;
1342 if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1343 {
1344 break;
1345 }
1346 if current.start_byte() >= nested_fragment_end
1347 && let Some(recovered) = fragmented_class_body(current, source)
1348 {
1349 nested_fragment_end = recovered.body.class_range.end_byte;
1350 }
1351 namespace_items.push(current);
1352 sibling = current.next_named_sibling();
1353 }
1354 Some(DisplacedFragmentNamespaceBoundary {
1355 class_close,
1356 class_semicolon: trailing_semicolon,
1357 namespace_items,
1358 })
1359}
1360
1361fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1363 (0..node.child_count())
1364 .filter_map(|index| node.child(index))
1365 .find(|child| !child.is_named() && child.kind() == "}")
1366}
1367
1368fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1371 node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1372}
1373
1374fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1385 let bytes = source.as_bytes();
1386 if bytes.get(open_byte) != Some(&b'{') {
1387 return None;
1388 }
1389 let mut depth = 0usize;
1390 let mut i = open_byte;
1391 while i < bytes.len() {
1392 match bytes[i] {
1393 b'{' => depth += 1,
1394 b'}' => {
1395 depth = depth.checked_sub(1)?;
1396 if depth == 0 {
1397 return Some(i);
1398 }
1399 }
1400 b'/' if bytes.get(i + 1) == Some(&b'/') => {
1401 while i < bytes.len() && bytes[i] != b'\n' {
1402 i += 1;
1403 }
1404 continue;
1405 }
1406 b'/' if bytes.get(i + 1) == Some(&b'*') => {
1407 i += 2;
1408 while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1409 i += 1;
1410 }
1411 i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1412 continue;
1413 }
1414 quote @ (b'"' | b'\'') => {
1415 if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1418 return None;
1419 }
1420 i += 1;
1421 while i < bytes.len() && bytes[i] != quote {
1422 i += if bytes[i] == b'\\' { 2 } else { 1 };
1423 }
1424 if i >= bytes.len() {
1425 return None;
1426 }
1427 }
1428 _ => {}
1429 }
1430 i += 1;
1431 }
1432 None
1433}
1434
1435fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1436 let mut name = None;
1437 let mut colon_count = 0;
1438 let mut access_count = 0;
1439 for index in 0..node.child_count() {
1440 let child = node.child(index)?;
1441 match child.kind() {
1442 "identifier" | "type_identifier" if child.is_named() => {
1443 if name.is_some() {
1444 return None;
1445 }
1446 let candidate = normalize_cpp_whitespace(node_text(child, source));
1447 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1448 return None;
1449 }
1450 name = Some(candidate);
1451 }
1452 "template_function" | "template_type" if child.is_named() => {
1453 if name.is_some() {
1454 return None;
1455 }
1456 let candidate = child
1457 .child_by_field_name("name")
1458 .and_then(|name| direct_identifier_name(name, source))?;
1459 if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1460 return None;
1461 }
1462 name = Some(candidate);
1463 }
1464 ":" if !child.is_named() => colon_count += 1,
1465 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1466 _ => return None,
1467 }
1468 }
1469 (colon_count == 1 && access_count == 1)
1470 .then_some(name)
1471 .flatten()
1472}
1473
1474fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1475 if node.kind() != "ERROR" || node.named_child_count() != 1 {
1476 return false;
1477 }
1478 node.named_child(0)
1479 .and_then(|child| direct_identifier_name(child, source))
1480 .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1481}
1482
1483fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1484 (0..node.child_count()).any(|index| {
1485 node.child(index)
1486 .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1487 })
1488}
1489
1490fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1491 match node.kind() {
1492 "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1493 recovered_base_atom(node, source)
1494 }
1495 "template_type" | "template_function" => node
1496 .child_by_field_name("name")
1497 .and_then(|name| recovered_malformed_base_name(name, source)),
1498 "ERROR" => None,
1499 "qualified_identifier" | "scoped_type_identifier" => {
1500 let suffix = node
1501 .child_by_field_name("name")
1502 .and_then(|name| recovered_malformed_base_name(name, source))?;
1503 let scope = node
1504 .child_by_field_name("scope")
1505 .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1506 let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1507 malformed_qualified_prefix(node, source)?
1508 } else {
1509 if malformed_qualified_prefix(node, source).is_some() {
1510 return None;
1511 }
1512 scope
1513 };
1514 Some(format!("{prefix}::{suffix}"))
1515 }
1516 _ => None,
1517 }
1518}
1519
1520fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1521 if !matches!(
1522 node.kind(),
1523 "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1524 ) {
1525 return None;
1526 }
1527 let name = normalize_cpp_whitespace(node_text(node, source));
1528 (!name.is_empty()).then_some(name)
1529}
1530
1531fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1532 let mut prefix = None;
1533 let mut cursor = node.walk();
1534 for error in node
1535 .named_children(&mut cursor)
1536 .filter(|child| child.kind() == "ERROR")
1537 {
1538 if prefix.is_some() {
1539 return None;
1540 }
1541 let mut error_cursor = error.walk();
1546 let atoms = error
1547 .named_children(&mut error_cursor)
1548 .map(|child| recovered_base_atom(child, source))
1549 .collect::<Option<Vec<_>>>()?;
1550 let [atom] = atoms
1551 .iter()
1552 .filter(|atom| atom.as_str() != "virtual")
1553 .collect::<Vec<_>>()[..]
1554 else {
1555 return None;
1556 };
1557 prefix = Some(atom.clone());
1558 }
1559 prefix
1560}
1561
1562struct StrandedRun<'tree> {
1566 declarations: Vec<MacroWrappedDeclaration<'tree>>,
1567 complete: bool,
1573}
1574
1575struct MacroWrappedDeclaration<'tree> {
1576 declarator: Node<'tree>,
1577 range: Range,
1578 is_static: bool,
1583}
1584
1585fn is_declaration_scope_position(node: Node<'_>) -> bool {
1599 declaration_scope_container(node).is_some()
1600}
1601
1602fn declaration_scope_container(node: Node<'_>) -> Option<Node<'_>> {
1607 let mut parent = node.parent()?;
1608 loop {
1609 match parent.kind() {
1610 "translation_unit" => return Some(parent),
1611 "declaration_list" => {
1612 return parent
1613 .parent()
1614 .is_some_and(|grandparent| {
1615 matches!(
1616 grandparent.kind(),
1617 "namespace_definition" | "linkage_specification"
1618 )
1619 })
1620 .then_some(parent);
1621 }
1622 "ERROR" => match parent.parent() {
1623 Some(grandparent) => parent = grandparent,
1624 None => return Some(parent),
1627 },
1628 _ => return None,
1629 }
1630 }
1631}
1632
1633fn is_declaration_scope_error(node: Node<'_>) -> bool {
1635 node.kind() == "ERROR" && is_declaration_scope_position(node)
1636}
1637
1638fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1643 matches!(
1644 node.kind(),
1645 "identifier"
1646 | "type_identifier"
1647 | "primitive_type"
1648 | "sized_type_specifier"
1649 | "struct_specifier"
1650 | "union_specifier"
1651 | "enum_specifier"
1652 | "type_qualifier"
1653 | "storage_class_specifier"
1654 | "explicit_function_specifier"
1655 | "virtual_function_specifier"
1656 | "qualified_identifier"
1657 | "template_type"
1658 | "dependent_type"
1659 | "placeholder_type_specifier"
1660 )
1661}
1662
1663fn is_macro_argument_error(node: Node<'_>) -> bool {
1669 if node.kind() != "ERROR" {
1670 return false;
1671 }
1672 let mut cursor = node.walk();
1673 node.named_children(&mut cursor).all(|child| {
1674 matches!(
1675 child.kind(),
1676 "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1677 )
1678 })
1679}
1680
1681fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1690 declarator
1691 .next_sibling()
1692 .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1693 .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1694}
1695
1696fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1718 let mut parts = Vec::new();
1719 let mut cursor = node.walk();
1720 for child in node.named_children(&mut cursor) {
1721 if child.kind() == "ERROR" {
1722 let mut error_cursor = child.walk();
1723 parts.extend(child.named_children(&mut error_cursor));
1724 } else {
1725 parts.push(child);
1726 }
1727 }
1728
1729 let mut declarations = Vec::new();
1730 let mut start = None;
1731 let mut is_static = false;
1732 let mut complete = true;
1733 for part in parts {
1734 if part.kind() == "comment" {
1735 continue;
1736 }
1737 if let Some(declarator) = extract_function_declarator(part) {
1738 let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1739 declarations.push(MacroWrappedDeclaration {
1740 declarator,
1741 range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
1742 is_static,
1743 });
1744 is_static = false;
1745 continue;
1746 }
1747 if !is_recovered_declaration_type_part(part) {
1748 complete = false;
1749 break;
1750 }
1751 is_static |= part.kind() == "storage_class_specifier"
1752 && normalize_cpp_whitespace(node_text(part, source)) == "static";
1753 start.get_or_insert(part.start_byte());
1754 }
1755 StrandedRun {
1756 declarations,
1757 complete: complete && start.is_none(),
1758 }
1759}
1760
1761fn macro_wrapped_declarations<'tree>(
1787 envelope: Node<'tree>,
1788 source: &str,
1789) -> Vec<MacroWrappedDeclaration<'tree>> {
1790 let mut declarations = Vec::new();
1791 if !is_declaration_scope_error(envelope) {
1792 return declarations;
1793 }
1794 let mut cursor = envelope.walk();
1795 let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
1796 let [macro_name, arguments @ ..] = children.as_slice() else {
1797 return declarations;
1798 };
1799 if macro_name.kind() != "identifier" {
1800 return declarations;
1801 }
1802 let mut wrapped_declaration_seen = false;
1803 for argument in arguments {
1804 match argument.kind() {
1805 "comment" => {}
1806 "parameter_declaration" => {
1807 let recovered = stranded_declaration_run(*argument, source).declarations;
1808 if recovered.is_empty() {
1809 break;
1810 }
1811 wrapped_declaration_seen = true;
1812 declarations.extend(recovered);
1813 }
1814 "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
1819 _ => break,
1820 }
1821 }
1822 declarations
1823}
1824
1825struct CollapsedMacroDeclarationRun {
1828 invocation_end: usize,
1831 region_end: usize,
1836}
1837
1838struct MacroInvocationTokens<'tree> {
1850 stack: Vec<Node<'tree>>,
1851 next_sibling: Option<Node<'tree>>,
1852}
1853
1854impl<'tree> MacroInvocationTokens<'tree> {
1855 fn new(node: Node<'tree>) -> Self {
1856 Self {
1857 stack: vec![node],
1858 next_sibling: node.next_sibling(),
1859 }
1860 }
1861}
1862
1863impl<'tree> Iterator for MacroInvocationTokens<'tree> {
1864 type Item = Node<'tree>;
1865
1866 fn next(&mut self) -> Option<Node<'tree>> {
1867 loop {
1868 let Some(node) = self.stack.pop() else {
1869 let sibling = self.next_sibling?;
1870 self.next_sibling = sibling.next_sibling();
1871 self.stack.push(sibling);
1872 continue;
1873 };
1874 if node.child_count() == 0 {
1875 if node.kind() == "comment" || node.is_missing() {
1876 continue;
1877 }
1878 return Some(node);
1879 }
1880 let mut cursor = node.walk();
1881 let children = node.children(&mut cursor).collect::<Vec<_>>();
1882 self.stack.extend(children.into_iter().rev());
1883 }
1884 }
1885}
1886
1887fn collapsed_macro_declaration_run(
1934 node: Node<'_>,
1935 source: &str,
1936) -> Option<CollapsedMacroDeclarationRun> {
1937 let container_end = declaration_scope_container(node)?.end_byte();
1938 let mut tokens = MacroInvocationTokens::new(node);
1939 let name = tokens.next()?;
1940 if !matches!(name.kind(), "identifier" | "type_identifier")
1941 || !cpp_export_macro_token(node_text(name, source))
1942 {
1943 return None;
1944 }
1945 if tokens.next()?.kind() != "(" {
1946 return None;
1947 }
1948 let mut depth = 1usize;
1949 let invocation_end = loop {
1950 let token = tokens.next()?;
1951 match token.kind() {
1952 "(" => depth += 1,
1953 ";" => return None,
1954 ")" => {
1955 depth -= 1;
1956 if depth == 0 {
1957 let semicolon = tokens.next()?;
1958 if semicolon.kind() != ";" {
1959 return None;
1960 }
1961 break semicolon.end_byte();
1962 }
1963 }
1964 _ => {}
1965 }
1966 };
1967 if invocation_end >= container_end {
1973 return None;
1974 }
1975 let region_end = if node.end_byte() > invocation_end {
1984 container_end
1985 } else {
1986 invocation_end
1987 };
1988 Some(CollapsedMacroDeclarationRun {
1989 invocation_end,
1990 region_end,
1991 })
1992}
1993
1994fn string_attribute_macro_member_declarators<'tree>(
2015 field: Node<'tree>,
2016 source: &str,
2017) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2018 if field.kind() != "field_declaration"
2019 || field
2020 .child_by_field_name("type")
2021 .is_none_or(|type_node| type_node.kind() != "type_identifier")
2022 {
2023 return None;
2024 }
2025 let declarator = field.child_by_field_name("declarator")?;
2026 if declarator.kind() != "parenthesized_declarator" {
2027 return None;
2028 }
2029 let opening = declarator.named_child(0)?;
2030 if opening.kind() != "ERROR"
2031 || opening
2032 .named_child(0)
2033 .is_none_or(|word| word.kind() != "identifier")
2034 {
2035 return None;
2036 }
2037 let declarations = stranded_declaration_run(declarator, source).declarations;
2038 (!declarations.is_empty()).then_some(declarations)
2039}
2040
2041fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2048 let Some(call) = (node.kind() == "expression_statement")
2049 .then(|| node.named_child(0))
2050 .flatten()
2051 .filter(|child| child.kind() == "call_expression")
2052 else {
2053 return false;
2054 };
2055 call.child_by_field_name("function")
2056 .is_some_and(|function| function.kind() == "identifier")
2057 && call
2058 .child_by_field_name("arguments")
2059 .is_some_and(|arguments| {
2060 let mut cursor = arguments.walk();
2061 arguments.named_child_count() > 0
2062 && arguments
2063 .named_children(&mut cursor)
2064 .all(|argument| argument.kind() == "string_literal")
2065 })
2066}
2067
2068fn cpp_access_label_constructor_call_start(
2081 node: Node<'_>,
2082 class_name: &str,
2083 source: &str,
2084) -> Option<usize> {
2085 if node.kind() != "labeled_statement" {
2086 return None;
2087 }
2088 let label = node.named_child(0)?;
2089 if label.kind() != "statement_identifier"
2090 || !matches!(
2091 node_text(label, source).trim(),
2092 "public" | "private" | "protected"
2093 )
2094 {
2095 return None;
2096 }
2097 let mut starts = Vec::new();
2098 let mut stack = vec![node];
2099 while let Some(current) = stack.pop() {
2100 if current.kind() == "call_expression"
2101 && current
2102 .child_by_field_name("function")
2103 .is_some_and(|function| {
2104 function.kind() == "identifier"
2105 && node_text(function, source).trim() == class_name
2106 })
2107 {
2108 starts.push(current.start_byte());
2109 }
2110 let mut cursor = current.walk();
2111 stack.extend(current.named_children(&mut cursor));
2112 }
2113 let [start] = starts.as_slice() else {
2114 return None;
2115 };
2116 Some(*start)
2117}
2118
2119fn cpp_declarator_function_definition<'tree>(
2123 declarator: Node<'tree>,
2124 ancestry: &ParentIndex<'tree>,
2125) -> Option<Node<'tree>> {
2126 let mut current = declarator;
2127 while let Some(parent) = ancestry.parent(current) {
2128 match parent.kind() {
2129 "function_definition" if parent.child_by_field_name("body").is_some() => {
2130 return Some(parent);
2131 }
2132 "pointer_declarator"
2133 | "reference_declarator"
2134 | "parenthesized_declarator"
2135 | "array_declarator" => current = parent,
2136 _ => return None,
2137 }
2138 }
2139 None
2140}
2141
2142fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2146 let mut current = node;
2147 while let Some(parent) = ancestry.parent(current) {
2148 if parent.kind() == "namespace_definition"
2149 && parent.child_by_field_name("body") == Some(current)
2150 {
2151 return true;
2152 }
2153 current = parent;
2154 }
2155 false
2156}
2157
2158pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2172 let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2173 let root = tree.root_node();
2174 let mut cursor = root.walk();
2175 let items = root
2176 .named_children(&mut cursor)
2177 .filter(|child| child.kind() != "comment")
2178 .collect::<Vec<_>>();
2179 let [item] = items.as_slice() else {
2180 return None;
2181 };
2182 if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2183 return None;
2184 }
2185 match item.kind() {
2186 "function_definition" => Some(item.child_by_field_name("body").is_some()),
2187 "declaration" | "field_declaration" => Some(false),
2188 _ => None,
2189 }
2190}
2191
2192pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2204 !macro_wrapped_declarations(node, source).is_empty()
2205 || collapsed_macro_declaration_run(node, source).is_some()
2206}
2207
2208fn recover_exported_class_function_definition<'tree>(
2209 node: Node<'tree>,
2210 source: &str,
2211) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
2212 if node.kind() != "function_definition" {
2213 return None;
2214 }
2215 if let Some(prefix) = node.prev_named_sibling()
2216 && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2217 && recovered.range.end_byte == node.end_byte()
2218 {
2219 return Some((node, recovered.name, recovered.raw_supertypes));
2220 }
2221 let type_node = node.child_by_field_name("type")?;
2222 let declarator = node.child_by_field_name("declarator")?;
2223
2224 if matches!(
2225 type_node.kind(),
2226 "class_specifier" | "struct_specifier" | "union_specifier"
2227 ) {
2228 let type_name = type_node
2229 .child_by_field_name("name")
2230 .and_then(|name| direct_identifier_name(name, source));
2231 let exported_macro_type = type_name
2232 .as_ref()
2233 .is_some_and(|name| cpp_export_macro_token(name));
2234 if exported_macro_type {
2235 let mut cursor = node.walk();
2236 let errors_before_declarator = node
2237 .named_children(&mut cursor)
2238 .filter(|child| {
2239 child.kind() == "ERROR"
2240 && child.start_byte() >= type_node.end_byte()
2241 && child.end_byte() <= declarator.start_byte()
2242 })
2243 .collect::<Vec<_>>();
2244 if let Some(name) = errors_before_declarator
2245 .iter()
2246 .find_map(|error| displaced_exported_class_name(*error, source))
2247 {
2248 let raw_supertypes = errors_before_declarator
2249 .iter()
2250 .any(|error| malformed_inheritance_syntax(*error))
2251 .then(|| recovered_malformed_base_name(declarator, source))
2252 .flatten()
2253 .map(|base| vec![base]);
2254 return Some((node, name, raw_supertypes));
2255 }
2256 if errors_before_declarator
2257 .iter()
2258 .any(|error| malformed_inheritance_syntax(*error))
2259 {
2260 return None;
2261 }
2262 }
2263 if !exported_macro_type
2264 && let Some(name) = type_name
2265 && !cpp_export_macro_token(&name)
2266 && let Some(base) =
2267 recovered_postfix_export_macro_base(node, type_node, declarator, source)
2268 {
2269 return Some((node, name, Some(vec![base])));
2270 }
2271 if let Some(name) = direct_identifier_name(declarator, source)
2272 && exported_macro_type
2273 && !cpp_export_macro_token(&name)
2274 {
2275 let raw_supertypes = exported_macro_type
2276 .then(|| recovered_single_base_after_declarator(node, declarator, source))
2277 .flatten()
2278 .map(|base| vec![base]);
2279 return Some((node, name, raw_supertypes));
2280 }
2281 if declarator.kind() == "parenthesized_declarator"
2282 && type_node
2283 .child_by_field_name("name")
2284 .and_then(|name| direct_identifier_name(name, source))
2285 .is_some_and(|name| cpp_export_macro_token(&name))
2286 {
2287 if let Some((name, base)) =
2288 recovered_function_like_export_class_owner(declarator, source)
2289 {
2290 return Some((node, name, Some(vec![base])));
2291 }
2292 let body_start = node
2293 .child_by_field_name("body")
2294 .map(|body| body.start_byte())
2295 .unwrap_or(node.end_byte());
2296 let mut cursor = node.walk();
2297 if let Some(name) = node
2298 .named_children(&mut cursor)
2299 .filter(|child| {
2300 child.kind() == "ERROR"
2301 && child.start_byte() >= declarator.end_byte()
2302 && child.end_byte() <= body_start
2303 })
2304 .find_map(|error| declarator_name_from_node(error, source))
2305 {
2306 return Some((node, name, None));
2307 }
2308 }
2309 }
2310
2311 let declarator_text = direct_identifier_name(declarator, source)?;
2312 if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2313 return None;
2314 }
2315 class_identifier_before_body(node, source).map(|name| (node, name, None))
2316}
2317
2318fn recovered_function_like_export_class_owner(
2319 declarator: Node<'_>,
2320 source: &str,
2321) -> Option<(String, String)> {
2322 if declarator.kind() != "parenthesized_declarator" {
2323 return None;
2324 }
2325 let mut cursor = declarator.walk();
2326 let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2327 let [prefix, base] = children.as_slice() else {
2328 return None;
2329 };
2330 if prefix.kind() != "ERROR"
2331 || !matches!(
2332 base.kind(),
2333 "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2334 )
2335 {
2336 return None;
2337 }
2338 let mut identifiers = Vec::new();
2339 let mut prefix_cursor = prefix.walk();
2340 for child in prefix.named_children(&mut prefix_cursor) {
2341 match child.kind() {
2342 "number_literal" | "string_literal" | "char_literal" => {}
2343 "identifier" | "type_identifier" => {
2344 identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2345 }
2346 _ => return None,
2347 }
2348 }
2349 let name = match identifiers.as_slice() {
2350 [name] => name.clone(),
2351 [name, final_token] if final_token == "final" => name.clone(),
2352 _ => return None,
2353 };
2354 if name.is_empty() || cpp_export_macro_token(&name) {
2355 return None;
2356 }
2357 let base = recovered_malformed_base_name(*base, source)?;
2358 Some((name, base))
2359}
2360
2361fn recovered_export_head_bases(
2375 node: Node<'_>,
2376 after: usize,
2377 before: usize,
2378 source: &str,
2379) -> Vec<String> {
2380 let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2381 let mut bases = Vec::new();
2382 let mut cursor = node.walk();
2383 for child in node.named_children(&mut cursor) {
2384 if child.kind() == "ERROR" {
2385 let mut error_cursor = child.walk();
2386 bases.extend(
2387 child
2388 .named_children(&mut error_cursor)
2389 .filter(within)
2390 .filter_map(|part| recovered_malformed_base_name(part, source)),
2391 );
2392 } else if within(&child)
2393 && let Some(base) = recovered_malformed_base_name(child, source)
2394 {
2395 bases.push(base);
2396 }
2397 }
2398 bases.retain(|base| {
2399 !matches!(
2400 base.as_str(),
2401 "final" | "public" | "protected" | "private" | "virtual"
2402 )
2403 });
2404 bases
2405}
2406
2407fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2412 if token.is_named() {
2413 token.kind() == "identifier" && node_text(token, source) == "final"
2414 } else {
2415 token.kind() == "final"
2416 }
2417}
2418
2419fn recovered_export_head_name<'tree>(
2432 node: Node<'tree>,
2433 tail: Node<'tree>,
2434 source: &str,
2435) -> Option<Node<'tree>> {
2436 export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
2437}
2438
2439fn recovered_export_pair_head_name<'tree>(
2446 prefix: Node<'tree>,
2447 sibling: Node<'tree>,
2448 tail: Node<'tree>,
2449 source: &str,
2450) -> Option<Node<'tree>> {
2451 let mut tokens = export_head_tokens(prefix, None);
2452 tokens.extend(export_head_tokens(sibling, Some(tail)));
2453 export_head_name_from_tokens(&tokens, source)
2454}
2455
2456fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
2460 let mut tokens = Vec::new();
2461 let mut cursor = node.walk();
2462 for child in node.children(&mut cursor) {
2463 if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
2464 break;
2465 }
2466 if child.kind() == "ERROR" {
2467 let mut fragment_cursor = child.walk();
2468 tokens.extend(child.children(&mut fragment_cursor));
2469 } else {
2470 tokens.push(child);
2471 }
2472 }
2473 tokens
2474}
2475
2476fn export_head_name_from_tokens<'tree>(
2478 tokens: &[Node<'tree>],
2479 source: &str,
2480) -> Option<Node<'tree>> {
2481 let mut name = None;
2482 for token in tokens.iter().copied() {
2483 if recovered_export_head_final(token, source) || (!token.is_named() && token.kind() == ":")
2484 {
2485 break;
2486 }
2487 if token.is_named()
2488 && !token.is_missing()
2489 && matches!(
2490 token.kind(),
2491 "identifier" | "type_identifier" | "field_identifier"
2492 )
2493 {
2494 name = Some(token);
2495 }
2496 }
2497 name
2498}
2499
2500fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2503 let mut cursor = declaration.walk();
2504 declaration
2505 .named_children(&mut cursor)
2506 .find(|child| child.kind() == "init_declarator")
2507}
2508
2509fn recovered_export_declaration_tail<'tree>(
2517 declaration: Node<'tree>,
2518 head_end: usize,
2519 source: &str,
2520) -> Option<(Vec<String>, Node<'tree>)> {
2521 let init = recovered_export_init_declarator(declaration)?;
2522 let body = init.child_by_field_name("value")?;
2523 if body.kind() != "initializer_list" {
2524 return None;
2525 }
2526 let mut bases = recovered_export_head_bases(declaration, head_end, init.start_byte(), source);
2527 bases.extend(recovered_export_head_bases(
2528 init,
2529 init.start_byte(),
2530 body.start_byte(),
2531 source,
2532 ));
2533 Some((bases, body))
2534}
2535
2536fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
2550 match node.kind() {
2551 "ERROR" => {
2552 let Some(class_node) = first_class_like_child(node) else {
2553 return false;
2554 };
2555 if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2556 return false;
2557 }
2558 if class_node
2559 .child_by_field_name("name")
2560 .and_then(|name| direct_identifier_name(name, source))
2561 .is_none()
2562 {
2563 return false;
2564 }
2565 class_node
2566 .next_sibling()
2567 .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
2568 }
2569 "declaration" => {
2570 let mut cursor = node.walk();
2571 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
2572 let Some(keyword) = children
2578 .iter()
2579 .copied()
2580 .flat_map(|child| {
2581 let mut cursor = child.walk();
2582 if child.kind() == "ERROR" {
2583 child.named_children(&mut cursor).collect::<Vec<_>>()
2584 } else {
2585 vec![child]
2586 }
2587 })
2588 .find(|child| {
2589 child.kind() == "identifier"
2590 && matches!(node_text(*child, source), "class" | "struct" | "union")
2591 })
2592 else {
2593 return false;
2594 };
2595 children.last().is_some_and(|init| {
2596 init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
2597 let mut cursor = init.walk();
2598 let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
2599 matches!(parts.as_slice(), [macro_name, arguments]
2600 if matches!(macro_name.kind(), "identifier" | "type_identifier")
2601 && arguments.kind() == "argument_list")
2602 }
2603 })
2604 }
2605 _ => false,
2606 }
2607}
2608
2609fn recover_function_like_export_class_pair(
2610 node: Node<'_>,
2611 source: &str,
2612) -> Option<RecoveredFunctionLikeExportClassPair> {
2613 if !is_function_like_export_class_head(node, source) {
2614 return None;
2615 }
2616 let sibling = node.next_named_sibling()?;
2617 let (name, raw_supertypes, body) = match sibling.kind() {
2618 "compound_statement" => (
2624 recovered_export_pair_head_name(node, sibling, sibling, source)
2625 .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2626 None,
2627 sibling,
2628 ),
2629 "expression_statement" => {
2630 let compound = sibling.named_child(0)?;
2631 if compound.kind() != "compound_literal_expression" {
2632 return None;
2633 }
2634 let body = compound.child_by_field_name("value")?;
2635 if body.kind() != "initializer_list" {
2636 return None;
2637 }
2638 (
2639 compound
2640 .child_by_field_name("type")
2641 .and_then(|name| direct_identifier_name(name, source))?,
2642 None,
2643 body,
2644 )
2645 }
2646 "labeled_statement" => {
2647 let label = sibling.child_by_field_name("label")?;
2648 if label.kind() != "statement_identifier" {
2649 return None;
2650 }
2651 let name = normalize_cpp_whitespace(node_text(label, source));
2652 let declaration = sibling
2653 .named_children(&mut sibling.walk())
2654 .find(|child| child.kind() == "declaration")?;
2655 let access = declaration.child_by_field_name("type")?;
2656 if !matches!(
2657 node_text(access, source),
2658 "public" | "protected" | "private"
2659 ) {
2660 return None;
2661 }
2662 let (bases, body) =
2663 recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
2664 (name, (!bases.is_empty()).then_some(bases), body)
2665 }
2666 "function_definition" => {
2672 let body = sibling.child_by_field_name("body")?;
2673 if body.kind() != "compound_statement" {
2674 return None;
2675 }
2676 let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
2677 let bases = recovered_export_head_bases(
2678 sibling,
2679 name_node.end_byte(),
2680 body.start_byte(),
2681 source,
2682 );
2683 (
2684 normalize_cpp_whitespace(node_text(name_node, source)),
2685 (!bases.is_empty()).then_some(bases),
2686 body,
2687 )
2688 }
2689 "declaration" => {
2693 let init = recovered_export_init_declarator(sibling)?;
2694 let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
2695 let (bases, body) =
2696 recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
2697 (
2698 normalize_cpp_whitespace(node_text(name_node, source)),
2699 (!bases.is_empty()).then_some(bases),
2700 body,
2701 )
2702 }
2703 _ => return None,
2704 };
2705 debug_assert!(
2706 !name.is_empty(),
2707 "a recovered class head names its class by an identifier token"
2708 );
2709 let range = Range {
2710 start_byte: node.start_byte(),
2711 end_byte: sibling.end_byte(),
2712 start_line: node.start_position().row + 1,
2713 end_line: sibling.end_position().row + 1,
2714 };
2715 Some(RecoveredFunctionLikeExportClassPair {
2716 name,
2717 raw_supertypes,
2718 range,
2719 fragmented_body: recovered_fragmented_export_body(body, range)?,
2720 })
2721}
2722
2723fn recover_embedded_function_like_export_classes(
2730 node: Node<'_>,
2731 source: &str,
2732) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
2733 if !node.is_error() {
2734 return Vec::new();
2735 }
2736
2737 let mut nodes = Vec::new();
2738 let mut stack = vec![node];
2739 while let Some(current) = stack.pop() {
2740 nodes.push(current);
2741 push_children_reversed(current, &mut stack);
2742 }
2743 nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2744
2745 let language = node.language();
2748 let class_kind = NodeKindIds::new(&language, "class");
2749 let identifier_kinds = [
2750 NodeKindIds::new(&language, "identifier"),
2751 NodeKindIds::new(&language, "type_identifier"),
2752 NodeKindIds::new(&language, "field_identifier"),
2753 ];
2754 let argument_list_kind = NodeKindIds::new(&language, "argument_list");
2755 let colon_kind = NodeKindIds::new(&language, ":");
2756 let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
2757
2758 let mut recovered = Vec::new();
2759 for class_token in nodes
2760 .iter()
2761 .copied()
2762 .filter(|child| !child.is_named() && class_kind.matches(*child))
2763 {
2764 let row = class_token.start_position().row;
2765 let is_identifier = |candidate: &Node<'_>| {
2771 !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
2772 };
2773 let Some(macro_name) = nodes.iter().copied().find(|candidate| {
2774 candidate.start_byte() >= class_token.end_byte()
2775 && candidate.start_position().row == row
2776 && is_identifier(candidate)
2777 }) else {
2778 continue;
2779 };
2780 let Some(arguments) = nodes.iter().copied().find(|candidate| {
2781 argument_list_kind.matches(*candidate)
2782 && candidate.start_byte() >= macro_name.end_byte()
2783 && candidate.start_position().row == row
2784 }) else {
2785 continue;
2786 };
2787 if nodes.iter().any(|candidate| {
2788 is_identifier(candidate)
2789 && candidate.start_byte() >= macro_name.end_byte()
2790 && candidate.end_byte() <= arguments.start_byte()
2791 }) {
2792 continue;
2793 }
2794 let Some(head_end) = nodes.iter().copied().find(|candidate| {
2795 candidate.start_byte() >= arguments.end_byte()
2796 && (recovered_export_head_final(*candidate, source)
2797 || (!candidate.is_named() && colon_kind.matches(*candidate)))
2798 }) else {
2799 continue;
2800 };
2801 let Some(name_node) = nodes.iter().copied().rfind(|candidate| {
2802 is_identifier(candidate)
2803 && candidate.start_byte() >= arguments.end_byte()
2804 && candidate.end_byte() <= head_end.start_byte()
2805 && candidate.start_position().row == row
2806 }) else {
2807 continue;
2808 };
2809 let name = normalize_cpp_whitespace(node_text(name_node, source));
2810 let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
2811 field_initializer_kind.matches(*candidate)
2812 && candidate.start_byte() >= name_node.end_byte()
2813 && candidate
2814 .child_by_field_name("field")
2815 .or_else(|| candidate.named_child(0))
2816 .is_some()
2817 && candidate
2818 .child_by_field_name("value")
2819 .or_else(|| {
2820 let mut cursor = candidate.walk();
2821 candidate
2822 .named_children(&mut cursor)
2823 .find(|child| child.kind() == "initializer_list")
2824 })
2825 .is_some_and(|value| value.kind() == "initializer_list")
2826 }) else {
2827 continue;
2828 };
2829 let has_access = nodes.iter().copied().any(|candidate| {
2830 candidate.start_byte() >= name_node.end_byte()
2831 && candidate.end_byte() <= base_initializer.start_byte()
2832 && matches!(
2833 normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
2834 "public" | "protected" | "private"
2835 )
2836 });
2837 if !has_access {
2838 continue;
2839 }
2840 let Some(base_node) = base_initializer
2841 .child_by_field_name("field")
2842 .or_else(|| base_initializer.named_child(0))
2843 else {
2844 continue;
2845 };
2846 let Some(base) = recovered_malformed_base_name(base_node, source) else {
2847 continue;
2848 };
2849 let body = base_initializer
2850 .child_by_field_name("value")
2851 .or_else(|| {
2852 let mut cursor = base_initializer.walk();
2853 base_initializer
2854 .named_children(&mut cursor)
2855 .find(|child| child.kind() == "initializer_list")
2856 })
2857 .expect("initializer-list value checked above");
2858 let range = Range {
2859 start_byte: class_token.start_byte(),
2860 end_byte: body.end_byte(),
2861 start_line: class_token.start_position().row + 1,
2862 end_line: body.end_position().row + 1,
2863 };
2864 if recovered
2865 .iter()
2866 .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
2867 existing.name == name && existing.range == range
2868 })
2869 {
2870 continue;
2871 }
2872 recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
2873 name,
2874 range,
2875 raw_supertypes: vec![base],
2876 fragmented_body: match recovered_fragmented_export_body(body, range) {
2877 Some(fragmented) => fragmented,
2878 None => continue,
2879 },
2880 });
2881 }
2882 recovered
2883}
2884
2885fn lifted_function_like_export_class_namespace<'tree>(
2886 node: Node<'tree>,
2887 source: &str,
2888 ancestry: &ParentIndex<'tree>,
2889) -> Option<String> {
2890 let mut anchor = node;
2896 let parent = loop {
2897 let parent = ancestry.parent(anchor)?;
2898 if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
2899 break parent;
2900 }
2901 anchor = parent;
2902 };
2903 let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
2904 sibling.start_byte() > anchor.end_byte()
2905 && sibling.kind() == "ERROR"
2906 && sibling.named_child_count() == 0
2907 && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
2908 });
2909 if !has_later_close {
2910 return None;
2911 }
2912 let candidates = parent
2913 .named_children(&mut parent.walk())
2914 .filter(|sibling| {
2915 sibling.kind() == "namespace_definition"
2916 && sibling.has_error()
2917 && sibling.end_byte() < anchor.start_byte()
2918 })
2919 .filter_map(|namespace| {
2920 namespace
2921 .child_by_field_name("name")
2922 .map(|name| normalize_cpp_whitespace(node_text(name, source)))
2923 .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
2924 })
2925 .collect::<Vec<_>>();
2926 let [namespace] = candidates.as_slice() else {
2927 return None;
2928 };
2929 Some(namespace.clone())
2930}
2931
2932pub(crate) fn recovered_function_like_export_class_pair_has_body(
2933 node: Node<'_>,
2934 source: &str,
2935 identifier: &str,
2936 range: &Range,
2937) -> bool {
2938 recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
2939 recovered.name == identifier
2940 && recovered.range.start_byte == range.start_byte
2941 && recovered.range.end_byte == range.end_byte
2942 })
2943}
2944
2945#[derive(Default)]
2961pub struct CppRecoveredExportClassIndex {
2962 by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
2963}
2964
2965impl CppRecoveredExportClassIndex {
2966 pub fn build(root: Node<'_>, source: &str) -> Self {
2967 let mut by_error_node: HashMap<
2968 (usize, usize),
2969 Vec<RecoveredEmbeddedFunctionLikeExportClass>,
2970 > = HashMap::default();
2971 let mut cursor = root.walk();
2974 let mut stack = vec![root];
2975 while let Some(node) = stack.pop() {
2976 if node.is_error() {
2977 let recovered = recover_embedded_function_like_export_classes(node, source);
2978 if !recovered.is_empty() {
2979 by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
2980 }
2981 }
2982 stack.extend(node.named_children(&mut cursor));
2983 }
2984 Self { by_error_node }
2985 }
2986
2987 pub fn approximate_size(&self) -> usize {
2989 self.by_error_node
2990 .values()
2991 .fold(0usize, |total, recovered| {
2992 recovered.iter().fold(
2993 total.saturating_add(std::mem::size_of::<(usize, usize)>()),
2994 |acc, class| {
2995 acc.saturating_add(std::mem::size_of::<
2996 RecoveredEmbeddedFunctionLikeExportClass,
2997 >())
2998 .saturating_add(class.name.len())
2999 .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3000 },
3001 )
3002 })
3003 }
3004
3005 fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3006 self.by_error_node
3007 .get(&(node.start_byte(), node.end_byte()))
3008 .is_some_and(|recovered| {
3009 recovered.iter().any(|class| {
3010 class.name == identifier
3011 && class.range.start_byte == range.start_byte
3012 && class.range.end_byte == range.end_byte
3013 })
3014 })
3015 }
3016}
3017
3018#[cfg(any(test, feature = "test-support"))]
3025thread_local! {
3026 static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3027 const { std::cell::Cell::new(0) };
3028}
3029
3030#[cfg(any(test, feature = "test-support"))]
3034#[doc(hidden)]
3035pub fn recovered_class_body_node_visits_for_test() -> usize {
3036 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3037}
3038
3039#[cfg(any(test, feature = "test-support"))]
3041#[doc(hidden)]
3042pub fn reset_recovered_class_body_node_visits_for_test() {
3043 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3044}
3045
3046#[cfg(any(test, feature = "test-support"))]
3047fn record_recovered_class_body_visit() {
3048 RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3049}
3050
3051#[cfg(not(any(test, feature = "test-support")))]
3052fn record_recovered_class_body_visit() {}
3053
3054pub(crate) fn recovered_class_body_at(
3077 recovered_export_classes: &CppRecoveredExportClassIndex,
3078 root: Node<'_>,
3079 source: &str,
3080 identifier: &str,
3081 range: &Range,
3082) -> Option<bool> {
3083 let covers_range_start = |node: &Node<'_>| {
3084 node.start_byte() <= range.start_byte
3085 && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3086 };
3087 let mut stack = vec![root];
3088 let mut saw_forward = false;
3089 while let Some(node) = stack.pop() {
3090 record_recovered_class_body_visit();
3091 if (node.start_byte() == range.start_byte
3095 && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3096 || recovered_export_classes.claims(node, identifier, range)
3097 || (node.start_byte() == range.start_byte
3098 && recovered_fragmented_class_has_body(node, source, identifier, range))
3099 {
3100 return Some(true);
3101 }
3102 if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3103 return Some(true);
3104 }
3105 if node.start_byte() <= range.start_byte
3112 && range.start_byte < node.end_byte()
3113 && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3114 {
3115 if has_body {
3116 return Some(true);
3117 }
3118 saw_forward = true;
3119 continue;
3120 }
3121 let mut cursor = node.walk();
3122 stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3123 }
3124 saw_forward.then_some(false)
3125}
3126
3127fn recovered_collapsed_aggregate_has_body(
3140 node: Node<'_>,
3141 source: &str,
3142 identifier: &str,
3143 range: &Range,
3144) -> bool {
3145 let claims = |head: &CppCollapsedAggregateHead<'_>| {
3146 head.key.start_byte() == range.start_byte
3147 && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3148 };
3149 if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3150 return true;
3151 }
3152 if !node.is_error() {
3153 return false;
3154 }
3155 let mut cursor = node.walk();
3156 let children = node.children(&mut cursor).collect::<Vec<_>>();
3157 children
3158 .iter()
3159 .enumerate()
3160 .filter(|(_, child)| {
3161 child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3162 })
3163 .any(|(index, _)| {
3164 cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3165 })
3166}
3167
3168pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3176 if !matches!(
3177 node.kind(),
3178 "qualified_identifier" | "scoped_type_identifier" | "template_type"
3179 ) {
3180 return false;
3181 }
3182 if let Some(function) = node.parent().filter(|parent| {
3183 parent.kind() == "function_definition"
3184 && parent
3185 .child_by_field_name("declarator")
3186 .is_some_and(|declarator| same_node(declarator, node))
3187 }) {
3188 return recover_exported_class_function_definition(function, source)
3189 .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
3190 }
3191 let Some(initializer) = node.parent().filter(|parent| {
3192 parent.kind() == "init_declarator"
3193 && parent
3194 .child_by_field_name("declarator")
3195 .is_some_and(|declarator| same_node(declarator, node))
3196 }) else {
3197 return false;
3198 };
3199 initializer
3200 .parent()
3201 .filter(|parent| parent.kind() == "declaration")
3202 .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3203 .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3204}
3205
3206struct CppSentinelReparsedClass<'tree> {
3212 declaration_node: Node<'tree>,
3213 name: String,
3214 body: Node<'tree>,
3215 raw_supertypes: Option<Vec<String>>,
3216}
3217
3218fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3219 let mut cursor = root.walk();
3220 root.named_children(&mut cursor)
3221 .find(|child| child.kind() != "comment")
3222 .filter(|child| child.kind() == "template_declaration")
3223}
3224
3225fn cpp_sentinel_reparsed_class<'tree>(
3226 root: Node<'tree>,
3227 template_node: Option<Node<'tree>>,
3228 source: &str,
3229 ancestry: &ParentIndex<'tree>,
3230) -> Option<CppSentinelReparsedClass<'tree>> {
3231 let container = template_node.unwrap_or(root);
3232 let mut cursor = container.walk();
3233 for child in container.named_children(&mut cursor) {
3234 if matches!(
3235 child.kind(),
3236 "class_specifier" | "struct_specifier" | "union_specifier"
3237 ) {
3238 let name = class_like_name(child, source, ancestry)?;
3239 let body = cpp_body_node(child)?;
3240 let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
3241 .then(|| extract_cpp_supertypes(child, source));
3242 return Some(CppSentinelReparsedClass {
3243 declaration_node: child,
3244 name,
3245 body,
3246 raw_supertypes,
3247 });
3248 }
3249 if child.kind() == "declaration"
3250 && let Some(class_node) = first_class_like_child(child)
3251 {
3252 let name = class_like_name(class_node, source, ancestry)?;
3253 let body = cpp_body_node(class_node)?;
3254 let raw_supertypes =
3255 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3256 .then(|| extract_cpp_supertypes(class_node, source));
3257 return Some(CppSentinelReparsedClass {
3258 declaration_node: class_node,
3259 name,
3260 body,
3261 raw_supertypes,
3262 });
3263 }
3264 if child.kind() == "function_definition"
3269 && let Some(class_node) = first_class_like_child(child)
3270 && let Some(body) = cpp_body_node(class_node)
3271 && let Some(name) = class_like_name(class_node, source, ancestry)
3272 {
3273 let raw_supertypes =
3274 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3275 .then(|| extract_cpp_supertypes(class_node, source));
3276 return Some(CppSentinelReparsedClass {
3277 declaration_node: class_node,
3278 name,
3279 body,
3280 raw_supertypes,
3281 });
3282 }
3283 if child.kind() == "function_definition"
3284 && let Some((_, name, raw_supertypes)) =
3285 recover_exported_class_function_definition(child, source)
3286 {
3287 let body = cpp_body_node(child)?;
3288 return Some(CppSentinelReparsedClass {
3289 declaration_node: child,
3290 name,
3291 body,
3292 raw_supertypes,
3293 });
3294 }
3295 }
3296 None
3297}
3298
3299fn recovered_postfix_export_macro_base(
3300 node: Node<'_>,
3301 type_node: Node<'_>,
3302 declarator: Node<'_>,
3303 source: &str,
3304) -> Option<String> {
3305 let mut cursor = node.walk();
3306 let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
3307 child.kind() == "ERROR"
3308 && child.start_byte() >= type_node.end_byte()
3309 && child.end_byte() <= declarator.start_byte()
3310 && postfix_export_macro_inheritance(*child, source)
3311 });
3312 malformed_clauses.next()?;
3313 if malformed_clauses.next().is_some() {
3314 return None;
3315 }
3316 recovered_malformed_base_name(declarator, source)
3317}
3318
3319fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
3320 let mut macro_count = 0;
3321 let mut colon_count = 0;
3322 let mut access_count = 0;
3323 for index in 0..node.child_count() {
3324 let Some(child) = node.child(index) else {
3325 return false;
3326 };
3327 match child.kind() {
3328 "identifier" | "type_identifier" if child.is_named() => {
3329 let candidate = normalize_cpp_whitespace(node_text(child, source));
3330 if !cpp_export_macro_token(&candidate) {
3331 return false;
3332 }
3333 macro_count += 1;
3334 }
3335 ":" if !child.is_named() => colon_count += 1,
3336 "public" | "protected" | "private" if !child.is_named() => access_count += 1,
3337 _ => return false,
3338 }
3339 }
3340 macro_count == 1 && colon_count == 1 && access_count == 1
3341}
3342
3343fn recovered_single_base_after_declarator(
3344 node: Node<'_>,
3345 declarator: Node<'_>,
3346 source: &str,
3347) -> Option<String> {
3348 let body_start = node
3349 .child_by_field_name("body")
3350 .map(|body| body.start_byte())
3351 .unwrap_or(node.end_byte());
3352 let mut cursor = node.walk();
3353 let mut bases = node
3354 .named_children(&mut cursor)
3355 .filter(|child| {
3356 child.kind() == "ERROR"
3357 && child.start_byte() >= declarator.end_byte()
3358 && child.end_byte() <= body_start
3359 })
3360 .filter_map(|error| displaced_exported_class_name(error, source));
3361 let base = bases.next()?;
3362 bases.next().is_none().then_some(base)
3363}
3364
3365fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
3366 (0..node.child_count()).any(|index| {
3367 node.child(index)
3368 .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
3369 })
3370}
3371
3372pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3373 recover_exported_class_function_definition(node, source).is_some()
3374}
3375
3376fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3377 matches!(
3378 kind,
3379 "ERROR"
3380 | "preproc_if"
3381 | "preproc_ifdef"
3382 | "preproc_ifndef"
3383 | "preproc_else"
3384 | "preproc_elif"
3385 ) || (kind == "labeled_statement" && in_class_scope)
3386}
3387
3388pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3389 if node.kind() != "declaration" {
3390 return false;
3391 }
3392 let mut ancestor = node.parent();
3393 while let Some(container) = ancestor {
3394 match container.kind() {
3395 "compound_statement" => {
3396 return container.parent().is_some_and(|class_container| {
3397 is_recovered_exported_class_container(class_container, source)
3398 });
3399 }
3400 "template_declaration" | "linkage_specification" | "declaration_list" => {}
3403 kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3404 _ => return false,
3405 }
3406 ancestor = container.parent();
3407 }
3408 false
3409}
3410
3411pub fn recovered_exported_class_has_body(
3412 node: Node<'_>,
3413 source: &str,
3414 expected_name: &str,
3415) -> Option<bool> {
3416 match node.kind() {
3417 "function_definition" => {
3418 let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
3419 (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3420 }
3421 "declaration" | "field_declaration" => {
3422 let recovered = recover_exported_class_declaration(node, source)?;
3423 (recovered.name == expected_name).then(|| recovered.body.is_some())
3424 }
3425 _ => None,
3426 }
3427}
3428
3429fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3430 let body_start = node
3431 .child_by_field_name("body")
3432 .map(|body| body.start_byte())
3433 .unwrap_or(node.end_byte());
3434 let mut stack = Vec::new();
3435 for index in (0..node.named_child_count()).rev() {
3436 let Some(child) = node.named_child(index) else {
3437 continue;
3438 };
3439 if child.start_byte() >= body_start {
3440 continue;
3441 }
3442 stack.push(child);
3443 }
3444
3445 let mut best = None;
3446 while let Some(current) = stack.pop() {
3447 if matches!(current.kind(), "identifier" | "type_identifier") {
3448 let name = normalize_cpp_whitespace(node_text(current, source));
3449 if !name.is_empty()
3450 && !cpp_export_macro_token(&name)
3451 && !matches!(name.as_str(), "class" | "struct" | "union")
3452 {
3453 best = Some(name);
3454 }
3455 continue;
3456 }
3457
3458 for index in (0..current.named_child_count()).rev() {
3459 if let Some(child) = current.named_child(index)
3460 && child.start_byte() < body_start
3461 {
3462 stack.push(child);
3463 }
3464 }
3465 }
3466 best
3467}
3468
3469fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3470 if node.kind() == "declaration"
3471 && node
3472 .child_by_field_name("type")
3473 .or_else(|| first_class_like_child(node))
3474 .is_some_and(|type_node| {
3475 matches!(
3476 type_node.kind(),
3477 "class_specifier" | "struct_specifier" | "union_specifier"
3478 )
3479 })
3480 && let Some(name) = node
3481 .child_by_field_name("declarator")
3482 .and_then(|declarator| declarator_name_from_node(declarator, source))
3483 && !cpp_export_macro_token(&name)
3484 {
3485 return Some(name);
3486 }
3487
3488 if node.kind() == "function_definition"
3489 && node.child_by_field_name("type").is_some_and(|type_node| {
3490 matches!(
3491 type_node.kind(),
3492 "class_specifier" | "struct_specifier" | "union_specifier"
3493 )
3494 })
3495 && let Some(name) = node
3496 .child_by_field_name("declarator")
3497 .and_then(|declarator| direct_identifier_name(declarator, source))
3498 && !cpp_export_macro_token(&name)
3499 {
3500 return Some(name);
3501 }
3502
3503 let class_node = if matches!(
3504 node.kind(),
3505 "class_specifier" | "struct_specifier" | "union_specifier"
3506 ) {
3507 node
3508 } else {
3509 first_class_like_child(node)?
3510 };
3511 class_like_name_from_children(class_node, source)
3512}
3513
3514fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3515 if !matches!(
3516 node.kind(),
3517 "identifier" | "field_identifier" | "type_identifier"
3518 ) {
3519 return None;
3520 }
3521 let name = normalize_cpp_whitespace(node_text(node, source));
3522 (!name.is_empty()).then_some(name)
3523}
3524
3525fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3526 match node.kind() {
3527 "identifier" | "field_identifier" | "type_identifier" => {
3528 let name = normalize_cpp_whitespace(node_text(node, source));
3529 (!name.is_empty()).then_some(name)
3530 }
3531 _ => {
3532 let mut cursor = node.walk();
3533 node.named_children(&mut cursor)
3534 .find_map(|child| declarator_name_from_node(child, source))
3535 }
3536 }
3537}
3538
3539fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3540 let mut cursor = node.walk();
3541 node.named_children(&mut cursor).find(|child| {
3542 matches!(
3543 child.kind(),
3544 "class_specifier" | "struct_specifier" | "union_specifier"
3545 )
3546 })
3547}
3548
3549fn push_cpp_container_work<'tree>(
3554 node: Node<'tree>,
3555 scope: ScopeInfo,
3556 stack: &mut Vec<CppWork<'tree>>,
3557) {
3558 push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3559}
3560
3561fn push_cpp_sibling_range<'tree>(
3565 parent: Node<'tree>,
3566 start_index: usize,
3567 end_index: usize,
3568 scope: ScopeInfo,
3569 stack: &mut Vec<CppWork<'tree>>,
3570) {
3571 let mut cursor = parent.walk();
3572 let children = parent
3573 .named_children(&mut cursor)
3574 .skip(start_index)
3575 .take(end_index.saturating_sub(start_index))
3576 .collect::<Vec<_>>()
3577 .into_iter();
3578 stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3579}
3580
3581fn advance_cpp_siblings<'tree>(
3589 mut siblings: CppSiblingsWork<'tree>,
3590 source: &str,
3591 stack: &mut Vec<CppWork<'tree>>,
3592) {
3593 let Some(child) = siblings.children.next() else {
3594 return;
3595 };
3596 let current_scope = siblings.scope.clone();
3597 if let Some(namespace) = cpp_using_namespace_target(child, source) {
3598 siblings.scope.visible_using_namespaces.push(namespace);
3599 }
3600 if !siblings.children.as_slice().is_empty() {
3601 stack.push(CppWork::Siblings(siblings));
3602 }
3603 stack.push(CppWork::Node(CppNodeWork {
3604 node: child,
3605 scope: current_scope,
3606 }));
3607}
3608
3609fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3616 if node.kind() != "using_declaration" {
3617 return None;
3618 }
3619 let mut cursor = node.walk();
3620 let is_namespace_directive = node
3621 .children(&mut cursor)
3622 .any(|child| child.kind() == "namespace");
3623 if !is_namespace_directive {
3624 return None;
3625 }
3626 let target = node.named_child(0)?;
3627 let start = target
3635 .child(0)
3636 .filter(|child| !child.is_named() && child.kind() == "::")
3637 .map_or(target.start_byte(), |marker| marker.end_byte());
3638 let text = normalize_cpp_whitespace(
3639 source
3640 .get(start..target.end_byte())
3641 .expect("using-directive target covers one source range"),
3642 );
3643 (!text.is_empty()).then_some(text)
3644}
3645
3646pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
3659 let mut parser = Parser::new();
3660 if parser
3661 .set_language(&tree_sitter_cpp::LANGUAGE.into())
3662 .is_err()
3663 {
3664 return Vec::new();
3665 }
3666 let Some(tree) = parser.parse(source, None) else {
3667 return Vec::new();
3668 };
3669 let mut namespaces = Vec::new();
3670 let mut seen = std::collections::HashSet::new();
3671 let mut stack = vec![tree.root_node()];
3672 while let Some(node) = stack.pop() {
3673 if let Some(namespace) = cpp_using_namespace_target(node, source)
3674 && seen.insert(namespace.clone())
3675 {
3676 namespaces.push(namespace);
3677 }
3678 let mut cursor = node.walk();
3679 stack.extend(node.named_children(&mut cursor));
3680 }
3681 namespaces
3682}
3683
3684pub struct CppVisitor<'a> {
3685 pub file: &'a ProjectFile,
3686 pub source: &'a str,
3687 pub parsed: &'a mut ParsedFile,
3688 pub c_tag_semantics: bool,
3699 pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
3700 pub consumed_fragment_regions: Vec<(usize, usize)>,
3709 pub orphaned_namespaces: OrphanedNamespaceScopeIndex,
3714 pub partitioned_regions: Vec<(Tree, std::ops::Range<usize>, ScopeInfo)>,
3717 pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
3722 pub field_owners: Option<CppFieldOwnerIndex>,
3726 pub recovery_captures: Vec<CppRecoveryCapture>,
3730 pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
3734 pub ambiguous_object_macro_fields: HashSet<String>,
3737}
3738
3739#[derive(Clone, Debug, PartialEq, Eq)]
3746pub enum ObjectMacroFieldEvent {
3747 Define {
3748 name: String,
3749 replacement: ObjectMacroReplacement,
3750 conditional: bool,
3751 },
3752 Undef {
3753 name: String,
3754 conditional: bool,
3755 },
3756}
3757
3758pub fn collect_cpp_object_macro_fields<'tree>(
3762 root: Node<'tree>,
3763 source: &str,
3764) -> HashMap<String, ObjectMacroReplacement> {
3765 let mut fields = HashMap::default();
3766 let mut ambiguous = HashSet::default();
3767 for event in collect_cpp_object_macro_field_events(root, source) {
3768 match event {
3769 ObjectMacroFieldEvent::Define {
3770 name,
3771 replacement: value,
3772 conditional,
3773 } => {
3774 if value.is_empty() {
3775 fields.remove(&name);
3776 if conditional {
3777 ambiguous.insert(name);
3778 } else {
3779 ambiguous.remove(&name);
3780 }
3781 } else if ambiguous.contains(&name) {
3782 } else if let Some(previous) = fields.get(&name) {
3784 if previous != &value {
3785 fields.remove(&name);
3786 ambiguous.insert(name);
3787 }
3788 } else {
3789 fields.insert(name, value);
3790 }
3791 }
3792 ObjectMacroFieldEvent::Undef { name, conditional } => {
3793 fields.remove(&name);
3794 if conditional {
3795 ambiguous.insert(name);
3796 } else {
3797 ambiguous.remove(&name);
3798 }
3799 }
3800 }
3801 }
3802 fields
3803}
3804
3805pub fn collect_cpp_object_macro_field_events<'tree>(
3810 root: Node<'tree>,
3811 source: &str,
3812) -> Vec<ObjectMacroFieldEvent> {
3813 let mut events = Vec::new();
3814 let mut stack = vec![root];
3815 while let Some(node) = stack.pop() {
3816 if node.kind() == "preproc_def"
3817 && let Some(name) = extract_macro_name(node, source)
3818 {
3819 let replacement = object_macro_replacement_of(node, source);
3820 events.push(ObjectMacroFieldEvent::Define {
3821 name,
3822 replacement,
3823 conditional: inside_preprocessor_conditional(node),
3824 });
3825 } else if is_cpp_undef_directive(node, source)
3826 && let Some(argument) = node.child_by_field_name("argument")
3827 {
3828 events.push(ObjectMacroFieldEvent::Undef {
3829 name: node_text(argument, source).trim().to_string(),
3830 conditional: inside_preprocessor_conditional(node),
3831 });
3832 }
3833 let mut cursor = node.walk();
3834 let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3835 stack.extend(children.into_iter().rev());
3836 }
3837 events
3838}
3839
3840fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
3841 let mut current = node.parent();
3842 while let Some(parent) = current {
3843 if matches!(
3844 parent.kind(),
3845 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
3846 ) {
3847 return true;
3848 }
3849 current = parent.parent();
3850 }
3851 false
3852}
3853
3854fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
3855 node.kind() == "preproc_call"
3856 && node
3857 .child_by_field_name("directive")
3858 .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
3859}
3860
3861impl<'a> CppVisitor<'a> {
3862 fn add_declaration(
3870 &mut self,
3871 code_unit: CodeUnit,
3872 node: Node<'_>,
3873 parent: Option<CodeUnit>,
3874 top_level: Option<CodeUnit>,
3875 ) {
3876 self.note_declaration(&code_unit);
3877 let source = self.source;
3878 self.parsed
3879 .add_code_unit(code_unit, node, source, parent, top_level);
3880 }
3881
3882 fn add_declaration_with_range(
3884 &mut self,
3885 code_unit: CodeUnit,
3886 range: Range,
3887 parent: Option<CodeUnit>,
3888 top_level: Option<CodeUnit>,
3889 ) {
3890 self.note_declaration(&code_unit);
3891 self.parsed
3892 .add_code_unit_with_range(code_unit, range, parent, top_level);
3893 }
3894
3895 fn replace_declaration_deferred(
3897 &mut self,
3898 code_unit: CodeUnit,
3899 node: Node<'_>,
3900 parent: Option<CodeUnit>,
3901 top_level: Option<CodeUnit>,
3902 ) {
3903 self.note_replaced_declaration(&code_unit);
3904 let source = self.source;
3905 self.parsed
3906 .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
3907 }
3908
3909 fn replace_declaration_with_range_deferred(
3911 &mut self,
3912 code_unit: CodeUnit,
3913 range: Range,
3914 parent: Option<CodeUnit>,
3915 top_level: Option<CodeUnit>,
3916 ) {
3917 self.note_replaced_declaration(&code_unit);
3918 self.parsed
3919 .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
3920 }
3921
3922 fn note_declaration(&mut self, code_unit: &CodeUnit) {
3929 if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
3930 for capture in &mut self.recovery_captures {
3931 if capture.removed_pre_existing.contains(code_unit) {
3932 continue;
3933 }
3934 if capture.created_units.insert(code_unit.clone()) {
3935 capture.created.push(code_unit.clone());
3936 }
3937 }
3938 }
3939 if let Some(field_owners) = self.field_owners.as_mut() {
3940 field_owners.record(code_unit, self.file);
3941 }
3942 }
3943
3944 fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
3953 let removes_children = self.parsed.contains_declaration(code_unit)
3954 && self
3955 .parsed
3956 .children
3957 .get(code_unit)
3958 .is_some_and(|children| !children.is_empty());
3959 if removes_children {
3960 if !self.recovery_captures.is_empty() {
3961 let removed = self.declarations_a_replacement_removes(code_unit);
3962 for capture in &mut self.recovery_captures {
3963 for unit in &removed {
3964 if !capture.created_units.contains(unit) {
3969 capture.removed_pre_existing.insert(unit.clone());
3970 }
3971 }
3972 }
3973 }
3974 self.field_owners = None;
3975 }
3976 self.note_declaration(code_unit);
3977 }
3978
3979 fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
3982 let mut removed = Vec::new();
3983 let mut seen = HashSet::default();
3984 let mut pending: Vec<CodeUnit> = self
3985 .parsed
3986 .children
3987 .get(code_unit)
3988 .cloned()
3989 .unwrap_or_default();
3990 while let Some(unit) = pending.pop() {
3991 if !seen.insert(unit.clone()) {
3992 continue;
3993 }
3994 if let Some(children) = self.parsed.children.get(&unit) {
3995 pending.extend(children.iter().cloned());
3996 }
3997 removed.push(unit);
3998 }
3999 removed
4000 }
4001
4002 fn visit_function_like_export_class_pair<'tree>(
4003 &mut self,
4004 node: Node<'tree>,
4005 scope: &ScopeInfo,
4006 stack: &mut Vec<CppWork<'tree>>,
4007 ancestry: &ParentIndex<'tree>,
4008 ) -> bool {
4009 let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
4010 return false;
4011 };
4012 let member_outcome = self
4013 .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
4014 let mut displaced = node.next_named_sibling();
4020 while let Some(candidate) = displaced {
4021 if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
4022 break;
4023 }
4024 displaced = candidate.next_named_sibling();
4025 }
4026 let class_unit = self.visit_named_class_like_shape(
4027 node,
4028 recovered.name,
4029 None,
4034 true,
4035 Some(recovered.range),
4036 recovered.raw_supertypes,
4037 scope,
4038 stack,
4039 ancestry,
4040 );
4041 self.parsed
4042 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4043 recovery: recovered.range,
4044 unit: class_unit.clone(),
4045 });
4046 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4047 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4048 tree.root_node(),
4049 class_unit.identifier(),
4050 self.source,
4051 )
4052 {
4053 self.visit_recovered_fragment_constructor(
4054 range,
4055 body,
4056 node,
4057 &class_unit,
4058 scope,
4059 ancestry,
4060 );
4061 }
4062 if let Some(outcome) = member_outcome {
4063 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4064 }
4065 self.consumed_fragment_regions
4066 .push((node.start_byte(), recovered.range.end_byte));
4067 true
4068 }
4069
4070 fn visit_embedded_function_like_export_classes<'tree>(
4071 &mut self,
4072 node: Node<'tree>,
4073 scope: &ScopeInfo,
4074 stack: &mut Vec<CppWork<'tree>>,
4075 ancestry: &ParentIndex<'tree>,
4076 ) -> bool {
4077 let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
4078 let found = !recovered_classes.is_empty();
4079 for recovered in recovered_classes {
4080 let member_outcome = self.reparse_fragmented_export_class_members(
4081 &recovered.fragmented_body,
4082 &recovered.name,
4083 );
4084 let class_unit = self.visit_named_class_like_shape(
4085 node,
4086 recovered.name,
4087 None,
4088 true,
4089 Some(recovered.range),
4090 Some(recovered.raw_supertypes),
4091 scope,
4092 stack,
4093 ancestry,
4094 );
4095 self.parsed
4096 .record_materialization(MaterializationRecord::RecoveredDeclaration {
4097 recovery: recovered.range,
4098 unit: class_unit.clone(),
4099 });
4100 if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4101 && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4102 tree.root_node(),
4103 class_unit.identifier(),
4104 self.source,
4105 )
4106 {
4107 self.visit_recovered_fragment_constructor(
4108 range,
4109 body,
4110 node,
4111 &class_unit,
4112 scope,
4113 ancestry,
4114 );
4115 }
4116 if let Some(outcome) = member_outcome {
4117 self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4118 }
4119 }
4120 found
4121 }
4122
4123 #[allow(clippy::too_many_arguments)]
4131 pub fn visit_container<'tree>(
4132 &mut self,
4133 node: Node<'tree>,
4134 ancestry: &ParentIndex<'tree>,
4135 package_name: &str,
4136 module: Option<CodeUnit>,
4137 class_unit: Option<CodeUnit>,
4138 template_signature: Option<String>,
4139 visible_using_namespaces: Vec<String>,
4140 ) {
4141 let scope = ScopeInfo {
4142 package_name: package_name.to_string(),
4143 module,
4144 class_unit,
4145 template_signature,
4146 template_metadata: None,
4147 declarations_are_fields: false,
4148 recovered_specialization_member_scope: false,
4149 visible_using_namespaces,
4150 };
4151 if node.is_error() {
4158 self.visit_object_macro_error_classes(node, &scope);
4159 }
4160 self.run_container_work(node, scope, ancestry);
4161 while let Some((tree, range, scope)) = self.partitioned_regions.pop() {
4162 let root = tree.root_node();
4163 let container = root
4164 .descendant_for_byte_range(range.start, range.end)
4165 .expect("the queued container belongs to this tree");
4166 assert_eq!(container.byte_range(), range);
4167 self.run_container_work(container, scope, &ParentIndex::new(root));
4168 }
4169 }
4170
4171 fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
4175 self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
4176 }
4177
4178 fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
4183 self.consumed_fragment_regions
4184 .iter()
4185 .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
4186 }
4187
4188 fn run_container_work<'tree>(
4200 &mut self,
4201 node: Node<'tree>,
4202 scope: ScopeInfo,
4203 ancestry: &ParentIndex<'tree>,
4204 ) {
4205 self.drain_cpp_work(
4206 vec![CppWork::Container(CppContainer { node, scope })],
4207 ancestry,
4208 );
4209 }
4210
4211 fn drain_cpp_work<'tree>(
4218 &mut self,
4219 mut stack: Vec<CppWork<'tree>>,
4220 ancestry: &ParentIndex<'tree>,
4221 ) {
4222 while let Some(work) = stack.pop() {
4223 match work {
4224 CppWork::Container(container) => {
4225 push_cpp_container_work(container.node, container.scope, &mut stack);
4226 }
4227 CppWork::Siblings(siblings) => {
4228 advance_cpp_siblings(siblings, self.source, &mut stack);
4229 }
4230 CppWork::Node(work) => {
4231 if self.node_is_inside_consumed_fragment(work.node) {
4232 continue;
4233 }
4234 self.visit_node(work.node, &work.scope, &mut stack, ancestry);
4235 }
4236 }
4237 }
4238 }
4239
4240 fn reparse_fragmented_export_class_members(
4245 &self,
4246 fragmented: &FragmentedExportBody,
4247 class_name: &str,
4248 ) -> Option<FragmentedExportMembers> {
4249 if fragmented.reparse_start >= fragmented.reparse_end {
4250 return None;
4251 }
4252 let tree = cpp_reparse_fragmented_class_body(
4253 self.source,
4254 fragmented.reparse_start,
4255 fragmented.reparse_end,
4256 )?;
4257 if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
4258 return Some(FragmentedExportMembers::Complete(tree));
4259 }
4260 let has_conditional_constructor = {
4261 let root = tree.root_node();
4262 let mut cursor = root.walk();
4263 root.named_children(&mut cursor).any(|child| {
4264 cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
4265 })
4266 };
4267 has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
4268 }
4269
4270 fn visit_fragmented_export_class_members(
4273 &mut self,
4274 outcome: FragmentedExportMembers,
4275 class_unit: CodeUnit,
4276 scope: &ScopeInfo,
4277 ) -> bool {
4278 let (tree, complete) = match outcome {
4279 FragmentedExportMembers::Complete(tree) => (tree, true),
4280 FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
4281 };
4282 let root = tree.root_node();
4283 let class_name = class_unit.identifier().to_string();
4284 let member_scope = ScopeInfo {
4285 package_name: class_unit.package_name().to_string(),
4290 module: scope.module.clone(),
4291 class_unit: Some(class_unit),
4292 template_signature: scope.template_signature.clone(),
4293 template_metadata: None,
4294 declarations_are_fields: true,
4295 recovered_specialization_member_scope: false,
4296 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4297 };
4298 if !complete {
4299 let mut cursor = root.walk();
4305 let constructors = root
4306 .named_children(&mut cursor)
4307 .filter_map(|child| {
4308 cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
4309 })
4310 .collect::<Vec<_>>();
4311 let reparsed_ancestry = ParentIndex::new(root);
4314 for constructor in constructors {
4315 let mut stack = Vec::new();
4316 self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
4317 while let Some(work) = stack.pop() {
4318 match work {
4319 CppWork::Container(container) => {
4320 push_cpp_container_work(container.node, container.scope, &mut stack);
4321 }
4322 CppWork::Siblings(siblings) => {
4323 advance_cpp_siblings(siblings, self.source, &mut stack);
4324 }
4325 CppWork::Node(work) => {
4326 self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
4327 }
4328 }
4329 }
4330 }
4331 return false;
4332 }
4333 self.run_container_work(root, member_scope, &ParentIndex::new(root));
4335 true
4336 }
4337
4338 fn visit_recovered_fragment_constructor<'tree>(
4339 &mut self,
4340 range: std::ops::Range<usize>,
4341 constructor_body: Node<'tree>,
4342 class_declaration: Node<'tree>,
4343 class_unit: &CodeUnit,
4344 scope: &ScopeInfo,
4345 ancestry: &ParentIndex<'tree>,
4346 ) {
4347 let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
4348 return;
4349 };
4350 let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
4351 tree.root_node(),
4352 range.start,
4353 class_unit.identifier(),
4354 self.source,
4355 ) else {
4356 return;
4357 };
4358 let member_scope = ScopeInfo {
4359 package_name: class_unit.package_name().to_string(),
4360 module: scope.module.clone(),
4361 class_unit: Some(class_unit.clone()),
4362 template_signature: scope.template_signature.clone(),
4363 template_metadata: None,
4364 declarations_are_fields: true,
4365 recovered_specialization_member_scope: false,
4366 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4367 };
4368 let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
4369 else {
4370 return;
4371 };
4372 debug_assert_eq!(function.name, class_unit.identifier());
4373 let code_unit = function.code_unit(self.file.clone());
4374 self.add_declaration_with_range(
4375 code_unit.clone(),
4376 Range {
4377 start_byte: function_declarator.start_byte(),
4378 end_byte: constructor_body.end_byte(),
4379 start_line: function_declarator.start_position().row + 1,
4380 end_line: constructor_body.end_position().row + 1,
4381 },
4382 None,
4383 None,
4384 );
4385 self.parsed.add_signature_with_metadata(
4386 code_unit.clone(),
4387 cpp_signature_metadata(
4388 normalize_cpp_whitespace(node_text(function_declarator, self.source)),
4389 function_declarator,
4390 self.source,
4391 ancestry,
4392 )
4393 .with_declaration_only(false)
4394 .with_callable_linkage(cpp_callable_linkage(
4395 class_declaration,
4396 self.source,
4397 ancestry,
4398 )),
4399 );
4400 self.parsed.add_child(class_unit.clone(), code_unit);
4401 }
4402
4403 fn visit_recovered_fragment_prefix_members<'tree>(
4404 &mut self,
4405 root: Node<'tree>,
4406 constructor_start: usize,
4407 class_unit: &CodeUnit,
4408 scope: &ScopeInfo,
4409 ancestry: &ParentIndex<'tree>,
4410 ) {
4411 let member_scope = ScopeInfo {
4412 package_name: class_unit.package_name().to_string(),
4413 module: scope.module.clone(),
4414 class_unit: Some(class_unit.clone()),
4415 template_signature: scope.template_signature.clone(),
4416 template_metadata: None,
4417 declarations_are_fields: true,
4418 recovered_specialization_member_scope: false,
4419 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4420 };
4421 let mut stack = vec![root];
4422 while let Some(current) = stack.pop() {
4423 if current.kind() == "comment" || current.start_byte() >= constructor_start {
4424 continue;
4425 }
4426 if current.end_byte() <= constructor_start
4427 && current.kind() != "translation_unit"
4428 && current.kind() != "labeled_statement"
4429 && current.kind() != "ERROR"
4430 {
4431 let mut work_stack = Vec::new();
4432 self.visit_node(current, &member_scope, &mut work_stack, ancestry);
4433 while let Some(work) = work_stack.pop() {
4434 match work {
4435 CppWork::Container(container) => {
4436 push_cpp_container_work(
4437 container.node,
4438 container.scope,
4439 &mut work_stack,
4440 );
4441 }
4442 CppWork::Siblings(siblings) => {
4443 advance_cpp_siblings(siblings, self.source, &mut work_stack);
4444 }
4445 CppWork::Node(work) => {
4446 self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
4447 }
4448 }
4449 }
4450 continue;
4451 }
4452 if matches!(
4453 current.kind(),
4454 "translation_unit" | "labeled_statement" | "ERROR"
4455 ) {
4456 let mut cursor = current.walk();
4457 stack.extend(current.named_children(&mut cursor));
4458 }
4459 }
4460 }
4461
4462 fn visit_node<'tree>(
4463 &mut self,
4464 node: Node<'tree>,
4465 scope: &ScopeInfo,
4466 stack: &mut Vec<CppWork<'tree>>,
4467 ancestry: &ParentIndex<'tree>,
4468 ) {
4469 if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
4470 self.visit_node(node, &recovered_scope, stack, ancestry);
4471 return;
4472 }
4473 if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
4474 self.visit_node(node, &recovered_scope, stack, ancestry);
4475 return;
4476 }
4477 if node.kind() == "function_definition" && node.has_error() {
4483 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4484 }
4485 if let Some(FragmentedClassRecovery {
4486 declaration_node: class_node,
4487 name,
4488 raw_supertypes,
4489 body: fragmented,
4490 }) = fragmented_class_body(node, self.source)
4491 {
4492 let displaced_namespace_items =
4493 displaced_fragment_namespace_geometry(node, self.source)
4494 .map(|boundary| boundary.namespace_items)
4495 .unwrap_or_default();
4496 let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
4497 let mut class_stack = Vec::new();
4498 let parser_visible_body =
4501 (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
4502 .then(|| cpp_body_node(class_node))
4503 .flatten();
4504 let class_unit = self.visit_named_class_like_shape(
4505 class_node,
4506 name,
4507 parser_visible_body,
4508 true,
4509 Some(fragmented.class_range),
4510 Some(raw_supertypes),
4511 scope,
4512 &mut class_stack,
4513 ancestry,
4514 );
4515 let member_scope = ScopeInfo {
4516 package_name: class_unit.package_name().to_string(),
4517 module: scope.module.clone(),
4518 class_unit: Some(class_unit.clone()),
4519 template_signature: scope.template_signature.clone(),
4520 template_metadata: None,
4521 declarations_are_fields: true,
4522 recovered_specialization_member_scope: false,
4523 visible_using_namespaces: scope.visible_using_namespaces.clone(),
4524 };
4525 let complete = outcome.is_some_and(|outcome| {
4526 self.visit_fragmented_export_class_members(outcome, class_unit, scope)
4527 });
4528 if complete {
4529 self.consumed_fragment_regions
4530 .push((node.start_byte(), fragmented.class_range.end_byte));
4531 } else {
4532 for candidate in cpp_following_named_siblings(node, self.source) {
4542 if candidate.start_byte() >= fragmented.reparse_end {
4543 break;
4544 }
4545 if cpp_fragment_sibling_is_class_member(
4546 candidate,
4547 fragmented.reparse_end,
4548 self.source,
4549 ) {
4550 self.recovered_class_sibling_scopes
4551 .insert(candidate.id(), member_scope.clone());
4552 }
4553 }
4554 }
4555 for item in displaced_namespace_items {
4556 self.recovered_class_sibling_scopes
4557 .insert(item.id(), scope.clone());
4558 }
4559 stack.extend(class_stack);
4560 return;
4561 }
4562 if self.visit_folded_aggregate(node, scope) {
4563 return;
4564 }
4565 match node.kind() {
4566 "template_declaration" => {
4567 if let Some(recovered) =
4568 recover_fragmented_preprocessor_class(node, self.source, ancestry)
4569 {
4570 let mut template_scope = scope.clone();
4571 template_scope.template_signature =
4572 cpp_template_signature(node, recovered.declaration_node, self.source);
4573 template_scope.template_metadata =
4574 cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
4575 let raw_supertypes =
4576 Some(extract_cpp_supertypes(recovered.class_node, self.source));
4577 let mut class_stack = Vec::new();
4578 let class_unit = self.visit_named_class_like_shape(
4579 recovered.class_node,
4580 recovered.name,
4581 Some(recovered.body),
4582 true,
4583 Some(recovered.range),
4584 raw_supertypes,
4585 &template_scope,
4586 &mut class_stack,
4587 ancestry,
4588 );
4589 self.parsed.record_materialization(
4590 MaterializationRecord::RecoveredDeclaration {
4591 recovery: recovered.range,
4592 unit: class_unit.clone(),
4593 },
4594 );
4595 let member_scope = ScopeInfo {
4596 package_name: template_scope.package_name.clone(),
4597 module: template_scope.module.clone(),
4598 class_unit: Some(class_unit.clone()),
4599 template_signature: template_scope.template_signature.clone(),
4600 template_metadata: None,
4601 declarations_are_fields: true,
4602 recovered_specialization_member_scope: recovered
4603 .class_node
4604 .child_by_field_name("name")
4605 .is_some_and(|name| name.kind() == "template_type"),
4606 visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
4607 };
4608 for tail_member in recovered.tail_members.into_iter().rev() {
4609 stack.push(CppWork::Node(CppNodeWork {
4610 node: tail_member,
4611 scope: member_scope.clone(),
4612 }));
4613 }
4614 stack.extend(class_stack);
4615 for sibling in recovered.member_siblings {
4616 self.recovered_class_sibling_scopes
4617 .insert(sibling.id(), member_scope.clone());
4618 }
4619 return;
4620 }
4621 for index in (0..node.named_child_count()).rev() {
4622 let Some(child) = node.named_child(index) else {
4623 continue;
4624 };
4625 if matches!(
4626 child.kind(),
4627 "class_specifier"
4628 | "struct_specifier"
4629 | "union_specifier"
4630 | "enum_specifier"
4631 | "function_definition"
4632 | "declaration"
4633 | "field_declaration"
4634 | "alias_declaration"
4635 | "namespace_definition"
4636 ) {
4637 let mut template_scope = scope.clone();
4638 template_scope.template_signature =
4639 cpp_template_signature(node, child, self.source);
4640 template_scope.template_metadata =
4641 cpp_template_metadata(node, child, self.source, ancestry);
4642 if let Some(recovered) = recover_fragmented_partial_specialization(
4643 node,
4644 child,
4645 self.source,
4646 ancestry,
4647 ) {
4648 let code_unit = self.visit_named_class_like_shape(
4649 recovered.declaration_node,
4650 recovered.name,
4651 None,
4652 true,
4653 Some(recovered.range),
4654 None,
4655 &template_scope,
4656 stack,
4657 ancestry,
4658 );
4659 self.parsed.record_materialization(
4660 MaterializationRecord::RecoveredDeclaration {
4661 recovery: recovered.range,
4662 unit: code_unit.clone(),
4663 },
4664 );
4665 let mut member_scope = template_scope.clone();
4666 member_scope.class_unit = Some(code_unit);
4667 member_scope.declarations_are_fields = true;
4668 member_scope.recovered_specialization_member_scope = true;
4669 for prefix_member in recovered.prefix_members.into_iter().rev() {
4670 stack.push(CppWork::Node(CppNodeWork {
4671 node: prefix_member,
4672 scope: member_scope.clone(),
4673 }));
4674 }
4675 for sibling in recovered.member_siblings {
4676 self.recovered_class_sibling_scopes
4677 .insert(sibling.id(), member_scope.clone());
4678 }
4679 for following in recovered.following_declarations.into_iter().rev() {
4680 stack.push(CppWork::Node(CppNodeWork {
4681 node: following,
4682 scope: scope.clone(),
4683 }));
4684 }
4685 return;
4686 }
4687 stack.push(CppWork::Node(CppNodeWork {
4688 node: child,
4689 scope: template_scope,
4690 }));
4691 }
4692 }
4693 }
4694 "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
4695 "linkage_specification" => {
4696 if let Some(body) = cpp_body_node(node) {
4697 stack.push(CppWork::Container(CppContainer {
4698 node: body,
4699 scope: scope.clone(),
4700 }));
4701 } else {
4702 stack.push(CppWork::Container(CppContainer {
4703 node,
4704 scope: scope.clone(),
4705 }));
4706 }
4707 }
4708 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
4709 self.visit_class_like(node, scope, stack, ancestry)
4710 }
4711 "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
4712 "ERROR" => {
4719 self.visit_object_macro_error_classes(node, scope);
4720 if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4721 self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4722 if self.visit_collapsed_macro_declaration_run(node, scope) {
4723 return;
4724 }
4725 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4726 return;
4727 }
4728 self.visit_macro_swallowed_function_declarations(node, scope);
4729 self.visit_macro_wrapped_declarations(node, scope, ancestry);
4730 self.visit_stranded_class_members(node, scope, ancestry);
4731 stack.push(CppWork::Container(CppContainer {
4732 node,
4733 scope: scope.clone(),
4734 }));
4735 }
4736 }
4737 "declaration" => {
4738 if node.has_error() {
4739 if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4746 return;
4747 }
4748 self.visit_prototype_macro_declarations(node, scope);
4749 if self.node_is_inside_consumed_fragment(node) {
4750 return;
4757 }
4758 }
4759 if scope.class_unit.is_some()
4760 && scope.declarations_are_fields
4761 && scope.recovered_specialization_member_scope
4762 && let Some(alias_name) =
4763 recovered_using_declaration_alias_name(node, self.source)
4764 {
4765 self.add_type_aliases(node, scope, vec![alias_name], ancestry);
4766 } else {
4767 self.visit_declaration(
4768 node,
4769 scope,
4770 scope.declarations_are_fields,
4771 stack,
4772 ancestry,
4773 )
4774 }
4775 }
4776 "expression_statement" => {
4790 if node.has_error() {
4791 self.visit_prototype_macro_declarations(node, scope);
4792 }
4793 }
4794 "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
4795 "preproc_call" => self.visit_preproc_call(node, scope),
4796 "type_definition" | "alias_declaration" => {
4797 self.visit_type_declaration(node, scope, stack, ancestry)
4798 }
4799 "preproc_def" | "preproc_function_def" => self.visit_macro(node),
4800 "preproc_include" => {}
4806 kind if preserves_declaration_scope_through_wrapper(
4807 kind,
4808 scope.class_unit.is_some(),
4809 ) =>
4810 {
4811 if kind == "labeled_statement" {
4817 self.visit_access_label_constructor(node, scope);
4818 }
4819 if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
4820 let mut range = cpp_declaration_range(node);
4821 if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
4822 range.end_byte = boundary.end_byte;
4823 range.end_line = boundary.end_line;
4824 }
4825 self.parsed.record_materialization(
4826 MaterializationRecord::ConfigurationConditional { range },
4827 );
4828 if node.has_error() {
4829 let mut candidates = vec![node];
4838 while let Some(candidate) = candidates.pop() {
4839 if candidate.kind() == "ERROR"
4850 && !cpp_is_inside_namespace_body(candidate, ancestry)
4851 && self.visit_function_like_export_class_pair(
4852 candidate, scope, stack, ancestry,
4853 )
4854 {
4855 continue;
4856 }
4857 for index in (0..candidate.named_child_count()).rev() {
4858 candidates.push(
4859 candidate
4860 .named_child(index)
4861 .expect("index below the node's own named child count"),
4862 );
4863 }
4864 }
4865 }
4866 }
4867 stack.push(CppWork::Container(CppContainer {
4868 node,
4869 scope: scope.clone(),
4870 }))
4871 }
4872 _ => {
4878 self.visit_collapsed_macro_declaration_run(node, scope);
4879 }
4880 }
4881 }
4882
4883 fn visit_macro_swallowed_function_declarations<'tree>(
4884 &mut self,
4885 envelope: Node<'tree>,
4886 scope: &ScopeInfo,
4887 ) {
4888 if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
4889 || envelope.kind() == "ERROR"
4890 && envelope
4891 .parent()
4892 .is_some_and(|parent| parent.kind() == "ERROR")
4893 {
4894 return;
4895 }
4896 let mut stack = (0..envelope.named_child_count())
4897 .filter_map(|index| envelope.named_child(index))
4898 .collect::<Vec<_>>();
4899 while let Some(node) = stack.pop() {
4900 if node.kind() == "function_declarator" {
4901 self.visit_error_swallowed_function_declaration(node, scope);
4902 }
4903 for child in named_children_iter(node) {
4904 stack.push(child);
4905 }
4906 }
4907 }
4908
4909 fn visit_macro_wrapped_declarations<'tree>(
4913 &mut self,
4914 envelope: Node<'tree>,
4915 scope: &ScopeInfo,
4916 ancestry: &ParentIndex<'tree>,
4917 ) {
4918 let recovered = macro_wrapped_declarations(envelope, self.source);
4919 if recovered.is_empty() {
4920 return;
4921 }
4922 let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
4923 self.record_recovered_declarations(recovery, |visitor| {
4924 for declaration in recovered {
4925 visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
4926 }
4927 });
4928 }
4929
4930 fn visit_collapsed_macro_declaration_run(
4957 &mut self,
4958 envelope: Node<'_>,
4959 scope: &ScopeInfo,
4960 ) -> bool {
4961 let Some(run) = collapsed_macro_declaration_run(envelope, self.source) else {
4962 return false;
4963 };
4964 let start = envelope.start_byte();
4965 let end = run.region_end;
4966 let recovery = cpp_recovery_window(self.source, start, end);
4967 self.record_recovered_declarations(recovery, |visitor| {
4968 let mut position = start;
4969 while position < end {
4970 let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
4971 return;
4972 };
4973 let root = tree.root_node();
4974 let ancestry = ParentIndex::new(root);
4977 let mut cursor = root.walk();
4978 let collapsed =
4979 root.named_children(&mut cursor)
4980 .enumerate()
4981 .find_map(|(index, item)| {
4982 collapsed_macro_declaration_run(item, visitor.source)
4983 .map(|run| (index, item, run))
4984 });
4985 let mut stack = Vec::new();
4990 push_cpp_sibling_range(
4991 root,
4992 0,
4993 collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
4994 scope.clone(),
4995 &mut stack,
4996 );
4997 visitor.drain_cpp_work(stack, &ancestry);
4998 let Some((_, item, run)) = collapsed else {
4999 return;
5000 };
5001 if let Some(head) =
5005 cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
5006 {
5007 let head_root = head.root_node();
5008 visitor.run_container_work(
5009 head_root,
5010 scope.clone(),
5011 &ParentIndex::new(head_root),
5012 );
5013 }
5014 assert!(
5015 run.invocation_end > position,
5016 "a collapsed run at {position} must end after the byte the scan resumed \
5017 from, but ended at {}",
5018 run.invocation_end
5019 );
5020 position = run.invocation_end;
5021 }
5022 });
5023 self.consumed_fragment_regions.push((start, end));
5024 true
5025 }
5026
5027 fn visit_stranded_class_members<'tree>(
5037 &mut self,
5038 node: Node<'tree>,
5039 scope: &ScopeInfo,
5040 ancestry: &ParentIndex<'tree>,
5041 ) {
5042 if scope.class_unit.is_none() || !scope.declarations_are_fields {
5043 return;
5044 }
5045 for member in stranded_declaration_run(node, self.source).declarations {
5046 self.add_macro_wrapped_declaration(member, scope, ancestry);
5047 }
5048 }
5049
5050 fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5058 let Some(class_unit) = scope.class_unit.clone() else {
5059 return;
5060 };
5061 if !scope.declarations_are_fields {
5062 return;
5063 }
5064 let class_name = class_unit.identifier().to_string();
5065 let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
5066 else {
5067 return;
5068 };
5069 let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
5070 return;
5071 };
5072 let root = tree.root_node();
5073 let Some(declarator) =
5074 cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
5075 else {
5076 return;
5077 };
5078 let reparsed_ancestry = ParentIndex::new(root);
5079 let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
5080 let range = cpp_declaration_range(definition.unwrap_or(declarator));
5081 let recovery = cpp_recovery_window(self.source, start, node.end_byte());
5082 self.record_recovered_declarations(recovery, |visitor| {
5083 visitor.add_macro_wrapped_declaration(
5084 MacroWrappedDeclaration {
5085 declarator,
5086 range,
5087 is_static: false,
5088 },
5089 scope,
5090 &reparsed_ancestry,
5091 );
5092 });
5093 }
5094
5095 fn add_macro_wrapped_declaration<'tree>(
5096 &mut self,
5097 declaration: MacroWrappedDeclaration<'tree>,
5098 scope: &ScopeInfo,
5099 ancestry: &ParentIndex<'tree>,
5100 ) {
5101 let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
5102 else {
5103 return;
5104 };
5105 let code_unit =
5106 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
5107 if self.parsed.contains_declaration(&code_unit) {
5108 self.parsed
5109 .record_navigation_range(code_unit, declaration.range);
5110 return;
5111 }
5112 self.add_declaration_with_range(code_unit.clone(), declaration.range, None, None);
5113 let signature = normalize_cpp_whitespace(
5114 self.source
5115 .get(declaration.range.start_byte..declaration.range.end_byte)
5116 .expect("a recovered declaration range covers one source range"),
5117 );
5118 let linkage = if declaration.is_static {
5119 CallableLinkage::Internal
5120 } else {
5121 cpp_callable_linkage(declaration.declarator, self.source, ancestry)
5122 };
5123 let declaration_only =
5127 cpp_declarator_function_definition(declaration.declarator, ancestry).is_none();
5128 self.parsed.add_signature_with_metadata(
5129 code_unit.clone(),
5130 cpp_signature_metadata(signature, declaration.declarator, self.source, ancestry)
5131 .with_declaration_only(declaration_only)
5132 .with_callable_linkage(linkage),
5133 );
5134 if let Some(parent) = &scope.class_unit {
5135 self.parsed.add_child(parent.clone(), code_unit);
5136 } else if let Some(module) = &scope.module {
5137 self.parsed.add_child(module.clone(), code_unit);
5138 }
5139 }
5140
5141 fn visit_c_anonymous_local_aggregates_in_function<'tree>(
5146 &mut self,
5147 function: Node<'tree>,
5148 scope: &ScopeInfo,
5149 stack: &mut Vec<CppWork<'tree>>,
5150 ancestry: &ParentIndex<'tree>,
5151 ) {
5152 if !self.c_tag_semantics || scope.class_unit.is_some() {
5153 return;
5154 }
5155 let Some(body) = cpp_body_node(function) else {
5156 return;
5157 };
5158 let mut pending = vec![body];
5159 while let Some(node) = pending.pop() {
5160 if matches!(node.kind(), "function_definition" | "lambda_expression") {
5161 continue;
5162 }
5163 if node.kind() == "declaration"
5164 && self.visit_c_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
5165 {
5166 continue;
5167 }
5168 if matches!(
5169 node.kind(),
5170 "class_specifier" | "struct_specifier" | "union_specifier"
5171 ) {
5172 continue;
5173 }
5174 let mut cursor = node.walk();
5175 let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
5176 children.reverse();
5177 pending.extend(children);
5178 }
5179 }
5180
5181 fn visit_error_swallowed_function_declaration<'tree>(
5182 &mut self,
5183 node: Node<'tree>,
5184 scope: &ScopeInfo,
5185 ) -> bool {
5186 let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
5187 return false;
5188 };
5189 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
5190 return false;
5191 };
5192 let root = tree.root_node();
5193 let mut cursor = root.walk();
5194 let declarations = root
5195 .named_children(&mut cursor)
5196 .filter(|child| child.kind() != "comment")
5197 .collect::<Vec<_>>();
5198 let [declaration] = declarations.as_slice() else {
5199 return false;
5200 };
5201 if declaration.kind() != "declaration"
5202 || declaration.has_error()
5203 || declaration.start_byte() != start
5204 || declaration.end_byte() != end
5205 {
5206 return false;
5207 }
5208 let recovery = cpp_recovery_window(self.source, start, end);
5209 let reparsed_ancestry = ParentIndex::new(root);
5211 self.record_recovered_declarations(recovery, |visitor| {
5212 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5213 });
5214 true
5215 }
5216
5217 fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5240 for candidate in cpp_prototype_macro_candidates(node, self.source) {
5241 let start = candidate.run_start;
5242 let end = candidate.semicolon_end;
5243 if self.byte_range_is_inside_consumed_fragment(start, end) {
5244 continue;
5245 }
5246 let Some(tree) = parse_source_ranges_with_cancellation(
5247 &tree_sitter_cpp::LANGUAGE.into(),
5248 self.source,
5249 &candidate.ranges(),
5250 None,
5251 ) else {
5252 continue;
5253 };
5254 let root = tree.root_node();
5255 let mut cursor = root.walk();
5256 let declarations = root
5257 .named_children(&mut cursor)
5258 .filter(|child| child.kind() != "comment")
5259 .collect::<Vec<_>>();
5260 let [declaration] = declarations.as_slice() else {
5261 continue;
5262 };
5263 if declaration.kind() != "declaration"
5264 || declaration.has_error()
5265 || declaration.start_byte() != start
5266 || declaration.end_byte() != end
5267 || declaration
5268 .child_by_field_name("declarator")
5269 .and_then(extract_function_declarator)
5270 .is_none()
5271 {
5272 continue;
5273 }
5274 let recovery = cpp_recovery_window(self.source, start, end);
5275 let reparsed_ancestry = ParentIndex::new(root);
5278 self.record_recovered_declarations(recovery, |visitor| {
5279 visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5280 });
5281 self.consumed_fragment_regions.push((start, end));
5282 }
5283 }
5284
5285 fn declare_namespace_levels(
5289 &mut self,
5290 mut package_name: String,
5291 components: Vec<String>,
5292 node: Node<'_>,
5293 ) -> (String, Option<CodeUnit>) {
5294 let mut module = None;
5295 for component in components {
5296 let full_name = if package_name.is_empty() {
5297 component
5298 } else {
5299 format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
5300 };
5301 let level = CodeUnit::new_fq(
5302 self.file.clone(),
5303 CodeUnitType::Module,
5304 "",
5305 full_name.clone(),
5306 cpp_namespace_fq(&full_name),
5307 );
5308 if !self.parsed.contains_declaration(&level) {
5309 self.add_declaration(level.clone(), node, None, None);
5310 }
5311 package_name = full_name;
5312 module = Some(level);
5313 }
5314 (package_name, module)
5315 }
5316
5317 fn recovered_namespace_scope(
5322 &mut self,
5323 node: Node<'_>,
5324 scope: &ScopeInfo,
5325 ) -> Option<ScopeInfo> {
5326 self.orphaned_namespaces.region_at(node.start_byte())?;
5327 let components = self
5328 .orphaned_namespaces
5329 .enclosing_namespace_components(node, self.source);
5330 let package_name = components.join(CPP_PACKAGE_SEPARATOR);
5331 if package_name == scope.package_name {
5332 return None;
5333 }
5334 let (package_name, module) = self.declare_namespace_levels(String::new(), components, node);
5335 Some(ScopeInfo {
5336 package_name,
5337 module,
5338 class_unit: None,
5343 template_signature: None,
5344 template_metadata: None,
5345 declarations_are_fields: false,
5346 recovered_specialization_member_scope: false,
5347 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5348 })
5349 }
5350
5351 fn visit_namespace<'tree>(
5352 &mut self,
5353 node: Node<'tree>,
5354 scope: &ScopeInfo,
5355 stack: &mut Vec<CppWork<'tree>>,
5356 ancestry: &ParentIndex<'tree>,
5357 ) {
5358 let name_node = node.child_by_field_name("name");
5359 let Some(name_node) = name_node else {
5360 if let Some(body) = cpp_body_node(node) {
5361 stack.push(CppWork::Container(CppContainer {
5362 node: body,
5363 scope: scope.clone(),
5364 }));
5365 }
5366 return;
5367 };
5368 let explicitly_global = name_node
5375 .child(0)
5376 .is_some_and(|child| !child.is_named() && child.kind() == "::");
5377 let components = cpp_namespace_name_components(name_node, self.source);
5378 if components.is_empty() {
5379 return;
5380 }
5381 let package_name = if explicitly_global {
5387 String::new()
5388 } else {
5389 scope.package_name.clone()
5390 };
5391 let (package_name, module) = self.declare_namespace_levels(package_name, components, node);
5392
5393 let namespace_scope = ScopeInfo {
5394 package_name,
5395 module,
5396 class_unit: None,
5404 template_signature: scope.template_signature.clone(),
5405 template_metadata: scope.template_metadata.clone(),
5406 declarations_are_fields: false,
5407 recovered_specialization_member_scope: false,
5408 visible_using_namespaces: scope.visible_using_namespaces.clone(),
5409 };
5410 let container = cpp_body_node(node).unwrap_or(node);
5411 let mut candidates = vec![container];
5419 while let Some(candidate) = candidates.pop() {
5420 if matches!(
5421 candidate.kind(),
5422 "ERROR" | "function_definition" | "labeled_statement"
5423 ) && self.visit_embedded_function_like_export_classes(
5424 candidate,
5425 &namespace_scope,
5426 stack,
5427 ancestry,
5428 ) {
5429 continue;
5430 }
5431 for index in (0..candidate.named_child_count()).rev() {
5432 candidates.push(
5433 candidate
5434 .named_child(index)
5435 .expect("index below the node's own named child count"),
5436 );
5437 }
5438 }
5439 stack.push(CppWork::Container(CppContainer {
5440 node: container,
5441 scope: namespace_scope,
5442 }));
5443 }
5444
5445 fn visit_class_like<'tree>(
5446 &mut self,
5447 node: Node<'tree>,
5448 scope: &ScopeInfo,
5449 stack: &mut Vec<CppWork<'tree>>,
5450 ancestry: &ParentIndex<'tree>,
5451 ) {
5452 let Some(name) = class_like_name(node, self.source, ancestry) else {
5453 return;
5454 };
5455 let name = qualified_class_name_chain(node, self.source, scope)
5456 .map(|chain| chain.join("$"))
5457 .unwrap_or(name);
5458 self.visit_named_class_like(node, name, scope, stack, ancestry);
5459 }
5460
5461 fn visit_named_class_like<'tree>(
5462 &mut self,
5463 node: Node<'tree>,
5464 name: String,
5465 scope: &ScopeInfo,
5466 stack: &mut Vec<CppWork<'tree>>,
5467 ancestry: &ParentIndex<'tree>,
5468 ) {
5469 let body = cpp_body_node(node);
5470 let definition_body_present = body.is_some();
5471 let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
5472 .then(|| extract_cpp_supertypes(node, self.source));
5473 self.visit_named_class_like_shape(
5474 node,
5475 name,
5476 body,
5477 definition_body_present,
5478 None,
5479 raw_supertypes,
5480 scope,
5481 stack,
5482 ancestry,
5483 );
5484 }
5485
5486 fn mints_tag_at_enclosing_c_scope(
5494 &self,
5495 declaration_node: Node<'_>,
5496 scope: &ScopeInfo,
5497 ancestry: &ParentIndex<'_>,
5498 ) -> bool {
5499 self.c_tag_semantics
5500 && scope.class_unit.is_some()
5501 && class_like_name(declaration_node, self.source, ancestry).is_some()
5502 && matches!(
5503 declaration_node.kind(),
5504 "struct_specifier" | "union_specifier" | "enum_specifier"
5505 )
5506 }
5507
5508 #[allow(clippy::too_many_arguments)]
5509 fn visit_named_class_like_shape<'tree>(
5510 &mut self,
5511 declaration_node: Node<'tree>,
5512 name: String,
5513 body: Option<Node<'tree>>,
5514 definition_body_present: bool,
5515 explicit_range: Option<Range>,
5516 raw_supertypes: Option<Vec<String>>,
5517 scope: &ScopeInfo,
5518 stack: &mut Vec<CppWork<'tree>>,
5519 ancestry: &ParentIndex<'tree>,
5520 ) -> CodeUnit {
5521 let displaced_macro_tail = if explicit_range.is_none() {
5522 body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
5523 } else {
5524 None
5525 };
5526 let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
5527 let recovered_scope = self.scope_for_recovered_exported_class(
5528 declaration_node,
5529 &name,
5530 definition_body_present,
5531 scope,
5532 ancestry,
5533 );
5534 let c_tag_scope;
5544 let scope =
5545 if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
5546 c_tag_scope = ScopeInfo {
5547 class_unit: None,
5548 ..recovered_scope.clone()
5549 };
5550 &c_tag_scope
5551 } else {
5552 &recovered_scope
5553 };
5554 let short_name = if let Some(parent) = &scope.class_unit {
5555 cpp_join_nested_short(parent.short_name(), &name)
5556 } else {
5557 name.clone()
5558 };
5559 let qualified_chain = if scope.class_unit.is_none() {
5566 qualified_class_name_chain(declaration_node, self.source, scope)
5567 .filter(|chain| chain.join("$") == name)
5568 } else {
5569 None
5570 };
5571 let fq = if let Some(chain) = qualified_chain {
5572 let mut fq = FqName::new();
5573 cpp_push_package(&mut fq, &scope.package_name);
5574 let mut first = true;
5575 for component in chain {
5576 let kind = if first {
5577 SegmentKind::Type
5578 } else {
5579 SegmentKind::Nested
5580 };
5581 fq.push(cpp_segment(&component, kind));
5582 first = false;
5583 }
5584 fq
5585 } else {
5586 cpp_leaf_fq(
5587 &scope.package_name,
5588 scope.class_unit.as_ref(),
5589 &name,
5590 SegmentKind::Nested,
5591 SegmentKind::Type,
5592 )
5593 };
5594 let code_unit = CodeUnit::with_signature_and_fq(
5595 self.file.clone(),
5596 CodeUnitType::Class,
5597 scope.package_name.clone(),
5598 short_name,
5599 scope.template_signature.clone(),
5600 false,
5601 fq,
5602 );
5603 let has_body = definition_body_present;
5604 if !has_body && self.parsed.contains_declaration(&code_unit) {
5605 self.parsed.record_navigation_range(
5606 code_unit.clone(),
5607 explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
5608 );
5609 return code_unit;
5610 }
5611 if has_body {
5612 if let Some(range) = explicit_range {
5613 self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
5614 } else {
5615 self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
5616 }
5617 } else {
5618 self.add_declaration(code_unit.clone(), declaration_node, None, None);
5619 }
5620 if let Some(raw_supertypes) = raw_supertypes {
5621 self.parsed
5622 .set_raw_supertypes(code_unit.clone(), raw_supertypes);
5623 }
5624 self.parsed.add_signature(
5625 code_unit.clone(),
5626 render_cpp_type_signature(
5627 declaration_node,
5628 self.source,
5629 scope.template_signature.as_deref(),
5630 ),
5631 );
5632 if let Some(metadata) = &scope.template_metadata {
5633 let primary_short_name = if let Some(parent) = &scope.class_unit {
5634 cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
5635 } else {
5636 metadata.primary_name.clone()
5637 };
5638 let primary_fq_name = CodeUnit::new(
5639 self.file.clone(),
5640 CodeUnitType::Class,
5641 scope.package_name.clone(),
5642 primary_short_name,
5643 )
5644 .fq_name();
5645 let mut metadata = metadata.clone();
5646 metadata.primary_fq_name = primary_fq_name;
5647 self.parsed
5648 .set_cpp_template_metadata(code_unit.clone(), metadata);
5649 }
5650 if let Some(parent) = &scope.class_unit {
5651 self.parsed.add_child(parent.clone(), code_unit.clone());
5652 } else if let Some(module) = &scope.module {
5653 self.parsed.add_child(module.clone(), code_unit.clone());
5654 }
5655
5656 if let Some(body) = body {
5657 let mut nested_scope = scope.clone();
5658 nested_scope.class_unit = Some(code_unit.clone());
5659 nested_scope.template_signature = scope.template_signature.clone();
5660 nested_scope.template_metadata = None;
5665 nested_scope.recovered_specialization_member_scope =
5668 scope.template_metadata.as_ref().is_some_and(|metadata| {
5669 declaration_node.kind() == "function_definition" && metadata.is_specialization()
5670 });
5671 nested_scope.declarations_are_fields =
5672 is_recovered_exported_class_container(declaration_node, self.source)
5673 || nested_scope.recovered_specialization_member_scope;
5674 if let Some(displaced) = displaced_macro_tail {
5675 push_cpp_sibling_range(
5683 body,
5684 displaced.split_index,
5685 usize::MAX,
5686 scope.clone(),
5687 stack,
5688 );
5689 push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
5690 } else {
5691 stack.push(CppWork::Container(CppContainer {
5692 node: body,
5693 scope: nested_scope,
5694 }));
5695 }
5696 }
5697 if declaration_node.kind() == "enum_specifier" {
5698 self.visit_enum_enumerators(declaration_node, scope, &code_unit);
5699 if !self.has_enum_enumerator_units(&code_unit) {
5700 self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
5701 }
5702 }
5703 code_unit
5704 }
5705
5706 fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
5714 if self.field_owners.is_none() {
5715 self.field_owners = Some(CppFieldOwnerIndex::of(
5716 self.parsed.declarations().iter(),
5717 self.file,
5718 ));
5719 }
5720 debug_assert_eq!(
5721 parent.source(),
5722 self.file,
5723 "the walk's declarations are declarations of the file it is walking"
5724 );
5725 let carried = self
5726 .field_owners
5727 .as_ref()
5728 .expect("the index was just ensured")
5729 .owns_fields(parent.package_name(), parent.short_name());
5730
5731 #[cfg(debug_assertions)]
5732 assert_eq!(
5733 carried,
5734 cpp_declarations_hold_owned_fields(
5735 self.parsed.declarations(),
5736 self.file,
5737 parent.package_name(),
5738 parent.short_name()
5739 ),
5740 "the carried-forward field index must answer what a fresh declaration scan \
5741 answers for {}",
5742 parent.fq_name()
5743 );
5744
5745 carried
5746 }
5747
5748 fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
5749 walk_named_tree_preorder(node, false, |child| {
5750 if child.kind() != "enumerator" {
5751 return WalkControl::Continue;
5752 }
5753 let Some(name_node) = child.child_by_field_name("name") else {
5754 return WalkControl::Continue;
5755 };
5756 let name = normalize_cpp_whitespace(node_text(name_node, self.source));
5757 if name.is_empty() {
5758 return WalkControl::Continue;
5759 }
5760 let code_unit = CodeUnit::new_fq(
5761 self.file.clone(),
5762 CodeUnitType::Field,
5763 scope.package_name.clone(),
5764 cpp_join_member_short(parent.short_name(), &name),
5765 parent
5766 .fq()
5767 .clone()
5768 .with_pushed(cpp_segment(&name, SegmentKind::Member)),
5769 );
5770 if self.parsed.contains_declaration(&code_unit) {
5771 return WalkControl::Continue;
5772 }
5773 self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
5774 self.parsed.add_signature(
5775 code_unit,
5776 normalize_cpp_whitespace(node_text(child, self.source)),
5777 );
5778 WalkControl::Continue
5779 });
5780 }
5781
5782 fn visit_enum_enumerators_from_text(
5783 &mut self,
5784 node: Node<'_>,
5785 scope: &ScopeInfo,
5786 parent: &CodeUnit,
5787 ) {
5788 let text = node_text(node, self.source);
5789 let Some((_, body)) = text.split_once('{') else {
5790 return;
5791 };
5792 let Some((body, _)) = body.rsplit_once('}') else {
5793 return;
5794 };
5795 for entry in body.split(',') {
5796 let trimmed = entry.trim();
5797 let name = trimmed
5798 .split('=')
5799 .next()
5800 .unwrap_or("")
5801 .split_whitespace()
5802 .next()
5803 .unwrap_or("");
5804 if name.is_empty() {
5805 continue;
5806 }
5807 let code_unit = CodeUnit::new_fq(
5808 self.file.clone(),
5809 CodeUnitType::Field,
5810 scope.package_name.clone(),
5811 cpp_join_member_short(parent.short_name(), name),
5812 parent
5813 .fq()
5814 .clone()
5815 .with_pushed(cpp_segment(name, SegmentKind::Member)),
5816 );
5817 if self.parsed.contains_declaration(&code_unit) {
5818 continue;
5819 }
5820 self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
5821 self.parsed.add_signature(code_unit, trimmed.to_string());
5822 }
5823 }
5824
5825 fn visit_function_definition<'tree>(
5826 &mut self,
5827 node: Node<'tree>,
5828 scope: &ScopeInfo,
5829 stack: &mut Vec<CppWork<'tree>>,
5830 ancestry: &ParentIndex<'tree>,
5831 ) {
5832 if self.visit_collapsed_macro_declaration_run(node, scope) {
5839 return;
5840 }
5841 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
5847 return;
5848 }
5849 if node.has_error() {
5850 self.visit_macro_swallowed_function_declarations(node, scope);
5851 }
5852 if let Some((class_node, name, raw_supertypes)) =
5853 recover_exported_class_function_definition(node, self.source)
5854 {
5855 if let Some(body) = cpp_body_node(node)
5856 && let Some(close) = self
5857 .orphaned_namespaces
5858 .matching_close_brace(body.start_byte())
5859 && close.end_byte < body.end_byte()
5860 {
5861 let class_range = Range {
5862 start_byte: node.start_byte(),
5863 end_byte: close.end_byte,
5864 start_line: node.start_position().row + 1,
5865 end_line: close.end_line,
5866 };
5867 let mut head = vec![node];
5872 let mut keyword = None;
5873 let mut name_node = None;
5874 while let Some(part) = head.pop() {
5875 if part.start_byte() >= body.start_byte() || part.is_missing() {
5876 continue;
5877 }
5878 if matches!(part.kind(), "class" | "struct" | "union") {
5879 keyword = Some(part);
5880 }
5881 if matches!(part.kind(), "identifier" | "type_identifier")
5882 && node_text(part, self.source) == name
5883 {
5884 name_node = Some(part);
5885 }
5886 let mut cursor = part.walk();
5887 head.extend(part.children(&mut cursor));
5888 }
5889 if let (Some(keyword), Some(name_node)) = (keyword, name_node)
5890 && let Some(type_name) = keyword
5891 .parent()
5892 .and_then(|parent| parent.child_by_field_name("name"))
5893 && let Some(tree) = parse_source_ranges_with_cancellation(
5894 &tree_sitter_cpp::LANGUAGE.into(),
5895 self.source,
5896 &[
5897 (keyword.start_byte(), type_name.start_byte()),
5898 (name_node.start_byte(), name_node.end_byte()),
5899 (body.start_byte(), close.end_byte),
5900 ],
5901 None,
5902 )
5903 && let Some(reparsed_class) = tree.root_node().named_child(0)
5904 && let Some(class_body) = cpp_body_node(reparsed_class)
5905 && class_body.start_byte() == body.start_byte()
5906 && class_body.end_byte() == close.end_byte
5907 && let Some(tail) =
5908 cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
5909 {
5910 let class_unit = self.visit_named_class_like_shape(
5911 class_node,
5912 name,
5913 None,
5914 true,
5915 Some(class_range),
5916 raw_supertypes,
5917 scope,
5918 stack,
5919 ancestry,
5920 );
5921 self.parsed.record_materialization(
5922 MaterializationRecord::RecoveredDeclaration {
5923 recovery: class_range,
5924 unit: class_unit.clone(),
5925 },
5926 );
5927 let member_scope = ScopeInfo {
5928 package_name: class_unit.package_name().to_string(),
5929 class_unit: Some(class_unit),
5930 declarations_are_fields: true,
5931 template_metadata: None,
5932 recovered_specialization_member_scope: false,
5933 ..scope.clone()
5934 };
5935 let class_body_range = class_body.byte_range();
5936 let tail_range = tail.root_node().byte_range();
5937 self.partitioned_regions
5938 .push((tail, tail_range, scope.clone()));
5939 self.partitioned_regions
5940 .push((tree, class_body_range, member_scope));
5941 return;
5942 }
5943 }
5944 let body = cpp_body_node(class_node);
5945 let displaced_namespace = cpp_body_node(node)
5946 .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
5947 let fragmented = cpp_body_node(node).and_then(|body| {
5948 fragmented_export_function_body_region(
5949 node,
5950 body,
5951 self.source,
5952 displaced_namespace.as_ref(),
5953 )
5954 });
5955 if let Some(fragmented) = fragmented {
5961 if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
5965 .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
5966 {
5967 let mut boundary_scope = scope.clone();
5968 for sibling in cpp_following_named_siblings(node, self.source) {
5969 if sibling.start_byte() >= boundary.start_byte() {
5970 break;
5971 }
5972 if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
5973 boundary_scope.visible_using_namespaces.push(namespace);
5974 }
5975 }
5976 self.recovered_class_sibling_scopes
5977 .insert(boundary.id(), boundary_scope);
5978 }
5979 let mut recovered_constructor = None;
5980 let mut recovered_prefix_tree = None;
5981 let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
5982 {
5983 Some(FragmentedExportMembers::Complete(tree)) => {
5984 if let Some(body) = body
5985 && let Some(range) =
5986 cpp_reparsed_synthetic_initializer_constructor_range(
5987 tree.root_node(),
5988 &name,
5989 self.source,
5990 body.end_byte(),
5991 )
5992 {
5993 recovered_constructor = Some(range);
5994 recovered_prefix_tree = Some(tree);
5995 None
5996 } else {
5997 Some(FragmentedExportMembers::Complete(tree))
5998 }
5999 }
6000 outcome => outcome,
6001 };
6002 let mut class_stack = Vec::new();
6003 let class_unit = self.visit_named_class_like_shape(
6004 class_node,
6005 name,
6006 None,
6007 true,
6008 Some(fragmented.class_range),
6009 raw_supertypes,
6010 scope,
6011 &mut class_stack,
6012 ancestry,
6013 );
6014 self.parsed
6015 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6016 recovery: fragmented.class_range,
6017 unit: class_unit.clone(),
6018 });
6019 let complete = outcome.is_some_and(|outcome| {
6020 self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
6021 });
6022 if complete {
6023 self.consumed_fragment_regions
6024 .push((node.start_byte(), fragmented.class_range.end_byte));
6025 } else {
6026 let member_scope = ScopeInfo {
6035 package_name: class_unit.package_name().to_string(),
6036 module: scope.module.clone(),
6037 class_unit: Some(class_unit.clone()),
6038 template_signature: scope.template_signature.clone(),
6039 template_metadata: None,
6040 declarations_are_fields: true,
6041 recovered_specialization_member_scope: false,
6042 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6043 };
6044 for candidate in cpp_following_named_siblings(node, self.source) {
6045 if candidate.start_byte() >= fragmented.reparse_end {
6046 break;
6047 }
6048 if cpp_fragment_sibling_is_class_member(
6049 candidate,
6050 fragmented.reparse_end,
6051 self.source,
6052 ) {
6053 self.recovered_class_sibling_scopes
6054 .insert(candidate.id(), member_scope.clone());
6055 }
6056 }
6057 if let Some(range) = recovered_constructor
6058 && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
6059 {
6060 self.visit_recovered_fragment_prefix_members(
6061 prefix_tree.root_node(),
6062 range.start,
6063 &class_unit,
6064 scope,
6065 ancestry,
6066 );
6067 self.visit_recovered_fragment_constructor(
6068 range,
6069 body,
6070 class_node,
6071 &class_unit,
6072 scope,
6073 ancestry,
6074 );
6075 }
6076 }
6077 if let Some(boundary) = displaced_namespace {
6078 for item in boundary.namespace_items {
6079 self.recovered_class_sibling_scopes
6080 .insert(item.id(), scope.clone());
6081 }
6082 }
6083 stack.extend(class_stack);
6084 return;
6085 }
6086 let mut stack = Vec::new();
6087 let class_unit = self.visit_named_class_like_shape(
6088 class_node,
6089 name,
6090 body,
6091 body.is_some(),
6092 None,
6093 raw_supertypes,
6094 scope,
6095 &mut stack,
6096 ancestry,
6097 );
6098 self.parsed
6099 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6100 recovery: cpp_declaration_range(node),
6101 unit: class_unit,
6102 });
6103 if let Some(body) = body
6110 && let Some(class_close) = self
6111 .orphaned_namespaces
6112 .matching_close_brace(body.start_byte())
6113 && class_close.start_byte < body.end_byte()
6114 {
6115 let split = {
6116 let mut cursor = body.walk();
6117 body.named_children(&mut cursor)
6118 .position(|child| child.start_byte() > class_close.start_byte)
6119 };
6120 if let Some(split) = split {
6121 let seeded = stack.pop();
6126 match seeded {
6127 Some(CppWork::Container(container)) => {
6128 push_cpp_sibling_range(
6129 body,
6130 split,
6131 usize::MAX,
6132 scope.clone(),
6133 &mut stack,
6134 );
6135 push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
6136 }
6137 _ => unreachable!("exported-class seed is always one Container"),
6140 }
6141 }
6142 }
6143 while let Some(work) = stack.pop() {
6144 match work {
6145 CppWork::Container(container) => {
6146 push_cpp_container_work(container.node, container.scope, &mut stack);
6147 }
6148 CppWork::Siblings(siblings) => {
6149 advance_cpp_siblings(siblings, self.source, &mut stack);
6150 }
6151 CppWork::Node(work) => {
6152 self.visit_node(work.node, &work.scope, &mut stack, ancestry)
6153 }
6154 }
6155 }
6156 return;
6157 }
6158 let recovered_constraint_constructor =
6159 cpp_recovered_template_macro_constructor(node, self.source);
6160 let declarator = recovered_constraint_constructor
6161 .map(|(declarator, _)| declarator)
6162 .or_else(|| node.child_by_field_name("declarator"));
6163 let Some(declarator) = declarator else {
6164 self.visit_malformed_function_definition_container(node, scope, stack);
6165 return;
6166 };
6167 let Some(function_declarator) = extract_function_declarator(declarator) else {
6168 self.visit_malformed_function_definition_container(node, scope, stack);
6169 return;
6170 };
6171 let function = if let Some((_, callable_name)) =
6172 cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
6173 {
6174 extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
6175 } else {
6176 extract_function_info(function_declarator, self.source, scope)
6177 };
6178 let Some(mut function) = function else {
6179 self.visit_malformed_function_definition_container(node, scope, stack);
6180 return;
6181 };
6182 if let Some((_, template_parameter)) = recovered_constraint_constructor {
6183 function.signature = format!(
6184 "template <{}>{}",
6185 normalize_cpp_whitespace(node_text(template_parameter, self.source)),
6186 function.signature
6187 );
6188 }
6189 let code_unit = function.code_unit(self.file.clone());
6190 self.add_declaration(code_unit.clone(), node, None, None);
6195 let signature = if recovered_constraint_constructor.is_some() {
6196 normalize_cpp_whitespace(node_text(function_declarator, self.source))
6197 } else {
6198 render_cpp_function_display_signature_from_node(
6199 node,
6200 self.source,
6201 scope.template_signature.as_deref(),
6202 true,
6203 ancestry,
6204 )
6205 };
6206 self.parsed.add_signature_with_metadata(
6207 code_unit.clone(),
6208 cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
6209 .with_declaration_only(false)
6210 .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
6211 );
6212 if let Some(parent) = &scope.class_unit {
6213 self.parsed.add_child(parent.clone(), code_unit);
6214 } else if let Some(module) = &scope.module {
6215 self.parsed.add_child(module.clone(), code_unit);
6216 }
6217 self.visit_c_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
6218 }
6219
6220 fn scope_for_recovered_exported_class<'tree>(
6225 &mut self,
6226 node: Node<'tree>,
6227 name: &str,
6228 definition_body_present: bool,
6229 scope: &ScopeInfo,
6230 ancestry: &ParentIndex<'tree>,
6231 ) -> ScopeInfo {
6232 if !definition_body_present
6233 || !scope.package_name.is_empty()
6234 || scope.class_unit.is_some()
6235 || !(is_recovered_exported_class_container(node, self.source)
6236 || recover_function_like_export_class_pair(node, self.source).is_some()
6237 || recover_embedded_function_like_export_classes(node, self.source)
6238 .iter()
6239 .any(|recovered| recovered.name == name)
6240 || matches!(node.kind(), "declaration" | "field_declaration")
6241 && recover_exported_class_declaration(node, self.source).is_some()
6242 || matches!(
6243 node.kind(),
6244 "class_specifier" | "struct_specifier" | "union_specifier"
6245 ) && (node.child_by_field_name("name").is_some_and(|name_node| {
6246 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
6247 name_node,
6248 self.source,
6249 )))
6250 }) || ancestry.parent(node).is_some_and(|parent| {
6251 matches!(parent.kind(), "declaration" | "field_declaration")
6252 && recover_exported_class_declaration(parent, self.source).is_some()
6253 || is_recovered_exported_class_container(parent, self.source)
6254 })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
6255 {
6256 return scope.clone();
6257 }
6258 let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
6259 let Some(package_name) = borrowed_namespace
6260 .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
6261 else {
6262 return scope.clone();
6263 };
6264
6265 let module = CodeUnit::new_fq(
6266 self.file.clone(),
6267 CodeUnitType::Module,
6268 "",
6269 package_name.clone(),
6270 cpp_namespace_fq(&package_name),
6271 );
6272 let mut recovered = scope.clone();
6273 recovered.package_name = package_name;
6274 recovered.module = Some(module);
6275 recovered
6276 }
6277
6278 fn unique_earlier_namespace_forward<'tree>(
6287 &mut self,
6288 recovered_node: Node<'tree>,
6289 name: &str,
6290 ancestry: &ParentIndex<'tree>,
6291 ) -> Option<String> {
6292 let mut root = recovered_node;
6293 while let Some(parent) = ancestry.parent(root) {
6294 root = parent;
6295 }
6296 let source = self.source;
6297 let scan = self
6298 .namespace_forward_scans
6299 .entry(CppTreeIdentity::of(root))
6300 .or_default();
6301 scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
6302 let borrowed = scan.unique_earlier_forward(name, recovered_node);
6303
6304 #[cfg(debug_assertions)]
6305 assert_eq!(
6306 borrowed,
6307 unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
6308 "the carried-forward namespace scan must answer what a fresh prefix scan answers \
6309 for {name} at byte {}",
6310 recovered_node.start_byte()
6311 );
6312
6313 borrowed
6314 }
6315
6316 fn visit_malformed_function_definition_container<'tree>(
6317 &mut self,
6318 node: Node<'tree>,
6319 scope: &ScopeInfo,
6320 stack: &mut Vec<CppWork<'tree>>,
6321 ) {
6322 let Some(body) = cpp_body_node(node) else {
6323 return;
6324 };
6325 if !cpp_contains_namespace_definition(body) {
6326 return;
6327 }
6328 stack.push(CppWork::Container(CppContainer {
6329 node: body,
6330 scope: scope.clone(),
6331 }));
6332 }
6333
6334 fn record_recovered_declarations(
6352 &mut self,
6353 recovery: Range,
6354 reparse_walk: impl FnOnce(&mut Self),
6355 ) {
6356 #[cfg(any(debug_assertions, test))]
6359 let before = self.parsed.declarations().clone();
6360
6361 self.recovery_captures.push(CppRecoveryCapture::default());
6362 reparse_walk(self);
6363 let captured = self
6364 .recovery_captures
6365 .pop()
6366 .expect("the capture this call pushed is the one it pops");
6367
6368 let mut minted: Vec<CodeUnit> = captured
6374 .created
6375 .into_iter()
6376 .filter(|unit| self.parsed.contains_declaration(unit))
6377 .collect();
6378 minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6379
6380 #[cfg(any(debug_assertions, test))]
6381 {
6382 let mut rediscovered: Vec<CodeUnit> = self
6383 .parsed
6384 .declarations()
6385 .iter()
6386 .filter(|unit| !before.contains(*unit))
6387 .cloned()
6388 .collect();
6389 rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6390 assert_eq!(
6391 minted, rediscovered,
6392 "the captured recovered set must be the declaration delta of the reparse \
6393 walk over {recovery:?}"
6394 );
6395 }
6396
6397 for unit in minted {
6398 self.parsed
6399 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6400 recovery,
6401 unit,
6402 });
6403 }
6404 }
6405
6406 fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
6409 let start = self
6410 .parsed
6411 .declaration_ranges(unit)
6412 .first()
6413 .map(|range| range.start_byte)
6414 .unwrap_or(usize::MAX);
6415 (start, unit.fq_name().to_string())
6416 }
6417
6418 fn visit_sentinel_macro_region<'tree>(
6419 &mut self,
6420 node: Node<'tree>,
6421 scope: &ScopeInfo,
6422 stack: &mut Vec<CppWork<'tree>>,
6423 ancestry: &ParentIndex<'tree>,
6424 ) -> bool {
6425 if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
6426 return true;
6427 }
6428 if let Some((
6429 reparse_start,
6430 class_start,
6431 body_start,
6432 class_close_start,
6433 class_close_end,
6434 class_close_line,
6435 )) = cpp_sentinel_macro_class_region(node, self.source)
6436 {
6437 let Some(class_tree) =
6438 cpp_reparse_region_items(self.source, reparse_start, class_close_end)
6439 else {
6440 return false;
6441 };
6442 let class_root = class_tree.root_node();
6443 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
6444 let class_ancestry = ParentIndex::new(class_root);
6446 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
6447 class_root,
6448 template_node,
6449 self.source,
6450 &class_ancestry,
6451 ) else {
6452 return false;
6453 };
6454 let class_node = reparsed_class.declaration_node;
6455 let name = reparsed_class.name;
6456 let mut class_scope = scope.clone();
6457 if let Some(template_node) = template_node {
6458 class_scope.template_signature =
6459 cpp_template_signature(template_node, class_node, self.source);
6460 class_scope.template_metadata =
6461 cpp_template_metadata(template_node, class_node, self.source, ancestry);
6462 }
6463 let Some(body_tree) =
6464 cpp_reparse_region_items(self.source, body_start, class_close_start)
6465 else {
6466 return false;
6467 };
6468 let raw_supertypes = reparsed_class.raw_supertypes;
6469 let class_range = Range {
6470 start_byte: class_start,
6471 end_byte: class_close_end,
6472 start_line: class_node.start_position().row + 1,
6473 end_line: class_close_line,
6474 };
6475 let class_scope = self.scope_for_recovered_exported_class(
6476 class_node,
6477 &name,
6478 true,
6479 &class_scope,
6480 ancestry,
6481 );
6482 let mut class_stack = Vec::new();
6483 let class_unit = self.visit_named_class_like_shape(
6484 class_node,
6485 name,
6486 None,
6487 true,
6488 Some(class_range),
6489 raw_supertypes,
6490 &class_scope,
6491 &mut class_stack,
6492 ancestry,
6493 );
6494 self.parsed
6495 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6496 recovery: class_range,
6497 unit: class_unit.clone(),
6498 });
6499 let member_scope = ScopeInfo {
6500 package_name: class_scope.package_name.clone(),
6501 module: class_scope.module.clone(),
6502 class_unit: Some(class_unit),
6503 template_signature: class_scope.template_signature.clone(),
6504 template_metadata: None,
6505 declarations_are_fields: true,
6506 recovered_specialization_member_scope: false,
6507 visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
6508 };
6509 let body_root = body_tree.root_node();
6511 self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
6512 self.consumed_fragment_regions
6515 .push((node.start_byte(), class_close_end));
6516 if node.kind() == "ERROR" && node.end_byte() > class_close_end {
6523 stack.push(CppWork::Container(CppContainer {
6524 node,
6525 scope: scope.clone(),
6526 }));
6527 }
6528 return true;
6529 }
6530 let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
6531 return false;
6532 };
6533 let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
6534 return false;
6535 };
6536 let root = tree.root_node();
6537 if !cpp_reparsed_items_are_indexable(root, self.source) {
6538 return false;
6539 }
6540 let recovery = cpp_recovery_window(self.source, start, end);
6541 let reparsed_ancestry = ParentIndex::new(root);
6543 self.record_recovered_declarations(recovery, |visitor| {
6544 visitor.visit_container(
6545 root,
6546 &reparsed_ancestry,
6547 &scope.package_name,
6548 scope.module.clone(),
6549 scope.class_unit.clone(),
6550 scope.template_signature.clone(),
6551 scope.visible_using_namespaces.clone(),
6552 );
6553 });
6554 if end > node.end_byte() {
6555 self.consumed_fragment_regions
6556 .push((node.start_byte(), end));
6557 } else if node.kind() == "ERROR" && node.end_byte() > end {
6558 self.consumed_fragment_regions
6565 .push((node.start_byte(), end));
6566 stack.push(CppWork::Container(CppContainer {
6567 node,
6568 scope: scope.clone(),
6569 }));
6570 }
6571 true
6572 }
6573
6574 fn visit_nested_namespace_sentinel<'tree>(
6580 &mut self,
6581 node: Node<'tree>,
6582 scope: &ScopeInfo,
6583 ancestry: &ParentIndex<'tree>,
6584 ) -> bool {
6585 let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
6586 return false;
6587 };
6588
6589 let mut package_name = scope.package_name.clone();
6590 let mut module = scope.module.clone();
6591 for component in recovered.namespace_components {
6592 package_name = if package_name.is_empty() {
6593 component
6594 } else {
6595 format!("{package_name}::{component}")
6596 };
6597 let namespace_module = CodeUnit::new_fq(
6598 self.file.clone(),
6599 CodeUnitType::Module,
6600 "",
6601 package_name.clone(),
6602 cpp_namespace_fq(&package_name),
6603 );
6604 if !self.parsed.contains_declaration(&namespace_module) {
6605 self.add_declaration(namespace_module.clone(), recovered.function, None, None);
6606 }
6607 module = Some(namespace_module);
6608 }
6609
6610 let recovered_scope = ScopeInfo {
6611 package_name,
6612 module,
6613 class_unit: None,
6624 template_signature: scope.template_signature.clone(),
6625 template_metadata: scope.template_metadata.clone(),
6626 declarations_are_fields: false,
6627 recovered_specialization_member_scope: false,
6628 visible_using_namespaces: scope.visible_using_namespaces.clone(),
6629 };
6630 if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
6631 recovered.function,
6632 recovered.body,
6633 self.source,
6634 ancestry,
6635 ) {
6636 let mut class_scope = recovered_scope.clone();
6637 if let Some(template_node) = fragmented.template_node {
6638 class_scope.template_signature =
6639 cpp_template_signature(template_node, fragmented.class_node, self.source);
6640 class_scope.template_metadata = cpp_template_metadata(
6641 template_node,
6642 fragmented.class_node,
6643 self.source,
6644 ancestry,
6645 );
6646 }
6647 if let Some(outcome) = self
6648 .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
6649 {
6650 let mut class_stack = Vec::new();
6651 let class_unit = self.visit_named_class_like_shape(
6652 fragmented.class_node,
6653 fragmented.name.clone(),
6654 None,
6655 true,
6656 Some(fragmented.fragmented.class_range),
6657 fragmented.raw_supertypes.clone(),
6658 &class_scope,
6659 &mut class_stack,
6660 ancestry,
6661 );
6662 self.parsed
6663 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6664 recovery: fragmented.fragmented.class_range,
6665 unit: class_unit.clone(),
6666 });
6667 if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
6668 self.consumed_fragment_regions.push((
6669 fragmented.consumed_start,
6670 fragmented.fragmented.class_range.end_byte,
6671 ));
6672 }
6673 }
6674 }
6675 self.run_container_work(recovered.body, recovered_scope, ancestry);
6680 true
6681 }
6682
6683 fn visit_declaration<'tree>(
6684 &mut self,
6685 node: Node<'tree>,
6686 scope: &ScopeInfo,
6687 in_class_body: bool,
6688 stack: &mut Vec<CppWork<'tree>>,
6689 ancestry: &ParentIndex<'tree>,
6690 ) {
6691 if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6692 return;
6693 }
6694 if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
6695 return;
6696 }
6697 if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
6698 !cpp_active_template_type_parameter(
6699 node,
6700 node_text(declarator, self.source),
6701 self.source,
6702 ancestry,
6703 )
6704 }) {
6705 return;
6706 }
6707 if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
6708 self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
6714 return;
6715 }
6716 if in_class_body
6717 && let Some(parent) = scope.class_unit.as_ref()
6718 && let Some(call) =
6719 recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
6720 {
6721 self.visit_recovered_macro_qualified_constructor_definition(
6722 node, call, scope, ancestry,
6723 );
6724 return;
6725 }
6726 if in_class_body
6727 && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
6728 {
6729 self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
6730 return;
6731 }
6732 if in_class_body
6733 && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
6734 {
6735 for member in members {
6736 self.add_macro_wrapped_declaration(member, scope, ancestry);
6737 }
6738 return;
6739 }
6740 if in_class_body
6741 && let Some(declarators) =
6742 recovered_macro_qualified_field_declarators(node, self.source)
6743 {
6744 for declarator in declarators {
6745 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6746 }
6747 return;
6748 }
6749 let recovered_alias_names = recovered_type_alias_names(node, self.source);
6750 if !recovered_alias_names.is_empty() {
6751 self.add_type_aliases(node, scope, recovered_alias_names, ancestry);
6752 return;
6753 }
6754 if self.visit_c_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry)
6755 {
6756 return;
6757 }
6758 if self.visit_c_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
6759 return;
6760 }
6761
6762 if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
6763 if let Some(fragmented) = recovered.fragmented_body.as_ref() {
6764 if let Some(outcome) =
6769 self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
6770 {
6771 let consumed_region = (
6772 recovered.declaration_node.end_byte(),
6773 fragmented.class_range.end_byte,
6774 );
6775 let code_unit = self.visit_named_class_like_shape(
6776 recovered.declaration_node,
6777 recovered.name,
6778 None,
6779 true,
6780 Some(fragmented.class_range),
6781 recovered.raw_supertypes,
6782 scope,
6783 stack,
6784 ancestry,
6785 );
6786 self.parsed.record_materialization(
6787 MaterializationRecord::RecoveredDeclaration {
6788 recovery: fragmented.class_range,
6789 unit: code_unit.clone(),
6790 },
6791 );
6792 let consume_fragment =
6793 self.visit_fragmented_export_class_members(outcome, code_unit, scope);
6794 if consume_fragment {
6800 self.consumed_fragment_regions.push(consumed_region);
6801 }
6802 return;
6803 }
6804 }
6805 let uses_initializer_body = recovered.uses_initializer_body;
6806 let definition_body_present = recovered.body.is_some();
6807 let class_unit = self.visit_named_class_like_shape(
6808 recovered.declaration_node,
6809 recovered.name,
6810 recovered.body,
6811 definition_body_present,
6812 None,
6813 recovered.raw_supertypes,
6814 scope,
6815 stack,
6816 ancestry,
6817 );
6818 self.parsed
6819 .record_materialization(MaterializationRecord::RecoveredDeclaration {
6820 recovery: cpp_declaration_range(node),
6821 unit: class_unit,
6822 });
6823 if uses_initializer_body {
6824 return;
6825 }
6826 }
6827
6828 let mut handled_function = false;
6829 let mut handled_declarator = false;
6830 let mut cursor = node.walk();
6831 for child in node.named_children(&mut cursor) {
6832 if matches!(
6833 child.kind(),
6834 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
6835 ) {
6836 if cpp_body_node(child).is_some() {
6845 self.visit_class_like(child, scope, stack, ancestry);
6846 }
6847 continue;
6848 }
6849 }
6850
6851 let mut cursor = node.walk();
6852 for child in node.children_by_field_name("declarator", &mut cursor) {
6853 if crate::structural::is_recovered_designator_init_declarator(child) {
6854 handled_declarator = true;
6855 continue;
6856 }
6857 if in_class_body
6858 && let Some(field) = recovered_function_like_field_declarator(node, self.source)
6859 {
6860 handled_declarator = true;
6861 self.visit_variable_declaration(node, field.name, scope, true, ancestry);
6862 continue;
6863 }
6864 if let Some(kind) = classify_declarator(child) {
6865 handled_declarator = true;
6866 match kind {
6867 DeclaratorKind::Function(function_declarator) => {
6868 handled_function = true;
6869 self.visit_function_declaration(node, function_declarator, scope, ancestry);
6870 }
6871 DeclaratorKind::Variable(variable_declarator) => {
6872 self.visit_variable_declaration(
6873 node,
6874 variable_declarator,
6875 scope,
6876 in_class_body,
6877 ancestry,
6878 );
6879 }
6880 }
6881 }
6882 }
6883
6884 if !handled_declarator {
6885 let mut cursor = node.walk();
6886 for child in node.named_children(&mut cursor) {
6887 if crate::structural::is_recovered_designator_init_declarator(child) {
6888 handled_declarator = true;
6889 continue;
6890 }
6891 if !is_unfielded_declarator_candidate(child) {
6892 continue;
6893 }
6894 let Some(kind) = classify_declarator(child) else {
6895 continue;
6896 };
6897 handled_declarator = true;
6898 match kind {
6899 DeclaratorKind::Function(function_declarator) => {
6900 handled_function = true;
6901 self.visit_function_declaration(node, function_declarator, scope, ancestry);
6902 }
6903 DeclaratorKind::Variable(variable_declarator) => {
6904 self.visit_variable_declaration(
6905 node,
6906 variable_declarator,
6907 scope,
6908 in_class_body,
6909 ancestry,
6910 );
6911 }
6912 }
6913 }
6914 }
6915
6916 if handled_function {
6917 return;
6918 }
6919
6920 if !handled_declarator {
6921 if in_class_body {
6922 self.visit_class_members_from_declaration(node, scope, ancestry);
6923 } else {
6924 self.visit_global_variables_from_declaration(node, scope, ancestry);
6925 }
6926 }
6927 }
6928
6929 fn visit_c_anonymous_aggregate_declaration<'tree>(
6938 &mut self,
6939 node: Node<'tree>,
6940 scope: &ScopeInfo,
6941 in_class_body: bool,
6942 stack: &mut Vec<CppWork<'tree>>,
6943 ancestry: &ParentIndex<'tree>,
6944 ) -> bool {
6945 if !self.c_tag_semantics || !in_class_body || scope.class_unit.is_none() {
6946 return false;
6947 }
6948 let Some(aggregate) = node.child_by_field_name("type") else {
6949 return false;
6950 };
6951 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
6952 || aggregate.child_by_field_name("name").is_some()
6953 {
6954 return false;
6955 }
6956 let Some(body) = cpp_body_node(aggregate) else {
6957 return false;
6958 };
6959
6960 let mut cursor = node.walk();
6961 let declarators = node
6962 .children_by_field_name("declarator", &mut cursor)
6963 .filter_map(|declarator| match classify_declarator(declarator) {
6964 Some(DeclaratorKind::Variable(variable)) => Some(variable),
6965 Some(DeclaratorKind::Function(_)) | None => None,
6966 })
6967 .collect::<Vec<_>>();
6968 if declarators.is_empty() {
6969 stack.push(CppWork::Container(CppContainer {
6970 node: body,
6971 scope: scope.clone(),
6972 }));
6973 return true;
6974 }
6975
6976 for declarator in declarators {
6977 let Some(name) = extract_variable_name(declarator, self.source) else {
6978 continue;
6979 };
6980 self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6981 self.visit_named_class_like_shape(
6982 aggregate,
6983 name,
6984 Some(body),
6985 true,
6986 None,
6987 None,
6988 scope,
6989 stack,
6990 ancestry,
6991 );
6992 }
6993 true
6994 }
6995
6996 fn visit_c_anonymous_local_aggregate_declaration<'tree>(
7005 &mut self,
7006 node: Node<'tree>,
7007 scope: &ScopeInfo,
7008 stack: &mut Vec<CppWork<'tree>>,
7009 ancestry: &ParentIndex<'tree>,
7010 ) -> bool {
7011 if !self.c_tag_semantics || scope.class_unit.is_some() || !has_function_scope_ancestor(node)
7012 {
7013 return false;
7014 }
7015 let Some(aggregate) = node.child_by_field_name("type") else {
7016 return false;
7017 };
7018 if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7019 || aggregate.child_by_field_name("name").is_some()
7020 {
7021 return false;
7022 }
7023 let Some(body) = cpp_body_node(aggregate) else {
7024 return false;
7025 };
7026 let mut cursor = node.walk();
7027 let declarators = node
7028 .children_by_field_name("declarator", &mut cursor)
7029 .filter_map(|declarator| match classify_declarator(declarator) {
7030 Some(DeclaratorKind::Variable(variable)) => Some(variable),
7031 Some(DeclaratorKind::Function(_)) | None => None,
7032 })
7033 .collect::<Vec<_>>();
7034 if declarators.is_empty() {
7035 return false;
7036 }
7037
7038 for declarator in &declarators {
7039 self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
7040 }
7041 let name = format!("<anonymous:{}>", aggregate.start_byte());
7042 self.visit_named_class_like_shape(
7043 aggregate,
7044 name,
7045 Some(body),
7046 true,
7047 None,
7048 None,
7049 scope,
7050 stack,
7051 ancestry,
7052 );
7053 true
7054 }
7055
7056 fn visit_function_declaration<'tree>(
7057 &mut self,
7058 declaration_node: Node<'tree>,
7059 declarator: Node<'tree>,
7060 scope: &ScopeInfo,
7061 ancestry: &ParentIndex<'tree>,
7062 ) {
7063 let Some(function) = extract_function_info(declarator, self.source, scope) else {
7064 return;
7065 };
7066 let code_unit =
7067 function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7068 if self.parsed.contains_declaration(&code_unit) {
7069 self.parsed
7070 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7071 return;
7072 }
7073 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7074 let signature = render_cpp_function_display_signature_from_node(
7075 declaration_node,
7076 self.source,
7077 scope.template_signature.as_deref(),
7078 false,
7079 ancestry,
7080 );
7081 self.parsed.add_signature_with_metadata(
7082 code_unit.clone(),
7083 cpp_signature_metadata(signature, declarator, self.source, ancestry)
7084 .with_declaration_only(true)
7085 .with_callable_linkage(cpp_callable_linkage(
7086 declaration_node,
7087 self.source,
7088 ancestry,
7089 )),
7090 );
7091 if let Some(parent) = &scope.class_unit {
7092 self.parsed.add_child(parent.clone(), code_unit);
7093 } else if let Some(module) = &scope.module {
7094 self.parsed.add_child(module.clone(), code_unit);
7095 }
7096 }
7097
7098 fn visit_recovered_macro_qualified_function_declaration<'tree>(
7099 &mut self,
7100 declaration_node: Node<'tree>,
7101 call: Node<'tree>,
7102 scope: &ScopeInfo,
7103 ancestry: &ParentIndex<'tree>,
7104 ) {
7105 let Some(parent) = &scope.class_unit else {
7106 return;
7107 };
7108 let Some(name_node) = call.child_by_field_name("function") else {
7109 return;
7110 };
7111 let Some(arguments) = call.child_by_field_name("arguments") else {
7112 return;
7113 };
7114 let Some((signature, parameter_labels)) =
7115 recovered_macro_qualified_function_parameters(arguments, self.source)
7116 else {
7117 return;
7118 };
7119 let arity = parameter_labels.len();
7120 let function = FunctionInfo {
7121 package_name: scope.package_name.clone(),
7122 owner: Some(CppMemberOwner::Unit(parent.clone())),
7123 name: normalize_cpp_whitespace(node_text(name_node, self.source)),
7124 signature,
7125 };
7126 if function.name.is_empty() {
7127 return;
7128 }
7129 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7130 if self.parsed.contains_declaration(&code_unit) {
7131 self.parsed
7132 .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7133 return;
7134 }
7135 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7136 let signature_label = render_cpp_function_display_signature_from_node(
7137 declaration_node,
7138 self.source,
7139 scope.template_signature.as_deref(),
7140 false,
7141 ancestry,
7142 );
7143 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7144 .with_declaration_only(true)
7145 .with_callable_arity(CallableArity::exact(arity))
7146 .with_callable_linkage(cpp_callable_linkage(
7147 declaration_node,
7148 self.source,
7149 ancestry,
7150 ));
7151 self.parsed
7152 .add_signature_with_metadata(code_unit.clone(), metadata);
7153 self.parsed.add_child(parent.clone(), code_unit);
7154 }
7155
7156 fn visit_recovered_macro_qualified_constructor_definition<'tree>(
7157 &mut self,
7158 declaration_node: Node<'tree>,
7159 call: Node<'tree>,
7160 scope: &ScopeInfo,
7161 ancestry: &ParentIndex<'tree>,
7162 ) {
7163 let Some(parent) = &scope.class_unit else {
7164 return;
7165 };
7166 let Some(arguments) = call.child_by_field_name("arguments") else {
7167 return;
7168 };
7169 let Some((mut signature, parameter_labels)) =
7170 recovered_macro_qualified_function_parameters(arguments, self.source)
7171 else {
7172 return;
7173 };
7174 if let Some(template_signature) = &scope.template_signature {
7175 signature = format!("{template_signature}{signature}");
7176 }
7177 let arity = parameter_labels.len();
7178 let function = FunctionInfo {
7179 package_name: scope.package_name.clone(),
7180 owner: Some(CppMemberOwner::Unit(parent.clone())),
7181 name: parent.identifier().to_string(),
7182 signature,
7183 };
7184 let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7185 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7186 let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
7187 let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7188 .with_declaration_only(false)
7189 .with_callable_arity(CallableArity::exact(arity))
7190 .with_callable_linkage(cpp_callable_linkage(
7191 declaration_node,
7192 self.source,
7193 ancestry,
7194 ));
7195 self.parsed
7196 .add_signature_with_metadata(code_unit.clone(), metadata);
7197 self.parsed.add_child(parent.clone(), code_unit);
7198 }
7199
7200 fn visit_variable_declaration<'tree>(
7201 &mut self,
7202 declaration_node: Node<'tree>,
7203 declarator: Node<'tree>,
7204 scope: &ScopeInfo,
7205 in_class_body: bool,
7206 ancestry: &ParentIndex<'tree>,
7207 ) {
7208 let Some(name) = extract_variable_name(declarator, self.source) else {
7209 return;
7210 };
7211 let parent = if in_class_body {
7212 let Some(parent) = &scope.class_unit else {
7213 return;
7214 };
7215 Some(parent)
7216 } else {
7217 None
7218 };
7219 let short_name = match parent {
7220 Some(parent) => cpp_join_member_short(parent.short_name(), &name),
7221 None => name.clone(),
7222 };
7223 let fq = cpp_leaf_fq(
7224 &scope.package_name,
7225 parent,
7226 &name,
7227 SegmentKind::Member,
7228 SegmentKind::Member,
7229 );
7230 let code_unit = CodeUnit::new_fq(
7231 self.file.clone(),
7232 CodeUnitType::Field,
7233 scope.package_name.clone(),
7234 short_name,
7235 fq,
7236 );
7237 if self.parsed.contains_declaration(&code_unit) {
7238 return;
7239 }
7240 self.add_declaration(code_unit.clone(), declaration_node, None, None);
7241 self.parsed.add_signature_with_metadata(
7242 code_unit.clone(),
7243 SignatureMetadata::new(
7244 render_cpp_field_signature(declaration_node, declarator, self.source),
7245 Vec::new(),
7246 )
7247 .with_cpp_field_linkage(cpp_field_declaration_linkage(
7248 declaration_node,
7249 self.source,
7250 ancestry,
7251 )),
7252 );
7253 if let Some(parent) = &scope.class_unit {
7254 self.parsed.add_child(parent.clone(), code_unit);
7255 } else if let Some(module) = &scope.module {
7256 self.parsed.add_child(module.clone(), code_unit);
7257 }
7258 }
7259
7260 fn visit_class_members_from_declaration<'tree>(
7261 &mut self,
7262 node: Node<'tree>,
7263 scope: &ScopeInfo,
7264 ancestry: &ParentIndex<'tree>,
7265 ) {
7266 let mut cursor = node.walk();
7267 for child in node.named_children(&mut cursor) {
7268 if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
7269 self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
7270 } else if child.kind() == "init_declarator"
7271 && let Some(inner) = child.child_by_field_name("declarator")
7272 {
7273 self.visit_variable_declaration(node, inner, scope, true, ancestry);
7274 } else if matches!(
7275 child.kind(),
7276 "identifier"
7277 | "field_identifier"
7278 | "pointer_declarator"
7279 | "reference_declarator"
7280 | "array_declarator"
7281 | "parenthesized_declarator"
7282 ) {
7283 self.visit_variable_declaration(node, child, scope, true, ancestry);
7284 }
7285 }
7286 }
7287
7288 fn visit_global_variables_from_declaration<'tree>(
7289 &mut self,
7290 node: Node<'tree>,
7291 scope: &ScopeInfo,
7292 ancestry: &ParentIndex<'tree>,
7293 ) {
7294 let mut cursor = node.walk();
7295 for child in node.named_children(&mut cursor) {
7296 if child.kind() == "init_declarator"
7297 && let Some(inner) = child.child_by_field_name("declarator")
7298 {
7299 self.visit_variable_declaration(node, inner, scope, false, ancestry);
7300 } else if matches!(
7301 child.kind(),
7302 "identifier"
7303 | "field_identifier"
7304 | "pointer_declarator"
7305 | "reference_declarator"
7306 | "array_declarator"
7307 | "parenthesized_declarator"
7308 ) {
7309 self.visit_variable_declaration(node, child, scope, false, ancestry);
7310 }
7311 }
7312 }
7313
7314 fn visit_type_declaration<'tree>(
7315 &mut self,
7316 node: Node<'tree>,
7317 scope: &ScopeInfo,
7318 stack: &mut Vec<CppWork<'tree>>,
7319 ancestry: &ParentIndex<'tree>,
7320 ) {
7321 let type_node = node.child_by_field_name("type");
7322 if let Some(type_node) = type_node
7323 && matches!(
7324 type_node.kind(),
7325 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
7326 )
7327 {
7328 self.visit_class_like(type_node, scope, stack, ancestry);
7329 }
7330
7331 if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
7332 let range = Range {
7333 start_byte: node.start_byte(),
7334 end_byte: recovered.end_node.end_byte(),
7335 start_line: node.start_position().row + 1,
7336 end_line: recovered.end_node.end_position().row + 1,
7337 };
7338 let signature = self
7339 .source
7340 .get(range.start_byte..range.end_byte)
7341 .map(normalize_cpp_whitespace)
7342 .unwrap_or_default();
7343 self.record_type_aliases(
7344 node,
7345 scope,
7346 vec![recovered.name],
7347 signature,
7348 range,
7349 ancestry,
7350 );
7351 return;
7352 }
7353
7354 let alias_names = match node.kind() {
7355 "alias_declaration" => extract_alias_declaration_name(node, self.source)
7356 .into_iter()
7357 .collect::<Vec<_>>(),
7358 "type_definition" => extract_typedef_alias_names(node, self.source),
7359 _ => Vec::new(),
7360 };
7361 let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
7362 (type_node, alias_names.as_slice())
7363 && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
7364 && type_node.child_by_field_name("name").is_none()
7365 {
7366 cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
7367 } else {
7368 None
7369 };
7370 self.add_type_aliases(node, scope, alias_names, ancestry);
7371 if let Some((body, alias_name)) = anonymous_aggregate {
7372 let signature = normalize_cpp_whitespace(node_text(node, self.source));
7378 let alias_unit = self.type_alias_unit(scope, alias_name, signature);
7379 debug_assert!(self.parsed.contains_declaration(&alias_unit));
7380 let mut nested_scope = scope.clone();
7381 nested_scope.class_unit = Some(alias_unit);
7382 nested_scope.template_signature = scope.template_signature.clone();
7383 nested_scope.template_metadata = None;
7384 nested_scope.declarations_are_fields = false;
7385 nested_scope.recovered_specialization_member_scope = false;
7386 stack.push(CppWork::Container(CppContainer {
7387 node: body,
7388 scope: nested_scope,
7389 }));
7390 }
7391 }
7392
7393 fn add_type_aliases(
7394 &mut self,
7395 node: Node<'_>,
7396 scope: &ScopeInfo,
7397 alias_names: Vec<String>,
7398 ancestry: &ParentIndex<'_>,
7399 ) {
7400 let signature = normalize_cpp_whitespace(node_text(node, self.source));
7401 self.record_type_aliases(
7402 node,
7403 scope,
7404 alias_names,
7405 signature,
7406 cpp_declaration_range(node),
7407 ancestry,
7408 );
7409 }
7410
7411 fn record_type_aliases(
7412 &mut self,
7413 node: Node<'_>,
7414 scope: &ScopeInfo,
7415 alias_names: Vec<String>,
7416 signature: String,
7417 range: Range,
7418 ancestry: &ParentIndex<'_>,
7419 ) {
7420 if signature.is_empty() {
7421 return;
7422 }
7423 let type_name = node
7424 .child_by_field_name("type")
7425 .and_then(|type_node| type_node.child_by_field_name("name"))
7426 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
7427 for alias_name in alias_names {
7428 if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
7429 continue;
7430 }
7431 let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
7432 self.add_declaration_with_range(code_unit.clone(), range, None, None);
7435 let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
7436 let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
7437 cpp_structured_type_identity(type_node, self.source, &lexical_scope)
7438 });
7439 self.parsed.add_signature_with_metadata(
7440 code_unit.clone(),
7441 SignatureMetadata::new(signature.clone(), Vec::new())
7442 .with_underlying_type_identity(underlying_type_identity),
7443 );
7444 if let Some(metadata) = &scope.template_metadata {
7445 let mut metadata = metadata.clone();
7446 metadata.primary_fq_name = code_unit.fq_name();
7447 self.parsed
7448 .set_cpp_template_metadata(code_unit.clone(), metadata);
7449 }
7450 if let Some(parent) = &scope.class_unit {
7451 self.parsed.add_child(parent.clone(), code_unit.clone());
7452 } else if let Some(module) = &scope.module {
7453 self.parsed.add_child(module.clone(), code_unit.clone());
7454 }
7455 self.parsed.mark_type_alias(code_unit);
7456 }
7457 }
7458
7459 fn type_alias_unit(
7460 &self,
7461 scope: &ScopeInfo,
7462 alias_name: String,
7463 signature: String,
7464 ) -> CodeUnit {
7465 let short_name = if let Some(parent) = &scope.class_unit {
7466 cpp_join_nested_short(parent.short_name(), &alias_name)
7467 } else {
7468 alias_name.clone()
7469 };
7470 let fq = cpp_leaf_fq(
7471 &scope.package_name,
7472 scope.class_unit.as_ref(),
7473 &alias_name,
7474 SegmentKind::Nested,
7475 SegmentKind::Type,
7476 );
7477 CodeUnit::with_signature_and_fq(
7478 self.file.clone(),
7479 CodeUnitType::Class,
7480 scope.package_name.clone(),
7481 short_name,
7482 Some(signature),
7483 false,
7484 fq,
7485 )
7486 }
7487
7488 fn visit_macro(&mut self, node: Node<'_>) {
7489 if let Some(replacement) =
7493 crate::graph::syntax::function_macro_replacement_span(node, self.source)
7494 {
7495 self.consumed_fragment_regions
7496 .push((replacement.start, replacement.end));
7497 }
7498 let Some(name) = extract_macro_name(node, self.source) else {
7499 return;
7500 };
7501 let signature = node_text(node, self.source).trim_end().to_string();
7502 if signature.is_empty() {
7503 return;
7504 }
7505 let fq = cpp_member_fq("", &name);
7506 let code_unit = CodeUnit::with_signature_and_fq(
7513 self.file.clone(),
7514 CodeUnitType::Macro,
7515 "",
7516 name.clone(),
7517 Some(signature.clone()),
7518 false,
7519 fq,
7520 );
7521 if !self.parsed.contains_declaration(&code_unit) {
7522 self.add_declaration(code_unit.clone(), node, None, None);
7523 let name_range = node
7524 .child_by_field_name("name")
7525 .map(cpp_declaration_range)
7526 .unwrap_or_else(|| cpp_declaration_range(node));
7527 self.parsed
7528 .record_materialization(MaterializationRecord::GeneratedDeclaration {
7529 site: cpp_declaration_range(node),
7530 argument: name_range,
7531 kind: GenerationKind::PreprocessorDefinition,
7532 unit: code_unit.clone(),
7533 });
7534 self.parsed.add_signature(code_unit, signature);
7535 }
7536 if node.kind() == "preproc_def" {
7537 update_object_macro_field_environment(
7538 node,
7539 self.source,
7540 &mut self.object_macro_fields,
7541 &mut self.ambiguous_object_macro_fields,
7542 );
7543 } else {
7544 self.object_macro_fields.remove(&name);
7545 self.ambiguous_object_macro_fields.remove(&name);
7546 }
7547 }
7548
7549 fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7550 let Some(directive) = node.child_by_field_name("directive") else {
7551 return;
7552 };
7553 let name = node_text(directive, self.source).trim();
7554 let range = cpp_declaration_range(node);
7555 let fields = object_macro_field_closure(&self.object_macro_fields, name);
7556 self.materialize_object_macro_fields(fields, range, scope);
7557 }
7558
7559 fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7564 if !matches!(node.kind(), "declaration" | "field_declaration") {
7565 return false;
7566 }
7567 if scope.class_unit.is_none() {
7575 return false;
7576 }
7577 let macro_nodes =
7578 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7579 for macro_node in ¯o_nodes {
7580 let name = node_text(*macro_node, self.source).trim();
7581 let fields = object_macro_field_closure(&self.object_macro_fields, name);
7582 self.materialize_object_macro_fields(fields, cpp_declaration_range(*macro_node), scope);
7583 }
7584 let Some(last) = macro_nodes.last() else {
7585 return false;
7586 };
7587 self.record_collapsed_aggregate_fields(
7593 last.end_byte()..node.end_byte(),
7594 node.start_position().row
7595 + 1
7596 + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
7597 scope,
7598 );
7599 true
7600 }
7601
7602 fn materialize_object_macro_fields(
7603 &mut self,
7604 fields: Vec<MacroReplacementField>,
7605 range: Range,
7606 scope: &ScopeInfo,
7607 ) {
7608 let Some(owner) = scope.class_unit.as_ref() else {
7609 return;
7610 };
7611 for field in fields {
7612 let signature = field.declaration.clone();
7613 let mut fq = owner.fq().clone();
7614 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7615 let short_name = if owner.short_name().is_empty() {
7616 field.name.clone()
7617 } else {
7618 format!("{}.{}", owner.short_name(), field.name)
7619 };
7620 let code_unit = CodeUnit::with_signature_and_fq(
7621 self.file.clone(),
7622 CodeUnitType::Field,
7623 owner.package_name().to_string(),
7624 short_name,
7625 Some(field.declaration),
7626 true,
7627 fq,
7628 );
7629 if self.parsed.contains_declaration(&code_unit) {
7630 continue;
7631 }
7632 self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7633 self.parsed.add_signature(code_unit, signature);
7634 }
7635 }
7636
7637 fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7644 let mut cursor = node.walk();
7645 let children = node.children(&mut cursor).collect::<Vec<_>>();
7646 let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember>)>::new();
7647 let mut object_macro_fields = self.object_macro_fields.clone();
7648 let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
7649 let mut open = Vec::<usize>::new();
7650 let mut index = 0;
7651 while index < children.len() {
7652 let keyword = children[index];
7653 if update_object_macro_field_environment(
7654 keyword,
7655 self.source,
7656 &mut object_macro_fields,
7657 &mut ambiguous_object_macro_fields,
7658 ) {
7659 index += 1;
7660 continue;
7661 }
7662 if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
7663 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7664 if !name.is_empty() {
7665 let parent = open
7666 .last()
7667 .and_then(|class| recovered.get(*class))
7668 .map(|(owner, _, _, _)| owner.clone())
7669 .or_else(|| scope.class_unit.clone());
7670 let short_name = parent.as_ref().map_or_else(
7671 || name.clone(),
7672 |parent| cpp_join_nested_short(parent.short_name(), &name),
7673 );
7674 let fq = cpp_leaf_fq(
7675 &scope.package_name,
7676 parent.as_ref(),
7677 &name,
7678 SegmentKind::Nested,
7679 SegmentKind::Type,
7680 );
7681 let owner = CodeUnit::with_signature_and_fq(
7682 self.file.clone(),
7683 CodeUnitType::Class,
7684 scope.package_name.clone(),
7685 short_name,
7686 None,
7687 false,
7688 fq,
7689 );
7690 recovered.push((
7691 owner,
7692 head.key.start_byte(),
7693 head.opening.end_byte(),
7694 Vec::new(),
7695 ));
7696 let class_index = recovered.len() - 1;
7697 match head.folded_members {
7698 None => open.push(class_index),
7702 Some(members) => {
7708 let macro_nodes = object_macro_identifier_nodes_with_environment(
7709 children[index],
7710 self.source,
7711 &mut object_macro_fields,
7712 &mut ambiguous_object_macro_fields,
7713 );
7714 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7715 .iter()
7716 .partition(|node| node.start_byte() < head.key.start_byte());
7717 if let Some(&enclosing) = open.last() {
7718 for macro_node in preceding {
7719 recovered[enclosing]
7720 .3
7721 .push(CppCollapsedMember::MacroFields {
7722 range: cpp_declaration_range(macro_node),
7723 fields: object_macro_field_closure(
7724 &object_macro_fields,
7725 &normalize_cpp_whitespace(node_text(
7726 macro_node,
7727 self.source,
7728 )),
7729 ),
7730 });
7731 }
7732 }
7733 let closing = cpp_collapsed_aggregate_closing_brace(members);
7734 recovered[class_index]
7735 .3
7736 .extend(cpp_collapsed_aggregate_members(
7737 &inner,
7738 head.opening.end_byte()..closing,
7739 head.opening.end_position().row + 1,
7740 self.source,
7741 &object_macro_fields,
7742 ));
7743 recovered[class_index].2 = members.end_byte();
7744 for &open_class in &open {
7745 recovered[open_class].2 =
7746 recovered[open_class].2.max(members.end_byte());
7747 }
7748 }
7749 }
7750 index += head.width;
7751 continue;
7752 }
7753 }
7754 if let Some(&class_index) = open.last()
7755 && children[index].kind() == "field_declaration"
7756 {
7757 let field = children[index];
7758 let macro_nodes = object_macro_identifier_nodes_with_environment(
7759 field,
7760 self.source,
7761 &mut object_macro_fields,
7762 &mut ambiguous_object_macro_fields,
7763 );
7764 recovered[class_index]
7765 .3
7766 .extend(cpp_collapsed_aggregate_members(
7767 ¯o_nodes,
7768 field.start_byte()..field.end_byte(),
7769 field.start_position().row + 1,
7770 self.source,
7771 &object_macro_fields,
7772 ));
7773 let end = field.end_byte();
7774 for &open_class in &open {
7775 recovered[open_class].2 = recovered[open_class].2.max(end);
7776 }
7777 let closes = count_close_brace_nodes(field);
7778 for _ in 0..closes {
7779 if let Some(closed) = open.pop() {
7780 recovered[closed].2 = end;
7781 }
7782 }
7783 }
7784 index += 1;
7785 }
7786
7787 let mut owners = Vec::with_capacity(recovered.len());
7788 for (owner, start, end, members) in recovered {
7789 let parent = owners
7790 .iter()
7791 .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
7792 .cloned()
7793 .or_else(|| scope.class_unit.clone());
7794 self.declare_collapsed_aggregate(owner.clone(), start..end, parent, members, scope);
7795 owners.push(owner);
7796 }
7797 }
7798
7799 fn declare_collapsed_aggregate(
7801 &mut self,
7802 owner: CodeUnit,
7803 span: std::ops::Range<usize>,
7804 parent: Option<CodeUnit>,
7805 members: Vec<CppCollapsedMember>,
7806 scope: &ScopeInfo,
7807 ) {
7808 let range = Range {
7809 start_byte: span.start,
7810 end_byte: span.end,
7811 start_line: self.source.get(..span.start).map_or(1, |source| {
7812 source.bytes().filter(|byte| *byte == b'\n').count() + 1
7813 }),
7814 end_line: self.source.get(..span.end).map_or(1, |source| {
7815 source.bytes().filter(|byte| *byte == b'\n').count() + 1
7816 }),
7817 };
7818 self.add_declaration_with_range(owner.clone(), range, parent, None);
7825 let owner_scope = ScopeInfo {
7826 class_unit: Some(owner),
7827 declarations_are_fields: true,
7828 ..scope.clone()
7829 };
7830 for member in members {
7831 match member {
7832 CppCollapsedMember::MacroFields { range, fields } => {
7833 self.materialize_object_macro_fields(fields, range, &owner_scope);
7834 }
7835 CppCollapsedMember::Declarations { span, start_line } => {
7836 self.record_collapsed_aggregate_fields(span, start_line, &owner_scope);
7837 }
7838 }
7839 }
7840 }
7841
7842 fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7853 if node.parent().is_some_and(|parent| parent.is_error()) {
7854 return false;
7857 }
7858 let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
7859 return false;
7860 };
7861 let members = head
7862 .folded_members
7863 .expect("a folded aggregate head carries its member list");
7864 let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7865 if name.is_empty() {
7866 return false;
7867 }
7868 let macro_nodes =
7869 object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7870 let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7871 .iter()
7872 .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
7873 for macro_node in preceding {
7874 let fields = object_macro_field_closure(
7875 &self.object_macro_fields,
7876 &normalize_cpp_whitespace(node_text(macro_node, self.source)),
7877 );
7878 self.materialize_object_macro_fields(fields, cpp_declaration_range(macro_node), scope);
7879 }
7880 let parent = scope.class_unit.clone();
7881 let short_name = parent.as_ref().map_or_else(
7882 || name.clone(),
7883 |parent| cpp_join_nested_short(parent.short_name(), &name),
7884 );
7885 let fq = cpp_leaf_fq(
7886 &scope.package_name,
7887 parent.as_ref(),
7888 &name,
7889 SegmentKind::Nested,
7890 SegmentKind::Type,
7891 );
7892 let owner = CodeUnit::with_signature_and_fq(
7893 self.file.clone(),
7894 CodeUnitType::Class,
7895 scope.package_name.clone(),
7896 short_name,
7897 None,
7898 false,
7899 fq,
7900 );
7901 let recovered = cpp_collapsed_aggregate_members(
7902 &inner,
7903 head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
7904 head.opening.end_position().row + 1,
7905 self.source,
7906 &self.object_macro_fields,
7907 );
7908 self.declare_collapsed_aggregate(
7909 owner,
7910 head.key.start_byte()..members.end_byte(),
7911 parent,
7912 recovered,
7913 scope,
7914 );
7915 true
7916 }
7917
7918 fn record_collapsed_aggregate_fields(
7923 &mut self,
7924 span: std::ops::Range<usize>,
7925 start_line: usize,
7926 scope: &ScopeInfo,
7927 ) {
7928 let Some(owner) = scope.class_unit.as_ref() else {
7929 return;
7930 };
7931 for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
7932 let range = Range {
7933 start_byte: field.range.start,
7934 end_byte: field.range.end,
7935 start_line: start_line
7936 + cpp_line_breaks_between(self.source, span.start, field.range.start),
7937 end_line: start_line
7938 + cpp_line_breaks_between(self.source, span.start, field.range.end),
7939 };
7940 let mut fq = owner.fq().clone();
7941 fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7942 let short_name = if owner.short_name().is_empty() {
7943 field.name.clone()
7944 } else {
7945 format!("{}.{}", owner.short_name(), field.name)
7946 };
7947 let signature = normalize_cpp_whitespace(&field.declaration);
7948 let code_unit = CodeUnit::with_signature_and_fq(
7949 self.file.clone(),
7950 CodeUnitType::Field,
7951 owner.package_name().to_string(),
7952 short_name,
7953 Some(signature.clone()),
7954 false,
7955 fq,
7956 );
7957 if self.parsed.contains_declaration(&code_unit) {
7958 continue;
7959 }
7960 self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7961 self.parsed.add_signature(code_unit, signature);
7962 }
7963 }
7964
7965 fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7966 let Some(_directive) = node.child_by_field_name("directive") else {
7967 return;
7968 };
7969 if is_cpp_undef_directive(node, self.source) {
7970 update_object_macro_field_environment(
7971 node,
7972 self.source,
7973 &mut self.object_macro_fields,
7974 &mut self.ambiguous_object_macro_fields,
7975 );
7976 return;
7977 }
7978 let directly_in_field_list = node
7979 .parent()
7980 .is_some_and(|parent| parent.kind() == "field_declaration_list");
7981 if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
7982 self.visit_object_macro_fields(node, scope);
7983 }
7984 }
7985}
7986
7987fn object_macro_field_closure(
7995 environment: &HashMap<String, ObjectMacroReplacement>,
7996 name: &str,
7997) -> Vec<MacroReplacementField> {
7998 let mut fields = Vec::new();
7999 let mut visited = HashSet::default();
8000 let mut stack = vec![name.to_string()];
8001 while let Some(current) = stack.pop() {
8002 if !visited.insert(current.clone()) {
8003 continue;
8004 }
8005 let Some(replacement) = environment.get(¤t) else {
8006 continue;
8007 };
8008 fields.extend(replacement.fields.iter().cloned());
8009 stack.extend(replacement.nested.iter().rev().cloned());
8010 }
8011 fields
8012}
8013
8014fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
8020 crate::graph::syntax::object_macro_replacement_span(node, source)
8021 .and_then(|span| source.get(span))
8022 .map(crate::graph::syntax::object_macro_replacement)
8023 .unwrap_or_default()
8024}
8025
8026fn update_object_macro_field_environment(
8031 node: Node<'_>,
8032 source: &str,
8033 fields: &mut HashMap<String, ObjectMacroReplacement>,
8034 ambiguous: &mut HashSet<String>,
8035) -> bool {
8036 match node.kind() {
8037 "preproc_def" => {
8038 let Some(name) = extract_macro_name(node, source) else {
8039 return false;
8040 };
8041 let replacement = object_macro_replacement_of(node, source);
8042 if replacement.is_empty() || ambiguous.contains(&name) {
8043 fields.remove(&name);
8044 ambiguous.insert(name);
8045 } else if let Some(previous) = fields.get(&name) {
8046 if previous != &replacement {
8047 fields.remove(&name);
8048 ambiguous.insert(name);
8049 }
8050 } else {
8051 fields.insert(name, replacement);
8052 }
8053 true
8054 }
8055 "preproc_call" if is_cpp_undef_directive(node, source) => {
8056 if let Some(argument) = node.child_by_field_name("argument") {
8057 let name = node_text(argument, source).trim();
8058 fields.remove(name);
8059 if inside_preprocessor_conditional(node) {
8060 ambiguous.insert(name.to_string());
8061 } else {
8062 ambiguous.remove(name);
8063 }
8064 }
8065 true
8066 }
8067 _ => false,
8068 }
8069}
8070
8071fn object_macro_identifier_nodes<'tree>(
8072 node: Node<'tree>,
8073 source: &str,
8074 fields: &HashMap<String, ObjectMacroReplacement>,
8075) -> Vec<Node<'tree>> {
8076 let mut result = Vec::new();
8077 let mut stack = vec![node];
8078 while let Some(current) = stack.pop() {
8079 if matches!(
8080 current.kind(),
8081 "identifier" | "field_identifier" | "type_identifier"
8082 ) && fields.contains_key(node_text(current, source).trim())
8083 {
8084 result.push(current);
8085 }
8086 let mut cursor = current.walk();
8087 let children = current.children(&mut cursor).collect::<Vec<_>>();
8088 stack.extend(children.into_iter().rev());
8089 }
8090 result.sort_by_key(|node| node.start_byte());
8091 result
8092}
8093
8094fn object_macro_identifier_nodes_with_environment<'tree>(
8099 node: Node<'tree>,
8100 source: &str,
8101 fields: &mut HashMap<String, ObjectMacroReplacement>,
8102 ambiguous: &mut HashSet<String>,
8103) -> Vec<Node<'tree>> {
8104 let mut result = Vec::new();
8105 let mut stack = vec![node];
8106 while let Some(current) = stack.pop() {
8107 if update_object_macro_field_environment(current, source, fields, ambiguous) {
8108 continue;
8109 }
8110 if matches!(
8111 current.kind(),
8112 "identifier" | "field_identifier" | "type_identifier"
8113 ) && fields.contains_key(node_text(current, source).trim())
8114 {
8115 result.push(current);
8116 }
8117 let mut cursor = current.walk();
8118 let children = current.children(&mut cursor).collect::<Vec<_>>();
8119 stack.extend(children.into_iter().rev());
8120 }
8121 result.sort_by_key(|node| node.start_byte());
8122 result
8123}
8124
8125enum CppCollapsedMember {
8128 MacroFields {
8131 range: Range,
8132 fields: Vec<MacroReplacementField>,
8133 },
8134 Declarations {
8137 span: std::ops::Range<usize>,
8138 start_line: usize,
8139 },
8140}
8141
8142struct CppCollapsedAggregateHead<'tree> {
8157 key: Node<'tree>,
8158 name: Node<'tree>,
8159 opening: Node<'tree>,
8160 folded_members: Option<Node<'tree>>,
8164 width: usize,
8166}
8167
8168fn cpp_collapsed_aggregate_head<'tree>(
8170 children: &[Node<'tree>],
8171 index: usize,
8172 source: &str,
8173) -> Option<CppCollapsedAggregateHead<'tree>> {
8174 let key = *children.get(index)?;
8175 if matches!(key.kind(), "struct" | "class" | "union") {
8176 let name = *children.get(index + 1)?;
8177 let opening = *children.get(index + 2)?;
8178 if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
8179 return None;
8180 }
8181 return Some(CppCollapsedAggregateHead {
8182 key,
8183 name,
8184 opening,
8185 folded_members: None,
8186 width: 3,
8187 });
8188 }
8189 cpp_folded_aggregate_head(key, source)
8190}
8191
8192fn cpp_folded_aggregate_head<'tree>(
8195 node: Node<'tree>,
8196 source: &str,
8197) -> Option<CppCollapsedAggregateHead<'tree>> {
8198 if !matches!(node.kind(), "declaration" | "field_declaration") {
8199 return None;
8200 }
8201 let mut cursor = node.walk();
8202 let children = node.children(&mut cursor).collect::<Vec<_>>();
8203 let key_index = children.iter().position(|child| {
8204 child.is_error()
8205 && matches!(
8206 node_text(*child, source).trim(),
8207 "struct" | "class" | "union"
8208 )
8209 })?;
8210 let declarator = *children.get(key_index + 1)?;
8211 let (name, members) = if declarator.kind() == "init_declarator" {
8214 (
8215 declarator.child_by_field_name("declarator")?,
8216 declarator.child_by_field_name("value")?,
8217 )
8218 } else {
8219 (declarator, *children.get(key_index + 2)?)
8220 };
8221 if !matches!(
8222 name.kind(),
8223 "field_identifier" | "type_identifier" | "identifier"
8224 ) {
8225 return None;
8226 }
8227 if members.kind() != "initializer_list" {
8228 return None;
8229 }
8230 let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
8231 Some(CppCollapsedAggregateHead {
8232 key: children[key_index],
8233 name,
8234 opening,
8235 folded_members: Some(members),
8236 width: 1,
8237 })
8238}
8239
8240fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
8244 let mut cursor = members.walk();
8245 members
8246 .children(&mut cursor)
8247 .filter(|child| child.kind() == "}" && !child.is_missing())
8248 .last()
8249 .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
8250}
8251
8252fn cpp_collapsed_aggregate_members(
8256 macro_nodes: &[Node<'_>],
8257 region: std::ops::Range<usize>,
8258 region_start_line: usize,
8259 source: &str,
8260 environment: &HashMap<String, ObjectMacroReplacement>,
8261) -> Vec<CppCollapsedMember> {
8262 let mut members = macro_nodes
8263 .iter()
8264 .map(|macro_node| CppCollapsedMember::MacroFields {
8265 range: cpp_declaration_range(*macro_node),
8266 fields: object_macro_field_closure(
8267 environment,
8268 &normalize_cpp_whitespace(node_text(*macro_node, source)),
8269 ),
8270 })
8271 .collect::<Vec<_>>();
8272 let declarations_start = macro_nodes
8276 .last()
8277 .map_or(region.start, |macro_node| macro_node.end_byte());
8278 members.push(CppCollapsedMember::Declarations {
8279 span: declarations_start..region.end,
8280 start_line: region_start_line
8281 + cpp_line_breaks_between(source, region.start, declarations_start),
8282 });
8283 members
8284}
8285
8286fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
8289 source.get(from..to).map_or(0, |slice| {
8290 slice.bytes().filter(|byte| *byte == b'\n').count()
8291 })
8292}
8293
8294fn count_close_brace_nodes(node: Node<'_>) -> usize {
8300 let mut opened = 0usize;
8301 let mut closed = 0usize;
8302 let mut stack = vec![node];
8303 while let Some(current) = stack.pop() {
8304 if !current.is_missing() {
8305 match current.kind() {
8306 "{" => opened += 1,
8307 "}" => closed += 1,
8308 _ => {}
8309 }
8310 }
8311 let mut cursor = current.walk();
8312 stack.extend(current.children(&mut cursor));
8313 }
8314 closed.saturating_sub(opened)
8315}
8316
8317pub fn cpp_field_declaration_linkage<'tree>(
8322 declaration: Node<'tree>,
8323 source: &str,
8324 ancestry: &ParentIndex<'tree>,
8325) -> CppFieldLinkage {
8326 let mut current = ancestry.parent(declaration);
8327 let mut enclosed_by_class = false;
8328 while let Some(node) = current {
8329 if node.kind() == "namespace_definition"
8330 && node
8331 .child_by_field_name("name")
8332 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8333 {
8334 return CppFieldLinkage::Internal;
8335 }
8336 if matches!(
8337 node.kind(),
8338 "class_specifier" | "struct_specifier" | "union_specifier"
8339 ) && node
8340 .child_by_field_name("name")
8341 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8342 {
8343 return CppFieldLinkage::Internal;
8344 }
8345 if matches!(
8346 node.kind(),
8347 "class_specifier" | "struct_specifier" | "union_specifier"
8348 ) {
8349 enclosed_by_class = true;
8350 }
8351 if matches!(node.kind(), "function_definition" | "lambda_expression") {
8352 return CppFieldLinkage::Internal;
8353 }
8354 current = ancestry.parent(node);
8355 }
8356 if enclosed_by_class {
8357 return CppFieldLinkage::External;
8358 }
8359 let mut cursor = declaration.walk();
8360 let mut has_static = false;
8361 let mut has_extern = false;
8362 let mut has_inline = false;
8363 let mut has_const = false;
8364 let mut has_constexpr = false;
8365 for child in declaration.named_children(&mut cursor) {
8366 let text = normalize_cpp_whitespace(node_text(child, source));
8367 match (child.kind(), text.as_str()) {
8368 ("storage_class_specifier", "static") => has_static = true,
8369 ("storage_class_specifier", "extern") => has_extern = true,
8370 ("storage_class_specifier", "inline") => has_inline = true,
8371 ("storage_class_specifier", "constexpr") => has_constexpr = true,
8372 ("type_qualifier", "const") => has_const = true,
8373 ("type_qualifier", "constexpr") => has_constexpr = true,
8374 _ => {}
8375 }
8376 }
8377 if has_static {
8378 CppFieldLinkage::Internal
8379 } else if has_extern || has_inline {
8380 CppFieldLinkage::External
8381 } else if has_const || has_constexpr {
8382 CppFieldLinkage::InternalUnlessExternalPeer
8383 } else {
8384 CppFieldLinkage::External
8385 }
8386}
8387
8388fn cpp_declaration_range(node: Node<'_>) -> Range {
8389 Range {
8390 start_byte: node.start_byte(),
8391 end_byte: node.end_byte(),
8392 start_line: node.start_position().row + 1,
8393 end_line: node.end_position().row + 1,
8394 }
8395}
8396
8397fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
8401 let line_at = |byte: usize| {
8402 source.as_bytes()[..byte]
8403 .iter()
8404 .filter(|&&b| b == b'\n')
8405 .count()
8406 + 1
8407 };
8408 Range {
8409 start_byte,
8410 end_byte,
8411 start_line: line_at(start_byte),
8412 end_line: line_at(end_byte),
8413 }
8414}
8415
8416pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
8423 walk_named_tree_preorder(root, true, |node| {
8424 if node.kind() == "preproc_include" {
8425 let raw = normalize_cpp_whitespace(node_text(node, source));
8426 if !raw.is_empty() {
8427 parsed.imports.push(ImportInfo {
8428 raw_snippet: raw,
8429 is_wildcard: false,
8430 is_global: false,
8431 identifier: None,
8432 alias: None,
8433 path: None,
8434 binder_span: None,
8435 });
8436 }
8437 return WalkControl::SkipChildren;
8438 }
8439 WalkControl::Continue
8440 });
8441}
8442
8443pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
8444 let mut in_block_comment = false;
8445 for line in source.lines() {
8446 let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
8447 let trimmed = stripped.trim();
8448 if !looks_like_quoted_include_line(trimmed) {
8449 continue;
8450 }
8451
8452 let raw = normalize_cpp_whitespace(trimmed);
8453 if parsed
8457 .imports
8458 .iter()
8459 .any(|import| import.raw_snippet == raw)
8460 {
8461 continue;
8462 }
8463
8464 parsed.imports.push(ImportInfo {
8465 raw_snippet: raw,
8466 is_wildcard: false,
8467 is_global: false,
8468 identifier: None,
8469 alias: None,
8470 path: None,
8471 binder_span: None,
8472 });
8473 }
8474}
8475
8476fn looks_like_quoted_include_line(line: &str) -> bool {
8477 let Some(rest) = line.trim_start().strip_prefix('#') else {
8478 return false;
8479 };
8480 let Some(rest) = rest.trim_start().strip_prefix("include") else {
8481 return false;
8482 };
8483 rest.trim_start().starts_with('"')
8484}
8485
8486fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
8487 let mut raw = Vec::new();
8488 let mut cursor = node.walk();
8489 for child in node.named_children(&mut cursor) {
8490 if child.kind() == "base_class_clause" {
8491 collect_cpp_base_nodes(child, source, &mut raw);
8492 }
8493 }
8494 raw
8495}
8496
8497fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
8498 walk_named_tree_preorder(node, false, |child| match child.kind() {
8499 "type_identifier" | "qualified_identifier" | "template_type" => {
8500 let text = normalize_cpp_whitespace(node_text(child, source));
8501 if !text.is_empty() {
8502 raw.push(text);
8503 }
8504 WalkControl::SkipChildren
8505 }
8506 _ => WalkControl::Continue,
8507 });
8508}
8509
8510fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
8511 let mut out = String::new();
8512 let chars: Vec<char> = line.chars().collect();
8513 let mut index = 0;
8514 let mut in_string = false;
8515 let mut in_char = false;
8516 let mut escape = false;
8517
8518 while index < chars.len() {
8519 let ch = chars[index];
8520 let next = chars.get(index + 1).copied();
8521
8522 if *in_block_comment {
8523 if ch == '*' && next == Some('/') {
8524 *in_block_comment = false;
8525 index += 2;
8526 } else {
8527 index += 1;
8528 }
8529 continue;
8530 }
8531
8532 if in_string {
8533 out.push(ch);
8534 if escape {
8535 escape = false;
8536 } else if ch == '\\' {
8537 escape = true;
8538 } else if ch == '"' {
8539 in_string = false;
8540 }
8541 index += 1;
8542 continue;
8543 }
8544
8545 if in_char {
8546 out.push(ch);
8547 if escape {
8548 escape = false;
8549 } else if ch == '\\' {
8550 escape = true;
8551 } else if ch == '\'' {
8552 in_char = false;
8553 }
8554 index += 1;
8555 continue;
8556 }
8557
8558 if ch == '/' && next == Some('/') {
8559 break;
8560 }
8561 if ch == '/' && next == Some('*') {
8562 *in_block_comment = true;
8563 index += 2;
8564 continue;
8565 }
8566 if ch == '"' {
8567 in_string = true;
8568 out.push(ch);
8569 index += 1;
8570 continue;
8571 }
8572 if ch == '\'' {
8573 in_char = true;
8574 out.push(ch);
8575 index += 1;
8576 continue;
8577 }
8578
8579 out.push(ch);
8580 index += 1;
8581 }
8582
8583 out
8584}
8585
8586#[derive(Clone)]
8587struct FunctionInfo {
8588 package_name: String,
8589 owner: Option<CppMemberOwner>,
8590 name: String,
8591 signature: String,
8592}
8593
8594#[derive(Clone)]
8601enum CppMemberOwner {
8602 Chain(Vec<String>),
8606 Unit(CodeUnit),
8609}
8610
8611impl CppMemberOwner {
8612 fn short_chain(&self) -> String {
8614 match self {
8615 Self::Chain(chain) => chain.join("$"),
8616 Self::Unit(parent) => parent.short_name().to_string(),
8617 }
8618 }
8619}
8620
8621enum DeclaratorKind<'a> {
8622 Function(Node<'a>),
8623 Variable(Node<'a>),
8624}
8625
8626impl FunctionInfo {
8627 fn code_unit(&self, file: ProjectFile) -> CodeUnit {
8628 self.code_unit_with_synthetic(file, false)
8629 }
8630
8631 fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
8632 let short_name = match &self.owner {
8633 Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
8634 None => self.name.clone(),
8635 };
8636 let fq = match &self.owner {
8637 Some(CppMemberOwner::Chain(chain)) => {
8638 debug_assert!(
8639 !chain.is_empty(),
8640 "an empty owner chain is no owner; producers return None instead"
8641 );
8642 let mut fq = FqName::new();
8643 cpp_push_package(&mut fq, &self.package_name);
8644 let mut first = true;
8645 for component in chain {
8646 let kind = if first {
8647 SegmentKind::Type
8648 } else {
8649 SegmentKind::Nested
8650 };
8651 fq.push(cpp_segment(component, kind));
8652 first = false;
8653 }
8654 fq.push(cpp_segment(&self.name, SegmentKind::Member));
8655 fq
8656 }
8657 Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
8658 .fq()
8659 .clone()
8660 .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
8661 Some(CppMemberOwner::Unit(_)) | None => {
8664 let mut fq = FqName::new();
8665 cpp_push_package(&mut fq, &self.package_name);
8666 fq.push(cpp_segment(&self.name, SegmentKind::Member));
8667 fq
8668 }
8669 };
8670 CodeUnit::with_signature_and_fq(
8671 file,
8672 CodeUnitType::Function,
8673 self.package_name.clone(),
8674 short_name,
8675 Some(self.signature.clone()),
8676 synthetic,
8677 fq,
8678 )
8679 }
8680}
8681
8682fn extract_function_info(
8683 declarator: Node<'_>,
8684 source: &str,
8685 scope: &ScopeInfo,
8686) -> Option<FunctionInfo> {
8687 let parameters_node = declarator.child_by_field_name("parameters")?;
8688 let declarator_name_node = declarator
8689 .child_by_field_name("declarator")
8690 .or_else(|| parameters_node.prev_named_sibling())?;
8691 extract_function_info_from_name(declarator, declarator_name_node, source, scope)
8692}
8693
8694fn extract_function_info_from_name(
8695 declarator: Node<'_>,
8696 declarator_name_node: Node<'_>,
8697 source: &str,
8698 scope: &ScopeInfo,
8699) -> Option<FunctionInfo> {
8700 let parameters_node = declarator.child_by_field_name("parameters")?;
8701 let parameters_text = cpp_parameter_signature(parameters_node, source);
8702 let recovered_specialization_member = scope
8703 .recovered_specialization_member_scope
8704 .then(|| {
8705 let terminal = declarator_name_node
8706 .child_by_field_name("name")
8707 .unwrap_or(declarator_name_node);
8708 let name = canonical_cpp_qualified_component(terminal, source)?.name;
8709 let owner = scope.class_unit.as_ref()?;
8710 Some((
8711 Some(CppMemberOwner::Unit(owner.clone())),
8712 name,
8713 scope.package_name.clone(),
8714 ))
8715 })
8716 .flatten();
8717 let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
8718 parts
8719 } else if let Some(parts) =
8720 split_structured_templated_cpp_name(declarator_name_node, source, scope)
8721 {
8722 parts
8723 } else {
8724 let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
8725 declarator_name_node,
8726 source,
8727 )?);
8728 if raw_name.is_empty() {
8729 return None;
8730 }
8731 split_cpp_name(&raw_name, scope)
8732 };
8733 let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
8734 let mut signature = if suffix.is_empty() {
8735 parameters_text
8736 } else {
8737 format!("{parameters_text} {suffix}")
8738 };
8739 if let Some(template_signature) = &scope.template_signature {
8740 signature = format!("{template_signature}{signature}");
8741 }
8742
8743 Some(FunctionInfo {
8744 package_name,
8745 owner,
8746 name,
8747 signature,
8748 })
8749}
8750
8751fn cpp_macro_displaced_callable_parts<'tree>(
8758 function_declarator: Node<'tree>,
8759 source: &str,
8760 ancestry: &ParentIndex<'tree>,
8761) -> Option<(Node<'tree>, Node<'tree>)> {
8762 let definition = ancestry.parent(function_declarator)?;
8763 if definition.kind() != "function_definition"
8764 || definition.child_by_field_name("declarator") != Some(function_declarator)
8765 || definition
8766 .child_by_field_name("body")
8767 .is_none_or(|body| body.kind() != "compound_statement")
8768 {
8769 return None;
8770 }
8771 let macro_type = definition.child_by_field_name("type")?;
8772 if macro_type.kind() != "type_identifier"
8773 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8774 {
8775 return None;
8776 }
8777
8778 let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
8779 if apparent_return_type.kind() == "qualified_identifier"
8780 && let (Some(return_type), Some(callable_name)) = (
8781 apparent_return_type.child_by_field_name("scope"),
8782 apparent_return_type.child_by_field_name("name"),
8783 )
8784 && return_type.kind() == "template_type"
8785 && matches!(callable_name.kind(), "identifier" | "field_identifier")
8786 && (0..apparent_return_type.child_count())
8787 .filter_map(|index| apparent_return_type.child(index))
8788 .any(|child| child.kind() == "::" && child.is_missing())
8789 && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
8790 && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8791 {
8792 return Some((return_type, callable_name));
8793 }
8794 if !matches!(
8795 apparent_return_type.kind(),
8796 "identifier" | "field_identifier" | "type_identifier"
8797 ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
8798 {
8799 return None;
8800 }
8801 let parameters = function_declarator.child_by_field_name("parameters")?;
8802 let mut cursor = function_declarator.walk();
8803 let between = function_declarator
8804 .named_children(&mut cursor)
8805 .filter(|child| child.kind() != "comment")
8806 .filter(|child| {
8807 child.start_byte() >= apparent_return_type.end_byte()
8808 && child.end_byte() <= parameters.start_byte()
8809 && !same_node(*child, apparent_return_type)
8810 && !same_node(*child, parameters)
8811 })
8812 .collect::<Vec<_>>();
8813 let [name_error] = between.as_slice() else {
8814 return None;
8815 };
8816 if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
8817 return None;
8818 }
8819 let callable_name = name_error.named_child(0)?;
8820 if !matches!(callable_name.kind(), "identifier" | "field_identifier")
8821 || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8822 {
8823 return None;
8824 }
8825 Some((apparent_return_type, callable_name))
8826}
8827
8828fn cpp_declarator_identity_suffix(
8844 declarator: Node<'_>,
8845 parameters_node: Node<'_>,
8846 source: &str,
8847) -> String {
8848 let mut cursor = declarator.walk();
8849 let parts = declarator
8850 .named_children(&mut cursor)
8851 .filter(|child| child.start_byte() >= parameters_node.end_byte())
8852 .filter(|child| {
8853 matches!(
8854 child.kind(),
8855 "type_qualifier"
8856 | "ref_qualifier"
8857 | "noexcept"
8858 | "throw_specifier"
8859 | "trailing_return_type"
8860 | "requires_clause"
8861 )
8862 })
8863 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8864 .filter(|text| !text.is_empty())
8865 .collect::<Vec<_>>();
8866 normalize_cpp_qualifier_suffix(&parts.join(" "))
8867}
8868
8869pub(crate) fn cpp_callable_identity_suffix(
8876 function_declarator: Node<'_>,
8877 source: &str,
8878) -> Option<String> {
8879 let parameters_node = function_declarator.child_by_field_name("parameters")?;
8880 Some(cpp_declarator_identity_suffix(
8881 function_declarator,
8882 parameters_node,
8883 source,
8884 ))
8885}
8886
8887pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
8888 match classify_declarator(node)? {
8889 DeclaratorKind::Function(function_declarator) => Some(function_declarator),
8890 DeclaratorKind::Variable(_) => None,
8891 }
8892}
8893
8894fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
8895 match node.kind() {
8896 "function_declarator" => {
8897 let inner = node
8898 .child_by_field_name("declarator")
8899 .or_else(|| node.child_by_field_name("name"))
8900 .or_else(|| last_named_child(node));
8901 if inner.is_some_and(is_function_pointer_like_inner_declarator) {
8902 Some(DeclaratorKind::Variable(node))
8903 } else {
8904 Some(DeclaratorKind::Function(node))
8905 }
8906 }
8907 "init_declarator"
8908 | "pointer_declarator"
8909 | "reference_declarator"
8910 | "parenthesized_declarator"
8911 | "array_declarator"
8912 | "attributed_declarator"
8913 | "template_function" => node
8914 .child_by_field_name("declarator")
8915 .or_else(|| node.child_by_field_name("name"))
8916 .or_else(|| last_named_child(node))
8917 .and_then(classify_declarator),
8918 "identifier" | "field_identifier" | "qualified_identifier" => {
8919 Some(DeclaratorKind::Variable(node))
8920 }
8921 _ => node
8922 .child_by_field_name("declarator")
8923 .or_else(|| node.child_by_field_name("name"))
8924 .or_else(|| last_named_child(node))
8925 .and_then(classify_declarator),
8926 }
8927}
8928
8929fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
8930 matches!(
8931 node.kind(),
8932 "function_declarator"
8933 | "init_declarator"
8934 | "pointer_declarator"
8935 | "reference_declarator"
8936 | "parenthesized_declarator"
8937 | "array_declarator"
8938 | "attributed_declarator"
8939 | "template_function"
8940 | "identifier"
8941 | "field_identifier"
8942 | "qualified_identifier"
8943 )
8944}
8945
8946fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
8947 let class_like = first_class_like_child(node);
8948 let mut cursor = node.walk();
8949 node.named_children(&mut cursor).any(|child| {
8950 matches!(
8951 child.kind(),
8952 "init_declarator"
8953 | "pointer_declarator"
8954 | "reference_declarator"
8955 | "array_declarator"
8956 | "function_declarator"
8957 | "parenthesized_declarator"
8958 | "attributed_declarator"
8959 ) || matches!(
8960 child.kind(),
8961 "identifier" | "field_identifier" | "qualified_identifier"
8962 ) && class_like.is_none_or(|class_node| {
8963 child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
8964 })
8965 })
8966}
8967
8968struct CppNamespaceForward {
8981 name: String,
8982 start_byte: usize,
8983 namespace_end_byte: usize,
8986 package_name: String,
8987}
8988
8989fn cpp_namespace_forward_entry<'tree>(
8995 node: Node<'tree>,
8996 source: &str,
8997 ancestry: &ParentIndex<'tree>,
8998) -> Option<CppNamespaceForward> {
8999 if !matches!(
9000 node.kind(),
9001 "class_specifier" | "struct_specifier" | "union_specifier"
9002 ) || cpp_body_node(node).is_some()
9003 {
9004 return None;
9005 }
9006 let parent = node.parent()?;
9007 if !(parent.kind() == "declaration_list"
9008 || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
9009 {
9010 return None;
9011 }
9012 let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
9013 if !namespace.has_error() {
9018 return None;
9019 }
9020 Some(CppNamespaceForward {
9021 name: class_like_name(node, source, ancestry)?,
9022 start_byte: node.start_byte(),
9023 namespace_end_byte: namespace.end_byte(),
9024 package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
9025 })
9026}
9027
9028fn cpp_namespace_forward_matches_recovery(
9032 forward: &CppNamespaceForward,
9033 recovered_node: Node<'_>,
9034) -> bool {
9035 forward.start_byte < recovered_node.start_byte()
9036 && forward.namespace_end_byte < recovered_node.start_byte()
9037 && malformed_namespace_is_nearest_recovery_region(
9038 forward.namespace_end_byte,
9039 recovered_node,
9040 )
9041}
9042
9043#[derive(Debug, Default)]
9059pub struct CppRecoveryCapture {
9060 created: Vec<CodeUnit>,
9062 created_units: HashSet<CodeUnit>,
9064 removed_pre_existing: HashSet<CodeUnit>,
9066}
9067
9068#[derive(Debug, Default)]
9080pub struct CppFieldOwnerIndex {
9081 owners: HashMap<String, HashSet<String>>,
9083 ownerless_packages: HashSet<String>,
9085}
9086
9087impl CppFieldOwnerIndex {
9088 fn of<'unit>(
9091 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9092 file: &ProjectFile,
9093 ) -> Self {
9094 let mut index = Self::default();
9095 for declaration in declarations {
9096 index.record(declaration, file);
9097 }
9098 index
9099 }
9100
9101 fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
9102 if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
9103 return;
9104 }
9105 let short_name = code_unit.short_name();
9106 let package_name = code_unit.package_name();
9107 if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
9108 self.ownerless_packages.insert(package_name.to_string());
9109 }
9110 if !short_name.contains('.') {
9111 return;
9112 }
9113 if !self.owners.contains_key(package_name) {
9114 self.owners
9115 .insert(package_name.to_string(), HashSet::default());
9116 }
9117 let owners = self
9118 .owners
9119 .get_mut(package_name)
9120 .expect("the package entry was just ensured");
9121 for (offset, _) in short_name.match_indices('.') {
9122 let owner = &short_name[..offset];
9123 if !owners.contains(owner) {
9124 owners.insert(owner.to_string());
9125 }
9126 }
9127 }
9128
9129 fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
9132 if owner_short_name.is_empty() {
9133 self.ownerless_packages.contains(package_name)
9134 } else {
9135 self.owners
9136 .get(package_name)
9137 .is_some_and(|owners| owners.contains(owner_short_name))
9138 }
9139 }
9140}
9141
9142#[cfg(any(debug_assertions, test))]
9146fn cpp_declarations_hold_owned_fields<'unit>(
9147 declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9148 file: &ProjectFile,
9149 package_name: &str,
9150 owner_short_name: &str,
9151) -> bool {
9152 let prefix = format!("{owner_short_name}.");
9153 declarations.into_iter().any(|unit| {
9154 unit.kind() == CodeUnitType::Field
9155 && unit.source() == file
9156 && unit.package_name() == package_name
9157 && if owner_short_name.is_empty() {
9158 !unit.short_name().contains(['.', '$'])
9162 } else {
9163 unit.short_name().starts_with(&prefix)
9164 }
9165 })
9166}
9167
9168#[derive(PartialEq, Eq, Hash)]
9176pub struct CppTreeIdentity {
9177 root_id: usize,
9178 start_byte: usize,
9179 end_byte: usize,
9180 kind_id: u16,
9181 child_count: usize,
9182}
9183
9184impl CppTreeIdentity {
9185 fn of(root: Node<'_>) -> Self {
9186 Self {
9187 root_id: root.id(),
9188 start_byte: root.start_byte(),
9189 end_byte: root.end_byte(),
9190 kind_id: root.kind_id(),
9191 child_count: root.child_count(),
9192 }
9193 }
9194}
9195
9196#[derive(Default)]
9210pub struct CppNamespaceForwardScan {
9211 scanned_through: usize,
9213 forwards: HashMap<String, Vec<CppNamespaceForward>>,
9214}
9215
9216impl CppNamespaceForwardScan {
9217 fn advance_to<'tree>(
9224 &mut self,
9225 root: Node<'tree>,
9226 cutoff: usize,
9227 source: &str,
9228 ancestry: &ParentIndex<'tree>,
9229 ) {
9230 if cutoff <= self.scanned_through {
9231 return;
9232 }
9233 let folded_through = self.scanned_through;
9234 let mut cursor = root.walk();
9235 let mut stack = vec![root];
9236 while let Some(current) = stack.pop() {
9237 if (folded_through..cutoff).contains(¤t.start_byte())
9238 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9239 {
9240 self.forwards
9241 .entry(forward.name.clone())
9242 .or_default()
9243 .push(forward);
9244 }
9245 for child in current.named_children(&mut cursor) {
9250 if child.start_byte() < cutoff && child.end_byte() >= folded_through {
9251 stack.push(child);
9252 }
9253 }
9254 }
9255 self.scanned_through = cutoff;
9256 }
9257
9258 fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
9262 let mut matching = self
9263 .forwards
9264 .get(name)
9265 .into_iter()
9266 .flatten()
9267 .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
9268 let first = matching.next()?;
9269 matching
9270 .next()
9271 .is_none()
9272 .then(|| first.package_name.clone())
9273 }
9274}
9275
9276#[cfg(any(debug_assertions, test))]
9281fn unique_earlier_cpp_namespace_forward<'tree>(
9282 recovered_node: Node<'tree>,
9283 name: &str,
9284 source: &str,
9285 ancestry: &ParentIndex<'tree>,
9286) -> Option<String> {
9287 let mut root = recovered_node;
9288 while let Some(parent) = ancestry.parent(root) {
9289 root = parent;
9290 }
9291
9292 let mut candidates = Vec::new();
9293 let mut stack = vec![root];
9294 while let Some(current) = stack.pop() {
9295 if current.start_byte() < recovered_node.start_byte()
9296 && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9297 && forward.name == name
9298 && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
9299 {
9300 candidates.push(forward.package_name);
9301 }
9302
9303 let mut cursor = current.walk();
9304 for child in current.named_children(&mut cursor) {
9305 if child.start_byte() < recovered_node.start_byte() {
9306 stack.push(child);
9307 }
9308 }
9309 }
9310
9311 if candidates.len() == 1 {
9312 candidates.pop()
9313 } else {
9314 None
9315 }
9316}
9317
9318fn malformed_namespace_is_nearest_recovery_region(
9319 namespace_end_byte: usize,
9320 recovered_node: Node<'_>,
9321) -> bool {
9322 let mut root = recovered_node;
9323 while let Some(parent) = root.parent() {
9324 root = parent;
9325 }
9326 let mut cursor = root.walk();
9327 root.named_children(&mut cursor)
9328 .filter(|sibling| {
9329 namespace_end_byte <= sibling.start_byte()
9330 && sibling.end_byte() <= recovered_node.start_byte()
9331 })
9332 .all(is_malformed_namespace_recovery_trivia)
9333}
9334
9335fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
9336 matches!(node.kind(), "ERROR" | "comment")
9337 || node.kind().starts_with("preproc_")
9338 || node.kind() == "expression_statement" && node.named_child_count() == 0
9339}
9340
9341fn cpp_namespace_name_for_forward<'tree>(
9345 node: Node<'tree>,
9346 source: &str,
9347 ancestry: &ParentIndex<'tree>,
9348) -> Option<String> {
9349 cpp_namespace_definition_for_forward(node, ancestry)?;
9350 cpp_lexical_namespace_name(node, source, ancestry)
9351}
9352
9353fn cpp_namespace_definition_for_forward<'tree>(
9354 node: Node<'tree>,
9355 ancestry: &ParentIndex<'tree>,
9356) -> Option<Node<'tree>> {
9357 let declaration = ancestry.parent(node)?;
9358 let mut ancestor = ancestry.parent(declaration);
9359 while let Some(current) = ancestor {
9360 if matches!(
9361 current.kind(),
9362 "compound_statement"
9363 | "field_declaration_list"
9364 | "class_specifier"
9365 | "struct_specifier"
9366 | "union_specifier"
9367 | "function_definition"
9368 | "lambda_expression"
9369 ) {
9370 return None;
9371 }
9372 if current.kind() == "namespace_definition" {
9373 return Some(current);
9374 }
9375 ancestor = ancestry.parent(current);
9376 }
9377 None
9378}
9379
9380fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
9381 match node.kind() {
9382 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9383 "parenthesized_declarator" => node
9384 .child_by_field_name("declarator")
9385 .or_else(|| last_named_child(node))
9386 .is_some_and(is_pointer_wrapper_declarator),
9387 "template_function" => node
9388 .child_by_field_name("name")
9389 .is_some_and(is_function_pointer_like_inner_declarator),
9390 _ => false,
9391 }
9392}
9393
9394fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
9395 match node.kind() {
9396 "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9397 "parenthesized_declarator" => node
9398 .child_by_field_name("declarator")
9399 .or_else(|| last_named_child(node))
9400 .is_some_and(is_pointer_wrapper_declarator),
9401 _ => false,
9402 }
9403}
9404
9405fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
9406 let cleaned = raw_name.trim_start_matches("template ").trim();
9407 let cleaned = cleaned.trim_start_matches("::");
9414 let parts: Vec<_> = cleaned
9423 .split("::")
9424 .filter(|component| !component.is_empty())
9425 .collect();
9426 if parts.is_empty() {
9427 return (None, cleaned.to_string(), scope.package_name.clone());
9428 }
9429 if parts.len() > 1 {
9430 let name = parts.last().unwrap_or(&cleaned).to_string();
9431 let owner_parts = &parts[..parts.len() - 1];
9432 if let Some(class_unit) = &scope.class_unit {
9433 return (
9436 Some(CppMemberOwner::Unit(class_unit.clone())),
9437 name,
9438 scope.package_name.clone(),
9439 );
9440 }
9441 if !scope.package_name.is_empty() {
9442 let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
9457 let owner = (!nested.is_empty()).then(|| {
9458 CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
9459 });
9460 return (owner, name, scope.package_name.clone());
9461 }
9462 let (owner, package_name) = if owner_parts.len() > 1 {
9464 (
9476 Some(CppMemberOwner::Chain(vec![
9477 owner_parts.last().unwrap_or(&"").to_string(),
9478 ])),
9479 owner_parts[..owner_parts.len() - 1].join("::"),
9480 )
9481 } else {
9482 (
9494 Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
9495 cpp_using_directive_namespace_for_bare_owner(scope),
9496 )
9497 };
9498 return (owner, name, package_name);
9499 }
9500
9501 let package_name = scope.package_name.clone();
9502 let owner = scope
9503 .class_unit
9504 .as_ref()
9505 .map(|parent| CppMemberOwner::Unit(parent.clone()));
9506 (owner, cleaned.to_string(), package_name)
9507}
9508
9509fn strip_redundant_namespace_prefix<'a>(
9523 owner_parts: &'a [&'a str],
9524 package_name: &str,
9525) -> &'a [&'a str] {
9526 if package_name.is_empty() {
9527 return owner_parts;
9528 }
9529 let package_segments: Vec<&str> = package_name.split("::").collect();
9530 let max_prefix = owner_parts.len().min(package_segments.len());
9531 for prefix_len in (1..=max_prefix).rev() {
9532 let package_suffix = &package_segments[package_segments.len() - prefix_len..];
9533 if &owner_parts[..prefix_len] == package_suffix {
9534 return &owner_parts[prefix_len..];
9535 }
9536 }
9537 owner_parts
9538}
9539
9540fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
9551 scope
9552 .visible_using_namespaces
9553 .iter()
9554 .min_by_key(|namespace| namespace.split("::").count())
9555 .cloned()
9556 .unwrap_or_default()
9557}
9558
9559struct CppQualifiedNameComponent {
9560 name: String,
9561 is_template_id: bool,
9562}
9563
9564fn qualified_class_name_chain(
9577 class_node: Node<'_>,
9578 source: &str,
9579 scope: &ScopeInfo,
9580) -> Option<Vec<String>> {
9581 if scope.package_name.is_empty() || scope.class_unit.is_some() {
9582 return None;
9583 }
9584 let name = class_node.child_by_field_name("name")?;
9585 let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
9586 if explicitly_global
9587 || components.len() < 2
9588 || components.iter().any(|component| component.is_template_id)
9589 {
9590 return None;
9591 }
9592 let names = components
9593 .iter()
9594 .map(|component| component.name.as_str())
9595 .collect::<Vec<_>>();
9596 let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
9597 if class_chain.is_empty() {
9598 return None;
9599 }
9600 Some(class_chain.iter().map(|name| name.to_string()).collect())
9601}
9602
9603fn structured_cpp_qualified_components(
9604 qualified_name: Node<'_>,
9605 source: &str,
9606) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
9607 if qualified_name.kind() != "qualified_identifier" {
9608 return None;
9609 }
9610
9611 let mut components = Vec::new();
9612 let mut current = qualified_name;
9613 let mut explicitly_global = false;
9614 loop {
9615 if current.kind() == "qualified_identifier" {
9616 if let Some(component) = current.child_by_field_name("scope") {
9617 components.push(canonical_cpp_qualified_component(component, source)?);
9618 } else if components.is_empty() {
9619 explicitly_global = true;
9620 } else {
9621 return None;
9622 }
9623 current = current.child_by_field_name("name")?;
9624 } else {
9625 components.push(canonical_cpp_qualified_component(current, source)?);
9626 break;
9627 }
9628 }
9629 Some((components, explicitly_global))
9630}
9631
9632fn split_structured_templated_cpp_name(
9633 declarator_name: Node<'_>,
9634 source: &str,
9635 scope: &ScopeInfo,
9636) -> Option<(Option<CppMemberOwner>, String, String)> {
9637 let (mut components, explicitly_global) =
9638 structured_cpp_qualified_components(declarator_name, source)?;
9639
9640 let terminal = components.pop()?;
9641 let owner_start = components
9642 .iter()
9643 .position(|component| component.is_template_id)?;
9644 let explicit_package = components[..owner_start]
9645 .iter()
9646 .map(|component| component.name.as_str())
9647 .collect::<Vec<_>>()
9648 .join("::");
9649 let explicit_package_is_empty = explicit_package.is_empty();
9650 let package_name = match (
9651 explicitly_global,
9652 scope.package_name.is_empty(),
9653 explicit_package_is_empty,
9654 ) {
9655 (true, _, _) => explicit_package,
9656 (false, _, true) => scope.package_name.clone(),
9657 (false, true, false) => explicit_package,
9658 (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
9659 };
9660 let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
9666 {
9667 cpp_using_directive_namespace_for_bare_owner(scope)
9668 } else {
9669 package_name
9670 };
9671 let owner_chain = components[owner_start..]
9672 .iter()
9673 .map(|component| component.name.clone())
9674 .collect::<Vec<_>>();
9675 if owner_chain.is_empty() || terminal.name.is_empty() {
9676 return None;
9677 }
9678
9679 Some((
9680 Some(CppMemberOwner::Chain(owner_chain)),
9681 terminal.name,
9682 package_name,
9683 ))
9684}
9685
9686fn canonical_cpp_qualified_component(
9687 mut component: Node<'_>,
9688 source: &str,
9689) -> Option<CppQualifiedNameComponent> {
9690 let mut is_template_id = false;
9691 loop {
9692 match component.kind() {
9693 "template_type" => {
9694 is_template_id = true;
9695 component = component.child_by_field_name("name")?;
9696 }
9697 "dependent_name" => component = component.named_child(0)?,
9698 "identifier"
9699 | "field_identifier"
9700 | "namespace_identifier"
9701 | "type_identifier"
9702 | "operator_name"
9703 | "destructor_name" => {
9704 let name = normalize_cpp_whitespace(node_text(component, source));
9705 return (!name.is_empty()).then_some(CppQualifiedNameComponent {
9706 name,
9707 is_template_id,
9708 });
9709 }
9710 _ => component = component.child_by_field_name("name")?,
9711 }
9712 }
9713}
9714
9715fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
9716 if let Some(name) = macro_decorated_unqualified_name(node) {
9717 return extract_declarator_name(name, source);
9718 }
9719 match node.kind() {
9720 "identifier"
9721 | "field_identifier"
9722 | "type_identifier"
9723 | "operator_name"
9724 | "destructor_name"
9725 | "qualified_identifier" => node_text(node, source).to_string(),
9726 "function_declarator"
9727 | "pointer_declarator"
9728 | "reference_declarator"
9729 | "parenthesized_declarator"
9730 | "array_declarator"
9731 | "template_function" => node
9732 .child_by_field_name("declarator")
9733 .or_else(|| node.child_by_field_name("name"))
9734 .or_else(|| last_named_child(node))
9735 .map(|child| extract_declarator_name(child, source))
9736 .unwrap_or_else(|| node_text(node, source).to_string()),
9737 _ => node
9738 .child_by_field_name("name")
9739 .map(|child| extract_declarator_name(child, source))
9740 .unwrap_or_else(|| node_text(node, source).to_string()),
9741 }
9742}
9743
9744fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
9749 if let Some(name) = macro_decorated_unqualified_name(node) {
9750 return extract_callable_declarator_name(name, source);
9751 }
9752 match node.kind() {
9753 "identifier"
9754 | "field_identifier"
9755 | "type_identifier"
9756 | "operator_name"
9757 | "destructor_name"
9758 | "qualified_identifier" => Some(node_text(node, source).to_string()),
9759 "function_declarator"
9760 | "pointer_declarator"
9761 | "reference_declarator"
9762 | "parenthesized_declarator"
9763 | "array_declarator"
9764 | "template_function" => node
9765 .child_by_field_name("declarator")
9766 .or_else(|| node.child_by_field_name("name"))
9767 .and_then(|child| extract_callable_declarator_name(child, source)),
9768 _ => None,
9769 }
9770}
9771
9772fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
9773 match node.kind() {
9774 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
9775 let name = node_text(node, source).trim().to_string();
9776 (!name.is_empty()).then_some(name)
9777 }
9778 _ => node
9779 .child_by_field_name("declarator")
9780 .or_else(|| node.child_by_field_name("name"))
9781 .or_else(|| last_named_child(node))
9782 .and_then(|child| extract_variable_name(child, source)),
9783 }
9784}
9785
9786#[derive(Clone, Copy)]
9792pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
9793 pub(crate) name: Node<'tree>,
9794 pub(crate) declarator: Node<'tree>,
9795}
9796
9797impl RecoveredFunctionLikeFieldDeclarator<'_> {
9798 pub(crate) fn pointer_depth(self) -> i32 {
9799 let mut depth = 0;
9800 let mut current = self.declarator;
9801 while current.kind() != "function_declarator" {
9802 if current.kind() == "pointer_declarator" {
9803 depth += 1;
9804 }
9805 current = current
9806 .child_by_field_name("declarator")
9807 .expect("recovered field wrapper has an inner declarator");
9808 }
9809 depth
9810 }
9811}
9812
9813pub(crate) fn recovered_function_like_field_declarator<'tree>(
9814 node: Node<'tree>,
9815 source: &str,
9816) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
9817 if node.kind() != "field_declaration" {
9818 return None;
9819 }
9820 let outer_declarator = node.child_by_field_name("declarator")?;
9821 let mut declarator = outer_declarator;
9822 while matches!(
9823 declarator.kind(),
9824 "pointer_declarator"
9825 | "reference_declarator"
9826 | "array_declarator"
9827 | "parenthesized_declarator"
9828 ) {
9829 declarator = declarator.child_by_field_name("declarator")?;
9830 }
9831 if declarator.kind() != "function_declarator" {
9832 return None;
9833 }
9834 let macro_name = declarator.child_by_field_name("declarator")?;
9835 if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
9836 {
9837 return None;
9838 }
9839 let parameters = declarator.child_by_field_name("parameters")?;
9840 let mut cursor = parameters.walk();
9841 let mut arguments = parameters.named_children(&mut cursor);
9842 let parameter = arguments.next()?;
9843 if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
9844 return None;
9845 }
9846 let name = parameter.child_by_field_name("type").filter(|argument| {
9847 matches!(
9848 argument.kind(),
9849 "identifier" | "field_identifier" | "type_identifier"
9850 )
9851 })?;
9852 Some(RecoveredFunctionLikeFieldDeclarator {
9853 name,
9854 declarator: outer_declarator,
9855 })
9856}
9857
9858fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
9859 let count = node.named_child_count();
9860 if count == 0 {
9861 None
9862 } else {
9863 node.named_child(count - 1)
9864 }
9865}
9866
9867fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
9868 let name_node = node.child_by_field_name("name")?;
9869 let name = normalize_cpp_whitespace(node_text(name_node, source));
9870 (!name.is_empty()).then_some(name)
9871}
9872
9873fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
9874 if node.kind() != "declaration" {
9875 return Vec::new();
9876 }
9877 let Some(keyword) = node.child_by_field_name("type").filter(|node| {
9878 node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
9879 }) else {
9880 return Vec::new();
9881 };
9882 let Some(declarator) = node.child_by_field_name("declarator") else {
9883 return Vec::new();
9884 };
9885 if node_text(keyword, source) == "using"
9886 && (declarator.kind() != "init_declarator"
9887 || declarator.child_by_field_name("value").is_none())
9888 {
9889 return Vec::new();
9890 }
9891 if node_text(keyword, source) == "typedef"
9892 && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
9893 {
9894 return vec![alias_name];
9895 }
9896 extract_typedef_declarator_name(declarator, source)
9897 .into_iter()
9898 .collect()
9899}
9900
9901fn recovered_typedef_error_alias_name(
9902 declaration: Node<'_>,
9903 declarator: Node<'_>,
9904 source: &str,
9905) -> Option<String> {
9906 if declarator.kind() != "qualified_identifier" {
9916 return None;
9917 }
9918 let mut cursor = declaration.walk();
9919 let mut errors = declaration
9920 .named_children(&mut cursor)
9921 .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
9922 let error = errors.next()?;
9923 if errors.next().is_some() || error.named_child_count() != 1 {
9924 return None;
9925 }
9926 let name = error.named_child(0)?;
9927 if !matches!(
9928 name.kind(),
9929 "identifier" | "field_identifier" | "type_identifier"
9930 ) {
9931 return None;
9932 }
9933 let name = normalize_cpp_whitespace(node_text(name, source));
9934 (!name.is_empty()).then_some(name)
9935}
9936
9937fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
9938 if fragmented_parenthesized_typedef_type(node).is_some() {
9942 return Vec::new();
9943 }
9944 let has_function_like_macro_type = node
9945 .child_by_field_name("type")
9946 .filter(|type_node| type_node.kind() == "type_identifier")
9947 .is_some_and(|type_node| {
9948 cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
9949 });
9950 let mut names = Vec::new();
9951 let mut cursor = node.walk();
9952 for declarator in node.children_by_field_name("declarator", &mut cursor) {
9953 if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
9954 continue;
9955 }
9956 if let Some(name) = extract_typedef_declarator_name(declarator, source)
9957 && !names.contains(&name)
9958 {
9959 names.push(name);
9960 }
9961 }
9962 names
9963}
9964
9965struct RecoveredMacroTypedefAlias<'tree> {
9966 name: String,
9967 end_node: Node<'tree>,
9968}
9969
9970fn recovered_macro_typedef_alias<'tree>(
9974 node: Node<'tree>,
9975 source: &str,
9976) -> Option<RecoveredMacroTypedefAlias<'tree>> {
9977 let type_node = fragmented_parenthesized_typedef_type(node)?;
9978 if type_node.kind() != "type_identifier"
9979 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
9980 {
9981 return None;
9982 }
9983
9984 let end_node = node.next_named_sibling()?;
9985 if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
9986 return None;
9987 }
9988 let name_node = end_node.named_child(0)?;
9989 if name_node.kind() != "identifier" {
9990 return None;
9991 }
9992 let has_terminator = (0..end_node.child_count()).any(|index| {
9993 end_node
9994 .child(index)
9995 .is_some_and(|child| child.kind() == ";" && !child.is_missing())
9996 });
9997 if !has_terminator {
9998 return None;
9999 }
10000 let name = normalize_cpp_whitespace(node_text(name_node, source));
10001 (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
10002}
10003
10004fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
10005 if node.kind() != "type_definition" {
10006 return None;
10007 }
10008 let mut declarator_cursor = node.walk();
10009 let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
10010 if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
10011 return None;
10012 }
10013 let has_missing_terminator = (0..node.child_count()).any(|index| {
10014 node.child(index)
10015 .is_some_and(|child| child.kind() == ";" && child.is_missing())
10016 });
10017 if !has_missing_terminator {
10018 return None;
10019 }
10020 node.child_by_field_name("type")
10021}
10022
10023fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
10024 match node.kind() {
10025 "identifier" | "field_identifier" | "type_identifier" => {
10026 let name = normalize_cpp_whitespace(node_text(node, source));
10027 (!name.is_empty()).then_some(name)
10028 }
10029 "qualified_identifier" => node
10030 .child_by_field_name("name")
10031 .and_then(|name| extract_typedef_declarator_name(name, source)),
10032 _ => node
10033 .child_by_field_name("declarator")
10034 .or_else(|| node.child_by_field_name("name"))
10035 .or_else(|| last_named_child(node))
10036 .and_then(|child| extract_typedef_declarator_name(child, source)),
10037 }
10038}
10039
10040fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
10041 let name = node
10042 .child_by_field_name("name")
10043 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10044 .or_else(|| {
10045 let mut cursor = node.walk();
10046 node.named_children(&mut cursor)
10047 .find(|child| {
10048 matches!(
10049 child.kind(),
10050 "identifier" | "field_identifier" | "type_identifier"
10051 )
10052 })
10053 .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10054 })?;
10055 (!name.is_empty()).then_some(name)
10056}
10057
10058fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
10059 left.id() == right.id()
10060}
10061
10062fn render_cpp_type_signature(
10063 node: Node<'_>,
10064 source: &str,
10065 template_signature: Option<&str>,
10066) -> String {
10067 let text = normalize_cpp_whitespace(node_text(node, source));
10068 let head = text.split('{').next().unwrap_or(text.as_str()).trim();
10069 let rendered = if head.ends_with(';') {
10070 head.to_string()
10071 } else {
10072 format!("{head} {{")
10073 };
10074 if let Some(template_signature) = template_signature {
10075 format!("template {template_signature} {rendered}")
10076 } else {
10077 rendered
10078 }
10079}
10080
10081fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
10082 if let Some(recovered) = recovered_pyobject_head_field(node, source)
10083 && recovered.declarator == declarator
10084 {
10085 let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
10086 let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
10087 return format!("{type_text} {declarator};");
10088 }
10089 if let Some(recovered) = recovered_function_like_field_declarator(node, source)
10090 && recovered.name == declarator
10091 {
10092 let type_text = node
10093 .child_by_field_name("type")
10094 .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
10095 .unwrap_or_default();
10096 let name = normalize_cpp_whitespace(node_text(recovered.name, source));
10097 let mut prefix = String::new();
10098 let mut suffix = String::new();
10099 let mut current = recovered.declarator;
10100 while current.kind() != "function_declarator" {
10101 match current.kind() {
10102 "pointer_declarator" => prefix.push('*'),
10103 "reference_declarator" => prefix.push('&'),
10104 "array_declarator" => {
10105 let size = current
10106 .child_by_field_name("size")
10107 .map(|size| normalize_cpp_whitespace(node_text(size, source)))
10108 .unwrap_or_default();
10109 suffix.push('[');
10110 suffix.push_str(&size);
10111 suffix.push(']');
10112 }
10113 "parenthesized_declarator" => {}
10114 _ => unreachable!("validated recovered field declarator wrapper"),
10115 }
10116 current = current
10117 .child_by_field_name("declarator")
10118 .expect("recovered field wrapper has an inner declarator");
10119 }
10120 let separator = if prefix.is_empty() { "" } else { " " };
10121 return format!("{type_text} {prefix}{separator}{name}{suffix};");
10122 }
10123 if let Some(signature) =
10124 render_recovered_macro_qualified_field_signature(node, declarator, source)
10125 {
10126 return signature;
10127 }
10128 let declaration_text = normalize_cpp_whitespace(node_text(node, source));
10129 let prefix = cpp_declaration_prefix(node, source);
10130 let name = extract_variable_name(declarator, source).unwrap_or_default();
10131 let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
10132 let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
10133 || (prefix.ends_with('&') && raw_suffix == "&")
10134 {
10135 String::new()
10136 } else {
10137 raw_suffix
10138 };
10139
10140 let mut rendered = if suffix.is_empty() {
10141 format!("{prefix} {name}")
10142 } else if suffix.starts_with('*') || suffix.starts_with('&') {
10143 format!("{prefix}{suffix} {name}")
10144 } else if suffix.starts_with('[') || suffix.starts_with('(') {
10145 format!("{prefix} {name}{suffix}")
10146 } else {
10147 format!("{prefix} {suffix}{name}")
10148 };
10149 rendered = collapse_cpp_whitespace(&rendered);
10150
10151 if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10152 format!("{rendered} = {initializer};")
10153 } else if declaration_text.ends_with(';') {
10154 format!("{rendered};")
10155 } else {
10156 rendered
10157 }
10158}
10159
10160fn render_recovered_macro_qualified_field_signature(
10161 node: Node<'_>,
10162 declarator: Node<'_>,
10163 source: &str,
10164) -> Option<String> {
10165 let recovered = recovered_macro_qualified_field_declarators(node, source)?;
10166 if !recovered
10167 .iter()
10168 .any(|candidate| same_node(*candidate, declarator))
10169 {
10170 return None;
10171 }
10172 let pseudo_declarator = node.child_by_field_name("declarator")?;
10173 let mut cursor = node.walk();
10174 let clause = node
10175 .named_children(&mut cursor)
10176 .find(|child| child.kind() == "bitfield_clause")?;
10177 let mut cursor = clause.walk();
10178 let error = clause
10179 .named_children(&mut cursor)
10180 .find(|child| child.kind() == "ERROR")?;
10181 let qualified_type =
10182 normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
10183 let prefix = cpp_declaration_prefix(node, source);
10184 let name = extract_variable_name(declarator, source)?;
10185 let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
10186 let mut rendered = if suffix.is_empty() {
10187 format!("{prefix} {qualified_type} {name}")
10188 } else {
10189 format!("{prefix} {qualified_type} {suffix} {name}")
10190 };
10191 rendered = collapse_cpp_whitespace(&rendered);
10192
10193 if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
10194 Some(format!(
10195 "{rendered} = {};",
10196 normalize_cpp_whitespace(node_text(initializer, source))
10197 ))
10198 } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10199 Some(format!("{rendered} = {initializer};"))
10200 } else {
10201 Some(format!("{rendered};"))
10202 }
10203}
10204
10205fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
10206 match node.kind() {
10207 "pointer_expression" => {
10208 let operator = node
10209 .child_by_field_name("operator")
10210 .or_else(|| node.child(0))
10211 .map(|operator| node_text(operator, source))
10212 .unwrap_or("*");
10213 let argument = node
10214 .child_by_field_name("argument")
10215 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10216 .unwrap_or_default();
10217 format!("{operator}{argument}")
10218 }
10219 "unary_expression" => {
10220 let operator = node
10221 .child_by_field_name("operator")
10222 .or_else(|| node.child(0))
10223 .map(|operator| node_text(operator, source))
10224 .unwrap_or_default();
10225 let argument = node
10226 .child_by_field_name("argument")
10227 .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10228 .unwrap_or_default();
10229 format!("{operator}{argument}")
10230 }
10231 "identifier" | "field_identifier" => String::new(),
10232 _ => cpp_declarator_suffix_without_name(node, source),
10233 }
10234}
10235
10236fn recovered_macro_qualified_field_initializer<'tree>(
10237 clause: Node<'tree>,
10238 declarator: Node<'tree>,
10239) -> Option<Node<'tree>> {
10240 let mut stack = vec![clause];
10241 while let Some(current) = stack.pop() {
10242 if current.kind() == "assignment_expression"
10243 && current
10244 .child_by_field_name("left")
10245 .is_some_and(|left| same_node(left, declarator))
10246 {
10247 return current.child_by_field_name("right");
10248 }
10249 let mut cursor = current.walk();
10250 stack.extend(current.named_children(&mut cursor));
10251 }
10252 None
10253}
10254
10255fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
10256 let text = node_text(node, source);
10257 let mut cursor = node.walk();
10258 let first_declarator = node.named_children(&mut cursor).find(|child| {
10259 matches!(
10260 child.kind(),
10261 "init_declarator"
10262 | "identifier"
10263 | "field_identifier"
10264 | "pointer_declarator"
10265 | "reference_declarator"
10266 | "array_declarator"
10267 | "function_declarator"
10268 )
10269 });
10270 let prefix = if let Some(first_declarator) = first_declarator {
10271 let end = first_declarator
10272 .start_byte()
10273 .saturating_sub(node.start_byte());
10274 let mut prefix = text.get(..end).unwrap_or(text).to_string();
10275 let declarator_suffix = match first_declarator.kind() {
10276 "init_declarator" => first_declarator
10277 .child_by_field_name("declarator")
10278 .map(|inner| cpp_declarator_suffix_without_name(inner, source))
10279 .unwrap_or_default(),
10280 _ => cpp_declarator_suffix_without_name(first_declarator, source),
10281 };
10282 if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
10283 prefix.push_str(&declarator_suffix);
10284 }
10285 return collapse_cpp_whitespace(&prefix)
10286 .trim_end_matches(',')
10287 .trim_end_matches(';')
10288 .trim()
10289 .to_string();
10290 } else {
10291 text
10292 };
10293 collapse_cpp_whitespace(prefix)
10294 .trim_end_matches(',')
10295 .trim_end_matches(';')
10296 .trim()
10297 .to_string()
10298}
10299
10300fn cpp_preserved_initializer(
10301 declaration_node: Node<'_>,
10302 declarator: Node<'_>,
10303 source: &str,
10304) -> Option<String> {
10305 let name = extract_variable_name(declarator, source)?;
10306 let mut cursor = declaration_node.walk();
10307 for child in declaration_node.named_children(&mut cursor) {
10308 if child.kind() != "init_declarator" {
10309 continue;
10310 }
10311 let Some(inner) = child.child_by_field_name("declarator") else {
10312 continue;
10313 };
10314 if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
10315 continue;
10316 }
10317 let value = child.child_by_field_name("value")?;
10318 let kind = value.kind();
10319 if matches!(
10320 kind,
10321 "number_literal" | "float_literal" | "char_literal" | "true" | "false"
10322 ) {
10323 return Some(normalize_cpp_whitespace(node_text(value, source)));
10324 }
10325 break;
10326 }
10327 let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
10328 let pattern = format!(
10329 r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
10330 regex::escape(&name)
10331 );
10332 Regex::new(&pattern)
10333 .ok()
10334 .and_then(|regex| regex.captures(&declaration_text))
10335 .and_then(|captures| captures.get(1))
10336 .map(|value| value.as_str().to_string())
10337}
10338
10339fn render_cpp_function_display_signature_from_node<'tree>(
10340 node: Node<'tree>,
10341 source: &str,
10342 template_signature: Option<&str>,
10343 has_body: bool,
10344 ancestry: &ParentIndex<'tree>,
10345) -> String {
10346 let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
10347 let parent_text = node_text(root, source);
10348 let body_local_start = root
10349 .child_by_field_name("body")
10350 .map(|body| body.start_byte().saturating_sub(root.start_byte()))
10351 .unwrap_or(parent_text.len());
10352 let display = parent_text
10353 .get(..body_local_start)
10354 .unwrap_or(parent_text)
10355 .trim()
10356 .trim();
10357 let display = if let Some(template_signature) = template_signature {
10358 if display.starts_with("template ") {
10359 display.to_string()
10360 } else {
10361 format!("template {template_signature} {display}")
10362 }
10363 } else {
10364 display.to_string()
10365 };
10366 let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
10367 if has_body {
10368 format!("{display} {{...}}")
10369 } else {
10370 format!("{display};")
10371 }
10372}
10373
10374fn cpp_template_signature(
10375 template_node: Node<'_>,
10376 declaration_child: Node<'_>,
10377 source: &str,
10378) -> Option<String> {
10379 let text = source
10380 .get(template_node.start_byte()..declaration_child.start_byte())
10381 .unwrap_or("");
10382 let text = normalize_cpp_whitespace(text);
10383 let start = text.find('<')?;
10384 let end = text.rfind('>')?;
10385 if end < start {
10386 return None;
10387 }
10388 Some(text[start..=end].to_string())
10389}
10390
10391struct RecoveredFragmentedPartialSpecialization<'tree> {
10392 declaration_node: Node<'tree>,
10393 name: String,
10394 range: Range,
10395 prefix_members: Vec<Node<'tree>>,
10396 member_siblings: Vec<Node<'tree>>,
10397 following_declarations: Vec<Node<'tree>>,
10398}
10399
10400struct RecoveredFragmentedPreprocessorClass<'tree> {
10401 declaration_node: Node<'tree>,
10402 class_node: Node<'tree>,
10403 body: Node<'tree>,
10404 name: String,
10405 range: Range,
10406 tail_members: Vec<Node<'tree>>,
10407 member_siblings: Vec<Node<'tree>>,
10408}
10409
10410fn recover_fragmented_preprocessor_class<'tree>(
10419 template_node: Node<'tree>,
10420 source: &str,
10421 ancestry: &ParentIndex<'tree>,
10422) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
10423 let alternative = ancestry.parent(template_node)?;
10424 if alternative.kind() != "preproc_else" {
10425 return None;
10426 }
10427 let conditional = alternative.parent()?;
10428 if conditional.kind() != "preproc_if" {
10429 return None;
10430 }
10431 let declaration_node = template_node
10432 .named_children(&mut template_node.walk())
10433 .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
10434 let class_node = declaration_node
10435 .named_children(&mut declaration_node.walk())
10436 .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
10437 let body = cpp_body_node(class_node)?;
10438 if class_node.end_byte() >= declaration_node.end_byte() {
10439 return None;
10440 }
10441 let name = class_like_name(class_node, source, ancestry)?;
10442 let is_partial_specialization = class_node
10443 .child_by_field_name("name")
10444 .is_some_and(|class_name| class_name.kind() == "template_type");
10445 if is_partial_specialization {
10446 let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
10447 if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
10448 return None;
10449 }
10450 } else {
10451 if !class_has_displaced_preprocessor_terminator(class_node) {
10452 return None;
10453 }
10454 let matching_other_branch = conditional
10455 .named_children(&mut conditional.walk())
10456 .take_while(|child| !same_node(*child, alternative))
10457 .filter(|child| child.kind() == "template_declaration")
10458 .filter_map(first_class_like_child)
10459 .any(|candidate| {
10460 cpp_body_node(candidate).is_none()
10461 && class_like_name(candidate, source, ancestry).as_deref()
10462 == Some(name.as_str())
10463 });
10464 if !matching_other_branch {
10465 return None;
10466 }
10467 }
10468
10469 let mut tail_members = Vec::new();
10470 let mut saw_class = false;
10471 let mut declaration_cursor = declaration_node.walk();
10472 for child in declaration_node.named_children(&mut declaration_cursor) {
10473 if same_node(child, class_node) {
10474 saw_class = true;
10475 } else if saw_class {
10476 tail_members.push(child);
10477 }
10478 }
10479
10480 let mut member_siblings = Vec::new();
10481 let mut saw_template = false;
10482 let mut terminator = None;
10483 for index in 0..alternative.child_count() {
10484 let Some(child) = alternative.child(index) else {
10485 continue;
10486 };
10487 if same_node(child, template_node) {
10488 saw_template = true;
10489 continue;
10490 }
10491 if !saw_template {
10492 continue;
10493 }
10494 if displaced_fragmented_class_terminator(alternative, index) {
10495 terminator = alternative.child(index + 1);
10496 break;
10497 }
10498 if child.is_named() {
10499 member_siblings.push(child);
10500 }
10501 }
10502 let terminator = terminator?;
10503 Some(RecoveredFragmentedPreprocessorClass {
10504 declaration_node,
10505 class_node,
10506 body,
10507 name,
10508 range: Range {
10509 start_byte: class_node.start_byte(),
10510 end_byte: terminator.end_byte(),
10511 start_line: class_node.start_position().row + 1,
10512 end_line: terminator.end_position().row + 1,
10513 },
10514 tail_members,
10515 member_siblings,
10516 })
10517}
10518
10519fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
10520 (0..class_node.child_count()).any(|index| {
10521 class_node.child(index).is_some_and(|child| {
10522 child.kind() == "ERROR"
10523 && (0..child.child_count()).any(|error_index| {
10524 child
10525 .child(error_index)
10526 .is_some_and(|token| token.kind() == "#endif")
10527 })
10528 })
10529 })
10530}
10531
10532pub fn cpp_displaced_preprocessor_terminator<'tree>(
10541 conditional: Node<'tree>,
10542) -> Option<Node<'tree>> {
10543 if !conditional.has_error() {
10544 return None;
10545 }
10546 let has_concrete_direct_terminator = conditional
10547 .child_count()
10548 .checked_sub(1)
10549 .and_then(|index| conditional.child(index))
10550 .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
10551 if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
10552 return None;
10556 }
10557 let mut displaced = None;
10558 let mut stack = (0..conditional.child_count())
10559 .filter_map(|index| conditional.child(index))
10560 .map(|child| (child, false))
10561 .collect::<Vec<_>>();
10562 while let Some((node, inside_error)) = stack.pop() {
10563 if !inside_error && node.kind() != "ERROR" && !node.has_error() {
10564 continue;
10565 }
10566 if node.kind() == "#endif" && !node.is_missing() && inside_error {
10567 if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
10568 displaced = Some(node);
10569 }
10570 continue;
10571 }
10572 if node != conditional
10573 && matches!(
10574 node.kind(),
10575 "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
10576 )
10577 {
10578 continue;
10579 }
10580 let inside_error = inside_error || node.kind() == "ERROR";
10581 for child in children_iter(node) {
10582 stack.push((child, inside_error));
10583 }
10584 }
10585 displaced
10586}
10587
10588#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10601pub struct CppDisplacedPreprocessorBoundary {
10602 pub end_byte: usize,
10603 pub end_line: usize,
10604}
10605
10606pub fn cpp_displaced_preprocessor_boundary(
10607 conditional: Node<'_>,
10608) -> Option<CppDisplacedPreprocessorBoundary> {
10609 if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
10610 return Some(CppDisplacedPreprocessorBoundary {
10611 end_byte: terminator.end_byte(),
10612 end_line: terminator.end_position().row + 1,
10613 });
10614 }
10615 if let Some(declaration) = displaced_split_declaration(conditional) {
10616 return Some(CppDisplacedPreprocessorBoundary {
10617 end_byte: declaration.end_byte(),
10618 end_line: declaration.end_position().row + 1,
10619 });
10620 }
10621 if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
10622 return Some(CppDisplacedPreprocessorBoundary {
10623 end_byte: terminator.end_byte(),
10624 end_line: terminator.end_position().row + 1,
10625 });
10626 }
10627 if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
10628 return Some(CppDisplacedPreprocessorBoundary {
10629 end_byte: terminator.end_byte(),
10630 end_line: terminator.end_position().row + 1,
10631 });
10632 }
10633 None
10634}
10635
10636fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10642 if !conditional.has_error()
10643 || conditional.child_by_field_name("alternative").is_some()
10644 || conditional
10645 .child(conditional.child_count().saturating_sub(1))
10646 .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
10647 {
10648 return None;
10649 }
10650 let mut recovered = None;
10651 for nested in named_children_iter(conditional) {
10652 if !matches!(
10653 nested.kind(),
10654 "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
10655 ) || nested.child_by_field_name("alternative").is_some()
10656 {
10657 continue;
10658 }
10659 let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
10660 continue;
10661 };
10662 if direct.kind() != "#endif" || direct.is_missing() {
10663 continue;
10664 }
10665 let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
10666 continue;
10667 };
10668 if displaced.end_byte() >= direct.start_byte() {
10669 continue;
10670 }
10671 if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
10672 recovered = Some(direct);
10673 }
10674 }
10675 recovered
10676}
10677
10678fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10679 if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
10680 return None;
10681 }
10682 let mut cursor = conditional.walk();
10683 let declarations = conditional
10684 .named_children(&mut cursor)
10685 .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
10686 .collect::<Vec<_>>();
10687 let declaration = *declarations.first()?;
10688 if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
10689 return None;
10690 }
10691 let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
10692 let mut terminator = None;
10693 let mut stack = (0..declaration.child_count())
10694 .filter_map(|index| declaration.child(index))
10695 .filter(|child| child.start_byte() < declarator_start)
10696 .map(|child| (child, false))
10697 .collect::<Vec<_>>();
10698 while let Some((node, inside_error)) = stack.pop() {
10699 let inside_error = inside_error || node.kind() == "ERROR";
10700 if inside_error && node.kind() == "#endif" && !node.is_missing() {
10701 terminator = Some(node);
10702 continue;
10703 }
10704 for index in 0..node.child_count() {
10705 if let Some(child) = node.child(index)
10706 && child.start_byte() < declarator_start
10707 {
10708 stack.push((child, inside_error));
10709 }
10710 }
10711 }
10712 terminator
10713}
10714
10715fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10716 if !conditional.has_error()
10717 || conditional.child_by_field_name("alternative").is_some()
10718 || conditional
10719 .prev_named_sibling()
10720 .filter(|sibling| {
10721 sibling.kind() == "ERROR"
10722 && sibling.child_count() == 1
10723 && sibling
10724 .child(0)
10725 .is_some_and(|child| child.kind() == "typedef")
10726 })
10727 .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
10728 .is_none()
10729 {
10730 return None;
10731 }
10732 let mut cursor = conditional.walk();
10733 let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
10734 let declaration_index = children
10735 .iter()
10736 .position(|child| child.kind() == "declaration" && child.has_error())?;
10737 let declaration = children[declaration_index];
10738 if !children
10739 .iter()
10740 .skip(declaration_index + 1)
10741 .any(|child| child.end_byte() > declaration.end_byte())
10742 {
10743 return None;
10744 }
10745 let declarator = declaration.child_by_field_name("declarator")?;
10746 let mut error_end = None;
10747 let mut names = Vec::new();
10748 let mut stack = vec![declarator];
10749 while let Some(node) = stack.pop() {
10750 if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
10751 error_end =
10752 Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
10753 continue;
10754 }
10755 if matches!(node.kind(), "identifier" | "type_identifier") {
10756 names.push(node.start_byte());
10757 }
10758 push_named_children_reversed(node, &mut stack);
10759 }
10760 let error_end = error_end?;
10761 names
10762 .into_iter()
10763 .any(|start| start >= error_end)
10764 .then_some(declaration)
10765}
10766
10767fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
10768 let Some(error) = parent.child(error_index) else {
10769 return false;
10770 };
10771 if error.kind() != "ERROR"
10772 || error.child_count() != 1
10773 || error.child(0).is_none_or(|child| child.kind() != "}")
10774 {
10775 return false;
10776 }
10777 let Some(semicolon) = parent.child(error_index + 1) else {
10778 return false;
10779 };
10780 semicolon.kind() == "expression_statement"
10781 && semicolon.child_count() == 1
10782 && semicolon.child(0).is_some_and(|child| child.kind() == ";")
10783}
10784
10785fn displaced_macro_class_tail(
10791 declaration_node: Node<'_>,
10792 body: Node<'_>,
10793 source: &str,
10794) -> Option<DisplacedMacroClassTail> {
10795 if !matches!(
10796 declaration_node.kind(),
10797 "class_specifier" | "struct_specifier" | "union_specifier"
10798 ) || body.kind() != "field_declaration_list"
10799 {
10800 return None;
10801 }
10802
10803 let child_count = body.named_child_count();
10804 for index in 0..child_count {
10805 let child = body.named_child(index)?;
10806 let Some(terminator) = displaced_macro_field_terminator(child, source) else {
10807 continue;
10808 };
10809 let split_index = index + 1;
10810 if split_index >= child_count {
10811 return None;
10812 }
10813 let mut cursor = body.walk();
10814 if !body
10815 .named_children(&mut cursor)
10816 .skip(split_index)
10817 .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
10818 {
10819 return None;
10820 }
10821 return Some(DisplacedMacroClassTail {
10822 split_index,
10823 class_range: Range {
10824 start_byte: declaration_node.start_byte(),
10825 end_byte: terminator.end_byte(),
10826 start_line: declaration_node.start_position().row + 1,
10827 end_line: terminator.end_position().row + 1,
10828 },
10829 });
10830 }
10831 None
10832}
10833
10834fn displaced_macro_field_terminator<'tree>(
10835 field: Node<'tree>,
10836 source: &str,
10837) -> Option<Node<'tree>> {
10838 if field.kind() != "field_declaration" {
10839 return None;
10840 }
10841 let macro_type = field.child_by_field_name("type")?;
10842 if macro_type.kind() != "type_identifier"
10843 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10844 || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
10845 {
10846 return None;
10847 }
10848 for index in 0..field.child_count() {
10849 let error = field.child(index)?;
10850 if error.kind() != "ERROR"
10851 || error.child_count() != 1
10852 || error.child(0).is_none_or(|child| child.kind() != "}")
10853 {
10854 continue;
10855 }
10856 let semicolon = field.child(index + 1)?;
10857 if semicolon.kind() == ";" {
10858 return Some(semicolon);
10859 }
10860 }
10861 None
10862}
10863
10864fn recover_fragmented_partial_specialization<'tree>(
10865 template_node: Node<'tree>,
10866 declaration_child: Node<'tree>,
10867 source: &str,
10868 ancestry: &ParentIndex<'tree>,
10869) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
10870 if declaration_child.kind() != "function_definition" {
10871 return None;
10872 }
10873 let class_node = declaration_child.child_by_field_name("type")?;
10874 if !matches!(
10875 class_node.kind(),
10876 "class_specifier" | "struct_specifier" | "union_specifier"
10877 ) || !class_node
10878 .child_by_field_name("name")
10879 .and_then(|name| direct_identifier_name(name, source))
10880 .is_some_and(|name| cpp_export_macro_token(&name))
10881 {
10882 return None;
10883 }
10884 let declarator = declaration_child.child_by_field_name("declarator")?;
10885 if declarator.kind() != "template_function" {
10886 return None;
10887 }
10888 let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
10889 if metadata.specialization_arguments.is_empty() {
10890 return None;
10891 }
10892 let body = declaration_child.child_by_field_name("body")?;
10893 if body.kind() != "compound_statement" {
10894 return None;
10895 }
10896 let complete_prefix = body.named_child(0).filter(|first| {
10897 first.kind() == "labeled_statement"
10898 && first.has_error()
10899 && first
10900 .named_child(first.named_child_count().saturating_sub(1))
10901 .is_some_and(recovered_declaration_has_class_terminator)
10902 });
10903 let complete_body = complete_prefix.is_some();
10904 let mut prefix_members = Vec::new();
10905 if let Some(prefix) = complete_prefix {
10906 prefix_members.push(prefix);
10907 } else {
10908 let mut body_cursor = body.walk();
10909 for child in body.named_children(&mut body_cursor) {
10910 if !is_structurally_valid_fragmented_class_prefix_member(child) {
10911 break;
10912 }
10913 prefix_members.push(child);
10914 }
10915 }
10916 let containing_declarations = template_node.parent()?;
10917 if !matches!(
10918 containing_declarations.kind(),
10919 "declaration_list" | "compound_statement"
10920 ) {
10921 return None;
10922 }
10923 let mut member_siblings = Vec::new();
10924 let mut following_declarations = Vec::new();
10925 let terminator;
10926 if complete_body {
10927 terminator = complete_prefix?;
10928 let mut cursor = body.walk();
10929 let mut after_prefix = false;
10930 for child in body.named_children(&mut cursor) {
10931 if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
10932 after_prefix = true;
10933 } else if after_prefix {
10934 following_declarations.push(child);
10935 }
10936 }
10937 } else {
10938 let mut found_template = false;
10939 let mut cursor = containing_declarations.walk();
10940 let mut class_terminator = None;
10941 for child in containing_declarations.children(&mut cursor) {
10942 if same_node(child, template_node) {
10943 found_template = true;
10944 continue;
10945 }
10946 if found_template && child.kind() == "}" {
10947 class_terminator = Some(child);
10948 break;
10949 }
10950 if found_template && child.kind() == "namespace_definition" {
10957 return None;
10958 }
10959 if found_template && child.is_named() {
10960 member_siblings.push(child);
10961 }
10962 }
10963 terminator = class_terminator?;
10964 }
10965 let name = format!(
10966 "{}<{}>",
10967 metadata.primary_name,
10968 metadata
10969 .specialization_arguments
10970 .iter()
10971 .map(|argument| argument.text.as_str())
10972 .collect::<Vec<_>>()
10973 .join(", ")
10974 );
10975 Some(RecoveredFragmentedPartialSpecialization {
10976 declaration_node: declaration_child,
10977 name,
10978 range: Range {
10979 start_byte: declaration_child.start_byte(),
10980 end_byte: terminator.end_byte(),
10981 start_line: declaration_child.start_position().row + 1,
10982 end_line: terminator.end_position().row + 1,
10983 },
10984 prefix_members,
10985 member_siblings,
10986 following_declarations,
10987 })
10988}
10989
10990pub fn is_recovered_fragmented_partial_specialization_container(
10997 node: Node<'_>,
10998 source: &str,
10999) -> bool {
11000 let Some(template) = node
11001 .parent()
11002 .filter(|parent| parent.kind() == "template_declaration")
11003 else {
11004 return false;
11005 };
11006 let mut root = template;
11007 while let Some(parent) = root.parent() {
11008 root = parent;
11009 }
11010 recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
11011 .is_some()
11012}
11013
11014fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
11015 if declaration.kind() != "declaration" {
11016 return false;
11017 }
11018 (0..declaration.child_count().saturating_sub(1)).any(|index| {
11023 let Some(error) = declaration.child(index) else {
11024 return false;
11025 };
11026 error.kind() == "ERROR"
11027 && error.child_count() == 1
11028 && error.child(0).is_some_and(|child| child.kind() == "}")
11029 && declaration
11030 .child(index + 1)
11031 .is_some_and(|child| child.kind() == ";")
11032 })
11033}
11034
11035fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
11036 if node.has_error() {
11037 return false;
11038 }
11039 match node.kind() {
11040 "declaration"
11041 | "field_declaration"
11042 | "alias_declaration"
11043 | "type_definition"
11044 | "static_assert_declaration" => true,
11045 "labeled_statement" => node
11046 .named_child(node.named_child_count().saturating_sub(1))
11047 .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
11048 "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
11049 matches!(
11050 child.kind(),
11051 "declaration"
11052 | "field_declaration"
11053 | "alias_declaration"
11054 | "type_definition"
11055 | "function_definition"
11056 )
11057 }),
11058 _ => false,
11059 }
11060}
11061
11062fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
11063 (node.kind() == "declaration" && node.child(0)?.kind() == "using")
11064 .then(|| node.child_by_field_name("declarator"))
11065 .flatten()
11066 .and_then(|declarator| extract_variable_name(declarator, source))
11067}
11068
11069fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
11070 while let Some(parent) = node.parent() {
11071 if matches!(parent.kind(), "function_definition" | "lambda_expression") {
11072 return true;
11073 }
11074 node = parent;
11075 }
11076 false
11077}
11078
11079fn cpp_template_metadata<'tree>(
11080 template_node: Node<'tree>,
11081 declaration_child: Node<'tree>,
11082 source: &str,
11083 ancestry: &ParentIndex<'tree>,
11084) -> Option<CppTemplateMetadata> {
11085 let parameters_node = template_node.child_by_field_name("parameters")?;
11086 let name_node = cpp_templated_class_name_node(declaration_child)?;
11087 let primary_node = match name_node.kind() {
11088 "template_type" | "template_function" => name_node.child_by_field_name("name")?,
11089 _ => name_node,
11090 };
11091 let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
11092 if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
11093 return None;
11094 }
11095
11096 let mut parameter_nodes = Vec::new();
11097 let mut parameter_names = Vec::new();
11098 let mut cursor = parameters_node.walk();
11099 for parameter in parameters_node.named_children(&mut cursor) {
11100 if !matches!(
11101 parameter.kind(),
11102 "type_parameter_declaration"
11103 | "optional_type_parameter_declaration"
11104 | "variadic_type_parameter_declaration"
11105 | "template_template_parameter_declaration"
11106 | "parameter_declaration"
11107 | "optional_parameter_declaration"
11108 | "variadic_parameter_declaration"
11109 ) {
11110 continue;
11111 }
11112 let index = parameter_nodes.len();
11113 let name = cpp_template_parameter_name(parameter, source)
11118 .unwrap_or_else(|| format!("<anonymous:{index}>"));
11119 parameter_names.push(name);
11120 parameter_nodes.push(parameter);
11121 }
11122 let parameters = parameter_nodes
11123 .into_iter()
11124 .zip(parameter_names.iter().cloned())
11125 .map(|(parameter, name)| CppTemplateParameterMetadata {
11126 name,
11127 kind: cpp_template_parameter_kind(parameter),
11128 variadic: matches!(
11129 parameter.kind(),
11130 "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
11131 ),
11132 default: cpp_template_parameter_default_expression(
11133 parameter,
11134 source,
11135 ¶meter_names,
11136 ancestry,
11137 ),
11138 })
11139 .collect();
11140 let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
11141 Vec::new()
11142 } else {
11143 cpp_template_argument_expressions(name_node, source, ¶meter_names, ancestry)
11144 .unwrap_or_default()
11145 };
11146 let alias_target = (declaration_child.kind() == "alias_declaration")
11147 .then(|| cpp_template_alias_target(declaration_child, source, ¶meter_names, ancestry))
11148 .flatten();
11149 Some(CppTemplateMetadata {
11150 primary_name,
11151 primary_fq_name: String::new(),
11152 parameters,
11153 specialization_arguments,
11154 alias_target,
11155 })
11156}
11157
11158fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
11159 match node.kind() {
11160 "class_specifier" | "struct_specifier" | "union_specifier" => {
11161 node.child_by_field_name("name")
11162 }
11163 "function_definition" => {
11164 let declarator = node.child_by_field_name("declarator")?;
11165 if matches!(declarator.kind(), "identifier" | "template_function") {
11166 Some(declarator)
11167 } else {
11168 None
11169 }
11170 }
11171 "alias_declaration" => node.child_by_field_name("name"),
11172 _ => None,
11173 }
11174}
11175
11176fn cpp_template_alias_target<'tree>(
11177 alias: Node<'tree>,
11178 source: &str,
11179 parameter_names: &[String],
11180 ancestry: &ParentIndex<'tree>,
11181) -> Option<CppTemplateAliasTargetMetadata> {
11182 let mut type_node = alias.child_by_field_name("type")?;
11183 while type_node.kind() == "type_descriptor" {
11184 type_node = type_node.child_by_field_name("type")?;
11185 }
11186 let global = type_node.child_by_field_name("scope").is_none()
11187 && type_node.child(0).is_some_and(|child| child.kind() == "::");
11188 let mut components = Vec::new();
11189 cpp_template_target_components(type_node, source, &mut components)?;
11190 let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
11191 (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
11192 components,
11193 global,
11194 arguments,
11195 })
11196}
11197
11198fn cpp_template_target_components(
11199 node: Node<'_>,
11200 source: &str,
11201 out: &mut Vec<String>,
11202) -> Option<()> {
11203 match node.kind() {
11204 "identifier" | "namespace_identifier" | "type_identifier" => {
11205 out.push(node_text(node, source).to_string());
11206 Some(())
11207 }
11208 "template_type" => {
11209 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11210 }
11211 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11212 if let Some(scope) = node.child_by_field_name("scope") {
11213 cpp_template_target_components(scope, source, out)?;
11214 }
11215 cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11216 }
11217 _ => None,
11218 }
11219}
11220
11221fn cpp_template_argument_expressions<'tree>(
11222 mut node: Node<'tree>,
11223 source: &str,
11224 parameter_names: &[String],
11225 ancestry: &ParentIndex<'tree>,
11226) -> Option<Vec<CppTemplateExpression>> {
11227 loop {
11228 match node.kind() {
11229 "template_type" | "template_function" => {
11230 let arguments = node.child_by_field_name("arguments")?;
11231 let mut cursor = arguments.walk();
11232 return Some(
11233 arguments
11234 .named_children(&mut cursor)
11235 .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
11236 .map(|argument| {
11237 cpp_template_expression(argument, source, parameter_names, ancestry)
11238 })
11239 .collect(),
11240 );
11241 }
11242 "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
11243 node = node
11244 .child_by_field_name("name")
11245 .or_else(|| node.child_by_field_name("type"))?;
11246 }
11247 _ => return None,
11248 }
11249 }
11250}
11251
11252fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
11253 let candidate = node
11254 .child_by_field_name("name")
11255 .or_else(|| node.child_by_field_name("declarator"))
11256 .or_else(|| {
11257 let mut cursor = node.walk();
11258 node.named_children(&mut cursor).find(|child| {
11259 matches!(
11260 child.kind(),
11261 "identifier" | "type_identifier" | "field_identifier"
11262 )
11263 })
11264 })?;
11265 let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
11266 (!name.is_empty()).then_some(name)
11267}
11268
11269fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
11270 match node.kind() {
11271 "type_parameter_declaration"
11272 | "optional_type_parameter_declaration"
11273 | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
11274 "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
11275 _ => CppTemplateParameterKind::Value,
11276 }
11277}
11278
11279fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
11280 node.child_by_field_name("default_type")
11281 .or_else(|| node.child_by_field_name("default_value"))
11282}
11283
11284fn cpp_template_parameter_default_expression<'tree>(
11285 parameter: Node<'tree>,
11286 source: &str,
11287 parameter_names: &[String],
11288 ancestry: &ParentIndex<'tree>,
11289) -> Option<CppTemplateExpression> {
11290 let default = cpp_template_parameter_default(parameter)?;
11291 let base = cpp_template_expression(default, source, parameter_names, ancestry);
11292 let Some(pointer_error) = parameter.next_named_sibling() else {
11293 return Some(base);
11294 };
11295 let Some(pointer_declarator) =
11296 recovered_abstract_pointer_declarator_term(pointer_error, source)
11297 else {
11298 return Some(base);
11299 };
11300 Some(CppTemplateExpression {
11301 text: format!(
11302 "{}{}",
11303 base.text,
11304 normalize_cpp_whitespace(node_text(pointer_error, source))
11305 ),
11306 term: CppTemplateTerm::Node {
11307 kind: "type_descriptor".to_string(),
11308 children: vec![base.term, pointer_declarator],
11309 },
11310 })
11311}
11312
11313fn recovered_abstract_pointer_declarator_term(
11314 node: Node<'_>,
11315 source: &str,
11316) -> Option<CppTemplateTerm> {
11317 if node.kind() != "ERROR" || node.child_count() == 0 {
11318 return None;
11319 }
11320 let mut children = Vec::new();
11321 for index in 0..node.child_count() {
11322 let child = node.child(index)?;
11323 if child.kind() != "*" {
11324 return None;
11325 }
11326 children.push(CppTemplateTerm::Atom {
11327 kind: "*".to_string(),
11328 text: normalize_cpp_whitespace(node_text(child, source)),
11329 });
11330 }
11331 Some(CppTemplateTerm::Node {
11332 kind: "abstract_pointer_declarator".to_string(),
11333 children,
11334 })
11335}
11336
11337fn cpp_template_expression<'tree>(
11338 node: Node<'tree>,
11339 source: &str,
11340 parameter_names: &[String],
11341 ancestry: &ParentIndex<'tree>,
11342) -> CppTemplateExpression {
11343 let text = normalize_cpp_whitespace(node_text(node, source));
11344 CppTemplateExpression {
11345 text,
11346 term: cpp_template_term(node, source, parameter_names, ancestry),
11347 }
11348}
11349
11350pub fn cpp_template_term<'tree>(
11351 node: Node<'tree>,
11352 source: &str,
11353 parameter_names: &[String],
11354 ancestry: &ParentIndex<'tree>,
11355) -> CppTemplateTerm {
11356 enum Work<'tree> {
11357 Visit(Node<'tree>),
11358 Build { kind: String, child_count: usize },
11359 }
11360
11361 let mut work = vec![Work::Visit(node)];
11362 let mut terms = Vec::new();
11363 while let Some(next) = work.pop() {
11364 match next {
11365 Work::Visit(current) => {
11366 let text = normalize_cpp_whitespace(node_text(current, source));
11367 if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
11368 terms.push(CppTemplateTerm::Parameter(text));
11369 continue;
11370 }
11371 if matches!(current.kind(), "type_descriptor" | "dependent_type") {
11372 let mut cursor = current.walk();
11373 let named = current
11374 .named_children(&mut cursor)
11375 .filter(|child| !child.is_extra() && child.kind() != "comment")
11376 .collect::<Vec<_>>();
11377 if let [child] = named.as_slice() {
11378 work.push(Work::Visit(*child));
11379 continue;
11380 }
11381 }
11382 if current.child_count() == 0 {
11383 terms.push(CppTemplateTerm::Atom {
11384 kind: if matches!(
11385 current.kind(),
11386 "identifier"
11387 | "type_identifier"
11388 | "field_identifier"
11389 | "namespace_identifier"
11390 ) {
11391 "identifier".to_string()
11392 } else {
11393 current.kind().to_string()
11394 },
11395 text,
11396 });
11397 continue;
11398 }
11399 let children = (0..current.child_count())
11400 .filter_map(|index| current.child(index))
11401 .filter(|child| !child.is_extra() && child.kind() != "comment")
11402 .collect::<Vec<_>>();
11403 work.push(Work::Build {
11404 kind: current.kind().to_string(),
11405 child_count: children.len(),
11406 });
11407 work.extend(children.into_iter().rev().map(Work::Visit));
11408 }
11409 Work::Build { kind, child_count } => {
11410 let children = terms.split_off(terms.len() - child_count);
11411 terms.push(CppTemplateTerm::Node { kind, children });
11412 }
11413 }
11414 }
11415 terms.pop().expect("template term traversal emits one root")
11416}
11417
11418fn cpp_template_term_leaf_is_parameter<'tree>(
11419 node: Node<'tree>,
11420 text: &str,
11421 parameter_names: &[String],
11422 ancestry: &ParentIndex<'tree>,
11423) -> bool {
11424 if !parameter_names.iter().any(|parameter| parameter == text) {
11425 return false;
11426 }
11427 !ancestry.parent(node).is_some_and(|parent| {
11428 matches!(
11429 parent.kind(),
11430 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
11431 ) && parent.child_by_field_name("scope").is_some()
11432 && parent.child_by_field_name("name") == Some(node)
11433 })
11434}
11435
11436fn enclosing_cpp_declaration_node<'tree>(
11437 mut node: Node<'tree>,
11438 ancestry: &ParentIndex<'tree>,
11439) -> Option<Node<'tree>> {
11440 loop {
11441 match node.kind() {
11442 "declaration"
11443 | "function_declaration"
11444 | "field_declaration"
11445 | "function_definition" => return Some(node),
11446 _ => node = ancestry.parent(node)?,
11447 }
11448 }
11449}
11450
11451fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
11452 let mut params = Vec::new();
11453 let mut cursor = parameters_node.walk();
11454 for child in parameters_node.children(&mut cursor) {
11455 match child.kind() {
11456 "parameter_declaration" | "optional_parameter_declaration" => {
11457 params.push(cpp_parameter_type(child, source));
11458 }
11459 "variadic_parameter_declaration" => {
11460 params.push(cpp_parameter_type(child, source));
11461 }
11462 "variadic_parameter" | "..." => params.push("...".to_string()),
11463 _ => {}
11464 }
11465 }
11466
11467 if params.is_empty() {
11468 "()".to_string()
11469 } else {
11470 format!("({})", params.join(", "))
11471 }
11472}
11473
11474fn cpp_signature_metadata<'tree>(
11475 signature: String,
11476 function_declarator: Node<'tree>,
11477 source: &str,
11478 ancestry: &ParentIndex<'tree>,
11479) -> SignatureMetadata {
11480 let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
11481 let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
11482 let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
11483 let return_type_identity =
11484 cpp_callable_return_type_identity(function_declarator, source, ancestry);
11485 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
11486 return enrich(
11487 SignatureMetadata::new(signature, Vec::new())
11488 .with_return_type_text(return_type_text)
11489 .with_return_type_identity(return_type_identity),
11490 );
11491 };
11492 let callable_arity = cpp_callable_arity(parameters_node, source);
11493 let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
11494 let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
11495 let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
11496 let Some(relative_start) = signature
11497 .get(search_from..)
11498 .and_then(|suffix| suffix.find(¶meter_text))
11499 else {
11500 return enrich(
11501 SignatureMetadata::new(signature, Vec::new())
11502 .with_callable_arity(callable_arity)
11503 .with_callable_parameter_types(callable_parameter_types)
11504 .with_return_type_text(return_type_text)
11505 .with_return_type_identity(return_type_identity),
11506 );
11507 };
11508 let parameters_start = search_from + relative_start;
11509 let parameters_end = parameters_start + parameter_text.len();
11510 let mut search_start = parameters_start;
11511 let parameters = cpp_parameter_label_nodes(parameters_node)
11512 .into_iter()
11513 .filter_map(|label_node| {
11514 let label = normalize_cpp_whitespace(node_text(label_node, source));
11515 if label.is_empty() || search_start > parameters_end {
11516 return None;
11517 }
11518 let haystack = signature.get(search_start..parameters_end)?;
11519 let relative_start = haystack.find(&label)?;
11520 let start_byte = search_start + relative_start;
11521 let end_byte = start_byte + label.len();
11522 search_start = end_byte;
11523 Some(ParameterMetadata::new(label, start_byte, end_byte))
11524 })
11525 .collect();
11526 enrich(
11527 SignatureMetadata::new(signature, parameters)
11528 .with_callable_arity(callable_arity)
11529 .with_callable_parameter_types(callable_parameter_types)
11530 .with_return_type_text(return_type_text)
11531 .with_return_type_identity(return_type_identity),
11532 )
11533}
11534
11535fn cpp_callable_is_structural_constructor<'tree>(
11536 function_declarator: Node<'tree>,
11537 source: &str,
11538 ancestry: &ParentIndex<'tree>,
11539) -> bool {
11540 let Some(name_node) = function_declarator
11541 .child_by_field_name("declarator")
11542 .or_else(|| function_declarator.child_by_field_name("name"))
11543 .or_else(|| last_named_child(function_declarator))
11544 else {
11545 return false;
11546 };
11547 let Some(callable_name) = direct_identifier_name(name_node, source) else {
11548 return false;
11549 };
11550
11551 let mut current = ancestry.parent(function_declarator);
11552 while let Some(ancestor) = current {
11553 let owner_name = match ancestor.kind() {
11554 "class_specifier" | "struct_specifier" | "union_specifier" => {
11555 class_like_name(ancestor, source, ancestry)
11556 }
11557 "ERROR" => malformed_class_error_owner_name(ancestor, source),
11558 _ => None,
11559 };
11560 if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
11561 return true;
11562 }
11563 current = ancestry.parent(ancestor);
11564 }
11565 false
11566}
11567
11568fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
11578 if node.kind() != "ERROR" {
11579 return None;
11580 }
11581 let keyword = node.child(0)?;
11582 if !matches!(keyword.kind(), "class" | "struct" | "union") {
11583 return None;
11584 }
11585 let name_node = node.child(1)?;
11586 let name = direct_identifier_name(name_node, source)?;
11587 let has_body = (2..node.child_count())
11588 .filter_map(|index| node.child(index))
11589 .any(|child| child.kind() == "{");
11590 has_body.then_some(name)
11591}
11592
11593pub fn cpp_callable_declaration_return_type_identity<'tree>(
11599 callable: Node<'tree>,
11600 source: &str,
11601 ancestry: &ParentIndex<'tree>,
11602) -> Option<StructuredTypeIdentity> {
11603 let declarator = callable
11604 .child_by_field_name("declarator")
11605 .and_then(extract_function_declarator)?;
11606 cpp_callable_return_type_identity(declarator, source, ancestry)
11607}
11608
11609pub(crate) fn cpp_callable_return_type_identity<'tree>(
11610 function_declarator: Node<'tree>,
11611 source: &str,
11612 ancestry: &ParentIndex<'tree>,
11613) -> Option<StructuredTypeIdentity> {
11614 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
11615 return None;
11616 }
11617 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
11618 if let Some((return_type, _)) =
11619 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
11620 {
11621 return cpp_structured_type_identity(return_type, source, &lexical_scope);
11622 }
11623 let mut cursor = function_declarator.walk();
11624 if let Some(trailing) = function_declarator
11625 .named_children(&mut cursor)
11626 .find(|child| child.kind() == "trailing_return_type")
11627 && let Some(type_descriptor) = trailing.named_child(0)
11628 {
11629 return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
11630 }
11631
11632 let mut current = function_declarator;
11633 let mut wrappers = Vec::new();
11634 while let Some(parent) = ancestry.parent(current) {
11635 if matches!(
11636 parent.kind(),
11637 "function_definition" | "declaration" | "field_declaration"
11638 ) {
11639 let type_node = parent.child_by_field_name("type")?;
11640 if cpp_export_macro_token(node_text(type_node, source))
11641 && (0..parent.named_child_count()).any(|index| {
11642 parent
11643 .named_child(index)
11644 .is_some_and(|child| child.kind() == "ERROR")
11645 })
11646 {
11647 return None;
11648 }
11649 let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
11650 for wrapper in wrappers.into_iter().rev() {
11651 identity = cpp_wrap_structured_type(identity, wrapper)?;
11652 }
11653 return Some(identity);
11654 }
11655 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
11656 || (matches!(
11657 parent.kind(),
11658 "pointer_declarator"
11659 | "reference_declarator"
11660 | "array_declarator"
11661 | "parenthesized_declarator"
11662 ) && parent.named_child_count() == 1
11663 && parent.named_child(0) == Some(current));
11664 if !wraps_current_declarator {
11665 return None;
11666 }
11667 match parent.kind() {
11668 "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
11669 "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
11670 "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
11671 "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
11672 _ => return None,
11673 }
11674 current = parent;
11675 }
11676 None
11677}
11678
11679fn cpp_structured_type_identity(
11680 node: Node<'_>,
11681 source: &str,
11682 lexical_scope: &[String],
11683) -> Option<StructuredTypeIdentity> {
11684 enum Work<'tree> {
11685 Visit(Node<'tree>),
11686 Wrap(CppStructuredTypeWrapper),
11687 ApplyWrappers(Vec<CppStructuredTypeWrapper>),
11688 BuildGeneric { argument_count: usize },
11689 }
11690
11691 let mut work = vec![Work::Visit(node)];
11692 let mut values = Vec::new();
11693 let mut builder = StructuredTypeIdentityBuilder::default();
11694 while let Some(next) = work.pop() {
11695 match next {
11696 Work::Visit(current) => match current.kind() {
11697 "type_descriptor" => {
11698 let type_node = current
11699 .child_by_field_name("type")
11700 .or_else(|| current.named_child(0))?;
11701 let mut wrappers = Vec::new();
11702 let mut cursor = current.walk();
11703 for child in current.named_children(&mut cursor) {
11704 if child.id() != type_node.id() {
11705 wrappers.extend(cpp_structured_declarator_wrappers(child)?);
11706 }
11707 }
11708 work.push(Work::ApplyWrappers(wrappers));
11709 work.push(Work::Visit(type_node));
11710 }
11711 "pointer_declarator" | "abstract_pointer_declarator" => {
11712 let child = current
11713 .child_by_field_name("declarator")
11714 .or_else(|| current.named_child(0))?;
11715 work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
11716 work.push(Work::Visit(child));
11717 }
11718 "reference_declarator" => {
11719 let child = current
11720 .child_by_field_name("declarator")
11721 .or_else(|| current.named_child(0))?;
11722 work.push(Work::Wrap(cpp_reference_wrapper(current)?));
11723 work.push(Work::Visit(child));
11724 }
11725 "array_declarator" | "abstract_array_declarator" => {
11726 let child = current
11727 .child_by_field_name("declarator")
11728 .or_else(|| current.named_child(0))?;
11729 work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
11730 work.push(Work::Visit(child));
11731 }
11732 "template_type" => {
11733 let name_node = current.child_by_field_name("name")?;
11734 let arguments = current
11735 .child_by_field_name("arguments")
11736 .map(|arguments_node| {
11737 let mut cursor = arguments_node.walk();
11738 arguments_node
11739 .named_children(&mut cursor)
11740 .filter(|child| !child.is_extra() && child.kind() != "comment")
11741 .collect::<Vec<_>>()
11742 })
11743 .unwrap_or_default();
11744 work.push(Work::BuildGeneric {
11745 argument_count: arguments.len(),
11746 });
11747 work.extend(arguments.into_iter().rev().map(Work::Visit));
11748 work.push(Work::Visit(name_node));
11749 }
11750 "qualified_identifier"
11751 | "scoped_identifier"
11752 | "scoped_type_identifier"
11753 | "type_identifier"
11754 | "field_identifier"
11755 | "identifier"
11756 | "namespace_identifier"
11757 | "primitive_type" => {
11758 values.push(builder.named(cpp_structured_named_type(
11759 current,
11760 source,
11761 lexical_scope,
11762 )?)?);
11763 }
11764 _ => {
11765 let child = current.child_by_field_name("type").or_else(|| {
11766 (current.named_child_count() == 1)
11767 .then(|| current.named_child(0))
11768 .flatten()
11769 })?;
11770 work.push(Work::Visit(child));
11771 }
11772 },
11773 Work::Wrap(wrapper) => {
11774 let root = values.pop()?;
11775 values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
11776 }
11777 Work::ApplyWrappers(wrappers) => {
11778 let mut root = values.pop()?;
11779 for wrapper in wrappers.into_iter().rev() {
11780 root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
11781 }
11782 values.push(root);
11783 }
11784 Work::BuildGeneric { argument_count } => {
11785 let value_count = argument_count.checked_add(1)?;
11786 let start = values.len().checked_sub(value_count)?;
11787 let mut built = values.split_off(start);
11788 let base = built.remove(0);
11789 values.push(builder.generic(base, built)?);
11790 }
11791 }
11792 }
11793 (values.len() == 1)
11794 .then(|| values.pop())
11795 .flatten()
11796 .and_then(|root| builder.finish(root))
11797}
11798
11799fn cpp_structured_named_type(
11800 node: Node<'_>,
11801 source: &str,
11802 lexical_scope: &[String],
11803) -> Option<StructuredTypeName> {
11804 let path = cpp_structured_type_path(node, source)?;
11805 let absolute = node.child_by_field_name("scope").is_none()
11806 && node.child(0).is_some_and(|child| child.kind() == "::");
11807 StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
11808}
11809
11810#[derive(Clone, Copy)]
11811enum CppStructuredTypeWrapper {
11812 Pointer,
11813 LvalueReference,
11814 RvalueReference,
11815 Array,
11816}
11817
11818fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
11819 let mut wrappers = Vec::new();
11820 let mut current = node;
11821 loop {
11822 match current.kind() {
11823 "pointer_declarator" | "abstract_pointer_declarator" => {
11824 wrappers.push(CppStructuredTypeWrapper::Pointer)
11825 }
11826 "reference_declarator" | "abstract_reference_declarator" => {
11827 wrappers.push(cpp_reference_wrapper(current)?);
11828 }
11829 "array_declarator" | "abstract_array_declarator" => {
11830 wrappers.push(CppStructuredTypeWrapper::Array)
11831 }
11832 _ => break,
11833 }
11834 let Some(child) = current
11835 .child_by_field_name("declarator")
11836 .or_else(|| current.named_child(0))
11837 else {
11838 break;
11839 };
11840 current = child;
11841 }
11842 Some(wrappers)
11843}
11844
11845fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
11846 node.children(&mut node.walk())
11847 .find_map(|child| match child.kind() {
11848 "&" => Some(CppStructuredTypeWrapper::LvalueReference),
11849 "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
11850 _ => None,
11851 })
11852}
11853
11854fn cpp_wrap_structured_type(
11855 identity: StructuredTypeIdentity,
11856 wrapper: CppStructuredTypeWrapper,
11857) -> Option<StructuredTypeIdentity> {
11858 match wrapper {
11859 CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
11860 CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
11861 CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
11862 CppStructuredTypeWrapper::Array => identity.wrap_array(),
11863 }
11864}
11865
11866fn cpp_wrap_structured_type_node(
11867 builder: &mut StructuredTypeIdentityBuilder,
11868 inner: StructuredTypeNodeId,
11869 wrapper: CppStructuredTypeWrapper,
11870) -> Option<StructuredTypeNodeId> {
11871 match wrapper {
11872 CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
11873 CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
11874 CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
11875 CppStructuredTypeWrapper::Array => builder.array(inner),
11876 }
11877}
11878
11879fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
11880 let mut path = Vec::new();
11881 let mut stack = vec![node];
11882 while let Some(current) = stack.pop() {
11883 match current.kind() {
11884 "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
11885 let component = node_text(current, source).to_string();
11886 if component.is_empty() {
11887 return None;
11888 }
11889 path.push(component);
11890 }
11891 "template_type" | "dependent_type" => {
11892 stack.push(current.child_by_field_name("name")?);
11893 }
11894 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11895 stack.push(current.child_by_field_name("name")?);
11896 if let Some(scope) = current.child_by_field_name("scope") {
11897 stack.push(scope);
11898 }
11899 }
11900 _ => return None,
11901 }
11902 }
11903 (!path.is_empty()).then_some(path)
11904}
11905
11906fn cpp_callable_lexical_scope<'tree>(
11907 node: Node<'tree>,
11908 source: &str,
11909 ancestry: &ParentIndex<'tree>,
11910) -> Vec<String> {
11911 let mut groups = Vec::new();
11912 let mut current = ancestry.parent(node);
11913 while let Some(parent) = current {
11914 if matches!(
11915 parent.kind(),
11916 "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
11917 ) && let Some(name_node) = parent.child_by_field_name("name")
11918 && let Some(components) = cpp_structured_type_path(name_node, source)
11919 && !components.is_empty()
11920 {
11921 groups.push(components);
11922 }
11923 current = ancestry.parent(parent);
11924 }
11925 groups.reverse();
11926 groups.into_iter().flatten().collect()
11927}
11928
11929fn cpp_callable_dispatch_extensibility<'tree>(
11930 function_declarator: Node<'tree>,
11931 ancestry: &ParentIndex<'tree>,
11932) -> DispatchExtensibility {
11933 let mut declaration = None;
11934 let mut current = Some(function_declarator);
11935 while let Some(node) = current {
11936 match node.kind() {
11937 "template_declaration"
11938 | "preproc_if"
11939 | "preproc_ifdef"
11940 | "preproc_else"
11941 | "preproc_elif"
11942 | "preproc_call"
11943 | "ERROR" => return DispatchExtensibility::Open,
11944 "declaration" | "field_declaration" | "function_definition" => {
11945 declaration.get_or_insert(node);
11946 }
11947 "translation_unit" => break,
11948 _ => {}
11949 }
11950 current = ancestry.parent(node);
11951 }
11952 let Some(declaration) = declaration else {
11953 return DispatchExtensibility::Open;
11954 };
11955
11956 let mut saw_virtual_boundary = false;
11957 let mut stack = vec![declaration];
11958 while let Some(node) = stack.pop() {
11959 match node.kind() {
11960 "compound_statement" | "field_declaration_list" => continue,
11961 "final" | "final_specifier" => return DispatchExtensibility::Closed,
11962 "virtual"
11963 | "override"
11964 | "virtual_specifier"
11965 | "pure_virtual_clause"
11966 | "template_parameter_list"
11967 | "template_method"
11968 | "template_function"
11969 | "ERROR" => saw_virtual_boundary = true,
11970 _ => {}
11971 }
11972 let mut cursor = node.walk();
11973 stack.extend(node.children(&mut cursor));
11974 }
11975
11976 if saw_virtual_boundary {
11977 DispatchExtensibility::Open
11978 } else {
11979 DispatchExtensibility::Closed
11980 }
11981}
11982
11983fn cpp_callable_linkage<'tree>(
11984 declaration: Node<'tree>,
11985 source: &str,
11986 ancestry: &ParentIndex<'tree>,
11987) -> CallableLinkage {
11988 let mut enclosed_by_class = false;
11989 let mut current = ancestry.parent(declaration);
11990 while let Some(node) = current {
11991 if node.kind() == "namespace_definition"
11992 && node
11993 .child_by_field_name("name")
11994 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11995 {
11996 return CallableLinkage::Internal;
11997 }
11998 if matches!(
11999 node.kind(),
12000 "class_specifier" | "struct_specifier" | "union_specifier"
12001 ) {
12002 if node
12003 .child_by_field_name("name")
12004 .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12005 {
12006 return CallableLinkage::Internal;
12007 }
12008 enclosed_by_class = true;
12009 }
12010 if node.kind() == "lambda_expression"
12015 || node.kind() == "function_definition"
12016 && !is_recovered_exported_class_container(node, source)
12017 {
12018 return CallableLinkage::Internal;
12019 }
12020 current = ancestry.parent(node);
12021 }
12022
12023 if enclosed_by_class {
12024 return CallableLinkage::External;
12025 }
12026
12027 let mut cursor = declaration.walk();
12028 if declaration.named_children(&mut cursor).any(|child| {
12029 child.kind() == "storage_class_specifier"
12030 && normalize_cpp_whitespace(node_text(child, source)) == "static"
12031 }) {
12032 CallableLinkage::Internal
12033 } else {
12034 CallableLinkage::External
12035 }
12036}
12037
12038fn cpp_callable_return_type_text<'tree>(
12039 function_declarator: Node<'tree>,
12040 source: &str,
12041 ancestry: &ParentIndex<'tree>,
12042) -> Option<String> {
12043 if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
12044 return None;
12045 }
12046 if let Some((return_type, _)) =
12047 cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
12048 {
12049 let text = normalize_cpp_whitespace(node_text(return_type, source));
12050 return (!text.is_empty()).then_some(text);
12051 }
12052 let mut cursor = function_declarator.walk();
12053 if let Some(trailing) = function_declarator
12054 .named_children(&mut cursor)
12055 .find(|child| child.kind() == "trailing_return_type")
12056 && let Some(type_descriptor) = trailing.named_child(0)
12057 {
12058 let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
12059 if !text.is_empty() {
12060 return Some(text);
12061 }
12062 }
12063
12064 let mut current = function_declarator;
12065 let mut indirection = String::new();
12066 while let Some(parent) = ancestry.parent(current) {
12067 if matches!(
12068 parent.kind(),
12069 "function_definition" | "declaration" | "field_declaration"
12070 ) {
12071 let type_node = parent.child_by_field_name("type")?;
12072 if cpp_export_macro_token(node_text(type_node, source))
12073 && (0..parent.named_child_count()).any(|index| {
12074 parent
12075 .named_child(index)
12076 .is_some_and(|child| child.kind() == "ERROR")
12077 })
12078 {
12079 return None;
12084 }
12085 let base = normalize_cpp_whitespace(node_text(type_node, source));
12086 return (!base.is_empty()).then(|| format!("{base}{indirection}"));
12087 }
12088 let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
12089 || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
12090 && parent.named_child_count() == 1
12091 && parent.named_child(0) == Some(current));
12092 if wraps_current_declarator {
12093 match parent.kind() {
12094 "pointer_declarator" => indirection.push('*'),
12095 "reference_declarator" => {
12096 let reference = parent
12097 .children(&mut parent.walk())
12098 .find(|child| !child.is_named())
12099 .map(|child| node_text(child, source))
12100 .unwrap_or("&");
12101 indirection.push_str(reference);
12102 }
12103 "init_declarator" | "parenthesized_declarator" => {}
12104 _ => return None,
12105 }
12106 current = parent;
12107 continue;
12108 }
12109 return None;
12110 }
12111 None
12112}
12113
12114fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
12115 let mut required = 0;
12116 let mut total = 0;
12117 let mut repeated = false;
12118 let mut cursor = parameters_node.walk();
12119 for child in parameters_node.children(&mut cursor) {
12120 match child.kind() {
12121 "parameter_declaration" => {
12122 if cpp_parameter_is_explicit_object(child, source) {
12123 continue;
12124 }
12125 if child.child_by_field_name("declarator").is_none()
12126 && child
12127 .child_by_field_name("type")
12128 .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
12129 {
12130 continue;
12131 }
12132 required += 1;
12133 total += 1;
12134 }
12135 "optional_parameter_declaration" => total += 1,
12136 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12137 repeated = true;
12138 }
12139 _ => {}
12140 }
12141 }
12142 CallableArity::new(required, total, repeated)
12143}
12144
12145fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
12146 parameter
12147 .child_by_field_name("type")
12148 .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
12149 .and_then(|type_node| type_node.child_by_field_name("constraint"))
12150 .is_some_and(|constraint| {
12151 constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
12152 })
12153}
12154
12155#[derive(Clone, Copy)]
12163enum CppParameterSlot<'tree> {
12164 Declared(Node<'tree>),
12165 Ellipsis,
12166}
12167
12168fn cpp_callable_parameter_slots<'tree>(
12169 parameters_node: Node<'tree>,
12170 source: &str,
12171) -> Vec<CppParameterSlot<'tree>> {
12172 let mut slots = Vec::new();
12173 let mut cursor = parameters_node.walk();
12174 for parameter in parameters_node.children(&mut cursor) {
12175 match parameter.kind() {
12176 "parameter_declaration" | "optional_parameter_declaration" => {
12177 if cpp_parameter_is_explicit_object(parameter, source)
12178 || (parameter.child_by_field_name("declarator").is_none()
12179 && parameter
12180 .child_by_field_name("type")
12181 .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
12182 {
12183 continue;
12184 }
12185 slots.push(CppParameterSlot::Declared(parameter));
12186 }
12187 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12188 slots.push(CppParameterSlot::Ellipsis);
12189 }
12190 _ => {}
12191 }
12192 }
12193 slots
12194}
12195
12196fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
12197 cpp_callable_parameter_slots(parameters_node, source)
12198 .into_iter()
12199 .map(|slot| match slot {
12200 CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
12201 CppParameterSlot::Ellipsis => "...".to_string(),
12202 })
12203 .collect()
12204}
12205
12206#[derive(Debug, Clone, PartialEq, Eq)]
12212pub enum CppParameterType {
12213 Structured(StructuredTypeIdentity),
12216 Ellipsis,
12218 Unstructured,
12221}
12222
12223pub fn cpp_callable_parameter_type_identities<'tree>(
12229 function_declarator: Node<'tree>,
12230 source: &str,
12231 ancestry: &ParentIndex<'tree>,
12232) -> Vec<CppParameterType> {
12233 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12234 return Vec::new();
12235 };
12236 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12237 cpp_callable_parameter_slots(parameters_node, source)
12238 .into_iter()
12239 .map(|slot| match slot {
12240 CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
12241 CppParameterSlot::Declared(parameter) => {
12242 cpp_parameter_type_identity(parameter, source, &lexical_scope)
12243 .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
12244 }
12245 })
12246 .collect()
12247}
12248
12249pub fn cpp_declaration_type_identity<'tree>(
12256 declaration: Node<'tree>,
12257 declarator: Node<'tree>,
12258 source: &str,
12259 ancestry: &ParentIndex<'tree>,
12260) -> Option<StructuredTypeIdentity> {
12261 let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
12262 cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
12263}
12264
12265fn cpp_parameter_type_identity(
12266 parameter: Node<'_>,
12267 source: &str,
12268 lexical_scope: &[String],
12269) -> Option<StructuredTypeIdentity> {
12270 cpp_declaration_type_identity_in_scope(
12271 parameter,
12272 cpp_parameter_declarator(parameter),
12273 source,
12274 lexical_scope,
12275 )
12276}
12277
12278fn cpp_declaration_type_identity_in_scope(
12279 declaration: Node<'_>,
12280 declarator: Option<Node<'_>>,
12281 source: &str,
12282 lexical_scope: &[String],
12283) -> Option<StructuredTypeIdentity> {
12284 let type_node = declaration.child_by_field_name("type")?;
12285 let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
12286 if let Some(declarator) = declarator {
12287 for wrapper in cpp_structured_declarator_wrappers(declarator)?
12288 .into_iter()
12289 .rev()
12290 {
12291 identity = cpp_wrap_structured_type(identity, wrapper)?;
12292 }
12293 }
12294 Some(identity)
12295}
12296
12297#[derive(Debug, Clone, PartialEq, Eq)]
12310pub enum CppComparableSlot {
12311 Shape(CppComparableParameter),
12313 Ellipsis,
12315 Unstructured,
12318}
12319
12320#[derive(Debug, Clone, PartialEq, Eq)]
12333pub struct CppComparableParameter {
12334 nodes: Vec<CppComparableNode>,
12335 root: usize,
12336}
12337
12338#[derive(Debug, Clone, PartialEq, Eq)]
12346pub enum CppComparableNode {
12347 Named {
12348 name: StructuredTypeName,
12349 primitive: bool,
12350 konst: bool,
12351 volatil: bool,
12352 },
12353 Pointer {
12354 inner: usize,
12355 konst: bool,
12356 volatil: bool,
12357 },
12358 Reference {
12359 inner: usize,
12360 },
12361 Array {
12362 inner: usize,
12363 },
12364 Generic {
12365 base: usize,
12366 arguments: Vec<usize>,
12367 },
12368}
12369
12370impl CppComparableParameter {
12371 pub fn root(&self) -> usize {
12372 self.root
12373 }
12374
12375 pub fn node(&self, index: usize) -> &CppComparableNode {
12376 &self.nodes[index]
12377 }
12378
12379 fn adjust_parameter_top_level(&mut self) {
12390 let root = self.root;
12391 match &mut self.nodes[root] {
12392 CppComparableNode::Named { konst, volatil, .. }
12393 | CppComparableNode::Pointer { konst, volatil, .. } => {
12394 *konst = false;
12395 *volatil = false;
12396 }
12397 CppComparableNode::Array { inner } => {
12398 let inner = *inner;
12399 self.nodes[root] = CppComparableNode::Pointer {
12400 inner,
12401 konst: false,
12402 volatil: false,
12403 };
12404 }
12405 CppComparableNode::Generic { base, .. } => {
12406 let base = *base;
12407 let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
12408 unreachable!("a comparable generic's base is always a named leaf");
12409 };
12410 *konst = false;
12411 *volatil = false;
12412 }
12413 CppComparableNode::Reference { .. } => {}
12414 }
12415 }
12416}
12417
12418pub fn cpp_comparable_parameter_shapes<'tree>(
12425 function_declarator: Node<'tree>,
12426 source: &str,
12427 ancestry: &ParentIndex<'tree>,
12428) -> Vec<CppComparableSlot> {
12429 let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12430 return Vec::new();
12431 };
12432 let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12433 cpp_callable_parameter_slots(parameters_node, source)
12434 .into_iter()
12435 .map(|slot| match slot {
12436 CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
12437 CppParameterSlot::Declared(parameter) => {
12438 cpp_comparable_parameter(parameter, source, &lexical_scope)
12439 .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
12440 }
12441 })
12442 .collect()
12443}
12444
12445fn cpp_comparable_parameter(
12446 parameter: Node<'_>,
12447 source: &str,
12448 lexical_scope: &[String],
12449) -> Option<CppComparableParameter> {
12450 let type_node = parameter.child_by_field_name("type")?;
12451 let levels = match cpp_parameter_declarator(parameter) {
12452 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12453 None => Vec::new(),
12454 };
12455 let mut shape = cpp_comparable_type_shape(
12456 type_node,
12457 cpp_cv_qualifiers(parameter, source),
12458 levels,
12459 source,
12460 lexical_scope,
12461 )?;
12462 shape.adjust_parameter_top_level();
12463 Some(shape)
12464}
12465
12466fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
12477 let mut qualifiers = CppCvQualifiers::default();
12478 let mut cursor = node.walk();
12479 for child in node.named_children(&mut cursor) {
12480 if child.kind() != "type_qualifier" {
12481 continue;
12482 }
12483 match node_text(child, source) {
12484 "const" => qualifiers.konst = true,
12485 "volatile" => qualifiers.volatil = true,
12486 _ => {}
12487 }
12488 }
12489 qualifiers
12490}
12491
12492#[derive(Clone, Copy, Default)]
12493struct CppCvQualifiers {
12494 konst: bool,
12495 volatil: bool,
12496}
12497
12498impl CppCvQualifiers {
12499 fn union(self, other: Self) -> Self {
12500 Self {
12501 konst: self.konst || other.konst,
12502 volatil: self.volatil || other.volatil,
12503 }
12504 }
12505}
12506
12507#[derive(Clone, Copy)]
12509enum CppComparableLevel {
12510 Pointer { konst: bool, volatil: bool },
12511 Reference,
12512 Array,
12513}
12514
12515fn cpp_comparable_declarator_levels(
12528 declarator: Node<'_>,
12529 source: &str,
12530) -> Option<Vec<CppComparableLevel>> {
12531 let mut levels = Vec::new();
12532 let mut current = declarator;
12533 loop {
12534 match current.kind() {
12535 "pointer_declarator" | "abstract_pointer_declarator" => {
12536 let qualifiers = cpp_cv_qualifiers(current, source);
12537 levels.push(CppComparableLevel::Pointer {
12538 konst: qualifiers.konst,
12539 volatil: qualifiers.volatil,
12540 });
12541 }
12542 "reference_declarator" | "abstract_reference_declarator" => {
12543 levels.push(CppComparableLevel::Reference);
12544 }
12545 "array_declarator" | "abstract_array_declarator" => {
12546 levels.push(CppComparableLevel::Array);
12547 }
12548 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
12549 "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
12550 _ => return None,
12551 }
12552 let Some(next) = cpp_nested_declarator(current) else {
12553 return Some(levels);
12554 };
12555 current = next;
12556 }
12557}
12558
12559fn cpp_comparable_type_shape(
12566 type_node: Node<'_>,
12567 qualifiers: CppCvQualifiers,
12568 levels: Vec<CppComparableLevel>,
12569 source: &str,
12570 lexical_scope: &[String],
12571) -> Option<CppComparableParameter> {
12572 enum Work<'tree> {
12573 Visit {
12574 node: Node<'tree>,
12575 qualifiers: CppCvQualifiers,
12576 },
12577 ApplyLevels(Vec<CppComparableLevel>),
12578 BuildGeneric {
12579 argument_count: usize,
12580 },
12581 }
12582
12583 let mut nodes: Vec<CppComparableNode> = Vec::new();
12584 let mut values: Vec<usize> = Vec::new();
12585 let mut work = vec![
12586 Work::ApplyLevels(levels),
12587 Work::Visit {
12588 node: type_node,
12589 qualifiers,
12590 },
12591 ];
12592 while let Some(next) = work.pop() {
12593 match next {
12594 Work::Visit { node, qualifiers } => match node.kind() {
12595 "type_descriptor" => {
12596 let inner_type = node
12597 .child_by_field_name("type")
12598 .or_else(|| node.named_child(0))?;
12599 let mut cursor = node.walk();
12600 let declarator = node.child_by_field_name("declarator").or_else(|| {
12601 node.named_children(&mut cursor).find(|child| {
12602 child.id() != inner_type.id() && child.kind() != "type_qualifier"
12603 })
12604 });
12605 let levels = match declarator {
12606 Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12607 None => Vec::new(),
12608 };
12609 work.push(Work::ApplyLevels(levels));
12610 work.push(Work::Visit {
12611 node: inner_type,
12612 qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
12613 });
12614 }
12615 "sized_type_specifier" => {
12616 let name = StructuredTypeName::new(
12621 vec![normalize_cpp_whitespace(node_text(node, source))],
12622 lexical_scope.to_vec(),
12623 false,
12624 )?;
12625 values.push(cpp_push_comparable_node(
12626 &mut nodes,
12627 CppComparableNode::Named {
12628 name,
12629 primitive: true,
12630 konst: qualifiers.konst,
12631 volatil: qualifiers.volatil,
12632 },
12633 ));
12634 }
12635 "qualified_identifier"
12636 | "scoped_identifier"
12637 | "scoped_type_identifier"
12638 | "type_identifier"
12639 | "field_identifier"
12640 | "identifier"
12641 | "namespace_identifier"
12642 | "primitive_type"
12643 | "template_type" => {
12644 let name = cpp_structured_named_type(node, source, lexical_scope)?;
12645 values.push(cpp_push_comparable_node(
12646 &mut nodes,
12647 CppComparableNode::Named {
12648 name,
12649 primitive: node.kind() == "primitive_type",
12650 konst: qualifiers.konst,
12651 volatil: qualifiers.volatil,
12652 },
12653 ));
12654 if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
12655 let mut cursor = arguments_node.walk();
12656 let arguments = arguments_node
12657 .named_children(&mut cursor)
12658 .filter(|child| !child.is_extra() && child.kind() != "comment")
12659 .collect::<Vec<_>>();
12660 work.push(Work::BuildGeneric {
12661 argument_count: arguments.len(),
12662 });
12663 work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
12664 node: argument,
12665 qualifiers: CppCvQualifiers::default(),
12666 }));
12667 }
12668 }
12669 _ => {
12670 let inner = node.child_by_field_name("type").or_else(|| {
12671 (node.named_child_count() == 1)
12672 .then(|| node.named_child(0))
12673 .flatten()
12674 })?;
12675 work.push(Work::Visit {
12676 node: inner,
12677 qualifiers,
12678 });
12679 }
12680 },
12681 Work::ApplyLevels(levels) => {
12682 let mut root = values.pop()?;
12683 for level in levels {
12684 let node = match level {
12685 CppComparableLevel::Pointer { konst, volatil } => {
12686 CppComparableNode::Pointer {
12687 inner: root,
12688 konst,
12689 volatil,
12690 }
12691 }
12692 CppComparableLevel::Reference => {
12693 CppComparableNode::Reference { inner: root }
12694 }
12695 CppComparableLevel::Array => CppComparableNode::Array { inner: root },
12696 };
12697 root = cpp_push_comparable_node(&mut nodes, node);
12698 }
12699 values.push(root);
12700 }
12701 Work::BuildGeneric { argument_count } => {
12702 let value_count = argument_count.checked_add(1)?;
12703 let start = values.len().checked_sub(value_count)?;
12704 let mut built = values.split_off(start);
12705 let base = built.remove(0);
12706 values.push(cpp_push_comparable_node(
12707 &mut nodes,
12708 CppComparableNode::Generic {
12709 base,
12710 arguments: built,
12711 },
12712 ));
12713 }
12714 }
12715 }
12716 let root = (values.len() == 1).then(|| values.pop()).flatten()?;
12717 debug_assert_eq!(
12718 root,
12719 nodes.len().saturating_sub(1),
12720 "comparable nodes are appended in post-order, so the root is the last one"
12721 );
12722 Some(CppComparableParameter { nodes, root })
12723}
12724
12725fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
12726 nodes.push(node);
12727 nodes.len() - 1
12728}
12729
12730fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
12736 let mut current = node;
12737 loop {
12738 match current.kind() {
12739 "template_type" => return current.child_by_field_name("arguments"),
12740 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12741 current = current.child_by_field_name("name")?;
12742 }
12743 _ => return None,
12744 }
12745 }
12746}
12747
12748pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
12754 let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
12755 loop {
12756 if matches!(
12757 current.kind(),
12758 "declaration" | "field_declaration" | "function_definition"
12759 ) && let Some(declarator) = current
12760 .child_by_field_name("declarator")
12761 .and_then(extract_function_declarator)
12762 {
12763 return Some(declarator);
12764 }
12765 current = current.parent()?;
12766 }
12767}
12768
12769fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
12770 let mut labels = Vec::new();
12771 let mut cursor = parameters_node.walk();
12772 for child in parameters_node.children(&mut cursor) {
12773 match child.kind() {
12774 "parameter_declaration" | "optional_parameter_declaration" => {
12775 if let Some(name_node) = child
12776 .child_by_field_name("declarator")
12777 .and_then(cpp_declarator_label_node)
12778 {
12779 labels.push(name_node);
12780 } else {
12781 labels.push(child);
12782 }
12783 }
12784 "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12785 labels.push(child);
12786 }
12787 _ => {}
12788 }
12789 }
12790 labels
12791}
12792
12793fn cpp_signature_search_start<'tree>(
12794 signature: &str,
12795 function_declarator: Node<'tree>,
12796 source: &str,
12797 ancestry: &ParentIndex<'tree>,
12798) -> usize {
12799 let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
12800 return 0;
12801 };
12802 let raw = node_text(enclosing, source);
12803 let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
12804 let offset = function_declarator
12805 .start_byte()
12806 .saturating_sub(enclosing.start_byte())
12807 .saturating_sub(leading_trim_bytes);
12808 offset.min(signature.len())
12809}
12810
12811fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
12812 match node.kind() {
12813 "identifier" | "field_identifier" => Some(node),
12814 "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
12815 .child_by_field_name("declarator")
12816 .or_else(|| last_named_child(node))
12817 .and_then(cpp_declarator_label_node),
12818 "array_declarator" => node
12819 .child_by_field_name("declarator")
12820 .and_then(cpp_declarator_label_node),
12821 "function_declarator" => node
12822 .child_by_field_name("declarator")
12823 .or_else(|| node.child_by_field_name("name"))
12824 .or_else(|| last_named_child(node))
12825 .and_then(cpp_declarator_label_node),
12826 _ => None,
12827 }
12828}
12829
12830fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
12831 let base_type = parameter
12832 .child_by_field_name("type")
12833 .map(|node| normalize_cpp_whitespace(node_text(node, source)))
12834 .unwrap_or_default();
12835 let declarator = cpp_parameter_declarator(parameter);
12836 let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
12843 let mut cursor = parameter.walk();
12844 let qualifiers = parameter
12845 .named_children(&mut cursor)
12846 .filter(|child| child.kind() == "type_qualifier")
12847 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12848 .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
12849 .collect::<Vec<_>>()
12850 .join(" ");
12851 let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
12852 (true, _) => base_type,
12853 (_, true) => qualifiers,
12854 (false, false) => format!("{qualifiers} {base_type}"),
12855 };
12856 let declarator_suffix = declarator
12857 .map(|node| cpp_declarator_suffix_without_name(node, source))
12858 .unwrap_or_default();
12859
12860 let combined = if type_text.is_empty() {
12861 declarator_suffix
12862 } else if declarator_suffix.is_empty() {
12863 type_text
12864 } else {
12865 format!("{type_text} {declarator_suffix}")
12866 };
12867 normalize_cpp_type_text(&combined)
12868}
12869
12870fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
12871 parameter.child_by_field_name("declarator").or_else(|| {
12872 let mut cursor = parameter.walk();
12878 parameter
12879 .named_children(&mut cursor)
12880 .find(|child| is_cpp_abstract_declarator(child.kind()))
12881 })
12882}
12883
12884pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
12887 let mut current = Some(declarator);
12888 while let Some(node) = current {
12889 if matches!(
12890 node.kind(),
12891 "pointer_declarator"
12892 | "abstract_pointer_declarator"
12893 | "reference_declarator"
12894 | "abstract_reference_declarator"
12895 | "array_declarator"
12896 | "abstract_array_declarator"
12897 | "function_declarator"
12898 | "abstract_function_declarator"
12899 ) {
12900 return true;
12901 }
12902 current = cpp_nested_declarator(node);
12903 }
12904 false
12905}
12906
12907fn is_cpp_abstract_declarator(kind: &str) -> bool {
12908 matches!(
12909 kind,
12910 "abstract_pointer_declarator"
12911 | "abstract_reference_declarator"
12912 | "abstract_array_declarator"
12913 | "abstract_function_declarator"
12914 | "abstract_parenthesized_declarator"
12915 )
12916}
12917
12918fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
12919 node.child_by_field_name("declarator").or_else(|| {
12920 if is_cpp_abstract_declarator(node.kind()) {
12921 let mut cursor = node.walk();
12922 node.named_children(&mut cursor)
12923 .find(|child| is_cpp_abstract_declarator(child.kind()))
12924 } else {
12925 last_named_child(node)
12929 }
12930 })
12931}
12932
12933fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
12934 match node.kind() {
12935 "identifier" | "field_identifier" => String::new(),
12936 "pointer_declarator" | "abstract_pointer_declarator" => {
12937 let inner = cpp_nested_declarator(node)
12938 .map(|child| cpp_declarator_suffix_without_name(child, source))
12939 .unwrap_or_default();
12940 format!("*{inner}")
12941 }
12942 "reference_declarator" | "abstract_reference_declarator" => {
12943 let inner = cpp_nested_declarator(node)
12944 .map(|child| cpp_declarator_suffix_without_name(child, source))
12945 .unwrap_or_default();
12946 let reference = node
12947 .children(&mut node.walk())
12948 .find(|child| matches!(child.kind(), "&" | "&&"))
12949 .map(|child| node_text(child, source))
12950 .unwrap_or("&");
12951 format!("{reference}{inner}")
12952 }
12953 "array_declarator" | "abstract_array_declarator" => {
12954 let inner = cpp_nested_declarator(node)
12955 .map(|child| cpp_declarator_suffix_without_name(child, source))
12956 .unwrap_or_default();
12957 let size = node
12958 .child_by_field_name("size")
12959 .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12960 .unwrap_or_default();
12961 format!("{inner}[{size}]")
12962 }
12963 "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
12964 let inner = cpp_nested_declarator(node);
12965 inner
12966 .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
12967 .unwrap_or_default()
12968 }
12969 "function_declarator" | "abstract_function_declarator" => {
12970 let inner = cpp_nested_declarator(node)
12971 .map(|child| cpp_declarator_suffix_without_name(child, source))
12972 .unwrap_or_default();
12973 let params = node
12974 .child_by_field_name("parameters")
12975 .map(|child| cpp_parameter_signature(child, source))
12976 .unwrap_or_else(|| "()".to_string());
12977 format!("{inner}{params}")
12978 }
12979 _ => {
12980 let text = normalize_cpp_whitespace(node_text(node, source));
12981 let name = extract_declarator_name(node, source);
12982 if name.is_empty() {
12983 text
12984 } else {
12985 text.replace(&name, "").trim().to_string()
12986 }
12987 }
12988 }
12989}
12990
12991fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
12992 collapse_cpp_whitespace(
12993 suffix
12994 .trim()
12995 .trim_start_matches("->")
12996 .trim_start_matches('{')
12997 .trim_end_matches(';'),
12998 )
12999}
13000
13001pub fn normalize_cpp_whitespace(value: &str) -> String {
13002 collapse_cpp_whitespace(value)
13003}
13004
13005fn normalize_cpp_type_text(value: &str) -> String {
13006 collapse_cpp_whitespace(value)
13007 .replace(", ", ",")
13008 .replace(" <", "<")
13009 .replace("< ", "<")
13010 .replace(" >", ">")
13011}
13012
13013fn collapse_cpp_whitespace(value: &str) -> String {
13014 let mut result = String::new();
13015 let mut prev_space = false;
13016 for ch in value.chars() {
13017 if ch.is_whitespace() {
13018 if !prev_space {
13019 result.push(' ');
13020 }
13021 prev_space = true;
13022 } else {
13023 result.push(ch);
13024 prev_space = false;
13025 }
13026 }
13027 result.trim().to_string()
13028}
13029
13030pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
13031 node_source_text(node, source)
13032}
13033
13034pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
13035 walk_named_tree_preorder(node, true, |node| {
13036 match node.kind() {
13037 "type_identifier" | "identifier" | "qualified_identifier" => {
13038 let text = node_text(node, source).trim();
13039 if !text.is_empty() {
13040 identifiers.insert(text.to_string());
13041 }
13042 }
13043 _ => {}
13044 }
13045 WalkControl::Continue
13046 });
13047}
13048
13049fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
13050 node.child_by_field_name("body").or_else(|| {
13051 let mut cursor = node.walk();
13052 node.named_children(&mut cursor).find(|child| {
13053 matches!(
13054 child.kind(),
13055 "declaration_list" | "field_declaration_list" | "enumerator_list"
13056 )
13057 })
13058 })
13059}
13060
13061fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
13072 if !matches!(
13073 node.kind(),
13074 "class_specifier" | "struct_specifier" | "union_specifier"
13075 ) {
13076 return None;
13077 }
13078 let body = cpp_body_node(node)?;
13079 if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
13080 return None;
13081 }
13082 let open = body.child(0)?;
13083 let close = body.child(body.child_count().checked_sub(1)?)?;
13084 if open.kind() != "{"
13085 || open.is_missing()
13086 || close.kind() != "}"
13087 || close.is_missing()
13088 || close.end_byte() != body.end_byte()
13089 || body.end_byte() > node.end_byte()
13090 || node
13091 .parent()
13092 .is_some_and(|parent| body.end_byte() >= parent.end_byte())
13093 {
13094 return None;
13095 }
13096 Some(close)
13097}
13098
13099fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
13100 if node.kind() == "namespace_definition" {
13101 return true;
13102 }
13103 let mut cursor = node.walk();
13104 node.named_children(&mut cursor)
13105 .any(cpp_contains_namespace_definition)
13106}
13107
13108struct CppNestedNamespaceSentinel<'tree> {
13109 function: Node<'tree>,
13110 body: Node<'tree>,
13111 namespace_components: Vec<String>,
13112}
13113
13114#[derive(Debug, Clone)]
13124pub struct CppSentinelRecoveredOwner {
13125 pub range: Range,
13126 pub owner_name_start_byte: usize,
13130 pub namespace_component_count: usize,
13134 pub scope_components: Vec<String>,
13135}
13136
13137#[derive(Debug, Clone)]
13138pub struct CppSentinelRecoveredClass {
13139 pub namespace_range: Range,
13140 pub namespace_scope_components: Vec<String>,
13141 pub class_range: Range,
13142 pub scope_components: Vec<String>,
13144 pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
13148}
13149
13150pub fn cpp_sentinel_recovered_scope_for_node(
13156 node: Node<'_>,
13157 source: &str,
13158 recovered_classes: &[CppSentinelRecoveredClass],
13159) -> Option<Vec<String>> {
13160 let contains =
13161 |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
13162 let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
13163 for recovered in recovered_classes {
13164 for owner in recovered
13165 .owner_ranges
13166 .iter()
13167 .filter(|owner| contains(owner.range))
13168 {
13169 let replace = best_owner.is_none_or(|existing| {
13170 owner.range.end_byte.saturating_sub(owner.range.start_byte)
13171 < existing
13172 .range
13173 .end_byte
13174 .saturating_sub(existing.range.start_byte)
13175 });
13176 if replace {
13177 best_owner = Some(owner);
13178 }
13179 }
13180 }
13181 if let Some(owner) = best_owner {
13182 let mut scope = owner.scope_components.clone();
13183 if node.start_byte() < owner.owner_name_start_byte {
13184 scope.truncate(owner.namespace_component_count);
13185 }
13186 return Some(scope);
13187 }
13188
13189 let class = recovered_classes
13190 .iter()
13191 .filter(|recovered| contains(recovered.class_range))
13192 .min_by_key(|recovered| {
13193 recovered
13194 .class_range
13195 .end_byte
13196 .saturating_sub(recovered.class_range.start_byte)
13197 });
13198 let class_scope = class.is_some();
13199 let mut scope = if let Some(class) = class {
13200 class.scope_components.clone()
13201 } else {
13202 let namespace = recovered_classes
13203 .iter()
13204 .filter(|recovered| contains(recovered.namespace_range))
13205 .min_by_key(|recovered| {
13206 recovered
13207 .namespace_range
13208 .end_byte
13209 .saturating_sub(recovered.namespace_range.start_byte)
13210 })?;
13211 let mut scope = namespace.namespace_scope_components.clone();
13212 let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
13213 let common_prefix = scope
13214 .iter()
13215 .zip(&parser_namespace)
13216 .take_while(|(recovered, parser)| recovered == parser)
13217 .count();
13218 scope.extend(parser_namespace.into_iter().skip(common_prefix));
13219 scope
13220 };
13221 if class_scope {
13222 let mut ancestor_components = Vec::new();
13223 let mut ancestor = node.parent();
13224 while let Some(current) = ancestor {
13225 if matches!(
13226 current.kind(),
13227 "class_specifier" | "struct_specifier" | "union_specifier"
13228 ) && let Some(name) = current.child_by_field_name("name")
13229 && let Some(name_components) = cpp_name_components(name, source)
13230 {
13231 ancestor_components.push(
13232 name_components
13233 .into_iter()
13234 .map(|component| component.name)
13235 .collect::<Vec<_>>(),
13236 );
13237 }
13238 ancestor = current.parent();
13239 }
13240 ancestor_components.reverse();
13241 let base_len = scope.len();
13242 for component in ancestor_components.into_iter().flatten() {
13243 if scope.len() >= base_len && scope.last() == Some(&component) {
13244 continue;
13245 }
13246 scope.push(component);
13247 }
13248 }
13249 Some(scope)
13250}
13251
13252struct CppSentinelFragmentedClassTail<'tree> {
13253 class_node: Node<'tree>,
13254 template_node: Option<Node<'tree>>,
13255 name: String,
13256 raw_supertypes: Option<Vec<String>>,
13257 fragmented: FragmentedExportBody,
13258 consumed_start: usize,
13259}
13260
13261struct CppSentinelFragmentedClassErrorPrefix<'tree> {
13262 name: String,
13263 open: Node<'tree>,
13264 raw_supertypes: Option<Vec<String>>,
13265}
13266
13267struct CppSentinelDirectBodyClassRegion {
13268 namespace_components: Vec<String>,
13269 class_start: usize,
13270 class_start_line: usize,
13271 class_close_end: usize,
13272 class_close_line: usize,
13273 name: String,
13274}
13275
13276fn cpp_sentinel_body_class_candidate<'tree>(
13277 child: Node<'tree>,
13278) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13279 if matches!(
13280 child.kind(),
13281 "class_specifier" | "struct_specifier" | "union_specifier"
13282 ) {
13283 return Some((child, None));
13284 }
13285 if child.kind() != "template_declaration" {
13286 if child.kind() == "declaration" {
13287 return Some((first_class_like_child(child)?, None));
13288 }
13289 return None;
13290 }
13291 let mut cursor = child.walk();
13292 let class_node = child.named_children(&mut cursor).find_map(|candidate| {
13293 if matches!(
13294 candidate.kind(),
13295 "class_specifier" | "struct_specifier" | "union_specifier"
13296 ) {
13297 Some(candidate)
13298 } else if candidate.kind() == "declaration" {
13299 first_class_like_child(candidate)
13300 } else {
13301 None
13302 }
13303 })?;
13304 Some((class_node, Some(child)))
13305}
13306
13307fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
13313 node: Node<'tree>,
13314 source: &str,
13315) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
13316 let name = malformed_class_error_owner_name(node, source)?;
13317 let mut cursor = node.walk();
13318 let children = node.children(&mut cursor).collect::<Vec<_>>();
13319 let keyword = children.first()?;
13320 let open_index = children.iter().position(|child| child.kind() == "{")?;
13321 if children[open_index + 1..]
13322 .iter()
13323 .any(|child| child.kind() == "}")
13324 {
13325 return None;
13326 }
13327 let raw_supertypes =
13328 matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
13329 Some(CppSentinelFragmentedClassErrorPrefix {
13330 name,
13331 open: children[open_index],
13332 raw_supertypes,
13333 })
13334}
13335
13336fn cpp_sentinel_direct_body_class_candidate<'tree>(
13337 child: Node<'tree>,
13338) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13339 if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
13340 return Some(candidate);
13341 }
13342 if child.kind() != "template_declaration" {
13343 return None;
13344 }
13345 let mut cursor = child.walk();
13346 let wrapper = child
13347 .named_children(&mut cursor)
13348 .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
13349 Some((first_class_like_child(wrapper)?, Some(child)))
13350}
13351
13352fn cpp_sentinel_direct_namespace_components(
13353 function: Node<'_>,
13354 body: Node<'_>,
13355 source: &str,
13356) -> Option<Vec<String>> {
13357 let mut cursor = function.walk();
13358 let children = function
13359 .named_children(&mut cursor)
13360 .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
13361 .collect::<Vec<_>>();
13362 let sentinel_index = children.iter().rposition(|child| {
13363 direct_identifier_name(*child, source)
13364 .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
13365 })?;
13366 let mut identifiers = Vec::new();
13367 let mut stack = children[sentinel_index + 1..]
13368 .iter()
13369 .rev()
13370 .copied()
13371 .collect::<Vec<_>>();
13372 while let Some(current) = stack.pop() {
13373 if let Some(name) = direct_identifier_name(current, source) {
13374 identifiers.push(name);
13375 continue;
13376 }
13377 let mut cursor = current.walk();
13378 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
13379 stack.extend(children.into_iter().rev());
13380 }
13381 let [keyword, namespace] = identifiers.as_slice() else {
13382 return None;
13383 };
13384 (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
13385 .then(|| vec![namespace.clone()])
13386}
13387
13388fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
13389 let mut sibling = class_semicolon.next_named_sibling();
13390 let namespace_close = loop {
13391 let Some(current) = sibling else {
13392 return false;
13393 };
13394 sibling = current.next_named_sibling();
13395 if current.kind() != "comment" {
13396 break current;
13397 }
13398 };
13399 if !cpp_is_stray_close_brace(namespace_close, source) {
13400 return false;
13401 }
13402 loop {
13403 let Some(current) = sibling else {
13404 return false;
13405 };
13406 sibling = current.next_named_sibling();
13407 if current.kind() == "comment" {
13408 continue;
13409 }
13410 return direct_identifier_name(current, source)
13411 .is_some_and(|name| name.ends_with("NAMESPACE_END"));
13412 }
13413}
13414
13415fn cpp_sentinel_macro_body_class_region<'tree>(
13416 node: Node<'tree>,
13417 source: &str,
13418 ancestry: &ParentIndex<'tree>,
13419) -> Option<CppSentinelDirectBodyClassRegion> {
13420 let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
13421 return None;
13422 };
13423 if node.kind() != "function_definition" || !node.has_error() {
13424 return None;
13425 }
13426 let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
13427 let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
13428 let mut cursor = body.walk();
13429 let candidates = body
13430 .named_children(&mut cursor)
13431 .filter_map(cpp_sentinel_direct_body_class_candidate)
13432 .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
13433 .collect::<Vec<_>>();
13434 let [(class_node, template_node)] = candidates.as_slice() else {
13435 return None;
13436 };
13437 let original_body = cpp_body_node(*class_node)?;
13438 let name = class_like_name(*class_node, source, ancestry)?;
13439 if name.is_empty() || cpp_export_macro_token(&name) {
13440 return None;
13441 }
13442
13443 let mut sibling = node.next_named_sibling();
13444 let (class_close_start, class_close_end, class_close_line) = loop {
13445 let current = sibling?;
13446 let next = current.next_named_sibling();
13447 if cpp_is_stray_close_brace(current, source)
13448 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
13449 {
13450 let semicolon = next.expect("checked above");
13451 if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
13452 return None;
13453 }
13454 break (
13455 current.start_byte(),
13456 semicolon.end_byte(),
13457 semicolon.end_position().row + 1,
13458 );
13459 }
13460 sibling = next;
13461 };
13462 let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
13463 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
13464 let root = tree.root_node();
13465 let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
13466 let reparsed_ancestry = ParentIndex::new(root);
13469 let reparsed =
13470 cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
13471 if reparsed.name != name
13472 || reparsed.declaration_node.start_byte() != class_node.start_byte()
13473 || reparsed.body.start_byte() != original_body.start_byte()
13474 || class_close_start <= reparsed.body.end_byte()
13475 || class_close_end <= class_node.end_byte()
13476 {
13477 return None;
13478 }
13479 Some(CppSentinelDirectBodyClassRegion {
13480 namespace_components,
13481 class_start: reparse_start,
13482 class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
13483 node.start_position().row + 1
13484 }),
13485 class_close_end,
13486 class_close_line,
13487 name,
13488 })
13489}
13490
13491fn cpp_nested_namespace_sentinel<'tree>(
13503 node: Node<'tree>,
13504 source: &str,
13505 ancestry: &ParentIndex<'tree>,
13506) -> Option<CppNestedNamespaceSentinel<'tree>> {
13507 if !node.has_error() {
13508 return None;
13509 }
13510
13511 let (function, mut namespace_components) = if node.kind() == "ERROR" {
13512 let mut cursor = node.walk();
13513 let functions = node
13514 .named_children(&mut cursor)
13515 .filter(|child| child.kind() == "function_definition")
13516 .collect::<Vec<_>>();
13517 let [function] = functions.as_slice() else {
13518 return None;
13519 };
13520 if !function.has_error() {
13521 return None;
13522 }
13523 let mut cursor = node.walk();
13524 let children = node.children(&mut cursor).collect::<Vec<_>>();
13525 let function_index = children
13526 .iter()
13527 .position(|child| same_node(*child, *function))?;
13528 let [outer_keyword, outer_name, outer_open] =
13529 children.get(function_index.checked_sub(3)?..function_index)?
13530 else {
13531 return None;
13532 };
13533 if outer_keyword.kind() != "namespace"
13534 || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
13535 || outer_open.kind() != "{"
13536 {
13537 return None;
13538 }
13539 (
13540 *function,
13541 vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
13542 )
13543 } else if node.kind() == "function_definition" {
13544 let declaration_list = node.parent()?;
13545 let namespace = declaration_list.parent()?;
13546 if declaration_list.kind() != "declaration_list"
13547 || namespace.kind() != "namespace_definition"
13548 || namespace.child_by_field_name("body") != Some(declaration_list)
13549 {
13550 return None;
13551 }
13552 (node, Vec::new())
13553 } else {
13554 return None;
13555 };
13556
13557 let mut cursor = function.walk();
13558 let named = function
13559 .named_children(&mut cursor)
13560 .filter(|child| child.kind() != "comment")
13561 .collect::<Vec<_>>();
13562 let [first_type, inner_error, inner_name, body] = named.as_slice() else {
13563 return None;
13564 };
13565 if first_type.kind() != "type_identifier" {
13566 return None;
13567 }
13568 let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
13569 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
13570 return None;
13571 }
13572 if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
13573 return None;
13574 }
13575 let inner_keyword = inner_error.named_child(0)?;
13576 if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
13577 return None;
13578 }
13579 if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
13580 return None;
13581 }
13582 let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
13583 if inner_name.is_empty() || body.kind() != "compound_statement" {
13584 return None;
13585 }
13586 namespace_components.push(inner_name);
13587
13588 let mut cursor = body.walk();
13589 let has_complete_class = body.named_children(&mut cursor).any(|child| {
13590 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13591 cpp_body_node(class_node).is_some()
13592 && class_like_name(class_node, source, ancestry)
13593 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13594 })
13595 });
13596 if !has_complete_class
13597 && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
13598 {
13599 return None;
13600 }
13601
13602 Some(CppNestedNamespaceSentinel {
13603 function,
13604 body: *body,
13605 namespace_components,
13606 })
13607}
13608
13609fn cpp_root_namespace_sentinel<'tree>(
13618 node: Node<'tree>,
13619 source: &str,
13620 ancestry: &ParentIndex<'tree>,
13621) -> Option<CppNestedNamespaceSentinel<'tree>> {
13622 if node.kind() != "function_definition"
13623 || !node.has_error()
13624 || node.parent()?.kind() != "translation_unit"
13625 {
13626 return None;
13627 }
13628 let first_type = node.child_by_field_name("type")?;
13629 let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
13630 if first_type.kind() != "type_identifier"
13631 || sentinel.is_empty()
13632 || !cpp_export_macro_token(&sentinel)
13633 {
13634 return None;
13635 }
13636 let declarator = node.child_by_field_name("declarator")?;
13637 let body = node.child_by_field_name("body")?;
13638 if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
13639 return None;
13640 }
13641 let mut cursor = node.walk();
13642 let named = node
13643 .named_children(&mut cursor)
13644 .filter(|child| child.kind() != "comment")
13645 .collect::<Vec<_>>();
13646 let [named_type, named_declarator, named_body] = named.as_slice() else {
13647 return None;
13648 };
13649 if !same_node(*named_type, first_type)
13650 || !same_node(*named_declarator, declarator)
13651 || !same_node(*named_body, body)
13652 {
13653 return None;
13654 }
13655 let mut declarator_components = Vec::new();
13656 let mut valid_components = true;
13657 walk_named_tree_preorder(declarator, true, |component| {
13658 if !matches!(
13659 component.kind(),
13660 "identifier" | "namespace_identifier" | "type_identifier"
13661 ) {
13662 return WalkControl::Continue;
13663 }
13664 let Some(component) = canonical_cpp_qualified_component(component, source) else {
13665 valid_components = false;
13666 return WalkControl::Break;
13667 };
13668 declarator_components.push(component.name);
13669 WalkControl::SkipChildren
13670 });
13671 if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
13672 return None;
13673 }
13674 declarator_components.remove(0);
13675 let namespace_components = declarator_components;
13676 if namespace_components.is_empty()
13677 || namespace_components
13678 .iter()
13679 .any(|component| component.is_empty() || cpp_export_macro_token(component))
13680 {
13681 return None;
13682 }
13683
13684 let mut cursor = body.walk();
13685 let has_complete_class = body.named_children(&mut cursor).any(|child| {
13686 cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13687 cpp_body_node(class_node).is_some()
13688 && class_like_name(class_node, source, ancestry)
13689 .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13690 })
13691 });
13692 if !has_complete_class
13693 && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
13694 {
13695 return None;
13696 }
13697
13698 Some(CppNestedNamespaceSentinel {
13699 function: node,
13700 body,
13701 namespace_components,
13702 })
13703}
13704
13705fn cpp_sentinel_fragmented_class_tail<'tree>(
13714 function: Node<'tree>,
13715 body: Node<'tree>,
13716 source: &str,
13717 ancestry: &ParentIndex<'tree>,
13718) -> Option<CppSentinelFragmentedClassTail<'tree>> {
13719 let mut cursor = body.walk();
13720 let candidates = body
13721 .named_children(&mut cursor)
13722 .filter_map(|child| {
13723 if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
13724 let class_body = cpp_body_node(class_node)?;
13725 if !class_node.has_error() {
13726 return None;
13727 }
13728 let name = class_like_name(class_node, source, ancestry)?;
13729 let raw_supertypes =
13730 matches!(class_node.kind(), "class_specifier" | "struct_specifier")
13731 .then(|| extract_cpp_supertypes(class_node, source));
13732 return Some((
13733 class_node,
13734 template_node,
13735 name,
13736 class_body,
13737 class_body.start_byte().checked_add(1)?,
13738 raw_supertypes,
13739 ));
13740 }
13741 let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
13742 Some((
13743 child,
13744 None,
13745 prefix.name,
13746 prefix.open,
13747 prefix.open.end_byte(),
13748 prefix.raw_supertypes,
13749 ))
13750 })
13751 .collect::<Vec<_>>();
13752 let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
13753 candidates.as_slice()
13754 else {
13755 return None;
13756 };
13757 if name.is_empty() || cpp_export_macro_token(name) {
13758 return None;
13759 }
13760
13761 let (close, semicolon) =
13762 cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
13763
13764 let reparse_end = close.start_byte();
13765 if *reparse_start >= reparse_end {
13766 return None;
13767 }
13768 let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
13769 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
13770 return None;
13771 }
13772 let class_range = Range {
13773 start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13774 end_byte: semicolon.end_byte(),
13775 start_line: template_node.map_or(class_node.start_position().row, |node| {
13776 node.start_position().row
13777 }) + 1,
13778 end_line: semicolon.end_position().row + 1,
13779 };
13780 Some(CppSentinelFragmentedClassTail {
13781 class_node: *class_node,
13782 template_node: *template_node,
13783 name: name.clone(),
13784 raw_supertypes: raw_supertypes.clone(),
13785 fragmented: FragmentedExportBody {
13786 reparse_start: *reparse_start,
13787 reparse_end,
13788 class_range,
13789 },
13790 consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13791 })
13792}
13793
13794pub fn cpp_sentinel_recovered_classes(
13803 root: Node<'_>,
13804 source: &str,
13805) -> Vec<CppSentinelRecoveredClass> {
13806 if !root.has_error() {
13807 return Vec::new();
13808 }
13809 let ancestry = ParentIndex::new(root);
13813 let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
13814 let mut stack = vec![root];
13815 while let Some(current) = stack.pop() {
13816 if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
13817 .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
13818 {
13819 let namespace_components = cpp_sentinel_recovered_namespace_components(
13820 recovered.function,
13821 &recovered.namespace_components,
13822 source,
13823 );
13824 let fragmented = cpp_sentinel_fragmented_class_tail(
13825 recovered.function,
13826 recovered.body,
13827 source,
13828 &ancestry,
13829 );
13830 let mut class_candidates = Vec::new();
13831 let mut cursor = recovered.body.walk();
13832 for (class_node, template_node) in recovered
13833 .body
13834 .named_children(&mut cursor)
13835 .filter_map(cpp_sentinel_body_class_candidate)
13836 {
13837 let Some(name) = class_like_name(class_node, source, &ancestry) else {
13838 continue;
13839 };
13840 if name.is_empty() || cpp_export_macro_token(&name) {
13841 continue;
13842 }
13843 let is_fragmented = fragmented
13844 .as_ref()
13845 .is_some_and(|tail| same_node(tail.class_node, class_node));
13846 if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
13847 continue;
13848 }
13849 let class_range = if is_fragmented {
13850 fragmented
13851 .as_ref()
13852 .map(|tail| tail.fragmented.class_range)
13853 .expect("fragmented class range is present when class matches")
13854 } else {
13855 cpp_declaration_range(template_node.unwrap_or(class_node))
13856 };
13857 class_candidates.push((class_range, name));
13858 }
13859 if let Some(fragmented) = fragmented
13860 .as_ref()
13861 .filter(|tail| tail.class_node.kind() == "ERROR")
13862 {
13863 class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
13864 }
13865
13866 let mut owner_ranges =
13867 cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
13868 cpp_sentinel_extend_unique_owner_ranges(
13869 &mut owner_ranges,
13870 cpp_sentinel_recovered_sibling_owner_ranges(
13871 recovered.function,
13872 &namespace_components,
13873 source,
13874 ),
13875 );
13876 for (class_range, name) in class_candidates {
13877 push_cpp_sentinel_recovered_class(
13878 &mut recovered_classes,
13879 cpp_declaration_range(recovered.body),
13880 &namespace_components,
13881 class_range,
13882 name,
13883 &owner_ranges,
13884 );
13885 }
13886
13887 if let Some(declaration_list) = recovered
13888 .function
13889 .parent()
13890 .filter(|parent| parent.kind() == "declaration_list")
13891 {
13892 let outer_namespace =
13893 cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
13894 push_cpp_sentinel_sibling_classes(
13895 &mut recovered_classes,
13896 declaration_list,
13897 recovered.function,
13898 &outer_namespace,
13899 source,
13900 &ancestry,
13901 );
13902 }
13903 } else if let Some(region) =
13904 cpp_sentinel_macro_body_class_region(current, source, &ancestry)
13905 {
13906 let namespace_components = cpp_sentinel_recovered_namespace_components(
13907 current,
13908 ®ion.namespace_components,
13909 source,
13910 );
13911 let owner_container = current
13912 .parent()
13913 .filter(|parent| parent.kind() == "declaration_list")
13914 .unwrap_or(current);
13915 let owner_ranges =
13916 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
13917 push_cpp_sentinel_recovered_class(
13918 &mut recovered_classes,
13919 cpp_declaration_range(owner_container),
13920 &namespace_components,
13921 Range {
13922 start_byte: region.class_start,
13923 end_byte: region.class_close_end,
13924 start_line: region.class_start_line,
13925 end_line: region.class_close_line,
13926 },
13927 region.name,
13928 &owner_ranges,
13929 );
13930 } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
13931 let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
13936 region;
13937 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
13938 continue;
13939 };
13940 let root = tree.root_node();
13941 let template_node = cpp_sentinel_reparsed_leading_template(root);
13942 let reparsed_ancestry = ParentIndex::new(root);
13944 let Some(reparsed_class) =
13945 cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
13946 else {
13947 continue;
13948 };
13949 let class_node = reparsed_class.declaration_node;
13950 let name = reparsed_class.name;
13951 let namespace_components =
13952 cpp_sentinel_recovered_namespace_components(current, &[], source);
13953 let owner_container = current
13954 .parent()
13955 .filter(|parent| parent.kind() == "declaration_list")
13956 .unwrap_or(current);
13957 let mut owner_ranges =
13958 cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
13959 cpp_sentinel_extend_unique_owner_ranges(
13960 &mut owner_ranges,
13961 cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
13962 );
13963 push_cpp_sentinel_recovered_class(
13964 &mut recovered_classes,
13965 cpp_declaration_range(owner_container),
13966 &namespace_components,
13967 Range {
13968 start_byte: class_start,
13969 end_byte: close_end,
13970 start_line: class_node.start_position().row + 1,
13971 end_line: class_node.end_position().row + 1,
13972 },
13973 name,
13974 &owner_ranges,
13975 );
13976 if owner_container.kind() == "declaration_list" {
13977 push_cpp_sentinel_sibling_classes(
13978 &mut recovered_classes,
13979 owner_container,
13980 current,
13981 &namespace_components,
13982 source,
13983 &ancestry,
13984 );
13985 }
13986 }
13987
13988 let mut cursor = current.walk();
13989 stack.extend(current.named_children(&mut cursor));
13990 }
13991 let shadowed = recovered_classes
13997 .iter()
13998 .map(|candidate| {
13999 recovered_classes.iter().any(|container| {
14000 container.class_range.start_byte <= candidate.class_range.start_byte
14001 && container.class_range.end_byte >= candidate.class_range.end_byte
14002 && container.class_range != candidate.class_range
14003 && container.namespace_scope_components.len()
14004 > candidate.namespace_scope_components.len()
14005 && container
14006 .namespace_scope_components
14007 .starts_with(&candidate.namespace_scope_components)
14008 })
14009 })
14010 .collect::<Vec<_>>();
14011 let mut index = 0usize;
14012 recovered_classes.retain(|_| {
14013 let keep = !shadowed[index];
14014 index += 1;
14015 keep
14016 });
14017 recovered_classes
14018}
14019
14020fn push_cpp_sentinel_sibling_classes<'tree>(
14026 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14027 declaration_list: Node<'tree>,
14028 sentinel_node: Node<'tree>,
14029 namespace_components: &[String],
14030 source: &str,
14031 ancestry: &ParentIndex<'tree>,
14032) {
14033 let owner_ranges =
14034 cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
14035 let namespace_range = cpp_declaration_range(declaration_list);
14036 let mut cursor = declaration_list.walk();
14037 for (class_node, template_node) in declaration_list
14038 .named_children(&mut cursor)
14039 .filter(|child| !same_node(*child, sentinel_node))
14040 .filter_map(cpp_sentinel_body_class_candidate)
14041 {
14042 let Some(name) = class_like_name(class_node, source, ancestry) else {
14043 continue;
14044 };
14045 if name.is_empty()
14046 || cpp_export_macro_token(&name)
14047 || cpp_complete_class_body_close(class_node).is_none()
14048 {
14049 continue;
14050 }
14051 push_cpp_sentinel_recovered_class(
14052 recovered_classes,
14053 namespace_range,
14054 namespace_components,
14055 cpp_declaration_range(template_node.unwrap_or(class_node)),
14056 name,
14057 &owner_ranges,
14058 );
14059 }
14060}
14061
14062fn push_cpp_sentinel_recovered_class(
14063 recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14064 namespace_range: Range,
14065 namespace_components: &[String],
14066 class_range: Range,
14067 name: String,
14068 owner_ranges: &[CppSentinelRecoveredOwner],
14069) {
14070 let mut scope_components = namespace_components.to_vec();
14071 scope_components.push(name);
14072 let owner_ranges = owner_ranges
14073 .iter()
14074 .filter(|owner| owner.scope_components.starts_with(&scope_components))
14075 .cloned()
14076 .collect::<Vec<_>>();
14077 if recovered_classes.iter().any(|existing| {
14078 existing.class_range == class_range && existing.scope_components == scope_components
14079 }) {
14080 return;
14081 }
14082 recovered_classes.push(CppSentinelRecoveredClass {
14083 namespace_range,
14084 namespace_scope_components: namespace_components.to_vec(),
14085 class_range,
14086 scope_components,
14087 owner_ranges,
14088 });
14089}
14090
14091fn cpp_sentinel_recovered_namespace_components(
14092 function: Node<'_>,
14093 recovered_components: &[String],
14094 source: &str,
14095) -> Vec<String> {
14096 let mut ancestor_components = Vec::new();
14097 let mut ancestor = function.parent();
14098 while let Some(current) = ancestor {
14099 if current.kind() == "namespace_definition"
14100 && let Some(name_node) = current.child_by_field_name("name")
14101 && let Some(components) = cpp_name_components(name_node, source)
14102 {
14103 ancestor_components.push(
14104 components
14105 .into_iter()
14106 .map(|component| component.name)
14107 .collect::<Vec<_>>(),
14108 );
14109 }
14110 ancestor = current.parent();
14111 }
14112 ancestor_components.reverse();
14113 let mut ancestors = ancestor_components
14114 .into_iter()
14115 .flatten()
14116 .collect::<Vec<_>>();
14117
14118 let overlap = (0..=ancestors.len().min(recovered_components.len()))
14119 .rev()
14120 .find(|length| {
14121 ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
14122 })
14123 .unwrap_or(0);
14124 ancestors.extend(recovered_components.iter().skip(overlap).cloned());
14125 ancestors
14126}
14127
14128fn cpp_sentinel_recovered_owner_ranges(
14129 body: Node<'_>,
14130 namespace_components: &[String],
14131 source: &str,
14132) -> Vec<CppSentinelRecoveredOwner> {
14133 let mut owners = Vec::new();
14134 walk_named_tree_preorder(body, true, |node| {
14135 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14136 });
14137 owners
14138}
14139
14140fn cpp_sentinel_collect_owner_range(
14141 node: Node<'_>,
14142 namespace_components: &[String],
14143 source: &str,
14144 owners: &mut Vec<CppSentinelRecoveredOwner>,
14145) -> WalkControl {
14146 if node.kind() != "function_definition" {
14147 return WalkControl::Continue;
14148 }
14149 let Some(function_declarator) = extract_function_declarator(node) else {
14150 return WalkControl::Continue;
14151 };
14152 let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
14153 return WalkControl::Continue;
14154 };
14155 let Some(mut components) = cpp_name_components(name_node, source) else {
14156 return WalkControl::Continue;
14157 };
14158 if components.len() <= 1 {
14159 return WalkControl::Continue;
14160 }
14161 components.pop();
14162 let mut owner_components = components
14163 .into_iter()
14164 .map(|component| component.name)
14165 .collect::<Vec<_>>();
14166 let overlap = (0..=namespace_components.len().min(owner_components.len()))
14167 .rev()
14168 .find(|length| {
14169 owner_components[..*length]
14170 == namespace_components[namespace_components.len().saturating_sub(*length)..]
14171 })
14172 .unwrap_or(0);
14173 let mut scope_components = namespace_components.to_vec();
14174 scope_components.extend(owner_components.drain(overlap..));
14175 if scope_components.len() <= namespace_components.len() {
14176 return WalkControl::Continue;
14177 }
14178 let range = cpp_declaration_range(node);
14179 if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
14180 existing.range == range && existing.scope_components == scope_components
14181 }) {
14182 owners.push(CppSentinelRecoveredOwner {
14183 range,
14184 owner_name_start_byte: name_node.start_byte(),
14185 namespace_component_count: namespace_components.len(),
14186 scope_components,
14187 });
14188 }
14189 WalkControl::Continue
14190}
14191
14192fn cpp_sentinel_extend_unique_owner_ranges(
14193 owners: &mut Vec<CppSentinelRecoveredOwner>,
14194 additional: Vec<CppSentinelRecoveredOwner>,
14195) {
14196 for owner in additional {
14197 if !owners.iter().any(|existing| {
14198 existing.range == owner.range && existing.scope_components == owner.scope_components
14199 }) {
14200 owners.push(owner);
14201 }
14202 }
14203}
14204
14205fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
14206 if node.kind() != "ERROR" || node.named_child_count() != 1 {
14207 return false;
14208 }
14209 let Some(end_name) = node.named_child(0) else {
14210 return false;
14211 };
14212 if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
14213 return false;
14214 }
14215 let mut cursor = node.walk();
14216 node.children(&mut cursor)
14217 .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
14218}
14219
14220fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14224 parent: Node<'_>,
14225 sentinel_node: Node<'_>,
14226 namespace_components: &[String],
14227 source: &str,
14228) -> Vec<CppSentinelRecoveredOwner> {
14229 let mut owners = Vec::new();
14230 let mut after_sentinel = false;
14231 let mut cursor = parent.walk();
14232 for child in parent.named_children(&mut cursor) {
14233 if !after_sentinel {
14234 if same_node(child, sentinel_node) {
14235 after_sentinel = true;
14236 }
14237 continue;
14238 }
14239 walk_named_tree_preorder(child, true, |node| {
14240 if node.kind() == "namespace_definition" {
14241 return WalkControl::SkipChildren;
14242 }
14243 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14244 });
14245 }
14246 owners
14247}
14248
14249fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14253 parent: Node<'_>,
14254 sentinel_node: Node<'_>,
14255 namespace_components: &[String],
14256 source: &str,
14257) -> Option<Vec<CppSentinelRecoveredOwner>> {
14258 let mut owners = Vec::new();
14259 let mut after_namespace = false;
14260 let mut cursor = parent.walk();
14261 for child in parent.named_children(&mut cursor) {
14262 if !after_namespace {
14263 if same_node(child, sentinel_node) {
14264 after_namespace = true;
14265 }
14266 continue;
14267 }
14268 if cpp_sentinel_namespace_end(child, source) {
14269 return Some(owners);
14270 }
14271 walk_named_tree_preorder(child, true, |node| {
14272 if node.kind() == "namespace_definition" {
14273 return WalkControl::SkipChildren;
14274 }
14275 cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14276 });
14277 }
14278 None
14279}
14280
14281fn cpp_sentinel_recovered_sibling_owner_ranges(
14282 sentinel_node: Node<'_>,
14283 namespace_components: &[String],
14284 source: &str,
14285) -> Vec<CppSentinelRecoveredOwner> {
14286 let Some(declaration_list) = sentinel_node
14287 .parent()
14288 .filter(|parent| parent.kind() == "declaration_list")
14289 else {
14290 return Vec::new();
14291 };
14292 let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14293 declaration_list,
14294 sentinel_node,
14295 namespace_components,
14296 source,
14297 );
14298
14299 let Some(namespace) = declaration_list
14300 .parent()
14301 .filter(|parent| parent.kind() == "namespace_definition")
14302 else {
14303 return owners;
14304 };
14305 let Some(outer_parent) = namespace.parent() else {
14306 return owners;
14307 };
14308 if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14309 outer_parent,
14310 namespace,
14311 namespace_components,
14312 source,
14313 ) {
14314 cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
14315 }
14316 owners
14317}
14318
14319fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
14320 let mut current = function_declarator.child_by_field_name("declarator")?;
14321 loop {
14322 if let Some(name) = macro_decorated_unqualified_name(current) {
14323 current = name;
14324 continue;
14325 }
14326 if matches!(
14327 current.kind(),
14328 "qualified_identifier"
14329 | "scoped_identifier"
14330 | "scoped_type_identifier"
14331 | "identifier"
14332 | "field_identifier"
14333 | "operator_name"
14334 | "destructor_name"
14335 | "literal_operator_name"
14336 ) {
14337 return Some(current);
14338 }
14339 current = current
14340 .child_by_field_name("declarator")
14341 .or_else(|| current.child_by_field_name("name"))
14342 .or_else(|| last_named_child(current))?;
14343 }
14344}
14345
14346fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
14359 if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
14360 return None;
14361 }
14362 let mut cursor = node.walk();
14363 if node
14364 .children(&mut cursor)
14365 .any(|child| child.kind() == "::" && !child.is_missing())
14366 {
14367 return None;
14368 }
14369 node.child_by_field_name("name")
14370}
14371
14372fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
14373 if let Some(name) = macro_decorated_unqualified_name(node) {
14374 return cpp_name_components(name, source);
14375 }
14376 match node.kind() {
14377 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14378 let mut components = match node.child_by_field_name("scope") {
14379 Some(scope) => cpp_name_components(scope, source)?,
14380 None => Vec::new(),
14381 };
14382 let name = node.child_by_field_name("name")?;
14383 components.push(canonical_cpp_qualified_component(name, source)?);
14384 Some(components)
14385 }
14386 _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
14387 }
14388}
14389
14390fn cpp_sentinel_fragment_boundary<'tree>(
14391 function: Node<'tree>,
14392 class_node: Node<'tree>,
14393 class_body: Node<'tree>,
14394 source: &str,
14395) -> Option<(Node<'tree>, Node<'tree>)> {
14396 let declaration_list = function.parent()?;
14397 if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
14398 return None;
14399 }
14400 let namespace = declaration_list.parent()?;
14401 if namespace.kind() != "namespace_definition"
14402 || namespace.child_by_field_name("body") != Some(declaration_list)
14403 {
14404 return None;
14405 }
14406 let mut cursor = declaration_list.walk();
14407 let closes = declaration_list
14408 .children(&mut cursor)
14409 .filter(|child| {
14410 !child.is_named()
14411 && child.kind() == "}"
14412 && child.start_byte() >= function.end_byte()
14413 && child.start_byte() > class_node.end_byte()
14414 && child.start_byte() > class_body.start_byte()
14415 })
14416 .collect::<Vec<_>>();
14417 let [close] = closes.as_slice() else {
14418 return None;
14419 };
14420 let semicolon = namespace.next_named_sibling()?;
14421 if !cpp_is_stray_semicolon(semicolon, source)
14422 || close.end_byte() != namespace.end_byte()
14423 || semicolon.start_byte() < namespace.end_byte()
14424 {
14425 return None;
14426 }
14427 Some((*close, semicolon))
14428}
14429
14430fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
14456 if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
14457 {
14458 return None;
14459 }
14460 let mut declarator_cursor = node.walk();
14466 let preserved_callable = node
14467 .children_by_field_name("declarator", &mut declarator_cursor)
14468 .find_map(extract_function_declarator);
14469 let mut cursor = node.walk();
14477 let first = node
14478 .named_children(&mut cursor)
14479 .find(|child| child.kind() != "comment")?;
14480 if first.kind() != "type_identifier" {
14481 return None;
14482 }
14483 let sentinel = normalize_cpp_whitespace(node_text(first, source));
14484 if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
14485 return None;
14486 }
14487 let mut start = first.end_byte();
14494 let mut after_first = false;
14495 let mut cursor = node.walk();
14496 for child in node.named_children(&mut cursor) {
14497 if !after_first {
14498 if same_node(child, first) {
14499 after_first = true;
14500 }
14501 continue;
14502 }
14503 if matches!(child.kind(), "identifier" | "type_identifier")
14504 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
14505 {
14506 start = child.end_byte();
14507 } else {
14508 break;
14509 }
14510 }
14511 let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
14520 let mut class_start = None;
14521 let mut template_start = None;
14522 let mut stack = vec![node];
14523 while let Some(current) = stack.pop() {
14524 if current.start_byte() >= prefix_end {
14525 continue;
14526 }
14527 if matches!(
14528 current.kind(),
14529 "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
14530 ) {
14531 match normalize_cpp_whitespace(node_text(current, source)).as_str() {
14532 "class" | "struct" | "union" | "enum" => {
14533 class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
14534 seen.min(current.start_byte())
14535 }));
14536 }
14537 "template" => {
14538 template_start =
14539 Some(template_start.map_or(current.start_byte(), |seen: usize| {
14540 seen.min(current.start_byte())
14541 }));
14542 }
14543 _ => {}
14544 }
14545 }
14546 let mut cursor = current.walk();
14547 stack.extend(current.children(&mut cursor));
14548 }
14549 if preserved_callable.is_some_and(|callable| {
14550 class_start.is_none_or(|class_start| class_start >= callable.start_byte())
14551 }) {
14552 return None;
14553 }
14554 if let Some(class_start) = class_start {
14555 start = template_start
14556 .filter(|template_start| *template_start < class_start)
14557 .unwrap_or(class_start);
14558 }
14559 Some((start, class_start))
14560}
14561
14562fn cpp_sentinel_macro_class_region<'tree>(
14568 node: Node<'tree>,
14569 source: &str,
14570) -> Option<(usize, usize, usize, usize, usize, usize)> {
14571 let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
14572 return None;
14573 };
14574 let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
14575 .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
14576 .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
14577 if class_start >= body_open_start {
14578 return None;
14579 }
14580 let sibling_close = {
14581 let mut sibling = node.next_named_sibling();
14582 let mut found = None;
14583 while let Some(current) = sibling {
14584 let next = current.next_named_sibling();
14585 if cpp_is_stray_close_brace(current, source)
14586 && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
14587 {
14588 let semicolon = next.expect("checked above");
14589 found = Some((
14590 current.start_byte(),
14591 semicolon.end_byte(),
14592 semicolon.end_position().row + 1,
14593 ));
14594 break;
14595 }
14596 sibling = next;
14597 }
14598 found
14599 };
14600 let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
14613 let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
14614 return false;
14615 };
14616 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14617 let reparsed_ancestry = ParentIndex::new(tree.root_node());
14619 let Some(reparsed_class) = cpp_sentinel_reparsed_class(
14620 tree.root_node(),
14621 template_node,
14622 source,
14623 &reparsed_ancestry,
14624 ) else {
14625 return false;
14626 };
14627 let body = reparsed_class.body;
14628 body.start_byte() == body_open_start && body.end_byte() == close_start + 1
14629 });
14630 let (class_close_start, class_close_end, class_close_line) =
14631 if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
14632 (class_close_start, class_close_end, class_close_line)
14633 } else {
14634 let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
14641 let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14642 let reparsed_ancestry = ParentIndex::new(tree.root_node());
14644 let reparsed_class = cpp_sentinel_reparsed_class(
14645 tree.root_node(),
14646 template_node,
14647 source,
14648 &reparsed_ancestry,
14649 )?;
14650 let body = reparsed_class.body;
14651 let class_close_end = body.end_byte();
14652 let class_close_start = class_close_end.checked_sub(1)?;
14653 let class_close_line = body.end_position().row + 1;
14654 (class_close_start, class_close_end, class_close_line)
14655 };
14656 if class_close_start <= class_start {
14657 return None;
14658 }
14659
14660 let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
14664 let class_root = tree.root_node();
14665 let template_node = cpp_sentinel_reparsed_leading_template(class_root);
14666 let reparsed_ancestry = ParentIndex::new(class_root);
14668 let reparsed_class =
14669 cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
14670 let body = reparsed_class.body;
14671 if body.start_byte() != body_open_start {
14675 return None;
14676 }
14677 let body_start = body.start_byte().checked_add(1)?;
14678 (body_start < class_close_start).then_some((
14679 reparse_start,
14680 class_start,
14681 body_start,
14682 class_close_start,
14683 class_close_end,
14684 class_close_line,
14685 ))
14686}
14687
14688fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
14693 let mut stack = vec![node];
14694 while let Some(current) = stack.pop() {
14695 if current.start_byte() == class_start
14696 && matches!(current.kind(), "class" | "struct" | "union" | "enum")
14697 {
14698 let mut sibling = current.next_sibling();
14699 while let Some(candidate) = sibling {
14700 if candidate.kind() == "{" {
14701 return Some(candidate.start_byte());
14702 }
14703 sibling = candidate.next_sibling();
14704 }
14705 }
14706 let mut cursor = current.walk();
14707 stack.extend(current.children(&mut cursor));
14708 }
14709 None
14710}
14711
14712fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
14723 node.next_named_sibling()
14724 .filter(|sibling| sibling.kind() == "compound_statement")
14725}
14726
14727fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
14728 let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
14729 let mut end = if class_start.is_some() {
14730 cpp_macro_prefixed_class_end(source, start)?
14731 } else {
14732 node.end_byte()
14733 };
14734 if class_start.is_none()
14735 && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
14736 {
14737 end = end.max(namespace_end);
14738 }
14739 let mut sibling = node.next_named_sibling();
14740 while let Some(current) = sibling {
14741 if !cpp_is_stray_semicolon(current, source) {
14742 break;
14743 }
14744 end = current.end_byte();
14745 sibling = current.next_named_sibling();
14746 }
14747 (start < end).then_some((start, end))
14748}
14749
14750fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
14761 let mut sibling = node.next_sibling();
14762 let keyword = loop {
14763 let candidate = sibling?;
14764 sibling = candidate.next_sibling();
14765 if candidate.kind() != "comment" {
14766 break candidate;
14767 }
14768 };
14769 if keyword.kind() != "namespace" {
14770 return None;
14771 }
14772 let name = loop {
14773 let candidate = sibling?;
14774 sibling = candidate.next_sibling();
14775 if candidate.kind() != "comment" {
14776 break candidate;
14777 }
14778 };
14779 if cpp_namespace_name_components(name, source).is_empty() {
14780 return None;
14781 }
14782 let open = loop {
14783 let candidate = sibling?;
14784 sibling = candidate.next_sibling();
14785 if candidate.kind() != "comment" {
14786 break candidate;
14787 }
14788 };
14789 if open.kind() != "{" {
14790 return None;
14791 }
14792
14793 let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
14794 let root = tree.root_node();
14795 let mut cursor = root.walk();
14796 let namespace = root
14797 .named_children(&mut cursor)
14798 .find(|candidate| candidate.kind() != "comment")?;
14799 (namespace.kind() == "namespace_definition"
14800 && namespace.start_byte() == keyword.start_byte()
14801 && namespace.child_by_field_name("body").is_some())
14802 .then_some(namespace.end_byte())
14803}
14804
14805fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
14811 let tree = cpp_reparse_region_items(source, start, source.len())?;
14812 let root = tree.root_node();
14813 let mut cursor = root.walk();
14814 for item in root.named_children(&mut cursor) {
14815 if item.end_byte() <= start || item.kind() == "comment" {
14816 continue;
14817 }
14818 let mut stack = vec![item];
14819 while let Some(current) = stack.pop() {
14820 if matches!(
14821 current.kind(),
14822 "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
14823 ) && cpp_body_node(current).is_some()
14824 {
14825 return Some(current.end_byte());
14826 }
14827 let mut cursor = current.walk();
14828 stack.extend(current.named_children(&mut cursor));
14829 }
14830 return None;
14834 }
14835 None
14836}
14837
14838fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
14841 node.kind() == "expression_statement"
14842 && node.named_child_count() == 0
14843 && node_text(node, source).trim() == ";"
14844}
14845
14846#[derive(Clone, Copy)]
14856pub(crate) struct RecoveredPyObjectHeadField<'tree> {
14857 pub(crate) type_node: Node<'tree>,
14858 pub(crate) name: Node<'tree>,
14859 pub(crate) declarator: Node<'tree>,
14860}
14861
14862impl RecoveredPyObjectHeadField<'_> {
14863 pub(crate) fn pointer_depth(self) -> i32 {
14864 let mut depth = 0;
14865 let mut current = self.declarator;
14866 while current != self.name {
14867 debug_assert_eq!(current.kind(), "pointer_declarator");
14868 depth += 1;
14869 current = current
14870 .child_by_field_name("declarator")
14871 .expect("recovered PyObject field pointer has an inner declarator");
14872 }
14873 depth
14874 }
14875}
14876
14877pub(crate) fn recovered_pyobject_head_field<'tree>(
14878 node: Node<'tree>,
14879 source: &str,
14880) -> Option<RecoveredPyObjectHeadField<'tree>> {
14881 if node.kind() != "field_declaration" {
14882 return None;
14883 }
14884 let type_node = node.child_by_field_name("type")?;
14885 if type_node.kind() != "type_identifier"
14886 || node_text(type_node, source).trim() != "PyObject_HEAD"
14887 {
14888 return None;
14889 }
14890 let pseudo_declarator = node.child_by_field_name("declarator")?;
14891 let mut cursor = node.walk();
14892 let errors = node
14893 .named_children(&mut cursor)
14894 .filter(|child| child.kind() == "ERROR")
14895 .collect::<Vec<_>>();
14896 let [error] = errors.as_slice() else {
14897 return None;
14898 };
14899 if error.named_child_count() != 1 {
14900 return None;
14901 }
14902 let error_child = error.named_child(0)?;
14903 if pseudo_declarator.kind() == "field_identifier"
14904 && error.start_byte() >= pseudo_declarator.end_byte()
14905 && error_child.kind() == "identifier"
14906 {
14907 return Some(RecoveredPyObjectHeadField {
14908 type_node: pseudo_declarator,
14909 name: error_child,
14910 declarator: error_child,
14911 });
14912 }
14913 if pseudo_declarator.kind() != "pointer_declarator"
14914 || error.end_byte() > pseudo_declarator.start_byte()
14915 || error_child.kind() != "identifier"
14916 {
14917 return None;
14918 }
14919 let mut name = pseudo_declarator;
14920 while name.kind() == "pointer_declarator" {
14921 name = name.child_by_field_name("declarator")?;
14922 }
14923 (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
14924 type_node: error_child,
14925 name,
14926 declarator: pseudo_declarator,
14927 })
14928}
14929
14930fn recovered_macro_qualified_field_declarators<'tree>(
14939 node: Node<'tree>,
14940 source: &str,
14941) -> Option<Vec<Node<'tree>>> {
14942 if node.kind() != "field_declaration" {
14943 return None;
14944 }
14945 let macro_type = node.child_by_field_name("type")?;
14946 if macro_type.kind() != "type_identifier"
14947 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
14948 {
14949 return None;
14950 }
14951 let pseudo_declarator = node.child_by_field_name("declarator")?;
14952 if pseudo_declarator.kind() != "field_identifier" {
14953 return None;
14954 }
14955 let mut cursor = node.walk();
14956 let clause = node
14957 .named_children(&mut cursor)
14958 .find(|child| child.kind() == "bitfield_clause")?;
14959 if !(0..clause.named_child_count()).any(|index| {
14960 clause
14961 .named_child(index)
14962 .is_some_and(|child| child.kind() == "ERROR")
14963 }) {
14964 return None;
14965 }
14966 let mut recovered = Vec::new();
14967 let mut stack = vec![clause];
14968 while let Some(current) = stack.pop() {
14969 if current.kind() == "assignment_expression"
14970 && let Some(left) = current.child_by_field_name("left")
14971 && extract_variable_name(left, source).is_some()
14972 {
14973 recovered.push(left);
14974 break;
14975 }
14976 let mut cursor = current.walk();
14977 stack.extend(current.named_children(&mut cursor));
14978 }
14979 if recovered.is_empty() {
14980 return None;
14981 }
14982 let mut cursor = node.walk();
14983 recovered.extend(
14984 node.children_by_field_name("declarator", &mut cursor)
14985 .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
14986 );
14987 Some(recovered)
14988}
14989
14990fn recovered_macro_qualified_constructor_call<'tree>(
14996 node: Node<'tree>,
14997 class_name: &str,
14998 source: &str,
14999) -> Option<Node<'tree>> {
15000 if node.kind() != "field_declaration" {
15001 return None;
15002 }
15003 let macro_type = node.child_by_field_name("type")?;
15004 if macro_type.kind() != "type_identifier"
15005 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15006 {
15007 return None;
15008 }
15009 let mut cursor = node.walk();
15010 let bitfield = node
15011 .named_children(&mut cursor)
15012 .find(|child| child.kind() == "bitfield_clause")?;
15013 let error = bitfield
15014 .named_child(0)
15015 .filter(|child| child.kind() == "ERROR")?;
15016 let mut stack = vec![error];
15017 while let Some(current) = stack.pop() {
15018 if current.kind() == "call_expression"
15019 && current
15020 .child_by_field_name("function")
15021 .is_some_and(|function| node_text(function, source) == class_name)
15022 && current
15023 .child_by_field_name("arguments")
15024 .is_some_and(|arguments| arguments.kind() == "argument_list")
15025 {
15026 return Some(current);
15027 }
15028 let mut cursor = current.walk();
15029 stack.extend(current.named_children(&mut cursor));
15030 }
15031 None
15032}
15033
15034fn recovered_macro_qualified_function_call<'tree>(
15042 node: Node<'tree>,
15043 source: &str,
15044) -> Option<Node<'tree>> {
15045 if node.kind() != "field_declaration" {
15046 return None;
15047 }
15048 let macro_type = node.child_by_field_name("type")?;
15049 if macro_type.kind() != "type_identifier"
15050 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15051 {
15052 return None;
15053 }
15054 let declarator = node.child_by_field_name("declarator")?;
15055 if declarator.kind() != "field_identifier" {
15056 return None;
15057 }
15058 let mut cursor = node.walk();
15059 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15060 if !named.iter().any(|child| {
15061 child.kind() == "storage_class_specifier"
15062 && normalize_cpp_whitespace(node_text(*child, source)) == "static"
15063 }) {
15064 return None;
15065 }
15066 let bitfield = named
15067 .iter()
15068 .find(|child| child.kind() == "bitfield_clause")?;
15069 let mut bitfield_cursor = bitfield.walk();
15070 let payload = bitfield
15071 .named_children(&mut bitfield_cursor)
15072 .collect::<Vec<_>>();
15073 let [displaced_error, call] = payload.as_slice() else {
15074 return None;
15075 };
15076 if displaced_error.kind() != "ERROR"
15077 || displaced_error.named_child_count() != 1
15078 || displaced_error
15079 .named_child(0)
15080 .is_none_or(|child| child.kind() != "identifier")
15081 || call.kind() != "call_expression"
15082 || call
15083 .child_by_field_name("function")
15084 .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
15085 || call
15086 .child_by_field_name("arguments")
15087 .is_none_or(|arguments| arguments.kind() != "argument_list")
15088 {
15089 return None;
15090 }
15091 Some(*call)
15092}
15093
15094fn recovered_macro_qualified_function_parameters(
15095 arguments: Node<'_>,
15096 source: &str,
15097) -> Option<(String, Vec<String>)> {
15098 if arguments.kind() != "argument_list" {
15099 return None;
15100 }
15101 let mut cursor = arguments.walk();
15102 let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
15103 if named.is_empty() {
15104 return Some(("()".to_string(), Vec::new()));
15105 }
15106 let mut types = Vec::new();
15107 let mut labels = Vec::new();
15108 let mut index = 0;
15109 while index < named.len() {
15110 let parameter_type = named[index];
15111 let parameter_name = named.get(index + 1).copied()?;
15112 if !matches!(
15113 parameter_type.kind(),
15114 "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
15115 ) || parameter_name.kind() != "ERROR"
15116 || parameter_name.named_child_count() != 1
15117 || parameter_name
15118 .named_child(0)
15119 .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
15120 {
15121 return None;
15122 }
15123 let parameter_name = parameter_name.named_child(0)?;
15124 types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
15125 labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
15126 index += 2;
15127 }
15128 Some((format!("({})", types.join(", ")), labels))
15129}
15130
15131pub fn recovered_macro_return_type_node<'tree>(
15143 node: Node<'tree>,
15144 source: &str,
15145) -> Option<Node<'tree>> {
15146 if node.kind() != "field_declaration" {
15147 return None;
15148 }
15149 let macro_type = node.child_by_field_name("type")?;
15150 if macro_type.kind() != "type_identifier"
15151 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15152 {
15153 return None;
15154 }
15155 let declarator = node.child_by_field_name("declarator")?;
15156 if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
15157 return None;
15158 }
15159 let mut has_missing_semicolon = false;
15160 let mut has_real_semicolon = false;
15161 for child in children_iter(node) {
15162 if child.kind() != ";" {
15163 continue;
15164 }
15165 if child.is_missing() {
15166 has_missing_semicolon = true;
15167 } else {
15168 has_real_semicolon = true;
15169 }
15170 }
15171 if !has_missing_semicolon || has_real_semicolon {
15172 return None;
15173 }
15174 let mut next = node.next_named_sibling();
15175 while next.is_some_and(|sibling| sibling.kind() == "comment") {
15176 next = next.and_then(|sibling| sibling.next_named_sibling());
15177 }
15178 let next = next?;
15179 if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
15180 return None;
15181 }
15182 let function_declarator = next.child_by_field_name("declarator")?;
15183 extract_function_declarator(function_declarator).map(|_| declarator)
15184}
15185
15186pub(crate) fn cpp_active_template_type_parameter<'tree>(
15193 node: Node<'tree>,
15194 name: &str,
15195 source: &str,
15196 ancestry: &ParentIndex<'tree>,
15197) -> bool {
15198 let mut ancestor = ancestry.parent(node);
15199 while let Some(current) = ancestor {
15200 if current.kind() == "template_declaration"
15201 && let Some(parameters) = current.child_by_field_name("parameters")
15202 {
15203 let mut cursor = parameters.walk();
15204 if parameters.named_children(&mut cursor).any(|parameter| {
15205 cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
15206 && cpp_template_parameter_name(parameter, source)
15207 .is_some_and(|parameter_name| parameter_name == name)
15208 }) {
15209 return true;
15210 }
15211 }
15212 ancestor = ancestry.parent(current);
15213 }
15214 false
15215}
15216
15217fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
15223 parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
15224}
15225
15226fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
15227 if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
15228 return None;
15229 }
15230 let semicolon = node.next_sibling()?;
15231 if semicolon.kind() != ";" || semicolon.is_missing() {
15232 return None;
15233 }
15234 let row = node.start_position().row;
15235 let mut start = node.start_byte();
15236 let mut sibling = node.prev_sibling();
15237 while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
15238 if previous.kind() == ";" {
15239 break;
15240 }
15241 start = previous.start_byte();
15242 sibling = previous.prev_sibling();
15243 }
15244 (start < node.start_byte()).then_some((start, semicolon.end_byte()))
15245}
15246
15247struct PrototypeMacroCandidate {
15251 run_start: usize,
15253 identifier_start: usize,
15256 inner_open_start: usize,
15260 inner_close_end: usize,
15262 outer_close_end: usize,
15264 semicolon_end: usize,
15267}
15268
15269impl PrototypeMacroCandidate {
15270 fn ranges(&self) -> [(usize, usize); 3] {
15276 [
15277 (self.run_start, self.identifier_start),
15278 (self.inner_open_start, self.inner_close_end),
15279 (self.outer_close_end, self.semicolon_end),
15280 ]
15281 }
15282}
15283
15284fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
15286 node.child(node.child_count().checked_sub(1)?)
15287 .filter(|child| child.kind() == ";" && !child.is_missing())
15288}
15289
15290fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
15295 matches!(
15296 node.kind(),
15297 "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
15298 ) && matches!(
15299 normalize_cpp_whitespace(node_text(node, source)).as_str(),
15300 "_" | "__P" | "OF" | "PROTO"
15301 )
15302}
15303
15304fn cpp_prototype_macro_qualified_parts<'tree>(
15308 node: Node<'tree>,
15309 source: &str,
15310) -> Option<(Node<'tree>, Node<'tree>)> {
15311 if node.kind() != "qualified_identifier" {
15312 return None;
15313 }
15314 let declared_name = node
15315 .child_by_field_name("scope")
15316 .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
15317 let macro_name = macro_decorated_unqualified_name(node)?;
15318 cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
15319}
15320
15321fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
15327 if arguments.kind() != "argument_list"
15328 || arguments.named_child_count() != 1
15329 || arguments.child_count() != 3
15330 || arguments
15331 .child(0)
15332 .is_none_or(|open| open.kind() != "(" || open.is_missing())
15333 || arguments
15334 .child(2)
15335 .is_none_or(|close| close.kind() != ")" || close.is_missing())
15336 {
15337 return None;
15338 }
15339 let inner = arguments.named_child(0)?;
15340 let close_index = match inner.kind() {
15341 "parenthesized_expression" => inner.child_count().checked_sub(1)?,
15342 "cast_expression" => inner.child_count().checked_sub(2)?,
15345 _ => return None,
15346 };
15347 (inner
15348 .child(0)
15349 .is_some_and(|open| open.kind() == "(" && !open.is_missing())
15350 && inner
15351 .child(close_index)
15352 .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
15353 .then_some(inner)
15354}
15355
15356fn cpp_prototype_macro_candidate_from_init_declaration(
15357 declaration: Node<'_>,
15358 source: &str,
15359) -> Option<PrototypeMacroCandidate> {
15360 let init = declaration
15361 .child_by_field_name("declarator")
15362 .filter(|declarator| declarator.kind() == "init_declarator")?;
15363 let malformed_declarator = init.child_by_field_name("declarator")?;
15364 let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
15365 cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
15366 } else {
15367 if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
15368 return None;
15369 }
15370 let declared_name_error = init
15371 .prev_named_sibling()
15372 .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
15373 let declared_name = declared_name_error
15374 .named_child(0)
15375 .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
15376 (declared_name, malformed_declarator)
15377 };
15378 let arguments = init
15379 .child_by_field_name("value")
15380 .filter(|value| value.kind() == "argument_list")?;
15381 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15382 let semicolon = cpp_direct_semicolon(declaration)?;
15383 let return_type = declaration.child_by_field_name("type")?;
15384 if return_type.end_byte() > declared_name.start_byte()
15385 || declared_name.end_byte() > macro_name.start_byte()
15386 || macro_name.end_byte() > arguments.start_byte()
15387 || arguments.end_byte() > semicolon.start_byte()
15388 {
15389 return None;
15390 }
15391 Some(PrototypeMacroCandidate {
15392 run_start: declaration.start_byte(),
15393 identifier_start: macro_name.start_byte(),
15394 inner_open_start: inner.start_byte(),
15395 inner_close_end: inner.end_byte(),
15396 outer_close_end: arguments.end_byte(),
15397 semicolon_end: semicolon.end_byte(),
15398 })
15399}
15400
15401fn cpp_prototype_macro_candidate_from_qualified_declaration(
15402 declaration: Node<'_>,
15403 source: &str,
15404) -> Option<PrototypeMacroCandidate> {
15405 let qualified = declaration
15406 .child_by_field_name("declarator")
15407 .filter(|declarator| declarator.kind() == "qualified_identifier")?;
15408 let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15409 let open_error = qualified
15410 .next_named_sibling()
15411 .filter(|next| next.kind() == "ERROR")?;
15412 let close_error = last_named_child(declaration)
15413 .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
15414 if open_error.child_count() < 3
15415 || open_error
15416 .child(0)
15417 .is_none_or(|open| open.kind() != "(" || open.is_missing())
15418 || open_error
15419 .child(1)
15420 .is_none_or(|open| open.kind() != "(" || open.is_missing())
15421 || close_error.child_count() != 2
15422 || close_error
15423 .child(0)
15424 .is_none_or(|close| close.kind() != ")" || close.is_missing())
15425 || close_error
15426 .child(1)
15427 .is_none_or(|close| close.kind() != ")" || close.is_missing())
15428 {
15429 return None;
15430 }
15431 let inner_open = open_error.child(1)?;
15432 let inner_close = close_error.child(0)?;
15433 let outer_close = close_error.child(1)?;
15434 let semicolon = cpp_direct_semicolon(declaration)?;
15435 let return_type = declaration.child_by_field_name("type")?;
15436 if return_type.end_byte() > qualified.start_byte()
15437 || macro_name.end_byte() > open_error.start_byte()
15438 || inner_open.start_byte() > inner_close.end_byte()
15439 || inner_close.end_byte() > outer_close.start_byte()
15440 || outer_close.end_byte() > semicolon.start_byte()
15441 {
15442 return None;
15443 }
15444 Some(PrototypeMacroCandidate {
15445 run_start: declaration.start_byte(),
15446 identifier_start: macro_name.start_byte(),
15447 inner_open_start: inner_open.start_byte(),
15448 inner_close_end: inner_close.end_byte(),
15449 outer_close_end: outer_close.end_byte(),
15450 semicolon_end: semicolon.end_byte(),
15451 })
15452}
15453
15454fn cpp_prototype_macro_candidate_from_pointer_expression(
15455 statement: Node<'_>,
15456 source: &str,
15457) -> Option<PrototypeMacroCandidate> {
15458 if statement.kind() != "expression_statement"
15459 || statement.named_child_count() != 1
15460 || !statement.has_error()
15461 {
15462 return None;
15463 }
15464 let expansion = statement
15465 .named_child(0)
15466 .filter(|child| child.kind() == "parameter_pack_expansion")?;
15467 let binary = expansion
15468 .child_by_field_name("pattern")
15469 .filter(|pattern| pattern.kind() == "binary_expression")?;
15470 if binary.child_count() != 3
15471 || binary
15472 .child(1)
15473 .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
15474 || expansion
15475 .child(expansion.child_count().checked_sub(1)?)
15476 .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
15477 {
15478 return None;
15479 }
15480 let return_type = binary.child_by_field_name("left")?;
15481 let call = binary
15482 .child_by_field_name("right")
15483 .filter(|right| right.kind() == "call_expression")?;
15484 let qualified = call.child_by_field_name("function")?;
15485 let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15486 let arguments = call
15487 .child_by_field_name("arguments")
15488 .filter(|arguments| arguments.kind() == "argument_list")?;
15489 let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15490 let semicolon = cpp_direct_semicolon(statement)?;
15491 if return_type.end_byte() > declared_name.start_byte()
15492 || macro_name.end_byte() > arguments.start_byte()
15493 || arguments.end_byte() > semicolon.start_byte()
15494 {
15495 return None;
15496 }
15497 Some(PrototypeMacroCandidate {
15498 run_start: statement.start_byte(),
15499 identifier_start: macro_name.start_byte(),
15500 inner_open_start: inner.start_byte(),
15501 inner_close_end: inner.end_byte(),
15502 outer_close_end: arguments.end_byte(),
15503 semicolon_end: semicolon.end_byte(),
15504 })
15505}
15506
15507fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
15512 let candidate = match node.kind() {
15513 "declaration" if node.has_error() => {
15514 cpp_prototype_macro_candidate_from_init_declaration(node, source)
15515 .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
15516 }
15517 "expression_statement" => {
15518 cpp_prototype_macro_candidate_from_pointer_expression(node, source)
15519 }
15520 _ => None,
15521 };
15522 candidate.into_iter().collect()
15523}
15524
15525fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
15526 if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
15527 return false;
15528 }
15529 if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
15530 return false;
15531 }
15532 let Some(declarator) = (if node.kind() == "function_definition" {
15533 node.child_by_field_name("declarator")
15534 .and_then(extract_function_declarator)
15535 } else {
15536 node.named_child(0)
15537 .filter(|child| child.kind() == "function_declarator")
15538 }) else {
15539 return false;
15540 };
15541 let Some(name) = cpp_function_declarator_name_node(declarator) else {
15542 return false;
15543 };
15544 declarator.start_byte() == node.start_byte()
15545 && name.kind() == "identifier"
15546 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
15547}
15548
15549fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15562 let region = cpp_reparse_region_items(source, start, end);
15563
15564 #[cfg(debug_assertions)]
15565 assert_eq!(
15566 region.as_ref().map(cpp_tree_shape),
15567 cpp_reparse_padded_class_body(source, start, end)
15568 .as_ref()
15569 .map(cpp_tree_shape),
15570 "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
15571 prefix produces"
15572 );
15573
15574 region
15575}
15576
15577#[cfg(any(debug_assertions, test))]
15581fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15582 if start >= end {
15583 return None;
15587 }
15588 let bytes = source.as_bytes();
15589 let prefix = bytes.get(..start)?;
15590 let interior = bytes.get(start..end)?;
15591 let mut padded = Vec::with_capacity(end);
15592 padded.extend(
15593 prefix
15594 .iter()
15595 .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
15596 );
15597 padded.extend_from_slice(interior);
15598 let padded = String::from_utf8(padded).ok()?;
15599 let mut parser = Parser::new();
15600 parser
15601 .set_language(&tree_sitter_cpp::LANGUAGE.into())
15602 .ok()?;
15603 parser.parse(&padded, None)
15604}
15605
15606#[cfg(any(debug_assertions, test))]
15610fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
15611 let mut shape = Vec::new();
15612 let mut cursor = tree.root_node().walk();
15613 let mut stack = vec![tree.root_node()];
15614 while let Some(node) = stack.pop() {
15615 shape.push((
15616 node.kind(),
15617 node.start_byte(),
15618 node.end_byte(),
15619 node.start_position().row,
15620 node.start_position().column,
15621 node.is_named(),
15622 node.is_missing(),
15623 ));
15624 let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
15625 stack.extend(children.into_iter().rev());
15626 }
15627 shape
15628}
15629
15630fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
15651 let mut cursor = root.walk();
15652 let mut saw_item = false;
15653 for child in root.named_children(&mut cursor) {
15654 match child.kind() {
15655 "comment" => {}
15656 "function_definition" => {
15657 if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
15658 return false;
15659 }
15660 saw_item = true;
15661 }
15662 kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
15663 _ => return false,
15664 }
15665 }
15666 saw_item
15667}
15668
15669fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
15679 if node.kind() != "ERROR" {
15680 return false;
15681 }
15682 let mut stack = Vec::new();
15683 let mut saw_function_declarator = false;
15684 let mut cursor = node.walk();
15685 for child in node.named_children(&mut cursor) {
15686 stack.push(child);
15687 }
15688 while let Some(current) = stack.pop() {
15689 match current.kind() {
15690 "ERROR" => {
15694 let mut cursor = current.walk();
15695 stack.extend(current.named_children(&mut cursor));
15696 }
15697 "function_declarator" => saw_function_declarator = true,
15698 _ => return false,
15699 }
15700 }
15701 saw_function_declarator
15702}
15703
15704fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
15705 if node.kind() != "ERROR" {
15706 return false;
15707 }
15708 let mut cursor = node.walk();
15709 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15710 let [explicit, constructor_error, destructor] = named.as_slice() else {
15711 return false;
15712 };
15713 let Some(constructor) = constructor_error.named_child(0) else {
15714 return false;
15715 };
15716 let Some(constructor_name) =
15717 extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
15718 else {
15719 return false;
15720 };
15721 let Some(destructor_name) =
15722 extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
15723 else {
15724 return false;
15725 };
15726 let Some(destroyed_type) = destructor_name.named_child(0) else {
15727 return false;
15728 };
15729 explicit.kind() == "explicit_function_specifier"
15730 && constructor_error.kind() == "ERROR"
15731 && constructor_error.named_child_count() == 1
15732 && constructor.kind() == "function_declarator"
15733 && constructor_name.kind() == "identifier"
15734 && destructor.kind() == "function_declarator"
15735 && destructor_name.kind() == "destructor_name"
15736 && destroyed_type.kind() == "identifier"
15737 && node_text(constructor_name, source) == node_text(destroyed_type, source)
15738}
15739
15740fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
15741 if node.kind() != "compound_statement" {
15742 return false;
15743 }
15744 let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
15745 return false;
15746 };
15747 if prefix.kind() == "labeled_statement"
15748 && prefix.named_child(0).is_some_and(|label| {
15749 matches!(
15750 node_text(label, source).trim(),
15751 "public" | "private" | "protected"
15752 )
15753 })
15754 {
15755 return prefix.named_children(&mut prefix.walk()).any(|child| {
15756 child.kind() == "declaration"
15757 && child.has_error()
15758 && child
15759 .named_children(&mut child.walk())
15760 .any(cpp_reparsed_member_error_is_indexable)
15761 });
15762 }
15763 prefix.kind() == "declaration"
15768 && prefix.has_error()
15769 && prefix
15770 .named_children(&mut prefix.walk())
15771 .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
15772}
15773
15774fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
15775 if !cpp_reparsed_member_error_is_indexable(node) {
15776 return false;
15777 }
15778 let Some(preproc) = node.next_named_sibling() else {
15779 return false;
15780 };
15781 preproc.kind() == "preproc_if"
15782 && preproc.has_error()
15783 && preproc
15784 .named_children(&mut preproc.walk())
15785 .any(|child| child.kind() == "expression_statement" && child.has_error())
15786 && preproc
15787 .next_named_sibling()
15788 .is_some_and(|body| body.kind() == "compound_statement")
15789}
15790
15791fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
15796 if node.kind() != "function_definition" {
15797 return None;
15798 }
15799 let body = node.child_by_field_name("body")?;
15800 if body.kind() != "compound_statement" {
15801 return None;
15802 }
15803 let open = body.child(0)?;
15804 let close = body.child(body.child_count().checked_sub(1)?)?;
15805 if open.kind() != "{"
15806 || open.is_missing()
15807 || close.kind() != "}"
15808 || close.is_missing()
15809 || close.end_byte() != body.end_byte()
15810 || body.end_byte() != node.end_byte()
15811 {
15812 return None;
15813 }
15814 Some(body)
15815}
15816
15817fn cpp_reparsed_member_function_errors_are_in_body(
15818 node: Node<'_>,
15819 body: Node<'_>,
15820 source: &str,
15821) -> bool {
15822 let mut cursor = node.walk();
15823 node.children(&mut cursor).all(|child| {
15824 same_node(child, body)
15825 || cpp_reparsed_member_attribute_error(child, source)
15826 || cpp_reparsed_member_signature_identifier_errors(child)
15827 || (!child.has_error() && !child.is_error() && !child.is_missing())
15828 })
15829}
15830
15831fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
15839 if !node.has_error() && !node.is_error() && !node.is_missing() {
15840 return false;
15841 }
15842 let mut stack = vec![node];
15843 let mut saw_error = false;
15844 while let Some(current) = stack.pop() {
15845 if current.is_missing() {
15846 return false;
15847 }
15848 if current.kind() == "ERROR" {
15849 saw_error = true;
15850 let mut cursor = current.walk();
15851 let children = current.named_children(&mut cursor).collect::<Vec<_>>();
15852 if children
15853 .iter()
15854 .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
15855 {
15856 return false;
15857 }
15858 stack.extend(children);
15859 continue;
15860 }
15861 let mut cursor = current.walk();
15862 stack.extend(current.children(&mut cursor));
15863 }
15864 saw_error
15865}
15866
15867fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
15868 node.kind() == "ERROR"
15869 && node.named_child_count() == 1
15870 && node.named_child(0).is_some_and(|attribute| {
15871 attribute.kind() == "identifier"
15872 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
15873 })
15874}
15875
15876fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
15882 let Some(body) = cpp_reparsed_member_function_body(node) else {
15883 return false;
15884 };
15885 let mut cursor = node.walk();
15886 let named = node
15887 .named_children(&mut cursor)
15888 .filter(|child| child.kind() != "comment")
15889 .collect::<Vec<_>>();
15890 let [type_node, error, attribute, body_node] = named.as_slice() else {
15891 return false;
15892 };
15893 if !same_node(*body_node, body)
15894 || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
15895 || attribute.kind() != "identifier"
15896 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
15897 || error.kind() != "ERROR"
15898 || error.named_child_count() != 1
15899 {
15900 return false;
15901 }
15902 error
15903 .named_child(0)
15904 .is_some_and(cpp_reparsed_attribute_callable_declarator)
15905}
15906
15907fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
15908 cpp_structured_type_path(node, source).is_some()
15909 && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
15910}
15911
15912fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
15913 let Some(body) = cpp_reparsed_member_function_body(node) else {
15914 return false;
15915 };
15916 let mut cursor = node.walk();
15917 let named = node
15918 .named_children(&mut cursor)
15919 .filter(|child| child.kind() != "comment")
15920 .collect::<Vec<_>>();
15921 let [friend, return_error, declarator, body_node] = named.as_slice() else {
15922 return false;
15923 };
15924 let Some(return_type) = return_error.named_child(0) else {
15925 return false;
15926 };
15927 same_node(*body_node, body)
15928 && friend.kind() == "type_identifier"
15929 && node_text(*friend, source) == "friend"
15930 && return_error.kind() == "ERROR"
15931 && return_error.named_child_count() == 1
15932 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
15933 && extract_function_declarator(*declarator)
15934 .and_then(cpp_function_declarator_name_node)
15935 .is_some()
15936}
15937
15938fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
15939 let Some(body) = cpp_reparsed_member_function_body(node) else {
15940 return false;
15941 };
15942 let mut cursor = node.walk();
15943 let named = node
15944 .named_children(&mut cursor)
15945 .filter(|child| child.kind() != "comment")
15946 .collect::<Vec<_>>();
15947 let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
15948 return false;
15949 };
15950 let Some(return_type) = return_error.named_child(0) else {
15951 return false;
15952 };
15953 same_node(*body_node, body)
15954 && prefix
15955 .iter()
15956 .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
15957 && attribute.kind() == "type_identifier"
15958 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
15959 && return_error.kind() == "ERROR"
15960 && return_error.named_child_count() == 1
15961 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
15962 && extract_function_declarator(*declarator)
15963 .and_then(cpp_function_declarator_name_node)
15964 .is_some()
15965}
15966
15967fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
15973 let Some(body) = cpp_reparsed_member_function_body(node) else {
15974 return false;
15975 };
15976 let mut cursor = node.walk();
15977 let named = node
15978 .named_children(&mut cursor)
15979 .filter(|child| child.kind() != "comment")
15980 .collect::<Vec<_>>();
15981 let [template_type, return_error, declarator, body_node] = named.as_slice() else {
15982 return false;
15983 };
15984 let Some(template_name) = template_type.child_by_field_name("name") else {
15985 return false;
15986 };
15987 let Some(arguments) = template_type.child_by_field_name("arguments") else {
15988 return false;
15989 };
15990 let Some(return_type) = return_error.named_child(0) else {
15991 return false;
15992 };
15993 let mut cursor = template_type.walk();
15994 let template_errors = template_type
15995 .named_children(&mut cursor)
15996 .filter(|child| child.kind() == "ERROR")
15997 .collect::<Vec<_>>();
15998 let [comment_error] = template_errors.as_slice() else {
15999 return false;
16000 };
16001 let mut cursor = comment_error.walk();
16002 let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
16003 let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
16004 return false;
16005 };
16006 same_node(*body_node, body)
16007 && template_type.kind() == "template_type"
16008 && template_name.kind() == "type_identifier"
16009 && matches!(
16010 node_text(template_name, source).trim(),
16011 "public" | "private" | "protected"
16012 )
16013 && arguments.kind() == "template_argument_list"
16014 && arguments.named_child_count() > 0
16015 && !arguments.has_error()
16016 && !colon.is_named()
16017 && colon.kind() == ":"
16018 && comments.iter().all(|child| child.kind() == "comment")
16019 && !template_keyword.is_named()
16020 && template_keyword.kind() == "template"
16021 && return_error.kind() == "ERROR"
16022 && return_error.named_child_count() == 1
16023 && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16024 && extract_function_declarator(*declarator)
16025 .and_then(cpp_function_declarator_name_node)
16026 .is_some()
16027}
16028
16029fn cpp_reparsed_preprocessor_constructor<'tree>(
16035 node: Node<'tree>,
16036 class_name: &str,
16037 source: &str,
16038) -> Option<Node<'tree>> {
16039 if node.kind() != "labeled_statement" {
16040 return None;
16041 }
16042 let mut cursor = node.walk();
16043 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16044 let [label, directive_error, declaration] = named.as_slice() else {
16045 return None;
16046 };
16047 if label.kind() != "statement_identifier"
16048 || !matches!(
16049 node_text(*label, source),
16050 "public" | "private" | "protected"
16051 )
16052 || directive_error.kind() != "ERROR"
16053 || directive_error.child_count() != 1
16054 || directive_error
16055 .child(0)
16056 .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
16057 || declaration.kind() != "declaration"
16058 || declaration.named_child_count() != 2
16059 {
16060 return None;
16061 }
16062 let apparent_type = declaration.child_by_field_name("type")?;
16063 if apparent_type.kind() != "type_identifier"
16064 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
16065 {
16066 return None;
16067 }
16068 let declarator = declaration.child_by_field_name("declarator")?;
16069 let function = extract_function_declarator(declarator)?;
16070 let name = cpp_function_declarator_name_node(function)?;
16071 (node_text(name, source) == class_name).then_some(*declaration)
16072}
16073
16074fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
16075 if extract_function_declarator(node)
16076 .and_then(cpp_function_declarator_name_node)
16077 .is_some()
16078 {
16079 return true;
16080 }
16081 node.kind() == "init_declarator"
16082 && node
16083 .child_by_field_name("declarator")
16084 .is_some_and(|declarator| declarator.kind() == "identifier")
16085 && node
16086 .child_by_field_name("value")
16087 .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
16088}
16089
16090fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
16095 if node.kind() != "ERROR" || node.named_child_count() != 3 {
16096 return false;
16097 }
16098 let mut cursor = node.walk();
16099 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16100 let [type_node, function_declarator, attribute] = named.as_slice() else {
16101 return false;
16102 };
16103 if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16104 || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
16105 || attribute.kind() != "identifier"
16106 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16107 {
16108 return false;
16109 }
16110 let Some(preproc) =
16111 cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16112 else {
16113 return false;
16114 };
16115 let Some(body) = cpp_next_non_comment_named_sibling(preproc)
16116 .filter(|sibling| sibling.kind() == "compound_statement")
16117 else {
16118 return false;
16119 };
16120 let Some(open) = body.child(0) else {
16121 return false;
16122 };
16123 let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
16124 return false;
16125 };
16126 let Some(condition) = preproc.child_by_field_name("condition") else {
16127 return false;
16128 };
16129 let mut cursor = preproc.walk();
16130 let payload = preproc
16131 .named_children(&mut cursor)
16132 .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
16133 .collect::<Vec<_>>();
16134 let [requires_statement] = payload.as_slice() else {
16135 return false;
16136 };
16137 let requires_clause = requires_statement.named_child(0);
16138
16139 open.kind() == "{"
16140 && !open.is_missing()
16141 && close.kind() == "}"
16142 && !close.is_missing()
16143 && close.end_byte() == body.end_byte()
16144 && requires_statement.kind() == "expression_statement"
16145 && requires_statement.named_child_count() == 1
16146 && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
16147}
16148
16149fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16150 let mut sibling = node.next_named_sibling();
16151 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16152 sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
16153 }
16154 sibling
16155}
16156
16157fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16158 let mut sibling = node.prev_named_sibling();
16159 while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16160 sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
16161 }
16162 sibling
16163}
16164
16165fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
16166 let Some(preproc) =
16167 cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16168 else {
16169 return false;
16170 };
16171 let Some(error) =
16172 cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
16173 else {
16174 return false;
16175 };
16176 cpp_reparsed_attribute_requires_error(error, source)
16177}
16178
16179fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
16180 node: Node<'tree>,
16181 source: &str,
16182) -> Option<Node<'tree>> {
16183 if node.kind() != "ERROR" {
16184 return None;
16185 }
16186 let mut cursor = node.walk();
16187 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16188 let [parameter, macro_name, message] = named.as_slice() else {
16189 return None;
16190 };
16191 let parameter_name = parameter.named_child(0)?;
16192 (parameter.kind() == "type_parameter_declaration"
16193 && parameter_name.kind() == "type_identifier"
16194 && macro_name.kind() == "type_identifier"
16195 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16196 && message.kind() == "string_literal")
16197 .then_some(parameter_name)
16198}
16199
16200fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
16205 node: Node<'tree>,
16206 source: &str,
16207) -> Option<Node<'tree>> {
16208 if node.kind() != "ERROR" {
16209 return None;
16210 }
16211 let mut cursor = node.walk();
16212 let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16213 let [parameter, macro_name, message, constraint] = named.as_slice() else {
16214 return None;
16215 };
16216 let parameter_name = parameter.named_child(0)?;
16217 let constraint_scope = constraint.child_by_field_name("scope")?;
16218 let constraint_template = constraint.child_by_field_name("name")?;
16219 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16220 let mut argument_cursor = constraint_arguments.walk();
16221 let constraint_types = constraint_arguments
16222 .named_children(&mut argument_cursor)
16223 .collect::<Vec<_>>();
16224 if parameter.kind() != "type_parameter_declaration"
16225 || parameter_name.kind() != "type_identifier"
16226 || macro_name.kind() != "type_identifier"
16227 || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16228 || message.kind() != "string_literal"
16229 || constraint.kind() != "qualified_identifier"
16230 || constraint_scope.kind() != "namespace_identifier"
16231 || !matches!(
16232 constraint_template.kind(),
16233 "template_function" | "template_type"
16234 )
16235 || !matches!(constraint_types.as_slice(), [left, right]
16236 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16237 || constraint_arguments.has_error()
16238 {
16239 return None;
16240 }
16241 let parameter_text = node_text(parameter_name, source);
16242 let mut stack = constraint_types;
16243 while let Some(current) = stack.pop() {
16244 if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
16245 return Some(parameter_name);
16246 }
16247 let mut cursor = current.walk();
16248 stack.extend(current.named_children(&mut cursor));
16249 }
16250 None
16251}
16252
16253fn cpp_reparsed_template_macro_companion_is_indexable(
16254 node: Node<'_>,
16255 parameter_name: Node<'_>,
16256 source: &str,
16257) -> bool {
16258 let Some(body) = cpp_reparsed_member_function_body(node) else {
16259 return false;
16260 };
16261 let mut cursor = node.walk();
16262 let named = node
16263 .named_children(&mut cursor)
16264 .filter(|child| child.kind() != "comment")
16265 .collect::<Vec<_>>();
16266 let [
16267 constraint,
16268 close_error,
16269 storage,
16270 return_error,
16271 declarator,
16272 body_node,
16273 ] = named.as_slice()
16274 else {
16275 return false;
16276 };
16277 let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
16278 return false;
16279 };
16280 let Some(constraint_template) = constraint.child_by_field_name("name") else {
16281 return false;
16282 };
16283 let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
16284 return false;
16285 };
16286 let Some(return_type) = return_error.named_child(0) else {
16287 return false;
16288 };
16289 let mut cursor = constraint_arguments.walk();
16290 let constraint_types = constraint_arguments
16291 .named_children(&mut cursor)
16292 .collect::<Vec<_>>();
16293 same_node(*body_node, body)
16294 && constraint.kind() == "qualified_identifier"
16295 && constraint_scope.kind() == "namespace_identifier"
16296 && constraint_template.kind() == "template_type"
16297 && matches!(constraint_types.as_slice(), [left, right]
16298 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16299 && !constraint_arguments.has_error()
16300 && close_error.kind() == "ERROR"
16301 && close_error.named_child_count() == 0
16302 && storage.kind() == "storage_class_specifier"
16303 && return_error.kind() == "ERROR"
16304 && return_error.named_child_count() == 1
16305 && return_type.kind() == "identifier"
16306 && node_text(return_type, source) == node_text(parameter_name, source)
16307 && extract_function_declarator(*declarator)
16308 .and_then(cpp_function_declarator_name_node)
16309 .is_some()
16310}
16311
16312fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
16313 node: Node<'tree>,
16314 parameter_name: Node<'_>,
16315 source: &str,
16316) -> Option<Node<'tree>> {
16317 let body = cpp_reparsed_member_function_body(node)?;
16318 let constraint = node.child_by_field_name("type")?;
16319 let constraint_template = constraint.child_by_field_name("name")?;
16320 let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16321 let mut argument_cursor = constraint_arguments.walk();
16322 let constraint_types = constraint_arguments
16323 .named_children(&mut argument_cursor)
16324 .collect::<Vec<_>>();
16325 if constraint.kind() != "qualified_identifier"
16326 || constraint_template.kind() != "template_type"
16327 || !matches!(constraint_types.as_slice(), [left, right]
16328 if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16329 || constraint_arguments.has_error()
16330 || node
16331 .child_by_field_name("body")
16332 .is_none_or(|candidate| !same_node(candidate, body))
16333 {
16334 return None;
16335 }
16336
16337 let mut cursor = node.walk();
16338 let recovery_errors = node
16339 .named_children(&mut cursor)
16340 .filter(|child| child.kind() == "ERROR")
16341 .collect::<Vec<_>>();
16342 if !recovery_errors
16343 .iter()
16344 .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
16345 || !recovery_errors.iter().all(|error| {
16346 error.named_child_count() == 0
16347 || cpp_reparsed_constraint_macro_error(*error, source)
16348 || (error.named_child_count() == 1
16349 && error
16350 .named_child(0)
16351 .is_some_and(|child| child.kind() == "function_declarator"))
16352 })
16353 {
16354 return None;
16355 }
16356
16357 let parameter_text = node_text(parameter_name, source);
16358 let mut declarators = node
16359 .child_by_field_name("declarator")
16360 .and_then(extract_function_declarator)
16361 .into_iter()
16362 .collect::<Vec<_>>();
16363 for error in recovery_errors {
16364 let mut stack = vec![error];
16365 while let Some(current) = stack.pop() {
16366 if current.kind() == "function_declarator" {
16367 declarators.push(current);
16368 }
16369 let mut cursor = current.walk();
16370 stack.extend(current.named_children(&mut cursor));
16371 }
16372 }
16373 declarators.into_iter().find(|declarator| {
16374 cpp_function_declarator_name_node(*declarator)
16375 .is_some_and(|name| name.kind() == "identifier")
16376 && declarator
16377 .child_by_field_name("parameters")
16378 .is_some_and(|parameters| {
16379 parameters
16380 .named_children(&mut parameters.walk())
16381 .filter_map(|parameter| parameter.child_by_field_name("type"))
16382 .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
16383 })
16384 })
16385}
16386
16387fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
16388 node: Node<'_>,
16389 parameter_name: Node<'_>,
16390 source: &str,
16391) -> bool {
16392 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
16393}
16394
16395fn cpp_reparsed_template_macro_function_companion_is_indexable(
16396 node: Node<'_>,
16397 parameter_name: Node<'_>,
16398 source: &str,
16399) -> bool {
16400 if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
16401 return false;
16402 }
16403 let Some(return_type) = node.child_by_field_name("type") else {
16404 return false;
16405 };
16406 let Some(function_declarator) = node
16407 .child_by_field_name("declarator")
16408 .and_then(extract_function_declarator)
16409 else {
16410 return false;
16411 };
16412 if cpp_function_declarator_name_node(function_declarator).is_none()
16413 || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
16414 {
16415 return false;
16416 }
16417 let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
16418 return false;
16419 };
16420 let parameter_text = node_text(parameter_name, source);
16421 parameters
16422 .named_children(&mut parameters.walk())
16423 .any(|parameter| {
16424 parameter
16425 .child_by_field_name("type")
16426 .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
16427 })
16428}
16429
16430fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
16431 if node.kind() != "ERROR" {
16432 return false;
16433 }
16434 let mut stack = vec![node];
16435 while let Some(current) = stack.pop() {
16436 let macro_shape = match current.kind() {
16437 "call_expression" => current
16438 .child_by_field_name("function")
16439 .zip(current.child_by_field_name("arguments")),
16440 "init_declarator" => current
16441 .child_by_field_name("declarator")
16442 .zip(current.child_by_field_name("value")),
16443 _ => None,
16444 };
16445 if let Some((name, arguments)) = macro_shape
16446 && name.kind() == "identifier"
16447 && arguments.kind() == "argument_list"
16448 && arguments.named_child_count() >= 2
16449 && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
16450 {
16451 return true;
16452 }
16453 let mut cursor = current.walk();
16454 stack.extend(current.named_children(&mut cursor));
16455 }
16456 false
16457}
16458
16459fn cpp_recovered_template_macro_constructor<'tree>(
16460 node: Node<'tree>,
16461 source: &str,
16462) -> Option<(Node<'tree>, Node<'tree>)> {
16463 let mut prefix = node.prev_named_sibling()?;
16464 while prefix.kind() == "comment" {
16465 prefix = prefix.prev_named_sibling()?;
16466 }
16467 let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
16468 let parameter = parameter_name
16469 .parent()
16470 .filter(|parent| parent.kind() == "type_parameter_declaration")?;
16471 let declarator =
16472 cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
16473 Some((declarator, parameter))
16474}
16475
16476fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
16477 if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
16478 return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16479 cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
16480 || cpp_reparsed_template_macro_constructor_companion_is_indexable(
16481 function,
16482 parameter_name,
16483 source,
16484 )
16485 });
16486 }
16487 let Some(parameter_name) =
16488 cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
16489 else {
16490 return false;
16491 };
16492 cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16493 cpp_reparsed_template_macro_function_companion_is_indexable(
16494 function,
16495 parameter_name,
16496 source,
16497 )
16498 })
16499}
16500
16501fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16502 let function_name = node
16503 .child_by_field_name("declarator")
16504 .and_then(extract_function_declarator)
16505 .and_then(cpp_function_declarator_name_node);
16506 if let Some(body) = cpp_reparsed_member_function_body(node)
16507 && function_name.is_some()
16508 && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
16509 {
16510 return true;
16511 }
16512 cpp_reparsed_attribute_member_function(node, source)
16513 || cpp_reparsed_friend_function_is_indexable(node, source)
16514 || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
16515 || cpp_reparsed_access_template_function_is_indexable(node, source)
16516 || cpp_recovered_template_macro_constructor(node, source).is_some()
16517}
16518
16519fn cpp_reparsed_macro_attribute_member_sequence(
16526 children: &[Node<'_>],
16527 index: usize,
16528 source: &str,
16529) -> bool {
16530 let Some(prefix) = children.get(index).copied() else {
16531 return false;
16532 };
16533 let declaration = if prefix.kind() == "labeled_statement" {
16534 prefix
16535 .named_child(prefix.named_child_count().saturating_sub(1))
16536 .filter(|child| child.kind() == "declaration")
16537 } else {
16538 (prefix.kind() == "declaration").then_some(prefix)
16539 };
16540 let Some(declaration) = declaration else {
16541 return false;
16542 };
16543 if !declaration.has_error()
16544 || declaration
16545 .child_by_field_name("declarator")
16546 .and_then(extract_function_declarator)
16547 .and_then(cpp_function_declarator_name_node)
16548 .is_none()
16549 {
16550 return false;
16551 }
16552 let Some(attribute_statement) = children.get(index + 1).copied() else {
16553 return false;
16554 };
16555 let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
16556 .then(|| attribute_statement.named_child(0))
16557 .flatten()
16558 .filter(|child| child.kind() == "call_expression")
16559 else {
16560 return false;
16561 };
16562 let Some(attribute_name) = attribute_call
16563 .child_by_field_name("function")
16564 .filter(|function| function.kind() == "identifier")
16565 .map(|function| normalize_cpp_whitespace(node_text(function, source)))
16566 else {
16567 return false;
16568 };
16569 if !cpp_export_macro_token(&attribute_name) {
16570 return false;
16571 }
16572 let Some(body) = children.get(index + 2).copied() else {
16573 return false;
16574 };
16575 body.kind() == "compound_statement"
16576 && body.child(0).is_some_and(|open| open.kind() == "{")
16577 && body
16578 .child(body.child_count().saturating_sub(1))
16579 .is_some_and(|close| close.kind() == "}" && !close.is_missing())
16580 && declaration.end_byte() <= attribute_statement.start_byte()
16581 && attribute_statement.end_byte() <= body.start_byte()
16582}
16583
16584fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
16592 if node.kind() != "ERROR" {
16593 return false;
16594 }
16595 let run = stranded_declaration_run(node, source);
16596 run.complete && !run.declarations.is_empty()
16597}
16598
16599fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
16600 let mut cursor = root.walk();
16601 let children = root.named_children(&mut cursor).collect::<Vec<_>>();
16602 let mut saw_member = false;
16603 let mut index = 0;
16604 while index < children.len() {
16605 let child = children[index];
16606 if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
16607 saw_member = true;
16608 index += 3;
16609 continue;
16610 }
16611 if let Some(recovered) = fragmented_class_body(child, source) {
16612 let Some(tree) = cpp_reparse_fragmented_class_body(
16613 source,
16614 recovered.body.reparse_start,
16615 recovered.body.reparse_end,
16616 ) else {
16617 return false;
16618 };
16619 if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
16620 return false;
16621 }
16622 saw_member = true;
16623 index += 1;
16624 while index < children.len()
16625 && children[index].end_byte() <= recovered.body.class_range.end_byte
16626 {
16627 index += 1;
16628 }
16629 continue;
16630 }
16631 match child.kind() {
16632 "comment" => {}
16633 "labeled_statement" => saw_member = true,
16634 "function_definition" => {
16635 if child.has_error()
16636 && !cpp_reparsed_member_function_is_indexable(child, source)
16637 && cpp_sentinel_macro_region(child, source).is_none()
16638 {
16639 return false;
16640 }
16641 saw_member = true;
16642 }
16643 "expression_statement" if is_string_attribute_macro_statement(child) => {}
16647 "ERROR"
16648 if (cpp_reparsed_member_error_is_indexable(child)
16649 || cpp_reparsed_adjacent_copy_control_error(child, source)
16650 || cpp_reparsed_stranded_member_error(child, source))
16651 && (child
16652 .next_named_sibling()
16653 .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
16654 || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
16655 {
16656 saw_member = true;
16657 }
16658 "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
16659 saw_member = true;
16660 }
16661 "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
16662 saw_member = true;
16663 }
16664 "expression_statement"
16665 if cpp_is_stray_semicolon(child, source)
16666 && child.prev_named_sibling().is_some_and(|error| {
16667 cpp_reparsed_member_error_is_indexable(error)
16668 || cpp_reparsed_adjacent_copy_control_error(error, source)
16669 || cpp_reparsed_stranded_member_error(error, source)
16670 }) =>
16671 {
16672 saw_member = true;
16673 }
16674 "compound_statement"
16675 if cpp_reparsed_constructor_body_is_indexable(child, source)
16676 || cpp_reparsed_attribute_requires_body(child, source) =>
16677 {
16678 saw_member = true;
16679 }
16680 kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
16681 _ => return false,
16682 }
16683 index += 1;
16684 }
16685 saw_member
16686}
16687
16688fn cpp_reparsed_synthetic_initializer_constructor_range(
16696 root: Node<'_>,
16697 class_name: &str,
16698 source: &str,
16699 constructor_end: usize,
16700) -> Option<std::ops::Range<usize>> {
16701 let mut stack = {
16702 let mut cursor = root.walk();
16703 root.named_children(&mut cursor).collect::<Vec<_>>()
16704 };
16705 while let Some(current) = stack.pop() {
16706 if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
16707 current,
16708 class_name,
16709 source,
16710 constructor_end,
16711 ) {
16712 return Some(range);
16713 }
16714 if current.kind() == "ERROR" {
16715 let mut cursor = current.walk();
16716 stack.extend(current.named_children(&mut cursor));
16717 }
16718 }
16719 None
16720}
16721
16722fn cpp_reparsed_merged_inline_constructor<'tree>(
16729 root: Node<'tree>,
16730 class_name: &str,
16731 source: &str,
16732) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
16733 let mut stack = vec![root];
16734 while let Some(current) = stack.pop() {
16735 if current.kind() != "labeled_statement" {
16736 let mut cursor = current.walk();
16737 stack.extend(current.named_children(&mut cursor));
16738 continue;
16739 }
16740 let declaration = current
16741 .named_children(&mut current.walk())
16742 .find(|child| child.kind() == "declaration")?;
16743 if declaration
16744 .child_by_field_name("type")
16745 .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
16746 {
16747 continue;
16748 }
16749 let following = declaration
16750 .child_by_field_name("declarator")
16751 .and_then(extract_function_declarator)
16752 .and_then(cpp_function_declarator_name_node);
16753 if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
16754 continue;
16755 }
16756 let mut declaration_cursor = declaration.walk();
16757 let Some(error) = declaration
16758 .named_children(&mut declaration_cursor)
16759 .find(|child| child.kind() == "ERROR")
16760 else {
16761 continue;
16762 };
16763 let mut error_cursor = error.walk();
16764 let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
16765 let Some(constructor) = error_children.iter().copied().find(|child| {
16766 child.kind() == "function_declarator"
16767 && cpp_function_declarator_name_node(*child)
16768 .is_some_and(|name| node_text(name, source).trim() == class_name)
16769 }) else {
16770 continue;
16771 };
16772 let Some(body) = error_children.iter().copied().find_map(|child| {
16773 (child.kind() == "init_declarator")
16774 .then(|| child.child_by_field_name("value"))
16775 .flatten()
16776 .filter(|value| value.kind() == "initializer_list")
16777 }) else {
16778 continue;
16779 };
16780 if constructor.end_byte() > body.start_byte() {
16781 continue;
16782 }
16783 return Some((constructor.start_byte()..body.end_byte(), body));
16784 }
16785 None
16786}
16787
16788fn cpp_reparsed_synthetic_initializer_constructor(
16789 node: Node<'_>,
16790 class_name: &str,
16791 source: &str,
16792 constructor_end: usize,
16793) -> Option<std::ops::Range<usize>> {
16794 if node.kind() != "labeled_statement" {
16795 return None;
16796 }
16797 let mut cursor = node.walk();
16798 let named = node
16799 .named_children(&mut cursor)
16800 .filter(|child| child.kind() != "comment")
16801 .collect::<Vec<_>>();
16802 let label = named.first()?;
16803 if label.kind() != "statement_identifier"
16804 || !matches!(
16805 node_text(*label, source).trim(),
16806 "public" | "private" | "protected"
16807 )
16808 {
16809 return None;
16810 }
16811 let call_error_index = named.iter().position(|child| {
16812 if child.kind() != "ERROR" {
16813 return false;
16814 }
16815 let mut stack = vec![*child];
16816 while let Some(current) = stack.pop() {
16817 if current.kind() == "call_expression"
16818 && current
16819 .child_by_field_name("function")
16820 .is_some_and(|function| {
16821 function.kind() == "identifier"
16822 && node_text(function, source).trim() == class_name
16823 })
16824 {
16825 return true;
16826 }
16827 let mut cursor = current.walk();
16828 stack.extend(current.named_children(&mut cursor));
16829 }
16830 false
16831 })?;
16832 let constructor_call = {
16833 let mut stack = vec![named[call_error_index]];
16834 let mut found = None;
16835 while let Some(current) = stack.pop() {
16836 if current.kind() == "call_expression"
16837 && current
16838 .child_by_field_name("function")
16839 .is_some_and(|function| {
16840 function.kind() == "identifier"
16841 && node_text(function, source).trim() == class_name
16842 })
16843 {
16844 found = Some(current);
16845 break;
16846 }
16847 let mut cursor = current.walk();
16848 stack.extend(current.named_children(&mut cursor));
16849 }
16850 found
16851 };
16852 let constructor_call = constructor_call?;
16853 named.iter().skip(call_error_index + 1).find(|child| {
16854 child.kind() == "declaration" && child.has_error() && {
16855 let mut cursor = child.walk();
16856 child.named_children(&mut cursor).any(|declarator| {
16857 declarator.kind() == "init_declarator"
16858 && declarator
16859 .child_by_field_name("declarator")
16860 .is_some_and(|declarator| declarator.kind() == "function_declarator")
16861 && declarator
16862 .child_by_field_name("value")
16863 .is_some_and(|value| value.kind() == "initializer_list")
16864 })
16865 }
16866 })?;
16867 Some(constructor_call.start_byte()..constructor_end)
16868}
16869
16870fn cpp_reparsed_exact_constructor_declarator<'tree>(
16871 root: Node<'tree>,
16872 start: usize,
16873 class_name: &str,
16874 source: &str,
16875) -> Option<Node<'tree>> {
16876 let mut candidate = None;
16877 let mut stack = vec![root];
16878 while let Some(current) = stack.pop() {
16879 if current.kind() == "function_declarator"
16880 && current.start_byte() == start
16881 && cpp_function_declarator_name_node(current)
16882 .is_some_and(|name| node_text(name, source).trim() == class_name)
16883 {
16884 if candidate.is_some() {
16885 return None;
16886 }
16887 candidate = Some(current);
16888 continue;
16889 }
16890 let mut cursor = current.walk();
16891 stack.extend(current.named_children(&mut cursor));
16892 }
16893 candidate
16894}
16895
16896fn cpp_is_indexable_item_kind(kind: &str) -> bool {
16897 matches!(
16898 kind,
16899 "namespace_definition"
16900 | "class_specifier"
16901 | "struct_specifier"
16902 | "union_specifier"
16903 | "enum_specifier"
16904 | "function_definition"
16905 | "template_declaration"
16906 | "declaration"
16907 | "field_declaration"
16908 | "alias_declaration"
16909 | "static_assert_declaration"
16910 | "type_definition"
16911 | "using_declaration"
16912 | "linkage_specification"
16913 | "preproc_def"
16914 | "preproc_function_def"
16915 | "preproc_include"
16916 | "preproc_if"
16917 | "preproc_ifdef"
16918 | "preproc_call"
16919 )
16920}
16921
16922#[cfg(test)]
16923mod tests {
16924 use super::*;
16925 use crate::adapter::parse_cpp_file;
16926 use brokk_bifrost_core::analyzer::parsed_file::{
16927 finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
16928 start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
16929 };
16930 use std::fmt::Write;
16931
16932 fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
16933 let mut parser = tree_sitter::Parser::new();
16934 parser
16935 .set_language(&tree_sitter_cpp::LANGUAGE.into())
16936 .unwrap();
16937 let tree = parser.parse(source, None).unwrap();
16938 let file = ProjectFile::new(std::env::temp_dir(), name);
16939 parse_cpp_file(&file, source, &tree)
16940 }
16941
16942 #[test]
16943 fn pyobject_head_field_recovery_publishes_only_the_real_member() {
16944 let source = "struct Image { PyObject_HEAD Imaging image; };";
16945 let parsed = parse_cpp_declarations(source, "image.h");
16946 let names = parsed
16947 .declarations()
16948 .iter()
16949 .map(|unit| unit.fq_name())
16950 .collect::<Vec<_>>();
16951
16952 assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
16953 assert!(
16954 names.iter().all(|name| name != "Image.Imaging"),
16955 "the pseudo-declarator must not become a field: {names:#?}"
16956 );
16957
16958 let pointer = parse_cpp_declarations(
16959 "struct Image { PyObject_HEAD Imaging *image; };",
16960 "image-pointer.h",
16961 );
16962 let pointer_names = pointer
16963 .declarations()
16964 .iter()
16965 .map(|unit| unit.fq_name())
16966 .collect::<Vec<_>>();
16967 assert!(
16968 pointer_names.iter().any(|name| name == "Image.image"),
16969 "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
16970 );
16971 assert!(
16972 pointer_names.iter().all(|name| name != "Image.Imaging"),
16973 "the pointer recovery error must not become a field: {pointer_names:#?}"
16974 );
16975
16976 let unrelated_macro = parse_cpp_declarations(
16977 "struct Image { OTHER_HEAD Imaging other; };",
16978 "image-near-miss.h",
16979 );
16980 let unrelated_names = unrelated_macro
16981 .declarations()
16982 .iter()
16983 .map(|unit| unit.fq_name())
16984 .collect::<Vec<_>>();
16985 assert!(
16986 unrelated_names.iter().all(|name| name != "Image.other"),
16987 "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
16988 );
16989 }
16990
16991 #[test]
16992 fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
16993 let source = r#"namespace testing {
16994namespace internal {
16995 namespace detail {
16996 class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
16997 public:
16998 int value() const { return count_ + 1; }
16999 private:
17000 int count_;
17001 };
17002 class GTEST_API_ [[nodiscard]] OtherReporter {
17003 public:
17004 int value() const { return count_ + 2; }
17005 private:
17006 int count_;
17007 };
17008 }
17009
17010 template <typename T>
17011 void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
17012
17013 class TailReporter {};
17014}
17015}
17016"#;
17017 let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
17018 let declarations = parsed.declarations();
17019 let mut parser = tree_sitter::Parser::new();
17020 parser
17021 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17022 .unwrap();
17023 let tree = parser.parse(source, None).unwrap();
17024 let tail_start = source.find("TailReporter").expect("tail class");
17025 let tail_node = tree
17026 .root_node()
17027 .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
17028 .expect("tail class AST node");
17029 let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
17030 assert!(
17031 tree.root_node().has_error(),
17032 "the malformed class must exercise recovery"
17033 );
17034 assert!(index.region_at(tail_start).is_some());
17035 assert_eq!(
17036 index.enclosing_namespace_components(tail_node, source),
17037 ["testing", "internal"]
17038 );
17039 let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
17040 let mut recovered_parsed = ParsedFile::new(String::new());
17041 let class_unit = CodeUnit::new_fq(
17042 file.clone(),
17043 CodeUnitType::Class,
17044 "testing",
17045 "ScopedFakeTestPartResultReporter",
17046 cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
17047 );
17048 let scope = ScopeInfo {
17049 package_name: "testing".to_string(),
17050 module: None,
17051 class_unit: Some(class_unit),
17052 template_signature: Some("<typename T>".to_string()),
17053 template_metadata: Some(CppTemplateMetadata {
17054 primary_name: "ScopedFakeTestPartResultReporter".to_string(),
17055 primary_fq_name: String::new(),
17056 parameters: Vec::new(),
17057 specialization_arguments: Vec::new(),
17058 alias_target: None,
17059 }),
17060 declarations_are_fields: true,
17061 recovered_specialization_member_scope: true,
17062 visible_using_namespaces: Vec::new(),
17063 };
17064 let mut visitor = CppVisitor {
17065 file: &file,
17066 source,
17067 parsed: &mut recovered_parsed,
17068 c_tag_semantics: false,
17069 recovered_class_sibling_scopes: HashMap::default(),
17070 consumed_fragment_regions: Vec::new(),
17071 orphaned_namespaces: index,
17072 partitioned_regions: Vec::new(),
17073 namespace_forward_scans: HashMap::default(),
17074 field_owners: None,
17075 recovery_captures: Vec::new(),
17076 object_macro_fields: HashMap::default(),
17077 ambiguous_object_macro_fields: HashSet::default(),
17078 };
17079 let recovered = visitor
17080 .recovered_namespace_scope(tail_node, &scope)
17081 .expect("the tail must use the stolen namespace scope");
17082 assert_eq!(recovered.package_name, "testing::internal");
17083 assert!(
17084 recovered.class_unit.is_none(),
17085 "recovered namespace declarations cannot retain the malformed class owner"
17086 );
17087 assert!(recovered.template_signature.is_none());
17088 assert!(recovered.template_metadata.is_none());
17089 assert!(!recovered.declarations_are_fields);
17090 assert!(!recovered.recovered_specialization_member_scope);
17091 assert!(
17092 declarations
17093 .iter()
17094 .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
17095 "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
17096 );
17097 assert!(
17098 declarations
17099 .iter()
17100 .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
17101 "the recovered free function remains in its namespace: {declarations:#?}"
17102 );
17103 assert!(
17104 declarations
17105 .iter()
17106 .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
17107 "the independent nested class keeps its ordinary class owner: {declarations:#?}"
17108 );
17109 assert!(
17110 declarations.iter().all(|unit| {
17111 !unit
17112 .short_name()
17113 .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
17114 }),
17115 "recovered namespace declarations must not retain a class owner: {declarations:#?}"
17116 );
17117 assert!(
17118 declarations
17119 .iter()
17120 .all(|unit| !unit.identifier().is_empty()),
17121 "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
17122 );
17123 }
17124
17125 #[test]
17126 fn object_like_field_macros_materialize_owner_specific_declarations() {
17127 let source = r#"#define PUBLIC_FIELDS int public_value;
17128#define PRIVATE_FIELDS int private_value;
17129#define NOT_A_FIELD_LIST not a declaration
17130
17131struct First {
17132 PUBLIC_FIELDS
17133 PRIVATE_FIELDS
17134};
17135struct Second {
17136 PUBLIC_FIELDS
17137 NOT_A_FIELD_LIST
17138};
17139#undef PUBLIC_FIELDS
17140struct Third {
17141 PUBLIC_FIELDS
17142};
17143"#;
17144 let parsed = parse_cpp_declarations(source, "macro-fields.c");
17145 let fields = parsed
17146 .declarations()
17147 .iter()
17148 .filter(|unit| unit.is_field())
17149 .map(|unit| unit.fq_name())
17150 .collect::<Vec<_>>();
17151
17152 assert!(
17153 fields.contains(&"First.public_value".to_string()),
17154 "{fields:?}"
17155 );
17156 assert!(
17157 fields.contains(&"First.private_value".to_string()),
17158 "{fields:?}"
17159 );
17160 assert!(
17161 fields.contains(&"Second.public_value".to_string()),
17162 "{fields:?}"
17163 );
17164 assert!(
17165 !fields.iter().any(|field| field.contains("not_a_field")),
17166 "malformed macro must fail closed: {fields:?}"
17167 );
17168 assert!(
17169 !fields.iter().any(|field| field.starts_with("Third.")),
17170 "undefined macro must fail closed: {fields:?}"
17171 );
17172 }
17173
17174 #[test]
17175 fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
17176 let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
17177 let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
17178 let mut macros = parsed
17179 .declarations()
17180 .iter()
17181 .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
17182 .collect::<Vec<_>>();
17183 macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
17184
17185 assert_eq!(macros.len(), 2, "{macros:#?}");
17186 assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
17187 assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
17188 assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
17189 assert_eq!(
17190 parsed.declaration_ranges(macros[1])[0].start_byte,
17191 source.rfind("#define VALUE 2").expect("second definition")
17192 );
17193 }
17194
17195 #[test]
17196 fn identifies_export_macro_class_base_displaced_into_declarator() {
17197 let source = r#"#define PROJECT_API_
17198namespace project {
17199namespace internal {
17200template <typename T>
17201class Base {};
17202}
17203template <typename T>
17204class Wrapper;
17205template <>
17206class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
17207}
17208"#;
17209 let mut parser = tree_sitter::Parser::new();
17210 parser
17211 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17212 .unwrap();
17213 let tree = parser.parse(source, None).unwrap();
17214 let start = source.find("internal::Base<int>").expect("base");
17215 let mut base = tree
17216 .root_node()
17217 .descendant_for_byte_range(start, start + 8)
17218 .expect("base syntax");
17219 while base.kind() != "qualified_identifier" {
17220 base = base.parent().expect("qualified base ancestor");
17221 }
17222 assert!(
17223 is_recovered_exported_class_base_type_node(base, source),
17224 "{}",
17225 tree.root_node().to_sexp()
17226 );
17227 }
17228
17229 fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
17230 let mut identities = parsed
17231 .declarations()
17232 .iter()
17233 .filter(|unit| unit.is_function())
17234 .map(|unit| {
17235 (
17236 unit.fq_name(),
17237 unit.signature().unwrap_or_default().to_string(),
17238 )
17239 })
17240 .collect::<Vec<_>>();
17241 identities.sort();
17242 identities
17243 }
17244
17245 #[test]
17252 fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
17253 let cases = [
17254 (
17255 "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
17256 "pg_typemap_fit_to_result",
17257 "(VALUE, VALUE)",
17258 ),
17259 (
17260 "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
17261 "pg_typemap_result_value",
17262 "(t_typemap *, VALUE, int, int)",
17263 ),
17264 (
17265 "void pg_typemap_mark _(( void * ));",
17266 "pg_typemap_mark",
17267 "(void *)",
17268 ),
17269 (
17270 "void init_pg_type_map _(( void ));",
17271 "init_pg_type_map",
17272 "(void)",
17273 ),
17274 ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
17275 (
17276 "extern VALUE pg_extern _(( VALUE, VALUE ));",
17277 "pg_extern",
17278 "(VALUE, VALUE)",
17279 ),
17280 (
17281 "size_t pg_typemap_memsize _(( const void * ));",
17282 "pg_typemap_memsize",
17283 "(const void *)",
17284 ),
17285 (
17286 "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
17287 "pg_wrap_socket_io",
17288 "(int, VALUE, VALUE *, int *)",
17289 ),
17290 ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
17291 ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
17292 ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
17293 ];
17294
17295 for (index, (source, name, signature)) in cases.into_iter().enumerate() {
17296 let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
17297 assert_eq!(
17298 function_identities(&parsed),
17299 vec![(name.to_string(), signature.to_string())],
17300 "{source}: {:#?}",
17301 parsed.declarations()
17302 );
17303 assert!(
17304 parsed.declarations().iter().all(|unit| !unit.is_field()),
17305 "{source} must not retain the malformed field: {:#?}",
17306 parsed.declarations()
17307 );
17308 }
17309 }
17310
17311 #[test]
17320 fn c_prototype_macro_recovers_pointer_return_expression_statements() {
17321 let cases = [
17322 (
17323 "PGconn *pg_get_pgconn _(( VALUE ));",
17324 "pg_get_pgconn",
17325 "(VALUE)",
17326 ),
17327 (
17328 "PGresult* pgresult_get _(( VALUE ));",
17329 "pgresult_get",
17330 "(VALUE)",
17331 ),
17332 ];
17333 for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
17334 let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
17335 let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
17336 assert_eq!(
17337 function_identities(&parsed),
17338 vec![(name.to_string(), signature.to_string())],
17339 "{source}: {:#?}",
17340 parsed.declarations()
17341 );
17342 assert_eq!(
17343 parsed
17344 .declarations()
17345 .iter()
17346 .filter(|unit| unit.is_field())
17347 .map(|unit| unit.fq_name())
17348 .collect::<Vec<_>>(),
17349 vec!["rb_mPG".to_string()],
17350 "{source}: {:#?}",
17351 parsed.declarations()
17352 );
17353 }
17354 }
17355
17356 #[test]
17364 fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
17365 let source = r#"VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));
17366VALUE pg_typemap_fit_to_query _(( VALUE, VALUE ));
17367int pg_typemap_fit_to_copy_get _(( VALUE ));
17368VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));
17369t_pg_coder *pg_typemap_typecast_query_param _(( t_typemap *, VALUE, int ));
17370VALUE pg_typemap_typecast_copy_get _(( t_typemap *, VALUE, int, int, int ));
17371void pg_typemap_mark _(( void * ));
17372size_t pg_typemap_memsize _(( const void * ));
17373void pg_typemap_compact _(( void * ));
17374
17375PGconn *pg_get_pgconn _(( VALUE ));
17376t_pg_connection *pg_get_connection _(( VALUE ));
17377VALUE pgconn_block _(( int, VALUE *, VALUE ));
17378#ifdef __GNUC__
17379__attribute__((format(printf, 3, 4)))
17380#endif
17381NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
17382VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
17383void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
17384
17385
17386VALUE pg_new_result _(( PGresult *, VALUE ));
17387VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
17388PGresult* pgresult_get _(( VALUE ));
17389VALUE pg_result_check _(( VALUE ));
17390VALUE pg_result_clear _(( VALUE ));
17391VALUE pg_tuple_new _(( VALUE, int ));
17392
17393/*
17394 * Fetch the data pointer for the result object
17395 */
17396static inline t_pg_result *
17397pgresult_get_this( VALUE self )
17398{
17399 return RTYPEDDATA_DATA(self);
17400}
17401
17402
17403rb_encoding * pg_get_pg_encname_as_rb_encoding _(( const char * ));
17404const char * pg_get_rb_encoding_as_pg_encoding _(( rb_encoding * ));
17405rb_encoding *pg_conn_enc_get _(( PGconn * ));
17406
17407"#;
17408 let parsed = parse_cpp_declarations(source, "pg.h");
17409 assert!(
17410 function_identities(&parsed)
17411 .iter()
17412 .any(|(name, signature)| name == "pg_typemap_result_value"
17413 && signature == "(t_typemap *, VALUE, int, int)"),
17414 "the real issue witness must be a Function with its C signature: {:#?}",
17415 parsed.declarations()
17416 );
17417 assert!(
17418 parsed
17419 .declarations()
17420 .iter()
17421 .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
17422 "the issue witness must not leave its return type as a Field name: {:#?}",
17423 parsed.declarations()
17424 );
17425 }
17426
17427 #[test]
17434 fn a_macro_decorated_constructor_is_named_for_the_constructor() {
17435 let source = r#"class SIMD_4x26 final {
17436 public:
17437 explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
17438 BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
17439 int m_v;
17440};
17441"#;
17442 let parsed = parse_cpp_declarations(source, "simd_4x26.h");
17443 assert_eq!(
17444 function_identities(&parsed),
17445 vec![
17446 ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
17447 ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
17448 ],
17449 "{:#?}",
17450 parsed.declarations()
17451 );
17452 }
17453
17454 #[test]
17459 fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
17460 let source = r#"#include <cstdint>
17461struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
17462 DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
17463 struct llama_model * model,
17464 struct llama_context_params params),
17465 "use llama_init_from_model instead");
17466 LLAMA_API int32_t llama_tokenize(
17467 const struct llama_vocab * vocab,
17468 const char * text,
17469 bool parse_special);
17470 LLAMA_API int32_t llama_other(int a);
17471"#;
17472 let parsed = parse_cpp_declarations(source, "llama.h");
17473 assert_eq!(
17474 function_identities(&parsed),
17475 vec![
17476 (
17477 "llama_new_context_with_model".to_string(),
17478 "(struct llama_model *, struct llama_context_params)".to_string()
17479 ),
17480 ("llama_other".to_string(), "(int)".to_string()),
17481 (
17482 "llama_tokenize".to_string(),
17483 "(const struct llama_vocab *, const char *, bool)".to_string()
17484 ),
17485 ],
17486 "{:#?}",
17487 parsed.declarations()
17488 );
17489
17490 for (name, expected) in [
17493 (
17494 "llama_new_context_with_model",
17495 "LLAMA_API struct llama_context * llama_new_context_with_model(",
17496 ),
17497 ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
17498 ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
17499 ] {
17500 let unit = parsed
17501 .declarations()
17502 .iter()
17503 .find(|unit| unit.is_function() && unit.fq_name() == name)
17504 .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
17505 let [range] = parsed.declaration_ranges(unit) else {
17506 panic!("{name} must have exactly one range");
17507 };
17508 let text = &source[range.start_byte..range.end_byte];
17509 assert!(
17510 text.starts_with(expected),
17511 "{name} range is {text:?}, expected it to start with {expected:?}"
17512 );
17513 assert!(
17514 text.ends_with(')') || text.ends_with(';'),
17515 "{name}: {text:?}"
17516 );
17517 }
17518 }
17519
17520 #[test]
17534 fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
17535 let source = r#" LIBRARY_DEPRECATED(
17536 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17537 "use library_init_from_file_with_params instead"
17538 );
17539 LIBRARY_DEPRECATED(
17540 LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
17541 "use library_init_from_buffer_with_params instead"
17542 );
17543 LIBRARY_DEPRECATED(
17544 LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
17545 "use library_init_with_params instead"
17546 );
17547"#;
17548 let mut parser = tree_sitter::Parser::new();
17549 parser
17550 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17551 .expect("C++ grammar");
17552 let tree = parser.parse(source, None).expect("C++ tree");
17553 let root = tree.root_node();
17554
17555 let mut cursor = root.walk();
17559 let items = root.children(&mut cursor).collect::<Vec<_>>();
17560 let [first, hint_statement, rest @ ..] = items.as_slice() else {
17561 panic!("{}", root.to_sexp());
17562 };
17563 assert_eq!(first.kind(), "ERROR");
17564 assert_eq!(hint_statement.kind(), "expression_statement");
17565 assert!(
17566 node_text(*first, source).ends_with(','),
17567 "the first invocation's own `)` and `;` are the statement's, not its own: {}",
17568 root.to_sexp()
17569 );
17570 let swallowing = rest
17571 .iter()
17572 .find(|item| item.kind() == "ERROR")
17573 .unwrap_or_else(|| panic!("{}", root.to_sexp()));
17574 let mut swallowing_cursor = swallowing.walk();
17575 let flattened = swallowing
17576 .children(&mut swallowing_cursor)
17577 .map(|child| child.kind())
17578 .collect::<Vec<_>>();
17579 assert_eq!(
17580 flattened,
17581 vec![
17582 "identifier",
17583 "(",
17584 "parameter_declaration",
17585 ",",
17586 "ERROR",
17587 "type_identifier",
17588 "(",
17589 "parameter_declaration",
17590 ",",
17591 "\"",
17592 "identifier",
17593 "identifier",
17594 "identifier",
17595 "\"",
17596 ")",
17597 ],
17598 "the third invocation must be flattened into the second one's node: {}",
17599 root.to_sexp()
17600 );
17601
17602 let first_run =
17604 collapsed_macro_declaration_run(*first, source).expect("the first invocation");
17605 assert_eq!(
17606 &source[..first_run.invocation_end],
17607 &source[..source.find("instead\"\n );").expect("first hint")
17608 + "instead\"\n );".len()]
17609 );
17610 assert_eq!(
17611 first_run.region_end, first_run.invocation_end,
17612 "the first invocation swallowed nothing, so the recovery owns only its own bytes"
17613 );
17614 let swallowing_run =
17615 collapsed_macro_declaration_run(*swallowing, source).expect("the second invocation");
17616 assert!(
17617 swallowing_run.invocation_end < swallowing.end_byte(),
17618 "the second invocation swallowed the third"
17619 );
17620 assert_eq!(
17621 swallowing_run.region_end,
17622 root.end_byte(),
17623 "a swallowing invocation owns the region to the close of its declaration scope"
17624 );
17625
17626 let parsed = parse_cpp_declarations(source, "library.h");
17627 assert_eq!(
17628 function_identities(&parsed),
17629 vec![
17630 (
17631 "library_init".to_string(),
17632 "(struct library_model_loader *)".to_string()
17633 ),
17634 (
17635 "library_init_from_buffer".to_string(),
17636 "(void *, size_t)".to_string()
17637 ),
17638 (
17639 "library_init_from_file".to_string(),
17640 "(const char *)".to_string()
17641 ),
17642 ],
17643 "{:#?}",
17644 parsed.declarations()
17645 );
17646 }
17647
17648 #[test]
17654 fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
17655 let source = r#"LIBRARY_DEPRECATED(
17656 LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17657 "use library_init_from_file_with_params instead"
17658 );"#;
17659 let mut parser = tree_sitter::Parser::new();
17660 parser
17661 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17662 .expect("C++ grammar");
17663 let tree = parser.parse(source, None).expect("C++ tree");
17664 let root = tree.root_node();
17665 let head = root.named_child(0).expect("the invocation");
17666 assert!(
17667 collapsed_macro_declaration_run(head, source).is_none(),
17668 "{}",
17669 root.to_sexp()
17670 );
17671 assert!(
17672 !macro_wrapped_declarations(head, source).is_empty(),
17673 "the ordinary reader must be the one that has it: {}",
17674 root.to_sexp()
17675 );
17676 }
17677
17678 #[test]
17683 fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
17684 for source in [
17685 "int before;\nFOO(1, 2);\nint after;\n",
17686 "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
17687 "int before;\nMACRO(int a, int b);\nint after;\n",
17688 "DECLARE_HANDLE(HWND);\nint after;\n",
17689 ] {
17690 let parsed = parse_cpp_declarations(source, "macro-call.h");
17691 assert_eq!(
17692 function_identities(&parsed),
17693 Vec::new(),
17694 "{source:?} must declare no function: {:#?}",
17695 parsed.declarations()
17696 );
17697 }
17698 }
17699
17700 #[test]
17705 fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
17706 let source = r#"#include <string_view>
17707namespace Botan {
17708class DL_Group final {
17709 public:
17710 DL_Group() = default;
17711 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17712 DL_Group(std::string_view pem, int format);
17713 size_t get_p() const;
17714};
17715}
17716"#;
17717 let parsed = parse_cpp_declarations(source, "dl_group.h");
17718 assert_eq!(
17719 function_identities(&parsed),
17720 vec![
17721 ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
17722 (
17723 "Botan.DL_Group.DL_Group".to_string(),
17724 "(std::string_view)".to_string()
17725 ),
17726 (
17727 "Botan.DL_Group.DL_Group".to_string(),
17728 "(std::string_view, int)".to_string()
17729 ),
17730 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
17731 ],
17732 "{:#?}",
17733 parsed.declarations()
17734 );
17735 }
17736
17737 #[test]
17742 fn an_export_macro_class_keeps_its_string_attribute_members() {
17743 let source = r#"#include <string_view>
17744namespace Botan {
17745class BOTAN_PUBLIC_API(2, 0) DL_Group final {
17746 public:
17747 BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17748 DL_Group(std::string_view pem, int format);
17749 size_t get_p() const;
17750};
17751}
17752"#;
17753 let parsed = parse_cpp_declarations(source, "dl_group.h");
17754 assert!(
17755 parsed
17756 .declarations()
17757 .iter()
17758 .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
17759 "{:#?}",
17760 parsed.declarations()
17761 );
17762 assert_eq!(
17763 function_identities(&parsed),
17764 vec![
17765 (
17766 "Botan.DL_Group.DL_Group".to_string(),
17767 "(std::string_view)".to_string()
17768 ),
17769 (
17770 "Botan.DL_Group.DL_Group".to_string(),
17771 "(std::string_view, int)".to_string()
17772 ),
17773 ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
17774 ],
17775 "{:#?}",
17776 parsed.declarations()
17777 );
17778 }
17779
17780 #[test]
17786 fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
17787 let source = r#"namespace Botan {
17788class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
17789 public:
17790 BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
17791 XMSS_Parameters(int oid, int len);
17792 size_t len() const;
17793
17794 private:
17795 XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
17796 m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
17797
17798 int m_oid;
17799 int m_wots_oid;
17800 size_t m_element_size;
17801 size_t m_tree_height;
17802};
17803}
17804"#;
17805 let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
17806 let constructors = function_identities(&parsed)
17807 .into_iter()
17808 .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
17809 .map(|(_, signature)| signature)
17810 .collect::<Vec<_>>();
17811 assert_eq!(
17812 constructors,
17813 vec![
17814 "()".to_string(),
17815 "(int, int)".to_string(),
17816 "(int, int, size_t, size_t)".to_string(),
17817 ],
17818 "{:#?}",
17819 parsed.declarations()
17820 );
17821 }
17822
17823 #[test]
17827 fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
17828 let source = r#"namespace shell {
17829struct Outer {
17830 struct Inner {
17831 Inner(int v);
17832 void run(int v);
17833 };
17834};
17835Outer::Inner::Inner(int v) {}
17836void Outer::Inner::run(int v) {}
17837}
17838"#;
17839 let parsed = parse_cpp_declarations(source, "outer.cpp");
17840 let names = function_identities(&parsed)
17841 .into_iter()
17842 .map(|(fq_name, _)| fq_name)
17843 .collect::<Vec<_>>();
17844 assert!(
17845 names
17846 .iter()
17847 .all(|name| name.starts_with("shell.Outer$Inner.")),
17848 "{names:#?}"
17849 );
17850 }
17851
17852 #[test]
17853 fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
17854 let source = r#"namespace control {
17855template <typename T>
17856class AnySpan;
17857template <typename T>
17858class ABSL_ATTRIBUTE_VIEW AnySpan {
17859 public:
17860 int begin() const;
17861};
17862}
17863
17864namespace absl {
17865ABSL_NAMESPACE_BEGIN
17866template <typename T>
17867class ABSL_ATTRIBUTE_VIEW Span {
17868 public:
17869 int begin() const;
17870 int back() const;
17871};
17872
17873int begin();
17874int back();
17875}
17876"#;
17877 let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
17878 let declarations = parsed.declarations();
17879 assert!(
17880 declarations
17881 .iter()
17882 .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
17883 );
17884 for method in ["begin", "back"] {
17885 assert!(declarations.iter().any(|unit| {
17886 unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
17887 }));
17888 assert!(
17889 declarations.iter().any(|unit| {
17890 unit.is_function() && unit.fq_name() == format!("absl.{method}")
17891 })
17892 );
17893 }
17894 assert!(
17895 declarations
17896 .iter()
17897 .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
17898 );
17899 assert!(
17900 declarations
17901 .iter()
17902 .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
17903 );
17904 assert!(
17905 declarations
17906 .iter()
17907 .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
17908 );
17909 }
17910
17911 #[test]
17912 fn explicit_global_member_definition_has_canonical_package_boundary() {
17913 let source = r#"
17914namespace arangodb::aql {
17915class ExecutionPlan {
17916 public:
17917 template<class... Args> Node* createNode(Args&&... args);
17918};
17919}
17920
17921template<class... Args>
17922Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
17923"#;
17924 let parsed = parse_cpp_declarations(source, "global-member.cpp");
17925
17926 assert!(parsed.declarations().iter().any(|unit| {
17927 unit.is_function()
17928 && unit.package_name() == "arangodb::aql"
17929 && unit.short_name() == "ExecutionPlan.createNode"
17930 && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
17931 }));
17932 }
17933
17934 #[test]
17935 fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
17936 let source = r#"
17937#ifndef TINYXML2_INCLUDED
17938#define TINYXML2_INCLUDED
17939namespace tinyxml2 {
17940class TINYXML2_LIB XMLUtil {
17941 public:
17942 static const char* SkipWhiteSpace(const char* p) {
17943 while (*p) {
17944 if (*p == ' ') {
17945 ++p;
17946 }
17947 }
17948 return p;
17949 }
17950 static bool StringEqual(const char* p, const char* q) {
17951 return p == q;
17952 }
17953 class TINYXML2_LIB Helper {
17954 public:
17955 void Touch();
17956 };
17957 static void ToStr(int value, char* buffer);
17958 private:
17959 static const char* writeBoolTrue;
17960};
17961
17962class TINYXML2_LIB XMLNode {
17963 public:
17964 virtual XMLNode* ShallowClone() const = 0;
17965 virtual bool ShallowEqual(const XMLNode* compare) const = 0;
17966};
17967}
17968#endif
17969"#;
17970 let mut parser = tree_sitter::Parser::new();
17971 parser
17972 .set_language(&tree_sitter_cpp::LANGUAGE.into())
17973 .unwrap();
17974 let tree = parser.parse(source, None).unwrap();
17975 let mut boundary_found = false;
17976 walk_named_tree_preorder(tree.root_node(), true, |node| {
17977 if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
17978 && name == "XMLUtil"
17979 {
17980 boundary_found = fragmented_export_sibling_class_boundary(node, source)
17981 .and_then(|boundary| {
17982 recover_exported_class_function_definition(boundary, source)
17983 })
17984 .is_some_and(|(_, name, _)| name == "XMLNode");
17985 }
17986 WalkControl::Continue
17987 });
17988 assert!(
17989 boundary_found,
17990 "fixture must exercise the recovered sibling boundary"
17991 );
17992
17993 let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
17994 assert!(
17995 parsed
17996 .declarations()
17997 .iter()
17998 .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
17999 "{:#?}",
18000 parsed.declarations()
18001 );
18002 assert!(
18003 parsed
18004 .declarations()
18005 .iter()
18006 .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
18007 "{:#?}",
18008 parsed.declarations()
18009 );
18010 assert!(parsed.declarations().iter().any(|unit| {
18011 unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
18012 }));
18013 assert!(
18014 parsed
18015 .declarations()
18016 .iter()
18017 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
18018 );
18019 assert!(
18020 parsed
18021 .declarations()
18022 .iter()
18023 .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
18024 );
18025 }
18026
18027 #[test]
18028 fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
18029 let parsed = parse_cpp_declarations(
18033 r#"
18034namespace cwg311 {
18035namespace X { namespace Y {} }
18036namespace ::cwg311::X {}
18037}
18038"#,
18039 "explicit-global-namespace.cpp",
18040 );
18041
18042 assert!(parsed.declarations().iter().any(|unit| {
18043 unit.kind() == CodeUnitType::Module
18044 && unit.short_name() == "cwg311::X"
18045 && unit.fq_name() == "cwg311::X"
18046 }));
18047 assert!(
18048 parsed
18049 .declarations()
18050 .iter()
18051 .all(|unit| !unit.short_name().contains("::::")),
18052 "recovered namespace names must not retain empty scope components: {:#?}",
18053 parsed.declarations()
18054 );
18055 }
18056
18057 #[test]
18058 fn repeated_scope_separator_does_not_create_empty_function_owner() {
18059 let scope = ScopeInfo {
18060 package_name: "X".to_string(),
18061 module: None,
18062 class_unit: None,
18063 template_signature: None,
18064 template_metadata: None,
18065 declarations_are_fields: false,
18066 recovered_specialization_member_scope: false,
18067 visible_using_namespaces: Vec::new(),
18068 };
18069
18070 let (owner, name, package) = split_cpp_name("X::::doit", &scope);
18071
18072 assert!(owner.is_none());
18073 assert_eq!(name, "doit");
18074 assert_eq!(package, "X");
18075 }
18076
18077 #[test]
18078 fn trailing_decltype_expression_is_not_a_function_declarator() {
18079 let source = r#"
18080namespace boost { namespace detail {
18081#if ! defined(BOOST_NO_SFINAE_EXPR) && \
18082 ! defined(BOOST_NO_CXX11_DECLTYPE) && \
18083 ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
18084#define BOOST_THREAD_PROVIDES_INVOKE
18085#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
18086template <class Fp, class A0, class ...Args>
18087inline auto
18088invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
18089 BOOST_THREAD_RV_REF(Args) ...args)
18090 -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
18091{
18092 return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
18093}
18094#endif
18095#endif
18096}}
18097"#;
18098 let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
18099
18100 assert!(
18101 parsed
18102 .declarations()
18103 .iter()
18104 .all(|unit| unit.short_name() != ".*f")
18105 );
18106 }
18107
18108 fn find_class_named<'tree>(
18109 root: Node<'tree>,
18110 source: &str,
18111 expected_name: &str,
18112 ) -> Option<Node<'tree>> {
18113 let mut stack = vec![root];
18114 while let Some(node) = stack.pop() {
18115 if node.kind() == "class_specifier"
18116 && node
18117 .child_by_field_name("name")
18118 .is_some_and(|name| node_text(name, source) == expected_name)
18119 {
18120 return Some(node);
18121 }
18122 let mut cursor = node.walk();
18123 stack.extend(node.named_children(&mut cursor));
18124 }
18125 None
18126 }
18127
18128 #[test]
18129 fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
18130 let source = r#"EXPORT void definition(struct Value value) {}
18131EXPORT void prototype(struct Value value);
18132"#;
18133 let mut parser = tree_sitter::Parser::new();
18134 parser
18135 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18136 .unwrap();
18137 let tree = parser.parse(source, None).unwrap();
18138 let root = tree.root_node();
18139 let mut cursor = root.walk();
18140 let callables = root
18141 .named_children(&mut cursor)
18142 .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
18143 .collect::<Vec<_>>();
18144
18145 assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
18146 for callable in callables {
18147 assert!(callable.has_error(), "fixture must exercise error recovery");
18148 assert!(
18149 cpp_sentinel_macro_parts(callable, source).is_none(),
18150 "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
18151 );
18152 }
18153 }
18154
18155 #[test]
18156 fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
18157 let source = r#"namespace absl {
18158ABSL_NAMESPACE_BEGIN
18159// Generate a floating-point variate conforming to a Beta distribution:
18160template <typename RealType = double>
18161class beta_distribution {
18162 public:
18163 using result_type = RealType;
18164
18165
18166 beta_distribution() : beta_distribution(1) {}
18167
18168 explicit beta_distribution(result_type alpha, result_type beta = 1)
18169 : param_(alpha, beta) {}
18170
18171 explicit beta_distribution(const param_type& p) : param_(p) {}
18172
18173 void reset() {}
18174
18175 // Generating functions
18176 template <typename URBG>
18177 result_type operator()(URBG& g) { // NOLINT(runtime/references)
18178 return (*this)(g, param_);
18179 }
18180
18181};
18182ABSL_NAMESPACE_END
18183} // namespace absl
18184"#;
18185 let mut parser = tree_sitter::Parser::new();
18186 parser
18187 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18188 .unwrap();
18189 let tree = parser.parse(source, None).unwrap();
18190 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18191 let body = namespace
18192 .child_by_field_name("body")
18193 .expect("fixture namespace body");
18194 let sentinel = body.named_child(0).expect("sentinel envelope");
18195 let callable = sentinel
18196 .child_by_field_name("declarator")
18197 .and_then(extract_function_declarator)
18198 .and_then(cpp_function_declarator_name_node)
18199 .expect("preserved callable name");
18200
18201 assert_eq!(sentinel.kind(), "function_definition");
18202 assert_eq!(callable.kind(), "operator_name");
18203 assert!(
18204 cpp_sentinel_macro_parts(sentinel, source).is_some(),
18205 "a class preceding its recovered member callable remains a sentinel: {sentinel}"
18206 );
18207 }
18208
18209 #[test]
18210 fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
18211 let source = r#"namespace absl {
18212ABSL_NAMESPACE_BEGIN
18213// absl::discrete_distribution
18214//
18215// A discrete distribution produces random integers i, where 0 <= i < n
18216template <typename IntType = int>
18217class discrete_distribution {
18218 public:
18219 using result_type = IntType;
18220 class param_type {
18221 public:
18222 param_type() { init(); }
18223 template <typename InputIterator>
18224 explicit param_type(InputIterator begin, InputIterator end)
18225 : p_(begin, end) {
18226 init();
18227 }
18228 };
18229 discrete_distribution() : param_() {}
18230 explicit discrete_distribution(const param_type& p) : param_(p) {}
18231};
18232ABSL_NAMESPACE_END
18233} // namespace absl
18234"#;
18235 let mut parser = tree_sitter::Parser::new();
18236 parser
18237 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18238 .unwrap();
18239 let tree = parser.parse(source, None).unwrap();
18240 let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18241 let body = namespace
18242 .child_by_field_name("body")
18243 .expect("fixture namespace body");
18244 let sentinel = body.named_child(0).expect("sentinel envelope");
18245 let callable = sentinel
18246 .child_by_field_name("declarator")
18247 .and_then(extract_function_declarator)
18248 .and_then(cpp_function_declarator_name_node)
18249 .expect("preserved callable name");
18250
18251 assert_eq!(sentinel.kind(), "function_definition");
18252 assert_eq!(callable.kind(), "identifier");
18253 assert!(
18254 cpp_sentinel_macro_parts(sentinel, source).is_some(),
18255 "a class preceding its recovered constructor remains a sentinel: {sentinel}"
18256 );
18257 }
18258
18259 #[test]
18260 fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
18261 let source = r#"
18262#define CPPCHECKLIB
18263class Library {
18264 struct Container {
18265 CPPCHECKLIB static std::string toString(Yield yield);
18266 CPPCHECKLIB static std::string toString(Action action);
18267 };
18268};
18269"#;
18270 let mut parser = tree_sitter::Parser::new();
18271 parser
18272 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18273 .unwrap();
18274 let tree = parser.parse(source, None).unwrap();
18275 let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
18276 let parsed = parse_cpp_file(&file, source, &tree);
18277 assert!(
18278 parsed
18279 .declarations()
18280 .iter()
18281 .all(|unit| unit.fq_name() != "Library$Container.std"),
18282 "the qualified return-type namespace must not become a field: {:#?}",
18283 parsed.declarations()
18284 );
18285 for expected in ["(Yield)", "(Action)"] {
18286 assert!(
18287 parsed.declarations().iter().any(|unit| {
18288 unit.is_function()
18289 && unit.fq_name() == "Library$Container.toString"
18290 && unit.signature() == Some(expected)
18291 }),
18292 "recovered toString overload {expected} is missing: {:#?}",
18293 parsed.declarations()
18294 );
18295 }
18296 }
18297
18298 #[test]
18299 fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
18300 let source = r#"
18301#define SIMPLECPP_LIB
18302namespace simplecpp {
18303using TokenString = std::string;
18304struct Location { int line{}; };
18305class SIMPLECPP_LIB Token {
18306 TokenString prefix;
18307 void prefix_method() {}
18308 public:
18309 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18310 whitespaceahead(wsahead), location(loc), string(s)
18311 // The comment must not hide the constructor body from recovery.
18312 {
18313 flags();
18314 }
18315 TokenString string;
18316 bool whitespaceahead;
18317 Location location;
18318 Token *previous{};
18319 private:
18320 void flags() {
18321 whitespaceahead = true;
18322 }
18323};
18324}
18325"#;
18326 let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
18327
18328 let location_fields = parsed
18329 .declarations()
18330 .iter()
18331 .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
18332 .collect::<Vec<_>>();
18333 assert_eq!(
18334 location_fields.len(),
18335 1,
18336 "location should have one class-owned declaration: {:#?}",
18337 parsed.declarations()
18338 );
18339 assert!(
18340 location_fields[0].is_field(),
18341 "location has wrong kind: {:#?}",
18342 parsed.declarations()
18343 );
18344 assert!(
18345 parsed.declarations().iter().all(|unit| {
18346 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
18347 })
18348 );
18349 assert!(
18350 parsed.declarations().iter().all(|unit| {
18351 !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18352 })
18353 );
18354 assert!(
18355 parsed
18356 .declarations()
18357 .iter()
18358 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
18359 );
18360 assert!(
18361 parsed
18362 .declarations()
18363 .iter()
18364 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
18365 "the recovered class must retain its constructor: {:#?}",
18366 parsed.declarations()
18367 );
18368 assert!(
18369 parsed
18370 .declarations()
18371 .iter()
18372 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
18373 );
18374 assert!(parsed.declarations().iter().any(|unit| {
18375 unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
18376 }));
18377 let constructor = parsed
18378 .declarations()
18379 .iter()
18380 .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
18381 .expect("recovered constructor");
18382 let constructor_start = source.find("Token(const").expect("constructor start");
18383 let constructor_end = source
18384 .get(
18385 ..source
18386 .find(" TokenString string;")
18387 .expect("constructor end"),
18388 )
18389 .expect("constructor slice")
18390 .trim_end()
18391 .len();
18392 assert!(
18393 parsed
18394 .navigation_ranges
18395 .get(constructor)
18396 .is_some_and(|ranges| {
18397 ranges.iter().any(|range| {
18398 range.start_byte == constructor_start && range.end_byte == constructor_end
18399 })
18400 }),
18401 "constructor navigation must span the full body: {:#?}",
18402 parsed.navigation_ranges
18403 );
18404 assert_eq!(
18405 parsed
18406 .signature_metadata
18407 .get(constructor)
18408 .and_then(|metadata| metadata.first())
18409 .and_then(SignatureMetadata::callable_linkage),
18410 Some(CallableLinkage::External)
18411 );
18412 let token_class = parsed
18413 .declarations()
18414 .iter()
18415 .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
18416 .expect("recovered Token class");
18417 let class_end = source.rfind("};\n}").expect("class terminator") + 2;
18418 assert!(
18419 parsed
18420 .navigation_ranges
18421 .get(token_class)
18422 .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
18423 "class navigation must include the terminating semicolon: {:#?}",
18424 parsed.navigation_ranges
18425 );
18426 }
18427
18428 #[test]
18429 fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
18430 let source = r#"
18431#define SIMPLECPP_LIB
18432namespace simplecpp {
18433using TokenString = std::string;
18434class Macro;
18435struct Location {
18436 unsigned int fileIndex{};
18437 unsigned int line{};
18438 unsigned int col{};
18439};
18440struct Output {
18441 int type;
18442};
18443class SIMPLECPP_LIB Token {
18444 public:
18445 Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18446 whitespaceahead(wsahead), location(loc), string(s) {
18447 flags();
18448 }
18449 Token(const Token &tok) :
18450 macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
18451 number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
18452 string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
18453 Token &operator=(const Token &tok) = delete;
18454 const TokenString& str() const { return string; }
18455 void setstr(const std::string &s) { string = s; flags(); }
18456 bool isOneOf(const char ops[]) const;
18457 TokenString macro;
18458 char op;
18459 bool comment;
18460 bool name;
18461 bool number;
18462 bool whitespaceahead;
18463 Location location;
18464 Token *previous{};
18465 Token *next{};
18466 private:
18467 void flags() {
18468 name = !string.empty();
18469 comment = false;
18470 number = false;
18471 op = 0;
18472 }
18473 TokenString string;
18474};
18475}
18476struct Following {
18477 int type;
18478};
18479class SIMPLECPP_LIB Later {
18480 public:
18481 Later(int value) : value(value) {}
18482 int value;
18483};
18484"#;
18485 let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
18486 assert!(
18487 parsed
18488 .declarations()
18489 .iter()
18490 .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
18491 );
18492 assert!(
18493 !parsed
18494 .declarations()
18495 .iter()
18496 .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
18497 );
18498 assert!(
18499 parsed
18500 .declarations()
18501 .iter()
18502 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
18503 );
18504 assert!(
18505 !parsed
18506 .declarations()
18507 .iter()
18508 .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18509 );
18510 assert!(
18511 parsed
18512 .declarations()
18513 .iter()
18514 .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
18515 );
18516 assert!(
18517 parsed
18518 .declarations()
18519 .iter()
18520 .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
18521 );
18522 assert!(
18523 parsed
18524 .declarations()
18525 .iter()
18526 .any(|unit| unit.is_class() && unit.fq_name() == "Following")
18527 );
18528 assert!(
18529 parsed
18530 .declarations()
18531 .iter()
18532 .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
18533 );
18534 assert!(
18535 parsed
18536 .declarations()
18537 .iter()
18538 .any(|unit| unit.is_class() && unit.fq_name() == "Later")
18539 );
18540 assert!(
18541 parsed
18542 .declarations()
18543 .iter()
18544 .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
18545 );
18546 assert!(parsed.declarations().iter().all(|unit| {
18547 !matches!(
18548 unit.fq_name().as_str(),
18549 "simplecpp.Token.Following" | "simplecpp.Token.Later"
18550 )
18551 }));
18552 assert!(
18553 !parsed
18554 .declarations()
18555 .iter()
18556 .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
18557 "the following struct must remain outside the recovered Token class"
18558 );
18559 }
18560
18561 #[test]
18562 fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
18563 let source = r#"
18564#define SIMPLECPP_LIB
18565namespace {
18566namespace simplecpp {
18567using TokenString = std::string;
18568struct Location { int line{}; };
18569class SIMPLECPP_LIB HiddenToken {
18570 public:
18571 HiddenToken(const TokenString &s, const Location &loc) :
18572 location(loc), string(s) {
18573 flags();
18574 }
18575 TokenString string;
18576 Location location;
18577 HiddenToken *previous{};
18578 private:
18579 void flags() {}
18580};
18581}
18582}
18583"#;
18584 let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
18585 let constructor = parsed
18586 .declarations()
18587 .iter()
18588 .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
18589 .expect("recovered anonymous-namespace constructor");
18590 assert_eq!(
18591 parsed
18592 .signature_metadata
18593 .get(constructor)
18594 .and_then(|metadata| metadata.first())
18595 .and_then(SignatureMetadata::callable_linkage),
18596 Some(CallableLinkage::Internal)
18597 );
18598 }
18599
18600 #[test]
18601 fn macro_qualified_static_field_keeps_real_declarator() {
18602 let source = r#"#define JSON_INLINE_VARIABLE
18603struct Reader {
18604static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
18605static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
18606static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
18607};"#;
18608 let mut parser = tree_sitter::Parser::new();
18609 parser
18610 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18611 .unwrap();
18612 let tree = parser.parse(source, None).unwrap();
18613 let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
18614 let parsed = parse_cpp_file(&file, source, &tree);
18615 for expected in [
18616 "Reader.npos",
18617 "Reader.other",
18618 "Reader.pointer",
18619 "Reader.reference",
18620 ] {
18621 assert!(
18622 parsed
18623 .declarations()
18624 .iter()
18625 .any(|unit| unit.is_field() && unit.fq_name() == expected),
18626 "real macro-decorated field {expected} is missing: {:#?}",
18627 parsed.declarations()
18628 );
18629 }
18630 assert!(
18631 parsed
18632 .declarations()
18633 .iter()
18634 .all(|unit| unit.fq_name() != "Reader.std"),
18635 "qualified type prefix became a pseudo-field: {:#?}",
18636 parsed.declarations()
18637 );
18638 let root = tree.root_node();
18639 let mut stack = vec![root];
18640 let mut signatures = Vec::new();
18641 while let Some(current) = stack.pop() {
18642 if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
18643 {
18644 signatures.extend(
18645 declarators
18646 .into_iter()
18647 .map(|declarator| render_cpp_field_signature(current, declarator, source)),
18648 );
18649 }
18650 let mut cursor = current.walk();
18651 stack.extend(current.named_children(&mut cursor));
18652 }
18653 signatures.sort();
18654 assert_eq!(
18655 signatures,
18656 [
18657 "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
18658 "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
18659 "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
18660 "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
18661 ]
18662 );
18663 }
18664
18665 fn member_function_linkage(source: &str) -> CallableLinkage {
18666 let mut parser = tree_sitter::Parser::new();
18667 parser
18668 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18669 .unwrap();
18670 let tree = parser.parse(source, None).unwrap();
18671 let ancestry = ParentIndex::new(tree.root_node());
18672 let mut stack = vec![tree.root_node()];
18673 while let Some(node) = stack.pop() {
18674 if node.kind() == "function_definition" {
18675 let mut current = node.parent();
18676 while let Some(parent) = current {
18677 if matches!(
18678 parent.kind(),
18679 "class_specifier" | "struct_specifier" | "union_specifier"
18680 ) {
18681 return cpp_callable_linkage(node, source, &ancestry);
18682 }
18683 current = parent.parent();
18684 }
18685 }
18686 let mut cursor = node.walk();
18687 stack.extend(node.named_children(&mut cursor));
18688 }
18689 panic!("fixture has no member function definition");
18690 }
18691
18692 #[test]
18693 fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
18694 assert_eq!(
18695 member_function_linkage("struct Named { int method() { return 1; } };"),
18696 CallableLinkage::External
18697 );
18698 assert_eq!(
18699 member_function_linkage(
18700 "int outer() { struct Local { int method() { return 1; } }; return 0; }"
18701 ),
18702 CallableLinkage::Internal
18703 );
18704 assert_eq!(
18705 member_function_linkage("struct { int method() { return 1; } } instance;"),
18706 CallableLinkage::Internal
18707 );
18708 assert_eq!(
18709 member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
18710 CallableLinkage::Internal
18711 );
18712 }
18713
18714 #[test]
18715 fn malformed_class_macro_constructors_have_no_decorator_return_type() {
18716 let source = r#"
18717#ifndef PROTON_VALUE_HPP
18718#define PROTON_VALUE_HPP
18719namespace proton {
18720namespace internal {
18721class value_base {
18722 protected:
18723 internal::data& data();
18724 internal::data data_;
18725 friend class codec::encoder;
18726 friend class codec::decoder;
18727};
18728}
18729class value : public internal::value_base, private internal::comparable<value> {
18730 private:
18731 template<class T, class U=void> struct assignable :
18732 public std::enable_if<codec::is_encodable<T>::value, U> {};
18733 template<class U> struct assignable<value, U> {};
18734 public:
18735 PN_CPP_EXTERN value();
18736 PN_CPP_EXTERN value(const value&);
18737 PN_CPP_EXTERN value& operator=(const value&);
18738 PN_CPP_EXTERN value(value&&);
18739 PN_CPP_EXTERN value& operator=(value&&);
18740 template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
18741 template <class T> typename assignable<T, value&>::type operator=(const T& x) {
18742 codec::encoder e(*this);
18743 e << x;
18744 return *this;
18745 }
18746 PN_CPP_EXTERN type_id type() const;
18747 PN_CPP_EXTERN bool empty() const;
18748 PN_CPP_EXTERN void clear();
18749 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
18750 template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
18751 friend PN_CPP_EXTERN void swap(value&, value&);
18752 friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
18753 friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
18754 friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
18755 value(pn_data_t* d);
18756 void reset(pn_data_t* d = 0);
18757};
18758}
18759#endif
18760"#;
18761 let mut parser = tree_sitter::Parser::new();
18762 parser
18763 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18764 .unwrap();
18765 let tree = parser.parse(source, None).unwrap();
18766 let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
18767 let parsed = parse_cpp_file(&file, source, &tree);
18768 let macro_constructors = parsed
18769 .signature_metadata
18770 .iter()
18771 .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
18772 .flat_map(|(_, metadata)| metadata)
18773 .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
18774 .collect::<Vec<_>>();
18775
18776 assert_eq!(
18777 macro_constructors.len(),
18778 3,
18779 "fixture must retain the three macro-decorated constructor declarations: {:#?}",
18780 parsed.declarations()
18781 );
18782 assert!(
18783 macro_constructors.iter().all(|metadata| {
18784 metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
18785 }),
18786 "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
18787 );
18788 }
18789
18790 #[test]
18791 fn recovered_export_class_typedef_uses_displaced_alias_name() {
18792 let source = r#"
18793namespace spi {
18794class Filter {
18795public:
18796 enum FilterDecision { DENY, NEUTRAL, ACCEPT };
18797};
18798}
18799namespace filter {
18800class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
18801{
18802public:
18803 typedef spi::Filter BASE_CLASS;
18804 DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
18805 BEGIN_LOG4CXX_CAST_MAP()
18806 LOG4CXX_CAST_ENTRY(LevelRangeFilter)
18807 LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
18808 END_LOG4CXX_CAST_MAP()
18809 FilterDecision decide() const;
18810};
18811}
18812"#;
18813 let mut parser = tree_sitter::Parser::new();
18814 parser
18815 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18816 .unwrap();
18817 let tree = parser.parse(source, None).unwrap();
18818 let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
18819 let parsed = parse_cpp_file(&file, source, &tree);
18820 assert!(
18821 parsed.declarations().iter().any(|unit| {
18822 unit.is_class()
18823 && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
18824 && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
18825 }),
18826 "the displaced typedef alias must retain its declared name: {:#?}",
18827 parsed.declarations()
18828 );
18829 assert!(
18830 parsed
18831 .declarations()
18832 .iter()
18833 .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
18834 "the qualified underlying type must not become a false nested alias: {:#?}",
18835 parsed.declarations()
18836 );
18837 }
18838
18839 #[test]
18840 fn exported_single_base_recovery_uses_displaced_class_name() {
18841 let source = r#"
18842class CORE_EXPORT QgsPoint : public AbstractGeometry
18843{
18844 Q_GADGET
18845
18846 Q_PROPERTY( double x READ x WRITE setX )
18847 Q_PROPERTY( double y READ y WRITE setY )
18848 Q_PROPERTY( double z READ z WRITE setZ )
18849 Q_PROPERTY( double m READ m WRITE setM )
18850
18851 public:
18852#ifndef SIP_RUN
18853 QgsPoint(
18854 double x = std::numeric_limits<double>::quiet_NaN(),
18855 double y = std::numeric_limits<double>::quiet_NaN(),
18856 double z = std::numeric_limits<double>::quiet_NaN(),
18857 double m = std::numeric_limits<double>::quiet_NaN(),
18858 Qgis::WkbType wkbType = Qgis::WkbType::Unknown
18859 );
18860#else
18861 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 )];
18862 % MethodCode
18863 if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
18864 {
18865 int state;
18866 sipIsErr = 0;
18867 QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
18868 if ( !sipIsErr )
18869 {
18870 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
18871 }
18872 sipReleaseType( p, sipType_QgsPointXY, state );
18873 }
18874 else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
18875 {
18876 int state;
18877 sipIsErr = 0;
18878
18879 QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
18880 if ( !sipIsErr )
18881 {
18882 sipCpp = new sipQgsPoint( QgsPoint( *p ) );
18883 }
18884 sipReleaseType( p, sipType_QPointF, state );
18885 }
18886 else if (
18887 ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
18888 ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
18889 ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
18890 ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
18891 {
18892 double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
18893 double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
18894 double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
18895 double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
18896 Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
18897 sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
18898 }
18899 else // Invalid ctor arguments
18900 {
18901 PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
18902 sipIsErr = 1;
18903 }
18904 % End
18905#endif
18906
18907 explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
18908 explicit QgsPoint( QPointF p ) SIP_SKIP;
18909 explicit QgsPoint(
18910 Qgis::WkbType wkbType,
18911 double x = std::numeric_limits<double>::quiet_NaN(),
18912 double y = std::numeric_limits<double>::quiet_NaN(),
18913 double z = std::numeric_limits<double>::quiet_NaN(),
18914 double m = std::numeric_limits<double>::quiet_NaN()
18915 ) SIP_SKIP;
18916 explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
18917 explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
18918 explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
18919#ifndef SIP_RUN
18920 private:
18921 bool fuzzyHelper(
18922 double epsilon,
18923 const AbstractGeometry &other,
18924 bool is3DFlag,
18925 bool isMeasureFlag
18926 ) const
18927 {
18928 return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
18929 }
18930#endif
18931};
18932class Ordinary : public Base { public: Ordinary(); };
18933class API_EXPORT Plain { public: Plain(); };
18934class API_EXPORT : public Base {};
18935class
18936PN_CPP_CLASS_EXTERN Sender : public Link {
18937 Sender();
18938 struct impl;
18939 struct impl& get_impl() const;
18940};
18941class thread_ctx_t {};
18942class ctx_t ZMQ_FINAL : public thread_ctx_t {
18943 bool start();
18944};
18945"#;
18946 let mut parser = tree_sitter::Parser::new();
18947 parser
18948 .set_language(&tree_sitter_cpp::LANGUAGE.into())
18949 .unwrap();
18950 let tree = parser.parse(source, None).unwrap();
18951 let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
18952 let parsed = parse_cpp_file(&file, source, &tree);
18953 let declarations = parsed.declarations();
18954
18955 for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
18956 assert!(
18957 declarations
18958 .iter()
18959 .any(|unit| unit.is_class() && unit.fq_name() == expected),
18960 "missing recovered class {expected}: {declarations:#?}"
18961 );
18962 }
18963 let qgs_point = declarations
18964 .iter()
18965 .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
18966 .expect("recovered QgsPoint class");
18967 assert_eq!(
18968 parsed.raw_supertypes.get(qgs_point),
18969 Some(&vec!["AbstractGeometry".to_string()]),
18970 "single-base export recovery must retain its displaced base"
18971 );
18972 let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
18973 assert!(
18974 parsed
18975 .navigation_ranges
18976 .get(qgs_point)
18977 .is_some_and(|ranges| {
18978 !ranges.is_empty()
18979 && ranges.iter().all(|range| range.end_byte <= ordinary_start)
18980 }),
18981 "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
18982 parsed.navigation_ranges.get(qgs_point)
18983 );
18984 let sender = declarations
18985 .iter()
18986 .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
18987 .expect("recovered Sender class");
18988 assert_eq!(
18989 parsed.raw_supertypes.get(sender),
18990 Some(&vec!["Link".to_string()]),
18991 "post-declarator export recovery must retain its displaced base"
18992 );
18993 let recovered_member = declarations
18994 .iter()
18995 .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
18996 .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
18997 assert_eq!(
18998 parsed
18999 .signature_metadata
19000 .get(recovered_member)
19001 .and_then(|metadata| metadata.first())
19002 .and_then(SignatureMetadata::callable_linkage),
19003 Some(CallableLinkage::External),
19004 "a named recovered class's members have external linkage"
19005 );
19006 let ctx = declarations
19007 .iter()
19008 .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
19009 .expect("recovered ctx_t class");
19010 assert_eq!(
19011 parsed.raw_supertypes.get(ctx),
19012 Some(&vec!["thread_ctx_t".to_string()]),
19013 "postfix export-macro recovery must retain its displaced base"
19014 );
19015 assert!(
19016 declarations.iter().any(|unit| {
19017 unit.is_function()
19018 && unit.fq_name() == "QgsPoint.QgsPoint"
19019 && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
19020 }),
19021 "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
19022 );
19023 assert!(
19024 declarations.iter().all(|unit| {
19025 !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
19026 }),
19027 "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
19028 );
19029 }
19030
19031 #[test]
19032 fn function_like_export_macro_classes_keep_names_and_base_edges() {
19033 let source = r#"
19037namespace api {
19038class PROJECT_PUBLIC_API(2, 0) Prelude {
19039 public:
19040 Prelude();
19041};
19042class PROJECT_PUBLIC_API(2, 0) Base {
19043 public:
19044 Base(int value);
19045};
19046class PROJECT_PUBLIC_API(2, 0) Mixin {
19047 public:
19048 Mixin();
19049};
19050class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
19051 public:
19052 Adopted(int value);
19053};
19054class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
19055 public:
19056 Derived(int value);
19057};
19058class PROJECT_PUBLIC_API(2, 0) Solo final {
19059 public:
19060 Solo();
19061};
19062class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
19063 public:
19064 Blended(int value);
19065};
19066class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
19067 public:
19068 Woven(int value);
19069};
19070} // namespace api
19071"#;
19072 let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
19073 let declarations = parsed.declarations();
19074 let class_named = |name: &str| {
19075 declarations
19076 .iter()
19077 .find(|unit| unit.is_class() && unit.fq_name() == name)
19078 .unwrap_or_else(|| {
19079 panic!("missing function-like export macro class {name}: {declarations:#?}")
19080 })
19081 };
19082 let base = class_named("api.Base");
19083 class_named("api.Prelude");
19084 class_named("api.Mixin");
19085
19086 assert_eq!(
19087 parsed.raw_supertypes.get(class_named("api.Adopted")),
19088 Some(&vec!["Base".to_string()])
19089 );
19090 assert_eq!(
19091 parsed.raw_supertypes.get(class_named("api.Derived")),
19092 Some(&vec!["Base".to_string()])
19093 );
19094 assert_eq!(
19095 parsed.raw_supertypes.get(class_named("api.Solo")),
19096 None,
19097 "a final class without a base list must not invent a supertype"
19098 );
19099 assert_eq!(
19100 parsed.raw_supertypes.get(class_named("api.Blended")),
19101 Some(&vec!["Base".to_string(), "Mixin".to_string()])
19102 );
19103 assert_eq!(
19104 parsed.raw_supertypes.get(class_named("api.Woven")),
19105 Some(&vec!["Base".to_string(), "Mixin".to_string()])
19106 );
19107 assert!(
19108 declarations
19109 .iter()
19110 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19111 "the export macro must not become a declaration: {declarations:#?}"
19112 );
19113 assert!(
19114 declarations.iter().all(|unit| !matches!(
19115 unit.identifier(),
19116 "final" | "public" | "protected" | "private"
19117 )),
19118 "the head specifiers must not become declarations: {declarations:#?}"
19119 );
19120 assert!(
19121 parsed
19122 .navigation_ranges
19123 .get(base)
19124 .is_some_and(|ranges| !ranges.is_empty()),
19125 "the recovered base must retain a navigable declaration range"
19126 );
19127 }
19128
19129 #[test]
19130 fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
19131 let source = r#"
19137namespace api {
19138class PROJECT_PUBLIC_API(2, 0) Base {
19139 public:
19140 Base();
19141};
19142class PROJECT_PUBLIC_API(2, 0) Mixin {
19143 public:
19144 Mixin();
19145};
19146class PROJECT_PUBLIC_API(2, 0) Name {
19147 public:
19148 Name();
19149};
19150class PROJECT_PUBLIC_API(2, 0) X509_CA final {
19151 public:
19152 X509_CA();
19153};
19154class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
19155 public:
19156 HSS_LMS_KEY();
19157};
19158class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
19159 public:
19160 GOST_3410();
19161};
19162class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
19163 public:
19164 PKCS11_RSA();
19165};
19166class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
19167 public:
19168 Plain();
19169};
19170class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
19171 public:
19172 Decorated();
19173};
19174class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
19175 public:
19176 Layered();
19177};
19178} // namespace api
19179"#;
19180 let parsed = parse_cpp_declarations(source, "positional-export.hpp");
19181 let declarations = parsed.declarations();
19182 let class_named = |name: &str| {
19183 declarations
19184 .iter()
19185 .find(|unit| unit.is_class() && unit.fq_name() == name)
19186 .unwrap_or_else(|| {
19187 panic!("missing function-like export macro class {name}: {declarations:#?}")
19188 })
19189 };
19190 for (name, bases) in [
19191 ("api.Name", None),
19192 ("api.X509_CA", None),
19193 ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
19194 ("api.GOST_3410", Some(vec!["Base"])),
19195 ("api.PKCS11_RSA", None),
19196 ("api.Plain", None),
19197 ("api.Decorated", Some(vec!["Base"])),
19198 ("api.Layered", Some(vec!["Base", "Mixin"])),
19199 ] {
19200 let expected =
19201 bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
19202 assert_eq!(
19203 parsed.raw_supertypes.get(class_named(name)),
19204 expected.as_ref(),
19205 "{name}"
19206 );
19207 }
19208 assert!(
19209 declarations.iter().all(|unit| !matches!(
19210 unit.identifier(),
19211 "PROJECT_PUBLIC_API"
19212 | "OTHER_MACRO"
19213 | "FIRST_MACRO"
19214 | "SECOND_MACRO"
19215 | "final"
19216 | "public"
19217 )),
19218 "macros and head specifiers must not become declarations: {declarations:#?}"
19219 );
19220 }
19221
19222 #[test]
19223 fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
19224 let source = r#"
19230namespace api {
19231
19232/**
19233* Doc comment
19234*/
19235class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
19236 public:
19237 /**
19238 * Construct from a point.
19239 */
19240 VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
19241
19242#if defined(PROJECT_HAS_LEGACY_POINT)
19243 /**
19244 * Construct from a legacy point.
19245 */
19246 VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
19247#endif
19248
19249 std::string algo_name() const override;
19250
19251 AlgorithmIdentifier algorithm_identifier() const override;
19252};
19253
19254}
19255"#;
19256 let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
19257 let declarations = parsed.declarations();
19258 let class = declarations
19259 .iter()
19260 .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
19261 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19262 assert_eq!(
19263 parsed.raw_supertypes.get(class),
19264 Some(&vec!["BaseKey".to_string()]),
19265 "the virtual base is the class's base: {declarations:#?}"
19266 );
19267 for member in [
19268 "api.VirtualBased.algo_name",
19269 "api.VirtualBased.algorithm_identifier",
19270 ] {
19271 assert!(
19272 declarations
19273 .iter()
19274 .any(|unit| unit.is_function() && unit.fq_name() == member),
19275 "{member} must be owned by the recovered class: {declarations:#?}"
19276 );
19277 }
19278 assert!(
19279 declarations
19280 .iter()
19281 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19282 "an unrecovered head must not mint a macro-named class: {declarations:#?}"
19283 );
19284 }
19285
19286 #[test]
19287 fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
19288 let source = r#"
19295namespace api {
19296
19297DIAGNOSTIC_PUSH
19298DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
19299
19300class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
19301 public virtual api::Inner::Key {
19302 public:
19303 std::string algo_name() const override;
19304};
19305
19306DIAGNOSTIC_POP
19307
19308}
19309"#;
19310 let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
19311 let declarations = parsed.declarations();
19312 let class = declarations
19313 .iter()
19314 .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
19315 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19316 assert_eq!(
19317 parsed.raw_supertypes.get(class),
19318 Some(&vec![
19319 "api::Outer::Key".to_string(),
19320 "api::Inner::Key".to_string()
19321 ]),
19322 "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
19323 );
19324 assert!(
19325 declarations
19326 .iter()
19327 .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
19328 "the member is owned by the recovered class: {declarations:#?}"
19329 );
19330 assert!(
19331 declarations
19332 .iter()
19333 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19334 "the macro invocation must not mint a declaration: {declarations:#?}"
19335 );
19336 }
19337
19338 #[test]
19339 fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
19340 let fixture = |head: &str, name: &str| {
19343 format!(
19344 r#"
19345namespace api {{
19346class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
19347 public:
19348 /** Return a descriptive string. */
19349 const char* what() const noexcept override {{ return m_msg.c_str(); }}
19350
19351 /** Return the type of error. */
19352 virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
19353
19354 /** Return an associated error code. */
19355 virtual int error_code() const noexcept {{ return 0; }}
19356
19357 /** Avoid throwing the base directly. */
19358 explicit Exception(std::string_view msg);
19359
19360 /** Avoid throwing the base directly. */
19361 Exception(const char* prefix, std::string_view msg);
19362
19363 /** Avoid throwing the base directly. */
19364 Exception(std::string_view msg, const std::exception& e);
19365
19366 private:
19367 std::string m_msg;
19368}};
19369
19370class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
19371 public:
19372 explicit {name}(std::string_view msg);
19373
19374 explicit {name}(std::string_view msg, std::string_view where);
19375
19376 {name}(std::string_view msg, const std::exception& e);
19377
19378 ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
19379}};
19380}} // namespace api
19381"#
19382 )
19383 };
19384 for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
19385 let source = fixture(head, name);
19386 let mut parser = Parser::new();
19387 parser
19388 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19389 .expect("set C++ grammar");
19390 let tree = parser.parse(&source, None).expect("parse fixture");
19391 let mut embedded = Vec::new();
19392 let mut stack = vec![tree.root_node()];
19393 while let Some(node) = stack.pop() {
19394 embedded.extend(
19395 recover_embedded_function_like_export_classes(node, &source)
19396 .into_iter()
19397 .map(|recovered| (recovered.name, recovered.raw_supertypes)),
19398 );
19399 let mut cursor = node.walk();
19400 stack.extend(node.named_children(&mut cursor));
19401 }
19402 assert!(
19403 embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
19404 "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
19405 tree.root_node().to_sexp()
19406 );
19407 assert!(
19408 embedded
19409 .iter()
19410 .all(|(recovered, _)| recovered != "OTHER_MACRO"),
19411 "{head}: the object-like macro is not a class: {embedded:#?}"
19412 );
19413
19414 let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
19415 let declarations = parsed.declarations();
19416 let class = declarations
19417 .iter()
19418 .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
19419 .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
19420 assert_eq!(
19421 parsed.raw_supertypes.get(class),
19422 Some(&vec!["Exception".to_string()]),
19423 "{head}"
19424 );
19425 assert!(
19426 declarations
19427 .iter()
19428 .all(|unit| unit.identifier() != "OTHER_MACRO"),
19429 "{head}: the object-like macro must not become a declaration: {declarations:#?}"
19430 );
19431 }
19432 }
19433
19434 #[test]
19435 fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
19436 let source = r#"
19446namespace api {
19447class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
19448 public virtual Botan::EC_PublicKey {
19449 public:
19450 std::string algo_name() const override { return "ECDSA"; }
19451};
19452} // namespace api
19453"#;
19454 let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
19455 let declarations = parsed.declarations();
19456 assert!(
19457 declarations
19458 .iter()
19459 .all(|unit| !unit.identifier().is_empty()),
19460 "no declaration may carry an empty name: {declarations:#?}"
19461 );
19462 assert!(
19463 declarations
19464 .iter()
19465 .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
19466 "macros and head specifiers must not become declarations: {declarations:#?}"
19467 );
19468 let class = declarations
19469 .iter()
19470 .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
19471 .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19472 assert_eq!(
19473 parsed.raw_supertypes.get(class),
19474 Some(&vec![
19475 "Botan::TPM2::PublicKey".to_string(),
19476 "Botan::EC_PublicKey".to_string()
19477 ]),
19478 "both qualified virtual bases are bases: {declarations:#?}"
19479 );
19480 assert!(
19481 declarations
19482 .iter()
19483 .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19484 "the head must not mint a macro-named class: {declarations:#?}"
19485 );
19486 }
19487
19488 #[test]
19489 fn function_like_export_class_survives_a_preceding_malformed_body() {
19490 let source = r#"
19491namespace api {
19492class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
19493 public:
19494 /** Return a descriptive string. */
19495 const char* what() const noexcept override { return m_msg.c_str(); }
19496
19497 /** Return the type of error. */
19498 virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
19499
19500 /** Return an associated error code. */
19501 virtual int error_code() const noexcept { return 0; }
19502
19503 /** Avoid throwing the base directly. */
19504 explicit Exception(std::string_view msg);
19505
19506 /** Avoid throwing the base directly. */
19507 Exception(const char* prefix, std::string_view msg);
19508
19509 /** Avoid throwing the base directly. */
19510 Exception(std::string_view msg, const std::exception& e);
19511
19512 private:
19513 std::string m_msg;
19514};
19515
19516class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
19517 public:
19518 explicit Invalid_Argument(std::string_view msg);
19519
19520 explicit Invalid_Argument(std::string_view msg, std::string_view where);
19521
19522 Invalid_Argument(std::string_view msg, const std::exception& e);
19523
19524 ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
19525};
19526} // namespace api
19527"#;
19528 let mut parser = Parser::new();
19529 parser
19530 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19531 .expect("set C++ grammar");
19532 let tree = parser.parse(source, None).expect("parse fixture");
19533 let mut stack = vec![tree.root_node()];
19534 let mut saw_embedded_shape = false;
19535 while let Some(node) = stack.pop() {
19536 saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
19537 .iter()
19538 .any(|recovered| recovered.name == "Invalid_Argument");
19539 let mut cursor = node.walk();
19540 stack.extend(node.named_children(&mut cursor));
19541 }
19542 assert!(
19543 saw_embedded_shape,
19544 "fixture must retain the embedded error geometry: {}",
19545 tree.root_node().to_sexp()
19546 );
19547
19548 let parsed = parse_cpp_file(
19549 &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
19550 source,
19551 &tree,
19552 );
19553 let declarations = parsed.declarations();
19554 let exception = declarations
19555 .iter()
19556 .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
19557 .expect("qualified-base export class");
19558 let invalid = declarations
19559 .iter()
19560 .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
19561 .expect("class embedded in the preceding malformed body");
19562
19563 assert_eq!(
19564 parsed.raw_supertypes.get(exception),
19565 Some(&vec!["std::exception".to_string()])
19566 );
19567 assert_eq!(
19568 parsed.raw_supertypes.get(invalid),
19569 Some(&vec!["Exception".to_string()])
19570 );
19571 assert!(
19572 parsed.materialization_records.iter().any(|record| matches!(
19573 record,
19574 MaterializationRecord::RecoveredDeclaration { unit, .. }
19575 if unit == invalid
19576 )),
19577 "the embedded class must retain recovery provenance: {:#?}",
19578 parsed.materialization_records
19579 );
19580 }
19581
19582 #[test]
19583 fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
19584 let source = r#"
19585public:
19586 explicit Lookup_Error(std::string_view err) : Exception(err) {}
19587
19588 Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
19589"#;
19590 let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
19591 .expect("reparse merged constructor body");
19592 let (range, body) =
19593 cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
19594 .unwrap_or_else(|| {
19595 panic!(
19596 "the merged constructor must retain its structured declarator/body: {}",
19597 tree.root_node().to_sexp()
19598 )
19599 });
19600 assert_eq!(
19601 source.get(range).expect("constructor range"),
19602 "Lookup_Error(std::string_view err) : Exception(err) {}"
19603 );
19604 assert_eq!(node_text(body, source), "{}");
19605 }
19606
19607 #[test]
19608 fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
19609 let positive_source =
19610 "private:\n virtual ~XMLElement();\n XMLElement( const XMLElement& )\n ;\n";
19611 let mut parser = tree_sitter::Parser::new();
19612 parser
19613 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19614 .unwrap();
19615 let positive_tree = parser.parse(positive_source, None).unwrap();
19616 assert!(cpp_reparsed_members_are_indexable(
19617 positive_tree.root_node(),
19618 positive_source
19619 ));
19620
19621 let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
19622 let negative_tree = parser.parse(negative_source, None).unwrap();
19623 assert!(!cpp_reparsed_members_are_indexable(
19624 negative_tree.root_node(),
19625 negative_source
19626 ));
19627 }
19628
19629 #[test]
19630 fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
19631 let copy_control_source = r#"
19632public:
19633 Token(const TokenList& tokenlist, std::shared_ptr<State> state);
19634 explicit Token(const Token* tok);
19635 ~Token();
19636 Token* astOperand1() { return nullptr; }
19637"#;
19638 let constraint_source = r#"
19639private:
19640 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19641 static T *tokAtImpl(T *tok, int index) {
19642 return tok;
19643 }
19644
19645 template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19646 static T *linkAtImpl(T *tok, int index) {
19647 return tok;
19648 }
19649
19650public:
19651 int late() const { return 1; }
19652"#;
19653 let mut parser = tree_sitter::Parser::new();
19654 parser
19655 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19656 .unwrap();
19657 let copy_control_tree = parser
19658 .parse(copy_control_source, None)
19659 .expect("parse copy-control fixture");
19660 assert!(
19661 copy_control_tree.root_node().has_error(),
19662 "fixture must exercise adjacent copy-control recovery"
19663 );
19664 assert!(
19665 cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
19666 "a complete late getter must remain recoverable after adjacent copy-control declarations"
19667 );
19668 let mut cursor = copy_control_tree.root_node().walk();
19669 assert!(
19670 copy_control_tree
19671 .root_node()
19672 .named_children(&mut cursor)
19673 .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
19674 "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
19675 copy_control_tree.root_node().to_sexp()
19676 );
19677 let constraint_tree = parser
19678 .parse(constraint_source, None)
19679 .expect("parse constraint-macro fixture");
19680 assert!(constraint_tree.root_node().has_error());
19681 assert!(
19682 cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
19683 "complete constraint-macro members must not hide a later ordinary member"
19684 );
19685 let mut cursor = constraint_tree.root_node().walk();
19686 assert!(
19687 constraint_tree
19688 .root_node()
19689 .named_children(&mut cursor)
19690 .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
19691 child,
19692 constraint_source
19693 )),
19694 "fixture must retain the split constraint-macro prefix/function geometry"
19695 );
19696 }
19697
19698 #[test]
19699 fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
19700 let source = r#"
19701struct Analyzer {
19702 struct Action {
19703 Action() = default;
19704 Action(const Action&) = default;
19705 Action& operator=(const Action& rhs) & = default;
19706
19707 template<class T,
19708 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
19709 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
19710 // NOLINTNEXTLINE(google-explicit-constructor)
19711 Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
19712 {}
19713
19714 enum : std::uint16_t { None = 0, Read = (1 << 0) };
19715 bool get(unsigned int f) const { return ((mFlag & f) != 0); }
19716
19717 private:
19718 unsigned int mFlag{};
19719 };
19720
19721 enum class Direction : unsigned char { Forward, Reverse };
19722 virtual Action analyze(Direction d) const = 0;
19723};
19724"#;
19725 let mut parser = tree_sitter::Parser::new();
19726 parser
19727 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19728 .unwrap();
19729 let tree = parser.parse(source, None).unwrap();
19730 assert!(tree.root_node().has_error());
19731 let root = tree.root_node();
19732 let outer = root
19733 .named_children(&mut root.walk())
19734 .find(|child| child.kind() == "ERROR")
19735 .expect("fragmented Analyzer prefix");
19736 let outer_recovered =
19737 fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
19738 assert_eq!(outer_recovered.name, "Analyzer");
19739 let outer_tree = cpp_reparse_fragmented_class_body(
19740 source,
19741 outer_recovered.body.reparse_start,
19742 outer_recovered.body.reparse_end,
19743 )
19744 .expect("reparse Analyzer body");
19745 let outer_root = outer_tree.root_node();
19746 let action_prefix = outer_root
19747 .named_children(&mut outer_root.walk())
19748 .find(|child| child.kind() == "ERROR")
19749 .expect("fragmented Action prefix");
19750 let action_recovered = fragmented_class_body(action_prefix, source)
19751 .expect("structured Action fragment boundary");
19752 assert_eq!(action_recovered.name, "Action");
19753 let action_tree = cpp_reparse_fragmented_class_body(
19754 source,
19755 action_recovered.body.reparse_start,
19756 action_recovered.body.reparse_end,
19757 )
19758 .expect("reparse Action body");
19759 let action_root = action_tree.root_node();
19760 let macro_prefix = action_root
19761 .named_children(&mut action_root.walk())
19762 .find(|child| child.kind() == "ERROR")
19763 .expect("constraint macro prefix");
19764 let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
19765 .expect("structured template macro prefix");
19766 let macro_companion =
19767 cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
19768 assert!(
19769 cpp_reparsed_template_macro_constructor_companion_is_indexable(
19770 macro_companion,
19771 macro_parameter,
19772 source,
19773 ),
19774 "split constrained constructor must be admitted: {}",
19775 macro_companion.to_sexp()
19776 );
19777 assert!(
19778 cpp_reparsed_members_are_indexable(action_root, source),
19779 "complete Action body must pass the recovery gate: {}",
19780 action_tree.root_node().to_sexp()
19781 );
19782 assert!(
19783 cpp_reparsed_members_are_indexable(outer_root, source),
19784 "complete Analyzer body must pass the recovery gate: {}",
19785 outer_tree.root_node().to_sexp()
19786 );
19787 let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
19788 let parsed = parse_cpp_file(&file, source, &tree);
19789 for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
19790 assert!(
19791 parsed
19792 .declarations()
19793 .iter()
19794 .any(|unit| unit.fq_name() == expected),
19795 "missing recovered declaration {expected}: {:#?}",
19796 parsed.declarations()
19797 );
19798 }
19799 assert!(
19800 parsed
19801 .declarations()
19802 .iter()
19803 .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
19804 "nested members must not remain flattened: {:#?}",
19805 parsed.declarations()
19806 );
19807 }
19808
19809 #[test]
19810 fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
19811 let source = r#"
19812raw_hash_set& operator=(raw_hash_set&& that) {
19813 return move_assign(
19814 std::move(that),
19815 typename AllocTraits::propagate_on_container_move_assignment());
19816}
19817
19818iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
19819 return {};
19820}
19821
19822void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
19823
19824iterator insert(const_iterator hint, value_type&& value)
19825 ABSL_ATTRIBUTE_LIFETIME_BOUND {
19826 return {};
19827}
19828
19829friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
19830 return left.size() == right.size();
19831}
19832
19833static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
19834 return static_cast<slot_type*>(buffer);
19835}
19836
19837protected:
19838// Included-range recovery can attach this comment to the template prefix.
19839template <class K>
19840void AssertOnFind([[maybe_unused]] const K& key) {
19841 Check(key);
19842}
19843"#;
19844 let mut parser = tree_sitter::Parser::new();
19845 parser
19846 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19847 .unwrap();
19848 let tree = parser.parse(source, None).unwrap();
19849 assert!(
19850 tree.root_node().has_error(),
19851 "the fixture must exercise tree-sitter's errorful member shapes"
19852 );
19853 assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
19854
19855 let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
19856 let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
19857 assert!(!cpp_reparsed_members_are_indexable(
19858 incomplete_tree.root_node(),
19859 incomplete_source
19860 ));
19861
19862 let outside_error_source = "int foo() stray_attribute {}\n";
19863 let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
19864 assert!(outside_error_tree.root_node().has_error());
19865 assert!(!cpp_reparsed_members_are_indexable(
19866 outside_error_tree.root_node(),
19867 outside_error_source
19868 ));
19869
19870 let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
19871 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
19872 assert!(!cpp_reparsed_members_are_indexable(
19873 variable_initializer_tree.root_node(),
19874 variable_initializer_source
19875 ));
19876 }
19877
19878 #[test]
19879 fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
19880 let positive_source = r#"
19881std::pair<iterator, bool> insert(init_type&& value)
19882 ABSL_ATTRIBUTE_LIFETIME_BOUND
19883#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
19884 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
19885#endif
19886{
19887 return emplace(std::move(value));
19888}
19889"#;
19890 let mut parser = tree_sitter::Parser::new();
19891 parser
19892 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19893 .unwrap();
19894 let positive_tree = parser.parse(positive_source, None).unwrap();
19895 assert!(
19896 positive_tree.root_node().has_error(),
19897 "the fixture must exercise the split attribute/requires shape"
19898 );
19899 assert!(cpp_reparsed_members_are_indexable(
19900 positive_tree.root_node(),
19901 positive_source
19902 ));
19903
19904 let template_return_source = r#"
19905pair<int> insert(init_type&& value)
19906 ABSL_ATTRIBUTE_LIFETIME_BOUND
19907#if LANGUAGE_LEVEL >= 202002L
19908 requires(!Predicate<init_type>::value)
19909#endif
19910// Attributes and the function body may be separated by comments.
19911{
19912 return {};
19913}
19914"#;
19915 let template_return_tree = parser.parse(template_return_source, None).unwrap();
19916 assert!(
19917 cpp_reparsed_members_are_indexable(
19918 template_return_tree.root_node(),
19919 template_return_source
19920 ),
19921 "template-return attribute/requires tree: {}",
19922 template_return_tree.root_node().to_sexp()
19923 );
19924
19925 let no_body_source = r#"
19926std::pair<iterator, bool> insert(init_type&& value)
19927 ABSL_ATTRIBUTE_LIFETIME_BOUND
19928#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
19929 requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
19930#endif
19931+ 0;
19932"#;
19933 let no_body_tree = parser.parse(no_body_source, None).unwrap();
19934 assert!(!cpp_reparsed_members_are_indexable(
19935 no_body_tree.root_node(),
19936 no_body_source
19937 ));
19938
19939 let extra_payload_source = r#"
19940pair<int> insert(init_type&& value)
19941 ABSL_ATTRIBUTE_LIFETIME_BOUND
19942#if LANGUAGE_LEVEL >= 202002L
19943 int unrelated;
19944 requires(Predicate<init_type>::value)
19945#endif
19946{
19947 return {};
19948}
19949"#;
19950 let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
19951 assert!(!cpp_reparsed_members_are_indexable(
19952 extra_payload_tree.root_node(),
19953 extra_payload_source
19954 ));
19955
19956 let variable_initializer_source = r#"
19957int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
19958#if LANGUAGE_LEVEL >= 202002L
19959 requires(true)
19960#endif
19961{
19962 bad;
19963}
19964"#;
19965 let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
19966 assert!(!cpp_reparsed_members_are_indexable(
19967 variable_initializer_tree.root_node(),
19968 variable_initializer_source
19969 ));
19970 }
19971
19972 #[test]
19973 fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
19974 let source = r#"namespace absl {
19975ABSL_NAMESPACE_BEGIN namespace container_internal {
19976
19977class raw_hash_set : public Base {
19978 public:
19979 using value_type = int;
19980
19981 template <class U,
19982 REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
19983 void insert(U value) { (void)value; }
19984
19985 struct InsertSlot {
19986 raw_hash_set& s;
19987 };
19988};
19989
19990}
19991ABSL_NAMESPACE_END
19992}"#;
19993 let mut parser = tree_sitter::Parser::new();
19994 parser
19995 .set_language(&tree_sitter_cpp::LANGUAGE.into())
19996 .unwrap();
19997 let tree = parser.parse(source, None).unwrap();
19998 let root = tree.root_node();
19999 let outer_namespace = root
20000 .named_children(&mut root.walk())
20001 .find(|child| child.kind() == "namespace_definition")
20002 .expect("outer absl namespace");
20003 let declaration_list = outer_namespace
20004 .child_by_field_name("body")
20005 .expect("outer namespace body");
20006 let sentinel_function = declaration_list
20007 .named_children(&mut declaration_list.walk())
20008 .find(|child| child.kind() == "function_definition")
20009 .expect("malformed namespace sentinel function");
20010 let ancestry = ParentIndex::new(root);
20011 let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
20012 .expect("structured nested namespace sentinel");
20013 let fragmented =
20014 cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
20015 .expect("fragmented raw_hash_set class");
20016 assert_eq!(fragmented.class_node.kind(), "ERROR");
20017 assert_eq!(fragmented.name, "raw_hash_set");
20018 assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
20019
20020 let outer_scope =
20021 cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
20022 let mut outer_siblings = Vec::new();
20023 push_cpp_sentinel_sibling_classes(
20024 &mut outer_siblings,
20025 declaration_list,
20026 sentinel.function,
20027 &outer_scope,
20028 source,
20029 &ancestry,
20030 );
20031 let [outer_shadow] = outer_siblings.as_slice() else {
20032 panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
20033 };
20034 assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
20035 assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
20036
20037 let field = " raw_hash_set& s;";
20038 let start = source.find(field).expect("InsertSlot field") + 4;
20039 let node = root
20040 .descendant_for_byte_range(start, start + "raw_hash_set".len())
20041 .expect("raw_hash_set type node");
20042 let recovered = cpp_sentinel_recovered_classes(root, source);
20043 let [deep_class] = recovered.as_slice() else {
20044 panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
20045 };
20046 assert_eq!(
20047 deep_class.namespace_scope_components,
20048 vec!["absl", "container_internal"]
20049 );
20050 assert_eq!(
20051 deep_class.scope_components,
20052 vec!["absl", "container_internal", "raw_hash_set"]
20053 );
20054 assert!(
20055 deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
20056 && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
20057 );
20058
20059 assert_eq!(
20060 cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
20061 Some(vec![
20062 "absl".to_string(),
20063 "container_internal".to_string(),
20064 "raw_hash_set".to_string(),
20065 "InsertSlot".to_string(),
20066 ])
20067 );
20068
20069 let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
20070 let parsed = parse_cpp_file(&file, source, &tree);
20071 let raw_hash_set = parsed
20072 .declarations()
20073 .iter()
20074 .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
20075 .expect("recovered raw_hash_set class");
20076 assert_eq!(
20077 raw_hash_set.fq_name(),
20078 "absl::container_internal.raw_hash_set",
20079 "the recovered declaration must publish under the deeper sentinel namespace"
20080 );
20081 assert_eq!(
20082 parsed.raw_supertypes.get(raw_hash_set),
20083 Some(&vec!["Base".to_string()]),
20084 "the structured base clause on the fragmented ERROR prefix must survive publication"
20085 );
20086 assert!(
20087 parsed.materialization_records.iter().any(|record| matches!(
20088 record,
20089 MaterializationRecord::RecoveredDeclaration { recovery, unit }
20090 if unit == raw_hash_set && *recovery == deep_class.class_range
20091 )),
20092 "the reconstructed class must publish recovered-declaration provenance: {:#?}",
20093 parsed.materialization_records
20094 );
20095 }
20096
20097 #[test]
20124 fn the_parent_index_answers_what_tree_sitter_answers() {
20125 const SHAPES: [&str; 5] = [
20126 "namespace outer { namespace inner { struct Tag { int field; }; } }",
20127 "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
20128 "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n T get() const;\n};",
20129 "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
20130 "class API Broken : public First, public Second {\n void member();\n",
20131 ];
20132 for source in SHAPES {
20133 let mut parser = tree_sitter::Parser::new();
20134 parser
20135 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20136 .unwrap();
20137 let tree = parser.parse(source, None).unwrap();
20138 let root = tree.root_node();
20139 let ancestry = ParentIndex::new(root);
20140 let mut nodes = 0usize;
20141 let mut stack = vec![root];
20142 while let Some(node) = stack.pop() {
20143 nodes += 1;
20144 assert_eq!(
20145 node.parent().map(|parent| parent.id()),
20146 ancestry.parent(node).map(|parent| parent.id()),
20147 "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
20148 );
20149 let mut cursor = node.walk();
20150 stack.extend(node.children(&mut cursor));
20151 }
20152 assert!(nodes > 1, "{source:?} produced no tree to compare");
20153 }
20154 }
20155
20156 #[test]
20163 fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
20164 const DEPTH: usize = 64;
20165 let mut source = String::new();
20166 for level in 0..DEPTH {
20167 writeln!(source, "namespace n{level} {{").unwrap();
20168 }
20169 source.push_str("int deepest(int value);\n");
20170 for _ in 0..DEPTH {
20171 source.push_str("}\n");
20172 }
20173
20174 let mut parser = tree_sitter::Parser::new();
20175 parser
20176 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20177 .unwrap();
20178 let tree = parser.parse(&source, None).unwrap();
20179 let root = tree.root_node();
20180 let ancestry = ParentIndex::new(root);
20181 let mut function_declarator = None;
20182 walk_named_tree_preorder(root, true, |node| {
20183 if node.kind() == "function_declarator" {
20184 function_declarator = Some(node);
20185 WalkControl::Break
20186 } else {
20187 WalkControl::Continue
20188 }
20189 });
20190 let function_declarator = function_declarator.expect("deepest function declarator");
20191 let ancestor_count =
20192 std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
20193
20194 ancestry.reset_parent_query_count_for_test();
20195 let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
20196 assert_eq!(DEPTH, lexical_scope.len());
20197 assert_eq!(
20198 ancestor_count + 1,
20199 ancestry.parent_query_count_for_test(),
20200 "lexical-scope ancestry bypassed the parent index"
20201 );
20202
20203 ancestry.reset_parent_query_count_for_test();
20204 assert_eq!(
20205 DispatchExtensibility::Closed,
20206 cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
20207 );
20208 assert_eq!(
20209 ancestor_count,
20210 ancestry.parent_query_count_for_test(),
20211 "dispatch ancestry bypassed the parent index"
20212 );
20213
20214 ancestry.reset_parent_query_count_for_test();
20215 assert_eq!(
20216 CallableLinkage::External,
20217 cpp_callable_linkage(function_declarator, &source, &ancestry)
20218 );
20219 assert_eq!(
20220 ancestor_count + 1,
20221 ancestry.parent_query_count_for_test(),
20222 "linkage ancestry bypassed the parent index"
20223 );
20224
20225 ancestry.reset_parent_query_count_for_test();
20226 assert!(!cpp_callable_is_structural_constructor(
20227 function_declarator,
20228 &source,
20229 &ancestry
20230 ));
20231 assert_eq!(
20232 ancestor_count + 1,
20233 ancestry.parent_query_count_for_test(),
20234 "constructor ancestry bypassed the parent index"
20235 );
20236 }
20237
20238 #[test]
20243 fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
20244 for aggregates in [64usize, 512] {
20245 let mut source =
20246 String::from("typedef unsigned long long u64;\nnamespace generated {\n");
20247 for index in 0..aggregates {
20248 writeln!(source, "struct tag{index};").unwrap();
20249 }
20250 for index in (0..aggregates).rev() {
20251 writeln!(
20252 source,
20253 "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
20254 )
20255 .unwrap();
20256 }
20257 source.push_str("}\n");
20258
20259 start_code_unit_removal_scan_probe();
20260 let parsed = parse_cpp_declarations(&source, "vmlinux.h");
20261 let scanned = finish_code_unit_removal_scan_probe();
20262
20263 let expected_names: Vec<String> = (0..aggregates)
20264 .rev()
20265 .map(|index| format!("tag{index}"))
20266 .collect();
20267 let top_level_names: Vec<String> = parsed
20268 .top_level_declarations
20269 .iter()
20270 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20271 .map(|unit| unit.short_name().to_string())
20272 .collect();
20273 let namespace = parsed
20274 .declarations()
20275 .iter()
20276 .find(|unit| {
20277 unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
20278 })
20279 .expect("generated namespace should be declared");
20280 let child_names: Vec<String> = parsed.children[namespace]
20281 .iter()
20282 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20283 .map(|unit| unit.short_name().to_string())
20284 .collect();
20285 assert_eq!(
20286 aggregates,
20287 parsed
20288 .declarations()
20289 .iter()
20290 .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20291 .count(),
20292 "every aggregate must still be declared at {aggregates} aggregates"
20293 );
20294 assert_eq!(expected_names, top_level_names);
20295 assert_eq!(expected_names, child_names);
20296 assert_eq!(
20297 0, scanned,
20298 "replacing {aggregates} forward declarations must compact their shared lists once"
20299 );
20300 }
20301 }
20302
20303 #[test]
20304 fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
20305 const DISTINCT_PER_KIND: usize = 64;
20306 let mut source = String::new();
20307 for index in 0..DISTINCT_PER_KIND {
20308 writeln!(source, "typedef int Alias{index};").unwrap();
20309 }
20310 writeln!(source, "typedef long Alias0;").unwrap();
20311 for index in 0..DISTINCT_PER_KIND {
20312 writeln!(source, "#define MACRO_{index} {index}").unwrap();
20313 }
20314 writeln!(source, "#define MACRO_0 duplicate").unwrap();
20315 source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
20316
20317 let mut parser = tree_sitter::Parser::new();
20318 parser
20319 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20320 .unwrap();
20321 let tree = parser.parse(&source, None).unwrap();
20322 let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
20323
20324 start_declaration_identity_comparison_probe();
20325 let parsed = parse_cpp_file(&file, &source, &tree);
20326 let comparisons = finish_declaration_identity_comparison_probe();
20327
20328 assert_eq!(
20329 DISTINCT_PER_KIND + 1,
20330 parsed
20331 .declarations()
20332 .iter()
20333 .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
20334 .count(),
20335 "every physical typedef alias declaration must be retained so \
20336 conditional branch guards stay available to the resolver"
20337 );
20338 assert_eq!(
20339 DISTINCT_PER_KIND + 1,
20340 parsed
20341 .declarations()
20342 .iter()
20343 .filter(|unit| {
20344 unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
20345 })
20346 .count(),
20347 "distinct macro redefinitions must remain available to temporal lookup"
20348 );
20349 assert_eq!(
20350 2,
20351 parsed
20352 .declarations()
20353 .iter()
20354 .filter(|unit| {
20355 unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
20356 })
20357 .count(),
20358 "function overloads must remain distinct"
20359 );
20360
20361 let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
20362 assert!(
20363 comparisons <= dedup_inputs * 4,
20364 "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
20365 );
20366 }
20367
20368 #[test]
20369 fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
20370 let source = r#"namespace absl {
20371ABSL_NAMESPACE_BEGIN namespace container_internal {
20372template <typename T>
20373class broken {
20374 public:
20375 using value_type = T;
20376 T operator->() const { return &operator*(); }
20377 using alias = value_type;
20378};
20379}
20380}
20381"#;
20382 let mut parser = tree_sitter::Parser::new();
20383 parser
20384 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20385 .unwrap();
20386 let tree = parser.parse(source, None).unwrap();
20387 let broken = find_class_named(tree.root_node(), source, "broken")
20388 .expect("the positive fixture must expose the broken class node");
20389 assert!(
20390 broken.has_error(),
20391 "the positive fixture must retain an internal parser error"
20392 );
20393 assert!(
20394 cpp_complete_class_body_close(broken).is_some(),
20395 "the positive fixture must expose a real class body close"
20396 );
20397 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20398 assert!(
20399 recovered.iter().any(|class| {
20400 class.scope_components == ["absl", "container_internal", "broken"]
20401 }),
20402 "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
20403 );
20404 }
20405
20406 #[test]
20407 fn sentinel_recovery_keeps_members_after_nested_body_close() {
20408 let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
20409NLOHMANN_BASIC_JSON_TPL_DECLARATION
20410class basic_json {
20411 private:
20412 union storage {
20413 int value;
20414 } data;
20415 public:
20416 using late_alias = int;
20417 late_alias value() const;
20418};
20419NLOHMANN_JSON_NAMESPACE_END
20420"#;
20421 let mut parser = tree_sitter::Parser::new();
20422 parser
20423 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20424 .unwrap();
20425 let tree = parser.parse(source, None).unwrap();
20426 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20427 let basic_json = recovered
20428 .iter()
20429 .find(|class| {
20430 class
20431 .scope_components
20432 .last()
20433 .is_some_and(|name| name == "basic_json")
20434 })
20435 .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
20436 let late_alias = source
20437 .find("late_alias value")
20438 .expect("late alias reference");
20439 assert!(
20440 basic_json.class_range.start_byte < late_alias
20441 && late_alias < basic_json.class_range.end_byte,
20442 "the recovered class range must include members after a nested close: {basic_json:#?}"
20443 );
20444 }
20445
20446 #[test]
20447 fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
20448 let source = r#"namespace absl {
20449ABSL_NAMESPACE_BEGIN namespace container_internal {
20450template <typename T>
20451class broken {
20452 public:
20453 using value_type = T;
20454 T operator->() const { return &operator*(); }
20455}
20456}
20457"#;
20458 let mut parser = tree_sitter::Parser::new();
20459 parser
20460 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20461 .unwrap();
20462 let tree = parser.parse(source, None).unwrap();
20463 let broken = find_class_named(tree.root_node(), source, "broken")
20464 .expect("the negative fixture must expose the malformed class node");
20465 assert!(
20466 broken.has_error(),
20467 "the negative fixture must retain a parser error"
20468 );
20469 assert!(
20470 cpp_complete_class_body_close(broken).is_none(),
20471 "the malformed class must not expose a real body close"
20472 );
20473 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20474 assert!(
20475 recovered
20476 .iter()
20477 .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
20478 "an incomplete class must not borrow the namespace close: {recovered:#?}"
20479 );
20480 }
20481
20482 #[test]
20483 fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
20484 let source = r#"namespace absl {
20485ABSL_NAMESPACE_BEGIN namespace container_internal {
20486template <typename T>
20487struct broken {
20488 using value_type = T;
20489};
20490}
20491
20492#ifdef OWNER_DEF
20493template <typename T>
20494typename broken<T>::value_type broken<T>::method() {
20495 value_type value{};
20496 return value;
20497}
20498#endif
20499
20500namespace sibling {
20501template <typename T>
20502typename broken<T>::value_type broken<T>::other() {
20503 value_type value{};
20504 return value;
20505}
20506}
20507
20508ABSL_NAMESPACE_END
20509}
20510"#;
20511 let mut parser = tree_sitter::Parser::new();
20512 parser
20513 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20514 .unwrap();
20515 let tree = parser.parse(source, None).unwrap();
20516 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20517 let broken = recovered
20518 .iter()
20519 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20520 .expect("the sentinel class must be recovered");
20521 let method_start = source
20522 .find("typename broken<T>::value_type broken<T>::method()")
20523 .expect("guarded sibling owner");
20524 let method_end = source[method_start..]
20525 .find("\n}")
20526 .map(|offset| method_start + offset + 2)
20527 .expect("guarded sibling owner close");
20528 assert!(
20529 broken
20530 .owner_ranges
20531 .iter()
20532 .any(|owner| owner.range.start_byte <= method_start
20533 && method_end <= owner.range.end_byte),
20534 "guarded sibling owner must be attached to the recovered class: {broken:#?}"
20535 );
20536 let sibling_start = source
20537 .find("typename broken<T>::value_type broken<T>::other()")
20538 .expect("nested namespace sibling owner");
20539 assert!(
20540 broken
20541 .owner_ranges
20542 .iter()
20543 .all(|owner| owner.range.start_byte > sibling_start
20544 || owner.range.end_byte <= sibling_start),
20545 "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
20546 );
20547 }
20548
20549 #[test]
20550 fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
20551 let source = r#"#ifdef OUTER
20552namespace absl {
20553ABSL_NAMESPACE_BEGIN namespace container_internal {
20554template <typename T>
20555struct broken {
20556 using value_type = T;
20557};
20558}
20559}
20560
20561#ifdef OWNER_DEF
20562template <typename T>
20563typename broken<T>::value_type broken<T>::method() {
20564 value_type value{};
20565 return value;
20566}
20567#endif
20568#endif
20569"#;
20570 let mut parser = tree_sitter::Parser::new();
20571 parser
20572 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20573 .unwrap();
20574 let tree = parser.parse(source, None).unwrap();
20575 let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20576 let broken = recovered
20577 .iter()
20578 .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20579 .expect("the sentinel class must be recovered");
20580 let method_start = source
20581 .find("typename broken<T>::value_type broken<T>::method()")
20582 .expect("outer sibling owner");
20583 assert!(
20584 broken
20585 .owner_ranges
20586 .iter()
20587 .all(|owner| owner.range.start_byte > method_start
20588 || owner.range.end_byte <= method_start),
20589 "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
20590 );
20591 }
20592
20593 fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
20595 let mut signatures = parsed
20596 .declarations()
20597 .iter()
20598 .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
20599 .filter_map(|unit| unit.signature().map(str::to_string))
20600 .collect::<Vec<_>>();
20601 signatures.sort();
20602 signatures.dedup();
20603 signatures
20604 }
20605
20606 #[test]
20607 fn callable_parameter_types_come_from_the_ast_parameter_list() {
20608 let source = r#"
20609template <typename T, ENABLE_BYTES(T)>
20610Vec256<T> DupOdd(Vec256<T> value) { return value; }
20611
20612struct Visitor {
20613 void fail(this auto const& self) {}
20614};
20615"#;
20616 let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
20617 let dup_odd = parsed
20618 .declarations()
20619 .iter()
20620 .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
20621 .expect("DupOdd declaration");
20622 assert_eq!(
20623 dup_odd.signature(),
20624 Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
20625 );
20626 assert_eq!(
20627 parsed
20628 .signature_metadata
20629 .get(dup_odd)
20630 .and_then(|metadata| metadata.first())
20631 .and_then(SignatureMetadata::callable_parameter_types),
20632 Some(["Vec256<T>".to_string()].as_slice())
20633 );
20634
20635 let fail = parsed
20636 .declarations()
20637 .iter()
20638 .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
20639 .expect("explicit-object member");
20640 assert_eq!(fail.signature(), Some("(const this auto &)"));
20641 let metadata = parsed
20642 .signature_metadata
20643 .get(fail)
20644 .and_then(|metadata| metadata.first())
20645 .expect("explicit-object signature metadata");
20646 assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
20647 assert!(
20648 metadata
20649 .callable_arity()
20650 .is_some_and(|arity| arity.accepts(0))
20651 );
20652 }
20653
20654 #[test]
20655 fn trailing_qualifiers_survive_parameter_list_whitespace() {
20656 let source = r#"
20661struct Widget {
20662 bool multiline(int settings, int supprs) const;
20663 bool doublespace(int settings, int supprs) const;
20664 bool noexcept_multiline(int settings, int supprs) noexcept;
20665 bool ref_multiline(int settings, int supprs) &&;
20666};
20667bool
20668Widget::multiline (int settings,
20669 int supprs) const
20670{ return settings + supprs > 0; }
20671bool Widget::doublespace(int settings, int supprs) const { return true; }
20672bool Widget::noexcept_multiline(int settings,
20673 int supprs) noexcept { return true; }
20674bool Widget::ref_multiline(int settings,
20675 int supprs) && { return true; }
20676"#;
20677 let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
20678 assert_eq!(
20679 vec!["(int, int) const".to_string()],
20680 identity_signatures(&parsed, "Widget.multiline")
20681 );
20682 assert_eq!(
20683 vec!["(int, int) const".to_string()],
20684 identity_signatures(&parsed, "Widget.doublespace")
20685 );
20686 assert_eq!(
20687 vec!["(int, int) noexcept".to_string()],
20688 identity_signatures(&parsed, "Widget.noexcept_multiline")
20689 );
20690 assert_eq!(
20691 vec!["(int, int) &&".to_string()],
20692 identity_signatures(&parsed, "Widget.ref_multiline")
20693 );
20694 }
20695
20696 #[test]
20697 fn macro_fragmented_plain_class_keeps_following_member_signature() {
20698 let source = r#"
20699struct CString {};
20700class CMessage {
20701public:
20702 CString GetParams(unsigned int index, unsigned int length = -1) const
20703 ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
20704 return GetParamsColon(index, length);
20705 }
20706 CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
20707};
20708CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
20709 return {};
20710}
20711"#;
20712 let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
20713 assert_eq!(
20714 vec!["(unsigned int, unsigned int) const".to_string()],
20715 identity_signatures(&parsed, "CMessage.GetParamsColon")
20716 );
20717 }
20718
20719 #[test]
20720 fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
20721 let source = r#"
20722#pragma once
20723#define DEMO_DEPRECATED(message)
20724namespace demo {
20725struct Base {
20726 static int aligned(int value) { return value; }
20727 int legacy(int value) const
20728 DEMO_DEPRECATED("use replacement()") { return value; }
20729 int replacement() const;
20730 void run(int value);
20731};
20732struct OtherBase {
20733 void run(int value);
20734 static int aligned(int value) { return value; }
20735};
20736struct Derived : Base {};
20737struct Override : Base {
20738 void run(int value);
20739 static int aligned(int value) { return value; }
20740};
20741struct RecoveredOverride : Base {
20742 int legacy(int value) const
20743 DEMO_DEPRECATED("use replacement()") { return value; }
20744 void run(int value);
20745};
20746struct Hidden : Base {
20747 void run(int first, int second);
20748 static int aligned(int first, int second) { return first + second; }
20749};
20750struct Ambiguous : Base, OtherBase {};
20751}
20752struct Global {};
20753"#;
20754 let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
20755 let declarations = parsed.declarations();
20756 let fq_names = declarations
20757 .iter()
20758 .map(|unit| unit.fq_name())
20759 .collect::<std::collections::BTreeSet<_>>();
20760
20761 for expected in [
20762 "demo.Base",
20763 "demo.Base.aligned",
20764 "demo.Base.legacy",
20765 "demo.Base.replacement",
20766 "demo.Base.run",
20767 "demo.Derived",
20768 "demo.OtherBase",
20769 "demo.Override",
20770 "demo.RecoveredOverride",
20771 "demo.Hidden",
20772 "demo.Ambiguous",
20773 "Global",
20774 ] {
20775 assert!(
20776 fq_names.contains(expected),
20777 "missing {expected} from namespaced macro fragment: {declarations:#?}"
20778 );
20779 }
20780 assert!(
20781 !fq_names.contains("Derived"),
20782 "following class escaped its namespace: {declarations:#?}"
20783 );
20784 assert!(
20785 !fq_names.contains("demo.Global"),
20786 "global class crossed the recovered namespace boundary: {declarations:#?}"
20787 );
20788 }
20789
20790 #[test]
20791 fn trailing_qualifiers_still_separate_genuine_overloads() {
20792 let source = r#"
20795struct Widget {
20796 int* slot(int index);
20797 const int* slot(int index) const;
20798 int log(int severity) &;
20799 int log(int severity) &&;
20800};
20801"#;
20802 let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
20803 assert_eq!(
20804 vec!["(int)".to_string(), "(int) const".to_string()],
20805 identity_signatures(&parsed, "Widget.slot")
20806 );
20807 assert_eq!(
20808 vec!["(int) &".to_string(), "(int) &&".to_string()],
20809 identity_signatures(&parsed, "Widget.log")
20810 );
20811 }
20812
20813 #[test]
20814 fn virtual_specifier_is_not_part_of_the_identity_signature() {
20815 let source = r#"
20818struct Base {
20819 virtual void run(int value) const;
20820};
20821struct Widget : Base {
20822 void run(int value) const override;
20823};
20824void Widget::run(int value) const {}
20825"#;
20826 let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
20827 assert_eq!(
20828 vec!["(int) const".to_string()],
20829 identity_signatures(&parsed, "Widget.run")
20830 );
20831 }
20832
20833 #[test]
20834 fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
20835 let source = r#"
20839struct Widget {
20840 bool value_params(const int settings, const int supprs);
20841 void pointee_const(const int* p);
20842 void pointer_const(int* const p);
20843 void both_const(const int* const p);
20844 void reference_const(const int& p);
20845 void array_const(const int values[4]);
20846};
20847bool Widget::value_params(int settings, int supprs) { return true; }
20848void Widget::pointer_const(int* p) {}
20849void Widget::both_const(const int* p) {}
20850"#;
20851 let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
20852 assert_eq!(
20853 vec!["(int, int)".to_string()],
20854 identity_signatures(&parsed, "Widget.value_params")
20855 );
20856 assert_eq!(
20857 vec!["(int *)".to_string()],
20858 identity_signatures(&parsed, "Widget.pointer_const")
20859 );
20860 assert_eq!(
20861 vec!["(const int *)".to_string()],
20862 identity_signatures(&parsed, "Widget.both_const")
20863 );
20864 assert_eq!(
20866 vec!["(const int *)".to_string()],
20867 identity_signatures(&parsed, "Widget.pointee_const")
20868 );
20869 assert_eq!(
20870 vec!["(const int &)".to_string()],
20871 identity_signatures(&parsed, "Widget.reference_const")
20872 );
20873 assert_eq!(
20874 vec!["(const int [4])".to_string()],
20875 identity_signatures(&parsed, "Widget.array_const")
20876 );
20877 }
20878
20879 #[test]
20880 fn top_level_parameter_const_still_separates_pointee_overloads() {
20881 let source = r#"
20882struct Widget {
20883 void take(const int* p);
20884 void take(int* p);
20885};
20886"#;
20887 let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
20888 assert_eq!(
20889 vec!["(const int *)".to_string(), "(int *)".to_string()],
20890 identity_signatures(&parsed, "Widget.take")
20891 );
20892 }
20893
20894 fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
20895 let mut parser = tree_sitter::Parser::new();
20896 parser
20897 .set_language(&tree_sitter_cpp::LANGUAGE.into())
20898 .unwrap();
20899 let tree = parser.parse(source, None).unwrap();
20900 let start = source.find(callable_name).expect("callable declaration");
20901 let declarator =
20902 cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
20903 cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
20904 }
20905
20906 fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
20907 let mut shapes = comparable_shapes(source, callable_name);
20908 assert_eq!(1, shapes.len(), "{shapes:?}");
20909 match shapes.remove(0) {
20910 CppComparableSlot::Shape(shape) => shape,
20911 other => panic!("expected a comparable shape, got {other:?}"),
20912 }
20913 }
20914
20915 fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
20916 let mut current = shape.root();
20917 loop {
20918 match shape.node(current) {
20919 CppComparableNode::Named { .. } => return shape.node(current),
20920 CppComparableNode::Pointer { inner, .. }
20921 | CppComparableNode::Reference { inner }
20922 | CppComparableNode::Array { inner } => current = *inner,
20923 CppComparableNode::Generic { base, .. } => current = *base,
20924 }
20925 }
20926 }
20927
20928 #[test]
20929 fn comparable_shape_keeps_pointee_const() {
20930 assert_ne!(
20931 sole_comparable_shape("void f(const char* p);", "f("),
20932 sole_comparable_shape("void f(char* p);", "f(")
20933 );
20934 }
20935
20936 #[test]
20937 fn comparable_shape_keeps_inner_pointer_const() {
20938 assert_ne!(
20939 sole_comparable_shape("void f(int** p);", "f("),
20940 sole_comparable_shape("void f(int* const* p);", "f(")
20941 );
20942 }
20943
20944 #[test]
20945 fn comparable_shape_drops_top_level_pointer_const() {
20946 assert_eq!(
20947 sole_comparable_shape("void f(int* const p);", "f("),
20948 sole_comparable_shape("void f(int* p);", "f(")
20949 );
20950 }
20951
20952 #[test]
20953 fn comparable_shape_drops_top_level_base_const() {
20954 assert_eq!(
20955 sole_comparable_shape("void f(const int p);", "f("),
20956 sole_comparable_shape("void f(int p);", "f(")
20957 );
20958 }
20959
20960 #[test]
20961 fn comparable_shape_decays_top_level_array_to_pointer() {
20962 assert_eq!(
20963 sole_comparable_shape("void f(int a[3]);", "f("),
20964 sole_comparable_shape("void f(int* a);", "f(")
20965 );
20966 assert_eq!(
20967 sole_comparable_shape("void f(int* a[3]);", "f("),
20968 sole_comparable_shape("void f(int** a);", "f(")
20969 );
20970 }
20971
20972 #[test]
20973 fn comparable_shape_keeps_array_behind_pointer() {
20974 assert_ne!(
20975 sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
20976 sole_comparable_shape("struct S { void f(int** a); };", "f(")
20977 );
20978 }
20979
20980 #[test]
20981 fn comparable_shape_records_written_name_and_lexical_scope() {
20982 let declared =
20983 sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
20984 let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
20985 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
20986 panic!("named leaf");
20987 };
20988 assert_eq!(["Msg".to_string()].as_slice(), name.path());
20989 assert_eq!(
20990 ["ns".to_string(), "S".to_string()].as_slice(),
20991 name.lexical_scope()
20992 );
20993 let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
20994 panic!("named leaf");
20995 };
20996 assert_eq!(
20997 ["ns".to_string(), "Msg".to_string()].as_slice(),
20998 name.path()
20999 );
21000 assert!(name.lexical_scope().is_empty());
21001 assert_ne!(declared, defined);
21002 }
21003
21004 #[test]
21005 fn comparable_shape_marks_sized_primitive_leaf() {
21006 let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
21007 let CppComparableNode::Named {
21008 name, primitive, ..
21009 } = comparable_named_leaf(&shape)
21010 else {
21011 panic!("named leaf");
21012 };
21013 assert!(primitive);
21014 assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
21015 assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
21016 }
21017
21018 #[test]
21019 fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
21020 assert_eq!(
21021 vec![CppComparableSlot::Unstructured],
21022 comparable_shapes("void f(void (*cb)(int));", "f(")
21023 );
21024 }
21025
21026 #[test]
21027 fn comparable_shape_reports_ellipsis_slot() {
21028 let shapes = comparable_shapes("void f(int a, ...);", "f(");
21029 assert_eq!(2, shapes.len(), "{shapes:?}");
21030 assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
21031 }
21032
21033 #[test]
21034 fn comparable_shape_keeps_template_argument_const() {
21035 assert_ne!(
21036 sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
21037 sole_comparable_shape("void f(std::vector<int*> v);", "f(")
21038 );
21039 }
21040
21041 #[test]
21044 fn c_file_mints_aggregate_member_tag_at_file_scope() {
21045 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
21046 let parsed = parse_cpp_declarations(source, "x.c");
21047 let declarations = parsed.declarations();
21048
21049 assert!(
21050 declarations
21051 .iter()
21052 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21053 "expected a file-scope inner tag, got {declarations:?}"
21054 );
21055 assert!(
21056 declarations
21057 .iter()
21058 .all(|unit| unit.fq_name() != "outer$inner"),
21059 "expected no nested identity, got {declarations:?}"
21060 );
21061 assert!(
21062 declarations
21063 .iter()
21064 .any(|unit| unit.is_class() && unit.fq_name() == "outer")
21065 );
21066 assert!(
21068 declarations
21069 .iter()
21070 .any(|unit| unit.fq_name() == "inner.value")
21071 );
21072 assert!(
21073 declarations
21074 .iter()
21075 .any(|unit| unit.fq_name() == "outer.item")
21076 );
21077
21078 let outer = declarations
21079 .iter()
21080 .find(|unit| unit.is_class() && unit.fq_name() == "outer")
21081 .expect("outer");
21082 assert!(
21083 parsed
21084 .children
21085 .get(outer)
21086 .into_iter()
21087 .flatten()
21088 .all(|child| child.fq_name() != "inner"),
21089 "the tag must not hang off the aggregate it is written inside: {:?}",
21090 parsed.children
21091 );
21092 }
21093
21094 #[test]
21098 fn header_and_cpp_files_keep_nested_tag_identity() {
21099 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
21100 for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
21101 let parsed = parse_cpp_declarations(source, name);
21102 let declarations = parsed.declarations();
21103 assert!(
21104 declarations
21105 .iter()
21106 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21107 "{name} must keep the nested identity, got {declarations:?}"
21108 );
21109 assert!(
21110 declarations.iter().all(|unit| unit.fq_name() != "inner"),
21111 "{name} must not mint a file-scope tag, got {declarations:?}"
21112 );
21113 assert!(
21114 declarations
21115 .iter()
21116 .any(|unit| unit.fq_name() == "outer$inner.value")
21117 );
21118 }
21119 }
21120
21121 #[test]
21123 fn uppercase_c_extension_keeps_cpp_tag_scope() {
21124 let source = "struct outer {\n struct inner { int value; } item;\n};\n";
21125 let parsed = parse_cpp_declarations(source, "x.C");
21126 assert!(
21127 parsed
21128 .declarations()
21129 .iter()
21130 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
21131 );
21132 }
21133
21134 #[test]
21137 fn c_file_mints_every_nesting_level_at_file_scope() {
21138 let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
21139 let parsed = parse_cpp_declarations(source, "z.c");
21140 let declarations = parsed.declarations();
21141
21142 for tag in ["a", "b", "c"] {
21143 assert!(
21144 declarations
21145 .iter()
21146 .any(|unit| unit.is_class() && unit.fq_name() == tag),
21147 "expected a file-scope {tag}, got {declarations:?}"
21148 );
21149 }
21150 assert!(
21151 declarations
21152 .iter()
21153 .all(|unit| !unit.fq_name().contains('$')),
21154 "no level may keep a nested identity, got {declarations:?}"
21155 );
21156 assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
21158 assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
21159 assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
21160 }
21161
21162 #[test]
21165 fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
21166 let source = "struct outer { enum color { RED, GREEN } c; };\n";
21167 let parsed = parse_cpp_declarations(source, "e.c");
21168 let declarations = parsed.declarations();
21169
21170 let color = declarations
21171 .iter()
21172 .find(|unit| unit.is_class() && unit.fq_name() == "color")
21173 .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
21174 assert!(
21175 declarations
21176 .iter()
21177 .all(|unit| unit.fq_name() != "outer$color")
21178 );
21179 for enumerator in ["color.RED", "color.GREEN"] {
21180 assert!(
21181 declarations.iter().any(|unit| unit.fq_name() == enumerator),
21182 "expected {enumerator}, got {declarations:?}"
21183 );
21184 }
21185 let children = parsed
21186 .children
21187 .get(color)
21188 .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
21189 assert!(
21190 ["color.RED", "color.GREEN"]
21191 .iter()
21192 .all(|name| children.iter().any(|child| child.fq_name() == *name)),
21193 "enumerators must hang off their enum: {children:?}"
21194 );
21195 }
21196
21197 #[test]
21198 fn c_file_mints_member_list_union_at_file_scope() {
21199 let source = "struct outer { union inner { int a; float b; } item; };\n";
21200 let parsed = parse_cpp_declarations(source, "u.c");
21201 let declarations = parsed.declarations();
21202 assert!(
21203 declarations
21204 .iter()
21205 .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21206 "expected a file-scope inner union, got {declarations:?}"
21207 );
21208 assert!(
21209 declarations
21210 .iter()
21211 .all(|unit| unit.fq_name() != "outer$inner")
21212 );
21213 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
21214 assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
21215 }
21216
21217 #[test]
21220 fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
21221 let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
21222 let parsed = parse_cpp_declarations(source, "n.c");
21223 let declarations = parsed.declarations();
21224 let inner = declarations
21225 .iter()
21226 .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
21227 .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
21228 assert_eq!(inner.package_name(), "ns");
21229 assert!(
21230 declarations
21231 .iter()
21232 .all(|unit| unit.fq_name() != "ns.outer$inner")
21233 );
21234 }
21235
21236 #[test]
21241 fn function_local_tags_are_unchanged_in_both_dialects() {
21242 let source =
21243 "void run(void) {\n struct localtag { struct deeper { int v; } d; } item;\n}\n";
21244 for name in ["y.c", "y.cpp"] {
21245 let parsed = parse_cpp_declarations(source, name);
21246 let declarations = parsed.declarations();
21247 assert!(
21248 declarations
21249 .iter()
21250 .any(|unit| unit.is_function() && unit.fq_name() == "run"),
21251 "{name}: {declarations:?}"
21252 );
21253 for tag in ["localtag", "deeper", "localtag$deeper"] {
21254 assert!(
21255 declarations.iter().all(|unit| unit.fq_name() != tag),
21256 "{name} must not mint {tag}, got {declarations:?}"
21257 );
21258 }
21259 }
21260 }
21261
21262 #[test]
21265 fn anonymous_typedef_struct_is_identical_in_both_dialects() {
21266 let source = "typedef struct { int v; } T;\n";
21267 for name in ["t.c", "t.cpp"] {
21268 let parsed = parse_cpp_declarations(source, name);
21269 let declarations = parsed.declarations();
21270 assert!(
21271 declarations
21272 .iter()
21273 .any(|unit| unit.is_class() && unit.fq_name() == "T"),
21274 "{name}: {declarations:?}"
21275 );
21276 }
21277 }
21278
21279 #[test]
21280 fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
21281 let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
21282 let parsed = parse_cpp_declarations(source, "socket.c");
21283 let declarations = parsed.declarations();
21284 assert_eq!(
21285 declarations
21286 .iter()
21287 .filter(|unit| unit.fq_name() == "PAL_HANDLE")
21288 .count(),
21289 1,
21290 "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
21291 );
21292 for expected in [
21293 "PAL_HANDLE",
21294 "PAL_HANDLE.sock",
21295 "PAL_HANDLE$sock",
21296 "PAL_HANDLE$sock.ops",
21297 ] {
21298 assert!(
21299 declarations.iter().any(|unit| unit.fq_name() == expected),
21300 "expected {expected}, got {declarations:?}"
21301 );
21302 }
21303 }
21304
21305 #[test]
21308 fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
21309 let source = "class outer { class inner { int v; }; };\n";
21310 let c_parsed = parse_cpp_declarations(source, "k.c");
21311 let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
21312 let c_declarations = c_parsed.declarations();
21313 let cpp_declarations = cpp_parsed.declarations();
21314 assert!(
21315 c_declarations
21316 .iter()
21317 .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21318 "{c_declarations:?}"
21319 );
21320 assert_eq!(
21321 c_declarations
21322 .iter()
21323 .map(|unit| unit.fq_name())
21324 .collect::<std::collections::BTreeSet<_>>(),
21325 cpp_declarations
21326 .iter()
21327 .map(|unit| unit.fq_name())
21328 .collect::<std::collections::BTreeSet<_>>()
21329 );
21330 }
21331
21332 fn namespace_forward_scan_agreement(source: &str) -> usize {
21346 let mut parser = tree_sitter::Parser::new();
21347 parser
21348 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21349 .unwrap();
21350 let tree = parser.parse(source, None).unwrap();
21351 let root = tree.root_node();
21352 let ancestry = ParentIndex::new(root);
21353
21354 let mut nodes = Vec::new();
21355 let mut names = std::collections::BTreeSet::new();
21356 let mut cursor = root.walk();
21357 let mut stack = vec![root];
21358 while let Some(node) = stack.pop() {
21359 if matches!(
21360 node.kind(),
21361 "class_specifier" | "struct_specifier" | "union_specifier"
21362 ) && let Some(name) = class_like_name(node, source, &ancestry)
21363 {
21364 names.insert(name);
21365 }
21366 nodes.push(node);
21367 stack.extend(node.named_children(&mut cursor));
21368 }
21369 nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
21370 assert!(!names.is_empty(), "fixture declares no class-like name");
21371
21372 let mut answered = 0usize;
21373 for reversed in [false, true] {
21374 let mut scan = CppNamespaceForwardScan::default();
21375 let ordered: Vec<_> = if reversed {
21376 nodes.iter().rev().copied().collect()
21377 } else {
21378 nodes.clone()
21379 };
21380 answered = 0;
21381 for node in ordered {
21382 for name in &names {
21383 scan.advance_to(root, node.start_byte(), source, &ancestry);
21384 let carried = scan.unique_earlier_forward(name, node);
21385 assert_eq!(
21386 carried,
21387 unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
21388 "carried-forward scan and prefix scan disagree about {name} at \
21389 {} node starting at byte {} (reversed order: {reversed})",
21390 node.kind(),
21391 node.start_byte()
21392 );
21393 answered += usize::from(carried.is_some());
21394 }
21395 }
21396 }
21397 answered
21398 }
21399
21400 const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
21407namespace ns {
21408class Widget;
21409class Gadget;
21410int x = ;
21411}
21412class API Widget {
21413public:
21414 void first();
21415};
21416class API Gadget {
21417public:
21418 void second();
21419};
21420"#;
21421
21422 #[test]
21423 fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
21424 assert!(
21425 namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
21426 "the fixture must actually reach the namespace-borrow path"
21427 );
21428
21429 for source in [
21434 "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n void method();\n};\n",
21435 r#"#define API
21436namespace ns {
21437class Widget;
21438class Widget;
21439int x = ;
21440}
21441class API Widget {
21442public:
21443 void method();
21444};
21445"#,
21446 r#"#define API
21447namespace ns {
21448void host() {
21449 class Widget;
21450}
21451int x = ;
21452}
21453class API Widget {
21454public:
21455 void method();
21456};
21457"#,
21458 ] {
21459 assert_eq!(
21460 namespace_forward_scan_agreement(source),
21461 0,
21462 "no borrow is justified here: {source}"
21463 );
21464 }
21465 }
21466
21467 #[test]
21471 fn carried_forward_namespace_scan_folds_each_node_once() {
21472 let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
21473 let mut parser = tree_sitter::Parser::new();
21474 parser
21475 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21476 .unwrap();
21477 let tree = parser.parse(source, None).unwrap();
21478 let root = tree.root_node();
21479 let ancestry = ParentIndex::new(root);
21480
21481 let mut incremental = CppNamespaceForwardScan::default();
21482 for cutoff in 0..=source.len() {
21483 incremental.advance_to(root, cutoff, source, &ancestry);
21484 }
21485 let mut whole = CppNamespaceForwardScan::default();
21486 whole.advance_to(root, source.len(), source, &ancestry);
21487
21488 let mut incremental_shape: Vec<_> = incremental
21489 .forwards
21490 .iter()
21491 .map(|(name, forwards)| {
21492 (
21493 name.clone(),
21494 forwards
21495 .iter()
21496 .map(|forward| (forward.start_byte, forward.package_name.clone()))
21497 .collect::<Vec<_>>(),
21498 )
21499 })
21500 .collect();
21501 let mut whole_shape: Vec<_> = whole
21502 .forwards
21503 .iter()
21504 .map(|(name, forwards)| {
21505 (
21506 name.clone(),
21507 forwards
21508 .iter()
21509 .map(|forward| (forward.start_byte, forward.package_name.clone()))
21510 .collect::<Vec<_>>(),
21511 )
21512 })
21513 .collect();
21514 incremental_shape.sort();
21515 whole_shape.sort();
21516 for (_, forwards) in &mut incremental_shape {
21517 forwards.sort();
21518 }
21519 for (_, forwards) in &mut whole_shape {
21520 forwards.sort();
21521 }
21522
21523 assert!(!whole_shape.is_empty(), "fixture folds no forward");
21524 assert_eq!(
21525 incremental_shape, whole_shape,
21526 "one byte at a time must fold exactly what one whole pass folds"
21527 );
21528 }
21529
21530 fn fragmented_class_reparse_agreement(body: &str) {
21540 for prefix in [
21541 String::new(),
21542 "// leading comment\n".to_string(),
21543 "class Widget : public Base { ".to_string(),
21548 "namespace filler {\n".to_string()
21549 + &"struct Filler { int member; };\n".repeat(200)
21550 + "}\n",
21551 "namespace filler {\n".to_string()
21552 + &"struct Filler { int member; };\n".repeat(200)
21553 + "}\nclass Widget : public Base { ",
21554 ] {
21555 let source = format!("{prefix}{body}");
21556 let start = prefix.len();
21557 let end = source.len();
21558 let region = cpp_reparse_fragmented_class_body(&source, start, end)
21559 .expect("the region reparse must produce a tree");
21560 let padded = cpp_reparse_padded_class_body(&source, start, end)
21561 .expect("the padded reparse must produce a tree");
21562 assert_eq!(
21563 cpp_tree_shape(®ion),
21564 cpp_tree_shape(&padded),
21565 "region and padded reparse disagree at offset {start} of {end} bytes"
21566 );
21567 assert_eq!(
21568 region.root_node().start_byte(),
21569 start,
21570 "the reparsed region keeps its original offsets"
21571 );
21572 }
21573 }
21574
21575 #[test]
21576 fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
21577 fragmented_class_reparse_agreement(
21581 "public:\n#ifdef HAS_FEATURE\n Widget(int value);\n#endif\n void method();\n",
21582 );
21583 fragmented_class_reparse_agreement(
21584 "public:\n#if defined(A) || defined(B)\n Widget();\n#else\n Widget(int);\n#endif\n",
21585 );
21586 fragmented_class_reparse_agreement(
21589 "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n Lookup_Error(std::string_view type, std::string_view algo);\n",
21590 );
21591 fragmented_class_reparse_agreement(
21592 "public:\n void first();\nclass Action {\npublic:\n void second();\n",
21593 );
21594 fragmented_class_reparse_agreement("public:\n value + other;\n return value;\n");
21597 }
21598
21599 fn many_enums_and_mixed_declarations() -> String {
21603 let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
21604 for index in 0..40 {
21605 let _ = write!(
21606 source,
21607 "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
21608 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
21609 class Color{index}Like {{ public: int member{index}; }};\n"
21610 );
21611 }
21612 source.push_str("namespace outer {\n");
21613 for index in 0..20 {
21614 let _ = write!(
21615 source,
21616 "enum Shade{index} {{ DARK{index} }};\n\
21617 struct Shade{index}Holder {{ int field{index}; }};\n"
21618 );
21619 }
21620 source.push_str("}\n");
21621 source
21622 }
21623
21624 fn field_owner_index_agreement(source: &str, name: &str) -> usize {
21640 let parsed = parse_cpp_declarations(source, name);
21641 let file = ProjectFile::new(std::env::temp_dir(), name);
21642 let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
21643
21644 let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
21645 declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
21646
21647 let foreign: Vec<CodeUnit> = declarations
21648 .iter()
21649 .filter(|unit| unit.kind() == CodeUnitType::Field)
21650 .map(|unit| {
21651 CodeUnit::new_fq(
21652 elsewhere.clone(),
21653 unit.kind(),
21654 unit.package_name().to_string(),
21655 unit.short_name().to_string(),
21656 unit.fq().clone(),
21657 )
21658 })
21659 .collect();
21660
21661 let mut packages: Vec<String> = declarations
21667 .iter()
21668 .map(|unit| unit.package_name().to_string())
21669 .collect();
21670 packages.push(String::new());
21671 packages.sort();
21672 packages.dedup();
21673 let deeper: Vec<CodeUnit> = packages
21674 .iter()
21675 .map(|package_name| {
21676 CodeUnit::new_fq(
21677 file.clone(),
21678 CodeUnitType::Field,
21679 package_name.clone(),
21680 "SynthOwner.middle.leaf".to_string(),
21681 cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
21682 )
21683 })
21684 .collect();
21685
21686 let mut questions: Vec<(String, String)> = Vec::new();
21690 for unit in declarations.iter().chain(deeper.iter()) {
21691 let package_name = unit.package_name().to_string();
21692 let short_name = unit.short_name();
21693 questions.push((package_name.clone(), short_name.to_string()));
21694 questions.push((package_name.clone(), String::new()));
21695 for (offset, _) in short_name.match_indices('.') {
21696 questions.push((package_name.clone(), short_name[..offset].to_string()));
21697 }
21698 }
21699 questions.sort();
21700 questions.dedup();
21701
21702 let mut index = CppFieldOwnerIndex::default();
21703 let mut recorded: Vec<&CodeUnit> = Vec::new();
21704 let mut answered = 0usize;
21705 for unit in foreign
21706 .iter()
21707 .chain(declarations.iter())
21708 .chain(deeper.iter())
21709 {
21710 index.record(unit, &file);
21711 recorded.push(unit);
21712 for (package_name, owner_short_name) in &questions {
21713 let carried = index.owns_fields(package_name, owner_short_name);
21714 assert_eq!(
21715 carried,
21716 cpp_declarations_hold_owned_fields(
21717 recorded.iter().copied(),
21718 &file,
21719 package_name,
21720 owner_short_name
21721 ),
21722 "the carried field index and the declaration scan disagree about \
21723 {package_name:?}/{owner_short_name:?} after recording {}",
21724 unit.fq_name()
21725 );
21726 answered += usize::from(carried);
21727 }
21728 }
21729
21730 let rebuilt = CppFieldOwnerIndex::of(
21733 foreign
21734 .iter()
21735 .chain(declarations.iter())
21736 .chain(deeper.iter()),
21737 &file,
21738 );
21739 for (package_name, owner_short_name) in &questions {
21740 assert_eq!(
21741 rebuilt.owns_fields(package_name, owner_short_name),
21742 index.owns_fields(package_name, owner_short_name),
21743 "a rebuilt index must answer what the incremental one answers for \
21744 {package_name:?}/{owner_short_name:?}"
21745 );
21746 }
21747 answered
21748 }
21749
21750 #[test]
21759 fn a_replacement_that_removes_children_drops_the_field_index() {
21760 let source =
21761 "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
21762 let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
21763 let mut names: Vec<_> = parsed
21764 .declarations()
21765 .iter()
21766 .map(|unit| unit.fq_name())
21767 .collect();
21768 names.sort();
21769 assert_eq!(
21770 names,
21771 vec![
21772 "Color".to_string(),
21773 "First".to_string(),
21774 "First.A".to_string()
21775 ],
21776 "the replaced Color owns no field any more"
21777 );
21778 }
21779
21780 #[test]
21789 fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
21790 let source = "namespace demo { struct Widget { void doWork(); }; }\n\
21791 BEGIN_NS\n\
21792 namespace demo { struct Widget { void doWork(); }; }\n\
21793 END_NS\n";
21794 let parsed = parse_cpp_declarations(source, "restored.cpp");
21795 let recovered: Vec<String> = parsed
21796 .materialization_records
21797 .iter()
21798 .filter_map(|record| match record {
21799 MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
21800 _ => None,
21801 })
21802 .collect();
21803 assert!(
21804 recovered.is_empty(),
21805 "the region declares nothing the file did not already declare: {recovered:?}"
21806 );
21807 let mut names: Vec<String> = parsed
21808 .declarations()
21809 .iter()
21810 .map(|unit| unit.fq_name())
21811 .collect();
21812 names.sort();
21813 assert_eq!(
21814 names,
21815 vec![
21816 "demo".to_string(),
21817 "demo.Widget".to_string(),
21818 "demo.Widget.doWork".to_string(),
21819 ]
21820 );
21821 }
21822
21823 #[test]
21828 fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
21829 let mut source = String::new();
21830 for index in 0..4 {
21831 let _ = write!(
21832 source,
21833 "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index} {{ void doWork{index}(); }}; }}\nEND_NS\n"
21834 );
21835 }
21836 source.push_str("void outside() {}\n");
21837 let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
21838
21839 let recovered: Vec<(String, (usize, usize))> = parsed
21840 .materialization_records
21841 .iter()
21842 .filter_map(|record| match record {
21843 MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
21844 Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
21845 }
21846 _ => None,
21847 })
21848 .collect();
21849
21850 let mut expected: Vec<(String, (usize, usize))> = Vec::new();
21851 for index in 0..4 {
21852 let region = format!("namespace demo{index}");
21855 let region_start = source.find(®ion).expect("each region is in the source");
21856 let start = source[..region_start]
21857 .rfind("BEGIN_NS")
21858 .expect("each region opens with a sentinel")
21859 + "BEGIN_NS".len();
21860 let end = start
21861 + source[start..]
21862 .find("END_NS")
21863 .expect("each region closes with a sentinel")
21864 - 1;
21865 let window = (start, end);
21866 for name in [
21867 format!("demo{index}"),
21868 format!("demo{index}.Widget{index}"),
21869 format!("demo{index}.Widget{index}.doWork{index}"),
21870 ] {
21871 expected.push((name, window));
21872 }
21873 }
21874 assert_eq!(
21875 recovered, expected,
21876 "each recovery records its own minted declarations, in order"
21877 );
21878 assert!(
21879 parsed
21880 .declarations()
21881 .iter()
21882 .any(|unit| unit.fq_name() == "outside"),
21883 "the declaration outside every region stays parsed and unrecovered"
21884 );
21885 }
21886
21887 #[test]
21888 fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
21889 assert!(
21890 field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
21891 "the fixture must actually own fields"
21892 );
21893
21894 for (source, name) in [
21900 ("struct S { enum E { V }; };\n", "nested.hpp"),
21901 (
21902 "enum Color { RED };\nstruct Color { int RED; };\n",
21903 "class-like.c",
21904 ),
21905 ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
21906 (
21907 "enum E { V };\nnamespace ns { enum E { V }; }\n",
21908 "repeated.hpp",
21909 ),
21910 ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
21911 ] {
21912 field_owner_index_agreement(source, name);
21913 }
21914 }
21915
21916 fn repeated_class_blocks(blocks: usize) -> String {
21921 let mut source = String::from("namespace demo {\n");
21922 for index in 0..blocks {
21923 let _ = write!(
21924 source,
21925 "\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"
21926 );
21927 }
21928 source.push_str("\n}\n");
21929 source
21930 }
21931
21932 #[test]
21940 fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
21941 let mut answers = Vec::new();
21942 let mut visits = Vec::new();
21943 let mut node_counts = Vec::new();
21944 for blocks in [200usize, 400] {
21945 let source = repeated_class_blocks(blocks);
21946 let mut parser = tree_sitter::Parser::new();
21947 parser
21948 .set_language(&tree_sitter_cpp::LANGUAGE.into())
21949 .unwrap();
21950 let tree = parser.parse(&source, None).unwrap();
21951 let start_byte = source.find("class Widget0 ").expect("first class");
21952 let end_byte = start_byte
21953 + source[start_byte..]
21954 .find("};")
21955 .expect("first class terminator")
21956 + "};".len();
21957 let range = Range {
21958 start_byte,
21959 end_byte,
21960 start_line: 0,
21961 end_line: 0,
21962 };
21963 reset_recovered_class_body_node_visits_for_test();
21964 let recovered_export_classes =
21965 CppRecoveredExportClassIndex::build(tree.root_node(), &source);
21966 answers.push(recovered_class_body_at(
21967 &recovered_export_classes,
21968 tree.root_node(),
21969 &source,
21970 "Widget0",
21971 &range,
21972 ));
21973 visits.push(recovered_class_body_node_visits_for_test());
21974 let mut nodes = 0usize;
21975 let mut stack = vec![tree.root_node()];
21976 while let Some(node) = stack.pop() {
21977 nodes += 1;
21978 let mut cursor = node.walk();
21979 stack.extend(node.named_children(&mut cursor));
21980 }
21981 node_counts.push(nodes);
21982 }
21983
21984 assert_eq!(
21985 answers,
21986 vec![None, None],
21987 "no recovered shape claims a plain class"
21988 );
21989 assert_eq!(
21990 visits[0], visits[1],
21991 "the walk must follow the range's own path, so doubling the unrelated \
21992 classes must not change the node count: {visits:?} over trees of \
21993 {node_counts:?} nodes"
21994 );
21995 assert!(
21996 visits[1] * 20 < node_counts[1],
21997 "the walk must stay far below one pass over the tree: {visits:?} over \
21998 trees of {node_counts:?} nodes"
21999 );
22000 }
22001
22002 #[test]
22003 fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
22004 let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
22005 let mut parser = tree_sitter::Parser::new();
22006 parser
22007 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22008 .expect("C++ grammar");
22009 let tree = parser.parse(source, None).expect("fixture tree");
22010 let mut stack = vec![tree.root_node()];
22011 let mut recovered = None;
22012 while let Some(node) = stack.pop() {
22013 if node.kind() == "field_declaration"
22014 && let Some(field) = recovered_function_like_field_declarator(node, source)
22015 {
22016 recovered = Some((node, field.name));
22017 break;
22018 }
22019 let mut cursor = node.walk();
22020 stack.extend(node.named_children(&mut cursor));
22021 }
22022 let (declaration, name) = recovered.unwrap_or_else(|| {
22023 panic!(
22024 "pointer-wrapped macro field was not recovered: {}",
22025 tree.root_node().to_sexp()
22026 )
22027 });
22028 assert_eq!(node_text(name, source), "handshake");
22029 let recovered =
22030 recovered_function_like_field_declarator(declaration, source).expect("recovered field");
22031 assert_eq!(recovered.pointer_depth(), 1);
22032 assert_eq!(
22033 render_cpp_field_signature(declaration, name, source),
22034 "struct handshake * handshake;"
22035 );
22036 }
22037
22038 #[test]
22039 fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
22040 let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
22041 let mut parser = tree_sitter::Parser::new();
22042 parser
22043 .set_language(&tree_sitter_cpp::LANGUAGE.into())
22044 .expect("C++ grammar");
22045 let tree = parser.parse(source, None).expect("fixture tree");
22046 let mut stack = vec![tree.root_node()];
22047 let mut recovered = None;
22048 while let Some(node) = stack.pop() {
22049 if let Some(field) = recovered_pyobject_head_field(node, source) {
22050 recovered = Some((node, field));
22051 break;
22052 }
22053 let mut cursor = node.walk();
22054 stack.extend(node.named_children(&mut cursor));
22055 }
22056 let (declaration, recovered) = recovered.unwrap_or_else(|| {
22057 panic!(
22058 "pointer field after PyObject_HEAD was not recovered: {}",
22059 tree.root_node().to_sexp()
22060 )
22061 });
22062 assert_eq!(node_text(recovered.name, source), "image");
22063 assert_eq!(recovered.pointer_depth(), 1);
22064 assert_eq!(
22065 render_cpp_field_signature(declaration, recovered.declarator, source),
22066 "ImagingObject *image;"
22067 );
22068 }
22069}